TB2ExtItems.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. unit TB2ExtItems;
  2. {
  3. Toolbar2000
  4. Copyright (C) 1998-2005 by Jordan Russell
  5. All rights reserved.
  6. The contents of this file are subject to the "Toolbar2000 License"; you may
  7. not use or distribute this file except in compliance with the
  8. "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
  9. TB2k-LICENSE.txt or at:
  10. https://jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
  11. Alternatively, the contents of this file may be used under the terms of the
  12. GNU General Public License (the "GPL"), in which case the provisions of the
  13. GPL are applicable instead of those in the "Toolbar2000 License". A copy of
  14. the GPL may be found in GPL-LICENSE.txt or at:
  15. https://jrsoftware.org/files/tb2k/GPL-LICENSE.txt
  16. If you wish to allow use of your version of this file only under the terms of
  17. the GPL and not to allow others to use your version of this file under the
  18. "Toolbar2000 License", indicate your decision by deleting the provisions
  19. above and replace them with the notice and other provisions required by the
  20. GPL. If you do not delete the provisions above, a recipient may use your
  21. version of this file under either the "Toolbar2000 License" or the GPL.
  22. $jrsoftware: tb2k/Source/TB2ExtItems.pas,v 1.63 2005/07/04 02:49:52 jr Exp $
  23. }
  24. interface
  25. {$I TB2Ver.inc}
  26. uses
  27. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  28. StdCtrls, CommCtrl, Menus, ActnList,
  29. TB2Item;
  30. type
  31. TTBEditItemOption = (tboUseEditWhenVertical);
  32. TTBEditItemOptions = set of TTBEditItemOption;
  33. const
  34. EditItemDefaultEditOptions = [];
  35. EditItemDefaultEditWidth = 64;
  36. { Change reasons for TTBEditItem.Text property }
  37. tcrSetProperty = 0; // direct assignment to TTBEditItem.Text property
  38. tcrActionLink = 1; // change comes from an action link
  39. tcrEditControl = 2; // change is caused by typing in edit area
  40. type
  41. TTBEditItem = class;
  42. TTBEditItemViewer = class;
  43. TTBAcceptTextEvent = procedure(Sender: TObject; var NewText: String;
  44. var Accept: Boolean) of object;
  45. TTBBeginEditEvent = procedure(Sender: TTBEditItem; Viewer: TTBEditItemViewer;
  46. EditControl: TEdit) of object;
  47. TTBEditAction = class(TAction)
  48. private
  49. FEditOptions: TTBEditItemOptions;
  50. FEditCaption: String;
  51. FEditWidth: Integer;
  52. FOnAcceptText: TTBAcceptTextEvent;
  53. FText: String;
  54. procedure SetEditCaption(Value: String);
  55. procedure SetEditOptions(Value: TTBEditItemOptions);
  56. procedure SetEditWidth(Value: Integer);
  57. procedure SetOnAcceptText(Value: TTBAcceptTextEvent);
  58. procedure SetText(Value: String);
  59. public
  60. constructor Create(AOwner: TComponent); override;
  61. published
  62. property EditCaption: String read FEditCaption write SetEditCaption;
  63. property EditOptions: TTBEditItemOptions read FEditOptions write SetEditOptions default EditItemDefaultEditOptions;
  64. property EditWidth: Integer read FEditWidth write SetEditWidth default EditItemDefaultEditWidth;
  65. property Text: String read FText write SetText;
  66. property OnAcceptText: TTBAcceptTextEvent read FOnAcceptText write SetOnAcceptText;
  67. end;
  68. TTBEditItemActionLink = class(TTBCustomItemActionLink)
  69. protected
  70. procedure AssignClient(AClient: TObject); override;
  71. function IsEditCaptionLinked: Boolean; virtual;
  72. function IsEditOptionsLinked: Boolean; virtual;
  73. function IsEditWidthLinked: Boolean; virtual;
  74. function IsOnAcceptTextLinked: Boolean; virtual;
  75. function IsTextLinked: Boolean; virtual;
  76. procedure SetEditCaption(const Value: String); virtual;
  77. procedure SetEditOptions(Value: TTBEditItemOptions); virtual;
  78. procedure SetEditWidth(const Value: Integer); virtual;
  79. procedure SetOnAcceptText(Value: TTBAcceptTextEvent); virtual;
  80. procedure SetText(const Value: String); virtual;
  81. end;
  82. TTBEditItem = class(TTBCustomItem)
  83. private
  84. FCharCase: TEditCharCase;
  85. FEditCaption: String;
  86. FEditOptions: TTBEditItemOptions;
  87. FEditWidth: Integer;
  88. FExtendedAccept: Boolean;
  89. FMaxLength: Integer;
  90. FOnAcceptText: TTBAcceptTextEvent;
  91. FOnBeginEdit: TTBBeginEditEvent;
  92. FText: String;
  93. function IsEditCaptionStored: Boolean;
  94. function IsEditOptionsStored: Boolean;
  95. function IsEditWidthStored: Boolean;
  96. function IsTextStored: Boolean;
  97. procedure SetCharCase(Value: TEditCharCase);
  98. procedure SetEditCaption(Value: String);
  99. procedure SetEditOptions(Value: TTBEditItemOptions);
  100. procedure SetEditWidth(Value: Integer);
  101. procedure SetMaxLength(Value: Integer);
  102. procedure SetText(Value: String);
  103. protected
  104. procedure ActionChange(Sender: TObject; CheckDefaults: Boolean); override;
  105. function DoAcceptText(var NewText: string): Boolean; virtual;
  106. procedure DoBeginEdit(Viewer: TTBEditItemViewer); virtual;
  107. procedure DoTextChanging(const OldText: String; var NewText: String; Reason: Integer); virtual;
  108. procedure DoTextChanged(Reason: Integer); virtual;
  109. function GetActionLinkClass: TTBCustomItemActionLinkClass; override;
  110. function GetItemViewerClass(AView: TTBView): TTBItemViewerClass; override;
  111. function NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean; override;
  112. property ExtendedAccept: Boolean read FExtendedAccept write FExtendedAccept default False;
  113. procedure SetTextEx(Value: String; Reason: Integer);
  114. public
  115. constructor Create(AOwner: TComponent); override;
  116. procedure Clear;
  117. procedure Click; override;
  118. procedure ChangeScale(M, D: Integer); override;
  119. published
  120. property Action;
  121. property AutoCheck;
  122. property Caption;
  123. property CharCase: TEditCharCase read FCharCase write SetCharCase default ecNormal;
  124. property Checked;
  125. property DisplayMode;
  126. property EditCaption: String read FEditCaption write SetEditCaption stored IsEditCaptionStored;
  127. property EditOptions: TTBEditItemOptions read FEditOptions write SetEditOptions stored IsEditOptionsStored;
  128. property EditWidth: Integer read FEditWidth write SetEditWidth stored IsEditWidthStored;
  129. property MaxLength: Integer read FMaxLength write SetMaxLength default 0;
  130. property Enabled;
  131. property GroupIndex;
  132. property HelpContext;
  133. { MP }
  134. property HelpKeyword;
  135. property Hint;
  136. property ImageIndex;
  137. property RadioItem;
  138. property ShortCut;
  139. property Text: String read FText write SetText stored IsTextStored;
  140. property Visible;
  141. property OnAcceptText: TTBAcceptTextEvent read FOnAcceptText write FOnAcceptText;
  142. property OnBeginEdit: TTBBeginEditEvent read FOnBeginEdit write FOnBeginEdit;
  143. property OnClick;
  144. property OnSelect;
  145. end;
  146. TEditClass = class of TEdit;
  147. TTBEditItemViewer = class(TTBItemViewer)
  148. private
  149. FEditControl: TEdit;
  150. FEditControlStatus: set of (ecsContinueLoop, ecsAccept, ecsClose);
  151. function EditLoop(const CapHandle: HWND): Boolean;
  152. procedure EditWndProc(var Message: TMessage);
  153. procedure MouseBeginEdit;
  154. protected
  155. procedure CalcSize(const Canvas: TCanvas; var AWidth, AHeight: Integer);
  156. override;
  157. function CaptionShown: Boolean; override;
  158. function DoExecute: Boolean; override;
  159. function GetAccRole: Integer; override;
  160. function GetAccValue(var Value: WideString): Boolean; override;
  161. function GetCaptionText: String; override;
  162. procedure GetCursor(const Pt: TPoint; var ACursor: HCURSOR); override;
  163. function GetEditControlClass: TEditClass; virtual;
  164. procedure GetEditRect(var R: TRect); virtual;
  165. procedure MouseDown(Shift: TShiftState; X, Y: Integer;
  166. var MouseDownOnMenu: Boolean); override;
  167. procedure MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean); override;
  168. procedure Paint(const Canvas: TCanvas; const ClientAreaRect: TRect;
  169. IsSelected, IsPushed, UseDisabledShadow: Boolean); override;
  170. function UsesSameWidth: Boolean; override;
  171. public
  172. property EditControl: TEdit read FEditControl;
  173. end;
  174. implementation
  175. uses
  176. TB2Common, TB2Consts;
  177. const
  178. EditMenuTextMargin = 3;
  179. EditMenuMidWidth = 4;
  180. type
  181. TControlAccess = class(TControl);
  182. TEditAccess = class(TEdit);
  183. { TTBEditAction }
  184. constructor TTBEditAction.Create(AOwner: TComponent);
  185. begin
  186. inherited;
  187. FEditOptions := EditItemDefaultEditOptions;
  188. FEditWidth := EditItemDefaultEditWidth;
  189. DisableIfNoHandler := False;
  190. end;
  191. procedure TTBEditAction.SetEditCaption(Value: String);
  192. var
  193. I: Integer;
  194. begin
  195. if FEditCaption <> Value then begin
  196. for I := 0 to ClientCount - 1 do
  197. if TBasicActionLink(Clients[I]) is TTBEditItemActionLink then
  198. TTBEditItemActionLink(Clients[I]).SetEditCaption(Value);
  199. FEditCaption := Value;
  200. Change;
  201. end;
  202. end;
  203. procedure TTBEditAction.SetEditOptions(Value: TTBEditItemOptions);
  204. var
  205. I: Integer;
  206. begin
  207. if FEditOptions <> Value then begin
  208. for I := 0 to ClientCount - 1 do
  209. if TBasicActionLink(Clients[I]) is TTBEditItemActionLink then
  210. TTBEditItemActionLink(Clients[I]).SetEditOptions(Value);
  211. FEditOptions := Value;
  212. Change;
  213. end;
  214. end;
  215. procedure TTBEditAction.SetEditWidth(Value: Integer);
  216. var
  217. I: Integer;
  218. begin
  219. if FEditWidth <> Value then begin
  220. for I := 0 to ClientCount - 1 do
  221. if TBasicActionLink(Clients[I]) is TTBEditItemActionLink then
  222. TTBEditItemActionLink(Clients[I]).SetEditWidth(Value);
  223. FEditWidth := Value;
  224. Change;
  225. end;
  226. end;
  227. procedure TTBEditAction.SetOnAcceptText(Value: TTBAcceptTextEvent);
  228. var
  229. I: Integer;
  230. begin
  231. if not MethodsEqual(TMethod(FOnAcceptText), TMethod(Value)) then begin
  232. for I := 0 to ClientCount - 1 do
  233. if TBasicActionLink(Clients[I]) is TTBEditItemActionLink then
  234. TTBEditItemActionLink(Clients[I]).SetOnAcceptText(Value);
  235. FOnAcceptText := Value;
  236. Change;
  237. end;
  238. end;
  239. procedure TTBEditAction.SetText(Value: String);
  240. var
  241. I: Integer;
  242. begin
  243. if FText <> Value then begin
  244. for I := 0 to ClientCount - 1 do
  245. if TBasicActionLink(Clients[I]) is TTBEditItemActionLink then
  246. TTBEditItemActionLink(Clients[I]).SetText(Value);
  247. FText := Value;
  248. Change;
  249. end;
  250. end;
  251. { TTBEditItemActionLink }
  252. procedure TTBEditItemActionLink.AssignClient(AClient: TObject);
  253. begin
  254. FClient := AClient as TTBEditItem;
  255. end;
  256. function TTBEditItemActionLink.IsEditCaptionLinked: Boolean;
  257. begin
  258. if Action is TTBEditAction then
  259. Result := TTBEditItem(FClient).EditCaption = TTBEditAction(Action).EditCaption
  260. else
  261. Result := False;
  262. end;
  263. function TTBEditItemActionLink.IsEditOptionsLinked: Boolean;
  264. begin
  265. if Action is TTBEditAction then
  266. Result := TTBEditItem(FClient).EditOptions = TTBEditAction(Action).EditOptions
  267. else
  268. Result := False;
  269. end;
  270. function TTBEditItemActionLink.IsEditWidthLinked: Boolean;
  271. begin
  272. if Action is TTBEditAction then
  273. Result := TTBEditItem(FClient).EditWidth = TTBEditAction(Action).EditWidth
  274. else
  275. Result := False;
  276. end;
  277. function TTBEditItemActionLink.IsOnAcceptTextLinked: Boolean;
  278. begin
  279. if Action is TTBEditAction then
  280. Result := MethodsEqual(TMethod(TTBEditItem(FClient).OnAcceptText),
  281. TMethod(TTBEditAction(Action).OnAcceptText))
  282. else
  283. Result := False;
  284. end;
  285. function TTBEditItemActionLink.IsTextLinked: Boolean;
  286. begin
  287. if Action is TTBEditAction then
  288. Result := TTBEditItem(FClient).Text = TTBEditAction(Action).Text
  289. else
  290. Result := False;
  291. end;
  292. procedure TTBEditItemActionLink.SetEditCaption(const Value: String);
  293. begin
  294. if IsEditCaptionLinked then TTBEditItem(FClient).EditCaption := Value;
  295. end;
  296. procedure TTBEditItemActionLink.SetEditOptions(Value: TTBEditItemOptions);
  297. begin
  298. if IsEditOptionsLinked then TTBEditItem(FClient).EditOptions := Value;
  299. end;
  300. procedure TTBEditItemActionLink.SetEditWidth(const Value: Integer);
  301. begin
  302. if IsEditWidthLinked then TTBEditItem(FClient).EditWidth := Value;
  303. end;
  304. procedure TTBEditItemActionLink.SetOnAcceptText(Value: TTBAcceptTextEvent);
  305. begin
  306. if IsOnAcceptTextLinked then TTBEditItem(FClient).OnAcceptText := Value;
  307. end;
  308. procedure TTBEditItemActionLink.SetText(const Value: String);
  309. begin
  310. if IsTextLinked then TTBEditItem(FClient).SetTextEx(Value , tcrActionLink);
  311. end;
  312. { TTBEditItem }
  313. constructor TTBEditItem.Create(AOwner: TComponent);
  314. begin
  315. inherited;
  316. FEditOptions := EditItemDefaultEditOptions;
  317. FEditWidth := EditItemDefaultEditWidth;
  318. end;
  319. procedure TTBEditItem.ActionChange(Sender: TObject; CheckDefaults: Boolean);
  320. begin
  321. inherited;
  322. if Action is TTBEditAction then
  323. with TTBEditAction(Sender) do
  324. begin
  325. if not CheckDefaults or (Self.EditCaption = '') then
  326. Self.EditCaption := EditCaption;
  327. if not CheckDefaults or (Self.EditOptions = []) then
  328. Self.EditOptions := EditOptions;
  329. if not CheckDefaults or (Self.Text = '') then
  330. Self.SetTextEx(Text, tcrActionLink);
  331. if not CheckDefaults or not Assigned(Self.OnAcceptText) then
  332. Self.OnAcceptText := OnAcceptText;
  333. end;
  334. end;
  335. function TTBEditItem.GetActionLinkClass: TTBCustomItemActionLinkClass;
  336. begin
  337. Result := TTBEditItemActionLink;
  338. end;
  339. function TTBEditItem.GetItemViewerClass(AView: TTBView): TTBItemViewerClass;
  340. begin
  341. if not(tboUseEditWhenVertical in EditOptions) and
  342. (AView.Orientation = tbvoVertical) then
  343. Result := inherited GetItemViewerClass(AView)
  344. else
  345. Result := TTBEditItemViewer;
  346. end;
  347. function TTBEditItem.NeedToRecreateViewer(AViewer: TTBItemViewer): Boolean;
  348. begin
  349. Result := GetItemViewerClass(AViewer.View) <> AViewer.ClassType;
  350. end;
  351. procedure TTBEditItem.Clear;
  352. begin
  353. Text := '';
  354. end;
  355. procedure TTBEditItem.Click;
  356. begin
  357. inherited;
  358. end;
  359. procedure TTBEditItem.DoBeginEdit(Viewer: TTBEditItemViewer);
  360. begin
  361. if Assigned(FOnBeginEdit) then
  362. FOnBeginEdit(Self, Viewer, Viewer.EditControl);
  363. end;
  364. function TTBEditItem.IsEditOptionsStored: Boolean;
  365. begin
  366. Result := (EditOptions <> EditItemDefaultEditOptions) and
  367. ((ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or
  368. not TTBEditItemActionLink(ActionLink).IsEditOptionsLinked);
  369. end;
  370. function TTBEditItem.IsEditCaptionStored: Boolean;
  371. begin
  372. Result := (ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or
  373. not TTBEditItemActionLink(ActionLink).IsEditCaptionLinked;
  374. end;
  375. function TTBEditItem.IsEditWidthStored: Boolean;
  376. begin
  377. Result := (EditWidth <> EditItemDefaultEditWidth) and
  378. ((ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or
  379. not TTBEditItemActionLink(ActionLink).IsEditWidthLinked);
  380. end;
  381. function TTBEditItem.IsTextStored: Boolean;
  382. begin
  383. Result := (ActionLink = nil) or not(ActionLink is TTBEditItemActionLink) or
  384. not TTBEditItemActionLink(ActionLink).IsTextLinked;
  385. end;
  386. procedure TTBEditItem.SetCharCase(Value: TEditCharCase);
  387. begin
  388. if FCharCase <> Value then begin
  389. FCharCase := Value;
  390. Text := Text; { update case }
  391. end;
  392. end;
  393. procedure TTBEditItem.SetEditOptions(Value: TTBEditItemOptions);
  394. begin
  395. if FEditOptions <> Value then begin
  396. FEditOptions := Value;
  397. Change(True);
  398. end;
  399. end;
  400. procedure TTBEditItem.SetEditCaption(Value: String);
  401. begin
  402. if FEditCaption <> Value then begin
  403. FEditCaption := Value;
  404. Change(True);
  405. end;
  406. end;
  407. procedure TTBEditItem.SetEditWidth(Value: Integer);
  408. begin
  409. if FEditWidth <> Value then begin
  410. FEditWidth := Value;
  411. Change(True);
  412. end;
  413. end;
  414. procedure TTBEditItem.SetMaxLength(Value: Integer);
  415. begin
  416. if FMaxLength <> Value then begin
  417. FMaxLength := Value;
  418. Change(False);
  419. end;
  420. end;
  421. function TTBEditItem.DoAcceptText(var NewText: string): Boolean;
  422. begin
  423. Result := True;
  424. if Assigned(FOnAcceptText) then FOnAcceptText(Self, NewText, Result);
  425. end;
  426. procedure TTBEditItem.DoTextChanging(const OldText: String; var NewText: String; Reason: Integer);
  427. begin
  428. case FCharCase of
  429. ecUpperCase: NewText := AnsiUpperCase(NewText);
  430. ecLowerCase: NewText := AnsiLowerCase(NewText);
  431. end;
  432. end;
  433. procedure TTBEditItem.DoTextChanged(Reason: Integer);
  434. begin
  435. end;
  436. procedure TTBEditItem.SetText(Value: String);
  437. begin
  438. DoTextChanging(FText, Value, tcrSetProperty);
  439. if FText <> Value then begin
  440. FText := Value;
  441. Change(False);
  442. DoTextChanged(tcrSetProperty);
  443. end;
  444. end;
  445. procedure TTBEditItem.SetTextEx(Value: String; Reason: Integer);
  446. begin
  447. DoTextChanging(FText, Value, Reason);
  448. if FText <> Value then begin
  449. FText := Value;
  450. Change(False);
  451. DoTextChanged(Reason);
  452. end;
  453. end;
  454. procedure TTBEditItem.ChangeScale(M, D: Integer);
  455. begin
  456. inherited;
  457. EditWidth := MulDiv(EditWidth, M, D);
  458. end;
  459. { TTBEditItemViewer }
  460. procedure TTBEditItemViewer.EditWndProc(var Message: TMessage);
  461. var
  462. Item: TTBEditItem;
  463. procedure AcceptText;
  464. var
  465. S: String;
  466. begin
  467. S := FEditControl.Text;
  468. if Item.DoAcceptText(S) then Item.SetTextEx(S, tcrEditControl);
  469. end;
  470. begin
  471. Item := TTBEditItem(Self.Item);
  472. if FEditControl = nil then
  473. Exit;
  474. if Message.Msg = WM_CHAR then
  475. case TWMChar(Message).CharCode of
  476. VK_TAB: begin
  477. FEditControlStatus := [ecsAccept];
  478. AcceptText;
  479. Exit;
  480. end;
  481. VK_RETURN: begin
  482. FEditControlStatus := [ecsAccept, ecsClose];
  483. AcceptText;
  484. Exit;
  485. end;
  486. VK_ESCAPE: begin
  487. FEditControlStatus := [];
  488. Exit;
  489. end;
  490. end;
  491. TEditAccess(FEditControl).WndProc(Message);
  492. if Message.Msg = WM_KILLFOCUS then begin
  493. { Someone has stolen the focus from us, so 'cancel mode'. (We have to
  494. handle WM_KILLFOCUS in addition to the upstream WM_CANCELMODE handling
  495. since we don't always hold the mouse capture.) }
  496. View.CancelMode;
  497. FEditControlStatus := [ecsClose];
  498. end;
  499. end;
  500. function TTBEditItemViewer.GetEditControlClass: TEditClass;
  501. begin
  502. Result := TEdit;
  503. end;
  504. procedure TTBEditItemViewer.GetEditRect(var R: TRect);
  505. var
  506. Item: TTBEditItem;
  507. DC: HDC;
  508. begin
  509. Item := TTBEditItem(Self.Item);
  510. DC := GetDC(0);
  511. try
  512. SelectObject(DC, View.GetFont.Handle);
  513. R := BoundsRect;
  514. if not View.IsToolbar and (Item.EditCaption <> '') then begin
  515. Inc(R.Left, GetTextWidth(DC, Item.EditCaption, True) +
  516. EditMenuMidWidth + EditMenuTextMargin * 2);
  517. end;
  518. finally
  519. ReleaseDC(0, DC);
  520. end;
  521. end;
  522. procedure TTBEditItemViewer.CalcSize(const Canvas: TCanvas;
  523. var AWidth, AHeight: Integer);
  524. var
  525. Item: TTBEditItem;
  526. DC: HDC;
  527. TextHeight, MinHeight: Integer;
  528. begin
  529. Item := TTBEditItem(Self.Item);
  530. DC := Canvas.Handle;
  531. TextHeight := GetTextHeight(DC);
  532. AWidth := Item.FEditWidth;
  533. AHeight := TextHeight;
  534. if not IsToolbarStyle and (Item.EditCaption <> '') then begin
  535. Inc(AWidth, GetTextWidth(DC, Item.EditCaption, True) + EditMenuMidWidth +
  536. EditMenuTextMargin * 2);
  537. end;
  538. MinHeight := AHeight + (EditMenuTextMargin * 2) + 1;
  539. if not IsToolbarStyle then
  540. Inc(AHeight, DivRoundUp(AHeight, 4));
  541. if AHeight < MinHeight then
  542. AHeight := MinHeight;
  543. end;
  544. function TTBEditItemViewer.CaptionShown: Boolean;
  545. begin
  546. Result := not IsToolbarStyle and inherited CaptionShown;
  547. end;
  548. function TTBEditItemViewer.GetCaptionText: String;
  549. begin
  550. Result := TTBEditItem(Item).EditCaption;
  551. end;
  552. procedure TTBEditItemViewer.Paint(const Canvas: TCanvas;
  553. const ClientAreaRect: TRect; IsSelected, IsPushed, UseDisabledShadow: Boolean);
  554. const
  555. FillColors: array[Boolean] of TColor = (clBtnFace, clWindow);
  556. TextColors: array[Boolean] of TColor = (clGrayText, clWindowText);
  557. var
  558. Item: TTBEditItem;
  559. S: String;
  560. R: TRect;
  561. W: Integer;
  562. begin
  563. Item := TTBEditItem(Self.Item);
  564. R := ClientAreaRect;
  565. { Caption }
  566. if not IsToolbarStyle and (Item.EditCaption <> '') then begin
  567. S := Item.EditCaption;
  568. W := GetTextWidth(Canvas.Handle, S, True) + EditMenuTextMargin * 2;
  569. R.Right := R.Left + W;
  570. if IsSelected then
  571. Canvas.FillRect(R);
  572. Inc(R.Left, EditMenuTextMargin);
  573. DrawItemCaption(Canvas, R, S, UseDisabledShadow, DT_SINGLELINE or
  574. DT_LEFT or DT_VCENTER);
  575. R := ClientAreaRect;
  576. Inc(R.Left, W + EditMenuMidWidth);
  577. end;
  578. { Border }
  579. if IsSelected and Item.Enabled then
  580. DrawEdge(Canvas.Handle, R, BDR_SUNKENOUTER, BF_RECT);
  581. InflateRect(R, -1, -1);
  582. Canvas.Brush.Color := FillColors[not Item.Enabled];
  583. Canvas.FrameRect(R);
  584. InflateRect(R, -1, -1);
  585. { Fill }
  586. Canvas.Brush.Color := FillColors[Item.Enabled];
  587. Canvas.FillRect(R);
  588. InflateRect(R, -1, -1);
  589. { Text }
  590. if Item.Text <> '' then begin
  591. S := Item.Text;
  592. Canvas.Brush.Style := bsClear; { speed optimization }
  593. Canvas.Font.Color := TextColors[Item.Enabled];
  594. DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_SINGLELINE or DT_NOPREFIX);
  595. end;
  596. end;
  597. procedure TTBEditItemViewer.GetCursor(const Pt: TPoint; var ACursor: HCURSOR);
  598. var
  599. R: TRect;
  600. begin
  601. if not Item.Enabled then
  602. Exit;
  603. GetEditRect(R);
  604. OffsetRect(R, -BoundsRect.Left, -BoundsRect.Top);
  605. InflateRect(R, -2, -2);
  606. if PtInRect(R, Pt) then
  607. ACursor := LoadCursor(0, IDC_IBEAM);
  608. end;
  609. function TTBEditItemViewer.EditLoop(const CapHandle: HWND): Boolean;
  610. procedure ControlMessageLoop;
  611. function PointInWindow(const Wnd: HWND; const P: TPoint): Boolean;
  612. var
  613. W: HWND;
  614. begin
  615. Result := False;
  616. W := WindowFromPoint(P);
  617. if W = 0 then Exit;
  618. if W = Wnd then
  619. Result := True
  620. else
  621. if IsChild(Wnd, W) then
  622. Result := True;
  623. end;
  624. function ContinueLoop: Boolean;
  625. begin
  626. Result := (ecsContinueLoop in FEditControlStatus) and
  627. not View.IsModalEnding and FEditControl.Focused and Item.Enabled;
  628. { Note: View.IsModalEnding is checked since TTBView.CancelMode doesn't
  629. destroy popup windows; it merely hides them and calls EndModal. So if
  630. IsModalEnding returns True we can infer that CancelMode was likely
  631. called. }
  632. end;
  633. var
  634. Msg: TMsg;
  635. IsKeypadDigit: Boolean;
  636. V: Integer;
  637. begin
  638. try
  639. while ContinueLoop do begin
  640. { Examine the next message before popping it out of the queue }
  641. if not PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin
  642. WaitMessage;
  643. Continue;
  644. end;
  645. case Msg.message of
  646. WM_SYSKEYDOWN: begin
  647. { Exit immediately if Alt+[key] or F10 are pressed, but not
  648. Alt+Shift, Alt+`, or Alt+[keypad digit] }
  649. if (Msg.wParam <> VK_MENU) and (Msg.wParam <> VK_SHIFT) and
  650. (Msg.wParam <> VK_HANJA) then begin
  651. IsKeypadDigit := False;
  652. { This detect digits regardless of whether Num Lock is on: }
  653. if Lo(LongRec(Msg.lParam).Hi) <> 0 then
  654. for V := VK_NUMPAD0 to VK_NUMPAD9 do
  655. if MapVirtualKey(V, 0) = Lo(LongRec(Msg.lParam).Hi) then begin
  656. IsKeypadDigit := True;
  657. Break;
  658. end;
  659. if not IsKeypadDigit then begin
  660. FEditControlStatus := [ecsClose];
  661. Exit;
  662. end;
  663. end;
  664. end;
  665. WM_SYSKEYUP: begin
  666. { Exit when Alt is released by itself }
  667. if Msg.wParam = VK_MENU then begin
  668. FEditControlStatus := [ecsClose];
  669. Exit;
  670. end;
  671. end;
  672. WM_LBUTTONDOWN, WM_LBUTTONDBLCLK,
  673. WM_RBUTTONDOWN, WM_RBUTTONDBLCLK,
  674. WM_MBUTTONDOWN, WM_MBUTTONDBLCLK,
  675. WM_NCLBUTTONDOWN, WM_NCLBUTTONDBLCLK,
  676. WM_NCRBUTTONDOWN, WM_NCRBUTTONDBLCLK,
  677. WM_NCMBUTTONDOWN, WM_NCMBUTTONDBLCLK: begin
  678. { If a mouse click outside the edit control is in the queue,
  679. exit and let the upstream message loop deal with it }
  680. if Msg.hwnd <> FEditControl.Handle then
  681. Exit;
  682. end;
  683. WM_MOUSEMOVE, WM_NCMOUSEMOVE: begin
  684. if GetCapture = CapHandle then begin
  685. if PointInWindow(FEditControl.Handle, Msg.pt) then
  686. ReleaseCapture;
  687. end
  688. else if GetCapture = 0 then begin
  689. if not PointInWindow(FEditControl.Handle, Msg.pt) then
  690. SetCapture(CapHandle);
  691. end;
  692. if GetCapture = CapHandle then
  693. SetCursor(LoadCursor(0, IDC_ARROW));
  694. end;
  695. end;
  696. { Now pop the message out of the queue }
  697. if not PeekMessage(Msg, 0, Msg.message, Msg.message, PM_REMOVE or PM_NOYIELD) then
  698. Continue;
  699. if ((Msg.message >= WM_MOUSEFIRST) and (Msg.message <= WM_MOUSELAST)) and
  700. (Msg.hwnd = CapHandle) then
  701. { discard, so that the selection doesn't get changed }
  702. else begin
  703. TranslateMessage(Msg);
  704. DispatchMessage(Msg);
  705. end;
  706. end;
  707. finally
  708. { Make sure there are no outstanding WM_*CHAR messages }
  709. RemoveMessages(WM_CHAR, WM_DEADCHAR);
  710. RemoveMessages(WM_SYSCHAR, WM_SYSDEADCHAR);
  711. end;
  712. end;
  713. var
  714. Item: TTBEditItem;
  715. R: TRect;
  716. ActiveWnd, FocusWnd: HWND;
  717. S: string;
  718. begin
  719. Item := TTBEditItem(Self.Item);
  720. GetEditRect(R);
  721. if IsRectEmpty(R) then begin
  722. Result := False;
  723. Exit;
  724. end;
  725. ActiveWnd := GetActiveWindow;
  726. FocusWnd := GetFocus;
  727. { Create the edit control }
  728. InflateRect(R, -3, -4);
  729. //View.FreeNotification (Self);
  730. FEditControl := GetEditControlClass.Create(nil);
  731. try
  732. FEditControl.Name := Format('%s_edit_control_%p', [ClassName,
  733. Pointer(FEditControl)]);
  734. FEditControl.Visible := False;
  735. FEditControl.BorderStyle := bsNone;
  736. FEditControl.AutoSize := False;
  737. FEditControl.Font.Assign(View.GetFont);
  738. FEditControl.Text := Item.Text;
  739. FEditControl.CharCase := Item.FCharCase;
  740. FEditControl.MaxLength := Item.FMaxLength;
  741. FEditControl.BoundsRect := R;
  742. FEditControl.WindowProc := EditWndProc;
  743. FEditControl.ParentWindow := View.Window.Handle;
  744. FEditControl.SelectAll;
  745. Item.DoBeginEdit(Self);
  746. FEditControl.Visible := True;
  747. FEditControl.SetFocus;
  748. if GetActiveWindow <> ActiveWnd then
  749. { don't gray out title bar of old active window }
  750. SendMessage(ActiveWnd, WM_NCACTIVATE, 1, 0)
  751. else
  752. ActiveWnd := 0;
  753. FEditControlStatus := [ecsContinueLoop];
  754. // During modal state of the toolbar, Windows logo key is not working.
  755. // It should be fixed more generically, but here we fix it at least for the most obvious case (= while in edit box)
  756. TTBModalHandler.UnlockForegroundWindow;
  757. ControlMessageLoop;
  758. finally
  759. TTBModalHandler.LockForegroundWindow;
  760. S := FEditControl.Text;
  761. FreeAndNil(FEditControl);
  762. end;
  763. with TTBEditItem(Item) do
  764. if (FEditControlStatus = [ecsContinueLoop]) and ExtendedAccept then
  765. if DoAcceptText(S) then SetTextEx(S, tcrEditControl);
  766. { ensure the area underneath the edit control is repainted immediately }
  767. View.Window.Update;
  768. { If app is still active, set focus to previous control and restore capture
  769. to CapHandle if another control hasn't taken it }
  770. if GetActiveWindow <> 0 then begin
  771. SetFocus(FocusWnd);
  772. if GetCapture = 0 then
  773. SetCapture(CapHandle);
  774. end;
  775. if ActiveWnd <> 0 then
  776. SendMessage(ActiveWnd, WM_NCACTIVATE, Ord(GetActiveWindow = ActiveWnd), 0);
  777. { The SetFocus call above can change the Z order of windows. If the parent
  778. window is a popup window, reassert its topmostness. }
  779. if View.Window is TTBPopupWindow then
  780. SetWindowPos(View.Window.Handle, HWND_TOPMOST, 0, 0, 0, 0,
  781. SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
  782. { Send an MSAA "focus" event now that we're returning to the regular modal loop }
  783. View.NotifyFocusEvent;
  784. Result := ecsClose in FEditControlStatus;
  785. if not Result and (GetCapture = CapHandle) then begin
  786. if ecsAccept in FEditControlStatus then
  787. { if we are accepting but not closing, Tab must have been pressed }
  788. View.Selected := View.NextSelectable(View.Selected,
  789. GetKeyState(VK_SHIFT) >= 0);
  790. end;
  791. end;
  792. function TTBEditItemViewer.DoExecute: Boolean;
  793. begin
  794. { Close any delay-close popup menus before entering the edit loop }
  795. View.CancelChildPopups;
  796. Result := False;
  797. if EditLoop(View.GetCaptureWnd) then begin
  798. View.EndModal;
  799. if ecsAccept in FEditControlStatus then
  800. Result := True;
  801. end;
  802. end;
  803. procedure TTBEditItemViewer.MouseBeginEdit;
  804. begin
  805. if Item.Enabled then
  806. Execute(True)
  807. else begin
  808. if (View.ParentView = nil) and not View.IsPopup then
  809. View.EndModal;
  810. end;
  811. end;
  812. procedure TTBEditItemViewer.MouseDown(Shift: TShiftState; X, Y: Integer;
  813. var MouseDownOnMenu: Boolean);
  814. begin
  815. if IsPtInButtonPart(X, Y) then { for TBX... }
  816. MouseBeginEdit
  817. else
  818. inherited;
  819. end;
  820. procedure TTBEditItemViewer.MouseUp(X, Y: Integer; MouseWasDownOnMenu: Boolean);
  821. begin
  822. if IsPtInButtonPart(X, Y) then { for TBX... }
  823. MouseBeginEdit
  824. else
  825. inherited;
  826. end;
  827. function TTBEditItemViewer.UsesSameWidth: Boolean;
  828. begin
  829. Result := False;
  830. end;
  831. function TTBEditItemViewer.GetAccRole: Integer;
  832. const
  833. ROLE_SYSTEM_TEXT = $2a; { from OleAcc.h }
  834. begin
  835. Result := ROLE_SYSTEM_TEXT;
  836. end;
  837. function TTBEditItemViewer.GetAccValue(var Value: WideString): Boolean;
  838. begin
  839. Value := TTBEditItem(Item).Text;
  840. Result := True;
  841. end;
  842. end.