INCLUDE SYSTEM "ixapp.4gh" INCLUDE SYSTEM "ixsuptbl.4gh" INCLUDE SYSTEM "ixsupfld.4gh" INCLUDE SYSTEM "ixdate.4gh" INCLUDE SYSTEM "ix4gl.4gh" INCLUDE "pickdate.4gh" VARIABLE origDate DATE = TODAY VARIABLE showDATE DATE = NULL VARIABLE rtrnDate DATE = NULL VARIABLE mPickDateWN pickDateWCL VARIABLE mDateFR ixFrame VARIABLE mMonthLB ixListBox VARIABLE mYearLB ixListBox FUNCTION pickDateWCL::getDate() RETURNING DATE RETURN rtrnDate END FUNCTION # pickDateWCL::getDate() FUNCTION pickDateWCL::setLocation(sourceWN ixWindow, topAnchor SMALLINT, leftAnchor SMALLINT) RETURNING VOID VARIABLE lWNWide INTEGER VARIABLE lWNTall INTEGER VARIABLE lTop INTEGER VARIABLE lLeft INTEGER # get the size of the containing Window LET lWNTall = sourceWN.getHeight() LET lWNWide = sourceWN.getWidth() # get the size of the pickDateWN LET lTop = mpickDateWN.getHeight() LET lLeft = mpickDateWN.getWidth() # if the window will fit from the topAnchor down, set it there IF topAnchor + lTop <= lWNTall THEN LET lTop = topAnchor ELSE # cast up from the top LET lTop = topAnchor - lTop IF lTop < 0 THEN LET lTop = 0 END IF END IF # Same song 2nd verse for width IF leftAnchor + lLeft <= lWNWide THEN LET lLeft = leftAnchor ELSE # cast over from the left LET lLeft = leftAnchor - lLeft IF lLeft < 0 THEN LET lLeft = 0 END IF END IF CALL mPickDateWN.setAnchor(lTop, lLeft) END FUNCTION # setLocation FUNCTION fillYearLB(lYear CHAR(*)) VARIABLE lYearTxt CHAR(*) VARIABLE lYearCt SMALLINT VARIABLE lLBCt SMALLINT VARIABLE ct SMALLINT # see if the year is in the current list LET lLBCt = mYearLB.getItemNumByTitle(lYearTxt) # if it's not there, # or within the first 5 or last five items of the list box, # refill it. LET ct = mYearLB.getNumItems() IF lLBCt IS NULL OR lLBCt <= 5 OR lLBCt >= (ct - 5) THEN # clear the current list FOR ct = mYearLB.getNumItems() TO 1 STEP -1 LET lYearTxt = mYearLB.delete(ct) END FOR LET lYearCt = lYear LET lYearCt = lYearCt - 25 # fill the list 25 years before the year and 25 years after FOR ct = 1 TO 51 LET lYearTxt = lYearCt USING "####" LET lLBCt = mYearLB.insert(itemVal: NEW ixString(lYearTxt), itemNum: ct) LET lYearCt = lYearCt + 1 END FOR END IF # lLBCt IS NULL OR lLBCt <= 5 OR lLBCt >= (ct - 5) # select the passed year LET lYearTxt = lYear USING "####" CALL mYearLB.selectItem(mYearLB.getItemNumByTitle(lYearTxt)) END FUNCTION # fillYearLB(lYear CHAR(*)) FUNCTION fillDateFR(pDate DATE) VARIABLE lDate DATE VARIABLE lCol SMALLINT VARIABLE lRow SMALLINT VARIABLE bnName CHAR(*) VARIABLE lxBN ixButton VARIABLE lTxt CHAR(*) IF showDate IS NULL OR MONTH(showDate) != MONTH(pDate) OR YEAR(showDate) !=YEAR(pDate) THEN IF ixApp::setCursor (ixApp::busyCur) THEN END IF LET showDate = pDate # put up the right Month CALL mMonthLB.selectItem(MONTH(pDate)) # fill the Year ListBox LET lTxt = YEAR(pDate) USING "####" CALL fillYearLB(lTxt) # set lDate to the first day of the origDate Month LET lDate = MDY(MONTH(pDate), 1, YEAR(pDate)) # walk back to Sunday WHILE WEEKDAY(lDate) != 0 LET lDate = lDate - 1 UNITS DAY END WHILE # now fill in the dates on each button FOR lRow = 1 TO 6 FOR lCol = 1 TO 7 LET bnName = "date", lCol USING "<", lRow USING "<", "BN" LET lxBN = mDateFR.getContainedObjByName(bnName) LET lTxt = DAY(lDate) USING "<<" CALL lxBN.setTitle(lTxt) # set font size and bold according to month: # this month: 12 pt Bold # today = Underline # not this month: 10 pt Not Bold # default is 12 pt Bold IF MONTH(lDate) = MONTH(pDate) THEN CALL lxBN.setFont(fontName: NULL, fontSize: 12, bold: TRUE, italic: NULL, underline: FALSE) ELSE CALL lxBN.setFont(fontName: NULL, fontSize: 10, bold: FALSE, italic: NULL, underline: FALSE) END IF LET lDate = lDate + 1 UNITS DAY END FOR # lCol = 1 TO 7 END FOR # lRow = 1 TO 6 IF ixApp::setCursor (ixApp::standardCur) THEN END IF END IF # showDate IS NULL END FUNCTION # fillDateFR(pDate DATE) FUNCTION dateTimeLength(myCOL ixColumn) RETURNING SMALLINT # take the syscolumns.collength of the DATETIME YEAR TO ???? field and # figure out the ???? part so we can return the length in characters the # datetime field is VARIABLE dtmLength SMALLINT LET dtmLength = myCOL.getLength() # Determine the string length the brutal way; # I created a table with a DATETIME of every scale and looked at the collength CASE WHEN dtmLength = 1024 # YEAR TO YEAR LET dtmLength = 4 WHEN dtmLength = 1538 # YEAR TO MONTH LET dtmLength = 7 WHEN dtmLength = 2052 # YEAR TO DAY LET dtmLength = 10 WHEN dtmLength = 2566 # YEAR TO HOUR LET dtmLength = 13 WHEN dtmLength = 3080 # YEAR TO MINUTE LET dtmLength = 16 WHEN dtmLength = 3594 # YEAR TO SECOND LET dtmLength = 19 WHEN dtmLength = 3851 # YEAR TO FRACTION(1) LET dtmLength = 21 WHEN dtmLength = 4108 # YEAR TO FRACTION(2) LET dtmLength = 22 WHEN dtmLength = 4365 # YEAR TO FRACTION LET dtmLength = 23 WHEN dtmLength = 4622 # YEAR TO FRACTION(4) LET dtmLength = 24 WHEN dtmLength = 4879 # YEAR TO FRACTION(5) LET dtmLength = 25 OTHERWISE # Not a YEAR TO ???? DATETIME LET dtmLength = 0 END CASE # dtmLength RETURN dtmLength END FUNCTION # dateTimeLength FUNCTION pickDateWCL::pickDateForSF(mySF ixSuperField, otherST ixSuperTable) RETURNING VOID # called from rightmouseclick of date SuperFields # populate the pickDateWN with the month of the date in the field, # if any, and allow picking of a Date. VARIABLE myST ixSuperTable VARIABLE myDate ixDate VARIABLE myDateTime ixDateTime VARIABLE oDate DATE = NULL VARIABLE lDate DATE VARIABLE lDateTime DATETIME YEAR TO FRACTION(5) VARIABLE myCOL ixColumn VARIABLE colType INTEGER VARIABLE cDate CHAR(*) VARIABLE dtmLength SMALLINT # don't do it if the SF is disabled IF NOT mySF.isEnabled() THEN IF MESSAGEBOX (title: NEW ixString("INVALID INPUT"), message: NEW ixString("No Input Allowed"), iconStyle: ixStopIcon, buttonStyle: ixOK, buttonDefault: ixOKButton) THEN END IF RETURN END IF # NOT mySF.isEnabled() # is this a DATE or DATETIME? LET myCOL = mySF.getColumn() LET colType = myCOL.getTypeCode() # get what's in the field LET myST = mySF.getSuperTable() CALL mySF.focus() LET cDate = myST.getCellBuffer() # since we might be in displayQuery mode, # make sure the contents can be a date IF colType = ixTypeInfo::SQLDate THEN LET myDate = NEW ixDate(value: NULL) CALL myDate.setValueStr(cDate) IF myDate.isNull() THEN # could not convert the string to a date LET cDate = NULL END IF ELSE # working with a DATETIME, we hope LET myDateTime = NEW ixDateTime(value: NULL) # cDate is of form yyyy-mm-dd hh:mm:ss.fffff, or some subset thereof # we assume at least yyyy-mm, and below we'll pad out the rest LET dtmLength = LENGTH(cDate) CASE WHEN dtmLength = 7 # yyyy-mm LET cDate = cDate CLIPPED, "-01 00:00:00.00000" WHEN dtmLength = 10 # yyyy-mm-dd LET cDate = cDate CLIPPED, " 00:00:00.00000" WHEN dtmLength = 13 # yyyy-mm-dd hh LET cDate = cDate CLIPPED, ":00:00.00000" WHEN dtmLength = 16 # yyyy-mm-dd hh:mm LET cDate = cDate CLIPPED, ":00.00000" WHEN dtmLength = 19 # yyyy-mm-dd hh:mm:ss LET cDate = cDate CLIPPED, ".00000" WHEN dtmLength = 21 # yyyy-mm-dd hh:mm:ss.f LET cDate = cDate CLIPPED, "0000" WHEN dtmLength > 21 # pad the fractions WHILE LENGTH(cDate) < 25 LET cDate = cDate CLIPPED, "0" END WHILE # LENGTH(cDate) < 25 OTHERWISE # I have no idea what I have here # OR I have a NULL field LET cDate = NULL END CASE # dtmLength # need the Length the column should be for filling in the SuperField below. # get it the brutal way; read the syscolumns.collength field, # which gives us the weird algorythm cited in the book, # which I converted back in 4GL days to actual datetime types. # for the purposes here, I need the length in characters the DateTime string # should be (e.g. YEAR TO DAY (yyyy-mm-dd) = 10; YEAR TO MINUTE (yyyy-mm-dd hh:mm) = 16 LET dtmLength = dateTimeLength(myCOL) IF cDate IS NOT NULL THEN CALL myDateTime.setValueStr(cDate) IF NOT myDateTime.isNull() THEN # could convert the string to a datetime; # take the yyyy-mm-dd part of the datetime # and format it like a mm/dd/yyyy date LET cDate = cDate[6,7], "/", cDate[9,10], "/", cDate[1,4] ELSE LET cDate = NULL END IF END IF # cDate IS NOT NULL END IF # colType = ixTypeInfo::SQLDate IF cDate IS NOT NULL THEN LET oDate = DATE(cDate) CALL mpickDateWN.setDate(oDate) END IF # cDate IS NOT NULL # put the pick window over the field they're picking CALL mpickDateWN.setLocation(mySF.getWindow(),mySF.getTop(),mySF.getLeft()) # put the modal popup on screen CALL mpickDateWN.show() # get the date they picked LET lDate = mpickDateWN.getDate() # lDate is NULL if they hit Cancel IF lDate IS NOT NULL AND (oDate IS NULL OR oDate != lDate) THEN # put it in the field and set focus IF colType = ixTypeInfo::SQLDate THEN LET cDate = lDate USING "mm/dd/yyyy" ELSE # DATETIME LET lDateTime = EXTEND(lDate, YEAR TO FRACTION(5)) LET cDate = lDateTime CALL myDatetime.setValueStr(cDate) # truncate cDate to fit LET cDate = cDate[1,dtmLength] CLIPPED END IF # colType = ixTypeInfo::SQLDate IF mySF.setText(cDate) THEN END IF # set the change flag so that the appropriate keys are enabled IF myST.getChangeFlag(colNum: NULL) = (ixSuperTable::isNew) OR myST.getChangeFlag(colNum: NULL) = (ixSuperTable::isNewModified) THEN CALL myST.setChangeFlag(changeFlag: ixSuperTable::isNewModified) ELSE CALL myST.setChangeFlag(changeFlag: ixSuperTable::isModified) END IF # If you want to be able to enable or disable any action buttons when # new data is input, you would call it here. # below is my example, used with buttonlb.4gl # CALL enableForUpdateBNs(mySF, otherST) END IF CALL mySF.focus() END FUNCTION # pickDateForSF(mySF ixSuperField, otherST ixSuperTable) FUNCTION pickDateWCL::setDate(dateToSet DATE) RETURNING VOID LET origDate = dateToSet CALL fillDateFR(origDate) END FUNCTION FUNCTION dateFocusIn() # called from pickdateWN.winFocusIn() event to fill in dates # as required CALL fillDateFR(origDate) END FUNCTION FUNCTION setSelectDate(lDay SMALLINT, lMonth SMALLINT, lYear SMALLINT) RETURNING DATE VARIABLE lDate DATE # make sure lDay can be part of the month we're setting IF lDay >= 28 THEN LET lDate = MDY(lMonth + 1, 1, lYear) LET lDate = lDate - 1 IF DAY(lDate) < lDay THEN LET lDay = DAY(lDate) END IF END IF LET lDate = MDY(lMonth, lDay, lYear) RETURN lDate END FUNCTION # setSelectDate(lDay, lMonth, lYear) RETURNING DATE FUNCTION startDate(myWN pickDateWCL) RETURNING VOID # set modulars from the start() handler LET mPickDateWN = myWN.getWindow() LET mDateFR = mPickDateWN.getContainedObjByName("dateFR") LET mMonthLB = mPickDateWN.getContainedObjByName("monthLB") LET mYearLB = mPickDateWN.getContainedObjByName("yearLB") END FUNCTION # startDate(myWN pickDateWCL) FUNCTION incrMonth(lct SMALLINT) VARIABLE lYearTxt CHAR(*) VARIABLE lYear SMALLINT VARIABLE lMonth SMALLINT VARIABLE lDay SMALLINT VARIABLE lDate DATE # get the year LET lYearTxt = mYearLB.getItemByNumber(mYearLB.getSelectedItem()) LET lYear = lYearTxt LET lMonth = mMonthLB.getSelectedItem() CASE WHEN lMonth = 1 AND lct = -1 # prior year LET lYear = lYear - 1 LET lMonth = 12 WHEN lMonth = 12 AND lct = 1 # next year LET lYear = lYear + 1 LET lMonth = 1 OTHERWISE LET lMonth = lMonth + lct END CASE # lYear is set to the year we want to fill in, # lMonth is set to the month we want to fill in LET lDay = DAY(origDate) LET lDate = setSelectDate(lDay, lMonth, lYear) # now fill in the calendar CALL fillDateFR(lDate) END FUNCTION # incrMonth(lct SMALLINT) FUNCTION incrYear(lct SMALLINT) VARIABLE lYearTxt CHAR(*) VARIABLE lYear SMALLINT VARIABLE lMonth SMALLINT VARIABLE lDay SMALLINT VARIABLE lDate DATE # get the year LET lYearTxt = mYearLB.getItemByNumber(mYearLB.getSelectedItem()) LET lYear = lYearTxt LET lYear = lYear + lct LET lMonth = mMonthLB.getSelectedItem() # lYear is set to the year we want to fill in, # lMonth is set to the month we want to fill in LET lDay = DAY(origDate) LET lDate = setSelectDate(lDay, lMonth, lYear) # now fill in the calendar CALL fillDateFR(lDate) END FUNCTION # incrYear(lct SMALLINT) FUNCTION selectMYLB() VARIABLE lYearTxt CHAR(*) VARIABLE lYear SMALLINT VARIABLE lMonth SMALLINT VARIABLE lDay SMALLINT VARIABLE lDate DATE # get the year LET lYearTxt = mYearLB.getItemByNumber(mYearLB.getSelectedItem()) LET lYear = lYearTxt LET lMonth = mMonthLB.getSelectedItem() # lYear is set to the year we want to fill in, # lMonth is set to the month we want to fill in LET lDay = DAY(origDate) LET lDate = setSelectDate(lDay, lMonth, lYear) # now fill in the calendar CALL fillDateFR(lDate) END FUNCTION FUNCTION dateBN(lxBN ixButton) VARIABLE lYearTxt CHAR(*) VARIABLE lYear SMALLINT VARIABLE lMonth SMALLINT VARIABLE lDay SMALLINT VARIABLE bnTitle CHAR(*) VARIABLE bnName CHAR(*) VARIABLE lxString ixString # get the Year and Month in the list boxes LET lYearTxt = mYearLB.getItemByNumber(mYearLB.getSelectedItem()) LET lYear = lYearTxt LET lMonth = mMonthLB.getSelectedItem() # get the day of the button; the button title LET bnTitle = lxBN.getTitle() LET lDay = bnTitle IF lxBN.getFontSize() = 10 THEN # this is either the prior or later year LET lxString = NEW ixString("") CALL lxString.concat(lxBN.name) LET bnName = lxString.getValueStr() # buttons are named date##BN, where the 1st # is column (1-7) # and the 2nd # is row (1-6) IF bnName[6] = "1" THEN # prior month LET lMonth = lMonth - 1 IF lMonth = 0 THEN LET lMonth = 12 LET lYear = lYear - 1 END IF ELSE # next month LET lMonth = lMonth + 1 IF lMonth > 12 THEN LET lMonth = 1 LET lYear = lYear + 1 END IF END IF END IF # lxBN.getFontSize() = 10 # lYear is set to the year we want to fill in, # lMonth is set to the month we want to fill in # lDay is the Day LET rtrnDate = MDY(lMonth, lDay, lYear) # done with the window CALL mPickDateWN.hide() END FUNCTION # dateBN(lxBN ixButton) FUNCTION dateCancelBN() RETURNING VOID # on a cancel, set the return date to NULL to signal that no date was picked LET rtrnDate = NULL CALL mPickDateWN.hide() END FUNCTION # dateCancelBN()