//---------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include <Dialogs.hpp>
//---------------------------------------------------------------------
#include <Common.h>
#include <CustomWinConfiguration.h>
#include <WinInterface.h>
#include <VCLCommon.h>
#include <TextsWin.h>
#include <HelpWin.h>
#include <CoreMain.h>
#include <PasTools.hpp>
#include <ProgParams.h>
#include <Tools.h>
#include <GUITools.h>
#include <HistoryComboBox.hpp>
#include <Math.hpp>

#include "Custom.h"
//---------------------------------------------------------------------
#pragma link "PasswordEdit"
#ifndef NO_RESOURCES
#pragma resource "*.dfm"
#endif
//---------------------------------------------------------------------
__fastcall TCustomDialog::TCustomDialog(UnicodeString AHelpKeyword)
  : TForm(GetFormOwner())
{
  UseSystemSettings(this);

  FControlPadding = ScaleByTextHeight(this, 8);
  FPos = ScaleByTextHeight(this, 8);
  FPrePos = FPos;
  FHorizontalMargin = ScaleByTextHeight(this, 8);
  FIndent = FHorizontalMargin;
  FGroupBox = NULL;

  HelpKeyword = AHelpKeyword;

  TBorderIcons BI = BorderIcons;
  if (HelpKeyword.IsEmpty())
  {
    BI >> biHelp;

    OKButton->Left = CancelButton->Left;
    CancelButton->Left = HelpButton->Left;
    HelpButton->Visible = false;
  }
  else
  {
    BI << biHelp;
  }
  BorderIcons = BI;
}
//---------------------------------------------------------------------
bool __fastcall TCustomDialog::Execute()
{
  Changed();
  return (ShowModal() == DefaultResult(this));
}
//---------------------------------------------------------------------
void __fastcall TCustomDialog::DoChange(bool & /*CanSubmit*/)
{
  // noop
}
//---------------------------------------------------------------------
void __fastcall TCustomDialog::Changed()
{
  bool CanSubmit = true;
  DoChange(CanSubmit);
  EnableControl(OKButton, CanSubmit);
}
//---------------------------------------------------------------------
void __fastcall TCustomDialog::Change(TObject * /*Sender*/)
{
  Changed();
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::DoHelp()
{
  FormHelp(this);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::HelpButtonClick(TObject * /*Sender*/)
{
  DoHelp();
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::DoShow()
{
  OKButton->TabOrder = FCount;
  CancelButton->TabOrder = static_cast<short>(FCount + 1);
  HelpButton->TabOrder = static_cast<short>(FCount + 2);
  Changed();
  TForm::DoShow();
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::DoValidate()
{
  // noop
}
//---------------------------------------------------------------------------
bool __fastcall TCustomDialog::CloseQuery()
{
  if (ModalResult == DefaultResult(this))
  {
    DoValidate();
  }
  return TForm::CloseQuery();
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::RemoveCancelButton()
{
  CancelButton->Visible = false;
  OKButton->Left = CancelButton->Left;
  OKButton->Cancel = true;
  DebugAssert(OKButton->Width == CancelButton->Width);
  DebugAssert(OKButton->Top == CancelButton->Top);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddDialogButton(TButton * Button)
{
  Button->Parent = this;
  Button->Top = OKButton->Top;
  Button->Left = FHorizontalMargin;
  Button->Height = OKButton->Height;
  AddWinControl(Button);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddImage(const UnicodeString & ImageName)
{
  TImage * Image = new TImage(this);
  Image->Name = L"Image";
  Image->Parent = GetDefaultParent();
  LoadDialogImage(Image, ImageName);
  Image->SetBounds(FIndent, FPos + ScaleByTextHeight(this, 3), Image->Picture->Width, Image->Picture->Height);
  FIndent += Image->Width + ScaleByTextHeight(this, 12);
}
//---------------------------------------------------------------------------
int __fastcall TCustomDialog::GetMaxControlWidth(TControl * Control)
{
  return GetDefaultParent()->ClientWidth - Control->Left - FHorizontalMargin;
}
//---------------------------------------------------------------------------
TWinControl * __fastcall TCustomDialog::GetDefaultParent()
{
  return (FGroupBox != NULL) ? FGroupBox : static_cast<TWinControl *>(this);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AdjustHeight(TControl * Control)
{
  FPos = Control->Top + Control->Height + FControlPadding;
  int Delta = (FPos - FPrePos);
  ClientHeight = ClientHeight + Delta;
  if (FGroupBox != NULL)
  {
    FGroupBox->Height = FGroupBox->Height + Delta;
  }
  FPrePos = FPos;
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddWinControl(TWinControl * Control)
{
  Control->TabOrder = FCount;
  FCount++;
}
//---------------------------------------------------------------------------
TCheckBox * __fastcall TCustomDialog::CreateAndAddCheckBox(const UnicodeString & Caption)
{
  TCheckBox * CheckBox = new TCheckBox(this);
  CheckBox->Caption = Caption;
  AddButtonControl(CheckBox);
  return CheckBox;
}
//---------------------------------------------------------------------------
TLabel * __fastcall TCustomDialog::CreateLabel(UnicodeString Label)
{
  TLabel * Result = new TLabel(this);
  Result->Caption = Label;
  return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddEditLikeControl(TWinControl * Edit, TLabel * Label, bool OneLine)
{
  Edit->Parent = GetDefaultParent();
  // this updates Height property to real value
  Edit->HandleNeeded();

  if (Label != NULL)
  {
    Label->Parent = GetDefaultParent();
    Label->Left = FIndent;

    if (OneLine)
    {
      DebugAssert(Edit->Height > Label->Height);
      Label->Top = FPos + ((Edit->Height - Label->Height) / 2);
    }
    else
    {
      Label->Top = FPos;

      FPos += Label->Height + ScaleByTextHeight(this, 4);
    }
  }

  Edit->Top = FPos;
  if (OneLine)
  {
    Edit->Left = GetDefaultParent()->ClientWidth - FHorizontalMargin - Edit->Width;
  }
  else
  {
    Edit->Left = FIndent;
    Edit->Width = GetMaxControlWidth(Edit);
  }

  AdjustHeight(Edit);

  if (Label != NULL)
  {
    if (Label->FocusControl == NULL)
    {
      Label->FocusControl = Edit;
    }
    else
    {
      DebugAssert(Label->FocusControl == Edit);
    }
  }

  AddWinControl(Edit);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddEdit(TCustomEdit * Edit, TLabel * Label, bool OneLine)
{
  AddEditLikeControl(Edit, Label, OneLine);

  TEdit * PublicEdit = reinterpret_cast<TEdit *>(Edit);
  if (PublicEdit->OnChange == NULL)
  {
    PublicEdit->OnChange = Change;
  }
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddComboBox(TCustomCombo * Combo, TLabel * Label, TStrings * Items, bool OneLine)
{
  AddEditLikeControl(Combo, Label, OneLine);

  if (Items != NULL)
  {
    Combo->Items = Items;
  }

  if (OneLine)
  {
    int Width = 0;
    for (int Index = 0; Index < Combo->Items->Count; Index++)
    {
      Width = Max(Width, Combo->Canvas->TextWidth(Combo->Items->Strings[Index]));
    }

    Width += ScaleByTextHeight(Combo, 4 + 16 + 14);
    Width = Max(Width, HelpButton->Width);

    Combo->Width = Width;
    Combo->Left = GetDefaultParent()->ClientWidth - FHorizontalMargin - Width;
  }

  TComboBox * PublicCombo = reinterpret_cast<TComboBox *>(Combo);
  if (PublicCombo->OnChange == NULL)
  {
    PublicCombo->OnChange = Change;
  }
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::ScaleButtonControl(TButtonControl * Control)
{
  // this updates Height property to real value
  Control->HandleNeeded();
  // buttons do not scale with text on their own
  Control->Height = ScaleByTextHeight(Control, Control->Height);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddButtonControl(TButtonControl * Control)
{
  Control->Parent = GetDefaultParent();
  Control->Left = FIndent + ScaleByTextHeight(this, 6);
  Control->Top = FPos;
  Control->Width = GetMaxControlWidth(Control);
  ScaleButtonControl(Control);

  AdjustHeight(Control);

  AddWinControl(Control);

  TCheckBox * PublicControl = reinterpret_cast<TCheckBox *>(Control);
  if (PublicControl->OnClick == NULL)
  {
    PublicControl->OnClick = Change;
  }
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddText(TLabel * Label)
{
  Label->Parent = GetDefaultParent();

  Label->WordWrap = true;
  Label->Left = FIndent;
  Label->Width = GetMaxControlWidth(Label);
  Label->Top = FPos;
  Label->ShowAccelChar = false;

  TRect TextRect;
  SetRect(&TextRect, 0, 0, Label->Width, 0);
  DrawText(Label->Canvas->Handle, Label->Caption.c_str(), Label->Caption.Length() + 1, &TextRect,
    DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK | DT_NOPREFIX |
    Label->DrawTextBiDiModeFlagsReadingOnly());
  Label->Height = TextRect.Height();

  AdjustHeight(Label);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddText(TStaticText * Label)
{
  Label->Parent = GetDefaultParent();

  Label->Left = FIndent;
  Label->Width = GetMaxControlWidth(Label);
  Label->Top = FPos;
  Label->ShowAccelChar = false;

  AdjustHeight(Label);
  AddWinControl(Label);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::AddSeparator()
{
  TBevel * Bevel = new TBevel(this);
  Bevel->Parent = GetDefaultParent();

  Bevel->Left = FIndent;
  Bevel->Top = FPos;
  Bevel->Height = 2;
  Bevel->Width = GetMaxControlWidth(Bevel);

  AdjustHeight(Bevel);
}
//---------------------------------------------------------------------------
void __fastcall TCustomDialog::StartGroup(const UnicodeString & Caption)
{
  if (FGroupBox != NULL)
  {
    FIndent = FGroupBox->Left;
    FPos = FGroupBox->Top + FGroupBox->Height + FControlPadding;
    FPrePos = FPos;
    FGroupBox = NULL;
  }

  TGroupBox * GroupBox = new TGroupBox(this);
  GroupBox->Parent = this;
  GroupBox->Caption = Caption;

  GroupBox->Left = FIndent;
  GroupBox->Top = FPos;
  GroupBox->Height = ScaleByTextHeight(GroupBox, 20);
  GroupBox->Width = GetMaxControlWidth(GroupBox);

  AdjustHeight(GroupBox);

  AddWinControl(GroupBox);

  FPos = ScaleByTextHeight(this, 16);
  FPrePos = FPos;
  FIndent = FHorizontalMargin;

  FGroupBox = GroupBox;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TSaveSessionDialog : public TCustomDialog
{
public:
  __fastcall TSaveSessionDialog(TComponent* AOwner);
  void __fastcall Init(bool CanSavePassword, bool NotRecommendedSavingPassword,
    TStrings * AdditionalFolders);

  bool __fastcall Execute(UnicodeString & SessionName, bool & SavePassword,
    bool & CreateShortcut, const UnicodeString & OriginalSessionName);

protected:
  virtual void __fastcall DoValidate();
  virtual void __fastcall DoChange(bool & CanSubmit);

private:
  UnicodeString FOriginalSessionName;
  TEdit * SessionNameEdit;
  TComboBox * FolderCombo;
  TCheckBox * SavePasswordCheck;
  TCheckBox * CreateShortcutCheck;
  UnicodeString FRootFolder;

  UnicodeString __fastcall GetSessionName();
};
//---------------------------------------------------------------------------
// Need to have an Owner argument for SafeFormCreate
__fastcall TSaveSessionDialog::TSaveSessionDialog(TComponent* /*AOwner*/) :
  TCustomDialog(HELP_SESSION_SAVE)
{
}
//---------------------------------------------------------------------------
void __fastcall TSaveSessionDialog::Init(bool CanSavePassword,
  bool NotRecommendedSavingPassword, TStrings * AdditionalFolders)
{
  Caption = LoadStr(SAVE_SESSION_CAPTION);

  SessionNameEdit = new TEdit(this);
  AddEdit(SessionNameEdit, CreateLabel(LoadStr(SAVE_SESSION_PROMPT)));

  FRootFolder = LoadStr(SAVE_SESSION_ROOT_FOLDER2);
  std::unique_ptr<TStringList> Folders(new TStringList());

  if (AdditionalFolders != NULL)
  {
    Folders->AddStrings(AdditionalFolders);
  }

  for (int Index = 0; Index < StoredSessions->Count; Index++)
  {
    TSessionData * Data = StoredSessions->Sessions[Index];
    if (!Data->Special && !Data->IsWorkspace)
    {
      UnicodeString Folder = Data->FolderName;
      if (!Folder.IsEmpty() && Folders->IndexOf(Folder) < 0)
      {
        Folders->Add(Folder);
      }
    }
  }

  DebugAssert(!Folders->CaseSensitive);
  Folders->Sort();

  FolderCombo = new TComboBox(this);
  AddComboBox(FolderCombo, CreateLabel(LoadStr(SAVE_SESSION_FOLDER)));
  FolderCombo->DropDownCount = Max(FolderCombo->DropDownCount, 16);
  FolderCombo->Items->Add(FRootFolder);
  FolderCombo->Items->AddStrings(Folders.get());

  SavePasswordCheck = CreateAndAddCheckBox(
    LoadStr(NotRecommendedSavingPassword ? SAVE_SESSION_PASSWORD :
      (CustomWinConfiguration->UseMasterPassword ? SAVE_SESSION_PASSWORD_MASTER : SAVE_SESSION_PASSWORD_RECOMMENDED)));

  CreateShortcutCheck = CreateAndAddCheckBox(LoadStr(SAVE_SITE_WORKSPACE_SHORTCUT));

  EnableControl(SavePasswordCheck, CanSavePassword);
}
//---------------------------------------------------------------------------
bool __fastcall TSaveSessionDialog::Execute(
  UnicodeString & SessionName, bool & SavePassword, bool & CreateShortcut,
  const UnicodeString & OriginalSessionName)
{
  FOriginalSessionName = OriginalSessionName;
  SessionNameEdit->Text = TSessionData::ExtractLocalName(SessionName);
  UnicodeString Folder = TSessionData::ExtractFolderName(SessionName);
  if (Folder.IsEmpty())
  {
    FolderCombo->Text = FRootFolder;
  }
  else
  {
    FolderCombo->Text = Folder;
  }
  SavePasswordCheck->Checked = SavePassword;
  CreateShortcutCheck->Checked = CreateShortcut;
  bool Result = TCustomDialog::Execute();
  if (Result)
  {
    SessionName = GetSessionName();
    SavePassword = SavePasswordCheck->Checked;
    CreateShortcut = CreateShortcutCheck->Checked;
  }
  return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TSaveSessionDialog::GetSessionName()
{
  UnicodeString Folder;
  if (FolderCombo->Text != FRootFolder)
  {
    Folder = FolderCombo->Text;
  }
  return TSessionData::ComposePath(Folder, SessionNameEdit->Text);
}
//---------------------------------------------------------------------------
void __fastcall TSaveSessionDialog::DoValidate()
{
  TSessionData::ValidateName(SessionNameEdit->Text);
  SessionNameValidate(GetSessionName(), FOriginalSessionName);

  UnicodeString Folder = TSessionData::ExtractFolderName(GetSessionName());
  if (!Folder.IsEmpty() && StoredSessions->IsWorkspace(Folder))
  {
    throw Exception(FMTLOAD(WORKSPACE_NOT_FOLDER, (Folder)));
  }

  if (SavePasswordCheck->Enabled && SavePasswordCheck->Checked &&
      CustomWinConfiguration->UseMasterPassword)
  {
    CustomWinConfiguration->AskForMasterPasswordIfNotSet();
  }

  TCustomDialog::DoValidate();
}
//---------------------------------------------------------------------------
void __fastcall TSaveSessionDialog::DoChange(bool & CanSubmit)
{
  CanSubmit = !SessionNameEdit->Text.IsEmpty();
  TCustomDialog::DoChange(CanSubmit);
}
//---------------------------------------------------------------------------
TSessionData * __fastcall DoSaveSession(TSessionData * SessionData,
  TSessionData * OriginalSession, bool ForceDialog,
  TStrings * AdditionalFolders)
{
  bool SavePassword = false;
  bool * PSavePassword;
  bool NotRecommendedSavingPassword =
    !CustomWinConfiguration->UseMasterPassword &&
    !SameText(SessionData->UserName, AnonymousUserName);

  if (Configuration->DisablePasswordStoring ||
      !SessionData->HasAnySessionPassword())
  {
    PSavePassword = NULL;
  }
  else
  {
    PSavePassword = &SavePassword;
    SavePassword =
      ((OriginalSession != NULL) && OriginalSession->HasAnySessionPassword()) ||
      !NotRecommendedSavingPassword;
  }

  UnicodeString SessionName = SessionData->SessionName;

  bool Result;
  bool CreateShortcut = false;
  if (!ForceDialog && ((PSavePassword == NULL) || SavePassword))
  {
    CustomWinConfiguration->AskForMasterPasswordIfNotSetAndNeededToPersistSessionData(SessionData);
    Result = true;
  }
  else
  {
    // This can be a standalone dialog when used with save URL (from GetLoginData)
    TSaveSessionDialog * Dialog = SafeFormCreate<TSaveSessionDialog>();
    try
    {
      Dialog->Init((PSavePassword != NULL), NotRecommendedSavingPassword, AdditionalFolders);
      Result = Dialog->Execute(SessionName, SavePassword, CreateShortcut, SessionData->Name);
    }
    __finally
    {
      delete Dialog;
    }
  }

  TSessionData * NewSession = NULL;
  if (Result)
  {
    if ((PSavePassword != NULL) && !SavePassword)
    {
      SessionData->ClearSessionPasswords();
    }

    NewSession =
      StoredSessions->NewSession(SessionName, SessionData);
    // modified only, explicit
    StoredSessions->Save(false, true);
    if (!SessionData->HostKey.IsEmpty())
    {
      SessionData->CacheHostKeyIfNotCached();
    }

    if (CreateShortcut)
    {
      TOperationVisualizer Visualizer;
      UnicodeString AdditionalParams =
        TProgramParams::FormatSwitch(DESKTOP_SWITCH) + L" " +
        TProgramParams::FormatSwitch(UPLOAD_IF_ANY_SWITCH);
      CreateDesktopSessionShortCut(SessionName, L"", AdditionalParams, -1, SITE_ICON);
    }
  }

  return NewSession;
}
//---------------------------------------------------------------------------
void __fastcall SessionNameValidate(const UnicodeString & Text,
  const UnicodeString & OriginalName)
{
  TSessionData::ValidatePath(Text);

  DebugAssert(StoredSessions);
  TSessionData * Data = (TSessionData *)StoredSessions->FindByName(Text);
  if (Data && Data->Special)
  {
    MessageDialog(FMTLOAD(CANNOT_OVERWRITE_SPECIAL_SESSION, (Text)),
      qtError, qaOK, HELP_NONE);
    Abort();
  }
  else if ((Data != NULL) && !Data->IsSameName(OriginalName) &&
    MessageDialog(MainInstructions(FMTLOAD(CONFIRM_OVERWRITE_SESSION, (Text))),
      qtConfirmation, qaYes | qaNo, HELP_SESSION_SAVE_OVERWRITE) != qaYes)
  {
    Abort();
  }
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TSaveWorkspaceDialog : public TCustomDialog
{
public:
  __fastcall TSaveWorkspaceDialog(bool CanSavePasswords,
    bool NotRecommendedSavingPasswords);

  bool __fastcall Execute(
    UnicodeString & WorkspaceName, bool & SavePasswords, bool & CreateShortcut,
    bool & EnableAutoSave);

protected:
  virtual void __fastcall DoValidate();
  virtual void __fastcall DoChange(bool & CanSubmit);

private:
  TComboBox * WorkspaceNameCombo;
  TCheckBox * SavePasswordsCheck;
  TCheckBox * CreateShortcutCheck;
  TCheckBox * EnableAutoSaveCheck;
};
//---------------------------------------------------------------------------
__fastcall TSaveWorkspaceDialog::TSaveWorkspaceDialog(
    bool CanSavePasswords, bool NotRecommendedSavingPasswords) :
  TCustomDialog(HELP_WORKSPACE_SAVE)
{
  Caption = LoadStr(SAVE_WORKSPACE_CAPTION);

  WorkspaceNameCombo = new TComboBox(this);
  WorkspaceNameCombo->AutoComplete = false;
  AddComboBox(WorkspaceNameCombo, CreateLabel(LoadStr(SAVE_WORKSPACE_PROMPT)));
  WorkspaceNameCombo->DropDownCount = Max(WorkspaceNameCombo->DropDownCount, 16);

  std::unique_ptr<TStrings> Workspaces(StoredSessions->GetWorkspaces());
  WorkspaceNameCombo->Items->AddStrings(Workspaces.get());

  SavePasswordsCheck = CreateAndAddCheckBox(
    LoadStr(NotRecommendedSavingPasswords ? SAVE_WORKSPACE_PASSWORDS :
      (CustomWinConfiguration->UseMasterPassword ?
        SAVE_WORKSPACE_PASSWORDS_MASTER : SAVE_WORKSPACE_PASSWORDS_RECOMMENDED)));

  EnableControl(SavePasswordsCheck, CanSavePasswords);

  CreateShortcutCheck = CreateAndAddCheckBox(LoadStr(SAVE_SITE_WORKSPACE_SHORTCUT));

  EnableAutoSaveCheck = CreateAndAddCheckBox(LoadStr(SAVE_WORKSPACE_AUTO));
}
//---------------------------------------------------------------------------
bool __fastcall TSaveWorkspaceDialog::Execute(
  UnicodeString & WorkspaceName, bool & SavePasswords, bool & CreateShortcut,
  bool & EnableAutoSave)
{
  WorkspaceNameCombo->Text = WorkspaceName;
  SavePasswordsCheck->Checked = SavePasswords;
  CreateShortcutCheck->Checked = CreateShortcut;
  EnableAutoSaveCheck->Checked = EnableAutoSave;
  bool Result = TCustomDialog::Execute();
  if (Result)
  {
    WorkspaceName = WorkspaceNameCombo->Text;
    SavePasswords = SavePasswordsCheck->Checked;
    CreateShortcut = CreateShortcutCheck->Checked;
    EnableAutoSave = EnableAutoSaveCheck->Checked;
  }
  return Result;
}
//---------------------------------------------------------------------------
void __fastcall TSaveWorkspaceDialog::DoValidate()
{
  TSessionData::ValidateName(WorkspaceNameCombo->Text);

  if (StoredSessions->IsFolder(WorkspaceNameCombo->Text))
  {
    throw Exception(FMTLOAD(FOLDER_NOT_WORKSPACE, (WorkspaceNameCombo->Text)));
  }

  if (SavePasswordsCheck->Enabled && SavePasswordsCheck->Checked &&
      CustomWinConfiguration->UseMasterPassword)
  {
    CustomWinConfiguration->AskForMasterPasswordIfNotSet();
  }

  TCustomDialog::DoValidate();
}
//---------------------------------------------------------------------------
void __fastcall TSaveWorkspaceDialog::DoChange(bool & CanSubmit)
{
  CanSubmit = !WorkspaceNameCombo->Text.IsEmpty();

  TCustomDialog::DoChange(CanSubmit);
}
//---------------------------------------------------------------------------
bool __fastcall DoSaveWorkspaceDialog(UnicodeString & WorkspaceName,
  bool * SavePasswords, bool NotRecommendedSavingPasswords,
  bool & CreateShortcut, bool & EnableAutoSave)
{
  std::unique_ptr<TSaveWorkspaceDialog> Dialog(
    new TSaveWorkspaceDialog((SavePasswords != NULL), NotRecommendedSavingPasswords));

  bool Dummy = false;
  if (SavePasswords == NULL)
  {
    SavePasswords = &Dummy;
  }
  return
    Dialog->Execute(
      WorkspaceName, *SavePasswords, CreateShortcut, EnableAutoSave);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TShortCutDialog : public TCustomDialog
{
public:
  __fastcall TShortCutDialog(const TShortCuts & ShortCuts, UnicodeString HelpKeyword);

  bool __fastcall Execute(TShortCut & ShortCut);

private:
  TComboBox * ShortCutCombo;
};
//---------------------------------------------------------------------------
__fastcall TShortCutDialog::TShortCutDialog(const TShortCuts & ShortCuts, UnicodeString HelpKeyword) :
  TCustomDialog(HelpKeyword)
{
  Caption = LoadStr(SHORTCUT_CAPTION);

  ShortCutCombo = new TComboBox(this);
  AddComboBox(ShortCutCombo, CreateLabel(LoadStr(SHORTCUT_LABEL)));
  InitializeShortCutCombo(ShortCutCombo, ShortCuts);
}
//---------------------------------------------------------------------------
bool __fastcall TShortCutDialog::Execute(TShortCut & ShortCut)
{
  SetShortCutCombo(ShortCutCombo, ShortCut);
  bool Result = TCustomDialog::Execute();
  if (Result)
  {
    ShortCut = GetShortCutCombo(ShortCutCombo);
  }
  return Result;
}
//---------------------------------------------------------------------------
bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
  const TShortCuts & ShortCuts, UnicodeString HelpKeyword)
{
  bool Result;
  TShortCutDialog * Dialog = new TShortCutDialog(ShortCuts, HelpKeyword);
  try
  {
    Result = Dialog->Execute(ShortCut);
  }
  __finally
  {
    delete Dialog;
  }
  return Result;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TRemoteMoveDialog : public TCustomDialog
{
public:
  __fastcall TRemoteMoveDialog(bool Multi);

  bool __fastcall Execute(UnicodeString & Target, UnicodeString & FileMask);

protected:
  DYNAMIC void __fastcall DoShow();
  virtual void __fastcall DoValidate();
  UnicodeString __fastcall GetFileMask();

private:
  THistoryComboBox * Combo;
  bool FMulti;
};
//---------------------------------------------------------------------------
__fastcall TRemoteMoveDialog::TRemoteMoveDialog(bool Multi) :
  TCustomDialog(HELP_REMOTE_MOVE)
{
  Caption = LoadStr(REMOTE_MOVE_TITLE);
  // The same as TRemoteTransferDialog
  ClientWidth = ScaleByTextHeight(this, 420);

  FMulti = Multi;

  AddImage(L"Move To");

  Combo = new THistoryComboBox(this);
  Combo->AutoComplete = false;
  AddComboBox(Combo, CreateLabel(LoadStr(REMOTE_TRANSFER_PROMPT2)));
}
//---------------------------------------------------------------------------
bool __fastcall TRemoteMoveDialog::Execute(UnicodeString & Target, UnicodeString & FileMask)
{
  Combo->Items = CustomWinConfiguration->History[L"RemoteTarget"];
  Combo->Text = UnixIncludeTrailingBackslash(Target) + FileMask;
  bool Result = TCustomDialog::Execute();
  if (Result)
  {
    Target = UnixExtractFilePath(Combo->Text);
    FileMask = GetFileMask();
    Combo->SaveToHistory();
    CustomWinConfiguration->History[L"RemoteTarget"] = Combo->Items;
  }
  return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TRemoteMoveDialog::GetFileMask()
{
  return UnixExtractFileName(Combo->Text);
}
//---------------------------------------------------------------------------
void __fastcall TRemoteMoveDialog::DoShow()
{
  TCustomDialog::DoShow();
  InstallPathWordBreakProc(Combo);
}
//---------------------------------------------------------------------------
void __fastcall TRemoteMoveDialog::DoValidate()
{
  if (!IsFileNameMask(GetFileMask()) && FMulti)
  {
    UnicodeString Message =
      FormatMultiFilesToOneConfirmation(Combo->Text, true);
    if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel, HELP_NONE) == qaCancel)
    {
      Abort();
    }
  }

  TCustomDialog::DoValidate();
}
//---------------------------------------------------------------------------
bool __fastcall DoRemoteMoveDialog(bool Multi, UnicodeString & Target, UnicodeString & FileMask)
{
  std::unique_ptr<TRemoteMoveDialog> Dialog(new TRemoteMoveDialog(Multi));
  return Dialog->Execute(Target, FileMask);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TCustomCommandOptionsDialog : public TCustomDialog
{
public:
  __fastcall TCustomCommandOptionsDialog(
    const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags,
    TCustomCommand * CustomCommandForOptions, const UnicodeString & Site);

  bool __fastcall Execute();

protected:
  virtual void __fastcall DoHelp();
  DYNAMIC void __fastcall DoShow();

private:
  const TCustomCommandType * FCommand;
  TStrings * FCustomCommandOptions;
  std::vector<TControl *> FControls;
  std::vector<std::vector<UnicodeString> > FValues;
  unsigned int FFlags;
  UnicodeString FSite;

  UnicodeString __fastcall HistoryKey(const TCustomCommandType::TOption & Option);
  THistoryComboBox * __fastcall CreateHistoryComboBox(const TCustomCommandType::TOption & Option, const UnicodeString & Value);
  void __fastcall BrowseButtonClick(TObject * Sender);
  void __fastcall LinkLabelClick(TObject * Sender);
  UnicodeString __fastcall SaveHistoryComboBoxValue(TControl * Control, const TCustomCommandType::TOption & Option);
  void __fastcall AddOptionComboBox(
    TComboBox * ComboBox, const UnicodeString & Value, const TCustomCommandType::TOption & Option,
    std::vector<UnicodeString> & Values);
  UnicodeString __fastcall GetComboBoxValue(TControl * Control, const UnicodeString & Default);
  int __fastcall GetOptionIndex(TControl * Control);
  int __fastcall GetControlIndex(TControl * Control);
};
//---------------------------------------------------------------------------
__fastcall TCustomCommandOptionsDialog::TCustomCommandOptionsDialog(
    const TCustomCommandType * Command, TStrings * CustomCommandOptions,
    unsigned int Flags, TCustomCommand * CustomCommandForOptions,
    const UnicodeString & Site) :
  TCustomDialog(HELP_EXTENSION_OPTIONS)
{
  FCommand = Command;
  FFlags = Flags;
  FCustomCommandOptions = CustomCommandOptions;
  FSite = Site;
  Caption = FMTLOAD(EXTENSION_OPTIONS_CAPTION, (StripEllipsis(StripHotkey(FCommand->Name))));
  Width = ScaleByTextHeight(this, 400);

  int ControlIndex = 0;
  for (int OptionIndex = 0; OptionIndex < FCommand->OptionsCount; OptionIndex++)
  {
    const TCustomCommandType::TOption & Option = FCommand->GetOption(OptionIndex);

    if ((Option.Flags & FFlags) != 0)
    {
      UnicodeString OptionKey = FCommand->GetOptionKey(Option, FSite);
      UnicodeString Value;
      if ((CustomCommandForOptions != NULL) &&
          Option.HasPatterns(CustomCommandForOptions))
      {
        Value = CustomCommandForOptions->Complete(Option.Default, true);
      }
      else
      {
        if (FCustomCommandOptions->IndexOfName(OptionKey) >= 0)
        {
          Value = FCustomCommandOptions->Values[OptionKey];
        }
        else
        {
          Value = Option.Default;
        }
      }

      int Tag = (OptionIndex << 16) + ControlIndex;
      TControl * Control = NULL;
      std::vector<UnicodeString> Values;
      if (Option.Kind == TCustomCommandType::okUnknown)
      {
        Control = NULL;
      }
      else if (Option.Kind == TCustomCommandType::okLabel)
      {
        TLabel * Label = CreateLabel(Option.Caption);
        AddText(Label);
        Control = Label;
      }
      else if (Option.Kind == TCustomCommandType::okLink)
      {
        TStaticText * Label = new TStaticText(this);
        Label->Caption = Option.Caption;
        if (IsHttpOrHttpsUrl(Label->Caption))
        {
          Label->Caption = SecureUrl(Label->Caption);
          LinkLabel(Label);
          Label->TabStop = true;
        }
        else if (!Option.Default.IsEmpty() && IsHttpOrHttpsUrl(Option.Default))
        {
          Label->OnClick = LinkLabelClick;
          LinkLabel(Label);
          Label->TabStop = true;
        }
        else
        {
          // keep it plain text, as we have no URL
        }
        AddText(Label);
        Control = Label;
      }
      else if (Option.Kind == TCustomCommandType::okGroup)
      {
        StartGroup(Option.Caption);
      }
      else if (Option.Kind == TCustomCommandType::okSeparator)
      {
        AddSeparator();
      }
      else if (Option.Kind == TCustomCommandType::okTextBox)
      {
        Control = CreateHistoryComboBox(Option, Value);
      }
      else if (Option.Kind == TCustomCommandType::okFile)
      {
        THistoryComboBox * ComboBox = CreateHistoryComboBox(Option, Value);
        TButton * Button = new TButton(this);
        Button->Parent = GetDefaultParent();
        Button->Width = HelpButton->Width;
        Button->Left = GetDefaultParent()->ClientWidth - Button->Width - HorizontalMargin;
        ComboBox->Width = Button->Left - ComboBox->Left - ScaleByTextHeight(this, 6);
        Button->Top = ComboBox->Top - ScaleByTextHeight(this, 2);
        Button->Tag = Tag;
        Button->Caption = LoadStr(EXTENSION_OPTIONS_BROWSE);
        Button->OnClick = BrowseButtonClick;
        ScaleButtonControl(Button);
        AddWinControl(Button);
        Control = ComboBox;
      }
      else if (Option.Kind == TCustomCommandType::okDropDownList)
      {
        TComboBox * ComboBox = new TComboBox(this);
        ComboBox->Style = csDropDownList;

        AddOptionComboBox(ComboBox, Value, Option, Values);

        Control = ComboBox;
      }
      else if (Option.Kind == TCustomCommandType::okComboBox)
      {
        TComboBox * ComboBox = new TComboBox(this);
        ComboBox->Style = csDropDown;

        AddOptionComboBox(ComboBox, Value, Option, Values);
        if (ComboBox->ItemIndex < 0)
        {
          ComboBox->Text = Value;
        }

        Control = ComboBox;
      }
      else if (Option.Kind == TCustomCommandType::okCheckBox)
      {
        TCheckBox * CheckBox = CreateAndAddCheckBox(Option.Caption);

        CheckBox->Checked =
          (Option.Params.size() >= 1) &&
          (Value == Option.Params[0]);

        Control = CheckBox;
      }
      else
      {
        DebugFail();
      }

      if (Control != NULL)
      {
        Control->Tag = Tag;
      }
      FControls.push_back(Control);
      FValues.push_back(Values);
      ControlIndex++;
      DebugAssert(static_cast<int>(FControls.size()) == ControlIndex);
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandOptionsDialog::AddOptionComboBox(
  TComboBox * ComboBox, const UnicodeString & Value, const TCustomCommandType::TOption & Option, std::vector<UnicodeString> & Values)
{
  std::unique_ptr<TStringList> Items(new TStringList());
  int ItemIndex = -1;

  TCustomCommandType::TOption::TParams::const_iterator ParamI = Option.Params.begin();
  while (ParamI != Option.Params.end())
  {
    UnicodeString Item = (*ParamI);
    int P = Item.Pos(L"=");
    UnicodeString ParamValue;
    if (P > 0)
    {
      ParamValue = Item.SubString(1, P - 1);
      Item.Delete(1, P);
    }
    else
    {
      ParamValue = Item;
    }
    Items->Add(Item);
    if (Value == ParamValue)
    {
      ItemIndex = Items->Count - 1;
    }
    Values.push_back(ParamValue);
    ParamI++;
  }

  AddComboBox(ComboBox, CreateLabel(Option.Caption), Items.get(), true);

  ComboBox->ItemIndex = ItemIndex;
}
//---------------------------------------------------------------------------
int __fastcall TCustomCommandOptionsDialog::GetOptionIndex(TControl * Control)
{
  return (Control->Tag >> 16);
}
//---------------------------------------------------------------------------
int __fastcall TCustomCommandOptionsDialog::GetControlIndex(TControl * Control)
{
  return (Control->Tag & 0xFFFF);
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandOptionsDialog::LinkLabelClick(TObject * Sender)
{
  TStaticText * Label = DebugNotNull(dynamic_cast<TStaticText *>(Sender));
  const TCustomCommandType::TOption & Option = FCommand->GetOption(GetOptionIndex(Label));
  OpenBrowser(SecureUrl(Option.Default));
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandOptionsDialog::BrowseButtonClick(TObject * Sender)
{
  TButton * Button = DebugNotNull(dynamic_cast<TButton *>(Sender));
  int OptionIndex = GetOptionIndex(Button);
  const TCustomCommandType::TOption & Option = FCommand->GetOption(OptionIndex);
  int ControlIndex = GetControlIndex(Button);
  THistoryComboBox * ComboBox = dynamic_cast<THistoryComboBox *>(FControls[ControlIndex]);

  std::unique_ptr<TOpenDialog> OpenDialog(new TOpenDialog(Application));

  UnicodeString Title;
  if (!Option.FileCaption.IsEmpty())
  {
    Title = Option.FileCaption;
  }
  else
  {
    UnicodeString Caption = Option.Caption;
    Caption = StripHotkey(Caption);
    if (!Caption.IsEmpty() && (Caption[Caption.Length()] == L':'))
    {
      Caption.SetLength(Caption.Length() - 1);
    }
    Title = FMTLOAD(EXTENSION_OPTIONS_BROWSE_TITLE, (Caption));
  }
  OpenDialog->Title = Title;

  UnicodeString Value;
  if (ComboBox->Text.IsEmpty())
  {
    Value = Option.FileInitial;
  }
  else
  {
    Value = ComboBox->Text;
  }
  UnicodeString ExpandedValue = ExpandEnvironmentVariables(Value);
  OpenDialog->FileName = ExpandedValue;
  UnicodeString InitialDir = ExtractFilePath(ExpandedValue);
  if (!InitialDir.IsEmpty())
  {
    OpenDialog->InitialDir = InitialDir;
  }
  OpenDialog->Filter = Option.FileFilter;
  OpenDialog->DefaultExt = Option.FileExt;

  if (OpenDialog->Execute())
  {
    if (OpenDialog->FileName != ExpandedValue)
    {
      ComboBox->Text = OpenDialog->FileName;
    }
    // If user just confirms the initial value, persist it
    else if (ComboBox->Text.IsEmpty())
    {
      DebugAssert(Option.FileInitial == Value);
      ComboBox->Text = Value;
    }
  }
}
//---------------------------------------------------------------------------
THistoryComboBox * __fastcall TCustomCommandOptionsDialog::CreateHistoryComboBox(
  const TCustomCommandType::TOption & Option, const UnicodeString & Value)
{
  THistoryComboBox * ComboBox = new THistoryComboBox(this);
  ComboBox->AutoComplete = false;
  AddComboBox(ComboBox, CreateLabel(Option.Caption));
  ComboBox->Items = CustomWinConfiguration->History[HistoryKey(Option)];
  ComboBox->Text = Value;
  return ComboBox;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomCommandOptionsDialog::HistoryKey(const TCustomCommandType::TOption & Option)
{
  UnicodeString Result = FCommand->GetOptionKey(Option, FSite);
  Result = CustomWinConfiguration->GetValidHistoryKey(Result);
  return L"CustomCommandOption_" + Result;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomCommandOptionsDialog::Execute()
{
  bool Result = TCustomDialog::Execute();

  if (Result)
  {
    int ControlIndex = 0;
    for (int OptionIndex = 0; OptionIndex < FCommand->OptionsCount; OptionIndex++)
    {
      const TCustomCommandType::TOption & Option = FCommand->GetOption(OptionIndex);
      if ((Option.Flags & FFlags) != 0)
      {
        if ((Option.Kind != TCustomCommandType::okUnknown) &&
            Option.IsControl)
        {
          UnicodeString OptionKey = FCommand->GetOptionKey(Option, FSite);

          TControl * Control = FControls[ControlIndex];

          UnicodeString Value;
          if (Option.Kind == TCustomCommandType::okTextBox)
          {
            Value = SaveHistoryComboBoxValue(Control, Option);
          }
          else if (Option.Kind == TCustomCommandType::okFile)
          {
            Value = SaveHistoryComboBoxValue(Control, Option);
          }
          else if (Option.Kind == TCustomCommandType::okDropDownList)
          {
            Value = GetComboBoxValue(Control, Option.Default);
          }
          else if (Option.Kind == TCustomCommandType::okComboBox)
          {
            TComboBox * ComboBox = DebugNotNull(dynamic_cast<TComboBox *>(Control));
            Value = GetComboBoxValue(Control, ComboBox->Text);
          }
          else if (Option.Kind == TCustomCommandType::okCheckBox)
          {
            TCheckBox * CheckBox = DebugNotNull(dynamic_cast<TCheckBox *>(Control));
            int Index = (CheckBox->Checked ? 0 : 1);
            Value = (Index < static_cast<int>(Option.Params.size())) ? Option.Params[Index] : UnicodeString();
          }
          else
          {
            DebugFail();
          }

          // The default value setter deletes the "name" when the value is empty.
          // It would cause us to fall back to the default value, but we want to remember the empty value.
          if (Value.IsEmpty())
          {
            int Index = FCustomCommandOptions->IndexOfName(OptionKey);
            if (Index < 0)
            {
              Index = FCustomCommandOptions->Add(L"");
            }
            UnicodeString Line = OptionKey + FCustomCommandOptions->NameValueSeparator;
            FCustomCommandOptions->Strings[Index] = Line;
          }
          else
          {
            FCustomCommandOptions->Values[OptionKey] = Value;
          }
        }

        ControlIndex++;
      }
    }
  }

  return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomCommandOptionsDialog::GetComboBoxValue(
  TControl * Control, const UnicodeString & Default)
{
  TComboBox * ComboBox = DebugNotNull(dynamic_cast<TComboBox *>(Control));
  UnicodeString Result;
  if (ComboBox->ItemIndex < 0)
  {
    Result = Default;
  }
  else
  {
    Result = FValues[GetControlIndex(Control)][ComboBox->ItemIndex];
  }
  return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomCommandOptionsDialog::SaveHistoryComboBoxValue(
  TControl * Control, const TCustomCommandType::TOption & Option)
{
  THistoryComboBox * ComboBox = DebugNotNull(dynamic_cast<THistoryComboBox *>(Control));
  ComboBox->SaveToHistory();
  CustomWinConfiguration->History[HistoryKey(Option)] = ComboBox->Items;
  return ComboBox->Text;
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandOptionsDialog::DoHelp()
{
  UnicodeString HelpPage;
  if (!FCommand->OptionsPage.IsEmpty())
  {
    HelpPage = FCommand->OptionsPage;
  }
  else
  {
    HelpPage = FCommand->HomePage;
  }

  if (!HelpPage.IsEmpty())
  {
    OpenBrowser(HelpPage);
  }
  else
  {
    TCustomDialog::DoHelp();
  }
}
//---------------------------------------------------------------------------
void __fastcall TCustomCommandOptionsDialog::DoShow()
{
  TCustomDialog::DoShow();

  int ControlIndex = 0;
  for (int OptionIndex = 0; OptionIndex < FCommand->OptionsCount; OptionIndex++)
  {
    const TCustomCommandType::TOption & Option = FCommand->GetOption(OptionIndex);

    if ((Option.Flags & FFlags) != 0)
    {
      if (Option.Kind == TCustomCommandType::okFile)
      {
        TControl * Control = FControls[ControlIndex];
        InstallPathWordBreakProc(DebugNotNull(dynamic_cast<TWinControl *>(Control)));
      }
      ControlIndex++;
    }
  }
}
//---------------------------------------------------------------------------
bool __fastcall DoCustomCommandOptionsDialog(
  const TCustomCommandType * Command, TStrings * CustomCommandOptions,
  unsigned int Flags, TCustomCommand * CustomCommandForOptions,
  const UnicodeString & Site)
{
  std::unique_ptr<TCustomCommandOptionsDialog> Dialog(
    new TCustomCommandOptionsDialog(Command, CustomCommandOptions, Flags, CustomCommandForOptions, Site));
  return Dialog->Execute();
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class TUsageStatisticsDialog : public TCustomDialog
{
public:
  __fastcall TUsageStatisticsDialog();

protected:
  virtual void __fastcall DoChange(bool & CanSubmit);

private:
  TEdit * FilterEdit;
  TMemo * UsageMemo;
  TButton * ClipboardButton;

  void __fastcall ClipboardButtonClick(TObject * Sender);
};
//---------------------------------------------------------------------------
__fastcall TUsageStatisticsDialog::TUsageStatisticsDialog() :
  TCustomDialog(HELP_USAGE)
{
  Caption = LoadStr(USAGE_CAPTION);
  Width = ScaleByTextHeight(this, 400);

  TLabel * Label = new TLabel(this);
  // UnformatMessage is called, because previously, ** markup was used and translations may still contain that
  Label->Caption = UnformatMessage(LoadStr(USAGE_DATA2));
  AddText(Label);

  FilterEdit = new TEdit(this);
  FilterEdit->Width = ScaleByTextHeight(this, 250);
  TLabel * FilterLabel = new TLabel(this);
  FilterLabel->Caption = LoadStr(USAGE_FILTER);
  AddEdit(FilterEdit, FilterLabel, true);

  UsageMemo = new TMemo(this);
  UsageMemo->Height = ScaleByTextHeight(this, 300);
  UsageMemo->ScrollBars = ssVertical;
  AddEdit(UsageMemo, NULL);
  ReadOnlyControl(UsageMemo);

  ClipboardButton = new TButton(this);
  ClipboardButton->Caption = LoadStr(USAGE_COPY);
  ClipboardButton->Width = ScaleByTextHeight(this, 121);
  ClipboardButton->OnClick = ClipboardButtonClick;
  AddDialogButton(ClipboardButton);

  RemoveCancelButton();
}
//---------------------------------------------------------------------------
void __fastcall TUsageStatisticsDialog::ClipboardButtonClick(TObject * /*Sender*/)
{
  TInstantOperationVisualizer Visualizer;
  CopyToClipboard(UsageMemo->Lines);
}
//---------------------------------------------------------------------------
void __fastcall TUsageStatisticsDialog::DoChange(bool & CanSubmit)
{
  TCustomDialog::DoChange(CanSubmit);
  UnicodeString Text = Configuration->Usage->Serialize(L"\n", FilterEdit->Text);
  bool NoUsage = Text.IsEmpty();
  ClipboardButton->Enabled = !NoUsage;
  if (NoUsage)
  {
    Text = LoadStr(USAGE_DATA_NONE2);
  }
  UsageMemo->Lines->Text = Text;
}
//---------------------------------------------------------------------------
void __fastcall DoUsageStatisticsDialog()
{
  std::unique_ptr<TUsageStatisticsDialog> Dialog(new TUsageStatisticsDialog());
  Dialog->Execute();
}
