MessageDlg.cpp 40 KB

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