/* Class Calendar  	A custom dBASE Calendar Component Class
			Purpose:		Get and set dates on visual calendar control
         Author:		A. A. Katz
         Date:			03-18-1997, 04-01-1997
         Copyright:	A. A. Katz. All rights reserved. However, this class
         				may be used by developers in their applications. They
                     may modify source code. They may NOT resell this
                     custom class as a component or class.

 			Usage:		Register this class in dBASE File/Set up Custom Controls
         				Drop on form as needed

	  		Main Methods: 	setDate(dDate) sets new calendar default date
         					GetDate()  returns the selected date on the calendar
         Event:			OnDateClick. Takes a function pointer, which is executed
         					when any valid date button is clicked.
         Main Property: Date (this.date). Contains the currently selected date.
         					If no date is sent on instantiation, today's date is
                        used

         Upgraded			01/28/2000    AAK  For Improved interprocess 
         					messaging

         Added          Support for an"owner object" with callback if
         					it has a setDate method

                        Support for automatic field updates if this.currentfield
                        object has a date-type value property
                        
         Fixed 			11/12/2002  Uses date class for generating date values
		 					   (instead of ctod).  Now works regardless of MDY,YMD, or DMY setting.

*/


Class Calendar(oF) of Container(oF) Custom
   with (this)
      onOpen = class::calendar_OnOpen
      left = 32
      top = 67
      width = 176
      height = 158
      OnDateClick = null // takes a function pointer,
                         // fires when date button clicked
   endwith

   this.aCurrentOwners = new array() // init exclusive owner
   this.aCurrentFields = new array() // update any number of fields

   this.TEXT2 = new TEXT(this)
   with (this.TEXT2)
      height = 14
      left = 7
      top = 24
      width = 157
      fontName = 'Arial'
      fontSize = 8
      text = "  S     M     T     W     T      F     S"
      pageno = 0
   endwith


   this.PUSHBUTTON1 = new CalButton(this)
   with (this.PUSHBUTTON1)
      left = 6
      top = 40
   endwith


   this.PUSHBUTTON2 = new CalButton(this)
   with (this.PUSHBUTTON2)
      left = 29
      top = 40
   endwith


   this.PUSHBUTTON3 = new CalButton(this)
   with (this.PUSHBUTTON3)
      left = 52
      top = 40
   endwith


   this.PUSHBUTTON4 = new CalButton(this)
   with (this.PUSHBUTTON4)
      left = 75
      top = 40
   endwith


   this.PUSHBUTTON5 = new CalButton(this)
   with (this.PUSHBUTTON5)
      left = 98
      top = 40
   endwith


   this.PUSHBUTTON6 = new CalButton(this)
   with (this.PUSHBUTTON6)
      left = 121
      top = 40
   endwith


   this.PUSHBUTTON7 = new CalButton(this)
   with (this.PUSHBUTTON7)
      left = 144
      top = 40
   endwith


   this.PUSHBUTTON8 = new CalButton(this)
   with (this.PUSHBUTTON8)
      left = 6
      top = 58
   endwith


   this.PUSHBUTTON9 = new CalButton(this)
   with (this.PUSHBUTTON9)
      left = 29
      top = 58
   endwith


   this.PUSHBUTTON10 = new CalButton(this)
   with (this.PUSHBUTTON10)
      left = 52
      top = 58
   endwith


   this.PUSHBUTTON11 = new CalButton(this)
   with (this.PUSHBUTTON11)
      left = 75
      top = 58
   endwith


   this.PUSHBUTTON12 = new CalButton(this)
   with (this.PUSHBUTTON12)
      left = 98
      top = 58
   endwith


   this.PUSHBUTTON13 = new CalButton(this)
   with (this.PUSHBUTTON13)
      left = 121
      top = 58
   endwith


   this.PUSHBUTTON14 = new CalButton(this)
   with (this.PUSHBUTTON14)
      left = 144
      top = 58
   endwith


   this.PUSHBUTTON15 = new CalButton(this)
   with (this.PUSHBUTTON15)
      left = 6
      top = 76
   endwith


   this.PUSHBUTTON16 = new CalButton(this)
   with (this.PUSHBUTTON16)
      left = 29
      top = 76
   endwith


   this.PUSHBUTTON17 = new CalButton(this)
   with (this.PUSHBUTTON17)
      left = 52
      top = 76
   endwith


   this.PUSHBUTTON18 = new CalButton(this)
   with (this.PUSHBUTTON18)
      left = 75
      top = 76
   endwith


   this.PUSHBUTTON19 = new CalButton(this)
   with (this.PUSHBUTTON19)
      left = 98
      top = 76

   endwith


   this.PUSHBUTTON20 = new CalButton(this)
   with (this.PUSHBUTTON20)
      left = 121
      top = 76
   endwith


   this.PUSHBUTTON21 = new CalButton(this)
   with (this.PUSHBUTTON21)
      left = 144
      top = 76
   endwith


   this.PUSHBUTTON22 = new CalButton(this)
   with (this.PUSHBUTTON22)
      left = 6
      top = 94
   endwith


   this.PUSHBUTTON23 = new CalButton(this)
   with (this.PUSHBUTTON23)
      left = 29
      top = 94
   endwith


   this.PUSHBUTTON24 = new CalButton(this)
   with (this.PUSHBUTTON24)
      left = 52
      top = 94
   endwith


   this.PUSHBUTTON25 = new CalButton(this)
   with (this.PUSHBUTTON25)
      left = 75
      top = 94
   endwith


   this.PUSHBUTTON26 = new CalButton(this)
   with (this.PUSHBUTTON26)
      left = 98
      top = 94
   endwith


   this.PUSHBUTTON27 = new CalButton(this)
   with (this.PUSHBUTTON27)
      left = 121
      top = 94
   endwith


   this.PUSHBUTTON28 = new CalButton(this)
   with (this.PUSHBUTTON28)
      left = 144
      top = 94
   endwith


   this.PUSHBUTTON29 = new CalButton(this)
   with (this.PUSHBUTTON29)
      left = 6
      top = 112
   endwith


   this.PUSHBUTTON30 = new CalButton(this)
   with (this.PUSHBUTTON30)
      left = 29
      top = 112
   endwith


   this.PUSHBUTTON31 = new CalButton(this)
   with (this.PUSHBUTTON31)
      left = 52
      top = 112
   endwith


   this.PUSHBUTTON32 = new CalButton(this)
   with (this.PUSHBUTTON32)
      left = 75
      top = 112
   endwith


   this.PUSHBUTTON33 = new CalButton(this)
   with (this.PUSHBUTTON33)
      left = 98
      top = 112
   endwith


   this.PUSHBUTTON34 = new CalButton(this)
   with (this.PUSHBUTTON34)
      left = 121
      top = 112
   endwith


   this.PUSHBUTTON35 = new CalButton(this)
   with (this.PUSHBUTTON35)
      left = 144
      top = 112
   endwith


   this.PUSHBUTTON36 = new CalButton(this)
   with (this.PUSHBUTTON36)
      left = 6
      top = 130
   endwith


   this.PUSHBUTTON37 = new CalButton(this)
   with (this.PUSHBUTTON37)
      left = 29
      top = 130
   endwith


   this.MONTHDISPLAY = new TEXT(this)
   with (this.MONTHDISPLAY)
      height = 17
      left = 45
      top = 4
      width = 82
      border = true
      colorNormal = "0x808040/BtnFace"
      alignHorizontal = 1	// Center
      fontSize = 9
      text = "November"
      borderStyle = 1	// Raised
      pageno = 0
   endwith


   this.YEARDISPLAY = new TEXT(this)
   with (this.YEARDISPLAY)
      height = 17
      left = 86
      top = 130
      width = 45
      colorNormal = "0x808040/BtnFace"
      alignHorizontal = 1	// Center
      fontSize = 9
      text = "1998"
      borderStyle = 1	// Raised
      pageno = 0
   endwith


   this.MONTHUPBUTTON = new PUSHBUTTON(this)
   with (this.MONTHUPBUTTON)
      onClick = class::MONTHUPBUTTON_ONCLICK
      height = 17
      left = 131
      top = 4
      width = 34
      text = ">>"
      colorNormal = "0x808040/BtnFace"
      pageno = 1
      value = false
   endwith


   this.MONTHDOWNBUTTON = new PUSHBUTTON(this)
   with (this.MONTHDOWNBUTTON)
      onClick = class::MONTHDOWNBUTTON_ONCLICK
      height = 17
      left = 6
      top = 4
      width = 35
      text = "<<"
      colorNormal = "0x808040/BtnFace"
      pageno = 1
      value = false
   endwith


   this.YEARUPBUTTON = new PUSHBUTTON(this)
   with (this.YEARUPBUTTON)
      onClick = class::YEARUPBUTTON_ONCLICK
      height = 17
      left = 134
      top = 130
      width = 31
      text = ">>"
      colorNormal = "0x808040/BtnFace"
      pageno = 1
      value = false
   endwith


   this.YEARDOWNBUTTON = new PUSHBUTTON(this)
   with (this.YEARDOWNBUTTON)
      onClick = class::YEARDOWNBUTTON_ONCLICK
      height = 17
      left = 52
      top = 130
      width = 31
      text = "<<"
      colorNormal = "0x808040/BtnFace"
      pageno = 1
      value = false
   endwith

   ////// Method setDate() ////////////////////////////////////
   ////// Purpose: Only entry point to set default/current selected
   //////				Date. Designed for Internal/External calls

   function setDate(dDate,cRepaint)
      // set defaults for missing params
      dDate = iif(empty(dDate),date(),dDate)
      cRepaint = iif(empty(cRepaint),'Y',cRepaint)

      this.Today = Date()               	// Today's date
      this.date = dDate                   // Selected date
      /*
      this.CalDate  = ctod( str(month(this.date),2)+;
         '/01/'+str(year(this.date),4)) 	// Used internally to
                                          // navigate calendar
      fixed by Rick as follows */
      this.CalDate  = new Date( year(this.date), month(this.date)-1, 1 ) 
    	// Used internally to navigate calendar
      if cRepaint = 'Y'
         this.DrawMonth()	              		// Draw calendar
      endif
     
   ////// Method GetDate() ////////////////////////////////////////
   ////// Purpose: returns calendar's selected date
   function GetDate()
   return this.date

  
   ////// Container's OnOpen ////////////////////////////////
   ////// Purpose: Ensures default date is set
   function calendar_OnOpen
      
      if type('this.CalDate') # 'D'
         this.CalDate = new date()
      endif
      this.setDate(Date())      
      return

   ////// DrawMonth()   /////////////////////////////////
   ////// Purpose: Draws the selected month on //////////////
   ////// the calendar. /////////////////////////////////////
   ////// Param: this = reference to container. Make sure that
   //////   		all calls to this method or to setDate() are
   //////			scoped to the container
   function drawMonth
      this.monthdisplay.text = cMonth(this.CalDate)
      this.YearDisplay.text =  str(year(this.CalDate),4)

            ////// Get the last day of the month
      cMonth = str(month(this.CalDate),2)
 
      if at(cMonth,' 9 4 611') > 0      // if 30 day month
         nLastDay = 30
      elseif cMonth = ' 2'              // if February, do leap year
         nLastDay = iif(this.CalDate.Year%4 = 0, 29,28)
      else
         nLastDay = 31                    // Otherwise, 31
      endif
       

      for n = 1 to dow(this.CalDate)        // draw empty starting days
         cN = trim(ltrim(str(n,2)))
         cColor   = 'this.pushbutton'+cN+'.Colornormal'
         &cColor  = 'black/silver'
         cText  = 'this.pushbutton'+cN+'.text'
         &cText = ''
         cEnabled = ' this.pushbutton'+cN+'.enabled'
         &cEnabled = false
      next

      nDay = 1

      for n = dow(this.CalDate) to 37     // draw days of the month
         cN = trim(ltrim(str(n,2)))
         cColor   = 'this.pushbutton'+cN+'.Colornormal'
         &cColor  = 'black/silver'
         cText    = 'this.pushbutton'+cN+'.text'
         cEnabled = ' this.pushbutton'+cN+'.enabled'
         if nDay > nLastDay
           &cText. = ' '
           &cEnabled = false
         else
           &cText. = str(nDay,2)
           &cEnabled = true
         endif
         nDay ++
      endfor

      class::highlightToday()
     
    
      							// If selected date falls on this calendar page
      							// change color and redisplay

      if month(this.CalDate) = month(this.date) and ;
         year(this.CalDate) = year(this.Date)
         n = dow(this.CalDate)+day(this.Date)-1
         cButton = 'this.pushbutton'+ltrim(str(n,2))+'.colornormal'
         &cButton = 'W+/0x808040'
      endif


   ////// Monthupbutton_OnClick() /////////////////////////////////
   ////// Increments calendar months //////////////////////////////
   function monthUpButton_onClick
      nNewMonth = month(this.parent.CalDate)
      nNewMonth ++
      
      if nNewMonth > 12
         nNewMonth = 1
         nNewYear = year(this.parent.CalDate)+1
      else
         nNewYear = year(this.parent.CalDate)
      endif

      this.parent.date = new Date( nNewYear, nNewMonth-1, day(this.parent.date) )
      this.parent.CalDate = new Date( nNewYear, nNewMonth-1, 1 )

      this.parent.drawmonth()

      return

   ////// MonthDownbutton_OnClick() /////////////////////////////////
   ////// Decrements calendar months //////////////////////////////
   function monthDownButton_onClick
      nNewMonth = month(this.parent.CalDate)
      nNewMonth --
      
      if nNewMonth < 1
          nNewMonth = 12
          nNewYear = year(this.parent.CalDate)-1
      else
          nNewYear = year(this.parent.CalDate)
      endif

      this.parent.date = new Date( nNewYear, nNewMonth-1, day(this.parent.date) )
      this.parent.CalDate = new Date( nNewYear, nNewMonth-1, 1 )

      this.parent.drawmonth()

      return

   ////// YearUpbutton_OnClick() /////////////////////////////////
   ////// Increments calendar yearss //////////////////////////////
   function yearUpBUTTON_onClick
      dDate = this.parent.Date

      this.parent.date    =  new Date( year(dDate)+1,  month(dDate)-1, day(this.Parent.Date) )
      this.parent.calDate = new Date( year(dDate)+1,  month(dDate)-1, 1 ) 
      this.parent.drawmonth() 
      return

   ////// YearDownbutton_OnClick() /////////////////////////////////
   ////// Decrements calendar Year //////////////////////////////
   function yearDownButton_OnClick
      dDate = this.parent.Date

      this.parent.date    = new Date( year(dDate)-1,  month(dDate)-1, day(this.Parent.Date) )
      this.parent.calDate = new Date( year(dDate)-1,  month(dDate)-1, 1 )
      this.parent.drawmonth()
      return

   ////// HighlightToday() ////////////////////////////////////////
   ////// Marks "today" button in red/silver //////////////////////
   function highlightToday()
     
      if month(this.Date) = month(date()) and ;
         year(this.Date) = year(date())
         n = dow(this.CalDate)+day(Date())-1
         cButton = 'this.pushbutton'+ltrim(str(n,2))+'.colornormal'
         &cButton = 'Red/Silver'
      endif

   ////// Register external owner objects - called externally
   ////// bExclusive kills off all other owners but this one
   ////// bExclusive By Class means that only One pushbutton or
   ////// one container of the same class may be registered at 
   ////// a time
   function RegisterOwners(oOwner,bExclusiveByClass)      
      private cClassName  // init var for exclusive

      bExclusiveByClass = iif(empty(bExclusiveByClass),false,bExclusiveByClass)

      ////// Go through array over and over until you remove all
      ////// of a matching class to the one passed in oOwnerRef
      if bExclusiveByClass
 
         for n = 1 to this.aCurrentOwners.size

            if type('this.aCurrentOwners[n]') = 'O' and ;
               this.aCurrentOwners[n].className = oOwner.ClassName
               this.aCurrentOwners.delete(n)
            endif

         next

      endif

      // Add new owner
      this.aCurrentOwners.add(oOwner)
      
      return true

   ////// Register external owner objects - called externally
   function RegisterFields(oFieldRef)

      this.aCurrentFields.add(oFieldRef)
      return true
      
   ////// Method:		Unregister ////////////////////////////////////////////
   ////// Purpose:	Removes all external references ///////////////////////

   function unRegister(oObj)

     
      for n = 1 to this.aCurrentOwners.size
         if empty(oObj) or (this.aCurrentOwners[n] = oObj)
            this.aCurrentOwners[n] = null
         endif
      next

      for n = 1 to this.aCurrentFields.size
         if empty(oObj) or (this.aCurrentOwners[n] = oObj)
            this.aCurrentFields[n] = null
         endif
      next


   ////// Method:     Enable()////////////////////////////////////
   ////// Purpose:    turns pusbuttons on or off  ////////////////
   ////// Call			 from container only ////////////////////////

   function enable(bEnable)
        
      oFirst = this.first
      oCurrent = oFirst

      do
         ////// see if it has an enabled property
         if upper(oCurrent.className) = 'CALBUTTON'
            oCurrent.enabled = bEnable
         endif


         oCurrent = oCurrent.before

      until oCurrent = oFirst
      return true 
                        
endclass  // end of class::Calendar

////// Calbutton: Custom pushbutton class ////////////////////////
////// Purpose: contains method that fires when date is selected
Class CalButton(formObj) of Pushbutton(formObj) Custom 
   with (this)
      height = 17
      left = 29
      onclick = class::OnClick
      top = 130
      width = 21
      text = " "
      fontSize = 9
      pageno = 1
   endwith

   ////// Method: Onclick ///////////////////////////////////////
   ////// Purpose: Resets date when button pushed
   function OnClick
      if empty(this.text)
         return null
      endif

      // build a date from this button text and 
      // current month/year as displayed
      cDispMonth = this.parent.monthdisplay.text
      nPos   = 0
      do 
         nPos++
      until cDispMonth = cMonth( new Date( year(date()), nPos-1, 1) )
      // turn off this color if it shows onscreen

      this.parent.setDate( ( new Date( val(this.parent.yeardisplay.text),;
                           nPos-1, val(this.text))), 'N' )

      // check all the buttons to see if any are highlighted as
      // "selected" date. If so, ditch the highlight
      oStart = this.parent.pushbutton1
      oCurrent = oStart
      do
        if upper(oCurrent.classname) = 'CALBUTTON'
           if oCurrent.colornormal = 'W+/0x808040'
              oCurrent.colornormal = 'black/silver'
           endif
        endif
        oCurrent = oCurrent.before  
      until oCurrent = oStart

      // highlight "today" if appropriate
      this.parent.highlightToday()

      // highlight this button as current date
      this.colornormal = 'W+/0x808040'

      ////// This class now supports the following:
      ////// 1) a passed function pointer for callback
      ////// 2) a Current "owner" object for callback
      ////// 3) a Current "field" for auto update

      ////// current owners callback
      ////// requires that it also have a "setdate" method
      private n
     
      for n =1 to this.parent.aCurrentOwners.size
         if type('this.parent.aCurrentOwners[n]') = 'O' and ;
            type('this.parent.aCurrentOwners[n].setDate') = 'FP' // if setdate exists
            this.parent.aCurrentOwners[n].setDate(this.parent.Date)
         endif
      next

      ///// current field
      for n = 1 to this.parent.aCurrentFields.size
         if type('this.parent.aCurrentFields[n].value') = 'D' // if it's a date field
            this.parent.aCurrentFields[n].value = this.parent.Date
         endif
      next

      return null

  
Endclass
