![]() |
API 0.9.5
|
00001 /* 00002 * CPBrowser.j 00003 * AppKit 00004 * 00005 * Created by Ross Boucher. 00006 * Copyright 2010, 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 00030 @implementation CPBrowser : CPControl 00031 { 00032 id _delegate; 00033 CPString _pathSeparator; 00034 00035 CPView _contentView; 00036 CPScrollView _horizontalScrollView; 00037 CPView _prototypeView; 00038 00039 CPArray _tableViews; 00040 CPArray _tableDelegates; 00041 00042 id _rootItem; 00043 00044 BOOL _delegateSupportsImages; 00045 00046 SEL _doubleAction; 00047 00048 BOOL _allowsMultipleSelection; 00049 BOOL _allowsEmptySelection; 00050 00051 Class _tableViewClass; 00052 00053 float _rowHeight; 00054 float _imageWidth; 00055 float _leafWidth; 00056 float _minColumnWidth; 00057 float _defaultColumnWidth; 00058 00059 CPArray _columnWidths; 00060 } 00061 00062 + (CPImage)branchImage 00063 { 00064 return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]] 00065 pathForResource:"browser-leaf.png"] 00066 size:CGSizeMake(9,9)]; 00067 } 00068 00069 + (CPImage)highlightedBranchImage 00070 { 00071 return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]] 00072 pathForResource:"browser-leaf-highlighted.png"] 00073 size:CGSizeMake(9,9)]; 00074 } 00075 00076 - (id)initWithFrame:(CGRect)aFrame 00077 { 00078 if (self = [super initWithFrame:aFrame]) 00079 { 00080 [self _init]; 00081 } 00082 00083 return self; 00084 } 00085 00086 - (void)_init 00087 { 00088 _rowHeight = 23.0; 00089 _defaultColumnWidth = 140.0; 00090 _minColumnWidth = 80.0; 00091 _imageWidth = 23.0; 00092 _leafWidth = 13.0; 00093 _columnWidths = []; 00094 00095 _pathSeparator = "/"; 00096 _tableViews = []; 00097 _tableDelegates = []; 00098 _allowsMultipleSelection = YES; 00099 _allowsEmptySelection = YES; 00100 _tableViewClass = [_CPBrowserTableView class]; 00101 00102 _prototypeView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()]; 00103 [_prototypeView setVerticalAlignment:CPCenterVerticalTextAlignment]; 00104 [_prototypeView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateSelectedDataView]; 00105 [_prototypeView setLineBreakMode:CPLineBreakByTruncatingTail]; 00106 00107 _horizontalScrollView = [[CPScrollView alloc] initWithFrame:[self bounds]]; 00108 00109 [_horizontalScrollView setHasVerticalScroller:NO]; 00110 [_horizontalScrollView setAutohidesScrollers:YES]; 00111 [_horizontalScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; 00112 00113 _contentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 0, CGRectGetHeight([self bounds]))]; 00114 [_contentView setAutoresizingMask:CPViewHeightSizable]; 00115 00116 [_horizontalScrollView setDocumentView:_contentView]; 00117 00118 [self addSubview:_horizontalScrollView]; 00119 } 00120 00121 - (void)setPrototypeView:(CPView)aPrototypeView 00122 { 00123 _prototypeView = [CPKeyedUnarchiver unarchiveObjectWithData: 00124 [CPKeyedArchiver archivedDataWithRootObject:aPrototypeView]]; 00125 } 00126 00127 - (CPView)prototypeView 00128 { 00129 return [CPKeyedUnarchiver unarchiveObjectWithData: 00130 [CPKeyedArchiver archivedDataWithRootObject:_prototypeView]]; 00131 } 00132 00133 - (void)setDelegate:(id)anObject 00134 { 00135 _delegate = anObject; 00136 _delegateSupportsImages = [_delegate respondsToSelector:@selector(browser:imageValueForItem:)]; 00137 00138 [self loadColumnZero]; 00139 } 00140 00141 - (id)delegate 00142 { 00143 return _delegate; 00144 } 00145 00146 - (CPTableView)tableViewInColumn:(unsigned)index 00147 { 00148 return _tableViews[index]; 00149 } 00150 00151 - (unsigned)columnOfTableView:(CPTableView)aTableView 00152 { 00153 return [_tableViews indexOfObject:aTableView]; 00154 } 00155 00156 - (void)loadColumnZero 00157 { 00158 if ([_delegate respondsToSelector:@selector(rootItemForBrowser:)]) 00159 _rootItem = [_delegate rootItemForBrowser:self]; 00160 else 00161 _rootItem = nil; 00162 00163 [self setLastColumn:-1]; 00164 [self addColumn]; 00165 } 00166 00167 - (void)setLastColumn:(int)columnIndex 00168 { 00169 if (columnIndex >= _tableViews.length) 00170 return; 00171 00172 var oldValue = _tableViews.length - 1, 00173 indexPlusOne = columnIndex + 1; // unloads all later columns. 00174 00175 [[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"] 00176 makeObjectsPerformSelector:@selector(removeFromSuperview)]; 00177 00178 _tableViews = _tableViews.slice(0, indexPlusOne); 00179 _tableDelegates = _tableDelegates.slice(0, indexPlusOne); 00180 00181 if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:toColumn:)]) 00182 [_delegate browser:self didChangeLastColumn:oldValue toColumn:columnIndex]; 00183 00184 [self tile]; 00185 } 00186 00187 - (int)lastColumn 00188 { 00189 return _tableViews.length - 1; 00190 } 00191 00192 - (void)addColumn 00193 { 00194 var lastIndex = [self lastColumn], 00195 lastColumn = _tableViews[lastIndex], 00196 selectionIndexes = [lastColumn selectedRowIndexes]; 00197 00198 if (lastIndex >= 0 && [selectionIndexes count] > 1) 00199 [CPException raise:CPInvalidArgumentException 00200 reason:"Can't add column, column "+lastIndex+" has invalid selection."]; 00201 00202 var index = lastIndex + 1, 00203 item = index === 0 ? _rootItem : [_tableDelegates[lastIndex] childAtIndex:[selectionIndexes firstIndex]]; 00204 00205 if (index > 0 && item && [self isLeafItem:item]) 00206 return; 00207 00208 var table = [[_tableViewClass alloc] initWithFrame:CGRectMakeZero() browser:self]; 00209 00210 [table setHeaderView:nil]; 00211 [table setCornerView:nil]; 00212 [table setAllowsMultipleSelection:_allowsMultipleSelection]; 00213 [table setAllowsEmptySelection:_allowsEmptySelection]; 00214 [table registerForDraggedTypes:[self registeredDraggedTypes]]; 00215 00216 [self _addTableColumnsToTableView:table forColumnIndex:index]; 00217 00218 var delegate = [[_CPBrowserTableDelegate alloc] init]; 00219 00220 [delegate _setDelegate:_delegate]; 00221 [delegate _setBrowser:self]; 00222 [delegate _setIndex:index]; 00223 [delegate _setItem:item]; 00224 00225 _tableViews[index] = table; 00226 _tableDelegates[index] = delegate; 00227 00228 [table setDelegate:delegate]; 00229 [table setDataSource:delegate]; 00230 [table setTarget:delegate]; 00231 [table setAction:@selector(_tableViewClicked:)]; 00232 [table setDoubleAction:@selector(_tableViewDoubleClicked:)]; 00233 [table setDraggingDestinationFeedbackStyle:CPTableViewDraggingDestinationFeedbackStyleRegular]; 00234 00235 var scrollView = [[_CPBrowserScrollView alloc] initWithFrame:CGRectMakeZero()]; 00236 [scrollView _setBrowser:self]; 00237 [scrollView setDocumentView:table]; 00238 [scrollView setHasHorizontalScroller:NO]; 00239 [scrollView setAutoresizingMask:CPViewHeightSizable]; 00240 00241 [_contentView addSubview:scrollView]; 00242 00243 [self tile]; 00244 00245 [self scrollColumnToVisible:index]; 00246 } 00247 00248 - (void)_addTableColumnsToTableView:(CPTableView)aTableView forColumnIndex:(unsigned)index 00249 { 00250 if (_delegateSupportsImages) 00251 { 00252 var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"], 00253 view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; 00254 00255 [view setImageScaling:CPScaleProportionally]; 00256 00257 [column setDataView:view]; 00258 [column setResizingMask:CPTableColumnNoResizing]; 00259 00260 [aTableView addTableColumn:column]; 00261 } 00262 00263 var column = [[CPTableColumn alloc] initWithIdentifier:@"Content"]; 00264 00265 [column setDataView:_prototypeView]; 00266 [column setResizingMask:CPTableColumnNoResizing]; 00267 00268 [aTableView addTableColumn:column]; 00269 00270 var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"], 00271 view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()]; 00272 00273 [view setBranchImage:[[self class] branchImage]]; 00274 [view setHighlightedBranchImage:[[self class] highlightedBranchImage]]; 00275 00276 [column setDataView:view]; 00277 [column setResizingMask:CPTableColumnNoResizing]; 00278 00279 [aTableView addTableColumn:column]; 00280 } 00281 00282 - (void)reloadColumn:(int)column 00283 { 00284 [[self tableViewInColumn:column] reloadData]; 00285 } 00286 00287 - (void)tile 00288 { 00289 var xOrigin = 0, 00290 scrollerWidth = [CPScroller scrollerWidth], 00291 height = CGRectGetHeight([_contentView bounds]); 00292 00293 for (var i = 0, count = _tableViews.length; i < count; i++) 00294 { 00295 var tableView = _tableViews[i], 00296 scrollView = [tableView enclosingScrollView], 00297 width = [self widthOfColumn:i], 00298 tableHeight = CGRectGetHeight([tableView bounds]); 00299 00300 [[tableView tableColumnWithIdentifier:"Image"] setWidth:_imageWidth]; 00301 [[tableView tableColumnWithIdentifier:"Content"] setWidth:[self columnContentWidthForColumnWidth:width]]; 00302 [[tableView tableColumnWithIdentifier:"Leaf"] setWidth:_leafWidth]; 00303 00304 [tableView setRowHeight:_rowHeight]; 00305 [tableView setFrameSize:CGSizeMake(width - scrollerWidth, tableHeight)]; 00306 [scrollView setFrameOrigin:CGPointMake(xOrigin, 0)]; 00307 [scrollView setFrameSize:CGSizeMake(width, height)]; 00308 00309 xOrigin += width; 00310 } 00311 00312 [_contentView setFrameSize:CGSizeMake(xOrigin, height)]; 00313 } 00314 00315 - (unsigned)rowAtPoint:(CGPoint)aPoint 00316 { 00317 var column = [self columnAtPoint:aPoint]; 00318 if (column === -1) 00319 return -1; 00320 00321 var tableView = _tableViews[column]; 00322 return [tableView rowAtPoint:[tableView convertPoint:aPoint fromView:self]]; 00323 } 00324 00325 - (unsigned)columnAtPoint:(CGPoint)aPoint 00326 { 00327 var adjustedPoint = [_contentView convertPoint:aPoint fromView:self]; 00328 00329 for (var i = 0, count = _tableViews.length; i < count; i++) 00330 { 00331 var frame = [[_tableViews[i] enclosingScrollView] frame]; 00332 if (CGRectContainsPoint(frame, adjustedPoint)) 00333 return i; 00334 } 00335 00336 return -1; 00337 } 00338 00339 - (CGRect)rectOfRow:(unsigned)aRow inColumn:(unsigned)aColumn 00340 { 00341 var tableView = _tableViews[aColumn], 00342 rect = [tableView rectOfRow:aRow]; 00343 00344 rect.origin = [self convertPoint:rect.origin fromView:tableView]; 00345 return rect; 00346 } 00347 00348 // ITEMS 00349 00350 - (id)itemAtRow:(int)row inColumn:(int)column 00351 { 00352 return [_tableDelegates[column] childAtIndex:row]; 00353 } 00354 00355 - (BOOL)isLeafItem:(id)item 00356 { 00357 return [_delegate respondsToSelector:@selector(browser:isLeafItem:)] && [_delegate browser:self isLeafItem:item]; 00358 } 00359 00360 - (id)parentForItemsInColumn:(int)column 00361 { 00362 return [_tableDelegates[column] _item]; 00363 } 00364 00365 - (CPSet)selectedItems 00366 { 00367 var selectedColumn = [self selectedColumn], 00368 selectedIndexes = [self selectedRowIndexesInColumn:selectedColumn], 00369 set = [CPSet set], 00370 index = [selectedIndexes firstIndex]; 00371 00372 while (index !== CPNotFound) 00373 { 00374 [set addObject:[self itemAtRow:index inColumn:selectedColumn]]; 00375 index = [selectedIndexes indexGreaterThanIndex:index]; 00376 } 00377 00378 return set; 00379 } 00380 00381 - (id)selectedItem 00382 { 00383 var selectedColumn = [self selectedColumn], 00384 selectedRow = [self selectedRowInColumn:selectedColumn]; 00385 00386 return [self itemAtRow:selectedRow inColumn:selectedColumn]; 00387 } 00388 00389 // CLICK EVENTS 00390 00391 - (void)trackMouse:(CPEvent)anEvent 00392 { 00393 } 00394 00395 - (void)_column:(unsigned)columnIndex clickedRow:(unsigned)rowIndex 00396 { 00397 [self setLastColumn:columnIndex]; 00398 00399 if (rowIndex >= 0) 00400 [self addColumn]; 00401 00402 [self doClick:self]; 00403 } 00404 00405 - (void)sendAction 00406 { 00407 [self sendAction:_action to:_target]; 00408 } 00409 00410 - (void)doClick:(id)sender 00411 { 00412 [self sendAction:_action to:_target]; 00413 } 00414 00415 - (void)doDoubleClick:(id)sender 00416 { 00417 [self sendAction:_doubleAction to:_target]; 00418 } 00419 00420 - (void)keyDown:(CPEvent)anEvent 00421 { 00422 var column = [self selectedColumn]; 00423 if (column === -1) 00424 return; 00425 00426 [_tableViews[column] keyDown:anEvent]; 00427 } 00428 00429 // SIZING 00430 00431 - (float)columnContentWidthForColumnWidth:(float)aWidth 00432 { 00433 var columnSpacing = [_tableViews[0] intercellSpacing].width; 00434 return aWidth - (_leafWidth + columnSpacing + (_delegateSupportsImages ? _imageWidth + columnSpacing : 0)) - columnSpacing - [CPScroller scrollerWidth]; 00435 } 00436 00437 - (float)columnWidthForColumnContentWidth:(float)aWidth 00438 { 00439 var columnSpacing = [_tableViews[0] intercellSpacing].width; 00440 return aWidth + (_leafWidth + columnSpacing + (_delegateSupportsImages ? _imageWidth + columnSpacing: 0)) + columnSpacing + [CPScroller scrollerWidth]; 00441 } 00442 00443 - (void)setImageWidth:(float)aWidth 00444 { 00445 _imageWidth = aWidth; 00446 [self tile]; 00447 } 00448 00449 - (float)imageWidth 00450 { 00451 return _imageWidth; 00452 } 00453 00454 - (void)setMinColumnWidth:(float)minWidth 00455 { 00456 _minColumnWidth = minWidth; 00457 [self tile]; 00458 } 00459 00460 - (float)minColumnWidth 00461 { 00462 return _minColumnWidth; 00463 } 00464 00465 - (void)setWidth:(float)aWidth ofColumn:(unsigned)column 00466 { 00467 _columnWidths[column] = aWidth; 00468 00469 if ([_delegate respondsToSelector:@selector(browser:didResizeColumn:)]) 00470 [_delegate browser:self didResizeColumn:column]; 00471 00472 [self tile]; 00473 } 00474 00475 - (float)widthOfColumn:(unsigned)column 00476 { 00477 var width = _columnWidths[column]; 00478 00479 if (width == null) 00480 width = _defaultColumnWidth; 00481 00482 return MAX([CPScroller scrollerWidth], MAX(_minColumnWidth, width)); 00483 } 00484 00485 - (void)setRowHeight:(float)aHeight 00486 { 00487 _rowHeight = aHeight; 00488 } 00489 00490 - (float)rowHeight 00491 { 00492 return _rowHeight; 00493 } 00494 00495 // SCROLLERS 00496 00497 - (void)scrollColumnToVisible:(unsigned)columnIndex 00498 { 00499 [_contentView scrollRectToVisible:[[[self tableViewInColumn:columnIndex] enclosingScrollView] frame]]; 00500 } 00501 00502 - (void)scrollRowToVisible:(unsigned)rowIndex inColumn:(unsigned)columnIndex 00503 { 00504 [self scrollColumnToVisible:columnIndex]; 00505 [[self tableViewInColumn:columnIndex] scrollRowToVisible:rowIndex]; 00506 } 00507 00508 - (BOOL)autohidesScroller 00509 { 00510 return [_horizontalScrollView autohidesScrollers]; 00511 } 00512 00513 - (void)setAutohidesScroller:(BOOL)shouldHide 00514 { 00515 [_horizontalScrollView setAutohidesScrollers:shouldHide]; 00516 } 00517 00518 // SELECTION 00519 00520 - (unsigned)selectedRowInColumn:(unsigned)columnIndex 00521 { 00522 if (columnIndex > [self lastColumn] || columnIndex < 0) 00523 return -1; 00524 00525 return [_tableViews[columnIndex] selectedRow]; 00526 } 00527 00528 - (unsigned)selectedColumn 00529 { 00530 var column = [self lastColumn], 00531 row = [self selectedRowInColumn:column]; 00532 00533 if (row >= 0) 00534 return column; 00535 else 00536 return column - 1; 00537 } 00538 00539 - (void)selectRow:(unsigned)row inColumn:(unsigned)column 00540 { 00541 var selectedIndexes = row === -1 ? [CPIndexSet indexSet] : [CPIndexSet indexSetWithIndex:row]; 00542 [self selectRowIndexes:selectedIndexes inColumn:column]; 00543 } 00544 00545 - (BOOL)allowsMultipleSelection 00546 { 00547 return _allowsMultipleSelection; 00548 } 00549 00550 - (void)setAllowsMultipleSelection:(BOOL)shouldAllow 00551 { 00552 if (_allowsMultipleSelection === shouldAllow) 00553 return; 00554 00555 _allowsMultipleSelection = shouldAllow; 00556 [_tableViews makeObjectsPerformSelector:@selector(setAllowsMultipleSelection:) withObject:shouldAllow]; 00557 } 00558 00559 - (BOOL)allowsEmptySelection 00560 { 00561 return _allowsEmptySelection; 00562 } 00563 00564 - (void)setAllowsEmptySelection:(BOOL)shouldAllow 00565 { 00566 if (_allowsEmptySelection === shouldAllow) 00567 return; 00568 00569 _allowsEmptySelection = shouldAllow; 00570 [_tableViews makeObjectsPerformSelector:@selector(setAllowsEmptySelection:) withObject:shouldAllow]; 00571 } 00572 00573 - (CPIndexSet)selectedRowIndexesInColumn:(unsigned)column 00574 { 00575 if (column < 0 || column > [self lastColumn] +1) 00576 return [CPIndexSet indexSet]; 00577 00578 return [[self tableViewInColumn:column] selectedRowIndexes]; 00579 } 00580 00581 - (void)selectRowIndexes:(CPIndexSet)indexSet inColumn:(unsigned)column 00582 { 00583 if (column < 0 || column > [self lastColumn] + 1) 00584 return; 00585 00586 if ([_delegate respondsToSelector:@selector(browser:selectionIndexesForProposedSelection:inColumn:)]) 00587 indexSet = [_delegate browser:self selectionIndexesForProposedSelection:indexSet inColumn:column]; 00588 00589 if ([_delegate respondsToSelector:@selector(browser:shouldSelectRowIndexes:inColumn:)] && 00590 ![_delegate browser:self shouldSelectRowIndexes:indexSet inColumn:column]) 00591 return; 00592 00593 if ([_delegate respondsToSelector:@selector(browserSelectionIsChanging:)]) 00594 [_delegate browserSelectionIsChanging:self]; 00595 00596 if (column > [self lastColumn]) 00597 [self addColumn]; 00598 00599 [self setLastColumn:column]; 00600 00601 [[self tableViewInColumn:column] selectRowIndexes:indexSet byExtendingSelection:NO]; 00602 00603 [self scrollColumnToVisible:column]; 00604 00605 if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)]) 00606 [_delegate browserSelectionDidChange:self]; 00607 } 00608 00609 - (void)setBackgroundColor:(CPColor)aColor 00610 { 00611 [super setBackgroundColor:aColor]; 00612 [_contentView setBackgroundColor:aColor]; 00613 } 00614 00615 - (BOOL)acceptsFirstResponder 00616 { 00617 return YES; 00618 } 00619 00620 // DRAG AND DROP 00621 00622 - (void)registerForDraggedTypes:(CPArray)types 00623 { 00624 [super registerForDraggedTypes:types]; 00625 [_tableViews makeObjectsPerformSelector:@selector(registerForDraggedTypes:) withObject:types]; 00626 } 00627 00628 - (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent 00629 { 00630 if ([_delegate respondsToSelector:@selector(browser:canDragRowsWithIndexes:inColumn:withEvent:)]) 00631 return [_delegate browser:self canDragRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent]; 00632 00633 return YES; 00634 } 00635 00636 - (CPImage)draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset 00637 { 00638 if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)]) 00639 return [_delegate browser:self draggingImageForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset]; 00640 00641 return nil; 00642 } 00643 00644 - (CPView)draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset 00645 { 00646 if ([_delegate respondsToSelector:@selector(browser:draggingViewForRowsWithIndexes:inColumn:withEvent:offset:)]) 00647 return [_delegate browser:self draggingViewForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset]; 00648 00649 return nil; 00650 } 00651 00652 @end 00653 00654 @implementation CPBrowser (CPCoding) 00655 00656 - (id)initWithCoder:(CPCoder)aCoder 00657 { 00658 self = [super initWithCoder:aCoder]; 00659 00660 if (self) 00661 { 00662 [self _init]; 00663 00664 _allowsEmptySelection = [aCoder decodeBoolForKey:@"CPBrowserAllowsEmptySelectionKey"]; 00665 _allowsMultipleSelection = [aCoder decodeBoolForKey:@"CPBrowserAllowsMultipleSelectionKey"]; 00666 _prototypeView = [aCoder decodeObjectForKey:@"CPBrowserPrototypeViewKey"]; 00667 _rowHeight = [aCoder decodeFloatForKey:@"CPBrowserRowHeightKey"]; 00668 _imageWidth = [aCoder decodeFloatForKey:@"CPBrowserImageWidthKey"]; 00669 _minColumnWidth = [aCoder decodeFloatForKey:@"CPBrowserMinColumnWidthKey"]; 00670 _columnWidths = [aCoder decodeObjectForKey:@"CPBrowserColumnWidthsKey"]; 00671 00672 [self setDelegate:[aCoder decodeObjectForKey:@"CPBrowserDelegateKey"]]; 00673 [self setAutohidesScroller:[aCoder decodeBoolForKey:@"CPBrowserAutohidesScrollerKey"]]; 00674 } 00675 00676 return self; 00677 } 00678 00679 - (void)encodeWithCoder:(CPCoder)aCoder 00680 { 00681 // Don't encode the subviews, they're transient and will be recreated from data. 00682 var actualSubviews = _subviews; 00683 _subviews = []; 00684 [super encodeWithCoder:aCoder]; 00685 _subviews = actualSubviews; 00686 00687 [aCoder encodeBool:[self autohidesScroller] forKey:@"CPBrowserAutohidesScrollerKey"]; 00688 [aCoder encodeBool:_allowsEmptySelection forKey:@"CPBrowserAllowsEmptySelectionKey"]; 00689 [aCoder encodeBool:_allowsMultipleSelection forKey:@"CPBrowserAllowsMultipleSelectionKey"]; 00690 [aCoder encodeObject:_delegate forKey:@"CPBrowserDelegateKey"]; 00691 [aCoder encodeObject:_prototypeView forKey:@"CPBrowserPrototypeViewKey"]; 00692 [aCoder encodeFloat:_rowHeight forKey:@"CPBrowserRowHeightKey"]; 00693 [aCoder encodeFloat:_imageWidth forKey:@"CPBrowserImageWidthKey"]; 00694 [aCoder encodeFloat:_minColumnWidth forKey:@"CPBrowserMinColumnWidthKey"]; 00695 [aCoder encodeObject:_columnWidths forKey:@"CPBrowserColumnWidthsKey"]; 00696 } 00697 00698 @end 00699 00700 00701 var _CPBrowserResizeControlBackgroundImage = nil; 00702 00703 @implementation _CPBrowserResizeControl : CPView 00704 { 00705 CGPoint _mouseDownX; 00706 CPBrowser _browser; 00707 unsigned _index; 00708 unsigned _width; 00709 } 00710 00711 + (CPImage)backgroundImage 00712 { 00713 if (!_CPBrowserResizeControlBackgroundImage) 00714 { 00715 var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"]; 00716 _CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path 00717 size:CGSizeMake(15, 14)]; 00718 } 00719 00720 return _CPBrowserResizeControlBackgroundImage; 00721 } 00722 00723 - (id)initWithFrame:(CGRect)aFrame 00724 { 00725 if (self = [super initWithFrame:aFrame]) 00726 [self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]]; 00727 00728 return self; 00729 } 00730 00731 - (void)mouseDown:(CPEvent)anEvent 00732 { 00733 _mouseDownX = [anEvent locationInWindow].x; 00734 _browser = [[self superview] _browser]; 00735 _index = [_browser columnOfTableView:[[self superview] documentView]]; 00736 _width = [_browser widthOfColumn:_index]; 00737 } 00738 00739 - (void)mouseDragged:(CPEvent)anEvent 00740 { 00741 var deltaX = [anEvent locationInWindow].x - _mouseDownX; 00742 [_browser setWidth:_width + deltaX ofColumn:_index]; 00743 } 00744 00745 - (void)mouseUp:(CPEvent)anEvent 00746 { 00747 } 00748 00749 @end 00750 00751 @implementation _CPBrowserScrollView : CPScrollView 00752 { 00753 _CPBrowserResizeControl _resizeControl; 00754 CPBrowser _browser; 00755 } 00756 00757 - (void)initWithFrame:(CGRect)aFrame 00758 { 00759 if (self = [super initWithFrame:aFrame]) 00760 { 00761 _resizeControl = [[_CPBrowserResizeControl alloc] initWithFrame:CGRectMakeZero()]; 00762 [self addSubview:_resizeControl]; 00763 } 00764 00765 return self; 00766 } 00767 00768 - (void)reflectScrolledClipView:(CPClipView)aClipView 00769 { 00770 [super reflectScrolledClipView:aClipView]; 00771 00772 var frame = [_verticalScroller frame]; 00773 frame.size.height = CGRectGetHeight([self bounds]) - 14.0 - frame.origin.y; 00774 [_verticalScroller setFrameSize:frame.size]; 00775 00776 var resizeFrame = CGRectMake(CGRectGetMinX(frame), CGRectGetMaxY(frame), [CPScroller scrollerWidth], 14.0); 00777 [_resizeControl setFrame:resizeFrame]; 00778 } 00779 00780 @end 00781 00782 @implementation _CPBrowserTableView : CPTableView 00783 { 00784 CPBrowser _browser; 00785 } 00786 00787 - (id)initWithFrame:(CGRect)aFrame browser:(CPBrowser)aBrowser 00788 { 00789 if (self = [super initWithFrame:aFrame]) 00790 _browser = aBrowser; 00791 00792 return self; 00793 } 00794 00795 - (BOOL)acceptsFirstResponder 00796 { 00797 return NO; 00798 } 00799 00800 - (void)mouseDown:(CPEvent)anEvent 00801 { 00802 [super mouseDown:anEvent]; 00803 [[self window] makeFirstResponder:_browser]; 00804 } 00805 00806 - (CPView)browserView 00807 { 00808 return _browser; 00809 } 00810 00811 - (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint 00812 { 00813 return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]]; 00814 } 00815 00816 - (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset 00817 { 00818 return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] || 00819 [super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset]; 00820 } 00821 00822 - (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPoint)dragViewOffset 00823 { 00824 var count = theTableColumns.length; 00825 while (count--) 00826 { 00827 if ([theTableColumns[count] identifier] === "Leaf") 00828 [theTableColumns removeObject:theTableColumns[count]]; 00829 } 00830 00831 return [_browser draggingViewForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragViewOffset] || 00832 [super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset]; 00833 } 00834 00835 - (void)moveUp:(id)sender 00836 { 00837 [super moveUp:sender]; 00838 [_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]]; 00839 } 00840 00841 - (void)moveDown:(id)sender 00842 { 00843 [super moveDown:sender]; 00844 [_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]]; 00845 } 00846 00847 - (void)moveLeft:(id)sender 00848 { 00849 var previousColumn = [_browser selectedColumn] - 1, 00850 selectedRow = [_browser selectedRowInColumn:previousColumn]; 00851 00852 [_browser selectRow:selectedRow inColumn:previousColumn]; 00853 } 00854 00855 - (void)moveRight:(id)sender 00856 { 00857 [_browser selectRow:0 inColumn:[_browser selectedColumn] + 1]; 00858 } 00859 00860 @end 00861 00862 00863 @implementation _CPBrowserTableDelegate : CPObject 00864 { 00865 CPBrowser _browser; 00866 unsigned _index; 00867 id _delegate; 00868 id _item; 00869 } 00870 00871 - (unsigned)numberOfRowsInTableView:(CPTableView)aTableView 00872 { 00873 return [_delegate browser:_browser numberOfChildrenOfItem:_item]; 00874 } 00875 00876 - (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)column row:(unsigned)row 00877 { 00878 if ([column identifier] === "Image") 00879 return [_delegate browser:_browser imageValueForItem:[self childAtIndex:row]]; 00880 else if ([column identifier] === "Leaf") 00881 return ![_browser isLeafItem:[self childAtIndex:row]]; 00882 else 00883 return [_delegate browser:_browser objectValueForItem:[self childAtIndex:row]]; 00884 } 00885 00886 - (void)_tableViewDoubleClicked:(CPTableView)aTableView 00887 { 00888 [_browser doDoubleClick:self]; 00889 } 00890 00891 - (void)_tableViewClicked:(CPTableView)aTableView 00892 { 00893 var selectedIndexes = [aTableView selectedRowIndexes]; 00894 [_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1]; 00895 } 00896 00897 - (id)childAtIndex:(unsigned)index 00898 { 00899 return [_delegate browser:_browser child:index ofItem:_item]; 00900 } 00901 00902 - (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id)info row:(int)row dropOperation:(CPTableViewDropOperation)operation 00903 { 00904 if ([_delegate respondsToSelector:@selector(browser:acceptDrop:atRow:column:dropOperation:)]) 00905 return [_delegate browser:_browser acceptDrop:info atRow:row column:_index dropOperation:operation]; 00906 else 00907 return NO; 00908 } 00909 00910 - (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id)info proposedRow:(int)row proposedDropOperation:(CPTableViewDropOperation)operation 00911 { 00912 if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)]) 00913 return [_delegate browser:_browser validateDrop:info proposedRow:row column:_index dropOperation:operation]; 00914 else 00915 return CPDragOperationNone; 00916 } 00917 00918 - (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard 00919 { 00920 if ([_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)]) 00921 return [_delegate browser:_browser writeRowsWithIndexes:rowIndexes inColumn:_index toPasteboard:pboard]; 00922 else 00923 return NO; 00924 } 00925 00926 - (BOOL)respondsToSelector:(SEL)aSelector 00927 { 00928 if (aSelector === @selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)) 00929 return [_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)]; 00930 else 00931 return [super respondsToSelector:aSelector]; 00932 } 00933 00934 @end 00935 00936 @implementation _CPBrowserLeafView : CPView 00937 { 00938 BOOL _isLeaf; 00939 CPImage _branchImage; 00940 CPImage _highlightedBranchImage; 00941 } 00942 00943 - (BOOL)objectValue 00944 { 00945 return _isLeaf; 00946 } 00947 00948 - (void)setObjectValue:(id)aValue 00949 { 00950 _isLeaf = !!aValue; 00951 [self setNeedsLayout]; 00952 } 00953 00954 - (CGRect)rectForEphemeralSubviewNamed:(CPString)aName 00955 { 00956 if (aName === "image-view") 00957 return CGRectInset([self bounds], 1, 1); 00958 00959 return [super rectForEphemeralSubviewNamed:aName]; 00960 } 00961 00962 - (CPView)createEphemeralSubviewNamed:(CPString)aName 00963 { 00964 if (aName === "image-view") 00965 return [[CPImageView alloc] initWithFrame:CGRectMakeZero()]; 00966 00967 return [super createEphemeralSubviewNamed:aName]; 00968 } 00969 00970 - (void)layoutSubviews 00971 { 00972 var imageView = [self layoutEphemeralSubviewNamed:@"image-view" 00973 positioned:CPWindowAbove 00974 relativeToEphemeralSubviewNamed:nil], 00975 isHighlighted = [self themeState] & CPThemeStateSelectedDataView; 00976 00977 [imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil]; 00978 [imageView setImageScaling:CPScaleNone]; 00979 } 00980 00981 - (void)encodeWithCoder:(CPCoder)aCoder 00982 { 00983 [super encodeWithCoder:aCoder]; 00984 00985 [aCoder encodeBool:_isLeaf forKey:"_CPBrowserLeafViewIsLeafKey"]; 00986 [aCoder encodeObject:_branchImage forKey:"_CPBrowserLeafViewBranchImageKey"]; 00987 [aCoder encodeObject:_highlightedBranchImage forKey:"_CPBrowserLeafViewHighlightedBranchImageKey"]; 00988 } 00989 00990 - (void)initWithCoder:(CPCoder)aCoder 00991 { 00992 if (self = [super initWithCoder:aCoder]) 00993 { 00994 _isLeaf = [aCoder decodeBoolForKey:"_CPBrowserLeafViewIsLeafKey"]; 00995 _branchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewBranchImageKey"]; 00996 _highlightedBranchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewHighlightedBranchImageKey"]; 00997 } 00998 00999 return self; 01000 } 01001 01002 @end 01003 01004 @implementation CPBrowser (CPSynthesizedAccessors) 01005 01009 - (SEL)doubleAction 01010 { 01011 return _doubleAction; 01012 } 01013 01017 - (void)setDoubleAction:(SEL)aValue 01018 { 01019 _doubleAction = aValue; 01020 } 01021 01025 - (Class)tableViewClass 01026 { 01027 return _tableViewClass; 01028 } 01029 01033 - (void)setTableViewClass:(Class)aValue 01034 { 01035 _tableViewClass = aValue; 01036 } 01037 01041 - (float)defaultColumnWidth 01042 { 01043 return _defaultColumnWidth; 01044 } 01045 01049 - (void)setDefaultColumnWidth:(float)aValue 01050 { 01051 _defaultColumnWidth = aValue; 01052 } 01053 01054 @end