This is Controller.m in view mode; [Download] [Up]
/* Controller.m * * * This subclass of Object handles all the user interface actions. * * * You may freely copy, distribute, and reuse the code in this example. * NeXT disclaims any warranty of any kind, expressed or implied, as to its * fitness for any particular use. * */ #import "Controller.h" #import "Localization.h" // Localization routines #import "ClockView.h" // Clock display routines #import <appkit/Matrix.h> #import <appkit/Cell.h> #import <appkit/NXBrowser.h> #import <appkit/NXBrowserCell.h> #import <appkit/ScrollView.h> #import <appkit/Text.h> #import <appkit/Panel.h> #import <sys/dir.h> //for getdirentries() #import <libc.h> #import <string.h> /* Static Functions to be defined later */ static char **addFile(const char *file, int length, char **list, int count); static void freeList(char **list); static BOOL isOk(const char *s); static int caseInsensitiveCompare(void *arg1, void *arg2); static char **fileList; #define MAX_TIME_CHARS 100 #define FILE_NOT_FOUND_MSG LocalString("File %s not found.", NULL, "The message the user receives if the given file is not found. This is normally an internal error") @implementation Controller /* Register some meaningful default values for the system, in case the user starts with * a virgin one. The following defaults are mostly used for strftime(). */ + initialize { static NXDefaultsVector AskMeDefaults = { {"NXDateAndTime", "%a %b %d %H:%M:%S %Z %Y"}, {"NXDate", "%a %b %d %Y"}, {"NXTime", "%H:%M:%S %Z"}, {"NXShortDays", "Sun Mon Tue Wed Thu Fri Sat"}, {"NXLongDays", "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" }, {"NXShortMonths", "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec" }, {"NXLongMonths", "January February March April May June July August September October November December"}, {NULL} }; NXRegisterDefaults("AskMe", AskMeDefaults); return self; } /* appDidInit: does miscellaneous initialization */ - appDidInit:sender { char timeBuffer[MAX_TIME_CHARS]; time_t curtime; const char *menuTitle, *welcomeMsg; NXRect fieldRect; /* Save window frame for later resizing */ [myWindow getFrame:&windowFrame]; /* Find the AskMeText directory */ sprintf(&textDirectory[0],"%s",findLocalDir()); /* Change the menu title */ menuTitle = LocalStringFromTable("Init", "Ask Me", NULL, "main menu title"); [[NXApp mainMenu] setTitle:menuTitle]; /* Set up the split view */ [self initSplitView]; /* Set up the text browser and bring up welcome message */ [myBrowser setDelegate:self]; [myBrowser loadColumnZero]; welcomeMsg = LocalStringFromTable("Init", "Welcome", NULL, "welcome message"); [self showTextFile:welcomeMsg:0:textDirectory]; /* Show current date and time - Note: date and time are not * localized yet (ie they are still English strings, or they follow * the GLOBAL settings of the language preference you chose last). * One could possibly perform a translation * before displaying here. */ curtime = time(0); LocalDate(timeBuffer, MAX_TIME_CHARS, &curtime); [dateField setStringValue:&timeBuffer[0]]; /* Set up a timed entry to update the time periodically */ [timeOfDayField getFrame:&fieldRect]; timeOfDayField = [ [ClockView alloc]initFrame:&fieldRect]; return self; } - appWillTerminate: sender { /* remove the timed entry */ [timeOfDayField stopTimedEntry]; return self; } /* splitview support */ - initSplitView { [mySplitView setDelegate:self]; [mySplitView addSubview:myBrowser]; [mySplitView addSubview:myScrollView]; [mySplitView display]; return self; } /* Adjust the subviews inside the splitview when the window resizes. * Make sure that the upper view doesn't get too small. */ - splitView:sender resizeSubviews:(const NXSize *)oldSize { NXRect lower, upper; float delta; [[sender window] disableDisplay]; [sender adjustSubviews]; [myBrowser getFrame:&upper]; [myScrollView getFrame:&lower]; if (upper.size.height < 100.0) { delta = 100.0 - upper.size.height; upper.size.height=100.0; lower.size.height-=delta; [myBrowser setFrame:&upper]; [myScrollView setFrame:&lower]; } [[sender window] reenableDisplay]; [[sender window] display]; return self; } /* Constrain the y coordinate limits of the splitview divider */ - splitView:sender getMinY:(NXCoord *)minY maxY:(NXCoord *)maxY ofSubviewAt:(int)offset { NXRect rect; offset = 0; [mySplitView getBounds:&rect]; *minY = 100.0; *maxY = rect.size.height - 100.0; if ( *maxY < 100.0 ) *maxY = 100.0; return self; } /* Browser support */ - showTextFile:(const char *)filename:(int)column:(const char*)directoryname { NXStream *stream; char textFile[MAXPATHLEN]; static NXPoint origin = {0.0,0.0}; if ( ! [self browser:myBrowser selectCell:filename inColumn:column] ) { NXRunAlertPanel(NULL, FILE_NOT_FOUND_MSG, NULL, NULL, NULL,filename); return self; } sprintf(textFile,"%s/%s",directoryname,filename); if ((stream = NXMapFile(textFile,NX_READONLY)) == NULL) { NXRunAlertPanel(NULL, FILE_NOT_FOUND_MSG, NULL, NULL, NULL,filename); return self; } if (stream != NULL) { [myWindow disableFlushWindow]; [[myScrollView docView] readRichText:stream]; [[myScrollView docView] scrollPoint:&origin]; [[myWindow reenableFlushWindow] flushWindow]; NXCloseMemory(stream,NX_FREEBUFFER); } [myWindow orderFront:self]; return self; } /* This is the target/action method from the AskMe browser. When * a topic is selected, this method will show the text file for that * topic. */ - browserHit: sender { [self showTextFile:[[[sender matrixInColumn:0] selectedCell] stringValue]:0:textDirectory ]; return self; } /* BROWSER DELEGATE METHODS */ - (int)browser:sender fillMatrix:matrix inColumn:(int)column /* This delegate method goes out to the text directory and gets a list * of all the files in that directory. It creates a list of file names * for the static variable fileList, and will load the filenames into the * browser on demand (lazy loading). */ { long basep; char *buf; struct direct *dp; char **list = NULL; int cc, fd, fileCount = 0; char dirbuf[8192]; if ((fd = open(textDirectory, O_RDONLY, 0644)) > 0) { cc = getdirentries(fd, (buf = dirbuf), 8192, &basep); while (cc) { dp = (struct direct *)buf; if (isOk(dp->d_name)) { list = addFile(dp->d_name, dp->d_namlen, list, fileCount++); } buf += dp->d_reclen; if (buf >= dirbuf + cc) { cc = getdirentries(fd, (buf = dirbuf), 8192, &basep); } } close(fd); if (list) qsort(list,fileCount,sizeof(char *),caseInsensitiveCompare); } freeList(fileList); fileList = list; return fileCount; } - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column /* This delegate method loads the cell for a given row. The stringValue * for that row comes from the fileList. */ { if (fileList) { [cell setStringValueNoCopy:fileList[row]]; [cell setLeaf:YES]; } return self; } - (BOOL)browser:sender selectCell:(const char *)title inColumn:(int)column /* This delegate method selects the cell with the given title. If it finds * a cell with that title, it verifies that it has a file entry in the * fileList, forces the loading of the cell, selects it (highlights) and * scrolls the browser so the cell is visible. It returns a boolean value * which indicates whether the cell was found. */ { int row; id matrix; if (title) { matrix = [sender matrixInColumn:column]; if (!fileList) return NO; for (row = [matrix cellCount]-1; row >= 0; row--) { if (fileList[row] && !strcmp(title, fileList[row])) { [sender getLoadedCellAtRow:row inColumn:column]; [matrix selectCellAt:row :0]; [matrix scrollCellToVisible:row :0]; return YES; } } } return NO; } /* INTERNAL ROUTINES TO HANDLE FILE OPERATIONS */ #define CHUNK 127 static char **addFile(const char *file, int length, char **list, int count) /* Adds the specified filename to the list of filenames. It allocates * more memory in chunks as needed. */ { char *suffix; if (!list) list = (char **)malloc(CHUNK*sizeof(char *)); if (suffix = rindex(file,'.')) *suffix = '\0'; /* strip rtf suffix */ list[count] = (char *)malloc((length+1)*sizeof(char)); strcpy(list[count], file); count++; if (!(count% CHUNK)) { list = (char **)realloc(list,(((count/CHUNK)+1)*CHUNK)*sizeof(char *)); } list[count] = NULL; return list; } static void freeList(char **list) /* Frees the array of filenames */ { char **strings; if (list) { strings = list; while (*strings) free(*strings++); free(list); } } static BOOL isOk(const char *s) /* checks to make sure the filename is not NULL and to verify that it is * not a "dot"--hidden file. */ { return (!s[0] || s[0] == '.') ? NO : YES; } static int caseInsensitiveCompare(void *arg1, void *arg2) /* Compares the two arguments without regard for case using strcasecmp(). */ { char *string1, *string2; string1 = *((char **)arg1); string2 = *((char **)arg2); return strcasecmp(string1,string2); } /* Methods to load separate nib sections: help panel, info panel */ - help:sender { if (helpPanel == NULL) { helpPanel = LoadLocalNib("Help.nib", self); } return[helpPanel makeKeyAndOrderFront:sender]; } - info:sender { if (infoPanel == NULL) { infoPanel = LoadLocalNib("Info.nib", self); } return [infoPanel makeKeyAndOrderFront:sender]; } /* window support */ - windowWillResize:sender toSize:(NXSize *)frameSize { /* Limit the height resizing to the initial height to preserve * data in splitview. Limit its width to max twice its original width. */ if (frameSize->width < 490.0) { frameSize->width = 490.0; } if (frameSize->width > 1000.0) { frameSize->width = 1000.0; } if (frameSize->height < windowFrame.size.height ) { frameSize->height = windowFrame.size.height; } return self; } - windowWillClose: sender { if (sender == infoPanel) infoPanel = NULL; else if (sender == helpPanel) helpPanel = NULL; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.