From 33882942e4ba5f9e6dbbe3a53bda4f832f1935ff Mon Sep 17 00:00:00 2001 From: Randall Luecke Date: Mon, 3 Jan 2011 20:17:12 -0600 Subject: [PATCH] Documentation of the outlineview and some API fixes. --- AppKit/CPOutlineView.j | 348 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 328 insertions(+), 20 deletions(-) diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j index ea4108ddc..b94f34453 100644 --- a/AppKit/CPOutlineView.j +++ b/AppKit/CPOutlineView.j @@ -75,6 +75,16 @@ var CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_ CPOutlineViewDropOnItemIndex = -1; +/*! + @ingroup appkit + @class CPOutlineView + + CPOutlineView is a subclass of CPTableView that inherates the row and column format to display hierarchial data. + The outlineview adds the ability to expand and collapse items. This is useful for browsing a tree like structure such as directories or a filesystem. + + Like the tableview, an outlineview uses a data source to supply its data. For this reason you must implement a couple data source methods (documented in setDataSource:) + +*/ @implementation CPOutlineView : CPTableView { id _outlineViewDataSource; @@ -139,7 +149,54 @@ CPOutlineViewDropOnItemIndex = -1; return self; } +/*! + In addition to standard delegation, the outline view also supports data source delegation. This method sets the data source object. + Just like the TableView you have CPTableColumns but instead of rows you deal with items. + You must implement these data source methods: + + - (id)outlineView:(CPOutlineView)outlineView child:(CPInteger)index ofItem:(id)item; + Returns the child item at an index of a given item. if item is nil you should return the appropriate root item. + + - (BOOL)outlineView:(CPOutlineView)outlineView isItemExpandable:(id)item; + Returns YES if the item is expandable, otherwise NO. + + - (int)outlineView:(CPOutlineView)outlineView numberOfChildrenOfItem:(id)item; + Returns the number of child items of a given item. If item is nil you should return the number of top level (root) items. + + - (id)outlineView:(CPOutlineView)outlineView objectValueForTableColumn:(CPTableColumn)tableColumn byItem:(id)item; + Returns the object value of the item in a given column. + + + The following methods are optional: + + Editing: + - (void)outlineView:(CPOutlineView)outlineView setObjectValue:(id)object forTableColumn:(CPTableColumn)tableColumn byItem:(id)item; + Sets the data object value for an item in a given column. This needs to be implemented if you want inline editing support. + + + Sorting: + - (void)outlineView:(CPOutlineView)outlineView sortDescriptorsDidChange:(CPArray)oldDescriptors; + The outlineview will call this method if you click the tableheader. You should sort the datasource based off of the new sort descriptors and reload the data + + Drag and Drop: + In order for the outlineview to recieve drops dont forget to first register the tableview for drag types like you do with every other view + + - (BOOL)outlineView:(CPOutlineView)outlineView acceptDrop:(id < CPDraggingInfo >)info item:(id)item childIndex:(CPInteger)index; + Return YES if the operation was successful otherwise return NO. + The data source should incorporate the data from the dragging pasteboard in this method implementation. + To get this data use the draggingPasteboard method on the CPDraggingInfo object. + + - (CPDragOperation)outlineView:(CPOutlineView)outlineView validateDrop:(id < CPDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(CPInteger)index; + Return the drag operation (move, copy, etc) that should be performaned if a registered drag type is over the tableview + The data source can retarget a drop if you want by calling -(void)setDropItem:(id)anItem dropChildIndex:(int)anIndex; + + - (BOOL)outlineView:(CPOutlineView)outlineView writeItems:(CPArray)items toPasteboard:(CPPasteboard)pboard; + Returns YES if the drop operation is allowed otherwise NO. + This method is invoked by the outlineview after a drag should begin, but before it is started. If you dont want the drag to being return NO. + If you want the drag to begin you should return YES and place the drag data on the pboard. + +*/ - (void)setDataSource:(id)aDataSource { if (_outlineViewDataSource === aDataSource) @@ -193,11 +250,22 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadData]; } +/*! + Returns the datasource object. + @return id - The data source object +*/ - (id)dataSource { return _outlineViewDataSource; } +/*! + Used to query whether an item is expandable or not. + + @param anItem - the item you are interested in. + + @return BOOL - YES if the item is expandable, otherwise NO. +*/ - (BOOL)isExpandable:(id)anItem { if (!anItem) @@ -211,7 +279,14 @@ CPOutlineViewDropOnItemIndex = -1; return itemInfo.isExpandable; } -- (void)isItemExpanded:(id)anItem +/*! + Used to find if an item is already expanded. + + @param anItem - the item you are interest in. + + @return BOOL - Yes if the item is already expanded, otherwise NO. +*/ +- (BOOL)isItemExpanded:(id)anItem { if (!anItem) return YES; @@ -224,11 +299,22 @@ CPOutlineViewDropOnItemIndex = -1; return itemInfo.isExpanded; } +/*! + Expends a given item. + + @param anItem - the item to expand. +*/ - (void)expandItem:(id)anItem { [self expandItem:anItem expandChildren:NO]; } +/*! + Expands a given item, and optionally all the children of that item. + + @param anItem - the item you want to expand. + @param shouldExpandChildren - Pass YES if you want to expand all the children of anItem, otherwise NO. +*/ - (void)expandItem:(id)anItem expandChildren:(BOOL)shouldExpandChildren { var itemInfo = null; @@ -260,6 +346,11 @@ CPOutlineViewDropOnItemIndex = -1; } } +/*! + Collapse a given item. + + @param anItem - The item you want to collapse. +*/ - (void)collapseItem:(id)anItem { if (!anItem) @@ -280,11 +371,22 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadItem:anItem reloadChildren:YES]; } +/*! + Reloads the data for an item. + + @param anItem - The item you want to reload. +*/ - (void)reloadItem:(id)anItem { [self reloadItem:anItem reloadChildren:NO]; } +/*! + Reloads the data for a given item and optionally the children. + + @param anItem - The item you want to reload. + @param shouldReloadChildren - Pass YES if you want to reload all the children, otherwise NO. +*/ - (void)reloadItem:(id)anItem reloadChildren:(BOOL)shouldReloadChildren { if (!!shouldReloadChildren || !anItem) @@ -295,11 +397,23 @@ CPOutlineViewDropOnItemIndex = -1; [super reloadData]; } +/*! + Returns the item at a given row index. If no item exists nil is returned. + + @param aRow - The rown index you want to find the item at. + @return id - The item at a given index. +*/ - (id)itemAtRow:(CPInteger)aRow { return _itemsForRows[aRow] || nil; } +/*! + Returns the row of a given item + + @param anItem - The item you want to find the row of. + @return int - The row index of a given item. +*/ - (CPInteger)rowForItem:(id)anItem { if (!anItem) @@ -313,6 +427,11 @@ CPOutlineViewDropOnItemIndex = -1; return itemInfo.row; } +/*! + Sets the table column you want to display the disclosure button in. + + @param aTableColumn - The CPTableColumn you want to use for hierarchical data. +*/ - (void)setOutlineTableColumn:(CPTableColumn)aTableColumn { if (_outlineTableColumn === aTableColumn) @@ -324,11 +443,22 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadData]; } +/*! + Returns the table column used to display hierarchical data. + + @return CPTableColumn - The table column that displays the disclosure button. +*/ - (CPTableColumn)outlineTableColumn { return _outlineTableColumn; } +/*! + Returns the indentation level of a given item. If the item is nil (the top level root item) CPNotFound is returned. Indentation levels are zero based, thus items that are not indented return 0. + + @param anItem - The item you want the indentation level for. + @return int - the indentation level of anItem. +*/ - (CPInteger)levelForItem:(id)anItem { if (!anItem) @@ -342,11 +472,22 @@ CPOutlineViewDropOnItemIndex = -1; return itemInfo.level; } +/*! + Returns the indentation level for a given row. If the row is invalid CPNotFound is returned. Rows that are nto indented return 0. + + @param aRow - the row of the reciever + @return int - the indentation level of aRow. +*/ - (CPInteger)levelForRow:(CPInteger)aRow { return [self levelForItem:[self itemAtRow:aRow]]; } +/*! + Sets the number of pixels to indent an item at each indentation level. + + @param anIndentationWidth - the width of each indentation level. +*/ - (void)setIndentationPerLevel:(float)anIndentationWidth { if (_indentationPerLevel === anIndentationWidth) @@ -358,11 +499,21 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadData]; } +/*! + Returns the width of an indentation level. + + @return float - the width of the indentation per level. +*/ - (float)indentationPerLevel { return _indentationPerLevel; } +/*! + Sets the layout behaviour of disclosure button. If you pass NO the disclosure button will always align itself to the left of the outline column. + + @param indentationMarkerShouldFollowDataView - Pass YES if the disclosure control should be indented along with the dataview, otherwise NO. +*/ - (void)setIndentationMarkerFollowsDataView:(BOOL)indentationMarkerShouldFollowDataView { if (_indentationMarkerFollowsDataView === indentationMarkerShouldFollowDataView) @@ -374,11 +525,22 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadData]; } +/*! + Returns the layout behaviour of the disclosure buttons. + + @return BOOL - YES if the disclosure control indents itself with the dataview, otherwise NO if the control is always aligned to the left of the outline column +*/ - (BOOL)indentationMarkerFollowsDataView { return _indentationMarkerFollowsDataView; } +/*! + Returns the parent item for a given item. If the item is a top level root object nil is returned. + + @param anItem - The item of the reciver. + @return id - The parent item of anItem. If no parent exists (the item is a root item) nil is returned. +*/ - (id)parentForItem:(id)anItem { if (!anItem) @@ -398,9 +560,15 @@ CPOutlineViewDropOnItemIndex = -1; return parent; } -- (CGRect)frameOfOutlineDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow +/*! + @ignore + + Returns the frame of the dataview at the row given for the outline column +*/ +- (CGRect)_frameOfOutlineDataViewAtRow:(CPInteger)aRow { - var frame = [super frameOfDataViewAtColumn:aColumn row:aRow], + var columnIndex = [[self tableColumns] indexOfObject:_outlineTableColumn], + frame = [super frameOfDataViewAtColumn:columnIndex row:aRow], indentationWidth = ([self levelForRow:aRow] + 1) * [self indentationPerLevel]; frame.origin.x += indentationWidth; @@ -409,6 +577,29 @@ CPOutlineViewDropOnItemIndex = -1; return frame; } +/*! + Returns the frame of the disclosure button for the outline column. + If the item is not expandable a CGZeroRect is returned. + Subclasses can return a CGZeroRect to prevent the disclosure control from being displayed. + + @param aRow - The row of the reciever + @return CGRect - The rect of the disclosure button at aRow. +*/ +- (CGRect)frameOfOutlineDisclosureControlAtRow:(CPInteger)aRow +{ + if (![self isExpandable:[self itemAtRow:aRow]]) + return _CGRectMakeZero(); + + var dataViewFrame = [self _frameOfOutlineDataViewAtRow:aRow], + frame = _CGRectMake(_CGRectGetMinX(dataViewFrame) - 10, _CGRectGetMinY(dataViewFrame), 10, _CGRectGetHeight(dataViewFrame)); + + return frame; +} + +/*! + @ignore + Select or deselect rows, this is overridden because we need to change the color or the outline control +*/ - (void)_performSelection:(BOOL)select forRow:(CPInteger)rowIndex context:(id)context { [super _performSelection:select forRow:rowIndex context:context]; @@ -419,6 +610,68 @@ CPOutlineViewDropOnItemIndex = -1; [control performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelected]; } +/*! + Sets the delegate for the outlineview. + + The following methods can be implemented: + + User Interaction Notifications: + - (void)outlineViewColumnDidMove:(CPNotification)notification; + Called when the user moves a column in the outlineview. + + - (void)outlineViewColumnDidResize:(CPNotification)notification; + Called when the user resizes a column in the outlineview. + + - (void)outlineViewItemDidCollapse:(CPNotification)notification; + Called when the user collapses an item in teh outlineview. + + - (void)outlineViewItemDidExpand:(CPNotification)notification; + Called when the user expands an item in the outlineview. + + - (void)outlineViewItemWillCollapse:(CPNotification)notification; + Called when the user collapses an item in the outlineview, but before the item is actually collapsed. + + - (void)outlineViewItemWillExpand:(CPNotification)notification; + Called when the used expands an item, but before the item is actually expanded. + + - (void)outlineViewSelectionDidChange:(CPNotification)notification; + Called when the user changes the selection of the outlineview. + + - (void)outlineViewSelectionIsChanging:(CPNotification)notification + Called when the user changes the selection of the outlineview, but before the change is made. + + Expanding and collapsing items: + - (BOOL)outlineView:(CPOutlineView)outlineView shouldExpandItem:(id)item; + Return YES if the item should be given permission to expand, otherwise NO. + + - (BOOL)outlineView:(CPOutlineView)outlineView shouldCollapseItem:(id)item; + Return YES if the item should be given permission to collapse, otherwise NO. + + Selection: + - (BOOL)outlineView:(CPOutlineView)outlineView shouldSelectTableColumn:(CPTableColumn)tableColumn; + Return YES to allow the selection of tableColumn, otherwise NO. + + - (BOOL)outlineView:(CPOutlineView)outlineView shouldSelectItem:(id)item; + Return YES to allow the selection of an item, otherwise NO. + + - (BOOL)selectionShouldChangeInOutlineView:(CPOutlineView)outlineView; + Return YES to allow the selection of the outlineview to be changed, otherwise NO. + + Displaying DataViews: + - (void)outlineView:(CPOutlineView)outlineView willDisplayView:(id)dataView forTableColumn:(CPTableColumn)tableColumn item:(id)item; + Called when a dataView is about to be displayed. This gives you the ability to alter the dataView if needed. + + Editing: + - (BOOL)outlineView:(CPOutlineView)outlineView shouldEditTableColumn:(CPTableColumn)tableColumn item:(id)item; + Return YES to allow for editing of a dataview at given item and tableColumn, otherwise NO to prevent the edit. + + Group Items: + - (BOOL)outlineView:(CPOutlineView)outlineView isGroupItem:(id)item; + Implement this to indicate whether a given item should be rendered using the group item style. + Return YES if the item is a group item, otherwise NO. + + @param aDelegate - the delegate object you wish to set for the reciever. +*/ - (void)setDelegate:(id)aDelegate { if (_outlineViewDelegate === aDelegate) @@ -581,11 +834,20 @@ CPOutlineViewDropOnItemIndex = -1; } +/*! + Returns the delegate object for the outlineview. +*/ - (id)delegate { return _outlineViewDelegate; } +/*! + Sets the prototype of the disclosure control. This is used if you want to set a special type of button, instead of the default triangle. + The control must implement CPCoding. + + @param aControl - the control to be used to expand and collapse items. +*/ - (void)setDisclosureControlPrototype:(CPControl)aControl { _disclosureControlPrototype = aControl; @@ -596,17 +858,24 @@ CPOutlineViewDropOnItemIndex = -1; [self reloadData]; } +/*! + Reloads all the data of the outlineview. +*/ - (void)reloadData { [self reloadItem:nil reloadChildren:YES]; } +/*! + @ignore + We overide this because we need a special behaviour for the outline column +*/ - (CGRect)frameOfDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow { var tableColumn = [self tableColumns][aColumn]; if (tableColumn === _outlineTableColumn) - return [self frameOfOutlineDataViewAtColumn:aColumn row:aRow]; + return [self _frameOfOutlineDataViewAtRow:aRow]; return [super frameOfDataViewAtColumn:aColumn row:aRow]; } @@ -671,7 +940,15 @@ CPOutlineViewDropOnItemIndex = -1; return dragView; } +/*! + Retargets the drop item for the outlineview. + To specify a drop on theItem, you specify item as theItem and index as CPOutlineViewDropOnItemIndex. + To specify a drop between child 1 and 2 of theItem, you specify item as theItem and index as 2. + To specify a drop on an item that canŐt be expanded theItem, you specify item as someOutlineItem and index as CPOutlineViewDropOnItemIndex. + @param theItem - The item you want to retarget the drop on. + @param theIndex - The index of the child item you want to retarget the drop between. Pass CPOutlineViewDropOnItemIndex if you want to drop on theItem. +*/ - (void)setDropItem:(id)theItem dropChildIndex:(int)theIndex { if (_dropItem !== theItem && theIndex < 0 && [self isExpandable:theItem] && ![self isItemExpanded:theItem]) @@ -718,6 +995,9 @@ CPOutlineViewDropOnItemIndex = -1; } } +/*! + @ignore +*/ - (void)_draggingEnded { [super _draggingEnded]; @@ -726,6 +1006,9 @@ CPOutlineViewDropOnItemIndex = -1; _dragHoverTimer = nil; } +/*! + @ignore +*/ - (id)_parentItemForUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex atMouseOffset:(CPPoint)theOffset { if (_shouldRetargetItem) @@ -753,6 +1036,9 @@ CPOutlineViewDropOnItemIndex = -1; return [self parentForItem:[self itemAtRow:theLowerRowIndex]]; } +/*! + @ignore +*/ - (CPRect)_rectForDropHighlightViewBetweenUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex offset:(CPPoint)theOffset { // Call super and the update x to reflect the current indentation level @@ -802,17 +1088,17 @@ CPOutlineViewDropOnItemIndex = -1; if (tableColumn === _outlineTableColumn) { var control = _disclosureControlsForRows[row], - frame = [control frame]; - if (frame) - { - frame.origin.x = _indentationMarkerFollowsDataView ? _CGRectGetMinX(dataViewFrame) - _CGRectGetWidth(frame) : 0.0; - [control setFrame:frame]; - } + frame = [self frameOfOutlineDisclosureControlAtRow:row]; + + [control setFrame:frame]; } } } } +/*! + @ignore +*/ - (void)_loadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns { [super _loadDataViewsInRows:rows columns:columns]; @@ -838,27 +1124,22 @@ CPOutlineViewDropOnItemIndex = -1; if (!isExpandable) continue; - var control = [self _dequeueDisclosureControl], - frame = [control frame], - dataViewFrame = [self frameOfDataViewAtColumn:outlineColumn row:row]; - - frame.origin.x = _indentationMarkerFollowsDataView ? _CGRectGetMinX(dataViewFrame) - _CGRectGetWidth(frame) : 0.0; - frame.origin.y = _CGRectGetMinY(dataViewFrame); - frame.size.height = _CGRectGetHeight(dataViewFrame); - // FIXME: center instead? - //frame.origin.y = _CGRectGetMidY(dataViewFrame) - _CGRectGetHeight(frame) / 2.0; + var control = [self _dequeueDisclosureControl]; _disclosureControlsForRows[row] = control; [control setState:[self isItemExpanded:item] ? CPOnState : CPOffState]; var selector = [self isRowSelected:row] ? @"setThemeState:" : @"unsetThemeState:"; [control performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelected]; - [control setFrame:frame]; + [control setFrame:[self frameOfOutlineDisclosureControlAtRow:row]]; [self addSubview:control]; } } +/*! + @ignore +*/ - (void)_unloadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns { [super _unloadDataViewsInRows:rows columns:columns]; @@ -891,6 +1172,9 @@ CPOutlineViewDropOnItemIndex = -1; } } +/*! + @ignore +*/ - (void)_toggleFromDisclosureControl:(CPControl)aControl { var controlFrame = [aControl frame], @@ -903,11 +1187,17 @@ CPOutlineViewDropOnItemIndex = -1; [self expandItem:item]; } +/*! + @ignore +*/ - (void)_enqueueDisclosureControl:(CPControl)aControl { _disclosureControlQueue.push(aControl); } +/*! + @ignore +*/ - (CPControl)_dequeueDisclosureControl { if (_disclosureControlQueue.length) @@ -927,6 +1217,9 @@ CPOutlineViewDropOnItemIndex = -1; return disclosureControl; } +/*! + @ignore +*/ - (void)_noteSelectionIsChanging { [[CPNotificationCenter defaultCenter] @@ -935,6 +1228,9 @@ CPOutlineViewDropOnItemIndex = -1; userInfo:nil]; } +/*! + @ignore +*/ - (void)_noteSelectionDidChange { [[CPNotificationCenter defaultCenter] @@ -943,6 +1239,9 @@ CPOutlineViewDropOnItemIndex = -1; userInfo:nil]; } +/*! + @ignore +*/ - (void)_noteItemWillExpand:(id)item { [[CPNotificationCenter defaultCenter] @@ -951,6 +1250,9 @@ CPOutlineViewDropOnItemIndex = -1; userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]]; } +/*! + @ignore +*/ - (void)_noteItemDidExpand:(id)item { [[CPNotificationCenter defaultCenter] @@ -959,6 +1261,9 @@ CPOutlineViewDropOnItemIndex = -1; userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]]; } +/*! + @ignore +*/ - (void)_noteItemWillCollapse:(id)item { [[CPNotificationCenter defaultCenter] @@ -967,6 +1272,9 @@ CPOutlineViewDropOnItemIndex = -1; userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]]; } +/*! + @ignore +*/ - (void)_noteItemDidCollapse:(id)item { [[CPNotificationCenter defaultCenter]