00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import "CPButton.j"
00024 @import "CPGeometry.j"
00025 @import "CPMenu.j"
00026 @import "CPMenuItem.j"
00027
00028 #include "CoreGraphics/CGGeometry.h"
00029
00030
00031 var VISIBLE_MARGIN = 7.0;
00032
00033 CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
00034
00041 @implementation CPPopUpButton : CPButton
00042 {
00043 int _selectedIndex;
00044 CPRectEdge _preferredEdge;
00045
00046 CPMenu _menu;
00047 }
00048
00049 + (CPString)themeClass
00050 {
00051 return "popup-button";
00052 }
00053
00060 - (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
00061 {
00062 self = [super initWithFrame:aFrame];
00063
00064 if (self)
00065 {
00066 _selectedIndex = CPNotFound;
00067 _preferredEdge = CPMaxYEdge;
00068
00069 [self setValue:CPImageLeft forThemeAttribute:@"image-position"];
00070 [self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
00071 [self setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
00072
00073 [self setMenu:[[CPMenu alloc] initWithTitle:@""]];
00074
00075 [self setPullsDown:shouldPullDown];
00076 }
00077
00078 return self;
00079 }
00080
00081 - (id)initWithFrame:(CGRect)aFrame
00082 {
00083 return [self initWithFrame:aFrame pullsDown:NO];
00084 }
00085
00086
00087
00093 - (void)setPullsDown:(BOOL)shouldPullDown
00094 {
00095 if (shouldPullDown)
00096 var changed = [self setThemeState:CPPopUpButtonStatePullsDown];
00097 else
00098 var changed = [self unsetThemeState:CPPopUpButtonStatePullsDown];
00099
00100 if (!changed)
00101 return;
00102
00103 var items = [_menu itemArray];
00104
00105 if ([items count] <= 0)
00106 return;
00107
00108 [items[0] setHidden:[self pullsDown]];
00109
00110 [self synchronizeTitleAndSelectedItem];
00111 }
00112
00116 - (BOOL)pullsDown
00117 {
00118 return [self hasThemeState:CPPopUpButtonStatePullsDown];
00119 }
00120
00121
00122
00126 - (void)addItem:(CPMenuItem)anItem
00127 {
00128 [_menu addItem:anItem];
00129 }
00130
00135 - (void)addItemWithTitle:(CPString)aTitle
00136 {
00137 [_menu addItemWithTitle:aTitle action:NULL keyEquivalent:nil];
00138 }
00139
00144 - (void)addItemsWithTitles:(CPArray)titles
00145 {
00146 var index = 0,
00147 count = [titles count];
00148
00149 for (; index < count; ++index)
00150 [self addItemWithTitle:titles[index]];
00151 }
00152
00158 - (void)insertItemWithTitle:(CPString)aTitle atIndex:(int)anIndex
00159 {
00160 var items = [self itemArray],
00161 count = [items count];
00162
00163 while (count--)
00164 if ([items[count] title] == aTitle)
00165 [self removeItemAtIndex:count];
00166
00167 [_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
00168 }
00169
00173 - (void)removeAllItems
00174 {
00175 var count = [_menu numberOfItems];
00176
00177 while (count--)
00178 [_menu removeItemAtIndex:0];
00179 }
00180
00185 - (void)removeItemWithTitle:(CPString)aTitle
00186 {
00187 [self removeItemAtIndex:[self indexOfItemWithTitle:aTitle]];
00188 [self synchronizeTitleAndSelectedItem];
00189 }
00190
00195 - (void)removeItemAtIndex:(int)anIndex
00196 {
00197 [_menu removeItemAtIndex:anIndex];
00198 [self synchronizeTitleAndSelectedItem];
00199 }
00200
00201
00205 - (CPMenuItem)selectedItem
00206 {
00207 if (_selectedIndex < 0)
00208 return nil;
00209
00210 return [_menu itemAtIndex:_selectedIndex];
00211 }
00212
00216 - (CPString)titleOfSelectedItem
00217 {
00218 return [[self selectedItem] title];
00219 }
00220
00224 - (int)indexOfSelectedItem
00225 {
00226 return _selectedIndex;
00227 }
00228
00229
00233 - (id)objectValue
00234 {
00235 return _selectedIndex;
00236 }
00237
00238
00243 - (void)selectItem:(CPMenuItem)aMenuItem
00244 {
00245 [self selectItemAtIndex:[self indexOfItem:aMenuItem]];
00246 }
00247
00252 - (void)selectItemAtIndex:(int)anIndex
00253 {
00254 if (_selectedIndex == anIndex)
00255 return;
00256
00257 if (_selectedIndex >= 0 && ![self pullsDown])
00258 [[self selectedItem] setState:CPOffState];
00259
00260 _selectedIndex = anIndex;
00261
00262 if (_selectedIndex >= 0 && ![self pullsDown])
00263 [[self selectedItem] setState:CPOnState];
00264
00265 [self synchronizeTitleAndSelectedItem];
00266 }
00267
00272 - (void)selectItemWithTag:(int)aTag
00273 {
00274 [self selectItemAtIndex:[self indexOfItemWithTag:aTag]];
00275 }
00276
00281 - (void)selectItemWithTitle:(CPString)aTitle
00282 {
00283 [self selectItemAtIndex:[self indexOfItemWithTitle:aTitle]];
00284 }
00285
00290 - (void)setObjectValue:(id)aValue
00291 {
00292 [self selectItemAtIndex:[aValue intValue]];
00293 }
00294
00295
00299 - (CPMenu)menu
00300 {
00301 return _menu;
00302 }
00303
00307 - (void)setMenu:(CPMenu)aMenu
00308 {
00309 if (_menu === aMenu)
00310 return;
00311
00312 var defaultCenter = [CPNotificationCenter defaultCenter];
00313
00314 if (_menu)
00315 {
00316 [defaultCenter
00317 removeObserver:self
00318 name:CPMenuDidAddItemNotification
00319 object:_menu];
00320
00321 [defaultCenter
00322 removeObserver:self
00323 name:CPMenuDidChangeItemNotification
00324 object:_menu];
00325
00326 [defaultCenter
00327 removeObserver:self
00328 name:CPMenuDidRemoveItemNotification
00329 object:_menu];
00330 }
00331
00332 _menu = aMenu;
00333
00334 if (_menu)
00335 {
00336 [defaultCenter
00337 addObserver:self
00338 selector:@selector(menuDidAddItem:)
00339 name:CPMenuDidAddItemNotification
00340 object:_menu];
00341
00342 [defaultCenter
00343 addObserver:self
00344 selector:@selector(menuDidChangeItem:)
00345 name:CPMenuDidChangeItemNotification
00346 object:_menu];
00347
00348 [defaultCenter
00349 addObserver:self
00350 selector:@selector(menuDidRemoveItem:)
00351 name:CPMenuDidRemoveItemNotification
00352 object:_menu];
00353 }
00354
00355 [self synchronizeTitleAndSelectedItem];
00356 }
00357
00361 - (int)numberOfItems
00362 {
00363 return [_menu numberOfItems];
00364 }
00365
00369 - (CPArray)itemArray
00370 {
00371 return [_menu itemArray];
00372 }
00373
00378 - (CPMenuItem)itemAtIndex:(unsigned)anIndex
00379 {
00380 return [_menu itemAtIndex:anIndex];
00381 }
00382
00387 - (CPString)itemTitleAtIndex:(unsigned)anIndex
00388 {
00389 return [[_menu itemAtIndex:anIndex] title];
00390 }
00391
00395 - (CPArray)itemTitles
00396 {
00397 var titles = [],
00398 items = [self itemArray],
00399
00400 index = 0,
00401 count = [items count];
00402
00403 for (; index < count; ++index)
00404 items.push([items[index] title]);
00405 }
00406
00411 - (CPMenuItem)itemWithTitle:(CPString)aTitle
00412 {
00413 return [_menu itemAtIndex:[_menu indexOfItemWithTitle:aTitle]];
00414 }
00415
00419 - (CPMenuItem)lastItem
00420 {
00421 return [[_menu itemArray] lastObject];
00422 }
00423
00424
00429 - (int)indexOfItem:(CPMenuItem)aMenuItem
00430 {
00431 return [_menu indexOfItem:aMenuItem];
00432 }
00433
00438 - (int)indexOfItemWithTag:(int)aTag
00439 {
00440 return [_menu indexOfItemWithTag:aTag];
00441 }
00442
00447 - (int)indexOfItemWithTitle:(CPString)aTitle
00448 {
00449 return [_menu indexOfItemWithTitle:aTitle];
00450 }
00451
00458 - (int)indexOfItemWithRepresentedObject:(id)anObject
00459 {
00460 return [_menu indexOfItemWithRepresentedObject:anObject];
00461 }
00462
00470 - (int)indexOfItemWithTarget:(id)aTarget action:(SEL)anAction
00471 {
00472 return [_menu indexOfItemWithTarget:aTarget action:anAction];
00473 }
00474
00475
00481 - (CPRectEdge)preferredEdge
00482 {
00483 return _preferredEdge;
00484 }
00485
00491 - (void)setPreferredEdge:(CPRectEdge)aRectEdge
00492 {
00493 _preferredEdge = aRectEdge;
00494 }
00495
00496
00501 - (void)setTitle:(CPString)aTitle
00502 {
00503 if ([self title] === aTitle)
00504 return;
00505
00506 if ([self pullsDown])
00507 {
00508 var items = [_menu itemArray];
00509
00510 if ([items count] <= 0)
00511 [self addItemWithTitle:aTitle];
00512
00513 else
00514 {
00515 [items[0] setTitle:aTitle];
00516 [self synchronizeTitleAndSelectedItem];
00517 }
00518 }
00519 else
00520 {
00521 var index = [self indexOfItemWithTitle:aTitle];
00522
00523 if (index < 0)
00524 {
00525 [self addItemWithTitle:aTitle];
00526
00527 index = [self numberOfItems] - 1;
00528 }
00529
00530 [self selectItemAtIndex:index];
00531 }
00532 }
00533
00534
00540 - (void)setImage:(CPImage)anImage
00541 {
00542
00543 }
00544
00545
00550 - (void)synchronizeTitleAndSelectedItem
00551 {
00552 var item = nil;
00553
00554 if ([self pullsDown])
00555 {
00556 var items = [_menu itemArray];
00557
00558 if ([items count] > 0)
00559 item = items[0];
00560 }
00561 else
00562 item = [self selectedItem];
00563
00564 [super setImage:[item image]];
00565 [super setTitle:[item title]];
00566 }
00567
00568
00573 - (void)menuDidAddItem:(CPNotification)aNotification
00574 {
00575 var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
00576
00577 if (_selectedIndex < 0)
00578 [self selectItemAtIndex:0];
00579
00580 else if (index == _selectedIndex)
00581 [self synchronizeTitleAndSelectedItem];
00582
00583 else if (index < _selectedIndex)
00584 ++_selectedIndex;
00585
00586 if (index == 0 && [self pullsDown])
00587 {
00588 var items = [_menu itemArray];
00589
00590 [items[0] setHidden:YES];
00591
00592 if (items.length > 0)
00593 [items[1] setHidden:NO];
00594 }
00595
00596 var item = [_menu itemArray][index],
00597 action = [item action];
00598
00599 if (!action || (action === @selector(_popUpItemAction:)))
00600 {
00601 [item setTarget:self];
00602 [item setAction:@selector(_popUpItemAction:)];
00603 }
00604 }
00605
00610 - (void)menuDidChangeItem:(CPNotification)aNotification
00611 {
00612 var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
00613
00614 if ([self pullsDown] && index != 0)
00615 return;
00616
00617 if (![self pullsDown] && index != _selectedIndex)
00618 return;
00619
00620 [self synchronizeTitleAndSelectedItem];
00621 }
00622
00627 - (void)menuDidRemoveItem:(CPNotification)aNotification
00628 {
00629 var numberOfItems = [self numberOfItems];
00630
00631 if (numberOfItems <= _selectedIndex)
00632 [self selectItemAtIndex:numberOfItems - 1];
00633 }
00634
00635 - (void)mouseDown:(CPEvent)anEvent
00636 {
00637 if (![self isEnabled])
00638 return;
00639
00640 [self highlight:YES];
00641
00642 var menu = [self menu],
00643 theWindow = [self window],
00644 menuWindow = [_CPMenuWindow menuWindowWithMenu:menu font:[self font]];
00645
00646 [menuWindow setDelegate:self];
00647 [menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
00648
00649
00650 if ([self pullsDown])
00651 var menuOrigin = [theWindow convertBaseToBridge:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
00652
00653
00654 else
00655 {
00656
00657
00658
00659
00660
00661 var contentRect = [self convertRect:[self contentRectForBounds:[self bounds]] toView:nil],
00662 menuOrigin = [theWindow convertBaseToBridge:contentRect.origin],
00663 menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
00664
00665 menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
00666 menuOrigin.y -= CGRectGetMinY(menuItemRect) + (CGRectGetHeight(menuItemRect) - CGRectGetHeight(contentRect)) / 2.0;
00667 }
00668
00669 [menuWindow setFrameOrigin:menuOrigin];
00670
00671 var menuMaxX = CGRectGetMaxX([menuWindow frame]),
00672 buttonMaxX = [theWindow convertBaseToBridge:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
00673
00674 if (menuMaxX < buttonMaxX)
00675 [menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - ([self pullsDown] ? 0.0 : VISIBLE_MARGIN)];
00676
00677 [menuWindow orderFront:self];
00678 [menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(menuWindowDidFinishTracking:highlightedItem:)];
00679 }
00680
00681
00682
00683
00684 - (void)menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem
00685 {
00686 [_CPMenuWindow poolMenuWindow:aMenuWindow];
00687
00688 [self highlight:NO];
00689
00690 var index = [_menu indexOfItem:aMenuItem];
00691
00692 if (index == CPNotFound)
00693 return;
00694
00695 [self selectItemAtIndex:index];
00696
00697 [CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
00698 }
00699
00700 - (void)_popUpItemAction:(id)aSender
00701 {
00702 [self sendAction:[self action] to:[self target]];
00703 }
00704
00705 @end
00706
00707 var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
00708 CPPopUpButtonSelectedIndexKey = @"CPPopUpButtonSelectedIndexKey",
00709 CPPopUpButtonPullsDownKey = @"CPPopUpButtonPullsDownKey";
00710
00711 @implementation CPPopUpButton (CPCoding)
00719 - (id)initWithCoder:(CPCoder)aCoder
00720 {
00721 self = [super initWithCoder:aCoder];
00722
00723 if (self)
00724 {
00725
00726 _selectedIndex = -1;
00727
00728 [self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
00729 [self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
00730 }
00731
00732 return self;
00733 }
00734
00740 - (void)encodeWithCoder:(CPCoder)aCoder
00741 {
00742 [super encodeWithCoder:aCoder];
00743
00744 [aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
00745 [aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
00746 }
00747
00748 @end