VERSION 5.00 Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} CSE_Calendar Caption = "Calendar" ClientHeight = 4455 ClientLeft = 30 ClientTop = 390 ClientWidth = 4440 OleObjectBlob = "CSE_Calendar.frx":0000 StartUpPosition = 2 'CenterScreen End Attribute VB_Name = "CSE_Calendar" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False ' =========== Calendar picker native VBA ================ ' Shared module version: 20210502 ' Dependencies: z_CalendarUI, CSE_CallbackCalendar Option Explicit Private Const GWL_STYLE = -16 Private Const WS_CAPTION = &HC00000 Private Const MOUSE_LEFT_BTN = 1 Private Const CALENDAR_DAYS_COUNT = 42 Private Const CALENDAR_WEEKDAY_COUNT = 7 Private Const CALENDAR_MONTH_COUNT = 12 Private Const CALENDAR_YEARS_PER_PAGE = 12 Private Type CalendarThemeColors background_ As Long foreground_ As Long curDateBG_ As Long curDateFG_ As Long inactiveDates_ As Long End Type Private Enum CalendarMode CAL_MODE_DAY = 0 CAL_MODE_MONTH = 1 CAL_MODE_YEAR = 2 End Enum Private Declare PtrSafe Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare PtrSafe Function GetWindowLong Lib "user32" _ Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long Private Declare PtrSafe Function SetWindowLong Lib "user32" _ Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long Private Declare PtrSafe Function DrawMenuBar Lib "user32" (ByVal hwnd As LongPtr) As LongPtr Public isCancelled_ As Boolean Private curDate_ As Long Private month_ As Long Private year_ As Long Private mode_ As CalendarMode Private theme_ As TCalendarTheme Private colors_ As CalendarThemeColors Private firstYear_ As Long Private lastYear_ As Long Private xPos_ As Double Private yPos_ As Double Private callbacks_ As Collection Private Sub UserForm_Initialize() Call ResetBorders Call HideTitleBar Call InitLabelCallbacks isCancelled_ = True mode_ = CAL_MODE_DAY Call PositionInMiddle Call SetCurrentDateInternal(Int(Now)) Call SetTheme(T_CT_LIGHT) End Sub Public Function Init(theDate&, aTheme As TCalendarTheme) isCancelled_ = True Call SetCurrentDateInternal(theDate) Call ChangeMode(CAL_MODE_DAY) Call UpdateTitle Call SetTheme(aTheme) Call ResetBorders End Function Public Property Get SelectedDate() As Long SelectedDate = curDate_ End Property Public Property Get Theme() As TCalendarTheme Theme = theme_ End Property Public Function SetTheme(newValue As TCalendarTheme) theme_ = newValue Call UpdateThemeColors Call RepaintAllColors End Function ' ==== Events handlings ====== Public Function OnDayClicked(cLabel As MSForms.Label) Call ReturnResult(cLabel.Tag) End Function Public Function OnMonthClicked(cLabel As MSForms.Label) month_ = TagToMonth(cLabel.Caption) Call ChangeMode(CAL_MODE_DAY) End Function Public Function OnYearClicked(cLabel As MSForms.Label) If Len(Trim(cLabel.Caption)) = 0 Then _ Exit Function year_ = val(cLabel.Caption) Call ChangeMode(CAL_MODE_MONTH) End Function Private Sub lblToday_Click() If mode_ <> CAL_MODE_DAY Then Call SetCurrentDateInternal(Int(Now)) Call ChangeMode(CAL_MODE_DAY) Else Call ReturnResult(Int(Now)) End If End Sub Private Sub lblThemes_Click() If theme_ < TCalendarTheme.[_Last] Then Call SetTheme(theme_ + 1) Else Call SetTheme(TCalendarTheme.[_First]) End If End Sub Private Sub lblUnload_Click() isCancelled_ = True Call Me.Hide End Sub Private Sub btnClose_Click() If mode_ <> CAL_MODE_DAY Then Call ChangeMode(mode_ - 1) Else isCancelled_ = True Call Me.Hide End If End Sub Private Sub lblUP_Click() Select Case mode_ Case CAL_MODE_DAY Call IncrementMonth(-1) Case CAL_MODE_MONTH Case CAL_MODE_YEAR lastYear_ = firstYear_ - 1 firstYear_ = firstYear_ - CALENDAR_YEARS_PER_PAGE Call RepaintYearsColor End Select End Sub Private Sub lblDOWN_Click() Select Case mode_ Case CAL_MODE_DAY Call IncrementMonth(1) Case CAL_MODE_MONTH Case CAL_MODE_YEAR firstYear_ = lastYear_ + 1 lastYear_ = lastYear_ + CALENDAR_YEARS_PER_PAGE Call RepaintYearsColor End Select End Sub Private Sub UserForm_MouseDown(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single) If nBtn = MOUSE_LEFT_BTN Then xPos_ = mvX yPos_ = mvY End If End Sub Private Sub UserForm_MouseMove(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single) If nBtn And MOUSE_LEFT_BTN Then Me.Left = Me.Left + (mvX - xPos_) Me.Top = Me.Top + (mvY - yPos_) End If End Sub Private Sub Frame1_MouseDown(ByVal nBtn As Integer, ByVal nShift As Integer, ByVal mvX As Single, ByVal mvY As Single) If nBtn = MOUSE_LEFT_BTN Then xPos_ = mvX yPos_ = mvY End If End Sub Private Sub lblTitleCurMY_Click() '--> Handles the month to year multipage display If mode_ = CAL_MODE_YEAR Then _ Exit Sub Call ChangeMode(mode_ + 1) End Sub ' ========= Private Function ReturnResult(theDate&) curDate_ = theDate isCancelled_ = False Call Me.Hide End Function Private Function PositionInMiddle() Dim nTopOffset&: nTopOffset = (Application.UsableHeight / 2) - (Me.Height / 2) Dim nLeftOffset&: nLeftOffset = (Application.UsableWidth / 2) - (Me.Width / 2) Me.StartUpPosition = 0 Me.Top = Application.Top + IIf(nTopOffset > 0, nTopOffset, 0) Me.Left = Application.Left + IIf(nLeftOffset > 0, nLeftOffset, 0) End Function Private Function HideTitleBar() Dim frameHandle As Long: frameHandle = FindWindow(vbNullString, Me.Caption) Dim windowHandle As Long: windowHandle = GetWindowLong(frameHandle, GWL_STYLE) windowHandle = windowHandle And (Not WS_CAPTION) Call SetWindowLong(frameHandle, GWL_STYLE, windowHandle) Call DrawMenuBar(frameHandle) End Function Private Function ResetBorders() Dim aLabel As control For Each aLabel In Me.Controls If TypeOf aLabel Is MSForms.Label Then _ aLabel.BorderStyle = fmBorderStyleNone Next End Function Private Function InitLabelCallbacks() Dim aControl As control Dim controlType$ Set callbacks_ = New Collection For Each aControl In Me.Controls If Not TypeOf aControl Is MSForms.Label Then _ GoTo NEXT_CONTROL controlType = Left(aControl.Name, 1) If Not controlType Like "[DMY]" Then _ GoTo NEXT_CONTROL Call AddCallback(aControl) NEXT_CONTROL: Next aControl Call AddCallback(lblUP) Call AddCallback(lblDOWN) Call AddCallback(lblThemes) Call AddCallback(lblUnload) Call AddCallback(lblTitleCurMY, bUnderline:=True) Call AddCallback(lblToday, bUnderline:=True) End Function Private Function AddCallback(target As control, Optional bUnderline As Boolean = False) Dim newCallback As New CSE_CallbackCalendar Call newCallback.Init(target, bUnderline) Call callbacks_.Add(newCallback) End Function Private Function SetCurrentDateInternal(theDate&) curDate_ = theDate month_ = Month(curDate_) year_ = Year(curDate_) Call UpdateDays End Function Private Function IncrementMonth(inc&) year_ = year_ + ((month_ + inc + CALENDAR_MONTH_COUNT - 1) \ CALENDAR_MONTH_COUNT - 1) month_ = 1 + ((CALENDAR_MONTH_COUNT + (month_ + inc - 1) Mod CALENDAR_MONTH_COUNT) Mod CALENDAR_MONTH_COUNT) Call UpdateTitle Call UpdateDays End Function Private Function ChangeMode(newMode As CalendarMode) If newMode = mode_ Then _ Exit Function Me.MPmainDisplay.Value = newMode Call SetArrowsVisibility(newMode <> CAL_MODE_MONTH) mode_ = newMode Select Case mode_ Case CAL_MODE_DAY Call UpdateDays Case CAL_MODE_MONTH Call RepaintMonthsColors Case CAL_MODE_YEAR lastYear_ = year_ + 1 firstYear_ = lastYear_ - CALENDAR_YEARS_PER_PAGE + 1 Call RepaintYearsColor End Select Call UpdateTitle End Function Private Function UpdateDays() '--> Populate the calendar Dim firstDay&: firstDay = DateSerial(year_, month_, 1) firstDay = firstDay - Weekday(firstDay, vbMonday) + 1 Dim lastDay&: lastDay = firstDay + CALENDAR_DAYS_COUNT - 1 Dim nItem& For nItem = 1 To CALENDAR_DAYS_COUNT With Me.Controls("D" & nItem) .Caption = Day(firstDay + nItem - 1) .Tag = firstDay + nItem - 1 End With Next nItem Call RepaintDaysColors End Function Private Function UpdateTitle() Select Case mode_ Case CAL_MODE_DAY lblTitleCurMY.Caption = Format(DateSerial(year_, month_, 1), "MMMM yyyy") Case CAL_MODE_MONTH lblTitleCurMY.Caption = year_ Case CAL_MODE_YEAR lblTitleCurMY.Caption = firstYear_ & " - " & lastYear_ End Select End Function Private Function UpdateThemeColors() With colors_ Select Case theme_ Case TCalendarTheme.T_CT_DARK .background_ = RGB(69, 69, 69) .foreground_ = RGB(252, 248, 248) .curDateBG_ = RGB(246, 127, 8) .curDateFG_ = RGB(0, 0, 0) .inactiveDates_ = RGB(120, 120, 120) Case TCalendarTheme.T_CT_RED .background_ = RGB(87, 0, 0) .foreground_ = RGB(203, 146, 146) .curDateBG_ = RGB(122, 185, 247) .curDateFG_ = RGB(0, 0, 0) .inactiveDates_ = RGB(144, 70, 70) Case TCalendarTheme.T_CT_BLUE .background_ = RGB(42, 48, 92) .foreground_ = RGB(179, 179, 179) .curDateBG_ = RGB(122, 185, 247) .curDateFG_ = RGB(0, 0, 0) .inactiveDates_ = RGB(80, 80, 166) Case TCalendarTheme.T_CT_LIGHT .background_ = RGB(240, 240, 240) .foreground_ = RGB(0, 0, 0) .curDateBG_ = RGB(246, 127, 8) .curDateFG_ = RGB(0, 0, 0) .inactiveDates_ = RGB(200, 200, 200) End Select End With End Function Private Function SetArrowsVisibility(isVisible As Boolean) lblDOWN.Visible = isVisible lblUP.Visible = isVisible End Function Private Function TagToMonth(sTag$) As Long Select Case sTag Case "JAN": TagToMonth = 1 Case "FEB": TagToMonth = 2 Case "MAR": TagToMonth = 3 Case "APR": TagToMonth = 4 Case "MAY": TagToMonth = 5 Case "JUN": TagToMonth = 6 Case "JUL": TagToMonth = 7 Case "AUG": TagToMonth = 8 Case "SEP": TagToMonth = 9 Case "OCT": TagToMonth = 10 Case "NOV": TagToMonth = 11 Case "DEC": TagToMonth = 12 End Select End Function Private Function RepaintAllColors() Me.BackColor = colors_.background_ FrameDay.BackColor = colors_.background_ FrameMonth.BackColor = colors_.background_ FrameYr.BackColor = colors_.background_ lblToday.ForeColor = colors_.foreground_ lblToday.BorderColor = colors_.foreground_ lblTitleCurMY.ForeColor = colors_.foreground_ lblTitleCurMY.BorderColor = colors_.foreground_ lblUnload.ForeColor = colors_.foreground_ lblThemes.ForeColor = colors_.foreground_ lblUP.ForeColor = colors_.foreground_ lblDOWN.ForeColor = colors_.foreground_ Dim nItem& For nItem = 1 To CALENDAR_WEEKDAY_COUNT Step 1 Me.Controls("WD" & nItem).ForeColor = colors_.foreground_ Next nItem Call RepaintDaysColors Call RepaintMonthsColors Call RepaintYearsColor End Function Private Function RepaintDaysColors() Dim nItem& Dim theDate& For nItem = 1 To CALENDAR_DAYS_COUNT Step 1 With Me.Controls("D" & nItem) theDate = .Tag .BorderStyle = fmBorderStyleNone If .Tag = curDate_ Then .BackStyle = fmBackStyleOpaque .BackColor = colors_.curDateBG_ .ForeColor = colors_.curDateFG_ ElseIf Month(.Tag) = month_ Then .BackStyle = fmBackStyleTransparent .BackColor = colors_.background_ .ForeColor = colors_.foreground_ Else .BackStyle = fmBackStyleTransparent .BackColor = colors_.background_ .ForeColor = colors_.inactiveDates_ End If End With Next nItem End Function Private Function RepaintMonthsColors() Dim nMonth& For nMonth = 1 To CALENDAR_MONTH_COUNT Step 1 With Me.Controls("M" & nMonth) .BorderStyle = fmBorderStyleNone If nMonth = month_ Then .BackStyle = fmBackStyleOpaque .BackColor = colors_.curDateBG_ .ForeColor = colors_.curDateFG_ Else .BackStyle = fmBackStyleTransparent .BackColor = colors_.background_ .ForeColor = colors_.foreground_ End If End With Next nMonth End Function Private Function RepaintYearsColor() Dim nYear& For nYear = 1 To CALENDAR_YEARS_PER_PAGE Step 1 With Me.Controls("Y" & nYear) .BorderStyle = fmBorderStyleNone .Caption = CStr(firstYear_ + nYear - 1) If nYear + firstYear_ - 1 = year_ Then .BackStyle = fmBackStyleOpaque .BackColor = colors_.curDateBG_ .ForeColor = colors_.curDateFG_ Else .BackStyle = fmBackStyleTransparent .BackColor = colors_.background_ .ForeColor = colors_.foreground_ End If End With Next nYear End Function