Watch.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. unit Watch;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5. Dialogs, Grids, ValEdit, ComCtrls, Menus, StdCtrls, ExtCtrls, SynEdit,
  6. JvComponent, JvDockControlForm, JvDragDrop, VirtualTrees, ActiveX,
  7. ImgList, ToolWin;
  8. const
  9. // Helper message to decouple node change handling from edit handling.
  10. WM_STARTEDITING = WM_USER + 778;
  11. type
  12. PWatchNodeData = ^TWatchNodeData;
  13. TWatchNodeData = record
  14. Value: String;
  15. Name: String;
  16. NestedTableCount: Integer;
  17. ToKeep: Boolean;
  18. end;
  19. // Our own edit link to implement several different node editors.
  20. TEditLinker = class(TInterfacedObject, IVTEditLink)
  21. private
  22. FTree: TVirtualStringTree; // A back reference to the tree calling.
  23. FNode: PVirtualNode; // The node being edited.
  24. FColumn: Integer; // The column of the node being edited.
  25. public
  26. constructor Create;
  27. destructor Destroy; override;
  28. function BeginEdit: Boolean; stdcall;
  29. function CancelEdit: Boolean; stdcall;
  30. function EndEdit: Boolean; stdcall;
  31. function GetBounds: TRect; stdcall;
  32. function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
  33. procedure SetBounds(R: TRect); stdcall;
  34. procedure ProcessMessage(var Message: TMessage); stdcall;
  35. end;
  36. TfrmWatch = class(TForm)
  37. ppmWatch: TPopupMenu;
  38. memoSwap: TMemo;
  39. JvDockClient1: TJvDockClient;
  40. vstWatch: TVirtualStringTree;
  41. tblWatch: TToolBar;
  42. tbtnAddWatch: TToolButton;
  43. ToolButton1: TToolButton;
  44. tbtnRefreshWatch: TToolButton;
  45. FEdit: TEdit;
  46. tbtnDelete: TToolButton;
  47. DeleteSelectedItem1: TMenuItem;
  48. N1: TMenuItem;
  49. Refresh1: TMenuItem;
  50. AddWatch1: TMenuItem;
  51. N2: TMenuItem;
  52. ExpandAll1: TMenuItem;
  53. CollapseAll1: TMenuItem;
  54. Expand1: TMenuItem;
  55. Collapse1: TMenuItem;
  56. N3: TMenuItem;
  57. procedure vstWatchGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  58. procedure vstWatchEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
  59. procedure vstWatchCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
  60. procedure vstWatchGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  61. procedure vstWatchAfterItemPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  62. procedure vstWatchDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
  63. procedure vstWatchDragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
  64. procedure tbtnRefreshWatchClick(Sender: TObject);
  65. procedure tbtnAddWatchClick(Sender: TObject);
  66. procedure vstWatchEdited(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
  67. procedure vstWatchChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
  68. procedure FEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  69. procedure vstWatchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  70. procedure DeleteSelected();
  71. procedure tbtnDeleteClick(Sender: TObject);
  72. procedure FormCreate(Sender: TObject);
  73. procedure AddWatch1Click(Sender: TObject);
  74. procedure Refresh1Click(Sender: TObject);
  75. procedure DeleteSelectedItem1Click(Sender: TObject);
  76. procedure Expand1Click(Sender: TObject);
  77. procedure Collapse1Click(Sender: TObject);
  78. procedure ExpandAll1Click(Sender: TObject);
  79. procedure CollapseAll1Click(Sender: TObject);
  80. procedure vstWatchMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  81. procedure vstWatchDblClick(Sender: TObject);
  82. private
  83. { Private declarations }
  84. procedure WMStartEditing(var Message: TMessage); message WM_STARTEDITING;
  85. public
  86. { Public declarations }
  87. end;
  88. var
  89. frmWatch: TfrmWatch;
  90. implementation
  91. uses Main, Types;
  92. {$R *.dfm}
  93. ////////////////////////////////////// TEditLinker implementation //////////////////////////////////////
  94. constructor TEditLinker.Create;
  95. begin
  96. FTree := nil;
  97. FNode := nil;
  98. FColumn := 0;
  99. end;
  100. destructor TEditLinker.Destroy;
  101. begin
  102. // nothing for now...
  103. inherited;
  104. end;
  105. function TEditLinker.BeginEdit: Boolean;
  106. begin
  107. Result := True;
  108. frmWatch.FEdit.Show;
  109. frmWatch.FEdit.SetFocus;
  110. end;
  111. function TEditLinker.CancelEdit: Boolean;
  112. begin
  113. Result := True;
  114. frmWatch.FEdit.Hide;
  115. end;
  116. function TEditLinker.EndEdit: Boolean;
  117. var
  118. Data: PWatchNodeData;
  119. S: String;
  120. begin
  121. Result := True;
  122. Data := FTree.GetNodeData(FNode);
  123. S := frmWatch.FEdit.Text;
  124. if S <> Data.Name then
  125. begin
  126. Data.Name := S;
  127. FTree.InvalidateNode(FNode);
  128. end;
  129. frmWatch.FEdit.Hide;
  130. frmWatch.tbtnDelete.Enabled := True;
  131. frmWatch.DeleteSelectedItem1.Enabled := True;
  132. FTree.SetFocus;
  133. end;
  134. procedure TEditLinker.SetBounds(R: TRect);
  135. var
  136. Dummy: Integer;
  137. begin
  138. // Since we don't want to activate grid extensions in the tree (this would influence how the selection is drawn)
  139. // we have to set the edit's width explicitly to the width of the column.
  140. FTree.Header.Columns.GetColumnBounds(FColumn, Dummy, R.Right);
  141. frmWatch.FEdit.BoundsRect := R;
  142. end;
  143. function TEditLinker.GetBounds: TRect;
  144. begin
  145. Result := frmWatch.FEdit.BoundsRect;
  146. end;
  147. function TEditLinker.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
  148. var
  149. Data: PWatchNodeData;
  150. begin
  151. Result := True;
  152. FTree := Tree as TVirtualStringTree;
  153. FNode := Node;
  154. FColumn := Column;
  155. Data := FTree.GetNodeData(Node);
  156. frmWatch.tbtnDelete.Enabled := False;
  157. frmWatch.DeleteSelectedItem1.Enabled := False;
  158. with frmWatch.FEdit do
  159. begin
  160. Visible := False;
  161. Parent := FTree;
  162. AutoSize := False;
  163. MaxLength := 1000;
  164. Ctl3D := False;
  165. Text := Data.Name;
  166. end;
  167. end;
  168. procedure TEditLinker.ProcessMessage(var Message: TMessage);
  169. begin
  170. frmWatch.FEdit.WindowProc(Message);
  171. end;
  172. ////////////////////////////////////// TfrmWatch implementation //////////////////////////////////////
  173. procedure TfrmWatch.vstWatchCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink);
  174. begin
  175. // Create the editor class wich will interact with the user when editing a variable name
  176. EditLink := TEditLinker.Create;
  177. end;
  178. // This is called whenever the tree needs to get the text for the current cell
  179. procedure TfrmWatch.vstWatchGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  180. var
  181. pData: PWatchNodeData;
  182. begin
  183. if TextType = ttNormal then
  184. begin
  185. case Column of
  186. 0:
  187. begin
  188. pData := Sender.GetNodeData(Node);
  189. CellText := pData.Name;
  190. end;
  191. 1:
  192. begin
  193. pData := Sender.GetNodeData(Node);
  194. CellText := pData.Value;
  195. end;
  196. end;
  197. end;
  198. end;
  199. procedure TfrmWatch.vstWatchEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean);
  200. begin
  201. Allowed := ((Column = 0) and (Node.Parent = Sender.RootNode));
  202. end;
  203. procedure TfrmWatch.vstWatchGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
  204. begin
  205. NodeDataSize := SizeOf(TWatchNodeData);
  206. end;
  207. procedure TfrmWatch.vstWatchAfterItemPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; ItemRect: TRect);
  208. var
  209. pRect: TRect;
  210. begin
  211. pRect := ItemRect;
  212. pRect.Bottom := pRect.Bottom - 1;
  213. pRect.Right := pRect.Left + 22;
  214. TargetCanvas.Brush.Color := clWhite;
  215. TargetCanvas.FillRect(pRect);
  216. // Draw node button since the noda has some child
  217. if ((Node.Parent = Sender.RootNode) and (Node.ChildCount <> 0)) then
  218. begin
  219. // Draw the frame around the button
  220. TargetCanvas.Pen.Color := clBtnShadow;
  221. TargetCanvas.Rectangle(5, 4, 14, 13);
  222. TargetCanvas.MoveTo(14, 8);
  223. TargetCanvas.LineTo(20, 8);
  224. TargetCanvas.Pen.Color := clBlack;
  225. if not (vsExpanded in Node.States) then
  226. begin
  227. // Draw expandable node button (plus sign)
  228. TargetCanvas.MoveTo(7, 8);
  229. TargetCanvas.LineTo(12, 8);
  230. TargetCanvas.MoveTo(9, 6);
  231. TargetCanvas.LineTo(9, 11);
  232. end
  233. else
  234. begin
  235. // Draw non-expandable node button (minus sign)
  236. TargetCanvas.MoveTo(7, 8);
  237. TargetCanvas.LineTo(12, 8);
  238. end;
  239. end;
  240. end;
  241. procedure TfrmWatch.vstWatchDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode);
  242. var
  243. pNode: PVirtualNode;
  244. pData: PWatchNodeData;
  245. begin
  246. // Only works over the list view lvwWatch
  247. if ((Sender = vstWatch) and (TSynEdit(Source).SelText <> ''))then
  248. begin
  249. pNode := Sender.AddChild(Sender.RootNode);
  250. pData := Sender.GetNodeData(pNode);
  251. pData.Name := TSynEdit(Source).SelText;
  252. frmLuaEditMain.PrintWatch(frmLuaEditMain.LuaState);
  253. end;
  254. end;
  255. procedure TfrmWatch.vstWatchDragOver(Sender: TBaseVirtualTree; Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode; var Effect: Integer; var Accept: Boolean);
  256. begin
  257. Accept := (Source is TSynEdit);
  258. end;
  259. procedure TfrmWatch.tbtnRefreshWatchClick(Sender: TObject);
  260. begin
  261. frmLuaEditMain.PrintWatch(frmLuaEditMain.LuaState);
  262. end;
  263. procedure TfrmWatch.tbtnAddWatchClick(Sender: TObject);
  264. begin
  265. frmLuaEditMain.DoAddWatchExecute;
  266. end;
  267. procedure TfrmWatch.vstWatchEdited(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex);
  268. begin
  269. frmLuaEditMain.PrintWatch(frmLuaEditMain.LuaState);
  270. end;
  271. procedure TfrmWatch.WMStartEditing(var Message: TMessage);
  272. // This message was posted by ourselves from the node change handler above to decouple that change event and our
  273. // intention to start editing a node. This is necessary to avoid interferences between nodes editors potentially created
  274. // for an old edit action and the new one we start here.
  275. var
  276. Node: PVirtualNode;
  277. begin
  278. Node := Pointer(Message.WParam);
  279. // Note: the test whether a node can really be edited is done in the OnEditing event.
  280. vstWatch.EditNode(Node, 1);
  281. end;
  282. procedure TfrmWatch.vstWatchChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
  283. begin
  284. with Sender do
  285. begin
  286. if Assigned(Node) then
  287. begin
  288. tbtnDelete.Enabled := (Node.Parent = RootNode);
  289. DeleteSelectedItem1.Enabled := (Node.Parent = RootNode);
  290. Expand1.Enabled := (Node.ChildCount > 0);
  291. ExpandAll1.Enabled := (Node.ChildCount > 0);
  292. Collapse1.Enabled := (Node.ChildCount > 0);
  293. CollapseAll1.Enabled := (Node.ChildCount > 0);
  294. end
  295. else
  296. begin
  297. tbtnDelete.Enabled := False;
  298. DeleteSelectedItem1.Enabled := False;
  299. Expand1.Enabled := False;
  300. ExpandAll1.Enabled := False;
  301. Collapse1.Enabled := False;
  302. CollapseAll1.Enabled := False;
  303. end;
  304. // Start immediate editing as soon as another node gets focused.
  305. if Assigned(Node) and (Node.Parent <> RootNode) then
  306. begin
  307. // We want to start editing the currently selected node. However it might well happen that this change event
  308. // here is caused by the node editor if another node is currently being edited. It causes trouble
  309. // to start a new edit operation if the last one is still in progress. So we post us a special message and
  310. // in the message handler we then can start editing the new node. This works because the posted message
  311. // is first executed *after* this event and the message, which triggered it is finished.
  312. PostMessage(Self.Handle, WM_STARTEDITING, Integer(Node), 0);
  313. end;
  314. end;
  315. end;
  316. procedure TfrmWatch.FEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  317. var
  318. CanAdvance: Boolean;
  319. begin
  320. CanAdvance := True;
  321. case Key of
  322. VK_ESCAPE:
  323. if CanAdvance then
  324. begin
  325. vstWatch.CancelEditNode;
  326. Key := 0;
  327. end;
  328. VK_RETURN:
  329. if CanAdvance then
  330. begin
  331. vstWatch.EndEditNode;
  332. Key := 0;
  333. end;
  334. VK_UP,
  335. VK_DOWN:
  336. begin
  337. // Consider special cases before finishing edit mode.
  338. CanAdvance := Shift = [];
  339. if CanAdvance then
  340. begin
  341. // Forward the keypress to the tree. It will asynchronously change the focused node.
  342. PostMessage(vstWatch.Handle, WM_KEYDOWN, Key, 0);
  343. Key := 0;
  344. end;
  345. end;
  346. end;
  347. end;
  348. procedure TfrmWatch.vstWatchKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  349. begin
  350. if not vstWatch.IsEditing then
  351. begin
  352. // Seek delete key for node deletion
  353. if Key = VK_DELETE then
  354. DeleteSelected;
  355. end;
  356. end;
  357. procedure TfrmWatch.DeleteSelected();
  358. begin
  359. if Assigned(vstWatch.GetFirstSelected()) then
  360. vstWatch.DeleteNode(vstWatch.GetFirstSelected());
  361. end;
  362. procedure TfrmWatch.tbtnDeleteClick(Sender: TObject);
  363. begin
  364. DeleteSelected;
  365. end;
  366. procedure TfrmWatch.FormCreate(Sender: TObject);
  367. begin
  368. tbtnDelete.Enabled := False;
  369. DeleteSelectedItem1.Enabled := False;
  370. Expand1.Enabled := False;
  371. ExpandAll1.Enabled := False;
  372. Collapse1.Enabled := False;
  373. CollapseAll1.Enabled := False;
  374. end;
  375. procedure TfrmWatch.AddWatch1Click(Sender: TObject);
  376. begin
  377. tbtnAddWatch.Click;
  378. end;
  379. procedure TfrmWatch.Refresh1Click(Sender: TObject);
  380. begin
  381. tbtnRefreshWatch.Click;
  382. end;
  383. procedure TfrmWatch.DeleteSelectedItem1Click(Sender: TObject);
  384. begin
  385. tbtnDelete.Click;
  386. end;
  387. procedure TfrmWatch.Expand1Click(Sender: TObject);
  388. begin
  389. if Assigned(vstWatch.GetFirstSelected()) then
  390. vstWatch.Expanded[vstWatch.GetFirstSelected] := True;
  391. end;
  392. procedure TfrmWatch.Collapse1Click(Sender: TObject);
  393. begin
  394. if Assigned(vstWatch.GetFirstSelected()) then
  395. vstWatch.Expanded[vstWatch.GetFirstSelected] := False;
  396. end;
  397. procedure TfrmWatch.ExpandAll1Click(Sender: TObject);
  398. begin
  399. if Assigned(vstWatch.GetFirstSelected()) then
  400. vstWatch.FullExpand(vstWatch.GetFirstSelected());
  401. end;
  402. procedure TfrmWatch.CollapseAll1Click(Sender: TObject);
  403. begin
  404. if Assigned(vstWatch.GetFirstSelected()) then
  405. vstWatch.FullCollapse(vstWatch.GetFirstSelected());
  406. end;
  407. procedure TfrmWatch.vstWatchMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  408. begin
  409. if Assigned(vstWatch.GetFirstSelected()) then
  410. begin
  411. Expand1.Enabled := (vstWatch.GetFirstSelected().ChildCount > 0);
  412. ExpandAll1.Enabled := (vstWatch.GetFirstSelected().ChildCount > 0);
  413. Collapse1.Enabled := (vstWatch.GetFirstSelected().ChildCount > 0);
  414. CollapseAll1.Enabled := (vstWatch.GetFirstSelected().ChildCount > 0);
  415. end;
  416. end;
  417. procedure TfrmWatch.vstWatchDblClick(Sender: TObject);
  418. begin
  419. if Assigned(vstWatch.GetFirstSelected()) then
  420. begin
  421. // todo: Allow Variable Inspection...
  422. end;
  423. end;
  424. end.