API 0.9.5
AppKit/CPCollectionView.j
Go to the documentation of this file.
00001 /*
00002  * CPCollectionView.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 
00059 @implementation CPCollectionView : CPView
00060 {
00061     CPArray                 _content;
00062     CPArray                 _items;
00063 
00064     CPData                  _itemData;
00065     CPCollectionViewItem    _itemPrototype;
00066     CPCollectionViewItem    _itemForDragging;
00067     CPMutableArray          _cachedItems;
00068 
00069     unsigned                _maxNumberOfRows;
00070     unsigned                _maxNumberOfColumns;
00071 
00072     CGSize                  _minItemSize;
00073     CGSize                  _maxItemSize;
00074 
00075     CPArray                 _backgroundColors;
00076 
00077     float                   _tileWidth;
00078 
00079     BOOL                    _isSelectable;
00080     BOOL                    _allowsMultipleSelection;
00081     BOOL                    _allowsEmptySelection;
00082     CPIndexSet              _selectionIndexes;
00083 
00084     CGSize                  _itemSize;
00085 
00086     float                   _horizontalMargin;
00087     float                   _verticalMargin;
00088 
00089     unsigned                _numberOfRows;
00090     unsigned                _numberOfColumns;
00091 
00092     id                      _delegate;
00093 
00094     CPEvent                 _mouseDownEvent;
00095 }
00096 
00097 - (id)initWithFrame:(CGRect)aFrame
00098 {
00099     self = [super initWithFrame:aFrame];
00100 
00101     if (self)
00102     {
00103         _items = [];
00104         _content = [];
00105 
00106         _cachedItems = [];
00107 
00108         _itemSize = CGSizeMakeZero();
00109         _minItemSize = CGSizeMakeZero();
00110         _maxItemSize = CGSizeMakeZero();
00111 
00112         [self setBackgroundColors:nil];
00113 
00114         _verticalMargin = 5.0;
00115         _tileWidth = -1.0;
00116 
00117         _selectionIndexes = [CPIndexSet indexSet];
00118         _allowsEmptySelection = YES;
00119         _isSelectable = YES;
00120     }
00121 
00122     return self;
00123 }
00124 
00172 - (void)setItemPrototype:(CPCollectionViewItem)anItem
00173 {
00174     _cachedItems = [];
00175     _itemData = nil;
00176     _itemForDragging = nil;
00177     _itemPrototype = anItem;
00178 
00179     [self reloadContent];
00180 }
00181 
00185 - (CPCollectionViewItem)itemPrototype
00186 {
00187     return _itemPrototype;
00188 }
00189 
00194 - (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
00195 {
00196     var item = nil;
00197 
00198     if (_cachedItems.length)
00199         item = _cachedItems.pop();
00200 
00201     else
00202     {
00203         if (!_itemData)
00204             if (_itemPrototype)
00205                 _itemData = [CPKeyedArchiver archivedDataWithRootObject:_itemPrototype];
00206 
00207         item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
00208     }
00209 
00210     [item setRepresentedObject:anObject];
00211     [[item view] setFrameSize:_itemSize];
00212 
00213     return item;
00214 }
00215 
00216 // Working with the Responder Chain
00220 - (BOOL)acceptsFirstResponder
00221 {
00222     return YES;
00223 }
00224 
00228 - (BOOL)isFirstResponder
00229 {
00230     return [[self window] firstResponder] === self;
00231 }
00232 
00233 // Setting the Content
00240 - (void)setContent:(CPArray)anArray
00241 {
00242     // reset the _selectionIndexes
00243     [self setSelectionIndexes:[CPIndexSet indexSet]];
00244 
00245     _content = anArray;
00246 
00247     [self reloadContent];
00248 }
00249 
00253 - (CPArray)content
00254 {
00255     return _content;
00256 }
00257 
00261 - (CPArray)items
00262 {
00263     return _items;
00264 }
00265 
00266 // Setting the Selection Mode
00271 - (void)setSelectable:(BOOL)isSelectable
00272 {
00273     if (_isSelectable == isSelectable)
00274         return;
00275 
00276     _isSelectable = isSelectable;
00277 
00278     if (!_isSelectable)
00279     {
00280         var index = CPNotFound;
00281 
00282         while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
00283             [_items[index] setSelected:NO];
00284     }
00285 }
00286 
00291 - (BOOL)isSelectable
00292 {
00293     return _isSelectable;
00294 }
00295 
00300 - (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
00301 {
00302     _allowsEmptySelection = shouldAllowEmptySelection;
00303 }
00304 
00308 - (BOOL)allowsEmptySelection
00309 {
00310     return _allowsEmptySelection;
00311 }
00312 
00317 - (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
00318 {
00319     _allowsMultipleSelection = shouldAllowMultipleSelection;
00320 }
00321 
00325 - (BOOL)allowsMultipleSelection
00326 {
00327     return _allowsMultipleSelection;
00328 }
00329 
00334 - (void)setSelectionIndexes:(CPIndexSet)anIndexSet
00335 {
00336     if ([_selectionIndexes isEqual:anIndexSet] || !_isSelectable)
00337         return;
00338 
00339     var index = CPNotFound;
00340 
00341     while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
00342         [_items[index] setSelected:NO];
00343 
00344     _selectionIndexes = anIndexSet;
00345 
00346     var index = CPNotFound;
00347 
00348     while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
00349         [_items[index] setSelected:YES];
00350 
00351     var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
00352     [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
00353 
00354     if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
00355         [_delegate collectionViewDidChangeSelection:self];
00356 }
00357 
00361 - (CPIndexSet)selectionIndexes
00362 {
00363     return [_selectionIndexes copy];
00364 }
00365 
00366 /* @ignore */
00367 - (void)reloadContent
00368 {
00369     // Remove current views
00370     var count = _items.length;
00371 
00372     while (count--)
00373     {
00374         [[_items[count] view] removeFromSuperview];
00375         [_items[count] setSelected:NO];
00376 
00377         _cachedItems.push(_items[count]);
00378     }
00379 
00380     _items = [];
00381 
00382     if (!_itemPrototype)
00383         return;
00384 
00385     var index = 0;
00386 
00387     count = _content.length;
00388 
00389     for (; index < count; ++index)
00390     {
00391         _items.push([self newItemForRepresentedObject:_content[index]]);
00392 
00393         [self addSubview:[_items[index] view]];
00394     }
00395 
00396     index = CPNotFound;
00397     while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
00398         [_items[index] setSelected:YES];
00399 
00400     [self tile];
00401 }
00402 
00403 /* @ignore */
00404 - (void)tile
00405 {
00406     var width = CGRectGetWidth([self bounds]);
00407 
00408     if (width == _tileWidth)
00409         return;
00410 
00411     // We try to fit as many views per row as possible.  Any remaining space is then
00412     // either proportioned out to the views (if their minSize != maxSize) or used as
00413     // margin
00414     var itemSize = CGSizeMakeCopy(_minItemSize);
00415 
00416     _numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
00417 
00418     if (_maxNumberOfColumns > 0)
00419         _numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
00420 
00421     var remaining = width - _numberOfColumns * itemSize.width,
00422         itemsNeedSizeUpdate = NO;
00423 
00424     if (remaining > 0 && itemSize.width < _maxItemSize.width)
00425         itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
00426 
00427     // When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
00428     if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
00429         itemSize.width = MIN(_maxItemSize.width, width);
00430 
00431     if (!CGSizeEqualToSize(_itemSize, itemSize))
00432     {
00433         _itemSize = itemSize;
00434         itemsNeedSizeUpdate = YES;
00435     }
00436 
00437     var index = 0,
00438         count = _items.length;
00439 
00440     if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
00441         count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
00442 
00443     _numberOfRows = CEIL(count / _numberOfColumns);
00444 
00445     _horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
00446 
00447     var x = _horizontalMargin,
00448         y = -itemSize.height;
00449 
00450     for (; index < count; ++index)
00451     {
00452         if (index % _numberOfColumns == 0)
00453         {
00454             x = _horizontalMargin;
00455             y += _verticalMargin + itemSize.height;
00456         }
00457 
00458         var view = [_items[index] view];
00459 
00460         [view setFrameOrigin:CGPointMake(x, y)];
00461 
00462         if (itemsNeedSizeUpdate)
00463             [view setFrameSize:_itemSize];
00464 
00465         x += itemSize.width + _horizontalMargin;
00466     }
00467 
00468     var superview = [self superview],
00469         proposedHeight =  y + itemSize.height + _verticalMargin;
00470 
00471     if ([superview isKindOfClass:[CPClipView class]])
00472     {
00473         var superviewSize = [superview bounds].size;
00474         proposedHeight = MAX(superviewSize.height, proposedHeight);
00475     }
00476 
00477     _tileWidth = width;
00478     [self setFrameSize:CGSizeMake(width, proposedHeight)];
00479     _tileWidth = -1.0;
00480 }
00481 
00482 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
00483 {
00484     [self tile];
00485 }
00486 
00487 // Laying Out the Collection View
00492 - (void)setMaxNumberOfRows:(unsigned)aMaxNumberOfRows
00493 {
00494     if (_maxNumberOfRows == aMaxNumberOfRows)
00495         return;
00496 
00497     _maxNumberOfRows = aMaxNumberOfRows;
00498 
00499     [self tile];
00500 }
00501 
00505 - (unsigned)maxNumberOfRows
00506 {
00507     return _maxNumberOfRows;
00508 }
00509 
00514 - (void)setMaxNumberOfColumns:(unsigned)aMaxNumberOfColumns
00515 {
00516     if (_maxNumberOfColumns == aMaxNumberOfColumns)
00517         return;
00518 
00519     _maxNumberOfColumns = aMaxNumberOfColumns;
00520 
00521     [self tile];
00522 }
00523 
00527 - (unsigned)maxNumberOfColumns
00528 {
00529     return _maxNumberOfColumns;
00530 }
00531 
00535 - (unsigned)numberOfRows
00536 {
00537     return _numberOfRows;
00538 }
00539 
00544 - (unsigned)numberOfColumns
00545 {
00546     return _numberOfColumns;
00547 }
00548 
00553 - (void)setMinItemSize:(CGSize)aSize
00554 {
00555     if (CGSizeEqualToSize(_minItemSize, aSize))
00556         return;
00557 
00558     _minItemSize = CGSizeMakeCopy(aSize);
00559 
00560     [self tile];
00561 }
00562 
00566 - (CGSize)minItemSize
00567 {
00568     return _minItemSize;
00569 }
00570 
00575 - (void)setMaxItemSize:(CGSize)aSize
00576 {
00577     if (CGSizeEqualToSize(_maxItemSize, aSize))
00578         return;
00579 
00580     _maxItemSize = CGSizeMakeCopy(aSize);
00581 
00582     [self tile];
00583 }
00584 
00588 - (CGSize)maxItemSize
00589 {
00590     return _maxItemSize;
00591 }
00592 
00593 - (void)setBackgroundColors:(CPArray)backgroundColors
00594 {
00595     if (_backgroundColors === backgroundColors)
00596         return;
00597 
00598     _backgroundColors = backgroundColors;
00599 
00600     if (!_backgroundColors)
00601         _backgroundColors = [CPColor whiteColor];
00602 
00603     if ([_backgroundColors count] === 1)
00604         [self setBackgroundColor:_backgroundColors[0]];
00605 
00606     else
00607         [self setBackgroundColor:nil];
00608 
00609     [self setNeedsDisplay:YES];
00610 }
00611 
00612 - (CPArray)backgroundColors
00613 {
00614     return _backgroundColors;
00615 }
00616 
00617 - (void)mouseUp:(CPEvent)anEvent
00618 {
00619     if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
00620         [_delegate collectionView:self didDoubleClickOnItemAtIndex:[_selectionIndexes firstIndex]];
00621 }
00622 
00623 - (void)mouseDown:(CPEvent)anEvent
00624 {
00625     _mouseDownEvent = anEvent;
00626 
00627     var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
00628         index = [self _indexAtPoint:location];
00629 
00630     if (index >= 0 && index < _items.length)
00631     {
00632         if (_allowsMultipleSelection && ([anEvent modifierFlags] & CPCommandKeyMask || [anEvent modifierFlags] & CPShiftKeyMask))
00633         {
00634             if ([anEvent modifierFlags] & CPCommandKeyMask)
00635             {
00636                 var indexes = [_selectionIndexes copy];
00637 
00638                 if ([indexes containsIndex:index])
00639                     [indexes removeIndex:index];
00640                 else
00641                     [indexes addIndex:index];
00642             }
00643             else if ([anEvent modifierFlags] & CPShiftKeyMask)
00644             {
00645                 var firstSelectedIndex = [[self selectionIndexes] firstIndex],
00646                     newSelectedRange = nil;
00647 
00648                 if (index < firstSelectedIndex)
00649                     newSelectedRange = CPMakeRange(index, (firstSelectedIndex - index) + 1);
00650                 else
00651                     newSelectedRange = CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
00652 
00653                 indexes = [[self selectionIndexes] copy];
00654                 [indexes addIndexesInRange:newSelectedRange];
00655             }
00656         }
00657         else
00658             indexes = [CPIndexSet indexSetWithIndex:index];
00659 
00660         [self setSelectionIndexes:indexes];
00661     }
00662     else if (_allowsEmptySelection)
00663         [self setSelectionIndexes:[CPIndexSet indexSet]];
00664 }
00665 
00666 - (void)mouseDragged:(CPEvent)anEvent
00667 {
00668     var locationInWindow = [anEvent locationInWindow],
00669         mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
00670 
00671     // FIXME: This is because Safari's drag hysteresis is 3px x 3px
00672     if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
00673         (ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
00674         return;
00675 
00676     if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
00677         return;
00678 
00679     // If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
00680     if (![_selectionIndexes count])
00681         return;
00682 
00683     if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)] &&
00684         ![_delegate collectionView:self canDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
00685         return;
00686 
00687     // Set up the pasteboard
00688     var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
00689 
00690     [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
00691 
00692     if (!_itemForDragging)
00693         _itemForDragging = [self newItemForRepresentedObject:_content[[_selectionIndexes firstIndex]]];
00694     else
00695         [_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
00696 
00697     var view = [_itemForDragging view];
00698 
00699     [view setFrameSize:_itemSize];
00700     [view setAlphaValue:0.7];
00701 
00702     [self dragView:view
00703         at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
00704         offset:CGSizeMakeZero()
00705         event:_mouseDownEvent
00706         pasteboard:nil
00707         source:self
00708         slideBack:YES];
00709 }
00710 
00716 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
00717 {
00718     [aPasteboard setData:[_delegate collectionView:self dataForItemsAtIndexes:_selectionIndexes forType:aType] forType:aType];
00719 }
00720 
00721 // Cappuccino Additions
00722 
00728 - (void)setVerticalMargin:(float)aVerticalMargin
00729 {
00730     if (_verticalMargin == aVerticalMargin)
00731         return;
00732 
00733     _verticalMargin = aVerticalMargin;
00734 
00735     [self tile];
00736 }
00737 
00742 - (float)verticalMargin
00743 {
00744     return _verticalMargin;
00745 }
00746 
00751 - (void)setDelegate:(id)aDelegate
00752 {
00753     _delegate = aDelegate;
00754 }
00755 
00759 - (id)delegate
00760 {
00761     return _delegate;
00762 }
00763 
00767 - (CPMenu)menuForEvent:(CPEvent)theEvent
00768 {
00769     if (![[self delegate] respondsToSelector:@selector(collectionView:menuForItemAtIndex:)])
00770         return [super menuForEvent:theEvent];
00771 
00772     var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
00773         index = [self _indexAtPoint:location];
00774 
00775     return [_delegate collectionView:self menuForItemAtIndex:index];
00776 }
00777 
00778 - (int)_indexAtPoint:(CGPoint)thePoint
00779 {
00780     var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin)),
00781         column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
00782 
00783     return row * _numberOfColumns + column;
00784 }
00785 
00786 - (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
00787 {
00788     return [_items objectAtIndex:anIndex];
00789 }
00790 
00791 - (CGRect)frameForItemAtIndex:(unsigned)anIndex
00792 {
00793     return [[[self itemAtIndex:anIndex] view] frame];
00794 }
00795 
00796 - (CGRect)frameForItemsAtIndexes:(CPIndexSet)anIndexSet
00797 {
00798     var indexArray = [],
00799         frame = CGRectNull;
00800 
00801     [anIndexSet getIndexes:indexArray maxCount:-1 inIndexRange:nil];
00802 
00803     var index = 0,
00804         count = [indexArray count];
00805 
00806     for (; index < count; ++index)
00807         frame = CGRectUnion(frame, [self frameForItemAtIndex:indexArray[index]]);
00808 
00809     return frame;
00810 }
00811 
00812 @end
00813 
00814 @implementation CPCollectionView (KeyboardInteraction)
00815 
00816 - (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
00817 {
00818     anIndex = MIN(MAX(anIndex, 0), [[self items] count] - 1);
00819 
00820     if (_allowsMultipleSelection && shouldExpand)
00821     {
00822         var indexes = [_selectionIndexes copy],
00823             bottomAnchor = [indexes firstIndex],
00824             topAnchor = [indexes lastIndex];
00825 
00826         // if the direction is backward (-1) check with the bottom anchor
00827         if (aDirection === -1)
00828             [indexes addIndexesInRange:CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
00829         else
00830             [indexes addIndexesInRange:CPMakeRange(topAnchor, anIndex -  topAnchor + 1)];
00831     }
00832     else
00833         indexes = [CPIndexSet indexSetWithIndex:anIndex];
00834 
00835     [self setSelectionIndexes:indexes];
00836     [self _scrollToSelection];
00837 }
00838 
00839 - (void)_scrollToSelection
00840 {
00841     var frame = [self frameForItemsAtIndexes:[self selectionIndexes]];
00842 
00843     if (!CGRectIsNull(frame))
00844         [self scrollRectToVisible:frame];
00845 }
00846 
00847 - (void)moveLeft:(id)sender
00848 {
00849     var index = [[self selectionIndexes] firstIndex];
00850     if (index === CPNotFound)
00851         index = [[self items] count];
00852 
00853     [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO];
00854 }
00855 
00856 - (void)moveLeftAndModifySelection:(id)sender
00857 {
00858     var index = [[self selectionIndexes] firstIndex];
00859     if (index === CPNotFound)
00860         index = [[self items] count];
00861 
00862     [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES];
00863 }
00864 
00865 - (void)moveRight:(id)sender
00866 {
00867     [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO];
00868 }
00869 
00870 - (void)moveRightAndModifySelection:(id)sender
00871 {
00872     [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES];
00873 }
00874 
00875 - (void)moveDown:(id)sender
00876 {
00877     [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO];
00878 }
00879 
00880 - (void)moveDownAndModifySelection:(id)sender
00881 {
00882     [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES];
00883 }
00884 
00885 - (void)moveUp:(id)sender
00886 {
00887     var index = [[self selectionIndexes] firstIndex];
00888     if (index == CPNotFound)
00889         index = [[self items] count];
00890 
00891     [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO];
00892 }
00893 
00894 - (void)moveUpAndModifySelection:(id)sender
00895 {
00896     var index = [[self selectionIndexes] firstIndex];
00897     if (index == CPNotFound)
00898         index = [[self items] count];
00899 
00900     [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES];
00901 }
00902 
00903 - (void)deleteBackward:(id)sender
00904 {
00905     if ([[self delegate] respondsToSelector:@selector(collectionView:shouldDeleteItemsAtIndexes:)])
00906     {
00907         [[self delegate] collectionView:self shouldDeleteItemsAtIndexes:[self selectionIndexes]];
00908 
00909         var index = [[self selectionIndexes] firstIndex];
00910         if (index > [[self content] count] - 1)
00911             [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:[[self content] count] - 1]];
00912 
00913         [self _scrollToSelection];
00914         [self setNeedsDisplay:YES];
00915     }
00916 }
00917 
00918 - (void)keyDown:(CPEvent)anEvent
00919 {
00920     [self interpretKeyEvents:[anEvent]];
00921 }
00922 
00923 @end
00924 
00925 @implementation CPCollectionView (Deprecated)
00926 
00927 - (CGRect)rectForItemAtIndex:(int)anIndex
00928 {
00929     _CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemAtIndex:));
00930 
00931     // Don't re-compute anything just grab the current frame
00932     // This allows subclasses to override tile without messing this up.
00933     return [self frameForItemAtIndex:anIndex];
00934 }
00935 
00936 - (CGRect)rectForItemsAtIndexes:(CPIndexSet)anIndexSet
00937 {
00938     _CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemsAtIndexes:));
00939 
00940     return [self frameForItemsAtIndexes:anIndexSet];
00941 }
00942 
00943 @end
00944 
00945 var CPCollectionViewMinItemSizeKey              = @"CPCollectionViewMinItemSizeKey",
00946     CPCollectionViewMaxItemSizeKey              = @"CPCollectionViewMaxItemSizeKey",
00947     CPCollectionViewVerticalMarginKey           = @"CPCollectionViewVerticalMarginKey",
00948     CPCollectionViewMaxNumberOfRowsKey          = @"CPCollectionViewMaxNumberOfRowsKey",
00949     CPCollectionViewMaxNumberOfColumnsKey       = @"CPCollectionViewMaxNumberOfColumnsKey",
00950     CPCollectionViewSelectableKey               = @"CPCollectionViewSelectableKey",
00951     CPCollectionViewAllowsMultipleSelectionKey  = @"CPCollectionViewAllowsMultipleSelectionKey",
00952     CPCollectionViewBackgroundColorsKey         = @"CPCollectionViewBackgroundColorsKey";
00953 
00954 
00955 @implementation CPCollectionView (CPCoding)
00956 
00957 - (void)awakeFromCib
00958 {
00959     [super awakeFromCib];
00960 
00961     if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()) || CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
00962     {
00963         var item = _itemPrototype;
00964 
00965         if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
00966             _minItemSize = [[item view] frameSize];
00967         else if (CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
00968             _maxItemSize = [[item view] frameSize];
00969     }
00970 }
00971 
00972 - (id)initWithCoder:(CPCoder)aCoder
00973 {
00974     self = [super initWithCoder:aCoder];
00975 
00976     if (self)
00977     {
00978         _items = [];
00979         _content = [];
00980 
00981         _cachedItems = [];
00982 
00983         _itemSize = CGSizeMakeZero();
00984 
00985         _minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
00986         _maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
00987 
00988         _maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey] || 0;
00989         _maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey] || 0;
00990 
00991         _verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
00992 
00993         _isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
00994         _allowsMultipleSelection = [aCoder decodeBoolForKey:CPCollectionViewAllowsMultipleSelectionKey];
00995 
00996         [self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
00997 
00998         _tileWidth = -1.0;
00999 
01000         _selectionIndexes = [CPIndexSet indexSet];
01001 
01002         _allowsEmptySelection = YES;
01003     }
01004 
01005     return self;
01006 }
01007 
01008 - (void)encodeWithCoder:(CPCoder)aCoder
01009 {
01010     [super encodeWithCoder:aCoder];
01011 
01012     if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
01013       [aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
01014 
01015     if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
01016       [aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
01017 
01018     [aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
01019     [aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey];
01020 
01021     [aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
01022     [aCoder encodeBool:_allowsMultipleSelection forKey:CPCollectionViewAllowsMultipleSelectionKey];
01023 
01024     [aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
01025 
01026     [aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
01027 }
01028 
01029 @end
 All Classes Files Functions Variables Defines