home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.bin / SourceCode / MiscKit1.2.6 / Bundles / MiscFindPanel / MiscFindPanel.m < prev    next >
Encoding:
Text File  |  1994-04-03  |  13.5 KB  |  537 lines

  1. /*
  2.  *  Copyright (c) 1993 Christopher J. Kane.  All rights reserved.
  3.  *
  4.  *  This software is subject to the terms of the MiscKit license
  5.  *  agreement.  Refer to the license document included with the
  6.  *  MiscKit distribution for these terms.
  7.  *
  8.  *  Version: 1.1.1 (14 December 1993)
  9.  *
  10.  *  Incorporates a bug-fix by Annard Brouwer <annard@stack.urc.tue.nl>.
  11.  *  The nextText outlet of the findTextField was remaining set to the
  12.  *  replaceTextField.  When the replace controls were hidden, this
  13.  *  caused the Tab key in the findTextField to appear to do nothing.
  14.  *  The text in the field is now correctly selected when the Tab key
  15.  *  is pressed.
  16.  */
  17.  
  18. #import <misckit/MiscFindPanel.h>
  19. #import <misckit/SearchableText.h>
  20.  
  21. #define DURING        NX_DURING
  22. #define HANDLER        NX_HANDLER switch (NXLocalHandler.code) {
  23. #define ENDHANDLER    default: NXLogError("Uncaught exception: %d," \
  24.             " in %s:%s.\n", NXLocalHandler.code, __FILE__, \
  25.             sel_getName(_cmd)); abort();} NX_ENDHANDLER
  26. #define IGNORE(E)    case E: NXLogError("%s exception in %s:%s. " \
  27.             "Ignored.\n", #E, __FILE__, sel_getName(_cmd)); break
  28.  
  29. #define LocalString(K)    NXLoadLocalizedStringFromTableInBundle(NULL, \
  30.             [NXBundle bundleForClass:[self class]], K, "")
  31.  
  32. static MiscFindPanel *_sharedFindPanel=nil;
  33. static Pasteboard *_findPb=nil;
  34. static Object *_firstConformer=nil;
  35. static int _pbChangeCount=0;
  36. static BOOL _replEnabled=YES;
  37. static BOOL _useFindPb=YES;
  38. static BOOL _allocationOK=NO;
  39. static BOOL _resizeOK=NO;
  40. static BOOL _fpLoaded=NO;
  41.  
  42. @implementation MiscFindPanel
  43.  
  44. - _calcFindPanelTarget
  45. {
  46.   id i;
  47.   if (_firstConformer!=nil)
  48.     return _firstConformer;
  49.   if (self!=[NXApp keyWindow])
  50.     {
  51.       for(i=[[NXApp keyWindow] firstResponder]; i; i = [i nextResponder])
  52.         if ([i conformsTo:@protocol(SearchableText)])
  53.           return i;
  54.       i = [[NXApp keyWindow] delegate];
  55.       if ([i conformsTo:@protocol(SearchableText)])
  56.         return i;
  57.     }
  58.   if ([NXApp keyWindow]!=[NXApp mainWindow])
  59.     {
  60.       for(i=[[NXApp mainWindow] firstResponder]; i; i = [i nextResponder])
  61.         if ([i conformsTo:@protocol(SearchableText)])
  62.           return i;
  63.       i = [[NXApp mainWindow] delegate];
  64.       if ([i conformsTo:@protocol(SearchableText)])
  65.         return i;
  66.     }
  67.   i = NXApp;
  68.   if ([i conformsTo:@protocol(SearchableText)])
  69.     return i;
  70.   i = [NXApp delegate];
  71.   if ([i conformsTo:@protocol(SearchableText)])
  72.     return i;
  73.   return nil;
  74. }
  75.  
  76. - (void)_getFindPbData
  77. {
  78.   if (_pbChangeCount==[_findPb changeCount])
  79.     return;
  80.   DURING
  81.     if ([_findPb findAvailableTypeFrom:&NXAsciiPboardType num:1])
  82.       {
  83.         char *text;
  84.         int len;
  85.         if ([_findPb readType:NXAsciiPboardType data:&text length:&len])
  86.           {
  87.             [findTextField setStringValue:text];
  88.             [_findPb deallocatePasteboardData:text length:len];
  89.           }
  90.       }
  91.   HANDLER
  92.     IGNORE(NX_pasteboardComm);
  93.     IGNORE(NX_appkitVMError);
  94.   ENDHANDLER
  95.   _pbChangeCount = [_findPb changeCount];
  96. }
  97.  
  98. - (void)_modifyFindPanel
  99. {
  100.   NXRect r;
  101.   [contentView getBounds:&r];
  102.   [self disableFlushWindow];
  103.   if (_replEnabled)
  104.     {
  105.       _resizeOK = YES;
  106.       [super sizeWindow:NX_WIDTH(&r) :NX_HEIGHT(&r)+25.0];
  107.       _resizeOK = NO;
  108.       [[[findTextField superview] superview] moveBy:0.0 :25.0];
  109.       [contentView addSubview:_replBox :NX_BELOW relativeTo:nil];
  110.       [findTextField setNextText:replaceTextField];    /* added by Annard */
  111.     }
  112.   else
  113.     {
  114.       [_replBox removeFromSuperview];
  115.       [[[findTextField superview] superview] moveBy:0.0 :-25.0];
  116.       _resizeOK = YES;
  117.       [super sizeWindow:NX_WIDTH(&r) :NX_HEIGHT(&r)-25.0];
  118.       _resizeOK = NO;
  119.       [findTextField setNextText:findTextField];    /* added by Annard */
  120.     }
  121.   [self display];
  122.   [[self reenableFlushWindow] flushWindowIfNeeded];
  123. }
  124.  
  125. - (void)_resetFindPanel
  126. {
  127.   [messageTextField setStringValue:""];
  128.   [findTextField selectText:nil];
  129. }
  130.  
  131. - (void)_setFindPbData
  132. {
  133.   const char *text;
  134.   if (_pbChangeCount==[_findPb changeCount])
  135.     return;
  136.   text = [findTextField stringValue];
  137.   if (*text=='\0')
  138.     return;
  139.   DURING
  140.     _pbChangeCount = [_findPb declareTypes:&NXAsciiPboardType num:1 owner:NULL];
  141.     [_findPb writeType:NXAsciiPboardType data:text length:(int)strlen(text)];
  142.   HANDLER
  143.     IGNORE(NX_pasteboardComm);
  144.   ENDHANDLER
  145. }
  146.  
  147. + setFindPbEnabled:(BOOL)aBool
  148. {
  149.   _useFindPb = aBool;
  150.   return self;
  151. }
  152.  
  153. + setFirstConformer:aConformer
  154. {
  155.   id formerConformer = _firstConformer;
  156.   if (aConformer==nil || [aConformer conformsTo:@protocol(SearchableText)])
  157.     _firstConformer = aConformer;
  158.   return formerConformer;
  159. }
  160.  
  161. + setReplacementEnabled:(BOOL)aBool
  162. {
  163.   if (aBool!=_replEnabled)
  164.     {
  165.       _replEnabled = aBool;
  166.       [_sharedFindPanel _modifyFindPanel];
  167.     }
  168.   return self;
  169. }
  170.  
  171. + sharedInstance
  172. {
  173.   if (_sharedFindPanel==nil && !_fpLoaded)
  174.     {
  175.       char path[MAXPATHLEN+16];
  176.       _fpLoaded = YES;
  177.       _findPb = [Pasteboard newName:NXFindPboard];
  178.       _allocationOK = YES;
  179.       if ([[NXBundle bundleForClass:self] getPath:path forResource:"FindPanel.nib" ofType:NULL])
  180.         _sharedFindPanel = [NXApp loadNibFile:path owner:NXApp withNames:NO];
  181.       _allocationOK = NO;
  182.       if (_sharedFindPanel==nil)
  183.         NXRunAlertPanel(LocalString("LOAD_ERR"), LocalString("LOAD_ERR_MSG"), NULL, NULL, NULL);
  184.       else
  185.         {
  186.           if (!_replEnabled)
  187.             [_sharedFindPanel _modifyFindPanel];
  188.           [_sharedFindPanel setExcludedFromWindowsMenu:YES];
  189.           [_sharedFindPanel useOptimizedDrawing:YES];
  190.         }
  191.     }
  192.   return _sharedFindPanel;
  193. }
  194.  
  195. - enterSelection:sender
  196. {
  197.   [[self _calcFindPanelTarget] writeSelectionToPasteboard:_findPb asType:NXAsciiPboardType];
  198.   [self _getFindPbData];
  199.   [self _resetFindPanel];
  200.   return self;
  201. }
  202.  
  203. - findBackward:sender
  204. {
  205.   [self _resetFindPanel];
  206.   if (*[findTextField stringValue]=='\0' && _useFindPb)
  207.     [self _getFindPbData];
  208.   if (*[findTextField stringValue]=='\0')
  209.     [self makeKeyAndOrderFront:nil];
  210.   else
  211.     {
  212.       int pos, size, result;
  213.       id target = [self _calcFindPanelTarget];
  214.       if (_useFindPb)
  215.         [self _setFindPbData];
  216.       result = [target searchFor:[findTextField stringValue] mode:SelStartToSelEnd reverse:YES regexpr:[regExprButton state] cases:![ignoreCaseButton state] position:&pos size:&size];
  217.       if (target==nil || result<0)
  218.         {
  219.           [messageTextField setStringValue:LocalString("INVALID_OP")];
  220.           NXBeep();
  221.         }
  222.       else if (result==0)
  223.         {
  224.           [messageTextField setStringValue:LocalString("NOT_FOUND")];
  225.           NXBeep();
  226.         }
  227.       else
  228.         {
  229.           [target selectTextFrom:pos to:pos+size];
  230.           [target makeSelectionVisible];
  231.         }
  232.     }
  233.   return self;
  234. }
  235.  
  236. - findForward:sender
  237. {
  238.   [self _resetFindPanel];
  239.   if (*[findTextField stringValue]=='\0' && _useFindPb)
  240.     [self _getFindPbData];
  241.   if (*[findTextField stringValue]=='\0')
  242.     [self makeKeyAndOrderFront:nil];
  243.   else
  244.     {
  245.       int pos, size, result;
  246.       id target = [self _calcFindPanelTarget];
  247.       if (_useFindPb)
  248.         [self _setFindPbData];
  249.       result = [target searchFor:[findTextField stringValue] mode:SelEndToSelStart reverse:NO regexpr:[regExprButton state] cases:![ignoreCaseButton state] position:&pos size:&size];
  250.       if (target==nil || result<0)
  251.         {
  252.           [messageTextField setStringValue:LocalString("INVALID_OP")];
  253.           NXBeep();
  254.         }
  255.       else if (result==0)
  256.         {
  257.           [messageTextField setStringValue:LocalString("NOT_FOUND")];
  258.           NXBeep();
  259.         }
  260.       else
  261.         {
  262.           [target selectTextFrom:pos to:pos+size];
  263.           [target makeSelectionVisible];
  264.         }
  265.     }
  266.   return self;
  267. }
  268.  
  269. - jumpToSelection:sender
  270. {
  271.   [[self _calcFindPanelTarget] makeSelectionVisible];
  272.   return self;
  273. }
  274.  
  275. - replace:sender
  276. {
  277.   [self _resetFindPanel];
  278.   [[self _calcFindPanelTarget] replaceSelection:[replaceTextField stringValue]];
  279.   return self;
  280. }
  281.  
  282. - replaceAll:sender
  283. {
  284.   id target;
  285.   int count;
  286.   [self _resetFindPanel];
  287.   if (*[findTextField stringValue]=='\0')
  288.     return self;
  289.   target = [self _calcFindPanelTarget];
  290.   count = [target replaceAll:[findTextField stringValue] with:[replaceTextField stringValue] mode:([scopeMatrix selectedRow]?SelStartToSelEnd:TextEdgeToTextEdge) regexpr:[regExprButton state] cases:![ignoreCaseButton state]];
  291.   if (count<0 || target==nil)
  292.     {
  293.       [messageTextField setStringValue:LocalString("INVALID_OP")];
  294.       NXBeep();
  295.     }
  296.   else
  297.     {
  298.       char buffer[256];
  299.       sprintf(buffer, LocalString("N_REPLACED"), count);
  300.       [messageTextField setStringValue:buffer];
  301.       [target makeSelectionVisible];
  302.     }
  303.   return self;
  304. }
  305.  
  306. - replaceAndFind:sender
  307. {
  308.   [[self replace:sender] findForward:sender];
  309.   return self;
  310. }
  311.  
  312. - (TextField *)findTextField
  313. {
  314.   return findTextField;
  315. }
  316.  
  317. - (Button *)ignoreCaseButton
  318. {
  319.   return ignoreCaseButton;
  320. }
  321.  
  322. - (TextField *)messageTextField
  323. {
  324.   return messageTextField;
  325. }
  326.  
  327. - (Button *)regExprButton
  328. {
  329.   return regExprButton;
  330. }
  331.  
  332. - (TextField *)replaceTextField
  333. {
  334.   return replaceTextField;
  335. }
  336.  
  337. - (Matrix *)scopeMatrix
  338. {
  339.   return scopeMatrix;
  340. }
  341.  
  342. - textDidEnd:sender endChar:(unsigned short)theChar
  343. {
  344.   if (theChar==NX_RETURN)
  345.     [self orderOut:nil];
  346.   return self;
  347. }
  348.  
  349. - (BOOL)textWillChange:sender
  350. {
  351.   return NO;
  352. }
  353.  
  354. - (BOOL)textWillEnd:sender
  355. {
  356.   return NO;
  357. }
  358.  
  359. + (BOOL)_canAlloc
  360. {
  361.   return _allocationOK;
  362. }
  363.  
  364. + alloc
  365. {
  366.   if (!_allocationOK)
  367.     return [self doesNotRecognize:_cmd];
  368.   return class_createInstanceFromZone(self, 0, NXDefaultMallocZone());
  369. }
  370.  
  371. + allocFromZone:(NXZone *)zone
  372. {
  373.   if (!_allocationOK)
  374.     return [self doesNotRecognize:_cmd];
  375.   if (zone!=NX_NOZONE)
  376.     return class_createInstanceFromZone(self, 0, zone);
  377.   return nil;
  378. }
  379.  
  380. + (BOOL)instancesRespondTo:(SEL)aSel
  381. {
  382.   if (aSel==@selector(copy) ||
  383.       aSel==@selector(copyFromZone:) ||
  384.       aSel==@selector(free) ||
  385.       aSel==@selector(init) ||
  386.       aSel==@selector(initContent:style:backing:buttonMask:defer:) ||
  387.       aSel==@selector(initContent:style:backing:buttonMask:defer:screen:) ||
  388.       aSel==@selector(miniaturize:) ||
  389.       aSel==@selector(placeWindow:) ||
  390.       aSel==@selector(placeWindow:screen:) ||
  391.       aSel==@selector(placeWindowAndDisplay:) ||
  392.       aSel==@selector(setDocEdited:) ||
  393.       aSel==@selector(sizeWindow::))
  394.     return NO;
  395.   return [super instancesRespondTo:aSel];
  396. }
  397.  
  398. + new
  399. {
  400.   return [self doesNotRecognize:_cmd];
  401. }
  402.  
  403. + newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
  404. {
  405.   return [self doesNotRecognize:_cmd];
  406. }
  407.  
  408. + newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag screen:(const NXScreen *)screen
  409. {
  410.   return [self doesNotRecognize:_cmd];
  411. }
  412.  
  413. - copyFromZone:(NXZone *)zone
  414. {
  415.   return self;
  416. }
  417.  
  418. - free
  419. {
  420.   return self;
  421. }
  422.  
  423. - init
  424. {
  425.   return [self doesNotRecognize:_cmd];
  426. }
  427.  
  428. - initContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
  429. {
  430.   if (!_allocationOK)
  431.     return [self doesNotRecognize:_cmd];
  432.   return [super initContent:contentRect style:aStyle backing:bufferingType buttonMask:mask defer:flag];
  433. }
  434.  
  435. - initContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag screen:(const NXScreen *)screen
  436. {
  437.   return [self doesNotRecognize:_cmd];
  438. }
  439.  
  440. - miniaturize:sender
  441. {
  442.   return self;
  443. }
  444.  
  445. - orderWindow:(int)place relativeTo:(int)otherWin
  446. {
  447.   if (place!=NX_OUT)
  448.     {
  449.       if (_useFindPb)
  450.         [self _getFindPbData];
  451.       [self _resetFindPanel];
  452.     }
  453.   return [super orderWindow:place relativeTo:otherWin];
  454. }
  455.  
  456. - placeWindow:(const NXRect *)aRect
  457. {
  458.   if (_resizeOK)
  459.     return [super placeWindow:aRect];
  460.   return [self moveTo:NX_X(aRect) :NX_Y(aRect)];
  461. }
  462.  
  463. - placeWindow:(const NXRect *)aRect screen:(const NXScreen *)aScreen
  464. {
  465.   if (_resizeOK)
  466.     return [super placeWindow:aRect screen:aScreen];
  467.   return [self moveTo:NX_X(aRect) :NX_Y(aRect) screen:aScreen];
  468. }
  469.  
  470. - placeWindowAndDisplay:(const NXRect *)aRect
  471. {
  472.   if (_resizeOK)
  473.     return [super placeWindowAndDisplay:aRect];
  474.   return [[self moveTo:NX_X(aRect) :NX_Y(aRect)] display];
  475. }
  476.  
  477. - read:(NXTypedStream *)stream
  478. {
  479.   [super read:stream];
  480.   findTextField = NXReadObject(stream);
  481.   replaceTextField = NXReadObject(stream);
  482.   messageTextField = NXReadObject(stream);
  483.   ignoreCaseButton = NXReadObject(stream);
  484.   regExprButton = NXReadObject(stream);
  485.   scopeMatrix = NXReadObject(stream);
  486.   _replBox = NXReadObject(stream);
  487.   return self;
  488. }
  489.  
  490. - (BOOL)respondsTo:(SEL)aSel
  491. {
  492.   if (aSel==@selector(alloc) ||
  493.       aSel==@selector(allocFromZone:) ||
  494.       aSel==@selector(new) ||
  495.       aSel==@selector(newContent:style:backing:buttonMask:defer:) ||
  496.       aSel==@selector(newContent:style:backing:buttonMask:defer:screen:) ||
  497.       aSel==@selector(copy) ||
  498.       aSel==@selector(copyFromZone:) ||
  499.       aSel==@selector(free) ||
  500.       aSel==@selector(init) ||
  501.       aSel==@selector(initContent:style:backing:buttonMask:defer:) ||
  502.       aSel==@selector(initContent:style:backing:buttonMask:defer:screen:) ||
  503.       aSel==@selector(miniaturize:) ||
  504.       aSel==@selector(placeWindow:) ||
  505.       aSel==@selector(placeWindow:screen:) ||
  506.       aSel==@selector(placeWindowAndDisplay:) ||
  507.       aSel==@selector(setDocEdited:) ||
  508.       aSel==@selector(sizeWindow::))
  509.     return NO;
  510.   return [super respondsTo:aSel];
  511. }
  512.  
  513. - setDocEdited:(BOOL)aBool
  514. {
  515.   return self;
  516. }
  517.  
  518. - sizeWindow:(float)x :(float)y
  519. {
  520.   return self;
  521. }
  522.  
  523. - write:(NXTypedStream *)stream
  524. {
  525.   [super write:stream];
  526.   NXWriteObjectReference(stream, findTextField);
  527.   NXWriteObjectReference(stream, replaceTextField);
  528.   NXWriteObjectReference(stream, messageTextField);
  529.   NXWriteObjectReference(stream, ignoreCaseButton);
  530.   NXWriteObjectReference(stream, regExprButton);
  531.   NXWriteObjectReference(stream, scopeMatrix);
  532.   NXWriteObjectReference(stream, _replBox);
  533.   return self;
  534. }
  535.  
  536. @end
  537.