MessageDlg.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <Consts.hpp>
  5. #include <GUITools.h>
  6. #include <Common.h>
  7. #include <VCLCommon.h>
  8. #include <CoreMain.h>
  9. #include <WinInterface.h>
  10. #include <Tools.h>
  11. #include <TextsWin.h>
  12. #include <TextsCore.h>
  13. #include <Vcl.Imaging.pngimage.hpp>
  14. #include <StrUtils.hpp>
  15. #include <PasTools.hpp>
  16. #include <Math.hpp>
  17. #include <vssym32.h>
  18. #include <WebBrowserEx.hpp>
  19. #include <Setup.h>
  20. //---------------------------------------------------------------------------
  21. #pragma package(smart_init)
  22. //---------------------------------------------------------------------------
  23. class TMessageButton : public TButton
  24. {
  25. public:
  26. __fastcall TMessageButton(TComponent * Owner);
  27. protected:
  28. virtual void __fastcall Dispatch(void * Message);
  29. private:
  30. void __fastcall WMGetDlgCode(TWMGetDlgCode & Message);
  31. };
  32. //---------------------------------------------------------------------------
  33. __fastcall TMessageButton::TMessageButton(TComponent * Owner) :
  34. TButton(Owner)
  35. {
  36. }
  37. //---------------------------------------------------------------------------
  38. void __fastcall TMessageButton::Dispatch(void * Message)
  39. {
  40. TMessage * M = reinterpret_cast<TMessage*>(Message);
  41. if (M->Msg == WM_GETDLGCODE)
  42. {
  43. WMGetDlgCode(*((TWMGetDlgCode *)Message));
  44. }
  45. else
  46. {
  47. TButton::Dispatch(Message);
  48. }
  49. }
  50. //---------------------------------------------------------------------------
  51. void __fastcall TMessageButton::WMGetDlgCode(TWMGetDlgCode & Message)
  52. {
  53. TButton::Dispatch(&Message);
  54. // WORKAROUND
  55. // Windows default handler returns DLGC_WANTARROWS for split buttons,
  56. // what prevent left/right keys from being used for focusing next/previous buttons/controls.
  57. // Overrwide that. Though note that we need to pass the up/down keys back to button
  58. // to allow drop down, see TMessageForm::CMDialogKey
  59. Message.Result = Message.Result & ~DLGC_WANTARROWS;
  60. }
  61. //---------------------------------------------------------------------------
  62. class TMessageForm : public TForm
  63. {
  64. public:
  65. static TForm * __fastcall Create(const UnicodeString & Msg, TStrings * MoreMessages,
  66. TMsgDlgType DlgType, unsigned int Answers,
  67. const TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  68. unsigned int TimeoutAnswer, TButton ** TimeoutButton, const UnicodeString & ImageName,
  69. const UnicodeString & NeverAskAgainCaption, const UnicodeString & MoreMessagesUrl,
  70. TSize MoreMessagesSize, const UnicodeString & CustomCaption);
  71. virtual int __fastcall ShowModal();
  72. void __fastcall InsertPanel(TPanel * Panel);
  73. void __fastcall NavigateToUrl(const UnicodeString & Url);
  74. protected:
  75. __fastcall TMessageForm(TComponent * AOwner);
  76. virtual __fastcall ~TMessageForm();
  77. DYNAMIC void __fastcall KeyDown(Word & Key, TShiftState Shift);
  78. DYNAMIC void __fastcall KeyUp(Word & Key, TShiftState Shift);
  79. UnicodeString __fastcall GetFormText();
  80. UnicodeString __fastcall GetReportText();
  81. UnicodeString __fastcall NormalizeNewLines(UnicodeString Text);
  82. virtual void __fastcall CreateParams(TCreateParams & Params);
  83. DYNAMIC void __fastcall DoShow();
  84. virtual void __fastcall Dispatch(void * Message);
  85. void __fastcall MenuItemClick(TObject * Sender);
  86. void __fastcall ButtonDropDownClick(TObject * Sender);
  87. void __fastcall UpdateForShiftStateTimer(TObject * Sender);
  88. DYNAMIC void __fastcall SetZOrder(bool TopMost);
  89. private:
  90. typedef std::map<unsigned int, TButton *> TAnswerButtons;
  91. UnicodeString MessageText;
  92. TPanel * ContentsPanel;
  93. TMemo * MessageMemo;
  94. TPanel * MessageBrowserPanel;
  95. TWebBrowserEx * MessageBrowser;
  96. UnicodeString MessageBrowserUrl;
  97. TShiftState FShiftState;
  98. TTimer * FUpdateForShiftStateTimer;
  99. TForm * FDummyForm;
  100. bool FShowNoActivate;
  101. void __fastcall HelpButtonClick(TObject * Sender);
  102. void __fastcall ReportButtonClick(TObject * Sender);
  103. void __fastcall CMDialogKey(TWMKeyDown & Message);
  104. void __fastcall CMShowingChanged(TMessage & Message);
  105. void __fastcall UpdateForShiftState();
  106. TButton * __fastcall CreateButton(
  107. UnicodeString Name, UnicodeString Caption, unsigned int Answer,
  108. TNotifyEvent OnClick, bool IsTimeoutButton,
  109. int GroupWith, TShiftState GrouppedShiftState, bool ElevationRequired,
  110. TAnswerButtons & AnswerButtons, bool HasMoreMessages, int & ButtonWidths);
  111. };
  112. //---------------------------------------------------------------------------
  113. __fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner, 0)
  114. {
  115. FShowNoActivate = false;
  116. MessageMemo = NULL;
  117. MessageBrowserPanel = NULL;
  118. MessageBrowser = NULL;
  119. FUpdateForShiftStateTimer = NULL;
  120. Position = poOwnerFormCenter;
  121. UseSystemSettingsPre(this);
  122. FDummyForm = new TForm(this);
  123. UseSystemSettings(FDummyForm);
  124. }
  125. //---------------------------------------------------------------------------
  126. __fastcall TMessageForm::~TMessageForm()
  127. {
  128. SAFE_DESTROY(FDummyForm);
  129. SAFE_DESTROY(FUpdateForShiftStateTimer);
  130. }
  131. //---------------------------------------------------------------------------
  132. void __fastcall TMessageForm::HelpButtonClick(TObject * /*Sender*/)
  133. {
  134. if (HelpKeyword != HELP_NONE)
  135. {
  136. FormHelp(this);
  137. }
  138. else
  139. {
  140. MessageWithNoHelp(GetReportText());
  141. }
  142. }
  143. //---------------------------------------------------------------------------
  144. void __fastcall TMessageForm::ReportButtonClick(TObject * /*Sender*/)
  145. {
  146. // Report text goes last, as it may exceed URL parameters limit (2048) and get truncated.
  147. // And we need to preserve the other parameters.
  148. UnicodeString Url =
  149. FMTLOAD(ERROR_REPORT_URL2,
  150. (Configuration->ProductVersion, GUIConfiguration->LocaleHex,
  151. EncodeUrlString(GetReportText())));
  152. OpenBrowser(Url);
  153. }
  154. //---------------------------------------------------------------------------
  155. void __fastcall TMessageForm::UpdateForShiftState()
  156. {
  157. TShiftState ShiftState =
  158. KeyboardStateToShiftState() * AllKeyShiftStates();
  159. if (FShiftState != ShiftState)
  160. {
  161. FShiftState = ShiftState;
  162. for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
  163. {
  164. TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
  165. if ((Button != NULL) && (Button->DropDownMenu != NULL))
  166. {
  167. TMenuItem * MenuItems = Button->DropDownMenu->Items;
  168. for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)
  169. {
  170. TMenuItem * Item = MenuItems->Items[ItemIndex];
  171. TShiftState GrouppedShiftState(Item->Tag >> 16);
  172. if (Item->Enabled &&
  173. ((ShiftState.Empty() && Item->Default) ||
  174. (!ShiftState.Empty() && (ShiftState == GrouppedShiftState))))
  175. {
  176. Button->Caption = CopyToChar(Item->Caption, L'\t', false);
  177. Button->ModalResult = Item->Tag & 0xFFFF;
  178. DebugAssert(Button->OnClick == NULL);
  179. DebugAssert(Item->OnClick == MenuItemClick);
  180. break;
  181. }
  182. }
  183. }
  184. }
  185. }
  186. }
  187. //---------------------------------------------------------------------------
  188. void __fastcall TMessageForm::KeyUp(Word & Key, TShiftState Shift)
  189. {
  190. UpdateForShiftState();
  191. TForm::KeyUp(Key, Shift);
  192. }
  193. //---------------------------------------------------------------------------
  194. void __fastcall TMessageForm::KeyDown(Word & Key, TShiftState Shift)
  195. {
  196. if (Shift.Contains(ssCtrl) && (Key == L'C'))
  197. {
  198. TInstantOperationVisualizer Visualizer;
  199. CopyToClipboard(GetFormText());
  200. }
  201. else
  202. {
  203. if (!Shift.Contains(ssCtrl))
  204. {
  205. for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
  206. {
  207. TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
  208. if ((Button != NULL) && (Button->DropDownMenu != NULL))
  209. {
  210. TMenuItem * MenuItems = Button->DropDownMenu->Items;
  211. for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)
  212. {
  213. TMenuItem * Item = MenuItems->Items[ItemIndex];
  214. if (IsAccel(Key, MenuItems->Items[ItemIndex]->Caption))
  215. {
  216. Item->OnClick(Item);
  217. Key = 0;
  218. break;
  219. }
  220. }
  221. }
  222. if (Key == 0)
  223. {
  224. break;
  225. }
  226. }
  227. }
  228. UpdateForShiftState();
  229. TForm::KeyDown(Key, Shift);
  230. }
  231. }
  232. //---------------------------------------------------------------------------
  233. UnicodeString __fastcall TMessageForm::NormalizeNewLines(UnicodeString Text)
  234. {
  235. Text = ReplaceStr(Text, L"\r", L"");
  236. Text = ReplaceStr(Text, L"\n", L"\r\n");
  237. return Text;
  238. }
  239. //---------------------------------------------------------------------------
  240. UnicodeString __fastcall TMessageForm::GetFormText()
  241. {
  242. UnicodeString DividerLine, ButtonCaptions;
  243. DividerLine = UnicodeString::StringOfChar(L'-', 27) + sLineBreak;
  244. for (int i = 0; i < ComponentCount - 1; i++)
  245. {
  246. if (dynamic_cast<TButton*>(Components[i]) != NULL)
  247. {
  248. ButtonCaptions += dynamic_cast<TButton*>(Components[i])->Caption +
  249. UnicodeString::StringOfChar(L' ', 3);
  250. }
  251. }
  252. ButtonCaptions = ReplaceStr(ButtonCaptions, L"&", L"");
  253. UnicodeString MoreMessages;
  254. if (MessageMemo != NULL)
  255. {
  256. MoreMessages = MessageMemo->Text + DividerLine;
  257. }
  258. else if (MessageBrowser != NULL)
  259. {
  260. MessageBrowser->SelectAll();
  261. MessageBrowser->CopyToClipBoard();
  262. if (NonEmptyTextFromClipboard(MoreMessages))
  263. {
  264. if (!EndsStr(sLineBreak, MoreMessages))
  265. {
  266. MoreMessages += sLineBreak;
  267. }
  268. MoreMessages += DividerLine;
  269. }
  270. // http://www.ssicom.org/js/x277333.htm
  271. MessageBrowser->DoCommand(L"UNSELECT");
  272. }
  273. UnicodeString MessageCaption = NormalizeNewLines(MessageText);
  274. UnicodeString Result = FORMAT(L"%s%s%s%s%s%s%s%s%s%s%s", (DividerLine, Caption, sLineBreak,
  275. DividerLine, MessageCaption, sLineBreak, DividerLine, MoreMessages,
  276. ButtonCaptions, sLineBreak, DividerLine));
  277. return Result;
  278. }
  279. //---------------------------------------------------------------------------
  280. UnicodeString __fastcall TMessageForm::GetReportText()
  281. {
  282. UnicodeString Text = MessageText;
  283. Text = Text.TrimRight();
  284. if (MessageMemo != NULL)
  285. {
  286. Text += L"\n\n" + MessageMemo->Text;
  287. }
  288. // Currently we use browser for updates box only and it has help context,
  289. // and does not have Report button, so we cannot get here.
  290. DebugAssert(MessageBrowser == NULL);
  291. Text = NormalizeNewLines(Text);
  292. UnicodeString ReportErrorText = NormalizeNewLines(FMTLOAD(REPORT_ERROR, (L"")));
  293. Text = ReplaceStr(Text, ReportErrorText, L"");
  294. Text = Trim(Text);
  295. return Text;
  296. }
  297. //---------------------------------------------------------------------------
  298. void __fastcall TMessageForm::CMDialogKey(TWMKeyDown & Message)
  299. {
  300. // this gets used in WinInterface.cpp SetTimeoutEvents
  301. if (OnKeyDown != NULL)
  302. {
  303. OnKeyDown(this, Message.CharCode, KeyDataToShiftState(Message.KeyData));
  304. }
  305. if (Message.CharCode == VK_MENU)
  306. {
  307. bool AnyButtonWithGrouppedCommandsWithShiftState = false;
  308. for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)
  309. {
  310. TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);
  311. if ((Button != NULL) && (Button->DropDownMenu != NULL))
  312. {
  313. // we should check if there are any commands with shift state,
  314. // but it's bit overkill
  315. AnyButtonWithGrouppedCommandsWithShiftState = true;
  316. break;
  317. }
  318. }
  319. // this is to make Alt only alter button meaning (if there is any
  320. // alternable button) and not popup system menu
  321. if (AnyButtonWithGrouppedCommandsWithShiftState)
  322. {
  323. Message.Result = 1;
  324. UpdateForShiftState();
  325. }
  326. else
  327. {
  328. TForm::Dispatch(&Message);
  329. }
  330. }
  331. else if ((Message.CharCode == VK_UP) || (Message.CharCode == VK_DOWN))
  332. {
  333. // WORKAROUND
  334. // noop to make up/down be passed back to button to allow drop down,
  335. // see TMessageButton::WMGetDlgCode
  336. }
  337. else
  338. {
  339. TForm::Dispatch(&Message);
  340. }
  341. }
  342. //---------------------------------------------------------------------------
  343. int __fastcall TMessageForm::ShowModal()
  344. {
  345. if (IsApplicationMinimized())
  346. {
  347. FShowNoActivate = true;
  348. }
  349. int Result = TForm::ShowModal();
  350. UnhookFormActivation(this);
  351. return Result;
  352. }
  353. //---------------------------------------------------------------------------
  354. void __fastcall TMessageForm::SetZOrder(bool TopMost)
  355. {
  356. // WORKAROUND: If application is minimized,
  357. // swallow call to BringToFront() from TForm::ShowModal()
  358. if (FShowNoActivate && TopMost)
  359. {
  360. // noop
  361. }
  362. else
  363. {
  364. TForm::SetZOrder(TopMost);
  365. }
  366. }
  367. //---------------------------------------------------------------------------
  368. void __fastcall TMessageForm::CMShowingChanged(TMessage & Message)
  369. {
  370. if (Showing && FShowNoActivate)
  371. {
  372. // With is same as SendToBack, except for added SWP_NOACTIVATE (VCLCOPY)
  373. SetWindowPos(WindowHandle, HWND_BOTTOM, 0, 0, 0, 0,
  374. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  375. // This replaces TCustomForm::CMShowingChanged()
  376. // which calls ShowWindow(Handle, SW_SHOWNORMAL).
  377. ShowWindow(Handle, SW_SHOWNOACTIVATE);
  378. // - so we have to call DoShow explicitly.
  379. DoShow();
  380. // - also we skip applying TForm::Position (VCLCOPY)
  381. if (DebugAlwaysTrue(Position == poOwnerFormCenter))
  382. {
  383. TCustomForm * CenterForm = Application->MainForm;
  384. TCustomForm * OwnerForm = dynamic_cast<TCustomForm *>(Owner);
  385. if (OwnerForm != NULL)
  386. {
  387. CenterForm = OwnerForm;
  388. }
  389. int X, Y;
  390. if ((CenterForm != NULL) && (CenterForm != this))
  391. {
  392. TRect Bounds = CenterForm->BoundsRect;
  393. X = ((Bounds.Width() - Width) / 2) + CenterForm->Left;
  394. Y = ((Bounds.Height() - Height) / 2) + CenterForm->Top;
  395. }
  396. else
  397. {
  398. X = (Screen->Width - Width) / 2;
  399. Y = (Screen->Height - Height) / 2;
  400. }
  401. if (X < Screen->DesktopLeft)
  402. {
  403. X = Screen->DesktopLeft;
  404. }
  405. if (Y < Screen->DesktopTop)
  406. {
  407. Y = Screen->DesktopTop;
  408. }
  409. SetBounds(X, Y, Width, Height);
  410. // We cannot call SetWindowToMonitor().
  411. // We cannot set FPosition = poDesigned, so workarea-checking code
  412. // in DoFormWindowProc is not triggered
  413. }
  414. // If application is restored, message box is not activated, do it manually.
  415. // Wait for application to be activate to activate ourself.
  416. HookFormActivation(this);
  417. }
  418. else
  419. {
  420. TForm::Dispatch(&Message);
  421. }
  422. }
  423. //---------------------------------------------------------------------------
  424. void __fastcall TMessageForm::Dispatch(void * Message)
  425. {
  426. TMessage * M = reinterpret_cast<TMessage*>(Message);
  427. if (M->Msg == CM_DIALOGKEY)
  428. {
  429. CMDialogKey(*((TWMKeyDown *)Message));
  430. }
  431. else if (M->Msg == CM_SHOWINGCHANGED)
  432. {
  433. CMShowingChanged(*M);
  434. }
  435. else
  436. {
  437. TForm::Dispatch(Message);
  438. }
  439. }
  440. //---------------------------------------------------------------------------
  441. void __fastcall TMessageForm::CreateParams(TCreateParams & Params)
  442. {
  443. TForm::CreateParams(Params);
  444. if ((Screen != NULL) && (Screen->ActiveForm != NULL) &&
  445. Screen->ActiveForm->HandleAllocated())
  446. {
  447. Params.WndParent = Screen->ActiveForm->Handle;
  448. }
  449. }
  450. //---------------------------------------------------------------------------
  451. void __fastcall TMessageForm::DoShow()
  452. {
  453. UseSystemSettingsPost(this);
  454. if (!MessageBrowserUrl.IsEmpty() &&
  455. // Guard against repeated calls to DoOpen()
  456. (MessageBrowser == NULL))
  457. {
  458. // Web Browser component does not seem to work,
  459. // when created before any window is shown.
  460. // I.e. when the message dialog is the first window (like when /update is used).
  461. // So we have to delay its creation until at least the dialog box is shown.
  462. MessageBrowser = CreateBrowserViewer(MessageBrowserPanel, LoadStr(MESSAGE_LOADING));
  463. MessageBrowser->SendToBack();
  464. MessageBrowser->Navigate(MessageBrowserUrl.c_str());
  465. }
  466. // Need OnShow to be called only after MessageBrowser is created,
  467. // so that the implementation (InsertDonateLink) can call InsertPanelToMessageDialog
  468. TForm::DoShow();
  469. }
  470. //---------------------------------------------------------------------------
  471. void __fastcall TMessageForm::MenuItemClick(TObject * Sender)
  472. {
  473. TMenuItem * Item = DebugNotNull(dynamic_cast<TMenuItem *>(Sender));
  474. ModalResult = (Item->Tag & 0xFFFF);
  475. }
  476. //---------------------------------------------------------------------------
  477. void __fastcall TMessageForm::UpdateForShiftStateTimer(TObject * /*Sender*/)
  478. {
  479. // this is needed to reflect shift state, even when we do not have a keyboard
  480. // focus, what happens when drop down menu is popped up
  481. UpdateForShiftState();
  482. }
  483. //---------------------------------------------------------------------------
  484. void __fastcall TMessageForm::ButtonDropDownClick(TObject * /*Sender*/)
  485. {
  486. // as optimization, do not waste time running timer, unless
  487. // user pops up drop down menu. we do not have a way to stop timer, once
  488. // it closes, but functionaly it does not matter
  489. if (FUpdateForShiftStateTimer == NULL)
  490. {
  491. FUpdateForShiftStateTimer = new TTimer(this);
  492. FUpdateForShiftStateTimer->Interval = 50;
  493. FUpdateForShiftStateTimer->OnTimer = UpdateForShiftStateTimer;
  494. }
  495. }
  496. //---------------------------------------------------------------------------
  497. const ResourceString * Captions[] = { &_SMsgDlgWarning, &_SMsgDlgError, &_SMsgDlgInformation,
  498. &_SMsgDlgConfirm, NULL };
  499. const wchar_t * ImageNames[] = { L"Warning", L"Error", L"Information",
  500. L"Help Blue", NULL };
  501. const int mcHorzMargin = 10;
  502. const int mcVertMargin = 13;
  503. const int mcHorzSpacing = 12;
  504. const int mcButtonVertMargin = 7;
  505. const int mcButtonSpacing = 5;
  506. // includes mcVertMargin
  507. const int mcMoreMessageHeight = 86;
  508. // approximately what Windows Vista task dialogs use,
  509. // actually they probably has fixed width
  510. const int mcMaxDialogWidth = 340;
  511. const int mcMinDialogWidth = 310;
  512. const int mcMinDialogwithMoreMessagesWidth = 400;
  513. //---------------------------------------------------------------------------
  514. static UnicodeString __fastcall GetKeyNameStr(int Key)
  515. {
  516. wchar_t Buf[MAX_PATH];
  517. LONG VirtualKey = MapVirtualKey(Key, MAPVK_VK_TO_VSC);
  518. VirtualKey <<= 16;
  519. if (GetKeyNameText(VirtualKey, Buf, LENOF(Buf)) > 0)
  520. {
  521. NULL_TERMINATE(Buf);
  522. }
  523. else
  524. {
  525. Buf[0] = L'\0';
  526. }
  527. return Buf;
  528. }
  529. //---------------------------------------------------------------------------
  530. TButton * __fastcall TMessageForm::CreateButton(
  531. UnicodeString Name, UnicodeString Caption, unsigned int Answer,
  532. TNotifyEvent OnClick, bool IsTimeoutButton,
  533. int GroupWith, TShiftState GrouppedShiftState, bool ElevationRequired,
  534. TAnswerButtons & AnswerButtons, bool HasMoreMessages, int & ButtonWidths)
  535. {
  536. UnicodeString MeasureCaption = Caption;
  537. if (IsTimeoutButton)
  538. {
  539. MeasureCaption = FMTLOAD(TIMEOUT_BUTTON, (MeasureCaption, 99));
  540. }
  541. TRect TextRect;
  542. DrawText(Canvas->Handle,
  543. UnicodeString(MeasureCaption).c_str(), -1,
  544. &TextRect, DT_CALCRECT | DT_LEFT | DT_SINGLELINE |
  545. DrawTextBiDiModeFlagsReadingOnly());
  546. int CurButtonWidth = TextRect.Right - TextRect.Left + ScaleByTextHeightRunTime(this, 16);
  547. if (ElevationRequired && IsVista())
  548. {
  549. // Elevation icon
  550. CurButtonWidth += ScaleByTextHeightRunTime(this, 16);
  551. }
  552. TButton * Button = NULL;
  553. if (SupportsSplitButton() &&
  554. (GroupWith >= 0) &&
  555. DebugAlwaysTrue(AnswerButtons.find(GroupWith) != AnswerButtons.end()))
  556. {
  557. TButton * GroupWithButton = AnswerButtons[GroupWith];
  558. if (GroupWithButton->DropDownMenu == NULL)
  559. {
  560. GroupWithButton->Style = TCustomButton::bsSplitButton;
  561. GroupWithButton->DropDownMenu = new TPopupMenu(this);
  562. // cannot handle subitems with shift state,
  563. // if the button has its own handler
  564. // (though it may not be the case still here)
  565. DebugAssert(GroupWithButton->OnClick == NULL);
  566. TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);
  567. GroupWithButton->DropDownMenu->Items->Add(Item);
  568. GroupWithButton->OnDropDownClick = ButtonDropDownClick;
  569. Item->Caption = GroupWithButton->Caption;
  570. Item->OnClick = MenuItemClick;
  571. DebugAssert(GroupWithButton->ModalResult <= 0xFFFF);
  572. Item->Tag = GroupWithButton->ModalResult;
  573. Item->Default = true;
  574. }
  575. TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);
  576. GroupWithButton->DropDownMenu->Items->Add(Item);
  577. // See ShortCutToText in Vcl.Menus.pas
  578. if (GrouppedShiftState == (TShiftState() << ssAlt))
  579. {
  580. Caption = Caption + L"\t" + GetKeyNameStr(VK_MENU);
  581. }
  582. else if (GrouppedShiftState == (TShiftState() << ssCtrl))
  583. {
  584. Caption = Caption + L"\t" + GetKeyNameStr(VK_CONTROL);
  585. }
  586. else if (GrouppedShiftState == (TShiftState() << ssShift))
  587. {
  588. Caption = Caption + L"\t" + GetKeyNameStr(VK_SHIFT);
  589. }
  590. else
  591. {
  592. // do not support combined shift states yet
  593. DebugAssert(GrouppedShiftState == TShiftState());
  594. }
  595. Item->Caption = Caption;
  596. if (OnClick != NULL)
  597. {
  598. Item->OnClick = OnClick;
  599. }
  600. else
  601. {
  602. Item->OnClick = MenuItemClick;
  603. DebugAssert((Answer <= 0xFFFF) && (GrouppedShiftState.ToInt() <= 0xFFFF));
  604. Item->Tag = Answer + (GrouppedShiftState.ToInt() << 16);
  605. }
  606. // Hard-coded drop down button width (do not know how to ask for system width).
  607. // Also we do not update the max button width for the default groupped
  608. // button caption. We just blindly hope that captions of advanced commands
  609. // are always longer than the caption of simple default command
  610. CurButtonWidth += ScaleByTextHeightRunTime(this, 15);
  611. // never shrink buttons below their default width
  612. if (GroupWithButton->Width < CurButtonWidth)
  613. {
  614. ButtonWidths += CurButtonWidth - GroupWithButton->Width;
  615. GroupWithButton->Width = CurButtonWidth;
  616. }
  617. DebugAssert(!ElevationRequired);
  618. }
  619. else
  620. {
  621. Button = new TMessageButton(this);
  622. Button->Name = Name;
  623. Button->Parent = this;
  624. Button->Caption = Caption;
  625. // Scale buttons using regular font, so that they are as large as buttons
  626. // on other dialogs (note that they are still higher than Windows Task dialog
  627. // buttons)
  628. Button->Height = ScaleByTextHeightRunTime(FDummyForm, Button->Height);
  629. Button->Width = ScaleByTextHeightRunTime(FDummyForm, Button->Width);
  630. if (OnClick != NULL)
  631. {
  632. Button->OnClick = OnClick;
  633. }
  634. else
  635. {
  636. Button->ModalResult = Answer;
  637. }
  638. if (HasMoreMessages)
  639. {
  640. Button->Anchors = TAnchors() << akBottom << akLeft;
  641. }
  642. // never shrink buttons below their default width
  643. if (Button->Width < CurButtonWidth)
  644. {
  645. Button->Width = CurButtonWidth;
  646. }
  647. Button->ElevationRequired = ElevationRequired;
  648. ButtonWidths += Button->Width;
  649. }
  650. return Button;
  651. }
  652. //---------------------------------------------------------------------------
  653. void __fastcall TMessageForm::InsertPanel(TPanel * Panel)
  654. {
  655. if (DebugAlwaysTrue(MessageBrowser != NULL))
  656. {
  657. // we currently use this for updates message box only
  658. TControl * ContentsControl = static_cast<TControl *>(DebugNotNull(MessageBrowser))->Parent;
  659. Panel->Width = ContentsControl->Width;
  660. Panel->Left = ContentsControl->Left;
  661. int ContentsBottom = ContentsControl->Top + ContentsControl->Height;
  662. Panel->Top = ContentsBottom + ((ContentsPanel->Height - ContentsBottom) / 2);
  663. Height = Height + Panel->Height;
  664. ContentsPanel->Height = ContentsPanel->Height + Panel->Height;
  665. Panel->Parent = ContentsPanel;
  666. // The panel itself does not need this, as the ParentBackground (true by default)
  667. // has the same effect, but an eventual TStaticText on the panel
  668. // uses a wrong background color, if panel's ParentColor is not set.
  669. Panel->ParentColor = true;
  670. }
  671. }
  672. //---------------------------------------------------------------------------
  673. void __fastcall TMessageForm::NavigateToUrl(const UnicodeString & Url)
  674. {
  675. if (DebugAlwaysTrue(MessageBrowser != NULL))
  676. {
  677. NavigateBrowserToUrl(MessageBrowser, Url);
  678. }
  679. }
  680. //---------------------------------------------------------------------------
  681. void __fastcall AnswerNameAndCaption(
  682. unsigned int Answer, UnicodeString & Name, UnicodeString & Caption)
  683. {
  684. switch (Answer)
  685. {
  686. case qaYes:
  687. Caption = LoadStr(_SMsgDlgYes.Identifier);
  688. Name = L"Yes";
  689. break;
  690. case qaNo:
  691. Caption = LoadStr(_SMsgDlgNo.Identifier);
  692. Name = L"No";
  693. break;
  694. case qaOK:
  695. Caption = LoadStr(_SMsgDlgOK.Identifier);
  696. Name = L"OK";
  697. break;
  698. case qaCancel:
  699. Caption = LoadStr(_SMsgDlgCancel.Identifier);
  700. Name = L"Cancel";
  701. break;
  702. case qaAbort:
  703. Caption = LoadStr(_SMsgDlgAbort.Identifier);
  704. Name = L"Abort";
  705. break;
  706. case qaRetry:
  707. Caption = LoadStr(_SMsgDlgRetry.Identifier);
  708. Name = L"Retry";
  709. break;
  710. case qaIgnore:
  711. Caption = LoadStr(_SMsgDlgIgnore.Identifier);
  712. Name = L"Ignore";
  713. break;
  714. // Own variant to avoid accelerator conflict with "Abort" button.
  715. // Note that as of now, ALL_BUTTON is never actually used,
  716. // because qaAll is always aliased
  717. case qaAll:
  718. Caption = LoadStr(ALL_BUTTON);
  719. Name = L"All";
  720. break;
  721. case qaNoToAll:
  722. Caption = LoadStr(_SMsgDlgNoToAll.Identifier);
  723. Name = L"NoToAll";
  724. break;
  725. // Own variant to avoid accelerator conflict with "Abort" button.
  726. case qaYesToAll:
  727. Caption = LoadStr(YES_TO_ALL_BUTTON);
  728. Name = L"YesToAll";
  729. break;
  730. case qaHelp:
  731. Caption = LoadStr(_SMsgDlgHelp.Identifier);
  732. Name = L"Help";
  733. break;
  734. case qaSkip:
  735. Caption = LoadStr(SKIP_BUTTON);
  736. Name = L"Skip";
  737. break;
  738. case qaReport:
  739. Caption = LoadStr(REPORT_BUTTON);
  740. Name = L"Report";
  741. break;
  742. default:
  743. DebugFail();
  744. throw Exception(L"Undefined answer");
  745. }
  746. }
  747. //---------------------------------------------------------------------------
  748. static int __fastcall CalculateWidthOnCanvas(UnicodeString Text, void * Arg)
  749. {
  750. TCanvas * Canvas = static_cast<TCanvas *>(Arg);
  751. return Canvas->TextWidth(Text);
  752. }
  753. //---------------------------------------------------------------------------
  754. TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
  755. TStrings * MoreMessages, TMsgDlgType DlgType, unsigned int Answers,
  756. const TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  757. unsigned int TimeoutAnswer, TButton ** TimeoutButton, const UnicodeString & AImageName,
  758. const UnicodeString & NeverAskAgainCaption, const UnicodeString & MoreMessagesUrl,
  759. TSize MoreMessagesSize, const UnicodeString & CustomCaption)
  760. {
  761. unsigned int DefaultAnswer;
  762. if (FLAGSET(Answers, qaOK))
  763. {
  764. DefaultAnswer = qaOK;
  765. }
  766. else if (FLAGSET(Answers, qaYes))
  767. {
  768. DefaultAnswer = qaYes;
  769. }
  770. else
  771. {
  772. DefaultAnswer = qaRetry;
  773. }
  774. unsigned int CancelAnswer = ::CancelAnswer(Answers);
  775. if (TimeoutButton != NULL)
  776. {
  777. *TimeoutButton = NULL;
  778. }
  779. TColor MainInstructionColor = Graphics::clNone;
  780. HFONT MainInstructionFont = 0;
  781. HFONT InstructionFont = 0;
  782. HTHEME Theme = OpenThemeData(0, L"TEXTSTYLE");
  783. if (Theme != NULL)
  784. {
  785. LOGFONT AFont;
  786. COLORREF AColor;
  787. memset(&AFont, 0, sizeof(AFont));
  788. if (GetThemeFont(Theme, NULL, TEXT_MAININSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
  789. {
  790. MainInstructionFont = CreateFontIndirect(&AFont);
  791. }
  792. if (GetThemeColor(Theme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &AColor) == S_OK)
  793. {
  794. MainInstructionColor = (TColor)AColor;
  795. }
  796. memset(&AFont, 0, sizeof(AFont));
  797. if (GetThemeFont(Theme, NULL, TEXT_INSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
  798. {
  799. InstructionFont = CreateFontIndirect(&AFont);
  800. }
  801. CloseThemeData(Theme);
  802. }
  803. TMessageForm * Result = SafeFormCreate<TMessageForm>();
  804. if (InstructionFont != 0)
  805. {
  806. Result->Font->Handle = InstructionFont;
  807. }
  808. else
  809. {
  810. Result->Font->Assign(Screen->MessageFont);
  811. }
  812. // Can be possibly nul when error occurs before configuration is created
  813. if (Configuration != NULL)
  814. {
  815. Configuration->Usage->Set(L"ThemeMessageFontSize", Result->Font->Size);
  816. }
  817. // make sure we consider sizes of the monitor,
  818. // that is set in DoFormWindowProc(CM_SHOWINGCHANGED) later.
  819. Forms::TMonitor * Monitor = FormMonitor(Result);
  820. bool HasMoreMessages = (MoreMessages != NULL) || !MoreMessagesUrl.IsEmpty();
  821. Result->BiDiMode = Application->BiDiMode;
  822. Result->BorderStyle = bsDialog;
  823. Result->Canvas->Font = Result->Font;
  824. Result->KeyPreview = true;
  825. int HorzMargin = ScaleByTextHeightRunTime(Result, mcHorzMargin);
  826. int VertMargin = ScaleByTextHeightRunTime(Result, mcVertMargin);
  827. int HorzSpacing = ScaleByTextHeightRunTime(Result, mcHorzSpacing);
  828. int ButtonVertMargin = ScaleByTextHeightRunTime(Result, mcButtonVertMargin);
  829. int ButtonWidths = 0;
  830. int ButtonHeight = -1;
  831. std::vector<TButton *> ButtonControls;
  832. TAnswerButtons AnswerButtons;
  833. for (unsigned int Answer = qaFirst; Answer <= qaLast; Answer = Answer << 1)
  834. {
  835. if (FLAGSET(Answers, Answer))
  836. {
  837. DebugAssert(Answer != mrCancel);
  838. UnicodeString Caption;
  839. UnicodeString Name;
  840. AnswerNameAndCaption(Answer, Name, Caption);
  841. TNotifyEvent OnClick = NULL;
  842. int GroupWith = -1;
  843. TShiftState GrouppedShiftState;
  844. bool ElevationRequired = false;
  845. if (Aliases != NULL)
  846. {
  847. for (unsigned int i = 0; i < AliasesCount; i++)
  848. {
  849. if (Answer == Aliases[i].Button)
  850. {
  851. if (!Aliases[i].Alias.IsEmpty())
  852. {
  853. Caption = Aliases[i].Alias;
  854. }
  855. OnClick = Aliases[i].OnClick;
  856. GroupWith = Aliases[i].GroupWith;
  857. GrouppedShiftState = Aliases[i].GrouppedShiftState;
  858. ElevationRequired = Aliases[i].ElevationRequired;
  859. DebugAssert((OnClick == NULL) || (GrouppedShiftState == TShiftState()));
  860. break;
  861. }
  862. }
  863. }
  864. // we hope that all grouped-with buttons are for answer with greater
  865. // value that the answer to be grouped with
  866. if (GroupWith >= 0)
  867. {
  868. if (DebugAlwaysFalse(GroupWith >= static_cast<int>(Answer)) ||
  869. DebugAlwaysFalse(Answer == TimeoutAnswer) &&
  870. DebugAlwaysFalse(Answer == DefaultAnswer) &&
  871. DebugAlwaysFalse(Answer == CancelAnswer))
  872. {
  873. GroupWith = -1;
  874. }
  875. }
  876. bool IsTimeoutButton = (TimeoutButton != NULL) && (Answer == TimeoutAnswer);
  877. if (Answer == qaHelp)
  878. {
  879. DebugAssert(OnClick == NULL);
  880. OnClick = Result->HelpButtonClick;
  881. }
  882. if (Answer == qaReport)
  883. {
  884. DebugAssert(OnClick == NULL);
  885. OnClick = Result->ReportButtonClick;
  886. }
  887. TButton * Button = Result->CreateButton(
  888. Name, Caption, Answer,
  889. OnClick, IsTimeoutButton, GroupWith, GrouppedShiftState, ElevationRequired,
  890. AnswerButtons, HasMoreMessages, ButtonWidths);
  891. if (Button != NULL)
  892. {
  893. ButtonControls.push_back(Button);
  894. Button->Default = (Answer == DefaultAnswer);
  895. Button->Cancel = (Answer == CancelAnswer);
  896. if (ButtonHeight < 0)
  897. {
  898. ButtonHeight = Button->Height;
  899. }
  900. DebugAssert(ButtonHeight == Button->Height);
  901. AnswerButtons.insert(TAnswerButtons::value_type(Answer, Button));
  902. if (IsTimeoutButton)
  903. {
  904. *TimeoutButton = Button;
  905. }
  906. }
  907. }
  908. }
  909. int NeverAskAgainWidth = 0;
  910. if (!NeverAskAgainCaption.IsEmpty())
  911. {
  912. NeverAskAgainWidth =
  913. ScaleByTextHeightRunTime(Result, 16) + // checkbox
  914. Result->Canvas->TextWidth(NeverAskAgainCaption) +
  915. ScaleByTextHeightRunTime(Result, 16); // margin
  916. }
  917. int ButtonSpacing = ScaleByTextHeightRunTime(Result, mcButtonSpacing);
  918. int ButtonGroupWidth = NeverAskAgainWidth;
  919. if (!ButtonControls.empty())
  920. {
  921. ButtonGroupWidth += ButtonWidths +
  922. ButtonSpacing * (ButtonControls.size() - 1);
  923. }
  924. DebugAssert((ButtonHeight > 0) && (ButtonWidths > 0));
  925. TPanel * Panel = CreateBlankPanel(Result);
  926. Result->ContentsPanel = Panel;
  927. Panel->Name = L"Panel";
  928. Panel->Parent = Result;
  929. Panel->Color = clWindow;
  930. Panel->ParentBackground = false;
  931. Panel->Anchors = TAnchors() << akLeft << akRight << akTop;
  932. Panel->Caption = L"";
  933. int IconWidth = 0;
  934. int IconHeight = 0;
  935. UnicodeString ImageName = AImageName;
  936. if (ImageName.IsEmpty() &&
  937. DebugAlwaysTrue(ImageNames[DlgType] != NULL))
  938. {
  939. ImageName = ImageNames[DlgType];
  940. }
  941. if (DebugAlwaysTrue(!ImageName.IsEmpty()))
  942. {
  943. TImage * Image = new TImage(Panel);
  944. Image->Name = L"Image";
  945. Image->Parent = Panel;
  946. LoadDialogImage(Image, ImageName);
  947. Image->SetBounds(HorzMargin, VertMargin, Image->Picture->Width, Image->Picture->Height);
  948. IconWidth = Image->Width + HorzSpacing;
  949. IconHeight = Image->Height;
  950. }
  951. int MaxTextWidth = ScaleByTextHeightRunTime(Result, mcMaxDialogWidth);
  952. // if the dialog would be wide anyway (overwrite confirmation on Windows XP),
  953. // to fit the buttons, do not restrict the text
  954. if (MaxTextWidth < ButtonGroupWidth - IconWidth)
  955. {
  956. MaxTextWidth = ButtonGroupWidth - IconWidth;
  957. }
  958. UnicodeString BodyMsg = Msg;
  959. BodyMsg = RemoveInteractiveMsgTag(BodyMsg);
  960. UnicodeString MainMsg;
  961. if (ExtractMainInstructions(BodyMsg, MainMsg))
  962. {
  963. Result->MessageText = MainMsg + BodyMsg;
  964. BodyMsg = BodyMsg.TrimLeft();
  965. }
  966. else
  967. {
  968. Result->MessageText = BodyMsg;
  969. }
  970. ApplyTabs(Result->MessageText, L' ', NULL, NULL);
  971. // Windows XP (not sure about Vista) does not support Hair space.
  972. // For Windows XP, we still keep the existing hack by using hard-coded spaces
  973. // in resource string
  974. if (CheckWin32Version(6, 1))
  975. {
  976. // Have to be padding with spaces (the smallest space defined, hair space = 1px),
  977. // as tabs actually do not tab, just expand to 8 spaces.
  978. // Otherwise we would have to do custom drawing
  979. // (using GetTabbedTextExtent and TabbedTextOut)
  980. const wchar_t HairSpace = L'\x200A';
  981. ApplyTabs(BodyMsg, HairSpace, CalculateWidthOnCanvas, Result->Canvas);
  982. }
  983. DebugAssert(MainMsg.Pos(L"\t") == 0);
  984. int IconTextWidth = -1;
  985. int IconTextHeight = 0;
  986. int ALeft = IconWidth + HorzMargin;
  987. for (int MessageIndex = 0; MessageIndex <= 1; MessageIndex++)
  988. {
  989. UnicodeString LabelMsg;
  990. UnicodeString LabelName;
  991. TColor LabelColor = Graphics::clNone;
  992. HFONT LabelFont = 0;
  993. switch (MessageIndex)
  994. {
  995. case 0:
  996. LabelMsg = MainMsg;
  997. LabelName = L"MainMessage";
  998. LabelColor = MainInstructionColor;
  999. LabelFont = MainInstructionFont;
  1000. break;
  1001. case 1:
  1002. LabelMsg = BodyMsg;
  1003. LabelName = L"Message";
  1004. break;
  1005. default:
  1006. DebugFail();
  1007. break;
  1008. }
  1009. if (!LabelMsg.IsEmpty())
  1010. {
  1011. TLabel * Message = new TLabel(Panel);
  1012. Message->Name = LabelName;
  1013. Message->Parent = Panel;
  1014. Message->WordWrap = true;
  1015. Message->Caption = LabelMsg;
  1016. Message->BiDiMode = Result->BiDiMode;
  1017. // added to show & as & for messages containing !& pattern of custom commands
  1018. // (suppose that we actually never want to use & as accel in message text)
  1019. Message->ShowAccelChar = false;
  1020. if (LabelFont != 0)
  1021. {
  1022. Message->Font->Handle = LabelFont;
  1023. if (DebugAlwaysTrue(LabelFont == MainInstructionFont))
  1024. {
  1025. Configuration->Usage->Set(L"ThemeMainInstructionFontSize", Message->Font->Size);
  1026. }
  1027. }
  1028. if (LabelColor != Graphics::clNone)
  1029. {
  1030. Message->Font->Color = LabelColor;
  1031. }
  1032. TRect TextRect;
  1033. SetRect(&TextRect, 0, 0, MaxTextWidth, 0);
  1034. DrawText(Message->Canvas->Handle, LabelMsg.c_str(), LabelMsg.Length() + 1, &TextRect,
  1035. DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |
  1036. Result->DrawTextBiDiModeFlagsReadingOnly());
  1037. int MaxWidth = Monitor->Width - HorzMargin * 2 - IconWidth - 30;
  1038. if (TextRect.right > MaxWidth)
  1039. {
  1040. // this will truncate the text, we should implement something smarter eventually
  1041. TextRect.right = MaxWidth;
  1042. }
  1043. IconTextWidth = Max(IconTextWidth, IconWidth + TextRect.Right);
  1044. if (IconTextHeight > 0)
  1045. {
  1046. IconTextHeight += VertMargin;
  1047. }
  1048. Message->SetBounds(ALeft, VertMargin + IconTextHeight, TextRect.Right, TextRect.Bottom);
  1049. IconTextHeight += TextRect.Bottom;
  1050. }
  1051. }
  1052. DebugAssert((IconTextWidth > 0) && (IconTextHeight > 0));
  1053. IconTextHeight = Max(IconTextHeight, IconHeight);
  1054. int MoreMessageHeight =
  1055. (HasMoreMessages ?
  1056. ScaleByTextHeightRunTime(Result, (MoreMessagesSize.Height > 0 ? MoreMessagesSize.Height : mcMoreMessageHeight)) : 0);
  1057. Panel->SetBounds(0, 0, Result->ClientWidth, VertMargin + IconTextHeight + VertMargin + MoreMessageHeight);
  1058. TControl * MoreMessagesControl = NULL;
  1059. if (HasMoreMessages)
  1060. {
  1061. if (MoreMessages != NULL)
  1062. {
  1063. DebugAssert(MoreMessagesUrl.IsEmpty());
  1064. TMemo * MessageMemo = new TMemo(Panel);
  1065. MoreMessagesControl = MessageMemo;
  1066. MessageMemo->Name = L"MessageMemo";
  1067. MessageMemo->Parent = Panel;
  1068. MessageMemo->ReadOnly = true;
  1069. MessageMemo->WantReturns = False;
  1070. MessageMemo->ScrollBars = ssVertical;
  1071. MessageMemo->Anchors = TAnchors() << akLeft << akRight << akTop;
  1072. MessageMemo->Lines->Text = MoreMessages->Text;
  1073. Result->MessageMemo = MessageMemo;
  1074. }
  1075. else if (DebugAlwaysTrue(!MoreMessagesUrl.IsEmpty()))
  1076. {
  1077. TPanel * MessageBrowserPanel = CreateBlankPanel(Panel);
  1078. MessageBrowserPanel->Parent = Panel;
  1079. MessageBrowserPanel->Anchors = TAnchors() << akLeft << akRight << akTop;
  1080. MessageBrowserPanel->BevelKind = bkTile; // flat border
  1081. Result->MessageBrowserPanel = MessageBrowserPanel;
  1082. MoreMessagesControl = Result->MessageBrowserPanel;
  1083. UnicodeString FontSizeParam = FORMAT(L"fontsize=%d", (Result->Font->Size));
  1084. UnicodeString Url = AppendUrlParams(CampaignUrl(MoreMessagesUrl), FontSizeParam);
  1085. Result->MessageBrowserUrl = Url;
  1086. }
  1087. }
  1088. int MinClientWidth =
  1089. ScaleByTextHeightRunTime(Result,
  1090. HasMoreMessages ? (MoreMessagesSize.Width > 0 ? MoreMessagesSize.Width : mcMinDialogwithMoreMessagesWidth) : mcMinDialogWidth);
  1091. int AClientWidth =
  1092. Max(
  1093. (IconTextWidth > ButtonGroupWidth ? IconTextWidth : ButtonGroupWidth) +
  1094. HorzMargin * 2,
  1095. MinClientWidth);
  1096. Result->ClientWidth = AClientWidth;
  1097. Result->ClientHeight =
  1098. Panel->Height + ButtonVertMargin + ButtonHeight + ButtonVertMargin;
  1099. Result->Left = (Monitor->Width / 2) - (Result->Width / 2);
  1100. Result->Top = (Monitor->Height / 2) - (Result->Height / 2);
  1101. if (!CustomCaption.IsEmpty())
  1102. {
  1103. Result->Caption = CustomCaption;
  1104. }
  1105. else if (DebugAlwaysTrue(DlgType != mtCustom))
  1106. {
  1107. Result->Caption = LoadResourceString(Captions[DlgType]);
  1108. }
  1109. else
  1110. {
  1111. Result->Caption = Application->Title;
  1112. }
  1113. if (MoreMessagesControl != NULL)
  1114. {
  1115. MoreMessagesControl->SetBounds(
  1116. ALeft,
  1117. Panel->Height - MoreMessageHeight,
  1118. Result->ClientWidth - ALeft - HorzMargin,
  1119. MoreMessageHeight - VertMargin);
  1120. }
  1121. int ButtonTop = Panel->Height + ButtonVertMargin;
  1122. int X = Result->ClientWidth - ButtonGroupWidth + NeverAskAgainWidth - HorzMargin;
  1123. for (unsigned int i = 0; i < ButtonControls.size(); i++)
  1124. {
  1125. ButtonControls[i]->SetBounds(
  1126. X, ButtonTop, ButtonControls[i]->Width, ButtonControls[i]->Height);
  1127. X += ButtonControls[i]->Width + ButtonSpacing;
  1128. }
  1129. if (!NeverAskAgainCaption.IsEmpty() &&
  1130. !ButtonControls.empty())
  1131. {
  1132. TCheckBox * NeverAskAgainCheck = new TCheckBox(Result);
  1133. NeverAskAgainCheck->Name = L"NeverAskAgainCheck";
  1134. NeverAskAgainCheck->Parent = Result;
  1135. NeverAskAgainCheck->Caption = NeverAskAgainCaption;
  1136. NeverAskAgainCheck->Anchors = TAnchors() << akBottom << akLeft;
  1137. TButton * FirstButton = ButtonControls[0];
  1138. int NeverAskAgainHeight = ScaleByTextHeightRunTime(Result, NeverAskAgainCheck->Height);
  1139. int NeverAskAgainTop = FirstButton->Top + ((FirstButton->Height - NeverAskAgainHeight) / 2);
  1140. int NeverAskAgainLeft = HorzMargin;
  1141. NeverAskAgainCheck->SetBounds(
  1142. NeverAskAgainLeft, NeverAskAgainTop, NeverAskAgainWidth, NeverAskAgainHeight);
  1143. }
  1144. return Result;
  1145. }
  1146. //---------------------------------------------------------------------------
  1147. TForm * __fastcall CreateMoreMessageDialog(const UnicodeString & Msg,
  1148. TStrings * MoreMessages, TMsgDlgType DlgType, unsigned int Answers,
  1149. const TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  1150. unsigned int TimeoutAnswer, TButton ** TimeoutButton, const UnicodeString & ImageName,
  1151. const UnicodeString & NeverAskAgainCaption, const UnicodeString & MoreMessagesUrl,
  1152. TSize MoreMessagesSize, const UnicodeString & CustomCaption)
  1153. {
  1154. return TMessageForm::Create(Msg, MoreMessages, DlgType, Answers,
  1155. Aliases, AliasesCount, TimeoutAnswer, TimeoutButton, ImageName,
  1156. NeverAskAgainCaption, MoreMessagesUrl, MoreMessagesSize, CustomCaption);
  1157. }
  1158. //---------------------------------------------------------------------------
  1159. void __fastcall InsertPanelToMessageDialog(TCustomForm * Form, TPanel * Panel)
  1160. {
  1161. TMessageForm * MessageForm = DebugNotNull(dynamic_cast<TMessageForm *>(Form));
  1162. MessageForm->InsertPanel(Panel);
  1163. }
  1164. //---------------------------------------------------------------------------
  1165. void __fastcall NavigateMessageDialogToUrl(TCustomForm * Form, const UnicodeString & Url)
  1166. {
  1167. TMessageForm * MessageForm = DebugNotNull(dynamic_cast<TMessageForm *>(Form));
  1168. MessageForm->NavigateToUrl(Url);
  1169. }