PathLabel.pas 22 KB

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