Option Explicit
'===========================================================================================
'Design and development by Sam Mathai Chacko
'An idea remains an idea until it is implemented
'Due credit to Neil Holder
'===========================================================================================
'And this is where we have dimensioned our module variables
'This is our ribbon control variable
Dim miruCalendar As IRibbonUI
'The following constant can be used to define the starting day of the week
'So one can use the enumerated type members of the VbDayOfWeek class to pass value to this constant
'The values can be vbSunday, vbMonday,......vbSaturday (I am using vbUseSystemDayOfWeek because I like to go with default)
Private Const mcbytDayOfWeek As Byte = vbUseSystemDayOfWeek
'Changing this value will allow you to specific the big increment/decrement for years
Private Const mclngBigYearIncrementDecrement As Long = 10
Private Const mclngYearWindowFromSelectedYear As Long = 50
'I'm just trying to be generous with the 64-bit users.
'Why check for VB 7 environment if it's already a 64 bit environment, right? Yeah, I get that all the time. Go figure.
'If you are a 'Winchester-Waco-Johnny-Dean-developer', you should look at this
'http://social.msdn.microsoft.com/Forums/office/en-US/999c5d69-a176-43e5-b5df-716f8960fc6e/my-code-if-else-for-64-bit-is-not-being-recognized
#If VBA7 Then
#If Win64 Then
Dim lngStartDay As LongPtr, _
lngEndDay As LongPtr, _
lngDayCount As LongPtr, _
lngDaySlotCount As LongPtr, _
lngSelectedYear As LongPtr, _
lngSelectedMonth As LongPtr, _
lngMonthDays(0 To 48) As LongPtr
#Else
Dim lngStartDay As Long, _
lngEndDay As Long, _
lngDayCount As Long, _
lngDaySlotCount As Long, _
lngSelectedYear As Long, _
lngSelectedMonth As Long, _
lngMonthDays(0 To 48) As Long
#End If
#Else 'Yes, it seems like a paradox, but who knows, what if it's 64 bit and still VB6
Dim lngStartDay As Long, _
lngEndDay As Long, _
lngDayCount As Long, _
lngDaySlotCount As Long, _
lngSelectedYear As Long, _
lngSelectedMonth As Long, _
lngMonthDays(0 To 48) As Long
#End If
'Callback for customUI.onLoad
Sub LoadCalendar(ribbon As IRibbonUI)
'So this is the first callback that will run for the ribbonUI, ie, the OnLoad function
'We use an IRibbonUI control to set the object
Set miruCalendar = ribbon
'We also initialize our calendar control for the current year and date (so this is what the user will see by default)
lngSelectedYear = Year(Date)
lngSelectedMonth = Month(Date)
End Sub
'Callback for grpCalendar getLabel
Sub GetLabelCalendarGroup(control As IRibbonControl, ByRef returnedVal)
'I've decided to leave this open for any other developer who would like to use a translator or any other mechanism,
'to give a local language name for the calendar. You could either hard-code it below, or use any reliable method to,
'get proper local language translation
returnedVal = "Calendar"
End Sub
'Callback for galCalendar getEnabled
Sub GetEnabled(control As IRibbonControl, ByRef returnedVal)
'Of course we want our calendar to be enabled
'Having said that we could have avoided using the getEnabled feature, and just used enabled="true" in the XML
returnedVal = True
End Sub
'Callback for galCalendar getLabel
Sub GetLabelDay(control As IRibbonControl, ByRef returnedVal)
'If you want to provide any label (caption) for the date selection gallery, you can pass that value here
'Note the some special characters are not accepted
'I've decided to leave this open for any other developer who would like to use a translator or any other mechanism,
'to give a local language name for 'Day'. You could either hard-code it below, or use any reliable method to,
'get proper local language translation
returnedVal = "Day"
End Sub
'Callback for galCalendar getItemCount
Sub GetItemCount(control As IRibbonControl, ByRef returnedVal)
'I use 49 because that's what all the normal date calendars use. After the top row is used for the name of the 7 days, 42 remains.
'So 42 is basically 7 days * 6 rows
'The first row will be used to display week days Monday to Sunday
'The next 6 rows will be used to display the dates depending on where the first date for the corresponding month starts from
'For example, if we selected a non-leap year february, with the first day starting on a Monday, we would end up using only 4 rows
'and the remaining 1 row will be entirely blank
'Similarly, there will be months which start on a Sunday, which is the last column of our calendar control,
'effectively using at 1 or 2 columns of the last row, depending on whether there are 30 or 31 days for that month
returnedVal = 49
End Sub
'Callback for galCalendar getItemLabel
Sub GetItemLabel(control As IRibbonControl, index As Integer, ByRef returnedVal)
'So this is where all the action happens (well, at least most of it)
'So first of all, we need the top row to have the names of the week
'Of course it is up to the developer to decide on any algorithm to come up with the names of the weekdays
'Based on feedback from some non-English office users,
'I've decided to use the WeekDayName function to return the correct Weekday name depending on which day their week starts
'We now need to identify the weekday on which the first day of the month starts
lngStartDay = Weekday(DateSerial(CInt(lngSelectedYear), CInt(lngSelectedMonth), 1), mcbytDayOfWeek) 'Year(Date), Month(Date), 1))
'We also need to know how many days there are in that month
lngEndDay = Day(DateSerial(CInt(lngSelectedYear), CInt(lngSelectedMonth) + 1, 0)) 'DateSerial(Year(Date), Month(Date) + 1, 0))
'Now we use a select case to distinquish between the top row of our calendar, and the remaining rows
'As you know, we have 7 columns. But what we need to be aware of is that the index parameter passed by this function starts from zero (0)
'So in my select case, I used < 7 instead of <= 7
Select Case index < 7
Case True 'Of course you know what this means
'Here we just pass back the name of each of the 7 weekdays as labels (yeah that's right, labels. Isn't that what the function name suggests?).
'In other words, you can consider labels as a caption (you know, like for a commandbutton, or a userform. OK, you got the idea)
returnedVal = Left(WeekdayName(index + 1, True, mcbytDayOfWeek), 2) 'This is where we pass the name of the weekday as a label for the control
Case Else 'Now, here's where the date part begins (because this is after the first 7 controls (or the top most row of our calendar)
'You remember we had already captured the weekday on which the first day of the month starts
'We also know how many days there are in the month
'Now we need to keep track of how many controls we are iterating through. For that I simply use a variable and increment it
'Note that the variables I am using have module scope (to know about scope of variables, visit http://support.microsoft.com/kb/141693)
lngDaySlotCount = lngDaySlotCount + 1
'So now we need to know when to start passing the days as labels
'For that I'm also using another module variable to increment the days and check if the days haven't exceeded the maximum days in that month
If lngDaySlotCount >= lngStartDay And lngDayCount < lngEndDay Then
lngDayCount = lngDayCount + 1 'This is the day increment variable
lngMonthDays(index) = lngDayCount 'This is an array of 49 items (0 to 48) where I keep track of the current months days. Will explain why I used this where I am using this
returnedVal = lngDayCount 'This is where we pass the day as a label for the control
End If
End Select
End Sub