This is MODocManager.m in view mode; [Download] [Up]
// MODocManager.m
//
// by Mike Ferris
// Part of MOKit
// Copyright 1993, all rights reserved.
// ABOUT MOKit
//
// MOKit is a collection of useful and general objects. Permission is
// granted by the author to use MOKit in your own programs in any way
// you see fit. All other rights to the kit are reserved by the author
// including the right to sell these objects as part of a LIBRARY or as
// SOURCE CODE. In plain English, I wish to retain rights to these
// objects as objects, but allow the use of the objects as pieces in a
// fully functional program. NO WARRANTY is expressed or implied. The author
// will under no circumstances be held responsible for ANY consequences to
// you from the use of these objects. Since you don't have to pay for
// them, and full source is provided, I think this is perfectly fair.
#import "MOKit/MODocManager.h"
#import "MOKit/MODocController.h"
#import "MOKit/MODocType.h"
#import "MOKit/MOString.h"
#import "MOKit/MOPathString.h"
#import <objc/objc-runtime.h>
#define CLASS_VERSION 0
#define CLASS_NAME "MODocManager"
#define MOSTRING_CLASS_NAME "MOString"
#define MOPATHSTRING_CLASS_NAME "MOPathString"
#define MODOCTYPE_CLASS_NAME "MODocType"
#define BUNDLE_TYPE "bundle"
#define MOKIT_BUNDLE "MOKitBundle"
#define QUITPANEL_NIBNAME "MODocQuitPanel.nib"
#define QUITPANEL_REVIEW 0
#define QUITPANEL_SAVEALL 1
#define QUITPANEL_QUIT 2
#define QUITPANEL_CANCEL 3
#define MAX_FRAME_CYCLE 10
#define FRAME_ADJUST 25.0
#define FRAME_START_LOC_X 150.0
#define FRAME_START_LOC_Y 30.0 // top of screen to top of window
#define NEW_MENU_TITLE "New"
#define OPEN_MENU_TITLE "Open"
@interface MODocManager(Private)
+ (Class)MO_loadClassBundle:(const char *)className;
+ (BOOL)MO_loadMOKitBundle;
- MO_resetOpenMenus;
@end
@implementation MODocManager
static Class MOStringClass;
static Class MOPathStringClass;
static Class MODocTypeClass;
static NXBundle *MOKitBundle;
+ (Class)MO_loadClassBundle:(const char *)className
// Finds the bundle of the same name as the class, grabs it and loads the
// class from it and returns the named class.
{
char pathBuff[MAXPATHLEN+1];
id classBundle = nil;
Class class = nil;
// Load the bundle
if ((class = objc_lookUpClass(className)) == nil) {
// class is not already loaded... load it.
// Look for the bundle in the main bundle first,
// else try in this class's bundle.
if (![[NXBundle mainBundle] getPath:pathBuff forResource:className
ofType:BUNDLE_TYPE]) {
if (![[NXBundle bundleForClass:[self class]] getPath:pathBuff
forResource:className ofType:BUNDLE_TYPE]) {
NXLogError("[%s loadClassBundle] failed to "
"find %s class bundle.", [self name], className);
return nil;
}
}
classBundle = [[NXBundle allocFromZone:[self zone]]
initForDirectory:pathBuff];
if (!classBundle) {
NXLogError("[%s loadClassBundle] failed to "
"create bundle for class %s.", [self name], className);
return nil;
}
if ((class = [classBundle classNamed:className]) == nil) {
NXLogError("[%s loadClassBundle] failed to "
"load %s class from bundle.", [self name], className);
return nil;
}
}
return class;
}
+ (BOOL)MO_loadMOKitBundle;
// Locates and loads (hopefully) the MOKit bundle which contains a nib we need.
{
char pathBuff[MAXPATHLEN+1];
// Look for the bundle in the main bundle first,
// else try in this class's bundle.
if (![[NXBundle mainBundle] getPath:pathBuff forResource:MOKIT_BUNDLE
ofType:BUNDLE_TYPE]) {
if (![[NXBundle bundleForClass:[self class]] getPath:pathBuff
forResource:MOKIT_BUNDLE ofType:BUNDLE_TYPE]) {
NXLogError("[%s MO_loadMOKitBundle] failed to "
"find MOKit bundle.", [self name]);
return NO;
}
}
MOKitBundle = [[NXBundle allocFromZone:[self zone]]
initForDirectory:pathBuff];
if (!MOKitBundle) {
NXLogError("[%s MO_loadMOKitBundle] failed to "
"create bundle for MOKit bundle.", [self name]);
return NO;
}
return YES;
}
+ initialize
// Set the version. Load classes. Load the MOKit bundle.
{
if (self == [MODocManager class]) {
[self setVersion:CLASS_VERSION];
// Load all the classes we need
MOStringClass = [self MO_loadClassBundle:MOSTRING_CLASS_NAME];
MOPathStringClass = [self MO_loadClassBundle:MOPATHSTRING_CLASS_NAME];
MODocTypeClass = [self MO_loadClassBundle:MODOCTYPE_CLASS_NAME];
// Find the MOKit bundle which contains MOKit's nibs and
// other non-code resources.
[self MO_loadMOKitBundle];
}
return self;
}
- init
// Call the DI.
{
return [self initDocumentClass:nil];
}
- initDocumentClass:(Class)docControllerClass;
// Deesignated initializer.
// Init the manager with defaults. If docControllerClass is non-nil it is
// added as the first of potentially several document controller classes this
// manager will manage.
{
const NXScreen *screen = NULL;
[super init];
docList = [[List allocFromZone:[self zone]] init];
currentDoc = nil;
docClassList = [[List allocFromZone:[self zone]] init];
if (docControllerClass) [docClassList addObject:docControllerClass];
untitledCount = 0;
frameCycle = 0;
screen = [NXApp mainScreen];
windowStartingLocation.x = FRAME_START_LOC_X;
windowStartingLocation.y = screen->screenBounds.size.height -
FRAME_START_LOC_Y;
return self;
}
- free
// free stuff we allocated.
{
[[docList freeObjects] free];
[docClassList empty];
[self MO_resetOpenMenus];
if (newSubmenuCell) [newSubmenuCell free];
if (newMenu) [newMenu free];
if (openSubmenuCell) [openSubmenuCell free];
if (openMenu) [openMenu free];
[docClassList free];
return [super free];
}
- awakeFromNib
// Set the updateActions for the menu items we'll manage.
{
if (docMenu) {
[NXApp setAutoupdate:YES];
[docMenu setDelegate:self];
[openCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[newCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[saveCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[saveAsCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[saveToCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[saveAllCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[revertCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
[closeCell setUpdateAction:@selector(menuUpdate:) forMenu:docMenu];
}
[self MO_resetOpenMenus];
return self;
}
- new:sender
// create a new untitled doc. This is only used when we have one
// document class
{
id newDoc;
if ([docClassList count] != 1) {
NXBeep();
return nil;
}
newDoc = [[[docClassList objectAt:0] allocFromZone:[self zone]]
initFromFile:NULL manager:self];
if (!newDoc) {
return nil;
}
return self;
}
- newFromDocumentClass:sender
// Create a new untitled doc from the class that's at the same position
// in our doc class list as the selectedRow of the sender. This is only
// used when there is more than one doc class.
{
id newDoc = [[[docClassList objectAt:[sender selectedTag]]
allocFromZone:[self zone]] initFromFile:NULL manager:self];
if (!newDoc) {
return nil;
}
return self;
}
static BOOL isExtensionInList(const char *ext, List *list, int *pos)
// Return YES and the position of the type in the list if we find a match.
// If there is no match, try matching a NULL extension. Returns YES only if
// the extension is actually in the list. Returns NO if not. pos is set to
// the position of the entry if the match is made. If no match is made, but
// there is a null-extension type in the list, it's position is put in pos.
// Otherwise pos is set to -1.
{
int i, c = [list count];
for (i=0; i<c; i++) {
const char *curExt = [[list objectAt:i] typeExtension];
// They match if they are both empty or if they match
if ((!ext || !*ext) && (!curExt || !*curExt)) {
if (pos) *pos = i;
return YES;
}
if (ext && curExt && (strcmp(ext, curExt)) == 0) {
if (pos) *pos = i;
return YES;
}
}
if (ext && *ext) {
if (!isExtensionInList(NULL, list, pos)) {
if (pos) *pos = -1;
}
} else {
if (pos) *pos = -1;
}
return NO;
}
- open:sender
// Allow the user to open a document from disk. We must handle all
// doc extensions supported by all doc controller classes. If two
// classes support reading the same extension, the first class which
// supports that extension is used to open files with that extension.
{
id openPanel = [OpenPanel new];
int currentClass, docClassCount, currentType, typeCount;
List *openTypes = nil;
// We'll fill this list with each open type supported by each doc
// controller class. No duplicates, though.
List *allTypes = [[List allocFromZone:[self zone]] init];
const char **types = NULL;
const char *aType;
BOOL useTypes = YES;
// First, fill allTypes and classForTypes with info for all the extensions
// we need to handle.
docClassCount = [docClassList count];
if (docClassCount == 0) {
[allTypes free];
return nil;
}
for (currentClass = 0; currentClass < docClassCount; currentClass++) {
openTypes = [[docClassList objectAt:currentClass]
getOpenTypesList:nil];
typeCount = [openTypes count];
for (currentType = 0; currentType < typeCount; currentType++) {
MODocType *type = [openTypes objectAt:currentType];
if (!isExtensionInList([type typeExtension], allTypes,
NULL)) {
[allTypes addObject:type];
}
}
[openTypes free];
}
// Now make a nasty array like the open panel wants.
typeCount = [allTypes count];
if (typeCount == 0) {
[allTypes free];
return nil;
}
NX_MALLOC(types, char *, typeCount+1);
for (currentType=0; currentType<typeCount; currentType++) {
aType = [[allTypes objectAt:currentType] typeExtension];
useTypes = (useTypes && aType && *aType);
types[currentType] = (aType?aType:"");
}
types[typeCount] = NULL;
// Now run the damn thing.
[openPanel allowMultipleFiles:NO];
[openPanel chooseDirectories:NO];
[openPanel setAccessoryView:nil];
[openPanel setDelegate:self];
[openPanel setTreatsFilePackagesAsDirectories:NO];
if ([openPanel runModalForTypes:(useTypes?types:NULL)]) {
MOPathString *path = [[MOPathStringClass allocFromZone:[self zone]]
initPath:[openPanel filename]];
MOPathString *extension = [path fileExtension];
// Now we need to open the damn thing with the right class.
int index = -1;
isExtensionInList([extension stringValue], allTypes, &index);
if (index < 0) {
// we shouldn't have been allowed to open the doc if we can't find
// a type for it... why are we here
[allTypes free];
if (types) NX_FREE(types);
[path free];
[extension free];
return nil;
}
[self openDocument:[path stringValue]
withClass:[[allTypes objectAt:index] controllerClass]];
[extension free];
[path free];
}
[allTypes free];
if (types) NX_FREE(types);
return self;
}
- openFromDocumentClass:sender
// This runs the open panel for the types which are supported by the
// document class at the same position in our doc class list as the
// selectedRow of the sender. This is used when there is more than
// one doc class.
{
id openPanel = [OpenPanel new];
id openTypes = [[docClassList objectAt:[sender selectedTag]]
getOpenTypesList:nil];
int i, c;
const char **types = NULL;
const char * aType;
BOOL useTypes = YES;
c = [openTypes count];
if (c == 0) {
[openTypes free];
return nil;
}
NX_MALLOC(types, char *, c+1);
for (i=0; i<c; i++) {
aType = [[openTypes objectAt:i] typeExtension];
useTypes = (useTypes && aType && *aType);
types[i] = (aType?aType:"");
}
types[i] = NULL;
[openPanel allowMultipleFiles:NO];
[openPanel chooseDirectories:NO];
[openPanel setAccessoryView:nil];
[openPanel setDelegate:self];
[openPanel setTreatsFilePackagesAsDirectories:NO];
if ([openPanel runModalForTypes:(useTypes?types:NULL)]) {
[self openDocument:[openPanel filename]
withClass:[docClassList objectAt:[sender selectedTag]]];
}
NX_FREE(types);
[openTypes free];
return self;
}
- openDocument:(const char *)path withClass:(Class)docClass;
// Make a new documentof the given class and initialize it from the given file.
{
id newDoc;
newDoc = [[docClass allocFromZone:[self zone]]
initFromFile:path manager:self];
return self;
}
- save:sender
// Tell the current doc to save.
{
if (currentDoc) {
[currentDoc save:self];
}
return self;
}
- saveAs:sender
// Tell the current doc to save as.
{
if (currentDoc) {
[currentDoc saveAs:self];
}
return self;
}
- saveTo:sender
// Tell the current doc to save to.
{
if (currentDoc) {
[currentDoc saveTo:self];
}
return self;
}
- saveAll:sender
// Save all the open docs.
{
int i, c = [docList count];
id temp;
for (i=0; i<c; i++) {
temp = [docList objectAt:i];
[temp saveIfNeeded];
}
return self;
}
- revert:sender
// Tell the current doc to revert.
{
if (currentDoc) {
if (NXRunAlertPanel("Revert", "Do you really want to revert to "
"%s version? This cannot be undone.",
"Revert", "Cancel", NULL,
([currentDoc isUntitled]?"blank":"previously saved")) ==
NX_ALERTDEFAULT) {
[currentDoc revert:self];
}
}
return self;
}
- close:sender
// Tell the current doc to close.
{
if (currentDoc) {
[currentDoc close:self];
}
return self;
}
- print:sender
// Tell the current doc to print.
{
if (currentDoc) {
[currentDoc print:self];
}
return self;
}
- getNextWindowLocation:(NXPoint *)pt
// Get the location for the next document window.
{
pt->x = windowStartingLocation.x + (FRAME_ADJUST * frameCycle);
pt->y = windowStartingLocation.y - (FRAME_ADJUST * frameCycle);
frameCycle++;
if (frameCycle>=MAX_FRAME_CYCLE) frameCycle = 0;
return self;
}
- (int)nextUntitledNum
// Get the number for the next untitled doc.
{
untitledCount++;
return untitledCount;
}
- setWindowStartingLocation:(const NXPoint *)pt
// Set the starting window location from which the staggering of doc
// windows will begin. This should be set before any windows are
// created for best results.
{
windowStartingLocation.x = pt->x;
windowStartingLocation.y = pt->y;
return self;
}
- addDocument:(MODocController *)aDocument
// Add a new document to our list. DocController calls this automatically.
{
[docList addObject:aDocument];
return self;
}
- removeDocument:(MODocController *)aDocument
// Remove a document to our list. (DocController calls this when the
// document is closing.)
{
[docList removeObject:aDocument];
if (currentDoc == aDocument) {
currentDoc = nil;
}
return self;
}
- (MODocController *)findDocumentForWindow:aWindow
// Find the controller that controls the given window (if any).
{
int i, c = [docList count];
id temp;
for (i=0; i<c; i++) {
temp = [docList objectAt:i];
if ([temp window] == aWindow) {
return temp;
}
}
return nil;
}
- (List *)documentList
// Return the List object we use to store our docs.
{
return docList;
}
- makeDocumentsPerform:(SEL)aMethod with:anArg
// Distribute a message to the documents.
{
[docList makeObjectsPerform:aMethod with:anArg];
return self;
}
- (BOOL)areDocumentsDirty
// Returns YES if at least one open document is dirty.
{
int i, c = [docList count];
for (i=0; i<c; i++) {
if ([[docList objectAt:i] isDirty]) {
return YES;
}
}
return NO;
}
- setCurrentDocument:(MODocController *)aDocument
// Sets the current doc. Called from DocController's window delegate
// methods.
{
currentDoc = aDocument;
return self;
}
- currentDocument
// Returns the current doc's controller.
{
return currentDoc;
}
- addDocumentClass:(Class)docControllerClass
// Add the class to the list of classes we manage. Rebuilds the Document menu.
{
[docClassList addObject:docControllerClass];
[self MO_resetOpenMenus];
return self;
}
- removeDocumentClass:(Class)docControllerClass
// Remove the class from the list of classes we manage.
// Rebuilds the Document menu.
{
[docClassList removeObject:docControllerClass];
[self MO_resetOpenMenus];
return self;
}
- (List *)documentClassList
// Return the list of classes we manage.
{
return docClassList;
}
- (int)docClassCount
// Returns the number of classes that this manager is managing.
{
return [docClassList count];
}
- appWillTerminate:sender
// If there are unsaved docs, put up a panel asking if the user wants to
// Review Unsaved, Save All, Quit Anyway, or Cancel.
{
int ret;
int i, c;
if ([self areDocumentsDirty]) {
if (!quitPanel) {
if (MOKitBundle) {
char pathBuff[MAXPATHLEN+1];
if (![MOKitBundle getPath:pathBuff
forResource:QUITPANEL_NIBNAME ofType:NULL]) {
NXLogError("%s: failed to find nib file %s in "
"MOKit bundle.",
[[self class] name], QUITPANEL_NIBNAME);
return nil;
}
if (![NXApp loadNibFile:pathBuff owner:self]) {
NXLogError("%s: failed to load nib file %s.",
[[self class] name], pathBuff);
return nil;
}
} else {
if (![NXApp loadNibSection:QUITPANEL_NIBNAME owner:self]) {
NXLogError("%s: failed to load nib file %s.",
[[self class] name], QUITPANEL_NIBNAME);
return nil;
}
}
}
ret = [NXApp runModalFor:quitPanel];
[quitPanel orderOut:self];
if (ret == QUITPANEL_REVIEW) {
c = [docList count];
for (i=c-1;i>=0;i--) {
[[[docList objectAt:i] window] makeKeyAndOrderFront:self];
if ([[docList objectAt:i] windowWillClose:[[docList
objectAt:i] window]] == nil) {
return nil;
}
[[[docList objectAt:i] window] orderOut:self];
}
return self;
} else if (ret == QUITPANEL_SAVEALL) {
[self saveAll:self];
return self;
} else if (ret == QUITPANEL_QUIT) {
return self;
} else if (ret == QUITPANEL_CANCEL) {
return nil;
}
}
return self; // nil to abort termination
}
- quitPanelStopModalAction:sender
// This is the action for the buttons in the quit panel.
{
[NXApp stopModal:[sender selectedTag]];
return self;
}
#define QUITPANEL_OK_BUTTON_ROW 1
#define QUITPANEL_OK_BUTTON_COL 2
- windowDidBecomeKey:sender
// For the quit panel... make sure that the return icon is only there
// if the window is key.
{
if (sender == quitPanel) {
[[buttonMatrix cellAt:QUITPANEL_OK_BUTTON_ROW:QUITPANEL_OK_BUTTON_COL]
setIconPosition:NX_ICONRIGHT];
[buttonMatrix setIcon:"NXreturnSign"
at:QUITPANEL_OK_BUTTON_ROW:QUITPANEL_OK_BUTTON_COL];
}
return self;
}
- windowDidResignKey:sender
// For the quit panel... make sure that the return icon is only there
// if the window is key.
{
if (sender == quitPanel) {
[[buttonMatrix cellAt:QUITPANEL_OK_BUTTON_ROW:QUITPANEL_OK_BUTTON_COL]
setIconPosition:NX_TITLEONLY];
[buttonMatrix setIcon:NULL
at:QUITPANEL_OK_BUTTON_ROW:QUITPANEL_OK_BUTTON_COL];
}
return self;
}
- (BOOL)menuUpdate:menuCell
// This is the updateAction for all the Document menu stuff.
{
BOOL enabled = [menuCell isEnabled];
BOOL docsDirty;
if ((menuCell == openCell) || (menuCell == newCell)) {
if (([docClassList count] == 0) && (enabled)) {
[menuCell setEnabled:NO];
return YES;
} else if (!enabled) {
[menuCell setEnabled:YES];
return YES;
}
} else if ((menuCell == saveCell) || (menuCell == saveAsCell) ||
(menuCell == saveToCell) || (menuCell == closeCell)) {
if ((currentDoc == nil) && (enabled)) {
[menuCell setEnabled:NO];
return YES;
} else if ((currentDoc != nil) && (!enabled)) {
[menuCell setEnabled:YES];
return YES;
}
} else if (menuCell == saveAllCell) {
docsDirty = [self areDocumentsDirty];
if ((docsDirty) && (!enabled)) {
[menuCell setEnabled:YES];
return YES;
} else if ((!docsDirty) && (enabled)) {
[menuCell setEnabled:NO];
return YES;
}
} else if (menuCell == revertCell) {
if ((currentDoc == nil) && (enabled)) {
[menuCell setEnabled:NO];
return YES;
} else if (currentDoc != nil) {
if (([currentDoc isDirty]) && (!enabled)) {
[menuCell setEnabled:YES];
return YES;
} else if ((![currentDoc isDirty]) && (enabled)) {
[menuCell setEnabled:NO];
return YES;
}
}
}
return NO;
}
- awake
// Initialize stuff we don't archive.
{
quitPanel = nil;
buttonMatrix = nil;
newMenu = nil;
newSubmenuCell = nil;
openMenu = nil;
openSubmenuCell = nil;
return self;
}
- read:(NXTypedStream *)strm
// This method is probably not useful. But here it is.
{
int classVersion;
const NXScreen *screen = NULL;
[super read:strm];
classVersion = NXTypedStreamClassVersion(strm, CLASS_NAME);
switch (classVersion) {
case 0: // First version.
docList = NXReadObject(strm);
currentDoc = NXReadObject(strm);
docClassList = NXReadObject(strm);
NXReadType(strm, "i", &untitledCount);
NXReadType(strm, "i", &frameCycle);
NXReadPoint(strm, &windowStartingLocation);
docMenu = NXReadObject(strm);
openCell = NXReadObject(strm);
newCell = NXReadObject(strm);
saveCell = NXReadObject(strm);
saveAsCell = NXReadObject(strm);
saveToCell = NXReadObject(strm);
saveAllCell = NXReadObject(strm);
revertCell = NXReadObject(strm);
closeCell = NXReadObject(strm);
break;
default:
NXLogError("[%s read:] class version %d cannot read "
"instances archived with version %d",
CLASS_NAME, CLASS_VERSION, classVersion);
docList = [[List allocFromZone:[self zone]] init];
currentDoc = nil;
docClassList = [[List allocFromZone:[self zone]] init];
untitledCount = 0;
frameCycle = 0;
screen = [NXApp mainScreen];
windowStartingLocation.x = FRAME_START_LOC_X;
windowStartingLocation.y = screen->screenBounds.size.height -
FRAME_START_LOC_Y;
docMenu = nil;
openCell = nil;
newCell = nil;
saveCell = nil;
saveAsCell = nil;
saveToCell = nil;
saveAllCell = nil;
revertCell = nil;
closeCell = nil;
break;
}
return self;
}
- write:(NXTypedStream *)strm
// This method is probably not useful. But here it is.
{
[super write:strm];
NXWriteObject(strm, docList);
NXWriteObject(strm, currentDoc);
NXWriteObject(strm, docClassList);
NXWriteType(strm, "i", &untitledCount);
NXWriteType(strm, "i", &frameCycle);
NXWritePoint(strm, &windowStartingLocation);
NXWriteObjectReference(strm, docMenu);
NXWriteObjectReference(strm, openCell);
NXWriteObjectReference(strm, newCell);
NXWriteObjectReference(strm, saveCell);
NXWriteObjectReference(strm, saveAsCell);
NXWriteObjectReference(strm, saveToCell);
NXWriteObjectReference(strm, saveAllCell);
NXWriteObjectReference(strm, revertCell);
NXWriteObjectReference(strm, closeCell);
return self;
}
- MO_resetOpenMenus
// Keeps the document menu in step with changes in the manager's class list.
// This is the method that makes sure the Open and New stuff is correct for
// the document classes being managed.
{
id itemList;
int i, c;
const char *name;
id cell;
int row, col;
// See if the nib is loaded and we do menus
if (!docMenu) {
return nil;
}
if ([docClassList count] > 1) {
// We should have submenus for new and open.
// First do the newMenu
if (!newMenu) {
newMenu = [[Menu allocFromZone:[Menu menuZone]]
initTitle:NEW_MENU_TITLE];
}
itemList = [newMenu itemList];
[itemList renewRows:0 cols:1];
c = [docClassList count];
for (i=0; i<c; i++) {
name = [[[docClassList objectAt:i] controllerName] stringValue];
[itemList addRow];
cell = [itemList cellAt:i:0];
[cell setTitle:name];
[cell setTag:i];
[cell setTarget:self];
[cell setAction:@selector(newFromDocumentClass:)];
}
[newMenu sizeToFit];
// Now make a newSubmenuItem
if (!newSubmenuCell) {
newSubmenuCell = [[MenuCell allocFromZone:[Menu menuZone]]
initTextCell:NEW_MENU_TITLE];
}
// Put the submenu item in the doc menu in place of the new item
itemList = [docMenu itemList];
if (![itemList getRow:&row andCol:&col ofCell:newSubmenuCell]) {
[itemList getRow:&row andCol:&col ofCell:newCell];
[itemList putCell:newSubmenuCell at:row :col];
[docMenu setSubmenu:newMenu forItem:newSubmenuCell];
[docMenu sizeToFit];
}
// Now do the open menu
if (!openMenu) {
openMenu = [[Menu allocFromZone:[Menu menuZone]]
initTitle:OPEN_MENU_TITLE];
}
itemList = [openMenu itemList];
[itemList renewRows:0 cols:1];
c = [docClassList count];
for (i=0; i<c; i++) {
name = [[[docClassList objectAt:i] controllerName] stringValue];
[itemList addRow];
cell = [itemList cellAt:i:0];
[cell setTitle:name];
[cell setTag:i];
[cell setTarget:self];
[cell setAction:@selector(openFromDocumentClass:)];
}
[openMenu sizeToFit];
// Now make an openSubmenuItem
if (!openSubmenuCell) {
openSubmenuCell = [[MenuCell allocFromZone:[Menu menuZone]]
initTextCell:OPEN_MENU_TITLE];
}
// Put the submenu item in the doc menu below the open item
itemList = [docMenu itemList];
[itemList getRow:&row andCol:&col ofCell:openCell];
if ([itemList cellAt:row+1:col] != openSubmenuCell) {
[itemList insertRowAt:row+1];
[[itemList putCell:openSubmenuCell at:row+1 :col] free];
[docMenu setSubmenu:openMenu forItem:openSubmenuCell];
[docMenu sizeToFit];
}
} else {
// We should not have submenus.
itemList = [docMenu itemList];
// take out the new submenu item if its there and put back the new item
if ([itemList getRow:&row andCol:&col ofCell:newSubmenuCell]) {
[itemList putCell:newCell at:row :col];
}
// take out the open submenu item if its there
if ([itemList getRow:&row andCol:&col ofCell:openSubmenuCell]) {
[itemList removeRowAt:row andFree:NO];
}
[docMenu sizeToFit];
}
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.