/* NSSearchFieldCell.h Text field cell class for text search Copyright (C) 2004 Free Software Foundation, Inc. Author: H. Nikolaus Schaller Date: Dec 2004 Author: Fred Kiefer 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 Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include "AppKit/NSMenuView.h" #include #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 */