API  1.0.0
CPCollectionView.j
Go to the documentation of this file.
1 /*
2  * CPCollectionView.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
35 
36 
37 @protocol CPCollectionViewDelegate <CPObject>
38 
39 @optional
40 - (BOOL)collectionView:(CPCollectionView)collectionView acceptDrop:(id)draggingInfo index:(CPInteger)index dropOperation:(CPCollectionViewDropOperation)dropOperation;
41 - (BOOL)collectionView:(CPCollectionView)collectionView canDragItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event;
42 - (BOOL)collectionView:(CPCollectionView)collectionView writeItemsAtIndexes:(CPIndexSet)indexes toPasteboard:(CPPasteboard)pasteboard;
43 - (CPArray)collectionView:(CPCollectionView)collectionView dragTypesForItemsAtIndexes:(CPIndexSet)indexes;
44 - (CPData)collectionView:(CPCollectionView)collectionView dataForItemsAtIndexes:(CPIndexSet)indices forType:(CPString)aType;
45 - (CPDragOperation)collectionView:(CPCollectionView)collectionView validateDrop:(id)draggingInfo proposedIndex:(CPInteger)proposedDropIndex dropOperation:(CPCollectionViewDropOperation)proposedDropOperation;
46 - (CPMenu)collectionView:(CPCollectionView)collectionView menuForItemAtIndex:(CPInteger)anIndex;
47 - (CPView)collectionView:(CPCollectionView)collectionView draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
48 - (void)collectionView:(CPCollectionView)collectionView didDoubleClickOnItemAtIndex:(int)index;
49 
50 @end
51 
53 
81 @implementation CPCollectionView : CPView
82 {
83  CPArray _content;
84  CPArray _items;
85 
86  CPData _itemData;
87  CPCollectionViewItem _itemPrototype;
88  CPCollectionViewItem _itemForDragging;
89  CPMutableArray _cachedItems;
90 
91  unsigned _maxNumberOfRows;
92  unsigned _maxNumberOfColumns;
93 
94  CGSize _minItemSize;
95  CGSize _maxItemSize;
96 
97  CPArray _backgroundColors;
98 
99  float _tileWidth;
100 
101  BOOL _isSelectable;
102  BOOL _allowsMultipleSelection;
103  BOOL _allowsEmptySelection;
104  CPIndexSet _selectionIndexes;
105 
106  CGSize _itemSize;
107 
108  float _horizontalMargin;
109  float _verticalMargin;
110 
111  unsigned _numberOfRows;
112  unsigned _numberOfColumns;
113 
114  id <CPCollectionViewDelegate> _delegate;
115  unsigned _implementedDelegateMethods;
116 
117  CPEvent _mouseDownEvent;
118 
119  BOOL _needsMinMaxItemSizeUpdate;
120  CGSize _storedFrameSize;
121 
122  BOOL _uniformSubviewsResizing;
123 
124  CPInteger _currentDropIndex;
125  CPDragOperation _currentDragOperation;
126 
127  _CPCollectionViewDropIndicator _dropView;
128 }
129 
130 + (Class)_binderClassForBinding:(CPString)aBinding
131 {
132  if (aBinding == CPContentBinding)
133  return [_CPCollectionViewContentBinder class];
134 
135  return [super _binderClassForBinding:aBinding];
136 }
137 
138 - (id)initWithFrame:(CGRect)aFrame
139 {
140  self = [super initWithFrame:aFrame];
141 
142  if (self)
143  {
144  _maxNumberOfRows = 0;
145  _maxNumberOfColumns = 0;
146 
147  _minItemSize = CGSizeMakeZero();
148  _maxItemSize = CGSizeMakeZero();
149 
150  [self setBackgroundColors:nil];
151 
152  _verticalMargin = 5.0;
153  _isSelectable = YES;
154  _allowsEmptySelection = YES;
155 
156  [self _init];
157  }
158 
159  return self;
160 }
161 
162 - (void)_init
163 {
164  _content = [];
165 
166  _items = [];
167  _cachedItems = [];
168 
169  _numberOfColumns = CPNotFound;
170  _numberOfRows = CPNotFound;
171 
172  _itemSize = CGSizeMakeZero();
173 
174  _selectionIndexes = [CPIndexSet indexSet];
175 
176  _storedFrameSize = CGSizeMakeZero();
177 
178  _needsMinMaxItemSizeUpdate = YES;
179  _uniformSubviewsResizing = NO;
180  _inLiveResize = NO;
181 
182  _currentDropIndex = -1;
183  _currentDragOperation = CPDragOperationNone;
184  _dropView = nil;
185 
186  [self setAutoresizesSubviews:NO];
187  [self setAutoresizingMask:0];
188 }
189 
190 
191 
192 #pragma mark -
193 #pragma mark Delegate
194 
199 - (void)setDelegate:(id <CPCollectionViewDelegate>)aDelegate
200 {
201  if (_delegate === aDelegate)
202  return;
203 
204  _delegate = aDelegate;
205  _implementedDelegateMethods = 0;
206 
207  if ([_delegate respondsToSelector:@selector(collectionView:acceptDrop:index:dropOperation:)])
209 
210  if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)])
212 
213  if ([_delegate respondsToSelector:@selector(collectionView:writeItemsAtIndexes:toPasteboard:)])
215 
216  if ([_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
218 
219  if ([_delegate respondsToSelector:@selector(collectionView:dataForItemsAtIndexes:forType:)])
221 
222  if ([_delegate respondsToSelector:@selector(collectionView:validateDrop:proposedIndex:dropOperation:)])
224 
225  if ([_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
227 
228  if ([_delegate respondsToSelector:@selector(collectionView:menuForItemAtIndex:)])
230 
231  if ([_delegate respondsToSelector:@selector(collectionView:draggingViewForItemsAtIndexes:withEvent:offset:)])
233 }
234 
279 - (void)setItemPrototype:(CPCollectionViewItem)anItem
280 {
281  _cachedItems = [];
282  _itemData = nil;
283  _itemForDragging = nil;
284  _itemPrototype = anItem;
285 
286  [self _reloadContentCachingRemovedItems:NO];
287 }
288 
292 - (CPCollectionViewItem)itemPrototype
293 {
294  return _itemPrototype;
295 }
296 
301 - (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
302 {
303  var item = nil;
304 
305  if (_cachedItems.length)
306  item = _cachedItems.pop();
307 
308  else
309  item = [_itemPrototype copy];
310 
311  [item setRepresentedObject:anObject];
312  [[item view] setFrameSize:_itemSize];
313 
314  return item;
315 }
316 
317 // Working with the Responder Chain
321 - (BOOL)acceptsFirstResponder
322 {
323  return YES;
324 }
325 
329 - (BOOL)isFirstResponder
330 {
331  return [[self window] firstResponder] === self;
332 }
333 
334 // Setting the Content
346 - (void)setContent:(CPArray)anArray
347 {
348  _content = anArray;
349 
350  [self reloadContent];
351 }
352 
356 - (CPArray)content
357 {
358  return _content;
359 }
360 
364 - (CPArray)items
365 {
366  return _items;
367 }
368 
369 // Setting the Selection Mode
374 - (void)setSelectable:(BOOL)isSelectable
375 {
376  if (_isSelectable == isSelectable)
377  return;
378 
379  _isSelectable = isSelectable;
380 
381  if (!_isSelectable)
382  [self _applySelectionToItems:NO];
383 }
384 
389 - (BOOL)isSelectable
390 {
391  return _isSelectable;
392 }
393 
398 - (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
399 {
400  _allowsEmptySelection = shouldAllowEmptySelection;
401 }
402 
406 - (BOOL)allowsEmptySelection
407 {
408  return _allowsEmptySelection;
409 }
410 
415 - (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
416 {
417  _allowsMultipleSelection = shouldAllowMultipleSelection;
418 }
419 
423 - (BOOL)allowsMultipleSelection
424 {
425  return _allowsMultipleSelection;
426 }
427 
432 - (void)setSelectionIndexes:(CPIndexSet)anIndexSet
433 {
434  if (!anIndexSet)
435  anIndexSet = [CPIndexSet indexSet];
436 
437  if (!_isSelectable || [_selectionIndexes isEqual:anIndexSet])
438  return;
439 
440  [self _applySelectionToItems:NO];
441 
442  _selectionIndexes = anIndexSet;
443 
444  [self _applySelectionToItems:YES];
445 
446  var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
447  [[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
448 }
449 
453 - (CPIndexSet)selectionIndexes
454 {
455  return [_selectionIndexes copy];
456 }
457 
458 - (void)reloadContent
459 {
460  [self _reloadContentCachingRemovedItems:YES];
461 }
462 
463 /* @ignore */
464 - (void)_reloadContentCachingRemovedItems:(BOOL)shouldCache
465 {
466  // Remove current views
467  var count = _items.length;
468 
469  while (count--)
470  {
471  [[_items[count] view] removeFromSuperview];
472  [_items[count] setSelected:NO];
473 
474  if (shouldCache)
475  _cachedItems.push(_items[count]);
476  }
477 
478  _items = [];
479 
480  if (!_itemPrototype)
481  return;
482 
483  var index = 0;
484 
485  count = _content.length;
486 
487  for (; index < count; ++index)
488  {
489  _items.push([self newItemForRepresentedObject:_content[index]]);
490 
491  [self addSubview:[_items[index] view]];
492  }
493 
494  index = CPNotFound;
495  // Be wary of invalid selection ranges since setContent: does not clear selection indexes.
496  while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound && index < count)
497  [_items[index] setSelected:YES];
498 
499  [self tileIfNeeded:NO];
500 }
501 
502 - (void)resizeSubviewsWithOldSize:(CGSize)oldBoundsSize
503 {
504  // Desactivate subviews autoresizing
505 }
506 
507 - (void)resizeWithOldSuperviewSize:(CGSize)oldBoundsSize
508 {
509  if (_inLiveResize)
510  return;
511 
512  _inLiveResize = YES;
513 
514  [self tile];
515 
516  _inLiveResize = NO;
517 }
518 
519 - (void)tile
520 {
521  [self tileIfNeeded:!_uniformSubviewsResizing];
522 }
523 
524 - (void)tileIfNeeded:(BOOL)lazyFlag
525 {
526  var frameSize = [[self superview] frameSize],
527  count = _items.length,
528  oldNumberOfColumns = _numberOfColumns,
529  oldNumberOfRows = _numberOfRows,
530  oldItemSize = _itemSize,
531  storedFrameSize = _storedFrameSize;
532 
533  // No need to tile if we are not yet placed in the view hierarchy.
534  if (!frameSize)
535  return;
536 
537  [self _updateMinMaxItemSizeIfNeeded];
538 
539  [self _computeGridWithSize:frameSize count:@ref(count)];
540 
541  //CPLog.debug("frameSize="+CPStringFromSize(frameSize) + "itemSize="+CPStringFromSize(itemSize) + " ncols=" + colsRowsCount[0] +" nrows="+ colsRowsCount[1]+" displayCount="+ colsRowsCount[2]);
542 
543  [self setFrameSize:_storedFrameSize];
544 
545  //CPLog.debug("OLD " + oldNumberOfColumns + " NEW " + _numberOfColumns);
546  if (!lazyFlag ||
547  _numberOfColumns !== oldNumberOfColumns ||
548  _numberOfRows !== oldNumberOfRows ||
549  !CGSizeEqualToSize(_itemSize, oldItemSize))
550 
551  [self displayItems:_items frameSize:_storedFrameSize itemSize:_itemSize columns:_numberOfColumns rows:_numberOfRows count:count];
552 }
553 
554 - (void)_computeGridWithSize:(CGSize)aSuperviewSize count:(Function)countRef
555 {
556  var width = aSuperviewSize.width,
557  height = aSuperviewSize.height,
558  itemSize = CGSizeMakeCopy(_minItemSize),
559  maxItemSizeWidth = _maxItemSize.width,
560  maxItemSizeHeight = _maxItemSize.height,
561  itemsCount = [_items count],
562  numberOfRows,
563  numberOfColumns;
564 
565  numberOfColumns = FLOOR(width / itemSize.width);
566 
567  if (maxItemSizeWidth == 0)
568  numberOfColumns = MIN(numberOfColumns, _maxNumberOfColumns);
569 
570  if (_maxNumberOfColumns > 0)
571  numberOfColumns = MIN(MIN(_maxNumberOfColumns, itemsCount), numberOfColumns);
572 
573  numberOfColumns = MAX(1.0, numberOfColumns);
574 
575  itemSize.width = FLOOR(width / numberOfColumns);
576 
577  if (maxItemSizeWidth > 0)
578  {
579  itemSize.width = MIN(maxItemSizeWidth, itemSize.width);
580 
581  if (numberOfColumns == 1)
582  itemSize.width = MIN(maxItemSizeWidth, width);
583  }
584 
585  numberOfRows = CEIL(itemsCount / numberOfColumns);
586 
587  if (_maxNumberOfRows > 0)
588  numberOfRows = MIN(numberOfRows, _maxNumberOfRows);
589 
590  height = MAX(height, numberOfRows * (_minItemSize.height + _verticalMargin));
591 
592  var itemSizeHeight = FLOOR(height / numberOfRows) - _verticalMargin;
593 
594  if (maxItemSizeHeight > 0)
595  itemSizeHeight = MIN(itemSizeHeight, maxItemSizeHeight);
596 
597  _itemSize = CGSizeMake(MAX(_minItemSize.width, itemSize.width), MAX(_minItemSize.height, itemSizeHeight));
598  _storedFrameSize = CGSizeMake(MAX(width, _minItemSize.width), height);
599  _numberOfColumns = numberOfColumns;
600  _numberOfRows = numberOfRows;
601  countRef(MIN(itemsCount, numberOfColumns * numberOfRows));
602 }
603 
604 - (void)displayItems:(CPArray)displayItems frameSize:(CGSize)aFrameSize itemSize:(CGSize)anItemSize columns:(CPInteger)numberOfColumns rows:(CPInteger)numberOfRows count:(CPInteger)displayCount
605 {
606 // CPLog.debug("DISPLAY ITEMS " + numberOfColumns + " " + numberOfRows);
607 
608  _horizontalMargin = _uniformSubviewsResizing ? FLOOR((aFrameSize.width - numberOfColumns * anItemSize.width) / (numberOfColumns + 1)) : HORIZONTAL_MARGIN;
609 
610  var x = _horizontalMargin,
611  y = -anItemSize.height;
612 
613  [displayItems enumerateObjectsUsingBlock:function(item, idx, stop)
614  {
615  var view = [item view];
616 
617  if (idx >= displayCount)
618  {
619  [view setFrameOrigin:CGPointMake(-anItemSize.width, -anItemSize.height)];
620  return;
621  }
622 
623  if (idx % numberOfColumns == 0)
624  {
625  x = _horizontalMargin;
626  y += _verticalMargin + anItemSize.height;
627  }
628 
629  [view setFrameOrigin:CGPointMake(x, y)];
630  [view setFrameSize:anItemSize];
631 
632  x += anItemSize.width + _horizontalMargin;
633  }];
634 }
635 
636 - (void)_updateMinMaxItemSizeIfNeeded
637 {
638  if (!_needsMinMaxItemSizeUpdate)
639  return;
640 
641  var prototypeView;
642 
643  if (_itemPrototype && (prototypeView = [_itemPrototype view]))
644  {
645  if (_minItemSize.width == 0)
646  _minItemSize.width = [prototypeView frameSize].width;
647 
648  if (_minItemSize.height == 0)
649  _minItemSize.height = [prototypeView frameSize].height;
650 
651  if (_maxItemSize.height == 0 && !([prototypeView autoresizingMask] & CPViewHeightSizable))
652  _maxItemSize.height = [prototypeView frameSize].height;
653 
654  if (_maxItemSize.width == 0 && !([prototypeView autoresizingMask] & CPViewWidthSizable))
655  _maxItemSize.width = [prototypeView frameSize].width;
656 
657  _needsMinMaxItemSizeUpdate = NO;
658  }
659 }
660 
661 // Laying Out the Collection View
666 - (void)setMaxNumberOfRows:(unsigned)aMaxNumberOfRows
667 {
668  if (_maxNumberOfRows == aMaxNumberOfRows)
669  return;
670 
671  _maxNumberOfRows = aMaxNumberOfRows;
672 
673  [self tile];
674 }
675 
679 - (unsigned)maxNumberOfRows
680 {
681  return _maxNumberOfRows;
682 }
683 
688 - (void)setMaxNumberOfColumns:(unsigned)aMaxNumberOfColumns
689 {
690  if (_maxNumberOfColumns == aMaxNumberOfColumns)
691  return;
692 
693  _maxNumberOfColumns = aMaxNumberOfColumns;
694 
695  [self tile];
696 }
697 
701 - (unsigned)maxNumberOfColumns
702 {
703  return _maxNumberOfColumns;
704 }
705 
709 - (unsigned)numberOfRows
710 {
711  return _numberOfRows;
712 }
713 
718 - (unsigned)numberOfColumns
719 {
720  return _numberOfColumns;
721 }
722 
727 - (void)setMinItemSize:(CGSize)aSize
728 {
729  if (aSize === nil || aSize === undefined)
730  [CPException raise:CPInvalidArgumentException reason:"Invalid value provided for minimum size"];
731 
732  if (CGSizeEqualToSize(_minItemSize, aSize))
733  return;
734 
735  _minItemSize = CGSizeMakeCopy(aSize);
736 
737  if (CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
738  _needsMinMaxItemSizeUpdate = YES;
739 
740  [self tile];
741 }
742 
746 - (CGSize)minItemSize
747 {
748  return _minItemSize;
749 }
750 
755 - (void)setMaxItemSize:(CGSize)aSize
756 {
757  if (CGSizeEqualToSize(_maxItemSize, aSize))
758  return;
759 
760  _maxItemSize = CGSizeMakeCopy(aSize);
761 
762 // if (_maxItemSize.width == 0 || _maxItemSize.height == 0)
763 // _needsMinMaxItemSizeUpdate = YES;
764 
765  [self tile];
766 }
767 
771 - (CGSize)maxItemSize
772 {
773  return _maxItemSize;
774 }
775 
776 - (void)setBackgroundColors:(CPArray)backgroundColors
777 {
778  if (_backgroundColors === backgroundColors)
779  return;
780 
781  _backgroundColors = backgroundColors;
782 
783  if (!_backgroundColors)
784  _backgroundColors = [[CPColor whiteColor]];
785 
786  if ([_backgroundColors count] === 1)
787  [self setBackgroundColor:_backgroundColors[0]];
788 
789  else
790  [self setBackgroundColor:nil];
791 
792  [self setNeedsDisplay:YES];
793 }
794 
795 - (CPArray)backgroundColors
796 {
797  return _backgroundColors;
798 }
799 
800 - (void)mouseUp:(CPEvent)anEvent
801 {
802  if ([_selectionIndexes count] && [anEvent clickCount] == 2)
803  [self _sendDelegateDidDoubleClickOnItemAtIndex:[_selectionIndexes firstIndex]];
804 }
805 
806 - (void)mouseDown:(CPEvent)anEvent
807 {
808  _mouseDownEvent = anEvent;
809 
810  var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
811  index = [self _indexAtPoint:location];
812 
813  if (index >= 0 && index < _items.length)
814  {
815  if (_allowsMultipleSelection && ([anEvent modifierFlags] & CPPlatformActionKeyMask || [anEvent modifierFlags] & CPShiftKeyMask))
816  {
817  if ([anEvent modifierFlags] & CPPlatformActionKeyMask)
818  {
819  var indexes = [_selectionIndexes copy];
820 
821  if ([indexes containsIndex:index])
822  [indexes removeIndex:index];
823  else
824  [indexes addIndex:index];
825  }
826  else if ([anEvent modifierFlags] & CPShiftKeyMask)
827  {
828  var firstSelectedIndex = [[self selectionIndexes] firstIndex],
829  newSelectedRange = nil;
830 
831  // This catches the case where the shift key is held down for the first selection.
832  if (firstSelectedIndex === CPNotFound)
833  firstSelectedIndex = index;
834 
835  if (index < firstSelectedIndex)
836  newSelectedRange = CPMakeRange(index, (firstSelectedIndex - index) + 1);
837  else
838  newSelectedRange = CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
839 
840  indexes = [[self selectionIndexes] copy];
841  [indexes addIndexesInRange:newSelectedRange];
842  }
843  }
844  else
845  indexes = [CPIndexSet indexSetWithIndex:index];
846 
847  [self setSelectionIndexes:indexes];
848 
849  // TODO Is it allowable for collection view items to become the first responder? In that case they
850  // may have become that at this point by virtue of CPWindow's sendEvent: mouse down handling, and
851  // the following line will rudely snatch it away from them. For most cases though, clicking on an
852  // item should naturally make the collection view the first responder so that keyboard navigation
853  // is enabled.
854  [[self window] makeFirstResponder:self];
855  }
856  else if (_allowsEmptySelection)
858 }
859 
860 // Cappuccino Additions
861 
867 - (void)setVerticalMargin:(float)aVerticalMargin
868 {
869  if (_verticalMargin == aVerticalMargin)
870  return;
871 
872  _verticalMargin = aVerticalMargin;
873 
874  [self tile];
875 }
876 
877 - (void)setUniformSubviewsResizing:(BOOL)flag
878 {
879  _uniformSubviewsResizing = flag;
880  [self tileIfNeeded:NO];
881 }
882 
883 
888 - (float)verticalMargin
889 {
890  return _verticalMargin;
891 }
892 
896 - (id)delegate
897 {
898  return _delegate;
899 }
900 
904 - (CPMenu)menuForEvent:(CPEvent)theEvent
905 {
906  if (![self _delegateRespondsToCollectionViewMenuForItemAtIndex])
907  return [super menuForEvent:theEvent];
908 
909  var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
910  index = [self _indexAtPoint:location];
911 
912  return [self _sendDelegateMenuForItemAtIndex:index];
913 }
914 
915 - (int)_indexAtPoint:(CGPoint)thePoint
916 {
917  var column = FLOOR(thePoint.x / (_itemSize.width + _horizontalMargin));
918 
919  if (column < _numberOfColumns)
920  {
921  var row = FLOOR(thePoint.y / (_itemSize.height + _verticalMargin));
922 
923  if (row < _numberOfRows)
924  return (row * _numberOfColumns + column);
925  }
926 
927  return CPNotFound;
928 }
929 
930 - (CPCollectionViewItem)itemAtIndex:(CPUInteger)anIndex
931 {
932  return [_items objectAtIndex:anIndex];
933 }
934 
935 - (CGRect)frameForItemAtIndex:(CPUInteger)anIndex
936 {
937  return [[[self itemAtIndex:anIndex] view] frame];
938 }
939 
940 - (CGRect)frameForItemsAtIndexes:(CPIndexSet)anIndexSet
941 {
942  var indexArray = [],
943  frame = CGRectNull;
944 
945  [anIndexSet getIndexes:indexArray maxCount:-1 inIndexRange:nil];
946 
947  var index = 0,
948  count = [indexArray count];
949 
950  for (; index < count; ++index)
951  frame = CGRectUnion(frame, [self frameForItemAtIndex:indexArray[index]]);
952 
953  return frame;
954 }
955 
956 - (void)_applySelectionToItems:(BOOL)select
957 {
958  var numberOfItems = [_items count];
959 
960  [_selectionIndexes enumerateIndexesUsingBlock:function(idx, stop)
961  {
962  if (idx < numberOfItems)
963  [[_items objectAtIndex:idx] setSelected:select];
964  else {
965  stop(YES);
966  }
967  }];
968 }
969 
970 @end
971 
973 /*
974  TODO: dropOperation is not supported yet. The visible drop operation is like CPCollectionViewDropBefore.
975 */
976 
982 - (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
983 {
984  [aPasteboard setData:[self _sendDelegateDataForItemsAtIndexes:_selectionIndexes forType:aType] forType:aType];
985 }
986 
987 - (void)_createDropIndicatorIfNeeded
988 {
989  // Create and position the drop indicator view.
990 
991  if (!_dropView)
992  _dropView = [[_CPCollectionViewDropIndicator alloc] initWithFrame:CGRectMake(-8, -8, 0, 0)];
993 
994  [_dropView setFrameSize:CGSizeMake(10, _itemSize.height + _verticalMargin)];
995  [self addSubview:_dropView];
996 }
997 
998 - (void)mouseDragged:(CPEvent)anEvent
999 {
1000  // Don't crash if we never registered the intial click.
1001  if (!_mouseDownEvent)
1002  return;
1003 
1004  [self _createDropIndicatorIfNeeded];
1005 
1006  var locationInWindow = [anEvent locationInWindow],
1007  mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
1008 
1009  // FIXME: This is because Safari's drag hysteresis is 3px x 3px
1010  if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
1011  (ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
1012  return;
1013 
1014  if (![self _delegateRespondsToCollectionViewDragTypesForItemsAtIndexes])
1015  return;
1016 
1017  // If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
1018  if (![_selectionIndexes count])
1019  return;
1020 
1021  if (![self _sendDelegateCanDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
1022  return;
1023 
1024  // Set up the pasteboard
1025  var dragTypes = [self _sendDelegateDragTypesForItemsAtIndexes:_selectionIndexes];
1026 
1027  [[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
1028 
1029  var dragImageOffset = CGSizeMakeZero(),
1030  view = [self _sendDelegateDraggingViewForItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent offset:dragImageOffset];
1031 
1032  [view setFrameSize:_itemSize];
1033  [view setAlphaValue:0.7];
1034 
1035  var dragLocation = [self convertPoint:locationInWindow fromView:nil],
1036  dragPoint = CGPointMake(dragLocation.x - _itemSize.width / 2 , dragLocation.y - _itemSize.height / 2);
1037 
1038  [self dragView:view
1039  at:dragPoint
1040  offset:dragImageOffset
1041  event:_mouseDownEvent
1042  pasteboard:nil
1043  source:self
1044  slideBack:YES];
1045 }
1046 
1047 - (CPView)draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset
1048 {
1049  var idx = _content[[indexes firstIndex]];
1050 
1051  if (!_itemForDragging)
1052  _itemForDragging = [self newItemForRepresentedObject:idx];
1053  else
1054  [_itemForDragging setRepresentedObject:idx];
1055 
1056  return [_itemForDragging view];
1057 }
1058 
1059 - (CPDragOperation)draggingEntered:(id)draggingInfo
1060 {
1061  var dropIndex = -1,
1062  dropIndexRef = @ref(dropIndex),
1063  dragOp = [self _validateDragWithInfo:draggingInfo dropIndex:dropIndexRef dropOperation:1];
1064 
1065  dropIndex = dropIndexRef();
1066 
1067  [self _createDropIndicatorIfNeeded];
1068 
1069  [self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:dragOp newDropIndex:dropIndex newDropOperation:1];
1070 
1071  return _currentDragOperation;
1072 }
1073 
1074 - (CPDragOperation)draggingUpdated:(id)draggingInfo
1075 {
1076  if (![self _dropIndexDidChange:draggingInfo])
1077  return _currentDragOperation;
1078 
1079  var dropIndex,
1080  dropIndexRef = @ref(dropIndex);
1081 
1082  var dragOperation = [self _validateDragWithInfo:draggingInfo dropIndex:dropIndexRef dropOperation:1];
1083 
1084  dropIndex = dropIndexRef();
1085 
1086  [self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:dragOperation newDropIndex:dropIndex newDropOperation:1];
1087 
1088  return dragOperation;
1089 }
1090 
1091 - (CPDragOperation)_validateDragWithInfo:(id)draggingInfo dropIndex:(Function)dropIndexRef dropOperation:(int)dropOperation
1092 {
1093  var result = CPDragOperationMove,
1094  dropIndex = [self _dropIndexForDraggingInfo:draggingInfo proposedDropOperation:dropOperation];
1095 
1096  if ([self _delegateRespondsToCollectionViewValidateDropProposedIndexDropOperation])
1097  {
1098  var dropIndexRef2 = @ref(dropIndex);
1099 
1100  result = [self _sendDelegateValidateDrop:draggingInfo proposedIndex:dropIndexRef2 dropOperation:dropOperation];
1101 
1102  if (result !== CPDragOperationNone)
1103  dropIndex = dropIndexRef2();
1104  }
1105 
1106  dropIndexRef(dropIndex);
1107 
1108  return result;
1109 }
1110 
1111 - (void)draggingExited:(id)draggingInfo
1112 {
1113  [self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:0 newDropIndex:-1 newDropOperation:1];
1114 }
1115 
1116 - (void)draggingEnded:(id)draggingInfo
1117 {
1118  [self _updateDragAndDropStateWithDraggingInfo:draggingInfo newDragOperation:0 newDropIndex:-1 newDropOperation:1];
1119 }
1120 
1121 /*
1122 Not supported. Use -collectionView:dataForItemsAtIndexes:fortype:
1123 - (BOOL)_writeItemsAtIndexes:(CPIndexSet)indexes toPasteboard:(CPPasteboard)pboard
1124 {
1125  if ([self respondsToSelector:@selector(collectionView:writeItemsAtIndexes:toPasteboard:)])
1126  return [_delegate collectionView:self writeItemsAtIndexes:indexes toPasteboard:pboard];
1127 
1128  return NO;
1129 }
1130 */
1131 
1132 - (BOOL)performDragOperation:(id)draggingInfo
1133 {
1134  var result = NO;
1135 
1136  if (_currentDragOperation && _currentDropIndex !== -1)
1137  result = [self _sendDelegateAcceptDrop:draggingInfo index:_currentDropIndex dropOperation:1];
1138 
1139  [self draggingEnded:draggingInfo]; // Is this correct ?
1140 
1141  return result;
1142 }
1143 
1144 - (void)_updateDragAndDropStateWithDraggingInfo:(id)draggingInfo newDragOperation:(CPDragOperation)dragOperation newDropIndex:(CPInteger)dropIndex newDropOperation:(CPInteger)dropOperation
1145 {
1146  _currentDropIndex = dropIndex;
1147  _currentDragOperation = dragOperation;
1148 
1149  var frameOrigin,
1150  dropviewFrameWidth = CGRectGetWidth([_dropView frame]);
1151 
1152  if (_currentDropIndex == -1 || _currentDragOperation == CPDragOperationNone)
1153  frameOrigin = CGPointMake(-dropviewFrameWidth, 0);
1154  else if (_currentDropIndex == 0)
1155  frameOrigin = CGPointMake(0, 0);
1156  else
1157  {
1158  var offset;
1159 
1160  if ((_currentDropIndex % _numberOfColumns) !== 0 || _currentDropIndex == [_items count])
1161  {
1162  dropIndex = _currentDropIndex - 1;
1163  offset = (_horizontalMargin - dropviewFrameWidth) / 2;
1164  }
1165  else
1166  {
1167  offset = - _itemSize.width - dropviewFrameWidth - (_horizontalMargin - dropviewFrameWidth) / 2;
1168  }
1169 
1170  var rect = [self frameForItemAtIndex:dropIndex];
1171 
1172  frameOrigin = CGPointMake(CGRectGetMaxX(rect) + offset, rect.origin.y - _verticalMargin);
1173  }
1174 
1175  [_dropView setFrameOrigin:frameOrigin];
1176 }
1177 
1178 - (BOOL)_dropIndexDidChange:(id)draggingInfo
1179 {
1180  var dropIndex = [self _dropIndexForDraggingInfo:draggingInfo proposedDropOperation:1];
1181 
1182  if (dropIndex == CPNotFound)
1183  dropIndex = [[self content] count];
1184 
1185  return (_currentDropIndex !== dropIndex)
1186 }
1187 
1188 - (CPInteger)_dropIndexForDraggingInfo:(id)draggingInfo proposedDropOperation:(int)dropOperation
1189 {
1190  var location = [self convertPoint:[draggingInfo draggingLocation] fromView:nil],
1191  locationX = location.x + _itemSize.width / 2;
1192 
1193  var column = MIN(FLOOR(locationX / (_itemSize.width + _horizontalMargin)), _numberOfColumns),
1194  row = FLOOR(location.y / (_itemSize.height + _verticalMargin));
1195 
1196  if (row >= _numberOfRows - 1)
1197  {
1198  if (row >= _numberOfRows)
1199  {
1200  row = _numberOfRows - 1;
1201  column = _numberOfColumns;
1202  }
1203 
1204  return MIN((row * _numberOfColumns + column), [_items count]);
1205  }
1206 
1207  return (row * _numberOfColumns + column);
1208 }
1209 
1210 @end
1211 @implementation _CPCollectionViewDropIndicator : CPView
1212 {
1213  id __doxygen__;
1214 }
1215 
1216 - (void)drawRect:(CGRect)aRect
1217 {
1218  var context = [[CPGraphicsContext currentContext] graphicsPort],
1219  width = CGRectGetWidth(aRect),
1220  circleRect = CGRectMake(1, 1, width - 2, width - 2);
1221 
1222  CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]);
1223  CGContextSetFillColor(context, [CPColor whiteColor]);
1224  CGContextSetLineWidth(context, 3);
1225 
1226  //draw white under the circle thing
1227  CGContextFillRect(context, circleRect);
1228 
1229  //draw the circle thing
1230  CGContextStrokeEllipseInRect(context, circleRect);
1231 
1232  //then draw the line
1233  CGContextBeginPath(context);
1234  CGContextMoveToPoint(context, FLOOR(width / 2), CGRectGetMinY(aRect) + width);
1235  CGContextAddLineToPoint(context, FLOOR(width / 2), CGRectGetHeight(aRect));
1236  CGContextStrokePath(context);
1237 }
1238 
1239 @end
1240 
1242 
1243 - (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
1244 {
1245  var count = [[self items] count];
1246 
1247  if (count === 0)
1248  return;
1249 
1250  anIndex = MIN(MAX(anIndex, 0), count - 1);
1251 
1252  if (_allowsMultipleSelection && shouldExpand)
1253  {
1254  var indexes = [_selectionIndexes copy],
1255  bottomAnchor = [indexes firstIndex],
1256  topAnchor = [indexes lastIndex];
1257 
1258  // if the direction is backward (-1) check with the bottom anchor
1259  if (aDirection === -1)
1260  [indexes addIndexesInRange:CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
1261  else
1262  [indexes addIndexesInRange:CPMakeRange(topAnchor, anIndex - topAnchor + 1)];
1263  }
1264  else
1265  indexes = [CPIndexSet indexSetWithIndex:anIndex];
1266 
1267  [self setSelectionIndexes:indexes];
1268  [self _scrollToSelection];
1269 }
1270 
1271 - (void)_scrollToSelection
1272 {
1273  var frame = [self frameForItemsAtIndexes:[self selectionIndexes]];
1274 
1275  if (!CGRectIsEmpty(frame))
1276  [self scrollRectToVisible:frame];
1277 }
1278 
1279 - (void)moveLeft:(id)sender
1280 {
1281  var index = [[self selectionIndexes] firstIndex];
1282  if (index === CPNotFound)
1283  index = [[self items] count];
1284 
1285  [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO];
1286 }
1287 
1288 - (void)moveLeftAndModifySelection:(id)sender
1289 {
1290  var index = [[self selectionIndexes] firstIndex];
1291  if (index === CPNotFound)
1292  index = [[self items] count];
1293 
1294  [self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES];
1295 }
1296 
1297 - (void)moveRight:(id)sender
1298 {
1299  [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO];
1300 }
1301 
1302 - (void)moveRightAndModifySelection:(id)sender
1303 {
1304  [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES];
1305 }
1306 
1307 - (void)moveDown:(id)sender
1308 {
1309  [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO];
1310 }
1311 
1312 - (void)moveDownAndModifySelection:(id)sender
1313 {
1314  [self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES];
1315 }
1316 
1317 - (void)moveUp:(id)sender
1318 {
1319  var index = [[self selectionIndexes] firstIndex];
1320  if (index == CPNotFound)
1321  index = [[self items] count];
1322 
1323  [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO];
1324 }
1325 
1326 - (void)moveUpAndModifySelection:(id)sender
1327 {
1328  var index = [[self selectionIndexes] firstIndex];
1329  if (index == CPNotFound)
1330  index = [[self items] count];
1331 
1332  [self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES];
1333 }
1334 
1335 - (void)deleteBackward:(id)sender
1336 {
1337  if ([[self delegate] respondsToSelector:@selector(collectionView:shouldDeleteItemsAtIndexes:)])
1338  {
1339  [[self delegate] collectionView:self shouldDeleteItemsAtIndexes:[self selectionIndexes]];
1340 
1341  var index = [[self selectionIndexes] firstIndex];
1342  if (index > [[self content] count] - 1)
1343  [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:[[self content] count] - 1]];
1344 
1345  [self _scrollToSelection];
1346  [self setNeedsDisplay:YES];
1347  }
1348 }
1349 
1350 - (void)keyDown:(CPEvent)anEvent
1351 {
1352  [self interpretKeyEvents:[anEvent]];
1353 }
1354 
1355 - (void)setAutoresizingMask:(unsigned)aMask
1356 {
1357  [super setAutoresizingMask:0];
1358 }
1359 
1360 @end
1361 
1362 
1364 
1365 - (CGRect)rectForItemAtIndex:(int)anIndex
1366 {
1367  _CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemAtIndex:));
1368 
1369  // Don't re-compute anything just grab the current frame
1370  // This allows subclasses to override tile without messing this up.
1371  return [self frameForItemAtIndex:anIndex];
1372 }
1373 
1374 - (CGRect)rectForItemsAtIndexes:(CPIndexSet)anIndexSet
1375 {
1376  _CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemsAtIndexes:));
1377 
1378  return [self frameForItemsAtIndexes:anIndexSet];
1379 }
1380 
1381 @end
1382 
1383 
1385 
1386 /*
1387  @ignore
1388  Return YES if the delegate implements collectionView:validateDrop:proposedIndex:dropOperation:
1389 */
1390 - (BOOL)_delegateRespondsToCollectionViewValidateDropProposedIndexDropOperation
1391 {
1393 }
1394 
1395 /*
1396  @ignore
1397  Return YES if the delegate implements collectionView:menuForItemAtIndex:
1398 */
1399 - (BOOL)_delegateRespondsToCollectionViewMenuForItemAtIndex
1400 {
1401  return _implementedDelegateMethods & CPCollectionViewDelegate_collectionView_menuForItemAtIndex_;
1402 }
1403 
1404 /*
1405  @ignore
1406  Return YES if the delegate implements collectionView:dragTypesForItemsAtIndexes:
1407 */
1408 - (BOOL)_delegateRespondsToCollectionViewDragTypesForItemsAtIndexes
1409 {
1410  return _implementedDelegateMethods & CPCollectionViewDelegate_collectionView_dragTypesForItemsAtIndexes_;
1411 }
1412 
1417 - (BOOL)_sendDelegateAcceptDrop:(id)draggingInfo index:(CPInteger)index dropOperation:(CPCollectionViewDropOperation)dropOperation
1418 {
1420  return NO;
1421 
1422  return [_delegate collectionView:self acceptDrop:draggingInfo index:index dropOperation:dropOperation];
1423 }
1424 
1429 - (BOOL)_sendDelegateCanDragItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)anEvent
1430 {
1432  return YES;
1433 
1434  return [_delegate collectionView:self canDragItemsAtIndexes:indexes withEvent:anEvent];
1435 }
1436 
1441 - (BOOL)_sendDelegateWriteItemsAtIndexes:(CPIndexSet)indexes toPasteboard:(CPPasteboard)pasteboard
1442 {
1444  return NO;
1445 
1446  return [_delegate collectionView:self writeItemsAtIndexes:indexes toPasteboard:pasteboard];
1447 }
1448 
1453 - (CPArray)_sendDelegateDragTypesForItemsAtIndexes:(CPIndexSet)indexes
1454 {
1455  if (!(_implementedDelegateMethods & CPCollectionViewDelegate_collectionView_dragTypesForItemsAtIndexes_))
1456  return [];
1457 
1458  return [_delegate collectionView:self dragTypesForItemsAtIndexes:indexes];
1459 }
1460 
1465 - (CPData)_sendDelegateDataForItemsAtIndexes:(CPIndexSet)indexes forType:(CPString)aType
1466 {
1468  return nil;
1469 
1470  return [_delegate collectionView:self dataForItemsAtIndexes:indexes forType:aType];
1471 }
1472 
1477 - (CPDragOperation)_sendDelegateValidateDrop:(id)draggingInfo proposedIndex:(CPInteger)proposedDropIndex dropOperation:(CPCollectionViewDropOperation)proposedDropOperation
1478 {
1480  return CPDragOperationNone;
1481 
1482  return [_delegate collectionView:self validateDrop:draggingInfo proposedIndex:proposedDropIndex dropOperation:proposedDropOperation];
1483 }
1484 
1489 - (void)_sendDelegateDidDoubleClickOnItemAtIndex:(int)index
1490 {
1491  if (!(_implementedDelegateMethods & CPCollectionViewDelegate_collectionView_didDoubleClickOnItemAtIndex_))
1492  return;
1493 
1494  return [_delegate collectionView:self didDoubleClickOnItemAtIndex:index];
1495 }
1496 
1497 
1502 - (void)_sendDelegateMenuForItemAtIndex:(CPInteger)anIndex
1503 {
1504  if (!(_implementedDelegateMethods & CPCollectionViewDelegate_collectionView_menuForItemAtIndex_))
1505  return nil;
1506 
1507  return [_delegate collectionView:self menuForItemAtIndex:anIndex];
1508 }
1509 
1514 - (CPView)_sendDelegateDraggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)anEvent offset:(CGPoint)dragImageOffset
1515 {
1517  return [self draggingViewForItemsAtIndexes:indexes withEvent:anEvent offset:dragImageOffset];
1518 
1519  return [_delegate collectionView:self draggingViewForItemsAtIndexes:indexes withEvent:anEvent offset:dragImageOffset];
1520 }
1521 
1522 @end
1523 
1524 
1525 var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
1526  CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
1527  CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
1528  CPCollectionViewMaxNumberOfRowsKey = @"CPCollectionViewMaxNumberOfRowsKey",
1529  CPCollectionViewMaxNumberOfColumnsKey = @"CPCollectionViewMaxNumberOfColumnsKey",
1530  CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
1531  CPCollectionViewAllowsMultipleSelectionKey = @"CPCollectionViewAllowsMultipleSelectionKey",
1532  CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
1533 
1534 
1536 
1537 - (id)initWithCoder:(CPCoder)aCoder
1538 {
1539  self = [super initWithCoder:aCoder];
1540 
1541  if (self)
1542  {
1543  _minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey];
1544  _maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey];
1545 
1546  _maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey];
1547  _maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey];
1548 
1549  _verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
1550 
1551  _isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
1552  _allowsMultipleSelection = [aCoder decodeBoolForKey:CPCollectionViewAllowsMultipleSelectionKey];
1553 
1554  [self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
1555 
1556  [self _init];
1557  }
1558 
1559  return self;
1560 }
1561 
1562 - (void)encodeWithCoder:(CPCoder)aCoder
1563 {
1564  [super encodeWithCoder:aCoder];
1565 
1566  if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
1567  [aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
1568 
1569  if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
1570  [aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
1571 
1572  [aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
1573  [aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey];
1574 
1575  [aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
1576  [aCoder encodeBool:_allowsMultipleSelection forKey:CPCollectionViewAllowsMultipleSelectionKey];
1577 
1578  [aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
1579 
1580  [aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
1581 }
1582 
1583 @end
1584 @implementation _CPCollectionViewContentBinder : CPBinder
1585 {
1586  id __doxygen__;
1587 }
1588 
1589 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
1590 {
1591  [_source setContent:aValue];
1592 }
1593 
1594 @end
1595 
1597 
1601 - (BOOL)uniformSubviewsResizing
1602 {
1603  return _uniformSubviewsResizing;
1604 }
1605 
1609 - (void)setUniformSubviewsResizing:(BOOL)aValue
1610 {
1611  _uniformSubviewsResizing = aValue;
1612 }
1613 
1614 @end
id initWithFrame:(CGRect aFrame)
Definition: CPView.j:351
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
BOOL makeFirstResponder:(CPResponder aResponder)
Definition: CPWindow.j:1632
function CGContextStrokeEllipseInRect(aContext, aRect)
Definition: CGContext.j:606
Definition: CPMenu.h:2
CPInteger lastIndex()
Definition: CPIndexSet.j:289
void dragView:at:offset:event:pasteboard:source:slideBack:(CPView aView, [at] CGPoint aLocation, [offset] CGSize mouseOffset, [event] CPEvent anEvent, [pasteboard] CPPasteboard aPasteboard, [source] id aSourceObject, [slideBack] BOOL slideBack)
Definition: CPView.j:2455
var CPCollectionViewMaxNumberOfColumnsKey
unsigned numberOfColumns()
var CPCollectionViewDelegate_collectionView_didDoubleClickOnItemAtIndex_
CGRect frame
void tileIfNeeded:(BOOL lazyFlag)
var isEqual
CPGraphicsContext currentContext()
id initWithCoder:(CPCoder aCoder)
Definition: CPView.j:3696
CPViewWidthSizable
Definition: CPView.j:67
CPDragOperation CPDragOperationNone
var CPCollectionViewDelegate_collectionView_writeItemsAtIndexes_toPasteboard_
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
BOOL setData:forType:(CPData aData, [forType] CPString aType)
Definition: CPPasteboard.j:181
CPResponder firstResponder()
Definition: CPWindow.j:1657
CGPoint locationInWindow()
Definition: CPEvent.j:290
CPCollectionViewItem newItemForRepresentedObject:(id anObject)
CPInteger getIndexes:maxCount:inIndexRange:(CPArray anArray, [maxCount] CPInteger aMaxCount, [inIndexRange] CPRange aRange)
Definition: CPIndexSet.j:386
CPColor whiteColor()
Definition: CPColor.j:361
id delegate()
Definition: CALayer.j:965
void displayItems:frameSize:itemSize:columns:rows:count:(CPArray displayItems, [frameSize] CGSize aFrameSize, [itemSize] CGSize anItemSize, [columns] CPInteger numberOfColumns, [rows] CPInteger numberOfRows, [count] CPInteger displayCount)
int width
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
A collection of unique integers.
Definition: CPIndexSet.h:2
CPDragOperationMove
CPInteger firstIndex()
Definition: CPIndexSet.j:278
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
var CPCollectionViewDelegate_collectionView_menuForItemAtIndex_
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
CPWindow window()
Definition: CPView.j:527
CGSize frameSize()
Definition: CPView.j:1056
var CPCollectionViewMinItemSizeKey
var CPCollectionViewDelegate_collectionView_draggingViewForItemsAtIndexes_withEvent_offset
CPIndexSet selectionIndexes()
An immutable string (collection of characters).
Definition: CPString.h:2
CGPoint convertPoint:fromView:(CGPoint aPoint, [fromView] CPView aView)
Definition: CPView.j:2249
var CPCollectionViewVerticalMarginKey
CGRect frameForItemAtIndex:(CPUInteger anIndex)
var CPCollectionViewSelectableKey
function CGContextSetFillColor(aContext, aColor)
Definition: CGContext.j:663
void setAutoresizingMask:(unsigned aMask)
Definition: CPView.j:1511
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
CPMenu menuForEvent:(CPEvent anEvent)
Definition: CPView.j:1938
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3806
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CPShiftKeyMask
var CPCollectionViewDelegate_collectionView_dataForItemsAtIndexes_forType_
var CPCollectionViewMaxItemSizeKey
void setBackgroundColors:(CPArray backgroundColors)
var CPCollectionViewDelegate_collectionView_validateDrop_proposedIndex_dropOperation_
var CPCollectionViewDelegate_collectionView_canDragItemsAtIndexes_withEvent_
CPPlatformActionKeyMask
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPNotFound
Definition: CPObjJRuntime.j:62
unsigned declareTypes:owner:(CPArray types, [owner] id anOwner)
Definition: CPPasteboard.j:158
var CPCollectionViewBackgroundColorsKey
CPViewHeightSizable
Definition: CPView.j:85
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1947
var CPCollectionViewDelegate_collectionView_dragTypesForItemsAtIndexes_
var CPCollectionViewMaxNumberOfRowsKey
void setFrameSize:(CGSize aSize)
Definition: CPView.j:1124
void addIndexesInRange:(CPRange aRange)
Definition: CPIndexSet.j:732
function CGContextFillRect(aContext, aRect)
Definition: CGContext.j:358
Definition: CPEvent.h:2
CPView superview()
Definition: CPView.j:510
CGRect frame()
Definition: CPView.j:1046
void draggingEnded:(id draggingInfo)
id indexSetWithIndex:(int anIndex)
Definition: CPIndexSet.j:51
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
CGRect frameForItemsAtIndexes:(CPIndexSet anIndexSet)
var CPCollectionViewAllowsMultipleSelectionKey
CPRange function CPMakeRange(location, length)
Definition: CPRange.j:37
void setSelectionIndexes:(CPIndexSet anIndexSet)
id indexSet()
Definition: CPIndexSet.j:43
var HORIZONTAL_MARGIN
id pasteboardWithName:(CPString aName)
Definition: CPPasteboard.j:93
Definition: CPView.j:137
CPCollectionViewItem itemAtIndex:(CPUInteger anIndex)