// Metric: 6 - Pixels only /* File: kmDateWithCal.cc Author: Ken Mayer Date: January 30, 2017 Updated: February 7, 2017 -- moved the language property to the kmDateWithCalendar class (with code to assign the value to the subform in the onClick for the pushbutton); and added the autoTab property -- if set to true, will tab to next control on the form (default is false) when the calendar closes. March 19, 2018 -- added some improvements -- clicking on a date, the "Today" button, or the "Cancel" button, and the subform closes, rather than leaving it open, with no apparent way to close it. In dBASE Plus 11 the controls look better on the subform, but this should work for earlier versions of dBASE as well. In Plus 11, we use the new pushbutton property "themeBackground", in earlier versions we use "systemTheme" ... There are two primary classes here: kmDateWithCalendar kmDTWithCalendar The first is an entryfield/pushbutton combination designed to be dataLinked to a date field in a table. The pushbutton opens and closes a calendar, which interacts directly with the entryfield (changing the date on the calendar updates the entryfield as you go). The second is the same but for a DateTime (or Timestamp) field. The biggest difference is that the DateTime version (kmDTWithCalendar) is that it has an option to handle the time part of the field on the calendar. The calendar itself is customizable through the use of a header file (kmDateWithCal.h). See notes below and in the header file itself. This file contains a lot of code just to handle entryfields with a calendar attached, but that is because the calendar itself takes a lot of code to handle what needs to be done. The code for the entryfield also takes a bit of work, although most of that is associated with the pushbutton. Usage: set procedure to :dUFLP:kmDateWithCal.cc Two items will appear on the Component Palette (if you have not run the setup for the dUFLP, on the "Custom" tab, otherwise on the "dUFLP Misc. Controls" tab). The instructions below are for both controls -- drag the one you want (kmDateWithCalendar or kmDTWithCalendar) to the form, making sure to manipulate the position by dragging the underlying container, not the controls *on* the container. You may see some other controls appear on the Component Palette ("Custom" tab), ignore them -- these will be: CalPushbutton and WeekDayText -- these are not useful as independent controls, but I can't keep them off the Component Palette ... Set the dataLink for the entryfield to the field in the table you wish to link it to. Modifications: There are two properties of the control you may wish to change: language -- change the language -- see details in documentation below, and in the file :dUFLP:DateEx.cc. Details on the language strings can be found in the code at the beginning of :dUFLP:DateEx.cc, in the definition code for the associative array: asLang -- this is a long list, but you have to have the correct language definition as there needs to be an exact match on the text. Example: there are quite a few variations of "Arabic" in the list, you couldn't just use "Arabic", you would need to use the specific version: "Arabic (Saudi Arabia)" or "Arabic (Qatar)". Default: "English (United States)" ***NOTE*** This property only affects the month and day of week headings of the subform /calendar -- for any other language changes you would need to modify the file kmDateWithCal.h. autoTab -- tab from the DateEF (entryfield) when the calendar subform closes, the default is to place focus on the entryfield. (This is at the request of Mervyn Bick.) Default: false To change either of these, in the form's onOpen event handler: function form_onOpen() form.kmDateWithCalendar1.language := "German (Standard)" form.kmDateWithCalendar1.autoTab := true return Note: the form sets the form's doubleBufferred property to true, due to display issues of controls on containers. In addition, when the user clicks the CalendarButton, focus is set (briefly) to the DateEF control (the entryfield), which means if you wish to add some code for validation or an onLostFocus event handler, as soon as the Subform with the calendar appears, that code will fire. This allows you to hook in and add your own customized code. When the Subform is closed (by clicking the CalendarButton again), the entryfield regains focus, again allowing any additional code you may add to fire. There is (as noted above) an autoTab property you can set that will fire right after setting focus back on the entryfield, and tab to the next control on the form. The default setting is false. CONSTANTS: The constants used throughout the two primary controls are stored in "kmDateWithCal.cc" in the dUFLP, a copy is copied when you run the setup routine to whereever it is copied to for your version of dBASE and Windows. However, you should consider keeping a copy in the folder for your own application, if you wish to modify the values in the constants (language appropriate text for speedtips and the "Today" button, images for buttons, fontSize ...). Descriptions of the constants are noted here and in the pKenCalendar header comments below. When you compile the kmDateWithCal.cc class, then the constants in the header are included and accessible. CHANGING ENTRYFIELD COLORS / SPEEDTIP / IMAGES These constants are defined in kmDateWithCal.h EFNormalColor -- assigned to the Entryfield's colorNormal property Default: "WindowText/Window" EFColorHighlight -- assigned to the Entryfield's colorHighlight property, the color when it has focus: Default: "WindowText/0x80ffff" CalButton1 -- the Calendar Button when the calendar is not displayed. Default: "RESOURCE PNG/calendar :resources:dFugue_24.dll" CalButton2 -- the Calendar Button when the calendar *is* displayed. Default: "RESOURCE PNG/calendarexport :resources:dFugue_24.dll" CalBtnSpeedTip1 -- the screenTip for when the calendar is NOT displayed Default: CalBtnSpeedTip2 -- the screenTip for when the calendar *is* displayed. Default: "Click to close the calendar" When the form is run and the pushbutton is clicked, the entryfield will be linked to the calendar, and updating the date in the calendar will automatically update the value property of the entryfield. The "cancel" button will set the value property back to what it was to start with (if it wasn't empty). If the field is empty (a new record for example), the calendar will assign the current date to the value. If the user wishes the field to be left empty they would need to empty the entryfield (delete what is there). One could, of course, add a "delete" button next to the calendar button to empty the value property if one wanted to. The code would be simply: this.parent.DateEF.value := {} to provide a blank date. ENTRYFIELD CONCERNS: This code is set to set focus to the entryfield when the pushbutton is clicked, either direction (it needs to be clicked two different times, once to open the calendar, once to close it). The reason for this is based on something Mervyn Bick pointed out in the newsgroups, if you want to fire code like an onLostFocus event handler for the entryfield, it won't fire unless the entryfield has focus in the first place. This ensures that it has focus first. This could also be used to effect field-level validation (perhaps a date range, etc.). See information about the autoTab property above. IMPORTANT DESIGN NOTE: You will need to experiment to be sure your form is tall/wide enough for the calendar to display, as it becomes part of the form while it is open. Once you get the control on the form, try running it, click the pushbutton, and check to see if the whole calendar displays or not. This means it will be cut off at the bottom (or right) of the form if you do not plan for it. It may be possible to add a new property for where to display the calendar (right/bottom as options for example), but for the moment, this is the issue. The larger the font, the bigger the calendar, the more space needed ... WHAT IS INCLUDED: The following is a short breakdown of what is here. -------------------------------------------------- kmDateWithCalendar: This is a container with an entryfield designed to be dataLinked to a date field in a table, and a pushbutton that will open a calendar control under the container. kmDTWithCalendar: As above but for datetime (timestamp) fields, rather than pure date-only fields. DateEF: Contained inside kmDateWithCalendar, this is the entryfield object used to dataLink to the table, etc. CalendarButton: The pushbutton used to do the work. Clicking this will set the calendar's currentDate property to the value in the DateEF entryfield, set a reference that the calendar control understands to the entryfield, so that when clicking dates, the value changes ... the calendar will open on top of anything else on the form, and when closed (clicking the button again, using polymorphism to do this -- changing the appearance and the code just a bit) will close the calendar. All of the calendar code below has a HUGE amount of documentation. -------------------------------------------------- Calendar Code: pKenCalendarWithDate The container for the calendar. There are a ton of buttons and some text objects, as well as the code to deal with things. See the documentation for this class for details on customization (including colors, fontSize, fontName, language, images and text for speedTips, and so on ...) CalPushbutton A base pushbutton class with some shared properties for all pushbuttons on the container. DayPushbutton The pushbutton class used to define the days. WeekDayText The text controls above the days (S for Sunday, M for Monday, etc.). -------------------------------------------------- Code not associated with classes directly: Points2Pixels() -- a function that does a basic calculation to convert fontSize (points) to pixels -- helps determine the size of the buttons and other controls as well as the calendar itself. -------------------------------------------------- DEPENDENCIES: If you choose to use this set of code with your own application, there are a lot of dependences you need to be aware of. For the images: Note that the default images are using the .DLLs provided by dBASE in the :resources: source code alias. To find the exact path to these, check the Properties dialog in the IDE, find the "Source Aliases" tab, and the "resources" source alias. Click it, and the path will appear in the dialog. Example, for dBASE Plus 11, on my Windows 10 computer: C:\Users\Public\Documents\dBASE\Plus11\media\Resources This is important if you wish to deploy this application and wish to use the images provided. You would need to deploy: dFugue_16_1.dll dFugue_24.dll See below as well for other files needed. DEPENDENCIES (Important for deploying an application that uses this control): :dUFLP:DATEEX.CC :dUFLP:HOLIDAY.CC :dUFLP:TIME.CC :dUFLP:HEBREW.CC DUFLP.H *shouldn't need to be deployed, as it would be compiled into the file needing it (DateEx.cc, etc.)* :dUFLP:kmDateWithCal.h -- same as dUFLP.H ... shouldn't need to be deployed If using the default images, if you change the images (which is an option) you would need to deploy those images in whatever fashion is appropriate: :resources:dFugue_24.dll :resources:dFugue_16_1.dll */ // Don't remove this -- it's vital: #INCLUDE kmDateWithCal.h class kmDateWithCalendar( oParent ) of container( oParent ) custom // NOTE: constants (for images and speedTips) defined in kmDateWithCal.h with (this) metric = 6 // Pixels borderStyle = 3 // None transparent = true width = 110.0 height = 30.0 onDesignOpen = class::onDesignOpen onOpen = class::Container_onOpen endwith // Language -- for use with the API calls in DateEx.cc, so we can // get the correct info for the month and day of the week -- // see documentation in :dUFLP:DateEx.cc for details: this.language = "English (United States)" this.autoTab = false // can be modified in code, used to // tab off the entryfield when // calendar closes this.type = "Date" // don't change this this.DateEF = new ENTRYFIELD( this ) with ( this.DATEEF ) height = 22.0 left = 2.0 top = 3.0 width = 79.0 value = { / / } pageno = 0 colorNormal = EFNormalColor colorHighlight = EFColorHighlight endwith this.CalendarButton = new PUSHBUTTON( this ) with ( this.CALENDARBUTTON ) onClick = class::CALENDARBUTTON_ONCLICK height = 26.0 left = 83.0 top = 1.0 width = 26.0 text = "" speedBar = true upBitmap = CalButton1 scaleBitmaps = true pageno = 0 speedtip = CalBtnSpeedTip1 endwith function onDesignOpen // called from container's onDesignOpen: if form.metric # 6 msgbox( "Form's metric property is not '6 - Pixels', "+; "this will not display properly.", ; "Bad Form Metric!", 16 ) return endif return function Container_onOpen // force this so any display issues // are minimized: form.doubleBuffered := true return function CALENDARBUTTON_onClick() // a polymorphing button: meaning that it basically has // two states -- if the calendar is not displayed, // the button will display it when clicked, if the button // is displayed, the button will close it when clicked local oParent, oEF if type( "this.CalendarOn" ) == "U" this.CalendarOn = false endif // if it's not 'on', then display it ... ('this' is the pushbutton) if not this.CalendarOn this.CalendarOn := true // set it to true now // modify display: this.speedtip := CalBtnSpeedTip2 this.upBitmap := CalButton2 oParent = this.parent // container ... oEF = oParent.DateEF // entryfield with date // instantiate the calendar: this.oCalendar = new pKenCalendar( form ) // setting parent to the form // Pass the entryfield object to a custom // property of the calendar: this.oCalendar.EF = oEF // set language property of the subform: this.oCalendar.language := this.parent.language // if the date entryfield value is empty, set // it to the current date: if empty( oEF.value ) if oParent.type == "Date" oEF.value := date() else oEF.value := datetime() endif endif // set the calendar's currentDate property to the // value of the entryfield: this.oCalendar.currentDate = oEF.value // determine whether date or datetime this.oCalendar.type = oParent.type oEF.setFocus() // this would fire any onLostFocus or // similar events // set the position this.oCalendar.top := oParent.height+oParent.top this.oCalendar.left := oParent.left // open the subform which fires off the code for // the subform's onOpen event handler: this.oCalendar.open() else // reset flag: this.CalendarOn := false // reset display: this.speedtip := CalBtnSpeedTip1 this.upBitmap := CalButton1 // set focus back to the entryfield: this.parent.DateEF.setFocus() // autotab (put focus on next control of form)? if this.parent.autoTab keyboard( "{Tab}" ) endif // release calendar and cleanup: this.oCalendar.close() release object this.oCalendar this.oCalendar = null endif // not this.CalendarOn return // from CALENDARBUTTON_onClick endclass /* kmDTWithCalendar -- a copy of the above but designed for DateTime (or Timestamp) fields, rather than just date. This is a subclass of the kmDateWithCalendar class above. */ class kmDTWithCalendar( oParent ) of kmDateWithCalendar( oParent ) custom // NOTE: constants (for images and speedTips) defined in kmDateWithCal.h // the Container: with (this) width = 160.0 endwith this.type = "DateTime" // don't change this // modify the value and width only of the entryfield: with ( this.DATEEF ) width = 130.0 value = {00/00/0000 00:00:00.00} endwith // the calendar button, modify the position with ( this.CALENDARBUTTON ) left = 134.0 endwith endclass /* pKenCalendar -- calendar class used by kmDateWithCalendar entryfield/button combination, based on pKenCal3.cc in the dUFLP and massively modified ... Author : Ken Mayer January, 2017 in an attempt to make a more flexible calendar by allowing the developer using it to determine font, fontSize, and so on I ended up creating a whole new "thing". While very similar to some other calendars, this one is meant to be more flexible, with an updated appearance. I have given this one the ability to define (see below) text for language specific items in the code more easily (except for holidays -- that's trickier) ... See also comments for kmDateWithCalendar class above which provides more detail. Usage -- called from above, this is not designed as an independant control, although you could probably do something with it to make it useful for your own app without the kmDateWithCalendar class above. Modifying Constants and Properties: Open a local copy (one in the working folder for your development environment) of kmDateWithCal.h, and you can change the constants there. When you recompile kmDateWithCal.cc, the changes will take effect. The following are the various constants defined that affect the calendar itself. The ones that affect the Entryfield and Pushbutton parts of the control are explained in the header for that control above. CALENDAR TITLE TEXT: CalendarTitleText As this is a subform, the you can change the text in the title of the form. Default: "Calendar" COLOR CONSTANTS: The following custom color constants can be used to change the appearance of the calendar. You can change them in the file "kmDateWithCal.h". Below is a description of what they are used for. NormalColor This can be used to set the color of the non-selected days. Default: black/white SelectedColor This is used to set the selected day (current date) to a specific color so that it stands out. Default: white/blue SundayColor As above, this is used to display the first column in a specific color. NOTE: In this version, holidays that are defined in the "LoadHolidays" method of the container will appear in the "SundayColor" unless they are also the "selected" day ... Default: maroon/white HolidayColor As above, used specifically for Holidays, will override a sundaycolor setting ... Default: green/white MouseOverColor When the mouse is over a day, the color of the button changes to this color. Default: red/white Note: The colors used here are basic named colors, but you could use hexidecimal colors, such as: "0XA00000", but you would need to spend some time figuring out which values to use. For more details on colors, see the Appendices of The dBASE Book, by Ken Mayer. MONTH/YEAR INCREMENT/DECREMENT BUTTONS: These two values (also defined the same way as above, as constants) can be used to change the defaults for the buttons for adding or subtracting 'n' number of months/years -- the speedtip is updated as well as the actual calculation: MonthIncrement -- add/subtract 'n' months Default: 3 YearIncrement -- add/subtract 'n' years Default: 10 LANGUAGE Properties: The kmDateWithCalendar container has a "language" property that defaults to "English (United States)", used to format dates and deal with the months/day headings ... It can be changed either in the code here, or in a running form with: fMyForm.kmDateWithCalendar1.language := "German (Standard)" Details on these can be found in the code at the beginning of :dUFLP:DateEx.cc, in the definition code for the associative array: asLang -- this is a long list, but you have to have the correct language definition as there needs to be an exact match on the text. Example: there are quite a few variations of "Arabic" in the list, you couldn't just use "Arabic", you would need to use the specific version: "Arabic (Saudi Arabia)" or "Arabic (Qatar)". If you are using the subform on its own, then you would need to reference the subform to set the language appropriately: form.pKenCalendar1.language := ... LANGUAGE CONSTANTS: TodayText -- text of "Today" button Default: "Today" TodaySpeedTip -- text of speedtip for Today Button Default: "Go to Today's Date" CancelSpeedTip -- text of speedTip for the Cancel button in the lower right corner of the calendar. Default: "Cancel Changes" // these are all used in the speedtips for the // next/prev 'n' months, etc. buttons: MonthText -- singular (Next Month, Previous Month) Default: "Month" MonthsText -- multiple (n Months Ahead ...) Default: "Months" YearText -- singular (Next Year) Default: "Year" YearsText -- multiple (n Years Ahead ...) Default: "Years" BackText -- text used for "n Months Back" Default: "Back" PreviousText -- text used for "Previous Month" Default: "Previous" AheadText -- "n Months Ahead" Default: "Ahead" NextText -- "Next Month" Default: "Next" BUTTON IMAGES: NextButtonImg -- next month/year: Default: "RESOURCE PNG/control :resources:dFugue_16_1.dll" Next10BtnImg -- next 'n' months/years: Default: "RESOURCE PNG/controldouble :resources:dFugue_16_1.dll" PrevButtonImg -- previous month/year: Default: "RESOURCE PNG/control180 :resources:dFugue_16_1.dll" Prev10BtnImg -- back n months/years: Default: "RESOURCE PNG/controldouble180 :resources:dFugue_16_1.dll" TodayBtnImg -- Today Button: Default: "RESOURCE PNG/calendarselect :resources:dFugue_16_1.dll" UndoButtonImg -- Cancel/Undo Changes Button Default: "RESOURCE PNG/arrowcircle225left :resources:dFugue_16_1.dll" TimeButtonImg -- Change Time Button: Default: "RESOURCE PNG/alarmclockblue :resources:dFugue_24.dll" TimeBackImg -- time button back to calendar: Default: "RESOURCE PNG/calendarimport :resources:dFugue_16_1.dll" Note that these images are using the .DLLs provided by dBASE in the :resources: source code alias. To find the exact path to these, check the Properties dialog in the IDE, find the "Source Aliases" tab, and the "resources" source alias. Click it, and the path will appear in the dialog. Example, for dBASE Plus 11, on my Windows 10 computer: C:\Users\Public\Documents\dBASE\Plus11\media\Resources This is important if you wish to deploy this application and wish to use the images provided. You would need to deploy: dFugue_16_1.dll dFugue_24.dll See below as well for other files needed. HOLIDAYS: see code in method LoadHolidays -- you can change the holidays used, the ones that are automatically loaded are standard United States Holidays. You may want to add your own, see the code examples. You may not want all the U.S. holidays if you aren't in the US. In a case like that, comment out the ones you don't want on the calendar, and again add your own. You may want to change the text for the buttons. Note that at least currently the software only handles one holiday per date ... not sure how standard speedTips would handle a line break to deal with multiple lines of text. :) ** Note that this cc file uses Source Aliasing, specifically the ** dUFLP source Alias. If you do not know how to set up ** a Source Alias, see the instructions in WHATS.NEW at ** the top of the file, or in README.TXT. ** Any files referenced by :dUFLP:filename must be ** included in an executable if you are building ** one ... make sure that your project includes these ** files. ** ** This particular program uses: ** dateex.cc ** holiday.cc ** time.cc ** hebrew.cc ** duflp.h <== not referred to by source alias, used ** in DateEx.cc, etc. ** kmDateWithCal.h ** Images are from: ** :resources:dFugue_24.dll ** :resources:dFugue_16_1.dll ** You would either need to change the images, or ** deploy these two .dll files DEPENDENCIES (Important for deploying an application that uses this control): DATEEX.CC HOLIDAY.CC TIME.CC HEBREW.CC dFugue_24.dll dFugue_16_1.dll */ /* ------------------------------------------------------- The subform class here is what does all the work ... There are some custom button/text control definitions after the subform definition. ------------------------------------------------------- */ class pKenCalendar(parentObj) of subform(parentObj) set procedure to :dUFLP:dateex.cc additive set procedure to :dUFLP:holiday.cc additive // Note that size and position are determined based // on calculations ... with (this) metric := 6 // Pixels onOpen := class::CONTAINEROPEN systemTheme := false doubleBufferred := true colorNormal := "white" text := CalendarTitleText smallTitle := true showTaskBarButton := false mdi := false topMost := true escExit := false sizeable := false maximize := false minimize := false escExit := false sysMenu := false endwith // default, but the calendarButton in kmDateWithCalendar will set this // if it is changed in the form's onOpen event handler: this.language = "English (United States)" // NOTE: other constants (not below) are defined in kmDateWithCal.h // From here down, don't mess with these ... #define ButtonHeight Points2Pixels( ButtonFontSize ) + (PaddingSize * 2) // pixels #define ButtonWidth Points2Pixels( ButtonFontSize ) + (PaddingSize * 2) // pixels // need to define these here, but then need the column // positions below ... #define YearRow 4 // pixels from top of container // YMBtnHeight is the height of the Year and Month buttons // at the top of the calendar: #define YMBtnHeight ButtonHeight + PaddingSize // additional room for descenders #define MonthRow YearRow+YMBtnHeight // from top // Define rows based on button heights: #define TITLEROW MonthRow+YMBtnHeight+2 #define ROW1 (TitleRow-PaddingSize)+YMBtnHeight #define ROW2 ROW1+ButtonHeight #define ROW3 ROW2+ButtonHeight #define ROW4 ROW3+ButtonHeight #define ROW5 ROW4+ButtonHeight #define ROW6 Row5+ButtonHeight #define TodayButtonTop Row6+ButtonHeight // define columns based on button widths: #define COL1 7 // pixels from left of container #define COL2 COL1+ButtonWidth #define COL3 COL2+ButtonWidth #define COL4 Col3+ButtonWidth #define COL5 Col4+ButtonWidth #define COL6 Col5+ButtonWidth #define COL7 Col6+ButtonWidth // Year and Month rows, we need to define // where everything is, the calcs will be a bit more // complex than some of the above. #define PrevBtnLeft COL1+23 // go to the right side of the buttons at COL7, // subtract width of Next10 button, and width of // Next button: #define NextBtnLeft (COL7+ButtonWidth) - (23+18) // same as above but without the 'next' button #define Next10BtnLeft (COL7+ButtonWidth) - 23 // then we deal with the left and widths of the Month and Year // text controls: #define YMTextleft 23+18+8 #define YMTextWidth (COL7+ButtonWidth) - (2*(23+18)) - 8 // width and height of the subform will // vary based on width/height of buttons: #if __version__ < 11 #define ContainerWidth COL7+ButtonWidth+15 #else #define ContainerWidth COL7+ButtonWidth+5 #endif // once we add more buttons at the bottom, this will // need to be modified: #if __version__ < 11 #define ContainerHeight TodayButtonTop+ButtonHeight+PaddingSize+10 #else #define ContainerHeight TodayButtonTop+ButtonHeight+PaddingSize #endif this.width := ContainerWidth this.height := ContainerHeight // set left of TodayButton based on width of container // and width of TodayButton ... // left = (ContainerWidth - TodayButtonWidth) / 2 // width: image width + text: "Today" #define TodayButtonWidth ( 23 + ( Points2Pixels( ButtonFontSize ) * 4 ) ) #define TodayButtonLeft (ContainerWidth - TodayButtonWidth) / 2 // instance of dateex class and holiday class: this.DateEx = new DateEx() this.Holiday = new Holiday() // used to set speedtips ... this.cSpeedTip = "" // currentDate and beginDate: this.currentDate = {} this.beginDate = {} // constructor for calendar begins here ... don't mess with this! this.YEARMINUS10 = new CalPushbutton(this) with (this.YEARMINUS10) onClick := class::YEARMINUS10_ONCLICK height := YMBtnHeight left := COL1 top := YearRow width := 23 text := "" upBitmap := Prev10BtnImg speedBar := true speedTip := YearIncrement+" " + YearsText + " " + BackText colorNormal := NormalColor pageNo := 1 endwith this.PREVYEARPUSHBUTTON = new CalPushbutton(this) with (this.PREVYEARPUSHBUTTON) onClick := CLASS::PREVYEARPUSHBUTTON_OnClick upBitmap := PrevButtonImg text := "" height := YMBtnHeight width := 18 left := PrevBtnLeft top := YearRow speedTip := PreviousText + " " + YearText colorNormal := NormalColor pageNo := 1 endwith this.TEXTYEAR = new TEXT(this) with (this.TEXTYEAR) fontSize := ButtonFontSize text := "9999" alignHorizontal := 1 // center alignVertical := 1 // middle height := YMBtnHeight-1 // odd discrepency, possibly border? left := YMTextleft top := YearRow+1 // compensate for -1 in height above width := YMTextWidth border := true #if __version__ < 11 borderStyle := 1 // raised #else borderStyle := 9 // etched in #endif colorNormal := NormalColor pageNo := 1 endwith this.NEXTYEARPUSHBUTTON = new CalPushbutton(this) with (this.NEXTYEARPUSHBUTTON) onClick := CLASS::NEXTYEARPUSHBUTTON_OnClick upBitmap := NextButtonImg text := "" height := YMBtnHeight width := 18 left := NextBtnLeft top := YearRow speedTip := NextText +" " +YearText colorNormal := NormalColor pageNo := 1 endwith this.YEARPLUS10 = new CalPushbutton(this) with (this.YEARPLUS10) onClick := class::YEARPLUS10_ONCLICK upBitmap := Next10BtnImg height := YMBtnHeight left := Next10BtnLeft top := YearRow width := 23 text := "" speedTip := YearIncrement + " " + YearsText + " " + AheadText colorNormal := NormalColor pageNo := 1 endwith this.MONTHMINUS3 = new CalPushbutton(this) with (this.MONTHMINUS3) onClick := class::MONTHMINUS3_ONCLICK height := YMBtnHeight left := COL1 top := MonthRow width := 23 text := "" speedTip := MonthIncrement + " " + MonthsText + " " + BackText colorNormal := NormalColor upBitmap := Prev10BtnImg pageNo := 1 endwith this.PREVMONTHPUSHBUTTON = new CalPushbutton(this) with (this.PREVMONTHPUSHBUTTON) onClick := CLASS::PREVMONTHPUSHBUTTON_OnClick upBitmap := PrevButtonImg text := "" height := YMBtnHeight width := 18 left := PrevBtnLeft top := MonthRow speedTip := PreviousText + " " + MonthText colorNormal := NormalColor pageNo := 1 endwith this.TEXTMONTH = new TEXT(this) with (this.TEXTMONTH) fontSize := ButtonFontSize text := "Month" alignHorizontal := 1 // center alignVertical := 1 // middle height := YMBtnHeight-1 left := YMTextleft width := YMTextWidth top := MonthRow+1 border := true #if __version__ < 11 borderStyle := 1 // raised #else borderStyle := 9 // etched in #endif colorNormal := NormalColor pageNo := 1 endwith this.NEXTMONTHPUSHBUTTON = new CalPushbutton(this) with (this.NEXTMONTHPUSHBUTTON) onClick := CLASS::NEXTMONTHPUSHBUTTON_OnClick height := YMBtnHeight width := 18 left := NextBtnLeft top := MonthRow upBitmap := NextButtonImg text := "" speedTip := NextText + " " + MonthText colorNormal := NormalColor pageNo := 1 endwith this.MONTHPLUS3 = new CalPushbutton(this) with (this.MONTHPLUS3) onClick := class::MONTHPLUS3_ONCLICK height := YMBtnHeight left := Next10BtnLeft top := MonthRow width := 23 text := "" upBitmap := Next10BtnImg speedTip := MonthIncrement + " " + MonthsText + " " + AheadText colorNormal := NormalColor pageNo := 1 endwith this.TITLESUNDAY = new WeekDayText(this) with (this.TITLESUNDAY) left := COL1 top := TITLEROW endwith this.TITLEMONDAY = new WeekDayText(this) with (this.TITLEMONDAY) left := COL2 top := TITLEROW text := "M" endwith this.TITLETUESDAY = new WeekDayText(this) with (this.TITLETUESDAY) left := COL3 top := TITLEROW text := "T" endwith this.TITLEWEDNESDAY = new WeekDayText(this) with (this.TITLEWEDNESDAY) left := COL4 top := TITLEROW text := "W" endwith this.TITLETHURSDAY = new WeekDayText(this) with (this.TITLETHURSDAY) left := COL5 top := TITLEROW text := "T" endwith this.TITLEFRIDAY = new WeekDayText(this) with (this.TITLEFRIDAY) left := COL6 top := TITLEROW text := "F" endwith this.TITLESATURDAY = new WeekDayText(this) with (this.TITLESATURDAY) left := COL7 top := TITLEROW endwith this.ROW1COL1 = new DayPushButton(this) with (this.ROW1COL1) left = COL1 top = ROW1 endwith this.ROW1COL2 = new DayPushButton(this) with (this.ROW1COL2) left = COL2 top = ROW1 endwith this.ROW1COL3 = new DayPushButton(this) with (this.ROW1COL3) left = COL3 top = ROW1 endwith this.ROW1COL4 = new DayPushButton(this) with (this.ROW1COL4) left = COL4 top = ROW1 endwith this.ROW1COL5 = new DayPushButton(this) with (this.ROW1COL5) left = COL5 top = ROW1 endwith this.ROW1COL6 = new DayPushButton(this) with (this.ROW1COL6) left = COL6 top = ROW1 endwith this.ROW1COL7 = new DayPushButton(this) with (this.ROW1COL7) left = COL7 top = ROW1 endwith this.ROW2COL1 = new DayPushButton(this) with (this.ROW2COL1) left = COL1 top = ROW2 endwith this.ROW2COL2 = new DayPushButton(this) with (this.ROW2COL2) left = COL2 top = ROW2 endwith this.ROW2COL3 = new DayPushButton(this) with (this.ROW2COL3) left = COL3 top = ROW2 endwith this.ROW2COL4 = new DayPushButton(this) with (this.ROW2COL4) left = COL4 top = ROW2 endwith this.ROW2COL5 = new DayPushButton(this) with (this.ROW2COL5) left = COL5 top = ROW2 endwith this.ROW2COL6 = new DayPushButton(this) with (this.ROW2COL6) left = COL6 top = ROW2 endwith this.ROW2COL7 = new DayPushButton(this) with (this.ROW2COL7) left = COL7 top = ROW2 endwith this.ROW3COL1 = new DayPushButton(this) with (this.ROW3COL1) left = COL1 top = ROW3 endwith this.ROW3COL2 = new DayPushButton(this) with (this.ROW3COL2) left = COL2 top = ROW3 endwith this.ROW3COL3 = new DayPushButton(this) with (this.ROW3COL3) left = COL3 top = ROW3 endwith this.ROW3COL4 = new DayPushButton(this) with (this.ROW3COL4) left = COL4 top = ROW3 endwith this.ROW3COL5 = new DayPushButton(this) with (this.ROW3COL5) left = COL5 top = ROW3 endwith this.ROW3COL6 = new DayPushButton(this) with (this.ROW3COL6) left = COL6 top = ROW3 endwith this.ROW3COL7 = new DayPushButton(this) with (this.ROW3COL7) left = COL7 top = ROW3 endwith this.ROW4COL1 = new DayPushButton(this) with (this.ROW4COL1) left = COL1 top = ROW4 endwith this.ROW4COL2 = new DayPushButton(this) with (this.ROW4COL2) left = COL2 top = ROW4 endwith this.ROW4COL3 = new DayPushButton(this) with (this.ROW4COL3) left = COL3 top = ROW4 endwith this.ROW4COL4 = new DayPushButton(this) with (this.ROW4COL4) left = COL4 top = ROW4 endwith this.ROW4COL5 = new DayPushButton(this) with (this.ROW4COL5) left = COL5 top = ROW4 endwith this.ROW4COL6 = new DayPushButton(this) with (this.ROW4COL6) left = COL6 top = ROW4 endwith this.ROW4COL7 = new DayPushButton(this) with (this.ROW4COL7) left = COL7 top = ROW4 endwith this.ROW5COL1 = new DayPushButton(this) with (this.ROW5COL1) left = COL1 top = ROW5 endwith this.ROW5COL2 = new DayPushButton(this) with (this.ROW5COL2) left = COL2 top = ROW5 endwith this.ROW5COL3 = new DayPushButton(this) with (this.ROW5COL3) left = COL3 top = ROW5 endwith this.ROW5COL4 = new DayPushButton(this) with (this.ROW5COL4) left = COL4 top = ROW5 endwith this.ROW5COL5 = new DayPushButton(this) with (this.ROW5COL5) left = COL5 top = ROW5 endwith this.ROW5COL6 = new DayPushButton(this) with (this.ROW5COL6) left = COL6 top = ROW5 endwith this.ROW5COL7 = new DayPushButton(this) with (this.ROW5COL7) left = COL7 top = ROW5 endwith this.ROW6COL1 = new DayPushButton(this) with (this.ROW6COL1) left = COL1 top = ROW6 endwith this.ROW6COL2 = new DayPushButton(this) with (this.ROW6COL2) left = COl2 top = ROW6 endwith this.ROW6COL3 = new DayPushButton(this) with (this.ROW6COL3) left = COL3 top = ROW6 endwith this.ROW6COL4 = new DayPushButton(this) with (this.ROW6COL4) left = COL4 top = ROW6 endwith this.ROW6COL5 = new DayPushButton(this) with (this.ROW6COL5) left = COL5 top = ROW6 endwith this.ROW6COL6 = new DayPushButton(this) with (this.ROW6COL6) left = COL6 top = ROW6 endwith this.ROW6COL7 = new DayPushButton(this) with (this.ROW6COL7) left = COL7 top = ROW6 endwith this.TimeButton = new CalPushbutton(this) with (this.TimeButton) onClick = class::TimeButton_OnClick height = ButtonHeight width = ButtonWidth text = "" left = COL1 top = TodayButtonTop speedTip = TimeSpeedTip1 colorNormal = NormalColor upBitmap = TimeButtonImg visible = false pageNo = 0 endwith this.TodayPushButton = new CalPushbutton(this) with (this.TodayPushButton) onClick = CLASS::TodayPushButton_OnClick height = ButtonHeight left = TodayButtonLeft top = TodayButtonTop width = TodayButtonWidth text = TodayText fontSize = ButtonFontSize speedTip = TodaySpeedTip colorNormal = NormalColor upBitmap = TodayBtnImg pageNo = 1 endwith this.CancelButton = new CalPushbutton(this) with (this.CancelButton) onClick = CLASS::CancelButton_OnClick height = ButtonHeight width = ButtonWidth text = "" left = COL7 top = TodayButtonTop speedTip = CancelSpeedTip colorNormal = NormalColor upBitmap = UndoButtonImg pageNo = 1 endwith /* Define controls for the time part of the calendar ... Look at controls in the dUFLP for handling time values */ this.TimeTitle = new text( this ) with( this.TimeTitle ) fontSize := ButtonFontSize text := TimeTitleText alignHorizontal := 1 // center alignVertical := 1 // middle height := YMBtnHeight-1 // odd discrepency, possibly border? left := YMTextleft-10 top := YearRow+1 // compensate for -1 in height above width := YMTextWidth+20 border := true #if __version__ < 11 borderStyle := 1 // raised #else borderStyle := 9 // etched in #endif colorNormal := NormalColor pageNo := 2 endwith this.HOURLabel = new TEXT(this) with (this.HOURLabel) height = 16.0 left = COL2 top = ROW1 width = 18.0 fontBold = true text = HoursText pageNo = 2 endwith this.HOURSB = new SPINBOX(this) with (this.HOURSB) height = 22.0 left = COL2 top = ROW2 width = 47.0 speedTip = HoursSpeedTip picture = "99" function = "@L" rangeMax = 24 rangeMin = 1 value = 1 rangeRequired = true colorNormal = EFNormalColor colorHighlight = EFColorHighlight pageNo = 2 onChange = class::HoursSB_OnChange endwith this.MINUTESLabel = new TEXT(this) with (this.MINUTESLabel) height = 22.0 left = COL4 top = ROW1 width = 18.0 fontBold = true text = MinutesText pageNo = 2 endwith this.MINUTESSB = new SPINBOX(this) with (this.MINUTESSB) height = 22.0 left = COL4 top = ROW2 width = 47.0 speedTip = MinutesSpeedTip picture = "99" function = "@L" rangeMax = 59 rangeMin = 0 value = 1 rangeRequired = true colorNormal = EFNormalColor colorHighlight = EFColorHighlight pageNo = 2 onChange = class::MinutesSB_OnChange endwith this.SecondsLabel = new TEXT(this) with (this.SecondsLabel) height = 22.0 left = COL6 top = ROW1 width = 20.0 fontBold = true text = SecondsText pageNo = 2 endwith this.SECONDSSB = new SPINBOX(this) with (this.SECONDSSB) height = 22.0 left = COL6 top = ROW2 width = 47.0 speedTip = "Seconds" picture = "99" function = "@L" rangeMax = 59 rangeMin = 0 value = 1 rangeRequired = true colorNormal = EFNormalColor colorHighlight = EFColorHighlight pageNo = 2 onChange = class::SecondsSB_OnChange endwith function CheckMetric // called from container's onDesignOpen: if form.metric # 6 msgbox( "Form's metric property is not '6 - Pixels', "+; "this will not display properly.", ; "Bad Form Metric!", 16 ) endif return function containerOpen // if we have a custom property of "EF": if type( "this.EF" ) == "O" // object this.currentDate = this.EF.value endif // default date if needed: if type( 'this.currentDate' ) # "D" or empty( this.currentDate ) if this.type == "Date" this.currentDate = date() else // datetime this.currentDate = datetime() endif endif // Set color of "sunday" title button this.titleSunday.colorNormal := SundayColor // store starting date value, in // case there's a cancel option, // the user can return to the 'beginDate': this.beginDate = this.currentDate // update day-of-week headings class::DayHeadings() // if we are working with a datetime field: if this.type == "DateTime" this.TimeButton.visible := true endif // newMonth routine -- ensures all the // buttons are the 'correct' colors, and // speedTips added for holidays, etc. class::NewMonth() return function DayHeadings // deal with language stuff for the // day of the week: local cDay, dTemp // Set the language for the DateEx object: this.dateEx.Language := this.Language // we need a day known to be a Sunday: dTemp = new date( 2004, 1, 1 ) // February 1, 2004 // remember date object // is zero-based, hence // the second month of // the year is 1 // Get Sunday cDay = this.dateEx.intlcdow( dTemp ) // Assign first letter ...: this.TitleSunday.text := left( cDay, 1 ) // Get Monday cDay = this.dateEx.intlcdow( dTemp+1 ) // Assign first letter ...: this.TitleMonday.text := left( cDay, 1 ) // Get Tuesday cDay = this.dateEx.intlcdow( dTemp+2 ) // Assign first letter ...: this.TitleTuesday.text := left( cDay, 1 ) // Get Wednesday cDay = this.dateEx.intlcdow( dTemp+3 ) // Assign first letter ...: this.TitleWednesday.text := left( cDay, 1 ) // Get Thursday cDay = this.dateEx.intlcdow( dTemp+4 ) // Assign first letter ...: this.TitleThursday.text := left( cDay, 1 ) // Get Friday cDay = this.dateEx.intlcdow( dTemp+5 ) // Assign first letter ...: this.TitleFriday.text := left( cDay, 1 ) // Get Saturday cDay = this.dateEx.intlcdow( dTemp+6 ) // Assign first letter ...: this.TitleSaturday.text := left( cDay, 1 ) return function newMonth // this function deals with setting values based // on changes to month/year -- determine where // first day is, last day is, everything in // between: set button texts, colors ... // First: need to determine what level we're at -- // are we working from subform, or control // on subform (cRef needs to point to the subform): if "SUBFORM" $ upper( this.className ) or; "SUBFORM" $ upper( this.baseClassName ) // we're on the subform oRef = this else // we're on a pushbutton _on_ the subform oRef = this.parent endif // Make sure holiday list is current ... class::LoadHolidays() // now to get first day of month and last day of // month from 'currentDate' dDate = oRef.currentDate dFirst = oRef.dateEx.fdom( dDate ) dLast = oRef.dateEx.ldom( dDate ) nDay = day( dDate ) // current day of month bDateTime = ( oRef.type == "DateTime" ) if bDateTime // we need to deal with the time portion // of the value ... cTime = oRef.currentDate.hour+":"+; oRef.currentDate.minute+":"+; oRef.currentDate.second endif // character values for month and year: oRef.textMonth.text := oRef.dateEx.intlCMon( dDate ) oRef.textYear.text := ltrim( str( year( dDate ) ) ) // now the fun part ... assigning values, based // on where we start: nStart = dow( oRef.currentDate ) // Clear out any outstanding speedTips for nRows = 1 to 6 for nColumns = 1 to 7 cObject = "oRef.Row1Col"+nColumns cSpeedTip = cObject+".speedTip" &cSpeedTip. := "" next next // We need to loop through everything, and set // visible properties as well as text if it's going // to display: nCurrent = 0 for nRows = 1 to 6 for nColumns = 1 to 7 nCurrent++ // start at day 1, but ... // for first row, loop through until we find // the first one (and make everything before // the first day of month invisible): if nCurrent == 1 do while nCurrent <= nColumns if dow( dFirst ) == nColumns // set the text: cObject = "oRef.Row1Col"+nColumns cText = cObject+".text" &cText. := "1" // Set colors: cColor = cObject+".colorNormal" // if the day we're setting is the // selected/current date: dTestDate = new date(year(dDate), ; month(dDate)-1, nCurrent) // Make it a real date dTestDate = ctod( left( dTestDate.toLocaleString(), ; iif( set("CENTURY") == "ON", 10, 8 ) ) ) if nDay == nCurrent &cColor. := SelectedColor //oRef.SELECTEDCOLOR oRef.currentButton = &cObject. cSpeedTip = cObject+".speedTip" if class::isHoliday( dtestDate ) &cSpeedTip. = oRef.cSpeedTip endif else // is it a sunday or a holiday? cSpeedTip = cObject+".speedTip" if nColumns == 1 &cColor. := SundayColor //oRef.SUNDAYCOLOR if class::IsHoliday( dTestDate ) &cColor. := HolidayColor //oRef.HOLIDAYCOLOR &cSpeedTip. := oRef.cSpeedTip endif elseif class::IsHoliday( dTestDate ) &cColor. := HolidayColor //oRef.HOLIDAYCOLOR &cSpeedTip. := oRef.cSpeedTip else // it's any other day of week ... &cColor. := NormalColor //oRef.NORMALCOLOR endif endif // nDay == nCurrent // set visible property cVisible = cObject+".visible" &cVisible. := true // finish the row for i = nColumns to 7 cObject = "oRef.Row1Col"+i cText = cObject+".text" &cText. := "1" // set visible property cVisible = cObject+".visible" &cVisible. := true next exit // out of this loop, we're done else if nColumns > 7 nColumns = 7 exit endif cObject = "oRef.Row1Col"+nColumns cVisible = cObject+".visible" &cVisible. := false endif // dow( dFirst ) = nColumns nColumns++ // increment column counter loop // top of _this_ loop enddo // while nCurrent <= nColumns loop // back to columns for/next loop endif // nCurrent = 1 // now to attempt to handle setting // text and colors ... cObject = "oRef.Row"+nRows+"Col"+nColumns cSpeedTip = cObject+".speedTip" &cSpeedTip := "" // clear out whatever's there // if we're inside the month's dates: if nCurrent > 1 and nCurrent <= day( dLast ) // set the text property: cText = cObject+".text" &cText. := ""+nCurrent // set the button to visible: cVisible = cObject+".visible" &cVisible. := true // deal with colors: cColor = cObject+".colorNormal" // if the day we're setting is the // selected/current date: dTestDate = new date(year(dDate), ; month(dDate)-1, nCurrent) // Make it a real date dTestDate = ctod( left( dTestDate.toLocaleString(), ; iif( set("CENTURY") == "ON", 10, 8 ) ) ) if nDay == nCurrent &cColor. := SelectedColor oRef.currentButton = &cObject. cSpeedTip = cObject+".speedTip" if class::isHoliday( dtestDate ) &cSpeedTip. = oRef.cSpeedTip endif else // is it a sunday or a holiday? cSpeedTip = cObject+".speedTip" if nColumns == 1 &cColor. := SundayColor if class::IsHoliday( dTestDate ) &cColor. := HolidayColor &cSpeedTip. := oRef.cSpeedTip endif elseif class::IsHoliday( dTestDate ) &cColor. := HolidayColor &cSpeedTip. := oRef.cSpeedTip else // it's any other day of week ... &cColor. := NormalColor endif endif // nDay == nCurrent else // we want to make it invisible cVisible = cObject+".visible" &cVisible. := false endif // nCurrent > 1 and nCurrent <= day( dLast ) next // nColumns next // nRows // deal with DateTime: if oRef.type == "DateTime" cDate = dtoc( oRef.currentDate ) cDate += " " + cTime oRef.currentDate = ctodt( cDate ) endif // if it exists, refresh the entryfield: if type( "oRef.EF" ) == "O" // object oRef.EF.value = oRef.currentDate endif return function TodayPushButton_onClick // set today's date this.parent.currentDate = date() // refresh display class::NewMonth() // close the subform: this.parent.close() return function CancelButton_OnClick // reset date to the starting date: this.parent.currentDate = this.parent.beginDate // refresh display class::NewMonth() return function TimeButton_OnClick // need to change display of the subform to handle // time and morph the button back to point back to // the main calendar if this.parent.PageNo == 1 this.parent.PageNo := 2 // change pages // morph the button this.speedTip := TimeSpeedTip2 this.upBitMap := TimeBackImg // break up values for H/M/S and place in // controls. Need to get value from // this.parent.currentDate: this.parent.HourSB.value := this.parent.currentDate.hour this.parent.MinutesSB.value := this.parent.currentDate.minute this.parent.SecondsSB.value := this.parent.currentDate.second // set focus to Hours spinbox: this.parent.HourSB.setFocus() else this.parent.PageNo := 1 // morph the button: this.speedTip := TimeSpeedTip1 this.upBitMap := TimeButtonImg endif return function NEXTYEARPUSHBUTTON_onClick // code to add year to date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // increment year: this.parent.currentDate = ; this.parent.dateEx.addYears( this.parent.currentDate, 1 ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the whole thing class::NewMonth() return function YEARPLUS10_onClick // code to add 10 years to date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // increment year 'n' years: this.parent.currentDate = ; this.parent.dateEx.addYears( this.parent.currentDate, YearIncrement ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function PREVYEARPUSHBUTTON_onClick // code to subtract year from date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // decrement year this.parent.currentDate = ; this.parent.dateEx.addYears( this.parent.currentDate, -1 ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function YEARMINUS10_onClick // code to subtract 10 years from date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // decrement 'n' years this.parent.currentDate = ; this.parent.dateEx.addYears( this.parent.currentDate, -YearIncrement ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function NEXTMONTHPUSHBUTTON_onClick // code to add month to date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // increment month this.parent.currentDate = ; this.parent.dateEx.addMonths( this.parent.currentDate, 1 ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function MONTHPLUS3_onClick // code to add 3 months to date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // increment 'n' months this.parent.currentDate = ; this.parent.dateEx.addMonths( this.parent.currentDate, MonthIncrement ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function PREVMONTHPUSHBUTTON_onClick // code to subtract month from date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // decrement month this.parent.currentDate = ; this.parent.dateEx.addMonths( this.parent.currentDate, -1 ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function MONTHMINUS3_onClick // code to subtract 3 months from date, // and call newMonth routine // if dateTime we need to catch the time and add it back in if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif // decrement 'n' months this.parent.currentDate = ; this.parent.dateEx.addMonths( this.parent.currentDate, -MonthIncrement ) // if needed put the time back in if this.parent.type == "DateTime" this.parent.currentDate := ; ctodt( dtoc( this.parent.currentDate ) + " " + cTime ) endif // update the calendar class::NewMonth() return function HoursSB_OnChange // from the HoursSB (Spinbox), change the hours for the current date: oRef = this.parent // build datetime string: cDTString = oRef.currentDate.month+; "/"+; oRef.currentDate.date+; "/"+; oRef.currentDate.year+; " "+; this.value+; ":"+; oRef.currentDate.minute+; ":"+; oRef.currentDate.second // call method to update: class::UpdateTime( cDTString ) return function MinutesSB_OnChange // from the MinutesSB (Spinbox), change the minutes for the current date: oRef = this.parent // build datetime string: cDTString = oRef.currentDate.month+; "/"+; oRef.currentDate.date+; "/"+; oRef.currentDate.year+; " "+; oRef.currentDate.hour+; ":"+; this.value+; ":"+; oRef.currentDate.second // call method to update: class::UpdateTime( cDTString ) return function SecondsSB_OnChange // from the SecondsSB (Spinbox), change the seconds for the current date: oRef = this.parent // build datetime string: cDTString = oRef.currentDate.month+; "/"+; oRef.currentDate.date+; "/"+; oRef.currentDate.year+; " "+; oRef.currentDate.hour+; ":"+; oRef.currentDate.minute+; ":"+; this.value // call method to update: class::UpdateTime( cDTString ) return procedure UpdateTime( cDTString ) // called from HoursSP, MinutesSB or SecondsSB oRef = this.parent // save old date setting in case not MDY: cOldDate = set( "date" ) // set to MDY format: set date MDY // assign and convert string to currentDate property: oRef.currentDate = ctodt( cDTString ) // reset date format set date &cOldDate. // update the entryfield: oRef.EF.value := oRef.currentDate return procedure LoadHolidays // Creates/re-creates array used to determine // a holiday ... NOTE: It adds an array // to the form ... local nYear, oRef, nHoliday, d local cMarkSet private cCmd, cDateSet // Have to determine if being called from container // or from a button call ... if "CALENDAR" $ this.className nYear = year( this.currentDate ) oRef = this else nYear = year( this.parent.currentDate ) oRef = this.parent endif // Create array oRef.Holidays = new Array(1,2) // 1 row, 2 columns // Load holidays using holidaydate method below: // floating holidays (most of these are US Holidays -- // ********************************************* // you may wish to modify if not in the US ...): // ********************************************* // President's Day i = 0 i++ oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "P" ) oRef.Holidays[i,2] = "President's Day" // Daylight Savings time -- always a Sunday // Memorial Day i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "M" ) oRef.Holidays[i,2] = "Memorial Day" // Labor Day i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "L" ) oRef.Holidays[i,2] = "Labor Day" // Columbus Day i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "C" ) oRef.Holidays[i,2] = "Columbus Day" // Standard Time -- always a Sunday i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "S" ) oRef.Holidays[i,2] = "Standard Time" // Daylight Savings Time i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "D" ) oRef.Holidays[i,2] = "Daylight Savings Time" // Election Day i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "E" ) oRef.Holidays[i,2] = "Election Day" // Thanksgiving i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "T" ) oRef.Holidays[i,2] = "Thanksgiving" // Advent -- always a Sunday i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.HolidayDate( nYear, "A" ) oRef.Holidays[i,2] = "Advent" // Easter requires special code ...: i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = oRef.Holiday.EasterDay( nYear ) oRef.Holidays[i,2] = "Easter" // fixed dates: // deal with date format, so we don't // mess up international users ... cDateSet = set("DATE") cMarkSet = set("MARK") set date MDY // New Year's Day d = ctod( "01/01/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "New Year's Day" // Lincoln's Birthday d = ctod( "02/12/"+nYear) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Lincoln's Birthday" // Valentine's Day d = ctod( "02/14/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Valentine's Day" // Washington's Birthday d = ctod( "02/22/"+nYear) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Washington's Birthday" // St. Patrick's Day d = ctod( "03/17/"+nYear) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "St. Patrick's Day" // April Fool's Day d = ctod( "04/01/"+nYear) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "April Fool's Day" // Fourth of July d = ctod( "07/04/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Independance Day" // Halloween (All Hallows Eve) d = ctod( "10/31/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Halloween" // Christmas d = ctod( "12/25/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "Christmas" // New Year's Eve d = ctod( "12/31/"+nYear ) i++ oRef.Holidays.grow(1) oRef.Holidays[i,1] = d oRef.Holidays[i,2] = "New Year's Eve" // You can add more here if you wish ... // make sure you add a row to the array, // and add the name of the holiday or memorable // date ... Basically, copy the code as shown // above, and make appropriate changes // reset date format to whatever it was // before we changed to American set date &cDateSet. cCmd = "set mark to '"+cMarkSet+"'" &cCmd. // one last thing: oRef.Holidays.sort(1) // sort on first column return Function IsHoliday parameter dCheckIt // this routine reads the array "this.Holidays" // and checks the date to see if the date in // dCheck parameter is in the list, and returns // a logical value ... if "CALENDAR" $ upper( this.className ) oRef = this else oRef = this.parent endif lHoliday = false oRef.cSpeedTip = "" for i = 1 to oRef.Holidays.size/2 if oRef.Holidays[ i, 1 ] == dCheckIt lHoliday := true oRef.cSpeedTip = oRef.Holidays[ i, 2 ] endif next return ( lHoliday ) endclass // end of class pKenCalendarContainer /* ---------------------------------------------------------- Pushbutton class definitions CalPushButton -- default button definition used for all buttons on form DayPushButton -- used to display the days on the calendar ---------------------------------------------------------- */ class CalPushbutton( oParent ) of Pushbutton( oParent ) this.fontName := ButtonFontName this.speedBar := true // so they don't keep focus #if __version__ < 11 this.systemTheme := false #else // dBASE Plus 11 new property, // allows us to keep general pushbutton // appearance of theme, but change // the color of the button: this.themeBackground := false #endif this.visible := true // unless turned off this.metric := 6 // Pixels endclass class DayPushButton( oParent ) of CalPushbutton( oParent ) with (this) fontSize := ButtonFontSize height := ButtonHeight width := ButtonWidth text := "30" // set to this for layout purposes visible := false pageNo := 1 endwith function onClick // deal with whether or not this is date or dateTime if this.parent.type == "DateTime" cTime = this.parent.currentDate.hour+":"+; this.parent.currentDate.minute+":"+; this.parent.currentDate.second endif /* The tricky part of this little routine is resetting colors -- in order to do that, we need to store a reference to the 'current' button, so we can reset that, before we change the now current button's color ... */ dCurrentDate = new date(year(this.parent.currentDate), ; month(this.parent.currentDate)-1, ; VAL(this.parent.currentButton.text)) dCurrentDate = ctod( left( dCurrentDate.toLocaleString(), ; iif( set("CENTURY") == "ON", 10, 8 ) ) ) if type( "this.parent.currentbutton" ) # "U" // if it's in Column 1, it's a "sunday" ... if "COL1" $ this.parent.currentButton.Name this.parent.currentButton.colorNormal := ; SundayColor // this.parent.SUNDAYCOLOR // set holiday color if needed: elseif this.parent.isHoliday( dCurrentDate ) this.parent.currentButton.colorNormal := ; HolidayColor // this.parent.HOLIDAYCOLOR // otherwise, we have a "normal" day ... else this.parent.currentButton.colorNormal := ; NormalColor // this.parent.NORMALCOLOR endif endif // Assign currentButton property of container this.parent.currentButton = this // Assign color ... this.colorNormal := SelectedColor // this.parent.SELECTEDCOLOR // set current date to this one: this.parent.currentDate := ; new date( year(this.parent.currentDate), ; month(this.parent.currentDate)-1, ; VAL(this.text) ) this.parent.currentDate := ; ctod( left( this.parent.currentDate.toLocaleString(), ; iif( set("CENTURY") == "ON", 10, 8 ) ) ) // and we need the darn time ... if this.parent.type == "DateTime" cDate = this.parent.currentDate + " " + cTime this.parent.currentDate = ctoDT( cDate ) endif // because of adding the onMouseOver event, we // are having an issue here ... sequence // of event processing is throwing things // off (which makes sense, moving the mouse over // the control would happen before the onClick // event): this.oldColorNormal := this.colorNormal // deal with entryfield update: if type( "this.parent.EF" ) == "O" // object this.parent.EF.value := this.parent.currentDate endif // close the subform: this.parent.close() return function onMouseOver // save current color setting this.oldColorNormal = this.colorNormal // make it readable ...: this.colorNormal := MouseOverColor //this.parent.MouseOverColor return function onMouseOut this.colorNormal := this.oldColorNormal // revert to previous setting return endclass // custom class the "title" text so we have something nearly // identical to the pushbuttons used for the days for the titles class WeekDayText( oParent ) of Text( oParent ) with (this) fontName := ButtonFontName fontSize := ButtonFontSize height := ButtonHeight width := ButtonWidth metric := 6 // Pixels text := "S" border := true #if __version__ < 11 borderStyle := 1 // raised #else borderStyle := 9 // etched in #endif alignHorizontal := 1 // center alignVertical := 1 // middle colorNormal := normalColor pageNo := 1 endwith endclass // for conversion from fontSize (points) to Pixels (form metric): function Points2Pixels( nPoints ) return round( nPoints * 1.3333333333333333, 0 ) /* END OF FILE -- kmDateWithCal.cc */