//
// BrowserViewController.m
// Perforce
//
// Created by Adam Czubernat on 07.06.2013.
// Copyright (c) 2013 Perforce Software, Inc. All rights reserved.
//
#import "BrowserViewController.h"
#import "P4FileItem.h"
#import "P4DepotItem.h"
#import "P4ChangelistItem.h"
#import "P4UnreadItem.h"
#import "P4SearchItem.h"
#import "SubmitPanelController.h"
@interface BrowserViewController () {
__unsafe_unretained P4Item *lastSelectedItem;
NSString *searchQuery;
NSString *searchDirectory;
// Search
__weak IBOutlet NSBox *searchBar;
__weak IBOutlet NSTextField *searchLabel;
__weak IBOutlet NSButton *searchOptionsButton;
__weak IBOutlet NSButton *searchAllFilesButton;
__weak IBOutlet NSButton *searchWorkspaceButton;
__weak IBOutlet NSButton *searchDirectoryButton;
__weak IBOutlet NSPopUpButton *searchFilterPopUp;
__weak IBOutlet NSPopUpButton *searchTagsPopUp;
}
- (void)loadRootPath:(NSString *)path;
- (void)filteredTagsNotification:(NSNotification *)notification;
- (void)setSearchQuery:(NSString *)query;
- (void)reloadSearchBar;
// Search bar actions
- (IBAction)searchOptionsPressed:(id)sender;
- (IBAction)searchAllFilesPressed:(id)sender;
- (IBAction)searchWorkspacePressed:(id)sender;
- (IBAction)searchDirectoryPressed:(id)sender;
- (IBAction)searchFilterPopUpAction:(id)sender;
- (IBAction)searchFilterTagsAction:(id)sender;
@end
@implementation BrowserViewController
@synthesize delegate;
@synthesize searchBar;
- (id)init {
return self = [self initWithNibName:NSStringFromClass([self class]) bundle:nil];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[rootItem class] removeObserver:self];
}
- (void)loadView {
[super loadView];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(filteredTagsNotification:)
name:P4WorkspaceDefaultsTagFilterNotification
object:nil];
NSNib *nib = [[NSNib alloc] initWithNibNamed:@"BrowserSearchBar" bundle:nil];
[nib instantiateNibWithOwner:self topLevelObjects:NULL];
// Imitate click to hide more options
[self searchOptionsPressed:searchOptionsButton];
}
#pragma mark - Private
- (void)loadRootPath:(NSString *)path {
Class class;
P4Item *item = nil;
NSString *query = nil;
if ([path hasPrefix:@"//"]) {
if (![rootItem isKindOfClass:class = [P4DepotItem class]])
item = [[class alloc] init];
} else if ([path hasPrefix:@"/"]) {
if (![rootItem isKindOfClass:class = [P4FileItem class]])
item = [[class alloc] init];
} else if ([path hasPrefix:@"unread://"]) {
if (![rootItem isKindOfClass:class = [P4UnreadItem class]])
item = [[class alloc] init];
} else if ([path hasPrefix:@"changelist://nondeleted"]) {
if (![rootItem.path hasPrefix:@"changelist://nondeleted"])
item = [[P4ChangelistItem alloc] init];
[(P4ChangelistItem *)item setType:@"nondeleted"];
[(P4ChangelistItem *)item setPredicate:
[NSPredicate predicateWithFormat:@"status != 'move/delete' && status != 'delete'"]];
} else if ([path hasPrefix:@"changelist://deleted"]) {
if (![rootItem.path hasPrefix:@"changelist://deleted"])
item = [[P4ChangelistItem alloc] init];
[(P4ChangelistItem *)item setType:@"deleted"];
[(P4ChangelistItem *)item setPredicate:
[NSPredicate predicateWithFormat:@"status == 'move/delete' || status == 'delete'"]];
} else if ([path hasPrefix:@"search://"]) {
if (![rootItem isKindOfClass:class = [P4SearchItem class]])
item = [[class alloc] init];
query = [path stringByRemovingPrefix:@"search://"];
}
if (searchQuery || query)
[self setSearchQuery:query];
if (item) {
[[rootItem class] removeObserver:self];
[[item class] addObserver:self];
rootItem = item;
}
}
- (void)filteredTagsNotification:(NSNotification *)notification {
filteredTags = [[P4WorkspaceDefaults sharedInstance] filteredTags];
[self refresh];
}
- (void)setSearchQuery:(NSString *)query {
searchQuery = query;
if (!query)
searchDirectory = nil;
else if (!searchDirectory && workingItem != rootItem)
searchDirectory = workingItem.remotePath;
if (query) {
P4SearchItem *item = (id)rootItem;
if (![item isKindOfClass:[P4SearchItem class]]) {
item = [[P4SearchItem alloc] init];
[item setSearchQuery:query];
} else if (query) {
[item setSearchQuery:query];
[item reload];
}
[self setRootItem:item];
[self reloadSearchBar];
}
if ([delegate respondsToSelector:@selector(browserView:didChangeSearchQuery:)])
[delegate browserView:self didChangeSearchQuery:query];
}
- (void)reloadSearchBar {
P4SearchItem *item = (id)rootItem;
// Label
searchLabel.stringValue = [NSString stringWithFormat:@"\"%@\"", searchQuery];
// Buttons
searchAllFilesButton.state = !item.searchWorkspaceOnly;
searchWorkspaceButton.state = item.searchWorkspaceOnly;
[searchDirectoryButton setState:item.searchDirectory != nil];
[searchDirectoryButton setHidden:!searchDirectory];
[searchDirectoryButton setTitle:[NSString stringWithFormat:@"\"%@\"",
[searchDirectory lastPathComponent]]];
[searchDirectoryButton sizeToFit];
// Filter
NSPopUpButton *popup = searchFilterPopUp;
NSMutableArray *terms = [NSMutableArray array];
if (([popup itemAtIndex:1].state = item.searchFilenames))
[terms addObject:[popup itemAtIndex:1].title];
if (([popup itemAtIndex:2].state = item.searchContents))
[terms addObject:[popup itemAtIndex:2].title];
if (([popup itemAtIndex:3].state = item.searchTags))
[terms addObject:[popup itemAtIndex:3].title];
[popup itemAtIndex:0].title = [terms componentsJoinedByString:@", "];
// Tags filter
popup = searchTagsPopUp;
[popup removeAllItems];
[popup addItemWithTitle:@"-"];
[[popup itemAtIndex:0] setHidden:YES];
NSArray *searchTags = [item searchResultTags];
NSArray *searchFilteredTags = [item searchFilteredTags];
[popup addItemsWithTitles:searchFilteredTags];
[popup addItemsWithTitles:searchTags];
for (NSString *tag in searchFilteredTags)
[[popup itemWithTitle:tag] setState:NSOnState];
if (searchFilteredTags.count)
[popup itemAtIndex:0].title = [searchFilteredTags componentsJoinedByString:@", "];
else if (!searchTags.count)
[popup itemAtIndex:0].title = @"< No Tags >";
}
#pragma mark - Actions
- (IBAction)searchOptionsPressed:(NSButton *)sender {
CGRect frame = searchBar.frame;
frame.size.height = sender.state ? 92.0f : 30.0f;
searchBar.frame = frame;
}
- (IBAction)searchAllFilesPressed:(NSButton *)sender {
if (!sender.state && (sender.state = NSOnState))
return;
P4SearchItem *item = (id)rootItem;
item.searchWorkspaceOnly = NO;
[self setSearchQuery:searchQuery];
}
- (IBAction)searchWorkspacePressed:(NSButton *)sender {
if (!sender.state && (sender.state = NSOnState))
return;
P4SearchItem *item = (id)rootItem;
item.searchWorkspaceOnly = YES;
[self setSearchQuery:searchQuery];
}
- (IBAction)searchDirectoryPressed:(NSButton *)sender {
P4SearchItem *item = (id)rootItem;
item.searchDirectory = sender.state ? searchDirectory : nil;
[self setSearchQuery:searchQuery];
}
- (IBAction)searchFilterPopUpAction:(NSPopUpButton *)sender {
P4SearchItem *item = (id)rootItem;
NSInteger index = [sender indexOfSelectedItem];
if (index == 1)
item.searchFilenames = !item.searchFilenames;
else if (index == 2)
item.searchContents = !item.searchContents;
else if (index == 3)
item.searchTags = !item.searchTags;
[self setSearchQuery:searchQuery];
}
- (IBAction)searchFilterTagsAction:(id)sender {
P4SearchItem *item = (id)rootItem;
NSString *tag = [sender titleOfSelectedItem];
NSMutableArray *tags;
tags = [NSMutableArray arrayWithArray:[item searchFilteredTags]];
if ([tags containsObject:tag])
[tags removeObject:tag];
else
[tags addObject:tag];
[item setSearchFilteredTags:tags];
[self setSearchQuery:searchQuery];
}
#pragma mark - Public
- (P4Item *)rootItem { return rootItem; }
- (P4Item *)workingItem { return workingItem; }
- (void)setRootItem:(P4Item *)item {
filteredTags = [[P4WorkspaceDefaults sharedInstance] filteredTags];
[[rootItem class] removeObserver:self];
rootItem = item;
[self setWorkingItem:item];
[[rootItem class] addObserver:self];
if ([item isKindOfClass:[P4SearchItem class]]) {
if (!searchQuery) {
searchQuery = ((P4SearchItem *)item).searchQuery;
searchDirectory = ((P4SearchItem *)item).searchDirectory;
}
[self reloadSearchBar];
}
}
- (void)setWorkingItem:(P4Item *)item {
if (workingItem == item)
return;
workingItem = item;
PSLogStore(@"Working Item", @"%@", workingItem);
if (isLoading)
return;
if ([delegate respondsToSelector:@selector(browserView:didChangeWorkingItem:)])
[delegate browserView:self didChangeWorkingItem:workingItem];
}
- (void)setSelectedItems:(NSArray *)selectedItems {
P4Item *item = selectedItems.count ? [selectedItems objectAtIndex:0] : nil;
P4Item *parent = [item parent];
BOOL directory = [item isDirectory];
// Set current working item
[self setWorkingItem:(directory ? item : parent) ?: rootItem];
if (!directory) {
NSIndexSet *indexes = [parent.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [selectedItems indexOfObjectIdenticalTo:obj] != NSNotFound;
}];
[self setSelectedIndexes:indexes];
}
}
- (void)setSelectedIndexes:(NSIndexSet *)indexes PS_ABSTRACT_METHOD
- (void)loadPath:(NSString *)path {
[self loadRootPath:path];
P4Item *cachedItem = [rootItem cachedItemForPath:path];
if (rootItem == cachedItem)
[self setRootItem:rootItem];
else if (cachedItem)
[self setSelectedItems:@[ cachedItem ]];
else {
isLoading = YES;
[self willLoadPath:path];
if (![path hasSuffix:@"/"])
path = [path stringByDeletingPath];
path = [path directoryPath];
[rootItem loadPath:path];
}
}
- (void)search:(NSString *)query {
PSLog(@"Searching \"%@\"", query);
[self setSearchQuery:query];
}
- (void)reload {
// Retain current working item information
NSString *path = workingItem.path;
P4SearchItem *searchItem = [rootItem isKindOfClass:[P4SearchItem class]] ? (id)rootItem : nil;
// Reload root
isReloading = YES;
rootItem = nil;
workingItem = nil;
[self loadRootPath:path];
// Retrieve search information
if (searchItem) {
P4SearchItem *item = (id)rootItem;
item.searchDirectory = searchItem.searchDirectory;
item.searchWorkspaceOnly = searchItem.searchWorkspaceOnly;
item.searchFilenames = searchItem.searchFilenames;
item.searchContents = searchItem.searchContents;
item.searchTags = searchItem.searchTags;
item.searchFilteredTags = searchItem.searchFilteredTags;
[self reloadSearchBar];
}
// Load working path
isLoading = YES;
[self willLoadPath:path];
[rootItem loadPath:path];
}
- (void)refresh PS_ABSTRACT_METHOD
- (NSArray *)selectedItems PS_ABSTRACT_METHOD
- (void)willLoadPath:(NSString *)path PS_ABSTRACT_METHOD
- (void)showVersions:(P4Item *)item PS_ABSTRACT_METHOD
- (void)editItemName:(P4Item *)item PS_ABSTRACT_METHOD
- (void)selectionChanged:(P4Item *)item {
if (lastSelectedItem == item)
return;
lastSelectedItem = item;
if ([delegate respondsToSelector:@selector(browserView:didChangeSelectedItem:)])
[delegate browserView:self didChangeSelectedItem:lastSelectedItem];
}
- (void)renameItem:(P4Item *)item name:(NSString *)newName {
if ([item.name isEqualToString:newName])
return;
[item performAction:@selector(rename:) object:newName delegate:[self.view.window windowController]];
}
- (BOOL)insertFiles:(NSArray *)paths intoItem:(P4Item *)item copy:(BOOL)copy {
if (![item isDirectory] || ![item isEditable])
return NO;
if (!copy) {
// Check if moving into the same directory
NSString *itemPath = [item.localPath stringByRemovingSuffix:@"/"];
for (NSString *path in paths) {
if ([[path stringByDeletingLastPathComponent] isEqualToString:itemPath])
return NO;
}
}
// Insert files
SEL selector = copy ? @selector(copyFiles:) : @selector(moveFiles:);
[item performAction:selector object:paths delegate:[self.view.window windowController]];
return YES;
}
- (BOOL)hasFilteredTagsForItem:(P4Item *)item {
for (NSString *tag in filteredTags)
if ([item hasTag:tag])
return YES;
return NO;
}
- (void)failWithError:(NSError *)error {
if (isLoading) {
// Don't show alert for session expiration
if (error.code == P4ErrorSessionExpired)
return;
NSAlert *alert = [NSAlert alertWithError:error];
[alert setMessageText:@"\nLoading failed"];
[alert setInformativeText:error.localizedDescription];
[alert setIcon:[[NSImage alloc] init]];
[alert
beginSheetModalForWindow:self.view.window
modalDelegate:nil
didEndSelector:nil
contextInfo:NULL];
}
}
#pragma mark - Keyboard support
- (void)moveToBeginningOfDocument:(id)sender {
P4Item *parent = workingItem.parent;
if (parent)
[self loadPath:parent.path];
else
[self.nextResponder doCommandBySelector:_cmd];
}
- (void)moveToEndOfDocument:(id)sender {
NSArray *selection = [self selectedItems];
if (selection.count == 1) {
P4Item *item = [selection lastObject];
if ([item isDirectory]) {
[self loadPath:[item path]];
return;
}
}
[self.nextResponder doCommandBySelector:_cmd];
}
#pragma mark - P4Item delegate
- (void)itemDidLoad:(P4Item *)item {
if ([item isKindOfClass:[P4SearchItem class]]) {
PSLog(@"Search results : %ld", item.children.count);
[self reloadSearchBar];
}
}
- (void)itemDidInvalidate:(id)item PS_ABSTRACT_METHOD
- (void)item:(id)item didFailWithError:(NSError *)error PS_ABSTRACT_METHOD
@end
# |
Change |
User |
Description |
Committed |
|
#1
|
10691 |
DWishR |
Populate //guest/DWishR/piper/.... |
|
|
//guest/perforce_software/piper/Perforce/Classes/ViewControllers/BrowserViewController.m |
#1
|
8919 |
Matt Attaway |
Initial add of Piper, a lightweight Perforce client for artists and designers. |
|
|