Page 2 of 3

Posted: Fri Jul 28, 2006 7:30 pm
by Straker
Thanks. Yes - it works now on when I switched from 16 to 32-bit. Thanks again.

Posted: Fri Jul 28, 2006 7:49 pm
by yoxola
Changing to 32bit works (I never noticed that :shock: )

Posted: Fri Jul 28, 2006 7:59 pm
by Psychophanta
Please Trond, allow me to improve the source replacing the main code by my multimedia timer stuff, and improving 1 or 2 more things for CPU use saving:

Code: Select all

Declare UpdateClock(id.l,msg.l,dwUser.l,dw1.l,dw2.l)
                      ;***** Windows Multimedia Timer *****
                      ;***** by Psychophanta April 2006 *****
                      ;***************************************
Global m_hWnd.l,m_timerId.l,m_timerRes.l
Procedure.l min(a.l,b.l)
  If a<0
    If b>0 Or a>b:ProcedureReturn b:EndIf
  ElseIf b>0 And a>b:ProcedureReturn b
  EndIf
  ProcedureReturn a
EndProcedure
Procedure.l max(a.l,b.l)
  If a<0
    If b>0 Or a>b:ProcedureReturn a:EndIf
  ElseIf b>0 And a>b:ProcedureReturn a
  EndIf
  ProcedureReturn b
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////////////////
;// start mmTimer
;/////////////////////////////////////////////////////////////////////////////////////////////
Procedure.b StartTimer(period.l,oneShot.b=0,resolution.l=0)
  tc.TIMECAPS
  If timeGetDevCaps_(@tc,SizeOf(TIMECAPS))=#TIMERR_NOERROR
    m_timerRes.l=min(max(tc\wPeriodMin,resolution),tc\wPeriodMax)
    timeBeginPeriod_(m_timerRes);
  Else
    ProcedureReturn 0
  EndIf
  If oneShot:oneShot=#TIME_ONESHOT:Else:oneShot=#TIME_PERIODIC:EndIf
  result.l=timeSetEvent_(period,m_timerRes,@UpdateClock(),0,oneShot);CALLBACK_EVENT_SET
  If result.l
    m_timerId=result.l
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////////////////
;// stop mmTimer
;// could be modified in XP with TIME_KILL_SYNCHRONOUS
;/////////////////////////////////////////////////////////////////////////////////////////////
Procedure.b StopTimer(bEndTime.b=0)
  result.l=timeKillEvent_(m_timerId)
  If result.l=#TIMERR_NOERROR
    m_timerId=0
    If bEndTime
      For i.b=0 To 9
        Sleep_(10);                          //TIME_KILL_SYNCHRONOUS
      Next
    EndIf
  EndIf
  If m_timerRes
    timeEndPeriod_(m_timerRes)
    m_timerRes=0
  EndIf
  If result.l=#TIMERR_NOERROR:ProcedureReturn 1:EndIf
  ProcedureReturn 0
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////////////////
;// Resume mmTimer
;/////////////////////////////////////////////////////////////////////////////////////////////
Procedure.b SafeStartTimer(period.l=2,oneShot.b=0,resolution.l=0)
  If StartTimer(period,oneShot,resolution)=0
   ; PostMessage_(m_hWnd,#WM_CLOSE,0,0)
    ProcedureReturn 0
  EndIf
  ProcedureReturn 1
EndProcedure
                      ;END OF:
                      ;***** Windows Multimedia Timer *****
                      ;***** by Psychophanta April 2006 *****
                      ;***************************************
#APPNAME = "CoolClock"
UseTIFFImageDecoder()
UsePNGImageDecoder()

Enumeration
  #ImgCanvas
  #ImgBack
  #ImgBorder
  #ImgGloss
  #ImgLongPointer
  #ImgMediumPointer
  #ImgSmallPointer
EndEnumeration
Global Alpha = 255

OpenWindow(1, 0, 0, 0, 0, #APPNAME + " Parent", #PB_Window_Invisible)
OpenWindow(0, 0, 0, 512, 384, #APPNAME, #PB_Window_Invisible, WindowID(1))

SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_LAYERED)
CreatePopupMenu(0)
  MenuItem(0, "Stick to &desktop")
  MenuItem(1, "Always on &top")
  MenuItem(2, "&Normal")
  MenuItem(6, "Get UTC (Universal Time Coordinated)")
  MenuBar()
  MenuItem(3, "&Change translucency")
  MenuBar()
  MenuItem(4, "&Exit")
  MenuItem(5, "&About")
Procedure.l CheckinetConnection()
  State.l
  InternetGetConnectedState_(@State,0)
  ProcedureReturn State
EndProcedure
If CheckinetConnection()=0:DisableMenuItem(0,6,1):EndIf
;
CatchImage(#ImgGloss, ?ClockGloss)
CatchImage(#ImgBack, ?ClockBack)
CatchImage(#ImgBorder, ?ClockBorder)
StartDrawing(ImageOutput(#ImgBack))
DrawAlphaImage(ImageID(#ImgGloss), 0, 0)
DrawAlphaImage(ImageID(#ImgBorder), 0, 0)
StopDrawing()
CatchImage(#ImgLongPointer, ?ClockLongPointer)
CatchImage(#ImgMediumPointer, ?ClockMediumPointer)
CatchImage(#ImgSmallPointer, ?ClockSmallPointer)

Procedure RotateDC(hDC.l, x0.l, y0.l, Degrees.d)
  Static XFORM.XFORM
  Protected Radians.d = Degrees*(#PI/180)
  With XFORM
    \eM11 = Cos(Radians)
    \eM12 = Sin(Radians)
    \eM21 = -\eM12
    \eM22 = \eM11
    \ex = x0 - Cos(Radians)*x0 + Sin(Radians)*y0
    \ey = y0 - Cos(Radians)*y0 - Sin(Radians)*x0
  EndWith
  SetGraphicsMode_(hDC, #GM_ADVANCED)
  SetWorldTransform_(hDC, XFORM)
EndProcedure
Procedure UpdateClock(id.l,msg.l,dwUser.l,dw1.l,dw2.l)
  Date = Date()
  Static hDC
  Hour = Hour(Date)
  Minute = Minute(Date)
  Second = Second(Date)
 
  ; Fresh copy of background to draw on
  CopyImage(#ImgBack, #ImgCanvas)
 
  ; Draw on the background
  hDC = StartDrawing(ImageOutput(#ImgCanvas))
      ; Draw the large pointer
      RotateDC(hDC, 128, 128, Minute*6)
      DrawAlphaImage(ImageID(#ImgLongPointer),120,35) 
      ; Draw the medium pointer
      RotateDC(hDC, 128, 128, Hour*30+Minute/2)
      DrawAlphaImage(ImageID(#ImgMediumPointer),120,66)
      ; Draw the small pointer
      RotateDC(hDC, 128, 128, Second*6)
      DrawAlphaImage(ImageID(#ImgSmallPointer),121,19)
    ; Update the window
    Static ContextOffset.POINT
    Static BlendMode.BLENDFUNCTION
    Static BitmapInfo.BITMAP
    GetObject_(ImageID(#ImgCanvas), SizeOf(BITMAP), @BitmapInfo)
    BlendMode\SourceConstantAlpha = Alpha
    BlendMode\AlphaFormat = 1
    UpdateLayeredWindow_(WindowID(0), 0, 0, @BitmapInfo+4, hDC, @ContextOffset, 0, @BlendMode, 2)
  StopDrawing()
EndProcedure
Procedure StickToDesktop()
  StickyWindow(0, 0)
  SetParent_(WindowID(0), GetShellWindow_())
EndProcedure
Procedure AlwaysOnTop()
  SetParent_(WindowID(0), 0)
  StickyWindow(0, 1)
EndProcedure
Procedure ChangeTranslucency()
  OpenWindow(2,WindowX(0),WindowY(0)+WindowHeight(0),200,30,"Translucency",#PB_Window_SystemMenu,WindowID(1))
  CreateGadgetList(WindowID(2))
  TrackBarGadget(0,0,0,180,40,0,255):SetGadgetState(0,Alpha)
  TextGadget(1,180,0,40,18,Str(GetGadgetState(0)))
EndProcedure
Procedure Normal()
  SetParent_(WindowID(0), 0)
  StickyWindow(0, 0)
EndProcedure
Procedure.b GetUTCTime()
  Static Networkinitialized.b=0
  If Networkinitialized.b=0
    If InitNetwork()=0
      MessageRequester("Error", "Can't initialize the network !",0):ProcedureReturn 0
    Else:Networkinitialized.b=1
    EndIf
  EndIf
  Datebuffer.s=Space(1000)
  UTCServers.l=14:Dim UTC.s(UTCServers-1):port.w=13
  UTC(0)="208.184.49.9"; "nist1.ny.glassey.com", "Abovenet, New York City"
  UTC(1)="129.6.15.28"; "time-a.nist.gov", "NIST, Gaithersburg, Maryland"
  UTC(2)="129.6.15.29"; "time-b.nist.gov", "NIST, Gaithersburg, Maryland"
  UTC(3)="132.163.4.101"; "time-a.timefreq.bldrdoc.gov", "NIST, Boulder, Colorado"
  UTC(4)="132.163.4.102"; "time-b.timefreq.bldrdoc.gov", "NIST, Boulder, Colorado"
  UTC(5)="132.163.4.103"; "time-c.timefreq.bldrdoc.gov", "NIST, Boulder, Colorado"
  UTC(6)="128.138.140.44"; "utcnist.colorado.edu", "University of Colorado, Boulder"
  UTC(7)="192.43.244.18"; "time.nist.gov", "NCAR, Boulder, Colorado"
  UTC(8)="131.107.1.10"; "time-nw.nist.gov", "Microsoft, Redmond, Washington"
  UTC(9)="63.149.208.50"; "nist1.datum.com", "Datum, San Jose, California"
  UTC(10)="216.200.93.8"; "nist1.dc.glassey.com", "Abovenet, Virginia"
  UTC(11)="207.126.103.204"; "nist1.sj.glassey.com", "Abovenet, San Jose, California"
  UTC(12)="207.200.81.113"; "nist1.aol-ca.truetime.com", "TrueTime, AOL facility, Sunnyvale, California"
  UTC(13)="205.188.185.33"; "nist1.aol-va.truetime.com", "TrueTime, AOL facility, Virginia"
  For t=0 To UTCServers-1
    ConnectionID.l=OpenNetworkConnection(UTC(t),port.w)
    If ConnectionID
      ReceiveNetworkData(ConnectionID,@DateBuffer,1000)
      date.l=ParseDate("%yy/%mm/%dd/%hh/%ii/%ss",Mid(DateBuffer,8,2)+"/"+Mid(DateBuffer,11,2)+"/"+Mid(DateBuffer,14,2)+"/"+Mid(DateBuffer,17,2)+"/"+Mid(DateBuffer,20,2)+"/"+Mid(DateBuffer,23,2))
      Debug FormatDate("%yyyy/%mm/%dd/%hh/%ii/%ss",date) 
      systime.SYSTEMTIME
      systime\wYear=Year(date)
      systime\wMonth=Month(date)
      systime\wDayOfWeek=DayOfWeek(date)
      systime\wDay=Day(date)
      systime\wHour=Hour(date)
      systime\wMinute=Minute(date)
      systime\wSecond=Second(date)
      systime\wMilliseconds=0
      SetSystemTime_(@systime)
      CloseNetworkConnection(ConnectionID)
      ProcedureReturn 1
    ElseIf t>=UTCServers-1
      MessageRequester("Error","Can't connect to UTC Server !",0):ProcedureReturn 0
    EndIf
  Next
EndProcedure
Procedure About()
  Protected Text.s
  Text+#APPNAME + #CRLF$
  Text+"Copyright 2006 Trond Gundersen"+ #CRLF$
  Text+"Enhanced & timered version by Albert (Psychophanta)"
  MessageRequester("About", Text)
EndProcedure
UpdateClock(0,0,0,0,0)
HideWindow(0,0)
SafeStartTimer(1000)
Repeat
  Select WaitWindowEvent()
  Case #WM_RBUTTONUP
    DisplayPopupMenu(0, WindowID(1))
  Case #WM_LBUTTONDOWN
    SendMessage_(WindowID(0), #WM_NCLBUTTONDOWN, #HTCAPTION, 0)
  Case #PB_Event_Menu
    Select EventMenu()
      Case 0
        StickToDesktop()
      Case 1
        AlwaysOnTop()
      Case 2
        Normal()
      Case 6
        GetUTCTime()
      Case 3
        ChangeTranslucency()
      Case 4
        Break
      Case 5
        About()
    EndSelect
  Case #PB_Event_Gadget
    If IsWindow(2)
      Alpha=GetGadgetState(0)
      SetGadgetText(1,Str(Alpha))
    EndIf
  Case #PB_Event_CloseWindow
    If IsWindow(2):CloseWindow(2)
    Else:Break
    EndIf
  EndSelect
  Delay(20)
ForEver
StopTimer()
DataSection
  ClockBack:
    IncludeBinary "Clock - Back.tiff"
  ClockBorder:
    IncludeBinary "Border.tiff"
  ClockGloss:
    IncludeBinary "gloss.png"
  ClockLongPointer:
    IncludeBinary "hour_pointer.tiff"
  ClockMediumPointer:
    IncludeBinary "minute_pointer.tiff"
  ClockSmallPointer:
    IncludeBinary "second_pointer.tiff"
EndDataSection

Posted: Fri Jul 28, 2006 8:00 pm
by Joakim Christiansen
Disable html Psychophanta :P

Posted: Fri Jul 28, 2006 8:06 pm
by Psychophanta
Joakim Christiansen wrote:Disable html Psychophanta :P
Uhhff! thanx!

Posted: Fri Jul 28, 2006 8:48 pm
by Trond
The very nice think about your version, Psychophanta, is that the clock continues when menus and dialogs are displayed. However, it does not look the same any more. The hands are rendered in front of the glass. Obviously faster, but not as pretty.

And it updates every second instead of when the next second starts. So it could update and move the second hand in the middle of a second.

Posted: Fri Jul 28, 2006 9:23 pm
by Psychophanta
Trond wrote:The very nice think about your version, Psychophanta, is that the clock continues when menus and dialogs are displayed. However, it does not look the same any more. The hands are rendered in front of the glass. Obviously faster, but not as pretty.
Well, that is possible to fix making some change.
Trond wrote:And it updates every second instead of when the next second starts. So it could update and move the second hand in the middle of a second.
ahhh! come on! that's not important. Keep in mind tha a timer is the more elegant solution for these kind of apps.
It is very ugly to poll seconds every time.
EDIT: That has also solution even using timer. Just start the 1 sec timer at the date second bound and that's all ;)

Posted: Sat Jul 29, 2006 3:51 pm
by oridan
Very Nice!
Thanks

:D

Posted: Sat Jul 29, 2006 7:52 pm
by netmaestro
I was able to remove all visible CPU usage by means of only drawing the border and gloss once. This was achieved by putting them on their own window and drawing it above the main window, keeping them together by catching the WM_MOVING event in a callback. This way, everything is drawn in the correct order and all original timing logic is kept. (it's good, no need to change it)

[edit] The SetParent_() in the SticktoDesktop procedure is causing a zorder difficulty doing it this way, that will have to be dealt with somehow.

Code: Select all

Procedure CallBack(hwnd, message, wparam, lparam) 
  Result = #PB_ProcessPureBasicEvents 
  If message=#WM_MOVING 
    If hwnd = WindowID(0)
      ResizeWindow(2, WindowX(0),WindowY(0),#PB_Ignore,#PB_Ignore) 
    Else
      ResizeWindow(0, WindowX(2),WindowY(2),#PB_Ignore,#PB_Ignore) 
    EndIf
  EndIf 
  ProcedureReturn result 
EndProcedure 

#APPNAME = "CoolClock"

UseTIFFImageDecoder()
UsePNGImageDecoder()

Enumeration
  #ImgCanvas
  #ImgBack
  #ImgBorder
  #ImgGloss
  #ImgLongPointer
  #ImgMediumPointer
  #ImgSmallPointer
  #imgCover
EndEnumeration

Global Alpha = 255

OpenWindow(1, 0, 0, 0, 0, #APPNAME + " Parent", #PB_Window_Invisible)
OpenWindow(0, 0, 0, 512, 384, #APPNAME, #PB_Window_Invisible, WindowID(1))
SetWindowLong_(WindowID(0), #GWL_EXSTYLE, GetWindowLong_(WindowID(0), #GWL_EXSTYLE) | #WS_EX_LAYERED)

;//////////////////////////////////////////////////////////////////////////////////
OpenWindow(2, 0, 0, 512, 384, #APPNAME+" Cover",#PB_Window_Invisible,WindowID(0))
HideWindow(2, 0)
SetWindowCallback(@CallBack())
SetWindowLong_(WindowID(2), #GWL_EXSTYLE, GetWindowLong_(WindowID(2), #GWL_EXSTYLE) | #WS_EX_LAYERED)
;//////////////////////////////////////////////////////////////////////////////////

CreatePopupMenu(0)
  MenuItem(0, "Stick to &desktop")
  MenuItem(1, "Always on &top")
  MenuItem(2, "&Normal")
  MenuBar()
  MenuItem(3, "&Change translucency")
  MenuBar()
  MenuItem(4, "&Exit")
  MenuItem(5, "&About")

CatchImage(#ImgGloss, ?ClockGloss)
CatchImage(#ImgBack, ?ClockBack)
CatchImage(#ImgBorder, ?ClockBorder)
CatchImage(#ImgLongPointer, ?ClockLongPointer)
CatchImage(#ImgMediumPointer, ?ClockMediumPointer)
CatchImage(#ImgSmallPointer, ?ClockSmallPointer)

;//////////////////////////////////////////////////////////////////////
CreateImage(#imgCover, 512,384)
hdc=StartDrawing(ImageOutput(#imgCover))
  DrawAlphaImage(ImageID(#ImgGloss), 0, 0)
  DrawAlphaImage(ImageID(#ImgBorder), 0, 0)
  ContextOffset.POINT
  BlendMode.BLENDFUNCTION
  BitmapInfo.BITMAP
  GetObject_(ImageID(#Imgcover), SizeOf(BITMAP), @BitmapInfo)
  BlendMode\SourceConstantAlpha = 255
  BlendMode\AlphaFormat = 1
  UpdateLayeredWindow_(WindowID(2), 0, 0, @BitmapInfo+4, hDC, @ContextOffset, 0, @BlendMode, 2)
StopDrawing()  
;//////////////////////////////////////////////////////////////////////

Procedure RotateDC(hDC.l, x0.l, y0.l, Degrees.d)
  Static XFORM.XFORM
  Protected Radians.d = Degrees*(#PI/180)
  With XFORM
    \eM11 = Cos(Radians)
    \eM12 = Sin(Radians)
    \eM21 = -\eM12
    \eM22 = \eM11
    \ex = x0 - Cos(Radians)*x0 + Sin(Radians)*y0
    \ey = y0 - Cos(Radians)*y0 - Sin(Radians)*x0
  EndWith
  SetGraphicsMode_(hDC, #GM_ADVANCED)
  SetWorldTransform_(hDC, XFORM)
EndProcedure

Procedure UpdateClock()
  Static OldDate
  Protected Date = Date()
  If Date = OldDate
    ProcedureReturn 0
  Else
    OldDate = Date
  EndIf
  Static hDC
  Protected Hour = Hour(Date)
  Protected Minute = Minute(Date)
  Protected Second = Second(Date)
  
  ; Fresh copy of background to draw on
  CopyImage(#ImgBack, #ImgCanvas)
  
  ; Draw on the background
  hDC = StartDrawing(ImageOutput(#ImgCanvas))
    ;SaveDC_(hDC) ; Save the state for restoring after rotation
      ; Draw the large pointer
      RotateDC(hDC, 128, 128, Minute/60*360)
      DrawAlphaImage(ImageID(#ImgLongPointer), 128-9, 35)  
      ; Draw the medium pointer
      RotateDC(hDC, 128, 128, Hour/12*360)
      DrawAlphaImage(ImageID(#ImgMediumPointer), 128-7, 30+36)
      ; Draw the small pointer
      RotateDC(hDC, 128, 128, Second/60*360)
      DrawAlphaImage(ImageID(#ImgSmallPointer), 128-7, 19)
    ;RestoreDC_(hDC, -1)
    ;DrawAlphaImage(ImageID(#ImgGloss), 0, 0)
    ;DrawAlphaImage(ImageID(#ImgBorder), 0, 0)
    
    ; Update the window
    Static ContextOffset.POINT
    Static BlendMode.BLENDFUNCTION
    Static BitmapInfo.BITMAP
    GetObject_(ImageID(#ImgCanvas), SizeOf(BITMAP), @BitmapInfo)
    BlendMode\SourceConstantAlpha = Alpha
    BlendMode\AlphaFormat = 1
    UpdateLayeredWindow_(WindowID(0), 0, 0, @BitmapInfo+4, hDC, @ContextOffset, 0, @BlendMode, 2)
  StopDrawing()
EndProcedure

Procedure StickToDesktop()
  StickyWindow(0, 0)
  SetParent_(WindowID(0), GetShellWindow_())
EndProcedure

Procedure AlwaysOnTop()
  SetParent_(WindowID(0), 0)
  StickyWindow(0, 1)
EndProcedure

Procedure ChangeTranslucency()
  Protected AStr.s
  Protected VA
  AStr = InputRequester(#APPNAME, "Set the translucency (1 - 255):", Str(Alpha))
  VA = Val(AStr)
  If VA > 0 And VA < 256
    Alpha = VA
  Else
    MessageRequester(#APPNAME, "Invalid value: " + AStr, #MB_ICONINFORMATION)
  EndIf
EndProcedure

Procedure Normal()
  SetParent_(WindowID(0), 0)
  StickyWindow(0, 0)
EndProcedure

Procedure About()
  Protected Text.s
  Text + #APPNAME + #CRLF$
  Text + "Copyright 2006 Trond Gundersen"
  MessageRequester("About", Text)
EndProcedure

UpdateClock()
HideWindow(0, 0)

Repeat
  Select WaitWindowEvent(10)
    Case #WM_RBUTTONUP
      DisplayPopupMenu(0, WindowID(1))
    Case #WM_LBUTTONDOWN
      SendMessage_(WindowID(0), #WM_NCLBUTTONDOWN, #HTCAPTION, 0)
    Case #PB_Event_Menu
      Select EventMenu()
        Case 0
          StickToDesktop()
        Case 1
          AlwaysOnTop()
        Case 2
          Normal()
        Case 3
          ChangeTranslucency()
        Case 4
          Break
        Case 5
          About()
      EndSelect
    Case #PB_Event_CloseWindow 
      Break
  EndSelect
  UpdateClock()
ForEver


End
DataSection
  ClockBack:
    IncludeBinary "Clock - Back.tiff"
  ClockBorder:
    IncludeBinary "Border.tiff"
  ClockGloss:
    IncludeBinary "gloss.png"
  ClockLongPointer:
    IncludeBinary "hour_pointer.tiff"
  ClockMediumPointer:
    IncludeBinary "minute_pointer.tiff"
  ClockSmallPointer:
    IncludeBinary "second_pointer.tiff"
EndDataSection

Posted: Sat Jul 29, 2006 8:36 pm
by Psychophanta
Netmaestro, nice, but there is a small fail:
Clock stops when holding LMB on it. :twisted:

Posted: Sat Jul 29, 2006 8:41 pm
by netmaestro
I felt that it was important to let the user feel that his finger has at least the power to stop a clock :wink:

Posted: Sat Jul 29, 2006 8:42 pm
by Psychophanta
netmaestro wrote:I felt that it was important to let the user feel that his finger has at least the power to stop a clock :wink:
:lol:

EDIT: But see that: in this case the more power the user has, the less power for the tool. :wink:

Posted: Sun Jul 30, 2006 10:46 am
by Psychophanta
Could be added an option menu in which you are able to choose to automatically synchronize time with a time server?

EDIT: Done. (source code in my post above). A friend of mine wanted it, and now he has to invite me to eat to a chinese restaurant (perhaps my preferred food -sorry for ones who don't like :P - ).

Posted: Mon Jul 31, 2006 5:00 pm
by Dano
Very nice clock. I want it permanently on my dektop. The only thing I would change is to have the hour hand update a quarter of the way to the next hour every 15 minutes. At 9:59 it looks still like 9:00 until it hits 10:00.

Dan

Posted: Mon Jul 31, 2006 5:24 pm
by Psychophanta
Dano wrote:The only thing I would change is to have the hour hand update a quarter of the way to the next hour every 15 minutes. At 9:59 it looks still like 9:00 until it hits 10:00.
No in the version i uploaded yesterday. Have you tried? :wink: