MessageDlg.cpp 41 KB

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