API 0.9.5
AppKit/CPToolbar.j
Go to the documentation of this file.
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
 All Classes Files Functions Variables Defines