|
|
1.1 ! root 1: /* Controller.m: ! 2: * You may freely copy, distribute, and reuse the code in this example. ! 3: * NeXT disclaims any warranty of any kind, expressed or implied, as to its ! 4: * fitness for any particular use. ! 5: * ! 6: * Written by Mai Nguyen, NeXT Developer Support ! 7: * ! 8: */ ! 9: ! 10: #import <appkit/appkit.h> ! 11: #import <dbkit/dbkit.h> ! 12: #import <libc.h> ! 13: #import "Controller.h" ! 14: ! 15: /* Define localized strings */ ! 16: #define INSTALL_MODEL NXLocalizedString("Please install SybaseDemo.dbmodela into your project directory and restart.", NULL, "Notify user that SybaseDemo.dbmodela must be installed in his project directory.") ! 17: #define EMPTY_STRING NXLocalizedString("Cannot accept empty string", NULL, "Notify user that empty string input is not valid.") ! 18: #define INSERT_FAILED NXLocalizedString("Insert operation failed", NULL, "Notify user that insert operation has failed.") ! 19: ! 20: static char newAuthorID[100]; ! 21: ! 22: @implementation Controller ! 23: ! 24: /* Extract the actual database and recordlist from the DBModule UI Object ! 25: */ ! 26: -appDidInit:sender ! 27: { ! 28: /* Notify the user if the database can't be found */ ! 29: if (![DBDatabase findDatabaseNamed:"SybaseDemo" connect:YES]) { ! 30: NXRunAlertPanel(NULL,INSTALL_MODEL, "OK", NULL, NULL); ! 31: return self; ! 32: } ! 33: dbDatabase = [dbModule database]; ! 34: dbFetchGroup = [dbModule rootFetchGroup]; ! 35: dbRecordList = [dbFetchGroup recordList]; ! 36: recordOrder = DB_AscendingOrder; ! 37: dbQualifier = nil; ! 38: ! 39: /* Initialize the retrieval order to be ascending order ! 40: */ ! 41: sortProp = [[dbModule entity] propertyNamed:"lastName"]; ! 42: ! 43: [dbRecordList addRetrieveOrder:recordOrder for:sortProp]; ! 44: ! 45: /* Assign the controller object to be the delegate of dbFetchGroup. ! 46: * See also the method fetchGroup:didInsertRecordAt: ! 47: */ ! 48: [dbFetchGroup setDelegate:self]; ! 49: ! 50: /* Assign the record list of the root fetchgroup to be the delegate ! 51: * of DBBinder. This is a convenient way to make sure that the proper ! 52: * qualifier will be used at fetch time. ! 53: * See also the method binderWillSelect: ! 54: */ ! 55: [[dbFetchGroup recordList] setBinderDelegate:self]; ! 56: ! 57: /* Create a DBValue instance to be used later for accessing data ! 58: * inside a record list. ! 59: */ ! 60: aValue = [[DBValue alloc] init]; ! 61: ! 62: [theWindow makeKeyAndOrderFront:nil]; ! 63: return self; ! 64: } ! 65: ! 66: /* Change the retrieval order for the record list. To be set before pressing ! 67: * SELECT. ! 68: */ ! 69: -changeRetrieveOrder:sender ! 70: { ! 71: recordOrder = [sender selectedTag]; ! 72: return self; ! 73: } ! 74: ! 75: /* Build the DBQualifier before a SELECT operation. Note that you should give ! 76: * the external names as defined in dbModel for properties. ! 77: */ ! 78: - buildSelectQualifier:sender ! 79: { ! 80: const char * state; ! 81: id stateProperty; ! 82: ! 83: state = [qualifierField stringValue]; ! 84: if (!strcmp(state,"")) { ! 85: dbQualifier = nil; ! 86: return nil; ! 87: } ! 88: else { ! 89: stateProperty = [[dbModule entity] propertyNamed:"state"]; ! 90: dbQualifier = [[DBQualifier allocFromZone:[self zone] ] ! 91: initForEntity:[dbModule entity] ! 92: fromDescription:"%@ LIKE %s", stateProperty, state]; ! 93: } ! 94: return self; ! 95: } ! 96: ! 97: /* This method is called when the SELECT button is pressed. ! 98: * NOTE: If the qualifier form has a NULL string, the source is set to nil, ! 99: * which means that ALL records will be fetched. ! 100: */ ! 101: - select:sender ! 102: { ! 103: id source; ! 104: ! 105: [self buildSelectQualifier:nil]; ! 106: if (!dbQualifier) ! 107: source = nil; ! 108: else ! 109: source = [dbModule entity]; ! 110: ! 111: [dbFetchGroup fetchContentsOf:source usingQualifier:dbQualifier]; ! 112: ! 113: /* Update the display */ ! 114: [self display]; ! 115: return self; ! 116: } ! 117: ! 118: /* This method is called when the INSERT button is pressed. Check for blank ! 119: * record fields before the actual INSERT. ! 120: */ ! 121: - insert:sender ! 122: { ! 123: if ([self checkInputRecord:sender]==nil) ! 124: NXRunAlertPanel(NULL,INSERT_FAILED, "OK", NULL, NULL); ! 125: else ! 126: [dbModule insertNewRecord:sender]; ! 127: [self display]; ! 128: return self; ! 129: } ! 130: ! 131: /* Try to highlight the newly added row based on the token "newAuthorID". ! 132: * This method is called everytime the SELECT button is pressed. ! 133: * If there is no match, the first row is highlighted by default. ! 134: */ ! 135: - display ! 136: { ! 137: int rowCount, i, row; ! 138: id socSecProperty; ! 139: const char *keyValue; ! 140: ! 141: row = 0; ! 142: /* Search for a match only if the ID has meaningful data */ ! 143: if ( strlen(newAuthorID) > 0 ) { ! 144: rowCount = [dbTableView rowCount]; ! 145: socSecProperty = [[dbModule entity] propertyNamed:"authorID"]; ! 146: for (i= 0; i < rowCount; i++) { ! 147: [dbRecordList getValue:aValue forProperty:socSecProperty at:i]; ! 148: keyValue = (const char *)[aValue stringValue]; ! 149: if (!strcmp(keyValue, newAuthorID)) { ! 150: row = i; ! 151: break; } ! 152: } ! 153: } ! 154: [dbFetchGroup setCurrentRecord:row]; ! 155: [dbTableView display]; ! 156: return self; ! 157: } ! 158: ! 159: ! 160: ! 161: /* DBFetchGroup delegate methods */ ! 162: ! 163: /* ! 164: * This method is called at each SELECT operation. ! 165: */ ! 166: - fetchGroupWillFetch:fetchGroup ! 167: { ! 168: [dbRecordList addRetrieveOrder:recordOrder for:sortProp]; ! 169: return self; ! 170: } ! 171: ! 172: /* ! 173: * This method is called at each INSERT operation. ! 174: */ ! 175: ! 176: - fetchGroup:fetchGroup didInsertRecordAt:(int)index ! 177: { ! 178: /* Fill in the record fields before inserting the new record */ ! 179: [self fillNewRecordAt:index]; ! 180: return self; ! 181: } ! 182: ! 183: ! 184: /* ! 185: * This delegate method is called at each SAVE operation. ! 186: */ ! 187: - fetchGroupDidSave:fetchGroup ! 188: { ! 189: [self clearData]; ! 190: return self; ! 191: } ! 192: ! 193: /* ! 194: * This binder delegate method is called at each SELECT operation so that ! 195: * the proper qualifier is used. ! 196: */ ! 197: ! 198: - (BOOL)binderWillSelect:aBinder ! 199: { ! 200: [aBinder setQualifier:dbQualifier]; ! 201: return YES; ! 202: } ! 203: ! 204: /* checkInputRecord ! 205: * Check if any input field is empty. ! 206: */ ! 207: - checkInputRecord:sender ! 208: { ! 209: int i; ! 210: const char *inputStr; ! 211: ! 212: for ( i = 0; i < 9; i++ ) { ! 213: inputStr = (const char *)[formMatrix stringValueAt:i]; ! 214: /* If the string is empty, abort the operation */ ! 215: if ( inputStr == NULL){ ! 216: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 217: return nil; ! 218: } ! 219: } ! 220: return self; ! 221: } ! 222: ! 223: /* fillNewRecordAt:(int) index ! 224: * Method used for INSERT operation ! 225: * Get the user input from the form cells. ! 226: */ ! 227: - fillNewRecordAt:(int)index ! 228: { ! 229: const char *inputString; ! 230: int contractNum; ! 231: ! 232: /* set last name */ ! 233: inputString = (const char *)[formMatrix stringValueAt:0]; ! 234: [aValue setStringValue:inputString]; ! 235: [dbRecordList setValue:aValue ! 236: forProperty:[[dbModule entity] propertyNamed:"lastName"] ! 237: at:index]; ! 238: ! 239: /* set first name */ ! 240: inputString = (const char *)[formMatrix stringValueAt:1]; ! 241: [aValue setStringValue:(const char *)inputString]; ! 242: [dbRecordList setValue: aValue ! 243: forProperty: [[dbModule entity] propertyNamed:"firstName"] ! 244: at:index]; ! 245: ! 246: /* get the ssn or author id */ ! 247: inputString = (const char *)[formMatrix stringValueAt:2]; ! 248: [aValue setStringValue:inputString]; ! 249: [dbRecordList setValue:aValue ! 250: forProperty:[[dbModule entity] propertyNamed:"authorID"] ! 251: at:index]; ! 252: strcpy( newAuthorID, inputString); ! 253: ! 254: /* set address */ ! 255: inputString = (const char *)[formMatrix stringValueAt:3]; ! 256: [aValue setStringValue: inputString]; ! 257: [dbRecordList setValue: aValue ! 258: forProperty:[[dbModule entity] propertyNamed:"address"] ! 259: at:index]; ! 260: ! 261: /* set city name */ ! 262: inputString = (const char *)[formMatrix stringValueAt:4]; ! 263: [aValue setStringValue: inputString]; ! 264: [dbRecordList setValue: aValue ! 265: forProperty:[[dbModule entity] propertyNamed:"city"] ! 266: at:index]; ! 267: ! 268: /* set state */ ! 269: inputString = (const char *)[formMatrix stringValueAt:5]; ! 270: [aValue setStringValue: inputString]; ! 271: [dbRecordList setValue: aValue ! 272: forProperty:[[dbModule entity] propertyNamed:"state"] ! 273: at:index]; ! 274: ! 275: /* set zip code */ ! 276: inputString = (const char *)[formMatrix stringValueAt:6]; ! 277: [aValue setStringValue: inputString]; ! 278: [dbRecordList setValue: aValue ! 279: forProperty:[[dbModule entity] propertyNamed:"zipCode"] ! 280: at:index ]; ! 281: ! 282: /* set phone number */ ! 283: inputString = (const char *)[formMatrix stringValueAt:7]; ! 284: [aValue setStringValue: inputString]; ! 285: [dbRecordList setValue:aValue ! 286: forProperty:[[dbModule entity] propertyNamed:"phone"] ! 287: at:index]; ! 288: ! 289: /* set contract number */ ! 290: contractNum = [formMatrix intValueAt:8]; ! 291: [aValue setIntValue:contractNum]; ! 292: [dbRecordList setValue: aValue ! 293: forProperty:[[dbModule entity] propertyNamed:"contract"] ! 294: at:index]; ! 295: ! 296: return self; ! 297: } ! 298: ! 299: ! 300: /* Clear the form cells after each SAVE operation to avoid creating ! 301: * duplicate records. ! 302: */ ! 303: - clearData ! 304: { ! 305: int i; ! 306: ! 307: for (i = 0; i < 9; i++) ! 308: [formMatrix setStringValue:""at:i]; ! 309: return self; ! 310: } ! 311: ! 312: @end ! 313:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.