WMClock.Mod 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. MODULE WMClock; (** AUTHOR "TF/staubesv"; PURPOSE "Clock components & clock application"; *)
  2. IMPORT
  3. Modules, Kernel, Math, Dates, Strings, Locks, XML, Raster, WMRasterScale, WMRectangles, WMGraphics, WMGraphicUtilities,
  4. WMWindowManager, WMPopups, WMRestorable, WMMessages, WMComponents, WMProperties;
  5. CONST
  6. (** Clock.viewMode property *)
  7. ViewModeStandard* = 0;
  8. ViewModeDateTime* = 1;
  9. ViewModeDayOfWeek* = 2;
  10. ViewModeAnalog* = 3;
  11. ViewModeFormatted*= 4;
  12. WindowWidth = 150; WindowHeight = 50;
  13. TYPE
  14. ContextMenuPar = OBJECT
  15. VAR
  16. mode : LONGINT;
  17. PROCEDURE &New*(m : LONGINT);
  18. BEGIN
  19. mode := m;
  20. END New;
  21. END ContextMenuPar;
  22. TYPE
  23. KillerMsg = OBJECT
  24. END KillerMsg;
  25. Window = OBJECT(WMComponents.FormWindow);
  26. VAR
  27. clock : Clock;
  28. imageNameAnalog : Strings.String;
  29. contextMenu : WMPopups.Popup;
  30. dragging, resizing : BOOLEAN;
  31. lastX, lastY : LONGINT;
  32. PROCEDURE &New*(context : WMRestorable.Context; flags : SET);
  33. VAR configuration : WMRestorable.XmlElement; viewMode, color : LONGINT;
  34. BEGIN
  35. IncCount;
  36. IF (context # NIL) THEN
  37. Init(context.r - context.l, context.b - context.t, TRUE);
  38. ELSE
  39. Init(WindowWidth, WindowHeight, TRUE);
  40. END;
  41. NEW(clock);
  42. clock.alignment.Set(WMComponents.AlignClient);
  43. imageNameAnalog := clock.imageName.Get();
  44. IF (clock.viewMode.Get() # ViewModeAnalog) THEN clock.imageName.Set(NIL); END;
  45. SetContent(clock);
  46. SetTitle(Strings.NewString("Clock"));
  47. IF (context # NIL) THEN
  48. configuration := WMRestorable.GetElement(context, "Configuration");
  49. IF (configuration # NIL) THEN
  50. WMRestorable.LoadLongint(configuration, "color", color); clock.color.Set(color);
  51. WMRestorable.LoadLongint(configuration, "viewMode", viewMode); clock.viewMode.Set(viewMode);
  52. END;
  53. WMRestorable.AddByContext(SELF, context);
  54. ELSE
  55. IF (WMWindowManager.FlagNavigation IN flags) THEN
  56. WMWindowManager.ExtAddViewBoundWindow(SELF, 50, 50, NIL, flags);
  57. ELSE
  58. WMWindowManager.ExtAddWindow(SELF, 50, 50, flags)
  59. END;
  60. END;
  61. END New;
  62. PROCEDURE Close*;
  63. BEGIN
  64. Close^;
  65. DecCount;
  66. END Close;
  67. PROCEDURE HandleClose(sender, par: ANY);
  68. VAR manager : WMWindowManager.WindowManager;
  69. BEGIN
  70. manager := WMWindowManager.GetDefaultManager();
  71. manager.SetFocus(SELF);
  72. Close;
  73. END HandleClose;
  74. PROCEDURE HandleToggleColor(sender, data: ANY);
  75. BEGIN
  76. IF (clock.color.Get() = 0FFH) THEN clock.color.Set(LONGINT(0FFFFFFFFH)) ELSE clock.color.Set(0FFH) END;
  77. END HandleToggleColor;
  78. PROCEDURE HandleToggleView(sender, par: ANY);
  79. VAR manager : WMWindowManager.WindowManager; viewMode : LONGINT;
  80. BEGIN
  81. manager := WMWindowManager.GetDefaultManager();
  82. manager.SetFocus(SELF);
  83. IF (par # NIL) & (par IS ContextMenuPar) THEN
  84. viewMode := par(ContextMenuPar).mode;
  85. IF (clock.viewMode.Get() # viewMode) THEN
  86. IF (par(ContextMenuPar).mode = ViewModeAnalog) THEN
  87. clock.imageName.Set(imageNameAnalog);
  88. ELSE
  89. clock.imageName.Set(NIL);
  90. END;
  91. clock.viewMode.Set(par(ContextMenuPar).mode);
  92. END;
  93. ELSE
  94. clock.viewMode.Set(ViewModeStandard);
  95. clock.imageName.Set(NIL);
  96. END
  97. END HandleToggleView;
  98. PROCEDURE PointerDown*(x, y:LONGINT; keys:SET);
  99. BEGIN
  100. lastX := bounds.l + x; lastY:=bounds.t + y;
  101. IF keys = {0} THEN
  102. dragging := TRUE
  103. ELSIF keys = {1,2} THEN
  104. dragging := FALSE;
  105. resizing := TRUE;
  106. ELSIF keys = {2} THEN
  107. NEW(contextMenu);
  108. contextMenu.Add("Close", HandleClose);
  109. contextMenu.AddParButton("Time", HandleToggleView, contextMenuParStandard);
  110. contextMenu.AddParButton("Date", HandleToggleView, contextMenuParDateTime);
  111. contextMenu.AddParButton("Day of Week", HandleToggleView, contextMenuParDayOfWeek);
  112. contextMenu.AddParButton("Analog", HandleToggleView, contextMenuParAnalog);
  113. contextMenu.AddParButton("Toggle Color", HandleToggleColor, NIL);
  114. contextMenu.Popup(bounds.l + x, bounds.t + y)
  115. END
  116. END PointerDown;
  117. PROCEDURE PointerMove*(x,y:LONGINT; keys:SET);
  118. VAR dx, dy, width, height : LONGINT;
  119. BEGIN
  120. IF dragging OR resizing THEN
  121. x := bounds.l + x; y := bounds.t + y; dx := x - lastX; dy := y - lastY;
  122. lastX := lastX + dx; lastY := lastY + dy;
  123. IF (dx # 0) OR (dy # 0) THEN
  124. IF dragging THEN
  125. manager.SetWindowPos(SELF, bounds.l + dx, bounds.t + dy);
  126. ELSE
  127. width := GetWidth();
  128. height := GetHeight();
  129. width := MAX(10, width + dx);
  130. height := MAX(10, height + dy);
  131. manager.SetWindowSize(SELF, width, height);
  132. END;
  133. END;
  134. END;
  135. END PointerMove;
  136. PROCEDURE PointerUp*(x, y:LONGINT; keys:SET);
  137. BEGIN
  138. dragging := FALSE;
  139. IF (keys # {1,2}) THEN
  140. IF resizing THEN
  141. resizing := FALSE;
  142. Resized(GetWidth(), GetHeight());
  143. END;
  144. END;
  145. END PointerUp;
  146. PROCEDURE Handle*(VAR x: WMMessages.Message);
  147. VAR configuration : WMRestorable.XmlElement;
  148. BEGIN
  149. IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) THEN
  150. IF (x.ext IS KillerMsg) THEN
  151. Close;
  152. ELSIF (x.ext IS WMRestorable.Storage) THEN
  153. NEW(configuration); configuration.SetName("Configuration");
  154. WMRestorable.StoreBoolean(configuration, "stayOnTop", WMWindowManager.FlagStayOnTop IN flags);
  155. WMRestorable.StoreBoolean(configuration, "navigation", WMWindowManager.FlagNavigation IN flags);
  156. WMRestorable.StoreLongint(configuration, "color", clock.color.Get());
  157. WMRestorable.StoreLongint(configuration, "viewMode", clock.viewMode.Get());
  158. x.ext(WMRestorable.Storage).Add("WMClock", "WMClock.Restore", SELF, configuration);
  159. ELSE Handle^(x)
  160. END
  161. ELSE Handle^(x)
  162. END
  163. END Handle;
  164. END Window;
  165. TYPE
  166. Clock* = OBJECT(WMComponents.VisualComponent)
  167. VAR
  168. viewMode- : WMProperties.Int32Property;
  169. color- : WMProperties.ColorProperty;
  170. (** background image filename *)
  171. imageName- : WMProperties.StringProperty;
  172. (** time offset in hours *)
  173. timeOffset- : WMProperties.Int32Property;
  174. (** hand lengths in percent of component width/height *)
  175. secondHandLength-, minuteHandLength-, hourHandLength- : WMProperties.Int32Property;
  176. (** colors of hands *)
  177. secondHandColor-, minuteHandColor-, hourHandColor- : WMProperties.ColorProperty;
  178. (* format *)
  179. format-: WMProperties.StringProperty;
  180. currentTime : Dates.DateTime;
  181. lock : Locks.Lock; (* protects currentTime *)
  182. str : Strings.String;
  183. centerX, centerY : LONGINT;
  184. image : WMGraphics.Image;
  185. updateInterval : LONGINT;
  186. alive, dead : BOOLEAN;
  187. timer : Kernel.Timer;
  188. PROCEDURE &Init*;
  189. BEGIN
  190. Init^;
  191. SetNameAsString(StrClock);
  192. SetGenerator("WMClock.GenClock");
  193. NEW(imageName, PrototypeImageName, NIL, NIL); properties.Add(imageName);
  194. NEW(timeOffset, PrototypeTimeOffset, NIL, NIL); properties.Add(timeOffset);
  195. NEW(viewMode, PrototypeViewMode, NIL, NIL); properties.Add(viewMode);
  196. NEW(color, PrototypeColor, NIL, NIL); properties.Add(color);
  197. NEW(secondHandLength, PrototypeSecondHandLength, NIL, NIL); properties.Add(secondHandLength);
  198. NEW(minuteHandLength, PrototypeMinuteHandLength, NIL, NIL); properties.Add(minuteHandLength);
  199. NEW(hourHandLength, PrototypeHourHandLength, NIL, NIL); properties.Add(hourHandLength);
  200. NEW(secondHandColor, PrototypeSecondHandColor, NIL, NIL); properties.Add(secondHandColor);
  201. NEW(minuteHandColor, PrototypeMinuteHandColor, NIL, NIL); properties.Add(minuteHandColor);
  202. NEW(hourHandColor, PrototypeHourHandColor, NIL, NIL); properties.Add(hourHandColor);
  203. NEW(format, PrototypeFormat, NIL, NIL); properties.Add(format);
  204. NEW(lock);
  205. NEW(str, 32);
  206. image := NIL;
  207. updateInterval := 500;
  208. alive := TRUE; dead := FALSE;
  209. NEW(timer);
  210. SetFont(WMGraphics.GetFont("Oberon", 24, {WMGraphics.FontBold}));
  211. END Init;
  212. PROCEDURE PropertyChanged*(sender, property : ANY);
  213. VAR vmValue : LONGINT;
  214. BEGIN
  215. IF (property = viewMode) THEN
  216. vmValue := viewMode.Get();
  217. IF vmValue = ViewModeStandard THEN
  218. format.SetAOC("hh:nn:ss");
  219. ELSIF vmValue = ViewModeDateTime THEN
  220. format.SetAOC("dd.mm.yy");
  221. ELSIF vmValue = ViewModeDayOfWeek THEN
  222. format.SetAOC("www dd.");
  223. END;
  224. timer.Wakeup;
  225. ELSIF (property = color) THEN
  226. timer.Wakeup;
  227. ELSIF (property = imageName) THEN
  228. RecacheProperties;
  229. timer.Wakeup;
  230. ELSIF (property = bounds) THEN
  231. PropertyChanged^(sender, property);
  232. RecacheProperties;
  233. timer.Wakeup;
  234. ELSIF (property = timeOffset) THEN
  235. timer.Wakeup;
  236. ELSIF (property = secondHandLength) OR (property = minuteHandLength) OR (property = hourHandLength) OR
  237. (property = secondHandColor) OR (property = minuteHandColor) OR (property = hourHandColor) THEN
  238. timer.Wakeup;
  239. ELSE
  240. PropertyChanged^(sender, property);
  241. END;
  242. END PropertyChanged;
  243. PROCEDURE RecacheProperties*;
  244. VAR string : Strings.String; newImage, resizedImage : WMGraphics.Image; vmValue : LONGINT;
  245. BEGIN
  246. RecacheProperties^;
  247. vmValue := viewMode.Get();
  248. IF vmValue = ViewModeStandard THEN
  249. format.SetAOC("hh:nn:ss");
  250. ELSIF vmValue = ViewModeDateTime THEN
  251. format.SetAOC("dd.mm.yy");
  252. ELSIF vmValue = ViewModeDayOfWeek THEN
  253. format.SetAOC("www dd.");
  254. END;
  255. newImage := NIL;
  256. string := imageName.Get();
  257. IF (string # NIL) THEN
  258. newImage := WMGraphics.LoadImage(string^, TRUE);
  259. IF (newImage # NIL) THEN
  260. IF (bounds.GetWidth() # newImage.width) OR (bounds.GetHeight() # newImage.height) THEN
  261. NEW(resizedImage);
  262. Raster.Create(resizedImage, bounds.GetWidth(), bounds.GetHeight(), Raster.BGRA8888);
  263. WMRasterScale.Scale(
  264. newImage, WMRectangles.MakeRect(0, 0, newImage.width, newImage.height),
  265. resizedImage, WMRectangles.MakeRect(0, 0, resizedImage.width, resizedImage.height),
  266. WMRectangles.MakeRect(0, 0, resizedImage.width, resizedImage.height),
  267. WMRasterScale.ModeCopy, WMRasterScale.ScaleBilinear);
  268. newImage := resizedImage;
  269. END;
  270. END;
  271. END;
  272. image := newImage;
  273. centerX := ENTIER(bounds.GetWidth() / 2 + 0.5);
  274. centerY := ENTIER(bounds.GetHeight() / 2 + 0.5);
  275. END RecacheProperties;
  276. PROCEDURE DrawHands(canvas : WMGraphics.Canvas; time : Dates.DateTime);
  277. PROCEDURE DrawLine(handLengthInPercent : LONGINT; color : WMGraphics.Color; angle : REAL);
  278. VAR deltaX, deltaY : LONGINT; radiants : REAL; lengthX, lengthY : LONGINT;
  279. BEGIN
  280. lengthX := handLengthInPercent * bounds.GetWidth() DIV 2 DIV 100;
  281. lengthY := handLengthInPercent * bounds.GetHeight() DIV 2 DIV 100;
  282. radiants := (angle / 360) * 2*Math.pi;
  283. deltaX := ENTIER(lengthX * Math.sin(radiants) + 0.5);
  284. deltaY := ENTIER(lengthY * Math.cos(radiants) + 0.5);
  285. canvas.Line(centerX, centerY, centerX + deltaX, centerY - deltaY, color, WMGraphics.ModeSrcOverDst);
  286. END DrawLine;
  287. BEGIN
  288. IF (hourHandLength.Get() > 0) THEN
  289. time.hour := time.hour MOD 12;
  290. DrawLine(hourHandLength.Get(), hourHandColor.Get(), (time.hour + time.minute/60) * (360 DIV 12));
  291. END;
  292. IF (minuteHandLength.Get() > 0) THEN
  293. DrawLine(minuteHandLength.Get(), minuteHandColor.Get(), (time.minute + time.second/60) * (360 DIV 60));
  294. END;
  295. IF (secondHandLength.Get() > 0) THEN
  296. DrawLine(secondHandLength.Get(), secondHandColor.Get(), time.second * (360 DIV 60));
  297. END;
  298. END DrawHands;
  299. PROCEDURE DrawBackground*(canvas : WMGraphics.Canvas);
  300. VAR time : Dates.DateTime; formatString: Strings.String;
  301. BEGIN
  302. DrawBackground^(canvas);
  303. lock.Acquire;
  304. time := currentTime;
  305. lock.Release;
  306. IF image # NIL THEN
  307. canvas.DrawImage(0, 0, image, WMGraphics.ModeSrcOverDst);
  308. END;
  309. IF (viewMode.Get() = ViewModeAnalog) THEN
  310. DrawHands(canvas, time);
  311. ELSE
  312. formatString := format.Get();
  313. Strings.FormatDateTime(formatString^, time, str^);
  314. canvas.SetColor(color.Get());
  315. IF (image = NIL) THEN
  316. (*WMGraphicUtilities.DrawRect(canvas, GetClientRect(), color.Get(), WMGraphics.ModeCopy);*)
  317. END;
  318. WMGraphics.DrawStringInRect(canvas, GetClientRect(), FALSE, WMGraphics.AlignCenter, WMGraphics.AlignCenter, str^)
  319. END;
  320. END DrawBackground;
  321. PROCEDURE Finalize*;
  322. BEGIN
  323. Finalize^;
  324. alive := FALSE;
  325. timer.Wakeup;
  326. BEGIN {EXCLUSIVE} AWAIT(dead); END;
  327. END Finalize;
  328. BEGIN {ACTIVE}
  329. WHILE alive DO
  330. lock.Acquire;
  331. currentTime := Dates.Now();
  332. currentTime.hour := (currentTime.hour + timeOffset.Get());
  333. lock.Release;
  334. Invalidate;
  335. timer.Sleep(updateInterval);
  336. END;
  337. BEGIN {EXCLUSIVE} dead := TRUE; END;
  338. END Clock;
  339. VAR
  340. nofWindows : LONGINT;
  341. StrClock : Strings.String;
  342. PrototypeViewMode : WMProperties.Int32Property;
  343. PrototypeColor : WMProperties.ColorProperty;
  344. PrototypeImageName : WMProperties.StringProperty;
  345. PrototypeSecondHandLength, PrototypeMinuteHandLength, PrototypeHourHandLength : WMProperties.Int32Property;
  346. PrototypeSecondHandColor, PrototypeMinuteHandColor, PrototypeHourHandColor : WMProperties.ColorProperty;
  347. PrototypeTimeOffset, PrototypeUpdateInterval : WMProperties.Int32Property;
  348. PrototypeFormat: WMProperties.StringProperty;
  349. contextMenuParStandard, contextMenuParDateTime, contextMenuParDayOfWeek, contextMenuParAnalog : ContextMenuPar;
  350. PROCEDURE Open*;
  351. VAR window : Window;
  352. BEGIN
  353. NEW(window, NIL, {WMWindowManager.FlagStayOnTop, WMWindowManager.FlagNavigation, WMWindowManager.FlagHidden});
  354. END Open;
  355. PROCEDURE Restore*(context : WMRestorable.Context);
  356. VAR window : Window;
  357. BEGIN
  358. NEW(window, context, {});
  359. END Restore;
  360. PROCEDURE GenClock*() : XML.Element;
  361. VAR clock : Clock;
  362. BEGIN
  363. NEW(clock); RETURN clock;
  364. END GenClock;
  365. PROCEDURE InitStrings;
  366. BEGIN
  367. StrClock := Strings.NewString("Clock");
  368. END InitStrings;
  369. PROCEDURE InitPrototypes;
  370. BEGIN
  371. (* DigitalClock *)
  372. NEW(PrototypeColor, NIL, Strings.NewString("Color"), Strings.NewString("toggle clock color"));
  373. PrototypeColor.Set(0FFH);
  374. NEW(PrototypeViewMode, NIL, Strings.NewString("ViewMode"), Strings.NewString("select view mode: time=0, date=1, dayOfWeek=2, analog=3, formatted=4"));
  375. PrototypeViewMode.Set(ViewModeStandard);
  376. (* AnalogClock *)
  377. NEW(PrototypeImageName, NIL, Strings.NewString("ImageName"), Strings.NewString("Clock face image name"));
  378. PrototypeImageName.SetAOC("WMClockImages.tar://roman_numeral_wall_clock.png");
  379. NEW(PrototypeTimeOffset, NIL, Strings.NewString("TimeOffset"), Strings.NewString("Time offset in hours"));
  380. PrototypeTimeOffset.Set(0);
  381. NEW(PrototypeSecondHandLength, NIL, Strings.NewString("SecondHandLength"), Strings.NewString("Length of second hand in percent of radius"));
  382. PrototypeSecondHandLength.Set(90);
  383. NEW(PrototypeMinuteHandLength, NIL, Strings.NewString("MinuteHandLength"), Strings.NewString("Length of minute hand in percent of radius"));
  384. PrototypeMinuteHandLength.Set(80);
  385. NEW(PrototypeHourHandLength, NIL, Strings.NewString("HourHandLength"), Strings.NewString("Length of hour hand in percent of radius"));
  386. PrototypeHourHandLength.Set(60);
  387. NEW(PrototypeSecondHandColor, NIL, Strings.NewString("SecondHandColor"), Strings.NewString("Color of second hand"));
  388. PrototypeSecondHandColor.Set(WMGraphics.Red);
  389. NEW(PrototypeMinuteHandColor, NIL, Strings.NewString("MinuteHandColor"), Strings.NewString("Color of minute hand"));
  390. PrototypeMinuteHandColor.Set(WMGraphics.Black);
  391. NEW(PrototypeHourHandColor, NIL, Strings.NewString("HourHandColor"), Strings.NewString("Color of hour hand"));
  392. PrototypeHourHandColor.Set(WMGraphics.Black);
  393. NEW(PrototypeUpdateInterval, NIL, Strings.NewString("UpdateInterval"), Strings.NewString("Redraw rate"));
  394. PrototypeUpdateInterval.Set(500);
  395. NEW(PrototypeFormat, NIL, Strings.NewString("Format"), Strings.NewString("Textual Format (yy, mm, dd, www, hh, nn, ss)"));
  396. PrototypeFormat.Set(Strings.NewString("hh:nn:ss"));
  397. END InitPrototypes;
  398. PROCEDURE IncCount;
  399. BEGIN {EXCLUSIVE}
  400. INC(nofWindows)
  401. END IncCount;
  402. PROCEDURE DecCount;
  403. BEGIN {EXCLUSIVE}
  404. DEC(nofWindows)
  405. END DecCount;
  406. PROCEDURE Cleanup;
  407. VAR die : KillerMsg;
  408. msg : WMMessages.Message;
  409. m : WMWindowManager.WindowManager;
  410. BEGIN {EXCLUSIVE}
  411. NEW(die);
  412. msg.ext := die;
  413. msg.msgType := WMMessages.MsgExt;
  414. m := WMWindowManager.GetDefaultManager();
  415. m.Broadcast(msg);
  416. AWAIT(nofWindows = 0)
  417. END Cleanup;
  418. BEGIN
  419. nofWindows := 0;
  420. InitStrings;
  421. InitPrototypes;
  422. Modules.InstallTermHandler(Cleanup);
  423. NEW(contextMenuParStandard, ViewModeStandard);
  424. NEW(contextMenuParDateTime, ViewModeDateTime);
  425. NEW(contextMenuParDayOfWeek, ViewModeDayOfWeek);
  426. NEW(contextMenuParAnalog, ViewModeAnalog);
  427. END WMClock.
  428. System.Free WMClock~
  429. WMClock.Open ~