Merge OFBView into main controller

Using an NSView subclass wasn’t justified.
This commit is contained in:
Allan Odgaard
2013-01-11 13:36:38 +01:00
parent 87f1c109d3
commit 73e18d30db
4 changed files with 153 additions and 429 deletions

View File

@@ -9,6 +9,7 @@
PUBLIC @interface OakFileBrowser : NSResponder
@property (nonatomic, weak) id <OakFileBrowserDelegate> 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

View File

@@ -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 <rdar://10382268>
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 shouldnt 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<size_t> 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<size_t> 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<std::string, NSURL*, text::less_t> 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:))

View File

@@ -1,33 +0,0 @@
#import <oak/debug.h>
@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

View File

@@ -1,175 +0,0 @@
#import "OakFileBrowserView.h"
#import "OFBPathInfoCell.h"
#import "OFBOutlineView.h"
#import "OFBHeaderView.h"
#import <OakAppKit/NSImage Additions.h>
#import <OakAppKit/OakFileIconImage.h>
@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