|
|
1.1 ! root 1: /* AddressView.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 <objc/List.h> ! 12: #import <dbkit/DBRecordList.h> ! 13: #import <dbkit/DBValue.h> ! 14: #import <libc.h> ! 15: ! 16: #import "AddressView.h" ! 17: #import "Controller.h" ! 18: ! 19: #define EMPTY_STRING NXLocalizedString("Cannot accept empty string", NULL, "Notify user that empty string input is not valid.") ! 20: ! 21: static char cellName[100]; ! 22: ! 23: @implementation AddressView ! 24: ! 25: - initFrame:(const NXRect *)frameRect ! 26: { ! 27: NXSize interCellSpacing = {0.0, 0.0}, docSize; ! 28: ! 29: [ super initFrame:frameRect]; ! 30: cellMatrix = [[Matrix alloc] initFrame:frameRect ! 31: mode:NX_LISTMODE ! 32: cellClass:[TextFieldCell class] ! 33: numRows:0 ! 34: numCols:1]; ! 35: ! 36: /* ! 37: * In the following lines, ! 38: * remember that "cellMatrix" is the matrix that will be installed ! 39: * in the scrollview, "self" is the scrollview. ! 40: */ ! 41: ! 42: /* we don't want any space between the matrix's cells */ ! 43: [cellMatrix setIntercell:&interCellSpacing]; ! 44: /* resize the matrix to contain the cells */ ! 45: [cellMatrix sizeToCells]; ! 46: [cellMatrix setAutosizeCells:YES]; ! 47: ! 48: /* ! 49: * when the user clicks in the matrix and then drags the mouse out of ! 50: * scrollView's contentView, we want the matrix to scroll ! 51: */ ! 52: [cellMatrix setAutoscroll:YES]; ! 53: /* let the matrix stretch horizontally */ ! 54: [cellMatrix setAutosizing:NX_WIDTHSIZABLE]; ! 55: /* Install the matrix as the docview of the scrollview */ ! 56: [[self setDocView:cellMatrix] free]; ! 57: /* set up the visible attributes of the scrollview */ ! 58: [self setVertScrollerRequired:YES]; ! 59: [self setBorderType:NX_BEZEL]; ! 60: /* tell the subviews to resize along with the scrollview */ ! 61: [self setAutoresizeSubviews:YES]; ! 62: /* This is the only way to get the clipview to resize too */ ! 63: [[cellMatrix superview] setAutoresizeSubviews:YES]; ! 64: /* Allow the scrollview to stretch both horizontally and vertically */ ! 65: [self setAutosizing:NX_WIDTHSIZABLE|NX_HEIGHTSIZABLE]; ! 66: /* Resize the matrix to fill the inside of the scrollview */ ! 67: [self getContentSize:&docSize]; ! 68: [cellMatrix sizeTo:docSize.width :docSize.height]; ! 69: ! 70: /* Set the matrix single click action */ ! 71: [cellMatrix setTarget:self]; ! 72: [cellMatrix setAction:@selector(showInfo:)]; ! 73: ! 74: /* Allocate DBValue instances for accessing record contents */ ! 75: aValue = [[DBValue alloc] init]; ! 76: aValue2 = [[DBValue alloc] init]; ! 77: return self; ! 78: } ! 79: ! 80: - free ! 81: { ! 82: [cellMatrix free]; ! 83: if (aValue) ! 84: [aValue free]; ! 85: if (aValue2) ! 86: [aValue2 free]; ! 87: return [super free]; ! 88: } ! 89: ! 90: - cellMatrix ! 91: { ! 92: return cellMatrix; ! 93: } ! 94: ! 95: /* ! 96: * Since we recycle the cells (via renewRows:cols:), we also set the state ! 97: * of each cell to 0 and unhighlight it. If we don't do that, the recycled ! 98: * cells will display their previous state. ! 99: */ ! 100: - loadCellsFrom:sender ! 101: ! 102: { ! 103: int i, cellCount; ! 104: const char *firstName, *lastName; ! 105: char buf[100]; ! 106: DBRecordList * recordList; ! 107: List * propertyList; ! 108: ! 109: recordList = (DBRecordList *)[controller getRecordList]; ! 110: propertyList = (List *)[controller getPropertyList]; ! 111: cellCount = (int) [controller getRecordCount]; ! 112: ! 113: if (cellCount == 0 ) ! 114: return self; ! 115: ! 116: ! 117: /* tell the matrix there are 0 cells in it (but don't deallocate them) */ ! 118: [cellMatrix renewRows:0 cols:1]; ! 119: [cellMatrix lockFocus]; /* for highlightCellAt::lit: */ ! 120: for (i=0;i<cellCount;i++) { ! 121: TextFieldCell *cell; ! 122: /* ! 123: * add a row to the matrix. (This doesn't necessarily allocate a new ! 124: * cell, thanks to renewRows:cols:). ! 125: */ ! 126: [cellMatrix addRow]; ! 127: cell = [cellMatrix cellAt:i:0]; ! 128: /* make sure the cell is neither selected nor highlighted */ ! 129: [cellMatrix highlightCellAt:i:0 lit:NO]; ! 130: [cell setState:0]; ! 131: ! 132: /* install the string value in that cell */ ! 133: [recordList getValue:aValue ! 134: forProperty:[propertyList objectAt:1]at:i]; ! 135: lastName= (const char *)[aValue stringValue]; ! 136: ! 137: [recordList getValue:aValue2 ! 138: forProperty:[propertyList objectAt:2]at:i]; ! 139: firstName = (const char *)[aValue2 stringValue]; ! 140: sprintf(buf, "%s %s", lastName, firstName); ! 141: [cell setStringValue:buf]; ! 142: ! 143: } ! 144: [cellMatrix unlockFocus]; ! 145: [cellMatrix sizeToCells]; ! 146: [cellMatrix display]; ! 147: ! 148: return self; ! 149: } ! 150: ! 151: /* Get the newly added row */ ! 152: - (int) getNewRow ! 153: { ! 154: int i; ! 155: int count, row; ! 156: const char* name; ! 157: ! 158: count = [cellMatrix cellCount]; ! 159: row = count; /* Initialize row to a meaningful number */ ! 160: for (i = 0; i < count; i++) { ! 161: name = [[cellMatrix cellAt:i:0] stringValue]; ! 162: if (!strcmp(cellName, name) ) { ! 163: row = i; ! 164: break; ! 165: } ! 166: } ! 167: return row; ! 168: } ! 169: ! 170: ! 171: /* Show the information contained in each selected record ! 172: */ ! 173: ! 174: - showInfo:sender ! 175: { ! 176: int index; ! 177: DBRecordList * recordList; ! 178: List * propertyList; ! 179: ! 180: recordList = [controller getRecordList]; ! 181: propertyList = [controller getPropertyList]; ! 182: ! 183: index = [cellMatrix selectedRow]; ! 184: ! 185: /* Find the person's id */ ! 186: ! 187: [recordList getValue:aValue forProperty:[propertyList objectAt:0]at:index]; ! 188: [ssnField setStringValue:(const char *)[aValue stringValue]]; ! 189: ! 190: ! 191: /* Find the person's last name and first name */ ! 192: [recordList getValue:aValue forProperty:[propertyList objectAt:1]at:index]; ! 193: [lnameField setStringValue: (const char *)[aValue stringValue]]; ! 194: ! 195: [recordList getValue:aValue forProperty:[propertyList objectAt:2]at:index]; ! 196: [fnameField setStringValue:(const char *)[aValue stringValue]]; ! 197: ! 198: /* Find the person's phone number */ ! 199: [recordList getValue:aValue forProperty:[propertyList objectAt:3]at:index]; ! 200: [phoneField setStringValue:(const char *)[aValue stringValue]]; ! 201: ! 202: /* Find the person's address */ ! 203: [recordList getValue:aValue forProperty:[propertyList objectAt:4]at:index]; ! 204: [addressField setStringValue:(const char *)[aValue stringValue]]; ! 205: ! 206: /* Find the person's city of residence */ ! 207: [recordList getValue:aValue forProperty:[propertyList objectAt:5]at:index]; ! 208: [cityField setStringValue:(const char *)[aValue stringValue]]; ! 209: ! 210: ! 211: /* Find the person's state of residence */ ! 212: [recordList getValue:aValue forProperty:[propertyList objectAt:6]at:index]; ! 213: [stateField setStringValue:(const char *)[aValue stringValue]]; ! 214: ! 215: /* Find the person's zip code */ ! 216: [recordList getValue:aValue forProperty:[propertyList objectAt:7]at:index]; ! 217: [zipField setStringValue:(const char *)[aValue stringValue]]; ! 218: return self; ! 219: } ! 220: ! 221: /* Add a new record at the specified index. ! 222: */ ! 223: - addRecordFrom:sender at:(unsigned)index ! 224: { ! 225: DBRecordList * recordList; ! 226: List * propertyList; ! 227: ! 228: recordList = (DBRecordList *)[controller getRecordList]; ! 229: propertyList = (List *)[controller getPropertyList]; ! 230: ! 231: /* In order to get records in and out, ! 232: * we must use a value object all the time ! 233: */ ! 234: ! 235: /* add empty record */ ! 236: [recordList insertRecordAt:index]; ! 237: if ([self setNewRecordFrom:sender at:index] == nil) { ! 238: return nil; ! 239: } ! 240: ! 241: if ([recordList saveModifications] == 0) ! 242: return self; ! 243: ! 244: return nil; ! 245: } ! 246: ! 247: /* Update the record specified at index. ! 248: */ ! 249: ! 250: - updateRecordFrom:sender at:(unsigned) index ! 251: { ! 252: DBRecordList * recordList; ! 253: List * propertyList; ! 254: ! 255: recordList = (DBRecordList *)[controller getRecordList]; ! 256: propertyList = (List *) [controller getPropertyList]; ! 257: ! 258: if ([self setNewRecordFrom:sender at:index] == nil) { ! 259: return nil; ! 260: } ! 261: ! 262: if ([recordList saveModifications] == 0) ! 263: return self; ! 264: return nil; ! 265: } ! 266: ! 267: /* Get the user input from the text fields. Note that if a field is empty, ! 268: * the operation (either insert or update) would fail. Also, for simplicity, ! 269: * an arbitrary contract number is assigned to new records. ! 270: */ ! 271: - setNewRecordFrom:sender at:(unsigned)index ! 272: { ! 273: DBRecordList *aRecList; ! 274: List *aPropList; ! 275: const char *inputString; ! 276: const char *lastName; ! 277: ! 278: aRecList = (DBRecordList *)[controller getRecordList]; ! 279: aPropList = (List *)[controller getPropertyList]; ! 280: ! 281: /* get the ssn or author id */ ! 282: ! 283: inputString = (const char *)[ssnField stringValue]; ! 284: ! 285: /* If the string is empty, abort the operation */ ! 286: if ( !strcmp(inputString,"")){ ! 287: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 288: return nil; ! 289: } ! 290: else { ! 291: [aValue setStringValue:inputString]; ! 292: [aRecList setValue:aValue forProperty:[aPropList objectAt:0] at:index]; ! 293: } ! 294: ! 295: ! 296: /* set last name */ ! 297: lastName = (const char *)[lnameField stringValue]; ! 298: if ( !strcmp(lastName,"")){ ! 299: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 300: return nil; ! 301: } ! 302: else { ! 303: [aValue setStringValue:lastName]; ! 304: [aRecList setValue:aValue forProperty:[aPropList objectAt:1] at:index]; ! 305: } ! 306: ! 307: ! 308: /* set first name */ ! 309: inputString = (const char *)[fnameField stringValue]; ! 310: if ( !strcmp(inputString,"")){ ! 311: NXRunAlertPanel (NULL, EMPTY_STRING, NULL, NULL, NULL); ! 312: return nil; ! 313: } ! 314: else { ! 315: [aValue setStringValue:(const char *)inputString]; ! 316: [aRecList setValue:aValue forProperty:[aPropList objectAt:2] at:index]; ! 317: sprintf( cellName, "%s %s", lastName, inputString); ! 318: } ! 319: ! 320: /* set phone number */ ! 321: inputString = (const char *)[phoneField stringValue]; ! 322: if ( !strcmp(inputString,"")){ ! 323: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 324: return nil; ! 325: } ! 326: else { ! 327: [aValue setStringValue: inputString]; ! 328: [aRecList setValue:aValue forProperty:[aPropList objectAt:3] at:index]; ! 329: } ! 330: ! 331: /* set address */ ! 332: inputString = (const char *)[addressField stringValue]; ! 333: if ( !strcmp(inputString,"")){ ! 334: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 335: return nil; ! 336: } ! 337: else { ! 338: [aValue setStringValue: inputString]; ! 339: [aRecList setValue:aValue forProperty:[aPropList objectAt:4] at:index]; ! 340: } ! 341: ! 342: /* set city name */ ! 343: inputString = (const char *)[cityField stringValue]; ! 344: if ( !strcmp(inputString,"")){ ! 345: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 346: return nil; ! 347: } ! 348: else { ! 349: [aValue setStringValue: inputString]; ! 350: [aRecList setValue:aValue forProperty:[aPropList objectAt:5] at:index]; ! 351: } ! 352: ! 353: /* set state */ ! 354: inputString = (const char *)[stateField stringValue]; ! 355: if ( !strcmp(inputString,"")){ ! 356: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 357: return nil; ! 358: } ! 359: else { ! 360: [aValue setStringValue: inputString]; ! 361: [aRecList setValue:aValue forProperty:[aPropList objectAt:6] at:index]; ! 362: } ! 363: ! 364: /* set zip code */ ! 365: inputString = (const char *)[zipField stringValue]; ! 366: if ( !strcmp(inputString,"")){ ! 367: NXRunAlertPanel (NULL,EMPTY_STRING,NULL, NULL, NULL); ! 368: return nil; ! 369: } ! 370: else { ! 371: [aValue setStringValue: inputString]; ! 372: [aRecList setValue:aValue forProperty:[aPropList objectAt:7] at:index]; ! 373: } ! 374: ! 375: ! 376: /* assign an arbitrary contract number 1 to the new record */ ! 377: [aRecList getValue:aValue forProperty:[aPropList objectAt:8] at:index]; ! 378: ! 379: if ( ![aValue intValue]) { ! 380: [aValue setIntValue:1]; ! 381: [aRecList setValue:aValue forProperty:[aPropList objectAt:8] at:index]; ! 382: } ! 383: ! 384: return self; ! 385: } ! 386: ! 387: ! 388: ! 389: - deleteSelectedRecord:sender ! 390: { ! 391: int row; ! 392: DBRecordList * recordList; ! 393: ! 394: row = [cellMatrix selectedRow]; ! 395: recordList = (DBRecordList *)[controller getRecordList]; ! 396: [recordList deleteRecordAt:row]; ! 397: if ([recordList saveModifications] == 0) ! 398: return self; ! 399: else ! 400: return nil; ! 401: } ! 402: ! 403: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.