GUITools.cpp 102 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120
  1. //---------------------------------------------------------------------------
  2. #include <WinPCH.h>
  3. #pragma hdrstop
  4. #include "GUITools.h"
  5. #include <shlobj.h>
  6. #include <mshtmhst.h>
  7. #include <SessionData.h>
  8. #include <TBXUtils.hpp>
  9. #include "PngImageList.hpp"
  10. #include <Glyphs.h>
  11. #include <WinApi.h>
  12. #include <HistoryComboBox.hpp>
  13. #include <vssym32.h>
  14. #include <Queue.h>
  15. #include <Vcl.Themes.hpp>
  16. #include "Animations96.h"
  17. #include "Animations120.h"
  18. #include "Animations144.h"
  19. #include "Animations192.h"
  20. //---------------------------------------------------------------------------
  21. extern const UnicodeString PageantTool = L"pageant.exe";
  22. extern const UnicodeString PuttygenTool = L"puttygen.exe";
  23. //---------------------------------------------------------------------------
  24. bool __fastcall FindFile(UnicodeString & Path)
  25. {
  26. bool Result = FileExistsFix(Path);
  27. if (!Result)
  28. {
  29. UnicodeString ProgramFiles32 = IncludeTrailingBackslash(GetEnvironmentVariable(L"ProgramFiles"));
  30. UnicodeString ProgramFiles64 = IncludeTrailingBackslash(GetEnvironmentVariable(L"ProgramW6432"));
  31. if (!ProgramFiles32.IsEmpty() &&
  32. SameText(Path.SubString(1, ProgramFiles32.Length()), ProgramFiles32) &&
  33. !ProgramFiles64.IsEmpty())
  34. {
  35. UnicodeString Path64 =
  36. ProgramFiles64 + Path.SubString(ProgramFiles32.Length() + 1, Path.Length() - ProgramFiles32.Length());
  37. if (FileExists(ApiPath(Path64)))
  38. {
  39. Path = Path64;
  40. Result = true;
  41. }
  42. }
  43. }
  44. if (!Result && SameText(ExtractFileName(Path), Path))
  45. {
  46. UnicodeString Paths = GetEnvironmentVariable(L"PATH");
  47. if (!Paths.IsEmpty())
  48. {
  49. UnicodeString NewPath = FileSearch(Path, Paths);
  50. Result = !NewPath.IsEmpty();
  51. if (Result)
  52. {
  53. Path = NewPath;
  54. }
  55. else
  56. {
  57. // Basically the same what FileSearch does, except for FileExistsFix.
  58. // Once this proves working, we can ged rid of the FileSearch call.
  59. while (!Result && !Paths.IsEmpty())
  60. {
  61. UnicodeString P = CutToChar(Paths, L';', false);
  62. // Not using CombinePaths as it throws on an invalid path and PATH is not under our control
  63. NewPath = IncludeTrailingBackslash(P) + Path;
  64. Result = FileExistsFix(NewPath);
  65. if (Result)
  66. {
  67. Path = NewPath;
  68. }
  69. }
  70. }
  71. }
  72. }
  73. return Result;
  74. }
  75. //---------------------------------------------------------------------------
  76. bool DoesSessionExistInPutty(const UnicodeString & StorageKey)
  77. {
  78. std::unique_ptr<TRegistryStorage> Storage(new TRegistryStorage(Configuration->PuttySessionsKey));
  79. Storage->ConfigureForPutty();
  80. return Storage->OpenRootKey(true) && Storage->KeyExists(StorageKey);
  81. }
  82. //---------------------------------------------------------------------------
  83. bool __fastcall ExportSessionToPutty(TSessionData * SessionData, bool ReuseExisting, const UnicodeString & SessionName)
  84. {
  85. bool Result = true;
  86. std::unique_ptr<TRegistryStorage> Storage(new TRegistryStorage(Configuration->PuttySessionsKey));
  87. Storage->AccessMode = smReadWrite;
  88. Storage->ConfigureForPutty();
  89. if (Storage->OpenRootKey(true))
  90. {
  91. Result = ReuseExisting && Storage->KeyExists(SessionData->StorageKey);
  92. if (Result)
  93. {
  94. AppLogFmt(L"Reusing existing PuTTY session: %s", (SessionData->StorageKey));
  95. }
  96. else
  97. {
  98. std::unique_ptr<TRegistryStorage> SourceStorage(new TRegistryStorage(Configuration->PuttySessionsKey));
  99. SourceStorage->ConfigureForPutty();
  100. if (SourceStorage->OpenSubKey(StoredSessions->DefaultSettings->Name, false) &&
  101. Storage->OpenSubKey(SessionName, true))
  102. {
  103. Storage->Copy(SourceStorage.get());
  104. Storage->CloseSubKey();
  105. }
  106. std::unique_ptr<TSessionData> ExportData(new TSessionData(L""));
  107. ExportData->Assign(SessionData);
  108. ExportData->Modified = true;
  109. ExportData->Name = SessionName;
  110. ExportData->WinTitle = SessionData->SessionName;
  111. ExportData->Password = L"";
  112. if (SessionData->FSProtocol == fsFTP)
  113. {
  114. if (GUIConfiguration->TelnetForFtpInPutty)
  115. {
  116. ExportData->PuttyProtocol = PuttyTelnetProtocol;
  117. ExportData->PortNumber = TelnetPortNumber;
  118. }
  119. else
  120. {
  121. ExportData->PuttyProtocol = PuttySshProtocol;
  122. ExportData->PortNumber = SshPortNumber;
  123. }
  124. }
  125. ExportData->Save(Storage.get(), true);
  126. AppLogFmt(L"Exported site settings to PuTTY session: %s", (SessionName));
  127. }
  128. }
  129. return Result;
  130. }
  131. //---------------------------------------------------------------------------
  132. //---------------------------------------------------------------------------
  133. class TStandaloneThread : public TSimpleThread
  134. {
  135. public:
  136. virtual void __fastcall Terminate();
  137. protected:
  138. virtual bool __fastcall Finished();
  139. };
  140. //---------------------------------------------------------------------------
  141. void __fastcall TStandaloneThread::Terminate()
  142. {
  143. // noop - the thread always self-terminates
  144. DebugFail();
  145. }
  146. //---------------------------------------------------------------------------
  147. bool __fastcall TStandaloneThread::Finished()
  148. {
  149. // self-destroy
  150. return TSimpleThread::Finished() || true;
  151. }
  152. //---------------------------------------------------------------------------
  153. //---------------------------------------------------------------------------
  154. template<typename T, int TT, int I>
  155. class TSingletonThread : public TStandaloneThread
  156. {
  157. public:
  158. static void Schedule();
  159. static void Finalize();
  160. protected:
  161. virtual void __fastcall Execute();
  162. void DoSchedule();
  163. virtual void DoExecute(bool HasExpired) = 0;
  164. TDateTime FTimer;
  165. static T * FInstance;
  166. static std::unique_ptr<TCriticalSection> FSection;
  167. };
  168. //---------------------------------------------------------------------------
  169. template<typename T, int TT, int I>
  170. std::unique_ptr<TCriticalSection> TSingletonThread<T, TT, I>::FSection(TraceInitPtr(new TCriticalSection()));
  171. template<typename T, int TT, int I>
  172. T * TSingletonThread<T, TT, I>::FInstance;
  173. //---------------------------------------------------------------------------
  174. template<typename T, int TT, int I>
  175. void TSingletonThread<T, TT, I>::Schedule()
  176. {
  177. TGuard Guard(FSection.get());
  178. if (FInstance == NULL)
  179. {
  180. FInstance = new T();
  181. FInstance->DoSchedule();
  182. FInstance->Start();
  183. }
  184. else
  185. {
  186. FInstance->DoSchedule();
  187. }
  188. }
  189. //---------------------------------------------------------------------------
  190. template<typename T, int TT, int I>
  191. void TSingletonThread<T, TT, I>::Finalize()
  192. {
  193. while (true)
  194. {
  195. {
  196. TGuard Guard(FSection.get());
  197. if (FInstance == NULL)
  198. {
  199. return;
  200. }
  201. }
  202. Sleep(100);
  203. }
  204. }
  205. //---------------------------------------------------------------------------
  206. template<typename T, int TT, int I>
  207. void __fastcall TSingletonThread<T, TT, I>::Execute()
  208. {
  209. try
  210. {
  211. bool HasExpired;
  212. do
  213. {
  214. {
  215. TGuard Guard(FSection.get());
  216. HasExpired = (FTimer < Now());
  217. }
  218. DoExecute(HasExpired);
  219. if (!HasExpired)
  220. {
  221. Sleep(I);
  222. }
  223. }
  224. while (!HasExpired);
  225. }
  226. __finally
  227. {
  228. TGuard Guard(FSection.get());
  229. FInstance = NULL;
  230. }
  231. }
  232. //---------------------------------------------------------------------------
  233. template<typename T, int TT, int I>
  234. void TSingletonThread<T, TT, I>::DoSchedule()
  235. {
  236. FTimer = IncSecond(Now(), TT);
  237. }
  238. //---------------------------------------------------------------------------
  239. //---------------------------------------------------------------------------
  240. class TPuttyCleanupThread : public TSingletonThread<TPuttyCleanupThread, 10, 1000>
  241. {
  242. protected:
  243. virtual void DoExecute(bool HasExpired);
  244. };
  245. //---------------------------------------------------------------------------
  246. void TPuttyCleanupThread::DoExecute(bool HasExpired)
  247. {
  248. std::unique_ptr<TStrings> Sessions(new TStringList());
  249. TGuard Guard(FSection.get());
  250. std::unique_ptr<TRegistryStorage> Storage(new TRegistryStorage(Configuration->PuttySessionsKey));
  251. Storage->AccessMode = smReadWrite;
  252. Storage->ConfigureForPutty();
  253. std::unique_ptr<TStringList> Sessions2(new TStringList());
  254. if (Storage->OpenRootKey(true))
  255. {
  256. std::unique_ptr<TStrings> SubKeys(new TStringList());
  257. Storage->GetSubKeyNames(SubKeys.get());
  258. for (int Index = 0; Index < SubKeys->Count; Index++)
  259. {
  260. UnicodeString SessionName = SubKeys->Strings[Index];
  261. if (StartsStr(GUIConfiguration->PuttySession, SessionName))
  262. {
  263. Sessions2->Add(SessionName);
  264. }
  265. }
  266. Sessions2->Sort();
  267. if (!Sessions->Equals(Sessions2.get()))
  268. {
  269. // Just in case new sessions from another WinSCP instance are added, delay the cleanup
  270. // (to avoid having to implement some inter-process communication).
  271. // Both instances will attempt to do the cleanup, but that not a problem
  272. Sessions->Assign(Sessions2.get());
  273. DoSchedule();
  274. }
  275. }
  276. if (HasExpired)
  277. {
  278. for (int Index = 0; Index < Sessions->Count; Index++)
  279. {
  280. UnicodeString SessionName = Sessions->Strings[Index];
  281. Storage->RecursiveDeleteSubKey(SessionName);
  282. }
  283. }
  284. }
  285. //---------------------------------------------------------------------------
  286. class TPuttyPasswordThread : public TStandaloneThread
  287. {
  288. public:
  289. TPuttyPasswordThread(const UnicodeString & Password, const UnicodeString & PipeName);
  290. virtual __fastcall ~TPuttyPasswordThread();
  291. protected:
  292. virtual void __fastcall Execute();
  293. private:
  294. HANDLE FPipe;
  295. AnsiString FPassword;
  296. void DoSleep(int & Timeout);
  297. };
  298. //---------------------------------------------------------------------------
  299. TPuttyPasswordThread::TPuttyPasswordThread(const UnicodeString & Password, const UnicodeString & PipeName)
  300. {
  301. DWORD OpenMode = PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE;
  302. DWORD PipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS;
  303. DWORD BufferSize = 16 * 1024;
  304. FPipe = CreateNamedPipe(PipeName.c_str(), OpenMode, PipeMode, 1, BufferSize, BufferSize, NMPWAIT_USE_DEFAULT_WAIT, NULL);
  305. if (FPipe == INVALID_HANDLE_VALUE)
  306. {
  307. throw EOSExtException(L"Cannot create password pipe");
  308. }
  309. FPassword = AnsiString(Password);
  310. Start();
  311. }
  312. //---------------------------------------------------------------------------
  313. __fastcall TPuttyPasswordThread::~TPuttyPasswordThread()
  314. {
  315. DebugAssert(FFinished);
  316. AppLog(L"Disconnecting and closing password pipe");
  317. DisconnectNamedPipe(FPipe);
  318. CloseHandle(FPipe);
  319. }
  320. //---------------------------------------------------------------------------
  321. void TPuttyPasswordThread::DoSleep(int & Timeout)
  322. {
  323. unsigned int Step = 50;
  324. Sleep(Step);
  325. Timeout -= Step;
  326. if (Timeout <= 0)
  327. {
  328. AppLog(L"Timeout waiting for PuTTY");
  329. }
  330. }
  331. //---------------------------------------------------------------------------
  332. void __fastcall TPuttyPasswordThread::Execute()
  333. {
  334. AppLog(L"Waiting for PuTTY to connect to password pipe");
  335. int Timeout = MSecsPerSec * SecsPerMin;
  336. while (Timeout > 0)
  337. {
  338. if (ConnectNamedPipe(FPipe, NULL))
  339. {
  340. AppLog(L"Password pipe is ready");
  341. }
  342. else
  343. {
  344. int Error = GetLastError();
  345. if (Error == ERROR_PIPE_CONNECTED)
  346. {
  347. AppLog(L"PuTTY has connected to password pipe");
  348. int Pos = 0;
  349. while ((Timeout > 0) && (Pos < FPassword.Length()))
  350. {
  351. DWORD Written = 0;
  352. if (!WriteFile(FPipe, FPassword.c_str() + Pos, FPassword.Length() - Pos, &Written, NULL))
  353. {
  354. AppLog(L"Error writting password pipe");
  355. Timeout = 0;
  356. }
  357. else
  358. {
  359. Pos += Written;
  360. if (Pos >= FPassword.Length())
  361. {
  362. FlushFileBuffers(FPipe);
  363. AppLog(L"Complete password was written to pipe");
  364. Timeout = 0;
  365. }
  366. else
  367. {
  368. AppLog(L"Part of password was written to pipe");
  369. DoSleep(Timeout);
  370. }
  371. }
  372. }
  373. }
  374. else if (Error == ERROR_PIPE_LISTENING)
  375. {
  376. DoSleep(Timeout);
  377. }
  378. else
  379. {
  380. AppLogFmt(L"Password pipe error %d", (Error));
  381. Timeout = 0;
  382. }
  383. }
  384. }
  385. }
  386. //---------------------------------------------------------------------------
  387. void SplitPuttyCommand(UnicodeString & Program, UnicodeString & Params)
  388. {
  389. // See also TSiteAdvancedDialog::PuttySettingsButtonClick
  390. UnicodeString Dir;
  391. SplitCommand(GUIConfiguration->PuttyPath, Program, Params, Dir);
  392. Program = ExpandEnvironmentVariables(Program);
  393. Params = ExpandEnvironmentVariables(Params);
  394. }
  395. //---------------------------------------------------------------------------
  396. UnicodeString FindPuttyPath()
  397. {
  398. UnicodeString Program, Params;
  399. SplitPuttyCommand(Program, Params);
  400. if (!FindFile(Program))
  401. {
  402. throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (Program)));
  403. }
  404. return Program;
  405. }
  406. //---------------------------------------------------------------------------
  407. static unsigned int PipeCounter = 0;
  408. //---------------------------------------------------------------------------
  409. void OpenSessionInPutty(TSessionData * SessionData)
  410. {
  411. // putty does not support resolving environment variables in session settings
  412. SessionData->ExpandEnvironmentVariables();
  413. UnicodeString Program, AParams;
  414. SplitPuttyCommand(Program, AParams);
  415. AppLogFmt(L"PuTTY program: %s", (Program));
  416. AppLogFmt(L"Params: %s", (AParams));
  417. if (FindFile(Program))
  418. {
  419. UnicodeString Password;
  420. if (GUIConfiguration->PuttyPassword)
  421. {
  422. // Passphrase has precendence, as it's more likely entered by user during authentication, hence more likely really needed.
  423. if (!SessionData->Passphrase.IsEmpty())
  424. {
  425. Password = SessionData->Passphrase;
  426. }
  427. else if (!SessionData->Password.IsEmpty())
  428. {
  429. Password = SessionData->Password;
  430. }
  431. }
  432. TCustomCommandData Data(SessionData, SessionData->UserName, Password);
  433. TLocalCustomCommand LocalCustomCommand(Data, SessionData->RemoteDirectory, SessionData->LocalDirectory);
  434. TWinInteractiveCustomCommand InteractiveCustomCommand(
  435. &LocalCustomCommand, L"PuTTY", UnicodeString());
  436. UnicodeString Params =
  437. LocalCustomCommand.Complete(InteractiveCustomCommand.Complete(AParams, false), true);
  438. UnicodeString PuttyParams;
  439. AppLogFmt(L"Expanded params: %s", (Params));
  440. if (!LocalCustomCommand.IsSiteCommand(AParams))
  441. {
  442. {
  443. bool SessionList = false;
  444. std::unique_ptr<THierarchicalStorage> SourceHostKeyStorage(Configuration->CreateScpStorage(SessionList));
  445. std::unique_ptr<THierarchicalStorage> TargetHostKeyStorage(new TRegistryStorage(Configuration->PuttyRegistryStorageKey));
  446. TargetHostKeyStorage->Explicit = true;
  447. TargetHostKeyStorage->AccessMode = smReadWrite;
  448. std::unique_ptr<TStoredSessionList> HostKeySessionList(new TStoredSessionList());
  449. HostKeySessionList->OwnsObjects = false;
  450. HostKeySessionList->Add(SessionData);
  451. int Imported = TStoredSessionList::ImportHostKeys(SourceHostKeyStorage.get(), TargetHostKeyStorage.get(), HostKeySessionList.get(), false);
  452. AppLogFmt(L"Imported host keys: %d", (Imported));
  453. }
  454. if (IsUWP())
  455. {
  456. bool Telnet = (SessionData->FSProtocol == fsFTP) && GUIConfiguration->TelnetForFtpInPutty;
  457. if (Telnet)
  458. {
  459. AddToList(PuttyParams, L"-telnet", L" ");
  460. // PuTTY does not allow -pw for telnet
  461. Password = L"";
  462. }
  463. AddToList(PuttyParams, EscapePuttyCommandParam(SessionData->HostName), L" ");
  464. if (!SessionData->UserName.IsEmpty())
  465. {
  466. AddToList(PuttyParams, FORMAT(L"-l %s", (EscapePuttyCommandParam(SessionData->UserName))), L" ");
  467. }
  468. if ((SessionData->FSProtocol != fsFTP) && (SessionData->PortNumber != SshPortNumber))
  469. {
  470. AddToList(PuttyParams, FORMAT(L"-P %d", (SessionData->PortNumber)), L" ");
  471. }
  472. if (!Telnet)
  473. {
  474. UnicodeString PublicKeyFile = SessionData->ResolvePublicKeyFile();
  475. if (!PublicKeyFile.IsEmpty())
  476. {
  477. AddToList(PuttyParams, FORMAT(L"-i \"%s\"", (PublicKeyFile)), L" ");
  478. }
  479. AddToList(PuttyParams, (SessionData->TryAgent ? L"-agent" : L"-noagent"), L" ");
  480. if (SessionData->TryAgent)
  481. {
  482. AddToList(PuttyParams, (SessionData->AgentFwd ? L"-A" : L"-a"), L" ");
  483. }
  484. if (SessionData->Compression)
  485. {
  486. AddToList(PuttyParams, L"-C", L" ");
  487. }
  488. AddToList(PuttyParams, L"-2", L" ");
  489. if (!SessionData->LogicalHostName.IsEmpty())
  490. {
  491. AddToList(PuttyParams, FORMAT(L"-loghost \"%s\"", (SessionData->LogicalHostName)), L" ");
  492. }
  493. }
  494. if (SessionData->AddressFamily == afIPv4)
  495. {
  496. AddToList(PuttyParams, L"-4", L" ");
  497. }
  498. else if (SessionData->AddressFamily == afIPv6)
  499. {
  500. AddToList(PuttyParams, L"-6", L" ");
  501. }
  502. AppLogFmt(L"Using command-line instead of stored session in UWP: %s", (PuttyParams));
  503. }
  504. else
  505. {
  506. UnicodeString SessionName;
  507. UnicodeString PuttySession = GUIConfiguration->PuttySession;
  508. int Uniq = 1;
  509. while (DoesSessionExistInPutty(PuttySession))
  510. {
  511. Uniq++;
  512. PuttySession = FORMAT(L"%s (%d)", (GUIConfiguration->PuttySession, Uniq));
  513. }
  514. if (ExportSessionToPutty(SessionData, true, PuttySession))
  515. {
  516. SessionName = SessionData->SessionName;
  517. }
  518. else
  519. {
  520. SessionName = PuttySession;
  521. TPuttyCleanupThread::Schedule();
  522. if ((SessionData->FSProtocol == fsFTP) &&
  523. GUIConfiguration->TelnetForFtpInPutty)
  524. {
  525. // PuTTY does not allow -pw for telnet
  526. Password = L"";
  527. }
  528. }
  529. UnicodeString LoadSwitch = L"-load";
  530. int P = Params.LowerCase().Pos(LoadSwitch + L" ");
  531. if ((P == 0) || ((P > 1) && (Params[P - 1] != L' ')))
  532. {
  533. AddToList(PuttyParams, FORMAT(L"%s %s", (LoadSwitch, EscapePuttyCommandParam(SessionName))), L" ");
  534. }
  535. }
  536. }
  537. if (!Password.IsEmpty() && !LocalCustomCommand.IsPasswordCommand(AParams))
  538. {
  539. Password = NormalizeString(Password); // if password is empty, we should quote it always
  540. bool UsePuttyPwFile;
  541. if (GUIConfiguration->UsePuttyPwFile == asAuto)
  542. {
  543. UsePuttyPwFile = false;
  544. if (SameText(ExtractFileName(Program), OriginalPuttyExecutable))
  545. {
  546. unsigned int Version = GetFileVersion(Program);
  547. if (Version != static_cast<unsigned int>(-1))
  548. {
  549. int MajorVersion = HIWORD(Version);
  550. int MinorVersion = LOWORD(Version);
  551. if (CalculateCompoundVersion(MajorVersion, MinorVersion) >= CalculateCompoundVersion(0, 77))
  552. {
  553. UsePuttyPwFile = true;
  554. }
  555. }
  556. }
  557. }
  558. else
  559. {
  560. UsePuttyPwFile = (GUIConfiguration->UsePuttyPwFile == asOn);
  561. }
  562. UnicodeString PasswordParam;
  563. if (UsePuttyPwFile)
  564. {
  565. PipeCounter++;
  566. UnicodeString PipeName = FORMAT(L"\\\\.\\PIPE\\WinSCPPuTTYPassword.%.8x.%.8x.%.4x", (GetCurrentProcessId(), PipeCounter, rand()));
  567. new TPuttyPasswordThread(Password, PipeName);
  568. PasswordParam = FORMAT(L"-pwfile \"%s\"", (PipeName));
  569. }
  570. else
  571. {
  572. PasswordParam = FORMAT(L"-pw %s", (EscapePuttyCommandParam(Password)));
  573. }
  574. AddToList(PuttyParams, PasswordParam, L" ");
  575. }
  576. AddToList(PuttyParams, Params, L" ");
  577. // PuTTY is started in its binary directory to allow relative paths in private key,
  578. // when opening PuTTY's own stored session.
  579. ExecuteShellChecked(Program, PuttyParams, true);
  580. }
  581. else
  582. {
  583. throw Exception(FMTLOAD(FILE_NOT_FOUND, (Program)));
  584. }
  585. }
  586. //---------------------------------------------------------------------------
  587. //---------------------------------------------------------------------------
  588. static HWND OperationStatusWindow = 0;
  589. static TRect OperationStatusCenterRect;
  590. //---------------------------------------------------------------------------
  591. void PlaceOperationStatusWindow()
  592. {
  593. TRect CurRect;
  594. if (GetWindowRect(OperationStatusWindow, &CurRect))
  595. {
  596. TRect Rect = CurRect;
  597. CenterFormOn(Rect, OperationStatusCenterRect);
  598. if (Rect != CurRect)
  599. {
  600. // What TWinControl.SetBounds does
  601. SetWindowPos(OperationStatusWindow, 0, Rect.Left, Rect.Top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
  602. }
  603. }
  604. }
  605. //---------------------------------------------------------------------------
  606. class TOperationStatusWindowMonitorThread : public TSingletonThread<TOperationStatusWindowMonitorThread, 3, 25>
  607. {
  608. protected:
  609. virtual void DoExecute(bool HasExpired);
  610. };
  611. //---------------------------------------------------------------------------
  612. void TOperationStatusWindowMonitorThread::DoExecute(bool)
  613. {
  614. PlaceOperationStatusWindow();
  615. }
  616. //---------------------------------------------------------------------------
  617. void CheckOperationStatusWindow()
  618. {
  619. if (Screen->ActiveForm != nullptr)
  620. {
  621. OperationStatusCenterRect = GetCenterRect(Screen->ActiveForm, nullptr);
  622. HWND WindowHandle = GetForegroundWindow();
  623. if ((WindowHandle != nullptr) && (WindowHandle != OperationStatusWindow))
  624. {
  625. DWORD ProcessId;
  626. if ((GetWindowThreadProcessId(WindowHandle, &ProcessId) != 0) &&
  627. (ProcessId == GetCurrentProcessId()))
  628. {
  629. UnicodeString ClassName;
  630. ClassName.SetLength(256);
  631. GetClassName(WindowHandle, ClassName.c_str(), ClassName.Length());
  632. PackStr(ClassName);
  633. if ((ClassName == L"OperationStatusWindow") &&
  634. !IsIconic(WindowHandle))
  635. {
  636. OperationStatusWindow = WindowHandle;
  637. PlaceOperationStatusWindow();
  638. TOperationStatusWindowMonitorThread::Schedule();
  639. }
  640. }
  641. }
  642. }
  643. }
  644. //---------------------------------------------------------------------------
  645. bool __fastcall FindTool(const UnicodeString & Name, UnicodeString & Path)
  646. {
  647. UnicodeString AppPath = IncludeTrailingBackslash(ExtractFilePath(Application->ExeName));
  648. Path = AppPath + Name;
  649. bool Result = true;
  650. if (!FileExists(ApiPath(Path)))
  651. {
  652. Path = AppPath + L"PuTTY\\" + Name;
  653. if (!FileExists(ApiPath(Path)))
  654. {
  655. Path = Name;
  656. if (!FindFile(Path))
  657. {
  658. Result = false;
  659. }
  660. }
  661. }
  662. return Result;
  663. }
  664. //---------------------------------------------------------------------------
  665. void __fastcall ExecuteTool(const UnicodeString & Name)
  666. {
  667. UnicodeString Path;
  668. if (!FindTool(Name, Path))
  669. {
  670. throw Exception(FMTLOAD(EXECUTE_APP_ERROR, (Name)));
  671. }
  672. ExecuteShellChecked(Path, L"");
  673. }
  674. //---------------------------------------------------------------------------
  675. TObjectList * StartCreationDirectoryMonitorsOnEachDrive(unsigned int Filter, TFileChangedEvent OnChanged)
  676. {
  677. std::unique_ptr<TStrings> Drives(new TStringList());
  678. std::unique_ptr<TStrings> DDDrives(CommaTextToStringList(WinConfiguration->DDDrives));
  679. UnicodeString ExcludedDrives;
  680. for (int Index = 0; Index < DDDrives->Count; Index++)
  681. {
  682. UnicodeString S = Trim(DDDrives->Strings[Index]);
  683. if (!S.IsEmpty() && (S[1] == L'-'))
  684. {
  685. S = Trim(S.SubString(2, S.Length() - 1));
  686. if (!S.IsEmpty())
  687. {
  688. ExcludedDrives += S[1];
  689. }
  690. }
  691. else
  692. {
  693. Drives->Add(S);
  694. }
  695. }
  696. for (char Drive = FirstDrive; Drive <= LastDrive; Drive++)
  697. {
  698. if (ExcludedDrives.Pos(Drive) == 0)
  699. {
  700. // Not calling ReadDriveStatus(... dsSize), relying on drive ready status cached by the background thread
  701. TDriveInfoRec * DriveInfoRec = DriveInfo->Get(Drive);
  702. if (DriveInfoRec->Valid && DriveInfoRec->DriveReady &&
  703. (DriveInfoRec->DriveType != DRIVE_CDROM) &&
  704. ((DriveInfoRec->DriveType != DRIVE_REMOVABLE) || (Drive >= DriveInfo->FirstFixedDrive)))
  705. {
  706. Drives->Add(Drive);
  707. }
  708. }
  709. }
  710. std::unique_ptr<TObjectList> Result(new TObjectList());
  711. for (int Index = 0; Index < Drives->Count; Index++)
  712. {
  713. UnicodeString Drive = Drives->Strings[Index];
  714. std::unique_ptr<TDirectoryMonitor> Monitor(new TDirectoryMonitor(Application));
  715. try
  716. {
  717. Monitor->Path = DriveInfo->GetDriveRoot(Drive);
  718. Monitor->WatchSubtree = true;
  719. Monitor->WatchFilters = Filter;
  720. Monitor->OnCreated = OnChanged;
  721. Monitor->OnModified = OnChanged;
  722. Monitor->Active = true;
  723. Result->Add(Monitor.release());
  724. }
  725. catch (Exception & E)
  726. {
  727. // Ignore errors watching not-ready drives
  728. DebugUsedParam(E);
  729. }
  730. }
  731. return Result.release();
  732. }
  733. //---------------------------------------------------------------------------
  734. bool DontCopyCommandToClipboard = false;
  735. //---------------------------------------------------------------------------
  736. bool __fastcall CopyCommandToClipboard(const UnicodeString & Command)
  737. {
  738. bool Result = !DontCopyCommandToClipboard && UseAlternativeFunction() && IsKeyPressed(VK_CONTROL);
  739. if (Result)
  740. {
  741. TInstantOperationVisualizer Visualizer;
  742. AppLogFmt(L"Copied command to the clipboard: %s", (Command));
  743. CopyToClipboard(Command);
  744. }
  745. return Result;
  746. }
  747. //---------------------------------------------------------------------------
  748. static bool __fastcall DoExecuteShell(const UnicodeString Path, const UnicodeString Params,
  749. bool ChangeWorkingDirectory, HANDLE * Handle)
  750. {
  751. bool Result = CopyCommandToClipboard(FormatCommand(Path, Params));
  752. if (Result)
  753. {
  754. if (Handle != NULL)
  755. {
  756. *Handle = NULL;
  757. }
  758. }
  759. else
  760. {
  761. UnicodeString Directory = ExtractFilePath(Path);
  762. TShellExecuteInfoW ExecuteInfo;
  763. memset(&ExecuteInfo, 0, sizeof(ExecuteInfo));
  764. ExecuteInfo.cbSize = sizeof(ExecuteInfo);
  765. ExecuteInfo.fMask =
  766. SEE_MASK_FLAG_NO_UI |
  767. FLAGMASK((Handle != NULL), SEE_MASK_NOCLOSEPROCESS);
  768. ExecuteInfo.hwnd = Application->Handle;
  769. ExecuteInfo.lpFile = static_cast<const wchar_t *>(Path.data());
  770. ExecuteInfo.lpParameters = static_cast<const wchar_t *>(Params.data());
  771. ExecuteInfo.lpDirectory = (ChangeWorkingDirectory ? Directory.c_str() : NULL);
  772. ExecuteInfo.nShow = SW_SHOW;
  773. AppLogFmt(L"Executing program \"%s\" with params: %s", (Path, Params));
  774. Result = (ShellExecuteEx(&ExecuteInfo) != 0);
  775. if (Result)
  776. {
  777. if (Handle != NULL)
  778. {
  779. *Handle = ExecuteInfo.hProcess;
  780. }
  781. }
  782. }
  783. return Result;
  784. }
  785. //---------------------------------------------------------------------------
  786. void __fastcall ExecuteShellChecked(const UnicodeString Path, const UnicodeString Params, bool ChangeWorkingDirectory)
  787. {
  788. if (!DoExecuteShell(Path, Params, ChangeWorkingDirectory, NULL))
  789. {
  790. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Path)));
  791. }
  792. }
  793. //---------------------------------------------------------------------------
  794. void __fastcall ExecuteShellChecked(const UnicodeString Command)
  795. {
  796. UnicodeString Program, Params, Dir;
  797. SplitCommand(Command, Program, Params, Dir);
  798. ExecuteShellChecked(Program, Params);
  799. }
  800. //---------------------------------------------------------------------------
  801. bool __fastcall ExecuteShell(const UnicodeString Path, const UnicodeString Params,
  802. HANDLE & Handle)
  803. {
  804. return DoExecuteShell(Path, Params, false, &Handle);
  805. }
  806. //---------------------------------------------------------------------------
  807. void __fastcall ExecuteShellCheckedAndWait(const UnicodeString Command,
  808. TProcessMessagesEvent ProcessMessages)
  809. {
  810. UnicodeString Program, Params, Dir;
  811. SplitCommand(Command, Program, Params, Dir);
  812. HANDLE ProcessHandle;
  813. bool Result = DoExecuteShell(Program, Params, false, &ProcessHandle);
  814. if (!Result)
  815. {
  816. throw EOSExtException(FMTLOAD(EXECUTE_APP_ERROR, (Program)));
  817. }
  818. else
  819. {
  820. if (ProcessHandle != NULL) // only if command was copied to clipboard only
  821. {
  822. if (ProcessMessages != NULL)
  823. {
  824. unsigned long WaitResult;
  825. do
  826. {
  827. // Same as in ExecuteProcessAndReadOutput
  828. WaitResult = WaitForSingleObject(ProcessHandle, 50);
  829. if (WaitResult == WAIT_FAILED)
  830. {
  831. throw Exception(LoadStr(DOCUMENT_WAIT_ERROR));
  832. }
  833. ProcessMessages();
  834. }
  835. while (WaitResult == WAIT_TIMEOUT);
  836. }
  837. else
  838. {
  839. WaitForSingleObject(ProcessHandle, INFINITE);
  840. }
  841. }
  842. }
  843. }
  844. //---------------------------------------------------------------------------
  845. bool __fastcall SpecialFolderLocation(int PathID, UnicodeString & Path)
  846. {
  847. LPITEMIDLIST Pidl;
  848. wchar_t Buf[256];
  849. if (SHGetSpecialFolderLocation(NULL, PathID, &Pidl) == NO_ERROR &&
  850. SHGetPathFromIDList(Pidl, Buf))
  851. {
  852. Path = UnicodeString(Buf);
  853. return true;
  854. }
  855. return false;
  856. }
  857. //---------------------------------------------------------------------------
  858. UnicodeString __fastcall UniqTempDir(const UnicodeString BaseDir, const UnicodeString Identity,
  859. bool Mask)
  860. {
  861. DebugAssert(!BaseDir.IsEmpty());
  862. UnicodeString TempDir;
  863. do
  864. {
  865. TempDir = IncludeTrailingBackslash(BaseDir) + Identity;
  866. if (Mask)
  867. {
  868. TempDir += L"?????";
  869. }
  870. else
  871. {
  872. TempDir += IncludeTrailingBackslash(FormatDateTime(L"nnzzz", Now()));
  873. }
  874. }
  875. while (!Mask && DirectoryExists(ApiPath(TempDir)));
  876. return TempDir;
  877. }
  878. //---------------------------------------------------------------------------
  879. class TSessionColors : public TComponent
  880. {
  881. public:
  882. __fastcall TSessionColors(TComponent * Owner) : TComponent(Owner)
  883. {
  884. Name = QualifiedClassName();
  885. }
  886. static TSessionColors * __fastcall Retrieve(TComponent * Component)
  887. {
  888. TSessionColors * SessionColors = dynamic_cast<TSessionColors *>(Component->FindComponent(QualifiedClassName()));
  889. if (SessionColors == NULL)
  890. {
  891. SessionColors = new TSessionColors(Component);
  892. }
  893. return SessionColors;
  894. }
  895. typedef std::map<TColor, int> TColorMap;
  896. TColorMap ColorMap;
  897. };
  898. //---------------------------------------------------------------------------
  899. int __fastcall GetSessionColorImage(
  900. TCustomImageList * ImageList, TColor Color, int MaskIndex)
  901. {
  902. TSessionColors * SessionColors = TSessionColors::Retrieve(ImageList);
  903. int Result;
  904. TSessionColors::TColorMap::const_iterator I = SessionColors->ColorMap.find(Color);
  905. if (I != SessionColors->ColorMap.end())
  906. {
  907. Result = I->second;
  908. }
  909. else
  910. {
  911. // This overly complex drawing is here to support color button on SiteAdvanced
  912. // dialog. There we use plain TImageList, instead of TPngImageList,
  913. // TButton does not work with transparent images
  914. // (not even TBitmap with Transparent = true)
  915. std::unique_ptr<TBitmap> MaskBitmap(new TBitmap());
  916. ImageList->GetBitmap(MaskIndex, MaskBitmap.get());
  917. std::unique_ptr<TPngImage> MaskImage(new TPngImage());
  918. MaskImage->Assign(MaskBitmap.get());
  919. std::unique_ptr<TPngImage> ColorImage(new TPngImage(COLOR_RGB, 16, ImageList->Width, ImageList->Height));
  920. TColor MaskTransparentColor = MaskImage->Pixels[0][0];
  921. TColor TransparentColor = MaskTransparentColor;
  922. // Expecting that the color to be replaced is in the centre of the image (HACK)
  923. TColor MaskColor = MaskImage->Pixels[ImageList->Width / 2][ImageList->Height / 2];
  924. for (int Y = 0; Y < ImageList->Height; Y++)
  925. {
  926. for (int X = 0; X < ImageList->Width; X++)
  927. {
  928. TColor SourceColor = MaskImage->Pixels[X][Y];
  929. TColor DestColor;
  930. // this branch is pointless as long as MaskTransparentColor and
  931. // TransparentColor are the same
  932. if (SourceColor == MaskTransparentColor)
  933. {
  934. DestColor = TransparentColor;
  935. }
  936. else if (SourceColor == MaskColor)
  937. {
  938. DestColor = Color;
  939. }
  940. else
  941. {
  942. DestColor = SourceColor;
  943. }
  944. ColorImage->Pixels[X][Y] = DestColor;
  945. }
  946. }
  947. std::unique_ptr<TBitmap> Bitmap(new TBitmap());
  948. Bitmap->SetSize(ImageList->Width, ImageList->Height);
  949. ColorImage->AssignTo(Bitmap.get());
  950. Result = ImageList->AddMasked(Bitmap.get(), TransparentColor);
  951. SessionColors->ColorMap.insert(std::make_pair(Color, Result));
  952. }
  953. return Result;
  954. }
  955. //---------------------------------------------------------------------------
  956. void __fastcall RegenerateSessionColorsImageList(TCustomImageList * ImageList, int MaskIndex)
  957. {
  958. TSessionColors * SessionColors = TSessionColors::Retrieve(ImageList);
  959. std::vector<TColor> Colors;
  960. size_t FixedImages = static_cast<size_t>(ImageList->Count);
  961. Colors.resize(FixedImages + SessionColors->ColorMap.size());
  962. TSessionColors::TColorMap::const_iterator I = SessionColors->ColorMap.begin();
  963. while (I != SessionColors->ColorMap.end())
  964. {
  965. DebugAssert(Colors[I->second] == TColor());
  966. Colors[I->second] = I->first;
  967. I++;
  968. }
  969. TSessionColors::TColorMap ColorMap = SessionColors->ColorMap;
  970. SessionColors->ColorMap.clear();
  971. for (size_t Index = 0; Index < Colors.size(); Index++)
  972. {
  973. bool IsFixedImageIndex = (Index < FixedImages);
  974. DebugAssert((Colors[Index] == TColor()) == IsFixedImageIndex);
  975. if (!IsFixedImageIndex)
  976. {
  977. GetSessionColorImage(ImageList, Colors[Index], MaskIndex);
  978. }
  979. }
  980. DebugAssert(SessionColors->ColorMap == ColorMap);
  981. }
  982. //---------------------------------------------------------------------------
  983. void __fastcall SetSubmenu(TTBXCustomItem * Item, bool Enable)
  984. {
  985. class TTBXPublicItem : public TTBXCustomItem
  986. {
  987. public:
  988. __property ItemStyle;
  989. };
  990. TTBXPublicItem * PublicItem = reinterpret_cast<TTBXPublicItem *>(Item);
  991. DebugAssert(PublicItem != NULL);
  992. // See TTBItemViewer.IsPtInButtonPart (called from TTBItemViewer.MouseDown)
  993. if (Enable)
  994. {
  995. PublicItem->ItemStyle = PublicItem->ItemStyle << tbisSubmenu;
  996. }
  997. else
  998. {
  999. PublicItem->ItemStyle = PublicItem->ItemStyle >> tbisSubmenu;
  1000. }
  1001. }
  1002. //---------------------------------------------------------------------------
  1003. bool __fastcall IsEligibleForApplyingTabs(
  1004. UnicodeString Line, int & TabPos, UnicodeString & Start, UnicodeString & Remaining)
  1005. {
  1006. bool Result = false;
  1007. TabPos = Line.Pos(L"\t");
  1008. if (TabPos > 0)
  1009. {
  1010. Remaining = Line.SubString(TabPos + 1, Line.Length() - TabPos);
  1011. // WORKAROUND
  1012. // Some translations still use obsolete hack of consecutive tabs to aling the contents.
  1013. // Delete these, so that the following check does not fail on this
  1014. while (Remaining.SubString(1, 1) == L"\t")
  1015. {
  1016. Remaining.Delete(1, 1);
  1017. }
  1018. // We do not have, not support, mutiple tabs on a single line
  1019. if (DebugAlwaysTrue(Remaining.Pos(L"\t") == 0))
  1020. {
  1021. Start = Line.SubString(1, TabPos - 1);
  1022. // WORKAROUND
  1023. // Previously we padded the string before tab with spaces,
  1024. // to aling the contents across multiple lines
  1025. Start = Start.TrimRight();
  1026. // at least two normal spaces for separation
  1027. Start += L" ";
  1028. Result = true;
  1029. }
  1030. }
  1031. return Result;
  1032. }
  1033. //---------------------------------------------------------------------------
  1034. static int __fastcall CalculateWidthByLength(UnicodeString Text, void * /*Arg*/)
  1035. {
  1036. return Text.Length();
  1037. }
  1038. //---------------------------------------------------------------------------
  1039. void __fastcall ApplyTabs(
  1040. UnicodeString & Text, wchar_t Padding,
  1041. TCalculateWidth CalculateWidth, void * CalculateWidthArg)
  1042. {
  1043. if (CalculateWidth == NULL)
  1044. {
  1045. DebugAssert(CalculateWidthArg == NULL);
  1046. CalculateWidth = CalculateWidthByLength;
  1047. }
  1048. std::unique_ptr<TStringList> Lines(TextToStringList(Text));
  1049. int MaxWidth = -1;
  1050. for (int Index = 0; Index < Lines->Count; Index++)
  1051. {
  1052. UnicodeString Line = Lines->Strings[Index];
  1053. int TabPos;
  1054. UnicodeString Start;
  1055. UnicodeString Remaining;
  1056. if (IsEligibleForApplyingTabs(Line, TabPos, Start, Remaining))
  1057. {
  1058. int Width = CalculateWidth(Start, CalculateWidthArg);
  1059. MaxWidth = Max(MaxWidth, Width);
  1060. }
  1061. }
  1062. // Optimization and also to prevent potential regression for texts without tabs
  1063. if (MaxWidth >= 0)
  1064. {
  1065. for (int Index = 0; Index < Lines->Count; Index++)
  1066. {
  1067. UnicodeString Line = Lines->Strings[Index];
  1068. int TabPos;
  1069. UnicodeString Start;
  1070. UnicodeString Remaining;
  1071. if (IsEligibleForApplyingTabs(Line, TabPos, Start, Remaining))
  1072. {
  1073. int Width;
  1074. int Iterations = 0;
  1075. while ((Width = CalculateWidth(Start, CalculateWidthArg)) < MaxWidth)
  1076. {
  1077. int Wider = CalculateWidth(Start + Padding, CalculateWidthArg);
  1078. // If padded string is wider than max width by more pixels
  1079. // than non-padded string is shorter than max width
  1080. if ((Wider > MaxWidth) && ((Wider - MaxWidth) > (MaxWidth - Width)))
  1081. {
  1082. break;
  1083. }
  1084. Start += Padding;
  1085. Iterations++;
  1086. // In rare case a tab is zero-width with some strange font (like HYLE)
  1087. if (Iterations > 100)
  1088. {
  1089. break;
  1090. }
  1091. }
  1092. Lines->Strings[Index] = Start + Remaining;
  1093. }
  1094. }
  1095. Text = Lines->Text;
  1096. // remove trailing newline
  1097. Text = Text.TrimRight();
  1098. }
  1099. }
  1100. //---------------------------------------------------------------------------
  1101. static void __fastcall DoSelectScaledImageList(TImageList * ImageList)
  1102. {
  1103. TImageList * MatchingList = NULL;
  1104. int MachingPixelsPerInch = 0;
  1105. int PixelsPerInch = LargerPixelsPerInch(GetComponentPixelsPerInch(ImageList), WinConfiguration->LargerToolbar);
  1106. for (int Index = 0; Index < ImageList->Owner->ComponentCount; Index++)
  1107. {
  1108. TImageList * OtherList = dynamic_cast<TImageList *>(ImageList->Owner->Components[Index]);
  1109. if ((OtherList != NULL) &&
  1110. (OtherList != ImageList) &&
  1111. StartsStr(ImageList->Name, OtherList->Name))
  1112. {
  1113. UnicodeString OtherListPixelsPerInchStr =
  1114. OtherList->Name.SubString(
  1115. ImageList->Name.Length() + 1, OtherList->Name.Length() - ImageList->Name.Length());
  1116. int OtherListPixelsPerInch = StrToInt(OtherListPixelsPerInchStr);
  1117. if ((OtherListPixelsPerInch <= PixelsPerInch) &&
  1118. ((MatchingList == NULL) ||
  1119. (MachingPixelsPerInch < OtherListPixelsPerInch)))
  1120. {
  1121. MatchingList = OtherList;
  1122. MachingPixelsPerInch = OtherListPixelsPerInch;
  1123. }
  1124. }
  1125. }
  1126. if (MatchingList != NULL)
  1127. {
  1128. UnicodeString ImageListBackupName = ImageList->Name + IntToStr(USER_DEFAULT_SCREEN_DPI);
  1129. if (ImageList->Owner->FindComponent(ImageListBackupName) == NULL)
  1130. {
  1131. TImageList * ImageListBackup;
  1132. TPngImageList * PngImageList = dynamic_cast<TPngImageList *>(ImageList);
  1133. if (PngImageList != NULL)
  1134. {
  1135. ImageListBackup = new TPngImageList(ImageList->Owner);
  1136. }
  1137. else
  1138. {
  1139. ImageListBackup = new TImageList(ImageList->Owner);
  1140. }
  1141. ImageListBackup->Name = ImageListBackupName;
  1142. ImageList->Owner->InsertComponent(ImageListBackup);
  1143. CopyImageList(ImageListBackup, ImageList);
  1144. }
  1145. CopyImageList(ImageList, MatchingList);
  1146. }
  1147. }
  1148. //---------------------------------------------------------------------------
  1149. static void __fastcall ImageListRescale(TComponent * Sender, TObject * /*Token*/)
  1150. {
  1151. TImageList * ImageList = DebugNotNull(dynamic_cast<TImageList *>(Sender));
  1152. DoSelectScaledImageList(ImageList);
  1153. }
  1154. //---------------------------------------------------------------------------
  1155. void __fastcall SelectScaledImageList(TImageList * ImageList)
  1156. {
  1157. DoSelectScaledImageList(ImageList);
  1158. SetRescaleFunction(ImageList, ImageListRescale);
  1159. }
  1160. //---------------------------------------------------------------------------
  1161. void __fastcall CopyImageList(TImageList * TargetList, TImageList * SourceList)
  1162. {
  1163. // Maybe this is not necessary, once the TPngImageList::Assign was fixed.
  1164. // But if we ever use Assign, make sure the target keeps its Scaled property.
  1165. TPngImageList * PngTargetList = dynamic_cast<TPngImageList *>(TargetList);
  1166. TPngImageList * PngSourceList = dynamic_cast<TPngImageList *>(SourceList);
  1167. TargetList->Clear();
  1168. TargetList->Height = SourceList->Height;
  1169. TargetList->Width = SourceList->Width;
  1170. if ((PngTargetList != NULL) && (PngSourceList != NULL))
  1171. {
  1172. // AddImages won't copy the names and we need them for
  1173. // LoadDialogImage and TFrameAnimation
  1174. PngTargetList->PngImages->Assign(PngSourceList->PngImages);
  1175. }
  1176. else
  1177. {
  1178. TargetList->AddImages(SourceList);
  1179. }
  1180. }
  1181. //---------------------------------------------------------------------------
  1182. static bool __fastcall DoLoadDialogImage(TImage * Image, const UnicodeString & ImageName)
  1183. {
  1184. bool Result = false;
  1185. if (GlyphsModule != NULL)
  1186. {
  1187. TPngImageList * DialogImages = GetDialogImages(Image);
  1188. int Index;
  1189. for (Index = 0; Index < DialogImages->PngImages->Count; Index++)
  1190. {
  1191. TPngImageCollectionItem * PngItem = DialogImages->PngImages->Items[Index];
  1192. if (SameText(PngItem->Name, ImageName))
  1193. {
  1194. Image->Picture->Assign(PngItem->PngImage);
  1195. break;
  1196. }
  1197. }
  1198. DebugAssert(Index < DialogImages->PngImages->Count);
  1199. Result = true;
  1200. }
  1201. // When showing an exception from wWinMain, the images are released already.
  1202. // Non-existence of the glyphs module is just a good indication of that.
  1203. // We expect errors only.
  1204. else if (ImageName == L"Error")
  1205. {
  1206. Image->Picture->Icon->Handle = LoadIcon(0, IDI_HAND);
  1207. }
  1208. // For showing an information about trace files
  1209. else if (DebugAlwaysTrue(ImageName == L"Information"))
  1210. {
  1211. Image->Picture->Icon->Handle = LoadIcon(0, IDI_APPLICATION);
  1212. }
  1213. return Result;
  1214. }
  1215. //---------------------------------------------------------------------------
  1216. class TDialogImageName : public TObject
  1217. {
  1218. public:
  1219. UnicodeString ImageName;
  1220. };
  1221. //---------------------------------------------------------------------------
  1222. static void __fastcall DialogImageRescale(TComponent * Sender, TObject * Token)
  1223. {
  1224. TImage * Image = DebugNotNull(dynamic_cast<TImage *>(Sender));
  1225. TDialogImageName * DialogImageName = DebugNotNull(dynamic_cast<TDialogImageName *>(Token));
  1226. DoLoadDialogImage(Image, DialogImageName->ImageName);
  1227. }
  1228. //---------------------------------------------------------------------------
  1229. void __fastcall LoadDialogImage(TImage * Image, const UnicodeString & ImageName)
  1230. {
  1231. if (DoLoadDialogImage(Image, ImageName))
  1232. {
  1233. TDialogImageName * DialogImageName = new TDialogImageName();
  1234. DialogImageName->ImageName = ImageName;
  1235. SetRescaleFunction(Image, DialogImageRescale, DialogImageName, true);
  1236. }
  1237. }
  1238. //---------------------------------------------------------------------------
  1239. int __fastcall DialogImageSize(TForm * Form)
  1240. {
  1241. return ScaleByPixelsPerInch(32, Form);
  1242. }
  1243. //---------------------------------------------------------------------------
  1244. void __fastcall HideComponentsPanel(TForm * Form)
  1245. {
  1246. TComponent * Component = DebugNotNull(Form->FindComponent(L"ComponentsPanel"));
  1247. TPanel * Panel = DebugNotNull(dynamic_cast<TPanel *>(Component));
  1248. DebugAssert(Panel->Align == alBottom);
  1249. int Offset = Panel->Height;
  1250. Panel->Visible = false;
  1251. Panel->Height = 0;
  1252. Form->Height -= Offset;
  1253. for (int Index = 0; Index < Form->ControlCount; Index++)
  1254. {
  1255. TControl * Control = Form->Controls[Index];
  1256. // Shift back bottom-anchored controls
  1257. // (needed for toolbar panel on Progress window and buttons on Preferences dialog),
  1258. if ((Control->Align == alNone) &&
  1259. Control->Anchors.Contains(akBottom) &&
  1260. !Control->Anchors.Contains(akTop))
  1261. {
  1262. Control->Top += Offset;
  1263. }
  1264. // Resize back all-anchored controls
  1265. // (needed for main panel on Preferences dialog),
  1266. if (Control->Anchors.Contains(akBottom) &&
  1267. Control->Anchors.Contains(akTop))
  1268. {
  1269. Control->Height += Offset;
  1270. }
  1271. }
  1272. }
  1273. //---------------------------------------------------------------------------
  1274. TIncrementalSearchState::TIncrementalSearchState()
  1275. {
  1276. Reset();
  1277. }
  1278. //---------------------------------------------------------------------------
  1279. void TIncrementalSearchState::Reset()
  1280. {
  1281. Searching = false;
  1282. Text = EmptyStr;
  1283. HaveNext = false;
  1284. }
  1285. //---------------------------------------------------------------------------
  1286. UnicodeString FormatIncrementalSearchStatus(const TIncrementalSearchState & SearchState)
  1287. {
  1288. UnicodeString Result =
  1289. FMTLOAD(INC_SEARCH, (DefaultStr(SearchState.Text, LoadStr(INC_SEARCH_TYPE)))) +
  1290. ((SearchState.HaveNext && DebugAlwaysTrue(!SearchState.Text.IsEmpty())) ? L" " + LoadStr(INC_NEXT_SEARCH) : EmptyStr);
  1291. return Result;
  1292. }
  1293. //---------------------------------------------------------------------------
  1294. class TCustomDocHandler : public TComponent, public ::IDocHostUIHandler
  1295. {
  1296. public:
  1297. __fastcall TCustomDocHandler(TComponent * Owner, ICustomDoc * CustomDoc) :
  1298. TComponent(Owner), FCustomDoc(CustomDoc)
  1299. {
  1300. }
  1301. virtual __fastcall ~TCustomDocHandler()
  1302. {
  1303. // Something keeps the reference behind otherwise and calls it on WM_SETTINGCHANGE.
  1304. // Would make sense with TWebBrowserEx, which leaks. But it happens even with TWebBrowser.
  1305. FCustomDoc->SetUIHandler(NULL);
  1306. FCustomDoc->Release();
  1307. }
  1308. protected:
  1309. ICustomDoc * FCustomDoc;
  1310. #pragma clang diagnostic push
  1311. #pragma clang diagnostic ignored "-Woverloaded-virtual"
  1312. virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID ClassId, void ** Intf)
  1313. {
  1314. HRESULT Result = S_OK;
  1315. if (ClassId == IID_IUnknown)
  1316. {
  1317. *Intf = (IUnknown *)this;
  1318. }
  1319. else if (ClassId == ::IID_IDocHostUIHandler)
  1320. {
  1321. *Intf = (::IDocHostUIHandler *)this;
  1322. }
  1323. else
  1324. {
  1325. Result = E_NOINTERFACE;
  1326. }
  1327. return Result;
  1328. }
  1329. #pragma clang diagnostic pop
  1330. virtual ULONG STDMETHODCALLTYPE AddRef()
  1331. {
  1332. return -1;
  1333. }
  1334. virtual ULONG STDMETHODCALLTYPE Release()
  1335. {
  1336. return -1;
  1337. }
  1338. virtual HRESULT STDMETHODCALLTYPE ShowContextMenu(
  1339. DWORD DebugUsedArg(dwID), POINT *, IUnknown *, IDispatch *)
  1340. {
  1341. // No context menu
  1342. // (implementing IDocHostUIHandler reenabled context menu disabled by TBrowserViewer::DoContextPopup)
  1343. return S_OK;
  1344. }
  1345. virtual HRESULT STDMETHODCALLTYPE GetHostInfo(::_DOCHOSTUIINFO * Info)
  1346. {
  1347. // Setting ControlBorder is ignored with IDocHostUIHandler.
  1348. // DOCHOSTUIFLAG_DPI_AWARE does not seem to have any effect
  1349. Info->dwFlags |= DOCHOSTUIFLAG_SCROLL_NO | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_DPI_AWARE;
  1350. return S_OK;
  1351. }
  1352. virtual HRESULT STDMETHODCALLTYPE ShowUI(
  1353. DWORD DebugUsedArg(dwID), IOleInPlaceActiveObject *, IOleCommandTarget *, IOleInPlaceFrame *, IOleInPlaceUIWindow *)
  1354. {
  1355. return E_NOTIMPL;
  1356. }
  1357. virtual HRESULT STDMETHODCALLTYPE HideUI()
  1358. {
  1359. return E_NOTIMPL;
  1360. }
  1361. virtual HRESULT STDMETHODCALLTYPE UpdateUI()
  1362. {
  1363. return E_NOTIMPL;
  1364. }
  1365. virtual HRESULT STDMETHODCALLTYPE EnableModeless(BOOL DebugUsedArg(fEnable))
  1366. {
  1367. return E_NOTIMPL;
  1368. }
  1369. virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL DebugUsedArg(fActivate))
  1370. {
  1371. return E_NOTIMPL;
  1372. }
  1373. virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL DebugUsedArg(fActivate))
  1374. {
  1375. return E_NOTIMPL;
  1376. }
  1377. virtual HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT, IOleInPlaceUIWindow *, BOOL DebugUsedArg(fRameWindow))
  1378. {
  1379. return E_NOTIMPL;
  1380. }
  1381. virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG, const GUID * DebugUsedArg(pguidCmdGroup), DWORD DebugUsedArg(nCmdID))
  1382. {
  1383. return E_NOTIMPL;
  1384. }
  1385. virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR * DebugUsedArg(pchKey), DWORD DebugUsedArg(dw))
  1386. {
  1387. return E_NOTIMPL;
  1388. }
  1389. virtual HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget *, IDropTarget **)
  1390. {
  1391. return E_NOTIMPL;
  1392. }
  1393. virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **)
  1394. {
  1395. return E_NOTIMPL;
  1396. }
  1397. virtual HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD DebugUsedArg(dwTranslate), OLECHAR * DebugUsedArg(pchURLIn), OLECHAR ** DebugUsedArg(ppchURLOut))
  1398. {
  1399. return E_NOTIMPL;
  1400. }
  1401. virtual HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject *, IDataObject **)
  1402. {
  1403. return E_NOTIMPL;
  1404. }
  1405. };
  1406. //---------------------------------------------------------------------------
  1407. // Included only here as it defines ambiguous LONG_PTR, causing INVALID_HANDLE_VALUE to be unusable
  1408. #include <WebBrowserEx.hpp>
  1409. //---------------------------------------------------------------------------
  1410. class TBrowserViewer : public TWebBrowserEx
  1411. {
  1412. public:
  1413. __fastcall virtual TBrowserViewer(TComponent* AOwner);
  1414. void __fastcall AddLinkHandler(
  1415. const UnicodeString & Url, TNotifyEvent Handler);
  1416. void __fastcall NavigateToUrl(const UnicodeString & Url);
  1417. TControl * LoadingPanel;
  1418. protected:
  1419. DYNAMIC void __fastcall DoContextPopup(const TPoint & MousePos, bool & Handled);
  1420. void __fastcall DocumentComplete(
  1421. TObject * Sender, const _di_IDispatch Disp, const OleVariant & URL);
  1422. void __fastcall BeforeNavigate2(
  1423. TObject * Sender, const _di_IDispatch Disp, const OleVariant & URL,
  1424. const OleVariant & Flags, const OleVariant & TargetFrameName,
  1425. const OleVariant & PostData, const OleVariant & Headers, WordBool & Cancel);
  1426. bool FComplete;
  1427. std::map<UnicodeString, TNotifyEvent> FHandlers;
  1428. };
  1429. //---------------------------------------------------------------------------
  1430. __fastcall TBrowserViewer::TBrowserViewer(TComponent* AOwner) :
  1431. TWebBrowserEx(AOwner)
  1432. {
  1433. FComplete = false;
  1434. OnDocumentComplete = DocumentComplete;
  1435. OnBeforeNavigate2 = BeforeNavigate2;
  1436. LoadingPanel = NULL;
  1437. }
  1438. //---------------------------------------------------------------------------
  1439. void __fastcall TBrowserViewer::AddLinkHandler(
  1440. const UnicodeString & Url, TNotifyEvent Handler)
  1441. {
  1442. FHandlers.insert(std::make_pair(Url, Handler));
  1443. }
  1444. //---------------------------------------------------------------------------
  1445. void __fastcall TBrowserViewer::DoContextPopup(const TPoint & MousePos, bool & Handled)
  1446. {
  1447. // Suppress built-in context menu.
  1448. // Is ignored with IDocHostUIHandler. Needs to be overriden by ShowContextMenu.
  1449. Handled = true;
  1450. TWebBrowserEx::DoContextPopup(MousePos, Handled);
  1451. }
  1452. //---------------------------------------------------------------------------
  1453. void __fastcall TBrowserViewer::DocumentComplete(
  1454. TObject * /*Sender*/, const _di_IDispatch /*Disp*/, const OleVariant & /*URL*/)
  1455. {
  1456. SetBrowserDesignModeOff(this);
  1457. FComplete = true;
  1458. if (LoadingPanel != NULL)
  1459. {
  1460. LoadingPanel->Visible = false;
  1461. }
  1462. }
  1463. //---------------------------------------------------------------------------
  1464. void __fastcall TBrowserViewer::BeforeNavigate2(
  1465. TObject * /*Sender*/, const _di_IDispatch /*Disp*/, const OleVariant & AURL,
  1466. const OleVariant & /*Flags*/, const OleVariant & /*TargetFrameName*/,
  1467. const OleVariant & /*PostData*/, const OleVariant & /*Headers*/, WordBool & Cancel)
  1468. {
  1469. // If OnDocumentComplete was not called yet, is has to be our initial message URL,
  1470. // opened using TWebBrowserEx::Navigate(), allow it.
  1471. // Otherwise it's user navigating, block that and open link
  1472. // in an external browser, possibly adding campaign parameters on the way.
  1473. if (FComplete)
  1474. {
  1475. Cancel = 1;
  1476. UnicodeString URL = AURL;
  1477. if (FHandlers.count(URL) > 0)
  1478. {
  1479. FHandlers[URL](this);
  1480. }
  1481. else
  1482. {
  1483. OpenBrowser(URL);
  1484. }
  1485. }
  1486. }
  1487. //---------------------------------------------------------------------------
  1488. void __fastcall TBrowserViewer::NavigateToUrl(const UnicodeString & Url)
  1489. {
  1490. FComplete = false;
  1491. Navigate(Url.c_str());
  1492. }
  1493. //---------------------------------------------------------------------------
  1494. TWebBrowserEx * __fastcall CreateBrowserViewer(TPanel * Parent, const UnicodeString & LoadingLabel)
  1495. {
  1496. TBrowserViewer * Result = new TBrowserViewer(Parent);
  1497. // TWebBrowserEx has its own unrelated Name and Parent properties.
  1498. // The name is used in DownloadUpdate().
  1499. static_cast<TWinControl *>(Result)->Name = L"BrowserViewer";
  1500. static_cast<TWinControl *>(Result)->Parent = Parent;
  1501. Result->Align = alClient;
  1502. // Is ignored with IDocHostUIHandler. Needs to be overriden by DOCHOSTUIFLAG_NO3DBORDER in GetHostInfo.
  1503. Result->ControlBorder = cbNone;
  1504. Result->LoadingPanel = CreateLabelPanel(Parent, LoadingLabel);
  1505. return Result;
  1506. }
  1507. //---------------------------------------------------------------------------
  1508. void __fastcall SetBrowserDesignModeOff(TWebBrowserEx * WebBrowser)
  1509. {
  1510. if (DebugAlwaysTrue(WebBrowser->Document2 != NULL))
  1511. {
  1512. WebBrowser->Document2->designMode = L"Off";
  1513. }
  1514. }
  1515. //---------------------------------------------------------------------------
  1516. void __fastcall AddBrowserLinkHandler(TWebBrowserEx * WebBrowser,
  1517. const UnicodeString & Url, TNotifyEvent Handler)
  1518. {
  1519. TBrowserViewer * BrowserViewer = dynamic_cast<TBrowserViewer *>(WebBrowser);
  1520. if (DebugAlwaysTrue(BrowserViewer != NULL))
  1521. {
  1522. BrowserViewer->AddLinkHandler(Url, Handler);
  1523. }
  1524. }
  1525. //---------------------------------------------------------------------------
  1526. void __fastcall NavigateBrowserToUrl(TWebBrowserEx * WebBrowser, const UnicodeString & Url)
  1527. {
  1528. TBrowserViewer * BrowserViewer = dynamic_cast<TBrowserViewer *>(WebBrowser);
  1529. if (DebugAlwaysTrue(BrowserViewer != NULL))
  1530. {
  1531. BrowserViewer->NavigateToUrl(Url);
  1532. }
  1533. }
  1534. //---------------------------------------------------------------------------
  1535. void ReadyBrowserForStreaming(TWebBrowserEx * WebBrowser)
  1536. {
  1537. // This creates TWebBrowserEx::Document, which we need to stream in an in-memory document
  1538. NavigateBrowserToUrl(WebBrowser, L"about:blank");
  1539. // Needs to be followed by WaitBrowserToIdle
  1540. }
  1541. //---------------------------------------------------------------------------
  1542. void WaitBrowserToIdle(TWebBrowserEx * WebBrowser)
  1543. {
  1544. while (WebBrowser->ReadyState < ::READYSTATE_INTERACTIVE)
  1545. {
  1546. Application->ProcessMessages();
  1547. }
  1548. }
  1549. //---------------------------------------------------------------------------
  1550. void HideBrowserScrollbars(TWebBrowserEx * WebBrowser)
  1551. {
  1552. ICustomDoc * CustomDoc = NULL;
  1553. if (DebugAlwaysTrue(WebBrowser->Document != NULL) &&
  1554. SUCCEEDED(WebBrowser->Document->QueryInterface(&CustomDoc)) &&
  1555. DebugAlwaysTrue(CustomDoc != NULL))
  1556. {
  1557. TCustomDocHandler * Handler = new TCustomDocHandler(WebBrowser, CustomDoc);
  1558. CustomDoc->SetUIHandler(Handler);
  1559. }
  1560. }
  1561. //---------------------------------------------------------------------------
  1562. bool CopyTextFromBrowser(TWebBrowserEx * WebBrowser, UnicodeString & Text)
  1563. {
  1564. WebBrowser->SelectAll();
  1565. WebBrowser->CopyToClipBoard();
  1566. bool Result = NonEmptyTextFromClipboard(Text);
  1567. WebBrowser->DoCommand(L"UNSELECT");
  1568. return Result;
  1569. }
  1570. //---------------------------------------------------------------------------
  1571. TPanel * CreateBlankPanel(TComponent * Owner)
  1572. {
  1573. TPanel * Panel = new TPanel(Owner);
  1574. Panel->BevelOuter = bvNone;
  1575. Panel->BevelInner = bvNone; // default
  1576. Panel->BevelKind = bkNone;
  1577. Panel->Color = clWindow;
  1578. return Panel;
  1579. }
  1580. //---------------------------------------------------------------------------
  1581. TPanel * CreateLabelPanel(TPanel * Parent, const UnicodeString & Label)
  1582. {
  1583. TPanel * Result = CreateBlankPanel(Parent);
  1584. Result->Parent = Parent;
  1585. Result->Align = alClient;
  1586. Result->Caption = Label;
  1587. return Result;
  1588. }
  1589. //---------------------------------------------------------------------------
  1590. UnicodeString GenerateAppHtmlPage(TFont * Font, TPanel * Parent, const UnicodeString & Body, bool Seamless)
  1591. {
  1592. UnicodeString Result =
  1593. L"<!DOCTYPE html>\n"
  1594. L"<meta charset=\"utf-8\">\n"
  1595. L"<html>\n"
  1596. L"<head>\n"
  1597. L"<style>\n"
  1598. L"\n"
  1599. L"body\n"
  1600. L"{\n"
  1601. L" font-family: '" + Font->Name + L"';\n"
  1602. L" margin: " + UnicodeString(Seamless ? L"0" : L"0.5em") + L";\n"
  1603. L" color: " + ColorToWebColorStr(Parent->Font->Color) + L";\n"
  1604. L" background-color: " + ColorToWebColorStr(Parent->Color) + L";\n" +
  1605. UnicodeString(Seamless ? L" overflow: hidden;\n" : L"") +
  1606. L" font-size: " + IntToStr(Font->Size) + L"pt;\n"
  1607. L"}\n"
  1608. L"\n"
  1609. L"p\n"
  1610. L"{\n"
  1611. L" margin-top: 0;\n"
  1612. L" margin-bottom: 1em;\n"
  1613. L"}\n"
  1614. L"\n"
  1615. L"a, a:visited, a:hover, a:visited, a:current\n"
  1616. L"{\n"
  1617. L" color: " + ColorToWebColorStr(GetLinkColor(Parent)) + L";\n"
  1618. L"}\n"
  1619. L"</style>\n"
  1620. L"</head>\n"
  1621. L"<body>\n" +
  1622. Body +
  1623. L"</body>\n"
  1624. L"</html>\n";
  1625. return Result;
  1626. }
  1627. //---------------------------------------------------------------------------
  1628. void LoadBrowserDocument(TWebBrowserEx * WebBrowser, const UnicodeString & Document)
  1629. {
  1630. std::unique_ptr<TMemoryStream> DocumentStream(new TMemoryStream());
  1631. UTF8String DocumentUTF8 = UTF8String(Document);
  1632. DocumentStream->Write(DocumentUTF8.c_str(), DocumentUTF8.Length());
  1633. DocumentStream->Seek(0, 0);
  1634. // For stream-loaded document, when set only after loading from OnDocumentComplete,
  1635. // browser stops working
  1636. SetBrowserDesignModeOff(WebBrowser);
  1637. TStreamAdapter * DocumentStreamAdapter = new TStreamAdapter(DocumentStream.get(), soReference);
  1638. IPersistStreamInit * PersistStreamInit = NULL;
  1639. if (DebugAlwaysTrue(WebBrowser->Document != NULL) &&
  1640. SUCCEEDED(WebBrowser->Document->QueryInterface(IID_IPersistStreamInit, (void **)&PersistStreamInit)) &&
  1641. DebugAlwaysTrue(PersistStreamInit != NULL))
  1642. {
  1643. PersistStreamInit->Load(static_cast<_di_IStream>(*DocumentStreamAdapter));
  1644. PersistStreamInit->Release();
  1645. }
  1646. }
  1647. //---------------------------------------------------------------------------
  1648. TComponent * __fastcall FindComponentRecursively(TComponent * Root, const UnicodeString & Name)
  1649. {
  1650. for (int Index = 0; Index < Root->ComponentCount; Index++)
  1651. {
  1652. TComponent * Component = Root->Components[Index];
  1653. if (CompareText(Component->Name, Name) == 0)
  1654. {
  1655. return Component;
  1656. }
  1657. Component = FindComponentRecursively(Component, Name);
  1658. if (Component != NULL)
  1659. {
  1660. return Component;
  1661. }
  1662. }
  1663. return NULL;
  1664. }
  1665. //---------------------------------------------------------------------------
  1666. void __fastcall GetInstrutionsTheme(
  1667. TColor & MainInstructionColor, HFONT & MainInstructionFont, HFONT & InstructionFont)
  1668. {
  1669. MainInstructionColor = Graphics::clNone;
  1670. MainInstructionFont = 0;
  1671. InstructionFont = 0;
  1672. HTHEME Theme = OpenThemeData(0, L"TEXTSTYLE");
  1673. if (Theme != NULL)
  1674. {
  1675. LOGFONT AFont;
  1676. COLORREF AColor;
  1677. memset(&AFont, 0, sizeof(AFont));
  1678. // Using Canvas->Handle in the 2nd argument we can get scaled font,
  1679. // but at this point the form is sometime not scaled yet (difference is particularly for standalone messages like
  1680. // /UninstallCleanup), so the results are inconsistent.
  1681. if (GetThemeFont(Theme, NULL, TEXT_MAININSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
  1682. {
  1683. MainInstructionFont = CreateFontIndirect(&AFont);
  1684. }
  1685. if (GetThemeColor(Theme, TEXT_MAININSTRUCTION, 0, TMT_TEXTCOLOR, &AColor) == S_OK)
  1686. {
  1687. MainInstructionColor = (TColor)AColor;
  1688. }
  1689. memset(&AFont, 0, sizeof(AFont));
  1690. if (GetThemeFont(Theme, NULL, TEXT_INSTRUCTION, 0, TMT_FONT, &AFont) == S_OK)
  1691. {
  1692. InstructionFont = CreateFontIndirect(&AFont);
  1693. }
  1694. CloseThemeData(Theme);
  1695. }
  1696. }
  1697. //---------------------------------------------------------------------------
  1698. TLocalCustomCommand::TLocalCustomCommand()
  1699. {
  1700. }
  1701. //---------------------------------------------------------------------------
  1702. TLocalCustomCommand::TLocalCustomCommand(
  1703. const TCustomCommandData & Data, const UnicodeString & RemotePath, const UnicodeString & LocalPath) :
  1704. TFileCustomCommand(Data, RemotePath)
  1705. {
  1706. FLocalPath = LocalPath;
  1707. }
  1708. //---------------------------------------------------------------------------
  1709. TLocalCustomCommand::TLocalCustomCommand(const TCustomCommandData & Data,
  1710. const UnicodeString & RemotePath, const UnicodeString & LocalPath, const UnicodeString & FileName,
  1711. const UnicodeString & LocalFileName, const UnicodeString & FileList) :
  1712. TFileCustomCommand(Data, RemotePath, FileName, FileList)
  1713. {
  1714. FLocalPath = LocalPath;
  1715. FLocalFileName = LocalFileName;
  1716. }
  1717. //---------------------------------------------------------------------------
  1718. int __fastcall TLocalCustomCommand::PatternLen(const UnicodeString & Command, int Index)
  1719. {
  1720. int Len;
  1721. if ((Index < Command.Length()) && (Command[Index + 1] == L'\\'))
  1722. {
  1723. Len = 2;
  1724. }
  1725. else if ((Index < Command.Length()) && (Command[Index + 1] == L'^'))
  1726. {
  1727. Len = 3;
  1728. }
  1729. else
  1730. {
  1731. Len = TFileCustomCommand::PatternLen(Command, Index);
  1732. }
  1733. return Len;
  1734. }
  1735. //---------------------------------------------------------------------------
  1736. bool __fastcall TLocalCustomCommand::PatternReplacement(
  1737. int Index, const UnicodeString & Pattern, UnicodeString & Replacement, bool & Delimit)
  1738. {
  1739. bool Result;
  1740. if (Pattern == L"!\\")
  1741. {
  1742. // When used as "!\" in an argument to PowerShell, the trailing \ would escape the ",
  1743. // so we exclude it
  1744. Replacement = ExcludeTrailingBackslash(FLocalPath);
  1745. Result = true;
  1746. }
  1747. else if (Pattern == L"!^!")
  1748. {
  1749. Replacement = FLocalFileName;
  1750. Result = true;
  1751. }
  1752. else
  1753. {
  1754. Result = TFileCustomCommand::PatternReplacement(Index, Pattern, Replacement, Delimit);
  1755. }
  1756. return Result;
  1757. }
  1758. //---------------------------------------------------------------------------
  1759. void __fastcall TLocalCustomCommand::DelimitReplacement(
  1760. UnicodeString & /*Replacement*/, wchar_t /*Quote*/)
  1761. {
  1762. // never delimit local commands
  1763. }
  1764. //---------------------------------------------------------------------------
  1765. bool __fastcall TLocalCustomCommand::HasLocalFileName(const UnicodeString & Command)
  1766. {
  1767. return FindPattern(Command, L'^');
  1768. }
  1769. //---------------------------------------------------------------------------
  1770. bool __fastcall TLocalCustomCommand::IsFileCommand(const UnicodeString & Command)
  1771. {
  1772. return TFileCustomCommand::IsFileCommand(Command) || HasLocalFileName(Command);
  1773. }
  1774. //---------------------------------------------------------------------------
  1775. //---------------------------------------------------------------------------
  1776. typedef std::set<TDataModule *> TImagesModules;
  1777. static TImagesModules ImagesModules;
  1778. static std::map<int, TPngImageList *> AnimationsImages;
  1779. static std::map<int, TImageList *> ButtonImages;
  1780. static std::map<int, TPngImageList *> DialogImages;
  1781. //---------------------------------------------------------------------------
  1782. int __fastcall DoNormalizePixelsPerInch(int PixelsPerInch, bool Larger)
  1783. {
  1784. if (PixelsPerInch >= 192)
  1785. {
  1786. PixelsPerInch = 192;
  1787. }
  1788. else if (PixelsPerInch >= 144)
  1789. {
  1790. PixelsPerInch = Larger ? 192 : 144;
  1791. }
  1792. else if (PixelsPerInch >= 120)
  1793. {
  1794. PixelsPerInch = Larger ? 144 : 120;
  1795. }
  1796. else
  1797. {
  1798. PixelsPerInch = Larger ? 120 : 96;
  1799. }
  1800. return PixelsPerInch;
  1801. }
  1802. //---------------------------------------------------------------------------
  1803. int NormalizePixelsPerInch(int PixelsPerInch)
  1804. {
  1805. return DoNormalizePixelsPerInch(PixelsPerInch, false);
  1806. }
  1807. //---------------------------------------------------------------------------
  1808. int LargerPixelsPerInch(int PixelsPerInch, int Larger)
  1809. {
  1810. int Result = PixelsPerInch;
  1811. while (Larger > 0)
  1812. {
  1813. Result = DoNormalizePixelsPerInch(Result, true);
  1814. Larger--;
  1815. }
  1816. return Result;
  1817. }
  1818. //---------------------------------------------------------------------------
  1819. static int __fastcall NeedImagesModule(TControl * Control)
  1820. {
  1821. int PixelsPerInch = NormalizePixelsPerInch(GetControlPixelsPerInch(Control));
  1822. if (AnimationsImages.find(PixelsPerInch) == AnimationsImages.end())
  1823. {
  1824. TDataModule * ImagesModule;
  1825. HANDLE ResourceModule = GUIConfiguration->ChangeToDefaultResourceModule();
  1826. try
  1827. {
  1828. if (PixelsPerInch == 192)
  1829. {
  1830. ImagesModule = new TAnimations192Module(Application);
  1831. }
  1832. else if (PixelsPerInch == 144)
  1833. {
  1834. ImagesModule = new TAnimations144Module(Application);
  1835. }
  1836. else if (PixelsPerInch == 120)
  1837. {
  1838. ImagesModule = new TAnimations120Module(Application);
  1839. }
  1840. else
  1841. {
  1842. DebugAssert(PixelsPerInch == 96);
  1843. ImagesModule = new TAnimations96Module(Application);
  1844. }
  1845. ImagesModules.insert(ImagesModule);
  1846. TPngImageList * AAnimationImages =
  1847. DebugNotNull(dynamic_cast<TPngImageList *>(ImagesModule->FindComponent(L"AnimationImages")));
  1848. AnimationsImages.insert(std::make_pair(PixelsPerInch, AAnimationImages));
  1849. TImageList * AButtonImages =
  1850. DebugNotNull(dynamic_cast<TImageList *>(ImagesModule->FindComponent(L"ButtonImages")));
  1851. ButtonImages.insert(std::make_pair(PixelsPerInch, AButtonImages));
  1852. TPngImageList * ADialogImages =
  1853. DebugNotNull(dynamic_cast<TPngImageList *>(ImagesModule->FindComponent(L"DialogImages")));
  1854. DialogImages.insert(std::make_pair(PixelsPerInch, ADialogImages));
  1855. }
  1856. __finally
  1857. {
  1858. GUIConfiguration->ChangeResourceModule(ResourceModule);
  1859. }
  1860. }
  1861. return PixelsPerInch;
  1862. }
  1863. //---------------------------------------------------------------------------
  1864. TPngImageList * __fastcall GetAnimationsImages(TControl * Control)
  1865. {
  1866. int PixelsPerInch = NeedImagesModule(Control);
  1867. return DebugNotNull(AnimationsImages[PixelsPerInch]);
  1868. }
  1869. //---------------------------------------------------------------------------
  1870. TImageList * __fastcall GetButtonImages(TControl * Control)
  1871. {
  1872. int PixelsPerInch = NeedImagesModule(Control);
  1873. return DebugNotNull(ButtonImages[PixelsPerInch]);
  1874. }
  1875. //---------------------------------------------------------------------------
  1876. TPngImageList * __fastcall GetDialogImages(TControl * Control)
  1877. {
  1878. int PixelsPerInch = NeedImagesModule(Control);
  1879. return DebugNotNull(DialogImages[PixelsPerInch]);
  1880. }
  1881. //---------------------------------------------------------------------------
  1882. void __fastcall ReleaseImagesModules()
  1883. {
  1884. TImagesModules::iterator i = ImagesModules.begin();
  1885. while (i != ImagesModules.end())
  1886. {
  1887. delete (*i);
  1888. i++;
  1889. }
  1890. ImagesModules.clear();
  1891. }
  1892. //---------------------------------------------------------------------------
  1893. __fastcall TFrameAnimation::TFrameAnimation()
  1894. {
  1895. FFirstFrame = -1;
  1896. }
  1897. //---------------------------------------------------------------------------
  1898. void __fastcall TFrameAnimation::Init(TPaintBox * PaintBox, const UnicodeString & Name)
  1899. {
  1900. FName = Name;
  1901. FPaintBox = PaintBox;
  1902. FPaintBox->ControlStyle = FPaintBox->ControlStyle << csOpaque;
  1903. DebugAssert((FPaintBox->OnPaint == NULL) || (FPaintBox->OnPaint == PaintBoxPaint));
  1904. FPaintBox->OnPaint = PaintBoxPaint;
  1905. SetRescaleFunction(FPaintBox, PaintBoxRescale, reinterpret_cast<TObject *>(this));
  1906. DoInit();
  1907. }
  1908. //---------------------------------------------------------------------------
  1909. void __fastcall TFrameAnimation::DoInit()
  1910. {
  1911. FImageList = GetAnimationsImages(FPaintBox);
  1912. FFirstFrame = -1;
  1913. FFirstLoopFrame = -1;
  1914. FPaintBox->Width = FImageList->Width;
  1915. FPaintBox->Height = FImageList->Height;
  1916. if (!FName.IsEmpty())
  1917. {
  1918. int Frame = 0;
  1919. while (Frame < FImageList->PngImages->Count)
  1920. {
  1921. UnicodeString FrameData = FImageList->PngImages->Items[Frame]->Name;
  1922. UnicodeString FrameName;
  1923. FrameName = CutToChar(FrameData, L'_', false);
  1924. if (SameText(FName, FrameName))
  1925. {
  1926. int FrameIndex = StrToInt(CutToChar(FrameData, L'_', false));
  1927. if (FFirstFrame < 0)
  1928. {
  1929. FFirstFrame = Frame;
  1930. }
  1931. if ((FFirstLoopFrame < 0) && (FrameIndex > 0))
  1932. {
  1933. FFirstLoopFrame = Frame;
  1934. }
  1935. FLastFrame = Frame;
  1936. }
  1937. else
  1938. {
  1939. if (FFirstFrame >= 0)
  1940. {
  1941. // optimization
  1942. break;
  1943. }
  1944. }
  1945. Frame++;
  1946. }
  1947. DebugAssert(FFirstFrame >= 0);
  1948. DebugAssert(FFirstLoopFrame >= 0);
  1949. }
  1950. Stop();
  1951. }
  1952. //---------------------------------------------------------------------------
  1953. void __fastcall TFrameAnimation::PaintBoxRescale(TComponent * /*Sender*/, TObject * Token)
  1954. {
  1955. TFrameAnimation * FrameAnimation = reinterpret_cast<TFrameAnimation *>(Token);
  1956. FrameAnimation->Rescale();
  1957. }
  1958. //---------------------------------------------------------------------------
  1959. void __fastcall TFrameAnimation::Rescale()
  1960. {
  1961. bool Started = (FTimer != NULL) && FTimer->Enabled;
  1962. DoInit();
  1963. if (Started)
  1964. {
  1965. Start();
  1966. }
  1967. }
  1968. //---------------------------------------------------------------------------
  1969. void __fastcall TFrameAnimation::Start()
  1970. {
  1971. if (FFirstFrame >= 0)
  1972. {
  1973. FNextFrameTick = GetTickCount();
  1974. CalculateNextFrameTick();
  1975. if (FTimer == NULL)
  1976. {
  1977. FTimer = new TTimer(GetParentForm(FPaintBox));
  1978. FTimer->Interval = static_cast<int>(GUIUpdateInterval);
  1979. FTimer->OnTimer = Timer;
  1980. }
  1981. else
  1982. {
  1983. // reset timer
  1984. FTimer->Enabled = false;
  1985. FTimer->Enabled = true;
  1986. }
  1987. }
  1988. }
  1989. //---------------------------------------------------------------------------
  1990. void __fastcall TFrameAnimation::Timer(TObject * /*Sender*/)
  1991. {
  1992. Animate();
  1993. }
  1994. //---------------------------------------------------------------------------
  1995. void __fastcall TFrameAnimation::PaintBoxPaint(TObject * Sender)
  1996. {
  1997. if (FFirstFrame >= 0)
  1998. {
  1999. // Double-buffered drawing to prevent flicker (as the images are transparent)
  2000. DebugUsedParam(Sender);
  2001. DebugAssert(FPaintBox == Sender);
  2002. DebugAssert(FPaintBox->ControlStyle.Contains(csOpaque));
  2003. std::unique_ptr<TBitmap> Bitmap(new TBitmap());
  2004. Bitmap->SetSize(FPaintBox->Width, FPaintBox->Height);
  2005. Bitmap->Canvas->Brush->Color = FPaintBox->Color;
  2006. TRect Rect(0, 0, Bitmap->Width, Bitmap->Height);
  2007. Bitmap->Canvas->FillRect(Rect);
  2008. TGraphic * Graphic = GetCurrentImage()->PngImage;
  2009. DebugAssert(Graphic->Width == FPaintBox->Width);
  2010. DebugAssert(Graphic->Height == FPaintBox->Height);
  2011. Bitmap->Canvas->Draw(0, 0, Graphic);
  2012. FPaintBox->Canvas->Draw(0, 0, Bitmap.get());
  2013. }
  2014. FPainted = true;
  2015. }
  2016. //---------------------------------------------------------------------------
  2017. void __fastcall TFrameAnimation::Repaint()
  2018. {
  2019. FPainted = false;
  2020. // If the form is not showing yet, the Paint() is not even called
  2021. FPaintBox->Repaint();
  2022. if (!FPainted)
  2023. {
  2024. // Paint later, alternatively we may keep trying Repaint() in Animate().
  2025. // See also a hack in TAuthenticateForm::Log.
  2026. FPaintBox->Invalidate();
  2027. }
  2028. }
  2029. //---------------------------------------------------------------------------
  2030. void __fastcall TFrameAnimation::Stop()
  2031. {
  2032. FNextFrameTick = std::numeric_limits<DWORD>::max();
  2033. FCurrentFrame = FFirstFrame;
  2034. Repaint();
  2035. if (FTimer != NULL)
  2036. {
  2037. FTimer->Enabled = false;
  2038. }
  2039. }
  2040. //---------------------------------------------------------------------------
  2041. void __fastcall TFrameAnimation::Animate()
  2042. {
  2043. if (FFirstFrame >= 0)
  2044. {
  2045. // UPGRADE: Use GetTickCount64() when we stop supporting Windows XP.
  2046. DWORD TickCount = GetTickCount();
  2047. // Keep in sync with an opposite condition at the end of the loop.
  2048. // We may skip some frames if we got stalled for a while
  2049. while (TickCount >= FNextFrameTick)
  2050. {
  2051. if (FCurrentFrame >= FLastFrame)
  2052. {
  2053. FCurrentFrame = FFirstLoopFrame;
  2054. }
  2055. else
  2056. {
  2057. FCurrentFrame++;
  2058. }
  2059. CalculateNextFrameTick();
  2060. Repaint();
  2061. }
  2062. }
  2063. }
  2064. //---------------------------------------------------------------------------
  2065. TPngImageCollectionItem * __fastcall TFrameAnimation::GetCurrentImage()
  2066. {
  2067. return FImageList->PngImages->Items[FCurrentFrame];
  2068. }
  2069. //---------------------------------------------------------------------------
  2070. void __fastcall TFrameAnimation::CalculateNextFrameTick()
  2071. {
  2072. TPngImageCollectionItem * ImageItem = GetCurrentImage();
  2073. UnicodeString Duration = ImageItem->Name;
  2074. CutToChar(Duration, L'_', false);
  2075. // skip index (is not really used)
  2076. CutToChar(Duration, L'_', false);
  2077. // This should overflow, when tick count wraps.
  2078. FNextFrameTick += StrToInt(Duration) * 10;
  2079. }
  2080. //---------------------------------------------------------------------------
  2081. //---------------------------------------------------------------------------
  2082. // Hints use:
  2083. // - Cleanup list tooltip (multi line)
  2084. // - Combo edit button
  2085. // - Transfer settings label (multi line, follows label size and font)
  2086. // - HintLabel (hint and persistent hint, multi line)
  2087. // - status bar hints
  2088. //---------------------------------------------------------------------------
  2089. __fastcall TScreenTipHintWindow::TScreenTipHintWindow(TComponent * Owner) :
  2090. THintWindow(Owner)
  2091. {
  2092. FParentPainting = false;
  2093. }
  2094. //---------------------------------------------------------------------------
  2095. int __fastcall TScreenTipHintWindow::GetTextFlags(TControl * Control)
  2096. {
  2097. return DT_LEFT | DT_WORDBREAK | DT_NOPREFIX | Control->DrawTextBiDiModeFlagsReadingOnly();
  2098. }
  2099. //---------------------------------------------------------------------------
  2100. bool __fastcall TScreenTipHintWindow::UseBoldShortHint(TControl * HintControl)
  2101. {
  2102. return
  2103. (dynamic_cast<TTBCustomDockableWindow *>(HintControl) != NULL) ||
  2104. (dynamic_cast<TTBPopupWindow *>(HintControl) != NULL) ||
  2105. (HintControl->Perform(WM_WANTS_SCREEN_TIPS, 0, 0) == 1);
  2106. }
  2107. //---------------------------------------------------------------------------
  2108. bool __fastcall TScreenTipHintWindow::IsPathLabel(TControl * HintControl)
  2109. {
  2110. return (dynamic_cast<TPathLabel *>(HintControl) != NULL);
  2111. }
  2112. //---------------------------------------------------------------------------
  2113. int __fastcall TScreenTipHintWindow::GetMargin(TControl * HintControl, const UnicodeString & Hint)
  2114. {
  2115. int Result;
  2116. if (HasLabelHintPopup(HintControl, Hint) || IsPathLabel(HintControl))
  2117. {
  2118. Result = 3;
  2119. }
  2120. else
  2121. {
  2122. Result = 6;
  2123. }
  2124. Result = ScaleByTextHeight(HintControl, Result);
  2125. return Result;
  2126. }
  2127. //---------------------------------------------------------------------------
  2128. TFont * __fastcall TScreenTipHintWindow::GetFont(TControl * HintControl, const UnicodeString & Hint)
  2129. {
  2130. TFont * Result;
  2131. if (HasLabelHintPopup(HintControl, Hint) || IsPathLabel(HintControl))
  2132. {
  2133. Result = reinterpret_cast<TLabel *>(dynamic_cast<TCustomLabel *>(HintControl))->Font;
  2134. }
  2135. else
  2136. {
  2137. FScaledHintFont.reset(new TFont());
  2138. FScaledHintFont->Assign(Screen->HintFont);
  2139. FScaledHintFont->Size = ScaleByPixelsPerInchFromSystem(FScaledHintFont->Size, HintControl);
  2140. Result = FScaledHintFont.get();
  2141. }
  2142. return Result;
  2143. }
  2144. //---------------------------------------------------------------------------
  2145. void __fastcall TScreenTipHintWindow::CalcHintTextRect(TControl * Control, TCanvas * Canvas, TRect & Rect, const UnicodeString & Hint)
  2146. {
  2147. const int Flags = DT_CALCRECT | GetTextFlags(Control);
  2148. DrawText(Canvas->Handle, Hint.c_str(), -1, &Rect, Flags);
  2149. }
  2150. //---------------------------------------------------------------------------
  2151. TRect __fastcall TScreenTipHintWindow::CalcHintRect(int MaxWidth, const UnicodeString AHint, void * AData)
  2152. {
  2153. TControl * HintControl = GetHintControl(AData);
  2154. int Margin = GetMargin(HintControl, AHint);
  2155. UnicodeString ShortHint;
  2156. UnicodeString LongHint;
  2157. SplitHint(HintControl, AHint, ShortHint, LongHint);
  2158. Canvas->Font->Assign(GetFont(HintControl, AHint));
  2159. // from XE6 Vcl.ScreenTips.pas, but absent in 11
  2160. const int cScreenTipTextOnlyWidth = 210;
  2161. const int ScreenTipTextOnlyWidth = ScaleByTextHeight(HintControl, cScreenTipTextOnlyWidth);
  2162. int LongHintMargin = 0; // shut up
  2163. bool HasLongHint = !LongHint.IsEmpty();
  2164. if (HasLongHint)
  2165. {
  2166. // double-margin on the right
  2167. LongHintMargin = (3 * Margin);
  2168. MaxWidth = ScreenTipTextOnlyWidth - LongHintMargin;
  2169. }
  2170. // Multi line short hints can be twice as wide, to not break the individual lines unless really necessary.
  2171. // (login site tree, clean up dialog list, preferences custom command list, persistent hint, etc).
  2172. // And they also can be twice as wide, to not break the individual lines unless really necessary.
  2173. if (ShortHint.Pos(L"\n") > 0)
  2174. {
  2175. MaxWidth *= 2;
  2176. }
  2177. bool HintPopup = HasLabelHintPopup(HintControl, AHint);
  2178. if (HintPopup)
  2179. {
  2180. MaxWidth = HintControl->Width;
  2181. }
  2182. if (UseBoldShortHint(HintControl))
  2183. {
  2184. Canvas->Font->Style = TFontStyles() << fsBold;
  2185. }
  2186. TRect ShortRect(0, 0, MaxWidth, 0);
  2187. CalcHintTextRect(this, Canvas, ShortRect, ShortHint);
  2188. Canvas->Font->Style = TFontStyles();
  2189. TRect Result;
  2190. if (!HasLongHint)
  2191. {
  2192. Result = ShortRect;
  2193. if (HintPopup)
  2194. {
  2195. Result.Right = MaxWidth + 2 * Margin;
  2196. }
  2197. else
  2198. {
  2199. Result.Right += 3 * Margin;
  2200. }
  2201. Result.Bottom += 2 * Margin;
  2202. }
  2203. else
  2204. {
  2205. const int LongIndentation = Margin * 3 / 2;
  2206. TRect LongRect(0, 0, MaxWidth - LongIndentation, 0);
  2207. CalcHintTextRect(this, Canvas, LongRect, LongHint);
  2208. Result.Right = Max(ScreenTipTextOnlyWidth, LongRect.Right + LongIndentation + LongHintMargin);
  2209. Result.Bottom = Margin + ShortRect.Height() + (Margin / 3 * 5) + LongRect.Height() + Margin;
  2210. }
  2211. // VCLCOPY: To counter the increase in THintWindow::ActivateHintData
  2212. Result.Bottom -= 4;
  2213. return Result;
  2214. }
  2215. //---------------------------------------------------------------------------
  2216. void __fastcall TScreenTipHintWindow::SplitHint(
  2217. TControl * HintControl, const UnicodeString & Hint, UnicodeString & ShortHint, UnicodeString & LongHint)
  2218. {
  2219. if (HasLabelHintPopup(HintControl, Hint))
  2220. {
  2221. ShortHint = HintControl->Hint;
  2222. }
  2223. else
  2224. {
  2225. ShortHint = GetShortHint(Hint);
  2226. LongHint = GetLongHintIfAny(Hint);
  2227. }
  2228. }
  2229. //---------------------------------------------------------------------------
  2230. void __fastcall TScreenTipHintWindow::ActivateHintData(const TRect & ARect, const UnicodeString AHint, void * AData)
  2231. {
  2232. FHintControl = GetHintControl(AData);
  2233. SplitHint(FHintControl, AHint, FShortHint, FLongHint);
  2234. FMargin = GetMargin(FHintControl, AHint);
  2235. FHintPopup = HasLabelHintPopup(FHintControl, AHint);
  2236. Canvas->Font->Assign(GetFont(FHintControl, AHint));
  2237. TRect Rect = ARect;
  2238. if (FHintPopup)
  2239. {
  2240. Rect.SetLocation(FHintControl->ClientToScreen(TPoint(-FMargin, -FMargin)));
  2241. }
  2242. if (IsPathLabel(FHintControl))
  2243. {
  2244. Rect.Offset(-FMargin, -FMargin);
  2245. }
  2246. THintWindow::ActivateHintData(Rect, FShortHint, AData);
  2247. }
  2248. //---------------------------------------------------------------------------
  2249. TControl * __fastcall TScreenTipHintWindow::GetHintControl(void * Data)
  2250. {
  2251. return reinterpret_cast<TControl *>(DebugNotNull(Data));
  2252. }
  2253. //---------------------------------------------------------------------------
  2254. UnicodeString __fastcall TScreenTipHintWindow::GetLongHintIfAny(const UnicodeString & AHint)
  2255. {
  2256. UnicodeString Result;
  2257. int P = Pos(L"|", AHint);
  2258. if (P > 0)
  2259. {
  2260. Result = GetLongHint(AHint);
  2261. }
  2262. return Result;
  2263. }
  2264. //---------------------------------------------------------------------------
  2265. void __fastcall TScreenTipHintWindow::Dispatch(void * AMessage)
  2266. {
  2267. TMessage * Message = static_cast<TMessage*>(AMessage);
  2268. switch (Message->Msg)
  2269. {
  2270. case WM_GETTEXTLENGTH:
  2271. if (FParentPainting)
  2272. {
  2273. // make THintWindow::Paint() not paint the Caption
  2274. Message->Result = 0;
  2275. }
  2276. else
  2277. {
  2278. THintWindow::Dispatch(AMessage);
  2279. }
  2280. break;
  2281. default:
  2282. THintWindow::Dispatch(AMessage);
  2283. break;
  2284. }
  2285. }
  2286. //---------------------------------------------------------------------------
  2287. void __fastcall TScreenTipHintWindow::Paint()
  2288. {
  2289. // paint frame/background
  2290. {
  2291. TAutoFlag ParentPaintingFlag(FParentPainting);
  2292. THintWindow::Paint();
  2293. }
  2294. const int Flags = GetTextFlags(this);
  2295. const int Margin = FMargin - 1; // 1 = border
  2296. TRect Rect = ClientRect;
  2297. Rect.Inflate(-Margin, -Margin);
  2298. if (!FHintPopup)
  2299. {
  2300. Rect.Right -= FMargin;
  2301. }
  2302. if (UseBoldShortHint(FHintControl))
  2303. {
  2304. Canvas->Font->Style = TFontStyles() << fsBold;
  2305. }
  2306. DrawText(Canvas->Handle, FShortHint.c_str(), -1, &Rect, Flags);
  2307. TRect ShortRect = Rect;
  2308. DrawText(Canvas->Handle, FShortHint.c_str(), -1, &ShortRect, DT_CALCRECT | Flags);
  2309. Canvas->Font->Style = TFontStyles();
  2310. if (!FLongHint.IsEmpty())
  2311. {
  2312. Rect.Left += FMargin * 3 / 2;
  2313. Rect.Top += ShortRect.Height() + (FMargin / 3 * 5);
  2314. DrawText(Canvas->Handle, FLongHint.c_str(), -1, &Rect, Flags);
  2315. }
  2316. }
  2317. //---------------------------------------------------------------------------
  2318. static int HideAccelFlag(TControl * Control)
  2319. {
  2320. //ask the top level window about its UI state
  2321. while (Control->Parent != NULL)
  2322. {
  2323. Control = Control->Parent;
  2324. }
  2325. int Result;
  2326. if (FLAGSET(Control->Perform(WM_QUERYUISTATE, 0, 0), UISF_HIDEACCEL))
  2327. {
  2328. Result = DT_HIDEPREFIX;
  2329. }
  2330. else
  2331. {
  2332. Result = 0;
  2333. }
  2334. return Result;
  2335. }
  2336. //---------------------------------------------------------------------------
  2337. //---------------------------------------------------------------------------
  2338. class TDarkUxThemeStyle : public TUxThemeStyle
  2339. {
  2340. public:
  2341. static TDarkUxThemeStyle * Instance();
  2342. protected:
  2343. virtual bool __fastcall DoDrawText(
  2344. HDC DC, const TThemedElementDetails & Details, const UnicodeString S, TRect & R, TTextFormat Flags,
  2345. const TStyleTextOptions & Options, int DPI);
  2346. private:
  2347. static std::unique_ptr<TDarkUxThemeStyle> FInstance;
  2348. };
  2349. //---------------------------------------------------------------------------
  2350. std::unique_ptr<TDarkUxThemeStyle> TDarkUxThemeStyle::FInstance;
  2351. //---------------------------------------------------------------------------
  2352. TDarkUxThemeStyle * TDarkUxThemeStyle::Instance()
  2353. {
  2354. if (FInstance.get() == NULL)
  2355. {
  2356. FInstance.reset(new TDarkUxThemeStyle());
  2357. }
  2358. return FInstance.get();
  2359. }
  2360. //---------------------------------------------------------------------------
  2361. bool __fastcall TDarkUxThemeStyle::DoDrawText(
  2362. HDC DC, const TThemedElementDetails & Details, const UnicodeString S, TRect & R, TTextFormat Flags,
  2363. const TStyleTextOptions & AOptions, int DPI)
  2364. {
  2365. TStyleTextOptions Options = AOptions;
  2366. Options.TextColor = GetWindowTextColor(GetBtnFaceColor());
  2367. Options.Flags << stfTextColor;
  2368. return TUxThemeStyle::DoDrawText(DC, Details, S, R, Flags, Options, DPI);
  2369. }
  2370. //---------------------------------------------------------------------------
  2371. //---------------------------------------------------------------------------
  2372. // For controls that need both custom VCL theme style and custom Windows theme.
  2373. // Currently the checkbox (custom VCL theme for light caption and custom Windows theme for dark more checkbox bitmaps).
  2374. // Current this handles only "button" theme.
  2375. // It's a singleton and can be used only for controls that share the same Windows theme (curently used with "explorer").
  2376. class TDarkExplorerUxThemeStyle : public TDarkUxThemeStyle
  2377. {
  2378. public:
  2379. static const UnicodeString ThemeName;
  2380. virtual __fastcall TDarkExplorerUxThemeStyle();
  2381. virtual __fastcall ~TDarkExplorerUxThemeStyle();
  2382. void SetControl(TWinControl * Control);
  2383. protected:
  2384. virtual NativeUInt __fastcall GetTheme(TThemedElement Element);
  2385. virtual NativeUInt __fastcall GetThemeForDPI(TThemedElement Element, int DPI);
  2386. virtual void __fastcall UpdateThemes();
  2387. virtual bool __fastcall DoDrawText(
  2388. HDC DC, const TThemedElementDetails & Details, const UnicodeString S, TRect & R, TTextFormat Flags,
  2389. const TStyleTextOptions & Options, int DPI);
  2390. protected:
  2391. typedef std::map<int, NativeUInt> TThemes;
  2392. TThemes FThemes;
  2393. bool FInitialized;
  2394. HWND FControlHandle;
  2395. int FTextFlags;
  2396. bool DoGetTheme(TThemedElement Element, int DPI, NativeUInt & Result);
  2397. void Clear();
  2398. };
  2399. //---------------------------------------------------------------------------
  2400. const UnicodeString TDarkExplorerUxThemeStyle::ThemeName = L"explorer";
  2401. //---------------------------------------------------------------------------
  2402. __fastcall TDarkExplorerUxThemeStyle::TDarkExplorerUxThemeStyle()
  2403. {
  2404. FControlHandle = NULL;
  2405. FTextFlags = 0;
  2406. FInitialized = true;
  2407. }
  2408. //---------------------------------------------------------------------------
  2409. __fastcall TDarkExplorerUxThemeStyle::~TDarkExplorerUxThemeStyle()
  2410. {
  2411. Clear();
  2412. }
  2413. //---------------------------------------------------------------------------
  2414. void TDarkExplorerUxThemeStyle::SetControl(TWinControl * Control)
  2415. {
  2416. // Handle is safer than a pointer
  2417. if (Control->HandleAllocated())
  2418. {
  2419. FControlHandle = Control->Handle;
  2420. // WORKAROUND - VCL does not hide accelerator of custom-styled (at least) checkboxes
  2421. FTextFlags = HideAccelFlag(Control);
  2422. }
  2423. else
  2424. {
  2425. FControlHandle = 0;
  2426. FTextFlags = 0;
  2427. }
  2428. }
  2429. //---------------------------------------------------------------------------
  2430. NativeUInt __fastcall TDarkExplorerUxThemeStyle::GetTheme(TThemedElement Element)
  2431. {
  2432. NativeUInt Result;
  2433. if (!DoGetTheme(Element, 0, Result))
  2434. {
  2435. Result = TDarkUxThemeStyle::GetTheme(Element);
  2436. }
  2437. return Result;
  2438. }
  2439. //---------------------------------------------------------------------------
  2440. NativeUInt __fastcall TDarkExplorerUxThemeStyle::GetThemeForDPI(TThemedElement Element, int DPI)
  2441. {
  2442. NativeUInt Result;
  2443. if (!DoGetTheme(Element, DPI, Result))
  2444. {
  2445. Result = TDarkUxThemeStyle::GetThemeForDPI(Element, DPI);
  2446. }
  2447. return Result;
  2448. }
  2449. //---------------------------------------------------------------------------
  2450. bool TDarkExplorerUxThemeStyle::DoGetTheme(TThemedElement Element, int DPI, NativeUInt & Result)
  2451. {
  2452. bool Handle = DebugAlwaysTrue(FControlHandle != 0) && DebugAlwaysTrue(Element == teButton);
  2453. if (Handle)
  2454. {
  2455. if (DPI == 0)
  2456. {
  2457. DPI = Screen->PixelsPerInch;
  2458. }
  2459. TThemes::const_iterator ITheme = FThemes.find(DPI);
  2460. if (ITheme != FThemes.end())
  2461. {
  2462. Result = ITheme->second;
  2463. }
  2464. else
  2465. {
  2466. if (!UseThemes())
  2467. {
  2468. Result = 0;
  2469. }
  2470. else
  2471. {
  2472. HTHEME Theme;
  2473. const wchar_t * ClassName = L"button";
  2474. if (!IsWin10() || !IsWin10Build(15063) || (DPI == Screen->PixelsPerInch))
  2475. {
  2476. Theme = OpenThemeData(FControlHandle, ClassName);
  2477. }
  2478. else
  2479. {
  2480. Theme = OpenThemeDataForDpi(FControlHandle, ClassName, DPI);
  2481. }
  2482. Result = reinterpret_cast<NativeUInt>(Theme);
  2483. }
  2484. FThemes.insert(std::make_pair(DPI, Result));
  2485. }
  2486. }
  2487. return Result;
  2488. }
  2489. //---------------------------------------------------------------------------
  2490. void TDarkExplorerUxThemeStyle::Clear()
  2491. {
  2492. TThemes Themes;
  2493. // TUxThemeStyle.UnloadThemeData implementation (FThemeDataUnLoading) imply
  2494. // that GetTheme might be called while unloading, so protecting against that
  2495. FThemes.swap(Themes);
  2496. TThemes::iterator I = Themes.begin();
  2497. while (I != Themes.end())
  2498. {
  2499. CloseThemeData(reinterpret_cast<HTHEME>(I->second));
  2500. ++I;
  2501. }
  2502. }
  2503. //---------------------------------------------------------------------------
  2504. void __fastcall TDarkExplorerUxThemeStyle::UpdateThemes()
  2505. {
  2506. TDarkUxThemeStyle::UpdateThemes();
  2507. // This is only ever called from the constructor, where FThemes is not initialized yet.
  2508. // It's not called for theme updates, as it is called only for the detault global app theme instance.
  2509. if (DebugAlwaysFalse(FInitialized))
  2510. {
  2511. Clear();
  2512. }
  2513. }
  2514. //---------------------------------------------------------------------------
  2515. bool __fastcall TDarkExplorerUxThemeStyle::DoDrawText(
  2516. HDC DC, const TThemedElementDetails & Details, const UnicodeString S, TRect & R, TTextFormat Flags,
  2517. const TStyleTextOptions & Options, int DPI)
  2518. {
  2519. int TextFlags = FTextFlags;
  2520. if (FLAGSET(TextFlags, DT_HIDEPREFIX))
  2521. {
  2522. TextFlags -= DT_HIDEPREFIX;
  2523. Flags << tfHidePrefix;
  2524. }
  2525. DebugAssert(TextFlags == 0);
  2526. DebugUsedParam(TextFlags);
  2527. return TDarkUxThemeStyle::DoDrawText(DC, Details, S, R, Flags, Options, DPI);
  2528. }
  2529. //---------------------------------------------------------------------------
  2530. static std::unique_ptr<TDarkExplorerUxThemeStyle> DarkExplorerUxThemeStyle;
  2531. //---------------------------------------------------------------------------
  2532. //---------------------------------------------------------------------------
  2533. // Based on:
  2534. // https://stackoverflow.com/q/6912424/850848
  2535. // https://stackoverflow.com/q/4685863/850848
  2536. class TUIStateAwareLabel : public TLabel
  2537. {
  2538. public:
  2539. __fastcall virtual TUIStateAwareLabel(TComponent * AOwner);
  2540. protected:
  2541. DYNAMIC void __fastcall DoDrawText(TRect & Rect, int Flags);
  2542. virtual void __fastcall Dispatch(void * AMessage);
  2543. };
  2544. //---------------------------------------------------------------------------
  2545. TLabel * CreateLabel(TComponent * AOwner)
  2546. {
  2547. return new TUIStateAwareLabel(AOwner);
  2548. }
  2549. //---------------------------------------------------------------------------
  2550. __fastcall TUIStateAwareLabel::TUIStateAwareLabel(TComponent * AOwner) :
  2551. TLabel(AOwner)
  2552. {
  2553. }
  2554. //---------------------------------------------------------------------------
  2555. void __fastcall TUIStateAwareLabel::DoDrawText(TRect & Rect, int Flags)
  2556. {
  2557. if (ShowAccelChar)
  2558. {
  2559. Flags = Flags | HideAccelFlag(this);
  2560. }
  2561. TLabel::DoDrawText(Rect, Flags);
  2562. }
  2563. //---------------------------------------------------------------------------
  2564. void __fastcall TUIStateAwareLabel::Dispatch(void * AMessage)
  2565. {
  2566. TMessage * Message = static_cast<TMessage*>(AMessage);
  2567. // WORKAROUND: Particularly when focusing csDropDownList-style combobox via label, there's no visual feedback
  2568. // that the combobox was selected (strangely, there is, when the previous focus was on TTreeView).
  2569. // For consistency, we enable focus display for all controls types (like checkboxes).
  2570. if (Message->Msg == CM_DIALOGCHAR)
  2571. {
  2572. bool WasFocused = (FocusControl != NULL) ? FocusControl->Focused() : false;
  2573. TLabel::Dispatch(AMessage);
  2574. if (!WasFocused && (FocusControl != NULL) && FocusControl->Focused())
  2575. {
  2576. TCustomForm * ParentForm = GetParentForm(this);
  2577. if (ParentForm != NULL)
  2578. {
  2579. ParentForm->Perform(WM_CHANGEUISTATE, MAKELONG(UIS_CLEAR, UISF_HIDEFOCUS), 0);
  2580. }
  2581. }
  2582. }
  2583. else
  2584. {
  2585. TLabel::Dispatch(AMessage);
  2586. }
  2587. }
  2588. //---------------------------------------------------------------------------
  2589. //---------------------------------------------------------------------------
  2590. template<typename T>
  2591. bool HandleMessageByDarkStyleHook(TMessage & Msg, TWinControl * Control, std::unique_ptr<T> & StyleHook)
  2592. {
  2593. bool Result = false;
  2594. if (Control->HandleAllocated() &&
  2595. !Control->ComponentState.Contains(csDestroying) &&
  2596. !Control->ControlState.Contains(csDestroyingHandle) &&
  2597. !Control->ControlStyle.Contains(csOverrideStylePaint) &&
  2598. UseDarkModeForControl(Control))
  2599. {
  2600. if (StyleHook.get() == NULL)
  2601. {
  2602. StyleHook.reset(new T(Control));
  2603. }
  2604. Result = StyleHook->HandleMessage(Msg);
  2605. }
  2606. return Result;
  2607. }
  2608. //---------------------------------------------------------------------------
  2609. void SetColorModeTheme(TWinControl * Control, const UnicodeString & DarkSubAppName)
  2610. {
  2611. if (UseDarkModeForControl(Control))
  2612. {
  2613. SetDarkModeTheme(Control, DarkSubAppName);
  2614. }
  2615. }
  2616. //---------------------------------------------------------------------------
  2617. void SetExplorerTheme(TWinControl * Button)
  2618. {
  2619. SetColorModeTheme(Button, TDarkExplorerUxThemeStyle::ThemeName);
  2620. }
  2621. //---------------------------------------------------------------------------
  2622. //---------------------------------------------------------------------------
  2623. class TDarkGroupBoxStyleHook : public TGroupBoxStyleHook
  2624. {
  2625. public:
  2626. __fastcall virtual TDarkGroupBoxStyleHook(TWinControl * AControl) :
  2627. TGroupBoxStyleHook(AControl)
  2628. {
  2629. }
  2630. protected:
  2631. virtual TCustomStyleServices * __fastcall StyleServices()
  2632. {
  2633. return TDarkUxThemeStyle::Instance();
  2634. }
  2635. };
  2636. //---------------------------------------------------------------------------
  2637. class TGroupBoxEx : public TGroupBox
  2638. {
  2639. protected:
  2640. virtual void __fastcall PaintWindow(HDC DC);
  2641. virtual void __fastcall WndProc(TMessage & Msg);
  2642. private:
  2643. std::unique_ptr<TDarkGroupBoxStyleHook> FStyleHook;
  2644. };
  2645. //---------------------------------------------------------------------------
  2646. void __fastcall TGroupBoxEx::WndProc(TMessage & Msg)
  2647. {
  2648. if (!HandleMessageByDarkStyleHook(Msg, this, FStyleHook))
  2649. {
  2650. TGroupBox::WndProc(Msg);
  2651. }
  2652. }
  2653. //---------------------------------------------------------------------------
  2654. void __fastcall TGroupBoxEx::PaintWindow(HDC DC)
  2655. {
  2656. if (UseDarkModeForControl(this))
  2657. {
  2658. std::unique_ptr<TCanvas> Canvas(new TCanvas());
  2659. Canvas->Handle = DC;
  2660. Canvas->Font = Font;
  2661. TRect Rect = ClientRect;
  2662. Canvas->Brush->Style = bsSolid;
  2663. Canvas->Brush->Color = GetBtnFaceColor();
  2664. Canvas->FillRect(Rect);
  2665. }
  2666. TGroupBox::PaintWindow(DC);
  2667. }
  2668. //---------------------------------------------------------------------------
  2669. //---------------------------------------------------------------------------
  2670. class TDarkCheckBoxStyleHook : public TCheckBoxStyleHook
  2671. {
  2672. public:
  2673. __fastcall virtual TDarkCheckBoxStyleHook(TWinControl * AControl);
  2674. protected:
  2675. virtual void __fastcall PaintBackground(TCanvas * Canvas);
  2676. virtual TCustomStyleServices * __fastcall StyleServices();
  2677. };
  2678. //---------------------------------------------------------------------------
  2679. class TCheckBoxEx : public TCheckBox
  2680. {
  2681. public:
  2682. __fastcall virtual TCheckBoxEx(TComponent * AOwner);
  2683. protected:
  2684. virtual void __fastcall WndProc(TMessage & Msg);
  2685. virtual void __fastcall CreateWnd();
  2686. private:
  2687. std::unique_ptr<TDarkCheckBoxStyleHook> FStyleHook;
  2688. };
  2689. //---------------------------------------------------------------------------
  2690. TCheckBox * CreateCheckBox(TComponent * AOwner)
  2691. {
  2692. return new TCheckBoxEx(AOwner);
  2693. }
  2694. //---------------------------------------------------------------------------
  2695. __fastcall TDarkCheckBoxStyleHook::TDarkCheckBoxStyleHook(TWinControl * AControl) :
  2696. TCheckBoxStyleHook(AControl)
  2697. {
  2698. }
  2699. //---------------------------------------------------------------------------
  2700. void __fastcall TDarkCheckBoxStyleHook::PaintBackground(TCanvas * Canvas)
  2701. {
  2702. Canvas->Brush->Style = bsSolid;
  2703. Canvas->Brush->Color = GetBtnFaceColor();
  2704. Canvas->FillRect(Rect(0, 0, Control->Width, Control->Height));
  2705. }
  2706. //---------------------------------------------------------------------------
  2707. TCustomStyleServices * __fastcall TDarkCheckBoxStyleHook::StyleServices()
  2708. {
  2709. if (DarkExplorerUxThemeStyle.get() == NULL)
  2710. {
  2711. DarkExplorerUxThemeStyle.reset(new TDarkExplorerUxThemeStyle());
  2712. }
  2713. DarkExplorerUxThemeStyle->SetControl(Control);
  2714. return DarkExplorerUxThemeStyle.get();
  2715. }
  2716. //---------------------------------------------------------------------------
  2717. //---------------------------------------------------------------------------
  2718. __fastcall TCheckBoxEx::TCheckBoxEx(TComponent * AOwner) :
  2719. TCheckBox(AOwner)
  2720. {
  2721. }
  2722. //---------------------------------------------------------------------------
  2723. void __fastcall TCheckBoxEx::CreateWnd()
  2724. {
  2725. TCheckBox::CreateWnd();
  2726. SetColorModeTheme(this, TDarkExplorerUxThemeStyle::ThemeName);
  2727. }
  2728. //---------------------------------------------------------------------------
  2729. void __fastcall TCheckBoxEx::WndProc(TMessage & Msg)
  2730. {
  2731. if (!HandleMessageByDarkStyleHook(Msg, this, FStyleHook))
  2732. {
  2733. TCheckBox::WndProc(Msg);
  2734. }
  2735. }
  2736. //---------------------------------------------------------------------------
  2737. //---------------------------------------------------------------------------
  2738. class TButtonEx : public TButton
  2739. {
  2740. public:
  2741. __fastcall virtual TButtonEx(TComponent * AOwner);
  2742. protected:
  2743. virtual void __fastcall CreateWnd();
  2744. };
  2745. //---------------------------------------------------------------------------
  2746. __fastcall TButtonEx::TButtonEx(TComponent * AOwner) :
  2747. TButton(AOwner)
  2748. {
  2749. }
  2750. //---------------------------------------------------------------------------
  2751. void __fastcall TButtonEx::CreateWnd()
  2752. {
  2753. TButton::CreateWnd();
  2754. SetExplorerTheme(this);
  2755. }
  2756. //---------------------------------------------------------------------------
  2757. TButton * CreateButton(TComponent * AOwner)
  2758. {
  2759. return new TButtonEx(AOwner);
  2760. }
  2761. //---------------------------------------------------------------------------
  2762. //---------------------------------------------------------------------------
  2763. class TEditEx : public TEdit
  2764. {
  2765. public:
  2766. __fastcall virtual TEditEx(TComponent * AOwner);
  2767. protected:
  2768. virtual void __fastcall CreateWnd();
  2769. };
  2770. //---------------------------------------------------------------------------
  2771. __fastcall TEditEx::TEditEx(TComponent * AOwner) :
  2772. TEdit(AOwner)
  2773. {
  2774. }
  2775. //---------------------------------------------------------------------------
  2776. void __fastcall TEditEx::CreateWnd()
  2777. {
  2778. TEdit::CreateWnd();
  2779. SetColorModeTheme(this, L"CFD");
  2780. }
  2781. //---------------------------------------------------------------------------
  2782. TEdit * CreateEdit(TComponent * AOwner)
  2783. {
  2784. return new TEditEx(AOwner);
  2785. }
  2786. //---------------------------------------------------------------------------
  2787. //---------------------------------------------------------------------------
  2788. class TMemoEx : public TMemo
  2789. {
  2790. public:
  2791. __fastcall virtual TMemoEx(TComponent * AOwner);
  2792. protected:
  2793. virtual void __fastcall CreateWnd();
  2794. };
  2795. //---------------------------------------------------------------------------
  2796. __fastcall TMemoEx::TMemoEx(TComponent * AOwner) :
  2797. TMemo(AOwner)
  2798. {
  2799. }
  2800. //---------------------------------------------------------------------------
  2801. void __fastcall TMemoEx::CreateWnd()
  2802. {
  2803. TMemo::CreateWnd();
  2804. SetColorModeTheme(this, L"CFD");
  2805. }
  2806. //---------------------------------------------------------------------------
  2807. TMemo * CreateMemo(TComponent * AOwner)
  2808. {
  2809. return new TMemoEx(AOwner);
  2810. }
  2811. //---------------------------------------------------------------------------
  2812. //---------------------------------------------------------------------------
  2813. void __fastcall FindComponentClass(
  2814. void *, TReader *, const UnicodeString DebugUsedArg(ClassName), TComponentClass & ComponentClass)
  2815. {
  2816. if (ComponentClass == __classid(TLabel))
  2817. {
  2818. ComponentClass = __classid(TUIStateAwareLabel);
  2819. }
  2820. else if (ComponentClass == __classid(TComboBox))
  2821. {
  2822. ComponentClass = __classid(TUIStateAwareComboBox);
  2823. }
  2824. else if (ComponentClass == __classid(TGroupBox))
  2825. {
  2826. ComponentClass = __classid(TGroupBoxEx);
  2827. }
  2828. else if (ComponentClass == __classid(TCheckBox))
  2829. {
  2830. ComponentClass = __classid(TCheckBoxEx);
  2831. }
  2832. else if (ComponentClass == __classid(TButton))
  2833. {
  2834. ComponentClass = __classid(TButtonEx);
  2835. }
  2836. else if (ComponentClass == __classid(TEdit))
  2837. {
  2838. ComponentClass = __classid(TEditEx);
  2839. }
  2840. else if (ComponentClass == __classid(TMemo))
  2841. {
  2842. ComponentClass = __classid(TMemoEx);
  2843. }
  2844. }
  2845. //---------------------------------------------------------------------------
  2846. bool CanShowTimeEstimate(TDateTime StartTime)
  2847. {
  2848. return (SecondsBetween(StartTime, Now()) >= 3);
  2849. }
  2850. //---------------------------------------------------------------------------
  2851. class TSystemRequiredThread : public TSignalThread
  2852. {
  2853. public:
  2854. TSystemRequiredThread();
  2855. void Required();
  2856. protected:
  2857. virtual bool __fastcall WaitForEvent();
  2858. virtual void __fastcall ProcessEvent();
  2859. private:
  2860. bool FRequired;
  2861. TDateTime FLastRequired;
  2862. };
  2863. //---------------------------------------------------------------------------
  2864. static std::unique_ptr<TCriticalSection> SystemRequiredThreadSection(TraceInitPtr(new TCriticalSection()));
  2865. static TSystemRequiredThread * SystemRequiredThread = NULL;
  2866. //---------------------------------------------------------------------------
  2867. TSystemRequiredThread::TSystemRequiredThread() :
  2868. TSignalThread(true), FRequired(false)
  2869. {
  2870. }
  2871. //---------------------------------------------------------------------------
  2872. void TSystemRequiredThread::Required()
  2873. {
  2874. // guarded in SystemRequired()
  2875. FLastRequired = Now();
  2876. TriggerEvent();
  2877. }
  2878. //---------------------------------------------------------------------------
  2879. bool __fastcall TSystemRequiredThread::WaitForEvent()
  2880. {
  2881. const int ExpireInterval = 5000;
  2882. bool Result = (TSignalThread::WaitForEvent(ExpireInterval) > 0);
  2883. if (!Result && !FTerminated)
  2884. {
  2885. TGuard Guard(SystemRequiredThreadSection.get());
  2886. if (!FTerminated && FRequired &&
  2887. (MilliSecondsBetween(Now(), FLastRequired) > ExpireInterval))
  2888. {
  2889. AppLog(L"System is not required");
  2890. SetThreadExecutionState(ES_CONTINUOUS);
  2891. FLastRequired = TDateTime();
  2892. FRequired = false;
  2893. }
  2894. }
  2895. return Result;
  2896. }
  2897. //---------------------------------------------------------------------------
  2898. void __fastcall TSystemRequiredThread::ProcessEvent()
  2899. {
  2900. TGuard Guard(SystemRequiredThreadSection.get());
  2901. if (!FRequired &&
  2902. (FLastRequired != TDateTime()))
  2903. {
  2904. AppLog(L"System is required");
  2905. SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
  2906. FRequired = true;
  2907. }
  2908. }
  2909. //---------------------------------------------------------------------------
  2910. void SystemRequired()
  2911. {
  2912. if (IsWin11())
  2913. {
  2914. TGuard Guard(SystemRequiredThreadSection.get());
  2915. if (SystemRequiredThread == NULL)
  2916. {
  2917. AppLog(L"Starting system required thread");
  2918. SystemRequiredThread = new TSystemRequiredThread();
  2919. SystemRequiredThread->Start();
  2920. }
  2921. SystemRequiredThread->Required();
  2922. }
  2923. else
  2924. {
  2925. SetThreadExecutionState(ES_SYSTEM_REQUIRED);
  2926. }
  2927. }
  2928. //---------------------------------------------------------------------------
  2929. void GUIFinalize()
  2930. {
  2931. TPuttyCleanupThread::Finalize();
  2932. TSystemRequiredThread * Thread;
  2933. {
  2934. TGuard Guard(SystemRequiredThreadSection.get());
  2935. Thread = SystemRequiredThread;
  2936. SystemRequiredThread = NULL;
  2937. }
  2938. if (Thread != NULL)
  2939. {
  2940. AppLog(L"Stopping system required thread");
  2941. Thread->Terminate();
  2942. Thread->WaitFor();
  2943. delete Thread;
  2944. }
  2945. }
  2946. //---------------------------------------------------------------------------
  2947. TCustomImageList * TreeViewImageList(TPngImageList * ImageList)
  2948. {
  2949. // WORKAROUND Prevent DPI scaling, see TCustomTreeView.SetImages
  2950. ImageList->Scaled = true;
  2951. return ImageList;
  2952. }