mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
1477 lines
50 KiB
C++
1477 lines
50 KiB
C++
//---------------------------------------------------------------------------
|
|
#include <WinPCH.h>
|
|
#pragma hdrstop
|
|
|
|
#include "ScpCommander.h"
|
|
#include "ScpExplorer.h"
|
|
|
|
#include <Cryptography.h>
|
|
#include "TerminalManager.h"
|
|
#include "ProgParams.h"
|
|
#include "Custom.h"
|
|
//---------------------------------------------------------------------------
|
|
const UnicodeString AppName = L"WinSCP";
|
|
//---------------------------------------------------------------------------
|
|
TConfiguration * __fastcall CreateConfiguration()
|
|
{
|
|
WinConfiguration = new TWinConfiguration();
|
|
CustomWinConfiguration = WinConfiguration;
|
|
GUIConfiguration = CustomWinConfiguration;
|
|
|
|
TProgramParams * Params = TProgramParams::Instance();
|
|
UnicodeString IniFileName = Params->SwitchValue(INI_SWITCH);
|
|
if (!IniFileName.IsEmpty())
|
|
{
|
|
if (SameText(IniFileName, INI_NUL))
|
|
{
|
|
WinConfiguration->SetNulStorage();
|
|
}
|
|
else if (CheckSafe(Params))
|
|
{
|
|
IniFileName = ExpandFileName(ExpandEnvironmentVariables(IniFileName));
|
|
WinConfiguration->SetExplicitIniFileStorageName(IniFileName);
|
|
}
|
|
}
|
|
|
|
if (CheckSafe(Params))
|
|
{
|
|
std::unique_ptr<TStrings> RawConfig(new TStringList());
|
|
if (Params->FindSwitch(RAW_CONFIG_SWITCH, RawConfig.get()))
|
|
{
|
|
WinConfiguration->OptionsStorage = RawConfig.get();
|
|
}
|
|
}
|
|
|
|
return WinConfiguration;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TOptions * __fastcall GetGlobalOptions()
|
|
{
|
|
return TProgramParams::Instance();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TCustomScpExplorerForm * __fastcall CreateScpExplorer()
|
|
{
|
|
TCustomScpExplorerForm * ScpExplorer;
|
|
if (WinConfiguration->Interface == ifExplorer)
|
|
{
|
|
ScpExplorer = SafeFormCreate<TScpExplorerForm>();
|
|
}
|
|
else
|
|
{
|
|
ScpExplorer = SafeFormCreate<TScpCommanderForm>();
|
|
}
|
|
return ScpExplorer;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall SshVersionString()
|
|
{
|
|
return FORMAT(L"WinSCP-release-%s", (Configuration->Version));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall AppNameString()
|
|
{
|
|
return L"WinSCP";
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall GetCompanyRegistryKey()
|
|
{
|
|
return L"Software\\Martin Prikryl";
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall GetRegistryKey()
|
|
{
|
|
return GetCompanyRegistryKey() + L"\\WinSCP 2";
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static bool ForcedOnForeground = false;
|
|
void __fastcall SetOnForeground(bool OnForeground)
|
|
{
|
|
ForcedOnForeground = OnForeground;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall FlashOnBackground()
|
|
{
|
|
DebugAssert(Application);
|
|
if ((WinConfiguration != NULL) && WinConfiguration->FlashTaskbar && !ForcedOnForeground && !ForegroundTask())
|
|
{
|
|
FlashWindow(Application->MainFormHandle, true);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall LocalSystemSettings(TForm * /*Control*/)
|
|
{
|
|
// noop
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall ShowExtendedException(Exception * E)
|
|
{
|
|
ShowExtendedExceptionEx(NULL, E);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TerminateApplication()
|
|
{
|
|
Application->Terminate();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
struct TOpenLocalPathHandler
|
|
{
|
|
UnicodeString LocalPath;
|
|
UnicodeString LocalFileName;
|
|
|
|
void __fastcall Open(TObject * Sender, unsigned int & /*Answer*/)
|
|
{
|
|
TButton * Button = DebugNotNull(dynamic_cast<TButton *>(Sender));
|
|
// Reason for separate AMenu variable is given in TPreferencesDialog::EditorFontColorButtonClick
|
|
TPopupMenu * AMenu = new TPopupMenu(Application);
|
|
// Popup menu has to survive the popup as TBX calls click handler asynchronously (post).
|
|
Menu.reset(AMenu);
|
|
TMenuItem * Item;
|
|
|
|
Item = new TMenuItem(Menu.get());
|
|
Menu->Items->Add(Item);
|
|
Item->Caption = LoadStr(OPEN_TARGET_FOLDER);
|
|
Item->OnClick = OpenFolderClick;
|
|
|
|
if (!LocalFileName.IsEmpty())
|
|
{
|
|
Item = new TMenuItem(Menu.get());
|
|
Menu->Items->Add(Item);
|
|
Item->Caption = LoadStr(OPEN_DOWNLOADED_FILE);
|
|
Item->OnClick = OpenFileClick;
|
|
}
|
|
|
|
MenuPopup(Menu.get(), Button);
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<TPopupMenu> Menu;
|
|
|
|
void __fastcall OpenFolderClick(TObject * /*Sender*/)
|
|
{
|
|
if (LocalFileName.IsEmpty())
|
|
{
|
|
OpenFolderInExplorer(LocalPath);
|
|
}
|
|
else
|
|
{
|
|
OpenFileInExplorer(LocalFileName);
|
|
}
|
|
}
|
|
|
|
void __fastcall OpenFileClick(TObject * /*Sender*/)
|
|
{
|
|
ExecuteShellChecked(LocalFileName, L"");
|
|
}
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
|
|
Exception * E)
|
|
{
|
|
bool Show = ShouldDisplayException(E);
|
|
bool DoNotDisplay = false;
|
|
|
|
try
|
|
{
|
|
// This is special case used particularly when called from .NET assembly
|
|
// (which always uses /nointeractiveinput),
|
|
// but can be useful for other console runs too
|
|
TProgramParams * Params = TProgramParams::Instance();
|
|
if (Params->FindSwitch(NOINTERACTIVEINPUT_SWITCH))
|
|
{
|
|
DoNotDisplay = true;
|
|
if (Show && CheckXmlLogParam(Params))
|
|
{
|
|
// The Started argument won't be used with .NET assembly, as it never uses patterns in XML log file name.
|
|
// But it theoretically can be used, when started manually.
|
|
std::unique_ptr<TActionLog> ActionLog(new TActionLog(Now(), Configuration));
|
|
ActionLog->AddFailure(E);
|
|
// unnecessary explicit release
|
|
ActionLog.reset(NULL);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception &)
|
|
{
|
|
// swallow
|
|
}
|
|
|
|
TTerminalManager * Manager = TTerminalManager::Instance(false);
|
|
if (!DoNotDisplay)
|
|
{
|
|
ESshTerminate * Terminate = dynamic_cast<ESshTerminate*>(E);
|
|
bool CloseOnCompletion = (Terminate != NULL);
|
|
|
|
EFatal * FatalException = dynamic_cast<EFatal *>(E);
|
|
bool ForActiveTerminal =
|
|
(FatalException != NULL) && (Terminal != NULL) &&
|
|
(Manager != NULL) && (Manager->ActiveTerminal == Terminal);
|
|
|
|
unsigned int Result;
|
|
if (CloseOnCompletion)
|
|
{
|
|
if (ForActiveTerminal)
|
|
{
|
|
DebugAssert(!Terminal->Active);
|
|
Manager->DisconnectActiveTerminal();
|
|
}
|
|
|
|
if (Terminate->Operation == odoSuspend)
|
|
{
|
|
// suspend, so that exit prompt is shown only after windows resume
|
|
SuspendWindows();
|
|
}
|
|
|
|
DebugAssert(Show);
|
|
bool ConfirmExitOnCompletion =
|
|
CloseOnCompletion &&
|
|
((Terminate->Operation == odoDisconnect) || (Terminate->Operation == odoSuspend)) &&
|
|
WinConfiguration->ConfirmExitOnCompletion;
|
|
|
|
if (ConfirmExitOnCompletion)
|
|
{
|
|
TMessageParams Params(mpNeverAskAgainCheck);
|
|
unsigned int Answers = 0;
|
|
TQueryButtonAlias Aliases[1];
|
|
TOpenLocalPathHandler OpenLocalPathHandler;
|
|
if (!Terminate->TargetLocalPath.IsEmpty() && !ForActiveTerminal)
|
|
{
|
|
OpenLocalPathHandler.LocalPath = Terminate->TargetLocalPath;
|
|
OpenLocalPathHandler.LocalFileName = Terminate->DestLocalFileName;
|
|
|
|
Aliases[0].Button = qaIgnore;
|
|
Aliases[0].Alias = LoadStr(OPEN_BUTTON);
|
|
Aliases[0].OnSubmit = OpenLocalPathHandler.Open;
|
|
Aliases[0].MenuButton = true;
|
|
Answers |= Aliases[0].Button;
|
|
Params.Aliases = Aliases;
|
|
Params.AliasesCount = std::size(Aliases);
|
|
}
|
|
|
|
if (ForActiveTerminal)
|
|
{
|
|
UnicodeString MessageFormat =
|
|
(Manager->Count > 1) ?
|
|
FMTLOAD(DISCONNECT_ON_COMPLETION, (Manager->Count - 1)) :
|
|
LoadStr(EXIT_ON_COMPLETION);
|
|
// Remove the leading "%s\n\n" (not to change the translation originals - previously the error message was prepended)
|
|
MessageFormat = FORMAT(MessageFormat, (UnicodeString())).Trim();
|
|
MessageFormat = MainInstructions(MessageFormat) + L"\n\n%s";
|
|
Result = FatalExceptionMessageDialog(E, qtInformation,
|
|
MessageFormat,
|
|
Answers | qaYes | qaNo, HELP_NONE, &Params);
|
|
}
|
|
else
|
|
{
|
|
Result =
|
|
ExceptionMessageDialog(E, qtInformation, L"", Answers | qaOK, HELP_NONE, &Params);
|
|
}
|
|
|
|
if (Result == qaNeverAskAgain)
|
|
{
|
|
Result = qaYes;
|
|
WinConfiguration->ConfirmExitOnCompletion = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = qaYes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Show)
|
|
{
|
|
if (ForActiveTerminal)
|
|
{
|
|
bool HookedDialog = false;
|
|
try
|
|
{
|
|
TMessageParams Params;
|
|
if (DebugAlwaysTrue(Manager->ActiveTerminal != NULL) &&
|
|
((Configuration->SessionReopenTimeout == 0) ||
|
|
(Manager->ActiveTerminal->ReopenStart == TDateTime()) ||
|
|
(int(double(Now() - Manager->ActiveTerminal->ReopenStart) * MSecsPerDay) < Configuration->SessionReopenTimeout)))
|
|
{
|
|
Params.Timeout = GUIConfiguration->SessionReopenAutoIdleOn ? GUIConfiguration->SessionReopenAutoIdle : 0;
|
|
Params.TimeoutAnswer = qaRetry;
|
|
Params.TimeoutResponse = Params.TimeoutAnswer;
|
|
HookedDialog = Manager->HookFatalExceptionMessageDialog(Params);
|
|
}
|
|
|
|
bool InactiveTerminationMessage = (FatalException != NULL) && FatalException->InactiveTerminationMessage;
|
|
if (InactiveTerminationMessage)
|
|
{
|
|
Params.Params |= mpNeverAskAgainCheck;
|
|
Params.NeverAskAgainTitle = LoadStr(ALWAYS_RECONNECT);
|
|
Params.NeverAskAgainAnswer = qaRetry;
|
|
}
|
|
|
|
Result = FatalExceptionMessageDialog(E, qtError, EmptyStr, qaOK, EmptyStr, &Params);
|
|
|
|
if ((Result == qaNeverAskAgain) && DebugAlwaysTrue(InactiveTerminationMessage))
|
|
{
|
|
GUIConfiguration->SessionReopenAutoInactive = true;
|
|
Result = qaRetry;
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (HookedDialog)
|
|
{
|
|
Manager->UnhookFatalExceptionMessageDialog();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = ExceptionMessageDialog(E, qtError);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = qaOK;
|
|
}
|
|
}
|
|
|
|
if (Result == qaYes)
|
|
{
|
|
DebugAssert(CloseOnCompletion);
|
|
DebugAssert(Terminate != NULL);
|
|
DebugAssert(Terminate->Operation != odoIdle);
|
|
TerminateApplication();
|
|
|
|
switch (Terminate->Operation)
|
|
{
|
|
case odoDisconnect:
|
|
break;
|
|
|
|
case odoSuspend:
|
|
// suspended before already
|
|
break;
|
|
|
|
case odoShutDown:
|
|
ShutDownWindows();
|
|
break;
|
|
|
|
default:
|
|
DebugFail();
|
|
}
|
|
}
|
|
else if (Result == qaRetry)
|
|
{
|
|
// qaRetry is used by FatalExceptionMessageDialog
|
|
if (DebugAlwaysTrue(ForActiveTerminal))
|
|
{
|
|
Manager->ReconnectActiveTerminal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ForActiveTerminal)
|
|
{
|
|
Manager->DisconnectActiveTerminalIfPermanentFreeOtherwise();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall ShowNotification(TTerminal * Terminal, const UnicodeString & Str,
|
|
TQueryType Type)
|
|
{
|
|
TTerminalManager * Manager = TTerminalManager::Instance(false);
|
|
DebugAssert(Manager != NULL);
|
|
|
|
Manager->ScpExplorer->PopupTrayBalloon(Terminal, Str, Type);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString GetThemeName(bool Dark)
|
|
{
|
|
return Dark ? L"DarkOfficeXP" : L"OfficeXP";
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall ConfigureInterface()
|
|
{
|
|
DebugAssert(WinConfiguration != NULL);
|
|
int BidiModeFlag =
|
|
AdjustLocaleFlag(LoadStr(BIDI_MODE), WinConfiguration->BidiModeOverride, false, bdRightToLeft, bdLeftToRight);
|
|
Application->BiDiMode = static_cast<TBiDiMode>(BidiModeFlag);
|
|
SetTBXSysParam(TSP_XPVISUALSTYLE, XPVS_AUTOMATIC);
|
|
UnicodeString Theme = GetThemeName(WinConfiguration->UseDarkTheme());
|
|
if (!SameText(TBXCurrentTheme(), Theme))
|
|
{
|
|
TBXSetTheme(Theme);
|
|
}
|
|
// Has any effect on Wine only
|
|
// (otherwise initial UserDocumentDirectory is equivalent to GetPersonalFolder())
|
|
UserDocumentDirectory = GetPersonalFolder();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall DoAboutDialog(TConfiguration *Configuration)
|
|
{
|
|
DoAboutDialog(Configuration, true, NULL);
|
|
}
|
|
//---------------------------------------------------------------------
|
|
void __fastcall DoProductLicense()
|
|
{
|
|
DoLicenseDialog(lcWinScp);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
const UnicodeString PixelsPerInchKey = L"PixelsPerInch";
|
|
//---------------------------------------------------------------------
|
|
int __fastcall GetToolbarLayoutPixelsPerInch(TStrings * Storage, TControl * Control)
|
|
{
|
|
int Result;
|
|
if (Storage->IndexOfName(PixelsPerInchKey))
|
|
{
|
|
Result = LoadPixelsPerInch(Storage->Values[PixelsPerInchKey], Control);
|
|
}
|
|
else
|
|
{
|
|
Result = -1;
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
UnicodeString __fastcall GetToolbarKey(const UnicodeString & ToolbarName)
|
|
{
|
|
UnicodeString Result = ToolbarName;
|
|
Result = RemoveSuffix(Result, L"Toolbar", true);
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------
|
|
static inline void __fastcall GetToolbarKey(const UnicodeString & ToolbarName,
|
|
const UnicodeString & Value, UnicodeString & ToolbarKey)
|
|
{
|
|
ToolbarKey = GetToolbarKey(ToolbarName);
|
|
if (!Value.IsEmpty())
|
|
{
|
|
ToolbarKey += L"_" + Value;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static int __fastcall ToolbarReadInt(const UnicodeString ToolbarName,
|
|
const UnicodeString Value, const int Default, const void * ExtraData)
|
|
{
|
|
int Result;
|
|
if (Value == L"Rev")
|
|
{
|
|
Result = 2000;
|
|
}
|
|
else
|
|
{
|
|
TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
|
|
UnicodeString ToolbarKey;
|
|
GetToolbarKey(ToolbarName, Value, ToolbarKey);
|
|
if (Storage->IndexOfName(ToolbarKey) >= 0)
|
|
{
|
|
Result = StrToIntDef(Storage->Values[ToolbarKey], Default);
|
|
#if 0
|
|
// this does not work well, as it scales down the stretched
|
|
// toolbars (path toolbars) too much, it has to be reimplemented smarter
|
|
if (Value == L"DockPos")
|
|
{
|
|
int PixelsPerInch = GetToolbarLayoutPixelsPerInch(Storage);
|
|
// When DPI has decreased since the last time, scale down
|
|
// toolbar position to get rid of gaps caused by smaller labels.
|
|
// Do not do this when DPI has increased as it would introduce gaps,
|
|
// as toolbars consists mostly of icons only, that do not scale.
|
|
// The toolbars shift themselves anyway, when other toolbars to the left
|
|
// get wider. We also risk a bit that toolbar order changes,
|
|
// as with very small toolbars (History) we can get scaled down position
|
|
// of the following toolbar to the left of it.
|
|
// There's special handling (also for scaling-up) stretched toolbars
|
|
// in LoadToolbarsLayoutStr
|
|
if ((PixelsPerInch > 0) && (Screen->PixelsPerInch < PixelsPerInch))
|
|
{
|
|
Result = LoadDimension(Result, PixelsPerInch);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Result = Default;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static UnicodeString __fastcall ToolbarReadString(const UnicodeString ToolbarName,
|
|
const UnicodeString Value, const UnicodeString Default, const void * ExtraData)
|
|
{
|
|
UnicodeString Result;
|
|
TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
|
|
UnicodeString ToolbarKey;
|
|
GetToolbarKey(ToolbarName, Value, ToolbarKey);
|
|
if (Storage->IndexOfName(ToolbarKey) >= 0)
|
|
{
|
|
Result = Storage->Values[ToolbarKey];
|
|
}
|
|
else
|
|
{
|
|
Result = Default;
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ToolbarWriteInt(const UnicodeString ToolbarName,
|
|
const UnicodeString Value, const int Data, const void * ExtraData)
|
|
{
|
|
DebugFail();
|
|
if (Value != L"Rev")
|
|
{
|
|
TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
|
|
UnicodeString ToolbarKey;
|
|
GetToolbarKey(ToolbarName, Value, ToolbarKey);
|
|
DebugAssert(Storage->IndexOfName(ToolbarKey) < 0);
|
|
Storage->Values[ToolbarKey] = IntToStr(Data);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ToolbarWriteString(const UnicodeString ToolbarName,
|
|
const UnicodeString Value, const UnicodeString Data, const void * ExtraData)
|
|
{
|
|
DebugAssert(Value.IsEmpty());
|
|
TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
|
|
UnicodeString ToolbarKey;
|
|
GetToolbarKey(ToolbarName, Value, ToolbarKey);
|
|
DebugAssert(Storage->IndexOfName(ToolbarKey) < 0);
|
|
Storage->Values[ToolbarKey] = Data;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall GetToolbarsLayoutStr(TControl * OwnerControl)
|
|
{
|
|
UnicodeString Result;
|
|
TStrings * Storage = new TStringList();
|
|
try
|
|
{
|
|
TBCustomSavePositions(OwnerControl, ToolbarWriteInt, ToolbarWriteString,
|
|
Storage);
|
|
Storage->Values[PixelsPerInchKey] = SavePixelsPerInch(OwnerControl);
|
|
Result = Storage->CommaText;
|
|
}
|
|
__finally
|
|
{
|
|
delete Storage;
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall LoadToolbarsLayoutStr(TControl * OwnerControl, UnicodeString LayoutStr)
|
|
{
|
|
TStrings * Storage = CommaTextToStringList(LayoutStr);
|
|
try
|
|
{
|
|
TBCustomLoadPositions(OwnerControl, ToolbarReadInt, ToolbarReadString,
|
|
Storage);
|
|
int PixelsPerInch = GetToolbarLayoutPixelsPerInch(Storage, OwnerControl);
|
|
// Scale toolbars stretched to the first other toolbar to the right
|
|
if ((PixelsPerInch > 0) && (PixelsPerInch != GetControlPixelsPerInch(OwnerControl))) // optimization
|
|
{
|
|
for (int Index = 0; Index < OwnerControl->ComponentCount; Index++)
|
|
{
|
|
TTBXToolbar * Toolbar =
|
|
dynamic_cast<TTBXToolbar *>(OwnerControl->Components[Index]);
|
|
if ((Toolbar != NULL) && Toolbar->Stretch &&
|
|
(Toolbar->OnGetBaseSize != NULL) &&
|
|
// we do not support floating of stretched toolbars
|
|
DebugAlwaysTrue(!Toolbar->Floating))
|
|
{
|
|
TTBXToolbar * FollowingToolbar = NULL;
|
|
for (int Index2 = 0; Index2 < OwnerControl->ComponentCount; Index2++)
|
|
{
|
|
TTBXToolbar * Toolbar2 =
|
|
dynamic_cast<TTBXToolbar *>(OwnerControl->Components[Index2]);
|
|
if ((Toolbar2 != NULL) && !Toolbar2->Floating &&
|
|
(Toolbar2->Parent == Toolbar->Parent) &&
|
|
(Toolbar2->DockRow == Toolbar->DockRow) &&
|
|
(Toolbar2->DockPos > Toolbar->DockPos) &&
|
|
((FollowingToolbar == NULL) || (FollowingToolbar->DockPos > Toolbar2->DockPos)))
|
|
{
|
|
FollowingToolbar = Toolbar2;
|
|
}
|
|
}
|
|
|
|
if (FollowingToolbar != NULL)
|
|
{
|
|
int NewWidth = LoadDimension(Toolbar->Width, PixelsPerInch, Toolbar);
|
|
FollowingToolbar->DockPos += NewWidth - Toolbar->Width;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
delete Storage;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TTBXSeparatorItem * __fastcall AddMenuSeparator(TTBCustomItem * Menu)
|
|
{
|
|
TTBXSeparatorItem * Item = new TTBXSeparatorItem(Menu);
|
|
Menu->Add(Item);
|
|
return Item;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static TComponent * LastPopupComponent = NULL;
|
|
static TRect LastPopupRect(-1, -1, -1, -1);
|
|
static TDateTime LastCloseUp;
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ConvertMenu(TMenuItem * AItems, TTBCustomItem * Items, TBasicAction * ParentAction, TTBCustomItem *& ParentActionItem)
|
|
{
|
|
ParentActionItem = NULL;
|
|
for (int Index = 0; Index < AItems->Count; Index++)
|
|
{
|
|
TMenuItem * AItem = AItems->Items[Index];
|
|
TTBCustomItem * Item;
|
|
|
|
if (!AItem->Enabled && !AItem->Visible && (AItem->Action == NULL) &&
|
|
(AItem->OnClick == NULL) && DebugAlwaysTrue(AItem->Count == 0))
|
|
{
|
|
TTBXLabelItem * LabelItem = new TTBXLabelItem(Items->Owner);
|
|
// TTBXLabelItem has its own Caption
|
|
LabelItem->Caption = AItem->Caption;
|
|
LabelItem->SectionHeader = true;
|
|
Item = LabelItem;
|
|
}
|
|
else
|
|
{
|
|
TTBXSubmenuItem * SubmenuItem = NULL;
|
|
// see TB2DsgnConverter.pas DoConvert
|
|
if (AItem->Caption == L"-")
|
|
{
|
|
Item = new TTBXSeparatorItem(Items->Owner);
|
|
}
|
|
else
|
|
{
|
|
if (AItem->Count > 0)
|
|
{
|
|
SubmenuItem = new TTBXSubmenuItem(Items->Owner);
|
|
Item = SubmenuItem;
|
|
}
|
|
else
|
|
{
|
|
Item = new TTBXItem(Items->Owner);
|
|
}
|
|
Item->Action = AItem->Action;
|
|
if ((ParentAction != NULL) && (Item->Action == ParentAction))
|
|
{
|
|
ParentActionItem = Item;
|
|
}
|
|
Item->AutoCheck = AItem->AutoCheck;
|
|
Item->Caption = AItem->Caption;
|
|
Item->Checked = AItem->Checked;
|
|
if (AItem->Default)
|
|
{
|
|
Item->Options = Item->Options << tboDefault;
|
|
}
|
|
Item->Enabled = AItem->Enabled;
|
|
Item->GroupIndex = AItem->GroupIndex;
|
|
Item->HelpContext = AItem->HelpContext;
|
|
Item->ImageIndex = AItem->ImageIndex;
|
|
Item->RadioItem = AItem->RadioItem;
|
|
Item->ShortCut = AItem->ShortCut;
|
|
Item->SubMenuImages = AItem->SubMenuImages;
|
|
Item->OnClick = AItem->OnClick;
|
|
}
|
|
Item->Hint = AItem->Hint;
|
|
Item->Tag = AItem->Tag;
|
|
Item->Visible = AItem->Visible;
|
|
|
|
if (AItem->Count > 0)
|
|
{
|
|
TTBCustomItem * ActionItem;
|
|
ConvertMenu(AItem, Item, AItem->Action, ActionItem);
|
|
if ((AItem->Action != NULL) && (ActionItem != NULL) && DebugAlwaysTrue(SubmenuItem != NULL))
|
|
{
|
|
SubmenuItem->DropdownCombo = true;
|
|
TTBItemOptions Options = ActionItem->Options;
|
|
ActionItem->Options = Options << tboDefault;
|
|
SubmenuItem->DropdownCombo = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Items->Add(Item);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall MenuPopup(TPopupMenu * AMenu, TRect Rect,
|
|
TComponent * PopupComponent)
|
|
{
|
|
// Pressing the same button within 200ms after closing its popup menu
|
|
// does nothing.
|
|
// It is to immitate close-by-click behavior. Note that menu closes itself
|
|
// before onclick handler of button occurs.
|
|
// To support content menu popups, we have to check for the popup location too,
|
|
// to allow poping menu on different location (such as different node of TTreeView),
|
|
// even if there's another popup opened already (so that the time interval
|
|
// below does not elapse).
|
|
TDateTime N = Now();
|
|
TDateTime Diff = N - LastCloseUp;
|
|
if ((PopupComponent == LastPopupComponent) &&
|
|
(Rect == LastPopupRect) &&
|
|
(Diff < TDateTime(0, 0, 0, 200)))
|
|
{
|
|
LastPopupComponent = NULL;
|
|
}
|
|
else
|
|
{
|
|
TTBXPopupMenu * Menu = dynamic_cast<TTBXPopupMenu *>(AMenu);
|
|
if (Menu == NULL)
|
|
{
|
|
Menu = CreateTBXPopupMenu(AMenu->Owner);
|
|
Menu->OnPopup = AMenu->OnPopup;
|
|
Menu->Items->SubMenuImages = AMenu->Images;
|
|
|
|
TTBCustomItem * Dummy;
|
|
ConvertMenu(AMenu->Items, Menu->Items, NULL, Dummy);
|
|
}
|
|
|
|
Menu->PopupComponent = PopupComponent;
|
|
Menu->PopupEx(Rect);
|
|
|
|
LastPopupComponent = PopupComponent;
|
|
LastPopupRect = Rect;
|
|
LastCloseUp = Now();
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
const int ColorCols = 8;
|
|
const int StandardColorRows = 2;
|
|
const int StandardColorCount = ColorCols * StandardColorRows;
|
|
const int UserColorRows = 1;
|
|
const int UserColorCount = UserColorRows * ColorCols;
|
|
const wchar_t ColorSeparator = L',';
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall GetStandardSessionColorInfo(
|
|
int Col, int Row, TColor & Color, UnicodeString & Name)
|
|
{
|
|
#define COLOR_INFO(COL, ROW, NAME, COLOR) \
|
|
if ((Col == COL) && (Row == ROW)) { Name = NAME; Color = TColor(COLOR); } else
|
|
// bottom row of default TBX color set
|
|
COLOR_INFO(0, 0, L"Rose", 0xCC99FF)
|
|
COLOR_INFO(1, 0, L"Tan", 0x99CCFF)
|
|
COLOR_INFO(2, 0, L"Light Yellow", 0x99FFFF)
|
|
COLOR_INFO(3, 0, L"Light Green", 0xCCFFCC)
|
|
COLOR_INFO(4, 0, L"Light Turquoise", 0xFFFFCC)
|
|
COLOR_INFO(5, 0, L"Pale Blue", 0xFFCC99)
|
|
COLOR_INFO(6, 0, L"Lavender", 0xFF99CC)
|
|
|
|
// second row of Excel 2010 palette with second color (Lighter Black) skipped
|
|
COLOR_INFO(7, 0, L"Light Orange", 0xB5D5FB)
|
|
|
|
COLOR_INFO(0, 1, L"Darker White", 0xD8D8D8)
|
|
COLOR_INFO(1, 1, L"Darker Tan", 0x97BDC4)
|
|
COLOR_INFO(2, 1, L"Lighter Blue", 0xE2B38D)
|
|
COLOR_INFO(3, 1, L"Light Blue", 0xE4CCB8)
|
|
COLOR_INFO(4, 1, L"Lighter Red", 0xB7B9E5)
|
|
COLOR_INFO(5, 1, L"Light Olive Green", 0xBCE3D7)
|
|
COLOR_INFO(6, 1, L"Light Purple", 0xD9C1CC)
|
|
COLOR_INFO(7, 1, L"Light Aqua", 0xE8DDB7)
|
|
|
|
DebugFail();
|
|
#undef COLOR_INFO
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall SessionColorSetGetColorInfo(
|
|
void * /*Data*/, TTBXCustomColorSet * /*Sender*/, int Col, int Row, TColor & Color, UnicodeString & Name)
|
|
{
|
|
GetStandardSessionColorInfo(Col, Row, Color, Name);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TColor __fastcall RestoreColor(const UnicodeString & CStr)
|
|
{
|
|
return TColor(StrToInt(UnicodeString(L"$") + CStr));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
UnicodeString __fastcall StoreColor(TColor Color)
|
|
{
|
|
return IntToHex(Color, 6);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static UnicodeString __fastcall ExtractColorStr(UnicodeString & Colors)
|
|
{
|
|
return CutToChar(Colors, ColorSeparator, true);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static bool __fastcall IsStandardColor(bool SessionColors, TColor Color)
|
|
{
|
|
if (SessionColors)
|
|
{
|
|
for (int Row = 0; Row < StandardColorRows; Row++)
|
|
{
|
|
for (int Col = 0; Col < ColorCols; Col++)
|
|
{
|
|
TColor StandardColor;
|
|
UnicodeString Name; // unused
|
|
GetStandardSessionColorInfo(Col, Row, StandardColor, Name);
|
|
if (StandardColor == Color)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
std::unique_ptr<TTBXColorPalette> DefaultColorPalette(new TTBXColorPalette(NULL));
|
|
return (DefaultColorPalette->FindCell(Color).X >= 0);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
class TColorChangeData : public TComponent
|
|
{
|
|
public:
|
|
__fastcall TColorChangeData(TColorChangeEvent OnColorChange, TColor Color, bool SessionColors);
|
|
|
|
static TColorChangeData * __fastcall Retrieve(TObject * Object);
|
|
|
|
void __fastcall ColorChange(TColor Color);
|
|
|
|
__property TColor Color = { read = FColor };
|
|
|
|
__property bool SessionColors = { read = FSessionColors };
|
|
|
|
private:
|
|
TColorChangeEvent FOnColorChange;
|
|
TColor FColor;
|
|
bool FSessionColors;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
__fastcall TColorChangeData::TColorChangeData(
|
|
TColorChangeEvent OnColorChange, TColor Color, bool SessionColors) :
|
|
TComponent(NULL)
|
|
{
|
|
Name = QualifiedClassName();
|
|
FOnColorChange = OnColorChange;
|
|
FColor = Color;
|
|
FSessionColors = SessionColors;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TColorChangeData * __fastcall TColorChangeData::Retrieve(TObject * Object)
|
|
{
|
|
TComponent * Component = DebugNotNull(dynamic_cast<TComponent *>(Object));
|
|
TComponent * ColorChangeDataComponent = Component->FindComponent(QualifiedClassName());
|
|
return DebugNotNull(dynamic_cast<TColorChangeData *>(ColorChangeDataComponent));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void SaveCustomColors(bool SessionColors, const UnicodeString & Colors)
|
|
{
|
|
if (SessionColors)
|
|
{
|
|
CustomWinConfiguration->SessionColors = Colors;
|
|
}
|
|
else
|
|
{
|
|
CustomWinConfiguration->FontColors = Colors;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static UnicodeString LoadCustomColors(bool SessionColors)
|
|
{
|
|
return SessionColors ? CustomWinConfiguration->SessionColors : CustomWinConfiguration->FontColors;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TColorChangeData::ColorChange(TColor Color)
|
|
{
|
|
// Color palette returns clNone when no color is selected,
|
|
// though it should not really happen.
|
|
// See also CreateColorPalette
|
|
if (Color == Vcl::Graphics::clNone)
|
|
{
|
|
Color = TColor(0);
|
|
}
|
|
|
|
if ((Color != TColor(0)) &&
|
|
!IsStandardColor(SessionColors, Color))
|
|
{
|
|
UnicodeString Colors = StoreColor(Color);
|
|
UnicodeString Temp = LoadCustomColors(SessionColors);
|
|
while (!Temp.IsEmpty())
|
|
{
|
|
UnicodeString CStr = ExtractColorStr(Temp);
|
|
if (RestoreColor(CStr) != Color)
|
|
{
|
|
Colors += UnicodeString(ColorSeparator) + CStr;
|
|
}
|
|
}
|
|
SaveCustomColors(SessionColors, Colors);
|
|
}
|
|
|
|
FOnColorChange(Color);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ColorDefaultClick(void * /*Data*/, TObject * Sender)
|
|
{
|
|
TColorChangeData::Retrieve(Sender)->ColorChange(TColor(0));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ColorPaletteChange(void * /*Data*/, TObject * Sender)
|
|
{
|
|
TTBXColorPalette * ColorPalette = DebugNotNull(dynamic_cast<TTBXColorPalette *>(Sender));
|
|
TColorChangeData::Retrieve(Sender)->ColorChange(GetNonZeroColor(ColorPalette->Color));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static UnicodeString __fastcall CustomColorName(int Index)
|
|
{
|
|
return UnicodeString(L"Color") + wchar_t(L'A' + Index);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall ColorPickClick(void * /*Data*/, TObject * Sender)
|
|
{
|
|
TColorChangeData * ColorChangeData = TColorChangeData::Retrieve(Sender);
|
|
|
|
std::unique_ptr<TColorDialog> Dialog(new TColorDialog(Application));
|
|
Dialog->Options = Dialog->Options << cdFullOpen << cdAnyColor;
|
|
Dialog->Color = (ColorChangeData->Color != 0 ? ColorChangeData->Color : clSkyBlue);
|
|
|
|
UnicodeString Temp = LoadCustomColors(ColorChangeData->SessionColors);
|
|
int StandardColorIndex = 0;
|
|
for (int Index = 0; Index < MaxCustomColors; Index++)
|
|
{
|
|
TColor CustomColor;
|
|
if (!Temp.IsEmpty())
|
|
{
|
|
CustomColor = RestoreColor(ExtractColorStr(Temp));
|
|
}
|
|
else
|
|
{
|
|
if (ColorChangeData->SessionColors)
|
|
{
|
|
if (StandardColorIndex < StandardColorCount)
|
|
{
|
|
UnicodeString Name; // not used
|
|
GetStandardSessionColorInfo(
|
|
StandardColorIndex % ColorCols, StandardColorIndex / ColorCols,
|
|
CustomColor, Name);
|
|
StandardColorIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no standard font colors
|
|
break;
|
|
}
|
|
}
|
|
Dialog->CustomColors->Values[CustomColorName(Index)] = StoreColor(CustomColor);
|
|
}
|
|
|
|
if (Dialog->Execute())
|
|
{
|
|
// so that we do not have to try to preserve the excess colors
|
|
DebugAssert(UserColorCount <= MaxCustomColors);
|
|
UnicodeString Colors;
|
|
for (int Index = 0; Index < MaxCustomColors; Index++)
|
|
{
|
|
UnicodeString CStr = Dialog->CustomColors->Values[CustomColorName(Index)];
|
|
if (!CStr.IsEmpty())
|
|
{
|
|
TColor CustomColor = RestoreColor(CStr);
|
|
if ((CustomColor != static_cast<TColor>(-1)) &&
|
|
!IsStandardColor(ColorChangeData->SessionColors, CustomColor))
|
|
{
|
|
AddToList(Colors, StoreColor(CustomColor), ColorSeparator);
|
|
}
|
|
}
|
|
}
|
|
SaveCustomColors(ColorChangeData->SessionColors, Colors);
|
|
|
|
// call color change only after copying custom colors back,
|
|
// so that it can add selected color to the user list
|
|
ColorChangeData->ColorChange(GetNonZeroColor(Dialog->Color));
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TPopupMenu * __fastcall CreateSessionColorPopupMenu(TColor Color,
|
|
TColorChangeEvent OnColorChange)
|
|
{
|
|
std::unique_ptr<TTBXPopupMenu> PopupMenu(new TTBXPopupMenu(Application));
|
|
CreateSessionColorMenu(PopupMenu->Items, Color, OnColorChange);
|
|
return PopupMenu.release();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall UserCustomColorSetGetColorInfo(
|
|
void * /*Data*/, TTBXCustomColorSet * Sender, int Col, int Row, TColor & Color, UnicodeString & /*Name*/)
|
|
{
|
|
int Index = (Row * Sender->ColCount) + Col;
|
|
bool SessionColors = static_cast<bool>(Sender->Tag);
|
|
UnicodeString Temp = LoadCustomColors(SessionColors);
|
|
while ((Index > 0) && !Temp.IsEmpty())
|
|
{
|
|
ExtractColorStr(Temp);
|
|
Index--;
|
|
}
|
|
|
|
if (!Temp.IsEmpty())
|
|
{
|
|
Color = RestoreColor(ExtractColorStr(Temp));
|
|
}
|
|
else
|
|
{
|
|
// hide the trailing cells
|
|
Color = Vcl::Graphics::clNone;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CreateColorPalette(TTBCustomItem * Owner, TColor Color, int Rows,
|
|
TCSGetColorInfo OnGetColorInfo, TColorChangeEvent OnColorChange, bool SessionColors)
|
|
{
|
|
TTBXColorPalette * ColorPalette = new TTBXColorPalette(Owner);
|
|
|
|
if (OnGetColorInfo != NULL)
|
|
{
|
|
TTBXCustomColorSet * ColorSet = new TTBXCustomColorSet(Owner);
|
|
ColorPalette->InsertComponent(ColorSet);
|
|
ColorPalette->ColorSet = ColorSet;
|
|
|
|
// has to be set only after it's assigned to color palette
|
|
ColorSet->ColCount = ColorCols;
|
|
ColorSet->RowCount = Rows;
|
|
ColorSet->OnGetColorInfo = OnGetColorInfo;
|
|
ColorSet->Tag = static_cast<int>(SessionColors);
|
|
}
|
|
|
|
// clNone = no selection, see also ColorChange
|
|
ColorPalette->Color = (Color != 0) ? Color : Vcl::Graphics::clNone;
|
|
ColorPalette->OnChange = MakeMethod<TNotifyEvent>(NULL, ColorPaletteChange);
|
|
ColorPalette->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
|
|
Owner->Add(ColorPalette);
|
|
|
|
Owner->Add(new TTBXSeparatorItem(Owner));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static void __fastcall CreateColorMenu(TComponent * AOwner, TColor Color,
|
|
TColorChangeEvent OnColorChange, bool SessionColors,
|
|
const UnicodeString & DefaultColorCaption, const UnicodeString & DefaultColorHint,
|
|
const UnicodeString & HelpKeyword,
|
|
const UnicodeString & ColorPickHint)
|
|
{
|
|
TTBCustomItem * Owner = dynamic_cast<TTBCustomItem *>(AOwner);
|
|
if (DebugAlwaysTrue(Owner != NULL))
|
|
{
|
|
Owner->Clear();
|
|
|
|
TTBCustomItem * Item;
|
|
|
|
Item = new TTBXItem(Owner);
|
|
Item->Caption = DefaultColorCaption;
|
|
Item->Hint = DefaultColorHint;
|
|
Item->HelpKeyword = HelpKeyword;
|
|
Item->OnClick = MakeMethod<TNotifyEvent>(NULL, ColorDefaultClick);
|
|
Item->Checked = (Color == TColor(0));
|
|
Item->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
|
|
Owner->Add(Item);
|
|
|
|
Owner->Add(new TTBXSeparatorItem(Owner));
|
|
|
|
int CustomColorCount = 0;
|
|
UnicodeString Temp = LoadCustomColors(SessionColors);
|
|
while (!Temp.IsEmpty())
|
|
{
|
|
CustomColorCount++;
|
|
ExtractColorStr(Temp);
|
|
}
|
|
|
|
if (CustomColorCount > 0)
|
|
{
|
|
CustomColorCount = Min(CustomColorCount, UserColorCount);
|
|
int RowCount = ((CustomColorCount + ColorCols - 1) / ColorCols);
|
|
DebugAssert(RowCount <= UserColorRows);
|
|
|
|
CreateColorPalette(Owner, Color, RowCount,
|
|
MakeMethod<TCSGetColorInfo>(NULL, UserCustomColorSetGetColorInfo),
|
|
OnColorChange, SessionColors);
|
|
}
|
|
|
|
if (SessionColors)
|
|
{
|
|
CreateColorPalette(Owner, Color, StandardColorRows,
|
|
MakeMethod<TCSGetColorInfo>(NULL, SessionColorSetGetColorInfo),
|
|
OnColorChange, SessionColors);
|
|
}
|
|
else
|
|
{
|
|
CreateColorPalette(Owner, Color, -1, NULL, OnColorChange, SessionColors);
|
|
}
|
|
|
|
Owner->Add(new TTBXSeparatorItem(Owner));
|
|
|
|
Item = new TTBXItem(Owner);
|
|
Item->Caption = LoadStr(COLOR_PICK_CAPTION);
|
|
Item->Hint = ColorPickHint;
|
|
Item->HelpKeyword = HelpKeyword;
|
|
Item->OnClick = MakeMethod<TNotifyEvent>(NULL, ColorPickClick);
|
|
Item->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
|
|
Owner->Add(Item);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CreateSessionColorMenu(TComponent * AOwner, TColor Color,
|
|
TColorChangeEvent OnColorChange)
|
|
{
|
|
CreateColorMenu(
|
|
AOwner, Color, OnColorChange, true,
|
|
LoadStr(COLOR_TRUE_DEFAULT_CAPTION), LoadStr(COLOR_DEFAULT_HINT),
|
|
HELP_COLOR, LoadStr(COLOR_PICK_HINT));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CreateEditorBackgroundColorMenu(TComponent * AOwner, TColor Color,
|
|
TColorChangeEvent OnColorChange)
|
|
{
|
|
CreateColorMenu(
|
|
AOwner, Color, OnColorChange, true,
|
|
LoadStr(COLOR_TRUE_DEFAULT_CAPTION), LoadStr(EDITOR_BACKGROUND_COLOR_HINT),
|
|
HELP_COLOR, LoadStr(EDITOR_BACKGROUND_COLOR_PICK_HINT));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TPopupMenu * __fastcall CreateColorPopupMenu(TColor Color,
|
|
TColorChangeEvent OnColorChange)
|
|
{
|
|
std::unique_ptr<TTBXPopupMenu> PopupMenu(new TTBXPopupMenu(Application));
|
|
CreateColorMenu(
|
|
PopupMenu->Items, Color, OnColorChange, false,
|
|
LoadStr(COLOR_TRUE_DEFAULT_CAPTION), UnicodeString(),
|
|
HELP_NONE, UnicodeString());
|
|
return PopupMenu.release();
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
struct TThreadParam
|
|
{
|
|
TThreadFunc ThreadFunc;
|
|
void * Parameter;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
static int __fastcall ThreadProc(void * AParam)
|
|
{
|
|
TThreadParam * Param = reinterpret_cast<TThreadParam *>(AParam);
|
|
unsigned int Result = Param->ThreadFunc(Param->Parameter);
|
|
delete Param;
|
|
EndThread(Result);
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
int __fastcall StartThread(void * SecurityAttributes, unsigned StackSize,
|
|
TThreadFunc ThreadFunc, void * Parameter, unsigned CreationFlags,
|
|
TThreadID & ThreadId)
|
|
{
|
|
TThreadParam * Param = new TThreadParam;
|
|
Param->ThreadFunc = ThreadFunc;
|
|
Param->Parameter = Parameter;
|
|
return BeginThread(SecurityAttributes, StackSize, ThreadProc, Param,
|
|
CreationFlags, ThreadId);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static TShortCut FirstCtrlNumberShortCut = ShortCut(L'0', TShiftState() << ssCtrl);
|
|
static TShortCut LastCtrlNumberShortCut = ShortCut(L'9', TShiftState() << ssCtrl);
|
|
static TShortCut FirstCtrlKeyPadShortCut = ShortCut(VK_NUMPAD0, TShiftState() << ssCtrl);
|
|
static TShortCut LastCtrlKeyPadShortCut = ShortCut(VK_NUMPAD9, TShiftState() << ssCtrl);
|
|
static TShortCut FirstShiftCtrlAltLetterShortCut = ShortCut(L'A', TShiftState() << ssShift << ssCtrl << ssAlt);
|
|
static TShortCut LastShiftCtrlAltLetterShortCut = ShortCut(L'Z', TShiftState() << ssShift << ssCtrl << ssAlt);
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall InitializeShortCutCombo(TComboBox * ComboBox,
|
|
const TShortCuts & ShortCuts)
|
|
{
|
|
ComboBox->Items->BeginUpdate();
|
|
try
|
|
{
|
|
ComboBox->Items->Clear();
|
|
|
|
ComboBox->Items->AddObject(LoadStr(SHORTCUT_NONE), reinterpret_cast<TObject* >(0));
|
|
|
|
for (TShortCut AShortCut = FirstCtrlNumberShortCut; AShortCut <= LastCtrlNumberShortCut; AShortCut++)
|
|
{
|
|
if (!ShortCuts.Has(AShortCut))
|
|
{
|
|
ComboBox->Items->AddObject(ShortCutToText(AShortCut), reinterpret_cast<TObject* >(AShortCut));
|
|
}
|
|
}
|
|
|
|
for (TShortCut AShortCut = FirstShiftCtrlAltLetterShortCut; AShortCut <= LastShiftCtrlAltLetterShortCut; AShortCut++)
|
|
{
|
|
if (!ShortCuts.Has(AShortCut))
|
|
{
|
|
ComboBox->Items->AddObject(ShortCutToText(AShortCut), reinterpret_cast<TObject* >(AShortCut));
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
ComboBox->Items->EndUpdate();
|
|
}
|
|
|
|
ComboBox->Style = csDropDownList;
|
|
ComboBox->DropDownCount = Max(ComboBox->DropDownCount, DefaultHistoryDropDownCount);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall SetShortCutCombo(TComboBox * ComboBox, TShortCut Value)
|
|
{
|
|
for (int Index = ComboBox->Items->Count - 1; Index >= 0; Index--)
|
|
{
|
|
TShortCut AShortCut = TShortCut(ComboBox->Items->Objects[Index]);
|
|
if (AShortCut == Value)
|
|
{
|
|
ComboBox->ItemIndex = Index;
|
|
break;
|
|
}
|
|
else if (AShortCut < Value)
|
|
{
|
|
DebugAssert(Value != 0);
|
|
ComboBox->Items->InsertObject(Index + 1, ShortCutToText(Value),
|
|
reinterpret_cast<TObject* >(Value));
|
|
ComboBox->ItemIndex = Index + 1;
|
|
break;
|
|
}
|
|
DebugAssert(Index > 0);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TShortCut __fastcall GetShortCutCombo(TComboBox * ComboBox)
|
|
{
|
|
return TShortCut(ComboBox->Items->Objects[ComboBox->ItemIndex]);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TShortCut __fastcall NormalizeCustomShortCut(TShortCut ShortCut)
|
|
{
|
|
if ((FirstCtrlKeyPadShortCut <= ShortCut) && (ShortCut <= LastCtrlKeyPadShortCut))
|
|
{
|
|
ShortCut = FirstCtrlNumberShortCut + (ShortCut - FirstCtrlKeyPadShortCut);
|
|
}
|
|
return ShortCut;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall IsCustomShortCut(TShortCut ShortCut)
|
|
{
|
|
return
|
|
((FirstCtrlNumberShortCut <= ShortCut) && (ShortCut <= LastCtrlNumberShortCut)) ||
|
|
((FirstShiftCtrlAltLetterShortCut <= ShortCut) && (ShortCut <= LastShiftCtrlAltLetterShortCut));
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
class TMasterPasswordDialog : public TCustomDialog
|
|
{
|
|
public:
|
|
__fastcall TMasterPasswordDialog(TComponent * AOwner);
|
|
|
|
void Init(bool Current);
|
|
bool __fastcall Execute(UnicodeString & CurrentPassword, UnicodeString & NewPassword);
|
|
|
|
protected:
|
|
virtual void __fastcall DoValidate();
|
|
virtual void __fastcall DoChange(bool & CanSubmit);
|
|
|
|
private:
|
|
TEdit * CurrentEdit;
|
|
TEdit * NewEdit;
|
|
TEdit * ConfirmEdit;
|
|
|
|
TEdit * CreatePasswordEdit(int Label);
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
// Need to have an Owner argument for SafeFormCreate
|
|
__fastcall TMasterPasswordDialog::TMasterPasswordDialog(TComponent *) :
|
|
TCustomDialog(EmptyStr)
|
|
{
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
TEdit * TMasterPasswordDialog::CreatePasswordEdit(int Label)
|
|
{
|
|
TEdit * Result = new TEdit(this);
|
|
SetEditPasswordMode(Result);
|
|
AddEdit(Result, CreateLabel(LoadStr(Label)));
|
|
Result->MaxLength = PasswordMaxLength();
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void TMasterPasswordDialog::Init(bool Current)
|
|
{
|
|
HelpKeyword = Current ? HELP_MASTER_PASSWORD_CURRENT : HELP_MASTER_PASSWORD_CHANGE;
|
|
Caption = LoadStr(MASTER_PASSWORD_CAPTION);
|
|
|
|
CurrentEdit = CreatePasswordEdit(MASTER_PASSWORD_CURRENT);
|
|
EnableControl(CurrentEdit, Current || WinConfiguration->UseMasterPassword);
|
|
|
|
if (!Current)
|
|
{
|
|
NewEdit = CreatePasswordEdit(MASTER_PASSWORD_NEW);
|
|
|
|
if (!WinConfiguration->UseMasterPassword)
|
|
{
|
|
ActiveControl = NewEdit;
|
|
}
|
|
|
|
ConfirmEdit = CreatePasswordEdit(MASTER_PASSWORD_CONFIRM);
|
|
}
|
|
else
|
|
{
|
|
NewEdit = NULL;
|
|
ConfirmEdit = NULL;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall TMasterPasswordDialog::Execute(
|
|
UnicodeString & CurrentPassword, UnicodeString & NewPassword)
|
|
{
|
|
bool Result = TCustomDialog::Execute();
|
|
if (Result)
|
|
{
|
|
if (CurrentEdit->Enabled)
|
|
{
|
|
CurrentPassword = CurrentEdit->Text;
|
|
}
|
|
if (NewEdit != NULL)
|
|
{
|
|
NewPassword = NewEdit->Text;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TMasterPasswordDialog::DoChange(bool & CanSubmit)
|
|
{
|
|
CanSubmit =
|
|
(!WinConfiguration->UseMasterPassword || (IsValidPassword(CurrentEdit->Text) >= 0)) &&
|
|
((NewEdit == NULL) || (IsValidPassword(NewEdit->Text) >= 0)) &&
|
|
((ConfirmEdit == NULL) || (IsValidPassword(ConfirmEdit->Text) >= 0));
|
|
TCustomDialog::DoChange(CanSubmit);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall TMasterPasswordDialog::DoValidate()
|
|
{
|
|
TCustomDialog::DoValidate();
|
|
|
|
if (WinConfiguration->UseMasterPassword &&
|
|
!WinConfiguration->ValidateMasterPassword(CurrentEdit->Text))
|
|
{
|
|
CurrentEdit->SetFocus();
|
|
CurrentEdit->SelectAll();
|
|
throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_INCORRECT)));
|
|
}
|
|
|
|
if (NewEdit != NULL)
|
|
{
|
|
if (NewEdit->Text != ConfirmEdit->Text)
|
|
{
|
|
ConfirmEdit->SetFocus();
|
|
ConfirmEdit->SelectAll();
|
|
throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_DIFFERENT)));
|
|
}
|
|
|
|
int Valid = IsValidPassword(NewEdit->Text);
|
|
if (Valid <= 0)
|
|
{
|
|
DebugAssert(Valid == 0);
|
|
if (MessageDialog(LoadStr(MASTER_PASSWORD_SIMPLE2), qtWarning,
|
|
qaOK | qaCancel, HELP_MASTER_PASSWORD_SIMPLE) == qaCancel)
|
|
{
|
|
NewEdit->SetFocus();
|
|
NewEdit->SelectAll();
|
|
Abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
static bool __fastcall DoMasterPasswordDialog(bool Current,
|
|
UnicodeString & NewPassword)
|
|
{
|
|
bool Result;
|
|
// This can be a standalone dialog when opening session from commandline
|
|
TMasterPasswordDialog * Dialog = SafeFormCreate<TMasterPasswordDialog>();
|
|
try
|
|
{
|
|
Dialog->Init(Current);
|
|
UnicodeString CurrentPassword;
|
|
Result = Dialog->Execute(CurrentPassword, NewPassword);
|
|
if (Result)
|
|
{
|
|
if ((Current || WinConfiguration->UseMasterPassword) &&
|
|
DebugAlwaysTrue(!CurrentPassword.IsEmpty()))
|
|
{
|
|
WinConfiguration->SetMasterPassword(CurrentPassword);
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
delete Dialog;
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall DoMasterPasswordDialog()
|
|
{
|
|
UnicodeString NewPassword;
|
|
bool Result = DoMasterPasswordDialog(true, NewPassword);
|
|
DebugAssert(NewPassword.IsEmpty());
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall DoChangeMasterPasswordDialog(UnicodeString & NewPassword)
|
|
{
|
|
bool Result = DoMasterPasswordDialog(false, NewPassword);
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall MessageWithNoHelp(const UnicodeString & Message)
|
|
{
|
|
TMessageParams Params;
|
|
Params.AllowHelp = false; // to avoid recursion
|
|
if (MessageDialog(LoadStr(HELP_SEND_MESSAGE2), qtConfirmation,
|
|
qaOK | qaCancel, HELP_NONE, &Params) == qaOK)
|
|
{
|
|
SearchHelp(Message);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
void __fastcall CheckLogParam(TProgramParams * Params)
|
|
{
|
|
UnicodeString LogFile;
|
|
if (Params->FindSwitch(LOG_SWITCH, LogFile) && CheckSafe(Params))
|
|
{
|
|
Configuration->Usage->Inc(L"ScriptLog");
|
|
Configuration->TemporaryLogging(LogFile);
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall CheckXmlLogParam(TProgramParams * Params)
|
|
{
|
|
UnicodeString LogFile;
|
|
bool Result =
|
|
Params->FindSwitch(L"XmlLog", LogFile) &&
|
|
CheckSafe(Params);
|
|
if (Result)
|
|
{
|
|
Configuration->Usage->Inc(L"ScriptXmlLog");
|
|
Configuration->TemporaryActionsLogging(LogFile);
|
|
|
|
if (Params->FindSwitch(L"XmlLogRequired"))
|
|
{
|
|
Configuration->LogActionsRequired = true;
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool __fastcall CheckSafe(TProgramParams * Params)
|
|
{
|
|
// Originally we warned when the test didn't pass,
|
|
// but it would actually be helping hackers, so let's be silent.
|
|
|
|
// Earlier we tested presence of any URL on command-line.
|
|
// That was added to prevent abusing URL handler in 3.8.2 (2006).
|
|
// Later in 4.0.4 (2007) an /Unsafe switch was added to URL handler registration.
|
|
// So by now, we can check for the switch only and
|
|
// do not limit user from combining URL with say
|
|
// /rawconfig
|
|
|
|
return !Params->FindSwitch(UNSAFE_SWITCH);
|
|
}
|