WMMenus.Mod 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. MODULE WMMenus; (** AUTHOR "TF/staubesv"; PURPOSE "Menu support"; *)
  2. (*
  3. Vertical menu entry layout:
  4. | HMenuDistance | image.width OR MinImageWidth | HMenuDistance | TextImageDistance | TextWidth | HMenuDistance |
  5. *)
  6. IMPORT
  7. Inputs, Strings, Raster, WMRectangles, WMGraphics, WMGraphicUtilities, WMComponents,
  8. WMWindowManager, WMProperties, WMEvents, WMDropTarget, WMTrees;
  9. CONST
  10. OpenDefault* = OpenDownRight;
  11. OpenUpLeft* = 1;
  12. OpenUpRight* = 2;
  13. OpenDownLeft* = 3;
  14. OpenDownRight* = 4;
  15. (* ShadowWindow.type *)
  16. Right = 0;
  17. Bottom = 1;
  18. ShadowWidth = 5;
  19. ShadowHeight = 5;
  20. ShadowOffsetVertical = 5;
  21. ShadowOffsetHorizontal = 5;
  22. LightGrey = LONGINT(0C0C0C0FFH);
  23. LightGreyDrag = LONGINT(0C0C0C0C0H);
  24. WhiteDrag = LONGINT(0FFFFFFC0H);
  25. TextImageDistance = 4;
  26. MinImageWidth = 4;
  27. HMenuDistance = 8;
  28. VMenuDistance = 4;
  29. SeparatorCaption = "---";
  30. SeparatorWidth = 9;
  31. SeparatorHeight = 5;
  32. DragDist = 10;
  33. TYPE
  34. Separator* = OBJECT(WMTrees.TreeNode)
  35. END Separator;
  36. TYPE
  37. DragWrapper* = OBJECT
  38. END DragWrapper;
  39. TYPE
  40. MenuPanel*= OBJECT(WMComponents.VisualComponent)
  41. VAR
  42. horizontal- : WMProperties.BooleanProperty;
  43. horizontalI : BOOLEAN;
  44. openDirection- : WMProperties.Int32Property;
  45. openDirectionI : LONGINT;
  46. clSelected : WMProperties.ColorProperty;
  47. onSelect- : WMEvents.EventSource;
  48. menu : WMTrees.Tree;
  49. root, selection, hover : WMTrees.TreeNode;
  50. subMenuIndicatorImg : WMGraphics.Image;
  51. subMenu, parentWindow : MenuWindow;
  52. parentMenuPanel, focusPanel, rootMenuPanel : MenuPanel;
  53. greyBoxWidth : LONGINT;
  54. dragNode : WMTrees.TreeNode;
  55. dragObject : ANY;
  56. (* pointer handling *)
  57. leftClick, dragPossible : BOOLEAN;
  58. downX, downY : LONGINT;
  59. PROCEDURE &Init*;
  60. BEGIN
  61. Init^;
  62. SetNameAsString(StrMenuPanel);
  63. NEW(horizontal, NIL, NIL, NIL); properties.Add(horizontal);
  64. horizontalI := horizontal.Get();
  65. NEW(openDirection, NIL, NIL, NIL); properties.Add(openDirection);
  66. NEW(clSelected, NIL, NIL, NIL); properties.Add(clSelected);
  67. clSelected.Set(WMGraphics.Blue);
  68. NEW(onSelect, SELF, NIL, NIL, NIL);
  69. openDirectionI := OpenDefault;
  70. openDirection.Set(openDirectionI);
  71. menu := NIL;
  72. root := NIL; selection := NIL; hover := NIL;
  73. subMenuIndicatorImg := NIL;
  74. subMenu := NIL; parentWindow := NIL;
  75. greyBoxWidth := 2 * HMenuDistance + MinImageWidth;
  76. dragObject := NIL;
  77. parentMenuPanel := NIL; focusPanel := SELF; rootMenuPanel := SELF;
  78. takesFocus.Set(TRUE);
  79. END Init;
  80. PROCEDURE PropertyChanged*(sender, property : ANY);
  81. BEGIN
  82. IF (property = clSelected) THEN
  83. Invalidate;
  84. ELSIF (property = horizontal) THEN
  85. horizontalI := horizontal.Get();
  86. Invalidate;
  87. ELSIF (property = openDirection) THEN
  88. openDirectionI := openDirection.Get();
  89. Invalidate;
  90. ELSIF property=properties THEN
  91. RecacheProperties; Invalidate
  92. ELSE
  93. PropertyChanged^(sender, property);
  94. END;
  95. END PropertyChanged;
  96. PROCEDURE RecacheProperties*;
  97. BEGIN
  98. RecacheProperties^;
  99. horizontalI := horizontal.Get();
  100. openDirectionI := openDirection.Get();
  101. (*Invalidate;*)
  102. END RecacheProperties;
  103. PROCEDURE SetParent(parentMenuPanel : MenuPanel);
  104. BEGIN
  105. SELF.parentMenuPanel := parentMenuPanel;
  106. IF (parentMenuPanel # NIL) THEN
  107. rootMenuPanel := parentMenuPanel.rootMenuPanel;
  108. END;
  109. END SetParent;
  110. (* If menus are used as popup menus, the initial window most be registered here so it can be closed when an item has been selected *)
  111. PROCEDURE SetParentWindow(parentWindow : MenuWindow);
  112. BEGIN
  113. ASSERT(parentWindow # NIL);
  114. SELF.parentWindow := parentWindow;
  115. END SetParentWindow;
  116. PROCEDURE SetMenu*(menu : WMTrees.Tree; root : WMTrees.TreeNode);
  117. BEGIN
  118. ASSERT((menu # NIL) & (root # NIL));
  119. Acquire;
  120. SELF.menu := menu; SELF.root := root; hover := NIL;
  121. greyBoxWidth := MAX(MinImageWidth + 2 * HMenuDistance , MaxImageWidth() + 2 * HMenuDistance);
  122. Invalidate;
  123. Release
  124. END SetMenu;
  125. PROCEDURE Measure(VAR width, height : LONGINT);
  126. VAR child : WMTrees.TreeNode;
  127. BEGIN
  128. ASSERT((menu # NIL) & (root # NIL));
  129. width := 0; height := 0;
  130. IF horizontal.Get() THEN
  131. menu.Acquire;
  132. child := menu.GetChildren(root);
  133. WHILE (child # NIL) DO
  134. width := width + ItemWidth(child, TRUE);
  135. child := menu.GetNextSibling(child);
  136. END;
  137. menu.Release;
  138. ELSE
  139. menu.Acquire;
  140. child := menu.GetChildren(root);
  141. WHILE (child # NIL) DO
  142. height := height + ItemHeight(child);
  143. width := MAX(width, ItemWidth(child, FALSE));
  144. child := menu.GetNextSibling(child);
  145. END;
  146. menu.Release;
  147. END;
  148. END Measure;
  149. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  150. VAR
  151. child : WMTrees.TreeNode;
  152. x, y, dx, dy, t, textY : LONGINT;
  153. font : WMGraphics.Font;
  154. caption : Strings.String;
  155. image : Raster.Image;
  156. BEGIN
  157. DrawBackground^(canvas);
  158. IF (menu = NIL) OR (root = NIL) THEN RETURN; END;
  159. font := GetFont();
  160. canvas.SetFont(font);
  161. canvas.SetColor(WMGraphics.Black);
  162. IF horizontalI THEN
  163. x := 0;
  164. menu.Acquire;
  165. child := menu.GetChildren(root);
  166. WHILE (child # NIL) DO
  167. IF ~(child IS Separator) THEN
  168. IF (child = hover) THEN
  169. canvas.Fill(WMRectangles.MakeRect(x, 0, x + ItemWidth(child, horizontalI), bounds.GetHeight()), LONGINT(0FFFF00FFH), WMGraphics.ModeCopy);
  170. ELSIF (child = selection) THEN
  171. canvas.Fill(WMRectangles.MakeRect(x, 0, x + ItemWidth(child, horizontalI), bounds.GetHeight()), clSelected.Get(), WMGraphics.ModeCopy);
  172. END;
  173. x := x + HMenuDistance;
  174. image := menu.GetNodeImage(child);
  175. IF (image # NIL) THEN
  176. canvas.DrawImage(x, 0, image, WMGraphics.ModeSrcOverDst);
  177. x := x + image.width + HMenuDistance + TextImageDistance;
  178. END;
  179. caption := menu.GetNodeCaption(child);
  180. IF (caption # NIL) THEN
  181. font.GetStringSize(caption^, dx, dy); canvas.DrawString(x, dy, caption^);
  182. x := x + dx;
  183. END;
  184. INC(x, HMenuDistance);
  185. ELSE
  186. INC(x, HMenuDistance);
  187. canvas.Line(x + (SeparatorWidth DIV 2) + 1, 2, x + (SeparatorWidth DIV 2) + 1, bounds.GetHeight() - 2, WMGraphics.Black, WMGraphics.ModeCopy);
  188. x := x + SeparatorWidth + HMenuDistance;
  189. END;
  190. child := menu.GetNextSibling(child)
  191. END;
  192. menu.Release;
  193. ELSE
  194. y := 0;
  195. menu.Acquire;
  196. IF (openDirectionI = OpenDownLeft) OR (openDirectionI = OpenUpLeft) THEN
  197. canvas.Fill(WMRectangles.MakeRect(bounds.GetWidth() - greyBoxWidth, 0, bounds.GetWidth() - greyBoxWidth, bounds.GetHeight()), LightGrey, WMGraphics.ModeCopy);
  198. ELSE
  199. canvas.Fill(WMRectangles.MakeRect(0, 0, greyBoxWidth, bounds.GetHeight()), LightGrey, WMGraphics.ModeCopy);
  200. END;
  201. child := menu.GetChildren(root);
  202. WHILE (child # NIL) DO
  203. x := HMenuDistance;
  204. IF ~(child IS Separator) THEN
  205. IF (child = hover) THEN
  206. canvas.Fill(WMRectangles.MakeRect(0, y, bounds.GetWidth(), y + ItemHeight(child)), LONGINT(0FFFF00FFH), WMGraphics.ModeCopy);
  207. ELSIF (child = selection) THEN
  208. canvas.Fill(WMRectangles.MakeRect(0, y, bounds.GetWidth(), y + ItemHeight(child)), clSelected.Get(), WMGraphics.ModeCopy);
  209. END;
  210. INC(y, VMenuDistance);
  211. dy := 0;
  212. image := menu.GetNodeImage(child);
  213. IF (image # NIL) THEN
  214. canvas.DrawImage(x, y, image, WMGraphics.ModeSrcOverDst);
  215. x := x + image.width + HMenuDistance + TextImageDistance;
  216. dy := image.height;
  217. ELSE
  218. x := x + MinImageWidth + HMenuDistance + TextImageDistance;
  219. END;
  220. caption := menu.GetNodeCaption(child);
  221. IF (caption # NIL) THEN
  222. font.GetStringSize(caption^, dx, t);
  223. IF (image # NIL) & (image.height > t) THEN
  224. textY := y + ((image.height + t - font.GetDescent()) DIV 2);
  225. ELSE
  226. textY := y + font.ascent;
  227. dy := t;
  228. END;
  229. canvas.DrawString(x, textY, caption^);
  230. END;
  231. IF menu.GetChildren(child) # NIL THEN
  232. IF subMenuIndicatorImg # NIL THEN
  233. canvas.DrawImage(bounds.GetWidth() - subMenuIndicatorImg.width, 0, subMenuIndicatorImg, WMGraphics.ModeSrcOverDst)
  234. ELSE
  235. canvas.DrawString(bounds.GetWidth() - 10, textY, "...")
  236. END
  237. END;
  238. y := y + dy + VMenuDistance;
  239. ELSE
  240. y := y + VMenuDistance;
  241. canvas.Line(greyBoxWidth + 4, y + (SeparatorHeight DIV 2) + 1, bounds.GetWidth(), y + (SeparatorHeight DIV 2) + 1, LightGrey, WMGraphics.ModeCopy);
  242. y := y + SeparatorHeight + VMenuDistance;
  243. END;
  244. child := menu.GetNextSibling(child)
  245. END;
  246. menu.Release;
  247. END;
  248. END DrawBackground;
  249. (* caller must hold tree lock *)
  250. PROCEDURE ItemWidth(item : WMTrees.TreeNode; isHorizontal : BOOLEAN) : LONGINT;
  251. VAR
  252. width, dx, dy : LONGINT;
  253. font : WMGraphics.Font;
  254. caption : Strings.String;
  255. image : Raster.Image;
  256. BEGIN
  257. ASSERT(menu.HasLock());
  258. width := 0;
  259. IF ~(item IS Separator) THEN
  260. image := menu.GetNodeImage(item);
  261. IF (image # NIL) THEN
  262. width := image.width + HMenuDistance + TextImageDistance;
  263. ELSIF ~(isHorizontal) THEN
  264. width := width + MinImageWidth + HMenuDistance + TextImageDistance;
  265. END;
  266. caption := menu.GetNodeCaption(item);
  267. IF (caption # NIL) THEN
  268. font := GetFont(); font.GetStringSize(caption^, dx, dy);
  269. width := width + dx;
  270. END;
  271. ELSE
  272. width := SeparatorWidth;
  273. END;
  274. width := width + 2*HMenuDistance;
  275. RETURN width;
  276. END ItemWidth;
  277. (* caller must hold tree lock *)
  278. PROCEDURE ItemHeight(item : WMTrees.TreeNode) : LONGINT;
  279. VAR
  280. height, dx, dy : LONGINT;
  281. font : WMGraphics.Font;
  282. caption : Strings.String;
  283. image : Raster.Image;
  284. BEGIN
  285. height := 0;
  286. IF ~(item IS Separator) THEN
  287. caption := menu.GetNodeCaption(item);
  288. IF (caption # NIL) THEN
  289. font := GetFont(); font.GetStringSize(caption^, dx, dy);
  290. height := dy;
  291. END;
  292. image := menu.GetNodeImage(item);
  293. IF (image # NIL) THEN
  294. IF (image.height > height) THEN
  295. height := image.height;
  296. END;
  297. END;
  298. ELSE
  299. height := SeparatorHeight;
  300. END;
  301. height := height + 2 * VMenuDistance;
  302. RETURN height
  303. END ItemHeight;
  304. PROCEDURE MaxImageWidth() : LONGINT;
  305. VAR child : WMTrees.TreeNode; image : WMGraphics.Image; maxWidth : LONGINT;
  306. BEGIN
  307. maxWidth := 0;
  308. menu.Acquire;
  309. child := menu.GetChildren(root);
  310. WHILE (child # NIL) DO
  311. image := menu.GetNodeImage(child);
  312. IF (image # NIL) & (image.width > maxWidth) THEN
  313. maxWidth := image.width;
  314. END;
  315. child := menu.GetNextSibling(child);
  316. END;
  317. menu.Release;
  318. RETURN maxWidth;
  319. END MaxImageWidth;
  320. PROCEDURE IsSelectable(node : WMTrees.TreeNode) : BOOLEAN;
  321. BEGIN
  322. ASSERT(node # NIL);
  323. RETURN ~(node IS Separator);
  324. END IsSelectable;
  325. PROCEDURE FindHorizontal(x : LONGINT) : WMTrees.TreeNode;
  326. VAR p : LONGINT; child : WMTrees.TreeNode;
  327. BEGIN
  328. p := 0;
  329. menu.Acquire;
  330. child := menu.GetChildren(root);
  331. IF (child # NIL) THEN
  332. REPEAT
  333. p := p + ItemWidth(child, horizontalI);
  334. IF p < x THEN child := menu.GetNextSibling(child); END;
  335. UNTIL (child = NIL) OR (p >= x);
  336. END;
  337. menu.Release;
  338. RETURN child;
  339. END FindHorizontal;
  340. PROCEDURE FindVertical(y : LONGINT) : WMTrees.TreeNode;
  341. VAR p : LONGINT; child : WMTrees.TreeNode;
  342. BEGIN
  343. p := 0;
  344. menu.Acquire;
  345. child := menu.GetChildren(root);
  346. IF (child # NIL) THEN
  347. REPEAT
  348. p := p + ItemHeight(child);
  349. IF p < y THEN child := menu.GetNextSibling(child); END;
  350. UNTIL (child = NIL) OR (p >= y);
  351. END;
  352. menu.Release;
  353. RETURN child;
  354. END FindVertical;
  355. PROCEDURE GetItemRect(i : WMTrees.TreeNode; VAR r : WMRectangles.Rectangle);
  356. VAR child : WMTrees.TreeNode;
  357. BEGIN
  358. r.l := 0; r.t := 0;
  359. menu.Acquire;
  360. child := menu.GetChildren(root);
  361. WHILE (child # NIL) & (child # i) DO
  362. IF horizontal.Get() THEN
  363. INC(r.l, ItemWidth(child, horizontalI));
  364. ELSE
  365. INC(r.t, ItemHeight(child));
  366. END;
  367. child := menu.GetNextSibling(child);
  368. END;
  369. IF (child # NIL) THEN r.r := r.l + ItemWidth(child, horizontalI); r.b := r.t + ItemHeight(child) END;
  370. menu.Release
  371. END GetItemRect;
  372. PROCEDURE LeafSelect(item : WMTrees.TreeNode);
  373. VAR data : ANY;
  374. BEGIN
  375. IF parentMenuPanel = NIL THEN
  376. CloseSubMenu(FALSE);
  377. menu.Acquire;
  378. data := menu.GetNodeData(item);
  379. menu.Release;
  380. IF (data # NIL) THEN
  381. onSelect.Call(data);
  382. ELSE
  383. onSelect.Call(item);
  384. END;
  385. IF (parentWindow # NIL) THEN
  386. parentWindow.CloseMenu(SELF, NIL); parentWindow := NIL;
  387. END;
  388. ELSE
  389. parentMenuPanel.LeafSelect(item);
  390. END
  391. END LeafSelect;
  392. PROCEDURE SetSelection(node : WMTrees.TreeNode);
  393. BEGIN
  394. IF (selection # node) THEN
  395. selection := node;
  396. Invalidate;
  397. END;
  398. END SetSelection;
  399. PROCEDURE SelectNode(node : WMTrees.TreeNode; indicateLast : BOOLEAN);
  400. VAR child : WMTrees.TreeNode; r : WMRectangles.Rectangle; x, y : LONGINT;
  401. BEGIN
  402. ASSERT(node # NIL);
  403. menu.Acquire;
  404. child := menu.GetChildren(node);
  405. IF (child # NIL) THEN
  406. GetItemRect(node, r);
  407. IF horizontal.Get() THEN
  408. IF openDirection.Get() IN {OpenUpLeft, OpenUpRight} THEN ToWMCoordinates(r.l, r.t, x, y);
  409. ELSE ToWMCoordinates(r.l, r.b, x, y);
  410. END
  411. ELSE
  412. CASE openDirection.Get() OF
  413. |OpenUpLeft : ToWMCoordinates(r.l, r.b, x, y);
  414. |OpenUpRight : ToWMCoordinates(r.r, r.b, x, y);
  415. |OpenDownLeft : ToWMCoordinates(r.l, r.t, x, y);
  416. |OpenDownRight : ToWMCoordinates(r.r, r.t, x, y);
  417. ELSE
  418. ToWMCoordinates(r.r, r.t, x, y);
  419. END;
  420. END;
  421. CloseSubMenu(indicateLast);
  422. SetSelection(node);
  423. NEW(subMenu, x, y, openDirection.Get(), menu, node, SELF, FALSE, TRUE);
  424. rootMenuPanel.focusPanel := subMenu.menuPanel;
  425. PointerLeave;
  426. ELSE
  427. LeafSelect(node)
  428. END;
  429. menu.Release;
  430. END SelectNode;
  431. PROCEDURE CloseSubMenu(indicateLast : BOOLEAN);
  432. BEGIN
  433. IF (subMenu # NIL) THEN
  434. subMenu.CloseMenu(NIL, NIL); subMenu := NIL;
  435. IF (selection # NIL) THEN
  436. IF indicateLast THEN hover := selection; END;
  437. selection := NIL;
  438. Invalidate;
  439. END;
  440. rootMenuPanel.focusPanel := SELF;
  441. END;
  442. END CloseSubMenu;
  443. PROCEDURE PointerDown*(x, y : LONGINT; keys : SET);
  444. VAR node : WMTrees.TreeNode;
  445. BEGIN
  446. IF horizontal.Get() THEN
  447. node := FindHorizontal(x);
  448. ELSE
  449. node := FindVertical(y);
  450. END;
  451. leftClick := (0 IN keys);
  452. IF leftClick & (node # NIL) & IsSelectable(node) THEN
  453. dragObject := GetDragWrapper(node, menu);
  454. IF (dragObject # NIL) THEN
  455. dragPossible := TRUE;
  456. dragNode := node;
  457. END;
  458. ELSE
  459. CloseSubMenu(FALSE);
  460. END;
  461. END PointerDown;
  462. PROCEDURE PointerUp*(x, y : LONGINT; keys : SET);
  463. VAR node : WMTrees.TreeNode;
  464. BEGIN
  465. IF leftClick THEN
  466. IF horizontal.Get() THEN
  467. node := FindHorizontal(x);
  468. ELSE
  469. node := FindVertical(y);
  470. END;
  471. IF (node # NIL) THEN
  472. IF IsSelectable(node) THEN
  473. SelectNode(node, FALSE);
  474. END;
  475. ELSE
  476. CloseSubMenu(FALSE);
  477. END;
  478. END;
  479. dragPossible := FALSE;
  480. END PointerUp;
  481. PROCEDURE PointerMove*(x, y : LONGINT; keys : SET);
  482. VAR node : WMTrees.TreeNode;
  483. BEGIN
  484. IF dragPossible THEN
  485. IF (ABS(x - downX) > DragDist) OR (ABS(y - downY) > DragDist) THEN
  486. dragPossible := FALSE;
  487. IF (dragObject # NIL) THEN
  488. leftClick := FALSE;
  489. MyStartDrag(dragNode, dragObject);
  490. END;
  491. END;
  492. ELSE
  493. IF horizontal.Get() THEN
  494. node := FindHorizontal(x);
  495. ELSE
  496. node := FindVertical(y);
  497. END;
  498. IF (node # NIL) & ~IsSelectable(node) THEN node := NIL; END;
  499. IF (node # hover) THEN hover := node; Invalidate; END;
  500. END;
  501. END PointerMove;
  502. PROCEDURE PointerLeave*;
  503. BEGIN
  504. IF hover # NIL THEN hover := NIL; Invalidate; END;
  505. END PointerLeave;
  506. PROCEDURE MyStartDrag(node : WMTrees.TreeNode; object : ANY);
  507. VAR
  508. image, canvasImage : WMGraphics.Image; VAR caption : Strings.String;
  509. canvas : WMGraphics.BufferCanvas;
  510. width, height, x : LONGINT;
  511. BEGIN
  512. ASSERT((node # NIL) & (object # NIL));
  513. menu.Acquire;
  514. image := menu.GetNodeImage(node);
  515. caption := menu.GetNodeCaption(node);
  516. height := ItemHeight(node);
  517. menu.Release;
  518. width := bounds.GetWidth();
  519. NEW(canvasImage); Raster.Create(canvasImage, width, height, Raster.BGRA8888);
  520. NEW(canvas, canvasImage);
  521. (* actually should factor out node rendering code in DrawBackground and re-use it here... *)
  522. x := HMenuDistance;
  523. canvas.Fill(WMRectangles.MakeRect(0, 0, greyBoxWidth, height), LightGreyDrag, WMGraphics.ModeSrcOverDst);
  524. canvas.Fill(WMRectangles.MakeRect(greyBoxWidth, 0, width, height), WhiteDrag, WMGraphics.ModeSrcOverDst);
  525. IF (image # NIL) THEN
  526. canvas.DrawImage(x, VMenuDistance, image, WMGraphics.ModeSrcOverDst);
  527. x := x + image.width + HMenuDistance + TextImageDistance;
  528. END;
  529. IF (caption # NIL) THEN
  530. canvas.SetColor(WMGraphics.Black);
  531. WMGraphics.DrawStringInRect(canvas, WMRectangles.MakeRect(x, 0, width, height), FALSE, WMGraphics.AlignLeft, WMGraphics.AlignCenter, caption^);
  532. END;
  533. IF ~StartDrag(object, canvasImage, 0,0,DragWasAccepted, NIL) THEN dragNode := NIL; dragObject := NIL; END;
  534. END MyStartDrag;
  535. PROCEDURE DragWasAccepted(sender, data : ANY);
  536. VAR di : WMWindowManager.DragInfo; itf : WMDropTarget.DropInterface; ignoreRes : WORD;
  537. BEGIN
  538. IF (data # NIL) & (data IS WMWindowManager.DragInfo) THEN
  539. di := data(WMWindowManager.DragInfo);
  540. IF (di.data # NIL) & (di.data IS WMDropTarget.DropTarget) THEN
  541. itf := di.data(WMDropTarget.DropTarget).GetInterface(WMDropTarget.TypeObject);
  542. IF (itf # NIL) & (itf IS WMDropTarget.DropObject) THEN
  543. itf(WMDropTarget.DropObject).Set(dragObject, ignoreRes);
  544. END;
  545. END;
  546. END;
  547. IF (rootMenuPanel.parentWindow # NIL) THEN
  548. rootMenuPanel.parentWindow.Close;
  549. ELSE
  550. rootMenuPanel.CloseSubMenu(FALSE);
  551. END;
  552. END DragWasAccepted;
  553. PROCEDURE CursorUp;
  554. BEGIN
  555. IF horizontal.Get() THEN
  556. IF (openDirectionI = OpenUpLeft) OR (openDirectionI = OpenUpRight) THEN
  557. IF (hover # NIL) & HasChildren(hover, menu) THEN
  558. SelectNode(hover, TRUE);
  559. END;
  560. END;
  561. ELSE
  562. MoveToPrevious;
  563. END;
  564. END CursorUp;
  565. PROCEDURE CursorDown;
  566. BEGIN
  567. IF horizontal.Get() THEN
  568. IF (openDirectionI = OpenDownLeft) OR (openDirectionI = OpenDownRight) THEN
  569. IF (hover # NIL) & HasChildren(hover, menu) THEN
  570. SelectNode(hover, TRUE);
  571. END;
  572. END;
  573. ELSE
  574. MoveToNext;
  575. END;
  576. END CursorDown;
  577. PROCEDURE CursorLeft;
  578. BEGIN
  579. IF horizontal.Get() THEN
  580. MoveToPrevious;
  581. ELSE
  582. IF (openDirectionI = OpenUpLeft) OR (openDirectionI = OpenDownLeft) THEN
  583. Acquire;
  584. IF (hover # NIL) & HasChildren(hover, menu) THEN
  585. SelectNode(hover, TRUE);
  586. END;
  587. Release;
  588. ELSE
  589. IF (parentMenuPanel # NIL) THEN
  590. parentMenuPanel.CloseSubMenu(TRUE);
  591. END;
  592. END;
  593. END;
  594. END CursorLeft;
  595. PROCEDURE CursorRight;
  596. BEGIN
  597. IF horizontal.Get() THEN
  598. MoveToNext;
  599. ELSE
  600. IF (openDirectionI = OpenUpRight) OR (openDirectionI = OpenDownRight) THEN
  601. Acquire;
  602. IF (hover # NIL) & HasChildren(hover, menu) THEN
  603. SelectNode(hover, TRUE);
  604. END;
  605. Release;
  606. ELSE
  607. IF (parentMenuPanel # NIL) THEN
  608. parentMenuPanel.CloseSubMenu(TRUE);
  609. END;
  610. END;
  611. END;
  612. END CursorRight;
  613. PROCEDURE MoveToPrevious;
  614. BEGIN
  615. Acquire;
  616. menu.Acquire;
  617. IF (hover # NIL) THEN
  618. hover := menu.GetPrevSibling(hover);
  619. IF (hover = NIL) THEN
  620. hover := menu.GetLastChild(root);
  621. END;
  622. ELSE
  623. hover := menu.GetLastChild(root);
  624. END;
  625. menu.Release;
  626. Release;
  627. Invalidate;
  628. END MoveToPrevious;
  629. PROCEDURE MoveToNext;
  630. BEGIN
  631. Acquire;
  632. menu.Acquire;
  633. IF (hover # NIL) THEN
  634. hover := menu.GetNextSibling(hover);
  635. IF (hover = NIL) THEN
  636. hover := menu.GetChildren(root);
  637. END;
  638. ELSE
  639. hover := menu.GetChildren(root);
  640. END;
  641. menu.Release;
  642. Release;
  643. Invalidate;
  644. END MoveToNext;
  645. PROCEDURE SelectCurrent;
  646. BEGIN
  647. Acquire;
  648. IF (hover # NIL) THEN
  649. SelectNode(hover, TRUE);
  650. END;
  651. Release;
  652. END SelectCurrent;
  653. PROCEDURE KeyEvent*(ucs : LONGINT; flags: SET; VAR keySym: LONGINT); (** PROTECTED *)
  654. VAR focusPanel : MenuPanel;
  655. BEGIN
  656. ASSERT(IsCallFromSequencer());
  657. IF (Inputs.Release IN flags) THEN RETURN; END;
  658. focusPanel := rootMenuPanel.focusPanel;
  659. IF (focusPanel # NIL) THEN
  660. IF (keySym = Inputs.KsUp) THEN focusPanel.CursorUp;
  661. ELSIF (keySym = Inputs.KsDown) THEN focusPanel.CursorDown;
  662. ELSIF (keySym = Inputs.KsLeft) THEN focusPanel.CursorLeft;
  663. ELSIF (keySym = Inputs.KsRight) THEN focusPanel.CursorRight;
  664. ELSIF (ucs = 20H) OR (keySym = Inputs.KsReturn) THEN focusPanel.SelectCurrent;
  665. ELSIF (keySym = Inputs.KsEscape) THEN
  666. IF (focusPanel.parentMenuPanel # NIL) THEN
  667. focusPanel.parentMenuPanel.CloseSubMenu(TRUE);
  668. ELSIF (focusPanel.parentWindow # NIL) THEN
  669. focusPanel.parentWindow.CloseMenu(NIL, NIL);
  670. END;
  671. ELSE
  672. END;
  673. END;
  674. END KeyEvent;
  675. PROCEDURE FocusLost*;
  676. BEGIN
  677. FocusLost^;
  678. CloseSubMenu(FALSE);
  679. IF (selection # NIL) OR (hover # NIL) THEN
  680. selection := NIL; hover := NIL;
  681. Invalidate;
  682. END;
  683. END FocusLost;
  684. PROCEDURE Finalize*;
  685. BEGIN
  686. Finalize^;
  687. CloseSubMenu(FALSE);
  688. END Finalize;
  689. END MenuPanel;
  690. TYPE
  691. ShadowWindow = OBJECT(WMWindowManager.Window)
  692. VAR
  693. type, color : LONGINT;
  694. PROCEDURE &New(type : LONGINT);
  695. BEGIN
  696. ASSERT((type = Right) OR (type = Bottom));
  697. SELF.type := type;
  698. Init(0, 0, TRUE);
  699. color := 04FH;
  700. END New;
  701. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT);
  702. BEGIN
  703. canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), color, WMGraphics.ModeSrcOverDst);
  704. END Draw;
  705. END ShadowWindow;
  706. TYPE
  707. MenuWindow= OBJECT(WMComponents.FormWindow)
  708. VAR
  709. menuPanel : MenuPanel;
  710. takesFocus : BOOLEAN;
  711. PROCEDURE &Open*(x, y : LONGINT; openDirection : LONGINT; menu : WMTrees.Tree; root : WMTrees.TreeNode; parent : MenuPanel; takesFocus, indicate : BOOLEAN);
  712. VAR width, height, dx, dy : LONGINT; ignore : BOOLEAN; flags : SET;
  713. BEGIN
  714. NEW(menuPanel);
  715. menuPanel.openDirection.Set(openDirection);
  716. menuPanel.SetMenu(menu, root);
  717. menuPanel.SetParent(parent);
  718. IF (indicate) THEN
  719. menu.Acquire;
  720. menuPanel.hover := menu.GetChildren(root);
  721. menu.Release;
  722. END;
  723. SELF.takesFocus := takesFocus;
  724. menuPanel.Measure(width, height);
  725. IF (height < 5) THEN height := 5; END;
  726. IF (width < 5) THEN width := 5; END;
  727. CASE openDirection OF
  728. |OpenUpLeft : dx := -width; dy := -height;
  729. |OpenUpRight : dy := -height;
  730. |OpenDownLeft : dx := -width;
  731. ELSE
  732. dx := 0; dy := 0;
  733. END;
  734. menuPanel.bounds.SetExtents(width, height);
  735. menuPanel.fillColor.Set(WMGraphics.White);
  736. Init(menuPanel.bounds.GetWidth(), menuPanel.bounds.GetHeight(), FALSE);
  737. SetContent(menuPanel);
  738. flags := {WMWindowManager.FlagFrame, WMWindowManager.FlagHidden, WMWindowManager.FlagStayOnTop};
  739. IF ~takesFocus THEN flags := flags + {WMWindowManager.FlagNoFocus}; END;
  740. AddWindow(SELF, x + dx, y + dy, flags);
  741. ignore := manager.TransferPointer(SELF);
  742. manager.SetFocus(SELF);
  743. END Open;
  744. PROCEDURE CloseMenu(sender, data : ANY);
  745. BEGIN
  746. IF ~sequencer.IsCallFromSequencer() THEN
  747. sequencer.ScheduleEvent(SELF.CloseMenu, NIL, NIL);
  748. ELSE
  749. Close;
  750. END
  751. END CloseMenu;
  752. PROCEDURE FocusLost*;
  753. BEGIN
  754. FocusLost^;
  755. IF takesFocus THEN
  756. Close;
  757. END;
  758. END FocusLost;
  759. PROCEDURE Draw*(canvas : WMGraphics.Canvas; w, h, q : LONGINT); (** override *)
  760. BEGIN
  761. Draw^(canvas, w, h, q);
  762. WMGraphicUtilities.DrawRect(canvas, WMRectangles.MakeRect(0, 0, w, h), WMGraphics.Black, WMGraphics.ModeCopy);
  763. END Draw;
  764. END MenuWindow;
  765. VAR
  766. StrMenuPanel : Strings.String;
  767. PROCEDURE AddWindow(window : WMWindowManager.Window; x, y : LONGINT; flags : SET);
  768. VAR
  769. manager : WMWindowManager.WindowManager;
  770. view : WMWindowManager.ViewPort;
  771. oldDecorator : WMWindowManager.Decorator;
  772. BEGIN
  773. ASSERT(window # NIL);
  774. manager := WMWindowManager.GetDefaultManager();
  775. view := WMWindowManager.GetDefaultView();
  776. ASSERT((manager # NIL) & (view # NIL));
  777. manager.lock.AcquireWrite;
  778. oldDecorator := manager.decorate;
  779. manager.decorate := ShadowDecorator;
  780. manager.Add((*ENTIER(view.range.l) +*) x, (*ENTIER(view.range.t) + *)y, window, flags);
  781. manager.decorate := oldDecorator;
  782. manager.lock.ReleaseWrite;
  783. END AddWindow;
  784. PROCEDURE ShadowDecorator(window : WMWindowManager.Window);
  785. VAR shadow : ShadowWindow; l, r, t, b : LONGINT;
  786. PROCEDURE InsertAfter(old, new : WMWindowManager.Window);
  787. BEGIN
  788. new.next := old.next;
  789. new.prev := old;
  790. old.next := new;
  791. new.next.prev := new
  792. END InsertAfter;
  793. PROCEDURE InitShadow(shadow : ShadowWindow);
  794. BEGIN
  795. shadow.manager := window.manager;
  796. shadow.flags := {WMWindowManager.FlagNoFocus, WMWindowManager.FlagHidden};
  797. IF WMWindowManager.FlagStayOnBottom IN window.flags THEN INCL(shadow.flags, WMWindowManager.FlagStayOnBottom); END;
  798. IF WMWindowManager.FlagNoResizing IN window.flags THEN INCL(shadow.flags, WMWindowManager.FlagNoResizing); END;
  799. IF WMWindowManager.FlagNavigation IN window.flags THEN
  800. shadow.view := window.view;
  801. INCL(shadow.flags, WMWindowManager.FlagNavigation);
  802. END;
  803. InsertAfter(window, shadow);
  804. shadow.manager.AddDecorWindow(window, shadow);
  805. shadow.manager.AddVisibleDirty(shadow, shadow.bounds);
  806. END InitShadow;
  807. BEGIN
  808. ASSERT((window.manager # NIL) & (window.manager.lock.HasWriteLock()));
  809. l := window.bounds.l; r := window.bounds.r; t := window.bounds.t; b := window.bounds.b;
  810. NEW(shadow, Right); window.rightW := shadow;
  811. shadow.bounds := WMRectangles.MakeRect(r, t + ShadowOffsetVertical, r + ShadowWidth, b + ShadowHeight);
  812. InitShadow(shadow);
  813. NEW(shadow, Bottom); window.bottomW := shadow;
  814. shadow.bounds := WMRectangles.MakeRect(l + ShadowOffsetHorizontal, b, r, b + ShadowHeight);
  815. InitShadow(shadow);
  816. END ShadowDecorator;
  817. PROCEDURE HasChildren(parent : WMTrees.TreeNode; tree : WMTrees.Tree) : BOOLEAN;
  818. VAR hasChildren : BOOLEAN;
  819. BEGIN
  820. ASSERT(tree # NIL);
  821. IF (parent # NIL) THEN
  822. tree.Acquire;
  823. hasChildren := tree.GetChildren(parent) # NIL;
  824. tree.Release;
  825. ELSE
  826. hasChildren := FALSE;
  827. END;
  828. RETURN hasChildren;
  829. END HasChildren;
  830. PROCEDURE GetCaption*(data : ANY; menu : WMTrees.Tree) : Strings.String;
  831. VAR caption : Strings.String;
  832. BEGIN
  833. ASSERT(menu # NIL);
  834. IF (data # NIL) & (data IS WMTrees.TreeNode) THEN
  835. menu.Acquire;
  836. caption := menu.GetNodeCaption(data(WMTrees.TreeNode));
  837. menu.Release;
  838. ELSE
  839. caption := NIL;
  840. END;
  841. RETURN caption;
  842. END GetCaption;
  843. PROCEDURE GetDragWrapper*(node : WMTrees.TreeNode; menu : WMTrees.Tree) : DragWrapper;
  844. VAR data: ANY; drag : DragWrapper
  845. BEGIN
  846. ASSERT(menu # NIL);
  847. drag := NIL;
  848. IF (node # NIL) THEN
  849. menu.Acquire;
  850. data := menu.GetNodeData(node);
  851. menu.Release;
  852. IF (data # NIL) & (data IS DragWrapper) THEN
  853. drag := data(DragWrapper);
  854. END;
  855. END;
  856. RETURN drag;
  857. END GetDragWrapper;
  858. PROCEDURE FindChild(CONST caption : ARRAY OF CHAR; parent : WMTrees.TreeNode; tree : WMTrees.Tree) : WMTrees.TreeNode;
  859. VAR child : WMTrees.TreeNode; string : Strings.String; found : BOOLEAN;
  860. BEGIN
  861. ASSERT((parent # NIL) & (tree # NIL) & (tree.HasLock()));
  862. found := FALSE;
  863. child := tree.GetChildren(parent);
  864. WHILE (child # NIL) & ~found DO
  865. string := tree.GetNodeCaption(child);
  866. found := (string # NIL) & (string^ = caption);
  867. IF ~found THEN
  868. child := tree.GetNextSibling(child);
  869. END;
  870. END;
  871. RETURN child;
  872. END FindChild;
  873. PROCEDURE AddChild*(CONST caption : ARRAY OF CHAR; parent : WMTrees.TreeNode; tree : WMTrees.Tree) : WMTrees.TreeNode;
  874. VAR node : WMTrees.TreeNode; separator : Separator;
  875. BEGIN
  876. ASSERT((parent # NIL) & (tree # NIL) & (tree.HasLock()));
  877. IF (caption # SeparatorCaption) THEN
  878. NEW(node);
  879. tree.SetNodeCaption(node, Strings.NewString(caption));
  880. ELSE
  881. NEW(separator);
  882. node := separator;
  883. END;
  884. tree.AddChildNode(parent, node);
  885. RETURN node;
  886. END AddChild;
  887. PROCEDURE Find*(CONST path : ARRAY OF CHAR; menu : WMTrees.Tree) : WMTrees.TreeNode;
  888. VAR caption : ARRAY 256 OF CHAR; child, node, parent : WMTrees.TreeNode; i, j : LONGINT;
  889. BEGIN
  890. ASSERT(menu # NIL);
  891. node := NIL;
  892. menu.Acquire;
  893. parent := menu.GetRoot();
  894. IF (parent # NIL) THEN
  895. caption := "";
  896. i := 0; j := 0;
  897. LOOP
  898. IF (i >= LEN(path)) THEN
  899. EXIT;
  900. ELSIF (path[i] = ".") OR (path[i] = 0X) THEN
  901. caption[j] := 0X;
  902. child := FindChild(caption, parent, menu);
  903. IF (child = NIL) THEN
  904. EXIT;
  905. END;
  906. parent := child;
  907. IF (path[i] = 0X) THEN
  908. node := child;
  909. EXIT;
  910. ELSE
  911. caption := ""; j := 0;
  912. END;
  913. ELSIF (j < LEN(caption) - 1) THEN
  914. caption[j] := path[i];
  915. INC(j);
  916. END;
  917. INC(i);
  918. END;
  919. END;
  920. menu.Release;
  921. RETURN node;
  922. END Find;
  923. PROCEDURE AddItemNode*(CONST path : ARRAY OF CHAR; menu : WMTrees.Tree) : WMTrees.TreeNode;
  924. VAR caption : ARRAY 256 OF CHAR; node, parent : WMTrees.TreeNode; i, j : LONGINT;
  925. BEGIN
  926. ASSERT(menu # NIL);
  927. menu.Acquire;
  928. IF (menu.GetRoot() = NIL) THEN
  929. NEW(node); menu.SetRoot(node)
  930. END;
  931. i := 0; j := 0;
  932. caption := ""; parent := menu.GetRoot();
  933. LOOP
  934. IF (i >= LEN(path)) THEN
  935. EXIT;
  936. ELSIF (path[i] = ".") OR (path[i] = 0X) THEN
  937. caption[j] := 0X;
  938. node := FindChild(caption, parent, menu);
  939. IF (node = NIL) THEN
  940. node := AddChild(caption, parent, menu);
  941. END;
  942. parent := node;
  943. caption := ""; j := 0;
  944. IF (path[i] = 0X) THEN EXIT; END;
  945. ELSIF (j < LEN(caption) - 1) THEN
  946. caption[j] := path[i];
  947. INC(j);
  948. END;
  949. INC(i);
  950. END;
  951. menu.Release;
  952. ASSERT(node # NIL);
  953. RETURN node;
  954. END AddItemNode;
  955. PROCEDURE AddItem*(CONST path : ARRAY OF CHAR; menu : WMTrees.Tree);
  956. VAR ignore : WMTrees.TreeNode;
  957. BEGIN
  958. ignore := AddItemNode(path, menu);
  959. END AddItem;
  960. PROCEDURE Show*(menu : WMTrees.Tree; x, y : LONGINT; handler : WMEvents.EventListener);
  961. VAR window : MenuWindow; root : WMTrees.TreeNode;
  962. BEGIN
  963. ASSERT((menu # NIL) & (handler # NIL));
  964. menu.Acquire;
  965. root := menu.GetRoot();
  966. menu.Release;
  967. IF (root # NIL) THEN
  968. NEW(window, x, y, OpenDefault, menu, root, NIL, TRUE, FALSE);
  969. window.menuPanel.SetParentWindow(window);
  970. window.menuPanel.onSelect.Add(handler);
  971. END;
  972. END Show;
  973. BEGIN
  974. StrMenuPanel := Strings.NewString("MenuPanel");
  975. END WMMenus.