home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-02-14 | 34.7 KB | 1,386 lines |
- /* CEClassEditor.m
- *
- * This object controls the data of a beaker (molecules, cameras, groups etc.)
- * It is the main document of BeakerBoy and controls everything from loading to
- * setting up the browser which does most of the other work.
- *
- * 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: 12.11.1994
- */
-
- #define CURRENT_VERSION 1
-
- #import "CEClassEditor.h"
- #import "CEMethod.h"
- #import "CEBrowserCell.h"
- #import "Text_MiscExtensions.h"
- #import "MiscStringArray_List.h"
-
- #import <misckit/MiscString.h>
- #import <misckit/MiscStringArray.h>
- #import <misckit/MiscSortedList.h>
-
- @implementation CEClassEditor
-
-
- + initialize
- {
- if ( self == [CEClassEditor class] )
- [CEClassEditor setVersion:CURRENT_VERSION];
-
- return self;
- }
-
- - init
- {
- return [self initFromFile:""];
- }
-
- - initFromFile:(const char *)fileName
- {
- id basename;
- id dummyFile;
-
- // this should really become a global part of info !!
-
- id headerSearchPathes;
- id sourceSearchPathes;
- id docuSearchPathes;
-
- headerSearchPathes = [MiscStringArray new];
- [headerSearchPathes addString:"/NextDeveloper/Headers/appkit"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/foundation"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/soundkit"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/3Dkit"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/indexing"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/eointerface"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/eoaccess"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/nikit"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/remote"];
- [headerSearchPathes addString:"/NextDeveloper/Headers/dbkit"];
- [headerSearchPathes addString:"/LocalDeveloper/Headers/misckit"];
- [headerSearchPathes addString:"/LocalDeveloper/Headers/iconkit"];
- [headerSearchPathes addString:"/LocalDeveloper/Headers/WavesWorld"];
-
- // NOTE: We might use the source pathes for header search too. Should
- // take a look at it some day..
-
- sourceSearchPathes = [MiscStringArray new];
- [sourceSearchPathes addString:"~/Developer/ClassEditor"];
- [sourceSearchPathes addString:"~/Developer/BeakerBoy"];
- [sourceSearchPathes addString:"~/Developer/BeakerBoy"];
- [sourceSearchPathes addString:"~/Developer/ClipNotes"];
- [sourceSearchPathes addString:"~/Developer/UNIX-HatersTool"];
-
- docuSearchPathes = [MiscStringArray new];
- [docuSearchPathes addString:"/NextLibrary/Documentation/NextDev/GeneralRef/02_ApplicationKit/Classes"];
- [docuSearchPathes addString:"/LocalDeveloper/Documentation/MiscKit/Classes"];
- [docuSearchPathes addString:"/LocalDeveloper/Documentation/MiscKit/Categories"];
- [docuSearchPathes addString:"/LocalDeveloper/Documentation/MiscKit/Protocols"];
- [docuSearchPathes addString:"/LocalDeveloper/Documentation/IconKit/Classes"];
- [docuSearchPathes addString:"/LocalDeveloper/Documentation/IconKit/Protocols"];
-
- // On we go...
-
- self = [super init];
- if( !self ) return self;
-
- // OK. We really are an object...here we go with our init.
- // Lets load the NIB. It has set our beaker to be its window-delegate.
- // Without a NIB there will be no beaker object !
-
- if( [NXApp loadNibSection:"ClassWindow.nib" owner:self] == nil )
- {
- NXRunAlertPanel( NULL, "Couldn't load ClassWindow.nib",
- "OK", NULL, NULL );
- return nil;
- }
- // Lets setup the names.
- // We need to track all those areas where we update the text to find the
- // places where the user makes changes to the text!
-
- dummyFile = [[MiscString new] setPath:fileName];
-
- basename = [dummyFile fileBasename];
- filename = [dummyFile pathName];
- [filename addChar:[MiscString pathSeparator]];
- [filename concatenate:basename];
- [basename free];
-
- // Get the paseboard and ensure that nobody will see the texthandling we
- // do now.
-
- _tempPb = [Pasteboard newName:"CETempPb"];
- [self _setIsUpdatingTextViews:YES];
-
- // Now we will always refer to the file WITHOUT any extention.
- // If we are lucky we will be able to read it all..m, .h and .rtf
- // If we were able to read the file...make the view editable.
-
- [dummyFile takeStringValue:filename];
- [dummyFile addExtensionIfNeeded:"h"];
- [self _try:dummyFile withAlternatives:headerSearchPathes
- forText:headerFile andGetStyle:&headerStyle];
-
- // Well we really need a header file... it is crucial !
-
- if( headerStyle == CE_FILETYPE_NONE )
- {
- basename = [dummyFile fileBasename];
- NXRunAlertPanel( NULL, "Couldn't load %s.h. Not in any searchpath.",
- "Ooops", NULL, NULL,
- [basename stringValue] );
- [basename free];
- return nil;
- }
-
- // The next part is loading the source code for that class.
- // This can be either ASCII or RTF !
-
- [dummyFile takeStringValue:filename];
- [dummyFile addExtensionIfNeeded:"m"];
- [self _try:dummyFile withAlternatives:sourceSearchPathes
- forText:sourceFile andGetStyle:&sourceStyle];
-
- // Now the documentation. if there is no RTF docu we should ty to find a
- // RTFD docu too !
-
- [dummyFile takeStringValue:filename];
- [dummyFile addExtensionIfNeeded:"rtf"];
- [self _try:dummyFile withAlternatives:docuSearchPathes
- forText:docuFile andGetStyle:&docuStyle];
-
- [dummyFile free];
-
- // Its time to check all the methods we can find.
-
- methodList = [List new];
- [self reparseMethods:self];
-
- // If desired we should check to documentation first...maybe we have to add
- // something before we start. But the methods have to be know !!
-
- // if( [preferences should AutoCheckDocu] )
-
- [self checkDocumentation:self];
-
- // Now there are some window settings to be done.
-
- [browser setCellPrototype:[CEBrowserCell new]];
- [browser setTarget:self];
- [browser setAction:@selector(selectNewMethod:)];
- [browser setDoubleAction:@selector(selectNewMethod:)];
- [browser loadColumnZero];
-
- [window setTitleAsFilename:[self filename]];
- [window setMiniwindowImage:[NXImage findImageNamed:"CESmallObjectPink"]];
-
- // Now lets connect the popUp
-
- [[popUp target] setTarget:self];
- [[popUp target] setAction:@selector(switchToNewDisplayMode:)];
-
- // Well this is ugly.
- // We should update all the view bevor it gets visible....but then we
- // have no selected cell and bang..crash..not method..and bang..not string
- // for the Text object. And the find methdo crashes...
- // [browser selectCell:[browser getLoadedCellAtRow:0 inColumn:0]];
- // somehow this doesn't work...but I don't want to waste my time.
- // Ok I fixed the selectMethod to not crash...still its not clean.
-
- [self selectNewMethod:self];
-
- // ok now we are a new document so lets become active.
-
- [self _setIsUpdatingTextViews:NO];
- _changedTexts = NO;
- [window makeKeyAndOrderFront:self];
-
- return self;
- }
-
- - _try:path withAlternatives:anArray forText:aText andGetStyle:(int *)aStyle
- {
- id ourPath;
- id theFilename;
- id aSearchPath;
- int trys;
-
- trys = 0;
- *aStyle = CE_FILETYPE_NONE;
- ourPath = [path copy];
- theFilename = [ourPath fileName];
-
- while( *aStyle == CE_FILETYPE_NONE &&
- trys <= [anArray count] )
- {
- [self _readFile:ourPath forText:aText andGetStyle:(int *)aStyle];
-
- aSearchPath = [anArray objectAt:trys];
- [ourPath free];
- ourPath = nil;
- if( aSearchPath != nil )
- {
-
- ourPath = [aSearchPath copy];
- [ourPath addChar:[MiscString pathSeparator]];
- [ourPath concatenate: theFilename];
- }
- trys++;
- }
-
- if( ourPath != nil ) [ourPath free];
- [theFilename free];
- return self;
- }
-
- - _readFile:path forText:aText andGetStyle:(int *)aStyle
- {
- // We will try to load the requested file...and try to set the right
- // text type info.
-
- NXStream * aStream;
-
- [path replaceTildeWithHome];
- if( [path doesExistInFileSystem] == NO )
- return self;
-
- // Looks like we have a file...so lets get it into our text object.
- // We have to ensure that all the setting are in the proper fashion.
- // Setting the tabs for ASCII Files in one of them
-
- if( [self _isFileRTF:path] )
- {
- *aStyle = CE_FILETYPE_RTF;
- [aText setMonoFont:NO];
- }
- else
- {
- *aStyle = CE_FILETYPE_ASCII;
- [aText setMonoFont:YES];
- }
-
- [aText setEditable:YES];
-
- aStream = NXMapFile( [path stringValue], NX_READONLY );
- if( *aStyle == CE_FILETYPE_ASCII )
- [aText readText:aStream];
- else [aText readRichText:aStream];
- NXCloseMemory( aStream, NX_FREEBUFFER );
-
- return self;
- }
- - (BOOL)_isFileRTF:path
- {
- // Tells us if the requested file is a RTF text.
- // Lets get the first characters and test for "{\rtf0"
- // If it is there...we should load the same text as RTF again.
- // Well quite stupid code..
-
- FILE * aStream;
- char magic[10];
- id aString;
- BOOL answer;
-
- magic[6] = 0;
- answer = NO;
-
- if( [path doesExistInFileSystem] )
- {
- aStream = fopen( [path stringValue], "r" );
- fgets( magic, 7, aStream );
- fclose( aStream );
- aString = [MiscString newWithString:magic];
-
- if( [aString cmp:"{\\rtf0"] == 0 )
- answer = YES;
-
- [aString free];
- }
- return answer;
- }
-
- - free
- {
- // We do not free all the NIB objects...hmm should check that out.
-
- [window free];
- [cheatWindow free];
- [filename free];
- [[methodList freeObjects] free];
-
- return [super free];
- }
-
- - (const char *)filename
- {
- return [filename stringValue];
- }
-
- - window
- {
- // We have to return the main window to ensure that the right window gets
- // topped when the app decided that we should become visible.
-
- return window;
- }
-
- /*
- * Here come the methods we will implement to satisfy our window.
- * They are useful to change the inspector and handle oher tasks.
- */
-
- - reparseMethods:sender
- {
- // This is used to keep the two view up to date. Making changes to the
- // We will reset those origin pointers because copying our current text-
- // sections back would most propably destroy something!
-
- _docuOriginStart = 0;
- _docuOriginEnd = 0;
- _sourceOriginStart = 0;
- _sourceOriginEnd = 0;
- _changedTexts = NO;
-
- [self _setIsUpdatingTextViews:YES];
-
- [methodList freeObjects];
- [self _parseMethodFile];
- [self checkDocumentation:self];
-
- [browser loadColumnZero];
- [self _setIsUpdatingTextViews:NO];
-
- return self;
- }
-
- - _parseMethodFile
- {
- id newLine;
- id newMethod;
- int lines;
- int i;
-
- lines = [headerFile lineFromPosition:[headerFile textLength]];
-
- for( i=1; i<lines; i++ )
- {
- newLine = [headerFile substringFromLine:i];
- if ( [newLine charAt:0] == '-' ||
- [newLine charAt:0] == '+' )
- {
- newMethod = [[CEMethod alloc] initFromText:newLine];
- [methodList addObject:newMethod];
- }
- [newLine free];
- }
-
- return self;
- }
-
- - save:sender
- {
- // Here we will save all the files in a stupid fashion...anyway.
- // It works for the moment.
- // We will only save file we were able to load !!
-
- id dummyFile;
- id topWindow;
-
- if( ![window isDocEdited] ) return self;
-
- // Now lets be sure that ALL changes are in the main files...and
- // Nobody sees the changes going on.
- // But this should ONLY happen when the key window is or class browser.
- // We might corrupt data otherwise !!! The selection might not refer to the
- // right positions anymore !
- // We have to take care of which window was the source of the last
- // cahnges. Depening on that we will hvae to make some consitency
- // adjustment work !
-
- if( _changedTexts )
- topWindow = _changedWindow;
- else topWindow = nil;
-
- if( topWindow == window )
- [self _silentlySyncWindows];
-
- // Now if the class has been edited we will save the ASCII Header,
- // RTF source and RTF Documentaion. Quite stupid...but it works for me.
-
- dummyFile = [filename copy];
- [dummyFile addExtensionIfNeeded:"h"];
- [self _writeText:headerFile withStyle:headerStyle to:dummyFile];
-
- [dummyFile takeStringValue:filename];
- [dummyFile addExtensionIfNeeded:"m"];
- [self _writeText:sourceFile withStyle:sourceStyle to:dummyFile];
-
- [dummyFile takeStringValue:filename];
- if( docuStyle == CE_FILETYPE_RTFD )
- [dummyFile addExtensionIfNeeded:"rtfd"];
- else [dummyFile addExtensionIfNeeded:"rtf"];
- [self _writeText:docuFile withStyle:docuStyle to:dummyFile];
-
- [dummyFile free];
-
- // Ok ... well now there is nothing new here.
-
- [cheatWindow setDocEdited:NO];
- [window setDocEdited:NO];
-
- // Now tell the workspace that we did some changes..
-
- [[Application workspace] fileSystemChanged];
-
- // If we have saved from inside the cheat window we have to update the
- // main browser. Otherwise this one will no recognize the changes made.
-
- if( topWindow == cheatWindow )
- {
- [self reparseMethods:self];
- [self selectNewMethod:self];
- }
-
- return self;
- }
-
- - _writeText:aText withStyle:(int)aStyle to:path
- {
- // If the file does no exist...we will create it.
- // But it there is no real text for that file...we won't save anything !
- // RTFD requires a differnt style of saving !
-
- FILE * dummyStream;
- NXStream * aStream;
-
- if( aStyle == CE_FILETYPE_NONE ) return self;
-
- else if( aStyle == CE_FILETYPE_RTFD )
- return self;
-
- else
- {
- // Well the streams won't truncate a file so we have to do it on
- // our own.
- // We should do a backup of existing files here if requested.
-
- dummyStream = fopen( [path stringValue], "w" );
- if( dummyStream ) fclose( dummyStream );
-
- aStream = NXMapFile( [path stringValue], NX_WRITEONLY );
- switch( aStyle )
- {
- case CE_FILETYPE_RTF:
- [aText writeRichText:aStream];
- break;
- default:
- [aText writeText:aStream];
- }
- NXSaveToFile( aStream, [path stringValue] );
- NXCloseMemory( aStream, NX_FREEBUFFER );
- }
- return self;
- }
-
- - close:sender
- {
- // Tell both windows to go away...
- // To give feedback on which windows will dissappear we will make the
- // key first.
- // The browser window is the main window...the only one to focus at...
- // so we can close the other window if the main window is gone...
-
- [window makeKeyAndOrderFront:self];
- [window performClose:self];
- if( [window isVisible] ) return nil;
-
- // Oh...we really did close the window...the cheat window has
- // passed away during the main-close too...so we are done.
-
- return self;
- }
-
- - undo:sender
- {
- // Well sorry this is a stupid undo...it only undos alle the changes done
- // since switched to a certain method.
- // This only works if the key window is our main browser.
- // The replacement of the selection will be visiible to give visual
- // feedback that something is happening.
-
- id ourMethod;
- id focusView;
-
- if( [NXApp keyWindow] == window )
- {
- [self _setIsUpdatingTextViews:YES];
- ourMethod = [[browser selectedCell] source];
- if( ourMethod != nil )
- {
- [self _showDocuForMethod:ourMethod];
- [self _showSourceForMethod:ourMethod];
- }
- if( [sourceTextView isEditable] )
- focusView = sourceTextView;
- else focusView = docuTextView;
-
- [focusView setSel:0 :0];
- [focusView scrollSelToVisible];
- _changedTexts = NO;
- [self _setIsUpdatingTextViews:NO];
-
- }
- return self;
- }
-
- - switchToNewDisplayMode:sender
- {
- // Called by the PopUp when something changes.
-
- int tag;
- id popUpMatrix;
-
- popUpMatrix = [[popUp target] itemList];
- tag = [[popUpMatrix selectedCell] tag];
-
- if( tag == 1 || tag > 6 ) [self showCheatWindow:self];
-
- return self;
- }
-
- - selectNewMethod:sender
- {
- id ourMethod;
- id focusView;
-
- // Now lets stop redraws until we have finished...looks a little better.
- // This part is a little trick because somehow I have to find the right
- // selections inside the original texts. Now the Text object is not very
- // helpful here. The find method is more then stupid.
-
- [self _setIsUpdatingTextViews:YES];
- [self _syncWindows];
-
- // During the init phase ther is no selected method inside the browser..
- // To solve that we simple take the first method we have..
-
- ourMethod = [[browser selectedCell] source];
- if( ourMethod == nil ) ourMethod = [methodList objectAt:0];
-
- [selectorNameField setStringValue:[ourMethod selectorName]];
- [methodNameField setStringValue:[ourMethod name]];
-
- if( [ourMethod isInstanceMethod] )
- [methodTypeSwitches selectCellWithTag:2];
- else [methodTypeSwitches selectCellWithTag:1];
-
- // Lets update the method docz/source view with the new text.
-
- [self _showDocuForMethod:ourMethod];
- [self _showSourceForMethod:ourMethod];
-
- // It is time to redraw all the fun stuff again. We will make a startup
- // selection first.
-
- if( [sourceTextView isEditable] )
- focusView = sourceTextView;
- else focusView = docuTextView;
-
- [focusView setSel:0 :0];
- [focusView scrollSelToVisible];
-
- // Now be sure that the changes really get displayed.
-
- [self _setIsUpdatingTextViews:NO];
- [window display];
- [cheatWindow display];
-
- // Ok here comes the test section
- return self;
- }
-
- - _setIsUpdatingTextViews:(BOOL)flag
- {
- // If we have a switch of the docu update stuff...we should take
- // care that the pasteboard won't get corrupted !
- // Which means..wehn finshed with the updates...we will put back the
- // pervious pastebord contents.
-
- if( _updatingTextViews == NO &&
- flag == YES )
- {
- [window disableDisplay];
- [cheatWindow disableDisplay];
- }
-
- else if( _updatingTextViews == YES &&
- flag == NO )
- {
- [window reenableDisplay];
- [cheatWindow reenableDisplay];
- }
-
- _updatingTextViews = flag;
-
- return self;
- }
-
- - _showSourceForMethod:aMethod
- {
- // Now lets update the source view.
- // We assume that no changes get lost...so be sure to backup changes first.
- // Everything that is inside the sourceTextView will get lost.
-
- // Now if we really find the method we can show it.
-
- [self _selectSourceForMethod:aMethod];
-
- // Well...now we have a possible selection inside the original text.
- // If it is no useful selection we don't have any text...and we just mark
- // the area as useless.
-
- if( _sourceOriginStart > 0 && _sourceOriginEnd > 0 )
- {
- [sourceFile setSel:_sourceOriginStart :_sourceOriginEnd];
- [sourceFile copyTo:_tempPb];
- [sourceTextView setEditable:YES];
- [sourceTextView selectAll:self];
- [sourceTextView pasteFrom:_tempPb];
- }
- else
- {
- [sourceTextView setEditable:YES];
- [sourceTextView selectAll:self];
- [sourceTextView delete:self];
- [sourceTextView setEditable:NO];
- }
- // We shouldn't corrupt the fonts if we can only handle ASCII !
-
- if( sourceStyle == CE_FILETYPE_ASCII )
- [sourceTextView setMonoFont:YES];
- else [sourceTextView setMonoFont:NO];
-
- return self;
- }
-
- - (BOOL)_selectSourceForMethod:aMethod
- {
- // This private method searches for the method inside the SourceFile
- // and _selects_ the code-portion of this method.
- // This really is only for finding the part.
- // Returns success and sets the internal range information.
-
- // Now the text[] part is more then just ugly..sorry.
-
- char text[10000];
- NXSelPt from;
- NXSelPt to;
- int i;
- int first;
- int last;
- BOOL gotIt;
-
- // Find the definition of the method.
-
- _sourceOriginStart = 0;
- _sourceOriginEnd = 0;
- [sourceFile setSel:0 :0];
- gotIt = [sourceFile findText:[aMethod name]
- ignoreCase:NO backwards:NO wrap:NO];
-
- if( ! gotIt ) return NO;
-
- // Now we assume that people stick to the rules and have the
- // '{' and '}' as the first characters of each line.
-
- [sourceFile getSel:&from :&to];
- [sourceFile getSubstring:text start:from.cp length:9990];
-
- // The first '{' is our start. A '0' terminates the string and the
- // search !
-
- gotIt = NO;
- first = -1;
- for( i=0; i<9990; i++ )
- {
- if( text[i] == 0 )
- break;
- else if( text[i] == '{' )
- {
- gotIt = YES;
- first = i;
- break;
- }
- }
-
- // Now we need a new-line followed by a '}'. A '0' terminates the string
- // and the search ! Same as before.
-
- gotIt = NO;
- last = -1;
- for( i=0; i<9990; i++ )
- {
- if( text[i] == 0 )
- break;
- else if( text[i] == '\n' &&
- text[i+1] == '}' )
- {
- gotIt = YES;
- last = i+2;
- break;
- }
- }
-
- // Now select the right area.
-
- if( gotIt )
- {
- _sourceOriginStart = from.cp+first;
- _sourceOriginEnd = from.cp+last;
- [sourceFile setSel:_sourceOriginStart :_sourceOriginEnd];
- }
-
- return gotIt;
- }
-
- - _silentlySyncWindows
- {
- // this method silently syncs both window. You won't see any selections
- // cluttering your screen.
- // We have to reselect the source and documenation here because otherwise
- // our references won't be corret anymore !!!
-
- [window disableDisplay];
- [cheatWindow disableDisplay];
- [self _setIsUpdatingTextViews:YES];
- [self _syncWindows];
- [self _selectDocuForMethod:[[browser selectedCell] source]];
- [self _selectSourceForMethod:[[browser selectedCell] source]];
- [self _setIsUpdatingTextViews:NO];
- [cheatWindow reenableDisplay];
- [window reenableDisplay];
-
- return self;
- }
-
- - _syncWindows
- {
- // This method does sync the cheat window with the main window.
- // If there is a currently used documentation we have to copy it back
- // first !
- // After that it remarks that there are no more differneces between
- // the two views.
- // After all we DON'T trace the changes here. This is just a common
- // source part of _sliently Syncing and selecting a new method !!
-
- if( _docuOriginStart > 0 && _docuOriginEnd > 0 && _changedTexts )
- {
- [docuTextView selectAll:self];
- [docuTextView copyTo:_tempPb];
- [docuFile setSel:_docuOriginStart :_docuOriginEnd];
- [docuFile pasteFrom:_tempPb];
- [docuTextView setSel:0 :0];
- }
-
- // Same for the source code.
-
- if( _sourceOriginStart > 0 && _sourceOriginEnd > 0 && _changedTexts )
- {
- [sourceTextView selectAll:self];
- [sourceTextView copyTo:_tempPb];
- [sourceFile setSel:_sourceOriginStart :_sourceOriginEnd];
- [sourceFile pasteFrom:_tempPb];
- [sourceTextView setSel:0 :0];
- }
-
- _changedTexts = NO;
-
- return self;
- }
-
- - checkDocumentation:sender
- {
- // Now this method tries to get the documentation up to date.
- // Missing parts will be inserted...etc.pp.
-
- id aString;
- id aMethod;
- int i;
- id aList;
-
- // Be care full. we have the remember all the Docu and Source settings
- // becaus ehte method s we use will change them !!!
-
-
- // If there is no docu we ask the app to provide the default template.
-
- if( docuStyle == CE_FILETYPE_NONE )
- {
- [[NXApp delegate] copyClassDocuTemplate];
- [docuFile setMonoFont:NO];
- [docuFile setEditable:YES];
- [docuFile selectAll:self];
- [docuFile pasteFrom:_tempPb];
-
- docuStyle = CE_FILETYPE_RTF;
-
- // The main template does need some customization to be useful
- // First the version
-
- [docuFile setSel:0 :0];
-
- if( [docuFile findText:"MyVersion"
- ignoreCase:NO backwards:NO wrap:NO] )
- [docuFile replaceSel:"0.1"];
-
- // Then the Date
-
- if( [docuFile findText:"1994"
- ignoreCase:NO backwards:NO wrap:NO] )
- [docuFile replaceSel:"1995"];
-
- // Now the Copyright part
-
- if( [docuFile findText:"MyCompany"
- ignoreCase:NO backwards:NO wrap:NO] )
- [docuFile replaceSel:"ClassEditor"];
-
- // Its time for the class name
-
- if( [docuFile findText:"MyClass"
- ignoreCase:NO backwards:NO wrap:NO] )
- {
- aString = [filename fileName];
- [docuFile replaceSel:[aString stringValue]];
- [aString free];
- }
-
- // Some header infos please
-
- if( [docuFile findText:"MyClass.h"
- ignoreCase:NO backwards:NO wrap:NO] )
- {
- aString = [filename fileName];
- [aString cat:".h"];
- [docuFile replaceSel:[aString stringValue]];
- [aString free];
- }
- }
-
- // Now we should have a documentation...but lets see if there is one
- // for every method. If not we should try to find the right place
- // to place a template.
-
- // We add the in a sorted order.
- // Because the _addDocu method does not take care of ordering...we
- // have to sort things the other way around.
-
- aList = [MiscSortedList new];
- [aList setSortOrder:Misc_DESCENDING];
- [aList setSortEnabled:YES];
-
- for( i=0; i<[methodList count];i++ )
- [aList addObject:[methodList objectAt:i]];
-
- for( i=0; i<[aList count];i++ )
- {
- aMethod = [aList objectAt:i];
- if( [self _selectDocuForMethod:aMethod] == 0 )
- [self _addDocuForMethod:aMethod];
- }
- [aList free];
-
- // That's it for today..
-
- return self;
- }
-
- - _showDocuForMethod:aMethod
- {
- // Now lets update the documentation view.
- // We assume that no changes get lost...so be sure to backup changes first.
- // Everything that is inside the docuTextView will get lost.
-
- // Now if we really find the method we have to remove the blakn lines and
- // set the right selection part.
-
- [self _selectDocuForMethod:aMethod];
-
- // Well...now we have a possible selection inside the original text.
- // If it is no useful selection we don't have any text...and we just mark
- // the area as useless.
-
- if( _docuOriginStart > 0 && _docuOriginEnd > 0 )
- {
- [docuFile setSel:_docuOriginStart :_docuOriginEnd];
- [docuFile copyTo:_tempPb];
- [docuTextView setEditable:YES];
- [docuTextView selectAll:self];
- [docuTextView pasteFrom:_tempPb];
- }
- else
- {
- [docuTextView setEditable:YES];
- [docuTextView selectAll:self];
- [docuTextView delete:self];
- [docuTextView setEditable:NO];
- }
- return self;
- }
-
- - (BOOL)_selectDocuForMethod:aMethod
- {
- // This private method searches for the method inside the DocuFile
- // and _selects_ the docu-portion of this method.
- // This really is only for finding the part.
- // Returns the offset to the description text. An offset of '0' meens that
- // no method descript was found.
- // "See also:" is included..AND the blank lines below.
-
- // Now the text[] part is more then just ugly..sorry.
-
- char text[10000];
- NXSelPt from;
- NXSelPt to;
- int first;
- int last;
- int i;
- BOOL gotIt;
-
- // We have to find the right start-selection inside the documentation
- // first. This depends on the type of method.
-
- _docuOriginStart = 0;
- _docuOriginEnd = 0;
- [docuFile setSel:0 :0];
-
- if( [aMethod isInstanceMethod] )
- gotIt = [docuFile findText:"Instance Methods"
- ignoreCase:NO backwards:NO wrap:NO];
- else gotIt = [docuFile findText:"Class Methods"
- ignoreCase:NO backwards:NO wrap:NO];
-
- if( !gotIt ) return NO;
-
- // Well finding the right section is not enough.. we have to find the
- // right selector too. And sure...the font has to be correct. We
- // Don't want the find every ref to that method.
-
- gotIt = [docuFile findText:[aMethod selectorName]
- ignoreCase:NO
- backwards:NO
- wrap:NO
- font:[Font newFont:"Helvetica-Bold" size:14]];
- if( !gotIt ) return NO;
-
- // Well now we should have the first selector for that method.
- // Now if the docu was created with CM it should work if we grab for
- // those right numbers of linefeeds.
- // Really nasty. Don't look at it.
-
- [docuFile getSel:&from :&to];
- [docuFile getSubstring:text start:from.cp length:9990];
-
- // The first new-lines is our start. A '0' terminates the string and the
- // search !
-
- gotIt = NO;
- first = -1;
- for( i=0; i<9990; i++ )
- {
- if( text[i] == 0 )
- break;
- else if( text[i] == '\n' &&
- text[i+1] == '\n' )
- {
- gotIt = YES;
- first = i+2;
- break;
- }
- }
-
- // Now we need the 5 last new-lines. A '0' terminates the string and the
- // search !
-
- gotIt = NO;
- last = -1;
- for( i=0; i<9990; i++ )
- {
- if( text[i] == 0 )
- break;
- else if( text[i] == '\n' &&
- text[i+1] == '\n' &&
- text[i+2] == '\n' &&
- text[i+3] == '\n' &&
- text[i+4] == '\n' )
- {
- gotIt = YES;
- last = i+5;
- break;
- }
- }
- // Now select the right area.
-
- if( gotIt )
- {
- _docuOriginStart = from.cp+first;
- _docuOriginEnd = from.cp+last-5;
- [docuFile setSel:_docuOriginStart :_docuOriginEnd];
- }
-
- return gotIt;
- }
-
- - _addDocuForMethod:aMethod
- {
- // This method adds a template for the documentation of a certain method.
- // It DOES corrupt the pasteboard !
- // BUG: We also don't care about sorting ! This is bad !
-
- NXSelPt from;
- NXSelPt to;
- int i;
- int paramCount;
- BOOL gotIt;
- id tokens;
- id aString;
-
- // We have to find the right start-selection inside the documentation
- // first. This depends on the type of method.
-
- [docuFile setSel:0 :0];
-
- if( [aMethod isInstanceMethod] )
- gotIt = [docuFile findText:"Instance Methods"
- ignoreCase:NO backwards:NO wrap:NO];
- else gotIt = [docuFile findText:"Class Methods"
- ignoreCase:NO backwards:NO wrap:NO];
-
- if( !gotIt ) return self;
-
- // Now this is ugly...we should sort the methods alpahbetically !!
- // Buggy because we can only HOPE that nobody adds something behind the
- // "* Methods" section.
-
- [docuFile getSel:&from :&to];
- [docuFile setSel:to.cp+2 :to.cp+2];
-
- [[NXApp delegate] copyMethodDocuTemplate];
- [docuFile pasteFrom:_tempPb];
- [docuFile setSel:to.cp+2 :to.cp+2];
-
- [docuFile findText:"_CE_myMethod"
- ignoreCase:NO backwards:NO wrap:NO];
- [docuFile replaceSel:[aMethod selectorName]];
-
- [docuFile findText:"_CE_myMethod"
- ignoreCase:NO backwards:NO wrap:NO];
- [docuFile getSel:&from :&to];
- [docuFile replaceSel:[aMethod name]];
-
- // First lets fix the method prefix of the class methods and
- // remove the dulicate prefix"
-
- [docuFile setSel:from.cp-2 :from.cp-1];
-
- if( [aMethod isInstanceMethod] )
- {
- [docuFile findText:"- " ignoreCase:NO backwards:NO wrap:NO];
- [docuFile delete:self];
- }
- else
- {
- [docuFile replaceSel:"+"];
- [docuFile findText:"+ " ignoreCase:NO backwards:NO wrap:NO];
- [docuFile delete:self];
- }
-
- // Now its time to fix all the fonts.
- // Lets tokenize the methods name. This will create a list of
- // all the parts that have a different font.
- // We need to know how many parms the method takes to get a simpler
- // algorithm for coosing the fonts.
-
- tokens = [aMethod methodTokens];
- paramCount = [aMethod numberOfArguments];
-
- for( i=0; i<[tokens count]; i++ )
- {
- // Now select the new token...if we fail to find it..lets quit here.
-
- aString = [tokens objectAt:i];
- if( [docuFile findText:[aString stringValue]
- ignoreCase:NO backwards:NO wrap:NO] == NO ) break;
-
-
- // Depending on the type of token we will choose a different font.
- // First check for casts and then for paramerters.
-
- if( [aString grep:")"] )
- [docuFile setSelFont:[Font newFont:"Times-Roman" size:14]];
-
- // If it is not a cast the situation is a little bit trickier.
- // Imagine a - (BOOL)delegate or a simple -free method.
- // The token will be a part of the selector and still have no
- // ':' attached because there ist no argument
-
- else if( [aString grep:":"] == NO && paramCount > 0 )
- {
- [docuFile setSelFont:[Font newFont:"Times-Italic" size:14]];
- }
- }
-
- // The tokens are bound to die now.
-
- [[tokens freeObjects] free];
-
- return self;
- }
-
- - showCheatWindow:sender
- {
- id aString;
- // Lets choose a defaults selection to clean up the window.
- [docuFile setSel:0 :0];
- aString = [filename fileName];
- [aString cat:"...Cheat Window"];
- [cheatWindow setMiniwindowImage:
- [NXImage findImageNamed:"ObjectPink.rtf.m.h"]];
- [cheatWindow setTitle:[aString stringValue]];
- [aString free];
-
- [cheatWindow makeKeyAndOrderFront:self];
- return self;
- }
-
- - showDocumentationOnly:sender
- {
- return self;
- }
-
- - showInterfaceOnly:sender
- {
- return self;
- }
-
- - showImplementationOnly:sender
- {
- return self;
- }
-
- - (int)browser:sender fillMatrix:matrix inColumn:(int)column
- {
- // We just fill the matrix with the needed number of cells.
- // According the methods inside the list.
-
- int i, n;
-
- n = [methodList count];
- for( i=0; i<n; i++ ) [matrix addRow];
-
- return n;
- }
-
- - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
- {
- // Just set the atoms name as the String value.
- id theMethod;
-
- theMethod = [methodList objectAt:row];
- [cell setStringValue:[theMethod selectorName]];
- [cell setSource:theMethod];
- [cell setLoaded:YES];
- [cell setLeaf:YES];
-
- return self;
- }
-
- - textDidChange:sender
- {
- // Here we will make both windows as edited. This is for checking
- // before saving.
- // But if we are updating the views from the program we won't say that
- // this are real changes. Only user changes count !!
- // For internal use we are tracking the "real"changes since the last
- // sync between the two windows.
-
- if( _updatingTextViews == NO &&
- _changedTexts == NO )
- {
- [cheatWindow setDocEdited:YES];
- [window setDocEdited:YES];
- _changedTexts = YES;
- _changedWindow = [sender window];
- }
-
- return self;
- }
-
- - textDidGetKeys:sender isEmpty:(BOOL)flag
- {
- // This is for tracing down changes inside the window and highlighing the
- // character pairs.
-
- [self textDidChange:sender];
-
- // Now if someone typed a character that should have a corresponding pair
- // we will highlight temp here.
-
- return self;
- }
-
- - _highlightPrevious:(char *)aString inView:aText
- {
- // this is for highlighing the character pairs.
- // The current char and his counter part get highlighted if is is found
- // If not we will Ping loud.
-
- NXSelPt currentFrom;
- NXSelPt currentTo;
- NXSelPt from;
- NXSelPt to;
- BOOL gotIt;
-
- [aText getSel:¤tFrom :¤tTo];
- gotIt = [aText findText:aString
- ignoreCase:NO backwards:YES wrap:NO];
-
- if( gotIt )
- {
- [aText getSel:&from :&to];
- [aText setSel:currentFrom.cp-1 :currentFrom.cp];
- // wait( 1 );
- [aText setSel:from.cp :to.cp];
- }
- else // NXBeep();
-
- [aText setSel:currentFrom.cp :currentTo.cp];
- return self;
- }
-
- - textShouldPerformCompletion:sender
- {
- id nameArray;
- id aMethod;
- id aString;
- id stringToFind;
- int i;
- int foundItems;
- int firstMatch;
-
- // Ok..only inside the methodField we will do completion
-
- if( [sender delegate] != methodNameField ) return nil;
-
- // Now get the string and find what we need.
-
- stringToFind = [MiscString newWithString:[methodNameField stringValue]];
- nameArray = [MiscStringArray new];
-
- for( i=0; i<[methodList count]; i++ )
- {
- aMethod = [methodList objectAt:i];
- [nameArray addString:[aMethod selectorName]];
- [nameArray addString:[aMethod name]];
- }
- // Now cont the hits...
-
- foundItems = 0;
- firstMatch = -1;
-
- for( i=0; i<[nameArray count]; i++ )
- {
- aString = [nameArray objectAt:i];
- if( [aString spotOfString:stringToFind caseSensitive:YES] == 0 )
- {
- foundItems++;
- if( firstMatch == -1 ) firstMatch = i;
- }
- }
- // Now what have we found.. if there are more then one don't do anything.
-
- if( foundItems == 1 )
- [methodNameField setStringValue:[nameArray stringAt:firstMatch]];
-
- // Free it all! We hope that the Array frees the
-
- [stringToFind free];
- [nameArray free];
- return self;
- }
-
- - windowDidBecomeKey:sender
- {
- // Lets look only at the window. Depending on which window has become key
- // We have to make some updates between the two views.
-
- if( _changedTexts == NO ) return self;
-
- // If the sender is the window where we did make the last changes there
- // is no need to do some updates at all. Just leave it as it is.
-
- if( sender == _changedWindow ) return self;
-
- if( sender == window )
- {
- [self reparseMethods:self];
- [self selectNewMethod:self];
- }
-
- if( sender == cheatWindow )
- [self _silentlySyncWindows];
-
- return self;
- }
-
- - windowWillClose:sender
- {
- // Lets save the contents if the user wants it and free ourself.
-
- int result;
-
- if( sender != window ) return self;
-
- // Oh...looks like somebody wants to close the main window...
- // Lets check if we have something to save.
-
- if( [window isDocEdited] )
- {
- result = NXRunAlertPanel( "Close", "Save changes to %s?",
- "Save", "Don't Save", "Cancel",
- [filename stringValue] );
-
- if( result == NX_ALERTOTHER )
- return nil;
-
- else if( result == NX_ALERTDEFAULT )
- [self save:self];
- }
- // We will tell the window that they have to interesting information
- // anymore. This is because the app checks for these infos before quitting.
- // And if we decide "Don't save"...this should be done correct.
-
- [window setDocEdited:NO];
- [cheatWindow setDocEdited:NO];
- [cheatWindow deminiaturize:self];
- [cheatWindow performClose:self];
-
- // Its time to go....
-
- [[NXApp delegate] addToReleasePool:self];
- return self;
- }
-
- @end
-
- /*
- * History: 13.01.95 Buh
- *
- *
- * Bugs: - ...
- */