1/** <title>NSComboBox</title> 2 3 Copyright (C) 1999 Free Software Foundation, Inc. 4 5 Author: Gerrit van Dyk <gerritvd@decillion.net> 6 Date: 1999 7 8 This file is part of the GNUstep GUI Library. 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; see the file COPYING.LIB. 22 If not, see <http://www.gnu.org/licenses/> or write to the 23 Free Software Foundation, 51 Franklin Street, Fifth Floor, 24 Boston, MA 02110-1301, USA. 25*/ 26 27#import <Foundation/NSNotification.h> 28#import <Foundation/NSString.h> 29#import "AppKit/NSComboBox.h" 30#import "AppKit/NSComboBoxCell.h" 31#import "AppKit/NSEvent.h" 32#import "AppKit/NSTextView.h" 33 34/* 35 * Class variables 36 */ 37static Class usedCellClass; 38static Class comboBoxCellClass; 39static NSNotificationCenter *nc; 40 41/* 42 * Declaration of private cell method 43 */ 44 45@interface NSComboBoxCell (GNUstepPrivate) 46- (void) _performClickWithFrame: (NSRect)cellFrame 47 inView: (NSView *)controlView; 48@end 49 50/** 51 <unit> 52 <heading>Class Description</heading> 53 <p>An NSComboBox is what we can call a completion/choices box, derived from 54 NSTextField, it allows you to enter text like in a text field but also to click 55 in the ellipsis button (indicating the fact other user inputs are possible) on 56 the right of it to obtain a list of choices, you can use them as the text field 57 value by selecting a row in this list. You can also obtain direct completion 58 when it is enabled via <code>setCompletes:</code> to get a suggested text 59 field value updated as you type.</p> 60 <p>Like other NSControl classes, NSComboBox is a wrapper around a core piece which 61 implements the combo box behavior, a cell, which is in this case an 62 NSComboBoxCell.</p> 63 </unit> 64*/ 65 66/** 67<p>No special instructions to use NSComboBox or text to detail the implementation.</p> 68 */ 69@implementation NSComboBox 70 71+ (void) initialize 72{ 73 if (self == [NSComboBox class]) 74 { 75 [self setVersion: 1]; 76 comboBoxCellClass = [NSComboBoxCell class]; 77 usedCellClass = comboBoxCellClass; 78 nc = [NSNotificationCenter defaultCenter]; 79 } 80} 81 82/* 83 * Setting the Cell class 84 */ 85+ (Class) cellClass 86{ 87 return usedCellClass; 88} 89 90+ (void) setCellClass: (Class)factoryId 91{ 92 usedCellClass = factoryId ? factoryId : comboBoxCellClass; 93} 94 95/** 96 * Returns YES when the combo box cell displays a vertical scroller for its 97 * list, returns NO otherwise. 98 * Take note that the scroller will be displayed even when the sum of the items 99 * height in the list is inferior to the minimal height of the list displayed 100 * area. 101 */ 102- (BOOL)hasVerticalScroller 103{ 104 return [_cell hasVerticalScroller]; 105} 106 107/** 108 * Sets whether the combo box cell list displays a vertical scroller, by default 109 * it is the case. When <var>flag</var> is NO and the combo cell list has more 110 * items (either in its default list or from its data source) than the number 111 * returned by <code>numberOfVisibleItems</code>, only a subset of them will be 112 * displayed. Uses scroll related methods to position this subset in the combo 113 * box cell list. 114 * Take note that the scroller will be displayed even when the sum of the items 115 * height in the list is inferior to the minimal height of the list displayed 116 * area. 117 */ 118- (void)setHasVerticalScroller:(BOOL)flag 119{ 120 [_cell setHasVerticalScroller:flag]; 121} 122 123/** 124 * Returns the width and the height (as the values of an NSSize variable) 125 * between each item of the combo box cell list. 126 */ 127- (NSSize)intercellSpacing 128{ 129 return [_cell intercellSpacing]; 130} 131 132/** 133 * Sets the width and the height between each item of the combo box cell list to 134 * the values in <var>aSize</var>. 135 */ 136- (void)setIntercellSpacing:(NSSize)aSize 137{ 138 [_cell setIntercellSpacing:aSize]; 139} 140 141/** 142 * Returns the height of the items in the combo box cell list. 143 */ 144- (CGFloat)itemHeight 145{ 146 return [_cell itemHeight]; 147} 148 149/** 150 * Sets the height of the items in the combo box cell list to 151 * <var>itemHeight</var>. 152 */ 153- (void)setItemHeight:(CGFloat)itemHeight 154{ 155 [_cell setItemHeight:itemHeight]; 156} 157 158/** 159 * Returns the maximum number of allowed items to be displayed in the combo box 160 * cell list. 161 */ 162- (NSInteger)numberOfVisibleItems 163{ 164 return [_cell numberOfVisibleItems]; 165} 166 167/** 168 * Sets the maximum number of allowed items to be displayed in the combo box 169 * cell list. 170 */ 171- (void)setNumberOfVisibleItems:(NSInteger)visibleItems 172{ 173 [_cell setNumberOfVisibleItems:visibleItems]; 174} 175 176/** 177 * Marks the combo box cell in order to have its items list reloaded in the 178 * case it uses a data source, and to have it redisplayed. 179 */ 180- (void)reloadData 181{ 182 [_cell reloadData]; 183} 184 185/** 186 * Informs the combo box cell that the number of items in its data source has 187 * changed, in order to permit to the scrollers in its displayed list being 188 * updated without needing the reload of the data. 189 * It is recommended to use this method with a data source that continually 190 * receives data in the background, to keep the the combo box cell responsive to 191 * the user while the data is received. 192 * Take a look at the <code>NSComboBoxDataSource</code> informal protocol 193 * specification to know more on the messages NSComboBox sends to its data 194 * source. 195 */ 196- (void)noteNumberOfItemsChanged 197{ 198 [_cell noteNumberOfItemsChanged]; 199} 200 201/** 202 * Returns YES when the combo box cell uses a data source (which is external) to 203 * populate its items list, otherwise returns NO in the case it uses its default 204 * list. 205 */ 206- (BOOL)usesDataSource 207{ 208 return [_cell usesDataSource]; 209} 210 211/** 212 * Sets according to <var>flag</var> whether the combo box cell uses a data 213 * source (which is external) to populate its items list. 214 */ 215- (void)setUsesDataSource:(BOOL)flag 216{ 217 [_cell setUsesDataSource:flag]; 218} 219 220/** 221 * Scrolls the combo box cell list vertically in order to have the item at 222 * <var>index</var> in the closest position relative to the top. There is no 223 * need to have the list displayed when this method is invoked. 224 */ 225- (void)scrollItemAtIndexToTop:(NSInteger)index 226{ 227 [_cell scrollItemAtIndexToTop:index]; 228} 229 230/** 231 * Scrolls the combo box cell list vertically in order to have the item at 232 * <var>index</var> visible. There is no need to have the list displayed when 233 * this method is invoked. 234 */ 235- (void)scrollItemAtIndexToVisible:(NSInteger)index 236{ 237 [_cell scrollItemAtIndexToVisible:index]; 238} 239 240/** 241 * Selects the combo box cell list row at <var>index</var>. 242 * Take note no changes occurs in the combo box cell list when this method is 243 * called. 244 * Posts an NSComboBoxSelectionDidChangeNotification to the default notification 245 * center when there is a new selection different from the previous one. 246 */ 247- (void)selectItemAtIndex:(NSInteger)index 248{ 249 [_cell selectItemAtIndex:index]; 250} 251 252/** 253 * Deselects the combo box cell list row at <var>index</var> in the case this 254 * row is selected. 255 * Posts an NSComboBoxSelectionDidChangeNotification to the default notification 256 * center, when there is a new selection. 257 */ 258- (void)deselectItemAtIndex:(NSInteger)index 259{ 260 [_cell deselectItemAtIndex:index]; 261} 262 263/** 264 * Returns the index of the selected item in the combo box cell list or -1 when 265 * there is no selection, the selected item can be related to the data source 266 * object in the case <code>usesDataSource</code> returns YES else to the 267 * default items list. 268 */ 269- (NSInteger)indexOfSelectedItem 270{ 271 return [_cell indexOfSelectedItem]; 272} 273 274/** 275 * Returns the number of items in the the combo box cell list, the numbers of 276 * items can be be related to the data source object in the case 277 * <code>usesDataSource</code> returns YES else to the default items list. 278 */ 279- (NSInteger)numberOfItems 280{ 281 return [_cell numberOfItems]; 282} 283 284/** 285 * Returns the combo box cell data source object which is reponsible to provide 286 * the data to be displayed. To know how to implement a data source object, 287 * take a look at the NSComboBoxDataSource informal protocol description. In 288 * the case <code>usesDataSource</code> returns NO, this method logs a warning. 289 */ 290- (id)dataSource 291{ 292 return [_cell dataSource]; 293} 294 295/** 296 * Sets the combo box cell data source to <var>aSource</var>. Just calling this 297 * method doesn't set <code>usesDataSource</code> to return YES, you must call 298 * <code>setUsesDataSource:</code> with YES before or a warning will be logged. 299 * To know how to implement a data source objects, take a look at the 300 * NSComboBoxDataSource informal protocol description. When <var>aSource</var> 301 * doesn't respond to the methods <code>numberOfItemsInComboBox:</code> 302 * <code>comboBox:objectValueForItemAtIndex:</code>, this method 303 * logs a warning. 304 */ 305- (void)setDataSource:(id)aSource 306{ 307 [_cell setDataSource:aSource]; 308} 309 310/** 311 * Adds an item to the combo box cell default items list which is used when 312 * <code>usesDataSource</code> returns NO. In the case 313 * <code>usesDataSource</code> returns YES, this method logs a warning. 314 */ 315- (void)addItemWithObjectValue:(id)object 316{ 317 [_cell addItemWithObjectValue:object]; 318} 319 320/** 321 * Adds several items in an array to the combo box cell default items list which 322 * is used when <code>usesDataSource</code> returns NO. In the case 323 * <code>usesDataSource</code> returns YES, this method logs a warning. 324 */ 325- (void)addItemsWithObjectValues:(NSArray *)objects 326{ 327 [_cell addItemsWithObjectValues:objects]; 328} 329 330/** 331 * Inserts an item in the combo box cell default items list which 332 * is used when <code>usesDataSource</code> returns NO. In the case 333 * <code>usesDataSource</code> returns YES, this method logs a warning. 334 */ 335- (void)insertItemWithObjectValue:(id)object atIndex:(NSInteger)index 336{ 337 [_cell insertItemWithObjectValue:object atIndex:index]; 338} 339 340/** 341 * Removes an item in the combo box cell default items list which 342 * is used when <code>usesDataSource</code> returns NO. In the case 343 * <code>usesDataSource</code> returns YES, this method logs a warning. 344 */ 345- (void)removeItemWithObjectValue:(id)object 346{ 347 [_cell removeItemWithObjectValue:object]; 348} 349 350/** 351 * Removes the item with the specified <var>index</var> in the combo box cell 352 * default items list which is used when <code>usesDataSource</code> returns NO. 353 * In the case <code>usesDataSource</code> returns YES, this method logs a warning. 354 */ 355- (void)removeItemAtIndex:(NSInteger)index 356{ 357 [_cell removeItemAtIndex:index]; 358} 359 360/** 361 * Removes all the items in the combo box cell default items list which is used 362 * when <code>usesDataSource</code> returns NO. In the case 363 * <code>usesDataSource</code> returns YES, this method logs a warning. 364 */ 365- (void)removeAllItems 366{ 367 [_cell removeAllItems]; 368} 369 370/** 371 * Selects the first item in the default combo box cell list which is equal to 372 * <var>object</var>. In the case <code>usesDataSource</code> returns YES, this 373 * method logs a warning. 374 * Take note that this method doesn't update the text field part value. 375 * Posts an NSComboBoxSelectionDidChange notification to the default 376 * notification center when the new selection is different than the previous 377 * one. 378 */ 379- (void)selectItemWithObjectValue:(id)object 380{ 381 [_cell selectItemWithObjectValue:object]; 382} 383 384/** 385 * Returns the object value at <var>index</var> within combo box cell default 386 * items list. When the index is beyond the end of the list, an NSRangeException is 387 * raised. In the case <code>usesDataSource</code> returns YES, this method logs 388 * a warning. 389 */ 390- (id)itemObjectValueAtIndex:(NSInteger)index 391{ 392 return [_cell itemObjectValueAtIndex:index]; 393} 394 395/** 396 * Returns the object value of the selected item in the combo box cell default 397 * items list or nil when there is no selection. In the case 398 * <code>usesDataSource</code> returns YES, this method logs a warning. 399 */ 400- (id)objectValueOfSelectedItem 401{ 402 return [_cell objectValueOfSelectedItem]; 403} 404 405/** 406 * Returns the lowest index associated with a value in the combo box 407 * cell default items list, which is equal to <var>object</var>, and returns 408 * NSNotFound when there is no such value. In the case 409 * <code>usesDataSource</code> returns YES, this method logs a warning. 410 */ 411- (NSInteger)indexOfItemWithObjectValue:(id)object 412{ 413 return [_cell indexOfItemWithObjectValue:object]; 414} 415 416/** 417 * Returns the combo box cell default items list in an array. 418 */ 419- (NSArray *)objectValues 420{ 421 return [_cell objectValues]; 422} 423 424/** 425 * Returns YES when the combo box cell automatic completion is active, returns 426 * NO otherwise. 427 * Take a look at the <code>setCompletes:</code> method documentation to know 428 * how the automatic completion works. 429 */ 430- (BOOL)completes 431{ 432 return [_cell completes]; 433} 434 435/** 436 * Sets whether the combo box cell automatic completion is active or not. 437 * The automatic completion tries to complete what the user types in the text 438 * field part, it tries to complete only when the the user adds characters at 439 * the end of the string, not when it deletes characters or when the insertion 440 * point precedes the end of the string. 441 * To do the automatic completion, the <code>completedString:</code> method is 442 * called, and when the returned string is longer than the current one in the text 443 * field, the completion occurs and the completed part gets selected. 444 */ 445- (void)setCompletes:(BOOL)completes 446{ 447 [_cell setCompletes: completes]; 448} 449 450- (BOOL) isButtonBordered 451{ 452 return [_cell isButtonBordered]; 453} 454 455- (void) setButtonBordered:(BOOL)flag 456{ 457 [_cell setButtonBordered: flag]; 458} 459 460- (void) setDelegate: (id)anObject 461{ 462 [super setDelegate: anObject]; 463 464#define SET_DELEGATE_NOTIFICATION(notif_name) \ 465 if ([_delegate respondsToSelector: @selector(comboBox##notif_name:)]) \ 466 [nc addObserver: _delegate \ 467 selector: @selector(comboBox##notif_name:) \ 468 name: NSComboBox##notif_name##Notification object: self] 469 470 SET_DELEGATE_NOTIFICATION(SelectionDidChange); 471 SET_DELEGATE_NOTIFICATION(SelectionIsChanging); 472 SET_DELEGATE_NOTIFICATION(WillPopUp); 473 SET_DELEGATE_NOTIFICATION(WillDismiss); 474} 475 476// Overridden 477- (void) mouseDown: (NSEvent*)theEvent 478{ 479 BOOL buttonClicked; 480 // buttonClicked is set to the value NO when the click occurs in the text cell 481 // and to the value YES when it occurs in the button cell. 482 483 buttonClicked = [_cell trackMouse: theEvent inRect: [self bounds] 484 ofView: self untilMouseUp: YES]; 485 486 if (!buttonClicked) 487 [super mouseDown: theEvent]; 488} 489 490- (BOOL) textView: (NSTextView *)textView doCommandBySelector: (SEL)command 491{ 492 if ([super textView: textView doCommandBySelector: command]) 493 return YES; 494 if (sel_isEqual(command, @selector(moveDown:))) 495 { 496 [_cell _performClickWithFrame: [self bounds] inView: self]; 497 return YES; 498 } 499 return NO; 500} 501 502- (void) setFrame: (NSRect)frame 503{ 504 NSRect rect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, 21); 505 // FIX ME: We shouldn't harcode the height value 506 507 [super setFrame: rect]; 508} 509 510@end 511