mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-25 03:51:04 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@26619 72102866-910b-0410-8b05-ffd578937521
581 lines
14 KiB
Objective-C
581 lines
14 KiB
Objective-C
/*
|
|
NSSearchFieldCell.h
|
|
|
|
Text field cell class for text search
|
|
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
|
|
|
Author: H. Nikolaus Schaller <hns@computer.org>
|
|
Date: Dec 2004
|
|
Author: Fred Kiefer <fredkiefer@gmx.de>
|
|
Date: Mar 2006
|
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <Foundation/NSArray.h>
|
|
#include <Foundation/NSException.h>
|
|
#include <Foundation/NSNotification.h>
|
|
#include <Foundation/NSString.h>
|
|
#include <Foundation/NSUserDefaults.h>
|
|
#include <AppKit/NSButtonCell.h>
|
|
#include <AppKit/NSEvent.h>
|
|
#include <AppKit/NSImage.h>
|
|
#include <AppKit/NSMenu.h>
|
|
#include "AppKit/NSMenuView.h"
|
|
#include <AppKit/NSSearchFieldCell.h>
|
|
#include "AppKit/NSWindow.h"
|
|
|
|
@interface NSSearchFieldCell (Private)
|
|
|
|
- (NSMenu *) _buildTemplate;
|
|
- (void) _openPopup: (id)sender;
|
|
- (void) _clearSearches: (id)sender;
|
|
- (void) _loadSearches;
|
|
- (void) _saveSearches;
|
|
|
|
@end /* NSSearchFieldCell Private */
|
|
|
|
|
|
@implementation NSSearchFieldCell
|
|
|
|
- (id) initTextCell:(NSString *)aString
|
|
{
|
|
self = [super initTextCell: aString];
|
|
if (self)
|
|
{
|
|
NSButtonCell *c;
|
|
NSMenu *template;
|
|
|
|
c = [[NSButtonCell alloc] initImageCell: nil];
|
|
[self setCancelButtonCell: c];
|
|
RELEASE(c);
|
|
[self resetCancelButtonCell];
|
|
|
|
c = [[NSButtonCell alloc] initImageCell: nil];
|
|
[self setSearchButtonCell: c];
|
|
RELEASE(c);
|
|
[self resetSearchButtonCell];
|
|
|
|
template = [self _buildTemplate];
|
|
[self setSearchMenuTemplate: template];
|
|
RELEASE(template);
|
|
|
|
//_recent_searches = nil;
|
|
//_recents_autosave_name = nil;
|
|
_max_recents = 10;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
RELEASE(_cancel_button_cell);
|
|
RELEASE(_search_button_cell);
|
|
RELEASE(_recent_searches);
|
|
RELEASE(_recents_autosave_name);
|
|
RELEASE(_menu_template);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (id) copyWithZone:(NSZone *) zone;
|
|
{
|
|
NSSearchFieldCell *c = [super copyWithZone: zone];
|
|
|
|
c->_cancel_button_cell = [_cancel_button_cell copyWithZone: zone];
|
|
c->_search_button_cell = [_search_button_cell copyWithZone: zone];
|
|
c->_recent_searches = [_recent_searches copyWithZone: zone];
|
|
c->_recents_autosave_name = [_recents_autosave_name copyWithZone: zone];
|
|
c->_menu_template = [_menu_template copyWithZone: zone];
|
|
|
|
return c;
|
|
}
|
|
|
|
- (BOOL) isOpaque
|
|
{
|
|
// only if all components are opaque
|
|
return [super isOpaque] && [_cancel_button_cell isOpaque] &&
|
|
[_search_button_cell isOpaque];
|
|
}
|
|
|
|
- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
|
|
{
|
|
[_search_button_cell drawWithFrame: [self searchButtonRectForBounds: cellFrame]
|
|
inView: controlView];
|
|
[super drawWithFrame: [self searchTextRectForBounds: cellFrame]
|
|
inView: controlView];
|
|
[_cancel_button_cell drawWithFrame: [self cancelButtonRectForBounds: cellFrame]
|
|
inView: controlView];
|
|
}
|
|
|
|
- (BOOL) sendsWholeSearchString
|
|
{
|
|
return _sends_whole_search_string;
|
|
}
|
|
|
|
- (void) setSendsWholeSearchString: (BOOL)flag
|
|
{
|
|
_sends_whole_search_string = flag;
|
|
}
|
|
|
|
- (BOOL) sendsSearchStringImmediately
|
|
{
|
|
return _sends_search_string_immediatly;
|
|
}
|
|
|
|
- (void) setSendsSearchStringImmediately: (BOOL)flag
|
|
{
|
|
_sends_search_string_immediatly = flag;
|
|
}
|
|
|
|
- (int) maximumRecents
|
|
{
|
|
return _max_recents;
|
|
}
|
|
|
|
- (void) setMaximumRecents: (int)max
|
|
{
|
|
if (max > 254)
|
|
{
|
|
max = 254;
|
|
}
|
|
else if (max < 0)
|
|
{
|
|
max = 10;
|
|
}
|
|
|
|
_max_recents = max;
|
|
}
|
|
|
|
- (NSArray *) recentSearches
|
|
{
|
|
return _recent_searches;
|
|
}
|
|
|
|
- (NSString *) recentsAutosaveName
|
|
{
|
|
return _recents_autosave_name;
|
|
}
|
|
|
|
- (void) setRecentSearches: (NSArray *)searches
|
|
{
|
|
int max;
|
|
|
|
max = [self maximumRecents];
|
|
if ([searches count] > max)
|
|
{
|
|
id buffer[max];
|
|
|
|
[searches getObjects: buffer range: NSMakeRange(0, max)];
|
|
searches = [NSArray arrayWithObjects: buffer count: max];
|
|
}
|
|
ASSIGN(_recent_searches, searches);
|
|
}
|
|
|
|
- (void) setRecentsAutosaveName: (NSString *)name
|
|
{
|
|
ASSIGN(_recents_autosave_name, name);
|
|
}
|
|
|
|
- (NSMenu *) searchMenuTemplate
|
|
{
|
|
return _menu_template;
|
|
}
|
|
|
|
- (void) setSearchMenuTemplate: (NSMenu *)menu
|
|
{
|
|
ASSIGN(_menu_template, menu);
|
|
}
|
|
|
|
- (NSButtonCell *) cancelButtonCell
|
|
{
|
|
return _cancel_button_cell;
|
|
}
|
|
|
|
- (void) setCancelButtonCell: (NSButtonCell *)cell
|
|
{
|
|
ASSIGN(_cancel_button_cell, cell);
|
|
}
|
|
|
|
- (NSButtonCell *) searchButtonCell
|
|
{
|
|
return _search_button_cell;
|
|
}
|
|
|
|
- (void) setSearchButtonCell: (NSButtonCell *)cell
|
|
{
|
|
ASSIGN(_search_button_cell, cell);
|
|
}
|
|
|
|
- (void) resetCancelButtonCell
|
|
{
|
|
NSButtonCell *c;
|
|
|
|
c = [self cancelButtonCell];
|
|
// configure the button
|
|
[c setButtonType: NSMomentaryChangeButton];
|
|
[c setBezelStyle: NSRegularSquareBezelStyle];
|
|
[c setBordered: NO];
|
|
[c setBezeled: NO];
|
|
[c setEditable: NO];
|
|
[c setImagePosition: NSImageOnly];
|
|
[c setImage: [NSImage imageNamed: @"GSStop"]];
|
|
[c setAction: @selector(delete:)];
|
|
[c setTarget: nil];
|
|
[c setKeyEquivalent: @"\e"];
|
|
}
|
|
|
|
- (void) resetSearchButtonCell
|
|
{
|
|
NSButtonCell *c;
|
|
|
|
c = [self searchButtonCell];
|
|
// configure the button
|
|
[c setButtonType: NSMomentaryChangeButton];
|
|
[c setBezelStyle: NSRegularSquareBezelStyle];
|
|
[c setBordered: NO];
|
|
[c setBezeled: NO];
|
|
[c setEditable: NO];
|
|
[c setImagePosition: NSImageOnly];
|
|
[c setImage: [NSImage imageNamed: @"GSSearch"]];
|
|
[c setAction: [self action]];
|
|
[c setTarget: [self target]];
|
|
[c setKeyEquivalent: @"\r"];
|
|
}
|
|
|
|
#define ICON_WIDTH 16
|
|
|
|
- (NSRect) cancelButtonRectForBounds: (NSRect)rect
|
|
{
|
|
NSRect part, clear;
|
|
|
|
NSDivideRect(rect, &clear, &part, ICON_WIDTH, NSMaxXEdge);
|
|
return clear;
|
|
}
|
|
|
|
- (NSRect) searchTextRectForBounds: (NSRect)rect
|
|
{
|
|
NSRect search, text, clear, part;
|
|
|
|
if (!_search_button_cell)
|
|
{
|
|
// nothing to split off
|
|
part = rect;
|
|
}
|
|
else
|
|
{
|
|
NSDivideRect(rect, &search, &part, ICON_WIDTH, NSMinXEdge);
|
|
}
|
|
|
|
if (!_cancel_button_cell)
|
|
{
|
|
// nothing to split off
|
|
text = part;
|
|
}
|
|
else
|
|
{
|
|
NSDivideRect(part, &clear, &text, ICON_WIDTH, NSMaxXEdge);
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
- (NSRect) searchButtonRectForBounds: (NSRect)rect;
|
|
{
|
|
NSRect search, part;
|
|
|
|
NSDivideRect(rect, &search, &part, ICON_WIDTH, NSMinXEdge);
|
|
return search;
|
|
}
|
|
|
|
- (void) editWithFrame: (NSRect)aRect
|
|
inView: (NSView*)controlView
|
|
editor: (NSText*)textObject
|
|
delegate: (id)anObject
|
|
event: (NSEvent*)theEvent
|
|
{
|
|
// constrain to visible text area
|
|
[super editWithFrame: [self searchTextRectForBounds: aRect]
|
|
inView: controlView
|
|
editor: textObject
|
|
delegate: anObject
|
|
event: theEvent];
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(textDidChange:)
|
|
name: NSTextDidChangeNotification
|
|
object: textObject];
|
|
|
|
}
|
|
|
|
- (void) endEditing: (NSText *)editor
|
|
{
|
|
[super endEditing: editor];
|
|
[[NSNotificationCenter defaultCenter]
|
|
removeObserver: self
|
|
name: NSTextDidChangeNotification
|
|
object: editor];
|
|
}
|
|
|
|
- (void) selectWithFrame: (NSRect)aRect
|
|
inView: (NSView*)controlView
|
|
editor: (NSText*)textObject
|
|
delegate: (id)anObject
|
|
start: (int)selStart
|
|
length: (int)selLength
|
|
{
|
|
// constrain to visible text area
|
|
[super selectWithFrame: [self searchTextRectForBounds: aRect]
|
|
inView: controlView
|
|
editor: textObject
|
|
delegate: anObject
|
|
start: selStart
|
|
length: selLength];
|
|
}
|
|
|
|
- (BOOL) trackMouse: (NSEvent *)event
|
|
inRect: (NSRect)cellFrame
|
|
ofView: (NSView *)controlView
|
|
untilMouseUp: (BOOL)untilMouseUp
|
|
{
|
|
NSRect rect;
|
|
NSPoint thePoint;
|
|
NSPoint location = [event locationInWindow];
|
|
|
|
thePoint = [controlView convertPoint: location fromView: nil];
|
|
|
|
// check if we are within the search/stop buttons
|
|
rect = [self searchButtonRectForBounds: cellFrame];
|
|
if ([controlView mouse: thePoint inRect: rect])
|
|
{
|
|
return [[self searchButtonCell] trackMouse: event
|
|
inRect: rect
|
|
ofView: controlView
|
|
untilMouseUp: untilMouseUp];
|
|
}
|
|
|
|
rect = [self cancelButtonRectForBounds: cellFrame];
|
|
if ([controlView mouse: thePoint inRect: rect])
|
|
{
|
|
return [[self cancelButtonCell] trackMouse: event
|
|
inRect: rect
|
|
ofView: controlView
|
|
untilMouseUp: untilMouseUp];
|
|
}
|
|
|
|
return [super trackMouse: event
|
|
inRect: [self searchTextRectForBounds: cellFrame]
|
|
ofView: controlView
|
|
untilMouseUp: untilMouseUp];
|
|
}
|
|
|
|
- (void) textDidChange: (NSNotification *)notification
|
|
{
|
|
NSText *textObject;
|
|
|
|
// make textChanged send action (unless disabled)
|
|
if (_sends_whole_search_string)
|
|
{
|
|
// ignore
|
|
return;
|
|
}
|
|
|
|
textObject = [notification object];
|
|
// copy the current NSTextEdit string so that it can be read from the NSSearchFieldCell!
|
|
[self setStringValue: [textObject string]];
|
|
[[self searchButtonCell] performClickWithFrame:
|
|
[self searchButtonRectForBounds: [_control_view bounds]]
|
|
inView: _control_view];
|
|
}
|
|
//
|
|
// NSCoding protocol
|
|
//
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
{
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
[aCoder encodeObject: _search_button_cell];
|
|
[aCoder encodeObject: _cancel_button_cell];
|
|
[aCoder encodeObject: _recents_autosave_name];
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL)
|
|
at: &_sends_whole_search_string];
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int)
|
|
at: &_max_recents];
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder*)aDecoder
|
|
{
|
|
self = [super initWithCoder: aDecoder];
|
|
|
|
if ([aDecoder allowsKeyedCoding])
|
|
{
|
|
[self setSearchButtonCell: [aDecoder decodeObjectForKey: @"NSSearchButtonCell"]];
|
|
[self setCancelButtonCell: [aDecoder decodeObjectForKey: @"NSCancelButtonCell"]];
|
|
[self setRecentsAutosaveName: [aDecoder decodeObjectForKey: @"NSRecentsAutosaveName"]];
|
|
[self setSendsWholeSearchString: [aDecoder decodeBoolForKey: @"NSSendsWholeSearchString"]];
|
|
[self setMaximumRecents: [aDecoder decodeIntForKey: @"NSMaximumRecents"]];
|
|
}
|
|
else
|
|
{
|
|
[self setSearchButtonCell: [aDecoder decodeObject]];
|
|
[self setCancelButtonCell: [aDecoder decodeObject]];
|
|
[self setRecentsAutosaveName: [aDecoder decodeObject]];
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_sends_whole_search_string];
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &_max_recents];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
@end /* NSSearchFieldCell */
|
|
|
|
|
|
@implementation NSSearchFieldCell (Private)
|
|
|
|
/* Set up a default template
|
|
*/
|
|
- (NSMenu *) _buildTemplate
|
|
{
|
|
NSMenu *template;
|
|
NSMenuItem *item;
|
|
|
|
template = [[NSMenu alloc] init];
|
|
|
|
item = [[NSMenuItem alloc] initWithTitle: @"Recent searches"
|
|
action: NULL
|
|
keyEquivalent: @""];
|
|
[item setTag: NSSearchFieldRecentsTitleMenuItemTag];
|
|
[template addItem: item];
|
|
RELEASE(item);
|
|
|
|
item = [[NSMenuItem alloc] initWithTitle: @"Recent search item"
|
|
action: @selector(search:)
|
|
keyEquivalent: @""];
|
|
[item setTag: NSSearchFieldRecentsMenuItemTag];
|
|
[template addItem: item];
|
|
RELEASE(item);
|
|
|
|
item = [[NSMenuItem alloc] initWithTitle: @"Clear recent searches"
|
|
action: @selector(_clearSearches:)
|
|
keyEquivalent: @""];
|
|
[item setTag: NSSearchFieldClearRecentsMenuItemTag];
|
|
[item setTarget: self];
|
|
[template addItem: item];
|
|
|
|
RELEASE(item);
|
|
item = [[NSMenuItem alloc] initWithTitle: @"No recent searches"
|
|
action: NULL
|
|
keyEquivalent: @""];
|
|
[item setTag: NSSearchFieldNoRecentsMenuItemTag];
|
|
[template addItem: item];
|
|
RELEASE(item);
|
|
|
|
return template;
|
|
}
|
|
|
|
- (void) _openPopup: (id)sender
|
|
{
|
|
NSMenu *template;
|
|
NSMenu *popupmenu;
|
|
NSMenuView *mr;
|
|
NSWindow *cvWin;
|
|
NSRect cellFrame;
|
|
NSRect textRect;
|
|
int i;
|
|
|
|
template = [self searchMenuTemplate];
|
|
popupmenu = [[NSMenu alloc] init];
|
|
|
|
// Fill the popup menu
|
|
for (i = 0; i < [template numberOfItems]; i++)
|
|
{
|
|
int tag;
|
|
NSMenuItem *item;
|
|
int count = [_recent_searches count];
|
|
|
|
item = (NSMenuItem*)[template itemAtIndex: i];
|
|
tag = [item tag];
|
|
if ((tag == NSSearchFieldRecentsTitleMenuItemTag) ||
|
|
(tag == NSSearchFieldClearRecentsMenuItemTag) ||
|
|
((tag == NSSearchFieldRecentsMenuItemTag) && ((count == 0))))
|
|
{
|
|
NSMenuItem *copy;
|
|
|
|
copy = [item copy];
|
|
[popupmenu addItem: copy];
|
|
RELEASE(copy);
|
|
}
|
|
else if (tag == NSSearchFieldRecentsMenuItemTag)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < count; j++)
|
|
{
|
|
[popupmenu addItemWithTitle: [_recent_searches objectAtIndex: j]
|
|
action: [item action]
|
|
keyEquivalent: [item keyEquivalent]];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare to display the popup
|
|
cvWin = [_control_view window];
|
|
cellFrame = [_control_view bounds];
|
|
textRect = [self searchTextRectForBounds: cellFrame] ;
|
|
mr = [popupmenu menuRepresentation];
|
|
|
|
// Ask the MenuView to attach the menu to this rect
|
|
[mr setWindowFrameForAttachingToRect: textRect
|
|
onScreen: [cvWin screen]
|
|
preferredEdge: NSMinYEdge
|
|
popUpSelectedItem: -1];
|
|
|
|
// Last, display the window
|
|
[[mr window] orderFrontRegardless];
|
|
|
|
AUTORELEASE(popupmenu);
|
|
}
|
|
|
|
- (void) _clearSearches: (id)sender
|
|
{
|
|
[self setRecentSearches: [NSArray array]];
|
|
}
|
|
|
|
- (void) _loadSearches
|
|
{
|
|
NSArray *list;
|
|
NSString *name = [self recentsAutosaveName];
|
|
|
|
list = [[NSUserDefaults standardUserDefaults]
|
|
stringArrayForKey: name];
|
|
[self setRecentSearches: list];
|
|
}
|
|
|
|
- (void) _saveSearches
|
|
{
|
|
NSArray *list = [self recentSearches];
|
|
NSString *name = [self recentsAutosaveName];
|
|
|
|
[[NSUserDefaults standardUserDefaults]
|
|
setObject: list forKey: name];
|
|
}
|
|
|
|
@end /* NSSearchFieldCell Private */
|