CnAOTreeView.pas 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnAOTreeView;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:不可视工具组件包
  24. * 单元名称:自动参数设置 TreeView 组件单元
  25. * 单元作者:周劲羽 (zjy@cnpack.org)
  26. * 开发平台:PWin2000 SP4 + Delphi 5.01
  27. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7
  28. * 本 地 化:该单元中的字符串均符合本地化处理方式
  29. * 单元标识:$Id$
  30. * 备 注:该单元定义了自动参数设置 TreeView 组件
  31. * 该组件用于在运行时使用树状结构根据设置信息对象显示通用的设置界面。
  32. ================================================================================
  33. |</PRE>}
  34. interface
  35. {$I CnPack.inc}
  36. uses
  37. Windows, Messages, SysUtils, Classes, Graphics, Controls, ComCtrls, TypInfo,
  38. {$IFDEF COMPILER6_UP} Variants, {$ENDIF COMPILER6_UP}
  39. StdCtrls, ImgList, CnSpin, Dialogs, Menus, Math, Forms, CnGraphConsts,
  40. CnAutoOption;
  41. type
  42. { TCnAOTreeView }
  43. EUnsupportedPropKind = class(Exception);
  44. TCnOptionKind = (
  45. okUnknown, okGroup, okCustom, okBoolFalse, okBoolTrue, okString,
  46. okStringCombo, okInteger, okIntegerCombo, okFloat, okDateTime,
  47. okDate, okTime, okEnum, okSet, okVariant, okFont, okColor, okShortCut,
  48. okStrings);
  49. TCreateInplaceEditEvent = procedure(Sender: TObject; InplaceEdit: TControl;
  50. AOption: TCnBaseOption) of object;
  51. TGetItemTextEvent = procedure(Sender: TObject; AOption: TCnOptionItem;
  52. var AText: string) of object;
  53. TCnAOTreeView = class(TCustomTreeView)
  54. private
  55. FImageList: TImageList;
  56. FInplaceEdit: TControl;
  57. FModified: Boolean;
  58. FOptions: TCnOptionGroup;
  59. FOnCreateInplaceEdit: TCreateInplaceEditEvent;
  60. FOnGetItemText: TGetItemTextEvent;
  61. procedure ApplyInplaceEdit;
  62. procedure ComboBoxDropDown(Sender: TObject);
  63. procedure CreateInplaceEdit;
  64. procedure FreeInplaceEdit;
  65. procedure OnColorClick(Sender: TObject);
  66. procedure OnFontClick(Sender: TObject);
  67. procedure OnInplaceEditEnterExit(Sender: TObject);
  68. procedure SetNodeImageIndex(Node: TTreeNode; Index: Integer);
  69. procedure UpdateInplaceEdit;
  70. procedure SetOptions(Value: TCnOptionGroup);
  71. protected
  72. function CanEdit(Node: TTreeNode): Boolean; override;
  73. procedure Change(Node: TTreeNode); override;
  74. procedure Click; override;
  75. function DoClickNode(Node: TTreeNode): Boolean;
  76. procedure DoCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode;
  77. State: TCustomDrawState; var DefaultDraw: Boolean);
  78. procedure DoEnter; override;
  79. procedure DoExit; override;
  80. function GetOptionKind(Option: TCnBaseOption; RaiseError: Boolean = False):
  81. TCnOptionKind;
  82. procedure GetSelectedIndex(Node: TTreeNode); override;
  83. procedure UpdateNode(Node: TTreeNode);
  84. procedure WMChar(var Message: TWMChar); message WM_CHAR;
  85. procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
  86. procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
  87. procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  88. property ImageList: TImageList read FImageList;
  89. public
  90. constructor Create(AOwner: TComponent); override;
  91. {* 类构造器 }
  92. destructor Destroy; override;
  93. {* 类析构器 }
  94. procedure ApplyOption;
  95. {* 应用当前的设置到对象属性 }
  96. procedure DefaultOption;
  97. {* 恢复设置为原对象属性的默认值 }
  98. procedure ResetOption;
  99. {* 恢复设置为原对象属性的当前值 }
  100. procedure UpdateTreeView;
  101. {* 更新设置树 }
  102. property Modified: Boolean read FModified;
  103. {* 标识是否有属性被修改 }
  104. property Options: TCnOptionGroup read FOptions write SetOptions;
  105. {* 用于设置的参数组对象 }
  106. published
  107. property Align;
  108. property Anchors;
  109. property AutoExpand;
  110. property BiDiMode;
  111. property BorderStyle;
  112. property BorderWidth;
  113. property ChangeDelay;
  114. property Color;
  115. property Constraints;
  116. property Ctl3D;
  117. property DragCursor;
  118. property DragKind;
  119. property DragMode;
  120. property Enabled;
  121. property Font;
  122. property HideSelection;
  123. property HotTrack;
  124. property Indent;
  125. property OnChange;
  126. property OnChanging;
  127. property OnClick;
  128. property OnCollapsed;
  129. property OnCollapsing;
  130. property OnContextPopup;
  131. property OnDblClick;
  132. property OnDeletion;
  133. property OnDragDrop;
  134. property OnDragOver;
  135. property OnEndDock;
  136. property OnEndDrag;
  137. property OnEnter;
  138. property OnExit;
  139. property OnExpanded;
  140. property OnExpanding;
  141. property OnGetSelectedIndex;
  142. property OnKeyDown;
  143. property OnKeyPress;
  144. property OnKeyUp;
  145. property OnMouseDown;
  146. property OnMouseMove;
  147. property OnMouseUp;
  148. property OnStartDock;
  149. property OnStartDrag;
  150. property ParentBiDiMode;
  151. property ParentColor default False;
  152. property ParentCtl3D;
  153. property ParentFont;
  154. property ParentShowHint;
  155. property PopupMenu;
  156. property RightClickSelect;
  157. property RowSelect;
  158. property ShowButtons default False;
  159. property ShowHint;
  160. property ShowLines default False;
  161. property ShowRoot;
  162. property TabOrder;
  163. property TabStop default True;
  164. property ToolTips;
  165. property Visible;
  166. property OnCreateInplaceEdit: TCreateInplaceEditEvent
  167. read FOnCreateInplaceEdit write FOnCreateInplaceEdit;
  168. property OnGetItemText: TGetItemTextEvent read FOnGetItemText write FOnGetItemText;
  169. end;
  170. implementation
  171. {$IFDEF DEBUG}
  172. uses
  173. CnDebug;
  174. {$ENDIF}
  175. {$R *.res}
  176. const
  177. csIdxGroup = 0;
  178. csIdxCustom = 1;
  179. csIdxUnChecked = 2;
  180. csIdxChecked = 3;
  181. csIdxUnSelected = 4;
  182. csIdxSelected = 5;
  183. csIdxString = 6;
  184. csIdxStringCombo = 7;
  185. csIdxInteger = 8;
  186. csIdxIntegerCombo = 9;
  187. csIdxFloat = 10;
  188. csIdxEnum = 11;
  189. csIdxSet = 12;
  190. csIdxVariant = 13;
  191. csIdxFont = 14;
  192. csIdxDateTime = 15;
  193. csIdxDate = 16;
  194. csIdxTime = 17;
  195. csIdxColor = 18;
  196. csIdxShortCut = 19;
  197. csIdxStrings = 20;
  198. csSepStr = ': ';
  199. csInplaceEditHeight = 20;
  200. csInplaceEditWidth = 120;
  201. csInplaceButtonHeight = 20;
  202. csInplaceButtonWidth = 60;
  203. csInplaceMemoHeight = 60;
  204. csInplaceMemoWidth = 120;
  205. csInplaceSpace = 4;
  206. csMaxStrLength = 15;
  207. csImageIndexs: array[TCnOptionKind] of Integer = (
  208. -1, csIdxGroup, csIdxCustom, csIdxUnChecked, csIdxChecked, csIdxString,
  209. csIdxStringCombo, csIdxInteger, csIdxIntegerCombo, csIdxFloat, csIdxDateTime,
  210. csIdxDate, csIdxTime, csIdxEnum, csIdxSet, csIdxVariant, csIdxFont, csIdxColor,
  211. csIdxShortCut, csIdxStrings);
  212. type
  213. TWinControlHack = class(TWinControl);
  214. constructor TCnAOTreeView.Create(AOwner: TComponent);
  215. var
  216. Bitmap: TBitmap;
  217. begin
  218. inherited;
  219. FImageList := TImageList.Create(Self);
  220. Images := FImageList;
  221. Bitmap := TBitmap.Create;
  222. try
  223. Bitmap.LoadFromResourceName(HInstance, 'CNAOTREEVIEW');
  224. FImageList.AddMasked(Bitmap, Bitmap.TransparentColor);
  225. finally
  226. Bitmap.Free;
  227. end;
  228. ShowLines := False;
  229. ShowButtons := False;
  230. ReadOnly := True;
  231. OnCustomDrawItem := DoCustomDrawItem;
  232. end;
  233. destructor TCnAOTreeView.Destroy;
  234. begin
  235. FImageList.Free;
  236. inherited;
  237. end;
  238. procedure TCnAOTreeView.ApplyInplaceEdit;
  239. var
  240. Node: TTreeNode;
  241. Item: TCnOptionItem;
  242. Obj: TObject;
  243. // 将字符串转换为匹配的格式
  244. function GetString(PropKind: TTypeKind; const Value: string): Variant;
  245. var
  246. C: Char;
  247. WC: WideChar;
  248. WS: WideString;
  249. begin
  250. case PropKind of
  251. tkChar:
  252. begin
  253. if Value <> '' then
  254. C := Value[1]
  255. else
  256. C := #0;
  257. Result := C;
  258. end;
  259. tkWChar:
  260. begin
  261. WS := Value;
  262. if WS <> '' then
  263. WC := WS[1]
  264. else
  265. WC := #0;
  266. Result := WC;
  267. end;
  268. tkWString {$IFDEF UNICODE_STRING}, tkUString{$ENDIF}:
  269. begin
  270. WS := Value;
  271. Result := WS;
  272. end;
  273. else
  274. Result := Value;
  275. end;
  276. end;
  277. begin
  278. if FInplaceEdit = nil then Exit;
  279. Node := TTreeNode(FInplaceEdit.Tag);
  280. Item := TCnOptionItem(Node.Data);
  281. if FInplaceEdit is TDateTimePicker then
  282. begin
  283. Item.Value := TDateTimePicker(FInplaceEdit).DateTime;
  284. FModified := True;
  285. end
  286. else if FInplaceEdit is TCnSpinEdit then
  287. begin
  288. with TCnSpinEdit(FInplaceEdit) do
  289. begin
  290. if (MaxValue > MinValue) and ((MaxValue <> 0) or (MinValue <> 0)) then
  291. begin
  292. if Value > MaxValue then
  293. Value := MaxValue;
  294. if Value < MinValue then
  295. Value := MinValue;
  296. end;
  297. Item.Value := Value;
  298. end;
  299. FModified := True;
  300. end
  301. else if FInplaceEdit is TComboBox then
  302. begin
  303. case GetOptionKind(Item) of
  304. okIntegerCombo:
  305. Item.Value := TComboBox(FInplaceEdit).ItemIndex;
  306. okStringCombo:
  307. Item.Value := GetString(Item.PropKind, TComboBox(FInplaceEdit).Text);
  308. else
  309. Assert(False);
  310. end;
  311. FModified := True;
  312. end
  313. else if FInplaceEdit is THotKey then
  314. begin
  315. Item.Value := THotKey(FInplaceEdit).HotKey;
  316. FModified := True;
  317. end
  318. else if FInplaceEdit is TEdit then
  319. begin
  320. try
  321. case GetOptionKind(Item) of
  322. okFloat:
  323. Item.Value := StrToFloat(TEdit(FInplaceEdit).Text);
  324. okDateTime:
  325. Item.Value := StrToDateTime(TEdit(FInplaceEdit).Text);
  326. okString:
  327. Item.Value := GetString(Item.PropKind, TEdit(FInplaceEdit).Text);
  328. okVariant:
  329. Item.Value := TEdit(FInplaceEdit).Text;
  330. else
  331. Assert(False);
  332. end;
  333. FModified := True;
  334. except
  335. ;
  336. end;
  337. end
  338. else if FInplaceEdit is TMemo then
  339. begin
  340. {$IFDEF WIN64}
  341. Obj := TObject(Integer(Item.Value));
  342. {$ELSE}
  343. Integer(Obj) := Item.Value;
  344. {$ENDIF}
  345. if Obj is TStrings then
  346. TStrings(Obj).Text := TMemo(FInplaceEdit).Lines.Text;
  347. FModified := True;
  348. end
  349. else if FInplaceEdit is TButton then
  350. begin
  351. // None
  352. end
  353. else
  354. begin
  355. // None
  356. end;
  357. UpdateNode(Node);
  358. Repaint;
  359. end;
  360. procedure TCnAOTreeView.ApplyOption;
  361. procedure DoApplyOption(Option: TCnBaseOption);
  362. var
  363. i: Integer;
  364. begin
  365. if Option is TCnOptionItem then
  366. TCnOptionItem(Option).ApplyOption
  367. else if Option is TCnOptionGroup then
  368. for i := 0 to TCnOptionGroup(Option).Count - 1 do
  369. DoApplyOption(TCnOptionGroup(Option)[i]);
  370. end;
  371. begin
  372. ApplyInplaceEdit;
  373. DoApplyOption(FOptions);
  374. end;
  375. function TCnAOTreeView.CanEdit(Node: TTreeNode): Boolean;
  376. begin
  377. Result := False;
  378. end;
  379. procedure TCnAOTreeView.Change(Node: TTreeNode);
  380. begin
  381. inherited;
  382. CreateInplaceEdit;
  383. end;
  384. procedure TCnAOTreeView.Click;
  385. var
  386. P: TPoint;
  387. Node: TTreeNode;
  388. begin
  389. inherited;
  390. GetCursorPos(P);
  391. P := ScreenToClient(P);
  392. Node := GetNodeAt(P.X, P.Y);
  393. if Node <> nil then
  394. DoClickNode(Node);
  395. end;
  396. procedure TCnAOTreeView.ComboBoxDropDown(Sender: TObject);
  397. var
  398. i: Integer;
  399. MaxWidth: Integer;
  400. Bitmap: Graphics.TBitmap;
  401. ComboBox: TComboBox;
  402. begin
  403. if not (Sender is TComboBox) then
  404. Exit;
  405. ComboBox := TComboBox(Sender);
  406. MaxWidth := ComboBox.Width;
  407. Bitmap := Graphics.TBitmap.Create;
  408. try
  409. Bitmap.Canvas.Font.Assign(ComboBox.Font);
  410. for i := 0 to ComboBox.Items.Count - 1 do
  411. MaxWidth := Max(MaxWidth, Bitmap.Canvas.TextWidth(ComboBox.Items[i]) + 10);
  412. finally;
  413. Bitmap.Free;
  414. end;
  415. if ComboBox.Items.Count > ComboBox.DropDownCount then
  416. Inc(MaxWidth, GetSystemMetrics(SM_CXVSCROLL));
  417. MaxWidth := Min(400, MaxWidth);
  418. if MaxWidth > ComboBox.Width then
  419. SendMessage(ComboBox.Handle, CB_SETDROPPEDWIDTH, MaxWidth, 0)
  420. else
  421. SendMessage(ComboBox.Handle, CB_SETDROPPEDWIDTH, 0, 0)
  422. end;
  423. procedure TCnAOTreeView.CreateInplaceEdit;
  424. var
  425. Option: TCnBaseOption;
  426. Item: TCnOptionItem;
  427. procedure CreateSpin(Value, MinValue, MaxValue: Integer);
  428. begin
  429. FInplaceEdit := TCnSpinEdit.Create(Self);
  430. FInplaceEdit.Height := csInplaceEditHeight;
  431. FInplaceEdit.Width := csInplaceEditWidth;
  432. TCnSpinEdit(FInplaceEdit).Value := Value;
  433. TCnSpinEdit(FInplaceEdit).MinValue := MinValue;
  434. TCnSpinEdit(FInplaceEdit).MaxValue := MaxValue;
  435. UpdateInplaceEdit;
  436. end;
  437. procedure CreateDateTimePicker(Kind: TDateTimeKind; Value: TDateTime);
  438. begin
  439. FInplaceEdit := TDateTimePicker.Create(Self);
  440. FInplaceEdit.Height := csInplaceEditHeight;
  441. FInplaceEdit.Width := csInplaceEditWidth;
  442. TDateTimePicker(FInplaceEdit).Kind := Kind;
  443. TDateTimePicker(FInplaceEdit).DateTime := Value;
  444. UpdateInplaceEdit;
  445. end;
  446. procedure CreateEdit(const Value: string);
  447. begin
  448. FInplaceEdit := TEdit.Create(Self);
  449. FInplaceEdit.Height := csInplaceEditHeight;
  450. FInplaceEdit.Width := csInplaceEditWidth;
  451. UpdateInplaceEdit;
  452. TEdit(FInplaceEdit).Text := Value;
  453. end;
  454. procedure CreateComboBox(const Value: string; List: TStrings; DropDownList: Boolean);
  455. begin
  456. FInplaceEdit := TComboBox.Create(Self);
  457. FInplaceEdit.Height := csInplaceEditHeight;
  458. FInplaceEdit.Width := csInplaceEditWidth;
  459. UpdateInplaceEdit;
  460. TComboBox(FInplaceEdit).Items.Assign(List);
  461. TComboBox(FInplaceEdit).OnDropDown := ComboBoxDropDown;
  462. if DropDownList then
  463. begin
  464. TComboBox(FInplaceEdit).Style := csDropDownList;
  465. TComboBox(FInplaceEdit).ItemIndex := List.IndexOf(Value);
  466. end
  467. else
  468. begin
  469. TComboBox(FInplaceEdit).Style := csDropDown;
  470. TComboBox(FInplaceEdit).Text := Value;
  471. end;
  472. end;
  473. procedure CreateButton(Caption: string; OnClick: TNotifyEvent);
  474. begin
  475. FInplaceEdit := TButton.Create(Self);
  476. FInplaceEdit.Height := csInplaceButtonHeight;
  477. FInplaceEdit.Width := csInplaceButtonWidth;
  478. UpdateInplaceEdit;
  479. if Caption = '' then
  480. Caption := SCnAOCaptionOption;
  481. TButton(FInplaceEdit).Caption := Caption;
  482. TButton(FInplaceEdit).OnClick := OnClick;
  483. end;
  484. procedure CreateHotKey(ShortCut: TShortCut);
  485. begin
  486. FInplaceEdit := THotKey.Create(Self);
  487. FInplaceEdit.Height := csInplaceEditHeight;
  488. FInplaceEdit.Width := csInplaceEditWidth;
  489. UpdateInplaceEdit;
  490. THotKey(FInplaceEdit).HotKey := ShortCut;
  491. end;
  492. procedure CreateMemo(Value: Variant);
  493. var
  494. Obj: TPersistent;
  495. begin
  496. {$IFDEF WIN64}
  497. Obj := TPersistent(Integer(Value));
  498. {$ELSE}
  499. Integer(Obj) := Value;
  500. {$ENDIF}
  501. Assert(Obj is TStrings);
  502. FInplaceEdit := TMemo.Create(Self);
  503. FInplaceEdit.Width := csInplaceMemoWidth;
  504. FInplaceEdit.Height := csInplaceMemoHeight;
  505. UpdateInplaceEdit;
  506. TMemo(FInplaceEdit).Lines.Assign(Obj);
  507. end;
  508. begin
  509. FreeInplaceEdit;
  510. if (Selected = nil) or (Selected.Data = nil) then Exit;
  511. Option := TCnBaseOption(Selected.Data);
  512. Item := TCnOptionItem(Selected.Data);
  513. case GetOptionKind(Option) of
  514. okCustom:
  515. CreateButton(TCnOptionCustom(Option).Caption, TCnOptionCustom(Option).OnClick);
  516. okString:
  517. CreateEdit(Item.Value);
  518. okStringCombo:
  519. CreateComboBox(Item.Value, Item.List, False);
  520. okInteger:
  521. CreateSpin(Item.Value, Item.MinValue, Item.MaxValue);
  522. okIntegerCombo:
  523. CreateComboBox(Item.List[Item.Value], Item.List, True);
  524. okFloat:
  525. CreateEdit(FloatToStr(Item.Value));
  526. okDateTime:
  527. CreateEdit(DateTimeToStr(TDateTime(Item.Value)));
  528. okDate:
  529. CreateDateTimePicker(dtkDate, TDate(Item.Value));
  530. okTime:
  531. CreateDateTimePicker(dtkTime, TTime(Item.Value));
  532. okVariant:
  533. CreateEdit(VarToStr(Item.Value));
  534. okFont:
  535. CreateButton(SCnAOCaptionFont, OnFontClick);
  536. okColor:
  537. CreateButton(SCnAOCaptionColor, OnColorClick);
  538. okShortCut:
  539. CreateHotKey(Item.Value);
  540. okStrings:
  541. CreateMemo(Item.Value);
  542. else
  543. Exit;
  544. end;
  545. if Assigned(FOnCreateInplaceEdit) then
  546. FOnCreateInplaceEdit(Self, FInplaceEdit, Option);
  547. end;
  548. procedure TCnAOTreeView.DefaultOption;
  549. procedure DoDefaultOption(Option: TCnBaseOption);
  550. var
  551. i: Integer;
  552. begin
  553. if Option is TCnOptionItem then
  554. TCnOptionItem(Option).DefaultOption
  555. else if Option is TCnOptionGroup then
  556. for i := 0 to TCnOptionGroup(Option).Count - 1 do
  557. DoDefaultOption(TCnOptionGroup(Option)[i]);
  558. end;
  559. begin
  560. DoDefaultOption(FOptions);
  561. UpdateTreeView;
  562. end;
  563. function TCnAOTreeView.DoClickNode(Node: TTreeNode): Boolean;
  564. var
  565. i, Min, Max: Integer;
  566. Item: TCnOptionItem;
  567. BoolValue: Boolean;
  568. EnumInfo: PTypeInfo;
  569. SetValue: TIntegerSet;
  570. begin
  571. Result := False;
  572. if Node = nil then Exit;
  573. if Node.Data = nil then // 是集合或枚举子项
  574. begin
  575. Item := TCnOptionItem(Node.Parent.Data);
  576. Assert(Item is TCnOptionItem);
  577. Assert(GetOptionKind(Item) in [okEnum, okSet]);
  578. case GetOptionKind(Item) of
  579. okEnum:
  580. begin
  581. Item.Value := Node.Index + Item.MinValue;
  582. for i := 0 to Node.Parent.Count - 1 do
  583. if i = Node.Index then
  584. SetNodeImageIndex(Node.Parent.Item[i], csIdxSelected)
  585. else
  586. SetNodeImageIndex(Node.Parent.Item[i], csIdxUnSelected)
  587. end;
  588. okSet:
  589. begin
  590. EnumInfo := GetTypeData(Item.PropInfo^.PropType^)^.CompType^;
  591. if Node.ImageIndex = csIdxChecked then
  592. SetNodeImageIndex(Node, csIdxUnChecked)
  593. else
  594. SetNodeImageIndex(Node, csIdxChecked);
  595. SetValue := [];
  596. Min := GetTypeData(EnumInfo).MinValue;
  597. Max := GetTypeData(EnumInfo).MaxValue;
  598. for i := Min to Max do
  599. if Node.Parent.Item[i].ImageIndex = csIdxChecked then
  600. Include(SetValue, i + Min);
  601. Item.Value := Integer(SetValue);
  602. end;
  603. end;
  604. FModified := True;
  605. Result := True;
  606. end
  607. else if GetOptionKind(TCnBaseOption(Node.Data)) in [okBoolFalse, okBoolTrue] then
  608. begin
  609. Item := TCnOptionItem(Node.Data);
  610. BoolValue := not Item.Value;
  611. Item.Value := BoolValue;
  612. if BoolValue then
  613. SetNodeImageIndex(Node, csIdxChecked)
  614. else
  615. SetNodeImageIndex(Node, csIdxUnChecked);
  616. FModified := True;
  617. Result := True;
  618. end;
  619. end;
  620. procedure TCnAOTreeView.DoCustomDrawItem(Sender: TCustomTreeView;
  621. Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
  622. begin
  623. // Todo: 自绘制颜色属性及字体等
  624. DefaultDraw := True;
  625. end;
  626. procedure TCnAOTreeView.DoEnter;
  627. begin
  628. inherited;
  629. Change(Selected);
  630. end;
  631. procedure TCnAOTreeView.DoExit;
  632. begin
  633. inherited;
  634. FreeInplaceEdit;
  635. end;
  636. procedure TCnAOTreeView.FreeInplaceEdit;
  637. begin
  638. if FInplaceEdit <> nil then
  639. begin
  640. ApplyInplaceEdit;
  641. FreeAndNil(FInplaceEdit);
  642. end;
  643. end;
  644. function TCnAOTreeView.GetOptionKind(Option: TCnBaseOption; RaiseError: Boolean
  645. = False): TCnOptionKind;
  646. var
  647. Item: TCnOptionItem;
  648. BoolValue: Boolean;
  649. Obj: TObject;
  650. begin
  651. Result := okUnknown;
  652. if Option is TCnOptionCustom then
  653. Result := okCustom
  654. else if Option is TCnOptionGroup then
  655. Result := okGroup
  656. else if Option is TCnOptionItem then
  657. begin
  658. Item := TCnOptionItem(Option);
  659. case Item.PropKind of
  660. tkInteger, tkInt64:
  661. begin
  662. if Item.PropInfo^.PropType^ = TypeInfo(TColor) then
  663. Result := okColor
  664. else if Item.PropInfo^.PropType^ = TypeInfo(TShortCut) then
  665. Result := okShortCut
  666. else if (Item.List.Count > 0) and (Item.Value >= 0) and
  667. (Item.Value < Item.List.Count) then
  668. Result := okIntegerCombo
  669. else
  670. Result := okInteger;
  671. end;
  672. tkFloat:
  673. begin
  674. if Item.PropInfo^.PropType^ = TypeInfo(TDateTime) then
  675. Result := okDateTime
  676. else if Item.PropInfo^.PropType^ = TypeInfo(TDate) then
  677. Result := okDate
  678. else if Item.PropInfo^.PropType^ = TypeInfo(TTime) then
  679. Result := okTime
  680. else
  681. Result := okFloat;
  682. end;
  683. tkChar, tkString, tkWChar, tkLString, tkWString{$IFDEF UNICODE_STRING}, tkUString{$ENDIF}:
  684. begin
  685. if Item.List.Count > 0 then
  686. Result := okStringCombo
  687. else
  688. Result := okString
  689. end;
  690. tkEnumeration:
  691. begin
  692. if IsBooleanType(Item.PropInfo^.PropType^) or
  693. IsBoolType(Item.PropInfo^.PropType^) then
  694. begin
  695. BoolValue := Item.Value;
  696. if BoolValue then
  697. Result := okBoolTrue
  698. else
  699. Result := okBoolFalse;
  700. end
  701. else
  702. Result := okEnum;
  703. end;
  704. tkSet:
  705. begin
  706. Result := okSet;
  707. end;
  708. tkClass:
  709. begin
  710. {$IFDEF WIN64}
  711. Obj := TObject(Integer(Item.Value));
  712. {$ELSE}
  713. Integer(Obj) := Item.Value;
  714. {$ENDIF}
  715. if Obj is TFont then
  716. Result := okFont
  717. else if Obj is TStrings then
  718. Result := okStrings
  719. else if RaiseError then
  720. raise EUnsupportedPropKind.Create('Unsupported Property Kind: ' +
  721. Obj.ClassName);
  722. end;
  723. tkVariant:
  724. begin
  725. Result := okVariant;
  726. end;
  727. end;
  728. end;
  729. if (Result = okUnknown) and RaiseError then
  730. if Option is TCnOptionItem then
  731. raise EUnsupportedPropKind.Create('Unsupported Property Kind: ' +
  732. GetEnumName(TypeInfo(TTypeKind), Ord(TCnOptionItem(Option).PropKind)))
  733. else
  734. raise EUnsupportedPropKind.Create('Unsupported Property Define: ' + Option.Text);
  735. end;
  736. procedure TCnAOTreeView.GetSelectedIndex(Node: TTreeNode);
  737. begin
  738. Node.SelectedIndex := Node.ImageIndex;
  739. end;
  740. procedure TCnAOTreeView.OnColorClick(Sender: TObject);
  741. var
  742. Node: TTreeNode;
  743. Item: TCnOptionItem;
  744. begin
  745. if FInplaceEdit = nil then Exit;
  746. Node := TTreeNode(FInplaceEdit.Tag);
  747. Item := TCnOptionItem(Node.Data);
  748. with TColorDialog.Create(Self) do
  749. try
  750. Color := ColorToRGB(Item.Value);
  751. if Execute then
  752. Item.Value := Color;
  753. finally
  754. Free;
  755. end;
  756. end;
  757. procedure TCnAOTreeView.OnFontClick(Sender: TObject);
  758. var
  759. Node: TTreeNode;
  760. Item: TCnOptionItem;
  761. Obj: TFont;
  762. begin
  763. if FInplaceEdit = nil then Exit;
  764. Node := TTreeNode(FInplaceEdit.Tag);
  765. Item := TCnOptionItem(Node.Data);
  766. {$IFDEF WIN64}
  767. Obj := TFont(Integer(Item.Value));
  768. {$ELSE}
  769. Integer(Obj) := Item.Value;
  770. {$ENDIF}
  771. with TFontDialog.Create(Self) do
  772. try
  773. Font.Assign(Obj);
  774. if Execute then
  775. Obj.Assign(Font);
  776. finally
  777. Free;
  778. end;
  779. end;
  780. procedure TCnAOTreeView.OnInplaceEditEnterExit(Sender: TObject);
  781. var
  782. i: Integer;
  783. begin
  784. if FInplaceEdit <> nil then
  785. begin
  786. FInplaceEdit.Invalidate;
  787. if FInplaceEdit is TWinControl then
  788. for i := 0 to TWinControl(FInplaceEdit).ControlCount - 1 do
  789. TWinControl(FInplaceEdit).Controls[i].Invalidate;
  790. end;
  791. end;
  792. procedure TCnAOTreeView.ResetOption;
  793. procedure DoResetOption(Option: TCnBaseOption);
  794. var
  795. i: Integer;
  796. begin
  797. if Option is TCnOptionItem then
  798. TCnOptionItem(Option).ResetOption
  799. else if Option is TCnOptionGroup then
  800. for i := 0 to TCnOptionGroup(Option).Count - 1 do
  801. try
  802. DoResetOption(TCnOptionGroup(Option)[i]);
  803. except
  804. Application.HandleException(nil);
  805. end;
  806. end;
  807. begin
  808. DoResetOption(FOptions);
  809. UpdateTreeView;
  810. end;
  811. procedure TCnAOTreeView.SetNodeImageIndex(Node: TTreeNode; Index: Integer);
  812. begin
  813. Node.ImageIndex := Index;
  814. Node.SelectedIndex := Index;
  815. end;
  816. procedure TCnAOTreeView.UpdateInplaceEdit;
  817. var
  818. R1, R2: TRect;
  819. begin
  820. if (Selected <> nil) and (TopItem <> nil) and (FInplaceEdit <> nil) then
  821. begin
  822. FInplaceEdit.Tag := Integer(Selected);
  823. FInplaceEdit.Parent := Self;
  824. if FInplaceEdit is TWinControl then
  825. begin
  826. TWinControlHack(FInplaceEdit).OnEnter := OnInplaceEditEnterExit;
  827. TWinControlHack(FInplaceEdit).OnExit := OnInplaceEditEnterExit;
  828. end;
  829. Selected.Text := TCnBaseOption(Selected.Data).Text + csSepStr;
  830. R1 := Selected.DisplayRect(True);
  831. R2 := TopItem.DisplayRect(True);
  832. FInplaceEdit.Top := R1.Top - R2.Top;
  833. FInplaceEdit.Left := R1.Right + csInplaceSpace;
  834. FInplaceEdit.Invalidate;
  835. //Invalidate;
  836. // Todo: 滚动时刷新有时不正常
  837. end;
  838. end;
  839. procedure TCnAOTreeView.UpdateNode(Node: TTreeNode);
  840. var
  841. Idx: Integer;
  842. Item: TCnOptionItem;
  843. OptionKind: TCnOptionKind;
  844. OrdValue: Integer;
  845. EnumInfo: PTypeInfo;
  846. function FontToStr(Value: Variant): string;
  847. var
  848. Obj: TObject;
  849. begin
  850. {$IFDEF WIN64}
  851. Obj := TObject(Integer(Value));
  852. {$ELSE}
  853. Integer(Obj) := Value;
  854. {$ENDIF}
  855. if Obj is TFont then
  856. Result := Format('%s,%d', [TFont(Obj).Name, TFont(Obj).Size])
  857. else
  858. Result := '';
  859. end;
  860. function StringsToStr(Value: Variant): string;
  861. var
  862. Obj: TObject;
  863. begin
  864. {$IFDEF WIN64}
  865. Obj := TObject(Integer(Value));
  866. {$ELSE}
  867. Integer(Obj) := Value;
  868. {$ENDIF}
  869. if Obj is TStrings then
  870. Result := StringReplace(TStrings(Obj).Text, #13#10, ' ', [rfReplaceAll])
  871. else
  872. Result := '';
  873. if Length(Result) > csMaxStrLength - 3 then
  874. Result := Copy(Result, 1, csMaxStrLength - 3) + '...';
  875. end;
  876. procedure SetNodeText(AItem: TCnOptionItem; Text: string);
  877. begin
  878. if Assigned(FOnGetItemText) then
  879. FOnGetItemText(Self, AItem, Text);
  880. Node.Text := AItem.Text + csSepStr + Text;
  881. end;
  882. begin
  883. Assert(Node <> nil);
  884. if Node.Data = nil then // 是集合或枚举子项
  885. begin
  886. Item := TCnOptionItem(Node.Parent.Data);
  887. Assert(GetOptionKind(Item) in [okEnum, okSet]);
  888. case GetOptionKind(Item) of
  889. okEnum:
  890. begin
  891. OrdValue := Item.Value;
  892. Idx := Node.Index;
  893. if Idx < Item.List.Count then
  894. Node.Text := Item.List[Idx - Item.MinValue]
  895. else
  896. Node.Text := GetEnumName(Item.PropInfo^.PropType^, Idx);
  897. if Idx = OrdValue then
  898. SetNodeImageIndex(Node, csIdxSelected)
  899. else
  900. SetNodeImageIndex(Node, csIdxUnSelected);
  901. end;
  902. okSet:
  903. begin
  904. OrdValue := Item.Value;
  905. EnumInfo := GetTypeData(Item.PropInfo^.PropType^)^.CompType^;
  906. Idx := Node.Index;
  907. if Idx < Item.List.Count then
  908. Node.Text := Item.List[Idx - GetTypeData(EnumInfo).MinValue]
  909. else
  910. Node.Text := GetEnumName(EnumInfo, Idx);
  911. if Idx in TIntegerSet(OrdValue) then
  912. SetNodeImageIndex(Node, csIdxChecked)
  913. else
  914. SetNodeImageIndex(Node, csIdxUnChecked);
  915. end;
  916. end;
  917. end
  918. else
  919. begin
  920. Item := TCnOptionItem(Node.Data);
  921. OptionKind := GetOptionKind(TCnBaseOption(Node.Data));
  922. SetNodeImageIndex(Node, csImageIndexs[OptionKind]);
  923. case OptionKind of
  924. okString, okStringCombo:
  925. SetNodeText(Item, Item.Value);
  926. okInteger:
  927. SetNodeText(Item, IntToStr(Item.Value));
  928. okIntegerCombo:
  929. SetNodeText(Item, Item.List[Item.Value]);
  930. okFloat:
  931. SetNodeText(Item, FloatToStr(Item.Value));
  932. okDateTime:
  933. SetNodeText(Item, DateTimeToStr(Item.Value));
  934. okDate:
  935. SetNodeText(Item, DateToStr(Item.Value));
  936. okTime:
  937. SetNodeText(Item, TimeToStr(Item.Value));
  938. okVariant:
  939. SetNodeText(Item, VarToStr(Item.Value));
  940. okFont:
  941. SetNodeText(Item, FontToStr(Item.Value));
  942. okColor:
  943. SetNodeText(Item, IntToHex(ColorToRGB(Item.Value), 8));
  944. okShortCut:
  945. SetNodeText(Item, ShortCutToText(Item.Value));
  946. okStrings:
  947. SetNodeText(Item, StringsToStr(Item.Value));
  948. else
  949. Node.Text := TCnBaseOption(Node.Data).Text;
  950. end;
  951. end;
  952. end;
  953. procedure TCnAOTreeView.UpdateTreeView;
  954. function AddNode(ParentNode: TTreeNode; AOption: TCnBaseOption): TTreeNode;
  955. var
  956. i: Integer;
  957. EnumInfo: PTypeInfo;
  958. begin
  959. Result := Items.AddChildObject(ParentNode, AOption.Text, AOption);
  960. try
  961. UpdateNode(Result);
  962. case GetOptionKind(AOption, True) of
  963. okGroup:
  964. begin
  965. with TCnOptionGroup(AOption) do
  966. for i := 0 to Count - 1 do
  967. AddNode(Result, Items[i]);
  968. end;
  969. okEnum:
  970. with TCnOptionItem(AOption) do
  971. begin
  972. for i := MinValue to MaxValue do
  973. begin
  974. UpdateNode(Items.AddChildObject(Result, '', nil));
  975. end;
  976. end;
  977. okSet:
  978. with TCnOptionItem(AOption) do
  979. begin
  980. EnumInfo := GetTypeData(PropInfo^.PropType^)^.CompType^;
  981. for i := GetTypeData(EnumInfo).MinValue to GetTypeData(EnumInfo).MaxValue do
  982. UpdateNode(Items.AddChildObject(Result, '', nil));
  983. end;
  984. end;
  985. except
  986. Result.Free;
  987. Application.HandleException(Self);
  988. end;
  989. end;
  990. begin
  991. Items.BeginUpdate;
  992. try
  993. Items.Clear;
  994. AddNode(nil, Options);
  995. Selected := Items.GetFirstNode;
  996. FullExpand;
  997. TopItem := Selected;
  998. FModified := False;
  999. finally
  1000. Items.EndUpdate;
  1001. end;
  1002. end;
  1003. procedure TCnAOTreeView.SetOptions(Value: TCnOptionGroup);
  1004. begin
  1005. if FOptions <> Value then
  1006. begin
  1007. FOptions := Value;
  1008. ResetOption;
  1009. end;
  1010. end;
  1011. procedure TCnAOTreeView.WMChar(var Message: TWMChar);
  1012. begin
  1013. if (Char(Message.CharCode) <> ' ') or not DoClickNode(Selected) then
  1014. inherited;
  1015. end;
  1016. procedure TCnAOTreeView.WMHScroll(var Message: TWMHScroll);
  1017. begin
  1018. inherited;
  1019. UpdateInplaceEdit;
  1020. end;
  1021. procedure TCnAOTreeView.WMMouseWheel(var Message: TWMMouseWheel);
  1022. begin
  1023. inherited;
  1024. UpdateInplaceEdit;
  1025. end;
  1026. procedure TCnAOTreeView.WMVScroll(var Message: TWMVScroll);
  1027. begin
  1028. inherited;
  1029. UpdateInplaceEdit;
  1030. end;
  1031. end.