MODULE WMClock;
IMPORT
Modules, Kernel, Math, Dates, Strings, Locks, XML, Raster, WMRasterScale, WMRectangles, WMGraphics, WMGraphicUtilities,
WMWindowManager, WMPopups, WMRestorable, WMMessages, WMComponents, WMProperties;
CONST
ViewModeStandard* = 0;
ViewModeDateTime* = 1;
ViewModeDayOfWeek* = 2;
ViewModeAnalog* = 3;
ViewModeFormatted*= 4;
WindowWidth = 150; WindowHeight = 50;
TYPE
ContextMenuPar = OBJECT
VAR
mode : LONGINT;
PROCEDURE &New*(m : LONGINT);
BEGIN
mode := m;
END New;
END ContextMenuPar;
TYPE
KillerMsg = OBJECT
END KillerMsg;
Window = OBJECT(WMComponents.FormWindow);
VAR
clock : Clock;
imageNameAnalog : Strings.String;
contextMenu : WMPopups.Popup;
dragging, resizing : BOOLEAN;
lastX, lastY : LONGINT;
PROCEDURE &New*(context : WMRestorable.Context; flags : SET);
VAR configuration : WMRestorable.XmlElement; viewMode, color : LONGINT;
BEGIN
IncCount;
IF (context # NIL) THEN
Init(context.r - context.l, context.b - context.t, TRUE);
ELSE
Init(WindowWidth, WindowHeight, TRUE);
END;
NEW(clock);
clock.alignment.Set(WMComponents.AlignClient);
imageNameAnalog := clock.imageName.Get();
IF (clock.viewMode.Get() # ViewModeAnalog) THEN clock.imageName.Set(NIL); END;
SetContent(clock);
SetTitle(Strings.NewString("Clock"));
IF (context # NIL) THEN
configuration := WMRestorable.GetElement(context, "Configuration");
IF (configuration # NIL) THEN
WMRestorable.LoadLongint(configuration, "color", color); clock.color.Set(color);
WMRestorable.LoadLongint(configuration, "viewMode", viewMode); clock.viewMode.Set(viewMode);
END;
WMRestorable.AddByContext(SELF, context);
ELSE
IF (WMWindowManager.FlagNavigation IN flags) THEN
WMWindowManager.ExtAddViewBoundWindow(SELF, 50, 50, NIL, flags);
ELSE
WMWindowManager.ExtAddWindow(SELF, 50, 50, flags)
END;
END;
END New;
PROCEDURE Close;
BEGIN
Close^;
DecCount;
END Close;
PROCEDURE HandleClose(sender, par: ANY);
VAR manager : WMWindowManager.WindowManager;
BEGIN
manager := WMWindowManager.GetDefaultManager();
manager.SetFocus(SELF);
Close;
END HandleClose;
PROCEDURE HandleToggleColor(sender, data: ANY);
BEGIN
IF (clock.color.Get() = 0FFH) THEN clock.color.Set(LONGINT(0FFFFFFFFH)) ELSE clock.color.Set(0FFH) END;
END HandleToggleColor;
PROCEDURE HandleToggleView(sender, par: ANY);
VAR manager : WMWindowManager.WindowManager; viewMode : LONGINT;
BEGIN
manager := WMWindowManager.GetDefaultManager();
manager.SetFocus(SELF);
IF (par # NIL) & (par IS ContextMenuPar) THEN
viewMode := par(ContextMenuPar).mode;
IF (clock.viewMode.Get() # viewMode) THEN
IF (par(ContextMenuPar).mode = ViewModeAnalog) THEN
clock.imageName.Set(imageNameAnalog);
ELSE
clock.imageName.Set(NIL);
END;
clock.viewMode.Set(par(ContextMenuPar).mode);
END;
ELSE
clock.viewMode.Set(ViewModeStandard);
clock.imageName.Set(NIL);
END
END HandleToggleView;
PROCEDURE PointerDown(x, y:LONGINT; keys:SET);
BEGIN
lastX := bounds.l + x; lastY:=bounds.t + y;
IF keys = {0} THEN
dragging := TRUE
ELSIF keys = {1,2} THEN
dragging := FALSE;
resizing := TRUE;
ELSIF keys = {2} THEN
NEW(contextMenu);
contextMenu.Add("Close", HandleClose);
contextMenu.AddParButton("Time", HandleToggleView, contextMenuParStandard);
contextMenu.AddParButton("Date", HandleToggleView, contextMenuParDateTime);
contextMenu.AddParButton("Day of Week", HandleToggleView, contextMenuParDayOfWeek);
contextMenu.AddParButton("Analog", HandleToggleView, contextMenuParAnalog);
contextMenu.AddParButton("Toggle Color", HandleToggleColor, NIL);
contextMenu.Popup(bounds.l + x, bounds.t + y)
END
END PointerDown;
PROCEDURE PointerMove(x,y:LONGINT; keys:SET);
VAR dx, dy, width, height : LONGINT;
BEGIN
IF dragging OR resizing THEN
x := bounds.l + x; y := bounds.t + y; dx := x - lastX; dy := y - lastY;
lastX := lastX + dx; lastY := lastY + dy;
IF (dx # 0) OR (dy # 0) THEN
IF dragging THEN
manager.SetWindowPos(SELF, bounds.l + dx, bounds.t + dy);
ELSE
width := GetWidth();
height := GetHeight();
width := Strings.Max(10, width + dx);
height := Strings.Max(10, height + dy);
manager.SetWindowSize(SELF, width, height);
END;
END;
END;
END PointerMove;
PROCEDURE PointerUp(x, y:LONGINT; keys:SET);
BEGIN
dragging := FALSE;
IF (keys # {1,2}) THEN
IF resizing THEN
resizing := FALSE;
Resized(GetWidth(), GetHeight());
END;
END;
END PointerUp;
PROCEDURE Handle(VAR x: WMMessages.Message);
VAR configuration : WMRestorable.XmlElement;
BEGIN
IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) THEN
IF (x.ext IS KillerMsg) THEN
Close;
ELSIF (x.ext IS WMRestorable.Storage) THEN
NEW(configuration); configuration.SetName("Configuration");
WMRestorable.StoreBoolean(configuration, "stayOnTop", WMWindowManager.FlagStayOnTop IN flags);
WMRestorable.StoreBoolean(configuration, "navigation", WMWindowManager.FlagNavigation IN flags);
WMRestorable.StoreLongint(configuration, "color", clock.color.Get());
WMRestorable.StoreLongint(configuration, "viewMode", clock.viewMode.Get());
x.ext(WMRestorable.Storage).Add("WMClock", "WMClock.Restore", SELF, configuration);
ELSE Handle^(x)
END
ELSE Handle^(x)
END
END Handle;
END Window;
TYPE
Clock* = OBJECT(WMComponents.VisualComponent)
VAR
viewMode- : WMProperties.Int32Property;
color- : WMProperties.ColorProperty;
imageName- : WMProperties.StringProperty;
timeOffset- : WMProperties.Int32Property;
secondHandLength-, minuteHandLength-, hourHandLength- : WMProperties.Int32Property;
secondHandColor-, minuteHandColor-, hourHandColor- : WMProperties.ColorProperty;
format-: WMProperties.StringProperty;
currentTime : Dates.DateTime;
lock : Locks.Lock;
str : Strings.String;
centerX, centerY : LONGINT;
image : WMGraphics.Image;
updateInterval : LONGINT;
alive, dead : BOOLEAN;
timer : Kernel.Timer;
PROCEDURE &Init*;
BEGIN
Init^;
SetNameAsString(StrClock);
SetGenerator("WMClock.GenClock");
NEW(imageName, PrototypeImageName, NIL, NIL); properties.Add(imageName);
NEW(timeOffset, PrototypeTimeOffset, NIL, NIL); properties.Add(timeOffset);
NEW(viewMode, PrototypeViewMode, NIL, NIL); properties.Add(viewMode);
NEW(color, PrototypeColor, NIL, NIL); properties.Add(color);
NEW(secondHandLength, PrototypeSecondHandLength, NIL, NIL); properties.Add(secondHandLength);
NEW(minuteHandLength, PrototypeMinuteHandLength, NIL, NIL); properties.Add(minuteHandLength);
NEW(hourHandLength, PrototypeHourHandLength, NIL, NIL); properties.Add(hourHandLength);
NEW(secondHandColor, PrototypeSecondHandColor, NIL, NIL); properties.Add(secondHandColor);
NEW(minuteHandColor, PrototypeMinuteHandColor, NIL, NIL); properties.Add(minuteHandColor);
NEW(hourHandColor, PrototypeHourHandColor, NIL, NIL); properties.Add(hourHandColor);
NEW(format, PrototypeFormat, NIL, NIL); properties.Add(format);
NEW(lock);
NEW(str, 32);
image := NIL;
updateInterval := 500;
alive := TRUE; dead := FALSE;
NEW(timer);
SetFont(WMGraphics.GetFont("Oberon", 24, {WMGraphics.FontBold}));
END Init;
PROCEDURE PropertyChanged(sender, property : ANY);
VAR vmValue : LONGINT;
BEGIN
IF (property = viewMode) THEN
vmValue := viewMode.Get();
IF vmValue = ViewModeStandard THEN
format.SetAOC("hh:nn:ss");
ELSIF vmValue = ViewModeDateTime THEN
format.SetAOC("dd.mm.yy");
ELSIF vmValue = ViewModeDayOfWeek THEN
format.SetAOC("www dd.");
END;
timer.Wakeup;
ELSIF (property = color) THEN
timer.Wakeup;
ELSIF (property = imageName) THEN
RecacheProperties;
timer.Wakeup;
ELSIF (property = bounds) THEN
PropertyChanged^(sender, property);
RecacheProperties;
timer.Wakeup;
ELSIF (property = timeOffset) THEN
timer.Wakeup;
ELSIF (property = secondHandLength) OR (property = minuteHandLength) OR (property = hourHandLength) OR
(property = secondHandColor) OR (property = minuteHandColor) OR (property = hourHandColor) THEN
timer.Wakeup;
ELSE
PropertyChanged^(sender, property);
END;
END PropertyChanged;
PROCEDURE RecacheProperties;
VAR string : Strings.String; newImage, resizedImage : WMGraphics.Image; vmValue : LONGINT;
BEGIN
RecacheProperties^;
vmValue := viewMode.Get();
IF vmValue = ViewModeStandard THEN
format.SetAOC("hh:nn:ss");
ELSIF vmValue = ViewModeDateTime THEN
format.SetAOC("dd.mm.yy");
ELSIF vmValue = ViewModeDayOfWeek THEN
format.SetAOC("www dd.");
END;
newImage := NIL;
string := imageName.Get();
IF (string # NIL) THEN
newImage := WMGraphics.LoadImage(string^, TRUE);
IF (newImage # NIL) THEN
IF (bounds.GetWidth() # newImage.width) OR (bounds.GetHeight() # newImage.height) THEN
NEW(resizedImage);
Raster.Create(resizedImage, bounds.GetWidth(), bounds.GetHeight(), Raster.BGRA8888);
WMRasterScale.Scale(
newImage, WMRectangles.MakeRect(0, 0, newImage.width, newImage.height),
resizedImage, WMRectangles.MakeRect(0, 0, resizedImage.width, resizedImage.height),
WMRectangles.MakeRect(0, 0, resizedImage.width, resizedImage.height),
WMRasterScale.ModeCopy, WMRasterScale.ScaleBilinear);
newImage := resizedImage;
END;
END;
END;
image := newImage;
centerX := ENTIER(bounds.GetWidth() / 2 + 0.5);
centerY := ENTIER(bounds.GetHeight() / 2 + 0.5);
END RecacheProperties;
PROCEDURE DrawHands(canvas : WMGraphics.Canvas; time : Dates.DateTime);
PROCEDURE DrawLine(handLengthInPercent : LONGINT; color : LONGINT; angle : REAL);
VAR deltaX, deltaY : LONGINT; radiants : REAL; lengthX, lengthY : LONGINT;
BEGIN
lengthX := handLengthInPercent * bounds.GetWidth() DIV 2 DIV 100;
lengthY := handLengthInPercent * bounds.GetHeight() DIV 2 DIV 100;
radiants := (angle / 360) * 2*Math.pi;
deltaX := ENTIER(lengthX * Math.sin(radiants) + 0.5);
deltaY := ENTIER(lengthY * Math.cos(radiants) + 0.5);
canvas.Line(centerX, centerY, centerX + deltaX, centerY - deltaY, color, WMGraphics.ModeSrcOverDst);
END DrawLine;
BEGIN
IF (hourHandLength.Get() > 0) THEN
time.hour := time.hour MOD 12;
DrawLine(hourHandLength.Get(), hourHandColor.Get(), (time.hour + time.minute/60) * (360 DIV 12));
END;
IF (minuteHandLength.Get() > 0) THEN
DrawLine(minuteHandLength.Get(), minuteHandColor.Get(), (time.minute + time.second/60) * (360 DIV 60));
END;
IF (secondHandLength.Get() > 0) THEN
DrawLine(secondHandLength.Get(), secondHandColor.Get(), time.second * (360 DIV 60));
END;
END DrawHands;
PROCEDURE DrawBackground(canvas : WMGraphics.Canvas);
VAR time : Dates.DateTime; formatString: Strings.String;
BEGIN
DrawBackground^(canvas);
lock.Acquire;
time := currentTime;
lock.Release;
IF image # NIL THEN
canvas.DrawImage(0, 0, image, WMGraphics.ModeSrcOverDst);
END;
IF (viewMode.Get() = ViewModeAnalog) THEN
DrawHands(canvas, time);
ELSE
formatString := format.Get();
Strings.FormatDateTime(formatString^, time, str^);
canvas.SetColor(color.Get());
IF (image = NIL) THEN
END;
WMGraphics.DrawStringInRect(canvas, GetClientRect(), FALSE, WMGraphics.AlignCenter, WMGraphics.AlignCenter, str^)
END;
END DrawBackground;
PROCEDURE Finalize;
BEGIN
Finalize^;
alive := FALSE;
timer.Wakeup;
BEGIN {EXCLUSIVE} AWAIT(dead); END;
END Finalize;
BEGIN {ACTIVE}
WHILE alive DO
lock.Acquire;
currentTime := Dates.Now();
currentTime.hour := (currentTime.hour + timeOffset.Get());
lock.Release;
Invalidate;
timer.Sleep(updateInterval);
END;
BEGIN {EXCLUSIVE} dead := TRUE; END;
END Clock;
VAR
nofWindows : LONGINT;
StrClock : Strings.String;
PrototypeViewMode : WMProperties.Int32Property;
PrototypeColor : WMProperties.ColorProperty;
PrototypeImageName : WMProperties.StringProperty;
PrototypeSecondHandLength, PrototypeMinuteHandLength, PrototypeHourHandLength : WMProperties.Int32Property;
PrototypeSecondHandColor, PrototypeMinuteHandColor, PrototypeHourHandColor : WMProperties.ColorProperty;
PrototypeTimeOffset, PrototypeUpdateInterval : WMProperties.Int32Property;
PrototypeFormat: WMProperties.StringProperty;
contextMenuParStandard, contextMenuParDateTime, contextMenuParDayOfWeek, contextMenuParAnalog : ContextMenuPar;
PROCEDURE Open*;
VAR window : Window;
BEGIN
NEW(window, NIL, {WMWindowManager.FlagStayOnTop, WMWindowManager.FlagNavigation, WMWindowManager.FlagHidden});
END Open;
PROCEDURE Restore*(context : WMRestorable.Context);
VAR window : Window;
BEGIN
NEW(window, context, {});
END Restore;
PROCEDURE GenClock*() : XML.Element;
VAR clock : Clock;
BEGIN
NEW(clock); RETURN clock;
END GenClock;
PROCEDURE InitStrings;
BEGIN
StrClock := Strings.NewString("Clock");
END InitStrings;
PROCEDURE InitPrototypes;
BEGIN
NEW(PrototypeColor, NIL, Strings.NewString("Color"), Strings.NewString("toggle clock color"));
PrototypeColor.Set(0FFH);
NEW(PrototypeViewMode, NIL, Strings.NewString("ViewMode"), Strings.NewString("select view mode: time=0, date=1, dayOfWeek=2, analog=3, formatted=4"));
PrototypeViewMode.Set(ViewModeStandard);
NEW(PrototypeImageName, NIL, Strings.NewString("ImageName"), Strings.NewString("Clock face image name"));
PrototypeImageName.SetAOC("WMClockImages.tar://roman_numeral_wall_clock.png");
NEW(PrototypeTimeOffset, NIL, Strings.NewString("TimeOffset"), Strings.NewString("Time offset in hours"));
PrototypeTimeOffset.Set(0);
NEW(PrototypeSecondHandLength, NIL, Strings.NewString("SecondHandLength"), Strings.NewString("Length of second hand in percent of radius"));
PrototypeSecondHandLength.Set(90);
NEW(PrototypeMinuteHandLength, NIL, Strings.NewString("MinuteHandLength"), Strings.NewString("Length of minute hand in percent of radius"));
PrototypeMinuteHandLength.Set(80);
NEW(PrototypeHourHandLength, NIL, Strings.NewString("HourHandLength"), Strings.NewString("Length of hour hand in percent of radius"));
PrototypeHourHandLength.Set(60);
NEW(PrototypeSecondHandColor, NIL, Strings.NewString("SecondHandColor"), Strings.NewString("Color of second hand"));
PrototypeSecondHandColor.Set(WMGraphics.Red);
NEW(PrototypeMinuteHandColor, NIL, Strings.NewString("MinuteHandColor"), Strings.NewString("Color of minute hand"));
PrototypeMinuteHandColor.Set(WMGraphics.Black);
NEW(PrototypeHourHandColor, NIL, Strings.NewString("HourHandColor"), Strings.NewString("Color of hour hand"));
PrototypeHourHandColor.Set(WMGraphics.Black);
NEW(PrototypeUpdateInterval, NIL, Strings.NewString("UpdateInterval"), Strings.NewString("Redraw rate"));
PrototypeUpdateInterval.Set(500);
NEW(PrototypeFormat, NIL, Strings.NewString("Format"), Strings.NewString("Textual Format (yy, mm, dd, www, hh, nn, ss)"));
PrototypeFormat.Set(Strings.NewString("hh:nn:ss"));
END InitPrototypes;
PROCEDURE IncCount;
BEGIN {EXCLUSIVE}
INC(nofWindows)
END IncCount;
PROCEDURE DecCount;
BEGIN {EXCLUSIVE}
DEC(nofWindows)
END DecCount;
PROCEDURE Cleanup;
VAR die : KillerMsg;
msg : WMMessages.Message;
m : WMWindowManager.WindowManager;
BEGIN {EXCLUSIVE}
NEW(die);
msg.ext := die;
msg.msgType := WMMessages.MsgExt;
m := WMWindowManager.GetDefaultManager();
m.Broadcast(msg);
AWAIT(nofWindows = 0)
END Cleanup;
BEGIN
nofWindows := 0;
InitStrings;
InitPrototypes;
Modules.InstallTermHandler(Cleanup);
NEW(contextMenuParStandard, ViewModeStandard);
NEW(contextMenuParDateTime, ViewModeDateTime);
NEW(contextMenuParDayOfWeek, ViewModeDayOfWeek);
NEW(contextMenuParAnalog, ViewModeAnalog);
END WMClock.
SystemTools.Free WMClock~
WMClock.Open ~