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.