From 73e18d30dbb964ddad104490fc6b2047c07e87fd Mon Sep 17 00:00:00 2001 From: Allan Odgaard Date: Fri, 11 Jan 2013 13:36:38 +0100 Subject: [PATCH] Merge OFBView into main controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using an NSView subclass wasn’t justified. --- .../OakFileBrowser/src/OakFileBrowser.h | 5 +- .../OakFileBrowser/src/OakFileBrowser.mm | 369 ++++++++---------- .../src/ui/OakFileBrowserView.h | 33 -- .../src/ui/OakFileBrowserView.mm | 175 --------- 4 files changed, 153 insertions(+), 429 deletions(-) delete mode 100644 Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.h delete mode 100644 Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.mm diff --git a/Frameworks/OakFileBrowser/src/OakFileBrowser.h b/Frameworks/OakFileBrowser/src/OakFileBrowser.h index 17db6d46..935abec7 100644 --- a/Frameworks/OakFileBrowser/src/OakFileBrowser.h +++ b/Frameworks/OakFileBrowser/src/OakFileBrowser.h @@ -9,6 +9,7 @@ PUBLIC @interface OakFileBrowser : NSResponder @property (nonatomic, weak) id delegate; +@property (nonatomic, readonly) NSView* view; @property (nonatomic, readonly) NSString* location; @property (nonatomic, readonly) NSArray* selectedURLs; @property (nonatomic) NSArray* openURLs; @@ -32,7 +33,3 @@ PUBLIC @interface OakFileBrowser : NSResponder - (IBAction)goToSCMDataSource:(id)sender; - (IBAction)orderFrontGoToFolder:(id)sender; @end - -@interface OakFileBrowser (WorkaroundForUsingNSViewSubclassInImplementation) -@property (nonatomic, readonly) NSView* view; -@end diff --git a/Frameworks/OakFileBrowser/src/OakFileBrowser.mm b/Frameworks/OakFileBrowser/src/OakFileBrowser.mm index 85f8e357..bd5cf593 100644 --- a/Frameworks/OakFileBrowser/src/OakFileBrowser.mm +++ b/Frameworks/OakFileBrowser/src/OakFileBrowser.mm @@ -1,7 +1,7 @@ #import "OakFileBrowser.h" #import "OakFSUtilities.h" #import "OakHistoryController.h" -#import "ui/OakFileBrowserView.h" +#import "ui/OFBHeaderView.h" #import "ui/OFBOutlineView.h" #import "ui/OFBPathInfoCell.h" #import "io/FSDataSource.h" @@ -40,12 +40,15 @@ OAK_DEBUG_VAR(FileBrowser_Controller); @property (nonatomic) OakHistoryController* historyController; @property (nonatomic, readwrite, copy) NSURL* url; @property (nonatomic) FSOutlineViewDelegate* outlineViewDelegate; -@property (nonatomic, readwrite) OakFileBrowserView* view; +@property (nonatomic, readwrite) NSView* view; +@property (nonatomic) OFBHeaderView* headerView; +@property (nonatomic) OFBOutlineView* outlineView; @property (nonatomic) NSUInteger dataSourceOptions; @property (nonatomic, readonly) NSArray* selectedItems; @property (nonatomic, readonly) NSArray* selectedPaths; - (void)updateView; - (void)loadFileBrowserOptions; +- (NSRect)iconFrameForEntry:(id)anEntry; @end static NSString* const kUserDefaultsFileBrowserDataSourceOptions = @"FileBrowser DataSourceOptions"; @@ -82,9 +85,9 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (NSArray*)selectedItems { NSMutableArray* res = [NSMutableArray array]; - NSIndexSet* indexSet = [_view.outlineView selectedRowIndexes]; + NSIndexSet* indexSet = [_outlineView selectedRowIndexes]; for(NSUInteger index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex:index]) - [res addObject:[_view.outlineView itemAtRow:index]]; + [res addObject:[_outlineView itemAtRow:index]]; return res; } @@ -106,7 +109,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (void)reload:(id)sender { - [_historyController setCurrentURLScrollOffset:NSMinY([_view.outlineView visibleRect])]; + [_historyController setCurrentURLScrollOffset:NSMinY([_outlineView visibleRect])]; _outlineViewDelegate.dataSource = DataSourceForURL(_url, _dataSourceOptions); [_outlineViewDelegate scrollToOffset:_historyController.currentURLScrollOffset]; } @@ -116,7 +119,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot if(_outlineViewDelegate.dataSource && [_url isEqualTo:aURL]) return; - [_historyController setCurrentURLScrollOffset:NSMinY([_view.outlineView visibleRect])]; + [_historyController setCurrentURLScrollOffset:NSMinY([_outlineView visibleRect])]; _url = aURL; _outlineViewDelegate.dataSource = DataSourceForURL(aURL, _dataSourceOptions); [self updateView]; @@ -142,8 +145,8 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot else { BOOL alreadyVisible = NO; - for(NSInteger row = 0; !alreadyVisible && row < [_view.outlineView numberOfRows]; ++row) - alreadyVisible = [aURL isEqualTo:[[_view.outlineView itemAtRow:row] url]]; + for(NSInteger row = 0; !alreadyVisible && row < [_outlineView numberOfRows]; ++row) + alreadyVisible = [aURL isEqualTo:[[_outlineView itemAtRow:row] url]]; if(!alreadyVisible) [self pushURL:ParentForURL(aURL)]; @@ -166,7 +169,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (void)deselectAll:(id)sender { - [_view.outlineView deselectAll:sender]; + [_outlineView deselectAll:sender]; } - (NSArray*)openURLs @@ -192,8 +195,8 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot // make sure all items are accounted for // if the counts are equal, all items are in view and no need re-index folders if([updateRows count] == [symmetricDifference count]) - [_view.outlineView reloadDataForRowIndexes:updateRows columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - else [_view.outlineView reloadData]; + [_outlineView reloadDataForRowIndexes:updateRows columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + else [_outlineView reloadData]; } - (NSArray*)modifiedURLs @@ -218,18 +221,18 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot // make sure all items are accounted for // if the counts are equal, all items are in view and no need re-index folders if([updateRows count] == [symmetricDifference count]) - [_view.outlineView reloadDataForRowIndexes:updateRows columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - else [_view.outlineView reloadData]; + [_outlineView reloadDataForRowIndexes:updateRows columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + else [_outlineView reloadData]; } - (NSIndexSet*)indexSetforURLs:(NSSet*)urls { // make a note of files in view, with changed open state NSMutableIndexSet* updateRows = [NSMutableIndexSet indexSet]; - NSInteger len = [_view.outlineView numberOfRows]; + NSInteger len = [_outlineView numberOfRows]; for(int rowIndex = 0; rowIndex < len ; rowIndex++) { - NSURL* file = [[_view.outlineView itemAtRow:rowIndex] url]; + NSURL* file = [[_outlineView itemAtRow:rowIndex] url]; if([urls containsObject:file]) { [updateRows addIndex:rowIndex]; @@ -243,8 +246,8 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (NSDictionary*)sessionState { - if(_view.outlineView) - [_historyController setCurrentURLScrollOffset:NSMinY([_view.outlineView visibleRect])]; + if(_outlineView) + [_historyController setCurrentURLScrollOffset:NSMinY([_outlineView visibleRect])]; return _historyController.state; } @@ -254,7 +257,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (IBAction)didDoubleClickOutlineView:(id)sender { - NSArray* items = _view.outlineView.clickedRow != -1 ? @[ [_view.outlineView itemAtRow:_view.outlineView.clickedRow] ] : self.selectedItems; + NSArray* items = _outlineView.clickedRow != -1 ? @[ [_outlineView itemAtRow:_outlineView.clickedRow] ] : self.selectedItems; NSMutableArray* urlsToOpen = [NSMutableArray array]; NSMutableArray* itemsToAnimate = [NSMutableArray array]; @@ -295,21 +298,21 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot } for(FSItem* item in itemsToAnimate) - [OakZoomingIcon zoomIcon:[OakFileIconImage fileIconImageWithPath:item.path size:NSMakeSize(128, 128)] fromRect:[_view iconFrameForEntry:item]]; + [OakZoomingIcon zoomIcon:[OakFileIconImage fileIconImageWithPath:item.path size:NSMakeSize(128, 128)] fromRect:[self iconFrameForEntry:item]]; if([urlsToOpen count]) [_delegate fileBrowser:self openURLs:urlsToOpen]; } - (IBAction)didSingleClickOutlineView:(id)sender { - NSInteger row = [_view.outlineView clickedRow]; - NSInteger col = [_view.outlineView clickedColumn]; + NSInteger row = [_outlineView clickedRow]; + NSInteger col = [_outlineView clickedColumn]; col = row != -1 && col == -1 ? 0 : col; // Clicking a row which participates in multi-row selection causes clickedColumn to return -1 - OFBPathInfoCell* cell = (OFBPathInfoCell*)[_view.outlineView preparedCellAtColumn:col row:row]; - NSInteger hit = [cell hitTestForEvent:[NSApp currentEvent] inRect:[_view.outlineView frameOfCellAtColumn:col row:row] ofView:_view.outlineView]; + OFBPathInfoCell* cell = (OFBPathInfoCell*)[_outlineView preparedCellAtColumn:col row:row]; + NSInteger hit = [cell hitTestForEvent:[NSApp currentEvent] inRect:[_outlineView frameOfCellAtColumn:col row:row] ofView:_outlineView]; if(hit & OakImageAndTextCellHitImage) { - NSURL* itemURL = ((FSItem*)[_view.outlineView itemAtRow:row]).url; + NSURL* itemURL = ((FSItem*)[_outlineView itemAtRow:row]).url; if(([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) && [itemURL isFileURL]) [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[ itemURL ]]; @@ -317,7 +320,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot } else if(hit & OFBPathInfoCellHitCloseButton) { - FSItem* item = [_view.outlineView itemAtRow:row]; + FSItem* item = [_outlineView itemAtRow:row]; [_delegate fileBrowser:self closeURL:item.url]; } } @@ -329,7 +332,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (BOOL)canUndo { return NO; } - (BOOL)canRedo { return NO; } -- (void)editSelectedEntries:(id)sender { [_view.outlineView performEditSelectedRow:self]; } +- (void)editSelectedEntries:(id)sender { [_outlineView performEditSelectedRow:self]; } - (void)duplicateSelectedEntries:(id)sender { @@ -393,7 +396,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot if(![item.url isFileURL]) continue; // Perhaps we shouldn’t consider the selection if we encounter a non-file URL - if(!item.leaf && [_view.outlineView isItemExpanded:item]) + if(!item.leaf && [_outlineView isItemExpanded:item]) [folders addObject:item.path]; else if([_url isFileURL]) // TODO Test if parent folder is actually shown by current data source [folders addObject:[item.path stringByDeletingLastPathComponent]]; @@ -488,7 +491,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot for(FSItem* item in self.selectedItems) { if([item.url isEqual:aURL]) - return [_view iconFrameForEntry:item]; + return [self iconFrameForEntry:item]; } return NSZeroRect; } @@ -702,45 +705,9 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)setView:(OakFileBrowserView*)aView -{ - if(aView != _view) - { - if(_view) - { - _outlineViewDelegate = nil; - - if(_view.delegate == self) - _view.delegate = nil; - if(_view.persistentNextResponder == self) - _view.persistentNextResponder = nil; - } - - if(_view = aView) - { - _view.delegate = self; - _view.persistentNextResponder = self; - - _outlineViewDelegate = [FSOutlineViewDelegate new]; - _outlineViewDelegate.outlineView = _view.outlineView; - - _view.outlineView.target = self; - _view.outlineView.action = @selector(didSingleClickOutlineView:); - _view.outlineView.doubleAction = @selector(didDoubleClickOutlineView:); - _view.outlineView.menuDelegate = self; - - [_view.outlineView setDraggingSourceOperationMask:NSDragOperationCopy|NSDragOperationMove|NSDragOperationLink forLocal:YES]; - [_view.outlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; - [_view.outlineView registerForDraggedTypes:@[ NSFilenamesPboardType ]]; - - [self updateView]; - } - } -} - - (void)setupViewWithState:(NSDictionary*)fileBrowserState { - self.view = [[OakFileBrowserView alloc] initWithFrame:NSZeroRect]; + [self createViews]; _historyController.state = fileBrowserState; if(!_historyController.currentURL) [_historyController addURLToHistory:_url]; @@ -768,7 +735,7 @@ static NSMutableSet* SymmetricDifference (NSMutableSet* aSet, NSMutableSet* anot - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types { - return [_view.outlineView.dataSource outlineView:_view.outlineView writeItems:self.selectedItems toPasteboard:pboard]; + return [_outlineView.dataSource outlineView:_outlineView writeItems:self.selectedItems toPasteboard:pboard]; } // ================ @@ -791,7 +758,7 @@ static struct data_source_options_map_t { NSString* const name; NSUInteger flag; _dataSourceOptions |= it->flag; } } - +#if 0 - (void)saveFileBrowserOptions { NSMutableArray* array = [NSMutableArray array]; @@ -814,11 +781,6 @@ static struct data_source_options_map_t { NSString* const name; NSUInteger flag; [self reload:self]; } -- (IBAction)showFolderSpecificPreferences:(id)sender -{ - [_delegate fileBrowser:self openURLs:@[ [NSURL fileURLWithPath:[[_url path] stringByAppendingPathComponent:@".tm_properties"] isDirectory:NO]] ]; -} - - (IBAction)showOptionsPopUpMenu:(id)sender { NSMenu* menu = [NSMenu new]; @@ -843,18 +805,58 @@ static struct data_source_options_map_t { NSString* const name; NSUInteger flag; [_view displayMenu:menu fromHeaderColumn:fb::options selectedIndex:0 popup:NO]; } - +#endif // ======================= // = Header View Actions = // ======================= - (void)updateView { - _view.titleText = _outlineViewDelegate.dataSource.rootItem.name; - _view.titleImage = _outlineViewDelegate.dataSource.rootItem.icon; - _view.canGoBackward = _historyController.previousURL ? YES : NO; - _view.canGoForward = _historyController.nextURL ? YES : NO; - [_view setNeedsDisplay:YES]; + _headerView.goBackButton.enabled = _historyController.previousURL ? YES : NO; + _headerView.goForwardButton.enabled = _historyController.nextURL ? YES : NO; + + NSMenu* menu = [NSMenu new]; + NSMutableSet* visibleLocations = [NSMutableSet setWithObjects:kURLLocationComputer, kURLLocationHome, kURLLocationFavorites, nil]; + + // Add path hierarchy + for(NSURL* currentURL = _url; currentURL; currentURL = ParentForURL(currentURL)) + { + NSMenuItem* menuItem = [menu addItemWithTitle:DisplayName(currentURL) action:@selector(takeURLFrom:) keyEquivalent:@""]; + [menuItem setTarget:self]; + [menuItem setRepresentedObject:currentURL]; + [menuItem setImage:IconImage(currentURL)]; + [visibleLocations addObject:currentURL]; + } + + // Add recent locations + NSMutableArray* recentURLs = [NSMutableArray array]; + for(NSUInteger index = 0; index < _historyController.recentLocations.count && index < 10; ++index) + { + NSURL* recentURL = [_historyController.recentLocations objectAtIndex:index]; + if(![visibleLocations containsObject:recentURL]) + [recentURLs addObject:recentURL]; + } + + if([recentURLs count]) + { + [menu addItem:[NSMenuItem separatorItem]]; + [[menu addItemWithTitle:@"Recent Places" action:@selector(dummy:) keyEquivalent:@""] setEnabled:NO]; + + // TODO Disambiguate paths + // std::vector const& parents = path::disambiguate(recentPaths); + for(NSURL* recentURL in recentURLs) + { + NSMenuItem* menuItem = [menu addItemWithTitle:DisplayName(recentURL/*, parents[index]*/) action:@selector(takeURLFrom:) keyEquivalent:@""]; + [menuItem setTarget:self]; + [menuItem setRepresentedObject:recentURL]; + [menuItem setImage:IconImage(recentURL)]; + } + } + + [menu addItem:[NSMenuItem separatorItem]]; + [[menu addItemWithTitle:@"Other…" action:@selector(orderFrontGoToFolder:) keyEquivalent:@""] setTarget:self]; + + _headerView.folderPopUpButton.menu = menu; } - (IBAction)goToComputer:(id)sender { [self pushURL:kURLLocationComputer]; } @@ -908,154 +910,87 @@ static struct data_source_options_map_t { NSString* const name; NSUInteger flag; [self pushURL:[sender representedObject]]; } -- (IBAction)showFolderPopUpMenu:(id)sender +// =============== +// = Setup Views = +// =============== + +- (void)createViews { - NSMenu* menu = [NSMenu new]; - NSMutableSet* visibleLocations = [NSMutableSet setWithObjects:kURLLocationComputer, kURLLocationHome, kURLLocationFavorites, nil]; + _outlineView = [[OFBOutlineView alloc] initWithFrame:NSZeroRect]; + _outlineView.focusRingType = NSFocusRingTypeNone; + _outlineView.allowsMultipleSelection = YES; + _outlineView.autoresizesOutlineColumn = NO; + _outlineView.headerView = nil; + _outlineView.target = self; + _outlineView.action = @selector(didSingleClickOutlineView:); + _outlineView.doubleAction = @selector(didDoubleClickOutlineView:); + _outlineView.menuDelegate = self; - // Add path hierarchy - for(NSURL* currentURL = _url; currentURL; currentURL = ParentForURL(currentURL)) - { - NSMenuItem* menuItem = [menu addItemWithTitle:DisplayName(currentURL) action:@selector(takeURLFrom:) keyEquivalent:@""]; - [menuItem setTarget:self]; - [menuItem setRepresentedObject:currentURL]; - [menuItem setImage:IconImage(currentURL)]; - [visibleLocations addObject:currentURL]; - } + [_outlineView setDraggingSourceOperationMask:NSDragOperationCopy|NSDragOperationMove|NSDragOperationLink forLocal:YES]; + [_outlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; + [_outlineView registerForDraggedTypes:@[ NSFilenamesPboardType ]]; - // Add recent locations - NSMutableArray* recentURLs = [NSMutableArray array]; - for(NSUInteger index = 0; index < _historyController.recentLocations.count && index < 10; ++index) - { - NSURL* recentURL = [_historyController.recentLocations objectAtIndex:index]; - if(![visibleLocations containsObject:recentURL]) - [recentURLs addObject:recentURL]; - } + NSScrollView* scrollView = [NSScrollView new]; + scrollView.hasVerticalScroller = YES; + scrollView.hasHorizontalScroller = NO; + scrollView.borderType = NSNoBorder; + scrollView.documentView = _outlineView; - if([recentURLs count]) - { - [menu addItem:[NSMenuItem separatorItem]]; - [[menu addItemWithTitle:@"Recent Places" action:@selector(dummy:) keyEquivalent:@""] setEnabled:NO]; + _headerView = [[OFBHeaderView alloc] initWithFrame:NSZeroRect]; + _headerView.goBackButton.target = self; + _headerView.goBackButton.action = @selector(goBack:); + _headerView.goForwardButton.target = self; + _headerView.goForwardButton.action = @selector(goForward:); - // TODO Disambiguate paths - // std::vector const& parents = path::disambiguate(recentPaths); - for(NSURL* recentURL in recentURLs) - { - NSMenuItem* menuItem = [menu addItemWithTitle:DisplayName(recentURL/*, parents[index]*/) action:@selector(takeURLFrom:) keyEquivalent:@""]; - [menuItem setTarget:self]; - [menuItem setRepresentedObject:recentURL]; - [menuItem setImage:IconImage(recentURL)]; - } - } + NSBox* bottomDividerView = [[NSBox alloc] initWithFrame:NSZeroRect]; + bottomDividerView.boxType = NSBoxCustom; + bottomDividerView.borderType = NSLineBorder; + bottomDividerView.borderColor = [NSColor blackColor]; - [menu addItem:[NSMenuItem separatorItem]]; - [[menu addItemWithTitle:@"Other…" action:@selector(orderFrontGoToFolder:) keyEquivalent:@""] setTarget:self]; + _view = [NSView new]; + [_view addSubview:_headerView]; + [_view addSubview:bottomDividerView]; + [_view addSubview:scrollView]; - [_view displayMenu:menu fromHeaderColumn:fb::title selectedIndex:0 popup:YES]; + NSCell* cell = [OFBPathInfoCell new]; + cell.lineBreakMode = NSLineBreakByTruncatingMiddle; + [cell setEditable:YES]; + + NSTableColumn* tableColumn = [NSTableColumn new]; + [tableColumn setDataCell:cell]; + [_outlineView addTableColumn:tableColumn]; + [_outlineView setOutlineTableColumn:tableColumn]; + [_outlineView sizeLastColumnToFit]; + + _outlineViewDelegate = [FSOutlineViewDelegate new]; + _outlineViewDelegate.outlineView = _outlineView; + + NSDictionary* views = @{ + @"parent" : _view, + @"header" : _headerView, + @"divider" : bottomDividerView, + @"browser" : scrollView, + }; + + for(NSView* view in [views allValues]) + [view setTranslatesAutoresizingMaskIntoConstraints:NO]; + + [_view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[header(==browser,==divider)]|" options:0 metrics:nil views:views]]; + [_view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[header][divider(==1)][browser]|" options:0 metrics:nil views:views]]; } -- (IBAction)didClickHeaderColumn:(id)sender +- (NSRect)iconFrameForEntry:(id)anEntry { - switch([sender tag]) - { - case fb::goBack: return [self goBack:sender]; - case fb::goForward: return [self goForward:sender]; - case fb::scmDataSource: return [self goToSCMDataSource:sender]; - case fb::favorites: return [self goToFavorites:sender]; - case fb::home: return [self goToHome:sender]; - case fb::computer: return [self goToComputer:sender]; - case fb::title: return [self showFolderPopUpMenu:sender]; - case fb::options: return [self showOptionsPopUpMenu:sender]; - } -} - -- (IBAction)takeHistoryIndexFrom:(id)sender -{ - ASSERT([sender respondsToSelector:@selector(tag)]); - [self setURL:[_historyController urlAtIndex:[sender tag]]]; - _historyController.historyIndex = [sender tag]; - [_outlineViewDelegate scrollToOffset:_historyController.currentURLScrollOffset]; - [self updateView]; -} - -- (void)showHistoryItems:(NSArray*)urls fromHeaderColumn:(fb::header_column)columnTag -{ - // TODO DisplayName paths - NSMutableSet* seenPaths = [NSMutableSet set]; - NSMenu* menu = [NSMenu new]; - for(NSURL* aURL in urls) - { - if([seenPaths containsObject:[aURL path]]) - continue; - if([aURL isFileURL]) - [seenPaths addObject:[aURL path]]; - - NSMenuItem* menuItem = [menu addItemWithTitle:DisplayName(aURL) action:@selector(takeHistoryIndexFrom:) keyEquivalent:@""]; - [menuItem setTag:[[aURL fragment] intValue]]; - [menuItem setTarget:self]; - [menuItem setImage:IconImage(aURL)]; - } - [_view displayMenu:menu fromHeaderColumn:columnTag selectedIndex:0 popup:NO]; -} - -- (IBAction)showBackMenu:(id)sender -{ - NSMutableArray* urls = [NSMutableArray array]; - for(NSInteger historyIndex = _historyController.historyIndex - 1; historyIndex >= 0 && _historyController.historyIndex - historyIndex <= 10; --historyIndex) - [urls addObject:[NSURL URLWithString:[[[_historyController urlAtIndex:historyIndex] absoluteString] stringByAppendingFormat:@"#%ld", historyIndex]]]; - [self showHistoryItems:urls fromHeaderColumn:fb::goBack]; -} - -- (IBAction)showForwardMenu:(id)sender -{ - NSMutableArray* urls = [NSMutableArray array]; - for(NSInteger historyIndex = _historyController.historyIndex + 1; historyIndex < _historyController.historyCount && historyIndex - _historyController.historyIndex <= 10; ++historyIndex) - [urls addObject:[NSURL URLWithString:[[[_historyController urlAtIndex:historyIndex] absoluteString] stringByAppendingFormat:@"#%ld", historyIndex]]]; - [self showHistoryItems:urls fromHeaderColumn:fb::goForward]; -} - -- (void)showFolderContents:(NSURL*)aURL inMenuFromCell:(fb::header_column)column -{ - std::multimap urls; - if([[aURL scheme] isEqualToString:[kURLLocationComputer scheme]]) - { - citerate(volume, path::volumes()) - urls.insert(std::make_pair(path::display_name(*volume), [NSURL fileURLWithPath:[NSString stringWithCxxString:*volume] isDirectory:YES])); - } - else if([aURL isFileURL]) - { - std::string const dir = [[aURL path] fileSystemRepresentation]; - citerate(entry, path::entries(dir, "*")) - { - std::string const& path = path::join(dir, (*entry)->d_name); - uint32_t flags = path::info(path); - std::string const& resolved = (flags & path::flag::symlink) ? path::resolve(path) : path; - if((flags & path::flag::directory) || ((flags & path::flag::symlink) && (path::info(resolved) & path::flag::directory))) - urls.insert(std::make_pair(path::display_name(path), [NSURL fileURLWithPath:[NSString stringWithCxxString:resolved] isDirectory:YES])); - } - } - - NSMenu* menu = [NSMenu new]; - iterate(pair, urls) - { - NSMenuItem* menuItem = [menu addItemWithTitle:[NSString stringWithCxxString:pair->first] action:@selector(takeURLFrom:) keyEquivalent:@""]; - [menuItem setTarget:self]; - [menuItem setRepresentedObject:pair->second]; - [menuItem setImage:IconImage(pair->second)]; - } - [_view displayMenu:menu fromHeaderColumn:column selectedIndex:0 popup:NO]; -} - -- (IBAction)didTriggerMenuForHeaderColumn:(id)sender -{ - switch([sender tag]) - { - case fb::goBack: return [self showBackMenu:sender]; - case fb::goForward: return [self showForwardMenu:sender]; - case fb::favorites: return [self showFolderContents:kURLLocationFavorites inMenuFromCell:fb::header_column([sender tag])]; - case fb::home: return [self showFolderContents:kURLLocationHome inMenuFromCell:fb::header_column([sender tag])]; - case fb::computer: return [self showFolderContents:kURLLocationComputer inMenuFromCell:fb::header_column([sender tag])]; - } + NSInteger rowIndex = [_outlineView rowForItem:anEntry]; + if(rowIndex == -1) + return NSZeroRect; + NSRect r = [_outlineView frameOfCellAtColumn:0 row:rowIndex]; + r.origin.x += 7.0; // FIXME some hard-coded values here + r.origin.y -= 1.0; + r.size = NSMakeSize(16, 16); + r = [_outlineView convertRect:r toView:nil]; + r.origin = [_outlineView.window convertBaseToScreen:r.origin]; + return r; } // =================== @@ -1071,7 +1006,7 @@ static struct data_source_options_map_t { NSString* const name; NSUInteger flag; else if([item action] == @selector(goForward:)) return _historyController.nextURL ? YES : NO; else if([item action] == @selector(delete:)) - return [_view.outlineView numberOfSelectedRows] > 0; + return [_outlineView numberOfSelectedRows] > 0; else if([item action] == @selector(undo:)) return [self canUndo]; else if([item action] == @selector(redo:)) diff --git a/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.h b/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.h deleted file mode 100644 index 261eb2c1..00000000 --- a/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.h +++ /dev/null @@ -1,33 +0,0 @@ -#import - -@class OFBOutlineView; - -namespace fb -{ - enum header_column { goBack = 1, goForward, title, options, scmDataSource, favorites, home, computer }; -} - -@protocol OakFileBrowserUIDelegate -- (void)didClickHeaderColumn:(id)sender; -- (void)didTriggerMenuForHeaderColumn:(id)sender; -- (void)didClickCloseIconForEntry:(id)anEntry; -- (void)renameEntry:(id)anEntry to:(NSString*)name; -@end - -@interface OakFileBrowserView : NSView -// Initial setup -@property (nonatomic, weak) id delegate; -@property (nonatomic, weak) NSResponder* persistentNextResponder; - -// Header info -@property (nonatomic, assign) BOOL canGoBackward; -@property (nonatomic, assign) BOOL canGoForward; -@property (nonatomic, retain) NSString* titleText; -@property (nonatomic, retain) NSImage* titleImage; - -// Outline view -@property (nonatomic, readonly) OFBOutlineView* outlineView; - -- (void)displayMenu:(NSMenu*)aMenu fromHeaderColumn:(fb::header_column)columnTag selectedIndex:(NSUInteger)index popup:(BOOL)popup; -- (NSRect)iconFrameForEntry:(id)anEntry; -@end diff --git a/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.mm b/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.mm deleted file mode 100644 index 3801f48d..00000000 --- a/Frameworks/OakFileBrowser/src/ui/OakFileBrowserView.mm +++ /dev/null @@ -1,175 +0,0 @@ -#import "OakFileBrowserView.h" -#import "OFBPathInfoCell.h" -#import "OFBOutlineView.h" -#import "OFBHeaderView.h" -#import -#import - -@interface OakFileBrowserView () -{ - OBJC_WATCH_LEAKS(OakFileBrowserView); - - OFBOutlineView* outlineView; - OFBHeaderView* headerView; - - // Header view - BOOL canGoBackward; - BOOL canGoForward; - - NSString* titleText; - NSImage* titleImage; -} -- (void)setupViews; -@end - -OAK_DEBUG_VAR(FileBrowser_View); - -@implementation OakFileBrowserView -@synthesize outlineView; -@synthesize canGoBackward, canGoForward, titleText, titleImage; - -// ================== -// = Setup/Teardown = -// ================== - -- (id)initWithFrame:(NSRect)aFrame -{ - if(self = [super initWithFrame:aFrame]) - { - [self setupViews]; - } - return self; -} - -- (NSSize)intrinsicContentSize -{ - return NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); -} - -- (void)setupViews -{ - ASSERT(!outlineView); - - NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect]; - scrollView.hasVerticalScroller = YES; - scrollView.hasHorizontalScroller = NO; - scrollView.borderType = NSNoBorder; - [self addSubview:scrollView]; - - outlineView = [[OFBOutlineView alloc] initWithFrame:NSMakeRect(10, 10, scrollView.contentSize.width, scrollView.contentSize.height)]; - outlineView.focusRingType = NSFocusRingTypeNone; - outlineView.allowsMultipleSelection = YES; - outlineView.autoresizesOutlineColumn = NO; - outlineView.headerView = nil; - - scrollView.documentView = outlineView; - - headerView = [[OFBHeaderView alloc] initWithFrame:NSZeroRect]; - [self addSubview:headerView]; - - NSCell* cell = [OFBPathInfoCell new]; - cell.lineBreakMode = NSLineBreakByTruncatingMiddle; - [cell setEditable:YES]; - - NSTableColumn* tableColumn = [NSTableColumn new]; - [tableColumn setDataCell:cell]; - [outlineView addTableColumn:tableColumn]; - [outlineView setOutlineTableColumn:tableColumn]; - [outlineView sizeLastColumnToFit]; - - NSDictionary* views = NSDictionaryOfVariableBindings(headerView, scrollView); - for(id key in views) - [views[key] setTranslatesAutoresizingMaskIntoConstraints:NO]; - [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headerView(==scrollView)]|" options:0 metrics:nil views:views]]; - [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headerView][scrollView]|" options:0 metrics:nil views:views]]; -} - -// ======== -// = View = -// ======== - -- (void)setPersistentNextResponder:(NSResponder*)aResponder -{ - if(_persistentNextResponder != aResponder) - { - _persistentNextResponder = aResponder; - [self viewDidMoveToSuperview]; - } -} - -- (void)setNextResponder:(NSResponder*)aResponder -{ - if(!self.persistentNextResponder) - return [super setNextResponder:aResponder]; - - if(aResponder != self.persistentNextResponder) - self.persistentNextResponder.nextResponder = aResponder; - [super setNextResponder:self.persistentNextResponder]; -} - -- (BOOL)isOpaque -{ - return YES; -} - -- (BOOL)canBecomeKeyView -{ - return NO; -} - -- (void)displayMenu:(NSMenu*)aMenu fromHeaderColumn:(fb::header_column)columnTag selectedIndex:(NSUInteger)index popup:(BOOL)popup -{ -} - -- (NSRect)iconFrameForEntry:(id)anEntry -{ - NSInteger rowIndex = [outlineView rowForItem:anEntry]; - if(rowIndex == -1) - return NSZeroRect; - NSRect r = [outlineView frameOfCellAtColumn:0 row:rowIndex]; - r.origin.x += 7.0; // FIXME some hard-coded values here - r.origin.y -= 1.0; - r.size = NSMakeSize(16, 16); - r = [outlineView convertRect:r toView:nil]; - r.origin = [outlineView.window convertBaseToScreen:r.origin]; - return r; -} - -// =============== -// = Header view = -// =============== - -static inline NSImage* Image (NSString* name) { return [NSImage imageNamed:name inSameBundleAsClass:[OakFileBrowserView class]]; } -static inline NSImage* Pressed (NSString* name) { return Image([NSString stringWithFormat:@"%@ Pressed", name]); } - -- (void)updateHeaderView -{ - headerView.goBackButton.enabled = self.canGoBackward; - headerView.goForwardButton.enabled = self.canGoForward; -} - -- (void)setCanGoBackward:(BOOL)flag { canGoBackward = flag; [self updateHeaderView]; } -- (void)setCanGoForward:(BOOL)flag { canGoForward = flag; [self updateHeaderView]; } -- (void)setTitleText:(NSString*)text { titleText = text; [self updateHeaderView]; } -- (void)setTitleImage:(NSImage*)image -{ - titleImage = [[NSImage alloc] initWithSize:NSMakeSize(15, 15)]; - [titleImage lockFocus]; - [image drawInRect:NSMakeRect(1, 1, 13, 13) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; - [titleImage unlockFocus]; - [self updateHeaderView]; -} - -- (IBAction)clickHeaderCell:(id)sender { [_delegate didClickHeaderColumn:sender]; } -- (IBAction)holdHeaderCell:(id)sender { [_delegate didTriggerMenuForHeaderColumn:sender]; } - -- (void)swipeWithEvent:(NSEvent*)anEvent -{ - if([anEvent deltaX] == +1 && [_delegate respondsToSelector:@selector(goBack:)]) - [_delegate performSelector:@selector(goBack:) withObject:self]; - else if([anEvent deltaX] == -1 && [_delegate respondsToSelector:@selector(goForward:)]) - [_delegate performSelector:@selector(goForward:) withObject:self]; - else if([anEvent deltaY] == +1 && [_delegate respondsToSelector:@selector(goToParentFolder:)]) - [_delegate performSelector:@selector(goToParentFolder:) withObject:self]; -} -@end