- TimePickerGadget
Shardik hat sich (trotz ausgefallenem Weihnachtsurlaub) mächtig reingehängt und den MacOS-Teil angepasst sowie das Ganze auch noch in Module-Form gebracht.
Vom Umfang geht es z.Zt. etwas in Richtung amerikanischer SUV (= Deutscher Gigaliner)

- Das 'TimePickerGadget' (hält namentlich PureBasic den Weg für ein 'TimeGadget' frei) wird ähnlich zu PB-Gadgets angelegt.
Details sind im Header des Sourcecodes zu ersehen!
- Die zusätzliche Angabe des Parameters #Window ist jedoch zwingend (und wie ich beim Tippen dieser Zeilen grad merke wird hier auch (noch) kein #PB_Any akzeptiert!).
- Der Parameter #Gadget kann eine PBNo/Konstante (> -1) oder #PB_Any sein. (#Gadget kann als Dummy zur Erstellung verwendet werden, wird aber später nicht mehr benötigt!)
- Für lesende oder schreibende Zugriffe auf das Gadget (s.u.) wird nicht die PB-GadgetNo (ala #Gadget) sondern der Zeiger/OS-Handle auf das OS-Objekt benötigt! (ala GadgetID), da im MacOS keinerlei Dummy-PB-Gadget dafür existiert!
- Lesen und Schreiben einer Uhrzeit (Tageszeit als String oder Sekunden seit der vergangenen Mitternacht) geschieht mit ...
- GetTimePickerGadgetState(GadgetId)
GetTimePickerGadgetText(GadgetId)
SetTimePickerGadgetState(GadgetId, TimeVal)
SetTimePickerGadgetText(GadgetId, sTimeString.s) als formatierter String im Format #TPG_TimeMask24h (= "%hh:%ii:%ss")
- Ich hab noch einige OS-abhängigen Flags und Konstanten dafür integriert, siehe im Code-Header.
- Der Zugriff auf die Funktionen erfolgt (bei Verwendung von UseModule TimePickerGadget mit den Funktionsnamen,
ohne Verwendung von UseModule TimePickerGadget mit, den Funktionsaufrufen vorangestelltem TimePickerGadget::). Siehe auch in der folgenden Demo.
TimePickerGadget-Modul, All OS, eingebettet in einen Democode mit 2 Gadgets auf 2 Fenster verteilt: V0.21, 2018-01-20
Code: Alles auswählen
; Module/File: TimePickerGadget_AllOS_V0_21.pb
; Function: Gadget to edit, set and get time as module
; Author: Teamwork: Omi + Shardik + others (netmaestro, PB, ..)
; Date: Jan. 19, 2019
; Version: 0.21
; Target OS: Windows, Linux Gtk2/Gtk3, MacOS
; Target Compiler: PureBasic 5.46/5.62/5.70
; Compilation on MacOS 10.6.8 (Snow Leopard) with PB 5.46/5.62 only possible with x86 compiler
; Last version supporting x64 compilation on Snow Leopard was PB 5.43
; Minimum MacOS version for 5.70 x86 + x64: 10.8 (Mountain Lion)
; Links to topic: https://www.purebasic.fr/german/viewtopic.php?f=3&t=31214
; https://www.purebasic.fr/english/viewtopic.php?f=12&t=23797
; https://www.purebasic.fr/english/viewtopic.php?t=33184
; https://www.purebasic.fr/english/viewtopic.php?f=3&t=30025
;--------------------------------------------------------------
;V0.10 : Access with Gadget constant & GadgetNo (no Id)
;V0.11 : Access with var for GadgetId & GadgetId (no No/#Gadget)
;V0.12 : Trial to adapt and complete the Mac part
;V0.13 : 2 different module accesses in EventLoop with and without UseModule TimePickerGadget
;V0.14 : #PB_Any now works as #Gadget for TimePickerGadget
;V0.15 : Mac part bugs fixed:
; - GetTimePickerGadgetState(): Time had to be extracted from sCurrentDate and
; ProcedureReturn Mid(sCurrentDate, 11, 9)
; had to be changed to
; ProcedureReturn TimeVal
; - TimePickerGadget(): Date and time offset had to be added to sTime
; - SetTimePickerGadgetText(): Date and time offset had to be added to sTimeString
; - SetTimePickerGadgetState(): Date and time offset had to be added to sTime and sTime
; General Mac problems still to be solved:
; - Cocoa class NSDate needs time offset to UTC: currently staticly set to
; +01:00 for German timezone (Berlin)
; - Callback integration still necessary to update the TextGadgets "Time set:" and "Time value set (=seconds):"
;V0.16 : MacOS callback implemented
;V0.17 : Added missing debug warnings on not available flags. Flag names corrected in warnings.
;V0.18 : MacOS Sierra bug fix: in GetTimePickerGadgetState(GadgetId) und GetTimePickerGadgetText(GadgetId) one hour
; UTC correction had to be added (currently correct only in German timezone!), but now Snow Leopard display wrong time!
;V0.19 : For MacOS automatic detection of UTC offset implemented
; Detection of MacOS version added to add UTC offset only in Get-Procedures for versions newer than Snow Leopard
;V0.20 : Now #PB_Any instead of GadgetNo for second window for demo part
; Shardik: Fixed Windows issue on Ascii-executable
; Added Flag #TPG_Time_BorderLess for MacOS
;V0.21 : Fixed Flag names and some (now wrong) hints in this description and the code + tidy up a bit
; Changed for Win: Callback_Window_Main() to Callback_Window() cause now it applies to all windows
;T e r m s:
;GadgetNo: PB-No for the Gadget, like #Gadget (here i.e. #TimeGadget1) or the Return from Gadget creation with #PB_Any
;GadgetId: Pointer to the API control/widget/object like GadgetID(GadgetNo), *widget, ...
;C r e a t e Gadget for Module-Version (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; *GadgetId= TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags)
; P a r a m e t e r s:
; GadgetNo: Can be a valid constant number (but nor necessary) AND #PB_Any
; x,y,w,h : Coordinates as usual
; sTime : Time as valid string. From 00:00:00 to 23:59:59, for Windows #TPG_Time_12h format too !!! (it's auto converted)
; WindowNo: The PB #Window number (not WindowId or Result)
; Flags : see below
; -> USE ONLY THE RETURNED POINTER (*GadgetId) FOR ACCESS !!! (here *gTimeGadget1 or *gTimeGadget2)
;A c c e s s for Module-Version (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; Use the pointer (here *gTimeGadget1 or *gTimeGadget2) as GadgetID for access ....
; - GetTimePickerGadgetState(GadgetID); Equivalent to GetGadgetState(#Gadget)
; : Provides the time as TimeValue
; - SetTimePickerGadgetState(GadgetID, TimeVal); Equivalent to SetGadgetState(#Gadget, State)
; : Set Gadgets time from TimeValue
; - GetTimePickerGadgetText(GadgetID) Equivalent to GetGadgetText(#Gadget)
; : Provides the time as formated TimeString, #TPG_TimeMask24h: %hh:%ii:%ss, #TPG_Time_12h too
; - SetTimePickerGadgetText(GadgetID, TimeString.s) Equivalent to SetGadgetText(#Gadget, Text$)
; : Set the Gadgets time as formated TimeString, #TPG_TimeMask24h: %hh:%ii:%ss, #TPG_Time_12h too
;F l a g s for creating (without 'UseModule TimePickerGadget' use prefixed 'TimePickerGadget::'):
; Linux Gtk:
; - #TPG_Time_ReadOnly Same effect as #PB_String_ReadOnly on StringGadget
; - #TPG_Time_BorderLess Same effect as #PB_String_BorderLess on StringGadget
; - (#TPG_Time_NoUpDown) Always ON
; - (#TPG_Time_12h) Not yet possible
; Mac:
; - (#TPG_Time_ReadOnly?) Possible ?
; - #TPG_Time_BorderLess? Gadget w/o border
; - #TPG_Time_NoUpDown w/o up/down arrows
; - (#TPG_Time_12h) Seems not possible ?
; Windows:
; - (#TPG_Time_ReadOnly?) Seems not possible ?
; - #TPG_Time_BorderLess Gadget w/o border
; - (#TPG_Time_NoUpDown?) Seems not possible ?
; - #TPG_Time_12h 12h setting w. attached AM/PM segment.
; -> Time range in Windows AM/PM ...
; 24h: 00:00:00 = 12h: 12:00:00 AM
; 24h: 00:59:59 = 12h: 12:59:59 AM
; 24h: 01:00:00 = 12h: 01:00:00 AM
; 24h: 12:00:00 = 12h: 12:00:00 PM
; 24h: 12:59:59 = 12h: 12:59:59 PM
; 24h: 13:00:00 = 12h: 01:00:00 PM
; 24h: 23:59:59 = 12h: 11:59:59 PM
; see https://www.timeanddate.com/time/am-And-pm.html
;A d a p t i o n:
; Win: automated
; Lin: automated
; Mac: automated
;E v e n t s:
; Watch (EventType() on) pointer to TimePickerGadget within PB-WindowNo section. Here *gTimeGadget1 or *gTimeGadget2
;C O N T R O L L I N G:
; Flag or feature Linux Mac Windows
; ----------------------------------------------------------------------------
; #TPG_Time_ReadOnly + - -
; #TPG_Time_BorderLess + + +
; #TPG_Time_NoUpDown always + -
; #TPG_Time_12h - - +
; Value scrolling with mousewheel over segment + - -
; Value scrolling with mousewheel over arrows - - +
; Value scrolling with key <up>/<down> + + +
; Value scrolling with num pads <+>/<-> + - +
; Wrapping on segments value overrun/undershoot - ? +
; Value jumps to segments max. value <End> - <End>
; <Home>
; Value jumps to segments min. value (0) <Home> - -
; Value reset (recover format) <Esc> - -
; Segment selection with mouse click + + +
; Segment selection with key <left>/<right> + + +
; Wrapping on segment change + - <Right>
; Segment forwarding - <-> -
; </>
; <,>
;-Init Demo ...
EnableExplicit
; Object constants
Enumeration Windows
#Win_Main = 1; Set to 1 for tests (because 0 is a empty value too)
; #Win_Sec; 2. Window now with #PB_Any for Demo
EndEnumeration
Enumeration Gadgets
#TimeGadget1= 1; 1. TimePickerGadget with GadgetNo (not needed any longer!)
#StGMain1
#TxGMain1
#TxMainG2
#BtGMainSet
;#TimeGadget2; 2. TimePickerGadget now with #PB_Any
#StGSec1
#TxGSec1
#TxGSec2
#BtGSecSet
EndEnumeration
Global.i gEvent, gEventGadget, gEventWin, gQuit; For EventLoop
Global *gTimeGadget1, *gTimeGadget2; GadgetId (see above, pointer is neccessary)
Global.i gWin_Sec; Instead of #Win_Sec for #PB_Any on Window 2
;- Time Picker module ...
DeclareModule TimePickerGadget
;-{ DeclareModul: TimePickerGadget ...
;Flags for Gadget creation ...
#Event_TimePickerChanged = #PB_Event_FirstCustomValue
#TPG_Time_ReadOnly = 1 << 8; Time not editable. Only Linux
#TPG_Time_BorderLess = 1 << 9; Display without border (Mac without funktion)
#TPG_Time_NoUpDown = 1 << 10; Display without up/down arrow buttons (Linux always on, Windows not available)
#TPG_Time_12h = 1 << 11; Display + edit in 12h format 08:00:00 AM | 08:00:00 PM, only Windows
#TPG_TimeMask24h = "%hh:%ii:%ss"
#TPG_DebugOn = #True; Output in Debug window. Set to #False if it's annoying
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Linux
ImportC ""
gtk_widget_has_focus (*widget.GtkWidget)
gtk_editable_get_selection_bounds(*entry.GtkEntry, *start_pos, *end_pos)
gtk_entry_set_text (*entry.GtkEntry, text.p-utf8)
gtk_entry_get_text (*entry.GtkEntry)
gtk_entry_get_text_length (*entry.GtkEntry)
gtk_entry_set_alignment (*entry.GtkEntry, xalign.f)
g_signal_connect (*instance, detailed_signal.p-utf8, *c_handler, *data, destroy= 0, flags= 0) As "g_signal_connect_data"
EndImport
;from #define __GDK_KEYSYMS_H__
#GDK_KEY_Up = $FF52
#GDK_KEY_Down = $FF54
#GDK_KEY_Left = $FF51
#GDK_KEY_Right = $FF53
#GDK_KEY_Tab = $FF09
#GDK_KEY_Escape = $FF1B
#GDK_KEY_KP_Add = $FFAB
#GDK_KEY_KP_Subtract = $FFAD
#GDK_KEY_End = $FF57
#GDK_KEY_Home = $FF50
#GDK_KEY_KP_0 = $FFB0
#GDK_KEY_KP_9 = $FFB9
#GDK_KEY_0 = $0030
#GDK_KEY_9 = $0039
Structure CALLBACKUSERDATA
GadgetId.i
WindowNo.i
EndStructure
NewList GadgetData.CALLBACKUSERDATA()
CompilerCase #PB_OS_MacOS
#NSDatePickerStyleTextFieldAndStepper = 0
#NSDatePickerStyleTextField = 2
#NSHourMinuteSecondDatePickerElementFlag = 14
Structure TimePickerCellEntry
ID.I
WindowNo.I
EndStructure
NewList TimePickerCell.TimePickerCellEntry()
CompilerEndSelect
Declare.i GetTimePickerGadgetState(GadgetId)
Declare SetTimePickerGadgetState(GadgetId, TimeVal)
Declare.s GetTimePickerGadgetText (GadgetId)
Declare SetTimePickerGadgetText (GadgetId, sTimeString.s)
Declare TimePickerGadget (GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null)
;-} EndDeclareModul: TimePickerGadget ...
EndDeclareModule
Module TimePickerGadget
;-{ Modul: TimePickerGadget ...
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Linux
;-{ _Linux TimePickerGadget API ...
Procedure.i GetTimePickerGadgetState(GadgetId)
Protected.i TimeVal
TimeVal= ParseDate(#TPG_TimeMask24h, PeekS(gtk_entry_get_text(GadgetId), -1, #PB_UTF8))
ProcedureReturn TimeVal
EndProcedure
Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
If TimeVal > -1 And TimeVal < 86400
gtk_entry_set_text(GadgetId, FormatDate(#TPG_TimeMask24h, TimeVal))
EndIf
EndProcedure
Procedure.s GetTimePickerGadgetText(GadgetId)
Protected.i TimeVal
Protected.s sTimeString= PeekS(gtk_entry_get_text(GadgetId), -1, #PB_UTF8)
TimeVal= ParseDate(#TPG_TimeMask24h, sTimeString)
sTimeString= FormatDate(#TPG_TimeMask24h, TimeVal)
ProcedureReturn sTimeString
EndProcedure
Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
Protected.i TimeVal
If ParseDate(#TPG_TimeMask24h, sTimeString) > -1
gtk_entry_set_text(GadgetId, sTimeString)
EndIf
EndProcedure
Procedure _TimePickerGadget_SelectPart(*widget, Range, Colon1, Colon2); internal
Select Range
Case 1 : gtk_editable_select_region_(*widget, 0, Colon1 - 1)
Case 2 : gtk_editable_select_region_(*widget, Colon1, Colon2 - 1)
Case 3 : gtk_editable_select_region_(*widget, Colon2, -1)
EndSelect
EndProcedure
Procedure _TimePickerGadget_CheckInput(*widget.GtkWidget, EditAction, *user_data.CALLBACKUSERDATA); internal
; EditAction: 0 = nothing, -1 = scroll-, 1 = scroll+, -2 = lower limit, 2 = upper limit, 3 = number input
Protected.i cPos, I
Protected.i cBlock; 0= out, 1 = h, 2 = m, 3 = s
Protected.s sPart
Protected.s sTime= PeekS(gtk_entry_get_text_(*widget), -1, #PB_UTF8), sTime_
Protected.i Colon1= FindString(sTime, ":"); Find separators on existing string
Protected.i Colon2= FindString(sTime, ":", Colon1 + 1)
Static.s TimeMax= "23:59:59"
Protected.i hMax= Val(StringField(TimeMax, 1, ":"))
Protected.i mMax= Val(StringField(TimeMax, 2, ":"))
Protected.i sMax= Val(StringField(TimeMax, 3, ":"))
If gtk_widget_has_focus(*widget) And gtk_editable_get_editable_(*widget)
cPos= gtk_editable_get_position_(*widget); Get cursor offset
Select cPos; Specify segment index on existing time string
Case 0 To Colon1 - 1 : cBlock= 1
Case Colon1 To Colon2 - 1 : cBlock= 2
Case Colon2 To Len(sTime) : cBlock= 3
EndSelect
If EditAction < 3; All scroll inputs in segment (up/down, +/-, home/end or mouse scrolling) ...
For I= 1 To 3; Reformat segments
sTime_+ RSet(Str(Val(StringField(sTime, I, ":"))), 2, "0") + ":"
Next I
sTime = RTrim(sTime_, ":")
Colon1= FindString(sTime, ":"); Correct separators on existing string
Colon2= FindString(sTime, ":", Colon1 + 1)
gtk_entry_set_text (*widget, sTime); Reformat string
_TimePickerGadget_SelectPart(*widget, cBlock, Colon1, Colon2); Set selected segment
sPart= StringField(sTime, cBlock, ":"); Read segments value under cursor
Select EditAction; Calculate new values from scroll inputs
Case 1 : sPart= Str(Val(sPart) + 1)
Case -1 : sPart= Str(Val(sPart) - (Bool(Val(sPart) > 0)))
Case 2 : sPart= StringField(TimeMax, 2, ":"); will be clipped per segment further down ...
Case -2 : sPart= "0"
EndSelect
If cBlock And (Abs(EditAction) = 1 Or Abs(EditAction) = 2); Monitor and restrict the new values in the segments....
Select cBlock
Case 1
If Val(sPart) > hMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
sTime= RSet(sPart, 2, "0") + ":" + StringField(sTime, 2, ":") + ":" + StringField(sTime, 3, ":")
gtk_editable_select_region_(*widget, 0, Colon1 - 1)
Case 2
If Val(sPart) > mMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
sTime= StringField(sTime, 1, ":") + ":" + RSet(sPart, 2, "0") + ":" + StringField(sTime, 3, ":")
gtk_editable_select_region_(*widget, Colon1, Colon2 - 1)
Case 3
If Val(sPart) > sMax : sPart = StringField(TimeMax, cBlock, ":") : EndIf
sTime= StringField(sTime, 1, ":") + ":" + StringField(sTime, 2, ":") + ":" + RSet(sPart, 2, "0")
gtk_editable_select_region_(*widget, Colon2, -1)
EndSelect
gtk_entry_set_text (*widget, sTime)
_TimePickerGadget_SelectPart(*widget, cBlock, Colon1, Colon2)
PostEvent(#PB_Event_Gadget, *user_data\WindowNo, *user_data\GadgetId, #PB_EventType_Change)
EndIf
Else; Value input in segment ...
If cBlock
sPart= StringField(sTime, cBlock, ":"); Read segments value under cursor
Select cBlock; Monitor and restrict the newly entered value in the segments....
Case 1 : If Val(sPart) > hMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= sPart + ":" + StringField(sTime, 2, ":") + ":" + StringField(sTime, 3, ":") : EndIf
Case 2 : If Val(sPart) > mMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= StringField(sTime, 1, ":") + ":" + sPart + ":" + StringField(sTime, 3, ":") : EndIf
Case 3 : If Val(sPart) > sMax : sPart = StringField(TimeMax, cBlock, ":") : sTime= StringField(sTime, 1, ":") + ":" + StringField(sTime, 2, ":") + ":" + sPart : EndIf
EndSelect
gtk_entry_set_text(*widget, sTime); Reformat string
gtk_editable_set_position_(*widget, cPos); Reset cursor position
PostEvent(#PB_Event_Gadget, *user_data\WindowNo, *user_data\GadgetId, #PB_EventType_Change)
EndIf
EndIf
EndIf
EndProcedure; TimePickerGadgetCheckInput
ProcedureC Callback_TimePickerGadget_LostFocus(*widget.GtkWidget, *event.GdkEventFocus, *user_data.CALLBACKUSERDATA)
gtk_editable_select_region_(*widget, 0, 0)
EndProcedure
ProcedureC Callback_TimePickerGadget_ClickAction(*widget.GtkWidget, *event.GdkEventButton, *user_data.CALLBACKUSERDATA)
;Protected.i ModKey= *event\state & $F; reserved for bigger jumps with Shift modifier
If *event\type = #GDK_BUTTON_RELEASE
_TimePickerGadget_CheckInput(*widget, 0, *user_data)
EndIf
EndProcedure
ProcedureC Callback_TimePickerGadget_KeyAction(*widget.GtkWidget, *event.GdkEventKey, *user_data.CALLBACKUSERDATA)
Protected.i ModKey= *event\state & $F; (Required 'modifier keys' masked with low nibble)
Protected.i KeyVal= *event\keyval
Protected.i KeyRelease= *event\type
Protected.i Stop= #False
Protected.i EditAction= 0
Protected.i SelStart, SelEnd
gtk_editable_get_selection_bounds(*widget, @SelStart, @SelEnd); neu 0.12 ...
If SelEnd - SelStart > 2; destroy wrong selection
gtk_editable_set_position_(*widget, 1); 'SelEnd' instead of '1' sets cursor to selection end - if so wanted
Stop= #True
EndIf
If KeyRelease = #GDK_KEY_PRESS; push key or button
If ModKey = #Null; no modifier key
Select KeyVal
Case #GDK_KEY_KP_0 To #GDK_KEY_KP_9, #GDK_KEY_0 To #GDK_KEY_9, #GDK_KEY_Tab; Digits from main- and numeric pad
Case #GDK_KEY_Up, #GDK_KEY_KP_Add; Cursor up, NumPad +
_TimePickerGadget_CheckInput(*widget, 1, *user_data)
Stop= #True
Case #GDK_KEY_Down, #GDK_KEY_KP_Subtract; Cursor down, NumPad -
_TimePickerGadget_CheckInput(*widget, -1, *user_data)
Stop= #True
Case #GDK_KEY_End; Upper limit, End
_TimePickerGadget_CheckInput(*widget, 2, *user_data)
Stop= #True
Case #GDK_KEY_Home; Lower limit, Home/Pos1
_TimePickerGadget_CheckInput(*widget, -2, *user_data)
Stop= #True
Default; Stop handler for other keys
Stop= #True
EndSelect
EndIf
EndIf
If KeyRelease = #GDK_KEY_RELEASE; release key or button
If ModKey = #Null; w/o modifier key
Select KeyVal
Case #GDK_KEY_Escape; <Esc> for emergency reset. Comment out if unwanted
gtk_entry_set_text(*widget, "12:00:00")
Case #GDK_KEY_Left; Cursor left
gtk_editable_set_position_(*widget, SelStart - 1)
Case #GDK_KEY_Right; Cursor right
SelEnd * Bool(Not(SelEnd = gtk_entry_get_text_length(*widget))); Cursor position on 0 if behind string end
gtk_editable_set_position_(*widget, SelEnd + 1)
Case #GDK_KEY_KP_0 To #GDK_KEY_KP_9, #GDK_KEY_0 To #GDK_KEY_9; Digits from main- and numeric pad
EditAction= 3
Default; Stop handler for other keys
Stop= #True
EndSelect
EndIf
If Not Stop
_TimePickerGadget_CheckInput(*widget, EditAction, *user_data)
EndIf
EndIf
ProcedureReturn Stop
EndProcedure
ProcedureC Callback_TimePickerGadget_ScrollBlock(*widget.GtkWidget, *event.GdkEventScroll, *user_data.CALLBACKUSERDATA)
Protected.GdkEventKey *eventKey= gdk_event_new_(#GDK_KEY_RELEASE)
*eventKey\window= gtk_widget_get_root_window_(*widget)
*eventKey\type= #GDK_KEY_PRESS
If gtk_widget_has_focus(*widget)
If *event\direction = #GDK_SCROLL_UP
*eventKey\keyval = #GDK_KEY_Up
Callback_TimePickerGadget_KeyAction(*widget, *eventKey, *user_data)
ElseIf *event\direction = #GDK_SCROLL_DOWN
*eventKey\keyval = #GDK_KEY_Down
Callback_TimePickerGadget_KeyAction(*widget, *eventKey, *user_data)
EndIf
EndIf
EndProcedure
Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null); Linux
Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
Protected.i GadgetId, PBId
Protected.i _Flags= #Null
Shared GadgetData()
If Not MatchRegularExpression(TimePattern, sTime)
If #TPG_DebugOn
Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
Debug " -> Time is set to '00:00:00'!"
EndIf
sTime= "00:00:00"
EndIf
If TimePattern : FreeRegularExpression(TimePattern) : EndIf
If Flags & #TPG_Time_ReadOnly
_Flags | #PB_String_ReadOnly
EndIf
If Flags & #TPG_Time_BorderLess
_Flags | #PB_String_BorderLess
EndIf
If #TPG_DebugOn And Flags & #TPG_Time_NoUpDown
Debug "TimePickerGadget Flag: '#TPG_Time_NoUpDown' isn't implemented on Linux."
EndIf
If #TPG_DebugOn And Flags & #TPG_Time_12h
Debug "TimePickerGadget Flag: '#TPG_Time_12h' isn't implemented on Linux."
EndIf
PBId= StringGadget(GadgetNo, x, y, w, h, sTime, _Flags)
If GadgetNo = #PB_Any
GadgetId= GadgetID(PBId)
ElseIf GadgetNo > #PB_Any
GadgetId= GadgetID(GadgetNo)
Else
Debug "TimePickerGadget [" + Str(GadgetNo) + "]: GadgetNo < #PB_Any not defined! Programm stopped!" : End
EndIf
AddElement(GadgetData())
GadgetData()\GadgetId= GadgetId
GadgetData()\WindowNo= WindowNo
gtk_entry_set_max_length_(GadgetId, 8)
gtk_entry_set_alignment (GadgetId, 0.5)
gtk_widget_add_events_ (GadgetId, #GDK_FOCUS_CHANGE | #GDK_SCROLL_MASK | #GDK_KEY_PRESS_MASK)
g_signal_connect(GadgetId, "scroll-event", @Callback_TimePickerGadget_ScrollBlock(), @GadgetData())
g_signal_connect(GadgetId, "button-press-event", @Callback_TimePickerGadget_ClickAction(), @GadgetData())
g_signal_connect(GadgetId, "button-release-event", @Callback_TimePickerGadget_ClickAction(), @GadgetData())
g_signal_connect(GadgetId, "key-press-event", @Callback_TimePickerGadget_KeyAction(), @GadgetData())
g_signal_connect(GadgetId, "key-release-event", @Callback_TimePickerGadget_KeyAction(), @GadgetData())
g_signal_connect(GadgetId, "focus-out-event", @Callback_TimePickerGadget_LostFocus(), @GadgetData())
ProcedureReturn GadgetId
EndProcedure; Linux
;-} _Linux TimePickerGadget API END
CompilerCase #PB_OS_MacOS
;-{ _MacOS TimePickerGadget API ...
Procedure GetUTCOffset()
Shared.i iUTCOffsetSeconds
Shared.s sUTCOffset
iUTCOffsetSeconds = CocoaMessage(0, CocoaMessage(0, 0, "NSTimeZone systemTimeZone"), "secondsFromGMT")
If iUTCOffsetSeconds < 0
sUTCOffset = "-" + FormatDate("%hh%ii", 24 * 60 * 60 - iUTCOffsetSeconds)
Else
sUTCOffset = "+" + FormatDate("%hh%ii", iUTCOffsetSeconds)
EndIf
EndProcedure
Procedure.i GetTimePickerGadgetState(GadgetId)
Shared.i iUTCOffsetSeconds
Protected *CurrentDate
Protected.s sCurrentDate
Protected.i TimeVal
*CurrentDate= CocoaMessage(0, GadgetId, "dateValue")
sCurrentDate= PeekS(CocoaMessage(0, CocoaMessage(0, *CurrentDate, "description"), "UTF8String"), -1, #PB_UTF8)
If OSVersion() <= #PB_OS_MacOSX_10_6
TimeVal= ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8))
Else
TimeVal= AddDate(ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)), #PB_Date_Second, iUTCOffsetSeconds)
EndIf
ProcedureReturn TimeVal
EndProcedure
Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
Shared.s sUTCOffset
Protected.s sTime
If TimeVal > -1 And TimeVal < 86400
sTime = FormatDate("%yyyy-%mm-%dd", Date()) + " " + FormatDate(#TPG_TimeMask24h, TimeVal) + " " + sUTCOffset
*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTime)
CocoaMessage(0, GadgetId, "setDateValue:@", @*StartDate)
EndIf
EndProcedure
Procedure.s GetTimePickerGadgetText(GadgetId)
Shared.i iUTCOffsetSeconds
Protected *CurrentDate
Protected.s sCurrentDate
Protected.s sCurrentTime
*CurrentDate= CocoaMessage(0, GadgetId, "dateValue")
sCurrentDate= PeekS(CocoaMessage(0, CocoaMessage(0, *CurrentDate, "description"), "UTF8String"), -1, #PB_UTF8)
If OSVersion() <= #PB_OS_MacOSX_10_6
sCurrentTime= FormatDate(#TPG_TimeMask24h, ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)))
Else
sCurrentTime= FormatDate(#TPG_TimeMask24h, AddDate(ParseDate(#TPG_TimeMask24h, Mid(sCurrentDate, 12, 8)), #PB_Date_Second, iUTCOffsetSeconds))
EndIf
ProcedureReturn sCurrentTime
EndProcedure
Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
Shared.s sUTCOffset
Protected *StartDate
If ParseDate(#TPG_TimeMask24h, sTimeString) > -1
sTimeString = FormatDate("%yyyy-%mm-%dd", Date()) + " " + sTimeString + " " + sUTCOffset
*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTimeString)
CocoaMessage(0, GadgetId, "setDateValue:@", @*StartDate)
EndIf
EndProcedure
ProcedureC Callback_TimePickerGadget_Changed(Object.I, Selector.I, Cell.I, ProposedValue.I, ProposedTimeInterval.I)
Shared TimePickerCell()
ForEach TimePickerCell()
If TimePickerCell()\ID = Cell
PostEvent(#PB_Event_Gadget, TimePickerCell()\WindowNo, Cell, #PB_EventType_Change)
Break
EndIf
Next
EndProcedure
Procedure InitCallback(TimePicker.I)
Protected AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0, "NSApplication sharedApplication"), "delegate")
Protected DelegateClass.I= CocoaMessage(0, AppDelegate, "class")
Protected Selector.I = sel_registerName_("datePickerCell:validateProposedDateValue:timeInterval:")
class_addMethod_(DelegateClass, Selector, @Callback_TimePickerGadget_Changed(), "v@:@@@")
CocoaMessage(0, TimePicker, "setDelegate:", AppDelegate)
EndProcedure
Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null); MacOS
Shared.s sUTCOffset
Shared.i TimePickerCell()
Protected Frame.NSRect
Protected *StartDate
Protected.i TimePicker
Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
If sUTCOffset = ""
GetUTCOffset()
EndIf
If Not MatchRegularExpression(TimePattern, sTime)
If #TPG_DebugOn
Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
Debug " -> Time is set to '00:00:00'!"
EndIf
sTime= "00:00:00"
EndIf
If TimePattern : FreeRegularExpression(TimePattern) : EndIf
Frame\origin\x = x
Frame\origin\y = y
Frame\size\width = w
Frame\size\height = h
TimePicker = CocoaMessage(0, CocoaMessage(0, 0, "NSDatePicker alloc"), "initWithFrame:@", @Frame)
If #TPG_DebugOn And Flags & #TPG_Time_ReadOnly
Debug "TimePickerGadget Flag: '#TPG_Time_ReadOnly' isn't implemented on Mac."
EndIf
If Flags & #TPG_Time_BorderLess
CocoaMessage(0, TimePicker, "setBordered:", #False)
Else
CocoaMessage(0, TimePicker, "setBezeled:", #True)
EndIf
If Flags & #TPG_Time_NoUpDown
CocoaMessage(0, TimePicker, "setDatePickerStyle:", #NSDatePickerStyleTextField)
Else
CocoaMessage(0, TimePicker, "setDatePickerStyle:", #NSDatePickerStyleTextFieldAndStepper)
EndIf
If #TPG_DebugOn And Flags & #TPG_Time_12h
Debug "TimePickerGadget Flag: '#TPG_Time_12h' isn't implemented on Mac."
EndIf
CocoaMessage(0, TimePicker, "setDatePickerElements:", #NSHourMinuteSecondDatePickerElementFlag)
CocoaMessage(0, CocoaMessage(0, WindowID(WindowNo), "contentView"), "addSubview:", TimePicker)
sTime = FormatDate("%yyyy-%mm-%dd", Date()) + " " + sTime + " " + sUTCOffset
*StartDate = CocoaMessage(0, 0, "NSDate dateWithString:$", @sTime)
CocoaMessage(0, TimePicker, "setDateValue:@", @*StartDate)
InitCallback(TimePicker)
AddElement(TimePickerCell())
TimePickerCell()\ID = CocoaMessage(0, TimePicker, "cell")
TimePickerCell()\WindowNo = WindowNo
ProcedureReturn TimePickerCell()\ID
EndProcedure; MacOS
;-} _MacOS TimePickerGadget API END
CompilerCase #PB_OS_Windows
;-{ _Windows TimePickerGadget API ...
Procedure GetTimePickerGadgetState(GadgetId)
Protected.i TimeVal
Protected tTime.systemtime
SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
TimeVal = Val(Str(ParseDate(#TPG_TimeMask24h, RSet(Str(tTime\wHour), 2, "0") + ":" + RSet(Str(tTime\wMinute), 2, "0") + ":" + RSet(Str(tTime\wSecond), 2, "0"))))
ProcedureReturn TimeVal
EndProcedure
Procedure SetTimePickerGadgetState(GadgetId, TimeVal)
Protected tTime.systemtime
If TimeVal > -1 And TimeVal < 86400
SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
tTime\wYear = 1970
tTime\wMonth = 1
tTime\wDay = 1
tTime\wHour = Hour(TimeVal)
tTime\wMinute= Minute(TimeVal)
tTime\wSecond= Second(TimeVal)
SendMessage_(GadgetId, #MCM_SETCURSEL, 0, @tTime)
EndIf
EndProcedure
Procedure.s GetTimePickerGadgetText(GadgetId)
Protected.s sTimeString
Protected tTime.systemtime
SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
sTimeString= RSet(Str(tTime\wHour), 2, "0") + ":" + RSet(Str(tTime\wMinute), 2, "0") + ":" + RSet(Str(tTime\wSecond), 2, "0")
ProcedureReturn sTimeString
EndProcedure
Procedure SetTimePickerGadgetText(GadgetId, sTimeString.s)
Protected.i TimeVal
Protected tTime.systemtime
TimeVal= ParseDate(#TPG_TimeMask24h, sTimeString)
If TimeVal > -1 And TimeVal < 86400
SendMessage_(GadgetId, #MCM_GETCURSEL, 0, @tTime)
tTime\wYear = 1970
tTime\wMonth = 1
tTime\wDay = 1
tTime\wHour = Hour(TimeVal)
tTime\wMinute= Minute(TimeVal)
tTime\wSecond= Second(TimeVal)
SendMessage_(GadgetId, #MCM_SETCURSEL, 0, @tTime)
EndIf
EndProcedure
Procedure Callback_Window(WindowHandle, Msg, wParam, lParam)
Protected *pnmh.NMHDR
Protected WindowNo= GetProp_(WindowHandle, "PB_WINDOWID") - 1
Select Msg
Case #WM_NOTIFY
*pnmh = lParam
Select *pnmh\code
Case #DTN_DATETIMECHANGE
PostEvent(#PB_Event_Gadget, WindowNo, *pnmh\hwndFrom, #PB_EventType_Change)
EndSelect
EndSelect
ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure TimePickerGadget(GadgetNo, x, y, w, h, sTime.s, WindowNo, Flags= #Null); Windows
Protected.i TimePattern= CreateRegularExpression(#PB_Any, "^[0-2]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9]$")
Protected.i GadgetId, PBId
If Not MatchRegularExpression(TimePattern, sTime)
If #TPG_DebugOn
Debug "TimePickerGadget [" + Str(GadgetNo) + "]: Preset string is not in time format: '[h]h:[m]m:[s]s', or time is invalid!"
Debug " -> Time is set to '00:00:00'!"
EndIf
sTime= "00:00:00"
EndIf
If TimePattern : FreeRegularExpression(TimePattern) : EndIf
InitCommonControls_(); ?
PBId= DateGadget(GadgetNo, x, y, w, h, "", 0, #DTS_TIMEFORMAT); no other flags accepted
If GadgetNo = #PB_Any
GadgetId= GadgetID(PBId)
ElseIf GadgetNo > #PB_Any
GadgetId= GadgetID(GadgetNo)
Else
Debug "TimePickerGadget [" + Str(GadgetNo) + "]: GadgetNo < #PB_Any not defined! Programm stopped!" : End
EndIf
SetWindowCallback(@Callback_Window(), WindowNo)
If #TPG_DebugOn And Flags & #TPG_Time_ReadOnly
Debug "TimePickerGadget Flag: '#TPG_Time_ReadOnly' isn't available on Windows."
EndIf
If Flags & #TPG_Time_BorderLess
SetWindowLongPtr_(GadgetId, #GWL_EXSTYLE, 0)
SetWindowPos_(GadgetId, 0, 0, 0, 0, 0, #SWP_NOMOVE | #SWP_NOSIZE | #SWP_FRAMECHANGED); w/o frame
EndIf
If #TPG_DebugOn And Flags & #TPG_Time_NoUpDown
Debug "TimePickerGadget Flag: '#TPG_Time_NoUpDown' isn't available on Windows."
EndIf
If Flags & #TPG_Time_12h
CompilerIf #PB_Compiler_Unicode
SendMessage_(GadgetId, #DTM_SETFORMATW, 0, @"hh:mm:ss tt"); set time format 12h + am/pm in unicode
CompilerElse
SendMessage_(GadgetId, #DTM_SETFORMATA, 0, @"hh:mm:ss tt"); set time format 12h + am/pm in ascii
CompilerEndIf
EndIf
SetTimePickerGadgetText(GadgetId, sTime)
ProcedureReturn GadgetId
EndProcedure; Windows
;-} _Windows TimePickerGadget API END
CompilerEndSelect
;-} EndModul: TimePickerGadget
;-
EndModule
; Demo for TimePickerGadget() ...
CompilerIf #PB_Compiler_IsMainFile
UseModule TimePickerGadget; access with UseModule ...
Procedure Create_WinMain()
OpenWindow(#Win_Main, 100, 100, 800, 170, "TimePickerGadget, All OS - Demo Win 1 ", #PB_Window_SystemMenu)
TextGadget(#PB_Any, 5, 5, 790, 20, "Set a time in 24h format as 'hh:mm:ss':")
TextGadget(#PB_Any, 10, 25, 785, 20, "- choose segment (marked block) with <left>/<right> keys or mouseclick.")
TextGadget(#PB_Any, 10, 45, 785, 20, "- choose segments value w. number keys, <up>/<down> | num-pads <+>/<-> (not Mac) or mouse scrolling not Mac).")
TextGadget(#PB_Any, 10, 65, 785, 20, "- jump to segments min.-/max.- values with <home>/<end>. (not Mac, Win only max.)")
StringGadget(#StGMain1, 600, 90, 195, 26, "Only for focus tests!")
TextGadget(#PB_Any, 5, 94, 98, 22, "Set time :")
*gTimeGadget1= TimePickerGadget(#TimeGadget1, 200, 90, 90, 26, "05:33:20", #Win_Main, #TPG_Time_12h)
TextGadget(#PB_Any, 5, 118, 190, 22, "Time set:")
TextGadget(#PB_Any, 5, 138, 190, 22, "Time value set (=seconds):")
TextGadget(#TxGMain1, 200, 118, 98, 22, "")
TextGadget(#TxMainG2, 200, 138, 98, 22, "")
ButtonGadget(#BtGMainSet, 520, 135, 275, 26, "Set time to 8:00:00 from TimeVal")
EndProcedure
UnuseModule TimePickerGadget; access without UseModule. ...
Procedure Create_WinSec()
gWin_Sec= OpenWindow(#PB_Any, 100, 300, 800, 100, "TimePickerGadget, All OS - Demo Win 2 ", #PB_Window_SystemMenu)
StringGadget(#StGSec1, 600, 10, 195, 26, "Only for focus tests!")
TextGadget(#PB_Any, 5, 14, 98, 22, "Set time :")
*gTimeGadget2= TimePickerGadget::TimePickerGadget(#PB_Any, 200, 10, 90, 26, "11:06:40", gWin_Sec, TimePickerGadget::#TPG_Time_BorderLess)
TextGadget(#PB_Any, 5, 38, 190, 22, "Time set:")
TextGadget(#PB_Any, 5, 58, 190, 22, "Time value set (=seconds):")
TextGadget(#TxGSec1, 200, 38, 98, 22, "")
TextGadget(#TxGSec2, 200, 58, 98, 22, "")
ButtonGadget(#BtGSecSet, 520, 55, 275, 26, "Set time to 16:00:00 from TimeString")
EndProcedure
Create_WinMain()
Create_WinSec()
UseModule TimePickerGadget
Repeat
gEvent= WaitWindowEvent()
gEventWin= EventWindow()
Select gEventWin
Case #Win_Main; access with UseModule ...
Select gEvent
Case #PB_Event_CloseWindow
gQuit= #True
Case #PB_Event_Gadget
gEventGadget= EventGadget()
Select gEventGadget
Case *gTimeGadget1; watch the pointer and read time
If EventType() = #PB_EventType_Change
SetGadgetText(#TxGMain1, GetTimePickerGadgetText(*gTimeGadget1))
SetGadgetText(#TxMainG2, Str(GetTimePickerGadgetState(*gTimeGadget1)))
EndIf
Case #BtGMainSet; set TimePickerGadget to 08:00:00 by time value
SetTimePickerGadgetState(*gTimeGadget1, 28800)
PostEvent(#PB_Event_Gadget, #Win_Main, *gTimeGadget1, #PB_EventType_Change); trigger event for reading
EndSelect
EndSelect
UnuseModule TimePickerGadget
Case gWin_Sec; access without UseModule ...
Select gEvent
Case #PB_Event_CloseWindow
gQuit= #True
Case #PB_Event_Gadget
gEventGadget= EventGadget()
Select gEventGadget
Case *gTimeGadget2; watch the pointer and read time
If EventType() = #PB_EventType_Change
SetGadgetText(#TxGSec1, TimePickerGadget::GetTimePickerGadgetText(*gTimeGadget2))
SetGadgetText(#TxGSec2, Str(TimePickerGadget::GetTimePickerGadgetState(*gTimeGadget2)))
EndIf
Case #BtGSecSet; set TimePickerGadget to 16:00:00 by string
TimePickerGadget::SetTimePickerGadgetText(*gTimeGadget2, "16:00:00")
PostEvent(#PB_Event_Gadget, gWin_Sec, *gTimeGadget2, #PB_EventType_Change); trigger event for reading
EndSelect
EndSelect
EndSelect
Until gQuit
CompilerEndIf
PS: Der Sache mit #PB_Any für den WindowNo-Parameter muss ich mich (wie schon bei der GadgetNo) noch mal widmen!
Nachtrag: #PB_Any für den WindowNo-Parameter scheint keine Probleme zu machen. In diesem Fall den global-/shared-Rückgabewert aus OpenWindow() verwenden.
Update: 2018-10-20: V0.21