This is CEAppManager.m in view mode; [Download] [Up]
/* CEAppManager.m * * This is the basic controller of the App. It manages all big abstraction * tasks for the panels, documents and connections to the outside. * * For interface-info see the header file. The comments in this file mostly * cover only the real implementation details. * * Written by: Thomas Engel * Created: 23.10.1993 (Copyleft) * Last modified: 07.05.1994 */ #import <CEAppManager.h> #import <CEClassEditor.h> #import <CEListener.h> #import <CEInfo.h> #import <Text_MiscExtensions.h> #import <MiscEmacsText.h> #import <MiscCompletionText.h> #import <misckit/misckit.h> @implementation CEAppManager - appWillInit:sender { // This is the init part we need to pass before we get the messages that // we have to open some files. // To be able to init a class we need the have the Preferences, // preferences = [CEPreferencesManager new]; // Now lets load the docu templates NXStream * aStream; id aPath; id ourListener; // Setup out own Listener...this is really nasty..anyway. [[NXApp appListener] free]; ourListener = [[CEListener alloc] init]; [ourListener setDelegate:self]; [ourListener checkInAs:[NXApp appName]]; [ourListener addPort]; [NXApp setAppListener:ourListener]; // Well now this is a litle rough....maybe we should search for those files // inside the ~/AppInfo or even *Library/ClassEditor sections..anyway. aPath = [MiscString newWithString: [(NXBundle *)[NXBundle mainBundle] directory]]; [aPath cat:"/CEClassDocuTemplate.rtf"]; if( [aPath doesExistInFileSystem] ) { aStream = NXMapFile( [aPath stringValue], NX_READONLY ); [classDocTemplateView readRichText:aStream]; NXCloseMemory( aStream, NX_FREEBUFFER ); } [aPath free]; aPath = [MiscString newWithString: [(NXBundle *)[NXBundle mainBundle] directory]]; [aPath cat:"/CEMethodDocuTemplate.rtf"]; if( [aPath doesExistInFileSystem] ) { aStream = NXMapFile( [aPath stringValue], NX_READONLY ); [methodDocuTemplateView readRichText:aStream]; NXCloseMemory( aStream, NX_FREEBUFFER ); } [aPath free]; // Here comes the hack... // [MiscCompletionText poseAs:[Text class]]; [MiscEmacsText poseAs:[Text class]]; // Let's see if we have the necessary defaults at hand ! // Either hardcode them...or adjust them with a DefaultsEditor if( ![NXApp knowsDefaultValue:"HeaderDirectories"] ) [NXApp setDefault:"HeaderDirectories" to:".\t"\ "~/Developer/Headers\t"\ "/LocalDeveloper/Headers\t"\ "/LocalDeveloper/Headers/misckit\t"\ "/NextDeveloper/Headers/appkit\t"\ "/NextDeveloper/Headers/foundation\t"\ "/NextDeveloper/Headers/eoaccess\t"\ "/NextDeveloper/Headers/eointerface\t"\ "/NextDeveloper/Headers/3Dkit\t"\ "/NextDeveloper/Headers/indexing\t"\ "/NextDeveloper/Headers/eoaccess\t"\ "/NextDeveloper/Headers/nikit\t"\ "/NextDeveloper/Headers/remote\t"\ "/NextDeveloper/Headers/dbkit\t"\ "/NextDeveloper/Headers/soundkit"]; // NOTE: We might use the source pathes for header search too. Should // take a look at it some day.. if( ![NXApp knowsDefaultValue:"SourceDirectories"] ) [NXApp setDefault:"SourceDirectories" to:".\t"\ "~/Developer"]; if( ![NXApp knowsDefaultValue:"DocumentationDirectories"] ) [NXApp setDefault:"DocumentationDirectories" to:".\t"\ "./Documentation\t"\ "/LocalDeveloper/Documentation/MiscKit\t"\ "/NextLibrary/Documentation/NextDev/GeneralRef/02_ApplicationKit"]; // Here we will write down the defaults which show the ASCII prettyprinting // This DEFINITLY is stupid..it should be customizable. [NXApp setDefault:"text0" to:"Helvetica \\fs24 \\red0\\green0\\blue0 \\ul0"]; [NXApp setDefault:"text1" to:"Helvetica-Bold \\fs24 \\red0\\green0\\blue0 \\ul0"]; [NXApp setDefault:"text2" to:"Times-Bold \\fs28 \\red0\\green0\\blue0 \\ul0"]; [NXApp setDefault:"text3" to:"Helvetica \\fs24 \\red85\\green85\\blue85 \\ul0"]; [NXApp setDefault:"text4" to:"Ohlfs \\fs20 \\red4\\green8\\blue114 \\ul0"]; [NXApp setDefault:"text5" to:"Courier-Bold \\fs24 \\red0\\green0\\blue0 \\ul0"]; [NXApp setDefault:"text6" to:"Ohlfs \\fs20 \\red0\\green0\\blue0 \\ul0"]; // Now lets get all the Prefs into the real objects [self _readPreferences]; return self; } - appDidInit:sender { // This is some kind of late init. These object are need for working but // not for reading the classes or having a class initialized. return self; } - (int)app:sender openFile:(const char *)path type:(const char *)type { // This method is performed whenever a user opens a document from the // Workspace Manager. // We use our default file open method with some default params. return [self openFile:path onHost:"*" atTrueLine:0]; } - (BOOL)appAcceptsAnotherFile:sender { // Inform the workspace that we can open multiple files. return YES; } - appDidBecomeActive:sender { return self; } - appWillTerminate:sender { // Now lets see what we have...ask every window what is going on. BOOL ready; int i; id windowList; int count; int result; windowList = [NXApp windowList]; count = 0; for( i=0; i<[windowList count]; i++ ) if( [[windowList objectAt:i] isDocEdited] ) count++; // Well if there are windows with unsaved contents we should tell the // user about it. // We won't leave until the user has decided what to do on the top level. if( count == 0 ) return self; ready = NO; while( !ready ) { result = NXRunAlertPanel( "Quit", "There are edited windows.", "Review Unsaved", "Quit Anyway", "Cancel" ); if( result == NX_ALERTOTHER ) return nil; else if( result == NX_ALERTDEFAULT ) ready = [self _closeAllWindows]; else ready = YES; } return self; } - (BOOL)_closeAllWindows { // We will try to close all windows and if we succeed we will let the app // know. YES = all closed // NOTE: Well this is kind of ugly because the app should maintain a list // of all the controllers that are responsible for some data. But I // don't want to hack down another list access stuff. So we do it // this way. Maybe in the future I will change that. id delegateList; id aDelegate; int i; id windowList; BOOL success; windowList = [NXApp windowList]; delegateList = [List new]; // Lets find all the delegates first. for( i=0; i<[windowList count]; i++ ) { aDelegate = [[windowList objectAt:i] delegate]; if( aDelegate != nil && [aDelegate isKindOf:[CEClassEditor class]] ) [delegateList addObjectIfAbsent:aDelegate]; } success = YES; for( i=0; i<[delegateList count]; i++ ) { if( [[delegateList objectAt:i] close:self] == nil ) { success = NO; break; } } return success; } - addToReleasePool:anObject { return [NXApp delayedFree:anObject]; } - (int)openFile:(char *)path onHost:(char *)host atTrueLine:(int *)line; { // This method is performed whenever a user opens a document. // It takes care that every file is only opened once. id newEditor; id aString; id basename; id filetype; id thePath; id windowList; id aDelegate; int i; BOOL loadIt; // First we have to check if we already know this file ! // Some window delegate should have the same filename (without .h or .m) // Using the window list might not be the safest way..but I guess it // will work. aString = [MiscString newWithString:path]; filetype = [aString fileExtension]; newEditor = nil; if( [filetype cmp:"m"] != 0 && [filetype cmp:"h"] != 0 && [filetype cmp:"rtf"] != 0 ) { // Looks like this is no valid file ! [aString free]; [filetype free]; return -1; } // Now that it is clear that this is a valid file... [NXApp activateSelf:NO]; windowList = [NXApp windowList]; basename = [aString fileBasename]; thePath = [aString pathName]; [thePath addChar:[MiscString pathSeparator]]; [thePath concatenate:basename]; [basename free]; [aString free]; loadIt = YES; for( i=0; i<[windowList count]; i++ ) { aDelegate = [[windowList objectAt:i] delegate]; if( [aDelegate respondsTo:@selector(filename)] && aDelegate != nil ) { if( [thePath cmp:[aDelegate filename]] == 0 ) { // Looks like we have found a delegate that belongs to exactly // the same path. // We must to ask this delegate now what window we should top. // If we have to open at a certain line will will skip that. // opening at a line will currently bring the cheat window to // front anyway...this prevents window cluttter. if( line == 0 ) [[aDelegate window] makeKeyAndOrderFront:self]; loadIt = NO; // If we have to open at a certain line we should remember // who is controlling that window. newEditor = aDelegate; } } } [thePath free]; // If we really have a new editor...let the world know about it. if( loadIt ) { newEditor = [[CEClassEditor alloc] initFromFile:path]; if( !newEditor ) { [filetype free]; return NO; } } // Now lets see..do we have to select a certain line ? // If there is on ask the editor to do so. if( line > 0 ) { if( [filetype cmp:"m"] == 0 ) [newEditor selectTrueLine:(int)line inSource:YES]; else if( [filetype cmp:"h"] == 0 ) [newEditor selectTrueLine:(int)line inSource:NO]; } [filetype free]; return YES; } - showInfo:sender { if( !info ) info = [CEInfo new]; [info makeKeyAndOrderFront:self]; return self; } - sendSuggestion:sender { id mailer; id tomiAdr; id subject; id theText; tomiAdr = [MiscString newWithString: "tsengel@cip.informatik.uni-erlangen.de"]; subject = [MiscString newWithString: "ClassEditor v0.4 suggestion"]; theText = [MiscString newWithString: "Here is a little suggestion on ClassEditor v0.4:\n"]; mailer = [MiscMailApp localMailer]; [mailer setNoclutter:NO]; [mailer sendMailTo:tomiAdr subject:subject body:theText]; [theText free]; [tomiAdr free]; [subject free]; return self; } - showAccessHelper:sender { [accessHelper makeKeyAndOrderFront:self]; return self; } - showPreferences:sender { // if( preferences ) [preferences makeKeyAndOrderFront:self]; return self; } - preferences { return self; } - _readPreferences { id aString; id someString; id aList; int i; aString = [MiscString newWithString: [NXApp defaultValue:"HeaderDirectories"]]; headerSearchPathes = [aString tokenize:"\t" into:nil]; [aString setStringValue:[NXApp defaultValue:"SourceDirectories"]]; sourceSearchPathes = [aString tokenize:"\t" into:nil]; // We will check the paths plus path/Classes path/Categories and // path/Protocols [aString setStringValue:[NXApp defaultValue:"DocumentationDirectories"]]; aList = [aString tokenize:"\t" into:nil]; [aString free]; docuSearchPathes = [List new]; for( i=0; i<[aList count]; i++ ) { aString = [[aList objectAt:i] copy]; [docuSearchPathes addObject:aString]; someString = [aString copy]; [someString cat:"/Classes"]; [docuSearchPathes addObject:someString]; someString = [aString copy]; [someString cat:"/Categories"]; [docuSearchPathes addObject:someString]; someString = [aString copy]; [someString cat:"/Protocols"]; [docuSearchPathes addObject:someString]; } [[aList freeObjects] free]; return self; } - headerSearchPaths { return headerSearchPathes; } - sourceSearchPaths { return sourceSearchPathes; } - documentationSearchPaths { return docuSearchPathes; } - open:sender { id openPanel; char fullName[MAXPATHLEN]; const char *const *files; const char *const fileType[6] = { "m", "h", NULL}; openPanel = [[OpenPanel new] allowMultipleFiles:YES]; // Run the open panel, filtering for our type of documents // We try to open every file the panel returns. if( [openPanel runModalForTypes:fileType] ) { files = [openPanel filenames]; for( files = [openPanel filenames]; files && *files; files++) { // Now lets merge the fullFilename from the directory and the // current filename and try to init a editor from this file. // We use the default open method with some default params. sprintf( fullName, "%s/%s", [(OpenPanel *)openPanel directory], *files ); [self openFile:fullName onHost:"*" atTrueLine:0]; } } return self; } - new:sender { [CEClassEditor new]; return self; } - save:sender { // [[NXApp keyWindow] save:sender]; // // This is a posible solution // But we currently work with a firstResponder solution return self; } - saveAs:sender { return self; } - saveTo:sender { return self; } - saveAll:sender { return self; } - revert:sender { return self; } - close:sender { return self; } - print:sender { return self; } - setTextFontStyle:sender { [self _setSelectionFont:textFontField andColor:textFontColorWell]; return self; } - setMethodNameFontStyle:sender { [self _setSelectionFont:methodNameField andColor:methodNameColorWell]; return self; } - setParameterFontStyle:sender { id firstResponder = [[NXApp mainWindow] firstResponder]; if( [firstResponder respondsTo:@selector(setSelFont:)] ) [firstResponder setSelFont:[Font newFont:"Times-Italic" size:14]]; return self; } - setMathSymbolFontStyle:sender { id firstResponder = [[NXApp mainWindow] firstResponder]; if( [firstResponder respondsTo:@selector(setSelFont:)] ) [firstResponder setSelFont:[Font newFont:"Symbol" size:14]]; return self; } - setHeadlinesFontStyle:sender { [self _setSelectionFont:headlinesFontField andColor:headlinesFontColorWell]; return self; } - setExampleCodeFontStyle:sender { [self _setSelectionFont:exampleCodeFontField andColor:exampleCodeFontColorWell]; return self; } - setBlankLinesFontStyle:sender { [self _setSelectionFont:blankLinesFontField andColor:blankLinesFontColorWell]; return self; } - setSourceFontStyle:sender { [self _setSelectionFont:sourceFontField andColor:sourceFontColorWell]; return self; } - setKeywordsFontStyle:sender { [self _setSelectionFont:keywordsFontField andColor:keywordsFontColorWell]; return self; } - setTypecastFontStyle:sender { [self _setSelectionFont:typecastFontField andColor:typecastFontColorWell]; return self; } - setRemarkFontStyle:sender { [self _setSelectionFont:remarkFontField andColor:remarkFontColorWell]; return self; } - setBugNoteFontStyle:sender { [self _setSelectionFont:bugNoteFontField andColor:bugNoteFontColorWell]; return self; } - _setSelectionFont:aFont andColor:aColor { // Well this is a lie...we don't get the font but the TextField that has // the right font set !! id firstResponder = [[NXApp mainWindow] firstResponder]; if( [firstResponder respondsTo:@selector(setSelFont:)] && aFont != nil ) [firstResponder setSelFont:[aFont font]]; if( [firstResponder respondsTo:@selector(setSelColor:)] && aColor != nil ) [firstResponder setSelColor:[aColor color]]; // This is for letting the delegates know about the changes to // be able to track document modification. // The first responder must know about the window because the // textDidChange might require it. if( [firstResponder respondsTo:@selector(window)] ) [[[NXApp mainWindow] delegate] textDidChange:firstResponder]; return self; } - copyClassDocuTemplate { // All we do here is to copy the docu template to the pasteboard. [classDocTemplateView selectAll:self]; [classDocTemplateView copyTo:[Pasteboard newName:"CETempPb"]]; return self; } - copyMethodDocuTemplate { // All we do here is to copy the method template to the pasteboard. [methodDocuTemplateView selectAll:self]; [methodDocuTemplateView copyTo:[Pasteboard newName:"CETempPb"]]; return self; } - windowDidBecomeKey:sender { // now if our Docu window will become key...we should load the text into // it if we havn't yet. NXStream * aStream; id aPath; if( sender != docuTemplateWindow ) return self; if( [docuTemplateTextView textLength] < 10 ) { aPath = [MiscString newWithString: [(NXBundle *)[NXBundle mainBundle] directory]]; [aPath cat:"/DocuTemplate.rtf"]; if( [aPath doesExistInFileSystem] ) { aStream = NXMapFile( [aPath stringValue], NX_READONLY ); [docuTemplateTextView readRichText:aStream]; NXCloseMemory( aStream, NX_FREEBUFFER ); } [aPath free]; } return self; } @end /* * History: 13.01.95 Bla. * * * Bugs: No bugs and birds seen.... */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.