home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / ClassEditor.0.4 / Source / CEClassEditor.m < prev    next >
Encoding:
Text File  |  1995-06-07  |  34.0 KB  |  1,436 lines

  1. /* CEClassEditor.m                 
  2.  *
  3.  * This object controls the data of a beaker (molecules, cameras, groups etc.)
  4.  * It is the main document of BeakerBoy and controls everything from loading to
  5.  * setting up the browser which does most of the other work.
  6.  *
  7.  * For interface-info see the header file. The comments in this file mostly
  8.  * cover only the real implementation details.
  9.  *
  10.  * Written by:         Thomas Engel
  11.  * Created:            23.10.1993 (Copyleft)
  12.  * Last modified:     12.11.1994
  13.  */
  14.  
  15. #define CURRENT_VERSION 1
  16.  
  17. #import <CEClassEditor.h>
  18. #import <CEClassEditor+FileHandling.h>
  19. #import <CEMethod.h>
  20. #import <CEBrowserCell.h>
  21. #import <CECellMatrix.h>
  22. #import <Text_MiscExtensions.h>
  23. #import <MiscStringArray_List.h>
  24. #import <MiscEmacsText.h>
  25.  
  26. #import <misckit/MiscString.h>
  27. #import <misckit/MiscStringArray.h>
  28. #import <misckit/MiscSortedList.h>
  29.  
  30. @implementation CEClassEditor
  31.  
  32.  
  33. + initialize
  34. {
  35.     if ( self == [CEClassEditor class] )
  36.         [CEClassEditor setVersion:CURRENT_VERSION];
  37.  
  38.     return self;
  39. }
  40.  
  41. - init
  42. {
  43.     return [self initFromFile:""];
  44. }
  45.  
  46. - initFromFile:(const char *)fileName
  47. {    
  48.     id    basename;
  49.     id    dummyFile;
  50.     
  51.     id    cellPrototype;
  52.         
  53.     id    headerSearchPathes;
  54.     id    sourceSearchPathes;
  55.     id    docuSearchPathes;
  56.     
  57.     int    index;                        // To adjust the popUps initValue !
  58.     id    aMatrix;                    // Dito;
  59.     
  60.     self = [super init];
  61.     if( !self ) return self;
  62.  
  63.     // OK. We really are an object...here we go with our init.
  64.     // Lets load the NIB. It has set our beaker to be its window-delegate.
  65.     // Without a NIB there will be no beaker object !
  66.  
  67.     if( [NXApp loadNibSection:"ClassWindow.nib" owner:self] == nil )
  68.     {
  69.         NXRunAlertPanel( NULL, "Couldn't load ClassWindow.nib", 
  70.                                "OK", NULL, NULL );
  71.         return nil;
  72.     }
  73.     // Lets setup the names.
  74.     // We need to track all those areas where we update the text to find the
  75.     // places where the user makes changes to the text!
  76.     
  77.     headerSearchPathes = [[[NXApp delegate] preferences]
  78.                             headerSearchPaths];
  79.     sourceSearchPathes = [[[NXApp delegate] preferences]
  80.                             sourceSearchPaths];
  81.     docuSearchPathes = [[[NXApp delegate] preferences]
  82.                             documentationSearchPaths];
  83.  
  84.     _docuPath = [MiscString new];
  85.     _headerPath = [MiscString new];
  86.     _sourcePath = [MiscString new];
  87.     
  88.     // On we go...
  89.  
  90.     dummyFile = [[MiscString new] setPath:fileName];
  91.     
  92.     basename = [dummyFile fileBasename];
  93.     filename = [dummyFile pathName];
  94.     [filename addChar:[MiscString pathSeparator]];
  95.     [filename concatenate:basename];
  96.     [basename free];
  97.  
  98.     // Get the pasteboard and ensure that nobody will see the texthandling we
  99.     // do now.
  100.  
  101.     _tempPb = [Pasteboard newName:"CETempPb"];    
  102.     [self _setIsUpdatingTextViews:YES];
  103.  
  104.     // Now we will always refer to the file WITHOUT any extention.
  105.     // If we are lucky we will be able to read it all..m, .h and .rtf
  106.     // If we were able to read the file...make the view editable.
  107.     
  108.     [dummyFile takeStringValue:filename];
  109.     [dummyFile cat:".h"];
  110.     [self _try:dummyFile withAlternatives:headerSearchPathes
  111.             forText:headerFile andGetStyle:&headerStyle andSetPath:_headerPath];
  112.     
  113.     // Well we really need a header file... it is crucial !
  114.  
  115.     if( headerStyle == CE_FILETYPE_NONE )
  116.     {
  117.         basename = [dummyFile fileBasename];
  118.         NXRunAlertPanel( NULL, "Couldn't load %s.h. Not in any searchpath.", 
  119.                                "Ooops", NULL, NULL, 
  120.                         [basename stringValue] );
  121.         [basename free];
  122.         [dummyFile free];
  123.         [[NXApp delegate] addToReleasePool:self];
  124.         return nil;
  125.     }
  126.  
  127.     // The next part is loading the source code for that class.
  128.     // This can be either ASCII or RTF !
  129.     
  130.     [dummyFile takeStringValue:filename];
  131.     [dummyFile cat:".m"];
  132.     [self _try:dummyFile withAlternatives:sourceSearchPathes
  133.             forText:sourceFile andGetStyle:&sourceStyle andSetPath:_sourcePath];
  134.     
  135.     // Now the documentation. if there is no RTF docu we should ty to find a
  136.     // RTFD docu too !
  137.     
  138.     [dummyFile takeStringValue:filename];
  139.     [dummyFile cat:".rtf"];
  140.     [self _try:dummyFile withAlternatives:docuSearchPathes
  141.             forText:docuFile andGetStyle:&docuStyle andSetPath:_docuPath];
  142.     
  143.     [dummyFile free];
  144.     
  145.     // Its time to check all the methods we can find.
  146.     // NOTE: If there are No methods we will quit right here !
  147.     
  148.     methodList = [List new];
  149.     if( ![self reparseMethods:self] ) return nil;
  150.     
  151.     // If desired we should check to documentation first...maybe we have to add
  152.     // something before we start. But the methods have to be know !!
  153.     
  154.     // if( [preferences should AutoCheckDocu] )
  155.     
  156.     // [self checkDocumentation:self];    
  157.  
  158.     // Now there are some window settings to be done plus adjusting the
  159.     // browser.
  160.     
  161.     // First lets connect the popUps. This has to be done before the browsers
  162.     // try to get their info.
  163.     // Be sure we have the right initial selection inside the proper matrix !
  164.     
  165.     [[modePopup target] setTarget:self];
  166.     [[modePopup target] setAction:@selector(switchToNewDisplayMode:)];
  167.     index = [[modePopup target] indexOfItem:[modePopup title]];
  168.     
  169.     if( index > -1 )
  170.     {    
  171.         aMatrix = [[modePopup target] itemList];
  172.         
  173.          if( ![aMatrix selectedCell] )
  174.             [aMatrix selectCell:[[aMatrix cellList] objectAt:index]];
  175.     }
  176.  
  177.     // Lets do the same for the other popup..
  178.     
  179.     [[secondaryModePopup target] setTarget:self];
  180.     [[secondaryModePopup target] setAction:@selector(switchToNewDisplayMode:)];
  181.     index = [[secondaryModePopup target] indexOfItem:[modePopup title]];
  182.     
  183.     if( index > -1 )
  184.     {    
  185.         aMatrix = [[secondaryModePopup target] itemList];
  186.         
  187.          if( ![aMatrix selectedCell] )
  188.             [aMatrix selectCell:[[aMatrix cellList] objectAt:index]];
  189.     }
  190.  
  191.     // NOTE: We have to set the browser delegate by code because otherwise
  192.     // we would be requested to fill the browser before the right cell
  193.     // prototype is set (just right after loading the NIB!) !!!.
  194.     
  195.     cellPrototype = [[CEBrowserCell alloc] init];
  196.  
  197.     [cellPrototype addFixedTab:4];
  198.     [cellPrototype addFixedTab:20];
  199.     [cellPrototype addFixedTab:-15];
  200.  
  201.     [browser setMatrixClass:[CECellMatrix class]];
  202.     [browser setCellPrototype:cellPrototype];
  203.     [browser setTarget:self];
  204.     [browser setAction:@selector(selectNewMethod:)];
  205.     [browser setDoubleAction:@selector(selectNewMethod:)];
  206.     [browser setDelegate:self];
  207.     [browser loadColumnZero];
  208.     
  209.     [secondaryBrowser setMatrixClass:[CECellMatrix class]];
  210.     [secondaryBrowser setCellPrototype:cellPrototype];
  211.     [secondaryBrowser setDelegate:self];
  212.     [secondaryBrowser loadColumnZero];
  213.     
  214.     [window setTitleAsFilename:[self filename]];
  215.     [window setMiniwindowImage:[NXImage findImageNamed:"CESmallObjectPink"]];
  216.     
  217.     // Well this is ugly.
  218.     // We should update all the view bevor it gets visible....but then we 
  219.     // have no selected cell and bang..crash..not method..and bang..not string
  220.     // for the Text object. And the find methdo crashes...
  221.     // [browser selectCell:[browser getLoadedCellAtRow:0 inColumn:0]];
  222.     // somehow this doesn't work...but I don't want to waste my time.
  223.     // Ok I fixed the selectMethod to not crash...still its not clean.
  224.     
  225.     [self selectNewMethod:self];
  226.  
  227.     // ok now we are a new document so lets become active.
  228.  
  229.     [self _setIsUpdatingTextViews:NO];
  230.     _changedTexts = NO;
  231.     [window makeKeyAndOrderFront:self];
  232.     
  233.     return self;
  234. }
  235.  
  236. - free
  237.  {
  238.      // We do not free all the NIB objects...hmm should check that out.
  239.     
  240.       [window free];
  241.       [cheatWindow free];
  242.       [filename free];
  243.     [_docuPath free];
  244.     [_sourcePath free];
  245.     [_headerPath free];
  246.     [[methodList freeObjects] free];
  247.     
  248.       return [super free];
  249.  }
  250.  
  251. - window
  252. {
  253.     // We have to return the main window to ensure that the right window gets
  254.     // topped when the app decided that we should become visible.
  255.     
  256.     return window;
  257. }
  258.  
  259. /*
  260.  * Here come the methods we will implement to satisfy our window.
  261.  * They are useful to change the inspector and handle oher tasks.
  262.  */
  263.  
  264. - reparseMethods:sender
  265. {
  266.     // This is used to keep the two view up to date. Making changes to the
  267.     // We will reset those origin pointers because copying our current text-
  268.     // sections back would most propably destroy something!
  269.  
  270.     _docuOriginStart = 0;
  271.     _docuOriginEnd = 0;
  272.     _sourceOriginStart = 0;
  273.     _sourceOriginEnd = 0;
  274.     _changedTexts = NO;
  275.  
  276.     [self _setIsUpdatingTextViews:YES];
  277.  
  278.     [methodList freeObjects];
  279.     [self _parseMethodFile];
  280.     if( [methodList count] == 0 )
  281.     {
  282.         // Ooops its time to exit..ther are no method.
  283.         // BUG: This is not a Nice way of exiting !
  284.         
  285.         NXRunAlertPanel( NULL, "Could not find a single method inside %s\n"\
  286.                             "Methods MUST be definined in the header file "\
  287.                             "in order to work with a class!", 
  288.                             "Cancel", NULL, NULL, 
  289.                             [_headerPath stringValue] );
  290.         
  291.         [[NXApp delegate] addToReleasePool:self];
  292.         return nil;
  293.     }
  294.     // [self checkDocumentation:self];
  295.  
  296.     [browser loadColumnZero];
  297.     [self _setIsUpdatingTextViews:NO];
  298.     
  299.     return self;
  300. }
  301.  
  302. - save:sender
  303. {
  304.     // Here we will save all the files in a stupid fashion...anyway.
  305.     // It works for the moment.
  306.     // We will only save file we were able to load !!
  307.     
  308.     id    topWindow;
  309.     
  310.     if( ![window isDocEdited] ) return self;
  311.     
  312.     // Now lets be sure that ALL changes are in the main files...and
  313.     // Nobody sees the changes going on.
  314.     // But this should ONLY happen when the key window is or class browser.
  315.     // We might corrupt data otherwise !!! The selection might not refer to the
  316.     // right positions anymore !
  317.     // We have to take care of which window was the source of the last
  318.     // cahnges. Depening on that we will hvae to make some consitency 
  319.     // adjustment work !
  320.     
  321.     if( _changedTexts )
  322.             topWindow = _changedWindow;
  323.     else    topWindow = nil;
  324.  
  325.     if( topWindow == window )
  326.         [self _silentlySyncWindows];
  327.         
  328.     // Now if the class has been edited we will save the ASCII Header,
  329.     // RTF source and RTF Documentaion. Quite stupid...but it works for me.
  330.     
  331.     [self _writeText:headerFile withStyle:headerStyle to:_headerPath];
  332.     [self _writeText:sourceFile withStyle:sourceStyle to:_sourcePath];
  333.     [self _writeText:docuFile withStyle:docuStyle to:_docuPath];
  334.     
  335.     // Ok ... well now there is nothing new here.
  336.     
  337.     [cheatWindow setDocEdited:NO];
  338.     [window setDocEdited:NO];
  339.  
  340.     // Now tell the workspace that we did some changes..
  341.  
  342.     [[Application workspace] fileSystemChanged];
  343.  
  344.     // If we have saved from inside the cheat window we have to update the
  345.     // main browser. Otherwise this one will no recognize the changes made.
  346.     
  347.     if( topWindow == cheatWindow ) 
  348.     {
  349.         [self reparseMethods:self];
  350.         [self selectNewMethod:self];
  351.     }
  352.  
  353.     return self;
  354. }
  355.  
  356. - close:sender
  357. {
  358.     // Tell both windows to go away...
  359.     // To give feedback on which windows will dissappear we will make the 
  360.     // key first.
  361.     // The browser window is the main window...the only one to focus at...
  362.     // so we can close the other window if the main window is gone...
  363.     
  364.     [window makeKeyAndOrderFront:self];
  365.     [window performClose:self];
  366.     if( [window isVisible] ) return nil;
  367.     
  368.     // Oh...we really did close the window...the cheat window has
  369.     // passed away during the main-close too...so we are done.
  370.  
  371.     return self;
  372. }
  373.  
  374. - undo:sender
  375. {
  376.     // Well sorry this is a stupid undo...it only undos alle the changes done
  377.     // since switched to a certain method.
  378.     // This only works if the key window is our main browser.
  379.     // The replacement of the selection will be visiible to give visual
  380.     // feedback that something is happening.
  381.     
  382.     id    ourMethod;
  383.     id    focusView;
  384.     
  385.     if( [NXApp keyWindow] == window )
  386.     {
  387.         [self _setIsUpdatingTextViews:YES];
  388.         ourMethod = selection;
  389.         if( ourMethod != nil )
  390.         {
  391.             [self _showDocuForMethod:ourMethod];
  392.             [self _showSourceForMethod:ourMethod];
  393.         }
  394.         if( [sourceTextView isEditable] )
  395.                 focusView = sourceTextView;
  396.         else    focusView = docuTextView;
  397.     
  398.         [focusView setSel:0 :0];
  399.         [focusView scrollSelToVisible];
  400.         _changedTexts = NO;
  401.         [self _setIsUpdatingTextViews:NO];
  402.         [window display];
  403.         [cheatWindow display];
  404.     }
  405.     return self;    
  406. }
  407.  
  408. - resolveLock:sender
  409. {
  410.     return [[NXApp delegate] showAccessHelper:self];
  411. }
  412.  
  413. - switchToNewDisplayMode:sender
  414. {
  415.     id    primaryPopMatrix;
  416.     
  417.     // Called by the PopUp when something changes...just ask the browser..
  418.     // who ever that may be..to load its first column.
  419.  
  420.     primaryPopMatrix = [[modePopup target] itemList];
  421.  
  422.     if( sender == primaryPopMatrix )
  423.     {
  424.         [browser loadColumnZero];
  425.         [self _checkSelectionInBrowser:browser];
  426.     }    
  427.     else [secondaryBrowser loadColumnZero];
  428.     
  429.     // Perhaps we don't only have the update the browser..but also
  430.     // show the other window. this will disappear in a future release.
  431.     
  432.     if( sender == primaryPopMatrix &&
  433.         [[primaryPopMatrix selectedCell] tag] == CE_BrowsePlainC ) 
  434.         [self showCheatWindow:self];
  435.  
  436.     return self;
  437. }
  438.  
  439. - selectNewMethodFromText:sender
  440. {
  441.     const char * realMethod;
  442.     
  443.     // if we can't find the method..lets assume it is a new one...
  444.     // either rename the current...or create a new one.
  445.      
  446.     NXRunAlertPanel( NULL, "%s\nThis acction should either rename the "\
  447.                             "current method or add a new one\n"\
  448.                             "Not yet implemented :-(", 
  449.                                "Create New", "Rename Current", "Cancel", 
  450.                                [sender stringValue] );
  451.                                
  452.     // lets reset the right method string..
  453.     
  454.     realMethod = [selection name];
  455.     
  456.     if( realMethod ) [sender setStringValue:realMethod];
  457.     return self;
  458. }
  459.  
  460. - selectNewMethod:sender
  461. {
  462.     id    ourMethod;
  463.     id    focusView;
  464.     
  465.     // Now lets stop redraws until we have finished...looks a little better.
  466.     // This part is a little trick because somehow I have to find the right
  467.     // selections inside the original texts. Now the Text object is not very
  468.     // helpful here. The find method is more then stupid.
  469.     
  470.     [self _setIsUpdatingTextViews:YES];
  471.     [self _syncWindows];
  472.  
  473.     // During the init phase ther is no selected method inside the browser..
  474.     // To solve that we simple take the first method we have..
  475.     
  476.     ourMethod = [[browser selectedCell] source];
  477.     if( ourMethod == nil ) ourMethod = [methodList objectAt:0];
  478.     selection = ourMethod;
  479.     
  480.     [selectorNameField setStringValue:[ourMethod selectorName]];
  481.     [methodNameField setStringValue:[ourMethod name]];
  482.     
  483.     // Lets update the method docs/source view with the new text.
  484.     
  485.     [self _showDocuForMethod:ourMethod];
  486.     [self _showSourceForMethod:ourMethod];
  487.     
  488.     // It is time to redraw all the fun stuff again. We will make a startup
  489.     // selection first.
  490.  
  491.     if( [sourceTextView isEditable] )
  492.             focusView = sourceTextView;
  493.     else    focusView = docuTextView;
  494.     
  495.     [focusView setSel:0 :0];
  496.     [focusView scrollSelToVisible];
  497.  
  498.     // Now be sure that the changes really get displayed.
  499.  
  500.     [self _setIsUpdatingTextViews:NO];
  501.     [self _checkSelectionInBrowser:browser];
  502.  
  503.     [window display];
  504.     [cheatWindow display];
  505.  
  506.     // Ok here comes the test section
  507.     return self;
  508. }
  509.  
  510. - selectTrueLine:(int)line inSource:(BOOL)aFlag
  511. {
  512.     id    targetText;
  513.     
  514.     if( aFlag )
  515.     {
  516.         targetText = sourceFile;
  517.         [self showImplementationOnly:self];
  518.     }
  519.     else
  520.     {
  521.         targetText = headerFile;
  522.         [self showInterfaceOnly:self];
  523.     }
  524.     [self showCheatWindow:self];
  525.     
  526.     [targetText selectTrueLine:line];
  527.     [targetText scrollSelToVisible];
  528.     
  529.     return self;
  530. }
  531.  
  532. - _setIsUpdatingTextViews:(BOOL)flag
  533. {
  534.     // If we have a switch of the docu update stuff...we should take
  535.     // care that the pasteboard won't get corrupted !
  536.     // Which means..wehn finshed with the updates...we will put back the 
  537.     // pervious pastebord contents.
  538.     
  539.     if( _updatingTextViews == NO &&
  540.         flag == YES )
  541.     {
  542.         [window disableDisplay];
  543.         [cheatWindow disableDisplay];
  544.     }
  545.  
  546.     else if( _updatingTextViews == YES &&
  547.             flag == NO )
  548.     {
  549.         [window reenableDisplay];
  550.         [cheatWindow reenableDisplay];
  551.     }
  552.  
  553.     _updatingTextViews = flag;
  554.  
  555.     return self;
  556. }
  557.  
  558. - _showSourceForMethod:aMethod
  559. {
  560.     // Now lets update the source view. 
  561.     // We assume that no changes get lost...so be sure to backup changes first.
  562.     // Everything that is inside the sourceTextView will get lost.
  563.     
  564.     // Now if we really find the method we can show it.
  565.     
  566.     [self _selectSourceForMethod:aMethod];
  567.     
  568.     // Well...now we have a possible selection inside the original text.
  569.     // If it is no useful selection we don't have any text...and we just mark 
  570.     // the area as useless.
  571.     
  572.     if( _sourceOriginStart > 0 && _sourceOriginEnd > 0 )
  573.     {
  574.         [sourceFile setSel:_sourceOriginStart :_sourceOriginEnd];
  575.         [sourceFile copyTo:_tempPb];
  576.         [sourceTextView setEditable:YES];
  577.         [sourceTextView selectAll:self];
  578.         [sourceTextView pasteFrom:_tempPb];
  579.         
  580.         [sourceTextView setMonoFont:NO];
  581.  
  582.         if( sourceStyle == CE_FILETYPE_ASCII ) [sourceTextView format:self];
  583.     }
  584.     else
  585.     {
  586.         [sourceTextView setEditable:YES];
  587.         [sourceTextView selectAll:self];
  588.         [sourceTextView delete:self];
  589.         [sourceTextView setEditable:NO];
  590.     }
  591.     // We shouldn't corrupt the fonts if we can only handle ASCII !
  592.  
  593.     // if( sourceStyle == CE_FILETYPE_ASCII )
  594.     //         [sourceTextView setMonoFont:YES];
  595.     // else    [sourceTextView setMonoFont:NO];
  596.         
  597.     return self;
  598. }
  599.  
  600. - _silentlySyncWindows
  601. {
  602.     // this method silently syncs both window. You won't see any selections
  603.     // cluttering your screen.
  604.     // We have to reselect the source and documenation here because otherwise
  605.     // our references won't be corret anymore !!!
  606.     
  607.     [window disableDisplay];
  608.     [cheatWindow disableDisplay];
  609.     [self _setIsUpdatingTextViews:YES];
  610.     [self _syncWindows];
  611.     [self _selectDocuForMethod:selection];
  612.     [self _selectSourceForMethod:selection];
  613.     [self _setIsUpdatingTextViews:NO];
  614.     [cheatWindow reenableDisplay];
  615.     [window reenableDisplay];
  616.         
  617.     return self;
  618. }
  619.  
  620. - _syncWindows
  621. {
  622.     // This method does sync the cheat window with the main window.
  623.     // If there is a currently used documentation we have to copy it back
  624.     // first !
  625.     // After that it remarks that there are no more differneces between
  626.     // the two views.
  627.     // After all we DON'T trace the changes here. This is just a common
  628.     // source part of _sliently Syncing and selecting a new method !!
  629.     
  630.     if( _docuOriginStart > 0 && _docuOriginEnd > 0 && _changedTexts )
  631.     {
  632.         [docuTextView selectAll:self];
  633.         [docuTextView copyTo:_tempPb];
  634.         [docuFile setSel:_docuOriginStart :_docuOriginEnd];
  635.         [docuFile pasteFrom:_tempPb];
  636.         [docuTextView setSel:0 :0];
  637.     }
  638.     
  639.     // Same for the source code.
  640.     
  641.     if( _sourceOriginStart > 0 && _sourceOriginEnd > 0 && _changedTexts )
  642.     {
  643.         [sourceTextView selectAll:self];
  644.         [sourceTextView copyTo:_tempPb];
  645.         [sourceFile setSel:_sourceOriginStart :_sourceOriginEnd];
  646.         [sourceFile pasteFrom:_tempPb];
  647.         [sourceTextView setSel:0 :0];
  648.     }
  649.     
  650.     _changedTexts = NO;
  651.  
  652.     return self;
  653. }
  654.  
  655. - _showDocuForMethod:aMethod
  656. {
  657.     // Now lets update the documentation view. 
  658.     // We assume that no changes get lost...so be sure to backup changes first.
  659.     // Everything that is inside the docuTextView will get lost.
  660.     
  661.     // Now if we really find the method we have to remove the blakn lines and
  662.     // set the right selection part.
  663.     
  664.     [self _selectDocuForMethod:aMethod];
  665.     
  666.     // Well...now we have a possible selection inside the original text.
  667.     // If it is no useful selection we don't have any text...and we just mark 
  668.     // the area as useless.
  669.     
  670.     if( _docuOriginStart > 0 && _docuOriginEnd > 0 )
  671.     {
  672.         [docuFile setSel:_docuOriginStart :_docuOriginEnd];
  673.         [docuFile copyTo:_tempPb];
  674.         [docuTextView setEditable:YES];
  675.         [docuTextView selectAll:self];
  676.         [docuTextView pasteFrom:_tempPb];
  677.     }
  678.     else
  679.     {
  680.         [docuTextView setEditable:YES];
  681.         [docuTextView selectAll:self];
  682.         [docuTextView delete:self];
  683.         [docuTextView setEditable:NO];
  684.     }
  685.     return self;    
  686. }
  687.  
  688. - showCheatWindow:sender
  689. {
  690.     id    aString;
  691.     // Lets choose a defaults selection to clean up the window.
  692.     [docuFile setSel:0 :0];
  693.     aString = [filename fileName];
  694.     [aString cat:"...Cheat Window"];
  695.     [cheatWindow setMiniwindowImage:
  696.                     [NXImage findImageNamed:"ObjectPink.rtf.m.h"]];
  697.     [cheatWindow setTitle:[aString stringValue]];
  698.     [aString free];
  699.     
  700.     [cheatWindow makeKeyAndOrderFront:self];
  701.     return self;
  702. }
  703.  
  704. - showAllFiles:sender
  705. {
  706.     NXRect    bounds;
  707.     id        aView;
  708.  
  709.     [cheatWindow disableDisplay];
  710.     
  711.     // Resize the header view to some useful none-zero value.
  712.     // This is necessary to see anything at all.
  713.     // We take the header because it will then end up as the smallest view.
  714.     
  715.     aView = [[headerFile superview] superview];
  716.     [aView getBounds:&bounds];
  717.     [aView sizeTo:bounds.size.width :20];
  718.  
  719.     // Make the sourceFile and docuFile small enough..and redraw the window.
  720.     
  721.     aView = [[sourceFile superview] superview];
  722.     [aView getBounds:&bounds];
  723.     [aView sizeTo:bounds.size.width :50];
  724.     aView = [[docuFile superview] superview];
  725.     [aView getBounds:&bounds];
  726.     [aView sizeTo:bounds.size.width :50];
  727.  
  728.     [cheatWindow disableDisplay];
  729.     [cheatWindow display];
  730.     return self;
  731. }
  732.  
  733. - showDocumentationOnly:sender
  734. {
  735.     [self _resizeCheatWindowFor:docuFile
  736.                         andHide:headerFile :sourceFile];
  737.  
  738.     return self;
  739. }
  740.  
  741. - showInterfaceOnly:sender
  742. {
  743.     [self _resizeCheatWindowFor:headerFile
  744.                         andHide:docuFile :sourceFile];
  745.  
  746.     return self;
  747. }
  748.  
  749. - showImplementationOnly:sender
  750. {
  751.     [self _resizeCheatWindowFor:sourceFile
  752.                         andHide:docuFile :headerFile];
  753.  
  754.     return self;
  755. }
  756.  
  757. - _resizeCheatWindowFor:mainView andHide:secondView :thirdView
  758. {
  759.     NXRect    bounds;
  760.     id        aView;
  761.  
  762.     [cheatWindow disableDisplay];
  763.     
  764.     // Resize the main view to some useful none-zero value.
  765.     // This is necessary to see anything at all.
  766.     
  767.     aView = [[mainView superview] superview];
  768.     [aView getBounds:&bounds];
  769.     [aView sizeTo:bounds.size.width :20];
  770.  
  771.     // Make the secondView and thirdView small enough..and redraw the window.
  772.     
  773.     aView = [[secondView superview] superview];
  774.     [aView getBounds:&bounds];
  775.     [aView sizeTo:bounds.size.width :0];
  776.     aView = [[thirdView superview] superview];
  777.     [aView getBounds:&bounds];
  778.     [aView sizeTo:bounds.size.width :0];
  779.  
  780.     [cheatWindow disableDisplay];
  781.     [cheatWindow display];
  782.     return self;
  783. }
  784.  
  785. - (int)browser:sender fillMatrix:matrix inColumn:(int)column
  786. {
  787.     int    count;
  788.     
  789.     // BUG: Another "bug" circumvention ??
  790.     // I took this from the RZToDoList app. Perhaps it is quite useless...
  791.     // I don't have the time to diddle with it..but it seams to work without
  792.     // the next line too.
  793.     
  794.     [matrix renewRows:0 cols:1];
  795.  
  796.     // We don't use lazy browsers because it wouldn't save any space 
  797.     // (maintaining the lists twice) and it is easiers this way.
  798.     
  799.     if( sender == browser )
  800.     {
  801.         switch( [[[[modePopup target] itemList] selectedCell] tag] )
  802.         {
  803.             case CE_BrowseClassInfo:
  804.                 count = [self fillClassInfoIntoMatrix:matrix];
  805.                 break;
  806.             
  807.             case CE_BrowseInstanceVars:
  808.                 count = [self fillInstanceVarsIntoMatrix:matrix];
  809.                 break;
  810.             
  811.             case CE_BrowseMethods:
  812.                 count = [self fillMethodsIntoMatrix:matrix];
  813.                 break;
  814.             
  815.             case CE_BrowseInstanceMethods:
  816.                 count = [self fillInstanceMethodsIntoMatrix:matrix];
  817.                 break;
  818.             
  819.             case CE_BrowseClassMethods:
  820.                 count = [self fillClassMethodsIntoMatrix:matrix];
  821.                 break;
  822.             
  823.             case CE_BrowseMethodsByCategory:
  824.                 count = [self fillMethodsByCategoryIntoMatrix:matrix];
  825.                 break;
  826.             
  827.             case CE_BrowseDependencies:
  828.                 count = [self fillDependenciesIntoMatrix:matrix];
  829.                 break;
  830.             
  831.             case CE_BrowsePlainC:
  832.             default:
  833.                 count = [self fillPlainCIntoMatrix:matrix];
  834.         }
  835.     }
  836.     // Otherwise we have to fill the socond browser..
  837.  
  838.     else
  839.     {
  840. /*    
  841.         switch( [[[[secondaryModePopup target] itemList] selectedCell] tag] )
  842.         {
  843.             case CE_BrowseReferences:
  844.                 count = [self fillReferencesIntoMatrix:matrix
  845.                                 accordingTo:selection];
  846.                 break;
  847.             
  848.             case CE_BrowseHistory:
  849.                 count = [self fillHistoryIntoMatrix:matrix 
  850.                                 accordingTo:selection];
  851.                 break;
  852.             
  853.             case CE_BrowseBugs:
  854.             default:
  855.                 count = [self fillBugsIntoMatrix:matrix 
  856.                                 accordingTo:selection];
  857.         }
  858. */    
  859.         count = 0;
  860.     }
  861.     
  862.     return count;
  863. }
  864.  
  865. - _checkSelectionInBrowser:aBrowser
  866. {
  867.     id    matrix;
  868.     int    i;
  869.     int    count;
  870.     int    dummy;
  871.     
  872.     // Now lets see if we can make some selections. If possible the browser
  873.     // should reflect the current selection.
  874.         
  875.     if( aBrowser == browser &&
  876.         selection != nil &&
  877.         selection != [[aBrowser selectedCell] source] )
  878.     {
  879.         matrix = [aBrowser matrixInColumn:0];
  880.         [matrix getNumRows:&count numCols:&dummy];
  881.         
  882.         for( i=0; i<count; i++ )
  883.         {
  884.             if( [[matrix cellAt:i :0] source] == selection )
  885.             {
  886.                 [matrix selectCellAt:i :0];
  887.                 [matrix scrollCellToVisible:i :0];
  888.                 break;
  889.             }
  890.         }
  891.     }
  892.     return self;
  893. }
  894.  
  895. - (int)fillClassInfoIntoMatrix:matrix
  896. {
  897.     int    count;
  898.     id    theCell;
  899.     id    className;
  900.     
  901.     count = 0;
  902.  
  903.     className = [filename fileBasename];
  904.     [matrix addRow];
  905.     theCell = [matrix cellAt:count :0];
  906.     [theCell setImageNamed:"ClassS" at:0];
  907.     [theCell setText:[className stringValue] at:1 font:FONT_BOLD];
  908.     [theCell setLeaf:YES];
  909.     [theCell setLoaded:YES];
  910.     [theCell setEnabled:NO];
  911.  
  912.     [className free];
  913.     count++;
  914.     
  915.     [matrix addRow];
  916.     theCell = [matrix cellAt:count :0];
  917.     [theCell setImageNamed:"ProtocolS" at:0];
  918.     [theCell setText:"<SomeProtocol>" at:1];
  919.     [theCell setLeaf:YES];
  920.     [theCell setLoaded:YES];
  921.     [theCell setEnabled:NO];
  922.  
  923.     count++;
  924.     
  925.     [matrix addRow];
  926.     theCell = [matrix cellAt:count :0];
  927.     [theCell setImageNamed:"CategoryS" at:0];
  928.     [theCell setText:"Archiving" at:1 font:FONT_BOLD];
  929.     [theCell setLeaf:YES];
  930.     [theCell setLoaded:YES];
  931.     [theCell setEnabled:NO];
  932.  
  933.     count++;
  934.     
  935.     [matrix addRow];
  936.     theCell = [matrix cellAt:count :0];
  937.     [theCell setImageNamed:"CategoryS" at:0];
  938.     [theCell setText:"Working" at:1 font:FONT_BOLD];
  939.     [theCell setLeaf:YES];
  940.     [theCell setLoaded:YES];
  941.     [theCell setEnabled:NO];
  942.  
  943.     count++;
  944.     
  945.     [matrix addRow];
  946.     theCell = [matrix cellAt:count :0];
  947.     [theCell setImageNamed:"CategoryS" at:0];
  948.     [theCell setText:"Private" at:1 font:FONT_BOLD];
  949.     [theCell setLeaf:YES];
  950.     [theCell setLoaded:YES];
  951.     [theCell setEnabled:NO];
  952.  
  953.     count++;
  954.     
  955.     return count;
  956. }
  957.  
  958. - (int)fillInstanceVarsIntoMatrix:matrix
  959. {
  960.     int    count;
  961.     id    theCell;
  962.     
  963.     count = 0;
  964.  
  965.     // BUG:  There is no way to set the Tabs afterwards !!!!
  966.     
  967.     [matrix addRow];
  968.     theCell = [matrix cellAt:count :0];
  969.     [theCell clearTabs];
  970.     [theCell addFixedTab:4];
  971.     [theCell addProportionalTab:0.3 min:50 max:600];
  972.     [theCell setText:"id" at:0];
  973.     [theCell setText:"anInstance" at:1];
  974.     [theCell setLeaf:YES];
  975.     [theCell setLoaded:YES];
  976.     [theCell setEnabled:NO];
  977.  
  978.     count++;
  979.     
  980.     [matrix addRow];
  981.     theCell = [matrix cellAt:count :0];
  982.     [theCell clearTabs];
  983.     [theCell addFixedTab:4];
  984.     [theCell addProportionalTab:0.3 min:50 max:600];
  985.     [theCell setText:"id" at:0];
  986.     [theCell setText:"secondInstance" at:1];
  987.     [theCell setLeaf:YES];
  988.     [theCell setLoaded:YES];
  989.     [theCell setEnabled:NO];
  990.  
  991.     count++;
  992.     
  993.     [matrix addRow];
  994.     theCell = [matrix cellAt:count :0];
  995.     [theCell clearTabs];
  996.     [theCell addFixedTab:4];
  997.     [theCell addProportionalTab:0.3 min:50 max:600];
  998.     [theCell setText:"int *" at:0];
  999.     [theCell setText:"somePointer" at:1];
  1000.     [theCell setLeaf:YES];
  1001.     [theCell setLoaded:YES];
  1002.     [theCell setEnabled:NO];
  1003.  
  1004.     count++;
  1005.     
  1006.     [matrix addRow];
  1007.     theCell = [matrix cellAt:count :0];
  1008.     [theCell clearTabs];
  1009.     [theCell addFixedTab:4];
  1010.     [theCell addProportionalTab:0.3 min:50 max:600];
  1011.     [theCell setText:"is" at:0];
  1012.     [theCell setText:"thisUsefulAnyway" at:1];
  1013.     [theCell setLeaf:YES];
  1014.     [theCell setLoaded:YES];
  1015.     [theCell setEnabled:NO];
  1016.  
  1017.     count++;
  1018.     
  1019.     return count;
  1020. }
  1021.  
  1022. - (int)fillMethodsIntoMatrix:matrix
  1023. {
  1024.     int    i;
  1025.     int    count;
  1026.     id    theMethod;
  1027.     id    theCell;
  1028.     id    aList;
  1029.     
  1030.     aList = [MiscSortedList new];
  1031.     [aList setSortOrder:Misc_ASCENDING];
  1032.     [aList setSortEnabled:YES];
  1033.  
  1034.     for( i=0; i<[methodList count];i++ )
  1035.         [aList addObject:[methodList objectAt:i]];
  1036.  
  1037.     count = 0;
  1038.  
  1039.     for( i=0; i<[aList count]; i++ )
  1040.     {
  1041.         theMethod = [aList objectAt:i];
  1042.         
  1043.         [matrix addRow];
  1044.         
  1045.         theCell = [matrix cellAt:count :0];
  1046.         [self _fillCell:theCell forMethod:theMethod];
  1047.         [theCell setLeaf:YES];
  1048.         [theCell setLoaded:YES];
  1049.  
  1050.         count++;
  1051.     }    
  1052.     [aList free];
  1053.     return count;
  1054. }
  1055.  
  1056. - (int)fillInstanceMethodsIntoMatrix:matrix
  1057. {
  1058.     int    i;
  1059.     int    count;
  1060.     id    theMethod;
  1061.     id    theCell;
  1062.     id    aList;
  1063.     
  1064.     aList = [MiscSortedList new];
  1065.     [aList setSortOrder:Misc_ASCENDING];
  1066.     [aList setSortEnabled:YES];
  1067.  
  1068.     for( i=0; i<[methodList count];i++ )
  1069.         [aList addObject:[methodList objectAt:i]];
  1070.  
  1071.     count = 0;
  1072.  
  1073.     for( i=0; i<[methodList count]; i++ )
  1074.     {
  1075.         theMethod = [aList objectAt:i];
  1076.  
  1077.         if( [theMethod isInstanceMethod] )
  1078.         {
  1079.             [matrix addRow];
  1080.         
  1081.             theCell = [matrix cellAt:count :0];
  1082.             [self _fillCell:theCell forMethod:theMethod];
  1083.             [theCell setLeaf:YES];
  1084.             [theCell setLoaded:YES];
  1085.     
  1086.             count++;
  1087.         }
  1088.     }    
  1089.     [aList free];
  1090.     return count;
  1091. }
  1092.  
  1093. - (int)fillClassMethodsIntoMatrix:matrix
  1094. {
  1095.     int    i;
  1096.     int    count;
  1097.     id    theMethod;
  1098.     id    theCell;
  1099.     id    aList;
  1100.     
  1101.     aList = [MiscSortedList new];
  1102.     [aList setSortOrder:Misc_ASCENDING];
  1103.     [aList setSortEnabled:YES];
  1104.  
  1105.     for( i=0; i<[methodList count];i++ )
  1106.         [aList addObject:[methodList objectAt:i]];
  1107.  
  1108.  
  1109.     count = 0;
  1110.  
  1111.     for( i=0; i<[methodList count]; i++ )
  1112.     {
  1113.         theMethod = [aList objectAt:i];
  1114.  
  1115.         if( ![theMethod isInstanceMethod] )
  1116.         {
  1117.             [matrix addRow];
  1118.         
  1119.             theCell = [matrix cellAt:count :0];
  1120.             [self _fillCell:theCell forMethod:theMethod];
  1121.             [theCell setLeaf:YES];
  1122.             [theCell setLoaded:YES];
  1123.  
  1124.             count++;
  1125.         }
  1126.     }    
  1127.     [aList free];
  1128.     return count;
  1129. }
  1130.  
  1131. - (int)fillMethodsByCategoryIntoMatrix:matrix
  1132. {
  1133.     int    i;
  1134.     int    count;
  1135.     id    theMethod;
  1136.     id    theCell;
  1137.     id    className;
  1138.     
  1139.     count = 0;
  1140.  
  1141.     className = [filename fileBasename];
  1142.     [matrix addRow];
  1143.     theCell = [matrix cellAt:count :0];
  1144.     [theCell setImageNamed:"ClassS" at:0];
  1145.     [theCell setText:[className stringValue] at:1 font:FONT_BOLD];
  1146.     [theCell setLeaf:YES];
  1147.     [theCell setLoaded:YES];
  1148.     [theCell setEnabled:NO];
  1149.  
  1150.     [className free];
  1151.     count++;    
  1152.  
  1153.     // Now add all the methods...unsorted..
  1154.     
  1155.     for( i=0; i<[methodList count]; i++ )
  1156.     {
  1157.         theMethod = [methodList objectAt:i];
  1158.         
  1159.         [matrix addRow];
  1160.         
  1161.         theCell = [matrix cellAt:count :0];
  1162.         [self _fillCell:theCell forMethod:theMethod];
  1163.         [theCell setLeaf:YES];
  1164.         [theCell setLoaded:YES];
  1165.  
  1166.         count++;
  1167.     }    
  1168.     return count;
  1169. }
  1170.  
  1171. - (int)fillDependenciesIntoMatrix:matrix
  1172. {
  1173.     int    count;
  1174.     id    theCell;
  1175.     
  1176.     count = 0;
  1177.  
  1178.     [matrix addRow];
  1179.     theCell = [matrix cellAt:count :0];
  1180.     [theCell setText:"<appkit/appkit.h>" at:1];
  1181.     [theCell setLeaf:YES];
  1182.     [theCell setLoaded:YES];
  1183.     [theCell setEnabled:NO];
  1184.  
  1185.     count++;
  1186.     
  1187.     [matrix addRow];
  1188.     theCell = [matrix cellAt:count :0];
  1189.     [theCell setText:"<misckit/misckit.h>" at:1];
  1190.     [theCell setLeaf:YES];
  1191.     [theCell setLoaded:YES];
  1192.     [theCell setEnabled:NO];
  1193.  
  1194.     count++;
  1195.     
  1196.     [matrix addRow];
  1197.     theCell = [matrix cellAt:count :0];
  1198.     [theCell setText:"\"nothing/works.h\"" at:1];
  1199.     [theCell setLeaf:YES];
  1200.     [theCell setLoaded:YES];
  1201.     [theCell setEnabled:NO];
  1202.  
  1203.     count++;
  1204.     
  1205.     return count;
  1206. }
  1207.  
  1208. - (int)fillPlainCIntoMatrix:matrix
  1209. {
  1210.     return [self fillMethodsByCategoryIntoMatrix:matrix];
  1211. }
  1212.  
  1213. - _fillCell:theCell forMethod:aMethod
  1214. {
  1215.     if( [aMethod isInstanceMethod] )
  1216.             [theCell setImageNamed:"InstanceMethodS" at:0];
  1217.     else    [theCell setImageNamed:"ClassMethodS" at:0];
  1218.     
  1219.     [theCell setText:[aMethod selectorName] at:1];
  1220.     [theCell setSource:aMethod];
  1221.  
  1222.     // Now set the infos we need for dragging.
  1223.     
  1224.     [theCell setDraggingSource:aMethod at:0];    
  1225.     
  1226.     return self;
  1227. }
  1228.  
  1229. - (int)fillReferencesIntoMatrix:matrix accordingTo:theSelection
  1230. {
  1231.     return 0;
  1232. }
  1233.  
  1234. - (int)fillHistoryIntoMatrix:matrix accordingTo:theSelection
  1235. {
  1236.     return 0;
  1237. }
  1238.  
  1239. - (int)fillBugsIntoMatrix:matrix accordingTo:theSelection
  1240. {
  1241.     return 0;
  1242. }
  1243.  
  1244. - textDidChange:sender
  1245. {
  1246.     // Here we will make both windows as edited. This is for checking 
  1247.     // before saving.
  1248.     // But if we are updating the views from the program we won't say that 
  1249.     // this are real changes. Only user changes count !!
  1250.     // For internal use we are tracking the "real"changes since the last
  1251.     // sync between the two windows.
  1252.     
  1253.     if( sender != docuTextView &&
  1254.         sender != sourceTextView &&
  1255.         sender != docuFile &&
  1256.         sender != headerFile &&
  1257.         sender != sourceFile ) return self;
  1258.     
  1259.     // well only if one of the the relevant texts did send the msg we 
  1260.     // will procede.
  1261.     
  1262.     if( _updatingTextViews == NO &&
  1263.         _changedTexts == NO )
  1264.     {
  1265.         [cheatWindow setDocEdited:YES];
  1266.         [window setDocEdited:YES];
  1267.         _changedTexts = YES;
  1268.         _changedWindow = [sender window];
  1269.     }
  1270.     
  1271.     return self;
  1272. }
  1273.  
  1274. - textDidGetKeys:sender isEmpty:(BOOL)flag
  1275. {
  1276.     // This is for tracing down changes inside the window and highlighing the
  1277.     // character pairs.
  1278.     
  1279.     [self textDidChange:sender];
  1280.  
  1281.     // Now if someone typed a character that should have a corresponding pair
  1282.     // we will highlight temp here.
  1283.     
  1284.     return self;
  1285. }
  1286.  
  1287. - _highlightPrevious:(char *)aString inView:aText
  1288. {
  1289.     // this is for highlighing the character pairs.
  1290.     // The current char and his counter part get highlighted if is is found
  1291.     // If not we will Ping loud.
  1292.      
  1293.     NXSelPt    currentFrom;
  1294.     NXSelPt    currentTo;
  1295.     NXSelPt    from;
  1296.     NXSelPt    to;
  1297.     BOOL    gotIt;
  1298.     
  1299.     [aText getSel:¤tFrom :¤tTo];
  1300.     gotIt = [aText findText:aString 
  1301.                    ignoreCase:NO backwards:YES wrap:NO];
  1302.  
  1303.     if( gotIt )
  1304.     {
  1305.         [aText getSel:&from :&to];
  1306.         [aText setSel:currentFrom.cp-1 :currentFrom.cp];
  1307.     //    wait( 1 );
  1308.         [aText setSel:from.cp :to.cp];
  1309.     }
  1310.     else //    NXBeep();
  1311.     
  1312.     [aText setSel:currentFrom.cp :currentTo.cp];
  1313.     return self;
  1314. }
  1315.  
  1316. - textShouldPerformCompletion:sender
  1317. {
  1318.     id    nameArray;
  1319.     id    aMethod;
  1320.     id    aString;
  1321.     id    stringToFind;
  1322.     int    i;
  1323.     int    foundItems;
  1324.     int    firstMatch;
  1325.  
  1326.     // Ok..only inside the methodField we will do completion
  1327.  
  1328.     if( [sender delegate] != methodNameField ) return nil;
  1329.  
  1330.     // Now get the string and find what we need.
  1331.  
  1332.     stringToFind = [MiscString newWithString:[methodNameField stringValue]];
  1333.     nameArray = [MiscStringArray new];
  1334.  
  1335.     for( i=0; i<[methodList count]; i++ )
  1336.     {
  1337.         aMethod = [methodList objectAt:i];
  1338.         [nameArray addString:[aMethod selectorName]];
  1339.         [nameArray addString:[aMethod name]];
  1340.     }
  1341.     // Now cont the hits...
  1342.  
  1343.     foundItems = 0;
  1344.     firstMatch = -1;
  1345.     
  1346.     for( i=0; i<[nameArray count]; i++ )
  1347.     {
  1348.         aString = [nameArray objectAt:i];
  1349.         if( [aString spotOfString:stringToFind caseSensitive:YES] == 0 )
  1350.         {
  1351.             foundItems++;
  1352.             if( firstMatch == -1 ) firstMatch = i;
  1353.         }    
  1354.     }
  1355.     // Now what have we found.. if there are more then one don't do anything.
  1356.  
  1357.     if( foundItems == 1 )
  1358.         [methodNameField setStringValue:[nameArray stringAt:firstMatch]];
  1359.  
  1360.     // Free it all! We hope that the Array frees the 
  1361.  
  1362.     [stringToFind free];
  1363.     [nameArray free];
  1364.     return self;
  1365. }
  1366.  
  1367. - windowDidBecomeKey:sender
  1368. {
  1369.     // Lets look only at the window. Depending on which window has become key
  1370.     // We have to make some updates between the two views.
  1371.  
  1372.     if( _changedTexts == NO ) return self;
  1373.  
  1374.     // If the sender is the window where we did make the last changes there
  1375.     // is no need to do some updates at all. Just leave it as it is.
  1376.     
  1377.     if( sender == _changedWindow ) return self;
  1378.     
  1379.     if( sender == window ) 
  1380.     {
  1381.         [self reparseMethods:self];
  1382.         [self selectNewMethod:self];
  1383.     }
  1384.  
  1385.     if( sender == cheatWindow )
  1386.         [self _silentlySyncWindows];
  1387.     
  1388.     return self;
  1389. }
  1390.  
  1391. - windowWillClose:sender
  1392. {
  1393.     // Lets save the contents if the user wants it and free ourself.
  1394.  
  1395.     int    result;
  1396.  
  1397.     if( sender != window ) return self;
  1398.  
  1399.     // Oh...looks like somebody wants to close the main window...
  1400.     // Lets check if we have something to save.
  1401.  
  1402.     if( [window isDocEdited] ) 
  1403.     {
  1404.         result = NXRunAlertPanel( "Close", "Save changes to %s?", 
  1405.                                 "Save", "Don't Save", "Cancel", 
  1406.                                 [filename stringValue] );
  1407.  
  1408.         if( result == NX_ALERTOTHER )
  1409.             return nil;
  1410.  
  1411.         else if( result == NX_ALERTDEFAULT )
  1412.             [self save:self];
  1413.     }
  1414.     // We will tell the window that they have to interesting information
  1415.     // anymore. This is because the app checks for these infos before quitting.
  1416.     // And if we decide "Don't save"...this should be done correct.
  1417.  
  1418.     [window setDocEdited:NO];
  1419.     [cheatWindow setDocEdited:NO];
  1420.     [cheatWindow deminiaturize:self];
  1421.     [cheatWindow performClose:self];
  1422.  
  1423.     // Its time to go....
  1424.     
  1425.     [[NXApp delegate] addToReleasePool:self];
  1426.     return self; 
  1427. }
  1428.  
  1429. @end
  1430.  
  1431. /*
  1432.  * History: 13.01.95 Buh
  1433.  *            
  1434.  *
  1435.  * Bugs: - ...
  1436.  */