NewStaticText.pas 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. unit NewStaticText;
  2. {
  3. TNewStaticText - similar to TStaticText on D3+ but with multi-line AutoSize
  4. support and a WordWrap property
  5. }
  6. interface
  7. {$IFNDEF VER90}
  8. {$IFNDEF VER100}
  9. {$IFNDEF VER120}
  10. {$IFNDEF VER130}
  11. {$DEFINE Delphi6OrHigher}
  12. {$ENDIF}
  13. {$ENDIF}
  14. {$ENDIF}
  15. {$ENDIF}
  16. uses
  17. Windows, Messages, SysUtils, Classes, Controls, Forms;
  18. type
  19. TNewStaticText = class(TWinControl)
  20. private
  21. FAutoSize: Boolean;
  22. FFocusControl: TWinControl;
  23. FForceLTRReading: Boolean;
  24. FLastAdjustBoundsRTL: Boolean;
  25. FShowAccelChar: Boolean;
  26. FWordWrap: Boolean;
  27. procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  28. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  29. procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
  30. procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  31. procedure AdjustBounds;
  32. function CalcBounds: TPoint;
  33. function GetDrawTextFlags: UINT;
  34. procedure SetFocusControl(Value: TWinControl);
  35. procedure SetForceLTRReading(Value: Boolean);
  36. procedure SetShowAccelChar(Value: Boolean);
  37. procedure SetWordWrap(Value: Boolean);
  38. protected
  39. procedure CreateParams(var Params: TCreateParams); override;
  40. procedure Loaded; override;
  41. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  42. procedure SetAutoSize(Value: Boolean); {$IFDEF Delphi6OrHigher}override;{$ENDIF}
  43. public
  44. constructor Create(AOwner: TComponent); override;
  45. function AdjustHeight: Integer;
  46. published
  47. property Align;
  48. property Anchors;
  49. property AutoSize: Boolean read FAutoSize write SetAutoSize default True;
  50. property Caption;
  51. property Color;
  52. property DragCursor;
  53. property DragMode;
  54. property Enabled;
  55. property FocusControl: TWinControl read FFocusControl write SetFocusControl;
  56. property Font;
  57. property ForceLTRReading: Boolean read FForceLTRReading write SetForceLTRReading
  58. default False;
  59. property ParentColor;
  60. property ParentFont;
  61. property ParentShowHint;
  62. property PopupMenu;
  63. property ShowAccelChar: Boolean read FShowAccelChar write SetShowAccelChar
  64. default True;
  65. property ShowHint;
  66. property TabOrder;
  67. property TabStop;
  68. property Visible;
  69. property WordWrap: Boolean read FWordWrap write SetWordWrap default False;
  70. property OnClick;
  71. property OnDblClick;
  72. property OnDragDrop;
  73. property OnDragOver;
  74. property OnEndDrag;
  75. property OnMouseDown;
  76. property OnMouseMove;
  77. property OnMouseUp;
  78. property OnStartDrag;
  79. end;
  80. procedure Register;
  81. implementation
  82. uses
  83. BidiUtils;
  84. procedure Register;
  85. begin
  86. RegisterComponents('JR', [TNewStaticText]);
  87. end;
  88. procedure DrawTextACP(const DC: HDC; const S: String; var R: TRect;
  89. const AFormat: UINT);
  90. { Draws an ANSI string using the system's code page (CP_ACP), unlike DrawTextA
  91. which uses the code page defined by the selected font. }
  92. {$IFDEF UNICODE}
  93. begin
  94. DrawText(DC, PChar(S), Length(S), R, AFormat);
  95. end;
  96. {$ELSE}
  97. var
  98. SLen, WideLen: Integer;
  99. WideStr: PWideChar;
  100. begin
  101. SLen := Length(S);
  102. if SLen = 0 then
  103. Exit;
  104. if Win32Platform = VER_PLATFORM_WIN32_NT then begin
  105. if SLen > High(Integer) div SizeOf(WideChar) then
  106. Exit;
  107. GetMem(WideStr, SLen * SizeOf(WideChar));
  108. try
  109. WideLen := MultiByteToWideChar(CP_ACP, 0, PChar(S), SLen, WideStr, SLen);
  110. DrawTextW(DC, WideStr, WideLen, R, AFormat);
  111. finally
  112. FreeMem(WideStr);
  113. end;
  114. end
  115. else
  116. DrawText(DC, PChar(S), SLen, R, AFormat);
  117. end;
  118. {$ENDIF}
  119. { TNewStaticText }
  120. constructor TNewStaticText.Create(AOwner: TComponent);
  121. begin
  122. inherited Create(AOwner);
  123. ControlStyle := [csCaptureMouse, csClickEvents, csSetCaption,
  124. csOpaque, csReplicatable, csDoubleClicks];
  125. Width := 65;
  126. Height := 17;
  127. FAutoSize := True;
  128. FShowAccelChar := True;
  129. AdjustBounds;
  130. end;
  131. procedure TNewStaticText.CreateParams(var Params: TCreateParams);
  132. begin
  133. inherited CreateParams(Params);
  134. CreateSubClass(Params, 'STATIC');
  135. with Params do
  136. begin
  137. Style := Style or SS_NOTIFY;
  138. if not SetBiDiStyles(Self, Params) then begin
  139. { Quirk: No style is set for WordWrap=False in RTL mode; WS_EX_RIGHT
  140. overrides SS_LEFTNOWORDWRAP, and there is no SS_RIGHTNOWORDWRAP style.
  141. WordWrap=False still affects AdjustBounds, though. }
  142. if not FWordWrap then Style := Style or SS_LEFTNOWORDWRAP;
  143. end;
  144. if not FShowAccelChar then Style := Style or SS_NOPREFIX;
  145. if FForceLTRReading then ExStyle := ExStyle and not WS_EX_RTLREADING;
  146. end;
  147. end;
  148. procedure TNewStaticText.CMDialogChar(var Message: TCMDialogChar);
  149. begin
  150. if (FFocusControl <> nil) and Enabled and ShowAccelChar and
  151. IsAccel(Message.CharCode, Caption) then
  152. with FFocusControl do
  153. if CanFocus then
  154. begin
  155. SetFocus;
  156. Message.Result := 1;
  157. end;
  158. end;
  159. procedure TNewStaticText.CMFontChanged(var Message: TMessage);
  160. begin
  161. inherited;
  162. AdjustBounds;
  163. end;
  164. procedure TNewStaticText.CMParentFontChanged(var Message: TMessage);
  165. begin
  166. inherited;
  167. { What we're really trapping here is changes to Parent. Recalculate size
  168. if the new Parent's RTL setting is different. }
  169. if IsParentRightToLeft(Self) <> FLastAdjustBoundsRTL then
  170. AdjustBounds;
  171. end;
  172. procedure TNewStaticText.CMTextChanged(var Message: TMessage);
  173. begin
  174. inherited;
  175. Invalidate;
  176. AdjustBounds;
  177. end;
  178. procedure TNewStaticText.Loaded;
  179. begin
  180. inherited Loaded;
  181. AdjustBounds;
  182. end;
  183. function TNewStaticText.GetDrawTextFlags: UINT;
  184. begin
  185. Result := DT_EXPANDTABS or DT_NOCLIP;
  186. if FWordWrap then Result := Result or DT_WORDBREAK;
  187. if not FShowAccelChar then Result := Result or DT_NOPREFIX;
  188. if IsParentRightToLeft(Self) then begin
  189. { Note: DT_RTLREADING must be included even when just calculating the
  190. size, since on certain fonts it can affect the width of characters.
  191. (Consider the Hebrew string: 'a '#$F9' b'. On 2000 with Lucida Console
  192. as the font, the spaces aren't drawn as wide with RTLREADING.) }
  193. Result := Result or DT_RIGHT;
  194. if not FForceLTRReading then
  195. Result := Result or DT_RTLREADING;
  196. end;
  197. end;
  198. function TNewStaticText.CalcBounds: TPoint;
  199. var
  200. R: TRect;
  201. S: String;
  202. DC: HDC;
  203. begin
  204. { Note: The calculated width/height is actually one pixel wider/taller
  205. than the size of the text, so that when Enabled=False the white shadow
  206. does not get clipped }
  207. R := Rect(0, 0, Width, 0);
  208. if R.Right > 0 then Dec(R.Right);
  209. S := Caption;
  210. if (S = '') or (FShowAccelChar and (S[1] = '&') and (S[2] = #0)) then
  211. S := S + ' ';
  212. DC := GetDC(0);
  213. try
  214. SelectObject(DC, Font.Handle);
  215. { On NT platforms, static controls are Unicode-based internally; when
  216. ANSI text is assigned to them, it is converted to Unicode using the
  217. system code page (ACP). We must be sure to use the ACP here, too,
  218. otherwise the calculated size could be incorrect. The code page used
  219. by DrawTextA is defined by the font, and not necessarily equal to the
  220. ACP, so we can't use it. (To reproduce: with the ACP set to Hebrew
  221. (1255), try passing Hebrew text to DrawTextA with the font set to
  222. "Lucida Console". It appears to use CP 1252, not 1255.) }
  223. DrawTextACP(DC, S, R, DT_CALCRECT or GetDrawTextFlags);
  224. finally
  225. ReleaseDC(0, DC);
  226. end;
  227. Result.X := R.Right + 1;
  228. Result.Y := R.Bottom + 1;
  229. end;
  230. procedure TNewStaticText.AdjustBounds;
  231. var
  232. NewBounds: TPoint;
  233. NewLeft, NewWidth: Integer;
  234. begin
  235. if not (csLoading in ComponentState) and FAutoSize then
  236. begin
  237. FLastAdjustBoundsRTL := IsParentRightToLeft(Self);
  238. NewBounds := CalcBounds;
  239. NewLeft := Left;
  240. NewWidth := Width;
  241. if not FWordWrap then begin
  242. NewWidth := NewBounds.X;
  243. if IsParentFlipped(Self) then
  244. Inc(NewLeft, Width - NewWidth);
  245. end;
  246. SetBounds(NewLeft, Top, NewWidth, NewBounds.Y);
  247. end;
  248. end;
  249. function TNewStaticText.AdjustHeight: Integer;
  250. var
  251. OldHeight: Integer;
  252. begin
  253. OldHeight := Height;
  254. Height := CalcBounds.Y;
  255. Result := Height - OldHeight;
  256. end;
  257. procedure TNewStaticText.Notification(AComponent: TComponent;
  258. Operation: TOperation);
  259. begin
  260. inherited Notification(AComponent, Operation);
  261. if (Operation = opRemove) and (AComponent = FFocusControl) then
  262. FFocusControl := nil;
  263. end;
  264. procedure TNewStaticText.SetAutoSize(Value: Boolean);
  265. begin
  266. if FAutoSize <> Value then
  267. begin
  268. FAutoSize := Value;
  269. if Value then AdjustBounds;
  270. end;
  271. end;
  272. procedure TNewStaticText.SetFocusControl(Value: TWinControl);
  273. begin
  274. FFocusControl := Value;
  275. if Value <> nil then Value.FreeNotification(Self);
  276. end;
  277. procedure TNewStaticText.SetForceLTRReading(Value: Boolean);
  278. begin
  279. if FForceLTRReading <> Value then begin
  280. FForceLTRReading := Value;
  281. RecreateWnd;
  282. AdjustBounds;
  283. end;
  284. end;
  285. procedure TNewStaticText.SetShowAccelChar(Value: Boolean);
  286. begin
  287. if FShowAccelChar <> Value then
  288. begin
  289. FShowAccelChar := Value;
  290. RecreateWnd;
  291. AdjustBounds;
  292. end;
  293. end;
  294. procedure TNewStaticText.SetWordWrap(Value: Boolean);
  295. begin
  296. if FWordWrap <> Value then
  297. begin
  298. FWordWrap := Value;
  299. RecreateWnd;
  300. AdjustBounds;
  301. end;
  302. end;
  303. end.