| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264 | //---------------------------------------------------------------------------#include <vcl.h>#pragma hdrstop#include <Consts.hpp>#include <GUITools.h>#include <Common.h>#include <VCLCommon.h>#include <CoreMain.h>#include <WinInterface.h>#include <Tools.h>#include <TextsWin.h>#include <TextsCore.h>#include <Vcl.Imaging.pngimage.hpp>#include <StrUtils.hpp>#include <PasTools.hpp>#include <Math.hpp>#include <WebBrowserEx.hpp>#include <Setup.h>#include <WinApi.h>#include "MessageDlg.h"//---------------------------------------------------------------------------#ifndef NO_RESOURCES#pragma resource "*.dfm"#endif//---------------------------------------------------------------------------const UnicodeString MessagePanelName(L"Panel");const UnicodeString MainMessageLabelName(L"MainMessage");const UnicodeString MessageLabelName(L"Message");const UnicodeString YesButtonName(L"Yes");const UnicodeString OKButtonName(L"OK");//---------------------------------------------------------------------------class TMessageButton : public TButton{public:  __fastcall TMessageButton(TComponent * Owner);protected:  virtual void __fastcall Dispatch(void * Message);private:  void __fastcall WMGetDlgCode(TWMGetDlgCode & Message);};//---------------------------------------------------------------------------__fastcall TMessageButton::TMessageButton(TComponent * Owner) :  TButton(Owner){}//---------------------------------------------------------------------------void __fastcall TMessageButton::Dispatch(void * Message){  TMessage * M = reinterpret_cast<TMessage*>(Message);  if (M->Msg == WM_GETDLGCODE)  {    WMGetDlgCode(*((TWMGetDlgCode *)Message));  }  else  {    TButton::Dispatch(Message);  }}//---------------------------------------------------------------------------void __fastcall TMessageButton::WMGetDlgCode(TWMGetDlgCode & Message){  TButton::Dispatch(&Message);  // WORKAROUND  // Windows default handler returns DLGC_WANTARROWS for split buttons,  // what prevent left/right keys from being used for focusing next/previous buttons/controls.  // Overrwide that. Though note that we need to pass the up/down keys back to button  // to allow drop down, see TMessageForm::CMDialogKey  Message.Result = Message.Result & ~DLGC_WANTARROWS;}//---------------------------------------------------------------------------__fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner){  FShowNoActivate = false;  MessageMemo = NULL;  MessageBrowserPanel = NULL;  MessageBrowser = NULL;  FUpdateForShiftStateTimer = NULL;  UseSystemSettingsPre(this);  FDummyForm = new TForm(this);  UseSystemSettings(FDummyForm);}//---------------------------------------------------------------------------__fastcall TMessageForm::~TMessageForm(){  SAFE_DESTROY(FDummyForm);  SAFE_DESTROY(FUpdateForShiftStateTimer);}//---------------------------------------------------------------------------void __fastcall TMessageForm::HelpButtonSubmit(TObject * /*Sender*/, unsigned int & /*Answer*/){  if (HelpKeyword != HELP_NONE)  {    FormHelp(this);  }  else  {    MessageWithNoHelp(GetReportText());  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::ReportButtonSubmit(TObject * /*Sender*/, unsigned int & /*Answer*/){  // Report text goes last, as it may exceed URL parameters limit (2048) and get truncated.  // And we need to preserve the other parameters.  UnicodeString Url =    FMTLOAD(ERROR_REPORT_URL2,      (Configuration->ProductVersion, GUIConfiguration->AppliedLocaleHex,       EncodeUrlString(GetReportText())));  OpenBrowser(Url);}//---------------------------------------------------------------------------void __fastcall TMessageForm::UpdateForShiftState(){  TShiftState ShiftState =    KeyboardStateToShiftState() * AllKeyShiftStates();  if (FShiftState != ShiftState)  {    FShiftState = ShiftState;    for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)    {      TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);      if ((Button != NULL) && (Button->DropDownMenu != NULL))      {        TMenuItem * MenuItems = Button->DropDownMenu->Items;        for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)        {          TMenuItem * Item = MenuItems->Items[ItemIndex];          TShiftState GrouppedShiftState(Item->Tag >> 16);          if (Item->Enabled &&              ((ShiftState.Empty() && Item->Default) ||               (!ShiftState.Empty() && (ShiftState == GrouppedShiftState))))          {            Button->Caption = CopyToChar(Item->Caption, L'\t', false);            Button->ModalResult = Item->Tag & 0xFFFF;            DebugAssert(Button->OnClick == NULL);            DebugAssert(Item->OnClick == MenuItemClick);            break;          }        }      }    }  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::KeyUp(Word & Key, TShiftState Shift){  UpdateForShiftState();  TForm::KeyUp(Key, Shift);}//---------------------------------------------------------------------------void __fastcall TMessageForm::KeyDown(Word & Key, TShiftState Shift){  if (Shift.Contains(ssCtrl) && (Key == L'C'))  {    TInstantOperationVisualizer Visualizer;    CopyToClipboard(GetFormText());  }  else  {    if (!Shift.Contains(ssCtrl))    {      for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)      {        TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);        if ((Button != NULL) && (Button->DropDownMenu != NULL))        {          TMenuItem * MenuItems = Button->DropDownMenu->Items;          for (int ItemIndex = 0; ItemIndex < MenuItems->Count; ItemIndex++)          {            TMenuItem * Item = MenuItems->Items[ItemIndex];            if (IsAccel(Key, MenuItems->Items[ItemIndex]->Caption))            {              Item->OnClick(Item);              Key = 0;              break;            }          }        }        if (Key == 0)        {          break;        }      }    }    UpdateForShiftState();    TForm::KeyDown(Key, Shift);  }}//---------------------------------------------------------------------------UnicodeString __fastcall TMessageForm::NormalizeNewLines(UnicodeString Text){  Text = ReplaceStr(Text, L"\r", L"");  Text = ReplaceStr(Text, L"\n", L"\r\n");  return Text;}//---------------------------------------------------------------------------UnicodeString __fastcall TMessageForm::GetFormText(){  UnicodeString DividerLine, ButtonCaptions;  DividerLine = UnicodeString::StringOfChar(L'-', 27) + sLineBreak;  for (int i = 0; i < ComponentCount - 1; i++)  {    if (dynamic_cast<TButton*>(Components[i]) != NULL)    {      ButtonCaptions += dynamic_cast<TButton*>(Components[i])->Caption +        UnicodeString::StringOfChar(L' ', 3);    }  }  ButtonCaptions = ReplaceStr(ButtonCaptions, L"&", L"");  UnicodeString MoreMessages;  if (MessageMemo != NULL)  {    MoreMessages = MessageMemo->Text + DividerLine;  }  else if (MessageBrowser != NULL)  {    MessageBrowser->SelectAll();    MessageBrowser->CopyToClipBoard();    if (NonEmptyTextFromClipboard(MoreMessages))    {      if (!EndsStr(sLineBreak, MoreMessages))      {        MoreMessages += sLineBreak;      }      MoreMessages += DividerLine;    }    MessageBrowser->DoCommand(L"UNSELECT");  }  UnicodeString MessageCaption = NormalizeNewLines(MessageText);  UnicodeString Result = FORMAT(L"%s%s%s%s%s%s%s%s%s%s%s", (DividerLine, Caption, sLineBreak,    DividerLine, MessageCaption, sLineBreak, DividerLine, MoreMessages,    ButtonCaptions, sLineBreak, DividerLine));  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall TMessageForm::GetReportText(){  UnicodeString Text = MessageText;  Text = Text.TrimRight();  if (MessageMemo != NULL)  {    Text += L"\n\n" + MessageMemo->Text;  }  // Currently we use browser for updates box only and it has help context,  // and does not have Report button, so we cannot get here.  DebugAssert(MessageBrowser == NULL);  Text = NormalizeNewLines(Text);  UnicodeString ReportErrorText = NormalizeNewLines(FMTLOAD(REPORT_ERROR, (L"")));  Text = ReplaceStr(Text, ReportErrorText, L"");  Text = Trim(Text);  return Text;}//---------------------------------------------------------------------------void __fastcall TMessageForm::CMDialogKey(TWMKeyDown & Message){  // this gets used in WinInterface.cpp SetTimeoutEvents  if (OnKeyDown != NULL)  {    OnKeyDown(this, Message.CharCode, KeyDataToShiftState(Message.KeyData));  }  if (Message.CharCode == VK_MENU)  {    bool AnyButtonWithGrouppedCommandsWithShiftState = false;    for (int ComponentIndex = 0; ComponentIndex < ComponentCount - 1; ComponentIndex++)    {      TButton * Button = dynamic_cast<TButton*>(Components[ComponentIndex]);      if ((Button != NULL) && (Button->DropDownMenu != NULL))      {        // we should check if there are any commands with shift state,        // but it's bit overkill        AnyButtonWithGrouppedCommandsWithShiftState = true;        break;      }    }    // this is to make Alt only alter button meaning (if there is any    // alternable button) and not popup system menu    if (AnyButtonWithGrouppedCommandsWithShiftState)    {      Message.Result = 1;      UpdateForShiftState();    }    else    {      TForm::Dispatch(&Message);    }  }  else if ((Message.CharCode == VK_UP) || (Message.CharCode == VK_DOWN))  {    // WORKAROUND    // noop to make up/down be passed back to button to allow drop down,    // see TMessageButton::WMGetDlgCode  }  else  {    TForm::Dispatch(&Message);  }}//---------------------------------------------------------------------------int __fastcall TMessageForm::ShowModal(){  if (IsApplicationMinimized())  {    FShowNoActivate = true;  }  int Result = TForm::ShowModal();  UnhookFormActivation(this);  return Result;}//---------------------------------------------------------------------------void __fastcall TMessageForm::SetZOrder(bool TopMost){  // WORKAROUND: If application is minimized,  // swallow call to BringToFront() from TForm::ShowModal()  if (FShowNoActivate && TopMost)  {    // noop  }  else  {    TForm::SetZOrder(TopMost);  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::CMShowingChanged(TMessage & Message){  if (Showing && FShowNoActivate)  {    ShowFormNoActivate(this);  }  else  {    TForm::Dispatch(&Message);  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::Dispatch(void * Message){  TMessage * M = reinterpret_cast<TMessage*>(Message);  if (M->Msg == CM_DIALOGKEY)  {    CMDialogKey(*((TWMKeyDown *)Message));  }  else if (M->Msg == CM_SHOWINGCHANGED)  {    CMShowingChanged(*M);  }  else if (M->Msg == CM_DPICHANGED)  {    if (MessageBrowser != NULL)    {      LoadMessageBrowser();    }    TForm::Dispatch(Message);  }  else  {    TForm::Dispatch(Message);  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::CreateParams(TCreateParams & Params){  TForm::CreateParams(Params);  if ((Screen != NULL) && (Screen->ActiveForm != NULL) &&      Screen->ActiveForm->HandleAllocated())  {    Params.WndParent = Screen->ActiveForm->Handle;  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::LoadMessageBrowser(){  NavigateToUrl(MessageBrowserUrl);}//---------------------------------------------------------------------------void __fastcall TMessageForm::DoShow(){  UseSystemSettingsPost(this);  if (!MessageBrowserUrl.IsEmpty() &&      // Guard against repeated calls to DoOpen()      (MessageBrowser == NULL))  {    // Web Browser component does not seem to work,    // when created before any window is shown.    // I.e. when the message dialog is the first window (like when /update is used).    // So we have to delay its creation until at least the dialog box is shown.    MessageBrowser = CreateBrowserViewer(MessageBrowserPanel, LoadStr(MESSAGE_LOADING));    MessageBrowser->SendToBack();    LoadMessageBrowser();  }  // Need OnShow to be called only after MessageBrowser is created,  // so that the implementation (InsertDonateLink) can call InsertPanelToMessageDialog  TForm::DoShow();}//---------------------------------------------------------------------------void __fastcall TMessageForm::ButtonSubmit(TObject * Sender){  unsigned int Answer = 0;  FButtonSubmitEvents[Sender](Sender, Answer);  if (Answer != 0)  {    ModalResult = Answer;  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::MenuItemClick(TObject * Sender){  TMenuItem * Item = DebugNotNull(dynamic_cast<TMenuItem *>(Sender));  ModalResult = (Item->Tag & 0xFFFF);}//---------------------------------------------------------------------------void __fastcall TMessageForm::UpdateForShiftStateTimer(TObject * /*Sender*/){  // this is needed to reflect shift state, even when we do not have a keyboard  // focus, what happens when drop down menu is popped up  UpdateForShiftState();}//---------------------------------------------------------------------------void __fastcall TMessageForm::ButtonDropDownClick(TObject * /*Sender*/){  // as optimization, do not waste time running timer, unless  // user pops up drop down menu. we do not have a way to stop timer, once  // it closes, but functionaly it does not matter  if (FUpdateForShiftStateTimer == NULL)  {    FUpdateForShiftStateTimer = new TTimer(this);    FUpdateForShiftStateTimer->Interval = 50;    FUpdateForShiftStateTimer->OnTimer = UpdateForShiftStateTimer;  }}//---------------------------------------------------------------------------const ResourceString * Captions[] = { &_SMsgDlgWarning, &_SMsgDlgError, &_SMsgDlgInformation,  &_SMsgDlgConfirm, NULL };const wchar_t * ImageNames[] = { L"Warning", L"Error", L"Information",  L"Help Blue", NULL };const int mcHorzMargin = 10;const int mcVertMargin = 13;const int mcHorzSpacing = 12;const int mcButtonVertMargin = 7;const int mcButtonSpacing = 5; // includes mcVertMarginconst int mcMoreMessageHeight = 86;// approximately what Windows Vista task dialogs use,// actually they probably has fixed widthconst int mcMaxDialogWidth = 340;const int mcMinDialogWidth = 310;const int mcMinDialogwithMoreMessagesWidth = 400;//---------------------------------------------------------------------------static UnicodeString __fastcall GetKeyNameStr(int Key){  wchar_t Buf[MAX_PATH];  LONG VirtualKey = MapVirtualKey(Key, MAPVK_VK_TO_VSC);  VirtualKey <<= 16;  if (GetKeyNameText(VirtualKey, Buf, LENOF(Buf)) > 0)  {    NULL_TERMINATE(Buf);  }  else  {    Buf[0] = L'\0';  }  return Buf;}//---------------------------------------------------------------------------TButton * __fastcall TMessageForm::CreateButton(  UnicodeString Name, UnicodeString Caption, unsigned int Answer,  TButtonSubmitEvent OnSubmit, bool IsTimeoutButton,  int GroupWith, TShiftState GrouppedShiftState, bool ElevationRequired, bool MenuButton,  TAnswerButtons & AnswerButtons, bool HasMoreMessages, int & ButtonWidths){  UnicodeString MeasureCaption = Caption;  if (IsTimeoutButton)  {    MeasureCaption = FMTLOAD(TIMEOUT_BUTTON, (MeasureCaption, 99));  }  TRect TextRect;  DrawText(Canvas->Handle,    UnicodeString(MeasureCaption).c_str(), -1,    &TextRect, DT_CALCRECT | DT_LEFT | DT_SINGLELINE |    DrawTextBiDiModeFlagsReadingOnly());  int CurButtonWidth = TextRect.Right - TextRect.Left + ScaleByTextHeightRunTime(this, 16);  if (ElevationRequired && IsVista())  {    // Elevation icon    CurButtonWidth += ScaleByTextHeightRunTime(this, 16);  }  if (MenuButton)  {    CurButtonWidth += ScaleByTextHeightRunTime(this, 16);  }  TButton * Button = NULL;  if (SupportsSplitButton() &&      (GroupWith >= 0) &&      DebugAlwaysTrue(AnswerButtons.find(GroupWith) != AnswerButtons.end()))  {    TButton * GroupWithButton = AnswerButtons[GroupWith];    if (GroupWithButton->DropDownMenu == NULL)    {      GroupWithButton->Style = TCustomButton::bsSplitButton;      GroupWithButton->DropDownMenu = new TPopupMenu(this);      // cannot handle subitems with shift state,      // if the button has its own handler      // (though it may not be the case still here)      DebugAssert(GroupWithButton->OnClick == NULL);      TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);      GroupWithButton->DropDownMenu->Items->Add(Item);      GroupWithButton->OnDropDownClick = ButtonDropDownClick;      Item->Caption = GroupWithButton->Caption;      Item->OnClick = MenuItemClick;      DebugAssert(GroupWithButton->ModalResult <= 0xFFFF);      Item->Tag = GroupWithButton->ModalResult;      Item->Default = true;    }    TMenuItem * Item = new TMenuItem(GroupWithButton->DropDownMenu);    GroupWithButton->DropDownMenu->Items->Add(Item);    // See ShortCutToText in Vcl.Menus.pas    if (GrouppedShiftState == (TShiftState() << ssAlt))    {      Caption = Caption + L"\t" + GetKeyNameStr(VK_MENU);    }    else if (GrouppedShiftState == (TShiftState() << ssCtrl))    {      Caption = Caption + L"\t" + GetKeyNameStr(VK_CONTROL);    }    else if (GrouppedShiftState == (TShiftState() << ssShift))    {      Caption = Caption + L"\t" + GetKeyNameStr(VK_SHIFT);    }    else    {      // do not support combined shift states yet      DebugAssert(GrouppedShiftState == TShiftState());    }    Item->Caption = Caption;    if (OnSubmit != NULL)    {      Item->OnClick = ButtonSubmit;      FButtonSubmitEvents[Item] = OnSubmit;    }    else    {      Item->OnClick = MenuItemClick;      DebugAssert((Answer <= 0xFFFF) && (GrouppedShiftState.ToInt() <= 0xFFFF));      Item->Tag = Answer + (GrouppedShiftState.ToInt() << 16);    }    // Hard-coded drop down button width (do not know how to ask for system width).    // Also we do not update the max button width for the default groupped    // button caption. We just blindly hope that captions of advanced commands    // are always longer than the caption of simple default command    CurButtonWidth += ScaleByTextHeightRunTime(this, 15);    // never shrink buttons below their default width    if (GroupWithButton->Width < CurButtonWidth)    {      ButtonWidths += CurButtonWidth - GroupWithButton->Width;      GroupWithButton->Width = CurButtonWidth;    }    DebugAssert(!ElevationRequired);  }  else  {    Button = new TMessageButton(this);    Button->Name = Name;    Button->Parent = this;    Button->Caption = Caption;    // Scale buttons using regular font, so that they are as large as buttons    // on other dialogs (note that they are still higher than Windows Task dialog    // buttons)    Button->Height = ScaleByTextHeightRunTime(FDummyForm, Button->Height);    Button->Width = ScaleByTextHeightRunTime(FDummyForm, Button->Width);    if (OnSubmit != NULL)    {      Button->OnClick = ButtonSubmit;      FButtonSubmitEvents[Button] = OnSubmit;    }    else    {      Button->ModalResult = Answer;    }    if (HasMoreMessages)    {      Button->Anchors = TAnchors() << akBottom << akLeft;    }    // never shrink buttons below their default width    if (Button->Width < CurButtonWidth)    {      Button->Width = CurButtonWidth;    }    Button->ElevationRequired = ElevationRequired;    ButtonWidths += Button->Width;    if (MenuButton)    {      ::MenuButton(Button);    }  }  return Button;}//---------------------------------------------------------------------------void __fastcall TMessageForm::InsertPanel(TPanel * Panel){  if (DebugAlwaysTrue(MessageBrowser != NULL))  {    // we currently use this for updates message box only    TControl * ContentsControl = static_cast<TControl *>(DebugNotNull(MessageBrowser))->Parent;    Panel->Width = ContentsControl->Width;    Panel->Left = ContentsControl->Left;    int ContentsBottom = ContentsControl->Top + ContentsControl->Height;    Panel->Top = ContentsBottom + ((ContentsPanel->Height - ContentsBottom) / 2);    Height = Height + Panel->Height;    ContentsPanel->Height = ContentsPanel->Height + Panel->Height;    Panel->Parent = ContentsPanel;    // The panel itself does not need this, as the ParentBackground (true by default)    // has the same effect, but an eventual TStaticText on the panel    // uses a wrong background color, if panel's ParentColor is not set.    Panel->ParentColor = true;  }}//---------------------------------------------------------------------------int __fastcall TMessageForm::GetContentWidth(){  int Result = 0;  if (DebugAlwaysTrue(MessageBrowser != NULL))  {    // we currently use this for updates message box only    TControl * ContentsControl = static_cast<TControl *>(DebugNotNull(MessageBrowser))->Parent;    Result = ContentsControl->Width;  }  return Result;}//---------------------------------------------------------------------------void __fastcall TMessageForm::NavigateToUrl(const UnicodeString & Url){  if (DebugAlwaysTrue(MessageBrowser != NULL))  {    UnicodeString FontSizeParam = FORMAT(L"fontsize=%d", (Font->Size));    UnicodeString FullUrl = AppendUrlParams(Url, FontSizeParam);    NavigateBrowserToUrl(MessageBrowser, FullUrl);  }}//---------------------------------------------------------------------------void __fastcall TMessageForm::ReadState(TReader * Reader){  TForm::ReadState(Reader);  // Before we change form font  RecordFormImplicitRescale(this);}//---------------------------------------------------------------------------void __fastcall AnswerNameAndCaption(  unsigned int Answer, UnicodeString & Name, UnicodeString & Caption){  switch (Answer)  {    case qaYes:      Caption = LoadStr(_SMsgDlgYes.Identifier);      Name = YesButtonName;      break;    case qaNo:      Caption = LoadStr(_SMsgDlgNo.Identifier);      Name = L"No";      break;    case qaOK:      Caption = LoadStr(_SMsgDlgOK.Identifier);      Name = OKButtonName;      break;    case qaCancel:      Caption = LoadStr(_SMsgDlgCancel.Identifier);      Name = L"Cancel";      break;    case qaAbort:      Caption = LoadStr(_SMsgDlgAbort.Identifier);      Name = L"Abort";      break;    case qaRetry:      Caption = LoadStr(_SMsgDlgRetry.Identifier);      Name = L"Retry";      break;    case qaIgnore:      Caption = LoadStr(_SMsgDlgIgnore.Identifier);      Name = L"Ignore";      break;    // Own variant to avoid accelerator conflict with "Abort" button.    // Note that as of now, ALL_BUTTON is never actually used,    // because qaAll is always aliased    case qaAll:      Caption = LoadStr(ALL_BUTTON);      Name = L"All";      break;    case qaNoToAll:      Caption = LoadStr(_SMsgDlgNoToAll.Identifier);      Name = L"NoToAll";      break;    // Own variant to avoid accelerator conflict with "Abort" button.    case qaYesToAll:      Caption = LoadStr(YES_TO_ALL_BUTTON);      Name = L"YesToAll";      break;    case qaHelp:      Caption = LoadStr(_SMsgDlgHelp.Identifier);      Name = L"Help";      break;    case qaSkip:      Caption = LoadStr(SKIP_BUTTON);      Name = L"Skip";      break;    case qaReport:      Caption = LoadStr(REPORT_BUTTON);      Name = L"Report";      break;    default:      DebugFail();      throw Exception(L"Undefined answer");  }}//---------------------------------------------------------------------------static int __fastcall CalculateWidthOnCanvas(UnicodeString Text, void * Arg){  TCanvas * Canvas = static_cast<TCanvas *>(Arg);  return Canvas->TextWidth(Text);}//---------------------------------------------------------------------------TForm * __fastcall TMessageForm::Create(const UnicodeString & Msg,  TStrings * MoreMessages, TMsgDlgType DlgType, unsigned int Answers,  const TQueryButtonAlias * Aliases, unsigned int AliasesCount,  unsigned int TimeoutAnswer, TButton ** TimeoutButton, const UnicodeString & AImageName,  const UnicodeString & NeverAskAgainCaption, const UnicodeString & MoreMessagesUrl,  TSize MoreMessagesSize, const UnicodeString & CustomCaption){  unsigned int DefaultAnswer;  if (FLAGSET(Answers, qaOK))  {    DefaultAnswer = qaOK;  }  else if (FLAGSET(Answers, qaYes))  {    DefaultAnswer = qaYes;  }  else  {    DefaultAnswer = qaRetry;  }  unsigned int CancelAnswer = ::CancelAnswer(Answers);  if (TimeoutButton != NULL)  {    *TimeoutButton = NULL;  }  TColor MainInstructionColor;  HFONT MainInstructionFont;  HFONT InstructionFont;  GetInstrutionsTheme(MainInstructionColor, MainInstructionFont, InstructionFont);  TMessageForm * Result = SafeFormCreate<TMessageForm>();  if (InstructionFont != 0)  {    Result->Font->Handle = InstructionFont;  }  else  {    Result->Font->Assign(Screen->MessageFont);  }  // Can be possibly nul when error occurs before configuration is created  if (Configuration != NULL)  {    Configuration->Usage->Set(L"ThemeMessageFontSize", Result->Font->Size);  }  // make sure we consider sizes of the monitor,  // that is set in DoFormWindowProc(CM_SHOWINGCHANGED) later.  Forms::TMonitor * Monitor = FormMonitor(Result);  bool HasMoreMessages = (MoreMessages != NULL) || !MoreMessagesUrl.IsEmpty();  Result->BiDiMode = Application->BiDiMode;  Result->BorderStyle = bsDialog;  Result->Canvas->Font = Result->Font;  Result->KeyPreview = true;  int HorzMargin = ScaleByTextHeightRunTime(Result, mcHorzMargin);  int VertMargin = ScaleByTextHeightRunTime(Result, mcVertMargin);  int HorzSpacing = ScaleByTextHeightRunTime(Result, mcHorzSpacing);  int ButtonVertMargin = ScaleByTextHeightRunTime(Result, mcButtonVertMargin);  int ButtonWidths = 0;  int ButtonHeight = -1;  std::vector<TButton *> ButtonControls;  TStaticText * LinkControl = NULL;  TAnswerButtons AnswerButtons;  for (unsigned int Answer = qaFirst; Answer <= qaLast; Answer = Answer << 1)  {    if (FLAGSET(Answers, Answer))    {      DebugAssert(Answer != mrCancel);      UnicodeString Caption;      UnicodeString Name;      AnswerNameAndCaption(Answer, Name, Caption);      TButtonSubmitEvent OnSubmit = NULL;      int GroupWith = -1;      TShiftState GrouppedShiftState;      bool ElevationRequired = false;      bool MenuButton = false;      UnicodeString ActionAlias;      if (Aliases != NULL)      {        for (unsigned int i = 0; i < AliasesCount; i++)        {          if (Answer == Aliases[i].Button)          {            if (!Aliases[i].Alias.IsEmpty())            {              Caption = Aliases[i].Alias;            }            OnSubmit = Aliases[i].OnSubmit;            GroupWith = Aliases[i].GroupWith;            GrouppedShiftState = Aliases[i].GrouppedShiftState;            ElevationRequired = Aliases[i].ElevationRequired;            MenuButton = Aliases[i].MenuButton;            ActionAlias = Aliases[i].ActionAlias;            DebugAssert((OnSubmit == NULL) || (GrouppedShiftState == TShiftState()));            break;          }        }      }      // implemented for a one link only for now      if (!ActionAlias.IsEmpty() &&          DebugAlwaysTrue(LinkControl == NULL) &&          DebugAlwaysTrue(OnSubmit != NULL) &&          DebugAlwaysTrue(GroupWith < 0))      {        LinkControl = new TStaticText(Result);        LinkControl->Name = Name;        LinkControl->Caption = ActionAlias;        LinkControl->Alignment = taRightJustify;        LinkControl->Anchors = TAnchors() << akRight << akTop;        LinkActionLabel(LinkControl);        LinkControl->OnClick = Result->ButtonSubmit;        Result->FButtonSubmitEvents[LinkControl] = OnSubmit;      }      else      {        // we hope that all grouped-with buttons are for answer with greater        // value that the answer to be grouped with        if (GroupWith >= 0)        {          if (DebugAlwaysFalse(GroupWith >= static_cast<int>(Answer)) ||              DebugAlwaysFalse(Answer == TimeoutAnswer) &&              DebugAlwaysFalse(Answer == DefaultAnswer) &&              DebugAlwaysFalse(Answer == CancelAnswer))          {            GroupWith = -1;          }        }        bool IsTimeoutButton = (TimeoutButton != NULL) && (Answer == TimeoutAnswer);        if (Answer == qaHelp)        {          DebugAssert(OnSubmit == NULL);          OnSubmit = Result->HelpButtonSubmit;        }        if (Answer == qaReport)        {          DebugAssert(OnSubmit == NULL);          OnSubmit = Result->ReportButtonSubmit;        }        TButton * Button = Result->CreateButton(          Name, Caption, Answer,          OnSubmit, IsTimeoutButton, GroupWith, GrouppedShiftState, ElevationRequired, MenuButton,          AnswerButtons, HasMoreMessages, ButtonWidths);        if (Button != NULL)        {          ButtonControls.push_back(Button);          Button->Default = (Answer == DefaultAnswer);          Button->Cancel = (Answer == CancelAnswer);          if (ButtonHeight < 0)          {            ButtonHeight = Button->Height;          }          DebugAssert(ButtonHeight == Button->Height);          AnswerButtons.insert(TAnswerButtons::value_type(Answer, Button));          if (IsTimeoutButton)          {            *TimeoutButton = Button;          }        }      }    }  }  int NeverAskAgainWidth = 0;  if (!NeverAskAgainCaption.IsEmpty())  {    NeverAskAgainWidth =      ScaleByTextHeightRunTime(Result, 16) + // checkbox      Result->Canvas->TextWidth(NeverAskAgainCaption) +      ScaleByTextHeightRunTime(Result, 16); // margin  }  int ButtonSpacing = ScaleByTextHeightRunTime(Result, mcButtonSpacing);  int ButtonGroupWidth = NeverAskAgainWidth;  if (!ButtonControls.empty())  {    ButtonGroupWidth += ButtonWidths +      ButtonSpacing * (ButtonControls.size() - 1);  }  DebugAssert((ButtonHeight > 0) && (ButtonWidths > 0));  TPanel * Panel = CreateBlankPanel(Result);  Result->ContentsPanel = Panel;  Panel->Name = MessagePanelName;  Panel->Parent = Result;  Panel->Color = clWindow;  Panel->ParentBackground = false;  Panel->Anchors = TAnchors() << akLeft << akRight << akTop;  Panel->Caption = L"";  int IconWidth = 0;  int IconHeight = 0;  UnicodeString ImageName = AImageName;  if (ImageName.IsEmpty() &&      DebugAlwaysTrue(ImageNames[DlgType] != NULL))  {    ImageName = ImageNames[DlgType];  }  if (DebugAlwaysTrue(!ImageName.IsEmpty()))  {    TImage * Image = new TImage(Result);    Image->Name = L"Image";    Image->Parent = Panel;    LoadDialogImage(Image, ImageName);    Image->SetBounds(HorzMargin, VertMargin, Image->Picture->Width, Image->Picture->Height);    IconWidth = Image->Width + HorzSpacing;    IconHeight = Image->Height;  }  int MaxTextWidth = ScaleByTextHeightRunTime(Result, mcMaxDialogWidth);  // if the dialog would be wide anyway (overwrite confirmation on Windows XP),  // to fit the buttons, do not restrict the text  if (MaxTextWidth < ButtonGroupWidth - IconWidth)  {    MaxTextWidth = ButtonGroupWidth - IconWidth;  }  UnicodeString BodyMsg = Msg;  BodyMsg = RemoveInteractiveMsgTag(BodyMsg);  UnicodeString MainMsg;  if (ExtractMainInstructions(BodyMsg, MainMsg))  {    Result->MessageText = MainMsg + BodyMsg;    BodyMsg = BodyMsg.TrimLeft();  }  else  {    Result->MessageText = BodyMsg;  }  ApplyTabs(Result->MessageText, L' ', NULL, NULL);  // Windows XP (not sure about Vista) does not support Hair space.  // For Windows XP, we still keep the existing hack by using hard-coded spaces  // in resource string  if (CheckWin32Version(6, 1))  {    // Have to be padding with spaces (the smallest space defined, hair space = 1px),    // as tabs actually do not tab, just expand to 8 spaces.    // Otherwise we would have to do custom drawing    // (using GetTabbedTextExtent and TabbedTextOut)    const wchar_t HairSpace = L'\x200A';    ApplyTabs(BodyMsg, HairSpace, CalculateWidthOnCanvas, Result->Canvas);  }  DebugAssert(MainMsg.Pos(L"\t") == 0);  int IconTextWidth = -1;  int IconTextHeight = 0;  int ALeft = IconWidth + HorzMargin;  for (int MessageIndex = 0; MessageIndex <= 1; MessageIndex++)  {    UnicodeString LabelMsg;    UnicodeString LabelName;    TColor LabelColor = Graphics::clNone;    HFONT LabelFont = 0;    switch (MessageIndex)    {      case 0:        LabelMsg = MainMsg;        LabelName = MainMessageLabelName;        LabelColor = MainInstructionColor;        LabelFont = MainInstructionFont;        break;      case 1:        LabelMsg = BodyMsg;        LabelName = MessageLabelName;        break;      default:        DebugFail();        break;    }    if (!LabelMsg.IsEmpty())    {      TLabel * Message = new TLabel(Panel);      Message->Name = LabelName;      Message->Parent = Panel;      Message->WordWrap = true;      Message->Caption = LabelMsg;      Message->BiDiMode = Result->BiDiMode;      // added to show & as & for messages containing !& pattern of custom commands      // (suppose that we actually never want to use & as accel in message text)      Message->ShowAccelChar = false;      if (LabelFont != 0)      {        Message->Font->Handle = LabelFont;        if (DebugAlwaysTrue(LabelFont == MainInstructionFont) &&             // When showing an early error message            (Configuration != NULL))        {          Configuration->Usage->Set(L"ThemeMainInstructionFontSize", Message->Font->Size);        }      }      if (LabelColor != Graphics::clNone)      {        Message->Font->Color = LabelColor;      }      TRect TextRect;      SetRect(&TextRect, 0, 0, MaxTextWidth, 0);      DrawText(Message->Canvas->Handle, LabelMsg.c_str(), LabelMsg.Length() + 1, &TextRect,        DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |        Result->DrawTextBiDiModeFlagsReadingOnly());      int MaxWidth = Monitor->Width - HorzMargin * 2 - IconWidth - 30;      // 5% buffer for potential WM_DPICHANGED, as after re-scaling the text can otherwise narrowly not fit in.      // Though note that the buffer is lost on the first re-scale due to the AutoSize      TextRect.right = MulDiv(TextRect.right, 105, 100);      // this will truncate the text, we should implement something smarter eventually      TextRect.right = Min(TextRect.right, MaxWidth);      IconTextWidth = Max(IconTextWidth, IconWidth + TextRect.Right);      if (IconTextHeight > 0)      {        IconTextHeight += VertMargin;      }      Message->SetBounds(ALeft, VertMargin + IconTextHeight, TextRect.Right, TextRect.Bottom);      IconTextHeight += TextRect.Bottom;    }  }  if (LinkControl != NULL)  {    LinkControl->Parent = Panel;    LinkControl->Left = Panel->ClientWidth - HorzMargin - LinkControl->Width;    LinkControl->Top = VertMargin + IconTextHeight + VertMargin;    IconTextHeight += VertMargin + LinkControl->Height;  }  DebugAssert((IconTextWidth > 0) && (IconTextHeight > 0));  IconTextHeight = Max(IconTextHeight, IconHeight);  int MoreMessageHeight =    (HasMoreMessages ?      ScaleByTextHeightRunTime(Result, (MoreMessagesSize.Height > 0 ?  MoreMessagesSize.Height : mcMoreMessageHeight)) : 0);  Panel->SetBounds(0, 0, Result->ClientWidth, VertMargin + IconTextHeight + VertMargin + MoreMessageHeight);  TControl * MoreMessagesControl = NULL;  if (HasMoreMessages)  {    if (MoreMessages != NULL)    {      DebugAssert(MoreMessagesUrl.IsEmpty());      TMemo * MessageMemo = new TMemo(Panel);      MoreMessagesControl = MessageMemo;      MessageMemo->Name = L"MessageMemo";      MessageMemo->Parent = Panel;      MessageMemo->ReadOnly = true;      MessageMemo->WantReturns = False;      MessageMemo->ScrollBars = ssVertical;      MessageMemo->Anchors = TAnchors() << akLeft << akRight << akTop;      MessageMemo->Lines->Text = MoreMessages->Text;      Result->MessageMemo = MessageMemo;    }    else if (DebugAlwaysTrue(!MoreMessagesUrl.IsEmpty()))    {      TPanel * MessageBrowserPanel = CreateBlankPanel(Panel);      MessageBrowserPanel->Parent = Panel;      MessageBrowserPanel->Anchors = TAnchors() << akLeft << akRight << akTop;      MessageBrowserPanel->BevelKind = bkTile; // flat border      Result->MessageBrowserPanel = MessageBrowserPanel;      MoreMessagesControl = Result->MessageBrowserPanel;      Result->MessageBrowserUrl =  CampaignUrl(MoreMessagesUrl);    }  }  int MinClientWidth =    ScaleByTextHeightRunTime(Result,      HasMoreMessages ? (MoreMessagesSize.Width > 0 ? MoreMessagesSize.Width : mcMinDialogwithMoreMessagesWidth) : mcMinDialogWidth);  int AClientWidth =    Max(      (IconTextWidth > ButtonGroupWidth ? IconTextWidth : ButtonGroupWidth) +        HorzMargin * 2,      MinClientWidth);  Result->ClientWidth = AClientWidth;  Result->ClientHeight =    Panel->Height + ButtonVertMargin + ButtonHeight + ButtonVertMargin;  if (!CustomCaption.IsEmpty())  {    Result->Caption = CustomCaption;  }  else if (DebugAlwaysTrue(DlgType != mtCustom))  {    Result->Caption = LoadResourceString(Captions[DlgType]);  }  else  {    Result->Caption = Application->Title;  }  if (MoreMessagesControl != NULL)  {    MoreMessagesControl->SetBounds(      ALeft,      Panel->Height - MoreMessageHeight,      Result->ClientWidth - ALeft - HorzMargin,      MoreMessageHeight - VertMargin);  }  int ButtonTop = Panel->Height + ButtonVertMargin;  int X = Result->ClientWidth - ButtonGroupWidth + NeverAskAgainWidth - HorzMargin;  for (unsigned int i = 0; i < ButtonControls.size(); i++)  {    ButtonControls[i]->SetBounds(      X, ButtonTop, ButtonControls[i]->Width, ButtonControls[i]->Height);    X += ButtonControls[i]->Width + ButtonSpacing;  }  if (!NeverAskAgainCaption.IsEmpty() &&      !ButtonControls.empty())  {    TCheckBox * NeverAskAgainCheck = new TCheckBox(Result);    NeverAskAgainCheck->Name = L"NeverAskAgainCheck";    NeverAskAgainCheck->Parent = Result;    NeverAskAgainCheck->Caption = NeverAskAgainCaption;    // Previously we set anchor to akBottom, but as we do not do that for buttons, we removed that.    // When showing window on 100% DPI monitor, with system DPI 100%, but main monitor 150%,    // the title bar seems to start on 150% DPI reducing later, leaving the form client height    // sligtly higher than needed and the checkbox being aligned differently than the button.    // Removing the akBottom aligning improves this sligtly, while the main problem stil should be fixed.    TButton * FirstButton = ButtonControls[0];    int NeverAskAgainHeight = ScaleByTextHeightRunTime(Result, NeverAskAgainCheck->Height);    int NeverAskAgainTop = FirstButton->Top + ((FirstButton->Height - NeverAskAgainHeight) / 2);    int NeverAskAgainLeft = HorzMargin;    NeverAskAgainCheck->SetBounds(      NeverAskAgainLeft, NeverAskAgainTop, NeverAskAgainWidth, NeverAskAgainHeight);  }  return Result;}//---------------------------------------------------------------------------TForm * __fastcall CreateMoreMessageDialog(const UnicodeString & Msg,  TStrings * MoreMessages, TMsgDlgType DlgType, unsigned int Answers,  const TQueryButtonAlias * Aliases, unsigned int AliasesCount,  unsigned int TimeoutAnswer, TButton ** TimeoutButton, const UnicodeString & ImageName,  const UnicodeString & NeverAskAgainCaption, const UnicodeString & MoreMessagesUrl,  TSize MoreMessagesSize, const UnicodeString & CustomCaption){  return TMessageForm::Create(Msg, MoreMessages, DlgType, Answers,    Aliases, AliasesCount, TimeoutAnswer, TimeoutButton, ImageName,    NeverAskAgainCaption, MoreMessagesUrl, MoreMessagesSize, CustomCaption);}//---------------------------------------------------------------------------void __fastcall InsertPanelToMessageDialog(TCustomForm * Form, TPanel * Panel){  TMessageForm * MessageForm = DebugNotNull(dynamic_cast<TMessageForm *>(Form));  MessageForm->InsertPanel(Panel);}//---------------------------------------------------------------------------void __fastcall NavigateMessageDialogToUrl(TCustomForm * Form, const UnicodeString & Url){  TMessageForm * MessageForm = DebugNotNull(dynamic_cast<TMessageForm *>(Form));  MessageForm->NavigateToUrl(Url);}//---------------------------------------------------------------------------int __fastcall GetMessageDialogContentWidth(TCustomForm * Form){  TMessageForm * MessageForm = DebugNotNull(dynamic_cast<TMessageForm *>(Form));  return MessageForm->GetContentWidth();}
 |