|
|
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.