![]() |
API 0.9.5
|
00001 /* 00002 * CPToolbar.j 00003 * AppKit 00004 * 00005 * Portions based on NSToolbar.m (11/10/2008) in Cocotron (http://www.cocotron.org/) 00006 * Copyright (c) 2006-2007 Christopher J. W. Lloyd 00007 * 00008 * Created by Francisco Tolmasky. 00009 * Copyright 2008, 280 North, Inc. 00010 * 00011 * This library is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU Lesser General Public 00013 * License as published by the Free Software Foundation; either 00014 * version 2.1 of the License, or (at your option) any later version. 00015 * 00016 * This library is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 * Lesser General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU Lesser General Public 00022 * License along with this library; if not, write to the Free Software 00023 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00024 */ 00025 00026 00027 00028 00029 /* 00030 @global 00031 @group CPToolbarDisplayMode 00032 */ 00033 CPToolbarDisplayModeDefault = 0; 00034 /* 00035 @global 00036 @group CPToolbarDisplayMode 00037 */ 00038 CPToolbarDisplayModeIconAndLabel = 1; 00039 /* 00040 @global 00041 @group CPToolbarDisplayMode 00042 */ 00043 CPToolbarDisplayModeIconOnly = 2; 00044 /* 00045 @global 00046 @group CPToolbarDisplayMode 00047 */ 00048 CPToolbarDisplayModeLabelOnly = 3; 00049 00050 var CPToolbarsByIdentifier = nil, 00051 CPToolbarConfigurationsByIdentifier = nil; 00052 00080 @implementation CPToolbar : CPObject 00081 { 00082 CPString _identifier; 00083 CPToolbarDisplayMode _displayMode; 00084 BOOL _showsBaselineSeparator; 00085 BOOL _allowsUserCustomization; 00086 BOOL _isVisible; 00087 00088 id _delegate; 00089 00090 CPArray _itemIdentifiers; 00091 00092 CPDictionary _identifiedItems; 00093 CPArray _defaultItems; 00094 CPArray _allowedItems; 00095 CPArray _selectableItems; 00096 00097 CPArray _items; 00098 CPArray _itemsSortedByVisibilityPriority; 00099 00100 CPView _toolbarView; 00101 CPWindow _window; 00102 } 00103 00104 /* @ignore */ 00105 + (void)initialize 00106 { 00107 if (self != [CPToolbar class]) 00108 return; 00109 00110 CPToolbarsByIdentifier = [CPDictionary dictionary]; 00111 CPToolbarConfigurationsByIdentifier = [CPDictionary dictionary]; 00112 } 00113 00114 /* @ignore */ 00115 + (void)_addToolbar:(CPToolbar)toolbar forIdentifier:(CPString)identifier 00116 { 00117 var toolbarsSharingIdentifier = [CPToolbarsByIdentifier objectForKey:identifier]; 00118 00119 if (!toolbarsSharingIdentifier) 00120 { 00121 toolbarsSharingIdentifier = [] 00122 [CPToolbarsByIdentifier setObject:toolbarsSharingIdentifier forKey:identifier]; 00123 } 00124 00125 [toolbarsSharingIdentifier addObject:toolbar]; 00126 } 00127 00128 - (id)init 00129 { 00130 return [self initWithIdentifier:@""]; 00131 } 00132 00138 - (id)initWithIdentifier:(CPString)anIdentifier 00139 { 00140 self = [super init]; 00141 00142 if (self) 00143 { 00144 _items = []; 00145 00146 _identifier = anIdentifier; 00147 _isVisible = YES; 00148 00149 [CPToolbar _addToolbar:self forIdentifier:_identifier]; 00150 } 00151 00152 return self; 00153 } 00154 00155 00159 - (void)setDisplayMode:(CPToolbarDisplayMode)aDisplayMode 00160 { 00161 00162 } 00163 00167 - (CPString)identifier 00168 { 00169 return _identifier; 00170 } 00171 00175 - (id)delegate 00176 { 00177 return _delegate; 00178 } 00179 00183 - (BOOL)isVisible 00184 { 00185 return _isVisible; 00186 } 00187 00192 - (void)setVisible:(BOOL)aFlag 00193 { 00194 if (_isVisible === aFlag) 00195 return; 00196 00197 _isVisible = aFlag; 00198 00199 [_window _noteToolbarChanged]; 00200 } 00201 00202 - (CPWindow)_window 00203 { 00204 return _window; 00205 } 00206 00207 - (void)_setWindow:(CPWindow)aWindow 00208 { 00209 _window = aWindow; 00210 } 00211 00216 - (void)setDelegate:(id)aDelegate 00217 { 00218 if (_delegate === aDelegate) 00219 return; 00220 00221 _delegate = aDelegate; 00222 00223 [self _reloadToolbarItems]; 00224 } 00225 00226 /* @ignore */ 00227 - (void)_loadConfiguration 00228 { 00229 00230 } 00231 00232 /* @ignore */ 00233 - (CPView)_toolbarView 00234 { 00235 if (!_toolbarView) 00236 { 00237 _toolbarView = [[_CPToolbarView alloc] initWithFrame:CPRectMake(0.0, 0.0, 1200.0, 59.0)]; 00238 00239 [_toolbarView setToolbar:self]; 00240 [_toolbarView setAutoresizingMask:CPViewWidthSizable]; 00241 [_toolbarView reloadToolbarItems]; 00242 } 00243 00244 return _toolbarView; 00245 } 00246 00247 /* @ignore */ 00248 - (void)_reloadToolbarItems 00249 { 00250 // As of OS X 10.5 (Leopard), toolbar items can be set in IB and a 00251 // toolbar delegate is optional. Toolbar items can be combined from 00252 // both IB and a delegate (see Apple's NSToolbar guide for IB, for more details). 00253 00254 // _defaultItems may have been loaded from Cib 00255 _itemIdentifiers = [_defaultItems valueForKey:@"itemIdentifier"] || []; 00256 00257 if (_delegate) 00258 { 00259 var itemIdentifiersFromDelegate = [[_delegate toolbarDefaultItemIdentifiers:self] mutableCopy]; 00260 00261 if (itemIdentifiersFromDelegate) 00262 _itemIdentifiers = [_itemIdentifiers arrayByAddingObjectsFromArray:itemIdentifiersFromDelegate]; 00263 } 00264 00265 var index = 0, 00266 count = [_itemIdentifiers count]; 00267 00268 _items = []; 00269 00270 for (; index < count; ++index) 00271 { 00272 var identifier = _itemIdentifiers[index], 00273 item = [CPToolbarItem _standardItemWithItemIdentifier:identifier]; 00274 00275 // May come from a Cib. 00276 if (!item) 00277 item = [_identifiedItems objectForKey:identifier]; 00278 00279 if (!item && _delegate) 00280 item = [_delegate toolbar:self itemForItemIdentifier:identifier willBeInsertedIntoToolbar:YES]; 00281 00282 item = [item copy]; 00283 00284 if (item === nil) 00285 [CPException raise:CPInvalidArgumentException 00286 reason:@"Toolbar delegate " + _delegate + " returned nil toolbar item for identifier \"" + identifier + "\""]; 00287 00288 item._toolbar = self; 00289 00290 [_items addObject:item]; 00291 } 00292 00293 // _items = [[self _defaultToolbarItems] mutableCopy]; 00294 00295 // Store items sorted by priority. We want items to be removed first at the end of the array, 00296 // items to be removed last at the front. 00297 00298 _itemsSortedByVisibilityPriority = [_items sortedArrayUsingFunction:_CPToolbarItemVisibilityPriorityCompare context:NULL]; 00299 00300 [_toolbarView reloadToolbarItems]; 00301 } 00302 00306 - (CPArray)items 00307 { 00308 return _items; 00309 } 00310 00314 - (CPArray)visibleItems 00315 { 00316 return [_toolbarView visibleItems]; 00317 } 00318 00322 - (CPArray)itemsSortedByVisibilityPriority 00323 { 00324 return _itemsSortedByVisibilityPriority; 00325 } 00326 00331 - (void)validateVisibleItems 00332 { 00333 var toolbarItems = [self visibleItems], 00334 count = [toolbarItems count]; 00335 00336 while (count--) 00337 [toolbarItems[count] validate]; 00338 } 00339 00340 /* @ignore */ 00341 - (id)_itemForItemIdentifier:(CPString)identifier willBeInsertedIntoToolbar:(BOOL)toolbar 00342 { 00343 var item = [_identifiedItems objectForKey:identifier]; 00344 if (!item) 00345 { 00346 item = [CPToolbarItem _standardItemWithItemIdentifier:identifier]; 00347 if (_delegate && !item) 00348 { 00349 item = [[_delegate toolbar:self itemForItemIdentifier:identifier willBeInsertedIntoToolbar:toolbar] copy]; 00350 if (!item) 00351 [CPException raise:CPInvalidArgumentException 00352 reason:@"Toolbar delegate " + _delegate + " returned nil toolbar item for identifier " + identifier]; 00353 } 00354 00355 [_identifiedItems setObject:item forKey:identifier]; 00356 } 00357 00358 return item; 00359 } 00360 00361 /* @ignore */ 00362 - (id)_itemsWithIdentifiers:(CPArray)identifiers 00363 { 00364 var items = []; 00365 for (var i = 0; i < identifiers.length; i++) 00366 [items addObject:[self _itemForItemIdentifier:identifiers[i] willBeInsertedIntoToolbar:NO]]; 00367 00368 return items; 00369 } 00370 00371 /* @ignore */ 00372 - (id)_defaultToolbarItems 00373 { 00374 if (!_defaultItems && [_delegate respondsToSelector:@selector(toolbarDefaultItemIdentifiers:)]) 00375 { 00376 _defaultItems = []; 00377 00378 var identifiers = [_delegate toolbarDefaultItemIdentifiers:self], 00379 index = 0, 00380 count = [identifiers count]; 00381 00382 for (; index < count; ++index) 00383 [_defaultItems addObject:[self _itemForItemIdentifier:identifiers[index] willBeInsertedIntoToolbar:NO]]; 00384 } 00385 00386 return _defaultItems; 00387 } 00388 00393 - (void)toolbarItemDidChange:(CPToolbarItem)anItem 00394 { 00395 if ([_identifiedItems objectForKey:[anItem itemIdentifier]]) 00396 [_identifiedItems setObject:anItem forKey:[anItem itemIdentifier]]; 00397 00398 var index = 0, 00399 count = [_items count]; 00400 00401 for (; index <= count; ++index) 00402 { 00403 var item = _items[index]; 00404 00405 if ([item itemIdentifier] === [anItem itemIdentifier]) 00406 { 00407 _items[index] = anItem; 00408 _itemsSortedByVisibilityPriority = [_items sortedArrayUsingFunction:_CPToolbarItemVisibilityPriorityCompare context:NULL]; 00409 00410 [_toolbarView reloadToolbarItems]; 00411 } 00412 } 00413 } 00414 00415 @end 00416 00417 00418 var CPToolbarIdentifierKey = @"CPToolbarIdentifierKey", 00419 CPToolbarDisplayModeKey = @"CPToolbarDisplayModeKey", 00420 CPToolbarShowsBaselineSeparatorKey = @"CPToolbarShowsBaselineSeparatorKey", 00421 CPToolbarAllowsUserCustomizationKey = @"CPToolbarAllowsUserCustomizationKey", 00422 CPToolbarIsVisibleKey = @"CPToolbarIsVisibleKey", 00423 CPToolbarDelegateKey = @"CPToolbarDelegateKey", 00424 CPToolbarIdentifiedItemsKey = @"CPToolbarIdentifiedItemsKey", 00425 CPToolbarDefaultItemsKey = @"CPToolbarDefaultItemsKey", 00426 CPToolbarAllowedItemsKey = @"CPToolbarAllowedItemsKey", 00427 CPToolbarSelectableItemsKey = @"CPToolbarSelectableItemsKey"; 00428 00429 @implementation CPToolbar (CPCoding) 00430 00431 /* 00432 Initializes the toolbar by unarchiving data from \c aCoder. 00433 @param aCoder the coder containing the archived CPToolbar. 00434 */ 00435 - (id)initWithCoder:(CPCoder)aCoder 00436 { 00437 self = [super init]; 00438 00439 if (self) 00440 { 00441 _identifier = [aCoder decodeObjectForKey:CPToolbarIdentifierKey]; 00442 _displayMode = [aCoder decodeIntForKey:CPToolbarDisplayModeKey]; 00443 _showsBaselineSeparator = [aCoder decodeBoolForKey:CPToolbarShowsBaselineSeparatorKey]; 00444 _allowsUserCustomization = [aCoder decodeBoolForKey:CPToolbarAllowsUserCustomizationKey]; 00445 _isVisible = [aCoder decodeBoolForKey:CPToolbarIsVisibleKey]; 00446 00447 _identifiedItems = [aCoder decodeObjectForKey:CPToolbarIdentifiedItemsKey]; 00448 _defaultItems = [aCoder decodeObjectForKey:CPToolbarDefaultItemsKey]; 00449 _allowedItems = [aCoder decodeObjectForKey:CPToolbarAllowedItemsKey]; 00450 _selectableItems = [aCoder decodeObjectForKey:CPToolbarSelectableItemsKey]; 00451 00452 [[_identifiedItems allValues] makeObjectsPerformSelector:@selector(_setToolbar:) withObject:self]; 00453 00454 _items = []; 00455 00456 [CPToolbar _addToolbar:self forIdentifier:_identifier]; 00457 00458 // This won't come from a Cib, but can come from manual encoding. 00459 [self setDelegate:[aCoder decodeObjectForKey:CPToolbarDelegateKey]]; 00460 00461 // Because we don't know if a delegate will be set later (it is optional 00462 // as of OS X 10.5), we need to call -_reloadToolbarItems here. 00463 // In order to load any toolbar items that may have been configured in the 00464 // Cib. Unfortunately this means that if there is a delegate 00465 // specified, it will be read later and the resulting call to -setDelegate: 00466 // will cause -_reloadToolbarItems] to run again :-( 00467 // FIXME: Can we make this better? 00468 00469 // Do this at the end of the run loop to allow all the cib-stuff to 00470 // finish (establishing connections, etc.). 00471 [[CPRunLoop currentRunLoop] 00472 performSelector:@selector(_reloadToolbarItems) 00473 target:self 00474 argument:nil 00475 order:0 modes:[CPDefaultRunLoopMode]]; 00476 } 00477 00478 return self; 00479 } 00480 00481 /* 00482 Archives this toolbar into the provided coder. 00483 @param aCoder the coder to which the toolbar's instance data will be written. 00484 */ 00485 - (void)encodeWithCoder:(CPCoder)aCoder 00486 { 00487 [aCoder encodeObject:_identifier forKey:CPToolbarIdentifierKey]; 00488 [aCoder encodeInt:_displayMode forKey:CPToolbarDisplayModeKey]; 00489 [aCoder encodeBool:_showsBaselineSeparator forKey:CPToolbarShowsBaselineSeparatorKey]; 00490 [aCoder encodeBool:_allowsUserCustomization forKey:CPToolbarAllowsUserCustomizationKey]; 00491 [aCoder encodeBool:_isVisible forKey:CPToolbarIsVisibleKey]; 00492 00493 [aCoder encodeObject:_identifiedItems forKey:CPToolbarIdentifiedItemsKey]; 00494 [aCoder encodeObject:_defaultItems forKey:CPToolbarDefaultItemsKey]; 00495 [aCoder encodeObject:_allowedItems forKey:CPToolbarAllowedItemsKey]; 00496 [aCoder encodeObject:_selectableItems forKey:CPToolbarSelectableItemsKey]; 00497 00498 [aCoder encodeConditionalObject:_delegate forKey:CPToolbarDelegateKey]; 00499 } 00500 00501 @end 00502 00503 00504 var _CPToolbarViewBackgroundColor = nil, 00505 _CPToolbarViewExtraItemsImage = nil, 00506 _CPToolbarViewExtraItemsAlternateImage = nil; 00507 00508 var TOOLBAR_TOP_MARGIN = 5.0, 00509 TOOLBAR_ITEM_MARGIN = 10.0, 00510 TOOLBAR_EXTRA_ITEMS_WIDTH = 20.0; 00511 00512 var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth) 00513 { 00514 return { index:anIndex, view:aView, label:aLabel, minWidth:aMinWidth }; 00515 } 00516 00517 /* @ignore */ 00518 @implementation _CPToolbarView : CPView 00519 { 00520 CPToolbar _toolbar; 00521 00522 CPIndexSet _flexibleWidthIndexes; 00523 CPIndexSet _visibleFlexibleWidthIndexes; 00524 00525 CPDictionary _itemInfos; 00526 JSObject _viewsForToolbarItems; 00527 00528 CPArray _visibleItems; 00529 CPArray _invisibleItems; 00530 00531 CPPopUpButton _additionalItemsButton; 00532 CPColor _labelColor; 00533 CPColor _labelShadowColor; 00534 00535 float _minWidth; 00536 00537 BOOL _FIXME_isHUD; 00538 } 00539 00540 + (void)initialize 00541 { 00542 if (self !== [_CPToolbarView class]) 00543 return; 00544 00545 var bundle = [CPBundle bundleForClass:self]; 00546 00547 _CPToolbarViewExtraItemsImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsImage.png"] size: CPSizeMake(10.0, 15.0)]; 00548 00549 _CPToolbarViewExtraItemsAlternateImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png"] size:CGSizeMake(10.0, 15.0)]; 00550 } 00551 00552 - (id)initWithFrame:(CGRect)aFrame 00553 { 00554 self = [super initWithFrame:aFrame]; 00555 00556 if (self) 00557 { 00558 _minWidth = 0; 00559 00560 _labelColor = [CPColor blackColor]; 00561 _labelShadowColor = [CPColor colorWithWhite:1.0 alpha:0.75]; 00562 00563 _additionalItemsButton = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 15.0) pullsDown:YES]; 00564 [_additionalItemsButton setBordered:NO]; 00565 00566 [_additionalItemsButton setImagePosition:CPImageOnly]; 00567 [[_additionalItemsButton menu] setShowsStateColumn:NO]; 00568 [[_additionalItemsButton menu] setAutoenablesItems:NO]; 00569 00570 [_additionalItemsButton setAlternateImage:_CPToolbarViewExtraItemsAlternateImage]; 00571 } 00572 00573 return self; 00574 } 00575 00576 - (void)setToolbar:(CPToolbar)aToolbar 00577 { 00578 _toolbar = aToolbar; 00579 } 00580 00581 - (CPToolbar)toolbar 00582 { 00583 return _toolbar; 00584 } 00585 00586 - (void)FIXME_setIsHUD:(BOOL)shouldBeHUD 00587 { 00588 if (_FIXME_isHUD === shouldBeHUD) 00589 return; 00590 00591 _FIXME_isHUD = shouldBeHUD; 00592 00593 var items = [_toolbar items], 00594 count = [items count]; 00595 00596 while (count--) 00597 [[self viewForItem:items[count]] FIXME_setIsHUD:shouldBeHUD]; 00598 } 00599 00600 // This *should* be roughly O(3N) = O(N) 00601 - (void)resizeSubviewsWithOldSize:(CGSize)aSize 00602 { 00603 [self tile]; 00604 } 00605 00606 - (_CPToolbarItemView)viewForItem:(CPToolbarItem)anItem 00607 { 00608 return _viewsForToolbarItems[[anItem UID]] || nil; 00609 } 00610 00611 - (void)tile 00612 { 00613 // We begin by recalculating the visible items. 00614 var items = [_toolbar items], 00615 itemsWidth = CGRectGetWidth([self bounds]), 00616 minWidth = _minWidth, 00617 // FIXME: This should be a CPSet. 00618 invisibleItemsSortedByPriority = []; 00619 00620 _visibleItems = items; 00621 00622 // We only have hidden items if our actual width is smaller than our 00623 // minimum width for hiding items. 00624 if (itemsWidth < minWidth) 00625 { 00626 itemsWidth -= TOOLBAR_EXTRA_ITEMS_WIDTH; 00627 00628 _visibleItems = [_visibleItems copy]; 00629 00630 var itemsSortedByVisibilityPriority = [_toolbar itemsSortedByVisibilityPriority], 00631 count = itemsSortedByVisibilityPriority.length; 00632 00633 // Remove items until we fit: 00634 // The assumption here is that there are more visible items than there are 00635 // invisible items, if not it would be faster to add items until we *no 00636 // longer fit*. 00637 while (minWidth > itemsWidth && count) 00638 { 00639 var item = itemsSortedByVisibilityPriority[--count], 00640 view = [self viewForItem:item]; 00641 00642 minWidth -= [view minSize].width + TOOLBAR_ITEM_MARGIN; 00643 00644 [_visibleItems removeObjectIdenticalTo:item]; 00645 [invisibleItemsSortedByPriority addObject:item]; 00646 00647 [view setHidden:YES]; 00648 [view FIXME_setIsHUD:_FIXME_isHUD]; 00649 } 00650 } 00651 00652 // FIXME: minHeight? 00653 var count = [items count], 00654 height = 0.0; 00655 00656 while (count--) 00657 { 00658 var view = [self viewForItem:items[count]], 00659 minSize = [view minSize]; 00660 00661 if (height < minSize.height) 00662 height = minSize.height; 00663 } 00664 00665 // Determine all the items that have flexible width. 00666 // Also determine the height of the toolbar. 00667 var count = _visibleItems.length, 00668 flexibleItemIndexes = [CPIndexSet indexSet]; 00669 00670 while (count--) 00671 { 00672 var item = _visibleItems[count], 00673 view = [self viewForItem:item], 00674 minSize = [view minSize]; 00675 00676 if (minSize.width !== [view maxSize].width) 00677 [flexibleItemIndexes addIndex:count]; 00678 00679 // FIXME: Is this still necessary? (probably not since we iterate them all below). 00680 // If the item doesn't have flexible width, then make sure it's set to the 00681 // static width (min==max). This handles the case where the user did setView: 00682 // with a view of a different size than minSize/maxSize 00683 else 00684 [view setFrameSize:CGSizeMake(minSize.width, height)]; 00685 00686 [view setHidden:NO]; 00687 } 00688 00689 var remainingSpace = itemsWidth - minWidth, 00690 proportionate = 0.0; 00691 00692 // Continue to distribute space proportionately while we have it, 00693 // and there are flexible items left that want it. (Those with max 00694 // widths may eventually not want it anymore). 00695 while (remainingSpace && [flexibleItemIndexes count]) 00696 { 00697 // Divy out the space. 00698 proportionate += remainingSpace / [flexibleItemIndexes count]; 00699 00700 // Reset the remaining space to 0 00701 remainingSpace = 0.0; 00702 00703 var index = CPNotFound; 00704 00705 while ((index = [flexibleItemIndexes indexGreaterThanIndex:index]) !== CPNotFound) 00706 { 00707 var item = _visibleItems[index], 00708 view = [self viewForItem:item], 00709 proposedWidth = [view minSize].width + proportionate, 00710 constrainedWidth = MIN(proposedWidth, [view maxSize].width); 00711 00712 if (constrainedWidth < proposedWidth) 00713 { 00714 [flexibleItemIndexes removeIndex:index]; 00715 00716 remainingSpace += proposedWidth - constrainedWidth; 00717 } 00718 00719 [view setFrameSize:CGSizeMake(constrainedWidth, height)]; 00720 } 00721 } 00722 00723 // Now that all the visible items are the correct width, give them their final frames. 00724 var index = 0, 00725 count = _visibleItems.length, 00726 x = TOOLBAR_ITEM_MARGIN; 00727 00728 for (; index < count; ++index) 00729 { 00730 var view = [self viewForItem:_visibleItems[index]], 00731 viewWidth = CGRectGetWidth([view frame]); 00732 00733 [view setFrame:CGRectMake(x, 0.0, viewWidth, height)]; 00734 00735 x += viewWidth + TOOLBAR_ITEM_MARGIN; 00736 } 00737 00738 var needsAdditionalItemsButton = NO; 00739 00740 if ([invisibleItemsSortedByPriority count]) 00741 { 00742 var index = 0, 00743 count = [items count]; 00744 00745 _invisibleItems = []; 00746 00747 for (; index < count; ++index) 00748 { 00749 var item = items[index]; 00750 00751 if ([invisibleItemsSortedByPriority indexOfObjectIdenticalTo:item] !== CPNotFound) 00752 { 00753 [_invisibleItems addObject:item]; 00754 00755 var identifier = [item itemIdentifier]; 00756 00757 if (identifier !== CPToolbarSpaceItemIdentifier && 00758 identifier !== CPToolbarFlexibleSpaceItemIdentifier && 00759 identifier !== CPToolbarSeparatorItemIdentifier) 00760 needsAdditionalItemsButton = YES; 00761 } 00762 } 00763 } 00764 00765 if (needsAdditionalItemsButton) 00766 { 00767 [_additionalItemsButton setFrameOrigin:CGPointMake(itemsWidth + 5.0, (CGRectGetHeight([self bounds]) - CGRectGetHeight([_additionalItemsButton frame])) / 2.0)]; 00768 00769 [self addSubview:_additionalItemsButton]; 00770 00771 [_additionalItemsButton removeAllItems]; 00772 00773 [_additionalItemsButton addItemWithTitle:@"Additional Items"]; 00774 [[_additionalItemsButton itemArray][0] setImage:_CPToolbarViewExtraItemsImage]; 00775 00776 var index = 0, 00777 count = [_invisibleItems count], 00778 hasNonSeparatorItem = NO; 00779 00780 for (; index < count; ++index) 00781 { 00782 var item = _invisibleItems[index], 00783 identifier = [item itemIdentifier]; 00784 00785 if (identifier === CPToolbarSpaceItemIdentifier || 00786 identifier === CPToolbarFlexibleSpaceItemIdentifier) 00787 continue; 00788 00789 if (identifier === CPToolbarSeparatorItemIdentifier) 00790 { 00791 if (hasNonSeparatorItem) 00792 [_additionalItemsButton addItem:[CPMenuItem separatorItem]]; 00793 00794 continue; 00795 } 00796 00797 hasNonSeparatorItem = YES; 00798 00799 var menuItem = [[CPMenuItem alloc] initWithTitle:[item label] action:@selector(didSelectMenuItem:) keyEquivalent:nil]; 00800 00801 [menuItem setRepresentedObject:item]; 00802 [menuItem setImage:[item image]]; 00803 [menuItem setTarget:self]; 00804 [menuItem setEnabled:[item isEnabled]]; 00805 00806 [_additionalItemsButton addItem:menuItem]; 00807 } 00808 } 00809 else 00810 [_additionalItemsButton removeFromSuperview]; 00811 } 00812 00813 /* 00814 Used privately. 00815 @ignore 00816 */ 00817 00818 - (void)didSelectMenuItem:(id)aSender 00819 { 00820 var toolbarItem = [aSender representedObject]; 00821 00822 [CPApp sendAction:[toolbarItem action] to:[toolbarItem target] from:toolbarItem]; 00823 } 00824 00825 - (void)reloadToolbarItems 00826 { 00827 // Get rid of all our current subviews. 00828 var subviews = [self subviews], 00829 count = subviews.length; 00830 00831 while (count--) 00832 [subviews[count] removeFromSuperview]; 00833 00834 // Populate with new subviews. 00835 var items = [_toolbar items], 00836 index = 0; 00837 00838 count = items.length; 00839 00840 _minWidth = TOOLBAR_ITEM_MARGIN; 00841 _viewsForToolbarItems = { }; 00842 00843 for (; index < count; ++index) 00844 { 00845 var item = items[index], 00846 view = [[_CPToolbarItemView alloc] initWithToolbarItem:item toolbar:self]; 00847 00848 _viewsForToolbarItems[[item UID]] = view; 00849 00850 if ([item toolTip] && [view respondsToSelector:@selector(setToolTip:)]) 00851 [view setToolTip:[item toolTip]]; 00852 00853 [self addSubview:view]; 00854 00855 _minWidth += [view minSize].width + TOOLBAR_ITEM_MARGIN; 00856 } 00857 00858 [self tile]; 00859 } 00860 00861 @end 00862 00863 /* @ignore */ 00864 var _CPToolbarItemVisibilityPriorityCompare = function(lhs, rhs) 00865 { 00866 var lhsVisibilityPriority = [lhs visibilityPriority], 00867 rhsVisibilityPriority = [rhs visibilityPriority]; 00868 00869 if (lhsVisibilityPriority == rhsVisibilityPriority) 00870 return CPOrderedSame; 00871 00872 if (lhsVisibilityPriority > rhsVisibilityPriority) 00873 return CPOrderedAscending; 00874 00875 return CPOrderedDescending; 00876 } 00877 00878 var TOP_MARGIN = 5.0, 00879 LABEL_MARGIN = 2.0; 00880 00881 @implementation _CPToolbarItemView : CPControl 00882 { 00883 CGSize _minSize; 00884 CGSize _maxSize; 00885 CGSize _labelSize; 00886 00887 CPToolbarItem _toolbarItem; 00888 CPToolbar _toolbar; 00889 00890 CPImageView _imageView; 00891 CPView _view; 00892 00893 CPTextField _labelField; 00894 00895 BOOL _FIXME_isHUD; 00896 } 00897 00898 - (id)initWithToolbarItem:(CPToolbarItem)aToolbarItem toolbar:(CPToolbar)aToolbar 00899 { 00900 self = [super init]; 00901 00902 if (self) 00903 { 00904 _toolbarItem = aToolbarItem; 00905 00906 _labelField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; 00907 00908 [_labelField setFont:[CPFont systemFontOfSize:11.0]]; 00909 [_labelField setTextColor:[self FIXME_labelColor]]; 00910 [_labelField setTextShadowColor:[self FIXME_labelShadowColor]]; 00911 [_labelField setTextShadowOffset:CGSizeMake(0.0, 1.0)]; 00912 [_labelField setAutoresizingMask:CPViewWidthSizable | CPViewMinXMargin]; 00913 00914 [self addSubview:_labelField]; 00915 00916 [self updateFromItem]; 00917 00918 _toolbar = aToolbar; 00919 00920 var keyPaths = [@"label", @"image", @"alternateImage", @"minSize", @"maxSize", @"target", @"action", @"enabled"], 00921 index = 0, 00922 count = [keyPaths count]; 00923 00924 for (; index < count; ++index) 00925 [_toolbarItem 00926 addObserver:self 00927 forKeyPath:keyPaths[index] 00928 options:0 00929 context:NULL]; 00930 } 00931 00932 return self; 00933 } 00934 00935 - (void)FIXME_setIsHUD:(BOOL)shouldBeHUD 00936 { 00937 _FIXME_isHUD = shouldBeHUD; 00938 [_labelField setTextColor:[self FIXME_labelColor]]; 00939 [_labelField setTextShadowColor:[self FIXME_labelShadowColor]]; 00940 } 00941 00942 - (void)updateFromItem 00943 { 00944 var identifier = [_toolbarItem itemIdentifier]; 00945 00946 if (identifier === CPToolbarSpaceItemIdentifier || 00947 identifier === CPToolbarFlexibleSpaceItemIdentifier || 00948 identifier === CPToolbarSeparatorItemIdentifier) 00949 { 00950 [_view removeFromSuperview]; 00951 [_imageView removeFromSuperview]; 00952 00953 _minSize = [_toolbarItem minSize]; 00954 _maxSize = [_toolbarItem maxSize]; 00955 00956 if (identifier === CPToolbarSeparatorItemIdentifier) 00957 { 00958 _view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 2.0, 32.0)]; 00959 00960 // FIXME: Get rid of this old API!!! 00961 sizes = {}; 00962 sizes[@"CPToolbarItemSeparator"] = [CGSizeMake(2.0, 26.0), CGSizeMake(2.0, 1.0), CGSizeMake(2.0, 26.0)]; 00963 [_view setBackgroundColor:_CPControlThreePartImagePattern(YES, sizes, @"CPToolbarItem", @"Separator")]; 00964 00965 [self addSubview:_view]; 00966 } 00967 00968 return; 00969 } 00970 00971 [self setTarget:[_toolbarItem target]]; 00972 [self setAction:[_toolbarItem action]]; 00973 00974 var view = [_toolbarItem view] || nil; 00975 00976 if (view !== _view) 00977 { 00978 if (!view) 00979 [_view removeFromSuperview]; 00980 00981 else 00982 { 00983 [self addSubview:view]; 00984 [_imageView removeFromSuperview]; 00985 } 00986 00987 _view = view; 00988 } 00989 00990 if (!_view) 00991 { 00992 if (!_imageView) 00993 { 00994 _imageView = [[CPImageView alloc] initWithFrame:[self bounds]]; 00995 00996 [_imageView setImageScaling:CPScaleProportionally]; 00997 00998 [self addSubview:_imageView]; 00999 } 01000 01001 [_imageView setImage:[_toolbarItem image]]; 01002 } 01003 01004 var minSize = [_toolbarItem minSize], 01005 maxSize = [_toolbarItem maxSize]; 01006 01007 [_labelField setStringValue:[_toolbarItem label]]; 01008 [_labelField sizeToFit]; // FIXME 01009 01010 [self setEnabled:[_toolbarItem isEnabled]]; 01011 01012 _labelSize = [_labelField frame].size; 01013 01014 _minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN); 01015 _maxSize = CGSizeMake(MAX(_labelSize.width, maxSize.width), 100000000.0); 01016 01017 [_toolbar tile]; 01018 } 01019 01020 - (void)layoutSubviews 01021 { 01022 var identifier = [_toolbarItem itemIdentifier]; 01023 01024 if (identifier === CPToolbarSpaceItemIdentifier || 01025 identifier === CPToolbarFlexibleSpaceItemIdentifier) 01026 return; 01027 01028 var bounds = [self bounds], 01029 width = _CGRectGetWidth(bounds); 01030 01031 if (identifier === CPToolbarSeparatorItemIdentifier) 01032 return [_view setFrame:CGRectMake(ROUND((width - 2.0) / 2.0), 0.0, 2.0, _CGRectGetHeight(bounds))]; 01033 01034 var view = _view || _imageView, 01035 itemMaxSize = [_toolbarItem maxSize], 01036 height = _CGRectGetHeight(bounds) - _labelSize.height - LABEL_MARGIN - TOP_MARGIN, 01037 viewWidth = MIN(itemMaxSize.width, width), 01038 viewHeight = MIN(itemMaxSize.height, height); 01039 01040 [view setFrame:CGRectMake( ROUND((width - viewWidth) / 2.0), 01041 TOP_MARGIN + ROUND((height - viewHeight) / 2.0), 01042 viewWidth, 01043 viewHeight)]; 01044 01045 [_labelField setFrameOrigin:CGPointMake(ROUND((width - _labelSize.width) / 2.0), TOP_MARGIN + height + LABEL_MARGIN)]; 01046 } 01047 01048 - (void)mouseDown:(CPEvent)anEvent 01049 { 01050 if ([_toolbarItem view]) 01051 return [[self nextResponder] mouseDown:anEvent]; 01052 01053 var identifier = [_toolbarItem itemIdentifier]; 01054 01055 if (identifier === CPToolbarSpaceItemIdentifier || 01056 identifier === CPToolbarFlexibleSpaceItemIdentifier || 01057 identifier === CPToolbarSeparatorItemIdentifier) 01058 return [[self nextResponder] mouseDown:anEvent]; 01059 01060 [super mouseDown:anEvent]; 01061 } 01062 01063 - (void)setEnabled:(BOOL)shouldBeEnabled 01064 { 01065 [super setEnabled:shouldBeEnabled]; 01066 01067 if (shouldBeEnabled) 01068 { 01069 [_imageView setAlphaValue:1.0]; 01070 [_labelField setAlphaValue:1.0]; 01071 } 01072 else 01073 { 01074 [_imageView setAlphaValue:0.5]; 01075 [_labelField setAlphaValue:0.5]; 01076 } 01077 01078 [_toolbar tile]; 01079 } 01080 01081 - (CPColor)FIXME_labelColor 01082 { 01083 if (_FIXME_isHUD) 01084 return [CPColor whiteColor]; 01085 01086 return [CPColor blackColor]; 01087 } 01088 01089 - (CPColor)FIXME_labelShadowColor 01090 { 01091 if (_FIXME_isHUD) 01092 return [self isHighlighted] ? [CPColor colorWithWhite:1.0 alpha:0.5] : [CPColor clearColor]; 01093 01094 return [self isHighlighted] ? [CPColor colorWithWhite:0.0 alpha:0.3] : [CPColor colorWithWhite:1.0 alpha:0.75]; 01095 } 01096 01097 - (void)setHighlighted:(BOOL)shouldBeHighlighted 01098 { 01099 [super setHighlighted:shouldBeHighlighted]; 01100 01101 if (shouldBeHighlighted) 01102 { 01103 var alternateImage = [_toolbarItem alternateImage]; 01104 01105 if (alternateImage) 01106 [_imageView setImage:alternateImage]; 01107 01108 [_labelField setTextShadowOffset:CGSizeMakeZero()]; 01109 } 01110 else 01111 { 01112 var image = [_toolbarItem image]; 01113 01114 if (image) 01115 [_imageView setImage:image]; 01116 01117 [_labelField setTextShadowOffset:CGSizeMake(0.0, 1.0)]; 01118 } 01119 01120 [_labelField setTextShadowColor:[self FIXME_labelShadowColor]]; 01121 } 01122 01123 - (void)sendAction:(SEL)anAction to:(id)aSender 01124 { 01125 [CPApp sendAction:anAction to:aSender from:_toolbarItem]; 01126 } 01127 01128 - (void)observeValueForKeyPath:(CPString)aKeyPath 01129 ofObject:(id)anObject 01130 change:(CPDictionary)aChange 01131 context:(id)aContext 01132 { 01133 if (aKeyPath === "enabled") 01134 [self setEnabled:[anObject isEnabled]]; 01135 01136 else if (aKeyPath === @"target") 01137 [self setTarget:[anObject target]]; 01138 01139 else if (aKeyPath === @"action") 01140 [self setAction:[anObject action]]; 01141 01142 else 01143 [self updateFromItem]; 01144 } 01145 01146 @end