WMTrees.Mod 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. MODULE WMTrees; (** AUTHOR "TF"; PURPOSE "Tree component"; *)
  2. IMPORT
  3. WMWindowManager, Objects, XML, WMComponents, WMGraphics, Kernel,
  4. WMStandardComponents, WMProperties, WMEvents, Rect := WMRectangles, Strings, Inputs;
  5. CONST
  6. NodeExpanded* = 0;
  7. NodeSubnodesUnknown* = 1;
  8. NodeAlwaysExpanded* = 2;
  9. NodeHidden * = 3;
  10. NodeSubnodesOnExpand* = 4; (** visible subnodes will be created when node is expanded *)
  11. StateSelected* = 0;
  12. StateHover* = 1;
  13. StateHasSubNodes* = 2;
  14. DefaultHeight = 25;
  15. DragDist = 10;
  16. TYPE
  17. String = Strings.String;
  18. (** TreeNode may not be shared between processes *)
  19. TreeNode* = OBJECT
  20. VAR
  21. state : SET;
  22. parent, prevSibling, nextSibling, firstChild, lastChild : TreeNode;
  23. caption : String;
  24. img : WMGraphics.Image;
  25. data : ANY;
  26. inTree : BOOLEAN;
  27. PROCEDURE &Init*;
  28. BEGIN
  29. inTree := FALSE
  30. END Init;
  31. PROCEDURE AddChild(x : TreeNode);
  32. BEGIN
  33. x.parent := SELF;
  34. IF lastChild = NIL THEN lastChild := x; firstChild := x; x.prevSibling := NIL; x.nextSibling := NIL
  35. ELSE lastChild.nextSibling := x; x.prevSibling := lastChild; lastChild := x; x.nextSibling := NIL
  36. END
  37. END AddChild;
  38. PROCEDURE AddChildAfter(prev, x : TreeNode);
  39. BEGIN
  40. IF (lastChild = NIL) THEN AddChild(x)
  41. ELSE
  42. x.parent := SELF;
  43. x.nextSibling := prev.nextSibling;
  44. x.prevSibling := prev;
  45. prev.nextSibling := x;
  46. IF x.nextSibling # NIL THEN x.nextSibling.prevSibling := x ELSE lastChild := x END
  47. END
  48. END AddChildAfter;
  49. PROCEDURE AddChildBefore(next, x : TreeNode);
  50. BEGIN
  51. IF (lastChild = NIL) THEN AddChild(x)
  52. ELSE
  53. x.parent := SELF;
  54. IF next = firstChild THEN
  55. x.nextSibling := firstChild;
  56. x.prevSibling := NIL;
  57. firstChild := x
  58. ELSE
  59. x.nextSibling := next;
  60. x.prevSibling := next.prevSibling;
  61. next.prevSibling.nextSibling := x;
  62. next.prevSibling := x
  63. END
  64. END
  65. END AddChildBefore;
  66. PROCEDURE Remove;
  67. BEGIN
  68. IF SELF = parent.firstChild THEN parent.firstChild := parent.firstChild.nextSibling END;
  69. IF SELF = parent.lastChild THEN parent.lastChild := parent.lastChild.prevSibling END;
  70. IF prevSibling # NIL THEN prevSibling.nextSibling := nextSibling END;
  71. IF nextSibling # NIL THEN nextSibling.prevSibling := prevSibling END;
  72. parent := NIL; prevSibling := NIL; nextSibling := NIL;
  73. inTree := FALSE
  74. END Remove;
  75. END TreeNode;
  76. DrawNodeProc = PROCEDURE {DELEGATE} (canvas : WMGraphics.Canvas; w, h : LONGINT; node : TreeNode; state : SET);
  77. MeasureNodeProc = PROCEDURE {DELEGATE} (node : TreeNode; VAR w, h : LONGINT);
  78. (* Tree structure that can be visualized in the TreeView. No node may be inserted more than once.
  79. Before manipulating or querying, the tree must be locked with Acquire *)
  80. TYPE
  81. Tree* = OBJECT
  82. VAR root : TreeNode;
  83. lockedBy : ANY;
  84. lockLevel : LONGINT;
  85. viewChanged : BOOLEAN;
  86. onChanged* : WMEvents.EventSource; (** does not hold the lock, if called *)
  87. beforeExpand* : WMEvents.EventSource; (** does hold the lock, if called *)
  88. PROCEDURE &Init*;
  89. BEGIN
  90. NEW(onChanged, SELF, WMComponents.NewString("TreeModelChanged"), NIL, NIL);
  91. NEW(beforeExpand, SELF, WMComponents.NewString("BeforeExpand"), NIL, NIL);
  92. lockLevel :=0
  93. END Init;
  94. (** acquire a read/write lock on the object *)
  95. PROCEDURE Acquire*;
  96. VAR me : ANY;
  97. BEGIN {EXCLUSIVE}
  98. me := Objects.ActiveObject();
  99. IF lockedBy = me THEN
  100. ASSERT(lockLevel # -1); (* overflow *)
  101. INC(lockLevel)
  102. ELSE
  103. AWAIT(lockedBy = NIL); viewChanged := FALSE;
  104. lockedBy := me; lockLevel := 1
  105. END
  106. END Acquire;
  107. (** release the read/write lock on the object *)
  108. PROCEDURE Release*;
  109. VAR haschanged : BOOLEAN;
  110. BEGIN
  111. BEGIN {EXCLUSIVE}
  112. ASSERT(lockedBy = Objects.ActiveObject(), 3000);
  113. haschanged := FALSE;
  114. DEC(lockLevel);
  115. IF lockLevel = 0 THEN lockedBy := NIL; haschanged := viewChanged END
  116. END;
  117. IF haschanged THEN onChanged.Call(NIL) END
  118. END Release;
  119. PROCEDURE HasLock*() : BOOLEAN;
  120. BEGIN {EXCLUSIVE}
  121. RETURN lockedBy = Objects.ActiveObject();
  122. END HasLock;
  123. (** Set the root node of the tree. All this reinitializes the tree.*)
  124. PROCEDURE SetRoot*(x : TreeNode);
  125. BEGIN
  126. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  127. root := x; viewChanged := TRUE
  128. END SetRoot;
  129. (** Get the tree root *)
  130. PROCEDURE GetRoot*() : TreeNode;
  131. BEGIN
  132. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  133. RETURN root
  134. END GetRoot;
  135. (** Add a child node to parent *)
  136. PROCEDURE AddChildNode*(parent, node : TreeNode);
  137. BEGIN
  138. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  139. ASSERT(~node.inTree, 4000);
  140. parent.AddChild(node); node.inTree := TRUE; viewChanged := TRUE
  141. END AddChildNode;
  142. (** Add a child node to parent *)
  143. PROCEDURE AddChildNodeAfter*(parent, prev, node : TreeNode);
  144. BEGIN
  145. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  146. ASSERT(~node.inTree, 4000);
  147. parent.AddChildAfter(prev, node); node.inTree := TRUE; viewChanged := TRUE
  148. END AddChildNodeAfter;
  149. (** Add a child node to parent *)
  150. PROCEDURE AddChildNodeBefore*(parent, next, node : TreeNode);
  151. BEGIN
  152. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  153. ASSERT(~node.inTree, 4000);
  154. parent.AddChildBefore(next, node); node.inTree := TRUE; viewChanged := TRUE
  155. END AddChildNodeBefore;
  156. (** Remove a node (including all sub nodes) *)
  157. PROCEDURE RemoveNode*(node : TreeNode);
  158. BEGIN
  159. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  160. IF node = root THEN root := NIL
  161. ELSE node.Remove
  162. END; viewChanged := TRUE
  163. END RemoveNode;
  164. (** expand all parent nodes up to the root so that node is visible *)
  165. PROCEDURE ExpandToRoot*(node : TreeNode);
  166. BEGIN
  167. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  168. WHILE node.parent # NIL DO INCL(node.parent.state, NodeExpanded); node := node.parent END;
  169. viewChanged := TRUE
  170. END ExpandToRoot;
  171. (** Get the next sibling of a node *)
  172. PROCEDURE GetNextSibling*(node : TreeNode) : TreeNode;
  173. BEGIN
  174. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  175. IF node = NIL THEN RETURN NIL END;
  176. RETURN node.nextSibling
  177. END GetNextSibling;
  178. (** Get the previous sibling of a node *)
  179. PROCEDURE GetPrevSibling*(node : TreeNode) : TreeNode;
  180. BEGIN
  181. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  182. IF node = NIL THEN RETURN NIL END;
  183. RETURN node.prevSibling
  184. END GetPrevSibling;
  185. (** Get the first child node *)
  186. PROCEDURE GetChildren*(node : TreeNode) : TreeNode;
  187. BEGIN
  188. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  189. IF node = NIL THEN RETURN NIL END;
  190. RETURN node.firstChild
  191. END GetChildren;
  192. (** Get the last child node *)
  193. PROCEDURE GetLastChild*(node : TreeNode) : TreeNode;
  194. BEGIN
  195. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  196. IF node = NIL THEN RETURN NIL END;
  197. RETURN node.lastChild
  198. END GetLastChild;
  199. (** Get parent of node *)
  200. PROCEDURE GetParent*(node : TreeNode) : TreeNode;
  201. BEGIN
  202. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  203. IF node = NIL THEN RETURN NIL END;
  204. RETURN node.parent
  205. END GetParent;
  206. (** Set node state *)
  207. PROCEDURE SetNodeState*(node : TreeNode; state : SET);
  208. BEGIN
  209. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  210. IF node = NIL THEN RETURN END;
  211. IF ~(NodeExpanded IN node.state) & (NodeExpanded IN state) THEN
  212. beforeExpand.Call(node);
  213. IF GetChildren(node) = NIL THEN EXCL(state, NodeExpanded) END;
  214. END;
  215. IF NodeAlwaysExpanded IN state THEN INCL(state, NodeExpanded) END;
  216. IF node.state # state THEN
  217. viewChanged := TRUE;
  218. node.state := state
  219. END
  220. END SetNodeState;
  221. (** Incl node state *)
  222. PROCEDURE InclNodeState*(node : TreeNode; state : LONGINT);
  223. BEGIN
  224. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  225. IF node = NIL THEN RETURN END;
  226. IF ~(NodeExpanded IN node.state) & (state = NodeExpanded) THEN
  227. beforeExpand.Call(node);
  228. IF GetChildren(node) = NIL THEN RETURN END
  229. END;
  230. IF state = NodeAlwaysExpanded THEN INCL(node.state, NodeExpanded) END;
  231. viewChanged := TRUE;
  232. INCL(node.state, state)
  233. END InclNodeState;
  234. (**Excl node state *)
  235. PROCEDURE ExclNodeState*(node : TreeNode; state : LONGINT);
  236. BEGIN
  237. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  238. IF node = NIL THEN RETURN END;
  239. viewChanged := TRUE;
  240. EXCL(node.state, state);
  241. IF NodeAlwaysExpanded IN node.state THEN INCL(node.state, NodeExpanded) END
  242. END ExclNodeState;
  243. (** Get node state *)
  244. PROCEDURE GetNodeState*(node : TreeNode) : SET;
  245. BEGIN
  246. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  247. IF node = NIL THEN RETURN {} END;
  248. RETURN node.state
  249. END GetNodeState;
  250. PROCEDURE SetNodeCaption*(node : TreeNode; caption : String);
  251. BEGIN
  252. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  253. IF node = NIL THEN RETURN END;
  254. viewChanged := TRUE;
  255. node.caption := caption
  256. END SetNodeCaption;
  257. PROCEDURE GetNodeCaption*(node : TreeNode) : String;
  258. BEGIN
  259. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  260. IF node = NIL THEN RETURN NIL END;
  261. RETURN node.caption
  262. END GetNodeCaption;
  263. PROCEDURE SetNodeImage*(node : TreeNode; i : WMGraphics.Image);
  264. BEGIN
  265. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  266. IF node = NIL THEN RETURN END;
  267. viewChanged := TRUE;
  268. node.img := i
  269. END SetNodeImage;
  270. PROCEDURE GetNodeImage*(node : TreeNode) : WMGraphics.Image;
  271. BEGIN
  272. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  273. IF node = NIL THEN RETURN NIL END;
  274. RETURN node.img
  275. END GetNodeImage;
  276. PROCEDURE SetNodeData*(node : TreeNode; data : ANY);
  277. BEGIN
  278. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  279. IF node = NIL THEN RETURN END;
  280. node.data := data
  281. END SetNodeData;
  282. PROCEDURE GetNodeData*(node : TreeNode) : ANY;
  283. BEGIN
  284. ASSERT(Objects.ActiveObject() = lockedBy, 3000);
  285. IF node = NIL THEN RETURN NIL END;
  286. RETURN node.data
  287. END GetNodeData;
  288. END Tree;
  289. (* Tree view component *)
  290. TYPE
  291. TreeView* = OBJECT (WMComponents.VisualComponent)
  292. VAR tree : Tree;
  293. downX, downY, firstLine, lines : LONGINT;
  294. vscrollbar, hscrollbar : WMStandardComponents.Scrollbar;
  295. drawNode : DrawNodeProc;
  296. measureNode : MeasureNodeProc;
  297. selectedNode, hoverNode : TreeNode;
  298. overNodeTimer : Kernel.MilliTimer;
  299. draggedNode -: TreeNode;
  300. selecting, middleClicking, dragPossible : BOOLEAN;
  301. cs : WMGraphics.CanvasState;
  302. hindent, indent, hdelta : LONGINT;
  303. onSelectNode-, onExpandNode-, onClickNode-, onMiddleClickNode- : WMEvents.EventSource;
  304. clHover-, clSelected-,
  305. clTextDefault-, clTextHover-, clTextSelected- : WMProperties.ColorProperty;
  306. fontHeight- : WMProperties.Int32Property;
  307. PROCEDURE &Init*;
  308. BEGIN
  309. Init^;
  310. SetNameAsString(StrTreeView);
  311. SetGenerator("WMTrees.TreeViewGen");
  312. NEW(clHover, PrototypeTclHover, NIL, NIL); properties.Add(clHover);
  313. NEW(clSelected, PrototypeTclSelected, NIL, NIL); properties.Add(clSelected);
  314. NEW(clTextDefault, PrototypeTclTextDefault, NIL, NIL); properties.Add(clTextDefault);
  315. NEW(clTextHover, PrototypeTclTextHover, NIL, NIL); properties.Add(clTextHover);
  316. NEW(clTextSelected, PrototypeTclTextSelected, NIL, NIL); properties.Add(clTextSelected);
  317. NEW(fontHeight, PrototypeTfontHeight, NIL, NIL); properties.Add(fontHeight);
  318. takesFocus.Set(TRUE);
  319. NEW(tree);
  320. (* Events *)
  321. NEW(onSelectNode, SELF, Strings.NewString("onSelectNode"), Strings.NewString("if node selected"),
  322. SELF.StringToCompCommand);
  323. events.Add(onSelectNode);
  324. NEW(onExpandNode, SELF, Strings.NewString("onExpandNode"), Strings.NewString("if node expanded"),
  325. SELF.StringToCompCommand);
  326. NEW(onClickNode, SELF, Strings.NewString("onClickNode"), Strings.NewString("if node clicked"),
  327. SELF.StringToCompCommand);
  328. events.Add(onClickNode);
  329. NEW(onMiddleClickNode, SELF, Strings.NewString("onMiddleClickNode"), Strings.NewString("if node is middle-clicked"),
  330. SELF.StringToCompCommand);
  331. events.Add(onMiddleClickNode);
  332. (* Scrollbar *)
  333. NEW(vscrollbar);
  334. vscrollbar.alignment.Set(WMComponents.AlignRight);
  335. AddInternalComponent(vscrollbar); vscrollbar.onPositionChanged.Add(ScrollbarChanged);
  336. NEW(hscrollbar);
  337. hscrollbar.alignment.Set(WMComponents.AlignBottom); hscrollbar.vertical.Set(FALSE);
  338. AddInternalComponent(hscrollbar); hscrollbar.onPositionChanged.Add(ScrollbarChanged);
  339. SetMeasureNodeProc(MeasureNode);
  340. SetDrawNodeProc(DrawNode);
  341. SetIndent(30);
  342. hdelta := 0
  343. END Init;
  344. PROCEDURE FocusReceived*;
  345. BEGIN FocusReceived^
  346. END FocusReceived;
  347. PROCEDURE FocusLost*;
  348. BEGIN FocusLost^
  349. END FocusLost;
  350. PROCEDURE SetIndent*(indent : LONGINT);
  351. BEGIN
  352. Acquire;
  353. IF indent # SELF.indent THEN
  354. SELF.indent := indent; hindent := indent DIV 2;
  355. hscrollbar.pageSize.Set(indent);
  356. Invalidate
  357. END;
  358. Release
  359. END SetIndent;
  360. (** Return the tree. All modifications are performed on the tree *)
  361. PROCEDURE GetTree*() : Tree;
  362. BEGIN
  363. RETURN tree
  364. END GetTree;
  365. PROCEDURE Initialize*;
  366. BEGIN
  367. Initialize^;
  368. Invalidate;
  369. tree.onChanged.Add(TreeChanged)
  370. END Initialize;
  371. PROCEDURE TreeChanged*(sender, data : ANY);
  372. VAR width, t : LONGINT;
  373. BEGIN
  374. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.TreeChanged, sender, data)
  375. ELSE
  376. MeasureTree(lines, width);
  377. vscrollbar.max.Set(lines - 1);
  378. IF vscrollbar.pos.Get() >= lines THEN vscrollbar.pos.Set(lines - 1) END;
  379. t := width - (bounds.GetWidth() - vscrollbar.bounds.GetWidth());
  380. IF t > 0 THEN
  381. hscrollbar.visible.Set(TRUE);
  382. hscrollbar.max.Set(t)
  383. ELSE
  384. hdelta := 0;
  385. hscrollbar.visible.Set(FALSE)
  386. END;
  387. Invalidate
  388. END
  389. END TreeChanged;
  390. PROCEDURE SetFirstLine*(line : LONGINT; adjustScrollbar : BOOLEAN);
  391. BEGIN
  392. Acquire;
  393. firstLine := line;
  394. IF adjustScrollbar THEN vscrollbar.pos.Set(line) END;
  395. Release;
  396. Invalidate
  397. END SetFirstLine;
  398. PROCEDURE SetDrawNodeProc*(x : DrawNodeProc);
  399. BEGIN
  400. Acquire;
  401. drawNode := x;
  402. Release;
  403. Invalidate
  404. END SetDrawNodeProc;
  405. PROCEDURE SetMeasureNodeProc*(x : MeasureNodeProc);
  406. BEGIN
  407. Acquire;
  408. measureNode := x;
  409. Release;
  410. Invalidate
  411. END SetMeasureNodeProc;
  412. PROCEDURE MeasureTree(VAR lines, width : LONGINT);
  413. VAR cury : LONGINT;
  414. PROCEDURE RenderTree(x : TreeNode; level : LONGINT);
  415. VAR a : TreeNode;
  416. w, h : LONGINT;
  417. BEGIN
  418. IF (x = NIL) OR (NodeHidden IN x.state) THEN RETURN END;
  419. INC(lines);
  420. h := DefaultHeight; w := bounds.GetWidth();
  421. IF measureNode # NIL THEN measureNode(x, w, h) END;
  422. width := MAX(width, w + level * indent);
  423. INC(cury, h);
  424. IF NodeExpanded IN x.state THEN
  425. a := tree.GetChildren(x);
  426. WHILE a # NIL DO
  427. RenderTree(a, level + 1);
  428. a := tree.GetNextSibling(a)
  429. END
  430. END
  431. END RenderTree;
  432. BEGIN
  433. tree.Acquire;
  434. cury := 0; lines := 0; width := 0;
  435. RenderTree(tree.GetRoot(), 0);
  436. tree.Release
  437. END MeasureTree;
  438. (** default DrawNode, can be replaced with SetDrawNodeMethod *)
  439. PROCEDURE DrawNode(canvas : WMGraphics.Canvas; w, h : LONGINT; node : TreeNode; state : SET);
  440. VAR dx, tdx, tdy : LONGINT; f : WMGraphics.Font;
  441. BEGIN
  442. dx := 0;
  443. f := GetFont();
  444. IF node.img # NIL THEN
  445. canvas.DrawImage(0, 0, node.img, WMGraphics.ModeSrcOverDst); dx := node.img.width + 5;
  446. END;
  447. canvas.SetFont(f);
  448. IF StateSelected IN state THEN canvas.SetColor(clTextSelected.Get())
  449. ELSIF StateHover IN state THEN canvas.SetColor(clTextHover.Get())
  450. ELSE canvas.SetColor(clTextDefault.Get())
  451. END;
  452. f.GetStringSize(node.caption^, tdx, tdy);
  453. IF StateSelected IN state THEN canvas.Fill(WMGraphics.MakeRectangle(0, 0, dx + tdx, h), clSelected.Get(), WMGraphics.ModeSrcOverDst)
  454. ELSIF StateHover IN state THEN canvas.Fill(WMGraphics.MakeRectangle(0, 0, dx + tdx, h), clHover.Get(), WMGraphics.ModeSrcOverDst)
  455. END;
  456. IF node.caption # NIL THEN canvas.DrawString(dx, h - f.descent -1, node.caption^) END;
  457. END DrawNode;
  458. (** default MeasuereNode, can be replaced with SetMeasureNodeMethod *)
  459. PROCEDURE MeasureNode*(node : TreeNode; VAR w, h : LONGINT);
  460. VAR dx, dy : LONGINT; f : WMGraphics.Font;
  461. BEGIN
  462. w := 0; h := 0;
  463. f := WMGraphics.GetDefaultFont();
  464. IF node.img # NIL THEN w := node.img.width + 5; h := node.img.height END;
  465. IF node.caption # NIL THEN
  466. f.GetStringSize(node.caption^, dx, dy); dy := f.GetHeight() + 2;
  467. w := w + dx;
  468. IF dy > h THEN h := dy END
  469. END
  470. END MeasureNode;
  471. PROCEDURE RenderTreeNode(canvas : WMGraphics.Canvas; y, h : LONGINT; node : TreeNode; level : LONGINT);
  472. VAR x, i, px, py : LONGINT; t : TreeNode; height, color : LONGINT;
  473. state : SET;
  474. PROCEDURE HasMoreVisibleNodes(node : TreeNode): BOOLEAN;
  475. VAR u : TreeNode; hasMore : BOOLEAN;
  476. BEGIN
  477. u := tree.GetNextSibling(node); hasMore := FALSE;
  478. WHILE (u # NIL) & ~hasMore DO
  479. IF (~(NodeHidden IN tree.GetNodeState(u))) THEN hasMore := TRUE END;
  480. u := tree.GetNextSibling(u)
  481. END;
  482. RETURN hasMore
  483. END HasMoreVisibleNodes;
  484. PROCEDURE HasVisibleChilds(node : TreeNode): BOOLEAN;
  485. VAR u : TreeNode; hasMore : BOOLEAN;
  486. BEGIN
  487. u := tree.GetChildren(node); hasMore := FALSE;
  488. WHILE (u # NIL) & ~hasMore DO
  489. IF (~(NodeHidden IN tree.GetNodeState(u))) THEN hasMore := TRUE END;
  490. u := tree.GetNextSibling(u)
  491. END;
  492. RETURN hasMore
  493. END HasVisibleChilds;
  494. BEGIN
  495. canvas.RestoreState(cs);
  496. i := level; height := h;
  497. (* draw the vertical lines *)
  498. x := hindent + (level - 1) * indent - hdelta;
  499. t := node;
  500. (* on each level *)
  501. WHILE i > 0 DO
  502. (* vertical line is needed if node/parent on level has a next sibling *)
  503. IF HasMoreVisibleNodes(t) THEN canvas.Line(x, y, x, y + height, 0FFH, WMGraphics.ModeCopy) END;
  504. t := tree.GetParent(t);
  505. ASSERT(t # NIL);
  506. DEC(i); DEC(x, indent)
  507. END;
  508. x := level * indent - hdelta;
  509. (* if the current node is the last in chain it needs half a vertical line *)
  510. IF ~HasMoreVisibleNodes(node) THEN
  511. canvas.Line(x - hindent, y, x - hindent, y + height DIV 2, 0FFH, WMGraphics.ModeCopy)
  512. END;
  513. (* draw small horizontal line if not root node *)
  514. IF level > 0 THEN canvas.Line(x - hindent, y + height DIV 2, x - 5, y + height DIV 2, 0FFH, WMGraphics.ModeCopy) END;
  515. IF level > 0 THEN
  516. state := tree.GetNodeState(node);
  517. IF ~(NodeAlwaysExpanded IN state) &
  518. ((NodeSubnodesOnExpand IN state) OR (HasVisibleChilds(node) & ((tree.GetChildren(node) # NIL) OR (NodeSubnodesUnknown IN state))))
  519. THEN
  520. (* draw a plus sign *)
  521. px := x - hindent; py := y + height DIV 2;
  522. IF ~(NodeSubnodesUnknown IN state) THEN color := LONGINT(0FFFFFFFFH) ELSE color := LONGINT(0808080FFH) END;
  523. canvas.Fill(Rect.MakeRect(px - 5, py - 5, px + 5 + 1, py + 5 + 1), 0FFH, WMGraphics.ModeCopy);
  524. canvas.Fill(Rect.MakeRect(px - 4, py - 4, px + 4 + 1, py + 4 + 1), color, WMGraphics.ModeCopy);
  525. canvas.Line(px - 2, py , px + 2 + 1, py, 00000FFFFH, WMGraphics.ModeCopy);
  526. IF ~(NodeExpanded IN state) THEN (* | of the + *)
  527. canvas.Line(px, py - 2 , px, py + 2 + 1, 00000FFFFH, WMGraphics.ModeCopy)
  528. END;
  529. END
  530. END;
  531. IF drawNode # NIL THEN
  532. canvas.SetClipRect(WMGraphics.MakeRectangle(x, y, bounds.GetWidth(), y + height));
  533. canvas.ClipRectAsNewLimits(x, y);
  534. state := {};
  535. IF node = selectedNode THEN INCL(state, StateSelected) END;
  536. IF node = hoverNode THEN INCL(state, StateHover) END;
  537. drawNode(canvas, bounds.GetWidth() - x, height, node, state)
  538. END
  539. END RenderTreeNode;
  540. (* draw tree *)
  541. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  542. VAR y, height, i : LONGINT; clip : Rect.Rectangle;
  543. PROCEDURE RenderTree(x : TreeNode; level : LONGINT);
  544. VAR a : TreeNode; w, h : LONGINT; t: Rect.Rectangle;
  545. BEGIN
  546. IF (x = NIL) OR (NodeHidden IN x.state) OR (y > height) THEN RETURN END;
  547. INC(i);
  548. IF i > firstLine THEN
  549. h := DefaultHeight; w := bounds.GetWidth();
  550. IF measureNode # NIL THEN measureNode(x, w, h) END;
  551. t := Rect.MakeRect(0, y, w, y + h);
  552. IF Rect.Intersect(clip, t) THEN
  553. RenderTreeNode(canvas, y, h, x, level);
  554. END;
  555. INC(y, h)
  556. END;
  557. IF NodeExpanded IN x.state THEN
  558. a := tree.GetChildren(x);
  559. WHILE a # NIL DO
  560. RenderTree(a, level + 1);
  561. a := tree.GetNextSibling(a)
  562. END
  563. END
  564. END RenderTree;
  565. BEGIN
  566. tree.Acquire;
  567. height := bounds.GetHeight();
  568. canvas.GetClipRect(clip);
  569. y := 0;
  570. canvas.SaveState(cs);
  571. RenderTree(tree.GetRoot(), 0);
  572. canvas.RestoreState(cs);
  573. tree.Release
  574. END DrawBackground;
  575. (** Return the TreeNode at the position x, y *)
  576. PROCEDURE GetNodeAtPos*(x, y : LONGINT) : TreeNode;
  577. VAR cury, i, height : LONGINT; found : TreeNode;
  578. PROCEDURE RenderTree(x : TreeNode; level : LONGINT);
  579. VAR a : TreeNode;
  580. w, h : LONGINT;
  581. BEGIN
  582. IF (x = NIL) OR (NodeHidden IN x.state) OR (cury > height) THEN RETURN END;
  583. INC(i);
  584. IF i > firstLine THEN
  585. h := DefaultHeight; w := bounds.GetWidth();
  586. IF measureNode # NIL THEN measureNode(x, w, h) END;
  587. INC(cury, h)
  588. END;
  589. IF cury >= y THEN found := x; RETURN END;
  590. IF NodeExpanded IN x.state THEN
  591. a := tree.GetChildren(x);
  592. WHILE a # NIL DO
  593. IF found = NIL THEN RenderTree(a, level + 1) END;
  594. a := tree.GetNextSibling(a)
  595. END
  596. END
  597. END RenderTree;
  598. BEGIN
  599. tree.Acquire;
  600. found := NIL;
  601. height := bounds.GetHeight();
  602. cury := 0; i := 0;
  603. RenderTree(tree.GetRoot(), 0);
  604. tree.Release;
  605. RETURN found
  606. END GetNodeAtPos;
  607. PROCEDURE GetNextVisibleNode(this : TreeNode; ignoreChildren : BOOLEAN) : TreeNode;
  608. VAR state : SET;
  609. BEGIN
  610. state := tree.GetNodeState(this);
  611. IF ~ignoreChildren & (NodeExpanded IN state) & (tree.GetChildren(this) # NIL) THEN RETURN tree.GetChildren(this);
  612. ELSIF tree.GetNextSibling(this) # NIL THEN RETURN tree.GetNextSibling(this);
  613. ELSIF tree.GetParent(this) # NIL THEN RETURN GetNextVisibleNode(tree.GetParent(this), TRUE)
  614. ELSE RETURN NIL
  615. END;
  616. END GetNextVisibleNode;
  617. PROCEDURE GetPrevVisibleNode(this : TreeNode) : TreeNode;
  618. VAR state : SET; temp : TreeNode;
  619. BEGIN
  620. state := tree.GetNodeState(this);
  621. temp := tree.GetPrevSibling(this);
  622. IF (temp # NIL) THEN
  623. IF (NodeExpanded IN tree.GetNodeState(temp)) & (tree.GetChildren(temp) # NIL) THEN
  624. RETURN tree.GetLastChild(temp)
  625. ELSE RETURN temp
  626. END
  627. ELSIF tree.GetParent(this) # NIL THEN RETURN tree.GetParent(this)
  628. ELSE RETURN NIL
  629. END;
  630. END GetPrevVisibleNode;
  631. PROCEDURE KeyEvent*(ucs : LONGINT; flags : SET; VAR keysym : LONGINT);
  632. VAR state : SET; selNode : TreeNode;
  633. PROCEDURE Up;
  634. BEGIN
  635. tree.Acquire; selNode := GetPrevVisibleNode(selectedNode); tree.Release;
  636. IF selNode # NIL THEN selectedNode := selNode; Invalidate; onSelectNode.Call(selectedNode) END
  637. END Up;
  638. PROCEDURE Down;
  639. BEGIN
  640. tree.Acquire; selNode := GetNextVisibleNode(selectedNode, FALSE); tree.Release;
  641. IF selNode # NIL THEN selectedNode := selNode; Invalidate; onSelectNode.Call(selectedNode) END
  642. END Down;
  643. BEGIN
  644. IF ~ (Inputs.Release IN flags) THEN
  645. IF (keysym = 0FF54H) THEN Down (* cursor down *)
  646. ELSIF (keysym = 0FF52H) THEN Up(* cursor up *)
  647. ELSIF (keysym = 0FF51H) THEN (* cursor left *)
  648. tree.Acquire;
  649. IF NodeExpanded IN tree.GetNodeState(selectedNode) THEN
  650. tree.ExclNodeState(selectedNode, NodeExpanded)
  651. ELSE
  652. Up;
  653. END;
  654. tree.Release;
  655. ELSIF (keysym = 0FF53H) THEN (* cursor right *)
  656. tree.Acquire;
  657. state := tree.GetNodeState(selectedNode);
  658. IF (NodeExpanded IN state) OR (~(NodeSubnodesOnExpand IN state) & (tree.GetChildren(selectedNode) = NIL)) THEN
  659. Down;
  660. ELSE
  661. tree.InclNodeState(selectedNode, NodeExpanded);
  662. END;
  663. tree.Release;
  664. END
  665. END
  666. END KeyEvent;
  667. PROCEDURE PointerDown*(x, y : LONGINT; keys : SET);
  668. BEGIN
  669. ASSERT(IsCallFromSequencer());
  670. selecting := 0 IN keys;
  671. middleClicking := (keys = {1});
  672. IF keys = {2} THEN ShowContextMenu(x, y); END;
  673. dragPossible := TRUE;
  674. downX := x;
  675. downY := y
  676. END PointerDown;
  677. PROCEDURE PointerMove*(x, y : LONGINT; keys : SET);
  678. VAR new : TreeNode;
  679. BEGIN
  680. new := GetNodeAtPos(downX, downY);
  681. IF dragPossible THEN
  682. IF (ABS(x - downX) > DragDist) OR (ABS(y - downY) > DragDist) THEN
  683. dragPossible := FALSE;
  684. draggedNode := new;
  685. AutoStartDrag()
  686. END
  687. ELSE
  688. IF new # hoverNode THEN
  689. hoverNode := new;
  690. Invalidate
  691. END
  692. END
  693. END PointerMove;
  694. PROCEDURE ClickNode*(node : TreeNode);
  695. BEGIN
  696. onClickNode.Call(node);
  697. END ClickNode;
  698. PROCEDURE MiddleClickNode*(node : TreeNode);
  699. BEGIN
  700. onMiddleClickNode.Call(node);
  701. END MiddleClickNode;
  702. PROCEDURE PointerUp*(x, y : LONGINT; keys : SET);
  703. VAR new : TreeNode; tn : TreeNode; w : LONGINT;
  704. BEGIN
  705. tree.Acquire;
  706. IF selecting & ~(0 IN keys) THEN
  707. new := GetNodeAtPos(x, y);
  708. IF new # NIL THEN
  709. tn := new.parent; WHILE tn # NIL DO tn := tn.parent; INC(w, indent) END;
  710. IF x + hdelta < w THEN
  711. IF NodeExpanded IN tree.GetNodeState(new) THEN
  712. tree.ExclNodeState(new, NodeExpanded)
  713. ELSE
  714. tree.InclNodeState(new, NodeExpanded)
  715. END;
  716. onExpandNode.Call(new)
  717. ELSE
  718. ClickNode(new);
  719. IF new = selectedNode THEN
  720. IF NodeExpanded IN tree.GetNodeState(selectedNode) THEN
  721. tree.ExclNodeState(selectedNode, NodeExpanded)
  722. ELSE
  723. tree.InclNodeState(selectedNode, NodeExpanded)
  724. END;
  725. onExpandNode.Call(new)
  726. ELSE
  727. IF selectedNode # new THEN
  728. selectedNode := new;
  729. onSelectNode.Call(selectedNode);
  730. Invalidate
  731. END
  732. END
  733. END
  734. END
  735. ELSIF middleClicking & (keys = {}) THEN
  736. new := GetNodeAtPos(x, y);
  737. IF (new # NIL) THEN MiddleClickNode(new); END;
  738. END;
  739. tree.Release;
  740. dragPossible := FALSE;
  741. draggedNode := NIL;
  742. END PointerUp;
  743. PROCEDURE DragOver*(x, y : LONGINT; dragInfo : WMWindowManager.DragInfo);
  744. VAR node : TreeNode;
  745. BEGIN
  746. tree.Acquire;
  747. node := GetNodeAtPos(x, y);
  748. IF (node = hoverNode) & ~(NodeExpanded IN tree.GetNodeState(node)) THEN
  749. IF Kernel.Expired(overNodeTimer) THEN
  750. onExpandNode.Call(node);
  751. tree.InclNodeState(node, NodeExpanded)
  752. END
  753. END;
  754. IF node # hoverNode THEN
  755. Kernel.SetTimer(overNodeTimer, 500);
  756. hoverNode := node;
  757. Invalidate
  758. END;
  759. tree.Release
  760. END DragOver;
  761. PROCEDURE WheelMove*(dz: LONGINT); (** PROTECTED *)
  762. BEGIN
  763. SetFirstLine(MIN(MAX(firstLine + dz, 0), lines - 1), TRUE)
  764. END WheelMove;
  765. PROCEDURE SelectNode*(node : TreeNode);
  766. BEGIN
  767. IF selectedNode # node THEN
  768. selectedNode := node;
  769. Invalidate
  770. END
  771. END SelectNode;
  772. PROCEDURE PointerLeave*;
  773. BEGIN
  774. IF hoverNode # NIL THEN hoverNode := NIL; Invalidate END
  775. END PointerLeave;
  776. (** Hande scrollbar changed event *)
  777. PROCEDURE ScrollbarChanged*(sender, data : ANY);
  778. BEGIN
  779. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.ScrollbarChanged, sender, data)
  780. ELSE
  781. IF sender = vscrollbar THEN SetFirstLine(vscrollbar.pos.Get(), FALSE)
  782. ELSE hdelta := hscrollbar.pos.Get(); Invalidate
  783. END
  784. END
  785. END ScrollbarChanged;
  786. END TreeView;
  787. VAR
  788. ColorPrototype : WMProperties.ColorProperty;
  789. PrototypeTclHover*, PrototypeTclSelected*, PrototypeTclTextDefault*,
  790. PrototypeTclTextHover*, PrototypeTclTextSelected* : WMProperties.ColorProperty;
  791. PrototypeTfontHeight* : WMProperties.Int32Property;
  792. StrTreeView : Strings.String;
  793. PROCEDURE InitStrings;
  794. BEGIN
  795. StrTreeView := Strings.NewString("TreeView");
  796. END InitStrings;
  797. PROCEDURE InitPrototypes;
  798. VAR plTreeView : WMProperties.PropertyList;
  799. BEGIN
  800. NEW(plTreeView); WMComponents.propertyListList.Add("TreeView", plTreeView);
  801. (* background colors *)
  802. NEW(ColorPrototype, NIL, NewString("ClHover"), NewString("color of the tree item, if the mouse is over it")); ColorPrototype.Set(LONGINT(0FFFF0080H));
  803. NEW(PrototypeTclHover, ColorPrototype, NIL, NIL); plTreeView.Add(PrototypeTclHover);
  804. NEW(ColorPrototype, NIL, NewString("ClSelected"), NewString("color of the the tree item, if it is selected")); ColorPrototype.Set(00000FF80H);
  805. NEW(PrototypeTclSelected, ColorPrototype, NIL, NIL); plTreeView.Add(PrototypeTclSelected);
  806. (* font colors *)
  807. NEW(ColorPrototype, NIL, NewString("ClTextDefault"), NewString("default text color of the tree item")); ColorPrototype.Set(0000000FFH);
  808. NEW(PrototypeTclTextDefault, ColorPrototype, NIL, NIL); plTreeView.Add(PrototypeTclTextDefault);
  809. NEW(ColorPrototype, NIL, NewString("ClTextHover"), NewString("text color of the tree item, if the mouse is over it")); ColorPrototype.Set(00000FFFFH);
  810. NEW(PrototypeTclTextHover, ColorPrototype, NIL, NIL); plTreeView.Add(PrototypeTclTextHover);
  811. NEW(ColorPrototype, NIL, NewString("ClTextSelected"), NewString("text color of the tree item, when selected")); ColorPrototype.Set(LONGINT(0FFFFFFFFH));
  812. NEW(PrototypeTclTextSelected, ColorPrototype, NIL, NIL); plTreeView.Add(PrototypeTclTextSelected);
  813. NEW(PrototypeTfontHeight, NIL, NewString("FontHeight"), NewString("height of the tree item text"));
  814. plTreeView.Add(PrototypeTfontHeight); PrototypeTfontHeight.Set(12);
  815. WMComponents.propertyListList.UpdateStyle
  816. END InitPrototypes;
  817. PROCEDURE TreeViewGen*() : XML.Element;
  818. VAR x : TreeView;
  819. BEGIN
  820. NEW(x); RETURN x
  821. END TreeViewGen;
  822. PROCEDURE NewString*(CONST x : ARRAY OF CHAR) : String;
  823. VAR t : String;
  824. BEGIN
  825. NEW(t, LEN(x)); COPY(x, t^); RETURN t
  826. END NewString;
  827. BEGIN
  828. InitStrings;
  829. InitPrototypes;
  830. END WMTrees.