MessageDlg.cpp 42 KB

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