VCLCommon.cpp 50 KB

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