Page 1 of 2

[REVISED AGAIN] A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sat Jan 28, 2012 12:42 pm
by Randy Walker
Thanks to the many here who have helped me in the past and continue to do so. Here is a little 'something back' and hope some of you will find it useful. Please feel free to comment on results. In particular, I would be interested to know how it performs in various Windows flavors. Specific modifications required to adapt to other flavors, etc.

:evil: BTW: My intent here was to produce a plain and simple, cut and dry event reminder. Nothing glorious, nothing fancy to distract from the items in the list ... just a basic list. Going for the *Less is More* effect so I'm not really interested in how I could enhance it or how it may fall short of some feature packed utility. Only thing I'm really interested in is cross-platform performance on various flavors of Windows, and if you happen to like it, then that's just icing on the cake :)

Originally written and tested on Windows XP Pro using jaPBe v3.12 and PureBasic v4.60

[EDIT] 02/25/26 -- No longer 5.4x compatible but works great again in v6.30 after a few bug fixes.
[EDIT] 11/09/34 -- 2 modifications as noted in comments below:
[EDIT] 11/11/34 -- added patch to force listing to bottom when event count exceeds display area
[Updated] 11/27/24 -- Details inside code.
[EDIT] 11/30/24 -- added code at top of file to prevent more than one instance <<VERY CRITICAL
[EDIT] 10/4/25 -- Revised below to fix bug -- Calendar was not showing and double-click on calendar date not working.
BIG THANKS to Michael Vogel for that calendar fix!!!!!
[EDIT] 02/16/26 -- Fixed line number alignment for tool tips showing wrong descriptions after scrolling down list of events.

Code: Select all

; Snippet from 'Fluid byte' over there in the German forum
; used here as base starting point for 'Event Countdown Reminder':
; http://www.purebasic.fr/german/viewtopic.php?t=17692&start=4

; With special thanks to members of this thread:
; http://www.purebasic.fr/english/viewtopic.php?f=13&t=38387
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
  CompilerWarning "This code only supported in Windows, Sorry."
CompilerEndIf
Global hWin.i, AppName.s
AppName.s = "EventReminder"
#LVNI_VISIBLE = $0010
Procedure.l EnumWindows(WindowHandle.l, Parameter.l) 
  
  Title$ = Space(200) 
  GetWindowText_(WindowHandle, @Title$, 200) 
  
  If FindString(Title$, AppName, 1, #PB_String_NoCase ) <> 0
    HwndDebug = WindowHandle
    hWin.i = WindowHandle
    ProcedureReturn #False
  Else 
    ProcedureReturn #True
  EndIf 
  
EndProcedure
If GetCurrentDirectory() <> "D:\PureBasic\PB_prjct-630\"
  EnumWindows_(@EnumWindows(), 0)
  If hWin.i
    Debug "HERE IS YOUR Window Handle: "+Str(hwin.i)
    SetForegroundWindow_(hWin.i) ; and bring current instance into foreground.
    ShowWindow_(hWin.i, #SW_RESTORE)
    Delay(250)
    End ; Quit from this duplicate instance.
  EndIf
EndIf
Debug "NO, there is NO "+AppName
Enumeration 420
  ; Event Reminder
  #RemindCalendar
  #RemindEdit
  #RemindList
  #RemindAdd
  #RemindDiscard
  #RemindTrashAll
EndEnumeration
Debug "#RemindCalendar"+Str(#RemindCalendar)
Debug "#RemindEdit"+Str(#RemindEdit)
Debug "#RemindList"+Str(#RemindList)
Debug "#RemindAdd"+Str(#RemindAdd)
Debug "#RemindDiscard"+Str(#RemindDiscard)
Debug "#RemindTrashAll"+Str(#RemindTrashAll)
Global timenow.i, AnimateReminder.l, _x, _y
_x = GetSystemMetrics_(#SM_CXSCREEN)
_y = GetSystemMetrics_(#SM_CYSCREEN)
Debug Str(_x) +"  "+Str(_y)
Global DoMinimizedcSave
Global lippi.LASTINPUTINFO ; Wow! Who understands this stuff??!
lippi\cbSize = SizeOf(LASTINPUTINFO) ; Compensating for "dumb" API I guess?? 

#MCM_HITTEST = #MCM_FIRST + 14
#MCHT_CALENDAR = $20000
#MCHT_CALENDARDATE = #MCHT_CALENDAR | $0001
lvc.LV_COLUMN  ; set up structure for global use to format ListIconGadget columns
lvc\mask = #LVCF_FMT
lvc\fmt  = #LVCFMT_RIGHT
Global lpPrevFunc, WnHdl.i, now.i, selected.i, eventFile$, eventbackup$
TAG_CR$ = Chr(13)+Chr(10)
eventFile$=GetEnvironmentVariable("USERPROFILE") + "\MyEvents.txt"
eventbackup$=GetEnvironmentVariable("USERPROFILE") + "\MyEvents.bak"
#MainWindow = 1
#DateWindow = 0
If FileSize(eventFile$)= -1
  CreateFile(0,eventFile$)
  CloseFile(0)
EndIf

Procedure ReallySetForegroundWindow(m_hWnd.l)
  ; http://www.drdobbs.com/184405755
  hOtherWnd.l = GetForegroundWindow_()
  ;
  ; get thread handles on our window and foreground window
  hMyThread.l = GetWindowThreadProcessId_(m_hWnd, 0)
  hOtherThread.l = GetWindowThreadProcessId_(hOtherWnd,0)
  ;
  ; attach our thread to foreground thread, take foreground, and detach threads
  AttachThreadInput_(hMyThread,hOtherThread, #True)
  SetForegroundWindow_(m_hWnd)
  AttachThreadInput_(hMyThread,hOtherThread, #False)
  ;
  ; Now that our window "thread" has fisrt place in the queue...
  ; make sure our "window" is visible
  If IsIconic_(m_hWnd)
    ShowWindow_(m_hWnd,#SW_RESTORE)
  Else
    ShowWindow_(m_hWnd,#SW_SHOW)
  EndIf
  SetActiveWindow(GetDlgCtrlID_(m_hWnd))
  WaitWindowEvent()
  SetForegroundWindow_(m_hWnd)
EndProcedure 

Structure Cells
  chronos.l
  Descript$
EndStructure

Global NewList Ocassion.Cells()

Procedure FixTitle()
  s$ = " Reminder" + FormatDate("%mm/%dd/%yyyy",Date())
  SetWindowTitle(1,s$)
EndProcedure

Procedure TopOfToday()
  Protected TODAY$
  TODAY$ = Str(Month(Date()))
  TODAY$ + "/" + Str(Day(Date()))
  TODAY$ + "/" + Str(Year(Date()))
  Debug TODAY$
  now=ParseDate("%mm/%dd/%yyyy", TODAY$)
  ProcedureReturn now
EndProcedure

Procedure reList()
  Protected place
  ;FixTitle()
  TopOfToday()
  SortStructuredList(Ocassion(), 0, OffsetOf(Cells\chronos), #PB_Long)
  place = 0
  ForEach Ocassion()
    date1 = Ocassion()\chronos
    days = (date1 - now) /86400
    If days = 0
      dayZero.l = #True
      GetLastInputInfo_(@lippi) ; no recent user activity so let show and animate 'reminder'
      timenow.i = ElapsedMilliseconds() ; get current timestamp value for comparison calculations
      If timenow.i - lippi\dwTime > 100
        AnimateReminder.l = #True
      EndIf
      event$ = "TODAY is"
      If IsWindowVisible_(WindowID(0))
        ResizeWindow(#DateWindow, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
        HideWindow(1,0)
        ShowWindow_(WindowID(1),#SW_RESTORE)
        HideWindow(#DateWindow,0)
      Else
        HideWindow(1,0)
        ShowWindow_(WindowID(1),#SW_RESTORE)
      EndIf ;                                               BBGGRR
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  $10F8F8, -1)
    ElseIf days < 2
      event$ = Str(days) + " day until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  $0E0F0, -1)
    ElseIf days < 35
      event$ = Str(days) + " days until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  -1, -1)
    Else
      event$ = Str(days/7) + " weeks until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  -1, -1)
    EndIf
    SetGadgetItemText(2,place,event$,0)
    SetGadgetItemText(2,place,Ocassion()\Descript$,1)
    place + 1
  Next
  SendMessage_(GadgetID(2), #LVM_ENSUREVISIBLE, place-1, #False)
EndProcedure

Procedure ModEntry()
  date1 = ParseDate("%mm/%dd/%yyyy",FormatDate("%mm/%dd/%yyyy",GetGadgetState(0)))
  SelectElement(Ocassion(), selected)
  Ocassion()\chronos = date1
  s$ = GetGadgetText(1)
  If s$ = ""
    s$ = "undefined event"
  EndIf
  Ocassion()\Descript$ = s$
  reList()
  DisableGadget(3,0)
  SetGadgetState(2,-1) ; deselect all items
EndProcedure

Procedure LoadEvents()
  Protected F1.l, F2$
  If ReadFile(0,eventFile$)
    If Lof(0) > 10
      Repeat
        F2$ = ReadString(0,#PB_Ascii)
        F1.l = Val(F2$)
        F2$ = ReadString(0,#PB_Ascii)
        AddElement(Ocassion())
        Ocassion()\chronos = F1
        Ocassion()\Descript$ = F2$
        AddGadgetItem(2,-1,"Loading...")
      Until Eof(0)
      reList()
    EndIf
    success = #True
    CloseFile(0)
  Else
    Select MessageRequester("Read Failure!", "Unabblle to load events file." + Chr(10) + "Do you want to load events from backup?", #MB_YESNOCANCEL|#MB_ICONWARNING|#MB_TASKMODAL)
      Case #IDYES
        If ReadFile(0,eventbackup$)
          If Lof(0) > 10
            Repeat
              F2$ = ReadString(0,#PB_Ascii)
              F1.l = Val(F2$)
              F2$ = ReadString(0,#PB_Ascii)
              AddElement(Ocassion())
              Ocassion()\chronos = F1
              Ocassion()\Descript$ = F2$
              AddGadgetItem(2,-1,"Loading...")
            Until Eof(0)
            reList()
          EndIf
          CloseFile(0)
          success = #True
        EndIf
      Case #IDNO
        Select MessageRequester("WARNING!!!!", "Starting from scratch because Read Events Failed." + Chr(10) + "Do you want to continue from scratch?", #MB_OKCANCEL|#MB_ICONWARNING|#MB_TASKMODAL|#MB_DEFBUTTON2)
          Case #IDOK
            success = #True
          Case #IDCANCEL
            success = #False
        EndSelect        
      Case #IDCANCEL
        success = #False
    EndSelect
  EndIf
  ProcedureReturn success
EndProcedure

Procedure SaveEvents()
  DeleteFile(eventbackup$,#PB_FileSystem_Force)
  If  RenameFile(eventFile$, eventbackup$)
    If CreateFile(0,eventFile$)
      ForEach Ocassion()
        F1 = Ocassion()\chronos
        F2$ = Str(F1)
        WriteStringN(0,F2$,#PB_Ascii)
        F2$ = Ocassion()\Descript$
        WriteStringN(0,F2$,#PB_Ascii)
      Next
      CloseFile(0)
    EndIf
    success = #True
  Else
    MessageRequester("READ ERROR!", "Unable to Create backup!", #MB_OK|#MB_ICONWARNING|#MB_TASKMODAL)
  EndIf
  ProcedureReturn success
EndProcedure

Procedure JogReminder()
  Protected x, y
  Repeat
    x = Random(_x - 317,200)
  Until Abs(WindowX(1) - Abs(x)) > 100
  Repeat
    y = Random(_y - 199,200)
  Until Abs(WindowY(1) - Abs(y)) > 100
  Debug Str(x) +"  "+Str(y)
  ResizeWindow(1, Abs(x),Abs(y), #PB_Ignore,#PB_Ignore)
EndProcedure

Procedure TripTimer() ;system regulated - failproof interval trigger
                      ; Goofy internal Timer_() callback workaround
  tripTimer1 = #True  ; allows timer event to occur ONLY once on queue.
  SECOND.w = Second(Date())
  ; Required to prevent sluggish system after oneLook terminaltes.
EndProcedure

; &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

Structure MCHITTESTINFO
  cbSize.l
  pt.POINT
  uHIt.l
  st.SYSTEMTIME
EndStructure

Procedure WinCallback(WindowID1,message,wParam,lParam)
  Result = #PB_ProcessPureBasicEvents
  If WindowID1 = WnHdl
    Select message
      Case #WM_LBUTTONDBLCLK
        mcht.MCHITTESTINFO\cbSize = SizeOf(MCHITTESTINFO)
        mcht\pt\x = DesktopMouseX()
        mcht\pt\y = DesktopMouseY()
        ScreenToClient_(GadgetID(0), @mcht\pt)
        SendMessage_(GadgetID(0),#MCM_HITTEST,0,mcht)
        If mcht\uHIt = #MCHT_CALENDARDATE
          Debug "Date selected through double-click : " + FormatDate("%mm/%dd/%yyyy",GetGadgetState(0))
          ModEntry()
          HideWindow(0,1)
          SetActiveGadget(2)
          SaveEvents()
        EndIf
    EndSelect
    Debug "."+lpPrevFunc
    ProcedureReturn CallWindowProc_(lpPrevFunc,WindowID1,message,wParam,lParam)
  EndIf
  Debug "0"
  ProcedureReturn Result
EndProcedure

; &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
; Locate 24hr crossover in milliseconds for title change and list update
crossover.l = TopOfToday() + 86400  ; one full day into the future

;- CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS
LoadFont(1, "Segoe UI", 13)
If OpenWindow(#DateWindow,440,113,315,300,"Edit event & DblClick Date",#PB_Window_SystemMenu | #PB_Window_Invisible)
  WnHdl.i = CalendarGadget(0,0,35,305,250,Date())
  SetWindowTheme_(GadgetID(0), @"", @"")
  SetGadgetFont(0, FontID(1))
  lpPrevFunc = SetWindowLong_(GadgetID(0),#GWL_WNDPROC,@WinCallback())
  SetClassLong_(GadgetID(0),#GCL_STYLE,GetClassLong_(GadgetID(0),#GCL_STYLE) | #CS_DBLCLKS)
  StringGadget(1, 5, 10, 160, 20, "")
  SetGadgetAttribute(1,#PB_String_MaximumLength,29)  
  fontVerd9B.i = LoadFont(#PB_Default,"Verdana",34,#PB_Font_Bold)
  
  HWND1 = OpenWindow(#MainWindow, 312, 113, 570, 356, "EventReminder", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
  If HWND1
    LIG_Yval = 5
    If LoadFont(1, "Arial", 16)
      SetGadgetFont(#PB_Default, FontID(1))
    EndIf
    hLIG = ListIconGadget(2, 10, LIG_Yval, 553, 310, "", 170,   #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #LVS_NOCOLUMNHEADER)
    AddGadgetColumn ( 2 ,1 , "" ,377 )
    GadgetToolTip(2, "This is my tip.")
    SendMessage_(GadgetID(2), #LVM_SETCOLUMN, 0, @lvc)
    If LoadFont(2, "Arial", 12)
      SetGadgetFont(#PB_Default, FontID(2))
    EndIf
    ButtonGadget(3,10,320,155,25,"Add New Event")
    ButtonGadget(4,185,320,150,25,"Discard Event")
    ButtonGadget(5,350,320,195,25,"Discard All Changes")
    SetForegroundWindow_(WindowID(1))
    SetActiveWindow(1)
    If LoadEvents()
      check.l = 10000
      SetWindowCallback(@WinCallback())
      
      Repeat
        s.w = (Second(Date()) % 2)
      Until (s % 2) =  0
      trip.l = SetTimer_(WindowID(#MainWindow),0,2000,@TripTimer())
      
      ;-TOP OF LOOP
      Repeat
        Repeat
          EventID.l=WaitWindowEvent(check.l)
          If GetForegroundWindow_() <> WindowID(0)
            If IsWindowVisible_(WindowID(0))
              HideWindow(#DateWindow,1)
              DisableGadget(3,0)
              SetGadgetState(2,-1)
            EndIf
          ElseIf EventID = #PB_Event_CloseWindow
            EventID = 0
            HideWindow(#DateWindow,1)
            reList()
            DisableGadget(3,0)
            SetGadgetState(2,-1)
          EndIf
          Select EventID
            Case #WM_KEYDOWN
              Select EventwParam()
                Case #VK_RETURN
                  If GetActiveGadget() = 1  ; Enter on StringGadget - save the event
                    ModEntry()
                    HideWindow(#DateWindow, 1)
                    SaveEvents()
                  EndIf
                  h.l = GetGadgetState(2)
                  If h.l > -1
                    If GetActiveWindow() = 1
                      ResizeWindow(#DateWindow, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                      HideWindow(#DateWindow,0)
                      DisableGadget(3,1)
                      selected.i = GetGadgetState(2)
                      SelectElement(Ocassion(), selected)
                      date1 = Ocassion()\chronos
                      s$ = Ocassion()\Descript$
                      SetGadgetState(0, date1)
                      SetGadgetText(1,s$)
                      SetActiveGadget(1)
                      h.l = Len(GetGadgetText(1))
                      SendMessage_(GadgetID(1), #EM_SETSEL, h, h)
                      Repeat
                        WaitWindowEvent()
                      Until IsWindow(#DateWindow)
                      Repeat
                        WaitWindowEvent()
                      Until IsGadget(0)
                      While WindowEvent(): Wend
                    EndIf
                  EndIf
              EndSelect
            Case #PB_EventType_LostFocus
            Case #PB_Event_Gadget
              If GetActiveGadget() = 1
                ; StringGadget Enter key is caught via #WM_KEYDOWN / #VK_RETURN below
              EndIf
              Select EventGadget()
                Case 1
                  While WindowEvent(): Wend
                Case 2
                  If EventType() = #PB_EventType_LeftDoubleClick
                    If GetGadgetState(2) <> -1
                      Debug "Ok"
                      ResizeWindow(#DateWindow, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                      HideWindow(#DateWindow,0)
                      DisableGadget(3,1)
                      selected.i = GetGadgetState(2)
                      SelectElement(Ocassion(), selected)
                      date1 = Ocassion()\chronos
                      s$ = Ocassion()\Descript$
                      SetGadgetState(0, date1)
                      SetGadgetText(1,s$)
                      SetActiveGadget(1)
                      h.l = Len(GetGadgetText(1))
                      SendMessage_(GadgetID(1), #EM_SETSEL, h, h)
                      Repeat
                        WaitWindowEvent()
                      Until IsWindow(#DateWindow)
                      Repeat
                        WaitWindowEvent()
                      Until IsGadget(0)
                      While WindowEvent(): Wend
                    EndIf
                  EndIf
                Case 3 ; ADD NEW EVENT
                  If GetForegroundWindow_() = WindowID(1)
                    Debug "add event"
                    DisableGadget(3,1)
                    AddGadgetItem(2,0,"1 day until")
                    SetGadgetItemText(2,0,"undefined event",1)
                    selected.i = 0
                    SelectElement(Ocassion(), selected)
                    InsertElement(Ocassion())
                    TopOfToday()
                    date1 = ParseDate("%mm/%dd/%yyyy",FormatDate("%mm/%dd/%yyyy",now + 86400))
                    SetGadgetState(0, date1)
                    Debug now
                    Debug date1
                    Debug date1 - now
                    SelectElement(Ocassion(), selected)
                    Ocassion()\chronos = date1
                    Ocassion()\Descript$ = "undefined event"
                    date1 = Ocassion()\chronos
                    days = (date1 - now) /86400
                    event$ = Str(days) + " days until"
                    SetGadgetItemText(2,0,event$,0)
                    SetGadgetItemText(2,0,Ocassion()\Descript$,1)
                    ResizeWindow(#DateWindow, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                    HideWindow(#DateWindow,0)
                    SetGadgetText(1,"undefined event")
                    SetActiveGadget(2)
                    SetActiveGadget(1)
                    h.l = Len(GetGadgetText(1))
                    SendMessage_(GadgetID(1), #EM_SETSEL, 0, h.l)
                    Repeat
                      WaitWindowEvent()
                    Until IsWindow(#DateWindow)
                    Repeat
                      WaitWindowEvent()
                    Until IsGadget(0)
                    While WindowEvent(): Wend
                  EndIf
                  
                Case 4 ; DISCARD SELECTED EVENT
                  Debug "discard event"
                  If GetGadgetState(2) = -1
                    MessageRequester(" Nothing To Do", "Please highlight an item in the list and try again. ", #MB_OK)
                  Else
                    Select MessageRequester(" Delete Event From The List", "Are you sure you want to delete the selected event?  ", #MB_YESNO)
                      Case #IDYES
                        selected.i = GetGadgetState(2)
                        SelectElement(Ocassion(), selected)
                        DeleteElement(Ocassion())
                        RemoveGadgetItem(2, selected)
                        SaveEvents()
                    EndSelect
                    SetGadgetState(2,-1)
                  EndIf
                  SetActiveGadget(2)
                Case 5 ; FULL REVERT 
                  Debug "discard all changes"
                  Select MessageRequester(" Discard All Changes", "         Discard all changes" + Chr(10) + "     and revert to original list?  ", #MB_YESNO)
                    Case #IDYES
                      ClearList(Ocassion())
                      ClearGadgetItems(2)
                      LoadEvents()
                  EndSelect
                  SetActiveGadget(2)
              EndSelect
            Default
              If AnimateReminder = #True
                Debug "should be Jogging"
                GetLastInputInfo_(@lippi)
                timenow.i = ElapsedMilliseconds()
                If timenow.i - lippi\dwTime > 1500
                  If tripTimer1
                    jog + 1
                    tripTimer1 = #False
                  EndIf
                  If jog > 1
                    JogReminder()
                    jog = 0
                  EndIf
                Else
                  HideWindow(1,0)
                  SetForegroundWindow_(HWND20)
                  SetActiveWindow(1)
                  ResizeWindow(1, _x/2-160, _y/2-100, #PB_Ignore,#PB_Ignore)
                  AnimateReminder.l = #False
                  check.l = 1000
                EndIf
              Else
                Debug "Not Jogging"
                If dayZero.l = #True
                  If GetForegroundWindow_() <> HWND20
                    GetLastInputInfo_(@lippi)
                    timenow.i = ElapsedMilliseconds()
                    If timenow.i - lippi\dwTime > 300000
                      HideWindow(1,0)
                      ReallySetForegroundWindow(WindowID(1))
                      SetActiveWindow(1)
                      AnimateReminder.l = #True
                      check.l = 50
                    EndIf
                  EndIf
                EndIf
                GetCursorPos_(@CursorPosition.POINT) 
                a1.l = WindowFromPoint_(CursorPosition\y << 32 + CursorPosition\x)
                If a1 = hLIG
                  TopIndex = SendMessage_(GadgetID(2), #LVM_GETTOPINDEX, 0, 0)
                  insideY = (WindowMouseY(#MainWindow) -7)/36
                  hotline = TopIndex + insideY
                  ; FIX: guard hotline against out-of-bounds before SelectElement
                  ; catches the empty gap at the bottom of the list
                  If hotline < 0 Or hotline >= ListSize(Ocassion())
                    GadgetToolTip(2, " DoubleClick item to edit an event ")
                  Else
                    Debug "TopIndex = " + Str(TopIndex)
                    Debug "insideY = " + Str(insideY)
                    Debug "hotline = " + Str(hotline)
                    Debug " _ _ _ _ INSIDE THE LIST _ _ _ _"
                    y = ((WindowMouseY(1)) - LIG_Yval)
                    Debug "y = " + Str(y)
                    If y > 2
                      y - 3
                      itemLine = y / 14
                      Debug "itemLine = " + Str(itemLine)
                      If lastLine <> itemLine
                        If itemLine < CountGadgetItems(2)
                          SelectElement(Ocassion(), hotline)
                          date1 = Ocassion()\chronos
                          s$ = "`        " + FormatDate("%mm/%dd/%yyyy",date1) + " is " + Ocassion()\Descript$ + " "
                        Else
                          s$ = " DoubleClick item to edit an event "
                        EndIf
                        Debug itemLine
                        GadgetToolTip(2, s$)
                      EndIf
                      lastLine = itemLine
                    EndIf
                  EndIf
                EndIf
              EndIf
              Debug "check"
              If crossover.l - Date() < check.l
                check.l = 500
              EndIf
              If Date() >= crossover.l
                Debug "Crossover to next day"
                reList()
                crossover.l + 86400
                check.l = 10000
              EndIf
          EndSelect
          If EventID=#PB_Event_CloseWindow
            Select MessageRequester("  Event Reminder", "Reminder will Minimize or Quit." + Chr(10) + "Do you really want to Quit?", #MB_YESNOCANCEL|#MB_ICONQUESTION)
              Case #IDYES
              Case #IDNO
                SetWindowState(1,#PB_Window_Minimize)
                EventID.l=WaitWindowEvent(check.l)
              Case #IDCANCEL
                EventID=0
            EndSelect
          EndIf
        Until EventID=#PB_Event_CloseWindow
        ;- BOTTOM OF LOOP
      Until SaveEvents() = #True
    EndIf
    CloseWindow(1)
    CloseWindow(#DateWindow)
  EndIf
  KillTimer_(trip,1)
EndIf
Verified 6.30 PureBasic Demo compatible!

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sat Jan 28, 2012 3:03 pm
by oryaaaaa
I tested your app.
  • I alway use cell phone event reminder.
  • You should use StartDrawing(Windowoutput()), then use graphical interface.
  • coz Windows gadget is a fairy tale.
  • and USB Portable app target
  • Event name shortcut action
Japanese cell phone event remider
  • 2 month calender displayed
  • Please select event, holiday or aniversery?
  • 1.Icon Select
  • 2.day and night?
  • 3.start time/day
  • 4.finish time/day
  • 5.repeat? (day, week, monthry, year)
  • 6.Alarm ( annouce, prepare announce, off)
  • 7.Alarm sound ( FullSong/Melody, Music, Movie, OFF)
  • 8.event detail

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sat Jan 28, 2012 3:56 pm
by Randy Walker
Hi oryaaaaa :)
I guess I should have explained the exact intent of this little utility. To begin, I should also explain that I am ''chronologically challenged'' (to be politically correct). I have a big problem with people using dates on a calendar. Would be easier for me if weeks were all 10 days and months were 100 days... you know. Something less abstact than 7, 31 or 28, or 30 and sometimes 29 :shock:

I have a simple brain with simple capacitly so calendar dates just cause my brain to short circuit. On the other hand, when someone says 3 days from now, thats something I can easily relate to. You probably never have that problem. What I need for a scheduler is a very plain and dry list that shows days until the event and a short description. Short and simple. Thats me. (yeh, well... whatever :oops: )

Anyway, not indended to be glamorous in any way. Don't need alarms and whistles to make the world a noisier place. I can tell time of day so if I just have an item in my reminder that says '' 1 days until Dentist appnt at 2:30pm'' then thats enough to keep me on track.

Japan :!: :?: People in Japan really using PuireBasic? Thats too cool! Shoot me a note if you ever visit Bangkok.

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sat Jan 28, 2012 4:08 pm
by MachineCode
Love the dentist joke... it never gets old. :)

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sat Jan 28, 2012 5:10 pm
by oryaaaaa
I hate PuireBasic.

There is a special forum of PureBasic in the bulletin board that a special security force in Japan uses.
Because their request for participation standards were very high, I cannot pass it.
The forum was started up for myself therefore.

A special security force is excellent.
Middleware that beats the band without relying on the library in the foreign country is made.
As for me, they cannot oppose it.

A special security force Solicitation video

It's a Joke.

True story
The annual income of the software engineer of Japan is one million dollar.
Why is PureBasic needed though they are using Visual Studio Team Edition?
I introduced it to the community of Ubunutu.
However, they say for expensive PureBasic that to buy by 1/4 of the daily wages is high.

It's a Joke.

If you are new development, I have software that wants you to make it.
Trac like standalone, Version management in Purebasic
http://trac.edgewall.org/

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sun Jan 29, 2012 1:17 pm
by Randy Walker
MachineCode wrote:Love the dentist joke... it never gets old. :)
Ok, I edited code in my original post so it no longer shows '' 1 days until ...''
Now it shows '' 1 day until...' (<<singular) and I added background color to the '1day' and current day events.
Also did some houskeeping/renovation to throw out redundant code and organize the overall code.

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sun Jan 29, 2012 2:39 pm
by MachineCode
The joke wasn't about "day" or "days" (singular/plural). It was something else. Was it not intentional :?:

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sun Jan 29, 2012 6:15 pm
by HeX0R

Code: Select all

Procedure LoadEvents()
  If ReadFile(0,source$)
    If Lof(0) > 10
      Repeat
        F2$ = ReadString(0,#PB_Ascii)
        F1.l = Val(F2$)
        F2$ = ReadString(0,#PB_Ascii)
        AddElement(Ocassion())
        Ocassion()\chronos = F1
        Ocassion()\Descript$ = F2$
        AddGadgetItem(2,-1,"Loading...")
      Until Eof(0)
      CloseFile(0)
      refresh()
    EndIf
  EndIf
EndProcedure
If the file is less then 11 Bytes, it will never get closed.

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Mon Jan 30, 2012 4:20 am
by Randy Walker
HeX0R wrote:

Code: Select all

      Until Eof(0)
      refresh()
      CloseFile(0)  ; REMOVED THIS LINE !!!
    EndIf
    CloseFile(0)  ; ADDED THIS LINE
  EndIf
EndProcedure
If the file is less then 11 Bytes, it will never get closed.
Ouch!! Eww, yuck!! See now why I never claim to be a programmer.

Thanks HeXOR ... I also corrected code in my original post at top of this thread.

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Mon Jan 30, 2012 5:41 am
by Randy Walker
MachineCode wrote:The joke wasn't about "day" or "days" (singular/plural). It was something else. Was it not intentional :?:
Oh Crap :? I made a joke and don't even know what it is :o
No, I really have no clue what the joke was. What's the punchline? Er, uh. What was the joke? :oops:
I hate it when that happens :lol:

BTW: I updated code listing in the original post. More housekeeping. More comment lines. More bug fixes.

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Mon Jan 30, 2012 9:30 am
by MachineCode
Randy Walker wrote:Oh Crap :? I made a joke and don't even know what it is :o
No, I really have no clue what the joke was. What's the punchline? Er, uh. What was the joke? :oops:
I hate it when that happens :lol:
Okay, here it is:
Randy Walker wrote:1 days until Dentist appnt at 2:30pm
Now do you get it? ;)

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Mon Jan 30, 2012 7:13 pm
by Randy Walker
MachineCode wrote:Okay, here it is:
Randy Walker wrote:1 days until Dentist appnt at 2:30pm
Now do you get it? ;)
Nooo :? Call me dense. :|
Are you reading that?: ''One daze until dentist appointment at 2:30 PM.''
I don't get it. :(

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Mon Jan 30, 2012 10:38 pm
by MachineCode
Randy Walker wrote:I don't get it. :(
http://www.urbandictionary.com/define.p ... th%20hurty

8)

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Tue Jan 31, 2012 12:24 am
by Demivec
MachineCode wrote:Randy Walker wrote:
I don't get it.

http://www.urbandictionary.com/define.p ... th%20hurty
That is funny, thanks for explaining it to us less observant types. :)

Re: A Personal *Event Coundown Reminder* (Feedback Welcome)

Posted: Sun Oct 06, 2024 3:42 pm
by Randy Walker
Thought I would recompile using v6.12 but ran into a couple issues -- one minor issue and one catastrophic. Fonts used in my original creation were too big for the listbox and buttons so I've made changes there as seen in the code posted below.

The catastrophic issue is a crash with when I double click an item in the list or click add event:
"The Debugged Executable Quit Unexpectedly"
No clue why or what caused this, but net result is Unuseable.

Code: Select all

; Snipppet from 'Fluid byte' over there in the German forum
; used here as base starting point for 'Event Countdown Reminder':
; http://www.purebasic.fr/german/viewtopic.php?t=17692&start=4

; With special thanks to members of this thread:
; http://www.purebasic.fr/english/viewtopic.php?f=13&t=38387
;Define.i
Global active.w, targetWindow$, hWin.l
Procedure.l ListWindows(Window, Parameter) ; used inside Gt_Prog()
  ;  WindowClass.s = Space(255) 
  WindowTitle.s = Space(255) 
  ; e.l = GetWindowLong_(Window,#GWL_ID) ; PB Window_# (0,1,2,3, etc)
  ; GetClassName_(Window, WindowClass, 255) 
  GetWindowText_(Window, WindowTitle, 255)
  WindowTitle = LCase(WindowTitle)
  If LCase(WindowTitle) = targetWindow$
    active.w = 1
    hWin.l = Window
  ElseIf Left(WindowTitle,11) = targetWindow$
    active.w = 1
    hWin.l = Window
  ElseIf Left(WindowTitle,6) = Left(targetWindow$,6) ; "From: "
    If Right(WindowTitle,9) = Right(targetWindow$,9) ; " - E-mail"
      If Left(WindowTitle,6) <> Right(targetWindow$,6)
        active.w = 1
        hWin.l = Window
      EndIf
    EndIf
  EndIf 
  ProcedureReturn #True  
EndProcedure
Procedure Gt_Prog(dmy$)   ;   input : 11 char PROCESS name  ,  active.w = true if running : hWin.l holds Window handle
  targetWindow$ = LCase(dmy$)
  active.w = 0
  hWin.l = 0
  EnumWindows_(@ListWindows(), 0) ; Windows CallBack operation.
  ProcedureReturn active.w
EndProcedure
If Gt_Prog(" Event Coun")   ; Prevent 2nd instance if already running
  SetForegroundWindow_(hWin.l) ; and bring current instance into foreground.
  ShowWindow_(hWin.l, #SW_RESTORE)
  Delay(250)
  Debug "already running"
  ;While WindowEvent() <> 0 : Wend
  End ; Quit from this duplicate instance.
EndIf  

#MCM_HITTEST = #MCM_FIRST + 14
#MCHT_CALENDAR = $20000
#MCHT_CALENDARDATE = #MCHT_CALENDAR | $0001
lvc.LV_COLUMN  ; set up structure for global use to format ListIconGadget columns
lvc\mask = #LVCF_FMT
lvc\fmt  = #LVCFMT_RIGHT
Global lpPrevFunc, WnHdl.l, now.l, selected.l, eventFile$
TAG_CR$ = Chr(13)+Chr(10)
eventFile$=GetEnvironmentVariable("USERPROFILE") + "\MyEvents.txt"
If FileSize(eventFile$)= -1
  CreateFile(0,eventFile$)
  CloseFile(0)
EndIf

Structure Cells
  chronos.l
  Descript$
EndStructure

Global NewList Ocassion.Cells()

Procedure FixTitle()
  s$ = " Event Coundown Reminder  " + FormatDate("%mm/%dd/%yyyy",Date())
  SetWindowTitle(1,s$)
EndProcedure

Procedure TopOfToday()
  Protected TODAY$
  TODAY$ = Str(Month(Date()))
  TODAY$ + "/" + Str(Day(Date()))
  TODAY$ + "/" + Str(Year(Date()))
  Debug TODAY$
  now=ParseDate("%mm/%dd/%yyyy", TODAY$)
  ProcedureReturn now
EndProcedure

Procedure reList()
  Protected place
  ;FixTitle()
  TopOfToday()
  ;SortStructuredList(Ocassion(), 0, OffsetOf(Cells\chronos), #PB_Sort_Long)
  SortStructuredList(Ocassion(), 0, OffsetOf(Cells\chronos), #PB_Long)
  place = 0
  ForEach Ocassion()
    date1 = Ocassion()\chronos
    days = (date1 - now) /86400
    If days = 0
      event$ = "TODAY is"
      If IsWindowVisible_(WindowID(0))
        ResizeWindow(0, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
        HideWindow(1,0)
        ShowWindow_(WindowID(1),#SW_RESTORE)
        HideWindow(0,0)
      Else
        HideWindow(1,0)
        ShowWindow_(WindowID(1),#SW_RESTORE)
      EndIf ;                                               BBGGRR
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  $10F8F8, -1)
    ElseIf days < 2
      event$ = Str(days) + " day until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  $0AE0F0, -1)
    ElseIf days < 35
      event$ = Str(days) + " days until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  -1, -1)
    Else
      event$ = Str(days/7) + " weeks until"
      SetGadgetItemColor(2,  place, #PB_Gadget_BackColor,  -1, -1)
    EndIf
    SetGadgetItemText(2,place,event$,0)
    SetGadgetItemText(2,place,Ocassion()\Descript$,1)
    place + 1
  Next
EndProcedure

Procedure ModEntry()
  date1 = ParseDate("%mm/%dd/%yyyy",FormatDate("%mm/%dd/%yyyy",GetGadgetState(0)))
  SelectElement(Ocassion(), selected)
  Ocassion()\chronos = date1
  s$ = GetGadgetText(1)
  If s$ = ""
    s$ = "undefined event"
  EndIf
  Ocassion()\Descript$ = s$
  reList()
  DisableGadget(3,0)
  SetGadgetState(2,-1) ; deselect all items
EndProcedure

Procedure LoadEvents()
  Protected F1.l, F2$
  If ReadFile(0,eventFile$)
    If Lof(0) > 10
      Repeat
        F2$ = ReadString(0,#PB_Ascii)
        F1.l = Val(F2$)
        F2$ = ReadString(0,#PB_Ascii)
        AddElement(Ocassion())
        Ocassion()\chronos = F1
        Ocassion()\Descript$ = F2$
        AddGadgetItem(2,-1,"Loading...")
      Until Eof(0)
      reList()
    EndIf
    CloseFile(0)
  EndIf
EndProcedure

; &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

Structure MCHITTESTINFO
  cbSize.l
  pt.POINT
  uHIt.l
  st.SYSTEMTIME
EndStructure

Procedure WinCallback(WindowID1.l,Message.l,wParam.l,lParam.l)
  Result.l = #PB_ProcessPureBasicEvents
  If WindowID1.l = WnHdl.l
    Select Message
      Case #WM_LBUTTONDBLCLK
        mcht.MCHITTESTINFO\cbSize = SizeOf(MCHITTESTINFO)
        mcht\pt\x = DesktopMouseX()
        mcht\pt\y = DesktopMouseY()
        ScreenToClient_(GadgetID(0), @mcht\pt)
        SendMessage_(GadgetID(0),#MCM_HITTEST,0,mcht)
        If mcht\uHIt = #MCHT_CALENDARDATE
          Debug "Date selected through double-click : " + FormatDate("%mm/%dd/%yyyy",GetGadgetState(0))
          ModEntry()
          HideWindow(0,1)
          SetActiveGadget(2)
        EndIf
    EndSelect
    ProcedureReturn CallWindowProc_(lpPrevFunc,WindowID1.l,Message,wParam,lParam)
  EndIf
  ProcedureReturn Result
EndProcedure

; &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
; Locate 24hr crossover in milliseconds for title change and list update
crossover.l = TopOfToday() + 86400  ; one full day into the future

; CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS   CREATE WINDOWS
  ;LoadFont(1, "Segoe UI", 24)
  LoadFont(1, "Rockwell", 24)
If OpenWindow(0,440,113, 250, 230,"Edit event and DoubleClick",#PB_Window_SystemMenu | #PB_Window_Invisible)
  WnHdl.l = CalendarGadget(0,5,35, 240, 220)
  SetWindowTheme_(GadgetID(0), @"", @"")
  SetGadgetFont(0, FontID(1))
  lpPrevFunc = SetWindowLong_(GadgetID(0),#GWL_WNDPROC,@WinCallback())
  SetClassLong_(GadgetID(0),#GCL_STYLE,GetClassLong_(GadgetID(0),#GCL_STYLE) | #CS_DBLCLKS)
  ;LoadFont(1, "Segoe UI", 12)
  StringGadget(1, 5, 10, 195, 20, "", #ES_MULTILINE|#ES_AUTOVSCROLL|$10000000)
  
  If OpenWindow(1, 312, 113, 317, 215, " Event Coundown Reminder", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar)
    LIG_Yval = 5
    hLIG = ListIconGadget(2, 10, LIG_Yval, 298, 184, "", 80,   #PB_ListIcon_AlwaysShowSelection | #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #LVS_NOCOLUMNHEADER)
    AddGadgetColumn ( 2 ,1 , "" ,196 )
    GadgetToolTip(2, "This is my tip.")
    ;SendMessage_(GadgetID(2), #LVM_SETEXTENDEDLISTVIEWSTYLE, 0, #LVS_EX_LABELTIP) 
    SendMessage_(GadgetID(2), #LVM_SETCOLUMN, 0, @lvc)  ; Right justify column 1
    LoadFont(1, "Arial Rounded MT Bold", 8)
    SetGadgetFont(#PB_Default, FontID(1))
    ButtonGadget(3,15,190,90,17,"Add New Event")
    ButtonGadget(4,110,190,80,17,"Discard Event")
    ButtonGadget(5,193,190,110,17,"Discard All Changes")
    SetForegroundWindow_(WindowID(1))
    SetActiveWindow(1)
    LoadEvents()
    check.l = 10000 ; used to monitor ElapsedMilliseconds status
    
    ;  TOP OF LOOP     TOP OF LOOP     TOP OF LOOP     TOP OF LOOP     TOP OF LOOP
    Repeat
      EventID.l=WaitWindowEvent(check.l)
      If GetForegroundWindow_() <> WindowID(0)
        If IsWindowVisible_(WindowID(0))
          HideWindow(0,1)
          DisableGadget(3,0)
          SetGadgetState(2,-1) ; deselect all items
        EndIf
      ElseIf EventID = #PB_Event_CloseWindow
        EventID = 0
        HideWindow(0,1)
        reList()
        DisableGadget(3,0)
        SetGadgetState(2,-1) ; deselect all items
      EndIf
      Select EventID
        Case #WM_KEYDOWN  ;  PRESSED THE [ENTER} KEY
          Select EventwParam()
            Case #VK_RETURN
              h.l = GetGadgetState(2) ; first selected item
              If h.l > -1 ;Domething selected so edit
                If GetActiveWindow() = 1
                  ResizeWindow(0, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                  HideWindow(0,0)
                  DisableGadget(3,1)
                  selected.l = GetGadgetState(2)
                  SelectElement(Ocassion(), selected)
                  date1 = Ocassion()\chronos
                  s$ = Ocassion()\Descript$
                  
                  SetGadgetState(0, date1)
                  SetGadgetText(1,s$)
                  SetActiveGadget(1)
                  h.l = Len(GetGadgetText(1))
                  SendMessage_(GadgetID(1), #EM_SETSEL, h, h) ; 
                  Repeat
                    WaitWindowEvent()
                  Until IsWindow(0)
                  Repeat
                    WaitWindowEvent()
                  Until IsGadget(0)
                  While WindowEvent(): Wend
                EndIf
              EndIf
          EndSelect
        Case #PB_EventType_LostFocus ; useless message... so do nothing
        Case #PB_Event_Gadget
          If GetActiveGadget() = 1
            s$=GetGadgetText(1)
            h.l=FindString(s$,TAG_CR$,1) ; Stop CR/Esc Ding
            If h.l<>0  ; Was Enter pressed on the StringGadget?
              Debug "Pressed Enter on Calendar"
              SetGadgetText(1,ReplaceString(s$,TAG_CR$,"")) ; Yes, so remove CR+LF.
              SendMessage_(GadgetID(1), #EM_SETSEL, h.l-1, h.l-1)  ; Set cursor position back.
              While WindowEvent(): Wend
              Debug "Date selected through Enter Key : " + FormatDate("%mm/%dd/%yyyy",GetGadgetState(0))
              Debug GetGadgetState(0)
              ModEntry()
              HideWindow(0,1)
            EndIf
          EndIf
          Select EventGadget()
            Case 1
              While WindowEvent(): Wend
            Case 2  ;  DOUBLECLICK TO EDIT EVENT
              If EventType() = #PB_EventType_LeftDoubleClick
                If GetGadgetState(2) <> -1
                  Debug "Ok"
                  ResizeWindow(0, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                  HideWindow(0,0)
                  DisableGadget(3,1)
                  selected.l = GetGadgetState(2)
                  SelectElement(Ocassion(), selected)
                  date1 = Ocassion()\chronos
                  s$ = Ocassion()\Descript$
                  
                  SetGadgetState(0, date1)
                  SetGadgetText(1,s$)
                  SetActiveGadget(1)
                  h.l = Len(GetGadgetText(1))
                  SendMessage_(GadgetID(1), #EM_SETSEL, h, h) ; 
                  Repeat
                    WaitWindowEvent()
                  Until IsWindow(0)
                  Repeat
                    WaitWindowEvent()
                  Until IsGadget(0)
                  While WindowEvent(): Wend
                EndIf
              EndIf
            Case 3 ; ADD NEW EVENT
              If GetForegroundWindow_() = WindowID(1)
                Debug "add event"
                DisableGadget(3,1)
                AddGadgetItem(2,0,"1 day until")
                SetGadgetItemText(2,0,"undefined event",1)
                selected.l = 0
                SelectElement(Ocassion(), selected)
                InsertElement(Ocassion())
                
                TopOfToday()
                date1 = ParseDate("%mm/%dd/%yyyy",FormatDate("%mm/%dd/%yyyy",now + 86400))
                SetGadgetState(0, date1)
                Debug now
                Debug date1
                Debug date1 - now
                SelectElement(Ocassion(), selected)
                Ocassion()\chronos = date1
                Ocassion()\Descript$ = "undefined event"
                
                date1 = Ocassion()\chronos
                days = (date1 - now) /86400
                event$ = Str(days) + " days until"
                SetGadgetItemText(2,0,event$,0)
                SetGadgetItemText(2,0,Ocassion()\Descript$,1)
                ResizeWindow(0, WindowX(1)+112, WindowY(1), #PB_Ignore, #PB_Ignore)
                HideWindow(0,0)
                SetGadgetText(1,"undefined event")
                SetActiveGadget(2)
                SetActiveGadget(1)
                h.l = Len(GetGadgetText(1))
                SendMessage_(GadgetID(1), #EM_SETSEL, 0, h.l) ; 
                Repeat
                  WaitWindowEvent()
                Until IsWindow(0)
                Repeat
                  WaitWindowEvent()
                Until IsGadget(0)
                While WindowEvent(): Wend
              EndIf
              
            Case 4 ; DISCARD SELECTED EVENT
              Debug "discard event"
              If GetGadgetState(2) = -1
                MessageRequester(" Nothing To Do", "Please highlight an item in the list and try again. ", #MB_OK)
              Else
                Select MessageRequester(" Delete Event From The List", "Are you sure you want to delete the selected event?  ", #MB_YESNO)
                  Case #IDYES
                    selected.l = GetGadgetState(2)
                    SelectElement(Ocassion(), selected)
                    DeleteElement(Ocassion())
                    RemoveGadgetItem(2, selected)
                EndSelect
                SetGadgetState(2,-1) ; deselect all items
              EndIf
              SetActiveGadget(2)
            Case 5 ; FULL REVERT 
              Debug "discard all changes"
              Select MessageRequester(" Discard All Changes", "         Discard all changes" + Chr(10) + "     and revert to original list?  ", #MB_YESNO)
                Case #IDYES
                  ClearList(Ocassion())
                  ClearGadgetItems(2)
                  LoadEvents()
              EndSelect
              SetActiveGadget(2)
          EndSelect
        Default
          GetCursorPos_(@CursorPosition.POINT) 
          a1.l = WindowFromPoint_(CursorPosition\y<<  32 + CursorPosition\x) ;where on the screen?
          If a1 = hLIG  ;  using a1 to validate mouse inside #MyGadget
            Debug " _ _ _ _ INSIDE THE LIST _ _ _ _"
            y = ((WindowMouseY(1)) - LIG_Yval)
            Debug "y = " + Str(y)
            If y > 2 ; Adjust y value to compensate for column_header_height
              y - 3  ; Adjust y value to compensate for column_header_height 
              itemLine = y / 14  ; Divide y by pixel_height given to single_line_item.
              Debug "itemLine = " + Str(itemLine)
              itemLine + SendMessage_(hLIG,#LVM_GETTOPINDEX,0,0)
              If lastLine <> itemLine ; Anti-Flicker -- update tooltip only if required
                If itemLine < CountGadgetItems(2) ; Range restriction
                  SelectElement(Ocassion(), itemLine)
                  date1 = Ocassion()\chronos
                  s$ = " " + FormatDate("%mm/%dd/%yyyy",date1) + " is " + Ocassion()\Descript$ + " "
                Else ; out of range
                  s$ = " DoubleClick item to edit an event "
                EndIf
                GadgetToolTip(2, s$)
              EndIf
              lastLine = itemLine
            EndIf
          EndIf
          Debug "check"
          If crossover.l - Date() < check.l ; <<< Check value is usesd in WaitWindowEvent() to force time checks
            check.l = 500 ; expidite monitoring when approaching crossover to new day
          EndIf
          If Date() >= crossover.l
            Debug "Crossover to next day"
            reList() ; will adjust title date and countdown values in the list
            crossover.l + 86400 ; increment trigger to next day
            check.l = 10000 ; revert to non-aggressive ElapsedMilliseconds monitoring
          EndIf
      EndSelect
      If EventID=#PB_Event_CloseWindow
        Select MessageRequester("  Event Reminder", "               Reminder will Minimize or Quit." + Chr(10) + "                 Do you really want to Quit?", #MB_YESNOCANCEL|#MB_DEFBUTTON2)
          Case #IDYES
          Case #IDCANCEL
            EventID.l=WaitWindowEvent(check.l)
          Case #IDNO
            SetWindowState(1,#PB_Window_Minimize)
            EventID.l=WaitWindowEvent(check.l)
        EndSelect
      EndIf
    Until EventID=#PB_Event_CloseWindow
    ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ;eventFile$="E:\Temp\MyEvent.txt"
    If CreateFile(0,eventFile$)
      ForEach Ocassion()
        F1 = Ocassion()\chronos
        F2$ = Str(F1)
        WriteStringN(0,F2$,#PB_Ascii)
        F2$ = Ocassion()\Descript$
        WriteStringN(0,F2$,#PB_Ascii)
      Next
      CloseFile(0)
    EndIf
    CloseWindow(1)
    CloseWindow(0)
  EndIf
EndIf
Is it a bug or something I did wrong? I'm pretty good at that :oops: