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.