PathLabel.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  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. FHotTrack: Boolean;
  21. FMouseInView: Boolean;
  22. FIsActive: Boolean;
  23. procedure CMHintShow(var Message: TMessage); message CM_HINTSHOW;
  24. procedure CMMouseEnter(var Message: TMessage); message CM_MOUSEENTER;
  25. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  26. function GetColors(Index: Integer): TColor;
  27. procedure SetColors(Index: Integer; Value: TColor);
  28. procedure SetIndentHorizontal(AIndent: Integer);
  29. procedure SetIndentVertical(AIndent: Integer);
  30. procedure SetUnixPath(AUnixPath: Boolean);
  31. protected
  32. procedure AdjustBounds; override;
  33. procedure Click; override;
  34. procedure DoDrawText(var Rect: TRect; Flags: Longint); override;
  35. procedure Notification(AComponent: TComponent;
  36. Operation: TOperation); override;
  37. procedure Paint; override;
  38. function IsActive: Boolean;
  39. function HotTrackPath(Path: string): string;
  40. procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
  41. procedure DoPathClick(Path: string); virtual;
  42. public
  43. constructor Create(AnOwner: TComponent); override;
  44. procedure UpdateStatus;
  45. property ActiveColor: TColor index 1 read GetColors write SetColors
  46. default clActiveCaption;
  47. property ActiveTextColor: TColor index 3 read GetColors write SetColors
  48. default clCaptionText;
  49. property ActiveHotTrackColor: TColor index 5 read GetColors write SetColors
  50. default clGradientActiveCaption;
  51. property UnixPath: Boolean read FUnixPath write SetUnixPath default False;
  52. property IndentHorizontal: Integer read FIndentHorizontal
  53. write SetIndentHorizontal default 5;
  54. property IndentVertical: Integer read FIndentVertical
  55. write SetIndentVertical default 1;
  56. property InactiveColor: TColor index 0 read GetColors write SetColors
  57. default clInactiveCaption;
  58. property InactiveTextColor: TColor index 2 read GetColors write SetColors
  59. default clInactiveCaptionText;
  60. property InactiveHotTrackColor: TColor index 4 read GetColors write SetColors
  61. default clGradientInactiveCaption;
  62. property OnGetStatus: TPathLabelGetStatusEvent read FOnGetStatus write FOnGetStatus;
  63. property OnPathClick: TPathLabelPathClickEvent read FOnPathClick write FOnPathClick;
  64. property HotTrack: Boolean read FHotTrack write FHotTrack default False;
  65. property FocusControl;
  66. property Caption;
  67. property Hint stored False;
  68. property Align default alTop;
  69. end;
  70. type
  71. TPathLabel = class(TCustomPathLabel)
  72. published
  73. property ActiveColor;
  74. property ActiveTextColor;
  75. property ActiveHotTrackColor;
  76. property UnixPath;
  77. property IndentHorizontal;
  78. property IndentVertical;
  79. property InactiveColor;
  80. property InactiveTextColor;
  81. property InactiveHotTrackColor;
  82. property HotTrack;
  83. property OnGetStatus;
  84. property OnPathClick;
  85. property Align;
  86. property Alignment;
  87. property Anchors;
  88. property AutoSize;
  89. property BiDiMode;
  90. property Constraints;
  91. property DragCursor;
  92. property DragKind;
  93. property DragMode;
  94. property Enabled;
  95. property Font;
  96. property ParentBiDiMode;
  97. property ParentFont;
  98. property PopupMenu;
  99. property Transparent;
  100. property Visible;
  101. property OnClick;
  102. property OnDblClick;
  103. property OnDragDrop;
  104. property OnDragOver;
  105. property OnEndDock;
  106. property OnEndDrag;
  107. property OnMouseDown;
  108. property OnMouseMove;
  109. property OnMouseUp;
  110. property OnStartDock;
  111. property OnStartDrag;
  112. end;
  113. procedure Register;
  114. implementation
  115. uses
  116. { SysUtils must overload deprecated FileCtrl (implements MinimizeName) }
  117. FileCtrl, SysUtils;
  118. procedure Register;
  119. begin
  120. RegisterComponents('Martin', [TPathLabel]);
  121. end;
  122. { TCustomPathLabel }
  123. constructor TCustomPathLabel.Create(AnOwner: TComponent);
  124. begin
  125. inherited Create(AnOwner);
  126. WordWrap := False;
  127. Align := alTop;
  128. ShowAccelChar := False;
  129. FIndentHorizontal := 5;
  130. FIndentVertical := 1;
  131. FUnixPath := False;
  132. FHotTrack := False;
  133. FColors[0] := clInactiveCaption;
  134. FColors[1] := clActiveCaption;
  135. FColors[2] := clInactiveCaptionText;
  136. FColors[3] := clCaptionText;
  137. FColors[4] := clGradientInactiveCaption;
  138. FColors[5] := clGradientActiveCaption;
  139. end;
  140. procedure TCustomPathLabel.CMHintShow(var Message: TMessage);
  141. begin
  142. with TCMHintShow(Message).HintInfo^ do
  143. begin
  144. HintPos.X := ClientOrigin.X + IndentHorizontal - 3;
  145. HintPos.Y := ClientOrigin.Y + IndentVertical - 3;
  146. if HotTrack then Inc(HintPos.Y, Height);
  147. end;
  148. end; { CMHintShow }
  149. procedure TCustomPathLabel.Click;
  150. var
  151. HotPath: string;
  152. RemainingPath: string;
  153. begin
  154. HotPath := HotTrackPath(FDisplayPath);
  155. if HotPath <> '' then
  156. begin
  157. if FDisplayPath = Caption then DoPathClick(HotPath)
  158. else
  159. begin
  160. // Displayed path is shortened.
  161. // The below is based on knowledge in MinimizePath algorithm
  162. RemainingPath := Copy(FDisplayPath, Length(HotPath) + 1,
  163. Length(FDisplayPath) - Length(HotPath));
  164. if RemainingPath = Copy(Caption, Length(Caption) - Length(RemainingPath) + 1,
  165. Length(RemainingPath)) then
  166. begin
  167. DoPathClick(Copy(Caption, 1, Length(Caption) - Length(RemainingPath)));
  168. end
  169. else
  170. if HotPath = Copy(Caption, 1, Length(HotPath)) then
  171. begin
  172. DoPathClick(HotPath);
  173. end
  174. else Assert(False);
  175. end;
  176. end;
  177. if Assigned(FocusControl) then FocusControl.SetFocus;
  178. inherited;
  179. end; { Click }
  180. procedure TCustomPathLabel.SetUnixPath(AUnixPath: Boolean);
  181. begin
  182. if FUnixPath <> AUnixPath then
  183. begin
  184. FUnixPath := AUnixPath;
  185. AdjustBounds;
  186. Invalidate;
  187. end;
  188. end;
  189. procedure TCustomPathLabel.SetColors(Index: integer; Value: TColor);
  190. begin
  191. Assert(Index in [0..3]);
  192. if FColors[Index] <> Value then
  193. begin
  194. FColors[Index] := Value;
  195. UpdateStatus;
  196. end;
  197. end; { SetColors }
  198. procedure TCustomPathLabel.SetIndentHorizontal(AIndent: Integer);
  199. begin
  200. if FIndentHorizontal <> AIndent then
  201. begin
  202. FIndentHorizontal := AIndent;
  203. AdjustBounds;
  204. Invalidate;
  205. end;
  206. end;
  207. procedure TCustomPathLabel.SetIndentVertical(AIndent: Integer);
  208. begin
  209. if FIndentVertical <> AIndent then
  210. begin
  211. FIndentVertical := AIndent;
  212. AdjustBounds;
  213. Invalidate;
  214. end;
  215. end;
  216. procedure TCustomPathLabel.DoDrawText(var Rect: TRect; Flags: Longint);
  217. var
  218. i: Integer;
  219. Path: string;
  220. StandardColor: TColor;
  221. begin
  222. if (Flags and DT_CALCRECT <> 0) and ((Text = '') or ShowAccelChar and
  223. (Text[1] = '&') and (Text[2] = #0)) then Text := Text + ' ';
  224. if not ShowAccelChar then Flags := Flags or DT_NOPREFIX;
  225. Flags := DrawTextBiDiModeFlags(Flags);
  226. Canvas.Font := Font;
  227. Path := Caption;
  228. if FUnixPath then
  229. for i := 1 to Length(Path) do
  230. if Path[i] = '/' then Path[i] := '\';
  231. Path := MinimizeName(Path, Canvas, Rect.Right - Rect.Left);
  232. if FUnixPath then
  233. for i := 1 to Length(Path) do
  234. if Path[i] = '\' then Path[i] := '/';
  235. ShowHint :=
  236. (Path <> Caption) or
  237. (Canvas.TextWidth(Caption) > Rect.Right - Rect.Left);
  238. if not ShowHint then Hint := ''
  239. else
  240. if Hint <> Caption then Hint := Caption;
  241. FDisplayPath := Path;
  242. if not Enabled then
  243. begin
  244. OffsetRect(Rect, 1, 1);
  245. Canvas.Font.Color := clBtnHighlight;
  246. DrawText(Canvas.Handle, PChar(Path), Length(Path), Rect, Flags);
  247. OffsetRect(Rect, -1, -1);
  248. Canvas.Font.Color := clBtnShadow;
  249. DrawText(Canvas.Handle, PChar(Path), Length(Path), Rect, Flags);
  250. end
  251. else
  252. begin
  253. FDisplayHotTrack := HotTrackPath(FDisplayPath);
  254. if FDisplayHotTrack <> '' then
  255. begin
  256. StandardColor := Canvas.Font.Color;
  257. Canvas.Font.Color := FColors[4 + Integer(FIsActive)];
  258. DrawText(Canvas.Handle, PChar(FDisplayHotTrack), Length(FDisplayHotTrack), Rect, Flags);
  259. Canvas.Font.Color := StandardColor;
  260. Inc(Rect.Left, Canvas.TextWidth(FDisplayHotTrack));
  261. Delete(Path, 1, Length(FDisplayHotTrack));
  262. end;
  263. DrawText(Canvas.Handle, PChar(Path), Length(Path), Rect, Flags);
  264. end;
  265. end;
  266. function TCustomPathLabel.HotTrackPath(Path: string): string;
  267. var
  268. P: TPoint;
  269. DelimPos: Integer;
  270. Delim: Char;
  271. Len: Integer;
  272. begin
  273. Result := '';
  274. if FHotTrack and FMouseInView and (Path <> '') then
  275. begin
  276. P := ScreenToClient(Mouse.CursorPos);
  277. Len := P.X - FIndentHorizontal;
  278. if (Len >= 0) and (Len < Canvas.TextWidth(Path)) then
  279. begin
  280. if FUnixPath then Delim := '/'
  281. else Delim := '\';
  282. Result := '';
  283. repeat
  284. Assert(Path <> '');
  285. DelimPos := Pos(Delim, Path);
  286. if DelimPos > 0 then
  287. begin
  288. Result := Result + Copy(Path, 1, DelimPos);
  289. Delete(Path, 1, DelimPos);
  290. end
  291. else
  292. begin
  293. Result := Result + Path;
  294. Path := '';
  295. end;
  296. until (Canvas.TextWidth(Result) >= Len) or (Path = '');
  297. end;
  298. end;
  299. end;
  300. function TCustomPathLabel.GetColors(Index: Integer): TColor;
  301. begin
  302. Assert(Index in [0..3]);
  303. Result := FColors[Index];
  304. end; { GetColors }
  305. procedure TCustomPathLabel.Paint;
  306. const
  307. Alignments: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
  308. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  309. var
  310. Rect, CalcRect: TRect;
  311. DrawStyle: Longint;
  312. begin
  313. with Canvas do
  314. begin
  315. if not Transparent then
  316. begin
  317. Brush.Color := Self.Color;
  318. Brush.Style := bsSolid;
  319. FillRect(ClientRect);
  320. end;
  321. Brush.Style := bsClear;
  322. Rect := ClientRect;
  323. // DoDrawText takes care of BiDi alignments
  324. DrawStyle := DT_EXPANDTABS or WordWraps[WordWrap] or Alignments[Alignment];
  325. // MP
  326. Rect.Left := Rect.Left + FIndentHorizontal;
  327. Rect.Right := Rect.Right - FIndentHorizontal;
  328. Rect.Top := Rect.Top + FIndentVertical;
  329. Rect.Bottom := Rect.Bottom - FIndentVertical;
  330. // Calculate vertical layout
  331. if Layout <> tlTop then
  332. begin
  333. CalcRect := Rect;
  334. DoDrawText(CalcRect, DrawStyle or DT_CALCRECT);
  335. if Layout = tlBottom then OffsetRect(Rect, 0, Height - CalcRect.Bottom)
  336. else OffsetRect(Rect, 0, (Height - CalcRect.Bottom) div 2);
  337. end;
  338. DoDrawText(Rect, DrawStyle);
  339. end;
  340. end;
  341. procedure TCustomPathLabel.AdjustBounds;
  342. const
  343. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  344. var
  345. DC: HDC;
  346. X: Integer;
  347. Rect: TRect;
  348. AAlignment: TAlignment;
  349. begin
  350. if not (csReading in ComponentState) and AutoSize and (Caption <> '') then
  351. begin
  352. Rect := ClientRect;
  353. DC := GetDC(0);
  354. Canvas.Handle := DC;
  355. DoDrawText(Rect, (DT_EXPANDTABS or DT_CALCRECT) or WordWraps[WordWrap]);
  356. Canvas.Handle := 0;
  357. ReleaseDC(0, DC);
  358. X := Left;
  359. AAlignment := Alignment;
  360. if UseRightToLeftAlignment then ChangeBiDiModeAlignment(AAlignment);
  361. if AAlignment = taRightJustify then Inc(X, Width - 2*FIndentHorizontal + Rect.Right);
  362. SetBounds(X, Top, 2*FIndentHorizontal + Rect.Right,
  363. 2*FIndentVertical + Rect.Bottom);
  364. end;
  365. end;
  366. function TCustomPathLabel.IsActive: Boolean;
  367. begin
  368. if csDestroying in ComponentState then Result := False
  369. else
  370. begin
  371. Result := Assigned(FocusControl) and FocusControl.Focused;
  372. if Assigned(OnGetStatus) then
  373. OnGetStatus(Self, Result);
  374. end;
  375. end;
  376. procedure TCustomPathLabel.UpdateStatus;
  377. begin
  378. FIsActive := IsActive;
  379. Color := FColors[Integer(FIsActive)];
  380. // We don't want to stote Font properties in DFM
  381. // which would be if Font.Color is set to something else than clWindowText
  382. if not (csDesigning in ComponentState) then
  383. Font.Color := FColors[2 + Integer(FIsActive)];
  384. end; { UpdateStatus }
  385. procedure TCustomPathLabel.Notification(AComponent: TComponent;
  386. Operation: TOperation);
  387. var
  388. NeedUpdate: Boolean;
  389. begin
  390. NeedUpdate :=
  391. (Operation = opRemove) and (AComponent = FocusControl);
  392. inherited;
  393. if NeedUpdate then UpdateStatus;
  394. end; { Notification }
  395. procedure TCustomPathLabel.MouseMove(Shift: TShiftState; X: Integer; Y: Integer);
  396. begin
  397. inherited;
  398. if FMouseInView and HotTrack and (FDisplayHotTrack <> HotTrackPath(FDisplayPath)) then
  399. begin
  400. Invalidate;
  401. end;
  402. end;
  403. procedure TCustomPathLabel.DoPathClick(Path: string);
  404. begin
  405. if Assigned(OnPathClick) then
  406. OnPathClick(Self, Path);
  407. end;
  408. procedure TCustomPathLabel.CMMouseEnter(var Message: TMessage);
  409. begin
  410. inherited;
  411. FMouseInView := True;
  412. end;
  413. procedure TCustomPathLabel.CMMouseLeave(var Message: TMessage);
  414. begin
  415. FMouseInView := False;
  416. Invalidate;
  417. inherited;
  418. end;
  419. end.