PathLabel.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. unit PathLabel;
  2. interface
  3. {$WARN UNIT_PLATFORM OFF}
  4. uses
  5. Messages, StdCtrls, Controls, Classes, Forms, Windows, Graphics;
  6. type
  7. TCustomPathLabel = class;
  8. TPathLabelGetStatusEvent = procedure(Sender: TCustomPathLabel; var Active: Boolean) of object;
  9. TPathLabelPathClickEvent = procedure(Sender: TCustomPathLabel; Path: string) of object;
  10. TCustomPathLabel = class(TCustomLabel)
  11. private
  12. FColors: array[0..5] of TColor;
  13. FIndentHorizontal: Integer;
  14. FIndentVertical: Integer;
  15. FUnixPath: Boolean;
  16. FOnGetStatus: TPathLabelGetStatusEvent;
  17. FOnPathClick: TPathLabelPathClickEvent;
  18. FOnMaskClick: TNotifyEvent;
  19. FDisplayPath: string;
  20. FDisplayHotTrack: string;
  21. FDisplayMask: string;
  22. FHotTrack: Boolean;
  23. FMouseInView: Boolean;
  24. FIsActive: Boolean;
  25. FIsEnabled: Boolean;
  26. FMask: string;
  27. FAutoSizeVertical: Boolean;
  28. procedure CMHintShow(var Message: TMessage); message CM_HINTSHOW;
  29. procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
  30. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  31. function GetColors(Index: Integer): TColor;
  32. procedure SetColors(Index: Integer; Value: TColor);
  33. procedure SetIndentHorizontal(AIndent: Integer);
  34. procedure SetIndentVertical(AIndent: Integer);
  35. procedure SetUnixPath(AUnixPath: Boolean);
  36. procedure SetMask(Value: string);
  37. procedure SetAutoSizeVertical(Value: Boolean);
  38. procedure SetFocusControl(Value: TWinControl);
  39. function GetFocusControl: TWinControl;
  40. function CalculateAutoHotTrackColor(C: TColor): TColor;
  41. procedure CalculateAutoHotTrackColors;
  42. function CalculateAutoHotTrackColorComponent(C: Byte; Bright: Boolean): Byte;
  43. function UseHotTrack: Boolean;
  44. protected
  45. procedure AdjustBounds; override;
  46. procedure Click; override;
  47. procedure DoDrawTextIntern(var Rect: TRect; Flags: Longint; S: string);
  48. procedure DoDrawText(var Rect: TRect; Flags: Longint); override;
  49. procedure Notification(AComponent: TComponent;
  50. Operation: TOperation); override;
  51. procedure Paint; override;
  52. function IsActive: Boolean;
  53. function TrackingActive: Boolean;
  54. function HotTrackPath: string;
  55. function GetSeparator: Char;
  56. procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
  57. procedure DoPathClick(Path: string); virtual;
  58. procedure DoMaskClick;
  59. procedure DblClick; override;
  60. procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
  61. public
  62. constructor Create(AnOwner: TComponent); override;
  63. procedure UpdateStatus;
  64. function UseRightToLeftAlignment: Boolean; override;
  65. property ActiveColor: TColor index 1 read GetColors write SetColors
  66. default clActiveCaption;
  67. property ActiveTextColor: TColor index 3 read GetColors write SetColors
  68. default clCaptionText;
  69. property UnixPath: Boolean read FUnixPath write SetUnixPath default False;
  70. property IndentHorizontal: Integer read FIndentHorizontal
  71. write SetIndentHorizontal default 5;
  72. property IndentVertical: Integer read FIndentVertical
  73. write SetIndentVertical default 1;
  74. property InactiveColor: TColor index 0 read GetColors write SetColors
  75. default clInactiveCaption;
  76. property InactiveTextColor: TColor index 2 read GetColors write SetColors
  77. default clInactiveCaptionText;
  78. property OnGetStatus: TPathLabelGetStatusEvent read FOnGetStatus write FOnGetStatus;
  79. property OnPathClick: TPathLabelPathClickEvent read FOnPathClick write FOnPathClick;
  80. property OnMaskClick: TNotifyEvent read FOnMaskClick write FOnMaskClick;
  81. property HotTrack: Boolean read FHotTrack write FHotTrack default False;
  82. property Mask: string read FMask write SetMask;
  83. property AutoSizeVertical: Boolean read FAutoSizeVertical write SetAutoSizeVertical default False;
  84. property FocusControl: TWinControl read GetFocusControl write SetFocusControl;
  85. property Caption;
  86. property Hint stored False;
  87. property Align default alTop;
  88. end;
  89. type
  90. TPathLabel = class(TCustomPathLabel)
  91. published
  92. property ActiveColor;
  93. property ActiveTextColor;
  94. property UnixPath;
  95. property IndentHorizontal;
  96. property IndentVertical;
  97. property InactiveColor;
  98. property InactiveTextColor;
  99. property AutoSizeVertical;
  100. property HotTrack;
  101. property OnGetStatus;
  102. property OnPathClick;
  103. property OnMaskClick;
  104. property Align;
  105. property Alignment;
  106. property Anchors;
  107. property AutoSize;
  108. property BiDiMode;
  109. property Constraints;
  110. property DragCursor;
  111. property DragKind;
  112. property DragMode;
  113. property Enabled;
  114. property Font;
  115. property ParentBiDiMode;
  116. property ParentFont;
  117. property PopupMenu;
  118. property Transparent;
  119. property Visible;
  120. property OnClick;
  121. property OnDblClick;
  122. property OnDragDrop;
  123. property OnDragOver;
  124. property OnEndDock;
  125. property OnEndDrag;
  126. property OnMouseDown;
  127. property OnMouseMove;
  128. property OnMouseUp;
  129. property OnStartDock;
  130. property OnStartDrag;
  131. end;
  132. procedure Register;
  133. implementation
  134. uses
  135. { SysUtils must overload deprecated FileCtrl (implements MinimizeName) }
  136. FileCtrl, SysUtils, Math;
  137. const
  138. // magic value
  139. HotTrackMask: string = #1;
  140. procedure Register;
  141. begin
  142. RegisterComponents('Martin', [TPathLabel]);
  143. end;
  144. { TCustomPathLabel }
  145. constructor TCustomPathLabel.Create(AnOwner: TComponent);
  146. begin
  147. inherited Create(AnOwner);
  148. WordWrap := False;
  149. Align := alTop;
  150. ShowAccelChar := False;
  151. FIndentHorizontal := 5;
  152. FIndentVertical := 1;
  153. FUnixPath := False;
  154. FHotTrack := False;
  155. FColors[0] := clInactiveCaption;
  156. FColors[1] := clActiveCaption;
  157. FColors[2] := clInactiveCaptionText;
  158. FColors[3] := clCaptionText;
  159. FIsEnabled := True;
  160. CalculateAutoHotTrackColors;
  161. end;
  162. procedure TCustomPathLabel.CMHintShow(var Message: TMessage);
  163. begin
  164. with TCMHintShow(Message).HintInfo^ do
  165. begin
  166. HintPos.X := ClientOrigin.X + IndentHorizontal;
  167. HintPos.Y := ClientOrigin.Y + IndentVertical;
  168. if UseHotTrack then Inc(HintPos.Y, Height);
  169. end;
  170. end; { CMHintShow }
  171. procedure TCustomPathLabel.Click;
  172. var
  173. HotPath: string;
  174. RemainingPath: string;
  175. begin
  176. if FIsEnabled then
  177. begin
  178. HotPath := HotTrackPath;
  179. if HotPath <> '' then
  180. begin
  181. if Copy(HotPath, Length(HotPath), 1) = GetSeparator then
  182. SetLength(HotPath, Length(HotPath) - 1);
  183. if HotPath = HotTrackMask then DoMaskClick
  184. else
  185. if FDisplayPath = Caption then DoPathClick(HotPath)
  186. else
  187. begin
  188. // Displayed path is shortened.
  189. // The below is based on knowledge of MinimizeName algorithm
  190. RemainingPath := Copy(FDisplayPath, Length(HotPath) + 1,
  191. Length(FDisplayPath) - Length(HotPath));
  192. if Copy(RemainingPath, Length(RemainingPath), 1) = GetSeparator then
  193. SetLength(RemainingPath, Length(RemainingPath) - 1);
  194. if RemainingPath = Copy(Caption, Length(Caption) - Length(RemainingPath) + 1,
  195. Length(RemainingPath)) then
  196. begin
  197. DoPathClick(Copy(Caption, 1, Length(Caption) - Length(RemainingPath)));
  198. end
  199. else
  200. if HotPath = Copy(Caption, 1, Length(HotPath)) then
  201. begin
  202. DoPathClick(HotPath);
  203. end
  204. else Assert(False);
  205. end;
  206. end;
  207. if Assigned(FocusControl) then FocusControl.SetFocus;
  208. inherited;
  209. end;
  210. end; { Click }
  211. procedure TCustomPathLabel.SetUnixPath(AUnixPath: Boolean);
  212. begin
  213. if FUnixPath <> AUnixPath then
  214. begin
  215. FUnixPath := AUnixPath;
  216. AdjustBounds;
  217. Invalidate;
  218. end;
  219. end;
  220. procedure TCustomPathLabel.SetMask(Value: string);
  221. begin
  222. if FMask <> Value then
  223. begin
  224. FMask := Value;
  225. AdjustBounds;
  226. Invalidate;
  227. end;
  228. end;
  229. procedure TCustomPathLabel.SetColors(Index: integer; Value: TColor);
  230. begin
  231. Assert(Index in [0..3]);
  232. if FColors[Index] <> Value then
  233. begin
  234. FColors[Index] := Value;
  235. CalculateAutoHotTrackColors;
  236. UpdateStatus;
  237. end;
  238. end; { SetColors }
  239. // taken from PngImageListEditor
  240. const
  241. WeightR: single = 0.764706;
  242. WeightG: single = 1.52941;
  243. WeightB: single = 0.254902;
  244. function ColorDistance(C1, C2: Integer): Single;
  245. var
  246. DR, DG, DB: Integer;
  247. begin
  248. DR := (C1 and $FF) - (C2 and $FF);
  249. Result := Sqr(DR * WeightR);
  250. DG := (C1 shr 8 and $FF) - (C2 shr 8 and $FF);
  251. Result := Result + Sqr(DG * WeightG);
  252. DB := (C1 shr 16) - (C2 shr 16);
  253. Result := Result + Sqr(DB * WeightB);
  254. Result := Sqrt(Result);
  255. end;
  256. function GetAdjustedThreshold(BkgndIntensity, Threshold: Single): Single;
  257. begin
  258. if BkgndIntensity < 220 then
  259. Result := (2 - BkgndIntensity / 220) * Threshold
  260. else
  261. Result := Threshold;
  262. end;
  263. function IsContrastEnough(AColor, ABkgndColor: Integer; DoAdjustThreshold: Boolean; Threshold: Single): Boolean;
  264. begin
  265. if DoAdjustThreshold then
  266. Threshold := GetAdjustedThreshold(ColorDistance(ABkgndColor, $000000),
  267. Threshold);
  268. Result := ColorDistance(ABkgndColor, AColor) > Threshold;
  269. end;
  270. procedure AdjustContrast(var AColor: Integer; ABkgndColor: Integer; Threshold: Single);
  271. var
  272. X, Y, Z: Single;
  273. R, G, B: Single;
  274. RR, GG, BB: Integer;
  275. I1, I2, S, Q, W: Single;
  276. DoInvert: Boolean;
  277. begin
  278. I1 := ColorDistance(AColor, $000000);
  279. I2 := ColorDistance(ABkgndColor, $000000);
  280. Threshold := GetAdjustedThreshold(I2, Threshold);
  281. if I1 > I2 then
  282. DoInvert := I2 < 442 - Threshold
  283. else
  284. DoInvert := I2 < Threshold;
  285. X := (ABkgndColor and $FF) * WeightR;
  286. Y := (ABkgndColor shr 8 and $FF) * WeightG;
  287. Z := (ABkgndColor shr 16) * WeightB;
  288. R := (AColor and $FF) * WeightR;
  289. G := (AColor shr 8 and $FF) * WeightG;
  290. B := (AColor shr 16) * WeightB;
  291. if DoInvert then begin
  292. R := 195 - R;
  293. G := 390 - G;
  294. B := 65 - B;
  295. X := 195 - X;
  296. Y := 390 - Y;
  297. Z := 65 - Z;
  298. end;
  299. S := Sqrt(Sqr(B) + Sqr(G) + Sqr(R));
  300. if S < 0.01 then
  301. S := 0.01;
  302. Q := (R * X + G * Y + B * Z) / S;
  303. X := Q / S * R - X;
  304. Y := Q / S * G - Y;
  305. Z := Q / S * B - Z;
  306. W := Sqrt(Sqr(Threshold) - Sqr(X) - Sqr(Y) - Sqr(Z));
  307. R := (Q - W) * R / S;
  308. G := (Q - W) * G / S;
  309. B := (Q - W) * B / S;
  310. if DoInvert then begin
  311. R := 195 - R;
  312. G := 390 - G;
  313. B := 65 - B;
  314. end;
  315. if R < 0 then
  316. R := 0
  317. else if R > 195 then
  318. R := 195;
  319. if G < 0 then
  320. G := 0
  321. else if G > 390 then
  322. G := 390;
  323. if B < 0 then
  324. B := 0
  325. else if B > 65 then
  326. B := 65;
  327. RR := Trunc(R * (1 / WeightR) + 0.5);
  328. GG := Trunc(G * (1 / WeightG) + 0.5);
  329. BB := Trunc(B * (1 / WeightB) + 0.5);
  330. if RR > $FF then
  331. RR := $FF
  332. else if RR < 0 then
  333. RR := 0;
  334. if GG > $FF then
  335. GG := $FF
  336. else if GG < 0 then
  337. GG := 0;
  338. if BB > $FF then
  339. BB := $FF
  340. else if BB < 0 then
  341. BB := 0;
  342. AColor := (BB and $FF) shl 16 or (GG and $FF) shl 8 or (RR and $FF);
  343. end;
  344. procedure SetContrast(var Color: TColor; BkgndColor: TColor; Threshold: Integer);
  345. var
  346. T: Single;
  347. begin
  348. if Color < 0 then
  349. Color := GetSysColor(Color and $FF);
  350. if BkgndColor < 0 then
  351. BkgndColor := GetSysColor(BkgndColor and $FF);
  352. T := Threshold;
  353. if not IsContrastEnough(Color, BkgndColor, True, T) then
  354. AdjustContrast(Integer(Color), BkgndColor, T);
  355. end;
  356. function TCustomPathLabel.CalculateAutoHotTrackColorComponent(C: Byte; Bright: Boolean): Byte;
  357. var
  358. Delta: Byte;
  359. begin
  360. Delta := Max(Round(C * 0.3), 80);
  361. if Bright then
  362. Result := Byte(Max(Integer(C) - Delta, 0))
  363. else
  364. Result := Byte(Min(C + Delta, 255));
  365. end;
  366. function TCustomPathLabel.CalculateAutoHotTrackColor(C: TColor): TColor;
  367. var
  368. R, G, B: Byte;
  369. Bright: Boolean;
  370. begin
  371. C := ColorToRGB(C);
  372. R := GetRValue(C);
  373. G := GetGValue(C);
  374. B := GetBValue(C);
  375. Bright := (R + G + B) > (256 / 2 * 3);
  376. R := CalculateAutoHotTrackColorComponent(R, Bright);
  377. G := CalculateAutoHotTrackColorComponent(G, Bright);
  378. B := CalculateAutoHotTrackColorComponent(B, Bright);
  379. Result := RGB(R, G, B);
  380. end;
  381. procedure TCustomPathLabel.CalculateAutoHotTrackColors;
  382. begin
  383. FColors[4] := CalculateAutoHotTrackColor(FColors[2]);
  384. SetContrast(FColors[4], FColors[0], 50);
  385. FColors[5] := CalculateAutoHotTrackColor(FColors[3]);
  386. SetContrast(FColors[5], FColors[1], 50);
  387. end;
  388. procedure TCustomPathLabel.SetIndentHorizontal(AIndent: Integer);
  389. begin
  390. if FIndentHorizontal <> AIndent then
  391. begin
  392. FIndentHorizontal := AIndent;
  393. AdjustBounds;
  394. Invalidate;
  395. end;
  396. end;
  397. procedure TCustomPathLabel.SetIndentVertical(AIndent: Integer);
  398. begin
  399. if FIndentVertical <> AIndent then
  400. begin
  401. FIndentVertical := AIndent;
  402. AdjustBounds;
  403. Invalidate;
  404. end;
  405. end;
  406. procedure TCustomPathLabel.SetAutoSizeVertical(Value: Boolean);
  407. begin
  408. if FAutoSizeVertical <> Value then
  409. begin
  410. FAutoSizeVertical := Value;
  411. AdjustBounds;
  412. Invalidate;
  413. end;
  414. end;
  415. function TCustomPathLabel.GetFocusControl: TWinControl;
  416. begin
  417. Result := inherited FocusControl;
  418. end;
  419. procedure TCustomPathLabel.SetFocusControl(Value: TWinControl);
  420. begin
  421. if FocusControl <> Value then
  422. begin
  423. inherited FocusControl := Value;
  424. UpdateStatus;
  425. end;
  426. end;
  427. procedure TCustomPathLabel.DoDrawTextIntern(var Rect: TRect; Flags: Longint; S: string);
  428. var
  429. i: Integer;
  430. Width: Integer;
  431. WidthMask: Integer;
  432. WidthPath: Integer;
  433. HotTrackOffset: Integer;
  434. HotTrackBottom: Integer;
  435. Separator: string;
  436. Str: string;
  437. HotTrackColor: TColor;
  438. VirtualMask: string;
  439. IsEmptyMask: Boolean;
  440. IsVirtualMask: Boolean;
  441. begin
  442. if (Flags and DT_CALCRECT <> 0) and ((S = '') or ShowAccelChar and
  443. (S[1] = '&') and (S[2] = #0)) then S := S + ' ';
  444. if not ShowAccelChar then Flags := Flags or DT_NOPREFIX;
  445. // have to apply DrawTextBiDiModeFlags if we ever deal with dibi
  446. Canvas.Font := Font;
  447. Width := (Rect.Right - Rect.Left);
  448. FDisplayPath := S;
  449. VirtualMask := Mask;
  450. IsVirtualMask := False;
  451. IsEmptyMask := (VirtualMask = '');
  452. if IsEmptyMask and HotTrack then
  453. begin
  454. VirtualMask := '*.*';
  455. IsVirtualMask := not FMouseInView;
  456. end;
  457. if VirtualMask <> '' then
  458. begin
  459. if (Length(FDisplayPath) > 0) and (FDisplayPath[Length(FDisplayPath)] <> GetSeparator) then
  460. Separator := GetSeparator;
  461. FDisplayPath := FDisplayPath + Separator;
  462. S := S + Separator;
  463. WidthMask := Canvas.TextWidth(VirtualMask);
  464. if WidthMask > Width div 3 then
  465. WidthPath := Width - (Width div 3)
  466. else
  467. WidthPath := Width - WidthMask;
  468. end
  469. else
  470. begin
  471. WidthMask := 0;
  472. WidthPath := Width;
  473. end;
  474. if FUnixPath then
  475. for i := 1 to Length(FDisplayPath) do
  476. begin
  477. case FDisplayPath[i] of
  478. '/': FDisplayPath[i] := '\';
  479. '\': FDisplayPath[i] := '/';
  480. end;
  481. end;
  482. FDisplayPath := MinimizeName(FDisplayPath, Canvas, WidthPath);
  483. if FUnixPath then
  484. for i := 1 to Length(FDisplayPath) do
  485. begin
  486. case FDisplayPath[i] of
  487. '\': FDisplayPath[i] := '/';
  488. '/': FDisplayPath[i] := '\';
  489. end;
  490. end;
  491. WidthPath := Canvas.TextWidth(FDisplayPath);
  492. if VirtualMask <> '' then
  493. begin
  494. if WidthMask > Width - WidthPath then
  495. begin
  496. VirtualMask := VirtualMask + '...';
  497. repeat
  498. Delete(VirtualMask, Length(VirtualMask) - 3, 1);
  499. WidthMask := Canvas.TextWidth(VirtualMask);
  500. until (WidthMask <= Width - WidthPath) or (Length(VirtualMask) = 3);
  501. end;
  502. end;
  503. Str := FDisplayPath;
  504. if not IsVirtualMask then
  505. begin
  506. Str := Str + VirtualMask;
  507. FDisplayMask := VirtualMask;
  508. end
  509. else
  510. begin
  511. FDisplayMask := '';
  512. end;
  513. ShowHint :=
  514. (FDisplayPath <> S) or
  515. ((not IsEmptyMask) and (FDisplayMask <> Mask)) or
  516. (WidthPath + WidthMask > Width);
  517. if not ShowHint then Hint := ''
  518. else Hint := S + Mask;
  519. if not FIsEnabled then
  520. begin
  521. Canvas.Font.Color := clBtnShadow;
  522. DrawText(Canvas.Handle, PChar(Str), Length(Str), Rect, Flags);
  523. end
  524. else
  525. begin
  526. FDisplayHotTrack := HotTrackPath;
  527. HotTrackColor := TColor(0); // shut up
  528. if FDisplayHotTrack <> '' then
  529. begin
  530. if TrackingActive then
  531. begin
  532. HotTrackColor := FColors[4 + Integer(FIsActive)]
  533. end
  534. else
  535. begin
  536. // We do not have a path label with hot-track and not tracking-active,
  537. // so this is untested branch
  538. Assert(False);
  539. // As if it were active
  540. HotTrackColor := FColors[2 + 1];
  541. end;
  542. end;
  543. if (FDisplayHotTrack <> '') and (FDisplayHotTrack <> HotTrackMask) then
  544. begin
  545. Canvas.Font.Color := HotTrackColor;
  546. DrawText(Canvas.Handle, PChar(FDisplayHotTrack), Length(FDisplayHotTrack), Rect, Flags);
  547. HotTrackOffset := Canvas.TextWidth(FDisplayHotTrack);
  548. Inc(Rect.Left, HotTrackOffset);
  549. Delete(Str, 1, Length(FDisplayHotTrack));
  550. HotTrackBottom := Rect.Bottom;
  551. end
  552. else
  553. begin
  554. HotTrackOffset := 0;
  555. HotTrackBottom := 0;
  556. end;
  557. if TrackingActive then
  558. Canvas.Font.Color := FColors[2 + Integer(FIsActive)]
  559. else
  560. Canvas.Font.Color := clWindowText;
  561. if FDisplayHotTrack = HotTrackMask then
  562. begin
  563. DrawText(Canvas.Handle, PChar(FDisplayPath), Length(FDisplayPath), Rect, Flags);
  564. HotTrackOffset := Canvas.TextWidth(FDisplayPath);
  565. HotTrackBottom := Rect.Bottom;
  566. Inc(Rect.Left, HotTrackOffset);
  567. Canvas.Font.Color := HotTrackColor;
  568. DrawText(Canvas.Handle, PChar(VirtualMask), Length(VirtualMask), Rect, Flags);
  569. end
  570. else
  571. begin
  572. DrawText(Canvas.Handle, PChar(Str), Length(Str), Rect, Flags);
  573. end;
  574. Dec(Rect.Left, HotTrackOffset);
  575. Rect.Bottom := Max(Rect.Bottom, HotTrackBottom);
  576. end;
  577. end;
  578. procedure TCustomPathLabel.DoDrawText(var Rect: TRect; Flags: Longint);
  579. begin
  580. DoDrawTextIntern(Rect, Flags, Caption);
  581. end;
  582. function TCustomPathLabel.GetSeparator: Char;
  583. begin
  584. if FUnixPath then Result := '/'
  585. else Result := '\';
  586. end;
  587. function TCustomPathLabel.HotTrackPath: string;
  588. var
  589. P: TPoint;
  590. DelimPos: Integer;
  591. Len: Integer;
  592. Path: string;
  593. begin
  594. Result := '';
  595. if UseHotTrack and FMouseInView and (FDisplayPath <> '') then
  596. begin
  597. P := ScreenToClient(Mouse.CursorPos);
  598. Len := P.X - FIndentHorizontal;
  599. if Len >= 0 then
  600. begin
  601. if Len < Canvas.TextWidth(FDisplayPath) then
  602. begin
  603. Result := '';
  604. Path := FDisplayPath;
  605. repeat
  606. Assert(Path <> '');
  607. DelimPos := Pos(GetSeparator, Path);
  608. if DelimPos > 0 then
  609. begin
  610. Result := Result + Copy(Path, 1, DelimPos);
  611. Delete(Path, 1, DelimPos);
  612. end
  613. else
  614. begin
  615. Result := Result + Path;
  616. Path := '';
  617. end;
  618. until (Canvas.TextWidth(Result) >= Len) or (Path = '');
  619. end
  620. else
  621. if Len < Canvas.TextWidth(FDisplayPath + FDisplayMask) then
  622. begin
  623. Result := HotTrackMask;
  624. end;
  625. end;
  626. end;
  627. end;
  628. function TCustomPathLabel.GetColors(Index: Integer): TColor;
  629. begin
  630. Assert(Index in [0..5]);
  631. Result := FColors[Index];
  632. end; { GetColors }
  633. function TCustomPathLabel.UseRightToLeftAlignment: Boolean;
  634. begin
  635. // Not sure how to properly deal with RTL atm.
  636. // See also a comment on DrawTextBiDiModeFlags in DoDrawTextIntern
  637. Result := False;
  638. end;
  639. procedure TCustomPathLabel.Paint;
  640. const
  641. Alignments: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
  642. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  643. var
  644. Rect, CalcRect: TRect;
  645. DrawStyle: Longint;
  646. begin
  647. with Canvas do
  648. begin
  649. if not Transparent then
  650. begin
  651. Brush.Color := Self.Color;
  652. Brush.Style := bsSolid;
  653. FillRect(ClientRect);
  654. end;
  655. Brush.Style := bsClear;
  656. Rect := ClientRect;
  657. // DoDrawText takes care of BiDi alignments
  658. DrawStyle := DT_EXPANDTABS or WordWraps[WordWrap] or Alignments[Alignment];
  659. // MP
  660. Rect.Left := Rect.Left + FIndentHorizontal;
  661. Rect.Right := Rect.Right - FIndentHorizontal;
  662. Rect.Top := Rect.Top + FIndentVertical;
  663. Rect.Bottom := Rect.Bottom - FIndentVertical;
  664. // Calculate vertical layout
  665. if Layout <> tlTop then
  666. begin
  667. CalcRect := Rect;
  668. DoDrawText(CalcRect, DrawStyle or DT_CALCRECT);
  669. if Layout = tlBottom then OffsetRect(Rect, 0, Height - CalcRect.Bottom)
  670. else OffsetRect(Rect, 0, (Height - CalcRect.Bottom) div 2);
  671. end;
  672. DoDrawText(Rect, DrawStyle);
  673. end;
  674. end;
  675. procedure TCustomPathLabel.AdjustBounds;
  676. const
  677. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  678. var
  679. DC: HDC;
  680. X: Integer;
  681. Rect: TRect;
  682. AAlignment: TAlignment;
  683. AWidth: Integer;
  684. AHeight: Integer;
  685. S: string;
  686. begin
  687. if not (csReading in ComponentState) and
  688. (AutoSize or AutoSizeVertical) then
  689. begin
  690. Rect := ClientRect;
  691. DC := GetDC(0);
  692. Canvas.Handle := DC;
  693. S := Caption;
  694. if S = '' then S := 'SomeTextForSizing';
  695. DoDrawTextIntern(Rect, (DT_EXPANDTABS or DT_CALCRECT) or WordWraps[WordWrap], S);
  696. Canvas.Handle := 0;
  697. ReleaseDC(0, DC);
  698. X := Left;
  699. AAlignment := Alignment;
  700. if UseRightToLeftAlignment then ChangeBiDiModeAlignment(AAlignment);
  701. if AAlignment = taRightJustify then Inc(X, Width - 2*FIndentHorizontal + Rect.Right);
  702. if AutoSize then AWidth := 2*FIndentHorizontal + Rect.Right
  703. else AWidth := Width;
  704. if AutoSize or AutoSizeVertical then AHeight := 2*FIndentVertical + Rect.Bottom
  705. else AHeight := Height;
  706. SetBounds(X, Top, AWidth, AHeight);
  707. end;
  708. end;
  709. function TCustomPathLabel.IsActive: Boolean;
  710. begin
  711. if csDestroying in ComponentState then Result := False
  712. else
  713. begin
  714. Result := Assigned(FocusControl) and FocusControl.Focused;
  715. if Assigned(OnGetStatus) then
  716. OnGetStatus(Self, Result);
  717. end;
  718. end;
  719. function TCustomPathLabel.TrackingActive: Boolean;
  720. begin
  721. Result := Assigned(FocusControl) or Assigned(OnGetStatus);
  722. end;
  723. procedure TCustomPathLabel.UpdateStatus;
  724. var
  725. NewIsActive: Boolean;
  726. NewIsEnabled: Boolean;
  727. NewColor: TColor;
  728. begin
  729. if TrackingActive then
  730. begin
  731. NewIsActive := IsActive;
  732. NewIsEnabled :=
  733. Enabled and
  734. ((not Assigned(FocusControl)) or FocusControl.Enabled);
  735. NewColor := FColors[Integer(NewIsActive)];
  736. if (NewIsActive <> FIsActive) or
  737. (NewIsEnabled <> FIsEnabled) or
  738. (NewColor <> Color) then
  739. begin
  740. FIsActive := NewIsActive;
  741. FIsEnabled := NewIsEnabled;
  742. Color := NewColor;
  743. Invalidate;
  744. end;
  745. end
  746. else
  747. begin
  748. Color := clBtnFace;
  749. end;
  750. end; { UpdateStatus }
  751. procedure TCustomPathLabel.Notification(AComponent: TComponent;
  752. Operation: TOperation);
  753. var
  754. NeedUpdate: Boolean;
  755. begin
  756. NeedUpdate :=
  757. (Operation = opRemove) and (AComponent = FocusControl);
  758. inherited;
  759. if NeedUpdate then UpdateStatus;
  760. end; { Notification }
  761. function TCustomPathLabel.UseHotTrack: Boolean;
  762. begin
  763. Result := HotTrack and FIsEnabled;
  764. end;
  765. procedure TCustomPathLabel.MouseMove(Shift: TShiftState; X: Integer; Y: Integer);
  766. begin
  767. inherited;
  768. if FMouseInView and UseHotTrack and (FDisplayHotTrack <> HotTrackPath) then
  769. begin
  770. Invalidate;
  771. end;
  772. end;
  773. procedure TCustomPathLabel.DoPathClick(Path: string);
  774. begin
  775. if Assigned(OnPathClick) then
  776. OnPathClick(Self, Path);
  777. end;
  778. procedure TCustomPathLabel.DoMaskClick;
  779. begin
  780. if Assigned(OnMaskClick) then
  781. OnMaskClick(Self);
  782. end;
  783. procedure TCustomPathLabel.DblClick;
  784. begin
  785. if FIsEnabled then
  786. begin
  787. inherited;
  788. end;
  789. end;
  790. procedure TCustomPathLabel.DoContextPopup(MousePos: TPoint; var Handled: Boolean);
  791. begin
  792. if FIsEnabled then
  793. begin
  794. inherited;
  795. end
  796. else
  797. begin
  798. Handled := True;
  799. end;
  800. end;
  801. procedure TCustomPathLabel.CMMouseEnter(var Message: TMessage);
  802. begin
  803. inherited;
  804. FMouseInView := True;
  805. Invalidate;
  806. end;
  807. procedure TCustomPathLabel.CMMouseLeave(var Message: TMessage);
  808. begin
  809. FMouseInView := False;
  810. Invalidate;
  811. inherited;
  812. end;
  813. end.