MessageDlg.cpp 40 KB

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