The power of script
The real power of script is that it can just sit there attached to an object and do it's job. You really don't need to have to deal with it. Viewing a well commented and well structures script however is a great way to start to learn how to use them if you are not experienced in scripting.
There are 3 scripts in this object and we will go through them all separately to see how they work.
Calendar script
This script's purpose is to provide the functionality to draw the calendar for any given month.
Dim DateString Dim FirstDayOfWeek Dim CurrentDay Dim ColumnSpace Dim RowSpace Dim DaysRow Dim FirstRow Dim FirstColumn Dim TodayOffsetX Dim TodayOffsetY |
This first part of the code simply declares variables used in the script. We will describe these variables when we get to them. For now it is enough to know that it is good practice to declare the variables before they are used.
Sub Object_OnScriptEnter
' Set Variables ColumnSpace = 31 ' Gap between columns RowSpace = 27 ' Gap between rows DaysRow = 24 ' y coordinate of the days FirstRow = 46 ' y coordinate of first row FirstColumn = 29 ' x coordinate of first column TodayOffsetX = -8 ' x offset of the today image from top left of current day text TodayOffsetY = -11 ' y offset of the today image from top left of current day text
|
Here we set values for a lot of the variables we defined above. Earlier I mentioned how you may at times want to adjust the position of days and dates depending on the background object used and the font used. This is where you do it. Good scripts will always use variables where possible rather than hard coding values. This makes them far more flexible.
The parameters you can set are all clearly labeled and where x and y coordinates are used, these are relative to the top left of the background image. Try adjusting these values , and Applying the script to see the impact it has on the calendar. Don't worry if some of the dates appear off the calendar for now. You can vary the background image to allow for this.
To make a wider background object you can simply change the width parameter on the Summary tab. Because in the Advanced Properties for the image we set parameters for the object to stretch horizontally the object will still look correct.
To make a taller background object there are two elements you can change. You can obviously add extra space at the top or bottom of the object. However, if you are increasing to font spacing you really must make the size of the vertically tiled area larger by the same amount.
' Define current month and year e.g. September 2004 so we can start by showing this month x = Now() x = Month(x) & " " & Year(x)
' Define current date so we can monitor whether the day has changed CurrentDay = Date()
' Define a timer to run every minute and check if the date has changed so we can update the calendar if it has Object.SetTimer 1000, 60000
' Draw the initial calendar for this month DrawCalendar(x)
|
The commenting here should be fairly explicit. We get information on the current system date and then we call the DrawCalendar function which we will come onto passing a parameter of month and date like "9 2004" telling it what month to draw.
Also in here we define a timer to run every minute. We will look at this timer now before we move onto looking at the main DrawCalendar function.
Sub Object_OnTimer1000 ' Determine if the current date has changed If Date() <> CurrentDay Then ' If it has, update the current day and redraw the calendar based on the new current day CurrentDay = Date() x = Now() x = Month(x) & " " & Year(x) DrawCalendar(x) End If End Sub
|
What we do here is look at the current date and compare it to the date stored in CurrentDay which we just defined. Obviously in this case because we just set CurrentDay to Date() the two will be equal so nothing will happen, but if you leave this object running and the clock passes midnight to a new day then the code in the "If" statement will be triggered. What this does it to update CurrentDay to the new date and redraw the calendar. for the current month. Obviously on most days this will just redraw the current month, but if the day changes to a new month it ensures that the calendar draws the new month.
OK, now onto the main function that draws the calendar ...
Function DrawCalendar(thisdate)
' Get first day of week from system settings If Weekday(MonthName(8) & " 7, 2004") = 7 Then FirstDayOfWeek = 1 Else FirstDayOfWeek = 2 End If
|
Your computer will either consider Monday or Sunday the first day of the week depending on your personal settings. I know that August 8th 2004 was a Sunday. What this script is to check what day of the week the computer thinks that date is. From that I can define a variable called FirstDayOfWeek. If it thinks that date is the 7th day of the week then I Set FirstDay of week to 1 (Monday) otherwise I set it to set this to 2 (Sunday). This becomes important at several points later in the script.
Note that I use the function MonthName(8) rather than explicitly writing "August" because this would cause errors on non-English machines. MonthName will return a name that the computer understands like Aout (for French computers.
' Create a string of the first letter of each day For x = 1 To 7 DayString = DayString & Left(WeekdayName(x,False,FirstDayOfWeek),1) Next
|
At some point I need to create text labels for the days of the week. The way I do this is to build a string of the first character of the day name. I do this 7 times until I have a 7 character string. Note that the FirstDayOfWeek variable comes into play here, because the VBScript WeekdayName function requires that you specify which is the first day of the week so it can return the correct letter. The variable DayString will either contain "MTWTFSS" or "SMTWTFS" (assuming an English computer) depending on the FirstDayOfWeek setting.
' Delete existing days of week if present For x = 1 To 7 If DesktopX.IsObject("DayOfWeek" & x) Then DesktopX.Object("DayOfWeek" & x).Delete End If Next
' Create new days of week For x = 1 To 7 DesktopX.Object("Template_DayOfWeek").Clone "DayOfWeek" & x, FirstColumn + ((x - 1) * ColumnSpace), DaysRow DesktopX.Object("DayOfWeek" & x).Text = Mid(DayString, x, 1) DesktopX.Object("DayOfWeek" & x).Visible = True Next
|
What this is doing is to check for any existing day of the week objects and deleting them if they exist. The name of the objects for these days are DayOfWeek1, DayOfWeek2 ... DayOfWeek7 so it loops and checks for objects with these names and deletes them if required.
We then have a fairly cool bit of script which achieves a lot in 5 lines, mainly because of good use of variables and templates. We want to create 7 objects so we loop 7 times. Each time we do three things:
1) We Clone the template object for the weekdays, There are three parameters. The first is the name for the new object which we build by concatenating the string "DayOfWeek" with the number of the loop we are on. The next 2 parameters are the x and y coordinates to position the object. We use maths here based on the variables defined earlier. While the maths may look quite complicated it is really fairly simple especially as we gave the variables sensible names! This is also far more flexible as we mentioned earlier than hard coding numbers in here.
2) We then set the text of the object to be the first letter of that day of the week. We get this by using the Mid function to extract a single character from the DayString we build earlier. The character we take is determined by the position in the loop.
3) Finally we make our new object visible which we need to do because the template object we cloned was hidden
' Delete existing day objects if they exist For x = 1 To 42 If DesktopX.IsObject("Day" & x) Then DesktopX.Object("Day" & x).Delete End If Next
|
Just as we did for the days of the week, we delete any day objects drawn so we can start fresh.
' Determine the month and year that we are drawing from the variable we passed to the function thismonth = Left(thisdate, InStr(thisdate," ") -1) thisyear = Right(thisdate, Len(thisdate) - Len(thismonth) - 1)
' Set the year text object DesktopX.Object("Calendar_Year").Text = thisyear
' Get the month name and write it to the text object for the month name thisMonthName = MonthName(thismonth) DesktopX.Object("Calendar_Month").Text = thisMonthName
|
What this script does is extract the month and year from the variable passed to the function and stores them in the variables thismonth and thisyear. The year text object "Calendar_Year" is then set to the year in thisyear. We then get the name of the month in the variable thismonth using the MonthName function, and set the "Calendar_Month" text object to this value.
' Check which day number in the week the first day of the month occurs on calFirstDay = thisMonthName & " 1, " & thisYear calFirstDay = CDate(calFirstDay) calFirstDay = DatePart("w", calFirstDay, FirstDayOfWeek)
' Check how many days there are in the current month calDaysInMonth = DaysInAnyMonth(thisMonth, thisYear)
|
If order to accurately draw the month there are two key things we need to establish. We need to know which day of the week the first day of the month is so we know where on the calendar to draw the first day. We then need to know how many days there are in the month so we can draw them. This script builds a string of the first day of the current month e.g. "September 1, 2004". It then ensures it is in a Date format and uses the DatePart VBScript function to find out which day of the week this day is on. Note that again we need the FirstDayOfWeek variable again to ensure that the day of the week returned is appropriate for the user's computer settings.
We then use the DaysInAnyMonth function at the end of the script to determine the number of days in the month we are drawing.
' Check how many days there are in the previous month as we are going to be drawing these If calFirstDay > 1 Then If thismonth = 1 Then dayinprevmonth = DaysInAnyMonth(12, thisyear - 1) Else dayinprevmonth = DaysInAnyMonth(thismonth - 1, thisyear) End If
'Create days from the previous month from the calendar beginning to just before where the current month starts For x = 1 To calFirstDay - 1 ' Clone the hidden template object and position based on the variables at the top DesktopX.Object("Template_Day").Clone "Day" & x, FirstColumn + (ColumnSpace * (x-1)), FirstRow DesktopX.Object("Day" & x).Text = dayinprevmonth - calFirstDay + 1 + x ' make them semi transparent and then show them DesktopX.Object("Day" & x).Opacity = 30 DesktopX.Object("Day" & x).Visible = True Next Set daysinprevmonth = Nothing End If
|
Before we draw the days from this month we draw the last few days from the previous month in any space prior to where the first day is drawn. e.g. if the first day is on a Thursday then we can draw the previous month days in the spaces up to Wednesday.
The first part of the above script calculates what the previous month is and then gets the number of days in that month.
After that it clones the Template_Day object and positions it much as we did for the days of the week. It then works out what day that object was and sets the text appropriately. The opacity of the of these objects is lowered so they are less visible than the current month which is more important. The object is then shown.
If you wanted days from other months to look fundamentally different than the current month days then you could create a new Template object which looks as you want it and clone that instead.
'Draw days for this month For x = calFirstDay To calFirstDay + calDaysInMonth - 1 DesktopX.Object("Template_Day").Clone "Day" & x, FirstColumn + (ColumnSpace * ((x-1) Mod 7)), FirstRow + (Int((x-1)/7)*RowSpace) DesktopX.Object("Day" & x).Text = x - calFirstDay + 1 DesktopX.Object("Day" & x).Visible = True Next
|
Now onto the current month. This again is a cloning exercise that you will be familiar with by now. The maths looks complex on me so just trust me that it positions the object on a new line if appropriate!
'Calculate the number of rows required based on whether a date has been create on the first instance of every row If DesktopX.IsObject("Day22") Then RowCount = 4 If DesktopX.IsObject("Day29") Then RowCount = 5 If DesktopX.IsObject("Day36") Then RowCount = 6
' Scale the background to allow correct number of weeks to be shown Object.Height = 72 + (27 * RowCount) ' Reposition the year relative to the bottom of the object DesktopX.Object("Calendar_Year").Top = Object.Height - 33
|
We are now about to draw the objects for the next month, but before we do we we need to ensure that the object is the right height. We only want it to be as big as it needs to be to show the current month. We check to see if various objects exist. These would be the first object on each row so we can tell how many rows of days there are but seeing if these objects have been created.
Once we know this we can scale the background accordingly. This will tile the correct area we defined at the beginning and draw the image as required. We then reposition the object displaying the year relative to the bottom of the object.
'Draw days for next month to the end of the last row For x = calFirstDay + calDaysInMonth To RowCount * 7 DesktopX.Object("Template_Day").Clone "Day" & x, FirstColumn + (ColumnSpace * ((x-1) Mod 7)), FirstRow + (Int((x-1)/7)*RowSpace) DesktopX.Object("Day" & x).Text = x - calFirstDay - calDaysInMonth + 1 DesktopX.Object("Day" & x).Opacity = 30 DesktopX.Object("Day" & x).Visible = True Next
|
Now, just a final cloning run to add the few days from the beginning of the next month that will fit on the row.
' Check to see if the month being shown is the current month If DesktopX.Object("Calendar_Month").Text = MonthName(Month(Now())) And CInt(DesktopX.Object("Calendar_Year").Text) = Year(Now()) Then ' If it is then position and show the today image x = calFirstDay + Day(Now()) - 1 DesktopX.Object("Calendar_Today").Move DesktopX.Object("Day" & x).Left + TodayOffsetX, DesktopX.Object("Day" & x).Top + TodayOffsetY DesktopX.Object("Calendar_Today").Visible = True DesktopX.Object("Day" & x).OnTop Else ' If not then hide the today image DesktopX.Object("Calendar_Today").Visible = False End If End Function
|
This little section of code is designed to draw the image which highlights "today" if the current month is being shown. The "if" routine checks the month and year objects against the current date. If the current month is being shown then it works out which object represents "today". It then moves the today image relative to the today object using the offset variables defined at the beginning of the script. The object is shown and moved to the top of the z-order. This moving of the z-order is because I want it to look as if the circle has been drawn over the number. If you have a "today" image that you want to appear behind the date you can remove or comment out this object.
That's it we're done drawing the calendar!
' Function to return the number of days in a month for the month and year provided Function DaysInAnyMonth(intMonth, IntYear) If IntYear = 0 Then IntYear = Year(Now()) End If
Select Case (intMonth) Case 2 ' February (includes leap year calculation) If (IntYear Mod 4 = 0) And (IntYear Mod 100 <> 0) Or (IntYear Mod 400 = 0) Then DaysInAnyMonth = 29 Else DaysInAnyMonth = 28 End If Case 4, 6, 9, 11 DaysInAnyMonth = 30 Case Else DaysInAnyMonth = 31 End Select End Function
|
This little bit of script is what we called several times earlier to get the number of says in a given month. The first part of the script checks that a year variable was passed. It uses the current year if it isn't given buy we're always good and provide it anyway.
It then uses Select Case to do different things depending on the month. The first thing it does is run a scenario for February (month 2). It checks for a leap year and then returns the correct number of days. This is why passing the year was important in case you were wondering. It then returns 30 days for April, June, September and Novemeber and returns 31 for any other month. |