![]() |
|
Calendar Control |
|
| About |
This PRG source code implements the "Month Calendar" Common Control. The screen shot below is created with the locale of my computer (German). It shows the "Month Calendar Control" where the first day of the week is set to Sunday. The control displays six calender sheets using custom colors (minimum is one calendar sheet, maximum is restricted by the screen size). Calendar days for Saturdays and Sundays are displayed bold due to the GetDayState() function implemented in PRG code. |
| Screen shot |
|
| MonthCalControl.ch |
// FILE: MonthCalControl.ch // Copyright (c) Hannes Ziegler, 2008 // This file is published as Open Source for The YUKON Project (www.knowleXbase.com) #ifndef _MONTHCAL_CONTROL_CH #define _MONTHCAL_CONTROL_CH #include "YukonEVM.ch" // Constants for preparing the color array queried by the MCM_SETCOLOR callback // (the original #defines start at offset zero, so I had to use new ones) #define MC_CLR_BACKGROUND 1 // Color around the control #define MC_CLR_TEXT 2 // Text color (day numbers) #define MC_CLR_TITLEBK 3 // Title background color #define MC_CLR_TITLETEXT 4 // Title text color #define MC_CLR_MONTHBK 5 // Text background color (day numbers) #define MC_CLR_TRAILINGTEXT 6 // Text color for days of prev/next month #define MC_COLOR_ARRAY Array(6) #endif // _MONTHCAL_CONTROL_CH |
| MonthCalControl.prg |
// FILE: MonthCalControl.prg
// Copyright (c) Hannes Ziegler, 2008
// This file is published as Open Source for The YUKON Project (www.knowleXbase.com)
// Implementation of the MonthCalControl class
#include "MonthCalControl.ch"
CLASS MonthCalControl FROM ComCtl_MonthCal
PROTECTED:
VAR dateTime
VAR systemTimeStart
VAR systemTimeEnd
EXPORTED:
METHOD init, create, destroy
INLINE ACCESS METHOD currentDate
RETURN ::dateTime:date
INLINE ACCESS METHOD startDate
LOCAL cSave := ::dateTime:toSystemTime()
LOCAL cDate := ::systemTimeStart:str
LOCAL dDate := Ctod( "" )
IF .NOT. cDate == ZeroStr( 16 )
::dateTime:str := cDate
dDate := ::dateTime:date
::dateTime:str := cSave
ENDIF
RETURN dDate
INLINE ACCESS METHOD endDate
LOCAL cSave := ::dateTime:toSystemTime()
LOCAL cDate := ::systemTimeEnd:str
LOCAL dDate := Ctod( "" )
IF .NOT. cDate == ZeroStr( 16 )
::dateTime:str := cDate
dDate := ::dateTime:date
::dateTime:str := cSave
ENDIF
RETURN dDate
// Notifications
METHOD MCN_GetDayState
METHOD MCN_SelChange
METHOD MCN_Select
ENDCLASS
METHOD MonthCalControl:init( oParent, oOwner, aPos, aSize, aPP, lVisible )
::ComCtl_MonthCal:init( oParent, oOwner, aPos, aSize, aPP, lVisible )
::dateTime := DateTime():new()
::systemTimeStart := StructLoad( "SYSTEMTIME" )
::systemTimeEnd := StructLoad( "SYSTEMTIME" )
RETURN self
METHOD MonthCalControl:create( oParent, oOwner, aPos, aSize, aPP, lVisible )
LOCAL nMsg, xValue, i, imax, j, jmax, bBlock
LOCAL aInitVars, cBin, nLen, cSave, nGDTR := 0
::ComCtl_MonthCal:create( oParent, oOwner, aPos, aSize, aPP, lVisible )
aInitVars := ::initVars
imax := Len( aInitVars )
FOR i:=1 TO imax
nMsg := aInitVars[i,1] // message to send to the Common Control
bBlock := aInitVars[i,2] // initialization value
IF Valtype( bBlock ) == "B"
xValue := Eval( bBlock, self )
ELSE
xValue := bBlock
ENDIF
DO CASE
CASE nMsg == MCM_SETDAYSTATE
// Array of day display states must be transformed to binary
cBin := ""
AEval( xValue, {|n| cBin += U2Bin(n) } )
::sendMessage( nMsg, Len(xValue), @cBin )
CASE nMsg == MCM_SETCOLOR
// Array of color values
jmax := Len( xValue )
FOR j:=1 TO jmax
IF Valtype( xValue[j] ) == "N"
// the color index is zero-based
::sendMessage( nMsg, j-1, xValue[j] )
ENDIF
NEXT
CASE nMsg == MCM_SETFIRSTDAYOFWEEK
// Number for first day of week to display (Xpp: Sunday = 1)
xValue -= 2 // (Monday = 0, Sunday = 6)
IF xValue < 0
xValue += 7
ENDIF
::sendMessage( nMsg, 0, xValue )
CASE nMsg == MCM_SETRANGE
// First and last date of range must be transformed to SYSTEMTIME structures
cSave := ::dateTime:toSystemTime()
IF Valtype( xValue ) == "D"
xValue := { xValue, NIL }
ENDIF
FOR j:=1 TO 2
IF Valtype( xValue[j] ) == "D"
::dateTime:date := xValue[j]
IF j==1
nGDTR += 1 // GDTR_MIN
::systemTimeStart:str := ::dateTime:str
ELSE
nGDTR += 2 // GDTR_MAX
::systemTimeEnd:str := ::dateTime:str
ENDIF
ENDIF
NEXT
cBin := ::systemTimeStart:str + ::systemTimeEnd:str
::dateTime:str := cSave
::sendMessage( nMsg, nGDTR, @cBin )
CASE nMsg == MCM_SETTODAY
// Date value for today must be transformed to SYSTEMTIME structures
cSave := ::dateTime:toSystemTime()
::dateTime:date := xValue
::sendMessage( nMsg, 0, ::dateTime:ptr )
::dateTime:str := cSave
CASE nMsg == MCM_SETMAXSELCOUNT
// maximum number of days in a range can be passed as numeric
::sendMessage( nMsg, xValue, 0 )
CASE nMsg == MCM_SETCURSEL
// currently selected date must be passed as SYSTEMTIME structure
::dateTime:toSystemTime()
::dateTime:date := xValue
::sendMessage( nMsg, 0, ::dateTime:ptr )
ENDCASE
NEXT
RETURN self
METHOD MonthCalControl:destroy()
Kill::dateTime
Kill::systemTimeStart
Kill::systemTimeEnd
::ComCtl_MonthCal:destroy()
RETURN self
// This method queries and sets the day display states
// wParam: unused
// lParam: pointer to a NMDAYSTATE structure
METHOD MonthCalControl:MCN_GetDayState( wParam, lParam )
LOCAL bCallback := ::getCallback( MCN_GETDAYSTATE )
LOCAL cSave := ::dateTime:toSystemTime()
LOCAL cDayState := ""
LOCAL aDayState, dStartDate, i, imax, nPtr
::dateTime:loadFrom( lParam + 12 ) // NMDAYSTATE:stStart structure member
imax := Ptr2UShort( lParam + 28 ) // NMDAYSTATE:cDayState structure member
nPtr := Ptr2ULong ( lParam + 32 ) // NMDAYSTATE:prgDayState structure member
aDayState := AFill( Array(imax), 0 )
dStartDate := ::dateTime:date
IF Valtype( bCallback ) == "B"
aDayState := Eval( bCallback, self, dStartDate, aDayState )
ENDIF
FOR i := 1 TO imax
cDayState += U2Bin( aDayState[i] )
NEXT
imax := MemWrite( nPtr, cDayState, Len(cDayState) )
::dateTime:str := cSave
RETURN 0
// This method transfers the selected date to the :dateTime object
// wParam: unused
// lParam: pointer to a NMSELCHANGE structure
METHOD MonthCalControl:MCN_Select( wParam, lParam )
LOCAL bCallback := ::getCallback( MCN_SELECT )
LOCAL oEvent
::dateTime:toSystemTime()
::dateTime:loadFrom( lParam + 12 ) // NMSELCHANGE:stSelStart structure member
IF Valtype( bCallback ) == "B"
Eval( bCallback, self, ::dateTime:date )
ENDIF
RETURN 1
// This method transfers the first and last date of a selected range of days
// wParam: unused
// lParam: pointer to a NMSELCHANGE structure
METHOD MonthCalControl:MCN_SelChange( wParam, lParam )
LOCAL bCallback := ::getCallback( MCN_SELCHANGE )
LOCAL cSave := ::dateTime:toSystemTime()
LOCAL dFirst, dLast, oEvent
::dateTime:loadFrom( lParam + 12 ) // NMSELCHANGE:stSelStart structure member
::systemTimeStart:str := ::dateTime:str
dFirst := ::dateTime:date
::dateTime:loadFrom( lParam + 28 ) // NMSELCHANGE:stSelEnd structure member
::systemTimeEnd:str := ::dateTime:str
dLast := ::dateTime:date
::dateTime:str := cSave
IF Valtype( bCallback ) == "B"
Eval( bCallback, self, dFirst, dLast )
ENDIF
RETURN 0
|
| TestCalendar.prg |
// FILE: TestCalendar.prg
// Copyright (c) Hannes Ziegler, 2008
// This file is published as Open Source for The YUKON Project (www.knowleXbase.com)
#include "MonthCalCOntrol.ch"
#include "AppEvent.ch"
#include "Font.ch"
#include "Xbp.ch"
#include "Gra.ch"
#include "Dll.ch"
PROCEDURE Main
LOCAL oDlg := GuiStdDialog( "Test for MonthCalControl" )
LOCAL oWin, oCtrl, oStatic, oSle1, oSle2
LOCAL nEvent, mp1, mp2, oXbp
LOCAL dFirstDay := Stod( "20070101" )
LOCAL dLastDay := Stod( "20091231" )
LOCAL dCurrentDay := Stod( "20080113" )
LOCAL dToday := Stod( "20080125" )
LOCAL aDayState := GetDayState( dCurrentDay )
LOCAL aColors := MC_COLOR_ARRAY
LOCAL aRange := { dFirstDay, dLastDay }
aColors[ MC_CLR_BACKGROUND ] := GetColorRef( "LightGray" )
aColors[ MC_CLR_MONTHBK ] := GetColorRef( "GhostWhite" )
aColors[ MC_CLR_TITLEBK ] := GetColorRef( "SlateGray" )
oCtrl := MonthCalControl():new( oDlg:drawingArea,, {30,30}, { 600,400 } )
// Set control styles
oCtrl:windowStyle += MCS_WEEKNUMBERS + MCS_DAYSTATE // + MCS_MULTISELECT
// Set initialization values
oCtrl:initVars := { ;
{ MCM_SETFIRSTDAYOFWEEK , 1 }, ; // start with Sunday
{ MCM_SETCURSEL , Stod( "20080113" ) }, ; // current selection
{ MCM_SETCOLOR , aColors }, ; // colors
{ MCM_SETTODAY , Date() } ; // today's date
}
// useful when the MCS_MULTISELECT style is set
// { MCM_SETRANGE , aRange }, ;
// { MCM_SETMAXSELCOUNT , 5 }, ;
// Notification callback code blocks
oCtrl:setCallback( MCN_GETDAYSTATE , {|obj,dDate,aState| GetDayState( dDate, aState ) } )
oCtrl:setCallback( MCN_SELECT , {|obj,dDate| QOut(dDate) } )
oCtrl:setCallback( MCN_SELCHANGE , {|obj,dFirst,dLast| QOut(dFirst,dLast) } )
oCtrl:create()
/*
// This adjusts the size of the MonthCalControl to the required minimum
oRect := StructLoad( "RECT" )
oCtrl:sendMessage( MCM_GETMINREQRECT, 0, oRect )
oCtrl:setSize( {oRect:right, oRect:bottom} )
oRect:destroy()
*/
DO WHILE nEvent <> xbeP_Close
nEvent := AppEventEx( @mp1, @mp2, @oXbp )
IF oXbp <> NIL
IF nEvent == xbeP_Keyboard
EXIT
ENDIF
oXbp:handleEvent( nEvent, mp1, mp2 )
ENDIF
ENDDO
RETURN
// This function is called in response to the MCN_GETDAYSTATE notification
// via the :MCN_GetDayState method of a MonthCalControl object.
// It identifies the days that should be displayed bold in the calendar control.
// This implementation sets the bits for Saturdays and Sundays
FUNCTION GetDayState( dStartDate, aDayState )
LOCAL i, imax, j, jmax, nDay, nDayState, nMonth
IF Valtype( aDayState ) <> "A"
aDayState := Array(12)
ENDIF
imax := Len( aDayState )
nMonth := Month( dStartDate )
FOR i:=1 TO imax
nDayState := 0
FOR j:=1 TO 31
// day 0 == Monday, day 6 == Sunday
nDay := DoW( dStartDate ) - 2
IF nDay < 0
nDay += 7
ENDIF
// set bits for Saturday and Sunday
IF nDay > 4
nDayState[j] := .T.
ENDIF
dStartDate ++
IF Month( dStartDate ) <> nMonth
EXIT
ENDIF
NEXT
aDayState[i] := nDayState
nMonth := Month( dStartDate )
NEXT
RETURN aDayState
FUNCTION GuiStdDialog( cTitle )
LOCAL oDlg
LOCAL aSize := { 700,500 }
LOCAL aPos := { 0, Appdesktop():currentSize()[2] - aSize[2] }
oDlg := XbpDialog():new( AppDeskTop() ,,aPos, aSize, {{XBP_PP_FGCLR, GRA_CLR_WHITE} , ;
{XBP_PP_BGCLR, GRA_CLR_WHITE} ;
}, .T. )
oDlg:icon := 1
oDlg:taskList := .T.
oDlg:title := cTitle
oDlg:ClipChildren := .T.
oDlg:drawingArea:ClipChildren := .T.
oDlg:create()
oDlg:drawingArea:setFontCompoundName( FONT_DEFPROP_MEDIUM )
RETURN oDlg
|
| Copyright © Dr. Hannes Ziegler 2008 | |