VCLCommon.cpp 49 KB

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