This is CEClassEditor.m in view mode; [Download] [Up]
/* 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 <CEClassEditor+FileHandling.h>
#import <CEMethod.h>
#import <CEBrowserCell.h>
#import <CECellMatrix.h>
#import <Text_MiscExtensions.h>
#import <MiscStringArray_List.h>
#import <MiscEmacsText.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;
id cellPrototype;
id headerSearchPathes;
id sourceSearchPathes;
id docuSearchPathes;
int index; // To adjust the popUps initValue !
id aMatrix; // Dito;
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!
headerSearchPathes = [[[NXApp delegate] preferences]
headerSearchPaths];
sourceSearchPathes = [[[NXApp delegate] preferences]
sourceSearchPaths];
docuSearchPathes = [[[NXApp delegate] preferences]
documentationSearchPaths];
_docuPath = [MiscString new];
_headerPath = [MiscString new];
_sourcePath = [MiscString new];
// On we go...
dummyFile = [[MiscString new] setPath:fileName];
basename = [dummyFile fileBasename];
filename = [dummyFile pathName];
[filename addChar:[MiscString pathSeparator]];
[filename concatenate:basename];
[basename free];
// Get the pasteboard 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 cat:".h"];
[self _try:dummyFile withAlternatives:headerSearchPathes
forText:headerFile andGetStyle:&headerStyle andSetPath:_headerPath];
// 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];
[dummyFile free];
[[NXApp delegate] addToReleasePool:self];
return nil;
}
// The next part is loading the source code for that class.
// This can be either ASCII or RTF !
[dummyFile takeStringValue:filename];
[dummyFile cat:".m"];
[self _try:dummyFile withAlternatives:sourceSearchPathes
forText:sourceFile andGetStyle:&sourceStyle andSetPath:_sourcePath];
// Now the documentation. if there is no RTF docu we should ty to find a
// RTFD docu too !
[dummyFile takeStringValue:filename];
[dummyFile cat:".rtf"];
[self _try:dummyFile withAlternatives:docuSearchPathes
forText:docuFile andGetStyle:&docuStyle andSetPath:_docuPath];
[dummyFile free];
// Its time to check all the methods we can find.
// NOTE: If there are No methods we will quit right here !
methodList = [List new];
if( ![self reparseMethods:self] ) return nil;
// 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 plus adjusting the
// browser.
// First lets connect the popUps. This has to be done before the browsers
// try to get their info.
// Be sure we have the right initial selection inside the proper matrix !
[[modePopup target] setTarget:self];
[[modePopup target] setAction:@selector(switchToNewDisplayMode:)];
index = [[modePopup target] indexOfItem:[modePopup title]];
if( index > -1 )
{
aMatrix = [[modePopup target] itemList];
if( ![aMatrix selectedCell] )
[aMatrix selectCell:[[aMatrix cellList] objectAt:index]];
}
// Lets do the same for the other popup..
[[secondaryModePopup target] setTarget:self];
[[secondaryModePopup target] setAction:@selector(switchToNewDisplayMode:)];
index = [[secondaryModePopup target] indexOfItem:[modePopup title]];
if( index > -1 )
{
aMatrix = [[secondaryModePopup target] itemList];
if( ![aMatrix selectedCell] )
[aMatrix selectCell:[[aMatrix cellList] objectAt:index]];
}
// NOTE: We have to set the browser delegate by code because otherwise
// we would be requested to fill the browser before the right cell
// prototype is set (just right after loading the NIB!) !!!.
cellPrototype = [[CEBrowserCell alloc] init];
[cellPrototype addFixedTab:4];
[cellPrototype addFixedTab:20];
[cellPrototype addFixedTab:-15];
[browser setMatrixClass:[CECellMatrix class]];
[browser setCellPrototype:cellPrototype];
[browser setTarget:self];
[browser setAction:@selector(selectNewMethod:)];
[browser setDoubleAction:@selector(selectNewMethod:)];
[browser setDelegate:self];
[browser loadColumnZero];
[secondaryBrowser setMatrixClass:[CECellMatrix class]];
[secondaryBrowser setCellPrototype:cellPrototype];
[secondaryBrowser setDelegate:self];
[secondaryBrowser loadColumnZero];
[window setTitleAsFilename:[self filename]];
[window setMiniwindowImage:[NXImage findImageNamed:"CESmallObjectPink"]];
// 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;
}
- free
{
// We do not free all the NIB objects...hmm should check that out.
[window free];
[cheatWindow free];
[filename free];
[_docuPath free];
[_sourcePath free];
[_headerPath free];
[[methodList freeObjects] free];
return [super free];
}
- 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];
if( [methodList count] == 0 )
{
// Ooops its time to exit..ther are no method.
// BUG: This is not a Nice way of exiting !
NXRunAlertPanel( NULL, "Could not find a single method inside %s\n"\
"Methods MUST be definined in the header file "\
"in order to work with a class!",
"Cancel", NULL, NULL,
[_headerPath stringValue] );
[[NXApp delegate] addToReleasePool:self];
return nil;
}
// [self checkDocumentation:self];
[browser loadColumnZero];
[self _setIsUpdatingTextViews:NO];
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 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.
[self _writeText:headerFile withStyle:headerStyle to:_headerPath];
[self _writeText:sourceFile withStyle:sourceStyle to:_sourcePath];
[self _writeText:docuFile withStyle:docuStyle to:_docuPath];
// 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;
}
- 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 = selection;
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];
[window display];
[cheatWindow display];
}
return self;
}
- resolveLock:sender
{
return [[NXApp delegate] showAccessHelper:self];
}
- switchToNewDisplayMode:sender
{
id primaryPopMatrix;
// Called by the PopUp when something changes...just ask the browser..
// who ever that may be..to load its first column.
primaryPopMatrix = [[modePopup target] itemList];
if( sender == primaryPopMatrix )
{
[browser loadColumnZero];
[self _checkSelectionInBrowser:browser];
}
else [secondaryBrowser loadColumnZero];
// Perhaps we don't only have the update the browser..but also
// show the other window. this will disappear in a future release.
if( sender == primaryPopMatrix &&
[[primaryPopMatrix selectedCell] tag] == CE_BrowsePlainC )
[self showCheatWindow:self];
return self;
}
- selectNewMethodFromText:sender
{
const char * realMethod;
// if we can't find the method..lets assume it is a new one...
// either rename the current...or create a new one.
NXRunAlertPanel( NULL, "%s\nThis acction should either rename the "\
"current method or add a new one\n"\
"Not yet implemented :-(",
"Create New", "Rename Current", "Cancel",
[sender stringValue] );
// lets reset the right method string..
realMethod = [selection name];
if( realMethod ) [sender setStringValue:realMethod];
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];
selection = ourMethod;
[selectorNameField setStringValue:[ourMethod selectorName]];
[methodNameField setStringValue:[ourMethod name]];
// Lets update the method docs/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];
[self _checkSelectionInBrowser:browser];
[window display];
[cheatWindow display];
// Ok here comes the test section
return self;
}
- selectTrueLine:(int)line inSource:(BOOL)aFlag
{
id targetText;
if( aFlag )
{
targetText = sourceFile;
[self showImplementationOnly:self];
}
else
{
targetText = headerFile;
[self showInterfaceOnly:self];
}
[self showCheatWindow:self];
[targetText selectTrueLine:line];
[targetText scrollSelToVisible];
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];
[sourceTextView setMonoFont:NO];
if( sourceStyle == CE_FILETYPE_ASCII ) [sourceTextView format:self];
}
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;
}
- _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:selection];
[self _selectSourceForMethod:selection];
[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;
}
- _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;
}
- 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;
}
- showAllFiles:sender
{
NXRect bounds;
id aView;
[cheatWindow disableDisplay];
// Resize the header view to some useful none-zero value.
// This is necessary to see anything at all.
// We take the header because it will then end up as the smallest view.
aView = [[headerFile superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :20];
// Make the sourceFile and docuFile small enough..and redraw the window.
aView = [[sourceFile superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :50];
aView = [[docuFile superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :50];
[cheatWindow disableDisplay];
[cheatWindow display];
return self;
}
- showDocumentationOnly:sender
{
[self _resizeCheatWindowFor:docuFile
andHide:headerFile :sourceFile];
return self;
}
- showInterfaceOnly:sender
{
[self _resizeCheatWindowFor:headerFile
andHide:docuFile :sourceFile];
return self;
}
- showImplementationOnly:sender
{
[self _resizeCheatWindowFor:sourceFile
andHide:docuFile :headerFile];
return self;
}
- _resizeCheatWindowFor:mainView andHide:secondView :thirdView
{
NXRect bounds;
id aView;
[cheatWindow disableDisplay];
// Resize the main view to some useful none-zero value.
// This is necessary to see anything at all.
aView = [[mainView superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :20];
// Make the secondView and thirdView small enough..and redraw the window.
aView = [[secondView superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :0];
aView = [[thirdView superview] superview];
[aView getBounds:&bounds];
[aView sizeTo:bounds.size.width :0];
[cheatWindow disableDisplay];
[cheatWindow display];
return self;
}
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
int count;
// BUG: Another "bug" circumvention ??
// I took this from the RZToDoList app. Perhaps it is quite useless...
// I don't have the time to diddle with it..but it seams to work without
// the next line too.
[matrix renewRows:0 cols:1];
// We don't use lazy browsers because it wouldn't save any space
// (maintaining the lists twice) and it is easiers this way.
if( sender == browser )
{
switch( [[[[modePopup target] itemList] selectedCell] tag] )
{
case CE_BrowseClassInfo:
count = [self fillClassInfoIntoMatrix:matrix];
break;
case CE_BrowseInstanceVars:
count = [self fillInstanceVarsIntoMatrix:matrix];
break;
case CE_BrowseMethods:
count = [self fillMethodsIntoMatrix:matrix];
break;
case CE_BrowseInstanceMethods:
count = [self fillInstanceMethodsIntoMatrix:matrix];
break;
case CE_BrowseClassMethods:
count = [self fillClassMethodsIntoMatrix:matrix];
break;
case CE_BrowseMethodsByCategory:
count = [self fillMethodsByCategoryIntoMatrix:matrix];
break;
case CE_BrowseDependencies:
count = [self fillDependenciesIntoMatrix:matrix];
break;
case CE_BrowsePlainC:
default:
count = [self fillPlainCIntoMatrix:matrix];
}
}
// Otherwise we have to fill the socond browser..
else
{
/*
switch( [[[[secondaryModePopup target] itemList] selectedCell] tag] )
{
case CE_BrowseReferences:
count = [self fillReferencesIntoMatrix:matrix
accordingTo:selection];
break;
case CE_BrowseHistory:
count = [self fillHistoryIntoMatrix:matrix
accordingTo:selection];
break;
case CE_BrowseBugs:
default:
count = [self fillBugsIntoMatrix:matrix
accordingTo:selection];
}
*/
count = 0;
}
return count;
}
- _checkSelectionInBrowser:aBrowser
{
id matrix;
int i;
int count;
int dummy;
// Now lets see if we can make some selections. If possible the browser
// should reflect the current selection.
if( aBrowser == browser &&
selection != nil &&
selection != [[aBrowser selectedCell] source] )
{
matrix = [aBrowser matrixInColumn:0];
[matrix getNumRows:&count numCols:&dummy];
for( i=0; i<count; i++ )
{
if( [[matrix cellAt:i :0] source] == selection )
{
[matrix selectCellAt:i :0];
[matrix scrollCellToVisible:i :0];
break;
}
}
}
return self;
}
- (int)fillClassInfoIntoMatrix:matrix
{
int count;
id theCell;
id className;
count = 0;
className = [filename fileBasename];
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"ClassS" at:0];
[theCell setText:[className stringValue] at:1 font:FONT_BOLD];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
[className free];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"ProtocolS" at:0];
[theCell setText:"<SomeProtocol>" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"CategoryS" at:0];
[theCell setText:"Archiving" at:1 font:FONT_BOLD];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"CategoryS" at:0];
[theCell setText:"Working" at:1 font:FONT_BOLD];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"CategoryS" at:0];
[theCell setText:"Private" at:1 font:FONT_BOLD];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
return count;
}
- (int)fillInstanceVarsIntoMatrix:matrix
{
int count;
id theCell;
count = 0;
// BUG: There is no way to set the Tabs afterwards !!!!
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell clearTabs];
[theCell addFixedTab:4];
[theCell addProportionalTab:0.3 min:50 max:600];
[theCell setText:"id" at:0];
[theCell setText:"anInstance" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell clearTabs];
[theCell addFixedTab:4];
[theCell addProportionalTab:0.3 min:50 max:600];
[theCell setText:"id" at:0];
[theCell setText:"secondInstance" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell clearTabs];
[theCell addFixedTab:4];
[theCell addProportionalTab:0.3 min:50 max:600];
[theCell setText:"int *" at:0];
[theCell setText:"somePointer" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell clearTabs];
[theCell addFixedTab:4];
[theCell addProportionalTab:0.3 min:50 max:600];
[theCell setText:"is" at:0];
[theCell setText:"thisUsefulAnyway" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
return count;
}
- (int)fillMethodsIntoMatrix:matrix
{
int i;
int count;
id theMethod;
id theCell;
id aList;
aList = [MiscSortedList new];
[aList setSortOrder:Misc_ASCENDING];
[aList setSortEnabled:YES];
for( i=0; i<[methodList count];i++ )
[aList addObject:[methodList objectAt:i]];
count = 0;
for( i=0; i<[aList count]; i++ )
{
theMethod = [aList objectAt:i];
[matrix addRow];
theCell = [matrix cellAt:count :0];
[self _fillCell:theCell forMethod:theMethod];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
count++;
}
[aList free];
return count;
}
- (int)fillInstanceMethodsIntoMatrix:matrix
{
int i;
int count;
id theMethod;
id theCell;
id aList;
aList = [MiscSortedList new];
[aList setSortOrder:Misc_ASCENDING];
[aList setSortEnabled:YES];
for( i=0; i<[methodList count];i++ )
[aList addObject:[methodList objectAt:i]];
count = 0;
for( i=0; i<[methodList count]; i++ )
{
theMethod = [aList objectAt:i];
if( [theMethod isInstanceMethod] )
{
[matrix addRow];
theCell = [matrix cellAt:count :0];
[self _fillCell:theCell forMethod:theMethod];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
count++;
}
}
[aList free];
return count;
}
- (int)fillClassMethodsIntoMatrix:matrix
{
int i;
int count;
id theMethod;
id theCell;
id aList;
aList = [MiscSortedList new];
[aList setSortOrder:Misc_ASCENDING];
[aList setSortEnabled:YES];
for( i=0; i<[methodList count];i++ )
[aList addObject:[methodList objectAt:i]];
count = 0;
for( i=0; i<[methodList count]; i++ )
{
theMethod = [aList objectAt:i];
if( ![theMethod isInstanceMethod] )
{
[matrix addRow];
theCell = [matrix cellAt:count :0];
[self _fillCell:theCell forMethod:theMethod];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
count++;
}
}
[aList free];
return count;
}
- (int)fillMethodsByCategoryIntoMatrix:matrix
{
int i;
int count;
id theMethod;
id theCell;
id className;
count = 0;
className = [filename fileBasename];
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setImageNamed:"ClassS" at:0];
[theCell setText:[className stringValue] at:1 font:FONT_BOLD];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
[className free];
count++;
// Now add all the methods...unsorted..
for( i=0; i<[methodList count]; i++ )
{
theMethod = [methodList objectAt:i];
[matrix addRow];
theCell = [matrix cellAt:count :0];
[self _fillCell:theCell forMethod:theMethod];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
count++;
}
return count;
}
- (int)fillDependenciesIntoMatrix:matrix
{
int count;
id theCell;
count = 0;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setText:"<appkit/appkit.h>" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setText:"<misckit/misckit.h>" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
[matrix addRow];
theCell = [matrix cellAt:count :0];
[theCell setText:"\"nothing/works.h\"" at:1];
[theCell setLeaf:YES];
[theCell setLoaded:YES];
[theCell setEnabled:NO];
count++;
return count;
}
- (int)fillPlainCIntoMatrix:matrix
{
return [self fillMethodsByCategoryIntoMatrix:matrix];
}
- _fillCell:theCell forMethod:aMethod
{
if( [aMethod isInstanceMethod] )
[theCell setImageNamed:"InstanceMethodS" at:0];
else [theCell setImageNamed:"ClassMethodS" at:0];
[theCell setText:[aMethod selectorName] at:1];
[theCell setSource:aMethod];
// Now set the infos we need for dragging.
[theCell setDraggingSource:aMethod at:0];
return self;
}
- (int)fillReferencesIntoMatrix:matrix accordingTo:theSelection
{
return 0;
}
- (int)fillHistoryIntoMatrix:matrix accordingTo:theSelection
{
return 0;
}
- (int)fillBugsIntoMatrix:matrix accordingTo:theSelection
{
return 0;
}
- 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( sender != docuTextView &&
sender != sourceTextView &&
sender != docuFile &&
sender != headerFile &&
sender != sourceFile ) return self;
// well only if one of the the relevant texts did send the msg we
// will procede.
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: - ...
*/These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.