Files
gitx/PBGitSidebarController.m
T
Nathan Kinsinger 2321ec8c47 Add and delete refs from the source list view
Observe the branches array and update the sidebar when refs are added or deleted.

Sort new items based on the title.
2010-03-13 22:15:06 -07:00

290 lines
8.2 KiB
Objective-C

//
// PBGitSidebar.m
// GitX
//
// Created by Pieter de Bie on 9/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
#import "PBGitSidebarController.h"
#import "PBSourceViewItems.h"
#import "PBGitHistoryController.h"
#import "PBGitCommitController.h"
#import "PBRefController.h"
#import "PBSourceViewCell.h"
#import "NSOutlineViewExt.h"
@interface PBGitSidebarController ()
- (void)populateList;
- (void)addRevSpec:(PBGitRevSpecifier *)revSpec;
- (PBSourceViewItem *) itemForRev:(PBGitRevSpecifier *)rev;
- (void) removeRevSpec:(PBGitRevSpecifier *)rev;
@end
@implementation PBGitSidebarController
@synthesize items;
- (id)initWithRepository:(PBGitRepository *)theRepository superController:(PBGitWindowController *)controller
{
self = [super initWithRepository:theRepository superController:controller];
[sourceView setDelegate:self];
items = [NSMutableArray array];
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
window.contentView = self.view;
[self populateList];
historyViewController = [[PBGitHistoryController alloc] initWithRepository:repository superController:superController];
commitViewController = [[PBGitCommitController alloc] initWithRepository:repository superController:superController];
[repository addObserver:self forKeyPath:@"currentBranch" options:0 context:@"currentBranchChange"];
[repository addObserver:self forKeyPath:@"branches" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:@"branchesModified"];
[self selectCurrentBranch];
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([@"currentBranchChange" isEqualToString:context]) {
[sourceView reloadData];
[self selectCurrentBranch];
return;
}
if ([@"branchesModified" isEqualToString:context]) {
NSInteger changeKind = [(NSNumber *)[change objectForKey:NSKeyValueChangeKindKey] intValue];
if (changeKind == NSKeyValueChangeInsertion) {
NSArray *newRevSpecs = [change objectForKey:NSKeyValueChangeNewKey];
for (PBGitRevSpecifier *rev in newRevSpecs) {
[self addRevSpec:rev];
PBSourceViewItem *item = [self itemForRev:rev];
[sourceView PBExpandItem:item expandParents:YES];
}
}
else if (changeKind == NSKeyValueChangeRemoval) {
NSArray *removedRevSpecs = [change objectForKey:NSKeyValueChangeOldKey];
for (PBGitRevSpecifier *rev in removedRevSpecs)
[self removeRevSpec:rev];
}
return;
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
- (void) selectStage
{
NSIndexSet *index = [NSIndexSet indexSetWithIndex:[sourceView rowForItem:stage]];
[sourceView selectRowIndexes:index byExtendingSelection:NO];
}
- (void) selectCurrentBranch
{
PBGitRevSpecifier *rev = repository.currentBranch;
if (!rev)
return;
PBSourceViewItem *item = nil;
for (PBSourceViewItem *it in items)
if (item = [it findRev:rev])
break;
if (!item) {
[self addRevSpec:rev];
// Try to find the just added item again.
// TODO: refactor with above.
for (PBSourceViewItem *it in items)
if (item = [it findRev:rev])
break;
}
[sourceView PBExpandItem:item expandParents:YES];
NSIndexSet *index = [NSIndexSet indexSetWithIndex:[sourceView rowForItem:item]];
[sourceView selectRowIndexes:index byExtendingSelection:NO];
}
- (PBSourceViewItem *) itemForRev:(PBGitRevSpecifier *)rev
{
PBSourceViewItem *foundItem = nil;
for (PBSourceViewItem *item in items)
if (foundItem = [item findRev:rev])
return foundItem;
return nil;
}
- (void)addRevSpec:(PBGitRevSpecifier *)rev
{
if (![rev isSimpleRef]) {
[others addChild:[PBSourceViewItem itemWithRevSpec:rev]];
[sourceView reloadData];
return;
}
NSArray *pathComponents = [[rev simpleRef] componentsSeparatedByString:@"/"];
if ([pathComponents count] < 2)
[branches addChild:[PBSourceViewItem itemWithRevSpec:rev]];
else if ([[pathComponents objectAtIndex:1] isEqualToString:@"heads"])
[branches addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]];
else if ([[rev simpleRef] hasPrefix:@"refs/tags/"])
[tags addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]];
else if ([[rev simpleRef] hasPrefix:@"refs/remotes/"])
[remotes addRev:rev toPath:[pathComponents subarrayWithRange:NSMakeRange(2, [pathComponents count] - 2)]];
[sourceView reloadData];
}
- (void) removeRevSpec:(PBGitRevSpecifier *)rev
{
PBSourceViewItem *item = [self itemForRev:rev];
if (!item)
return;
PBSourceViewItem *parent = item.parent;
[parent removeChild:item];
[sourceView reloadData];
}
#pragma mark NSOutlineView delegate methods
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
NSInteger index = [sourceView selectedRow];
PBSourceViewItem *item = [sourceView itemAtRow:index];
if ([item revSpecifier]) {
repository.currentBranch = [item revSpecifier];
[superController changeContentController:historyViewController];
return;
}
if (item == stage)
[superController changeContentController:commitViewController];
/* ... */
/* Handle Remotes */
}
#pragma mark NSOutlineView delegate methods
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item
{
return [item isGroupItem];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(PBSourceViewCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(PBSourceViewItem *)item
{
cell.isCheckedOut = [item.revSpecifier isEqualTo:[repository headRef]];
[cell setImage:[item icon]];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
return ![item isGroupItem];
}
//
// The next method is necessary to hide the triangle for uncollapsible items
// That is, items which should always be displayed, such as the Project group.
// This also moves the group item to the left edge.
- (BOOL) outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item
{
return ![item isUncollapsible];
}
- (void)populateList
{
PBSourceViewItem *project = [PBSourceViewItem groupItemWithTitle:[repository projectName]];
project.isUncollapsible = YES;
stage = [PBGitSVStageItem stageItem];
[project addChild:stage];
branches = [PBSourceViewItem groupItemWithTitle:@"Branches"];
remotes = [PBSourceViewItem groupItemWithTitle:@"Remotes"];
tags = [PBSourceViewItem groupItemWithTitle:@"Tags"];
others = [PBSourceViewItem groupItemWithTitle:@"Other"];
for (PBGitRevSpecifier *rev in repository.branches)
[self addRevSpec:rev];
[items addObject:project];
[items addObject:branches];
[items addObject:remotes];
[items addObject:tags];
[items addObject:others];
[sourceView reloadData];
[sourceView expandItem:project];
[sourceView expandItem:branches expandChildren:YES];
[sourceView expandItem:remotes];
[sourceView reloadItem:nil reloadChildren:YES];
}
#pragma mark NSOutlineView Datasource methods
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if (!item)
return [items objectAtIndex:index];
return [[(PBSourceViewItem *)item children] objectAtIndex:index];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return [[(PBSourceViewItem *)item children] count];
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (!item)
return [items count];
return [[(PBSourceViewItem *)item children] count];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
return [(PBSourceViewItem *)item title];
}
#pragma mark Menus
- (NSMenu *) menuForRow:(NSInteger)row
{
PBSourceViewItem *viewItem = [sourceView itemAtRow:row];
PBGitRef *ref = nil;
// create a ref for a remote because they don't store one
if ([self outlineView:sourceView isItemExpandable:viewItem] && (viewItem.parent == remotes))
ref = [PBGitRef refFromString:[kGitXRemoteRefPrefix stringByAppendingString:[viewItem title]]];
else
ref = [[viewItem revSpecifier] ref];
if (!ref)
return nil;
NSArray *menuItems = [historyViewController.refController menuItemsForRef:ref];
NSMenu *menu = [[NSMenu alloc] init];
[menu setAutoenablesItems:NO];
for (NSMenuItem *item in menuItems)
[menu addItem:item];
return menu;
}
@end