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;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.