MessageDlg.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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 <WinInterface.h>
  9. #include <TextsWin.h>
  10. //---------------------------------------------------------------------------
  11. #pragma package(smart_init)
  12. //---------------------------------------------------------------------------
  13. class TMessageForm : public TForm
  14. {
  15. public:
  16. static TForm * __fastcall Create(const UnicodeString & Msg, TStrings * MoreMessages,
  17. TMsgDlgType DlgType, TMsgDlgButtons Buttons,
  18. TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  19. TMsgDlgBtn TimeoutResult, TButton ** TimeoutButton);
  20. protected:
  21. __fastcall TMessageForm(TComponent * AOwner);
  22. DYNAMIC void __fastcall KeyDown(Word & Key, TShiftState Shift);
  23. UnicodeString __fastcall GetFormText();
  24. virtual void __fastcall CreateParams(TCreateParams & Params);
  25. DYNAMIC void __fastcall DoShow();
  26. private:
  27. TLabel * Message;
  28. TMemo * MessageMemo;
  29. void __fastcall HelpButtonClick(TObject * Sender);
  30. };
  31. //---------------------------------------------------------------------------
  32. __fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner, 0)
  33. {
  34. Message = NULL;
  35. MessageMemo = NULL;
  36. TNonClientMetrics NonClientMetrics;
  37. NonClientMetrics.cbSize = sizeof(NonClientMetrics);
  38. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NonClientMetrics, 0))
  39. {
  40. Font->Handle = CreateFontIndirect(&NonClientMetrics.lfMessageFont);
  41. }
  42. Position = poOwnerFormCenter;
  43. UseSystemSettingsPre(this);
  44. }
  45. //---------------------------------------------------------------------------
  46. void __fastcall TMessageForm::HelpButtonClick(TObject * /*Sender*/)
  47. {
  48. if (HelpKeyword != HELP_NONE)
  49. {
  50. FormHelp(this);
  51. }
  52. else
  53. {
  54. UnicodeString Text = Message->Caption;
  55. if (MessageMemo != NULL)
  56. {
  57. Text += L"\n" + MessageMemo->Text;
  58. }
  59. MessageWithNoHelp(Text);
  60. }
  61. }
  62. //---------------------------------------------------------------------------
  63. void __fastcall TMessageForm::KeyDown(Word & Key, TShiftState Shift)
  64. {
  65. if (Shift.Contains(ssCtrl) && (Key == L'C'))
  66. {
  67. CopyToClipboard(GetFormText());
  68. }
  69. }
  70. //---------------------------------------------------------------------------
  71. UnicodeString __fastcall TMessageForm::GetFormText()
  72. {
  73. UnicodeString DividerLine, ButtonCaptions;
  74. DividerLine = UnicodeString::StringOfChar(L'-', 27) + sLineBreak;
  75. for (int i = 0; i < ComponentCount - 1; i++)
  76. {
  77. if (dynamic_cast<TButton*>(Components[i]) != NULL)
  78. {
  79. ButtonCaptions += dynamic_cast<TButton*>(Components[i])->Caption +
  80. UnicodeString::StringOfChar(L' ', 3);
  81. }
  82. }
  83. ButtonCaptions = StringReplace(ButtonCaptions, L"&", L"",
  84. TReplaceFlags() << rfReplaceAll);
  85. UnicodeString MoreMessages;
  86. if (MessageMemo != NULL)
  87. {
  88. MoreMessages = MessageMemo->Text + DividerLine;
  89. }
  90. UnicodeString MessageCaption;
  91. MessageCaption = StringReplace(Message->Caption, L"\r", L"", TReplaceFlags() << rfReplaceAll);
  92. MessageCaption = StringReplace(MessageCaption, L"\n", L"\r\n", TReplaceFlags() << rfReplaceAll);
  93. UnicodeString Result = FORMAT(L"%s%s%s%s%s%s%s%s%s%s%s", (DividerLine, Caption, sLineBreak,
  94. DividerLine, MessageCaption, sLineBreak, DividerLine, MoreMessages,
  95. ButtonCaptions, sLineBreak, DividerLine));
  96. return Result;
  97. }
  98. //---------------------------------------------------------------------------
  99. void __fastcall TMessageForm::CreateParams(TCreateParams & Params)
  100. {
  101. TForm::CreateParams(Params);
  102. if ((Screen != NULL) && (Screen->ActiveForm != NULL) &&
  103. Screen->ActiveForm->HandleAllocated())
  104. {
  105. Params.WndParent = Screen->ActiveForm->Handle;
  106. }
  107. }
  108. //---------------------------------------------------------------------------
  109. void __fastcall TMessageForm::DoShow()
  110. {
  111. UseSystemSettingsPost(this);
  112. TForm::DoShow();
  113. }
  114. //---------------------------------------------------------------------------
  115. const ResourceString * Captions[] = { &_SMsgDlgWarning, &_SMsgDlgError, &_SMsgDlgInformation,
  116. &_SMsgDlgConfirm, NULL };
  117. const wchar_t * IconIDs[] = { IDI_EXCLAMATION, IDI_HAND, IDI_ASTERISK,
  118. IDI_QUESTION, NULL };
  119. const int ButtonCount = 11;
  120. const UnicodeString ButtonNames[ButtonCount] = {
  121. L"Yes", L"No", L"OK", L"Cancel", L"Abort", L"Retry", L"Ignore", L"All", L"NoToAll",
  122. L"YesToAll", L"Help" };
  123. const ResourceString * ButtonCaptions[ButtonCount] = {
  124. &_SMsgDlgYes, &_SMsgDlgNo, &_SMsgDlgOK, &_SMsgDlgCancel, &_SMsgDlgAbort,
  125. &_SMsgDlgRetry, &_SMsgDlgIgnore, &_SMsgDlgAll, &_SMsgDlgNoToAll, &_SMsgDlgYesToAll,
  126. &_SMsgDlgHelp };
  127. extern const int ModalResults[ButtonCount] = {
  128. mrYes, mrNo, mrOk, mrCancel, mrAbort, mrRetry, mrIgnore, mrAll, mrNoToAll,
  129. mrYesToAll, 0 };
  130. const int mcHorzMargin = 8;
  131. const int mcVertMargin = 8;
  132. const int mcHorzSpacing = 10;
  133. const int mcVertSpacing = 10;
  134. const int mcButtonWidth = 50;
  135. const int mcButtonHeight = 14;
  136. const int mcButtonSpacing = 4;
  137. const int mcMoreMessageWidth = 320;
  138. const int mcMoreMessageHeight = 80;
  139. //---------------------------------------------------------------------------
  140. TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,
  141. TStrings * MoreMessages, TMsgDlgType DlgType, TMsgDlgButtons Buttons,
  142. TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  143. TMsgDlgBtn TimeoutResult, TButton ** TimeoutButton)
  144. {
  145. TRect TextRect;
  146. TMsgDlgBtn DefaultButton, CancelButton;
  147. if (Buttons.Contains(mbOK))
  148. {
  149. DefaultButton = mbOK;
  150. }
  151. else if (Buttons.Contains(mbYes))
  152. {
  153. DefaultButton = mbYes;
  154. }
  155. else
  156. {
  157. DefaultButton = mbRetry;
  158. }
  159. if (Buttons.Contains(mbCancel))
  160. {
  161. CancelButton = mbCancel;
  162. }
  163. else if (Buttons.Contains(mbNo))
  164. {
  165. CancelButton = mbNo;
  166. }
  167. else if (Buttons.Contains(mbAbort))
  168. {
  169. CancelButton = mbAbort;
  170. }
  171. else
  172. {
  173. CancelButton = mbOK;
  174. }
  175. if (TimeoutButton != NULL)
  176. {
  177. *TimeoutButton = NULL;
  178. }
  179. TMessageForm * Result = SafeFormCreate<TMessageForm>();
  180. Result->BiDiMode = Application->BiDiMode;
  181. Result->BorderStyle = bsDialog;
  182. Result->Canvas->Font = Result->Font;
  183. Result->KeyPreview = true;
  184. TPoint DialogUnits = GetAveCharSize(Result->Canvas);
  185. int HorzMargin = MulDiv(mcHorzMargin, DialogUnits.x, 4);
  186. int VertMargin = MulDiv(mcVertMargin, DialogUnits.y, 8);
  187. int HorzSpacing = MulDiv(mcHorzSpacing, DialogUnits.x, 4);
  188. int VertSpacing = MulDiv(mcVertSpacing, DialogUnits.y, 8);
  189. int ButtonWidth = MulDiv(mcButtonWidth, DialogUnits.x, 4);
  190. TButton * ButtonControls[ButtonCount + 1];
  191. int ButtonControlsCount = 0;
  192. for (unsigned int B = mbYes; B <= mbHelp; B++)
  193. {
  194. assert(B < ButtonCount);
  195. if (Buttons.Contains(TMsgDlgBtn(B)))
  196. {
  197. TextRect = Rect(0,0,0,0);
  198. UnicodeString Caption = LoadResourceString(ButtonCaptions[B]);
  199. // temporary fix of accelerators (&Abort vs. &All/Yes to &All)
  200. // must be removed
  201. if (Caption == L"&All")
  202. {
  203. Caption = L"A&ll";
  204. }
  205. else if (Caption == L"Yes to &All")
  206. {
  207. Caption = L"Yes to A&ll";
  208. }
  209. TNotifyEvent OnClick = NULL;
  210. if (Aliases != NULL)
  211. {
  212. for (unsigned int i = 0; i < AliasesCount; i++)
  213. {
  214. if (B == Aliases[i].Button)
  215. {
  216. Caption = Aliases[i].Alias;
  217. OnClick = Aliases[i].OnClick;
  218. break;
  219. }
  220. }
  221. }
  222. TButton * Button = new TButton(Result);
  223. UnicodeString MeasureCaption = Caption;
  224. if ((TimeoutButton != NULL) && (B == static_cast<unsigned int>(TimeoutResult)))
  225. {
  226. MeasureCaption = FMTLOAD(TIMEOUT_BUTTON, (MeasureCaption, 99));
  227. *TimeoutButton = Button;
  228. }
  229. DrawText(Result->Canvas->Handle,
  230. UnicodeString(MeasureCaption).c_str(), -1,
  231. &TextRect, DT_CALCRECT | DT_LEFT | DT_SINGLELINE |
  232. Result->DrawTextBiDiModeFlagsReadingOnly());
  233. int CurButtonWidth = TextRect.Right - TextRect.Left + 8;
  234. if (CurButtonWidth > ButtonWidth)
  235. {
  236. ButtonWidth = CurButtonWidth;
  237. }
  238. Button->Name = ButtonNames[TMsgDlgBtn(B)];
  239. Button->Parent = Result;
  240. Button->Caption = Caption;
  241. if (OnClick != NULL)
  242. {
  243. Button->OnClick = OnClick;
  244. }
  245. else
  246. {
  247. Button->ModalResult = ModalResults[B];
  248. Button->Default = (B == static_cast<unsigned int>(DefaultButton));
  249. Button->Cancel = (B == static_cast<unsigned int>(CancelButton));
  250. }
  251. if (MoreMessages != NULL)
  252. {
  253. Button->Anchors = TAnchors() << akBottom << akLeft;
  254. }
  255. if (B == mbHelp)
  256. {
  257. Button->OnClick = Result->HelpButtonClick;
  258. }
  259. ButtonControls[ButtonControlsCount] = Button;
  260. ButtonControlsCount++;
  261. }
  262. }
  263. int ButtonHeight = MulDiv(mcButtonHeight, DialogUnits.y, 8);
  264. int ButtonSpacing = MulDiv(mcButtonSpacing, DialogUnits.x, 4);
  265. SetRect(&TextRect, 0, 0, Screen->Width / 2, 0);
  266. DrawText(Result->Canvas->Handle, Msg.c_str(), Msg.Length() + 1, &TextRect,
  267. DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK |
  268. Result->DrawTextBiDiModeFlagsReadingOnly());
  269. int MaxWidth = Screen->Width - HorzMargin * 2 - 32 - HorzSpacing - 30;
  270. if (TextRect.right > MaxWidth)
  271. {
  272. // this will truncate the text, we should implement something smarter eventually
  273. TextRect.right = MaxWidth;
  274. }
  275. const wchar_t * IconID = IconIDs[DlgType];
  276. int IconTextWidth = TextRect.Right;
  277. int IconTextHeight = TextRect.Bottom;
  278. if (IconID != NULL)
  279. {
  280. IconTextWidth += 32 + HorzSpacing;
  281. if (IconTextHeight < 32)
  282. {
  283. IconTextHeight = 32;
  284. }
  285. }
  286. if (MoreMessages != NULL)
  287. {
  288. TMemo * MessageMemo = new TMemo(Result);
  289. MessageMemo->Parent = Result;
  290. MessageMemo->ReadOnly = true;
  291. MessageMemo->WantReturns = False;
  292. MessageMemo->ScrollBars = ssVertical;
  293. MessageMemo->Anchors = TAnchors() << akLeft << akRight << akTop; //akBottom;
  294. MessageMemo->Color = clBtnFace;
  295. MessageMemo->Lines->Text = MoreMessages->Text;
  296. Result->MessageMemo = MessageMemo;
  297. }
  298. int ButtonGroupWidth = 0;
  299. if (ButtonControlsCount > 0)
  300. {
  301. ButtonGroupWidth = ButtonWidth * ButtonControlsCount +
  302. ButtonSpacing * (ButtonControlsCount - 1);
  303. }
  304. int MoreMessageWidth = (MoreMessages != NULL ?
  305. MulDiv(mcMoreMessageWidth, DialogUnits.x, 4) : 0);
  306. int MoreMessageHeight = (MoreMessages != NULL ?
  307. MulDiv(mcMoreMessageHeight, DialogUnits.y, 12) : 0);
  308. int AClientWidth =
  309. (IconTextWidth > ButtonGroupWidth ? IconTextWidth : ButtonGroupWidth) +
  310. HorzMargin * 2;
  311. Result->ClientWidth = (AClientWidth > MoreMessageWidth ?
  312. AClientWidth : MoreMessageWidth);
  313. Result->ClientHeight = IconTextHeight + ButtonHeight + VertSpacing +
  314. VertMargin * 2 + MoreMessageHeight;
  315. Result->Left = (Screen->Width / 2) - (Result->Width / 2);
  316. Result->Top = (Screen->Height / 2) - (Result->Height / 2);
  317. if (DlgType != mtCustom)
  318. {
  319. Result->Caption = LoadResourceString(Captions[DlgType]);
  320. }
  321. else
  322. {
  323. Result->Caption = Application->Title;
  324. }
  325. if (IconID != NULL)
  326. {
  327. TImage * Image = new TImage(Result);
  328. Image->Name = L"Image";
  329. Image->Parent = Result;
  330. Image->Picture->Icon->Handle = LoadIcon(0, IconID);
  331. Image->SetBounds(HorzMargin, VertMargin, 32, 32);
  332. }
  333. TLabel * Message = new TLabel(Result);
  334. Result->Message = Message;
  335. Message->Name = L"Message";
  336. Message->Parent = Result;
  337. Message->WordWrap = true;
  338. Message->Caption = Msg;
  339. Message->BoundsRect = TextRect;
  340. Message->BiDiMode = Result->BiDiMode;
  341. // added to show & as & for messages containing !& pattern of custom commands
  342. // (suppose that we actually never want to use & as accel in message text)
  343. Message->ShowAccelChar = false;
  344. int ALeft = IconTextWidth - TextRect.Right + HorzMargin;
  345. Message->SetBounds(ALeft, VertMargin, TextRect.Right, TextRect.Bottom);
  346. int ButtonTop = IconTextHeight + VertMargin + VertSpacing + MoreMessageHeight;
  347. if (Result->MessageMemo != NULL)
  348. {
  349. Result->MessageMemo->BoundsRect = TRect(Message->Left,
  350. Message->Top + Message->Height + VertSpacing,
  351. Result->ClientWidth - HorzMargin,
  352. Message->Top + Message->Height + VertSpacing + MoreMessageHeight);
  353. // rather hack, whole control positioning is wrong
  354. if (Result->MessageMemo->Top + Result->MessageMemo->Height > ButtonTop - VertSpacing)
  355. {
  356. Result->MessageMemo->Height =
  357. (ButtonTop - VertSpacing) - Result->MessageMemo->Top;
  358. }
  359. }
  360. int X = (Result->ClientWidth - ButtonGroupWidth) / 2;
  361. for (int i = 0; i < ButtonControlsCount; i++)
  362. {
  363. ButtonControls[i]->SetBounds(X,
  364. ButtonTop, ButtonWidth, ButtonHeight);
  365. X += ButtonWidth + ButtonSpacing;
  366. }
  367. return Result;
  368. }
  369. //---------------------------------------------------------------------------
  370. TForm * __fastcall CreateMoreMessageDialog(const UnicodeString & Msg,
  371. TStrings * MoreMessages, TMsgDlgType DlgType, TMsgDlgButtons Buttons,
  372. TQueryButtonAlias * Aliases, unsigned int AliasesCount,
  373. TMsgDlgBtn TimeoutResult, TButton ** TimeoutButton)
  374. {
  375. return TMessageForm::Create(Msg, MoreMessages, DlgType, Buttons,
  376. Aliases, AliasesCount, TimeoutResult, TimeoutButton);
  377. }