UserInterface.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476
  1. //---------------------------------------------------------------------------
  2. #include <WinPCH.h>
  3. #pragma hdrstop
  4. #include "ScpCommander.h"
  5. #include "ScpExplorer.h"
  6. #include <Cryptography.h>
  7. #include "TerminalManager.h"
  8. #include "ProgParams.h"
  9. #include "Custom.h"
  10. //---------------------------------------------------------------------------
  11. const UnicodeString AppName = L"WinSCP";
  12. //---------------------------------------------------------------------------
  13. TConfiguration * __fastcall CreateConfiguration()
  14. {
  15. WinConfiguration = new TWinConfiguration();
  16. CustomWinConfiguration = WinConfiguration;
  17. GUIConfiguration = CustomWinConfiguration;
  18. TProgramParams * Params = TProgramParams::Instance();
  19. UnicodeString IniFileName = Params->SwitchValue(INI_SWITCH);
  20. if (!IniFileName.IsEmpty())
  21. {
  22. if (SameText(IniFileName, INI_NUL))
  23. {
  24. WinConfiguration->SetNulStorage();
  25. }
  26. else if (CheckSafe(Params))
  27. {
  28. IniFileName = ExpandFileName(ExpandEnvironmentVariables(IniFileName));
  29. WinConfiguration->SetExplicitIniFileStorageName(IniFileName);
  30. }
  31. }
  32. if (CheckSafe(Params))
  33. {
  34. std::unique_ptr<TStrings> RawConfig(new TStringList());
  35. if (Params->FindSwitch(RAW_CONFIG_SWITCH, RawConfig.get()))
  36. {
  37. WinConfiguration->OptionsStorage = RawConfig.get();
  38. }
  39. }
  40. return WinConfiguration;
  41. }
  42. //---------------------------------------------------------------------------
  43. TOptions * __fastcall GetGlobalOptions()
  44. {
  45. return TProgramParams::Instance();
  46. }
  47. //---------------------------------------------------------------------------
  48. TCustomScpExplorerForm * __fastcall CreateScpExplorer()
  49. {
  50. TCustomScpExplorerForm * ScpExplorer;
  51. if (WinConfiguration->Interface == ifExplorer)
  52. {
  53. ScpExplorer = SafeFormCreate<TScpExplorerForm>();
  54. }
  55. else
  56. {
  57. ScpExplorer = SafeFormCreate<TScpCommanderForm>();
  58. }
  59. return ScpExplorer;
  60. }
  61. //---------------------------------------------------------------------------
  62. UnicodeString __fastcall SshVersionString()
  63. {
  64. return FORMAT(L"WinSCP-release-%s", (Configuration->Version));
  65. }
  66. //---------------------------------------------------------------------------
  67. UnicodeString __fastcall AppNameString()
  68. {
  69. return L"WinSCP";
  70. }
  71. //---------------------------------------------------------------------------
  72. UnicodeString __fastcall GetCompanyRegistryKey()
  73. {
  74. return L"Software\\Martin Prikryl";
  75. }
  76. //---------------------------------------------------------------------------
  77. UnicodeString __fastcall GetRegistryKey()
  78. {
  79. return GetCompanyRegistryKey() + L"\\WinSCP 2";
  80. }
  81. //---------------------------------------------------------------------------
  82. static bool ForcedOnForeground = false;
  83. void __fastcall SetOnForeground(bool OnForeground)
  84. {
  85. ForcedOnForeground = OnForeground;
  86. }
  87. //---------------------------------------------------------------------------
  88. void __fastcall FlashOnBackground()
  89. {
  90. DebugAssert(Application);
  91. if ((WinConfiguration != NULL) && WinConfiguration->FlashTaskbar && !ForcedOnForeground && !ForegroundTask())
  92. {
  93. FlashWindow(Application->MainFormHandle, true);
  94. }
  95. }
  96. //---------------------------------------------------------------------------
  97. void __fastcall LocalSystemSettings(TForm * /*Control*/)
  98. {
  99. // noop
  100. }
  101. //---------------------------------------------------------------------------
  102. void __fastcall ShowExtendedException(Exception * E)
  103. {
  104. ShowExtendedExceptionEx(NULL, E);
  105. }
  106. //---------------------------------------------------------------------------
  107. void __fastcall TerminateApplication()
  108. {
  109. Application->Terminate();
  110. }
  111. //---------------------------------------------------------------------------
  112. struct TOpenLocalPathHandler
  113. {
  114. UnicodeString LocalPath;
  115. UnicodeString LocalFileName;
  116. void __fastcall Open(TObject * Sender, unsigned int & /*Answer*/)
  117. {
  118. TButton * Button = DebugNotNull(dynamic_cast<TButton *>(Sender));
  119. // Reason for separate AMenu variable is given in TPreferencesDialog::EditorFontColorButtonClick
  120. TPopupMenu * AMenu = new TPopupMenu(Application);
  121. // Popup menu has to survive the popup as TBX calls click handler asynchronously (post).
  122. Menu.reset(AMenu);
  123. TMenuItem * Item;
  124. Item = new TMenuItem(Menu.get());
  125. Menu->Items->Add(Item);
  126. Item->Caption = LoadStr(OPEN_TARGET_FOLDER);
  127. Item->OnClick = OpenFolderClick;
  128. if (!LocalFileName.IsEmpty())
  129. {
  130. Item = new TMenuItem(Menu.get());
  131. Menu->Items->Add(Item);
  132. Item->Caption = LoadStr(OPEN_DOWNLOADED_FILE);
  133. Item->OnClick = OpenFileClick;
  134. }
  135. MenuPopup(Menu.get(), Button);
  136. }
  137. private:
  138. std::unique_ptr<TPopupMenu> Menu;
  139. void __fastcall OpenFolderClick(TObject * /*Sender*/)
  140. {
  141. if (LocalFileName.IsEmpty())
  142. {
  143. OpenFolderInExplorer(LocalPath);
  144. }
  145. else
  146. {
  147. OpenFileInExplorer(LocalFileName);
  148. }
  149. }
  150. void __fastcall OpenFileClick(TObject * /*Sender*/)
  151. {
  152. ExecuteShellChecked(LocalFileName, L"");
  153. }
  154. };
  155. //---------------------------------------------------------------------------
  156. void __fastcall ShowExtendedExceptionEx(TTerminal * Terminal,
  157. Exception * E)
  158. {
  159. bool Show = ShouldDisplayException(E);
  160. bool DoNotDisplay = false;
  161. try
  162. {
  163. // This is special case used particularly when called from .NET assembly
  164. // (which always uses /nointeractiveinput),
  165. // but can be useful for other console runs too
  166. TProgramParams * Params = TProgramParams::Instance();
  167. if (Params->FindSwitch(NOINTERACTIVEINPUT_SWITCH))
  168. {
  169. DoNotDisplay = true;
  170. if (Show && CheckXmlLogParam(Params))
  171. {
  172. // The Started argument won't be used with .NET assembly, as it never uses patterns in XML log file name.
  173. // But it theoretically can be used, when started manually.
  174. std::unique_ptr<TActionLog> ActionLog(new TActionLog(Now(), Configuration));
  175. ActionLog->AddFailure(E);
  176. // unnecessary explicit release
  177. ActionLog.reset(NULL);
  178. }
  179. }
  180. }
  181. catch (Exception &)
  182. {
  183. // swallow
  184. }
  185. TTerminalManager * Manager = TTerminalManager::Instance(false);
  186. if (!DoNotDisplay)
  187. {
  188. ESshTerminate * Terminate = dynamic_cast<ESshTerminate*>(E);
  189. bool CloseOnCompletion = (Terminate != NULL);
  190. EFatal * FatalException = dynamic_cast<EFatal *>(E);
  191. bool ForActiveTerminal =
  192. (FatalException != NULL) && (Terminal != NULL) &&
  193. (Manager != NULL) && (Manager->ActiveTerminal == Terminal);
  194. unsigned int Result;
  195. if (CloseOnCompletion)
  196. {
  197. if (ForActiveTerminal)
  198. {
  199. DebugAssert(!Terminal->Active);
  200. Manager->DisconnectActiveTerminal();
  201. }
  202. if (Terminate->Operation == odoSuspend)
  203. {
  204. // suspend, so that exit prompt is shown only after windows resume
  205. SuspendWindows();
  206. }
  207. DebugAssert(Show);
  208. bool ConfirmExitOnCompletion =
  209. CloseOnCompletion &&
  210. ((Terminate->Operation == odoDisconnect) || (Terminate->Operation == odoSuspend)) &&
  211. WinConfiguration->ConfirmExitOnCompletion;
  212. if (ConfirmExitOnCompletion)
  213. {
  214. TMessageParams Params(mpNeverAskAgainCheck);
  215. unsigned int Answers = 0;
  216. TQueryButtonAlias Aliases[1];
  217. TOpenLocalPathHandler OpenLocalPathHandler;
  218. if (!Terminate->TargetLocalPath.IsEmpty() && !ForActiveTerminal)
  219. {
  220. OpenLocalPathHandler.LocalPath = Terminate->TargetLocalPath;
  221. OpenLocalPathHandler.LocalFileName = Terminate->DestLocalFileName;
  222. Aliases[0].Button = qaIgnore;
  223. Aliases[0].Alias = LoadStr(OPEN_BUTTON);
  224. Aliases[0].OnSubmit = OpenLocalPathHandler.Open;
  225. Aliases[0].MenuButton = true;
  226. Answers |= Aliases[0].Button;
  227. Params.Aliases = Aliases;
  228. Params.AliasesCount = LENOF(Aliases);
  229. }
  230. if (ForActiveTerminal)
  231. {
  232. UnicodeString MessageFormat =
  233. (Manager->Count > 1) ?
  234. FMTLOAD(DISCONNECT_ON_COMPLETION, (Manager->Count - 1)) :
  235. LoadStr(EXIT_ON_COMPLETION);
  236. // Remove the leading "%s\n\n" (not to change the translation originals - previously the error message was prepended)
  237. MessageFormat = FORMAT(MessageFormat, (UnicodeString())).Trim();
  238. MessageFormat = MainInstructions(MessageFormat) + L"\n\n%s";
  239. Result = FatalExceptionMessageDialog(E, qtInformation,
  240. MessageFormat,
  241. Answers | qaYes | qaNo, HELP_NONE, &Params);
  242. }
  243. else
  244. {
  245. Result =
  246. ExceptionMessageDialog(E, qtInformation, L"", Answers | qaOK, HELP_NONE, &Params);
  247. }
  248. if (Result == qaNeverAskAgain)
  249. {
  250. Result = qaYes;
  251. WinConfiguration->ConfirmExitOnCompletion = false;
  252. }
  253. }
  254. else
  255. {
  256. Result = qaYes;
  257. }
  258. }
  259. else
  260. {
  261. if (Show)
  262. {
  263. if (ForActiveTerminal)
  264. {
  265. bool HookedDialog = false;
  266. try
  267. {
  268. TMessageParams Params;
  269. if (DebugAlwaysTrue(Manager->ActiveTerminal != NULL) &&
  270. ((Configuration->SessionReopenTimeout == 0) ||
  271. ((double)Manager->ActiveTerminal->ReopenStart == 0) ||
  272. (int(double(Now() - Manager->ActiveTerminal->ReopenStart) * MSecsPerDay) < Configuration->SessionReopenTimeout)))
  273. {
  274. Params.Timeout = GUIConfiguration->SessionReopenAutoIdleOn ? GUIConfiguration->SessionReopenAutoIdle : 0;
  275. Params.TimeoutAnswer = qaRetry;
  276. Params.TimeoutResponse = Params.TimeoutAnswer;
  277. HookedDialog = Manager->HookFatalExceptionMessageDialog(Params);
  278. }
  279. bool InactiveTerminationMessage = (FatalException != NULL) && FatalException->InactiveTerminationMessage;
  280. if (InactiveTerminationMessage)
  281. {
  282. Params.Params |= mpNeverAskAgainCheck;
  283. Params.NeverAskAgainTitle = LoadStr(ALWAYS_RECONNECT);
  284. Params.NeverAskAgainAnswer = qaRetry;
  285. }
  286. Result = FatalExceptionMessageDialog(E, qtError, EmptyStr, qaOK, EmptyStr, &Params);
  287. if ((Result == qaNeverAskAgain) && DebugAlwaysTrue(InactiveTerminationMessage))
  288. {
  289. GUIConfiguration->SessionReopenAutoInactive = true;
  290. Result = qaRetry;
  291. }
  292. }
  293. __finally
  294. {
  295. if (HookedDialog)
  296. {
  297. Manager->UnhookFatalExceptionMessageDialog();
  298. }
  299. }
  300. }
  301. else
  302. {
  303. Result = ExceptionMessageDialog(E, qtError);
  304. }
  305. }
  306. else
  307. {
  308. Result = qaOK;
  309. }
  310. }
  311. if (Result == qaYes)
  312. {
  313. DebugAssert(CloseOnCompletion);
  314. DebugAssert(Terminate != NULL);
  315. DebugAssert(Terminate->Operation != odoIdle);
  316. TerminateApplication();
  317. switch (Terminate->Operation)
  318. {
  319. case odoDisconnect:
  320. break;
  321. case odoSuspend:
  322. // suspended before already
  323. break;
  324. case odoShutDown:
  325. ShutDownWindows();
  326. break;
  327. default:
  328. DebugFail();
  329. }
  330. }
  331. else if (Result == qaRetry)
  332. {
  333. // qaRetry is used by FatalExceptionMessageDialog
  334. if (DebugAlwaysTrue(ForActiveTerminal))
  335. {
  336. Manager->ReconnectActiveTerminal();
  337. }
  338. }
  339. else
  340. {
  341. if (ForActiveTerminal)
  342. {
  343. Manager->DisconnectActiveTerminalIfPermanentFreeOtherwise();
  344. }
  345. }
  346. }
  347. }
  348. //---------------------------------------------------------------------------
  349. void __fastcall ShowNotification(TTerminal * Terminal, const UnicodeString & Str,
  350. TQueryType Type)
  351. {
  352. TTerminalManager * Manager = TTerminalManager::Instance(false);
  353. DebugAssert(Manager != NULL);
  354. Manager->ScpExplorer->PopupTrayBalloon(Terminal, Str, Type);
  355. }
  356. //---------------------------------------------------------------------------
  357. UnicodeString GetThemeName(bool Dark)
  358. {
  359. return Dark ? L"DarkOfficeXP" : L"OfficeXP";
  360. }
  361. //---------------------------------------------------------------------------
  362. void __fastcall ConfigureInterface()
  363. {
  364. DebugAssert(WinConfiguration != NULL);
  365. int BidiModeFlag =
  366. AdjustLocaleFlag(LoadStr(BIDI_MODE), WinConfiguration->BidiModeOverride, false, bdRightToLeft, bdLeftToRight);
  367. Application->BiDiMode = static_cast<TBiDiMode>(BidiModeFlag);
  368. SetTBXSysParam(TSP_XPVISUALSTYLE, XPVS_AUTOMATIC);
  369. UnicodeString Theme = GetThemeName(WinConfiguration->UseDarkTheme());
  370. if (!SameText(TBXCurrentTheme(), Theme))
  371. {
  372. TBXSetTheme(Theme);
  373. }
  374. // Has any effect on Wine only
  375. // (otherwise initial UserDocumentDirectory is equivalent to GetPersonalFolder())
  376. UserDocumentDirectory = GetPersonalFolder();
  377. }
  378. //---------------------------------------------------------------------------
  379. void __fastcall DoAboutDialog(TConfiguration *Configuration)
  380. {
  381. DoAboutDialog(Configuration, true, NULL);
  382. }
  383. //---------------------------------------------------------------------
  384. void __fastcall DoProductLicense()
  385. {
  386. DoLicenseDialog(lcWinScp);
  387. }
  388. //---------------------------------------------------------------------------
  389. const UnicodeString PixelsPerInchKey = L"PixelsPerInch";
  390. //---------------------------------------------------------------------
  391. int __fastcall GetToolbarLayoutPixelsPerInch(TStrings * Storage, TControl * Control)
  392. {
  393. int Result;
  394. if (Storage->IndexOfName(PixelsPerInchKey))
  395. {
  396. Result = LoadPixelsPerInch(Storage->Values[PixelsPerInchKey], Control);
  397. }
  398. else
  399. {
  400. Result = -1;
  401. }
  402. return Result;
  403. }
  404. //---------------------------------------------------------------------
  405. UnicodeString __fastcall GetToolbarKey(const UnicodeString & ToolbarName)
  406. {
  407. UnicodeString Result = ToolbarName;
  408. Result = RemoveSuffix(Result, L"Toolbar", true);
  409. return Result;
  410. }
  411. //---------------------------------------------------------------------
  412. static inline void __fastcall GetToolbarKey(const UnicodeString & ToolbarName,
  413. const UnicodeString & Value, UnicodeString & ToolbarKey)
  414. {
  415. ToolbarKey = GetToolbarKey(ToolbarName);
  416. if (!Value.IsEmpty())
  417. {
  418. ToolbarKey += L"_" + Value;
  419. }
  420. }
  421. //---------------------------------------------------------------------------
  422. static int __fastcall ToolbarReadInt(const UnicodeString ToolbarName,
  423. const UnicodeString Value, const int Default, const void * ExtraData)
  424. {
  425. int Result;
  426. if (Value == L"Rev")
  427. {
  428. Result = 2000;
  429. }
  430. else
  431. {
  432. TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
  433. UnicodeString ToolbarKey;
  434. GetToolbarKey(ToolbarName, Value, ToolbarKey);
  435. if (Storage->IndexOfName(ToolbarKey) >= 0)
  436. {
  437. Result = StrToIntDef(Storage->Values[ToolbarKey], Default);
  438. #if 0
  439. // this does not work well, as it scales down the stretched
  440. // toolbars (path toolbars) too much, it has to be reimplemented smarter
  441. if (Value == L"DockPos")
  442. {
  443. int PixelsPerInch = GetToolbarLayoutPixelsPerInch(Storage);
  444. // When DPI has decreased since the last time, scale down
  445. // toolbar position to get rid of gaps caused by smaller labels.
  446. // Do not do this when DPI has increased as it would introduce gaps,
  447. // as toolbars consists mostly of icons only, that do not scale.
  448. // The toolbars shift themselves anyway, when other toolbars to the left
  449. // get wider. We also risk a bit that toolbar order changes,
  450. // as with very small toolbars (History) we can get scaled down position
  451. // of the following toolbar to the left of it.
  452. // There's special handling (also for scaling-up) stretched toolbars
  453. // in LoadToolbarsLayoutStr
  454. if ((PixelsPerInch > 0) && (Screen->PixelsPerInch < PixelsPerInch))
  455. {
  456. Result = LoadDimension(Result, PixelsPerInch);
  457. }
  458. }
  459. #endif
  460. }
  461. else
  462. {
  463. Result = Default;
  464. }
  465. }
  466. return Result;
  467. }
  468. //---------------------------------------------------------------------------
  469. static UnicodeString __fastcall ToolbarReadString(const UnicodeString ToolbarName,
  470. const UnicodeString Value, const UnicodeString Default, const void * ExtraData)
  471. {
  472. UnicodeString Result;
  473. TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
  474. UnicodeString ToolbarKey;
  475. GetToolbarKey(ToolbarName, Value, ToolbarKey);
  476. if (Storage->IndexOfName(ToolbarKey) >= 0)
  477. {
  478. Result = Storage->Values[ToolbarKey];
  479. }
  480. else
  481. {
  482. Result = Default;
  483. }
  484. return Result;
  485. }
  486. //---------------------------------------------------------------------------
  487. static void __fastcall ToolbarWriteInt(const UnicodeString ToolbarName,
  488. const UnicodeString Value, const int Data, const void * ExtraData)
  489. {
  490. DebugFail();
  491. if (Value != L"Rev")
  492. {
  493. TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
  494. UnicodeString ToolbarKey;
  495. GetToolbarKey(ToolbarName, Value, ToolbarKey);
  496. DebugAssert(Storage->IndexOfName(ToolbarKey) < 0);
  497. Storage->Values[ToolbarKey] = IntToStr(Data);
  498. }
  499. }
  500. //---------------------------------------------------------------------------
  501. static void __fastcall ToolbarWriteString(const UnicodeString ToolbarName,
  502. const UnicodeString Value, const UnicodeString Data, const void * ExtraData)
  503. {
  504. DebugAssert(Value.IsEmpty());
  505. TStrings * Storage = static_cast<TStrings *>(const_cast<void*>(ExtraData));
  506. UnicodeString ToolbarKey;
  507. GetToolbarKey(ToolbarName, Value, ToolbarKey);
  508. DebugAssert(Storage->IndexOfName(ToolbarKey) < 0);
  509. Storage->Values[ToolbarKey] = Data;
  510. }
  511. //---------------------------------------------------------------------------
  512. UnicodeString __fastcall GetToolbarsLayoutStr(TControl * OwnerControl)
  513. {
  514. UnicodeString Result;
  515. TStrings * Storage = new TStringList();
  516. try
  517. {
  518. TBCustomSavePositions(OwnerControl, ToolbarWriteInt, ToolbarWriteString,
  519. Storage);
  520. Storage->Values[PixelsPerInchKey] = SavePixelsPerInch(OwnerControl);
  521. Result = Storage->CommaText;
  522. }
  523. __finally
  524. {
  525. delete Storage;
  526. }
  527. return Result;
  528. }
  529. //---------------------------------------------------------------------------
  530. void __fastcall LoadToolbarsLayoutStr(TControl * OwnerControl, UnicodeString LayoutStr)
  531. {
  532. TStrings * Storage = CommaTextToStringList(LayoutStr);
  533. try
  534. {
  535. TBCustomLoadPositions(OwnerControl, ToolbarReadInt, ToolbarReadString,
  536. Storage);
  537. int PixelsPerInch = GetToolbarLayoutPixelsPerInch(Storage, OwnerControl);
  538. // Scale toolbars stretched to the first other toolbar to the right
  539. if ((PixelsPerInch > 0) && (PixelsPerInch != GetControlPixelsPerInch(OwnerControl))) // optimization
  540. {
  541. for (int Index = 0; Index < OwnerControl->ComponentCount; Index++)
  542. {
  543. TTBXToolbar * Toolbar =
  544. dynamic_cast<TTBXToolbar *>(OwnerControl->Components[Index]);
  545. if ((Toolbar != NULL) && Toolbar->Stretch &&
  546. (Toolbar->OnGetBaseSize != NULL) &&
  547. // we do not support floating of stretched toolbars
  548. DebugAlwaysTrue(!Toolbar->Floating))
  549. {
  550. TTBXToolbar * FollowingToolbar = NULL;
  551. for (int Index2 = 0; Index2 < OwnerControl->ComponentCount; Index2++)
  552. {
  553. TTBXToolbar * Toolbar2 =
  554. dynamic_cast<TTBXToolbar *>(OwnerControl->Components[Index2]);
  555. if ((Toolbar2 != NULL) && !Toolbar2->Floating &&
  556. (Toolbar2->Parent == Toolbar->Parent) &&
  557. (Toolbar2->DockRow == Toolbar->DockRow) &&
  558. (Toolbar2->DockPos > Toolbar->DockPos) &&
  559. ((FollowingToolbar == NULL) || (FollowingToolbar->DockPos > Toolbar2->DockPos)))
  560. {
  561. FollowingToolbar = Toolbar2;
  562. }
  563. }
  564. if (FollowingToolbar != NULL)
  565. {
  566. int NewWidth = LoadDimension(Toolbar->Width, PixelsPerInch, Toolbar);
  567. FollowingToolbar->DockPos += NewWidth - Toolbar->Width;
  568. }
  569. }
  570. }
  571. }
  572. }
  573. __finally
  574. {
  575. delete Storage;
  576. }
  577. }
  578. //---------------------------------------------------------------------------
  579. TTBXSeparatorItem * __fastcall AddMenuSeparator(TTBCustomItem * Menu)
  580. {
  581. TTBXSeparatorItem * Item = new TTBXSeparatorItem(Menu);
  582. Menu->Add(Item);
  583. return Item;
  584. }
  585. //---------------------------------------------------------------------------
  586. static TComponent * LastPopupComponent = NULL;
  587. static TRect LastPopupRect(-1, -1, -1, -1);
  588. static TDateTime LastCloseUp;
  589. //---------------------------------------------------------------------------
  590. static void __fastcall ConvertMenu(TMenuItem * AItems, TTBCustomItem * Items, TBasicAction * ParentAction, TTBCustomItem *& ParentActionItem)
  591. {
  592. ParentActionItem = NULL;
  593. for (int Index = 0; Index < AItems->Count; Index++)
  594. {
  595. TMenuItem * AItem = AItems->Items[Index];
  596. TTBCustomItem * Item;
  597. if (!AItem->Enabled && !AItem->Visible && (AItem->Action == NULL) &&
  598. (AItem->OnClick == NULL) && DebugAlwaysTrue(AItem->Count == 0))
  599. {
  600. TTBXLabelItem * LabelItem = new TTBXLabelItem(Items->Owner);
  601. // TTBXLabelItem has its own Caption
  602. LabelItem->Caption = AItem->Caption;
  603. LabelItem->SectionHeader = true;
  604. Item = LabelItem;
  605. }
  606. else
  607. {
  608. TTBXSubmenuItem * SubmenuItem = NULL;
  609. // see TB2DsgnConverter.pas DoConvert
  610. if (AItem->Caption == L"-")
  611. {
  612. Item = new TTBXSeparatorItem(Items->Owner);
  613. }
  614. else
  615. {
  616. if (AItem->Count > 0)
  617. {
  618. SubmenuItem = new TTBXSubmenuItem(Items->Owner);
  619. Item = SubmenuItem;
  620. }
  621. else
  622. {
  623. Item = new TTBXItem(Items->Owner);
  624. }
  625. Item->Action = AItem->Action;
  626. if ((ParentAction != NULL) && (Item->Action == ParentAction))
  627. {
  628. ParentActionItem = Item;
  629. }
  630. Item->AutoCheck = AItem->AutoCheck;
  631. Item->Caption = AItem->Caption;
  632. Item->Checked = AItem->Checked;
  633. if (AItem->Default)
  634. {
  635. Item->Options = Item->Options << tboDefault;
  636. }
  637. Item->Enabled = AItem->Enabled;
  638. Item->GroupIndex = AItem->GroupIndex;
  639. Item->HelpContext = AItem->HelpContext;
  640. Item->ImageIndex = AItem->ImageIndex;
  641. Item->RadioItem = AItem->RadioItem;
  642. Item->ShortCut = AItem->ShortCut;
  643. Item->SubMenuImages = AItem->SubMenuImages;
  644. Item->OnClick = AItem->OnClick;
  645. }
  646. Item->Hint = AItem->Hint;
  647. Item->Tag = AItem->Tag;
  648. Item->Visible = AItem->Visible;
  649. if (AItem->Count > 0)
  650. {
  651. TTBCustomItem * ActionItem;
  652. ConvertMenu(AItem, Item, AItem->Action, ActionItem);
  653. if ((AItem->Action != NULL) && (ActionItem != NULL) && DebugAlwaysTrue(SubmenuItem != NULL))
  654. {
  655. SubmenuItem->DropdownCombo = true;
  656. TTBItemOptions Options = ActionItem->Options;
  657. ActionItem->Options = Options << tboDefault;
  658. SubmenuItem->DropdownCombo = true;
  659. }
  660. }
  661. }
  662. Items->Add(Item);
  663. }
  664. }
  665. //---------------------------------------------------------------------------
  666. void __fastcall MenuPopup(TPopupMenu * AMenu, TRect Rect,
  667. TComponent * PopupComponent)
  668. {
  669. // Pressing the same button within 200ms after closing its popup menu
  670. // does nothing.
  671. // It is to immitate close-by-click behavior. Note that menu closes itself
  672. // before onclick handler of button occurs.
  673. // To support content menu popups, we have to check for the popup location too,
  674. // to allow poping menu on different location (such as different node of TTreeView),
  675. // even if there's another popup opened already (so that the time interval
  676. // below does not elapse).
  677. TDateTime N = Now();
  678. TDateTime Diff = N - LastCloseUp;
  679. if ((PopupComponent == LastPopupComponent) &&
  680. (Rect == LastPopupRect) &&
  681. (Diff < TDateTime(0, 0, 0, 200)))
  682. {
  683. LastPopupComponent = NULL;
  684. }
  685. else
  686. {
  687. TTBXPopupMenu * Menu = dynamic_cast<TTBXPopupMenu *>(AMenu);
  688. if (Menu == NULL)
  689. {
  690. Menu = CreateTBXPopupMenu(AMenu->Owner);
  691. Menu->OnPopup = AMenu->OnPopup;
  692. Menu->Items->SubMenuImages = AMenu->Images;
  693. TTBCustomItem * Dummy;
  694. ConvertMenu(AMenu->Items, Menu->Items, NULL, Dummy);
  695. }
  696. Menu->PopupComponent = PopupComponent;
  697. Menu->PopupEx(Rect);
  698. LastPopupComponent = PopupComponent;
  699. LastPopupRect = Rect;
  700. LastCloseUp = Now();
  701. }
  702. }
  703. //---------------------------------------------------------------------------
  704. const int ColorCols = 8;
  705. const int StandardColorRows = 2;
  706. const int StandardColorCount = ColorCols * StandardColorRows;
  707. const int UserColorRows = 1;
  708. const int UserColorCount = UserColorRows * ColorCols;
  709. const wchar_t ColorSeparator = L',';
  710. //---------------------------------------------------------------------------
  711. static void __fastcall GetStandardSessionColorInfo(
  712. int Col, int Row, TColor & Color, UnicodeString & Name)
  713. {
  714. #define COLOR_INFO(COL, ROW, NAME, COLOR) \
  715. if ((Col == COL) && (Row == ROW)) { Name = NAME; Color = TColor(COLOR); } else
  716. // bottom row of default TBX color set
  717. COLOR_INFO(0, 0, L"Rose", 0xCC99FF)
  718. COLOR_INFO(1, 0, L"Tan", 0x99CCFF)
  719. COLOR_INFO(2, 0, L"Light Yellow", 0x99FFFF)
  720. COLOR_INFO(3, 0, L"Light Green", 0xCCFFCC)
  721. COLOR_INFO(4, 0, L"Light Turquoise", 0xFFFFCC)
  722. COLOR_INFO(5, 0, L"Pale Blue", 0xFFCC99)
  723. COLOR_INFO(6, 0, L"Lavender", 0xFF99CC)
  724. // second row of Excel 2010 palette with second color (Lighter Black) skipped
  725. COLOR_INFO(7, 0, L"Light Orange", 0xB5D5FB)
  726. COLOR_INFO(0, 1, L"Darker White", 0xD8D8D8)
  727. COLOR_INFO(1, 1, L"Darker Tan", 0x97BDC4)
  728. COLOR_INFO(2, 1, L"Lighter Blue", 0xE2B38D)
  729. COLOR_INFO(3, 1, L"Light Blue", 0xE4CCB8)
  730. COLOR_INFO(4, 1, L"Lighter Red", 0xB7B9E5)
  731. COLOR_INFO(5, 1, L"Light Olive Green", 0xBCE3D7)
  732. COLOR_INFO(6, 1, L"Light Purple", 0xD9C1CC)
  733. COLOR_INFO(7, 1, L"Light Aqua", 0xE8DDB7)
  734. DebugFail();
  735. #undef COLOR_INFO
  736. }
  737. //---------------------------------------------------------------------------
  738. static void __fastcall SessionColorSetGetColorInfo(
  739. void * /*Data*/, TTBXCustomColorSet * /*Sender*/, int Col, int Row, TColor & Color, UnicodeString & Name)
  740. {
  741. GetStandardSessionColorInfo(Col, Row, Color, Name);
  742. }
  743. //---------------------------------------------------------------------------
  744. TColor __fastcall RestoreColor(const UnicodeString & CStr)
  745. {
  746. return TColor(StrToInt(UnicodeString(L"$") + CStr));
  747. }
  748. //---------------------------------------------------------------------------
  749. UnicodeString __fastcall StoreColor(TColor Color)
  750. {
  751. return IntToHex(Color, 6);
  752. }
  753. //---------------------------------------------------------------------------
  754. static UnicodeString __fastcall ExtractColorStr(UnicodeString & Colors)
  755. {
  756. return CutToChar(Colors, ColorSeparator, true);
  757. }
  758. //---------------------------------------------------------------------------
  759. static bool __fastcall IsStandardColor(bool SessionColors, TColor Color)
  760. {
  761. if (SessionColors)
  762. {
  763. for (int Row = 0; Row < StandardColorRows; Row++)
  764. {
  765. for (int Col = 0; Col < ColorCols; Col++)
  766. {
  767. TColor StandardColor;
  768. UnicodeString Name; // unused
  769. GetStandardSessionColorInfo(Col, Row, StandardColor, Name);
  770. if (StandardColor == Color)
  771. {
  772. return true;
  773. }
  774. }
  775. }
  776. return false;
  777. }
  778. else
  779. {
  780. std::unique_ptr<TTBXColorPalette> DefaultColorPalette(new TTBXColorPalette(NULL));
  781. return (DefaultColorPalette->FindCell(Color).X >= 0);
  782. }
  783. }
  784. //---------------------------------------------------------------------------
  785. class TColorChangeData : public TComponent
  786. {
  787. public:
  788. __fastcall TColorChangeData(TColorChangeEvent OnColorChange, TColor Color, bool SessionColors);
  789. static TColorChangeData * __fastcall Retrieve(TObject * Object);
  790. void __fastcall ColorChange(TColor Color);
  791. __property TColor Color = { read = FColor };
  792. __property bool SessionColors = { read = FSessionColors };
  793. private:
  794. TColorChangeEvent FOnColorChange;
  795. TColor FColor;
  796. bool FSessionColors;
  797. };
  798. //---------------------------------------------------------------------------
  799. __fastcall TColorChangeData::TColorChangeData(
  800. TColorChangeEvent OnColorChange, TColor Color, bool SessionColors) :
  801. TComponent(NULL)
  802. {
  803. Name = QualifiedClassName();
  804. FOnColorChange = OnColorChange;
  805. FColor = Color;
  806. FSessionColors = SessionColors;
  807. }
  808. //---------------------------------------------------------------------------
  809. TColorChangeData * __fastcall TColorChangeData::Retrieve(TObject * Object)
  810. {
  811. TComponent * Component = DebugNotNull(dynamic_cast<TComponent *>(Object));
  812. TComponent * ColorChangeDataComponent = Component->FindComponent(QualifiedClassName());
  813. return DebugNotNull(dynamic_cast<TColorChangeData *>(ColorChangeDataComponent));
  814. }
  815. //---------------------------------------------------------------------------
  816. static void SaveCustomColors(bool SessionColors, const UnicodeString & Colors)
  817. {
  818. if (SessionColors)
  819. {
  820. CustomWinConfiguration->SessionColors = Colors;
  821. }
  822. else
  823. {
  824. CustomWinConfiguration->FontColors = Colors;
  825. }
  826. }
  827. //---------------------------------------------------------------------------
  828. static UnicodeString LoadCustomColors(bool SessionColors)
  829. {
  830. return SessionColors ? CustomWinConfiguration->SessionColors : CustomWinConfiguration->FontColors;
  831. }
  832. //---------------------------------------------------------------------------
  833. void __fastcall TColorChangeData::ColorChange(TColor Color)
  834. {
  835. // Color palette returns clNone when no color is selected,
  836. // though it should not really happen.
  837. // See also CreateColorPalette
  838. if (Color == Vcl::Graphics::clNone)
  839. {
  840. Color = TColor(0);
  841. }
  842. if ((Color != TColor(0)) &&
  843. !IsStandardColor(SessionColors, Color))
  844. {
  845. UnicodeString Colors = StoreColor(Color);
  846. UnicodeString Temp = LoadCustomColors(SessionColors);
  847. while (!Temp.IsEmpty())
  848. {
  849. UnicodeString CStr = ExtractColorStr(Temp);
  850. if (RestoreColor(CStr) != Color)
  851. {
  852. Colors += UnicodeString(ColorSeparator) + CStr;
  853. }
  854. }
  855. SaveCustomColors(SessionColors, Colors);
  856. }
  857. FOnColorChange(Color);
  858. }
  859. //---------------------------------------------------------------------------
  860. static void __fastcall ColorDefaultClick(void * /*Data*/, TObject * Sender)
  861. {
  862. TColorChangeData::Retrieve(Sender)->ColorChange(TColor(0));
  863. }
  864. //---------------------------------------------------------------------------
  865. static void __fastcall ColorPaletteChange(void * /*Data*/, TObject * Sender)
  866. {
  867. TTBXColorPalette * ColorPalette = DebugNotNull(dynamic_cast<TTBXColorPalette *>(Sender));
  868. TColorChangeData::Retrieve(Sender)->ColorChange(GetNonZeroColor(ColorPalette->Color));
  869. }
  870. //---------------------------------------------------------------------------
  871. static UnicodeString __fastcall CustomColorName(int Index)
  872. {
  873. return UnicodeString(L"Color") + wchar_t(L'A' + Index);
  874. }
  875. //---------------------------------------------------------------------------
  876. static void __fastcall ColorPickClick(void * /*Data*/, TObject * Sender)
  877. {
  878. TColorChangeData * ColorChangeData = TColorChangeData::Retrieve(Sender);
  879. std::unique_ptr<TColorDialog> Dialog(new TColorDialog(Application));
  880. Dialog->Options = Dialog->Options << cdFullOpen << cdAnyColor;
  881. Dialog->Color = (ColorChangeData->Color != 0 ? ColorChangeData->Color : clSkyBlue);
  882. UnicodeString Temp = LoadCustomColors(ColorChangeData->SessionColors);
  883. int StandardColorIndex = 0;
  884. for (int Index = 0; Index < MaxCustomColors; Index++)
  885. {
  886. TColor CustomColor;
  887. if (!Temp.IsEmpty())
  888. {
  889. CustomColor = RestoreColor(ExtractColorStr(Temp));
  890. }
  891. else
  892. {
  893. if (ColorChangeData->SessionColors)
  894. {
  895. if (StandardColorIndex < StandardColorCount)
  896. {
  897. UnicodeString Name; // not used
  898. GetStandardSessionColorInfo(
  899. StandardColorIndex % ColorCols, StandardColorIndex / ColorCols,
  900. CustomColor, Name);
  901. StandardColorIndex++;
  902. }
  903. else
  904. {
  905. break;
  906. }
  907. }
  908. else
  909. {
  910. // no standard font colors
  911. break;
  912. }
  913. }
  914. Dialog->CustomColors->Values[CustomColorName(Index)] = StoreColor(CustomColor);
  915. }
  916. if (Dialog->Execute())
  917. {
  918. // so that we do not have to try to preserve the excess colors
  919. DebugAssert(UserColorCount <= MaxCustomColors);
  920. UnicodeString Colors;
  921. for (int Index = 0; Index < MaxCustomColors; Index++)
  922. {
  923. UnicodeString CStr = Dialog->CustomColors->Values[CustomColorName(Index)];
  924. if (!CStr.IsEmpty())
  925. {
  926. TColor CustomColor = RestoreColor(CStr);
  927. if ((CustomColor != static_cast<TColor>(-1)) &&
  928. !IsStandardColor(ColorChangeData->SessionColors, CustomColor))
  929. {
  930. AddToList(Colors, StoreColor(CustomColor), ColorSeparator);
  931. }
  932. }
  933. }
  934. SaveCustomColors(ColorChangeData->SessionColors, Colors);
  935. // call color change only after copying custom colors back,
  936. // so that it can add selected color to the user list
  937. ColorChangeData->ColorChange(GetNonZeroColor(Dialog->Color));
  938. }
  939. }
  940. //---------------------------------------------------------------------------
  941. TPopupMenu * __fastcall CreateSessionColorPopupMenu(TColor Color,
  942. TColorChangeEvent OnColorChange)
  943. {
  944. std::unique_ptr<TTBXPopupMenu> PopupMenu(new TTBXPopupMenu(Application));
  945. CreateSessionColorMenu(PopupMenu->Items, Color, OnColorChange);
  946. return PopupMenu.release();
  947. }
  948. //---------------------------------------------------------------------------
  949. static void __fastcall UserCustomColorSetGetColorInfo(
  950. void * /*Data*/, TTBXCustomColorSet * Sender, int Col, int Row, TColor & Color, UnicodeString & /*Name*/)
  951. {
  952. int Index = (Row * Sender->ColCount) + Col;
  953. bool SessionColors = static_cast<bool>(Sender->Tag);
  954. UnicodeString Temp = LoadCustomColors(SessionColors);
  955. while ((Index > 0) && !Temp.IsEmpty())
  956. {
  957. ExtractColorStr(Temp);
  958. Index--;
  959. }
  960. if (!Temp.IsEmpty())
  961. {
  962. Color = RestoreColor(ExtractColorStr(Temp));
  963. }
  964. else
  965. {
  966. // hide the trailing cells
  967. Color = Vcl::Graphics::clNone;
  968. }
  969. }
  970. //---------------------------------------------------------------------------
  971. void __fastcall CreateColorPalette(TTBCustomItem * Owner, TColor Color, int Rows,
  972. TCSGetColorInfo OnGetColorInfo, TColorChangeEvent OnColorChange, bool SessionColors)
  973. {
  974. TTBXColorPalette * ColorPalette = new TTBXColorPalette(Owner);
  975. if (OnGetColorInfo != NULL)
  976. {
  977. TTBXCustomColorSet * ColorSet = new TTBXCustomColorSet(Owner);
  978. ColorPalette->InsertComponent(ColorSet);
  979. ColorPalette->ColorSet = ColorSet;
  980. // has to be set only after it's assigned to color palette
  981. ColorSet->ColCount = ColorCols;
  982. ColorSet->RowCount = Rows;
  983. ColorSet->OnGetColorInfo = OnGetColorInfo;
  984. ColorSet->Tag = static_cast<int>(SessionColors);
  985. }
  986. // clNone = no selection, see also ColorChange
  987. ColorPalette->Color = (Color != 0) ? Color : Vcl::Graphics::clNone;
  988. ColorPalette->OnChange = MakeMethod<TNotifyEvent>(NULL, ColorPaletteChange);
  989. ColorPalette->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
  990. Owner->Add(ColorPalette);
  991. Owner->Add(new TTBXSeparatorItem(Owner));
  992. }
  993. //---------------------------------------------------------------------------
  994. static void __fastcall CreateColorMenu(TComponent * AOwner, TColor Color,
  995. TColorChangeEvent OnColorChange, bool SessionColors,
  996. const UnicodeString & DefaultColorCaption, const UnicodeString & DefaultColorHint,
  997. const UnicodeString & HelpKeyword,
  998. const UnicodeString & ColorPickHint)
  999. {
  1000. TTBCustomItem * Owner = dynamic_cast<TTBCustomItem *>(AOwner);
  1001. if (DebugAlwaysTrue(Owner != NULL))
  1002. {
  1003. Owner->Clear();
  1004. TTBCustomItem * Item;
  1005. Item = new TTBXItem(Owner);
  1006. Item->Caption = DefaultColorCaption;
  1007. Item->Hint = DefaultColorHint;
  1008. Item->HelpKeyword = HelpKeyword;
  1009. Item->OnClick = MakeMethod<TNotifyEvent>(NULL, ColorDefaultClick);
  1010. Item->Checked = (Color == TColor(0));
  1011. Item->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
  1012. Owner->Add(Item);
  1013. Owner->Add(new TTBXSeparatorItem(Owner));
  1014. int CustomColorCount = 0;
  1015. UnicodeString Temp = LoadCustomColors(SessionColors);
  1016. while (!Temp.IsEmpty())
  1017. {
  1018. CustomColorCount++;
  1019. ExtractColorStr(Temp);
  1020. }
  1021. if (CustomColorCount > 0)
  1022. {
  1023. CustomColorCount = Min(CustomColorCount, UserColorCount);
  1024. int RowCount = ((CustomColorCount + ColorCols - 1) / ColorCols);
  1025. DebugAssert(RowCount <= UserColorRows);
  1026. CreateColorPalette(Owner, Color, RowCount,
  1027. MakeMethod<TCSGetColorInfo>(NULL, UserCustomColorSetGetColorInfo),
  1028. OnColorChange, SessionColors);
  1029. }
  1030. if (SessionColors)
  1031. {
  1032. CreateColorPalette(Owner, Color, StandardColorRows,
  1033. MakeMethod<TCSGetColorInfo>(NULL, SessionColorSetGetColorInfo),
  1034. OnColorChange, SessionColors);
  1035. }
  1036. else
  1037. {
  1038. CreateColorPalette(Owner, Color, -1, NULL, OnColorChange, SessionColors);
  1039. }
  1040. Owner->Add(new TTBXSeparatorItem(Owner));
  1041. Item = new TTBXItem(Owner);
  1042. Item->Caption = LoadStr(COLOR_PICK_CAPTION);
  1043. Item->Hint = ColorPickHint;
  1044. Item->HelpKeyword = HelpKeyword;
  1045. Item->OnClick = MakeMethod<TNotifyEvent>(NULL, ColorPickClick);
  1046. Item->InsertComponent(new TColorChangeData(OnColorChange, Color, SessionColors));
  1047. Owner->Add(Item);
  1048. }
  1049. }
  1050. //---------------------------------------------------------------------------
  1051. void __fastcall CreateSessionColorMenu(TComponent * AOwner, TColor Color,
  1052. TColorChangeEvent OnColorChange)
  1053. {
  1054. CreateColorMenu(
  1055. AOwner, Color, OnColorChange, true,
  1056. LoadStr(COLOR_TRUE_DEFAULT_CAPTION), LoadStr(COLOR_DEFAULT_HINT),
  1057. HELP_COLOR, LoadStr(COLOR_PICK_HINT));
  1058. }
  1059. //---------------------------------------------------------------------------
  1060. void __fastcall CreateEditorBackgroundColorMenu(TComponent * AOwner, TColor Color,
  1061. TColorChangeEvent OnColorChange)
  1062. {
  1063. CreateColorMenu(
  1064. AOwner, Color, OnColorChange, true,
  1065. LoadStr(COLOR_TRUE_DEFAULT_CAPTION), LoadStr(EDITOR_BACKGROUND_COLOR_HINT),
  1066. HELP_COLOR, LoadStr(EDITOR_BACKGROUND_COLOR_PICK_HINT));
  1067. }
  1068. //---------------------------------------------------------------------------
  1069. TPopupMenu * __fastcall CreateColorPopupMenu(TColor Color,
  1070. TColorChangeEvent OnColorChange)
  1071. {
  1072. std::unique_ptr<TTBXPopupMenu> PopupMenu(new TTBXPopupMenu(Application));
  1073. CreateColorMenu(
  1074. PopupMenu->Items, Color, OnColorChange, false,
  1075. LoadStr(COLOR_TRUE_DEFAULT_CAPTION), UnicodeString(),
  1076. HELP_NONE, UnicodeString());
  1077. return PopupMenu.release();
  1078. }
  1079. //---------------------------------------------------------------------------
  1080. struct TThreadParam
  1081. {
  1082. TThreadFunc ThreadFunc;
  1083. void * Parameter;
  1084. };
  1085. //---------------------------------------------------------------------------
  1086. static int __fastcall ThreadProc(void * AParam)
  1087. {
  1088. TThreadParam * Param = reinterpret_cast<TThreadParam *>(AParam);
  1089. unsigned int Result = Param->ThreadFunc(Param->Parameter);
  1090. delete Param;
  1091. EndThread(Result);
  1092. return Result;
  1093. }
  1094. //---------------------------------------------------------------------------
  1095. int __fastcall StartThread(void * SecurityAttributes, unsigned StackSize,
  1096. TThreadFunc ThreadFunc, void * Parameter, unsigned CreationFlags,
  1097. TThreadID & ThreadId)
  1098. {
  1099. TThreadParam * Param = new TThreadParam;
  1100. Param->ThreadFunc = ThreadFunc;
  1101. Param->Parameter = Parameter;
  1102. return BeginThread(SecurityAttributes, StackSize, ThreadProc, Param,
  1103. CreationFlags, ThreadId);
  1104. }
  1105. //---------------------------------------------------------------------------
  1106. static TShortCut FirstCtrlNumberShortCut = ShortCut(L'0', TShiftState() << ssCtrl);
  1107. static TShortCut LastCtrlNumberShortCut = ShortCut(L'9', TShiftState() << ssCtrl);
  1108. static TShortCut FirstCtrlKeyPadShortCut = ShortCut(VK_NUMPAD0, TShiftState() << ssCtrl);
  1109. static TShortCut LastCtrlKeyPadShortCut = ShortCut(VK_NUMPAD9, TShiftState() << ssCtrl);
  1110. static TShortCut FirstShiftCtrlAltLetterShortCut = ShortCut(L'A', TShiftState() << ssShift << ssCtrl << ssAlt);
  1111. static TShortCut LastShiftCtrlAltLetterShortCut = ShortCut(L'Z', TShiftState() << ssShift << ssCtrl << ssAlt);
  1112. //---------------------------------------------------------------------------
  1113. void __fastcall InitializeShortCutCombo(TComboBox * ComboBox,
  1114. const TShortCuts & ShortCuts)
  1115. {
  1116. ComboBox->Items->BeginUpdate();
  1117. try
  1118. {
  1119. ComboBox->Items->Clear();
  1120. ComboBox->Items->AddObject(LoadStr(SHORTCUT_NONE), reinterpret_cast<TObject* >(0));
  1121. for (TShortCut AShortCut = FirstCtrlNumberShortCut; AShortCut <= LastCtrlNumberShortCut; AShortCut++)
  1122. {
  1123. if (!ShortCuts.Has(AShortCut))
  1124. {
  1125. ComboBox->Items->AddObject(ShortCutToText(AShortCut), reinterpret_cast<TObject* >(AShortCut));
  1126. }
  1127. }
  1128. for (TShortCut AShortCut = FirstShiftCtrlAltLetterShortCut; AShortCut <= LastShiftCtrlAltLetterShortCut; AShortCut++)
  1129. {
  1130. if (!ShortCuts.Has(AShortCut))
  1131. {
  1132. ComboBox->Items->AddObject(ShortCutToText(AShortCut), reinterpret_cast<TObject* >(AShortCut));
  1133. }
  1134. }
  1135. }
  1136. __finally
  1137. {
  1138. ComboBox->Items->EndUpdate();
  1139. }
  1140. ComboBox->Style = csDropDownList;
  1141. ComboBox->DropDownCount = Max(ComboBox->DropDownCount, 16);
  1142. }
  1143. //---------------------------------------------------------------------------
  1144. void __fastcall SetShortCutCombo(TComboBox * ComboBox, TShortCut Value)
  1145. {
  1146. for (int Index = ComboBox->Items->Count - 1; Index >= 0; Index--)
  1147. {
  1148. TShortCut AShortCut = TShortCut(ComboBox->Items->Objects[Index]);
  1149. if (AShortCut == Value)
  1150. {
  1151. ComboBox->ItemIndex = Index;
  1152. break;
  1153. }
  1154. else if (AShortCut < Value)
  1155. {
  1156. DebugAssert(Value != 0);
  1157. ComboBox->Items->InsertObject(Index + 1, ShortCutToText(Value),
  1158. reinterpret_cast<TObject* >(Value));
  1159. ComboBox->ItemIndex = Index + 1;
  1160. break;
  1161. }
  1162. DebugAssert(Index > 0);
  1163. }
  1164. }
  1165. //---------------------------------------------------------------------------
  1166. TShortCut __fastcall GetShortCutCombo(TComboBox * ComboBox)
  1167. {
  1168. return TShortCut(ComboBox->Items->Objects[ComboBox->ItemIndex]);
  1169. }
  1170. //---------------------------------------------------------------------------
  1171. TShortCut __fastcall NormalizeCustomShortCut(TShortCut ShortCut)
  1172. {
  1173. if ((FirstCtrlKeyPadShortCut <= ShortCut) && (ShortCut <= LastCtrlKeyPadShortCut))
  1174. {
  1175. ShortCut = FirstCtrlNumberShortCut + (ShortCut - FirstCtrlKeyPadShortCut);
  1176. }
  1177. return ShortCut;
  1178. }
  1179. //---------------------------------------------------------------------------
  1180. bool __fastcall IsCustomShortCut(TShortCut ShortCut)
  1181. {
  1182. return
  1183. ((FirstCtrlNumberShortCut <= ShortCut) && (ShortCut <= LastCtrlNumberShortCut)) ||
  1184. ((FirstShiftCtrlAltLetterShortCut <= ShortCut) && (ShortCut <= LastShiftCtrlAltLetterShortCut));
  1185. }
  1186. //---------------------------------------------------------------------------
  1187. //---------------------------------------------------------------------------
  1188. class TMasterPasswordDialog : public TCustomDialog
  1189. {
  1190. public:
  1191. __fastcall TMasterPasswordDialog(TComponent * AOwner);
  1192. void Init(bool Current);
  1193. bool __fastcall Execute(UnicodeString & CurrentPassword, UnicodeString & NewPassword);
  1194. protected:
  1195. virtual void __fastcall DoValidate();
  1196. virtual void __fastcall DoChange(bool & CanSubmit);
  1197. private:
  1198. TEdit * CurrentEdit;
  1199. TEdit * NewEdit;
  1200. TEdit * ConfirmEdit;
  1201. TEdit * CreatePasswordEdit(int Label);
  1202. };
  1203. //---------------------------------------------------------------------------
  1204. // Need to have an Owner argument for SafeFormCreate
  1205. __fastcall TMasterPasswordDialog::TMasterPasswordDialog(TComponent *) :
  1206. TCustomDialog(EmptyStr)
  1207. {
  1208. }
  1209. //---------------------------------------------------------------------------
  1210. TEdit * TMasterPasswordDialog::CreatePasswordEdit(int Label)
  1211. {
  1212. TEdit * Result = new TEdit(this);
  1213. SetEditPasswordMode(Result);
  1214. AddEdit(Result, CreateLabel(LoadStr(Label)));
  1215. Result->MaxLength = PasswordMaxLength();
  1216. return Result;
  1217. }
  1218. //---------------------------------------------------------------------------
  1219. void TMasterPasswordDialog::Init(bool Current)
  1220. {
  1221. HelpKeyword = Current ? HELP_MASTER_PASSWORD_CURRENT : HELP_MASTER_PASSWORD_CHANGE;
  1222. Caption = LoadStr(MASTER_PASSWORD_CAPTION);
  1223. CurrentEdit = CreatePasswordEdit(MASTER_PASSWORD_CURRENT);
  1224. EnableControl(CurrentEdit, Current || WinConfiguration->UseMasterPassword);
  1225. if (!Current)
  1226. {
  1227. NewEdit = CreatePasswordEdit(MASTER_PASSWORD_NEW);
  1228. if (!WinConfiguration->UseMasterPassword)
  1229. {
  1230. ActiveControl = NewEdit;
  1231. }
  1232. ConfirmEdit = CreatePasswordEdit(MASTER_PASSWORD_CONFIRM);
  1233. }
  1234. else
  1235. {
  1236. NewEdit = NULL;
  1237. ConfirmEdit = NULL;
  1238. }
  1239. }
  1240. //---------------------------------------------------------------------------
  1241. bool __fastcall TMasterPasswordDialog::Execute(
  1242. UnicodeString & CurrentPassword, UnicodeString & NewPassword)
  1243. {
  1244. bool Result = TCustomDialog::Execute();
  1245. if (Result)
  1246. {
  1247. if (CurrentEdit->Enabled)
  1248. {
  1249. CurrentPassword = CurrentEdit->Text;
  1250. }
  1251. if (NewEdit != NULL)
  1252. {
  1253. NewPassword = NewEdit->Text;
  1254. }
  1255. }
  1256. return Result;
  1257. }
  1258. //---------------------------------------------------------------------------
  1259. void __fastcall TMasterPasswordDialog::DoChange(bool & CanSubmit)
  1260. {
  1261. CanSubmit =
  1262. (!WinConfiguration->UseMasterPassword || (IsValidPassword(CurrentEdit->Text) >= 0)) &&
  1263. ((NewEdit == NULL) || (IsValidPassword(NewEdit->Text) >= 0)) &&
  1264. ((ConfirmEdit == NULL) || (IsValidPassword(ConfirmEdit->Text) >= 0));
  1265. TCustomDialog::DoChange(CanSubmit);
  1266. }
  1267. //---------------------------------------------------------------------------
  1268. void __fastcall TMasterPasswordDialog::DoValidate()
  1269. {
  1270. TCustomDialog::DoValidate();
  1271. if (WinConfiguration->UseMasterPassword &&
  1272. !WinConfiguration->ValidateMasterPassword(CurrentEdit->Text))
  1273. {
  1274. CurrentEdit->SetFocus();
  1275. CurrentEdit->SelectAll();
  1276. throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_INCORRECT)));
  1277. }
  1278. if (NewEdit != NULL)
  1279. {
  1280. if (NewEdit->Text != ConfirmEdit->Text)
  1281. {
  1282. ConfirmEdit->SetFocus();
  1283. ConfirmEdit->SelectAll();
  1284. throw Exception(MainInstructions(LoadStr(MASTER_PASSWORD_DIFFERENT)));
  1285. }
  1286. int Valid = IsValidPassword(NewEdit->Text);
  1287. if (Valid <= 0)
  1288. {
  1289. DebugAssert(Valid == 0);
  1290. if (MessageDialog(LoadStr(MASTER_PASSWORD_SIMPLE2), qtWarning,
  1291. qaOK | qaCancel, HELP_MASTER_PASSWORD_SIMPLE) == qaCancel)
  1292. {
  1293. NewEdit->SetFocus();
  1294. NewEdit->SelectAll();
  1295. Abort();
  1296. }
  1297. }
  1298. }
  1299. }
  1300. //---------------------------------------------------------------------------
  1301. static bool __fastcall DoMasterPasswordDialog(bool Current,
  1302. UnicodeString & NewPassword)
  1303. {
  1304. bool Result;
  1305. // This can be a standalone dialog when opening session from commandline
  1306. TMasterPasswordDialog * Dialog = SafeFormCreate<TMasterPasswordDialog>();
  1307. try
  1308. {
  1309. Dialog->Init(Current);
  1310. UnicodeString CurrentPassword;
  1311. Result = Dialog->Execute(CurrentPassword, NewPassword);
  1312. if (Result)
  1313. {
  1314. if ((Current || WinConfiguration->UseMasterPassword) &&
  1315. DebugAlwaysTrue(!CurrentPassword.IsEmpty()))
  1316. {
  1317. WinConfiguration->SetMasterPassword(CurrentPassword);
  1318. }
  1319. }
  1320. }
  1321. __finally
  1322. {
  1323. delete Dialog;
  1324. }
  1325. return Result;
  1326. }
  1327. //---------------------------------------------------------------------------
  1328. bool __fastcall DoMasterPasswordDialog()
  1329. {
  1330. UnicodeString NewPassword;
  1331. bool Result = DoMasterPasswordDialog(true, NewPassword);
  1332. DebugAssert(NewPassword.IsEmpty());
  1333. return Result;
  1334. }
  1335. //---------------------------------------------------------------------------
  1336. bool __fastcall DoChangeMasterPasswordDialog(UnicodeString & NewPassword)
  1337. {
  1338. bool Result = DoMasterPasswordDialog(false, NewPassword);
  1339. return Result;
  1340. }
  1341. //---------------------------------------------------------------------------
  1342. void __fastcall MessageWithNoHelp(const UnicodeString & Message)
  1343. {
  1344. TMessageParams Params;
  1345. Params.AllowHelp = false; // to avoid recursion
  1346. if (MessageDialog(LoadStr(HELP_SEND_MESSAGE2), qtConfirmation,
  1347. qaOK | qaCancel, HELP_NONE, &Params) == qaOK)
  1348. {
  1349. SearchHelp(Message);
  1350. }
  1351. }
  1352. //---------------------------------------------------------------------------
  1353. void __fastcall CheckLogParam(TProgramParams * Params)
  1354. {
  1355. UnicodeString LogFile;
  1356. if (Params->FindSwitch(LOG_SWITCH, LogFile) && CheckSafe(Params))
  1357. {
  1358. Configuration->Usage->Inc(L"ScriptLog");
  1359. Configuration->TemporaryLogging(LogFile);
  1360. }
  1361. }
  1362. //---------------------------------------------------------------------------
  1363. bool __fastcall CheckXmlLogParam(TProgramParams * Params)
  1364. {
  1365. UnicodeString LogFile;
  1366. bool Result =
  1367. Params->FindSwitch(L"XmlLog", LogFile) &&
  1368. CheckSafe(Params);
  1369. if (Result)
  1370. {
  1371. Configuration->Usage->Inc(L"ScriptXmlLog");
  1372. Configuration->TemporaryActionsLogging(LogFile);
  1373. if (Params->FindSwitch(L"XmlLogRequired"))
  1374. {
  1375. Configuration->LogActionsRequired = true;
  1376. }
  1377. }
  1378. return Result;
  1379. }
  1380. //---------------------------------------------------------------------------
  1381. bool __fastcall CheckSafe(TProgramParams * Params)
  1382. {
  1383. // Originally we warned when the test didn't pass,
  1384. // but it would actually be helping hackers, so let's be silent.
  1385. // Earlier we tested presence of any URL on command-line.
  1386. // That was added to prevent abusing URL handler in 3.8.2 (2006).
  1387. // Later in 4.0.4 (2007) an /Unsafe switch was added to URL handler registration.
  1388. // So by now, we can check for the switch only and
  1389. // do not limit user from combining URL with say
  1390. // /rawconfig
  1391. return !Params->FindSwitch(UNSAFE_SWITCH);
  1392. }