| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262 | //---------------------------------------------------------------------------#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"//---------------------------------------------------------------------------#pragma resource "*.dfm"//---------------------------------------------------------------------------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->Parent = Panel;      Message->Name = LabelName;      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();}
 |