This is MODateFormCell.m in view mode; [Download] [Up]
// MODateFormCell.m // // by Mike Ferris // Part of MOKit // Copyright 1993, all rights reserved. // ABOUT MOKit // by Mike Ferris (mike@lorax.com) // // MOKit is a collection of useful and general objects. Permission is // granted by the author to use MOKit in your own programs in any way // you see fit. All other rights pertaining to the kit are reserved by the // author including the right to sell these objects as objects, as part // of a LIBRARY, or as SOURCE CODE. In plain English, I wish to retain // rights to these objects as objects, but allow the use of the objects // as pieces in a fully functional program. Permission is also granted to // redistribute the source code of MOKit for FREE as long as this copyright // notice is left intact and unchanged. NO WARRANTY is expressed or implied. // The author will under no circumstances be held responsible for ANY // consequences from the use of these objects. Since you don't have to pay // for them, and full source is provided, I think this is perfectly fair. #import "MOKit/MODateFormCell.h" #import "MOKit/MOString.h" #import <objc/objc-runtime.h> #define CLASS_VERSION 0 #define CLASS_NAME "MODateFormCell" #define BUNDLE_TYPE "bundle" #define MOSTRING_CLASS_NAME "MOString" #define AM_DATE_REGEX_1 "^[ ]*(([0][1-9]|[1][0-2])([0][1-9]|[1-2][0-9]|[3][0-1])([0-9]?[0-9]?[0-9][0-9]))[ ]*$" #define AM_DATE_REGEX_2 "^[ ]*(([0]?[1-9]|[1][0-2])[-/]([0]?[1-9]|[1-2][0-9]|[3][0-1])[-/]([0-9]?[0-9]?[0-9][0-9]))[ ]*$" #define AM_DATE_REGEX_3 "^[ ]*(([jJ][aA][nN]?[uU]?[aA]?[rR]?[yY]?|[fF][eE]?[bB]?[rR]?[uU]?[aA]?[rR]?[yY]?|[mM][aA][rR][cC]?[hH]?|[aA][pP][rR]?[iI]?[lL]?|[mM][aA][yY]|[jJ][uU][nN][eE]?|[jJ][uU][lL][yY]?|[aA][uU][gG]?[uU]?[sS]?[tT]?|[sS][eE]?[pP]?[tT]?[eE]?[mM]?[bB]?[eE]?[rR]?|[oO][cC]?[tT]?[oO]?[bB]?[eE]?[rR]?|[nN][oO]?[vV]?[eE]?[mM]?[bB]?[eE]?[rR]?|[dD][eE]?[cC]?[eE]?[mM]?[bB]?[eE]?[rR]?)[ ]*([0]?[1-9]|[1-2][0-9]|[3][0-1])[ ]*[,]?[ ]*([0-9]?[0-9]?[0-9][0-9]))[ ]*$" #define EU_DATE_REGEX_1 "^[ ]*(([0][1-9]|[1-2][0-9]|[3][0-1])([0][1-9]|[1][0-2])([0-9]?[0-9]?[0-9][0-9]))[ ]*$" #define EU_DATE_REGEX_2 "^[ ]*(([0]?[1-9]|[1-2][0-9]|[3][0-1])[-/]([0]?[1-9]|[1][0-2])[-/]([0-9]?[0-9]?[0-9][0-9]))[ ]*$" #define EU_DATE_REGEX_3 "^[ ]*(([0]?[1-9]|[1-2][0-9]|[3][0-1])[ ]*[,]?[ ]*([jJ][aA][nN]?[uU]?[aA]?[rR]?[yY]?|[fF][eE]?[bB]?[rR]?[uU]?[aA]?[rR]?[yY]?|[mM][aA][rR][cC]?[hH]?|[aA][pP][rR]?[iI]?[lL]?|[mM][aA][yY]|[jJ][uU][nN][eE]?|[jJ][uU][lL][yY]?|[aA][uU][gG]?[uU]?[sS]?[tT]?|[sS][eE]?[pP]?[tT]?[eE]?[mM]?[bB]?[eE]?[rR]?|[oO][cC]?[tT]?[oO]?[bB]?[eE]?[rR]?|[nN][oO]?[vV]?[eE]?[mM]?[bB]?[eE]?[rR]?|[dD][eE]?[cC]?[eE]?[mM]?[bB]?[eE]?[rR]?)[ ]*[,]?[ ]*([0-9]?[0-9]?[0-9][0-9]))[ ]*$" @interface MODateFormCell(Private) + (Class)MO_loadClassBundle:(const char *)className; @end @implementation MODateFormCell static id MOStringClass; + (Class)MO_loadClassBundle:(const char *)className // Finds the bundle of the same name as the class, grabs it and loads the // class from it and returns the named class. { char pathBuff[MAXPATHLEN+1]; id classBundle = nil; Class class = nil; // Load the bundle if ((class = objc_lookUpClass(className)) == nil) { // class is not already loaded... load it. // Look for the bundle in the main bundle first, // else try in this class's bundle. if (![[NXBundle mainBundle] getPath:pathBuff forResource:className ofType:BUNDLE_TYPE]) { if (![[NXBundle bundleForClass:[self class]] getPath:pathBuff forResource:className ofType:BUNDLE_TYPE]) { NXLogError("[%s loadClassBundle] failed to " "find %s class bundle.", [self name], className); return nil; } } classBundle = [[NXBundle allocFromZone:[self zone]] initForDirectory:pathBuff]; if (!classBundle) { NXLogError("[%s loadClassBundle] failed to " "create bundle for class %s.", [self name], className); return nil; } if ((class = [classBundle classNamed:className]) == nil) { NXLogError("[%s loadClassBundle] failed to " "load %s class from bundle.", [self name], className); return nil; } } return class; } + initialize // Set the version. { if (self == objc_lookUpClass(CLASS_NAME)) { [self setVersion:CLASS_VERSION]; // Load the string class if necessary MOStringClass = [self MO_loadClassBundle:MOSTRING_CLASS_NAME]; } return self; } - initTextCell:(const char *)aString withRegex:(const char *)re // We'll ignore any regex expression passed to us, cause we manage our own // expressions. { [super initTextCell:aString withRegex:AM_DATE_REGEX_1]; isEuropeanStyle = NO; doesFormat = NO; formatString = nil; [self addRegexStr:AM_DATE_REGEX_2]; [self addRegexStr:AM_DATE_REGEX_3]; formatString = [[MOStringClass allocFromZone:[self zone]] init]; return self; } - MO_copyFormatString:str zone:(NXZone *)zone { formatString = [[MOStringClass allocFromZone:zone] initStringValue:[str stringValue]]; return self; } - copyFromZone:(NXZone *)zone { id obj = [super copyFromZone:zone]; [obj MO_copyFormatString:formatString zone:zone]; return obj; } - free { [formatString free]; return [super free]; } - (BOOL)isEuropeanStyle { return isEuropeanStyle; } - setEuropeanStyle:(BOOL)flag { if (flag != isEuropeanStyle) { isEuropeanStyle = flag; if (isEuropeanStyle) { [self setRegexStr:EU_DATE_REGEX_1]; [self addRegexStr:EU_DATE_REGEX_2]; [self addRegexStr:EU_DATE_REGEX_3]; } else { [self setRegexStr:AM_DATE_REGEX_1]; [self addRegexStr:AM_DATE_REGEX_2]; [self addRegexStr:AM_DATE_REGEX_3]; } } // reset the pieces and stuff. [self isEntryAcceptable:[self stringValue]]; return self; } - (BOOL)doesFormatDates { return doesFormat; } - setFormatDates:(BOOL)flag { doesFormat = flag; [self formatDate]; return self; } - (const char *)formatString { return [formatString stringValue]; } - setFormatString:(const char *)str { [formatString setStringValue:str]; [self formatDate]; return self; } - (BOOL)MO_finishCheckingDate // Make sure that the days in the month is valid. We can assume that if we're // here, the regular expression parts have checked out. { int monthNum= [self month], yearNum= [self year], dayNum= [self day]; if ((monthNum<0) || (yearNum<0) || (dayNum<0)) { return NO; } switch (monthNum) { // "thirty days has ..." case 9: // "September, ..." case 4: // "April, ..." case 6: // "June, and ..." case 11: // "November ..." if (dayNum>30) return NO; break; // "All the rest have thirty one ..." // (but the regular expression made sure it was at least 31 or // less.) // "Except for February which has 28 ..." // "(or 29 if its a leap year)." case 2: // February if (dayNum > 29) return NO; if (dayNum == 29) { BOOL isLeapYear=NO; // figure out if it's a leap year. if (yearNum%4 == 0) { if (yearNum%100 == 0) { if (yearNum%1000 == 0) { isLeapYear=YES; } } else { isLeapYear=YES; } } if (!isLeapYear) return NO; } break; default: break; } return YES; } - (BOOL)isEntryAcceptable:(const char *)aString { if ([super isEntryAcceptable:aString]) { if ((aString) && (*aString)) { return [self MO_finishCheckingDate]; } else { return YES; } } else { return NO; } } - (int)day { const char *day; int dayNum = 0; if (isEuropeanStyle) { day = [self pieceAt:2]; } else { day = [self pieceAt:3]; } if (day) { dayNum = atoi(day); } else { dayNum = -1; } return dayNum; } - (int)month { const char *month; int monthNum = 0; if (isEuropeanStyle) { month = [self pieceAt:3]; } else { month = [self pieceAt:2]; } if (!month) { return -1; } // Is month numeric or name? if (NXIsDigit(*month)) { // it's number monthNum = atoi(month); } else { // it's name if (NXOrderStrings(month, "ja", NO, 2, NULL) == 0) { monthNum = 1; } else if (NXOrderStrings(month, "f", NO, 1, NULL) == 0) { monthNum = 2; } else if (NXOrderStrings(month, "mar", NO, 3, NULL) == 0) { monthNum = 3; } else if (NXOrderStrings(month, "ap", NO, 2, NULL) == 0) { monthNum = 4; } else if (NXOrderStrings(month, "may", NO, 3, NULL) == 0) { monthNum = 5; } else if (NXOrderStrings(month, "jun", NO, 3, NULL) == 0) { monthNum = 6; } else if (NXOrderStrings(month, "jul", NO, 3, NULL) == 0) { monthNum = 7; } else if (NXOrderStrings(month, "au", NO, 2, NULL) == 0) { monthNum = 8; } else if (NXOrderStrings(month, "s", NO, 1, NULL) == 0) { monthNum = 9; } else if (NXOrderStrings(month, "o", NO, 1, NULL) == 0) { monthNum = 10; } else if (NXOrderStrings(month, "n", NO, 1, NULL) == 0) { monthNum = 11; } else if (NXOrderStrings(month, "d", NO, 1, NULL) == 0) { monthNum = 12; } else { // this shouldn't happen since the regular expression // has been checked to make sure it was a valid month. return -1; } } return monthNum; } - (int)year { const char *year; int yearNum = 0; if (isEuropeanStyle) { year = [self pieceAt:4]; } else { year = [self pieceAt:4]; } if (!year) { return -1; } yearNum=atoi(year); if (yearNum < 100) { // adjust for current century struct timeval tv; struct timezone tz; struct tm *tmp = NULL; int currentCentury; gettimeofday(&tv, &tz); tmp = localtime(&tv.tv_sec); currentCentury = 1900 + (tmp->tm_year / 100); yearNum += currentCentury; } return yearNum; } - endEditing:textObject { [super endEditing:textObject]; [self formatDate]; return self; } - (const char *)MO_monthNameForNum:(int)monthNum short:(BOOL)flag { static char buf[20]; switch(monthNum) { case 1: if (flag) { strcpy(buf, "Jan"); } else { strcpy(buf, "January"); } break; case 2: if (flag) { strcpy(buf, "Feb"); } else { strcpy(buf, "February"); } break; case 3: if (flag) { strcpy(buf, "Mar"); } else { strcpy(buf, "March"); } break; case 4: if (flag) { strcpy(buf, "Apr"); } else { strcpy(buf, "April"); } break; case 5: strcpy(buf, "May"); break; case 6: if (flag) { strcpy(buf, "Jun"); } else { strcpy(buf, "June"); } break; case 7: if (flag) { strcpy(buf, "Jul"); } else { strcpy(buf, "July"); } break; case 8: if (flag) { strcpy(buf, "Aug"); } else { strcpy(buf, "August"); } break; case 9: if (flag) { strcpy(buf, "Sep"); } else { strcpy(buf, "September"); } break; case 10: if (flag) { strcpy(buf, "Oct"); } else { strcpy(buf, "October"); } break; case 11: if (flag) { strcpy(buf, "Nov"); } else { strcpy(buf, "November"); } break; case 12: if (flag) { strcpy(buf, "Dec"); } else { strcpy(buf, "December"); } break; default: strcpy(buf, "");; } return buf; } - formatDate { static char buf[1024]; if (doesFormat) { const char *format = [formatString stringValue]; const char *fPtr = NULL; char *bPtr = NULL; const char *month = NULL; int dayNum = [self day], monthNum = [self month]; int yearNum = [self year]; if ((!format) || (!*format) || (dayNum < 0) || (monthNum < 0) || (yearNum < 0)) { return nil; } bPtr = buf; fPtr = format; while (*fPtr) { if (*fPtr == '%') { // Its a format code... fPtr++; switch (*fPtr) { case 'd': sprintf(bPtr, "%d", dayNum); break; case 'D': sprintf(bPtr, "%02d", dayNum); break; case 'm': sprintf(bPtr, "%d", monthNum); break; case 'M': sprintf(bPtr, "%02d", monthNum); break; case 'n': month = [self MO_monthNameForNum:monthNum short:YES]; sprintf(bPtr, "%s", month); break; case 'N': month = [self MO_monthNameForNum:monthNum short:NO]; sprintf(bPtr, "%s", month); break; case 'y': sprintf(bPtr, "%d", yearNum%100); break; case 'Y': sprintf(bPtr, "%d", yearNum); break; case '%': strcpy(bPtr, "%"); break; default: NXLogError("%s: Invalid date " "formatting code.", [[self class] name]); break; } bPtr = buf + strlen(buf); fPtr++; } else { // Just copy the character... *bPtr = *fPtr; bPtr++; fPtr++; } } *bPtr = '\0'; // call super to avoid an infinite loop... // (our setStringValue calls formatDate) [super setStringValue:buf]; } return self; } - setDoubleValue:(double)aDouble { id ret = [super setDoubleValue:aDouble]; [self formatDate]; return ret; } - setFloatValue:(float)aFloat { id ret = [super setFloatValue:aFloat]; [self formatDate]; return ret; } - setIntValue:(int)anInt { id ret = [super setIntValue:anInt]; [self formatDate]; return ret; } - setStringValue:(const char *)aString { id ret = [super setStringValue:aString]; [self formatDate]; return ret; } - setStringValueNoCopy:(const char *)aString { id ret = [super setStringValueNoCopy:aString]; [self formatDate]; return ret; } - setStringValueNoCopy:(char *)aString shouldFree:(BOOL)flag { id ret = [super setStringValueNoCopy:aString shouldFree:flag]; [self formatDate]; return ret; } - read:(NXTypedStream *)strm { int classVersion; [super read:strm]; classVersion = NXTypedStreamClassVersion(strm, CLASS_NAME); switch (classVersion) { case 0: // First version. NXReadType(strm, "C", &isEuropeanStyle); NXReadType(strm, "C", &doesFormat); formatString = NXReadObject(strm); break; default: NXLogError("[%s read:] class version %d cannot read " "instances archived with version %d", CLASS_NAME, CLASS_VERSION, classVersion); isEuropeanStyle = NO; doesFormat = NO; formatString = [[MOStringClass allocFromZone:[self zone]] init]; break; } return self; } - write:(NXTypedStream *)strm { [super write:strm]; NXWriteType(strm, "C", &isEuropeanStyle); NXWriteType(strm, "C", &doesFormat); NXWriteObject(strm, formatString); return self; } - (const char *)getInspectorClassName { return "MODateFormCellInspector"; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.