GUITools.cpp 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include <shlobj.h>
  5. #include <Common.h>
  6. #include "GUITools.h"
  7. #include "GUIConfiguration.h"
  8. #include <TextsCore.h>
  9. #include <CoreMain.h>
  10. #include <SessionData.h>
  11. #include <WinInterface.h>
  12. #include <TbxUtils.hpp>
  13. #include <Math.hpp>
  14. #include <WebBrowserEx.hpp>
  15. #include <Tools.h>
  16. #include "PngImageList.hpp"
  17. #include <StrUtils.hpp>
  18. #include <limits>
  19. #include <Glyphs.h>
  20. #include <PasTools.hpp>
  21. #include <VCLCommon.h>
  22. #include <Vcl.ScreenTips.hpp>
  23. #include "Animations96.h"
  24. #include "Animations120.h"
  25. #include "Animations144.h"
  26. #include "Animations192.h"
  27. //---------------------------------------------------------------------------
  28. #pragma package(smart_init)
  29. //---------------------------------------------------------------------------
  30. extern const UnicodeString PageantTool = L"pageant.exe";
  31. extern const UnicodeString PuttygenTool = L"puttygen.exe";
  32. //---------------------------------------------------------------------------
  33. bool __fastcall FindFile(UnicodeString & Path)
  34. {
  35. bool Result = FileExists(ApiPath(Path));
  36. if (!Result)
  37. {
  38. UnicodeString ProgramFiles32 = IncludeTrailingBackslash(GetEnvironmentVariable(L"ProgramFiles"));
  39. UnicodeString ProgramFiles64 = IncludeTrailingBackslash(GetEnvironmentVariable(L"ProgramW6432"));
  40. if (!ProgramFiles32.IsEmpty() &&
  41. SameText(Path.SubString(1, ProgramFiles32.Length()), ProgramFiles32) &&
  42. !ProgramFiles64.IsEmpty())
  43. {
  44. UnicodeString Path64 =
  45. ProgramFiles64 + Path.SubString(ProgramFiles32.Length() + 1, Path.Length() - ProgramFiles32.Length());
  46. if (FileExists(ApiPath(Path64)))
  47. {
  48. Path = Path64;
  49. Result = true;
  50. }
  51. }
  52. }
  53. if (!Result)
  54. {
  55. UnicodeString Paths = GetEnvironmentVariable(L"PATH");
  56. if (!Paths.IsEmpty())
  57. {
  58. UnicodeString NewPath = FileSearch(ExtractFileName(Path), Paths);
  59. Result = !NewPath.IsEmpty();
  60. if (Result)
  61. {
  62. Path = NewPath;
  63. }
  64. }
  65. }
  66. return Result;
  67. }
  68. //---------------------------------------------------------------------------
  69. void __fastcall OpenSessionInPutty(const UnicodeString PuttyPath,
  70. TSessionData * SessionData)
  71. {
  72. UnicodeString Program, AParams, Dir;
  73. SplitCommand(PuttyPath, Program, AParams, Dir);
  74. Program = ExpandEnvironmentVariables(Program);
  75. if (FindFile(Program))
  76. {
  77. AParams = ExpandEnvironmentVariables(AParams);
  78. UnicodeString Password = GUIConfiguration->PuttyPassword ? SessionData->Password : UnicodeString();
  79. TCustomCommandData Data(SessionData, SessionData->UserName, Password);
  80. TRemoteCustomCommand RemoteCustomCommand(Data, SessionData->RemoteDirectory);
  81. TWinInteractiveCustomCommand InteractiveCustomCommand(
  82. &RemoteCustomCommand, L"PuTTY", UnicodeString());
  83. UnicodeString Params =
  84. RemoteCustomCommand.Complete(InteractiveCustomCommand.Complete(AParams, false), true);
  85. UnicodeString PuttyParams;
  86. if (!RemoteCustomCommand.IsSiteCommand(AParams))
  87. {
  88. UnicodeString SessionName;
  89. TRegistryStorage * Storage = NULL;
  90. TSessionData * ExportData = NULL;
  91. TRegistryStorage * SourceStorage = NULL;
  92. try
  93. {
  94. Storage = new TRegistryStorage(Configuration->PuttySessionsKey);
  95. Storage->AccessMode = smReadWrite;
  96. // make it compatible with putty
  97. Storage->MungeStringValues = false;
  98. Storage->ForceAnsi = true;
  99. if (Storage->OpenRootKey(true))
  100. {
  101. if (Storage->KeyExists(SessionData->StorageKey))
  102. {
  103. SessionName = SessionData->SessionName;
  104. }
  105. else
  106. {
  107. SourceStorage = new TRegistryStorage(Configuration->PuttySessionsKey);
  108. SourceStorage->MungeStringValues = false;
  109. SourceStorage->ForceAnsi = true;
  110. if (SourceStorage->OpenSubKey(StoredSessions->DefaultSettings->Name, false) &&
  111. Storage->OpenSubKey(GUIConfiguration->PuttySession, true))
  112. {
  113. Storage->Copy(SourceStorage);
  114. Storage->CloseSubKey();
  115. }
  116. ExportData = new TSessionData(L"");
  117. ExportData->Assign(SessionData);
  118. ExportData->Modified = true;
  119. ExportData->Name = GUIConfiguration->PuttySession;
  120. ExportData->WinTitle = SessionData->SessionName;
  121. ExportData->Password = L"";
  122. if (SessionData->FSProtocol == fsFTP)
  123. {
  124. if (GUIConfiguration->TelnetForFtpInPutty)
  125. {
  126. ExportData->PuttyProtocol = PuttyTelnetProtocol;
  127. ExportData->PortNumber = TelnetPortNumber;
  128. // PuTTY does not allow -pw for telnet
  129. Password = L"";
  130. }
  131. else
  132. {
  133. ExportData->PuttyProtocol = PuttySshProtocol;
  134. ExportData->PortNumber = SshPortNumber;
  135. }
  136. }
  137. ExportData->Save(Storage, true);
  138. SessionName = GUIConfiguration->PuttySession;
  139. }
  140. }
  141. }
  142. __finally
  143. {
  144. delete Storage;
  145. delete ExportData;
  146. delete SourceStorage;
  147. }
  148. UnicodeString LoadSwitch = L"-load";
  149. int P = Params.LowerCase().Pos(LoadSwitch + L" ");
  150. if ((P == 0) || ((P > 1) && (Params[P - 1] != L' ')))
  151. {
  152. AddToList(PuttyParams, FORMAT(L"%s %s", (LoadSwitch, EscapePuttyCommandParam(SessionName))), L" ");
  153. }
  154. }
  155. if (!Password.IsEmpty() && !RemoteCustomCommand.IsPasswordCommand(AParams))
  156. {
  157. AddToList(PuttyParams, FORMAT(L"-pw %s", (EscapePuttyCommandParam(Password))), L" ");
  158. }
  159. AddToList(PuttyParams, Params, L" ");
  160. // PuTTY is started in its binary directory to allow relative paths in private key,
  161. // when opening PuTTY's own stored session.
  162. ExecuteShellChecked(Program, PuttyParams, true);
  163. }
  164. else
  165. {
  166. throw Exception(FMTLOAD(FILE_NOT_FOUND, (Program)));
  167. }
  168. }
  169. //---------------------------------------------------------------------------
  170. bool __fastcall FindTool(const UnicodeString & Name, UnicodeString & Path)
  171. {
  172. UnicodeString AppPath = IncludeTrailingBackslash(ExtractFilePath(Application->ExeName));
  173. Path = AppPath + Name;
  174. bool Result = true;
  175. if (!FileExists(ApiPath(Path)))
  176. {
  177. Path = AppPath + L"PuTTY\\" + Name;
  178. if (!FileExists(ApiPath(Path)))
  179. {
  180. Path = Name;
  181. if (!FindFile(Path))
  182. {
  183. Result = false;
  184. }
  185. }
  186. }
  187. return Result;
  188. }
  189. //---------------------------------------------------------------------------
  190. bool __fastcall CopyCommandToClipboard(const UnicodeString & Command)
  191. {
  192. bool Result = UseAlternativeFunction() && IsKeyPressed(VK_CONTROL);
  193. if (Result)
  194. {
  195. TInstantOperationVisualizer Visualizer;
  196. CopyToClipboard(Command);
  197. }
  198. return Result;
  199. }
  200. //---------------------------------------------------------------------------
  201. static bool __fastcall DoExecuteShell(const UnicodeString Path, const UnicodeString Params,
  202. bool ChangeWorkingDirectory, HANDLE * Handle)
  203. {
  204. bool Result = CopyCommandToClipboard(FormatCommand(Path, Params));
  205. if (Result)
  206. {
  207. if (Handle != NULL)
  208. {
  209. *Handle = NULL;
  210. }
  211. }
  212. else
  213. {
  214. UnicodeString Directory = ExtractFilePath(Path);
  215. TShellExecuteInfoW ExecuteInfo;
  216. memset(&ExecuteInfo, 0, sizeof(ExecuteInfo));
  217. ExecuteInfo.cbSize = sizeof(ExecuteInfo);
  218. ExecuteInfo.fMask =
  219. SEE_MASK_FLAG_NO_UI |
  220. FLAGMASK((Handle != NULL), SEE_MASK_NOCLOSEPROCESS);
  221. ExecuteInfo.hwnd = Application->Handle;
  222. ExecuteInfo.lpFile = (wchar_t*)Path.data();
  223. ExecuteInfo.lpParameters = (wchar_t*)Params.data();
  224. ExecuteInfo.lpDirectory = (ChangeWorkingDirectory ? Directory.c_str() : NULL);
  225. ExecuteInfo.nShow = SW_SHOW;
  226. Result = (ShellExecuteEx(&ExecuteInfo) != 0);
  227. if (Result)
  228. {
  229. if (Handle != NULL)
  230. {
  231. *Handle = ExecuteInfo.hProcess;
  232. }
  233. }
  234. }
  235. return Result;
  236. }
  237. //---------------------------------------------------------------------------
  238. void __fastcall ExecuteShellChecked(const UnicodeString Path, const UnicodeString Params, bool ChangeWorkingDirectory)
  239. {
  240. if (!DoExecuteShell(Path, Params, ChangeWorkingDirectory, NULL))
  241. {
  242. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Path)));
  243. }
  244. }
  245. //---------------------------------------------------------------------------
  246. void __fastcall ExecuteShellChecked(const UnicodeString Command)
  247. {
  248. UnicodeString Program, Params, Dir;
  249. SplitCommand(Command, Program, Params, Dir);
  250. ExecuteShellChecked(Program, Params);
  251. }
  252. //---------------------------------------------------------------------------
  253. bool __fastcall ExecuteShell(const UnicodeString Path, const UnicodeString Params,
  254. HANDLE & Handle)
  255. {
  256. return DoExecuteShell(Path, Params, false, &Handle);
  257. }
  258. //---------------------------------------------------------------------------
  259. void __fastcall ExecuteShellCheckedAndWait(const UnicodeString Command,
  260. TProcessMessagesEvent ProcessMessages)
  261. {
  262. UnicodeString Program, Params, Dir;
  263. SplitCommand(Command, Program, Params, Dir);
  264. HANDLE ProcessHandle;
  265. bool Result = DoExecuteShell(Program, Params, false, &ProcessHandle);
  266. if (!Result)
  267. {
  268. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Program)));
  269. }
  270. else
  271. {
  272. if (ProcessHandle != NULL) // only if command was copied to clipboard only
  273. {
  274. if (ProcessMessages != NULL)
  275. {
  276. unsigned long WaitResult;
  277. do
  278. {
  279. // Same as in ExecuteProcessAndReadOutput
  280. WaitResult = WaitForSingleObject(ProcessHandle, 200);
  281. if (WaitResult == WAIT_FAILED)
  282. {
  283. throw Exception(LoadStr(DOCUMENT_WAIT_ERROR));
  284. }
  285. ProcessMessages();
  286. }
  287. while (WaitResult == WAIT_TIMEOUT);
  288. }
  289. else
  290. {
  291. WaitForSingleObject(ProcessHandle, INFINITE);
  292. }
  293. }
  294. }
  295. }
  296. //---------------------------------------------------------------------------
  297. bool __fastcall SpecialFolderLocation(int PathID, UnicodeString & Path)
  298. {
  299. LPITEMIDLIST Pidl;
  300. wchar_t Buf[256];
  301. if (SHGetSpecialFolderLocation(NULL, PathID, &Pidl) == NO_ERROR &&
  302. SHGetPathFromIDList(Pidl, Buf))
  303. {
  304. Path = UnicodeString(Buf);
  305. return true;
  306. }
  307. return false;
  308. }
  309. //---------------------------------------------------------------------------
  310. UnicodeString __fastcall UniqTempDir(const UnicodeString BaseDir, const UnicodeString Identity,
  311. bool Mask)
  312. {
  313. DebugAssert(!BaseDir.IsEmpty());
  314. UnicodeString TempDir;
  315. do
  316. {
  317. TempDir = IncludeTrailingBackslash(BaseDir) + Identity;
  318. if (Mask)
  319. {
  320. TempDir += L"?????";
  321. }
  322. else
  323. {
  324. TempDir += IncludeTrailingBackslash(FormatDateTime(L"nnzzz", Now()));
  325. }
  326. }
  327. while (!Mask && DirectoryExists(ApiPath(TempDir)));
  328. return TempDir;
  329. }
  330. //---------------------------------------------------------------------------
  331. bool __fastcall DeleteDirectory(const UnicodeString DirName)
  332. {
  333. TSearchRecChecked sr;
  334. bool retval = true;
  335. if (FindFirstUnchecked(DirName + L"\\*", faAnyFile, sr) == 0) // VCL Function
  336. {
  337. if (FLAGSET(sr.Attr, faDirectory))
  338. {
  339. if (sr.Name != L"." && sr.Name != L"..")
  340. retval = DeleteDirectory(DirName + L"\\" + sr.Name);
  341. }
  342. else
  343. {
  344. retval = DeleteFile(ApiPath(DirName + L"\\" + sr.Name));
  345. }
  346. if (retval)
  347. {
  348. while (FindNextChecked(sr) == 0)
  349. { // VCL Function
  350. if (FLAGSET(sr.Attr, faDirectory))
  351. {
  352. if (sr.Name != L"." && sr.Name != L"..")
  353. retval = DeleteDirectory(DirName + L"\\" + sr.Name);
  354. }
  355. else
  356. {
  357. retval = DeleteFile(ApiPath(DirName + L"\\" + sr.Name));
  358. }
  359. if (!retval) break;
  360. }
  361. }
  362. }
  363. FindClose(sr);
  364. if (retval) retval = RemoveDir(ApiPath(DirName)); // VCL function
  365. return retval;
  366. }
  367. //---------------------------------------------------------------------------
  368. void __fastcall AddSessionColorImage(
  369. TCustomImageList * ImageList, TColor Color, int MaskIndex)
  370. {
  371. // This overly complex drawing is here to support color button on SiteAdvanced
  372. // dialog. There we use plain TImageList, instead of TPngImageList,
  373. // TButton does not work with transparent images
  374. // (not even TBitmap with Transparent = true)
  375. std::unique_ptr<TBitmap> MaskBitmap(new TBitmap());
  376. ImageList->GetBitmap(MaskIndex, MaskBitmap.get());
  377. std::unique_ptr<TPngImage> MaskImage(new TPngImage());
  378. MaskImage->Assign(MaskBitmap.get());
  379. std::unique_ptr<TPngImage> ColorImage(new TPngImage(COLOR_RGB, 16, ImageList->Width, ImageList->Height));
  380. TColor MaskTransparentColor = MaskImage->Pixels[0][0];
  381. TColor TransparentColor = MaskTransparentColor;
  382. // Expecting that the color to be replaced is in the centre of the image (HACK)
  383. TColor MaskColor = MaskImage->Pixels[ImageList->Width / 2][ImageList->Height / 2];
  384. for (int Y = 0; Y < ImageList->Height; Y++)
  385. {
  386. for (int X = 0; X < ImageList->Width; X++)
  387. {
  388. TColor SourceColor = MaskImage->Pixels[X][Y];
  389. TColor DestColor;
  390. // this branch is pointless as long as MaskTransparentColor and
  391. // TransparentColor are the same
  392. if (SourceColor == MaskTransparentColor)
  393. {
  394. DestColor = TransparentColor;
  395. }
  396. else if (SourceColor == MaskColor)
  397. {
  398. DestColor = Color;
  399. }
  400. else
  401. {
  402. DestColor = SourceColor;
  403. }
  404. ColorImage->Pixels[X][Y] = DestColor;
  405. }
  406. }
  407. std::unique_ptr<TBitmap> Bitmap(new TBitmap());
  408. Bitmap->SetSize(ImageList->Width, ImageList->Height);
  409. ColorImage->AssignTo(Bitmap.get());
  410. ImageList->AddMasked(Bitmap.get(), TransparentColor);
  411. }
  412. //---------------------------------------------------------------------------
  413. void __fastcall SetSubmenu(TTBXCustomItem * Item)
  414. {
  415. class TTBXPublicItem : public TTBXCustomItem
  416. {
  417. public:
  418. __property ItemStyle;
  419. };
  420. TTBXPublicItem * PublicItem = reinterpret_cast<TTBXPublicItem *>(Item);
  421. DebugAssert(PublicItem != NULL);
  422. // See TTBItemViewer.IsPtInButtonPart (called from TTBItemViewer.MouseDown)
  423. PublicItem->ItemStyle = PublicItem->ItemStyle << tbisSubmenu;
  424. }
  425. //---------------------------------------------------------------------------
  426. bool __fastcall IsEligibleForApplyingTabs(
  427. UnicodeString Line, int & TabPos, UnicodeString & Start, UnicodeString & Remaining)
  428. {
  429. bool Result = false;
  430. TabPos = Line.Pos(L"\t");
  431. if (TabPos > 0)
  432. {
  433. Remaining = Line.SubString(TabPos + 1, Line.Length() - TabPos);
  434. // WORKAROUND
  435. // Some translations still use obsolete hack of consecutive tabs to aling the contents.
  436. // Delete these, so that the following check does not fail on this
  437. while (Remaining.SubString(1, 1) == L"\t")
  438. {
  439. Remaining.Delete(1, 1);
  440. }
  441. // We do not have, not support, mutiple tabs on a single line
  442. if (DebugAlwaysTrue(Remaining.Pos(L"\t") == 0))
  443. {
  444. Start = Line.SubString(1, TabPos - 1);
  445. // WORKAROUND
  446. // Previously we padded the string before tab with spaces,
  447. // to aling the contents across multiple lines
  448. Start = Start.TrimRight();
  449. // at least two normal spaces for separation
  450. Start += L" ";
  451. Result = true;
  452. }
  453. }
  454. return Result;
  455. }
  456. //---------------------------------------------------------------------------
  457. static int __fastcall CalculateWidthByLength(UnicodeString Text, void * /*Arg*/)
  458. {
  459. return Text.Length();
  460. }
  461. //---------------------------------------------------------------------------
  462. void __fastcall ApplyTabs(
  463. UnicodeString & Text, wchar_t Padding,
  464. TCalculateWidth CalculateWidth, void * CalculateWidthArg)
  465. {
  466. if (CalculateWidth == NULL)
  467. {
  468. DebugAssert(CalculateWidthArg == NULL);
  469. CalculateWidth = CalculateWidthByLength;
  470. }
  471. std::unique_ptr<TStringList> Lines(TextToStringList(Text));
  472. int MaxWidth = -1;
  473. for (int Index = 0; Index < Lines->Count; Index++)
  474. {
  475. UnicodeString Line = Lines->Strings[Index];
  476. int TabPos;
  477. UnicodeString Start;
  478. UnicodeString Remaining;
  479. if (IsEligibleForApplyingTabs(Line, TabPos, Start, Remaining))
  480. {
  481. int Width = CalculateWidth(Start, CalculateWidthArg);
  482. MaxWidth = Max(MaxWidth, Width);
  483. }
  484. }
  485. // Optimization and also to prevent potential regression for texts without tabs
  486. if (MaxWidth >= 0)
  487. {
  488. for (int Index = 0; Index < Lines->Count; Index++)
  489. {
  490. UnicodeString Line = Lines->Strings[Index];
  491. int TabPos;
  492. UnicodeString Start;
  493. UnicodeString Remaining;
  494. if (IsEligibleForApplyingTabs(Line, TabPos, Start, Remaining))
  495. {
  496. int Width;
  497. int Iterations = 0;
  498. while ((Width = CalculateWidth(Start, CalculateWidthArg)) < MaxWidth)
  499. {
  500. int Wider = CalculateWidth(Start + Padding, CalculateWidthArg);
  501. // If padded string is wider than max width by more pixels
  502. // than non-padded string is shorter than max width
  503. if ((Wider > MaxWidth) && ((Wider - MaxWidth) > (MaxWidth - Width)))
  504. {
  505. break;
  506. }
  507. Start += Padding;
  508. Iterations++;
  509. // In rare case a tab is zero-width with some strange font (like HYLE)
  510. if (Iterations > 100)
  511. {
  512. break;
  513. }
  514. }
  515. Lines->Strings[Index] = Start + Remaining;
  516. }
  517. }
  518. Text = Lines->Text;
  519. // remove trailing newline
  520. Text = Text.TrimRight();
  521. }
  522. }
  523. //---------------------------------------------------------------------------
  524. static void __fastcall DoSelectScaledImageList(TImageList * ImageList)
  525. {
  526. TImageList * MatchingList = NULL;
  527. int MachingPixelsPerInch = 0;
  528. int PixelsPerInch = GetComponentPixelsPerInch(ImageList);
  529. for (int Index = 0; Index < ImageList->Owner->ComponentCount; Index++)
  530. {
  531. TImageList * OtherList = dynamic_cast<TImageList *>(ImageList->Owner->Components[Index]);
  532. if ((OtherList != NULL) &&
  533. (OtherList != ImageList) &&
  534. StartsStr(ImageList->Name, OtherList->Name))
  535. {
  536. UnicodeString OtherListPixelsPerInchStr =
  537. OtherList->Name.SubString(
  538. ImageList->Name.Length() + 1, OtherList->Name.Length() - ImageList->Name.Length());
  539. int OtherListPixelsPerInch = StrToInt(OtherListPixelsPerInchStr);
  540. if ((OtherListPixelsPerInch <= PixelsPerInch) &&
  541. ((MatchingList == NULL) ||
  542. (MachingPixelsPerInch < OtherListPixelsPerInch)))
  543. {
  544. MatchingList = OtherList;
  545. MachingPixelsPerInch = OtherListPixelsPerInch;
  546. }
  547. }
  548. }
  549. if (MatchingList != NULL)
  550. {
  551. UnicodeString ImageListBackupName = ImageList->Name + IntToStr(USER_DEFAULT_SCREEN_DPI);
  552. if (ImageList->Owner->FindComponent(ImageListBackupName) == NULL)
  553. {
  554. TImageList * ImageListBackup;
  555. TPngImageList * PngImageList = dynamic_cast<TPngImageList *>(ImageList);
  556. if (PngImageList != NULL)
  557. {
  558. ImageListBackup = new TPngImageList(ImageList->Owner);
  559. }
  560. else
  561. {
  562. ImageListBackup = new TImageList(ImageList->Owner);
  563. }
  564. ImageListBackup->Name = ImageListBackupName;
  565. ImageList->Owner->InsertComponent(ImageListBackup);
  566. CopyImageList(ImageListBackup, ImageList);
  567. }
  568. CopyImageList(ImageList, MatchingList);
  569. }
  570. }
  571. //---------------------------------------------------------------------------
  572. static void __fastcall ImageListRescale(TComponent * Sender, TObject * /*Token*/)
  573. {
  574. TImageList * ImageList = DebugNotNull(dynamic_cast<TImageList *>(Sender));
  575. DoSelectScaledImageList(ImageList);
  576. }
  577. //---------------------------------------------------------------------------
  578. void __fastcall SelectScaledImageList(TImageList * ImageList)
  579. {
  580. DoSelectScaledImageList(ImageList);
  581. SetRescaleFunction(ImageList, ImageListRescale);
  582. }
  583. //---------------------------------------------------------------------------
  584. void __fastcall CopyImageList(TImageList * TargetList, TImageList * SourceList)
  585. {
  586. // Maybe this is not necessary, once the TPngImageList::Assign was fixed
  587. TPngImageList * PngTargetList = dynamic_cast<TPngImageList *>(TargetList);
  588. TPngImageList * PngSourceList = dynamic_cast<TPngImageList *>(SourceList);
  589. TargetList->Clear();
  590. TargetList->Height = SourceList->Height;
  591. TargetList->Width = SourceList->Width;
  592. if ((PngTargetList != NULL) && (PngSourceList != NULL))
  593. {
  594. // AddImages won't copy the names and we need them for
  595. // LoadDialogImage and TFrameAnimation
  596. PngTargetList->PngImages->Assign(PngSourceList->PngImages);
  597. }
  598. else
  599. {
  600. TargetList->AddImages(SourceList);
  601. }
  602. }
  603. //---------------------------------------------------------------------------
  604. static bool __fastcall DoLoadDialogImage(TImage * Image, const UnicodeString & ImageName)
  605. {
  606. bool Result = false;
  607. if (GlyphsModule != NULL)
  608. {
  609. TPngImageList * DialogImages = GetDialogImages(Image);
  610. int Index;
  611. for (Index = 0; Index < DialogImages->PngImages->Count; Index++)
  612. {
  613. TPngImageCollectionItem * PngItem = DialogImages->PngImages->Items[Index];
  614. if (SameText(PngItem->Name, ImageName))
  615. {
  616. Image->Picture->Assign(PngItem->PngImage);
  617. break;
  618. }
  619. }
  620. DebugAssert(Index < DialogImages->PngImages->Count);
  621. Result = true;
  622. }
  623. // When showing an exception from wWinMain, the images are released already.
  624. // Non-existence of the glyphs module is just a good indication of that.
  625. // We expect errors only.
  626. else if (ImageName == L"Error")
  627. {
  628. Image->Picture->Icon->Handle = LoadIcon(0, IDI_HAND);
  629. }
  630. // For showing an information about trace files
  631. else if (DebugAlwaysTrue(ImageName == L"Information"))
  632. {
  633. Image->Picture->Icon->Handle = LoadIcon(0, IDI_APPLICATION);
  634. }
  635. return Result;
  636. }
  637. //---------------------------------------------------------------------------
  638. class TDialogImageName : public TObject
  639. {
  640. public:
  641. UnicodeString ImageName;
  642. };
  643. //---------------------------------------------------------------------------
  644. static void __fastcall DialogImageRescale(TComponent * Sender, TObject * Token)
  645. {
  646. TImage * Image = DebugNotNull(dynamic_cast<TImage *>(Sender));
  647. TDialogImageName * DialogImageName = DebugNotNull(dynamic_cast<TDialogImageName *>(Token));
  648. DoLoadDialogImage(Image, DialogImageName->ImageName);
  649. }
  650. //---------------------------------------------------------------------------
  651. void __fastcall LoadDialogImage(TImage * Image, const UnicodeString & ImageName)
  652. {
  653. if (DoLoadDialogImage(Image, ImageName))
  654. {
  655. TDialogImageName * DialogImageName = new TDialogImageName();
  656. DialogImageName->ImageName = ImageName;
  657. SetRescaleFunction(Image, DialogImageRescale, DialogImageName, true);
  658. }
  659. }
  660. //---------------------------------------------------------------------------
  661. int __fastcall DialogImageSize(TForm * Form)
  662. {
  663. return ScaleByPixelsPerInch(32, Form);
  664. }
  665. //---------------------------------------------------------------------------
  666. void __fastcall HideComponentsPanel(TForm * Form)
  667. {
  668. TComponent * Component = DebugNotNull(Form->FindComponent(L"ComponentsPanel"));
  669. TPanel * Panel = DebugNotNull(dynamic_cast<TPanel *>(Component));
  670. DebugAssert(Panel->Align == alBottom);
  671. int Offset = Panel->Height;
  672. Panel->Visible = false;
  673. Panel->Height = 0;
  674. Form->Height -= Offset;
  675. for (int Index = 0; Index < Form->ControlCount; Index++)
  676. {
  677. TControl * Control = Form->Controls[Index];
  678. // Shift back bottom-anchored controls
  679. // (needed for toolbar panel on Progress window and buttons on Preferences dialog),
  680. if ((Control->Align == alNone) &&
  681. Control->Anchors.Contains(akBottom) &&
  682. !Control->Anchors.Contains(akTop))
  683. {
  684. Control->Top += Offset;
  685. }
  686. // Resize back all-anchored controls
  687. // (needed for main panel on Preferences dialog),
  688. if (Control->Anchors.Contains(akBottom) &&
  689. Control->Anchors.Contains(akTop))
  690. {
  691. Control->Height += Offset;
  692. }
  693. }
  694. }
  695. //---------------------------------------------------------------------------
  696. class TBrowserViewer : public TWebBrowserEx
  697. {
  698. public:
  699. __fastcall virtual TBrowserViewer(TComponent* AOwner);
  700. void __fastcall AddLinkHandler(
  701. const UnicodeString & Url, TNotifyEvent Handler);
  702. void __fastcall NavigateToUrl(const UnicodeString & Url);
  703. TControl * LoadingPanel;
  704. protected:
  705. DYNAMIC void __fastcall DoContextPopup(const TPoint & MousePos, bool & Handled);
  706. void __fastcall DocumentComplete(
  707. TObject * Sender, const _di_IDispatch Disp, const OleVariant & URL);
  708. void __fastcall BeforeNavigate2(
  709. TObject * Sender, const _di_IDispatch Disp, const OleVariant & URL,
  710. const OleVariant & Flags, const OleVariant & TargetFrameName,
  711. const OleVariant & PostData, const OleVariant & Headers, WordBool & Cancel);
  712. bool FComplete;
  713. std::map<UnicodeString, TNotifyEvent> FHandlers;
  714. };
  715. //---------------------------------------------------------------------------
  716. __fastcall TBrowserViewer::TBrowserViewer(TComponent* AOwner) :
  717. TWebBrowserEx(AOwner)
  718. {
  719. FComplete = false;
  720. OnDocumentComplete = DocumentComplete;
  721. OnBeforeNavigate2 = BeforeNavigate2;
  722. LoadingPanel = NULL;
  723. }
  724. //---------------------------------------------------------------------------
  725. void __fastcall TBrowserViewer::AddLinkHandler(
  726. const UnicodeString & Url, TNotifyEvent Handler)
  727. {
  728. FHandlers.insert(std::make_pair(Url, Handler));
  729. }
  730. //---------------------------------------------------------------------------
  731. void __fastcall TBrowserViewer::DoContextPopup(const TPoint & MousePos, bool & Handled)
  732. {
  733. // suppress built-in context menu
  734. Handled = true;
  735. TWebBrowserEx::DoContextPopup(MousePos, Handled);
  736. }
  737. //---------------------------------------------------------------------------
  738. void __fastcall TBrowserViewer::DocumentComplete(
  739. TObject * /*Sender*/, const _di_IDispatch /*Disp*/, const OleVariant & /*URL*/)
  740. {
  741. SetBrowserDesignModeOff(this);
  742. FComplete = true;
  743. if (LoadingPanel != NULL)
  744. {
  745. LoadingPanel->Visible = false;
  746. }
  747. }
  748. //---------------------------------------------------------------------------
  749. void __fastcall TBrowserViewer::BeforeNavigate2(
  750. TObject * /*Sender*/, const _di_IDispatch /*Disp*/, const OleVariant & AURL,
  751. const OleVariant & /*Flags*/, const OleVariant & /*TargetFrameName*/,
  752. const OleVariant & /*PostData*/, const OleVariant & /*Headers*/, WordBool & Cancel)
  753. {
  754. // If OnDocumentComplete was not called yet, is has to be our initial message URL,
  755. // opened using TWebBrowserEx::Navigate(), allow it.
  756. // Otherwise it's user navigating, block that and open link
  757. // in an external browser, possibly adding campaign parameters on the way.
  758. if (FComplete)
  759. {
  760. Cancel = 1;
  761. UnicodeString URL = AURL;
  762. if (FHandlers.count(URL) > 0)
  763. {
  764. FHandlers[URL](this);
  765. }
  766. else
  767. {
  768. OpenBrowser(URL);
  769. }
  770. }
  771. }
  772. //---------------------------------------------------------------------------
  773. void __fastcall TBrowserViewer::NavigateToUrl(const UnicodeString & Url)
  774. {
  775. FComplete = false;
  776. Navigate(Url.c_str());
  777. }
  778. //---------------------------------------------------------------------------
  779. TPanel * __fastcall CreateLabelPanel(TPanel * Parent, const UnicodeString & Label)
  780. {
  781. TPanel * Result = CreateBlankPanel(Parent);
  782. Result->Parent = Parent;
  783. Result->Align = alClient;
  784. Result->Caption = Label;
  785. return Result;
  786. }
  787. //---------------------------------------------------------------------------
  788. TWebBrowserEx * __fastcall CreateBrowserViewer(TPanel * Parent, const UnicodeString & LoadingLabel)
  789. {
  790. TBrowserViewer * Result = new TBrowserViewer(Parent);
  791. // TWebBrowserEx has its own unrelated Name and Parent properties.
  792. // The name is used in DownloadUpdate().
  793. static_cast<TWinControl *>(Result)->Name = L"BrowserViewer";
  794. static_cast<TWinControl *>(Result)->Parent = Parent;
  795. Result->Align = alClient;
  796. Result->ControlBorder = cbNone;
  797. Result->LoadingPanel = CreateLabelPanel(Parent, LoadingLabel);
  798. return Result;
  799. }
  800. //---------------------------------------------------------------------------
  801. void __fastcall SetBrowserDesignModeOff(TWebBrowserEx * WebBrowser)
  802. {
  803. if (DebugAlwaysTrue(WebBrowser->Document2 != NULL))
  804. {
  805. WebBrowser->Document2->designMode = L"Off";
  806. }
  807. }
  808. //---------------------------------------------------------------------------
  809. void __fastcall AddBrowserLinkHandler(TWebBrowserEx * WebBrowser,
  810. const UnicodeString & Url, TNotifyEvent Handler)
  811. {
  812. TBrowserViewer * BrowserViewer = dynamic_cast<TBrowserViewer *>(WebBrowser);
  813. if (DebugAlwaysTrue(BrowserViewer != NULL))
  814. {
  815. BrowserViewer->AddLinkHandler(Url, Handler);
  816. }
  817. }
  818. //---------------------------------------------------------------------------
  819. void __fastcall NavigateBrowserToUrl(TWebBrowserEx * WebBrowser, const UnicodeString & Url)
  820. {
  821. TBrowserViewer * BrowserViewer = dynamic_cast<TBrowserViewer *>(WebBrowser);
  822. if (DebugAlwaysTrue(BrowserViewer != NULL))
  823. {
  824. BrowserViewer->NavigateToUrl(Url);
  825. }
  826. }
  827. //---------------------------------------------------------------------------
  828. TComponent * __fastcall FindComponentRecursively(TComponent * Root, const UnicodeString & Name)
  829. {
  830. for (int Index = 0; Index < Root->ComponentCount; Index++)
  831. {
  832. TComponent * Component = Root->Components[Index];
  833. if (CompareText(Component->Name, Name) == 0)
  834. {
  835. return Component;
  836. }
  837. Component = FindComponentRecursively(Component, Name);
  838. if (Component != NULL)
  839. {
  840. return Component;
  841. }
  842. }
  843. return NULL;
  844. }
  845. //---------------------------------------------------------------------------
  846. TLocalCustomCommand::TLocalCustomCommand()
  847. {
  848. }
  849. //---------------------------------------------------------------------------
  850. TLocalCustomCommand::TLocalCustomCommand(
  851. const TCustomCommandData & Data, const UnicodeString & RemotePath, const UnicodeString & LocalPath) :
  852. TFileCustomCommand(Data, RemotePath)
  853. {
  854. FLocalPath = LocalPath;
  855. }
  856. //---------------------------------------------------------------------------
  857. TLocalCustomCommand::TLocalCustomCommand(const TCustomCommandData & Data,
  858. const UnicodeString & RemotePath, const UnicodeString & LocalPath, const UnicodeString & FileName,
  859. const UnicodeString & LocalFileName, const UnicodeString & FileList) :
  860. TFileCustomCommand(Data, RemotePath, FileName, FileList)
  861. {
  862. FLocalPath = LocalPath;
  863. FLocalFileName = LocalFileName;
  864. }
  865. //---------------------------------------------------------------------------
  866. int __fastcall TLocalCustomCommand::PatternLen(const UnicodeString & Command, int Index)
  867. {
  868. int Len;
  869. if ((Index < Command.Length()) && (Command[Index + 1] == L'\\'))
  870. {
  871. Len = 2;
  872. }
  873. else if ((Index < Command.Length()) && (Command[Index + 1] == L'^'))
  874. {
  875. Len = 3;
  876. }
  877. else
  878. {
  879. Len = TFileCustomCommand::PatternLen(Command, Index);
  880. }
  881. return Len;
  882. }
  883. //---------------------------------------------------------------------------
  884. bool __fastcall TLocalCustomCommand::PatternReplacement(
  885. int Index, const UnicodeString & Pattern, UnicodeString & Replacement, bool & Delimit)
  886. {
  887. bool Result;
  888. if (Pattern == L"!\\")
  889. {
  890. // When used as "!\" in an argument to PowerShell, the trailing \ would escpae the ",
  891. // so we exclude it
  892. Replacement = ExcludeTrailingBackslash(FLocalPath);
  893. Result = true;
  894. }
  895. else if (Pattern == L"!^!")
  896. {
  897. Replacement = FLocalFileName;
  898. Result = true;
  899. }
  900. else
  901. {
  902. Result = TFileCustomCommand::PatternReplacement(Index, Pattern, Replacement, Delimit);
  903. }
  904. return Result;
  905. }
  906. //---------------------------------------------------------------------------
  907. void __fastcall TLocalCustomCommand::DelimitReplacement(
  908. UnicodeString & /*Replacement*/, wchar_t /*Quote*/)
  909. {
  910. // never delimit local commands
  911. }
  912. //---------------------------------------------------------------------------
  913. bool __fastcall TLocalCustomCommand::HasLocalFileName(const UnicodeString & Command)
  914. {
  915. return FindPattern(Command, L'^');
  916. }
  917. //---------------------------------------------------------------------------
  918. bool __fastcall TLocalCustomCommand::IsFileCommand(const UnicodeString & Command)
  919. {
  920. return TFileCustomCommand::IsFileCommand(Command) || HasLocalFileName(Command);
  921. }
  922. //---------------------------------------------------------------------------
  923. //---------------------------------------------------------------------------
  924. typedef std::set<TDataModule *> TImagesModules;
  925. static TImagesModules ImagesModules;
  926. static std::map<int, TPngImageList *> AnimationsImages;
  927. static std::map<int, TImageList *> ButtonImages;
  928. static std::map<int, TPngImageList *> DialogImages;
  929. //---------------------------------------------------------------------------
  930. int __fastcall NormalizePixelsPerInch(int PixelsPerInch)
  931. {
  932. if (PixelsPerInch >= 192)
  933. {
  934. PixelsPerInch = 192;
  935. }
  936. else if (PixelsPerInch >= 144)
  937. {
  938. PixelsPerInch = 144;
  939. }
  940. else if (PixelsPerInch >= 120)
  941. {
  942. PixelsPerInch = 120;
  943. }
  944. else
  945. {
  946. PixelsPerInch = 96;
  947. }
  948. return PixelsPerInch;
  949. }
  950. //---------------------------------------------------------------------------
  951. static int __fastcall NeedImagesModule(TControl * Control)
  952. {
  953. int PixelsPerInch = NormalizePixelsPerInch(GetControlPixelsPerInch(Control));
  954. if (AnimationsImages.find(PixelsPerInch) == AnimationsImages.end())
  955. {
  956. TDataModule * ImagesModule;
  957. HANDLE ResourceModule = GUIConfiguration->ChangeToDefaultResourceModule();
  958. try
  959. {
  960. if (PixelsPerInch == 192)
  961. {
  962. ImagesModule = new TAnimations192Module(Application);
  963. }
  964. else if (PixelsPerInch == 144)
  965. {
  966. ImagesModule = new TAnimations144Module(Application);
  967. }
  968. else if (PixelsPerInch == 120)
  969. {
  970. ImagesModule = new TAnimations120Module(Application);
  971. }
  972. else
  973. {
  974. DebugAssert(PixelsPerInch == 96);
  975. ImagesModule = new TAnimations96Module(Application);
  976. }
  977. ImagesModules.insert(ImagesModule);
  978. TPngImageList * AAnimationImages =
  979. DebugNotNull(dynamic_cast<TPngImageList *>(ImagesModule->FindComponent(L"AnimationImages")));
  980. AnimationsImages.insert(std::make_pair(PixelsPerInch, AAnimationImages));
  981. TImageList * AButtonImages =
  982. DebugNotNull(dynamic_cast<TImageList *>(ImagesModule->FindComponent(L"ButtonImages")));
  983. ButtonImages.insert(std::make_pair(PixelsPerInch, AButtonImages));
  984. TPngImageList * ADialogImages =
  985. DebugNotNull(dynamic_cast<TPngImageList *>(ImagesModule->FindComponent(L"DialogImages")));
  986. DialogImages.insert(std::make_pair(PixelsPerInch, ADialogImages));
  987. }
  988. __finally
  989. {
  990. GUIConfiguration->ChangeResourceModule(ResourceModule);
  991. }
  992. }
  993. return PixelsPerInch;
  994. }
  995. //---------------------------------------------------------------------------
  996. TPngImageList * __fastcall GetAnimationsImages(TControl * Control)
  997. {
  998. int PixelsPerInch = NeedImagesModule(Control);
  999. return DebugNotNull(AnimationsImages[PixelsPerInch]);
  1000. }
  1001. //---------------------------------------------------------------------------
  1002. TImageList * __fastcall GetButtonImages(TControl * Control)
  1003. {
  1004. int PixelsPerInch = NeedImagesModule(Control);
  1005. return DebugNotNull(ButtonImages[PixelsPerInch]);
  1006. }
  1007. //---------------------------------------------------------------------------
  1008. TPngImageList * __fastcall GetDialogImages(TControl * Control)
  1009. {
  1010. int PixelsPerInch = NeedImagesModule(Control);
  1011. return DebugNotNull(DialogImages[PixelsPerInch]);
  1012. }
  1013. //---------------------------------------------------------------------------
  1014. void __fastcall ReleaseImagesModules()
  1015. {
  1016. TImagesModules::iterator i = ImagesModules.begin();
  1017. while (i != ImagesModules.end())
  1018. {
  1019. delete (*i);
  1020. i++;
  1021. }
  1022. ImagesModules.clear();
  1023. }
  1024. //---------------------------------------------------------------------------
  1025. __fastcall TFrameAnimation::TFrameAnimation()
  1026. {
  1027. FFirstFrame = -1;
  1028. }
  1029. //---------------------------------------------------------------------------
  1030. void __fastcall TFrameAnimation::Init(TPaintBox * PaintBox, const UnicodeString & Name)
  1031. {
  1032. FName = Name;
  1033. FPaintBox = PaintBox;
  1034. FPaintBox->ControlStyle = FPaintBox->ControlStyle << csOpaque;
  1035. DebugAssert((FPaintBox->OnPaint == NULL) || (FPaintBox->OnPaint == PaintBoxPaint));
  1036. FPaintBox->OnPaint = PaintBoxPaint;
  1037. SetRescaleFunction(FPaintBox, PaintBoxRescale, reinterpret_cast<TObject *>(this));
  1038. DoInit();
  1039. }
  1040. //---------------------------------------------------------------------------
  1041. void __fastcall TFrameAnimation::DoInit()
  1042. {
  1043. FImageList = GetAnimationsImages(FPaintBox);
  1044. FFirstFrame = -1;
  1045. FFirstLoopFrame = -1;
  1046. FPaintBox->Width = FImageList->Width;
  1047. FPaintBox->Height = FImageList->Height;
  1048. if (!FName.IsEmpty())
  1049. {
  1050. int Frame = 0;
  1051. while (Frame < FImageList->PngImages->Count)
  1052. {
  1053. UnicodeString FrameData = FImageList->PngImages->Items[Frame]->Name;
  1054. UnicodeString FrameName;
  1055. FrameName = CutToChar(FrameData, L'_', false);
  1056. if (SameText(FName, FrameName))
  1057. {
  1058. int FrameIndex = StrToInt(CutToChar(FrameData, L'_', false));
  1059. if (FFirstFrame < 0)
  1060. {
  1061. FFirstFrame = Frame;
  1062. }
  1063. if ((FFirstLoopFrame < 0) && (FrameIndex > 0))
  1064. {
  1065. FFirstLoopFrame = Frame;
  1066. }
  1067. FLastFrame = Frame;
  1068. }
  1069. else
  1070. {
  1071. if (FFirstFrame >= 0)
  1072. {
  1073. // optimization
  1074. break;
  1075. }
  1076. }
  1077. Frame++;
  1078. }
  1079. DebugAssert(FFirstFrame >= 0);
  1080. DebugAssert(FFirstLoopFrame >= 0);
  1081. }
  1082. Stop();
  1083. }
  1084. //---------------------------------------------------------------------------
  1085. void __fastcall TFrameAnimation::PaintBoxRescale(TComponent * /*Sender*/, TObject * Token)
  1086. {
  1087. TFrameAnimation * FrameAnimation = reinterpret_cast<TFrameAnimation *>(Token);
  1088. FrameAnimation->Rescale();
  1089. }
  1090. //---------------------------------------------------------------------------
  1091. void __fastcall TFrameAnimation::Rescale()
  1092. {
  1093. bool Started = (FTimer != NULL) && FTimer->Enabled;
  1094. DoInit();
  1095. if (Started)
  1096. {
  1097. Start();
  1098. }
  1099. }
  1100. //---------------------------------------------------------------------------
  1101. void __fastcall TFrameAnimation::Start()
  1102. {
  1103. if (FFirstFrame >= 0)
  1104. {
  1105. FNextFrameTick = GetTickCount();
  1106. CalculateNextFrameTick();
  1107. if (FTimer == NULL)
  1108. {
  1109. FTimer = new TTimer(GetParentForm(FPaintBox));
  1110. FTimer->Interval = static_cast<int>(GUIUpdateInterval);
  1111. FTimer->OnTimer = Timer;
  1112. }
  1113. else
  1114. {
  1115. // reset timer
  1116. FTimer->Enabled = false;
  1117. FTimer->Enabled = true;
  1118. }
  1119. }
  1120. }
  1121. //---------------------------------------------------------------------------
  1122. void __fastcall TFrameAnimation::Timer(TObject * /*Sender*/)
  1123. {
  1124. Animate();
  1125. }
  1126. //---------------------------------------------------------------------------
  1127. void __fastcall TFrameAnimation::PaintBoxPaint(TObject * Sender)
  1128. {
  1129. if (FFirstFrame >= 0)
  1130. {
  1131. // Double-buffered drawing to prevent flicker (as the images are transparent)
  1132. DebugUsedParam(Sender);
  1133. DebugAssert(FPaintBox == Sender);
  1134. DebugAssert(FPaintBox->ControlStyle.Contains(csOpaque));
  1135. std::unique_ptr<TBitmap> Bitmap(new TBitmap());
  1136. Bitmap->SetSize(FPaintBox->Width, FPaintBox->Height);
  1137. Bitmap->Canvas->Brush->Color = FPaintBox->Color;
  1138. TRect Rect(0, 0, Bitmap->Width, Bitmap->Height);
  1139. Bitmap->Canvas->FillRect(Rect);
  1140. TGraphic * Graphic = GetCurrentImage()->PngImage;
  1141. DebugAssert(Graphic->Width == FPaintBox->Width);
  1142. DebugAssert(Graphic->Height == FPaintBox->Height);
  1143. Bitmap->Canvas->Draw(0, 0, Graphic);
  1144. FPaintBox->Canvas->Draw(0, 0, Bitmap.get());
  1145. }
  1146. FPainted = true;
  1147. }
  1148. //---------------------------------------------------------------------------
  1149. void __fastcall TFrameAnimation::Repaint()
  1150. {
  1151. FPainted = false;
  1152. // If the form is not showing yet, the Paint() is not even called
  1153. FPaintBox->Repaint();
  1154. if (!FPainted)
  1155. {
  1156. // Paint later, alternativelly we may keep trying Repaint() in Animate().
  1157. // See also a hack in TAuthenticateForm::Log.
  1158. FPaintBox->Invalidate();
  1159. }
  1160. }
  1161. //---------------------------------------------------------------------------
  1162. void __fastcall TFrameAnimation::Stop()
  1163. {
  1164. FNextFrameTick = std::numeric_limits<DWORD>::max();
  1165. FCurrentFrame = FFirstFrame;
  1166. Repaint();
  1167. if (FTimer != NULL)
  1168. {
  1169. FTimer->Enabled = false;
  1170. }
  1171. }
  1172. //---------------------------------------------------------------------------
  1173. void __fastcall TFrameAnimation::Animate()
  1174. {
  1175. if (FFirstFrame >= 0)
  1176. {
  1177. // UPGRADE: Use GetTickCount64() when we stop supporting Windows XP.
  1178. DWORD TickCount = GetTickCount();
  1179. // Keep in sync with an opposite condition at the end of the loop.
  1180. // We may skip some frames if we got stalled for a while
  1181. while (TickCount >= FNextFrameTick)
  1182. {
  1183. if (FCurrentFrame >= FLastFrame)
  1184. {
  1185. FCurrentFrame = FFirstLoopFrame;
  1186. }
  1187. else
  1188. {
  1189. FCurrentFrame++;
  1190. }
  1191. CalculateNextFrameTick();
  1192. Repaint();
  1193. }
  1194. }
  1195. }
  1196. //---------------------------------------------------------------------------
  1197. TPngImageCollectionItem * __fastcall TFrameAnimation::GetCurrentImage()
  1198. {
  1199. return FImageList->PngImages->Items[FCurrentFrame];
  1200. }
  1201. //---------------------------------------------------------------------------
  1202. void __fastcall TFrameAnimation::CalculateNextFrameTick()
  1203. {
  1204. TPngImageCollectionItem * ImageItem = GetCurrentImage();
  1205. UnicodeString Duration = ImageItem->Name;
  1206. CutToChar(Duration, L'_', false);
  1207. // skip index (is not really used)
  1208. CutToChar(Duration, L'_', false);
  1209. // This should overflow, when tick count wraps.
  1210. FNextFrameTick += StrToInt(Duration) * 10;
  1211. }
  1212. //---------------------------------------------------------------------------
  1213. //---------------------------------------------------------------------------
  1214. // Hints use:
  1215. // - Cleanup list tooltip (multi line)
  1216. // - Combo edit button
  1217. // - Transfer settings label (multi line, follows label size and font)
  1218. // - HintLabel (hint and persistent hint, multipline)
  1219. // - status bar hints
  1220. //---------------------------------------------------------------------------
  1221. __fastcall TScreenTipHintWindow::TScreenTipHintWindow(TComponent * Owner) :
  1222. THintWindow(Owner)
  1223. {
  1224. FParentPainting = false;
  1225. }
  1226. //---------------------------------------------------------------------------
  1227. int __fastcall TScreenTipHintWindow::GetTextFlags(TControl * Control)
  1228. {
  1229. return DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | Control->DrawTextBiDiModeFlagsReadingOnly();
  1230. }
  1231. //---------------------------------------------------------------------------
  1232. bool __fastcall TScreenTipHintWindow::UseBoldShortHint(TControl * HintControl)
  1233. {
  1234. return
  1235. (dynamic_cast<TTBCustomDockableWindow *>(HintControl) != NULL) ||
  1236. (dynamic_cast<TTBPopupWindow *>(HintControl) != NULL);
  1237. }
  1238. //---------------------------------------------------------------------------
  1239. bool __fastcall TScreenTipHintWindow::IsPathLabel(TControl * HintControl)
  1240. {
  1241. return (dynamic_cast<TPathLabel *>(HintControl) != NULL);
  1242. }
  1243. //---------------------------------------------------------------------------
  1244. bool __fastcall TScreenTipHintWindow::IsHintPopup(TControl * HintControl, const UnicodeString & Hint)
  1245. {
  1246. TLabel * HintLabel = dynamic_cast<TLabel *>(HintControl);
  1247. return (HintLabel != NULL) && HasLabelHintPopup(HintLabel, Hint);
  1248. }
  1249. //---------------------------------------------------------------------------
  1250. int __fastcall TScreenTipHintWindow::GetMargin(TControl * HintControl, const UnicodeString & Hint)
  1251. {
  1252. int Result;
  1253. if (IsHintPopup(HintControl, Hint) || IsPathLabel(HintControl))
  1254. {
  1255. Result = 3;
  1256. }
  1257. else
  1258. {
  1259. Result = 6;
  1260. }
  1261. Result = ScaleByTextHeight(HintControl, Result);
  1262. return Result;
  1263. }
  1264. //---------------------------------------------------------------------------
  1265. TFont * __fastcall TScreenTipHintWindow::GetFont(TControl * HintControl, const UnicodeString & Hint)
  1266. {
  1267. TFont * Result;
  1268. if (IsHintPopup(HintControl, Hint) || IsPathLabel(HintControl))
  1269. {
  1270. Result = reinterpret_cast<TLabel *>(dynamic_cast<TCustomLabel *>(HintControl))->Font;
  1271. }
  1272. else
  1273. {
  1274. FScaledHintFont.reset(new TFont());
  1275. FScaledHintFont->Assign(Screen->HintFont);
  1276. FScaledHintFont->Size = ScaleByPixelsPerInchFromSystem(FScaledHintFont->Size, HintControl);
  1277. Result = FScaledHintFont.get();
  1278. }
  1279. return Result;
  1280. }
  1281. //---------------------------------------------------------------------------
  1282. void __fastcall TScreenTipHintWindow::CalcHintTextRect(TControl * Control, TCanvas * Canvas, TRect & Rect, const UnicodeString & Hint)
  1283. {
  1284. const int Flags = DT_CALCRECT | GetTextFlags(Control);
  1285. DrawText(Canvas->Handle, Hint.c_str(), -1, &Rect, Flags);
  1286. }
  1287. //---------------------------------------------------------------------------
  1288. TRect __fastcall TScreenTipHintWindow::CalcHintRect(int MaxWidth, const UnicodeString AHint, void * AData)
  1289. {
  1290. TControl * HintControl = GetHintControl(AData);
  1291. int Margin = GetMargin(HintControl, AHint);
  1292. const UnicodeString ShortHint = GetShortHint(AHint);
  1293. const UnicodeString LongHint = GetLongHintIfAny(AHint);
  1294. Canvas->Font->Assign(GetFont(HintControl, AHint));
  1295. const int ScreenTipTextOnlyWidth = ScaleByTextHeight(HintControl, cScreenTipTextOnlyWidth);
  1296. if (!LongHint.IsEmpty())
  1297. {
  1298. // double-margin on the right
  1299. MaxWidth = ScreenTipTextOnlyWidth - (3 * Margin);
  1300. }
  1301. // Multi line short hints can be twice as wide, to not break the individual lines unless really necessary.
  1302. // (login site tree, clean up dialog list, preferences custom command list, persistent hint, etc).
  1303. // And they also can be twice as wide, to not break the individual lines unless really necessary.
  1304. if (ShortHint.Pos(L"\n") > 0)
  1305. {
  1306. MaxWidth *= 2;
  1307. }
  1308. bool HintPopup = IsHintPopup(HintControl, AHint);
  1309. if (HintPopup)
  1310. {
  1311. MaxWidth = HintControl->Width;
  1312. }
  1313. if (UseBoldShortHint(HintControl))
  1314. {
  1315. Canvas->Font->Style = TFontStyles() << fsBold;
  1316. }
  1317. TRect ShortRect(0, 0, MaxWidth, 0);
  1318. CalcHintTextRect(this, Canvas, ShortRect, ShortHint);
  1319. Canvas->Font->Style = TFontStyles();
  1320. TRect Result;
  1321. if (LongHint.IsEmpty())
  1322. {
  1323. Result = ShortRect;
  1324. if (HintPopup)
  1325. {
  1326. Result.Right = MaxWidth + 2 * Margin;
  1327. }
  1328. else
  1329. {
  1330. Result.Right += 3 * Margin;
  1331. }
  1332. Result.Bottom += 2 * Margin;
  1333. }
  1334. else
  1335. {
  1336. const int LongIndentation = Margin * 3 / 2;
  1337. TRect LongRect(0, 0, MaxWidth - LongIndentation, 0);
  1338. CalcHintTextRect(this, Canvas, LongRect, LongHint);
  1339. Result.Right = ScreenTipTextOnlyWidth;
  1340. Result.Bottom = Margin + ShortRect.Height() + (Margin / 3 * 5) + LongRect.Height() + Margin;
  1341. }
  1342. // VCLCOPY: To counter the increase in THintWindow::ActivateHintData
  1343. Result.Bottom -= 4;
  1344. return Result;
  1345. }
  1346. //---------------------------------------------------------------------------
  1347. void __fastcall TScreenTipHintWindow::ActivateHintData(const TRect & ARect, const UnicodeString AHint, void * AData)
  1348. {
  1349. FShortHint = GetShortHint(AHint);
  1350. FLongHint = GetLongHintIfAny(AHint);
  1351. FHintControl = GetHintControl(AData);
  1352. FMargin = GetMargin(FHintControl, AHint);
  1353. FHintPopup = IsHintPopup(FHintControl, AHint);
  1354. Canvas->Font->Assign(GetFont(FHintControl, AHint));
  1355. TRect Rect = ARect;
  1356. if (FHintPopup)
  1357. {
  1358. Rect.SetLocation(FHintControl->ClientToScreen(TPoint(-FMargin, -FMargin)));
  1359. }
  1360. if (IsPathLabel(FHintControl))
  1361. {
  1362. Rect.Offset(-FMargin, -FMargin);
  1363. }
  1364. THintWindow::ActivateHintData(Rect, FShortHint, AData);
  1365. }
  1366. //---------------------------------------------------------------------------
  1367. TControl * __fastcall TScreenTipHintWindow::GetHintControl(void * Data)
  1368. {
  1369. return reinterpret_cast<TControl *>(DebugNotNull(Data));
  1370. }
  1371. //---------------------------------------------------------------------------
  1372. UnicodeString __fastcall TScreenTipHintWindow::GetLongHintIfAny(const UnicodeString & AHint)
  1373. {
  1374. UnicodeString Result;
  1375. int P = Pos(L"|", AHint);
  1376. if (P > 0)
  1377. {
  1378. Result = GetLongHint(AHint);
  1379. }
  1380. return Result;
  1381. }
  1382. //---------------------------------------------------------------------------
  1383. void __fastcall TScreenTipHintWindow::Dispatch(void * AMessage)
  1384. {
  1385. TMessage * Message = static_cast<TMessage*>(AMessage);
  1386. switch (Message->Msg)
  1387. {
  1388. case WM_GETTEXTLENGTH:
  1389. if (FParentPainting)
  1390. {
  1391. // make THintWindow::Paint() not paint the Caption
  1392. Message->Result = 0;
  1393. }
  1394. else
  1395. {
  1396. THintWindow::Dispatch(AMessage);
  1397. }
  1398. break;
  1399. default:
  1400. THintWindow::Dispatch(AMessage);
  1401. break;
  1402. }
  1403. }
  1404. //---------------------------------------------------------------------------
  1405. void __fastcall TScreenTipHintWindow::Paint()
  1406. {
  1407. // paint frame/background
  1408. {
  1409. TAutoFlag ParentPaintingFlag(FParentPainting);
  1410. THintWindow::Paint();
  1411. }
  1412. const int Flags = GetTextFlags(this);
  1413. const int Margin = FMargin - 1; // 1 = border
  1414. TRect Rect = ClientRect;
  1415. Rect.Inflate(-Margin, -Margin);
  1416. if (!FHintPopup)
  1417. {
  1418. Rect.Right -= FMargin;
  1419. }
  1420. if (UseBoldShortHint(FHintControl))
  1421. {
  1422. Canvas->Font->Style = TFontStyles() << fsBold;
  1423. }
  1424. DrawText(Canvas->Handle, FShortHint.c_str(), -1, &Rect, Flags);
  1425. TRect ShortRect = Rect;
  1426. DrawText(Canvas->Handle, FShortHint.c_str(), -1, &ShortRect, DT_CALCRECT | Flags);
  1427. Canvas->Font->Style = TFontStyles();
  1428. if (!FLongHint.IsEmpty())
  1429. {
  1430. Rect.Left += FMargin * 3 / 2;
  1431. Rect.Top += ShortRect.Height() + (FMargin / 3 * 5);
  1432. DrawText(Canvas->Handle, FLongHint.c_str(), -1, &Rect, Flags);
  1433. }
  1434. }