VCLCommon.cpp 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "WinInterface.h"
  5. #include "VCLCommon.h"
  6. #include <Common.h>
  7. #include <TextsWin.h>
  8. #include <RemoteFiles.h>
  9. #include <GUITools.h>
  10. #include <Tools.h>
  11. #include <FileCtrl.hpp>
  12. #include <PathLabel.hpp>
  13. #include <PasTools.hpp>
  14. #include <Vcl.Imaging.pngimage.hpp>
  15. #include <Math.hpp>
  16. //---------------------------------------------------------------------------
  17. #pragma package(smart_init)
  18. //---------------------------------------------------------------------------
  19. void __fastcall AdjustListColumnsWidth(TListView * ListView)
  20. {
  21. int OriginalWidth, NewWidth, i, CWidth, LastResizible;
  22. OriginalWidth = 0;
  23. LastResizible = -1;
  24. for (i = 0; i < ListView->Columns->Count; i++)
  25. {
  26. OriginalWidth += ListView->Columns->Items[i]->Width;
  27. if (ListView->Columns->Items[i]->Tag == 0)
  28. {
  29. LastResizible = i;
  30. }
  31. }
  32. assert(LastResizible >= 0);
  33. int RowCount = ListView->Items->Count;
  34. NewWidth = 0;
  35. CWidth = ListView->ClientWidth;
  36. if ((ListView->VisibleRowCount < RowCount) &&
  37. (ListView->Width - ListView->ClientWidth < GetSystemMetrics(SM_CXVSCROLL)))
  38. {
  39. CWidth -= GetSystemMetrics(SM_CXVSCROLL);
  40. }
  41. for (i = 0; i < ListView->Columns->Count; i++)
  42. {
  43. if (i != LastResizible)
  44. {
  45. if (ListView->Columns->Items[i]->Tag == 0)
  46. {
  47. ListView->Columns->Items[i]->Width =
  48. (CWidth * ListView->Columns->Items[i]->Width) / OriginalWidth;
  49. }
  50. NewWidth += ListView->Columns->Items[i]->Width;
  51. }
  52. }
  53. ListView->Columns->Items[LastResizible]->Width = CWidth-NewWidth;
  54. }
  55. //---------------------------------------------------------------------------
  56. static void __fastcall SetParentColor(TControl * Control)
  57. {
  58. TColor Color = clBtnFace;
  59. ((TEdit*)Control)->Color = Color;
  60. }
  61. //---------------------------------------------------------------------------
  62. void __fastcall EnableControl(TControl * Control, bool Enable)
  63. {
  64. if (Control->Enabled != Enable)
  65. {
  66. TWinControl * WinControl = dynamic_cast<TWinControl *>(Control);
  67. if ((WinControl != NULL) &&
  68. (WinControl->ControlCount > 0))
  69. {
  70. for (int Index = 0; Index < WinControl->ControlCount; Index++)
  71. {
  72. EnableControl(WinControl->Controls[Index], Enable);
  73. }
  74. }
  75. Control->Enabled = Enable;
  76. }
  77. if ((dynamic_cast<TCustomEdit *>(Control) != NULL) ||
  78. (dynamic_cast<TCustomComboBox *>(Control) != NULL) ||
  79. (dynamic_cast<TCustomListView *>(Control) != NULL) ||
  80. (dynamic_cast<TTreeView *>(Control) != NULL))
  81. {
  82. if (Enable)
  83. {
  84. ((TEdit*)Control)->Color = clWindow;
  85. }
  86. else
  87. {
  88. ((TEdit*)Control)->Color = clBtnFace;
  89. }
  90. }
  91. };
  92. //---------------------------------------------------------------------------
  93. void __fastcall ReadOnlyControl(TControl * Control, bool ReadOnly)
  94. {
  95. if (dynamic_cast<TCustomEdit *>(Control) != NULL)
  96. {
  97. ((TEdit*)Control)->ReadOnly = ReadOnly;
  98. TMemo * Memo = dynamic_cast<TMemo *>(Control);
  99. if (ReadOnly)
  100. {
  101. SetParentColor(Control);
  102. if (Memo != NULL)
  103. {
  104. // Is true by default and makes the control swallow not only
  105. // returns but also escapes
  106. Memo->WantReturns = false;
  107. }
  108. }
  109. else
  110. {
  111. ((TEdit*)Control)->Color = clWindow;
  112. // not supported atm, we need to persist previous value of WantReturns
  113. assert(Memo == NULL);
  114. }
  115. }
  116. else if ((dynamic_cast<TCustomComboBox *>(Control) != NULL) ||
  117. (dynamic_cast<TCustomTreeView *>(Control) != NULL))
  118. {
  119. EnableControl(Control, !ReadOnly);
  120. }
  121. else
  122. {
  123. assert(false);
  124. }
  125. }
  126. //---------------------------------------------------------------------------
  127. struct TSavedSystemSettings
  128. {
  129. TCustomForm * Form;
  130. UnicodeString FontName;
  131. bool Flipped;
  132. TWndMethod OldWndProc;
  133. };
  134. //---------------------------------------------------------------------------
  135. class TPublicControl : public TWinControl
  136. {
  137. friend TWndMethod __fastcall ControlWndProc(TWinControl * Control);
  138. };
  139. //---------------------------------------------------------------------------
  140. TWndMethod __fastcall ControlWndProc(TWinControl * Control)
  141. {
  142. TPublicControl * PublicControl = static_cast<TPublicControl *>(Control);
  143. return &PublicControl->WndProc;
  144. }
  145. //---------------------------------------------------------------------------
  146. static Forms::TMonitor * LastMonitor = NULL;
  147. //---------------------------------------------------------------------------
  148. inline void __fastcall DoFormWindowProc(TCustomForm * Form, TWndMethod WndProc,
  149. TMessage & Message)
  150. {
  151. if ((Message.Msg == WM_SYSCOMMAND) &&
  152. (Message.WParam == SC_CONTEXTHELP))
  153. {
  154. InvokeHelp(Form->ActiveControl);
  155. Message.Result = 1;
  156. }
  157. else if (Message.Msg == CM_SHOWINGCHANGED)
  158. {
  159. TForm * AForm = dynamic_cast<TForm *>(Form);
  160. assert(AForm != NULL);
  161. if ((Application->MainForm == Form) ||
  162. // this particularly happens if error occurs while main
  163. // window is being shown (e.g. non existent local directory when opening
  164. // explorer)
  165. ((Application->MainForm != NULL) && !Application->MainForm->Visible))
  166. {
  167. if (Form->Showing)
  168. {
  169. SendMessage(Form->Handle, WM_SETICON, ICON_BIG, reinterpret_cast<long>(Application->Icon->Handle));
  170. }
  171. if (!Form->Showing)
  172. {
  173. // when closing main form, remember its monitor,
  174. // so that the next form is shown on the same one
  175. LastMonitor = Form->Monitor;
  176. }
  177. else if ((LastMonitor != NULL) && (LastMonitor != Form->Monitor) &&
  178. Form->Showing)
  179. {
  180. // would actually always be poScreenCenter, see _SafeFormCreate
  181. if ((AForm->Position == poMainFormCenter) ||
  182. (AForm->Position == poOwnerFormCenter) ||
  183. (AForm->Position == poScreenCenter))
  184. {
  185. // this would typically be an authentication dialog,
  186. // but it may as well be an message box
  187. // taken from TCustomForm::SetWindowToMonitor
  188. AForm->SetBounds(LastMonitor->Left + ((LastMonitor->Width - AForm->Width) / 2),
  189. LastMonitor->Top + ((LastMonitor->Height - AForm->Height) / 2),
  190. AForm->Width, AForm->Height);
  191. AForm->Position = poDesigned;
  192. }
  193. else if ((AForm->Position != poDesigned) &&
  194. (AForm->Position != poDefaultPosOnly))
  195. {
  196. // we do not expect any other positioning
  197. assert(false);
  198. }
  199. }
  200. else
  201. {
  202. TForm * AForm = dynamic_cast<TForm *>(Form);
  203. assert(AForm != NULL);
  204. // otherwise it would not get centered
  205. if ((AForm->Position == poMainFormCenter) ||
  206. (AForm->Position == poOwnerFormCenter))
  207. {
  208. AForm->Position = poScreenCenter;
  209. }
  210. }
  211. }
  212. bool WasFormCenter =
  213. (AForm->Position == poMainFormCenter) ||
  214. (AForm->Position == poOwnerFormCenter);
  215. WndProc(Message);
  216. // Make sure dialogs are shown on-screen even if center of the main window
  217. // is off-screen. Occurs e.g. if you move the main window so that
  218. // only window title is visible above taksbar.
  219. if (Form->Showing && WasFormCenter && (AForm->Position == poDesigned))
  220. {
  221. TRect Rect;
  222. // Reading Form.Left/Form.Top instead here does not work, likely due to some
  223. // bug, when querying TProgressForm opened from TEditorForm (reloading remote file)
  224. GetWindowRect(Form->Handle, &Rect);
  225. int Left = Rect.Left;
  226. int Top = Rect.Top;
  227. TRect WorkArea = AForm->Monitor->WorkareaRect;
  228. if (Left + Rect.Width() > WorkArea.Right)
  229. {
  230. Left = WorkArea.Right - Rect.Width();
  231. }
  232. if (Left < WorkArea.Left)
  233. {
  234. Left = WorkArea.Left;
  235. }
  236. if (Top + Rect.Height() > WorkArea.Bottom)
  237. {
  238. Top = WorkArea.Bottom - Rect.Height();
  239. }
  240. if (Top < WorkArea.Top)
  241. {
  242. Top = WorkArea.Top;
  243. }
  244. if ((Left != Rect.Left) ||
  245. (Top != Rect.Top))
  246. {
  247. SetWindowPos(Form->Handle, 0, Left, Top, Rect.Width(), Rect.Height(),
  248. SWP_NOZORDER + SWP_NOACTIVATE);
  249. }
  250. }
  251. }
  252. else
  253. {
  254. WndProc(Message);
  255. }
  256. }
  257. //---------------------------------------------------------------------------
  258. static void __fastcall FormWindowProc(void * Data, TMessage & Message)
  259. {
  260. TCustomForm * Form = static_cast<TCustomForm *>(Data);
  261. DoFormWindowProc(Form, ControlWndProc(Form), Message);
  262. }
  263. //---------------------------------------------------------------------------
  264. static void __fastcall FormWindowProcEx(void * Data, TMessage & Message)
  265. {
  266. TSavedSystemSettings * SSettings = static_cast<TSavedSystemSettings *>(Data);
  267. DoFormWindowProc(SSettings->Form, SSettings->OldWndProc, Message);
  268. }
  269. //---------------------------------------------------------------------------
  270. void __fastcall InitializeSystemSettings()
  271. {
  272. }
  273. //---------------------------------------------------------------------------
  274. void __fastcall FinalizeSystemSettings()
  275. {
  276. }
  277. //---------------------------------------------------------------------------
  278. #ifdef _DEBUG
  279. void __fastcall VerifyControl(TControl * Control)
  280. {
  281. // If at this time the control has allocated persistence data that are used
  282. // for delayed handle recreation, we are at potential risk, as the future
  283. // de-persistence may overwrite meanwhile changed data.
  284. // For instance it may happen with list view that DPI scaling gets lost.
  285. // This for example happens when the list view has both design time
  286. // ReadOnly = true and some items set. We cannot usually explicitly
  287. // check for the presence of items as while the listview does not have
  288. // a handle allocated, item count querying does not work
  289. // (see also a check below)
  290. assert(!ControlHasRecreationPersistenceData(Control));
  291. TCustomListView * ListView = dynamic_cast<TCustomListView *>(Control);
  292. if (ListView != NULL)
  293. {
  294. // As of now the HandleAllocated check is pointless as
  295. // ListView->Items->Count returns 0 when the handle is not allocated yet.
  296. // But we want to know if the implementation ever changes to allocate the handle
  297. // on the call. Because we do not want to allocate a handle here as
  298. // that would change the debug mode behaviour from release behaviour,
  299. // possibly hiding away some problems.
  300. assert(!ListView->HandleAllocated() || (ListView->Items->Count == 0));
  301. }
  302. }
  303. #endif
  304. //---------------------------------------------------------------------------
  305. void __fastcall ApplySystemSettingsOnControl(TControl * Control)
  306. {
  307. #ifdef _DEBUG
  308. VerifyControl(Control);
  309. #endif
  310. // WORKAROUND
  311. // VCL does not scale status par panels (while for instance it does
  312. // scale list view headers). Remove this if they ever "fix" this.
  313. // For TBX status bars, this is implemented in TTBXCustomStatusBar.ChangeScale
  314. TStatusBar * StatusBar = dynamic_cast<TStatusBar *>(Control);
  315. if (StatusBar != NULL)
  316. {
  317. for (int Index = 0; Index < StatusBar->Panels->Count; Index++)
  318. {
  319. TStatusPanel * Panel = StatusBar->Panels->Items[Index];
  320. Panel->Width = ScaleByTextHeight(StatusBar, Panel->Width);
  321. }
  322. }
  323. TWinControl * WinControl = dynamic_cast<TWinControl *>(Control);
  324. if (WinControl != NULL)
  325. {
  326. for (int Index = 0; Index < WinControl->ControlCount; Index++)
  327. {
  328. ApplySystemSettingsOnControl(WinControl->Controls[Index]);
  329. }
  330. }
  331. }
  332. //---------------------------------------------------------------------------
  333. // Settings that must be set as soon as possible.
  334. void __fastcall UseSystemSettingsPre(TCustomForm * Control, void ** Settings)
  335. {
  336. LocalSystemSettings(Control);
  337. TWndMethod WindowProc;
  338. if (Settings)
  339. {
  340. TSavedSystemSettings * SSettings;
  341. SSettings = new TSavedSystemSettings();
  342. *Settings = static_cast<void*>(SSettings);
  343. SSettings->Form = Control;
  344. SSettings->FontName = Control->Font->Name;
  345. SSettings->OldWndProc = Control->WindowProc;
  346. ((TMethod*)&WindowProc)->Data = SSettings;
  347. ((TMethod*)&WindowProc)->Code = FormWindowProcEx;
  348. }
  349. else
  350. {
  351. ((TMethod*)&WindowProc)->Data = Control;
  352. ((TMethod*)&WindowProc)->Code = FormWindowProc;
  353. }
  354. Control->WindowProc = WindowProc;
  355. if (Control->HelpKeyword.IsEmpty())
  356. {
  357. // temporary help keyword to enable F1 key in all forms
  358. Control->HelpKeyword = L"start";
  359. }
  360. ApplySystemSettingsOnControl(Control);
  361. };
  362. //---------------------------------------------------------------------------
  363. // Settings that must be set only after whole form is constructed
  364. void __fastcall UseSystemSettingsPost(TCustomForm * Control, void * Settings)
  365. {
  366. bool Flip;
  367. UnicodeString FlipStr = LoadStr(FLIP_CHILDREN);
  368. Flip = !FlipStr.IsEmpty() && static_cast<bool>(StrToInt(FlipStr));
  369. if (Settings != NULL)
  370. {
  371. static_cast<TSavedSystemSettings*>(Settings)->Flipped = Flip;
  372. }
  373. if (Flip)
  374. {
  375. Control->FlipChildren(true);
  376. }
  377. ResetSystemSettings(Control);
  378. };
  379. //---------------------------------------------------------------------------
  380. void __fastcall UseSystemSettings(TCustomForm * Control, void ** Settings)
  381. {
  382. UseSystemSettingsPre(Control, Settings);
  383. UseSystemSettingsPost(Control, (Settings != NULL) ? *Settings : NULL);
  384. };
  385. //---------------------------------------------------------------------------
  386. void __fastcall ResetSystemSettings(TCustomForm * /*Control*/)
  387. {
  388. // noop
  389. }
  390. //---------------------------------------------------------------------------
  391. void __fastcall DeleteSystemSettings(TCustomForm * Control, void * Settings)
  392. {
  393. assert(Settings);
  394. TSavedSystemSettings * SSettings = static_cast<TSavedSystemSettings *>(Settings);
  395. Control->WindowProc = SSettings->OldWndProc;
  396. delete SSettings;
  397. }
  398. //---------------------------------------------------------------------------
  399. void __fastcall RevokeSystemSettings(TCustomForm * Control, void * Settings)
  400. {
  401. assert(Settings);
  402. TSavedSystemSettings* SSettings = static_cast<TSavedSystemSettings*>(Settings);
  403. if (SSettings->Flipped)
  404. {
  405. Control->FlipChildren(true);
  406. }
  407. DeleteSystemSettings(Control, Settings);
  408. };
  409. //---------------------------------------------------------------------------
  410. class TPublicForm : public TForm
  411. {
  412. friend void __fastcall ShowAsModal(TForm * Form, void *& Storage);
  413. friend void __fastcall HideAsModal(TForm * Form, void *& Storage);
  414. };
  415. //---------------------------------------------------------------------------
  416. struct TShowAsModalStorage
  417. {
  418. void * FocusWindowList;
  419. HWND FocusActiveWindow;
  420. TFocusState FocusState;
  421. };
  422. //---------------------------------------------------------------------------
  423. void __fastcall ShowAsModal(TForm * Form, void *& Storage)
  424. {
  425. SetCorrectFormParent(Form);
  426. CancelDrag();
  427. if (GetCapture() != 0) SendMessage(GetCapture(), WM_CANCELMODE, 0, 0);
  428. ReleaseCapture();
  429. (static_cast<TPublicForm*>(Form))->FFormState << fsModal;
  430. TShowAsModalStorage * AStorage = new TShowAsModalStorage;
  431. AStorage->FocusActiveWindow = GetActiveWindow();
  432. AStorage->FocusState = SaveFocusState();
  433. Screen->SaveFocusedList->Insert(0, Screen->FocusedForm);
  434. Screen->FocusedForm = Form;
  435. AStorage->FocusWindowList = DisableTaskWindows(0);
  436. Form->Show();
  437. SendMessage(Form->Handle, CM_ACTIVATE, 0, 0);
  438. Storage = AStorage;
  439. }
  440. //---------------------------------------------------------------------------
  441. void __fastcall HideAsModal(TForm * Form, void *& Storage)
  442. {
  443. assert((static_cast<TPublicForm*>(Form))->FFormState.Contains(fsModal));
  444. TShowAsModalStorage * AStorage = static_cast<TShowAsModalStorage *>(Storage);
  445. Storage = NULL;
  446. SendMessage(Form->Handle, CM_DEACTIVATE, 0, 0);
  447. if (GetActiveWindow() != Form->Handle)
  448. {
  449. AStorage->FocusActiveWindow = 0;
  450. }
  451. Form->Hide();
  452. EnableTaskWindows(AStorage->FocusWindowList);
  453. if (Screen->SaveFocusedList->Count > 0)
  454. {
  455. Screen->FocusedForm = static_cast<TCustomForm *>(Screen->SaveFocusedList->First());
  456. Screen->SaveFocusedList->Remove(Screen->FocusedForm);
  457. }
  458. else
  459. {
  460. Screen->FocusedForm = NULL;
  461. }
  462. if (AStorage->FocusActiveWindow != 0)
  463. {
  464. SetActiveWindow(AStorage->FocusActiveWindow);
  465. }
  466. RestoreFocusState(AStorage->FocusState);
  467. (static_cast<TPublicForm*>(Form))->FFormState >> fsModal;
  468. delete AStorage;
  469. }
  470. //---------------------------------------------------------------------------
  471. void __fastcall ReleaseAsModal(TForm * Form, void *& Storage)
  472. {
  473. if (Storage != NULL)
  474. {
  475. HideAsModal(Form, Storage);
  476. }
  477. }
  478. //---------------------------------------------------------------------------
  479. bool __fastcall SelectDirectory(UnicodeString & Path, const UnicodeString Prompt,
  480. bool PreserveFileName)
  481. {
  482. bool Result;
  483. unsigned int ErrorMode;
  484. ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  485. try
  486. {
  487. UnicodeString Directory;
  488. UnicodeString FileName;
  489. if (!PreserveFileName || DirectoryExists(Path))
  490. {
  491. Directory = Path;
  492. }
  493. else
  494. {
  495. Directory = ExtractFilePath(Path);
  496. FileName = ExtractFileName(Path);
  497. }
  498. Result = SelectDirectory(Prompt, L"", Directory);
  499. if (Result)
  500. {
  501. Path = Directory;
  502. if (!FileName.IsEmpty())
  503. {
  504. Path = IncludeTrailingBackslash(Path) + FileName;
  505. }
  506. }
  507. }
  508. __finally
  509. {
  510. SetErrorMode(ErrorMode);
  511. }
  512. return Result;
  513. }
  514. //---------------------------------------------------------------------------
  515. bool __fastcall ListViewAnyChecked(TListView * ListView, bool Checked)
  516. {
  517. bool AnyChecked = false;
  518. for (int Index = 0; Index < ListView->Items->Count; Index++)
  519. {
  520. if (ListView->Items->Item[Index]->Checked == Checked)
  521. {
  522. AnyChecked = true;
  523. break;
  524. }
  525. }
  526. return AnyChecked;
  527. }
  528. //---------------------------------------------------------------------------
  529. void __fastcall ListViewCheckAll(TListView * ListView,
  530. TListViewCheckAll CheckAll)
  531. {
  532. bool Check;
  533. if (CheckAll == caToggle)
  534. {
  535. Check = ListViewAnyChecked(ListView, false);
  536. }
  537. else
  538. {
  539. Check = (CheckAll == caCheck);
  540. }
  541. for (int Index = 0; Index < ListView->Items->Count; Index++)
  542. {
  543. ListView->Items->Item[Index]->Checked = Check;
  544. }
  545. }
  546. //---------------------------------------------------------------------------
  547. void __fastcall ComboAutoSwitchInitialize(TComboBox * ComboBox)
  548. {
  549. int PrevIndex = ComboBox->ItemIndex;
  550. ComboBox->Items->BeginUpdate();
  551. try
  552. {
  553. ComboBox->Clear();
  554. ComboBox->Items->Add(LoadStr(AUTO_SWITCH_AUTO));
  555. ComboBox->Items->Add(LoadStr(AUTO_SWITCH_OFF));
  556. ComboBox->Items->Add(LoadStr(AUTO_SWITCH_ON));
  557. }
  558. __finally
  559. {
  560. ComboBox->Items->EndUpdate();
  561. }
  562. assert(PrevIndex < ComboBox->Items->Count);
  563. ComboBox->ItemIndex = PrevIndex;
  564. }
  565. //---------------------------------------------------------------------------
  566. void __fastcall ComboAutoSwitchLoad(TComboBox * ComboBox, TAutoSwitch Value)
  567. {
  568. ComboBox->ItemIndex = 2 - Value;
  569. if (ComboBox->ItemIndex < 0)
  570. {
  571. ComboBox->ItemIndex = 0;
  572. }
  573. }
  574. //---------------------------------------------------------------------------
  575. TAutoSwitch __fastcall ComboAutoSwitchSave(TComboBox * ComboBox)
  576. {
  577. return (TAutoSwitch)(2 - ComboBox->ItemIndex);
  578. }
  579. //---------------------------------------------------------------------------
  580. void __fastcall CheckBoxAutoSwitchLoad(TCheckBox * CheckBox, TAutoSwitch Value)
  581. {
  582. switch (Value)
  583. {
  584. case asOn:
  585. CheckBox->State = cbChecked;
  586. break;
  587. case asOff:
  588. CheckBox->State = cbUnchecked;
  589. break;
  590. default:
  591. CheckBox->State = cbGrayed;
  592. break;
  593. }
  594. }
  595. //---------------------------------------------------------------------------
  596. TAutoSwitch __fastcall CheckBoxAutoSwitchSave(TCheckBox * CheckBox)
  597. {
  598. switch (CheckBox->State)
  599. {
  600. case cbChecked:
  601. return asOn;
  602. case cbUnchecked:
  603. return asOff;
  604. default:
  605. return asAuto;
  606. }
  607. }
  608. //---------------------------------------------------------------------------
  609. static const wchar_t PathWordDelimiters[] = L"\\/ ;,.";
  610. //---------------------------------------------------------------------------
  611. static bool IsPathWordDelimiter(wchar_t Ch)
  612. {
  613. return (wcschr(PathWordDelimiters, Ch) != NULL);
  614. }
  615. //---------------------------------------------------------------------------
  616. // Windows algorithm is as follows (tested on W2k):
  617. // right:
  618. // is_delimiter(current)
  619. // false:
  620. // right(left(current) + 1)
  621. // true:
  622. // right(right(current) + 1)
  623. // left:
  624. // right(left(current) + 1)
  625. int CALLBACK PathWordBreakProc(wchar_t * Ch, int Current, int Len, int Code)
  626. {
  627. int Result;
  628. UnicodeString ACh(Ch, Len);
  629. if (Code == WB_ISDELIMITER)
  630. {
  631. // we return negacy of what WinAPI docs says
  632. Result = !IsPathWordDelimiter(ACh[Current + 1]);
  633. }
  634. else if (Code == WB_LEFT)
  635. {
  636. // skip consecutive delimiters
  637. while ((Current > 0) &&
  638. IsPathWordDelimiter(ACh[Current]))
  639. {
  640. Current--;
  641. }
  642. Result = ACh.SubString(1, Current - 1).LastDelimiter(PathWordDelimiters);
  643. }
  644. else if (Code == WB_RIGHT)
  645. {
  646. if (Current == 0)
  647. {
  648. // will be called again with Current == 1
  649. Result = 0;
  650. }
  651. else
  652. {
  653. const wchar_t * P = wcspbrk(ACh.c_str() + Current - 1, PathWordDelimiters);
  654. if (P == NULL)
  655. {
  656. Result = Len;
  657. }
  658. else
  659. {
  660. Result = P - ACh.c_str() + 1;
  661. // skip consecutive delimiters
  662. while ((Result < Len) &&
  663. IsPathWordDelimiter(ACh[Result + 1]))
  664. {
  665. Result++;
  666. }
  667. }
  668. }
  669. }
  670. else
  671. {
  672. assert(false);
  673. Result = 0;
  674. }
  675. return Result;
  676. }
  677. //---------------------------------------------------------------------------
  678. class TPublicCustomCombo : public TCustomCombo
  679. {
  680. friend void __fastcall InstallPathWordBreakProc(TWinControl * Control);
  681. };
  682. //---------------------------------------------------------------------------
  683. void __fastcall InstallPathWordBreakProc(TWinControl * Control)
  684. {
  685. // Since we are setting Application->ModalPopupMode = pmAuto,
  686. // this has to be called from OnShow, not from constructor anymore,
  687. // to have any effect
  688. HWND Wnd;
  689. if (dynamic_cast<TCustomCombo*>(Control) != NULL)
  690. {
  691. TPublicCustomCombo * Combo =
  692. static_cast<TPublicCustomCombo *>(dynamic_cast<TCustomCombo *>(Control));
  693. Combo->HandleNeeded();
  694. Wnd = Combo->EditHandle;
  695. }
  696. else
  697. {
  698. Wnd = Control->Handle;
  699. }
  700. SendMessage(Wnd, EM_SETWORDBREAKPROC, 0, (LPARAM)(EDITWORDBREAKPROC)PathWordBreakProc);
  701. }
  702. //---------------------------------------------------------------------------
  703. static void __fastcall RemoveHiddenControlsFromOrder(TControl ** ControlsOrder, int & Count)
  704. {
  705. int Shift = 0;
  706. for (int Index = 0; Index < Count; Index++)
  707. {
  708. if (ControlsOrder[Index]->Visible)
  709. {
  710. ControlsOrder[Index - Shift] = ControlsOrder[Index];
  711. }
  712. else
  713. {
  714. Shift++;
  715. }
  716. }
  717. Count -= Shift;
  718. }
  719. //---------------------------------------------------------------------------
  720. void __fastcall SetVerticalControlsOrder(TControl ** ControlsOrder, int Count)
  721. {
  722. RemoveHiddenControlsFromOrder(ControlsOrder, Count);
  723. if (Count > 0)
  724. {
  725. TWinControl * CommonParent = ControlsOrder[0]->Parent;
  726. CommonParent->DisableAlign();
  727. try
  728. {
  729. int Top = 0;
  730. for (int Index = 0; Index < Count; Index++)
  731. {
  732. assert(ControlsOrder[Index]->Parent == CommonParent);
  733. if ((Index == 0) || (Top > ControlsOrder[Index]->Top))
  734. {
  735. Top = ControlsOrder[Index]->Top;
  736. }
  737. }
  738. for (int Index = 0; Index < Count; Index++)
  739. {
  740. TControl * Control = ControlsOrder[Index];
  741. Control->Top = Top;
  742. if (((Control->Align == alTop) || (Control->Align == alBottom)) ||
  743. ((Index == Count - 1) || (ControlsOrder[Index + 1]->Align == alBottom)))
  744. {
  745. Top += Control->Height;
  746. }
  747. }
  748. }
  749. __finally
  750. {
  751. CommonParent->EnableAlign();
  752. }
  753. }
  754. }
  755. //---------------------------------------------------------------------------
  756. void __fastcall SetHorizontalControlsOrder(TControl ** ControlsOrder, int Count)
  757. {
  758. RemoveHiddenControlsFromOrder(ControlsOrder, Count);
  759. if (Count > 0)
  760. {
  761. TWinControl * CommonParent = ControlsOrder[0]->Parent;
  762. CommonParent->DisableAlign();
  763. try
  764. {
  765. int Left = 0;
  766. for (int Index = 0; Index < Count; Index++)
  767. {
  768. assert(ControlsOrder[Index]->Parent == CommonParent);
  769. if ((Index == 0) || (Left > ControlsOrder[Index]->Left))
  770. {
  771. Left = ControlsOrder[Index]->Left;
  772. }
  773. }
  774. for (int Index = 0; Index < Count; Index++)
  775. {
  776. TControl * Control = ControlsOrder[Index];
  777. Control->Left = Left;
  778. if (((Control->Align == alLeft) || (Control->Align == alRight)) ||
  779. ((Index == Count - 1) || (ControlsOrder[Index + 1]->Align == alRight)))
  780. {
  781. Left += Control->Width;
  782. }
  783. // vertical alignment has priority, so alBottom-aligned controls start
  784. // at the very left, even if there are any alLeft/alRight controls.
  785. // for the reason this code is not necessary in SetVerticalControlsOrder.
  786. // we could exit the loop as well here.
  787. if ((Index == Count - 1) || (ControlsOrder[Index + 1]->Align == alBottom))
  788. {
  789. Left = 0;
  790. }
  791. }
  792. }
  793. __finally
  794. {
  795. CommonParent->EnableAlign();
  796. }
  797. }
  798. }
  799. //---------------------------------------------------------------------------
  800. void __fastcall MakeNextInTabOrder(TWinControl * Control, TWinControl * After)
  801. {
  802. if (After->TabOrder > Control->TabOrder)
  803. {
  804. After->TabOrder = Control->TabOrder;
  805. }
  806. else if (After->TabOrder < Control->TabOrder - 1)
  807. {
  808. After->TabOrder = static_cast<TTabOrder>(Control->TabOrder - 1);
  809. }
  810. }
  811. //---------------------------------------------------------------------------
  812. void __fastcall CutFormToDesktop(TForm * Form)
  813. {
  814. assert(Form->Monitor != NULL);
  815. TRect Workarea = Form->Monitor->WorkareaRect;
  816. if (Form->Top + Form->Height > Workarea.Bottom)
  817. {
  818. Form->Height = Workarea.Bottom - Form->Top;
  819. }
  820. if (Form->Left + Form->Width >= Workarea.Right)
  821. {
  822. Form->Width = Workarea.Right - Form->Left;
  823. }
  824. }
  825. //---------------------------------------------------------------------------
  826. void __fastcall UpdateFormPosition(TCustomForm * Form, TPosition Position)
  827. {
  828. if ((Position == poScreenCenter) ||
  829. (Position == poOwnerFormCenter) ||
  830. (Position == poMainFormCenter))
  831. {
  832. TCustomForm * CenterForm = NULL;
  833. if ((Position == poOwnerFormCenter) ||
  834. (Position == poMainFormCenter))
  835. {
  836. CenterForm = Application->MainForm;
  837. if ((Position == poOwnerFormCenter) &&
  838. (dynamic_cast<TCustomForm*>(Form->Owner) != NULL))
  839. {
  840. CenterForm = dynamic_cast<TCustomForm*>(Form->Owner);
  841. }
  842. }
  843. TRect Bounds = Form->BoundsRect;
  844. int X, Y;
  845. if (CenterForm != NULL)
  846. {
  847. X = ((((TForm *)CenterForm)->Width - Bounds.Width()) / 2) +
  848. ((TForm *)CenterForm)->Left;
  849. Y = ((((TForm *)CenterForm)->Height - Bounds.Height()) / 2) +
  850. ((TForm *)CenterForm)->Top;
  851. }
  852. else
  853. {
  854. X = (Form->Monitor->Width - Bounds.Width()) / 2;
  855. Y = (Form->Monitor->Height - Bounds.Height()) / 2;
  856. }
  857. if (X < 0)
  858. {
  859. X = 0;
  860. }
  861. if (Y < 0)
  862. {
  863. Y = 0;
  864. }
  865. Form->SetBounds(X, Y, Bounds.Width(), Bounds.Height());
  866. }
  867. }
  868. //---------------------------------------------------------------------------
  869. void __fastcall ResizeForm(TCustomForm * Form, int Width, int Height)
  870. {
  871. // This has to be called only after DoFormWindowProc(CM_SHOWINGCHANGED),
  872. // so that a correct monitor is considered.
  873. // Note that we cannot use LastMonitor(), as ResizeForm is also called from
  874. // TConsoleDialog::DoAdjustWindow, where we need to use the actual monitor
  875. // (in case user moves the console window to a different monitor,
  876. // than where a main window is [no matter how unlikely that is])
  877. TRect WorkareaRect = Form->Monitor->WorkareaRect;
  878. if (Height > WorkareaRect.Height())
  879. {
  880. Height = WorkareaRect.Height();
  881. }
  882. if (Width > WorkareaRect.Width())
  883. {
  884. Width = WorkareaRect.Width();
  885. }
  886. if (Height < Form->Constraints->MinHeight)
  887. {
  888. Height = Form->Constraints->MinHeight;
  889. }
  890. if (Width < Form->Constraints->MinWidth)
  891. {
  892. Width = Form->Constraints->MinWidth;
  893. }
  894. TRect Bounds = Form->BoundsRect;
  895. int Top = Bounds.Top + ((Bounds.Height() - Height) / 2);
  896. int Left = Bounds.Left + ((Bounds.Width() - Width) / 2);
  897. if (Top + Height > WorkareaRect.Bottom)
  898. {
  899. Top = WorkareaRect.Bottom - Height;
  900. }
  901. if (Left + Width >= WorkareaRect.Right)
  902. {
  903. Left = WorkareaRect.Right - Width;
  904. }
  905. // WorkareaRect.Left is not 0, when secondary monitor is placed left of primary one.
  906. // Similarly for WorkareaRect.Top.
  907. if (Top < WorkareaRect.Top)
  908. {
  909. Top = WorkareaRect.Top;
  910. }
  911. if (Left < WorkareaRect.Left)
  912. {
  913. Left = WorkareaRect.Left;
  914. }
  915. Form->SetBounds(Left, Top, Width, Height);
  916. Bounds = Form->BoundsRect;
  917. // due to constraints, form can remain larger, make sure it is centered although
  918. Left = Bounds.Left + ((Width - Bounds.Width()) / 2);
  919. Top = Bounds.Top + ((Height - Bounds.Height()) / 2);
  920. Form->SetBounds(Left, Top, Width, Height);
  921. }
  922. //---------------------------------------------------------------------------
  923. TComponent * __fastcall GetFormOwner()
  924. {
  925. if (Screen->ActiveForm != NULL)
  926. {
  927. return Screen->ActiveForm;
  928. }
  929. else
  930. {
  931. return Application;
  932. }
  933. }
  934. //---------------------------------------------------------------------------
  935. void __fastcall SetCorrectFormParent(TForm * /*Form*/)
  936. {
  937. // noop
  938. // remove
  939. }
  940. //---------------------------------------------------------------------------
  941. void __fastcall InvokeHelp(TWinControl * Control)
  942. {
  943. assert(Control != NULL);
  944. HELPINFO HelpInfo;
  945. HelpInfo.cbSize = sizeof(HelpInfo);
  946. HelpInfo.iContextType = HELPINFO_WINDOW;
  947. HelpInfo.iCtrlId = 0;
  948. HelpInfo.hItemHandle = Control->Handle;
  949. HelpInfo.dwContextId = 0;
  950. HelpInfo.MousePos.x = 0;
  951. HelpInfo.MousePos.y = 0;
  952. SendMessage(Control->Handle, WM_HELP, NULL, reinterpret_cast<long>(&HelpInfo));
  953. }
  954. //---------------------------------------------------------------------------
  955. //---------------------------------------------------------------------------
  956. static void __fastcall FocusableLabelCanvas(TStaticText * StaticText,
  957. TControlCanvas ** ACanvas, TRect & R)
  958. {
  959. TControlCanvas * Canvas = new TControlCanvas();
  960. try
  961. {
  962. Canvas->Control = StaticText;
  963. R = StaticText->ClientRect;
  964. UnicodeString Caption = StaticText->Caption;
  965. bool AccelChar = false;
  966. if (StaticText->ShowAccelChar)
  967. {
  968. Caption = StripHotkey(Caption);
  969. AccelChar = (Caption != StaticText->Caption);
  970. }
  971. TSize TextSize = Canvas->TextExtent(Caption);
  972. assert(StaticText->BorderStyle == sbsNone); // not taken into account
  973. if (AccelChar)
  974. {
  975. TextSize.cy += 2;
  976. }
  977. R.Bottom = R.Top + TextSize.cy;
  978. switch (StaticText->Alignment)
  979. {
  980. case taLeftJustify:
  981. R.Right = R.Left + TextSize.cx;
  982. break;
  983. case taRightJustify:
  984. R.Left = Max(0, R.Right - TextSize.cx);
  985. break;
  986. case taCenter:
  987. {
  988. FAIL; // not used branch, possibly untested
  989. int Diff = R.Width() - TextSize.cx;
  990. R.Left += Diff / 2;
  991. R.Right -= Diff - (Diff / 2);
  992. }
  993. break;
  994. }
  995. }
  996. __finally
  997. {
  998. if (ACanvas == NULL)
  999. {
  1000. delete Canvas;
  1001. }
  1002. }
  1003. if (ACanvas != NULL)
  1004. {
  1005. *ACanvas = Canvas;
  1006. }
  1007. }
  1008. //---------------------------------------------------------------------------
  1009. static void __fastcall FocusableLabelWindowProc(void * Data, TMessage & Message,
  1010. bool & Clicked)
  1011. {
  1012. Clicked = false;
  1013. TStaticText * StaticText = static_cast<TStaticText *>(Data);
  1014. if (Message.Msg == WM_LBUTTONDOWN)
  1015. {
  1016. StaticText->SetFocus();
  1017. // in case the action takes long, make sure focus is shown immediatelly
  1018. UpdateWindow(StaticText->Handle);
  1019. Clicked = true;
  1020. Message.Result = 1;
  1021. }
  1022. else if (Message.Msg == WM_RBUTTONDOWN)
  1023. {
  1024. StaticText->SetFocus();
  1025. Message.Result = 1;
  1026. }
  1027. else if (Message.Msg == WM_CHAR)
  1028. {
  1029. if (reinterpret_cast<TWMChar &>(Message).CharCode == L' ')
  1030. {
  1031. Clicked = true;
  1032. Message.Result = 1;
  1033. }
  1034. else
  1035. {
  1036. ControlWndProc(StaticText)(Message);
  1037. }
  1038. }
  1039. else if (Message.Msg == CM_DIALOGCHAR)
  1040. {
  1041. if (StaticText->CanFocus() && StaticText->ShowAccelChar &&
  1042. IsAccel(reinterpret_cast<TCMDialogChar &>(Message).CharCode, StaticText->Caption))
  1043. {
  1044. StaticText->SetFocus();
  1045. // in case the action takes long, make sure focus is shown immediatelly
  1046. UpdateWindow(StaticText->Handle);
  1047. Clicked = true;
  1048. Message.Result = 1;
  1049. }
  1050. else
  1051. {
  1052. ControlWndProc(StaticText)(Message);
  1053. }
  1054. }
  1055. else
  1056. {
  1057. ControlWndProc(StaticText)(Message);
  1058. }
  1059. if (Message.Msg == WM_PAINT)
  1060. {
  1061. TRect R;
  1062. TControlCanvas * Canvas;
  1063. FocusableLabelCanvas(StaticText, &Canvas, R);
  1064. try
  1065. {
  1066. if (StaticText->Focused())
  1067. {
  1068. Canvas->DrawFocusRect(R);
  1069. }
  1070. else if (!StaticText->Font->Style.Contains(fsUnderline))
  1071. {
  1072. Canvas->Pen->Style = psDot;
  1073. Canvas->Brush->Style = bsClear;
  1074. if (!StaticText->Enabled)
  1075. {
  1076. Canvas->Pen->Color = clBtnHighlight;
  1077. Canvas->MoveTo(R.Left + 1 + 1, R.Bottom);
  1078. Canvas->LineTo(R.Right + 1, R.Bottom);
  1079. Canvas->Pen->Color = clGrayText;
  1080. }
  1081. Canvas->MoveTo(R.Left, R.Bottom - 1);
  1082. Canvas->LineTo(R.Right, R.Bottom - 1);
  1083. }
  1084. }
  1085. __finally
  1086. {
  1087. delete Canvas;
  1088. }
  1089. }
  1090. else if ((Message.Msg == WM_SETFOCUS) || (Message.Msg == WM_KILLFOCUS) ||
  1091. (Message.Msg == CM_ENABLEDCHANGED))
  1092. {
  1093. StaticText->Invalidate();
  1094. }
  1095. }
  1096. //---------------------------------------------------------------------------
  1097. static THintWindow * PersistentHintWindow = NULL;
  1098. static TControl * PersistentHintControl = NULL;
  1099. //---------------------------------------------------------------------------
  1100. void __fastcall CancelPersistentHint()
  1101. {
  1102. if (PersistentHintWindow != NULL)
  1103. {
  1104. PersistentHintControl = NULL;
  1105. SAFE_DESTROY(PersistentHintWindow);
  1106. }
  1107. }
  1108. //---------------------------------------------------------------------------
  1109. void __fastcall ShowPersistentHint(TControl * Control, TPoint HintPos)
  1110. {
  1111. CancelPersistentHint();
  1112. THintInfo HintInfo;
  1113. HintInfo.HintControl = Control;
  1114. HintInfo.HintPos = HintPos;
  1115. HintInfo.HintMaxWidth = GetParentForm(Control)->Monitor->Width;
  1116. HintInfo.HintColor = Application->HintColor;
  1117. HintInfo.HintStr = GetShortHint(Control->Hint);
  1118. HintInfo.HintData = NULL;
  1119. bool CanShow = true;
  1120. if (Application->OnShowHint != NULL)
  1121. {
  1122. Application->OnShowHint(HintInfo.HintStr, CanShow, HintInfo);
  1123. }
  1124. if (CanShow)
  1125. {
  1126. PersistentHintControl = Control;
  1127. PersistentHintWindow = new THintWindow(Application);
  1128. PersistentHintWindow->BiDiMode = Control->BiDiMode;
  1129. PersistentHintWindow->Color = HintInfo.HintColor;
  1130. TRect HintWinRect;
  1131. if (HintInfo.HintMaxWidth < Control->Width)
  1132. {
  1133. HintInfo.HintMaxWidth = Control->Width;
  1134. }
  1135. HintWinRect = PersistentHintWindow->CalcHintRect(
  1136. HintInfo.HintMaxWidth, HintInfo.HintStr, HintInfo.HintData);
  1137. OffsetRect(HintWinRect, HintInfo.HintPos.x, HintInfo.HintPos.y);
  1138. // TODO: right align window placement for UseRightToLeftAlignment, see Forms.pas
  1139. PersistentHintWindow->ActivateHintData(HintWinRect, HintInfo.HintStr, HintInfo.HintData);
  1140. }
  1141. }
  1142. //---------------------------------------------------------------------------
  1143. static void __fastcall HintLabelWindowProc(void * Data, TMessage & Message)
  1144. {
  1145. bool Clicked = false;
  1146. bool Cancel = false;
  1147. TStaticText * StaticText = static_cast<TStaticText *>(Data);
  1148. if (Message.Msg == CM_HINTSHOW)
  1149. {
  1150. TCMHintShow & HintShow = reinterpret_cast<TCMHintShow &>(Message);
  1151. if (PersistentHintControl == StaticText)
  1152. {
  1153. // do not allow standard hint when persistent is already shown
  1154. HintShow.Result = 1;
  1155. }
  1156. else
  1157. {
  1158. HintShow.HintInfo->HideTimeout = 100000; // never
  1159. }
  1160. }
  1161. else if (Message.Msg == CN_KEYDOWN)
  1162. {
  1163. if ((reinterpret_cast<TWMKey &>(Message).CharCode == VK_ESCAPE) &&
  1164. (PersistentHintControl == StaticText))
  1165. {
  1166. CancelPersistentHint();
  1167. StaticText->Invalidate();
  1168. Message.Result = 1;
  1169. }
  1170. else
  1171. {
  1172. FocusableLabelWindowProc(Data, Message, Clicked);
  1173. }
  1174. }
  1175. else
  1176. {
  1177. FocusableLabelWindowProc(Data, Message, Clicked);
  1178. }
  1179. if (Message.Msg == CM_CANCELMODE)
  1180. {
  1181. TCMCancelMode & CancelMessage = (TCMCancelMode&)Message;
  1182. if ((CancelMessage.Sender != StaticText) &&
  1183. (CancelMessage.Sender != PersistentHintWindow))
  1184. {
  1185. Cancel = true;
  1186. }
  1187. }
  1188. if ((Message.Msg == WM_DESTROY) || (Message.Msg == WM_KILLFOCUS))
  1189. {
  1190. Cancel = true;
  1191. }
  1192. if (Cancel && (PersistentHintControl == StaticText))
  1193. {
  1194. CancelPersistentHint();
  1195. }
  1196. if (Clicked && (PersistentHintControl != StaticText))
  1197. {
  1198. TRect R;
  1199. TPoint HintPos;
  1200. FocusableLabelCanvas(StaticText, NULL, R);
  1201. HintPos.y = R.Bottom - R.Top;
  1202. HintPos.x = R.Left;
  1203. ShowPersistentHint(StaticText, StaticText->ClientToScreen(HintPos));
  1204. }
  1205. }
  1206. //---------------------------------------------------------------------------
  1207. void __fastcall HintLabel(TStaticText * StaticText, UnicodeString Hint)
  1208. {
  1209. // Currently all are right-justified, when other alignemtn is used,
  1210. // test respective branches in FocusableLabelCanvas.
  1211. assert(StaticText->Alignment == taRightJustify);
  1212. // With right-justify, it has to be off. We may not notice on riginal
  1213. // English version, results will differ with translations only
  1214. assert(!StaticText->AutoSize);
  1215. StaticText->ParentFont = true;
  1216. if (!Hint.IsEmpty())
  1217. {
  1218. StaticText->Hint = Hint;
  1219. }
  1220. StaticText->ShowHint = true;
  1221. StaticText->Cursor = crHandPoint;
  1222. TWndMethod WindowProc;
  1223. ((TMethod*)&WindowProc)->Data = StaticText;
  1224. ((TMethod*)&WindowProc)->Code = HintLabelWindowProc;
  1225. StaticText->WindowProc = WindowProc;
  1226. }
  1227. //---------------------------------------------------------------------------
  1228. void __fastcall HintLabelRestore(TStaticText * StaticText)
  1229. {
  1230. StaticText->WindowProc = ControlWndProc(StaticText);
  1231. StaticText->ShowHint = false;
  1232. StaticText->Cursor = crDefault;
  1233. }
  1234. //---------------------------------------------------------------------------
  1235. static void __fastcall ComboBoxFixWindowProc(void * Data, TMessage & Message)
  1236. {
  1237. // it is TCustomComboxBox, but the properties are published only by TComboBox
  1238. TComboBox * ComboBox = static_cast<TComboBox *>(Data);
  1239. if (Message.Msg == WM_SIZE)
  1240. {
  1241. UnicodeString Text = ComboBox->Text;
  1242. try
  1243. {
  1244. ControlWndProc(ComboBox)(Message);
  1245. }
  1246. __finally
  1247. {
  1248. // workaround for bug in combo box, that causes it to change text to any
  1249. // item from drop down list which starts with current text,
  1250. // after control is resized (unless the text is in drop down list as well)
  1251. ComboBox->Text = Text;
  1252. // hide selection, which is wrongly shown when form is resized, even when the box has not focus
  1253. if (!ComboBox->Focused())
  1254. {
  1255. ComboBox->SelLength = 0;
  1256. }
  1257. }
  1258. }
  1259. else
  1260. {
  1261. ControlWndProc(ComboBox)(Message);
  1262. }
  1263. }
  1264. //---------------------------------------------------------------------------
  1265. void __fastcall FixComboBoxResizeBug(TCustomComboBox * ComboBox)
  1266. {
  1267. TWndMethod WindowProc;
  1268. ((TMethod*)&WindowProc)->Data = ComboBox;
  1269. ((TMethod*)&WindowProc)->Code = ComboBoxFixWindowProc;
  1270. ComboBox->WindowProc = WindowProc;
  1271. }
  1272. //---------------------------------------------------------------------------
  1273. static void __fastcall LinkLabelClick(TStaticText * StaticText)
  1274. {
  1275. if (StaticText->OnClick != NULL)
  1276. {
  1277. StaticText->OnClick(StaticText);
  1278. }
  1279. else
  1280. {
  1281. UnicodeString Url = StaticText->Caption;
  1282. if (!SameText(Url.SubString(1, 4), L"http") && (Url.Pos(L"@") > 0))
  1283. {
  1284. Url = L"mailto:" + Url;
  1285. }
  1286. OpenBrowser(Url);
  1287. }
  1288. }
  1289. //---------------------------------------------------------------------------
  1290. static void __fastcall LinkLabelWindowProc(void * Data, TMessage & Message)
  1291. {
  1292. bool Clicked = false;
  1293. TStaticText * StaticText = static_cast<TStaticText *>(Data);
  1294. if (Message.Msg == WM_CONTEXTMENU)
  1295. {
  1296. TWMContextMenu & ContextMenu = reinterpret_cast<TWMContextMenu &>(Message);
  1297. if ((ContextMenu.Pos.x < 0) && (ContextMenu.Pos.y < 0))
  1298. {
  1299. TRect R;
  1300. FocusableLabelCanvas(StaticText, NULL, R);
  1301. TPoint P = StaticText->ClientToScreen(TPoint(R.Left, R.Bottom));
  1302. ContextMenu.Pos.x = static_cast<short>(P.x);
  1303. ContextMenu.Pos.y = static_cast<short>(P.y);
  1304. }
  1305. }
  1306. else if (Message.Msg == WM_KEYDOWN)
  1307. {
  1308. TWMKey & Key = reinterpret_cast<TWMKey &>(Message);
  1309. if ((GetKeyState(VK_CONTROL) < 0) && (Key.CharCode == L'C'))
  1310. {
  1311. TInstantOperationVisualizer Visualizer;
  1312. CopyToClipboard(StaticText->Caption);
  1313. Message.Result = 1;
  1314. }
  1315. else
  1316. {
  1317. FocusableLabelWindowProc(Data, Message, Clicked);
  1318. }
  1319. }
  1320. FocusableLabelWindowProc(Data, Message, Clicked);
  1321. if (Message.Msg == WM_DESTROY)
  1322. {
  1323. delete StaticText->PopupMenu;
  1324. assert(StaticText->PopupMenu == NULL);
  1325. }
  1326. if (Clicked)
  1327. {
  1328. LinkLabelClick(StaticText);
  1329. }
  1330. }
  1331. //---------------------------------------------------------------------------
  1332. static void __fastcall LinkLabelContextMenuClick(void * Data, TObject * Sender)
  1333. {
  1334. TStaticText * StaticText = static_cast<TStaticText *>(Data);
  1335. TMenuItem * MenuItem = dynamic_cast<TMenuItem *>(Sender);
  1336. assert(MenuItem != NULL);
  1337. if (MenuItem->Tag == 0)
  1338. {
  1339. LinkLabelClick(StaticText);
  1340. }
  1341. else
  1342. {
  1343. TInstantOperationVisualizer Visualizer;
  1344. CopyToClipboard(StaticText->Caption);
  1345. }
  1346. }
  1347. //---------------------------------------------------------------------------
  1348. void __fastcall LinkLabel(TStaticText * StaticText, UnicodeString Url,
  1349. TNotifyEvent OnEnter)
  1350. {
  1351. StaticText->Transparent = false;
  1352. StaticText->ParentFont = true;
  1353. StaticText->Font->Style = StaticText->Font->Style << fsUnderline;
  1354. StaticText->Font->Color = clBlue;
  1355. StaticText->Cursor = crHandPoint;
  1356. reinterpret_cast<TButton*>(StaticText)->OnEnter = OnEnter;
  1357. if (!Url.IsEmpty())
  1358. {
  1359. StaticText->Caption = Url;
  1360. }
  1361. if (StaticText->OnClick == NULL)
  1362. {
  1363. assert(StaticText->PopupMenu == NULL);
  1364. StaticText->PopupMenu = new TPopupMenu(StaticText);
  1365. try
  1366. {
  1367. TNotifyEvent ContextMenuOnClick;
  1368. ((TMethod*)&ContextMenuOnClick)->Data = StaticText;
  1369. ((TMethod*)&ContextMenuOnClick)->Code = LinkLabelContextMenuClick;
  1370. TMenuItem * Item;
  1371. Item = new TMenuItem(StaticText->PopupMenu);
  1372. Item->Caption = LoadStr(URL_LINK_OPEN);
  1373. Item->Tag = 0;
  1374. Item->ShortCut = ShortCut(L' ', TShiftState());
  1375. Item->OnClick = ContextMenuOnClick;
  1376. StaticText->PopupMenu->Items->Add(Item);
  1377. Item = new TMenuItem(StaticText->PopupMenu);
  1378. Item->Caption = LoadStr(URL_LINK_COPY);
  1379. Item->Tag = 1;
  1380. Item->ShortCut = ShortCut(L'C', TShiftState() << ssCtrl);
  1381. Item->OnClick = ContextMenuOnClick;
  1382. StaticText->PopupMenu->Items->Add(Item);
  1383. }
  1384. catch(...)
  1385. {
  1386. delete StaticText->PopupMenu;
  1387. assert(StaticText->PopupMenu == NULL);
  1388. throw;
  1389. }
  1390. }
  1391. TWndMethod WindowProc;
  1392. ((TMethod*)&WindowProc)->Data = StaticText;
  1393. ((TMethod*)&WindowProc)->Code = LinkLabelWindowProc;
  1394. StaticText->WindowProc = WindowProc;
  1395. }
  1396. //---------------------------------------------------------------------------
  1397. static void __fastcall HotTrackLabelMouseEnter(void * /*Data*/, TObject * Sender)
  1398. {
  1399. reinterpret_cast<TLabel *>(Sender)->Font->Color = clBlue;
  1400. }
  1401. //---------------------------------------------------------------------------
  1402. static void __fastcall HotTrackLabelMouseLeave(void * /*Data*/, TObject * Sender)
  1403. {
  1404. reinterpret_cast<TLabel *>(Sender)->ParentFont = true;
  1405. }
  1406. //---------------------------------------------------------------------------
  1407. void __fastcall HotTrackLabel(TLabel * Label)
  1408. {
  1409. assert(Label->OnMouseEnter == NULL);
  1410. assert(Label->OnMouseLeave == NULL);
  1411. Label->OnMouseEnter = MakeMethod<TNotifyEvent>(NULL, HotTrackLabelMouseEnter);
  1412. Label->OnMouseLeave = MakeMethod<TNotifyEvent>(NULL, HotTrackLabelMouseLeave);
  1413. }
  1414. //---------------------------------------------------------------------------
  1415. Forms::TMonitor * __fastcall FormMonitor(TCustomForm * Form)
  1416. {
  1417. Forms::TMonitor * Result;
  1418. if ((Application->MainForm != NULL) && (Application->MainForm != Form))
  1419. {
  1420. Result = Application->MainForm->Monitor;
  1421. }
  1422. else if (LastMonitor != NULL)
  1423. {
  1424. Result = LastMonitor;
  1425. }
  1426. else
  1427. {
  1428. int i = 0;
  1429. while ((i < Screen->MonitorCount) && !Screen->Monitors[i]->Primary)
  1430. {
  1431. i++;
  1432. }
  1433. assert(Screen->Monitors[i]->Primary);
  1434. Result = Screen->Monitors[i];
  1435. }
  1436. return Result;
  1437. }
  1438. //---------------------------------------------------------------------------
  1439. int __fastcall GetLastMonitor()
  1440. {
  1441. if (LastMonitor != NULL)
  1442. {
  1443. return LastMonitor->MonitorNum;
  1444. }
  1445. else
  1446. {
  1447. return -1;
  1448. }
  1449. }
  1450. //---------------------------------------------------------------------------
  1451. void __fastcall SetLastMonitor(int MonitorNum)
  1452. {
  1453. if ((MonitorNum >= 0) && (MonitorNum < Screen->MonitorCount))
  1454. {
  1455. LastMonitor = Screen->Monitors[MonitorNum];
  1456. }
  1457. else
  1458. {
  1459. LastMonitor = NULL;
  1460. }
  1461. }
  1462. //---------------------------------------------------------------------------
  1463. TForm * __fastcall _SafeFormCreate(TMetaClass * FormClass, TComponent * Owner)
  1464. {
  1465. TForm * Form;
  1466. if (Owner == NULL)
  1467. {
  1468. Owner = GetFormOwner();
  1469. }
  1470. // If there is no main form yet, make this one main.
  1471. // This:
  1472. // - Makes other forms (dialogs invoked from this one),
  1473. // be placed on the same monitor (otherwise all new forms get placed
  1474. // on primary monitor)
  1475. // - Triggers MainForm-specific code in DoFormWindowProc.
  1476. // - Shows button on taskbar
  1477. if (Application->MainForm == NULL)
  1478. {
  1479. Application->CreateForm(FormClass, &Form);
  1480. assert(Application->MainForm == Form);
  1481. }
  1482. else
  1483. {
  1484. Form = dynamic_cast<TForm *>(Construct(FormClass, Owner));
  1485. assert(Form != NULL);
  1486. }
  1487. return Form;
  1488. }
  1489. //---------------------------------------------------------------------------
  1490. TImageList * __fastcall SharedSystemImageList(bool Large)
  1491. {
  1492. TSHFileInfo FileInfo;
  1493. TImageList * Result = new TImageList(Application);
  1494. int ImageListHandle = SHGetFileInfo(L"", 0, &FileInfo, sizeof(FileInfo),
  1495. SHGFI_SYSICONINDEX | (Large ? SHGFI_LARGEICON : SHGFI_SMALLICON));
  1496. if (ImageListHandle != 0)
  1497. {
  1498. Result->ShareImages = true;
  1499. Result->Handle = ImageListHandle;
  1500. }
  1501. return Result;
  1502. }
  1503. //---------------------------------------------------------------------------
  1504. bool __fastcall SupportsSplitButton()
  1505. {
  1506. return (Win32MajorVersion >= 6);
  1507. }
  1508. //---------------------------------------------------------------------------
  1509. static TButton * __fastcall FindDefaultButton(TWinControl * Control)
  1510. {
  1511. TButton * Result = NULL;
  1512. int Index = 0;
  1513. while ((Result == NULL) && (Index < Control->ControlCount))
  1514. {
  1515. TControl * ChildControl = Control->Controls[Index];
  1516. TButton * Button = dynamic_cast<TButton *>(ChildControl);
  1517. if ((Button != NULL) && Button->Default)
  1518. {
  1519. Result = Button;
  1520. }
  1521. else
  1522. {
  1523. TWinControl * WinControl = dynamic_cast<TWinControl *>(ChildControl);
  1524. if (WinControl != NULL)
  1525. {
  1526. Result = FindDefaultButton(WinControl);
  1527. }
  1528. }
  1529. Index++;
  1530. }
  1531. return Result;
  1532. }
  1533. //---------------------------------------------------------------------------
  1534. TModalResult __fastcall DefaultResult(TCustomForm * Form)
  1535. {
  1536. // The point of this is to avoid hardcoding mrOk when checking dialog results.
  1537. // Previously we used != mrCancel instead, as mrCancel is more reliable,
  1538. // being automatically used for Esc/X buttons (and hence kind of forced to be used
  1539. // for Cancel buttons). But that failed to be reliable in the end, for
  1540. // ModalResult being mrNone, when Windows session is being logged off.
  1541. // We interpreted mrNone as OK, causing lots of troubles.
  1542. TModalResult Result = mrNone;
  1543. TButton * Button = FindDefaultButton(Form);
  1544. if (ALWAYS_TRUE(Button != NULL))
  1545. {
  1546. Result = Button->ModalResult;
  1547. }
  1548. if (ALWAYS_FALSE(Result == mrNone))
  1549. {
  1550. Result = mrOk;
  1551. }
  1552. return Result;
  1553. }
  1554. //---------------------------------------------------------------------------
  1555. void __fastcall SetFormIcon(TForm * Form, int Size, const UnicodeString & IconName)
  1556. {
  1557. HICON Icon = LoadIcon(HInstance, IconName.c_str());
  1558. if (ALWAYS_TRUE(Icon != NULL))
  1559. {
  1560. LPARAM LParam = reinterpret_cast<LPARAM>(Icon);
  1561. SendMessage(Form->Handle, WM_SETICON, Size, LParam);
  1562. DestroyIcon(Icon);
  1563. }
  1564. }
  1565. //---------------------------------------------------------------------------
  1566. void __fastcall SetFormIcons(TForm * Form, const UnicodeString & BigIconName,
  1567. const UnicodeString & SmallIconName)
  1568. {
  1569. SetFormIcon(Form, ICON_SMALL, SmallIconName);
  1570. SetFormIcon(Form, ICON_BIG, BigIconName);
  1571. }
  1572. //---------------------------------------------------------------------------
  1573. void __fastcall UseDesktopFont(TControl * Control)
  1574. {
  1575. TCustomStatusBar * StatusBar = dynamic_cast<TCustomStatusBar *>(Control);
  1576. if (StatusBar != NULL)
  1577. {
  1578. // otherwise setting DesktopFont below has no effect
  1579. StatusBar->UseSystemFont = false;
  1580. }
  1581. class TPublicControl : public TControl
  1582. {
  1583. public:
  1584. __property DesktopFont;
  1585. };
  1586. reinterpret_cast<TPublicControl *>(Control)->DesktopFont = true;
  1587. }
  1588. //---------------------------------------------------------------------------
  1589. void __fastcall LoadResourceImage(TImage * Image, const UnicodeString & ImageName)
  1590. {
  1591. std::unique_ptr<TPngImage> Png(new TPngImage());
  1592. Png->LoadFromResourceName(0, ImageName);
  1593. Image->Picture->Assign(Png.get());
  1594. }