1/** <title>NSPopUpButton</title> 2 3 <abstract>Popup list class</abstract> 4 5 Copyright (C) 1996 Free Software Foundation, Inc. 6 7 Author: Scott Christley <scottc@net-community.com> 8 Date: 1996 9 Author: Michael Hanni <mhanni@sprintmail.com> 10 Date: June 1999 11 12 This file is part of the GNUstep GUI Library. 13 14 This library is free software; you can redistribute it and/or 15 modify it under the terms of the GNU Lesser General Public 16 License as published by the Free Software Foundation; either 17 version 2 of the License, or (at your option) any later version. 18 19 This library is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 Lesser General Public License for more details. 23 24 You should have received a copy of the GNU Lesser General Public 25 License along with this library; see the file COPYING.LIB. 26 If not, see <http://www.gnu.org/licenses/> or write to the 27 Free Software Foundation, 51 Franklin Street, Fifth Floor, 28 Boston, MA 02110-1301, USA. 29*/ 30 31#import <Foundation/NSKeyValueCoding.h> 32#import <Foundation/NSKeyValueObserving.h> 33#import <Foundation/NSValue.h> 34#import "AppKit/NSApplication.h" 35#import "AppKit/NSEvent.h" 36#import "AppKit/NSGraphics.h" 37#import "AppKit/NSKeyValueBinding.h" 38#import "AppKit/NSPopUpButton.h" 39#import "AppKit/NSPopUpButtonCell.h" 40#import "AppKit/NSMenu.h" 41#import "AppKit/NSMenuItem.h" 42#import "AppKit/NSMenuView.h" 43#import "AppKit/NSWindow.h" 44 45/* 46 * class variables 47 */ 48Class _nspopupbuttonCellClass = 0; 49 50/* 51 * NSPopUpButton implementation 52 */ 53 54@implementation NSPopUpButton 55 56/* 57 * Class methods 58 */ 59+ (void) initialize 60{ 61 if (self == [NSPopUpButton class]) 62 { 63 // Initial version 64 [self setVersion: 1]; 65 [self setCellClass: [NSPopUpButtonCell class]]; 66 67 [self exposeBinding: NSSelectedIndexBinding]; 68 [self exposeBinding: NSSelectedObjectBinding]; 69 [self exposeBinding: NSSelectedTagBinding]; 70 [self exposeBinding: NSSelectedValueBinding]; 71 [self exposeBinding: NSContentValuesBinding]; 72 } 73} 74 75+ (Class) cellClass 76{ 77 return _nspopupbuttonCellClass; 78} 79 80+ (void) setCellClass: (Class)classId 81{ 82 _nspopupbuttonCellClass = classId; 83} 84 85/* 86 * Initializing an NSPopUpButton 87 */ 88- (id) init 89{ 90 return [self initWithFrame: NSZeroRect pullsDown: NO]; 91} 92 93- (id) initWithFrame: (NSRect)frameRect 94{ 95 return [self initWithFrame: frameRect pullsDown: NO]; 96} 97 98/** <p>Initialize and returns a new NSPopUpButton into the frame frameRect 99 and specified by flag if the NSPopUpButton is a pull-down list</p> 100 <p>See Also: -setPullsDown: [NSView-initWithFrame:]</p> 101 */ 102- (id) initWithFrame: (NSRect)frameRect 103 pullsDown: (BOOL)flag 104{ 105 if ( ! ( self = [super initWithFrame: frameRect] ) ) 106 return nil; 107 108 [self setPullsDown: flag]; 109 110 return self; 111} 112 113 114/* 115In NSView, -menuForEvent: returns [self menu] as the context menu of the 116view. Since our -menu returns the menu for our pop-up, we need to override 117this to return nil to indicate that we have no context menu. 118*/ 119- (NSMenu *)menuForEvent:(NSEvent *)theEvent 120{ 121 return nil; 122} 123 124 125- (void) setMenu: (NSMenu*)menu 126{ 127 [_cell setMenu: menu]; 128} 129 130- (NSMenu*) menu 131{ 132 return [_cell menu]; 133} 134 135/**<p>Sets whether the NSPopUpButton's cell has a pulls-down list ( YES ) 136 or a pop-up list (NO) </p> <p>See Also: -pullsDown 137 [NSPopUpButtonCell-setPullsDown:]</p> 138*/ 139 140- (void) setPullsDown: (BOOL)flag 141{ 142 [_cell setPullsDown: flag]; 143} 144/** <p>Returns whether the NSPopUpButton's cell has a pulls-down list ( YES ) 145 or a pop-up list (NO) </p> 146 <p>See Also: -setPullsDown: [NSPopUpButtonCell-pullsDown]</p> 147 */ 148- (BOOL) pullsDown 149{ 150 return [_cell pullsDown]; 151} 152 153- (void) setAutoenablesItems: (BOOL)flag 154{ 155 [_cell setAutoenablesItems: flag]; 156} 157 158- (BOOL) autoenablesItems 159{ 160 return [_cell autoenablesItems]; 161} 162 163/** <p>Inserts a new item with title as its title at the end of the list and 164 synchronizes the NSPopUpButton's title with the title of the selected item. 165 </p><p>See Also: [NSPopUpButtonCell-addItemWithTitle:] 166 -synchronizeTitleAndSelectedItem</p> 167 */ 168- (void) addItemWithTitle: (NSString *)title 169{ 170 [_cell addItemWithTitle: title]; 171 172 [self synchronizeTitleAndSelectedItem]; 173} 174 175/** <p>Inserts a new list of items with titles as titles at the end of the list 176 and synchronizes the NSPopUpButton's title with the title of the selected 177 item.</p><p>See Also: [NSPopUpButtonCell-addItemsWithTitles:] 178 -synchronizeTitleAndSelectedItem</p> 179 */ 180- (void) addItemsWithTitles: (NSArray*)itemTitles 181{ 182 [_cell addItemsWithTitles: itemTitles]; 183 184 [self synchronizeTitleAndSelectedItem]; 185} 186 187/** <p>Inserts a new item with title as its title at the specified index 188 and synchronizes the NSPopUpButton's title with the title of the selected 189 item.</p><p>See Also: [NSPopUpButtonCell-insertItemWithTitle:atIndex:] 190 -synchronizeTitleAndSelectedItem</p> 191 */ 192- (void) insertItemWithTitle: (NSString*)title 193 atIndex: (NSInteger)index 194{ 195 [_cell insertItemWithTitle: title 196 atIndex: index]; 197 198 [self synchronizeTitleAndSelectedItem]; 199} 200 201/** <p>Removes all items from the item list and synchronizes the 202 NSPopUpButton's title with the title of the selected</p> 203 <p>See Also: [NSPopUpButtonCell-removeAllItems] -removeItemWithTitle: 204 -synchronizeTitleAndSelectedItem</p> 205*/ 206- (void) removeAllItems 207{ 208 [_cell removeAllItems]; 209 210 [self synchronizeTitleAndSelectedItem]; 211} 212 213/** <p>Removes the item specified with title as its title from the item list 214 and synchronizes the NSPopUpButton's title with the title of the selected 215 </p><p>See Also: [NSPopUpButtonCell-removeItemWithTitle:] 216 -removeAllItems -removeItemAtIndex: -synchronizeTitleAndSelectedItem</p> 217*/ 218- (void) removeItemWithTitle: (NSString*)title 219{ 220 [_cell removeItemWithTitle: title]; 221 222 [self synchronizeTitleAndSelectedItem]; 223} 224 225/** <p>Removes the item at the specified index index from the item list 226 and synchronizes the NSPopUpButton's title with the title of the selected 227 </p><p>See Also: [NSPopUpButtonCell-removeItemAtIndex:] 228 -removeAllItems -removeItemWithTitle: -synchronizeTitleAndSelectedItem</p> 229*/ 230- (void) removeItemAtIndex: (NSInteger)index 231{ 232 [_cell removeItemAtIndex: index]; 233 234 [self synchronizeTitleAndSelectedItem]; 235} 236 237/** <p>Returns the selected item</p> 238 <p>See Also: [NSPopUpButtonCell-selectedItem]</p> 239 */ 240- (id <NSMenuItem>) selectedItem 241{ 242 return [_cell selectedItem]; 243} 244 245/** <p>Returns the title of the selected item</p> 246 <p>See Also: [NSPopUpButtonCell-titleOfSelectedItem]</p> 247 */ 248- (NSString*) titleOfSelectedItem 249{ 250 return [_cell titleOfSelectedItem]; 251} 252 253/**<p>Returns the index of the selected item</p> 254 <p>See Also: [NSPopUpButtonCell-indexOfSelectedItem]</p> 255 */ 256- (NSInteger) indexOfSelectedItem 257{ 258 return [_cell indexOfSelectedItem]; 259} 260 261/**<p>Returns the tag of the selected item</p> 262 <p>See Also: [NSPopUpButtonCell-indexOfSelectedItem]</p> 263*/ 264- (NSInteger) selectedTag 265{ 266 return [[_cell selectedItem] tag]; 267} 268 269- (void) selectItem: (id <NSMenuItem>)anObject 270{ 271 [_cell selectItem: anObject]; 272 [self synchronizeTitleAndSelectedItem]; 273} 274 275/**<p>Select the item at index <var>index</var> and synchronizes the 276 NSPopUpButton's title with the title of the selected</p><p>See Also: 277 [NSPopUpButtonCell-selectItemAtIndex:] -synchronizeTitleAndSelectedItem</p> 278 */ 279- (void) selectItemAtIndex: (NSInteger)index 280{ 281 [_cell selectItemAtIndex: index]; 282 [self synchronizeTitleAndSelectedItem]; 283} 284 285/**<p>Select the item with title <var>title</var> and synchronizes the 286 NSPopUpButton's title with the title of the selected</p><p>See Also: 287 [NSPopUpButtonCell-selectItemWithTitle:] 288 -synchronizeTitleAndSelectedItem</p> 289 */ 290- (void) selectItemWithTitle: (NSString*)title 291{ 292 [_cell selectItemWithTitle: title]; 293 [self synchronizeTitleAndSelectedItem]; 294} 295 296- (BOOL) selectItemWithTag: (NSInteger)tag 297{ 298 NSInteger index = [self indexOfItemWithTag: tag]; 299 300 if (index >= 0) 301 { 302 [self selectItemAtIndex: index]; 303 return YES; 304 } 305 else 306 { 307 return NO; 308 } 309} 310 311 312/** <p>Returns the number of items in the item list</p> 313 <p>See Also: [NSPopUpButtonCell-numberOfItems]</p> 314 */ 315- (NSInteger) numberOfItems 316{ 317 return [_cell numberOfItems]; 318} 319 320- (NSArray*) itemArray 321{ 322 return [_cell itemArray]; 323} 324 325/**<p>Returns the NSMenuItem at index index or nil if index is out of 326 range</p><p>See Also: [NSPopUpButtonCell-itemAtIndex:] </p> 327 */ 328- (id <NSMenuItem>) itemAtIndex: (NSInteger)index 329{ 330 return [_cell itemAtIndex: index]; 331} 332 333/** <p>Returns the item's title at index <var>index</var></p> 334 */ 335- (NSString*) itemTitleAtIndex: (NSInteger)index 336{ 337 return [_cell itemTitleAtIndex: index]; 338} 339 340/**<p>Returns an array containing the items's titles</p> 341 */ 342- (NSArray*) itemTitles 343{ 344 return [_cell itemTitles]; 345} 346 347/**<p>Returns the NSMenuItem with title as its title</p> 348 */ 349- (id <NSMenuItem>) itemWithTitle: (NSString*)title 350{ 351 return [_cell itemWithTitle: title]; 352} 353 354/**<p> Returns the last NSMenuItem of the list</p> 355 */ 356- (id <NSMenuItem>) lastItem 357{ 358 return [_cell lastItem]; 359} 360 361- (NSInteger) indexOfItem: (id <NSMenuItem>)anObject 362{ 363 return [_cell indexOfItem: anObject]; 364} 365 366/**<p>Returns the index of the item with tag as its tag. Returns -1 367 if the cell is not found</p><p>See Also: 368 [NSPopUpButtonCell-indexOfItemWithTag:] -indexOfItemWithTitle: 369 -indexOfItemWithRepresentedObject:</p> 370*/ 371- (NSInteger) indexOfItemWithTag: (NSInteger)tag 372{ 373 return [_cell indexOfItemWithTag: tag]; 374} 375 376/**<p>Returns the index of the item with title as its title. Returns -1 377 if the cell is not found</p><p>See Also: 378 [NSPopUpButtonCell-indexOfItemWithTitle:] -indexOfItemWithTag: 379 -indexOfItemWithRepresentedObject:</p> 380*/ 381- (NSInteger) indexOfItemWithTitle: (NSString*)title 382{ 383 return [_cell indexOfItemWithTitle: title]; 384} 385 386- (NSInteger) indexOfItemWithRepresentedObject: (id)anObject 387{ 388 return [_cell indexOfItemWithRepresentedObject: anObject]; 389} 390 391- (NSInteger) indexOfItemWithTarget: (id)target 392 andAction: (SEL)actionSelector 393{ 394 return [_cell indexOfItemWithTarget: target andAction: actionSelector]; 395} 396 397- (void) setPreferredEdge: (NSRectEdge)edge 398{ 399 [_cell setPreferredEdge: edge]; 400} 401 402- (NSRectEdge) preferredEdge 403{ 404 return [_cell preferredEdge]; 405} 406 407- (void) setTitle: (NSString*)aString 408{ 409 [_cell setTitle: aString]; 410 [self synchronizeTitleAndSelectedItem]; 411} 412 413- (void) synchronizeTitleAndSelectedItem 414{ 415 [_cell synchronizeTitleAndSelectedItem]; 416 [self setNeedsDisplay: YES]; 417} 418 419- (BOOL) resignFirstResponder 420{ 421 [_cell dismissPopUp]; 422 423 return [super resignFirstResponder]; 424} 425 426- (BOOL) performKeyEquivalent: (NSEvent*)theEvent 427{ 428 NSMenu *m = [self menu]; 429 NSMenuItem *oldSelectedItem = (NSMenuItem *)[_cell selectedItem]; 430 431 if (m != nil) 432 { 433 if ([m performKeyEquivalent: theEvent]) 434 { 435 // pullsDown does not change selected item 436 if ([_cell pullsDown]) 437 { 438 [self selectItem: oldSelectedItem]; 439 } 440 else 441 { 442 /* If the key equivalent was performed, redisplay ourselves 443 * to account for potential changes in the selected item. 444 */ 445 [self setNeedsDisplay: YES]; 446 } 447 return YES; 448 } 449 } 450 return NO; 451} 452 453- (void) mouseDown: (NSEvent*)theEvent 454{ 455 [_cell trackMouse: theEvent 456 inRect: [self bounds] 457 ofView: self 458 untilMouseUp: YES]; 459} 460 461- (void) keyDown: (NSEvent*)theEvent 462{ 463 // FIXME: This method also handles the key events for the popup menu window, 464 // as menu windows cannot become key window. 465 if ([self isEnabled]) 466 { 467 NSString *characters = [theEvent characters]; 468 unichar character = 0; 469 470 if ([characters length] > 0) 471 { 472 character = [characters characterAtIndex: 0]; 473 } 474 475 switch (character) 476 { 477 case NSNewlineCharacter: 478 case NSEnterCharacter: 479 case NSCarriageReturnCharacter: 480 /* Handle Enter and Return keys only when the menu is visible. 481 The button's action to pop up the menu is initiated only by 482 the Space key similar to other buttons. */ 483 { 484 NSMenuView *menuView = [[_cell menu] menuRepresentation]; 485 if ([[menuView window] isVisible] == NO) 486 break; 487 } 488 case ' ': 489 { 490 NSInteger selectedIndex; 491 NSMenuView *menuView; 492 493 // Beep, as on OS, and then return. 494 if ([[_cell menu] numberOfItems] == 0) 495 { 496 NSBeep(); 497 return; 498 } 499 500 menuView = [[_cell menu] menuRepresentation]; 501 if ([[menuView window] isVisible] == NO) 502 { 503 // Attach the popUp 504 [_cell attachPopUpWithFrame: _bounds 505 inView: self]; 506 } 507 else 508 { 509 selectedIndex = [menuView highlightedItemIndex]; 510 if (selectedIndex >= 0) 511 { 512 [[_cell menu] performActionForItemAtIndex: selectedIndex]; 513 } 514 } 515 } 516 return; 517 case '\e': 518 [_cell dismissPopUp]; 519 return; 520 case NSUpArrowFunctionKey: 521 { 522 NSMenuView *menuView; 523 NSInteger selectedIndex, numberOfItems; 524 525 menuView = [[_cell menu] menuRepresentation]; 526 selectedIndex = [menuView highlightedItemIndex]; 527 numberOfItems = [self numberOfItems]; 528 529 switch (selectedIndex) 530 { 531 case -1: 532 selectedIndex = numberOfItems - 1; 533 break; 534 case 0: 535 return; 536 default: 537 selectedIndex--; 538 break; 539 } 540 541 [menuView setHighlightedItemIndex: selectedIndex]; 542 } 543 return; 544 case NSDownArrowFunctionKey: 545 { 546 NSMenuView *menuView; 547 NSInteger selectedIndex, numberOfItems; 548 549 menuView = [[_cell menu] menuRepresentation]; 550 selectedIndex = [menuView highlightedItemIndex]; 551 numberOfItems = [self numberOfItems]; 552 553 if (selectedIndex < numberOfItems-1) 554 [menuView setHighlightedItemIndex: selectedIndex + 1]; 555 } 556 return; 557 } 558 } 559 560 [super keyDown: theEvent]; 561} 562 563- (void) setValue: (id)anObject forKey: (NSString*)aKey 564{ 565 if ([aKey isEqual: NSSelectedIndexBinding]) 566 { 567 [self selectItemAtIndex: [anObject intValue]]; 568 } 569 else if ([aKey isEqual: NSSelectedTagBinding]) 570 { 571 [self selectItemWithTag: [anObject integerValue]]; 572 } 573 else if ([aKey isEqual: NSSelectedObjectBinding]) 574 { 575 // FIXME: This looks wrong to me 576 [self selectItemWithTag: [anObject intValue]]; 577 } 578 else if ([aKey isEqual: NSSelectedValueBinding]) 579 { 580 [self selectItemWithTitle: anObject]; 581 } 582 else if ([aKey isEqual: NSContentValuesBinding]) 583 { 584 [self removeAllItems]; 585 [self addItemsWithTitles: (NSArray*)anObject]; 586 } 587 else 588 { 589 [super setValue: anObject forKey: aKey]; 590 } 591} 592 593- (id) valueForKey: (NSString*)aKey 594{ 595 if ([aKey isEqual: NSSelectedIndexBinding]) 596 { 597 return [NSNumber numberWithInt: [self indexOfSelectedItem]]; 598 } 599 else if ([aKey isEqual: NSSelectedTagBinding]) 600 { 601 return [NSNumber numberWithInteger: [self selectedTag]]; 602 } 603 else if ([aKey isEqual: NSSelectedObjectBinding]) 604 { 605 // FIXME 606 return [NSNumber numberWithInt: [self selectedTag]]; 607 } 608 else if ([aKey isEqual: NSSelectedValueBinding]) 609 { 610 return [self titleOfSelectedItem]; 611 } 612 else if ([aKey isEqual: NSContentValuesBinding]) 613 { 614 return [self itemTitles]; 615 } 616 else 617 { 618 return [super valueForKey: aKey]; 619 } 620} 621 622@end 623