StartMenu.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. MODULE StartMenu;
  2. IMPORT
  3. Strings, XML, KernelLog, Modules, Inputs, UTF8Strings, XMLObjects,
  4. MainMenu, WM := WMWindowManager, WMComponents, WMStandardComponents, WMProperties, WMMessages, WMEvents,
  5. WMRectangles;
  6. CONST
  7. DefaultPlugin = "BlockStartMenu";
  8. FancyMenuDesc = "Dummy.XML";
  9. MaxMenus = 16;
  10. MaxMenuButtons = 10;
  11. TYPE
  12. String = Strings.String;
  13. EventListenerInfo = WMEvents.EventListenerInfo;
  14. Message = WMMessages.Message;
  15. Popup = OBJECT (WMComponents.FormWindow)
  16. PROCEDURE FocusLost*;
  17. BEGIN manager.Remove(SELF) END FocusLost;
  18. PROCEDURE FocusGot*;
  19. BEGIN manager.SetFocus(SELF) END FocusGot;
  20. PROCEDURE Handle*(VAR msg : Message);
  21. BEGIN
  22. Handle^(msg);
  23. IF (msg.msgType = WMMessages.MsgExt) & (msg.ext = closePopupMsg) THEN FocusLost END;
  24. END Handle;
  25. END Popup;
  26. ClosePopupMsg = OBJECT
  27. END ClosePopupMsg;
  28. PluginManager = OBJECT
  29. VAR
  30. plugin : Plugin;
  31. currentPluginName : Strings.String; (* { currentPluginName # NIL } *)
  32. factory : PluginFactory;
  33. startIndex : LONGINT;
  34. PROCEDURE & New*;
  35. BEGIN
  36. NEW(factory);
  37. currentPluginName := NIL;
  38. SetPlugin(Strings.NewString(DefaultPlugin));
  39. startIndex := 0;
  40. END New;
  41. PROCEDURE Refresh;
  42. BEGIN {EXCLUSIVE}
  43. IF manager = NIL THEN manager := WM.GetDefaultManager() END;
  44. SetPlugin(pluginName.Get());
  45. END Refresh;
  46. PROCEDURE SetPlugin(name : String);
  47. BEGIN
  48. IF plugin # NIL THEN
  49. plugin.Close;
  50. END;
  51. IF (name # NIL) & ((currentPluginName=NIL) OR (name^ # currentPluginName^)) THEN
  52. currentPluginName := name;
  53. plugin := factory.Get(name);
  54. startIndex := 0;
  55. END;
  56. IF plugin # NIL THEN
  57. plugin.Open
  58. END
  59. END SetPlugin;
  60. PROCEDURE Close;
  61. BEGIN
  62. IF plugin # NIL THEN plugin.Close END
  63. END Close;
  64. PROCEDURE ShiftMenuItems(upwards : BOOLEAN);
  65. BEGIN
  66. plugin.ReopenMenuItemsShifted(upwards)
  67. END ShiftMenuItems;
  68. END PluginManager;
  69. PluginFactory = OBJECT
  70. PROCEDURE Get(type : String) : Plugin;
  71. VAR a : ANY;
  72. BEGIN
  73. IF type^ = "FancyStartMenu" THEN
  74. a := GenFancyStartMenu(); RETURN a(Plugin)
  75. ELSIF type^ = "BlockStartMenu" THEN
  76. a := GenBlockStartMenu(); RETURN a(Plugin)
  77. ELSE (* return default *)
  78. a := GenBlockStartMenu(); RETURN a(Plugin)
  79. END
  80. END Get;
  81. END PluginFactory;
  82. Plugin = OBJECT
  83. PROCEDURE Open;
  84. BEGIN HALT(311) END Open;
  85. PROCEDURE Close;
  86. BEGIN HALT(311) END Close;
  87. PROCEDURE ReopenMenuItemsShifted(upwards : BOOLEAN);
  88. BEGIN HALT(311) END ReopenMenuItemsShifted;
  89. END Plugin;
  90. SubMenuOpener* = OBJECT(WMComponents.Component)
  91. VAR filename : WMProperties.StringProperty;
  92. eRun* : EventListenerInfo;
  93. PROCEDURE &Init*;
  94. BEGIN
  95. Init^;
  96. NEW(filename, NIL, Strings.NewString("Filename"), Strings.NewString("")); properties.Add(filename);
  97. NEW(eRun, Strings.NewString("Run"), Strings.NewString(""), SELF.Run); eventListeners.Add(eRun);
  98. END Init;
  99. PROCEDURE Run*(sender, par : ANY); (** Eventhandler *)
  100. VAR x, y : LONGINT; filename : String;
  101. rect : WMRectangles.Rectangle;
  102. BEGIN
  103. (* synchronize if not synchronized *)
  104. IF ~IsCallFromSequencer() THEN sequencer.ScheduleEvent(SELF.Run, sender, par)
  105. ELSE
  106. (* actual business logic *)
  107. filename := SELF.filename.Get();
  108. IF filename # NIL THEN
  109. rect := sender(WMComponents.VisualComponent).bounds.Get();
  110. sender(WMComponents.VisualComponent).ToWMCoordinates(0, 0, x, y);
  111. OpenPopup(x, y, filename^);
  112. END
  113. END
  114. END Run;
  115. END SubMenuOpener;
  116. MenuButtons = ARRAY MaxMenuButtons OF WMStandardComponents.Button;
  117. SMOArr = ARRAY MaxMenuButtons OF SubMenuOpener;
  118. FancyStartMenu = OBJECT(Plugin)
  119. VAR startMenu : WMComponents.FormWindow;
  120. nofLayouts, nofButtons, nofSMOs : LONGINT;
  121. menuButtons : MenuButtons;
  122. smos : SMOArr;
  123. PROCEDURE CountLayouts() : LONGINT;
  124. VAR i, n : LONGINT; done : BOOLEAN; s : Strings.String;
  125. BEGIN
  126. n := 0; done := FALSE;
  127. WHILE (i < LEN(layoutNames) - 1) & ~done DO
  128. s := layoutNames[i].Get();
  129. IF UTF8Strings.Compare(s^, FancyMenuDesc) # UTF8Strings.CmpEqual THEN
  130. INC(n)
  131. ELSE
  132. done := TRUE
  133. END;
  134. INC(i)
  135. END;
  136. RETURN n
  137. END CountLayouts;
  138. PROCEDURE FindSMO(c : XML.Content; VAR smo : SubMenuOpener);
  139. VAR enum : XMLObjects.Enumerator; found : BOOLEAN; ptr : ANY;
  140. BEGIN
  141. smo := NIL;
  142. IF c IS WMComponents.VisualComponent THEN
  143. enum := c(WMComponents.VisualComponent).GetContents();
  144. found := FALSE;
  145. WHILE enum.HasMoreElements() & ~found DO
  146. ptr := enum.GetNext();
  147. IF ptr IS SubMenuOpener THEN
  148. smo := ptr(SubMenuOpener);
  149. found := TRUE
  150. END
  151. END
  152. END;
  153. END FindSMO;
  154. PROCEDURE FindMenuButtonsSMO(c : XML.Content; VAR menuButtons : MenuButtons; VAR smos: SMOArr; VAR n, m : LONGINT);
  155. VAR enum : XMLObjects.Enumerator; i, j : LONGINT; ptr : ANY; s : Strings.String;
  156. b : WMStandardComponents.Button; smo : SubMenuOpener;
  157. BEGIN
  158. IF c IS WMComponents.VisualComponent THEN
  159. enum := c(WMComponents.VisualComponent).GetContents();
  160. i := 0; j := 0;
  161. WHILE enum.HasMoreElements() DO
  162. ptr := enum.GetNext();
  163. IF ptr IS WMStandardComponents.Button THEN
  164. b := ptr(WMStandardComponents.Button);
  165. s := b.caption.Get();
  166. IF (s # NIL) & (UTF8Strings.Compare(s^, "") # UTF8Strings.CmpEqual) THEN
  167. menuButtons[i] := b; INC(i);
  168. FindSMO(b, smo);
  169. IF smo # NIL THEN smos[j] := smo; INC(j) END;
  170. END;
  171. END;
  172. END;
  173. n := i; m := j
  174. ELSE
  175. n := 0; m := 0;
  176. END;
  177. END FindMenuButtonsSMO;
  178. PROCEDURE Open;
  179. VAR c : XML.Content; width, height : LONGINT; view : WM.ViewPort; s : String;
  180. BEGIN
  181. nofLayouts := CountLayouts();
  182. s := layoutNames[pm.startIndex].Get();
  183. KernelLog.String("loading "); KernelLog.String(s^); KernelLog.Ln;
  184. c := WMComponents.Load(s^);
  185. IF (c # NIL) & (c IS WMComponents.VisualComponent) THEN
  186. FindMenuButtonsSMO(c, menuButtons, smos, nofButtons, nofSMOs);
  187. width := c(WMComponents.VisualComponent).bounds.GetWidth();
  188. height := c(WMComponents.VisualComponent).bounds.GetHeight();
  189. NEW(startMenu, width, height, TRUE);
  190. startMenu.SetTitle(Strings.NewString("StartMenu"));
  191. startMenu.pointerThreshold := 10;
  192. startMenu.DisableUpdate;
  193. startMenu.SetContent(c);
  194. startMenu.EnableUpdate;
  195. startMenu.Invalidate(startMenu.bounds);
  196. view := WM.GetDefaultView();
  197. manager := WM.GetDefaultManager();
  198. manager.Add(ENTIER(view.range.l), ENTIER(view.range.b) - height + 1, startMenu, {WM.FlagHidden});
  199. ELSE
  200. KernelLog.String("XML-file not correctly loaded"); KernelLog.Ln
  201. END
  202. END Open;
  203. PROCEDURE Close;
  204. BEGIN {EXCLUSIVE}
  205. IF startMenu # NIL THEN startMenu.Close END
  206. END Close;
  207. PROCEDURE ReplaceMenuButtonsSMO(CONST mb, newmb : MenuButtons; CONST smos, newsmos : SMOArr);
  208. VAR i : LONGINT; s : Strings.String;
  209. BEGIN
  210. FOR i := 0 TO nofButtons - 1 DO
  211. s := newmb[i].caption.Get();
  212. mb[i].caption.Set(s);
  213. s := newsmos[i].filename.Get();
  214. smos[i].filename.Set(s);
  215. END;
  216. END ReplaceMenuButtonsSMO;
  217. PROCEDURE ReopenMenuItemsShifted(upwards : BOOLEAN);
  218. VAR old, i : LONGINT; s : String; c : XML.Content;
  219. newMButtons : MenuButtons;
  220. newsmos : SMOArr;
  221. n, m : LONGINT;
  222. BEGIN
  223. old := pm.startIndex;
  224. IF upwards THEN
  225. pm.startIndex := (pm.startIndex + 1) MOD nofLayouts
  226. ELSE
  227. pm.startIndex := (pm.startIndex - 1) MOD nofLayouts
  228. END;
  229. IF old # pm.startIndex THEN
  230. s := layoutNames[pm.startIndex].Get();
  231. c := WMComponents.Load(s^);
  232. IF (c # NIL) & (c IS WMComponents.VisualComponent) THEN
  233. FindMenuButtonsSMO(c, newMButtons, newsmos, n, m);
  234. IF (nofButtons = nofSMOs) & (nofButtons = n) & (nofSMOs = m) THEN
  235. ReplaceMenuButtonsSMO(menuButtons, newMButtons, smos, newsmos);
  236. FOR i := 0 TO nofButtons - 1 DO
  237. menuButtons[i].Invalidate;
  238. END;
  239. ELSE
  240. KernelLog.String("layout "); KernelLog.String(s^); KernelLog.String(" does not match."); KernelLog.Ln
  241. END
  242. ELSE
  243. KernelLog.String("XML-file not correctly loaded"); KernelLog.Ln
  244. END
  245. END
  246. END ReopenMenuItemsShifted;
  247. END FancyStartMenu;
  248. BlockStartMenu = OBJECT(Plugin)
  249. VAR startMenu : MainMenu.Window;
  250. PROCEDURE Open;
  251. BEGIN
  252. NEW(startMenu);
  253. startMenu.SetOriginator(NIL); (* includes page loading *)
  254. END Open;
  255. PROCEDURE Close;
  256. BEGIN
  257. IF startMenu # NIL THEN startMenu.Close END
  258. END Close;
  259. PROCEDURE ReopenMenuItemsShifted(upwards : BOOLEAN);
  260. END ReopenMenuItemsShifted;
  261. END BlockStartMenu;
  262. (* the starter decouples the sensitive callback from the WindowManager. *)
  263. Starter = OBJECT
  264. VAR originator : ANY;
  265. PROCEDURE &Init*(o : ANY);
  266. BEGIN
  267. originator := o
  268. END Init;
  269. BEGIN {ACTIVE}
  270. pm.Refresh
  271. END Starter;
  272. VAR
  273. stringPrototype, pluginName : WMProperties.StringProperty;
  274. layoutNames : ARRAY MaxMenus OF WMProperties.StringProperty;
  275. manager : WM.WindowManager;
  276. pm : PluginManager;
  277. p : Popup;
  278. closePopupMsg : ClosePopupMsg;
  279. PROCEDURE OpenPopup*(x, y : LONGINT; CONST filename : ARRAY OF CHAR);
  280. VAR m : WM.WindowManager;
  281. c : XML.Content;
  282. width, height : LONGINT;
  283. BEGIN
  284. c := WMComponents.Load(filename);
  285. IF (c # NIL) & (c IS WMComponents.VisualComponent) THEN
  286. width := c(WMComponents.VisualComponent).bounds.GetWidth();
  287. height := c(WMComponents.VisualComponent).bounds.GetHeight();
  288. IF width <= 0 THEN width := 10 END; IF height <= 0 THEN height := 10 END;
  289. NEW(p, width, height, TRUE);
  290. p.SetContent(c);
  291. m := WM.GetDefaultManager(); m.Add(x, y-height, p, {WM.FlagHidden}); m.SetFocus(p)
  292. ELSE
  293. KernelLog.String(filename); KernelLog.String(" not correctly loaded"); KernelLog.Ln
  294. END
  295. END OpenPopup;
  296. PROCEDURE ClosePopup*;
  297. VAR msg : WMMessages.Message; manager : WM.WindowManager;
  298. BEGIN
  299. msg.msgType := WMMessages.MsgExt; msg.ext := closePopupMsg;
  300. manager := WM.GetDefaultManager();
  301. manager.Broadcast(msg);
  302. END ClosePopup;
  303. PROCEDURE GenSubMenuOpener*() : XML.Element;
  304. VAR smo : SubMenuOpener;
  305. BEGIN
  306. NEW(smo); RETURN smo
  307. END GenSubMenuOpener;
  308. PROCEDURE Open*;
  309. BEGIN
  310. pm.SetPlugin(pluginName.Get());
  311. END Open;
  312. (* load start menu with buttons shifted to the right *)
  313. PROCEDURE ShiftMenuItemsRight*;
  314. BEGIN
  315. pm.ShiftMenuItems(TRUE);
  316. END ShiftMenuItemsRight;
  317. (* load start menu with buttons shifted to the left *)
  318. PROCEDURE ShiftMenuItemsLeft*;
  319. BEGIN
  320. pm.ShiftMenuItems(FALSE);
  321. END ShiftMenuItemsLeft;
  322. (* This procedure is directly called by the window manager. It must be safe. *)
  323. PROCEDURE MessagePreview(VAR m : WMMessages.Message; VAR discard : BOOLEAN);
  324. VAR starter : Starter;
  325. BEGIN
  326. IF m.msgType = WMMessages.MsgKey THEN
  327. IF (m.y = 0FF1BH) & ((m.flags * Inputs.Ctrl # {}) OR (m.flags * Inputs.Meta # {})) THEN
  328. NEW(starter, m.originator); discard := TRUE
  329. END;
  330. ELSIF (m.msgType = WMMessages.MsgExt) & (m.ext = WMComponents.componentStyleMsg) THEN
  331. NEW(starter, m.originator);
  332. END
  333. END MessagePreview;
  334. PROCEDURE GenFancyStartMenu() : Plugin;
  335. VAR menu : FancyStartMenu;
  336. BEGIN NEW(menu); RETURN menu
  337. END GenFancyStartMenu;
  338. PROCEDURE GenBlockStartMenu() : Plugin;
  339. VAR menu : BlockStartMenu;
  340. BEGIN NEW(menu); RETURN menu
  341. END GenBlockStartMenu;
  342. PROCEDURE InitPrototypes;
  343. VAR plStartMenu : WMProperties.PropertyList;
  344. s0, s1 : ARRAY 128 OF CHAR;
  345. i : LONGINT;
  346. BEGIN
  347. NEW(plStartMenu); WMComponents.propertyListList.Add("StartMenu", plStartMenu);
  348. NEW(stringPrototype, NIL, Strings.NewString("Plugin"), Strings.NewString("Plug-in-object that creates start-menu and determines its properties"));
  349. stringPrototype.Set(Strings.NewString(DefaultPlugin));
  350. NEW(pluginName, stringPrototype, NIL, NIL); plStartMenu.Add(pluginName);
  351. FOR i := 0 TO LEN(layoutNames) - 1 DO
  352. Strings.IntToStr(i, s0);
  353. COPY("Layout", s1);
  354. Strings.Append(s1, s0);
  355. NEW(stringPrototype, NIL, Strings.NewString(s1), Strings.NewString("XML-file that determins content and layout of the fancy start-menu"));
  356. stringPrototype.Set(Strings.NewString(FancyMenuDesc));
  357. NEW(layoutNames[i], stringPrototype, NIL, NIL); plStartMenu.Add(layoutNames[i]);
  358. END;
  359. END InitPrototypes;
  360. PROCEDURE Cleanup;
  361. BEGIN
  362. IF pm # NIL THEN pm.Close END;
  363. manager.RemoveMessagePreview(MessagePreview)
  364. END Cleanup;
  365. PROCEDURE Fancy*;
  366. BEGIN
  367. pluginName.Set(Strings.NewString("FancyStartMenu"));
  368. END Fancy;
  369. PROCEDURE Block*;
  370. BEGIN
  371. pluginName.Set(Strings.NewString("BlockStartMenu"));
  372. END Block;
  373. BEGIN
  374. NEW(pm);
  375. NEW(closePopupMsg);
  376. InitPrototypes;
  377. Modules.InstallTermHandler(Cleanup);
  378. manager := WM.GetDefaultManager();
  379. manager.InstallMessagePreview(MessagePreview);
  380. END StartMenu.