// FindPanel.m
//
// Subclass of Object which implements find panel functionality.
// Works on Text objects automatically; pretty easily can be
// made to work on any text container which responds to a given protocol.
//
// You may freely copy, distribute, and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.

#import <appkit/appkit.h>
#import <libc.h>
#import <strings.h>

#import "FindPanel.h"

@implementation FindPanel

// Allocate and initialize the object. If we already created an instance, return that one...

+ sharedInstance
{
static id sharedFindObject = nil;
NXZone *zone = NXApp ? [NXApp zone] : NXDefaultMallocZone();

if (sharedFindObject) {
return sharedFindObject;
}

self = [[super allocFromZone:zone] init];
if (![NXApp loadNibSection:"FindPanel.nib" owner:self withNames:NO fromZone:zone]) {
NXLogError ("Can't find FindPanel.nib!");
[self free];
return nil;
} else {
sharedFindObject = self;
[[findText window] setFra!0WtosaveName:"Find"];
return self;
}
}

+ new
{
return [self sharedInstance];
}

// Because find panel is shared, only one instance is created, and it should be created
// with new rather than alloc/init.

- allocFromZone:(NXZone *)zone
{
return [self notImplemented:_cmd];
}

- free
{
return self;
}

- (const char *)findString
{
return [findText stringValue];
}

- (void)setFindString:(const char *)string
{
[findText setStringValue:string];
[findText selectText:nil];
}

- (void)findIn:(Text *)textObject direction:(FindDirection)dir
{
if (!textObject ||
![textObject findText:[self findString]
ignoreCase:[ignoreCaseButton state]
backwards:(dir == FindBackward)
wrap:YES]) {
NXBeep ();
return;
}
}

- firstResponderText
{
id obj = [[NXApp mainWindow] firstResponder];
return (obj && [obj isKindOf:[Text class]]) ? obj : nil;
}

- (void)findDirection:(FindDirection)dir
{
[self findIn:[self firstResponderText] direction:dir];
}

- findNextReturn:sender
{
[findNextButton performClick:sender];
[self orderOut:sender];
return self;
}

- makeKeyAndOrderFront:sender
{
id retval = [[findText window] makeKeyAndOrderFront:sender];
[findText selectText:nil];
return retval;
}

- orderOut:sender
{
return [[findText window] orderOut:sender];
}

// Called to find the next occurrence of the search string.

- findNext:sender
{
[self findDirection:FindForward];
return self;
}

// Called to find the previous occurrence of the search string.

- findPrevious:sender
{
[self findDirection:FindBackward];
return self;
}

// In case the selection is shorter than this amount, we use a local buffer
// (on the stack) rather than bothering with allocating it.

#define LOCALBUFFERLEN 100

- enterSelection:sender
{
id obj = [self firstResponderText];

if (!obj) {
NXBeep();
} else {
char localBuffer[LOCALBUFFERLEN+1], *str;
NXSelPt from, to;
int bufLen;

[obj getSel:&from :&to];
bufLen = to.cp - from.cp;
str = (bufLen <= LOCALBUFFERLEN) ? localBuffer : NXZoneMalloc(NXDefaultMallocZone(), bufLen+1);

[obj getSubstring:str start:from.cp length:bufLen];
str[bufLen] = '\0';
[self setFindString:str];
if (str != localBuffer) free(str);
}

return self;
}

@end