PETTrees.Mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. MODULE PETTrees; (** AUTHOR "?/staubesv"; PURPOSE "Interface for PET sidepanel trees"; *)
  2. IMPORT
  3. KernelLog,
  4. Streams, Diagnostics, Strings, Texts, WMStandardComponents, WMRectangles, WMGraphics, WMComponents,
  5. WMTextView, WMEditors, WMTrees, WMEvents;
  6. CONST
  7. InvalidPosition* = -1;
  8. TYPE
  9. (** data parameter for onGoToExternalModule event *)
  10. ExternalInfo* = OBJECT
  11. VAR
  12. filename- : ARRAY 32 OF CHAR;
  13. position- : LONGINT;
  14. PROCEDURE &Init*(CONST filename : ARRAY OF CHAR; position : LONGINT);
  15. BEGIN
  16. COPY(filename, SELF.filename);
  17. SELF.position := position;
  18. END Init;
  19. END ExternalInfo;
  20. (** data parameter for onGoToExternalDefinition event *)
  21. ExternalDefinitionInfo* = OBJECT
  22. VAR
  23. filename-, definition- : ARRAY 256 OF CHAR;
  24. PROCEDURE &Init*(CONST filename, definition : ARRAY OF CHAR);
  25. BEGIN
  26. COPY(filename, SELF.filename);
  27. COPY(definition, SELF.definition);
  28. END Init;
  29. END ExternalDefinitionInfo;
  30. RefreshParameters* = OBJECT
  31. VAR
  32. diagnostics : Diagnostics.Diagnostics;
  33. log : Streams.Writer;
  34. PROCEDURE &Init*(diagnostics : Diagnostics.Diagnostics; log : Streams.Writer);
  35. BEGIN
  36. ASSERT((diagnostics # NIL) & (log # NIL));
  37. SELF.diagnostics := diagnostics;
  38. SELF.log := log;
  39. END Init;
  40. END RefreshParameters;
  41. TYPE
  42. TreeNode* = OBJECT(WMTrees.TreeNode)
  43. VAR
  44. pos* : Texts.TextPosition;
  45. color* : WMGraphics.Color;
  46. font* : WMGraphics.Font;
  47. external* : BOOLEAN;
  48. PROCEDURE &Init*;
  49. BEGIN
  50. Init^;
  51. pos := NIL;
  52. color := WMGraphics.Black;
  53. font := FontPlain;
  54. external := FALSE;
  55. END Init;
  56. END TreeNode;
  57. TYPE
  58. Tree* = OBJECT (WMStandardComponents.Panel)
  59. VAR
  60. (* Protected fields *)
  61. editor-: WMEditors.Editor;
  62. tree-: WMTrees.Tree;
  63. treeView-: WMTrees.TreeView;
  64. toolbar-: WMStandardComponents.Panel;
  65. onExpandNode-: WMEvents.EventSource;
  66. onGoToFile- : WMEvents.EventSource;
  67. onGoToDefinition- : WMEvents.EventSource;
  68. onRefresh- : WMEvents.EventSource;
  69. label: WMStandardComponents.Label;
  70. refreshBtn, sortBtn: WMStandardComponents.Button;
  71. highlight- : WMTextView.Highlight;
  72. PROCEDURE & Init*;
  73. BEGIN
  74. Init^;
  75. NEW(onGoToFile, NIL, NIL, NIL, NIL); events.Add(onGoToFile);
  76. NEW(onGoToDefinition, NIL, NIL, NIL, NIL); events.Add(onGoToDefinition);
  77. NEW(onRefresh, SELF, NIL, NIL, NIL); events.Add(onRefresh);
  78. NEW(label); label.alignment.Set(WMComponents.AlignTop);
  79. label.fillColor.Set(0CCCCCCFFH);
  80. label.SetCaption(""); label.bounds.SetHeight(20);
  81. SELF.AddContent(label);
  82. NEW(toolbar); toolbar.alignment.Set(WMComponents.AlignTop);
  83. toolbar.bounds.SetHeight(20);
  84. SELF.AddContent(toolbar);
  85. NEW(treeView); treeView.alignment.Set(WMComponents.AlignClient);
  86. treeView.clSelected.Set(0B0B0FFA0H);
  87. treeView.SetFont(FontPlain);
  88. SELF.AddContent(treeView);
  89. tree := treeView.GetTree();
  90. treeView.SetDrawNodeProc(DrawNode);
  91. treeView.onClickNode.Add(ClickNode);
  92. treeView.onMiddleClickNode.Add(MiddleClickNode);
  93. onExpandNode := treeView.onExpandNode;
  94. NEW(refreshBtn); refreshBtn.alignment.Set(WMComponents.AlignLeft);
  95. refreshBtn.bounds.SetWidth(30);
  96. refreshBtn.imageName.Set(Strings.NewString("PETIcons.tar://refresh.png"));
  97. refreshBtn.onClick.Add(RefreshHandler);
  98. toolbar.AddContent(refreshBtn);
  99. NEW(sortBtn); sortBtn.alignment.Set(WMComponents.AlignLeft);
  100. sortBtn.caption.SetAOC("Sort");
  101. sortBtn.onClick.Add(SortHandler);
  102. toolbar.AddContent(sortBtn);
  103. END Init;
  104. PROCEDURE SetTitle*(CONST title : ARRAY OF CHAR);
  105. BEGIN
  106. label.caption.SetAOC(title);
  107. END SetTitle;
  108. PROCEDURE SetEditor*(e: WMEditors.Editor);
  109. BEGIN {EXCLUSIVE}
  110. IF e = editor THEN RETURN END;
  111. IF (highlight # NIL) & (editor # NIL) THEN
  112. editor.tv.RemoveHighlight(highlight);
  113. highlight := NIL
  114. END;
  115. editor := e;
  116. highlight := editor.tv.CreateHighlight();
  117. highlight.SetColor(LONGINT(0DDDD0060H));
  118. highlight.SetKind(WMTextView.HLOver)
  119. END SetEditor;
  120. PROCEDURE Erase*;
  121. BEGIN
  122. tree.Acquire;
  123. tree.SetRoot(NIL);
  124. tree.Release;
  125. treeView.SetFirstLine(0, TRUE);
  126. label.SetCaption("");
  127. END Erase;
  128. PROCEDURE GetNextNode(this : WMTrees.TreeNode; ignoreChildren : BOOLEAN) : WMTrees.TreeNode;
  129. VAR state : SET;
  130. BEGIN
  131. state := tree.GetNodeState(this);
  132. IF ~ignoreChildren & (tree.GetChildren(this) # NIL) THEN RETURN tree.GetChildren(this);
  133. ELSIF tree.GetNextSibling(this) # NIL THEN RETURN tree.GetNextSibling(this)
  134. ELSIF tree.GetParent(this) # NIL THEN RETURN GetNextNode(tree.GetParent(this), TRUE)
  135. ELSE RETURN NIL
  136. END
  137. END GetNextNode;
  138. PROCEDURE RefreshHandler*(sender, data: ANY);
  139. TYPE
  140. StringList = POINTER TO ARRAY OF Strings.String;
  141. VAR
  142. rootNode: TreeNode;
  143. diagnostics : Diagnostics.Diagnostics;
  144. streamDiagnostics : Diagnostics.StreamDiagnostics; log, writer : Streams.Writer;
  145. dummyLog : Streams.StringWriter;
  146. nofOpenNodes : LONGINT;
  147. openNodes : StringList;
  148. i : LONGINT;
  149. PROCEDURE Store;
  150. VAR node, tnode : WMTrees.TreeNode;
  151. stack : ARRAY 32 OF WMTrees.TreeNode;
  152. caption : Strings.String;
  153. tos : LONGINT;
  154. path : ARRAY 1024 OF CHAR;
  155. sl, tl : StringList;
  156. i : LONGINT;
  157. BEGIN
  158. nofOpenNodes := 0;
  159. node := tree.GetRoot();
  160. NEW(sl, 16);
  161. WHILE node # NIL DO
  162. IF WMTrees.NodeExpanded IN tree.GetNodeState(node) THEN
  163. tnode := node;
  164. tos := 0;
  165. REPEAT
  166. stack[tos] := tnode; INC(tos);
  167. tnode := tree.GetParent(tnode)
  168. UNTIL tnode = NIL;
  169. DEC(tos);
  170. path := "";
  171. WHILE tos >= 0 DO
  172. caption := tree.GetNodeCaption(stack[tos]);
  173. Strings.Append(path, caption^);
  174. DEC(tos);
  175. IF tos >= 0 THEN Strings.Append(path, "/") END
  176. END;
  177. IF nofOpenNodes >= LEN(sl) THEN
  178. NEW(tl, LEN(sl) * 2);
  179. FOR i := 0 TO LEN(sl) - 1 DO tl[i] := sl[i] END;
  180. sl := tl
  181. END;
  182. sl[nofOpenNodes] := Strings.NewString(path); INC(nofOpenNodes)
  183. END;
  184. node := GetNextNode(node, FALSE)
  185. END;
  186. openNodes := sl
  187. END Store;
  188. PROCEDURE Expand(path : ARRAY OF CHAR);
  189. VAR node, tnode : WMTrees.TreeNode;
  190. pos : SIZE;
  191. found : BOOLEAN;
  192. ident : ARRAY 64 OF CHAR;
  193. string : Strings.String;
  194. BEGIN
  195. node := tree.GetRoot();
  196. pos := Strings.Pos("/", path);
  197. IF pos > 0 THEN
  198. Strings.Copy(path, 0, pos, ident);
  199. Strings.Delete(path, 0, pos + 1)
  200. END;
  201. WHILE (path # "") & (node # NIL) DO
  202. pos := Strings.Pos("/", path);
  203. IF pos > 0 THEN
  204. Strings.Copy(path, 0, pos, ident);
  205. Strings.Delete(path, 0, pos + 1)
  206. ELSE COPY(path, ident); path := ""
  207. END;
  208. tnode := tree.GetChildren(node);
  209. found := FALSE;
  210. WHILE (tnode # NIL) & ~ found DO
  211. string := tree.GetNodeCaption(tnode);
  212. IF (string # NIL) & (string^ = ident) THEN
  213. node := tnode;
  214. found := TRUE
  215. END;
  216. tnode := tree.GetNextSibling(tnode)
  217. END
  218. END;
  219. tree.InclNodeState(node, WMTrees.NodeExpanded);
  220. END Expand;
  221. BEGIN
  222. IF ~IsCallFromSequencer() THEN
  223. sequencer.ScheduleEvent(SELF.RefreshHandler, sender, data);
  224. ELSE
  225. IF (data # NIL) & (data IS RefreshParameters) THEN
  226. diagnostics := data(RefreshParameters).diagnostics;
  227. log := data(RefreshParameters).log;
  228. writer := NIL;
  229. ELSE
  230. NEW(writer, KernelLog.Send, 256);
  231. NEW(streamDiagnostics, writer); diagnostics := streamDiagnostics;
  232. NEW(dummyLog, 32); log := dummyLog;
  233. END;
  234. tree.Acquire;
  235. Store;
  236. editor.text.AcquireRead;
  237. rootNode := GetNewNode();
  238. tree.SetRoot(rootNode);
  239. AddNodes(rootNode, diagnostics, log);
  240. editor.text.ReleaseRead;
  241. i := 0;
  242. WHILE i < nofOpenNodes DO
  243. Expand(openNodes[i]^); INC(i)
  244. END;
  245. tree.Release;
  246. IF (writer # NIL) THEN
  247. writer.Update;
  248. END;
  249. treeView.SetFirstLine(0, TRUE);
  250. treeView.TreeChanged(SELF, NIL);
  251. onRefresh.Call(NIL);
  252. END;
  253. END RefreshHandler;
  254. PROCEDURE GetNewNode*() : TreeNode;
  255. VAR node : TreeNode;
  256. BEGIN
  257. NEW(node); RETURN node;
  258. END GetNewNode;
  259. PROCEDURE AddNodes*(parent : TreeNode; diagnostics : Diagnostics.Diagnostics; log : Streams.Writer);
  260. BEGIN
  261. ASSERT((parent # NIL) & (diagnostics # NIL) & (log # NIL));
  262. (* abstract *)
  263. END AddNodes;
  264. PROCEDURE SortHandler(sender, data: ANY);
  265. BEGIN
  266. tree.Acquire;
  267. SortTree(tree.GetRoot());
  268. tree.Release;
  269. END SortHandler;
  270. PROCEDURE SelectNodeByPos* (pos: LONGINT);
  271. VAR root, node: WMTrees.TreeNode;
  272. PROCEDURE FindNearestNode (node: WMTrees.TreeNode; pos: LONGINT): WMTrees.TreeNode;
  273. VAR nearestNode: WMTrees.TreeNode; distance, nearestDistance: LONGINT;
  274. PROCEDURE GetDistance (node: WMTrees.TreeNode; pos: LONGINT): LONGINT;
  275. BEGIN
  276. WHILE (node # NIL) & (~(node IS TreeNode) OR (node(TreeNode).pos = NIL)) DO
  277. node := tree.GetChildren(node);
  278. END;
  279. IF (node # NIL) & (node IS TreeNode) & (node(TreeNode).pos # NIL) & (pos >= node(TreeNode).pos.GetPosition()) THEN
  280. RETURN pos - node(TreeNode).pos.GetPosition()
  281. ELSE
  282. RETURN MAX(LONGINT)
  283. END
  284. END GetDistance;
  285. BEGIN
  286. nearestNode := NIL; nearestDistance := MAX (LONGINT);
  287. WHILE node # NIL DO
  288. IF (node IS TreeNode) & (node(TreeNode).external = FALSE) THEN
  289. distance := GetDistance (node, pos);
  290. IF distance < nearestDistance THEN nearestNode := node; nearestDistance := distance END;
  291. END;
  292. node := tree.GetNextSibling (node);
  293. END;
  294. RETURN nearestNode;
  295. END FindNearestNode;
  296. BEGIN
  297. tree.Acquire;
  298. root := FindNearestNode (tree.GetRoot (), pos); node := NIL;
  299. WHILE (root # NIL) & (WMTrees.NodeExpanded IN tree.GetNodeState (root)) & (tree.GetChildren (root) # NIL) DO
  300. node := FindNearestNode (tree.GetChildren (root), pos); root := node;
  301. END;
  302. tree.Release;
  303. IF (node # NIL) THEN treeView.SelectNode (node); END;
  304. END SelectNodeByPos;
  305. PROCEDURE BrowseToDefinition*(sender, data : ANY);
  306. BEGIN
  307. END BrowseToDefinition;
  308. PROCEDURE SortTree(parent: WMTrees.TreeNode);
  309. VAR
  310. n, left, right: WMTrees.TreeNode;
  311. nodeCount, i: LONGINT;
  312. BEGIN
  313. n := tree.GetChildren(parent);
  314. WHILE n # NIL DO
  315. SortTree(n);
  316. INC(nodeCount);
  317. n := tree.GetNextSibling(n);
  318. END;
  319. FOR i := 1 TO nodeCount-1 DO
  320. n := tree.GetChildren(parent);
  321. WHILE tree.GetNextSibling(n) # NIL DO
  322. left := n; right := tree.GetNextSibling(n);
  323. IF IsNodeGreater(left, right) THEN
  324. SwapSiblings(parent, left, right);
  325. n := left;
  326. ELSE
  327. n := right;
  328. END;
  329. END;
  330. END;
  331. END SortTree;
  332. PROCEDURE IsNodeGreater*(left, right: WMTrees.TreeNode): BOOLEAN;
  333. VAR leftCaption, rightCaption : Strings.String;
  334. BEGIN
  335. leftCaption := tree.GetNodeCaption(left);
  336. rightCaption := tree.GetNodeCaption(right);
  337. IF (leftCaption # NIL) & (rightCaption # NIL) THEN
  338. RETURN leftCaption^ > rightCaption^;
  339. ELSE
  340. RETURN FALSE;
  341. END;
  342. END IsNodeGreater;
  343. PROCEDURE SwapSiblings(parent, left, right: WMTrees.TreeNode);
  344. BEGIN
  345. ASSERT(tree.GetNextSibling(left) = right);
  346. tree.RemoveNode(left);
  347. tree.AddChildNodeAfter(parent, right, left);
  348. END SwapSiblings;
  349. PROCEDURE DrawNode(canvas: WMGraphics.Canvas; w, h: LONGINT; node: WMTrees.TreeNode; state: SET);
  350. VAR dx, tdx, tdy : LONGINT; f : WMGraphics.Font; image : WMGraphics.Image; caption: Strings.String;
  351. BEGIN
  352. dx := 0;
  353. image := tree.GetNodeImage(node);
  354. IF image # NIL THEN
  355. canvas.DrawImage(0, 0, image, WMGraphics.ModeSrcOverDst); dx := image.width + 5;
  356. END;
  357. IF (node IS TreeNode) THEN
  358. canvas.SetColor(node(TreeNode).color);
  359. f := node(TreeNode).font;
  360. canvas.SetFont(f);
  361. ELSE
  362. canvas.SetColor(treeView.clTextDefault.Get());
  363. canvas.SetFont(treeView.GetFont());
  364. f := treeView.GetFont();
  365. END;
  366. caption := tree.GetNodeCaption(node);
  367. f.GetStringSize(caption^, tdx, tdy);
  368. IF WMTrees.StateSelected IN state THEN
  369. canvas.Fill(WMRectangles.MakeRect(0, 0, dx + tdx, h), treeView.clSelected.Get(), WMGraphics.ModeSrcOverDst)
  370. ELSIF WMTrees.StateHover IN state THEN
  371. canvas.Fill(WMRectangles.MakeRect(0, 0, dx + tdx, h), treeView.clHover.Get(), WMGraphics.ModeSrcOverDst)
  372. END;
  373. IF caption # NIL THEN canvas.DrawString(dx, h - f.descent - 1 , caption^) END;
  374. END DrawNode;
  375. PROCEDURE SetEditorPosition*(position : LONGINT; doHighlight : BOOLEAN);
  376. VAR text : Texts.Text; a, b : LONGINT;
  377. BEGIN
  378. text := editor.text;
  379. text.AcquireRead;
  380. IF (position # InvalidPosition) THEN
  381. editor.tv.cursor.SetPosition(position);
  382. editor.tv.cursor.SetVisible(TRUE);
  383. IF doHighlight THEN
  384. editor.tv.FindCommand(position, a, b);
  385. highlight.SetFromTo(a, b);
  386. ELSE
  387. highlight.SetFromTo(0, 0); (* deactivate *)
  388. END;
  389. ELSE
  390. highlight.SetFromTo(0, 0);
  391. END;
  392. text.ReleaseRead;
  393. editor.SetFocus;
  394. END SetEditorPosition;
  395. PROCEDURE ClickNode*(sender, node : ANY);
  396. BEGIN
  397. IF (node # NIL) & (node IS TreeNode) & (node(TreeNode).pos # NIL) THEN
  398. KernelLog.String("POS");
  399. SetEditorPosition(node(TreeNode).pos.GetPosition(), TRUE);
  400. ELSE
  401. SetEditorPosition(InvalidPosition, FALSE);
  402. END;
  403. END ClickNode;
  404. PROCEDURE MiddleClickNode*(sender, data : ANY);
  405. BEGIN
  406. (* abstract *)
  407. END MiddleClickNode;
  408. PROCEDURE PrefixPostfixToCaption*(node: WMTrees.TreeNode; prePost: Strings.String; prefix: BOOLEAN); (** protected *)
  409. VAR
  410. oldCaption, newCaption: Strings.String;
  411. len: LONGINT;
  412. BEGIN
  413. oldCaption := tree.GetNodeCaption(node);
  414. len := LEN(oldCaption^) + LEN(prePost^);
  415. NEW(newCaption, len);
  416. IF prefix THEN
  417. Strings.Concat(prePost^, oldCaption^, newCaption^);
  418. ELSE
  419. Strings.Concat(oldCaption^, prePost^, newCaption^);
  420. END;
  421. tree.SetNodeCaption(node, newCaption);
  422. END PrefixPostfixToCaption;
  423. PROCEDURE AddPrefixToCaption*(node: WMTrees.TreeNode; prefix: Strings.String); (** protected *)
  424. BEGIN
  425. PrefixPostfixToCaption(node, prefix, TRUE);
  426. END AddPrefixToCaption;
  427. PROCEDURE AddPostfixToCaption*(node: WMTrees.TreeNode; postfix: Strings.String); (** protected *)
  428. BEGIN
  429. PrefixPostfixToCaption(node, postfix, FALSE);
  430. END AddPostfixToCaption;
  431. PROCEDURE AddNumberPostfixToCaption*(node : WMTrees.TreeNode; number : LONGINT); (** protected *)
  432. VAR postfix, nbr : ARRAY 16 OF CHAR;
  433. BEGIN
  434. Strings.IntToStr(number, nbr);
  435. postfix := " ("; Strings.Append(postfix, nbr); Strings.Append(postfix, ")");
  436. PrefixPostfixToCaption(node, Strings.NewString(postfix), FALSE);
  437. END AddNumberPostfixToCaption;
  438. END Tree;
  439. Factory* = PROCEDURE() : Tree;
  440. VAR
  441. FontPlain-, FontBold-, FontItalic-: WMGraphics.Font;
  442. font : WMGraphics.Font;
  443. BEGIN
  444. font := WMGraphics.GetDefaultFont();
  445. FontPlain := WMGraphics.GetFont(font.name, font.size, {});
  446. FontBold := WMGraphics.GetFont(font.name, font.size, {WMGraphics.FontBold});
  447. FontItalic := WMGraphics.GetFont(font.name, font.size, {WMGraphics.FontItalic});
  448. END PETTrees.