This is SavePanel.m in view mode; [Download] [Up]
/* Implementation of SavePanel class * * Copyright (C) 1994 The Board of Trustees of * The Leland Stanford Junior University. All Rights Reserved. * * Authors: Jeff Kamerer and Libing Wang * * This file is part of an Objective-C class library for a window system * */ #include "SavePanel.h" #include "Matrix.h" #include "Motif.h" #include "MList.h" #include "NXBrowser.h" #include "NXBrowserCell.h" #include "ScrollView.h" #include "stdmacros.h" #include <objc/List.h> #include "Application.h" #include "Button.h" #include "TextField.h" #include <stdlib.h> #include <ctype.h> #include <dirent.h> #include <errno.h> #include <signal.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> #include <objc/hashtable.h> #define LISTINCREMENT 10 /* the DIR_INDICATOR cannot be the same as the pathseparator-- it will foul up setPath */ #define DIR_INDICATOR "-->" #define DIR_INDICATOR_FIRSTCHAR '-' /* isLower and sort are used to sort the files the same way that NeXT sorts them. Fairly straightforward. */ static BOOL isLower(const char *wordOne, const char *wordTwo) { int i = 0; char one, two; BOOL isLetterOne, isLetterTwo; while ((wordOne[i] != '\0') && (wordTwo[i] != '\0')) { one = tolower(wordOne[i]); isLetterOne = ((one >= 'a') && (one <= 'z')); two = tolower(wordTwo[i]); isLetterTwo = ((two >= 'a') && (two <= 'z')); if ((isLetterOne) && (!isLetterTwo)) return YES; if ((!isLetterOne) && (isLetterTwo)) return NO; if (one < two) return YES; if (one > two) return NO; i++; } if (wordOne[i] == '\0') return YES; return NO; } static char** sort(char **list, int length) { int i, j, min; char *temp; for (i = 0; i < length; i++) { min = i; for (j = i + 1; j < length; j++) { if (isLower(list[j], list[min])) { min = j; } } if (min != i) { temp = list[i]; list[i] = list[min]; list[min] = temp; } } return list; } @interface SavePanel(PrivateMethods) - setUpPanel; - decodeDirectoryAsSeen; @end @implementation SavePanel(PrivateMethods) /* setUpPanel - I moved the sizing of all of the elements out of initContent... so that all of the elements could be easily resized relative to the panel when the panel is resized. Of course I never implemented this... - Jeff Kamerer If invoked from initialization method, then component objects are created. - Paul Kunz */ - setUpPanel { NXRect contentFrame; NXRect myFrame; [contentView getFrame:&contentFrame]; myFrame.size.width = contentFrame.size.width - 20; myFrame.size.height = contentFrame.size.height - 150; myFrame.origin.x = 10; myFrame.origin.y = 75; if ( browser ) { [browser setFrame:&myFrame]; } else { browser = [[NXBrowser alloc] initFrame:&myFrame]; } myFrame.size.width = 75; myFrame.size.height = 35; myFrame.origin.x = contentFrame.size.width - 80; myFrame.origin.y = 5; if ( okButton ) { [okButton setFrame:&myFrame]; } else { okButton = [[Button alloc] initFrame:&myFrame]; } myFrame.origin.x = myFrame.origin.x - 80; if ( _cancelButton ) { [_cancelButton setFrame:&myFrame]; } else { _cancelButton = [[Button alloc] initFrame:&myFrame]; } myFrame.size.width = 30; myFrame.size.height = 10; myFrame.origin.x = 10; myFrame.origin.y = 45; if ( _formLabel ) { [_formLabel setFrame:&myFrame]; } else { _formLabel = [[[TextField alloc] initFrame:&myFrame] _initAsLabel]; } myFrame.size.width = contentFrame.size.width - 58; myFrame.size.height = 21; myFrame.origin.x = 45; myFrame.origin.y = 45; if ( form ) { [form setFrame:&myFrame]; } else { form = [[TextField alloc] initFrame:&myFrame]; } return self; } /* The _directoryAsSeen is the directory path from the NXBrowser with all of the DIR_INDICATORs in it (I made it "-->"). This method removes the DIR_INDICATORs and puts the result in the instance variable directory. */ - decodeDirectoryAsSeen { int i = 0; int indicatorLength = strlen(DIR_INDICATOR); char *pathPtr; if ((!_directoryAsSeen) || (strlen(_directoryAsSeen) <= 1)) { return self; } pathPtr = _directoryAsSeen + 1; strcpy(directory, "/"); while (pathPtr[0] != '\0') { for (i = 0; ((pathPtr[i] != '/') && (pathPtr[i] != '\0')); i++); pathPtr[i - indicatorLength] = '\0'; strcat(directory, pathPtr); if (pathPtr[i] != '\0') { strcat(directory, "/"); } pathPtr[i - indicatorLength] = DIR_INDICATOR_FIRSTCHAR; pathPtr += i; if (pathPtr[0] == '/') { pathPtr++; } } return self; } @end @implementation SavePanel + newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag { return [[SavePanel alloc] initContent:contentRect style:aStyle backing:bufferingType buttonMask:mask defer:flag]; } - free { const char **myFilenames; free(filename); myFilenames = filenames; while (myFilenames && *myFilenames) { free((char *)*myFilenames); myFilenames++; } free(filenames); free(directory); free(_directoryAsSeen); return [super free]; } - init { NXRect myFrame; myFrame.size.width = 300; myFrame.size.height = 400; myFrame.origin.x = 0; myFrame.origin.y = 0; return [self initContent:&myFrame style:0 backing:0 buttonMask:0 defer:YES]; } - initContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)backingType buttonMask:(int)mask defer:(BOOL)flag { NXRect contentFrame; NXRect myFrame; FILE *file; [super initContent:contentRect style:aStyle backing:backingType buttonMask:mask defer:flag]; [self setUpPanel]; instancename = "SavePanel"; filename = malloc(MAXPATHLEN * sizeof(char)); filenames = NULL; directory = malloc(MAXPATHLEN * sizeof(char)); _directoryAsSeen = malloc(MAXPATHLEN*strlen(DIR_INDICATOR) * sizeof(char)); filename[0] = directory[0] = _directoryAsSeen[0] = '\0'; _beenUsed = NO; [super setTitle:""]; [contentView getFrame:&contentFrame]; myFrame.size.width = 300; myFrame.size.height = 25; myFrame.origin.x = 10; myFrame.origin.y = contentFrame.size.height - myFrame.size.height -10; _titleText = [[TextField alloc] _initAsLabel]; [_titleText setFrame:&myFrame]; [_titleText setStringValue:"Save"]; [contentView addSubview:_titleText]; [browser setMaxVisibleColumns:2]; [browser setDelegate:self]; [browser setTarget:self]; [browser setAction:@selector(highlighted:)]; [browser setDoubleAction:@selector(clickOK:)]; [contentView addSubview:browser]; [okButton setTitle:"OK"]; [okButton setTarget:self]; [okButton setAction:@selector(ok:)]; [contentView addSubview:okButton]; [_cancelButton setTitle:"Cancel"]; [_cancelButton setTarget:self]; [_cancelButton setAction:@selector(cancel:)]; [contentView addSubview:_cancelButton]; [_formLabel setStringValue:"File:"]; [contentView addSubview:_formLabel]; [form setTarget:self]; [form setAction:@selector(clickOK:)]; [contentView addSubview:form]; file = popen("csh -f -c 'echo ~'", "r"); fscanf(file, "%s", directory); pclose(file); return self; } - (int)runModal { int result; [self makeKeyAndOrderFront:self]; if (!_beenUsed) { _beenUsed = YES; [self setDirectory:directory]; } filetypes = NULL; result = [NXApp runModalFor:self]; return result; } - (int)runModalForDirectory:(const char *)path file:(const char *)file_name { if (path) { [self setDirectory:path]; } if (file_name) { [form setStringValue:file_name]; [form selectText:self]; } _beenUsed = YES; return [self runModal]; } - cancel:sender { [self performClose:self]; [NXApp stopModal:NX_CANCELTAG]; return self; } - ok:sender { const char *formString = [form stringValue]; if (formString[0] == '\0') { return self; } strcpy(filename, directory); strcat(filename, "/"); strcat(filename, formString); [self performClose:self]; [NXApp stopModal:NX_OKTAG]; return self; } - (const char *)filename { return filename; } - (const char *)directory { return directory; } /* setDirectory also has to encode the DIR_INDICATORs into _directoryAsSeen and set the path of the browser with that. */ - setDirectory:(const char *)path { int i = 0; char *pathPtr; char temp; if (!path) { return self; } if (directory != path) { strcpy(directory, path); } if (!_beenUsed) { return self; } if (directory[0] == '/') { strcpy(_directoryAsSeen, "/"); pathPtr = directory + 1; } else { _directoryAsSeen[0] = '\0'; pathPtr = directory; } while (pathPtr[0] != '\0') { for (i = 0; ((pathPtr[i] != '/') && (pathPtr[i] != '\0')); i++); temp = pathPtr[i]; pathPtr[i] = '\0'; strcat(_directoryAsSeen, pathPtr); strcat(_directoryAsSeen, DIR_INDICATOR); pathPtr[i] = temp; if ((pathPtr[i] == '/') && (pathPtr[i + 1] != '\0')) { strcat(_directoryAsSeen, "/"); } while (pathPtr[i] == '/') { i++; } pathPtr += i; } [browser setPath:_directoryAsSeen]; return self; } - setPrompt:(const char *)prompt { return self; } - setTitle:(const char *)aTitle { [_titleText setStringValue:aTitle]; return self; } - (const char *)requiredFileType { return requiredType; } - setRequiredFileType:(const char *)type { requiredType = NXCopyStringBuffer(type); /* do something appropriate */ return self; } - setAccessoryView:aView { /* not urgent for phase 1, we can work around not having it */ return self; } @end @implementation SavePanel(delegateMethods) /* the browser passes an empty matrix for this delegate to fill in */ - (int)browser:sender fillMatrix:matrix inColumn:(int)column { NXBrowserCell *aCell; DIR *dirp; struct dirent *entry; struct stat statbuf; int i, k; char file_path[MAXPATHLEN]; char *p, *q; char** namesList; int numNames; int namesSpaceAvail; char *tempString; int specialType; if (column == 0) { strcpy(directory, "/"); } else { [sender getPath:_directoryAsSeen toColumn:column-1]; [self decodeDirectoryAsSeen]; } if ((dirp = opendir(directory)) == NULL ) { return 0; } strcpy(file_path, directory); if (column != 0) strcat(file_path, "/"); p = file_path + strlen(file_path); namesList = malloc(LISTINCREMENT * sizeof(char*)); numNames = 0; namesSpaceAvail = 10; while( (entry = readdir(dirp) ) != NULL ) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; /* skip self and parent */ } tempString = malloc(sizeof(char) * (strlen(entry->d_name) + strlen(DIR_INDICATOR) + 1)); strcpy (tempString, entry->d_name); /* Special code used by runModalForTypes */ /* Select either directories or special types only */ if (filetypes != NULL) { *p = '\0'; strcat(p, tempString); stat(file_path, &statbuf); if ((statbuf.st_mode & S_IFMT) == S_IFDIR) goto select; q = strrchr(tempString, '.'); if (q == NULL) { continue; /* not what we're looking for */ } else { q++; k=0; while (filetypes[k] != NULL) { if (!strcmp(q, filetypes[k])) goto select; k++; } continue; /* not what we're looking for */ } } select: if (numNames == namesSpaceAvail) { namesSpaceAvail += LISTINCREMENT; namesList = (char**)realloc((void *)namesList, namesSpaceAvail * sizeof(char*)); } namesList[numNames++] = tempString; } sort(namesList, numNames); i = 0; while (i < numNames) { if (strcmp(namesList[i], ".") == 0 || strcmp(namesList[i], "..") == 0) { continue; /* skip self and parent */ } [matrix addRow]; aCell = [matrix cellAt:i :0]; *p = '\0'; strcat( p, namesList[i] ); stat( file_path, &statbuf ); specialType = 0; /* Treat special type as a 'leaf' */ if (filetypes != NULL) { q = strrchr(namesList[i], '.'); if (q != NULL) { q++; k=0; while (filetypes[k] != NULL) { if (!strcmp(q, filetypes[k])) specialType = 1; k++; } } } if ( (statbuf.st_mode & S_IFMT) == S_IFDIR && specialType == 0) { strcat(namesList[i], DIR_INDICATOR); [aCell setStringValue:namesList[i]]; [aCell setLeaf:NO]; } else { [aCell setStringValue:namesList[i]]; [aCell setLeaf:YES]; } free(namesList[i]); [aCell setLoaded:YES]; i++; } free(namesList); return i; } /* single click action from browser */ - highlighted:sender { int column; column = [sender selectedColumn]; if ([[sender selectedCell] isLeaf]) { [form setStringValue:[[sender selectedCell] stringValue]]; [form selectText:self]; if (column > 0) { [sender getPath:_directoryAsSeen toColumn:column - 1]; [self decodeDirectoryAsSeen]; } else { strcpy(directory, "/"); } } else { [sender getPath:_directoryAsSeen toColumn:column]; [self decodeDirectoryAsSeen]; } return self; } /* double click action from browser, return key action from form */ - clickOK:sender { [okButton performClick:sender]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.