From a26bca88456c0ebcb090f76e4df997d6a9e3e75e Mon Sep 17 00:00:00 2001 From: brotherbard Date: Sun, 8 Nov 2009 20:34:34 -0700 Subject: [PATCH] Add drop down menus to the push and pull toolbar items. TODO: need small disclosure triangles in the bottom right corner of the icons. --- GitX.xcodeproj/project.pbxproj | 6 + KBPopUpToolbarItem.h | 24 +++ KBPopUpToolbarItem.m | 280 +++++++++++++++++++++++++++++++++ PBGitHistoryView.xib | 65 ++++++-- PBRefController.h | 10 +- PBRefController.m | 189 +++++++++++++++++----- 6 files changed, 525 insertions(+), 49 deletions(-) create mode 100644 KBPopUpToolbarItem.h create mode 100644 KBPopUpToolbarItem.m diff --git a/GitX.xcodeproj/project.pbxproj b/GitX.xcodeproj/project.pbxproj index 91b6ff2..6b42961 100644 --- a/GitX.xcodeproj/project.pbxproj +++ b/GitX.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 93CB42C20EAB7B2200530609 /* PBGitDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 93CB42C10EAB7B2200530609 /* PBGitDefaults.m */; }; 93F7857F0EA3ABF100C1F443 /* PBCommitMessageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93F7857E0EA3ABF100C1F443 /* PBCommitMessageView.m */; }; D26DC6450E782C9000C777B2 /* gitx.icns in Resources */ = {isa = PBXBuildFile; fileRef = D26DC6440E782C9000C777B2 /* gitx.icns */; }; + D8E41F1410A7B023007BB8FC /* KBPopUpToolbarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E41F1310A7B023007BB8FC /* KBPopUpToolbarItem.m */; }; EB2A734A0FEE3F09006601CF /* PBCollapsibleSplitView.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2A73490FEE3F09006601CF /* PBCollapsibleSplitView.m */; }; F50A411F0EBB874C00208746 /* mainSplitterBar.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F50A411D0EBB874C00208746 /* mainSplitterBar.tiff */; }; F50A41200EBB874C00208746 /* mainSplitterDimple.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F50A411E0EBB874C00208746 /* mainSplitterDimple.tiff */; }; @@ -224,6 +225,8 @@ 93F7857E0EA3ABF100C1F443 /* PBCommitMessageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCommitMessageView.m; sourceTree = ""; }; 93FCCBA80EA8AF450061B02B /* PBGitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitConfig.m; sourceTree = ""; }; D26DC6440E782C9000C777B2 /* gitx.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = gitx.icns; sourceTree = ""; }; + D8E41F1210A7B019007BB8FC /* KBPopUpToolbarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KBPopUpToolbarItem.h; sourceTree = ""; }; + D8E41F1310A7B023007BB8FC /* KBPopUpToolbarItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KBPopUpToolbarItem.m; sourceTree = ""; }; EB2A73480FEE3F09006601CF /* PBCollapsibleSplitView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCollapsibleSplitView.h; sourceTree = ""; }; EB2A73490FEE3F09006601CF /* PBCollapsibleSplitView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCollapsibleSplitView.m; sourceTree = ""; }; F50A411D0EBB874C00208746 /* mainSplitterBar.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = mainSplitterBar.tiff; path = Images/mainSplitterBar.tiff; sourceTree = ""; }; @@ -609,6 +612,8 @@ F5FC41F30EBCBD4300191D80 /* PBGitXProtocol.m */, 653D9308109BEAFE00B26705 /* PBGitXErrors.h */, 653D9309109BEAFE00B26705 /* PBGitXErrors.m */, + D8E41F1210A7B019007BB8FC /* KBPopUpToolbarItem.h */, + D8E41F1310A7B023007BB8FC /* KBPopUpToolbarItem.m */, ); name = Aux; sourceTree = ""; @@ -998,6 +1003,7 @@ F59F1DD5105C4FF300115F88 /* PBGitIndex.m in Sources */, 654D16E8108C6CA6008D960C /* PBQLOutlineView.m in Sources */, 653D930A109BEAFE00B26705 /* PBGitXErrors.m in Sources */, + D8E41F1410A7B023007BB8FC /* KBPopUpToolbarItem.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/KBPopUpToolbarItem.h b/KBPopUpToolbarItem.h new file mode 100644 index 0000000..7a4096b --- /dev/null +++ b/KBPopUpToolbarItem.h @@ -0,0 +1,24 @@ +// +// KBPopUpToolbarItem.h +// -------------------- +// +// Created by Keith Blount on 14/05/2006. +// Copyright 2006 Keith Blount. All rights reserved. +// +// Provides a toolbar item that performs its given action if clicked, or displays a pop-up menu +// (if it has one) if held down for over half a second. +// + +#import +@class KBDelayedPopUpButton; + + +@interface KBPopUpToolbarItem : NSToolbarItem +{ + KBDelayedPopUpButton *button; + NSImage *smallImage; + NSImage *regularImage; +} +- (void)setMenu:(NSMenu *)menu; +- (NSMenu *)menu; +@end diff --git a/KBPopUpToolbarItem.m b/KBPopUpToolbarItem.m new file mode 100644 index 0000000..0ca7711 --- /dev/null +++ b/KBPopUpToolbarItem.m @@ -0,0 +1,280 @@ +// +// KBPopUpToolbarItem.m +// -------------------- +// +// Created by Keith Blount on 14/05/2006. +// Copyright 2006 Keith Blount. All rights reserved. +// + +#import "KBPopUpToolbarItem.h" + +@interface KBDelayedPopUpButtonCell : NSButtonCell +@end + +@implementation KBDelayedPopUpButtonCell + +- (NSPoint)menuPositionForFrame:(NSRect)cellFrame inView:(NSView *)controlView +{ + NSPoint result = [controlView convertPoint:cellFrame.origin toView:nil]; + result.x += 1.0; + result.y -= cellFrame.size.height + 5.5; + return result; +} + +- (void)showMenuForEvent:(NSEvent *)theEvent controlView:(NSView *)controlView cellFrame:(NSRect)cellFrame +{ + NSPoint menuPosition = [self menuPositionForFrame:cellFrame inView:controlView]; + + // Create event for pop up menu with adjusted mouse position + NSEvent *menuEvent = [NSEvent mouseEventWithType:[theEvent type] + location:menuPosition + modifierFlags:[theEvent modifierFlags] + timestamp:[theEvent timestamp] + windowNumber:[theEvent windowNumber] + context:[theEvent context] + eventNumber:[theEvent eventNumber] + clickCount:[theEvent clickCount] + pressure:[theEvent pressure]]; + + [NSMenu popUpContextMenu:[self menu] withEvent:menuEvent forView:controlView]; +} + +- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp +{ + + BOOL result = NO; + NSDate *endDate; + NSPoint currentPoint = [theEvent locationInWindow]; + BOOL done = NO; + BOOL trackContinously = [self startTrackingAt:currentPoint inView:controlView]; + + // Catch next mouse-dragged or mouse-up event until timeout + BOOL mouseIsUp = NO; + NSEvent *event; + while (!done) + { + NSPoint lastPoint = currentPoint; + + // Set up timer for pop-up menu if we have one + if ([self menu]) + endDate = [NSDate dateWithTimeIntervalSinceNow:0.6]; + else + endDate = [NSDate distantFuture]; + + event = [NSApp nextEventMatchingMask:(NSLeftMouseUpMask|NSLeftMouseDraggedMask) + untilDate:endDate + inMode:NSEventTrackingRunLoopMode + dequeue:YES]; + + if (event) // Mouse event + { + currentPoint = [event locationInWindow]; + + // Send continueTracking.../stopTracking... + if (trackContinously) + { + if (![self continueTracking:lastPoint at:currentPoint inView:controlView]) + { + done = YES; + [self stopTracking:lastPoint at:currentPoint inView:controlView mouseIsUp:mouseIsUp]; + } + if ([self isContinuous]) + { + [NSApp sendAction:[self action] to:[self target] from:controlView]; + } + } + + mouseIsUp = ([event type] == NSLeftMouseUp); + done = done || mouseIsUp; + + if (untilMouseUp) + { + result = mouseIsUp; + } + else + { + // Check if the mouse left our cell rect + result = NSPointInRect([controlView convertPoint:currentPoint fromView:nil], cellFrame); + if (!result) + done = YES; + } + + if (done && result && ![self isContinuous]) + [NSApp sendAction:[self action] to:[self target] from:controlView]; + + } + else // Show menu + { + done = YES; + result = YES; + [self showMenuForEvent:theEvent controlView:controlView cellFrame:cellFrame]; + } + } + return result; +} + +@end + +@interface KBDelayedPopUpButton : NSButton +@end + +@implementation KBDelayedPopUpButton + +- (id)initWithFrame:(NSRect)frameRect +{ + if (self = [super initWithFrame:frameRect]) + { + if (![[self cell] isKindOfClass:[KBDelayedPopUpButtonCell class]]) + { + NSString *title = [self title]; + if (title == nil) title = @""; + [self setCell:[[[KBDelayedPopUpButtonCell alloc] initTextCell:title] autorelease]]; + [[self cell] setControlSize:NSRegularControlSize]; + } + } + return self; +} + +@end + + +@implementation KBPopUpToolbarItem + +- (id)initWithItemIdentifier:(NSString *)ident +{ + if (self = [super initWithItemIdentifier:ident]) + { + button = [[KBDelayedPopUpButton alloc] initWithFrame:NSMakeRect(0,0,32,32)]; + [button setButtonType:NSMomentaryChangeButton]; + [button setBordered:NO]; + [self setView:button]; + [self setMinSize:NSMakeSize(32,32)]; + [self setMaxSize:NSMakeSize(32,32)]; + } + return self; +} + +// Note that we make no assumptions about the retain/release of the toolbar item's view, just to be sure - +// we therefore retain our button view until we are dealloc'd. +- (void)dealloc +{ + [button release]; + [regularImage release]; + [smallImage release]; + [super dealloc]; +} + +- (KBDelayedPopUpButtonCell *)popupCell +{ + return [(KBDelayedPopUpButton *)[self view] cell]; +} + +- (void)setMenu:(NSMenu *)menu +{ + [[self popupCell] setMenu:menu]; + + // Also set menu form representation - this is used in the toolbar overflow menu but also, more importantly, to display + // a menu in text-only mode. + NSMenuItem *menuFormRep = [[NSMenuItem alloc] initWithTitle:[self label] action:nil keyEquivalent:@""]; + [menuFormRep setSubmenu:menu]; + [self setMenuFormRepresentation:menuFormRep]; + [menuFormRep release]; +} + +- (NSMenu *)menu +{ + return [[self popupCell] menu]; +} + +- (void)setAction:(SEL)aSelector +{ + [[self popupCell] setAction:aSelector]; +} + +- (SEL)action +{ + return [[self popupCell] action]; +} + +- (void)setTarget:(id)anObject +{ + [[self popupCell] setTarget:anObject]; +} + +- (id)target +{ + return [[self popupCell] target]; +} + +- (void)setImage:(NSImage *)anImage +{ + [regularImage autorelease]; + [smallImage autorelease]; + + regularImage = [anImage retain]; + smallImage = [anImage copy]; + [smallImage setScalesWhenResized:YES]; + [smallImage setSize:NSMakeSize(24,24)]; + + if ([[self toolbar] sizeMode] == NSToolbarSizeModeSmall) anImage = smallImage; + + [[self popupCell] setImage:anImage]; +} + +- (NSImage *)image +{ + return [[self popupCell] image]; +} + +- (void)setToolTip:(NSString *)theToolTip +{ + [[self view] setToolTip:theToolTip]; +} + +- (NSString *)toolTip +{ + return [[self view] toolTip]; +} + +- (void)validate +{ + // First, make sure the toolbar image size fits the toolbar size mode; there must be a better place to do this! + NSToolbarSizeMode sizeMode = [[self toolbar] sizeMode]; + float imgWidth = [[self image] size].width; + + if (sizeMode == NSToolbarSizeModeSmall && imgWidth != 24) + { + [[self popupCell] setImage:smallImage]; + } + else if (sizeMode == NSToolbarSizeModeRegular && imgWidth == 24) + { + [[self popupCell] setImage:regularImage]; + } + + if ([[self toolbar] delegate]) + { + BOOL enabled = YES; + + if ([[[self toolbar] delegate] respondsToSelector:@selector(validateToolbarItem:)]) + enabled = [[[self toolbar] delegate] validateToolbarItem:self]; + + else if ([[[self toolbar] delegate] respondsToSelector:@selector(validateUserInterfaceItem:)]) + enabled = [[[self toolbar] delegate] validateUserInterfaceItem:self]; + + [self setEnabled:enabled]; + } + + else if ([self action]) + { + if (![self target]) + [self setEnabled:[[[[self view] window] firstResponder] respondsToSelector:[self action]]]; + + else + [self setEnabled:[[self target] respondsToSelector:[self action]]]; + } + + else + [super validate]; +} + +@end diff --git a/PBGitHistoryView.xib b/PBGitHistoryView.xib index 7e93924..305655b 100644 --- a/PBGitHistoryView.xib +++ b/PBGitHistoryView.xib @@ -21,12 +21,11 @@ YES - - - + + YES @@ -376,7 +375,6 @@ -2147483392 {{0, 310}, {852, 15}} - YES 1 _doScroller: @@ -401,7 +399,7 @@ {852, 325} - 688 + 560 @@ -2011,7 +2009,7 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA public.url - {311, 66} + {311, 14} @@ -2072,8 +2070,8 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA 6 - {463, 1e+07} - {223, 66} + {624, 1e+07} + {223, 0} @@ -2973,6 +2971,22 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA 510 + + + pushItem + + + + 511 + + + + pullItem + + + + 512 + @@ -4113,7 +4127,9 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA 354.IBPluginDependency 354.editorWindowContentRectSynchronizationRect 355.IBPluginDependency + 356.CustomClassName 356.IBPluginDependency + 357.CustomClassName 357.IBPluginDependency 358.IBPluginDependency 36.IBPluginDependency @@ -4140,10 +4156,14 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA 376.IBPluginDependency 377.IBPluginDependency 378.IBPluginDependency + 379.CustomClassName 379.IBPluginDependency 38.IBPluginDependency + 380.CustomClassName 380.IBPluginDependency + 381.CustomClassName 381.IBPluginDependency + 382.CustomClassName 382.IBPluginDependency 383.IBPluginDependency 384.IBPluginDependency @@ -4308,7 +4328,9 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA com.apple.InterfaceBuilder.CocoaPlugin {{132, 614}, {616, 0}} com.apple.InterfaceBuilder.CocoaPlugin + KBPopUpToolbarItem com.apple.InterfaceBuilder.CocoaPlugin + KBPopUpToolbarItem com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -4342,10 +4364,14 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + KBDelayedPopUpButton com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + KBDelayedPopUpButtonCell com.apple.InterfaceBuilder.CocoaPlugin + KBDelayedPopUpButton com.apple.InterfaceBuilder.CocoaPlugin + KBDelayedPopUpButtonCell com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -4468,11 +4494,24 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA - 510 + 512 YES + + KBDelayedPopUpButtonCell + NSButtonCell + + IBProjectSource + KBPopUpToolbarItem.h + + + + KBPopUpToolbarItem + NSToolbarItem + + NSToolbarItem NSObject @@ -4637,7 +4676,9 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA newTagButton: newTagSheet: pullButton: + pullMenuAction: pushButton: + pushMenuAction: rebaseButton: saveSheet: @@ -4654,7 +4695,9 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA id id id + NSMenuItem id + NSMenuItem id id @@ -4681,6 +4724,8 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA newTagSHA newTagSHALabel newTagSheet + pullItem + pushItem YES @@ -4702,6 +4747,8 @@ TG9jYWwgYnJhbmNoZXMnIHdpbGwgbm90IHdvcmsuA NSTextField NSTextField NSWindow + KBPopUpToolbarItem + KBPopUpToolbarItem diff --git a/PBRefController.h b/PBRefController.h index 1d7aefe..9ee4095 100644 --- a/PBRefController.h +++ b/PBRefController.h @@ -13,6 +13,8 @@ #import "PBGitCommit.h" #import "PBRefContextDelegate.h" +@class KBPopUpToolbarItem; + @interface PBRefController : NSObject { IBOutlet __weak PBGitHistoryController *historyController; IBOutlet NSArrayController *commitController; @@ -36,6 +38,8 @@ IBOutlet NSTextField *newTagSHALabel; IBOutlet NSPopUpButton *branchPopUp; + IBOutlet KBPopUpToolbarItem *pullItem; + IBOutlet KBPopUpToolbarItem *pushItem; } - (IBAction)addRef:(id)sender; @@ -59,7 +63,11 @@ - (void) changeBranch:(NSMenuItem *)sender; - (void) selectCurrentBranch; -- (void) updateBranchMenu; +- (void) updateBranchMenus; +- (void) updateAllBranchesMenuWithLocal:(NSMutableArray *)localBranches remote:(NSMutableArray *)remoteBranches tag:(NSMutableArray *)tags other:(NSMutableArray *)other; + +- (void) pullMenuAction:(NSMenuItem *)sender; +- (void) pushMenuAction:(NSMenuItem *)sender; - (BOOL) pullImpl:(NSString *)refName; - (BOOL) pushImpl:(NSString *)refName; diff --git a/PBRefController.m b/PBRefController.m index b690005..d4c0717 100644 --- a/PBRefController.m +++ b/PBRefController.m @@ -9,6 +9,7 @@ #import "PBRefController.h" #import "PBGitRevisionCell.h" #import "PBRefMenuItem.h" +#import "KBPopUpToolbarItem.h" @implementation PBRefController @@ -17,14 +18,14 @@ [commitList registerForDraggedTypes:[NSArray arrayWithObject:@"PBGitRef"]]; [historyController addObserver:self forKeyPath:@"repository.branches" options:0 context:@"branchChange"]; [historyController addObserver:self forKeyPath:@"repository.currentBranch" options:0 context:@"currentBranchChange"]; - [self updateBranchMenu]; + [self updateBranchMenus]; [self selectCurrentBranch]; } - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([(NSString *)context isEqualToString: @"branchChange"]) { - [self updateBranchMenu]; + [self updateBranchMenus]; } else if ([(NSString *)context isEqualToString:@"currentBranchChange"]) { [self selectCurrentBranch]; @@ -47,7 +48,7 @@ [historyController.repository removeBranch:[[PBGitRevSpecifier alloc] initWithRef:[refMenuItem ref]]]; [[refMenuItem commit] removeRef:[refMenuItem ref]]; [commitController rearrangeObjects]; - [self updateBranchMenu]; + [self updateBranchMenus]; } } @@ -105,11 +106,10 @@ return [PBRefMenuItem defaultMenuItemsForRef:ref commit:commit target:self]; } -- (BOOL) pushImpl:(NSString *)refName +- (BOOL) pushRef:(NSString *)refName toRemote:(NSString *)remote { int ret = 1; - BOOL success = NO; - NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]]; + BOOL success = NO; NSString *rval = [historyController.repository outputInWorkdirForArguments:[NSArray arrayWithObjects:@"push", remote, refName, nil] retValue: &ret]; if (!remote) { [self showMessageSheet:@"Push to Remote" message:PBMissingRemoteErrorMessage]; @@ -126,15 +126,20 @@ return success; } -- (BOOL) pullImpl:(NSString *)refName +- (BOOL) pushImpl:(NSString *)refName +{ + NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]]; + if (!remote) { + [self showMessageSheet:@"Push to Remote" message:PBMissingRemoteErrorMessage]; + return NO; + } + return [self pushRef:refName toRemote:remote]; +} + +- (BOOL) pullRef:(NSString *)refName fromRemote:(NSString *)remote { int ret = 1; BOOL success = NO; - NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]]; - if (!remote) { - [self showMessageSheet:@"Pull from Remote" message:PBMissingRemoteErrorMessage]; - return success; - } NSArray * args = [NSArray arrayWithObjects:@"pull", remote, refName, nil]; NSString *rval = [historyController.repository outputInWorkdirForArguments:args retValue: &ret]; if (ret) { @@ -148,6 +153,16 @@ return success; } +- (BOOL) pullImpl:(NSString *)refName +{ + NSString *remote = [[historyController.repository config] valueForKeyPath:[NSString stringWithFormat:@"branch.%@.remote", refName]]; + if (!remote) { + [self showMessageSheet:@"Pull from Remote" message:PBMissingRemoteErrorMessage]; + return NO; + } + return [self pullRef:refName fromRemote:remote]; +} + - (BOOL) rebaseImpl:(NSString *)refName { int ret = 1; @@ -550,37 +565,16 @@ [newTagSheet orderOut:self]; } -# pragma mark Branches menu +# pragma mark Branch menus -- (void) updateBranchMenu +- (void) updateAllBranchesMenuWithLocal:(NSMutableArray *)localBranches remote:(NSMutableArray *)remoteBranches tag:(NSMutableArray *)tags other:(NSMutableArray *)other { if (!branchPopUp) - return; - - NSMutableArray *localBranches = [NSMutableArray array]; - NSMutableArray *remoteBranches = [NSMutableArray array]; - NSMutableArray *tags = [NSMutableArray array]; - NSMutableArray *other = [NSMutableArray array]; + return; NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Branch menu"]; - for (PBGitRevSpecifier *rev in historyController.repository.branches) - { - if (![rev isSimpleRef]) - { - [other addObject:rev]; - continue; - } - - NSString *ref = [rev simpleRef]; - - if ([ref hasPrefix:@"refs/heads"]) - [localBranches addObject:rev]; - else if ([ref hasPrefix:@"refs/tags"]) - [tags addObject:rev]; - else if ([ref hasPrefix:@"refs/remote"]) - [remoteBranches addObject:rev]; - } + // Local for (PBGitRevSpecifier *rev in localBranches) { NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[rev description] action:@selector(changeBranch:) keyEquivalent:@""]; @@ -593,7 +587,7 @@ // Remotes NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Remotes"]; - NSMenu *currentMenu = NULL; + NSMenu *currentMenu = nil; for (PBGitRevSpecifier *rev in remoteBranches) { NSString *ref = [rev simpleRef]; @@ -634,7 +628,6 @@ [tagItem setSubmenu:tagMenu]; [menu addItem:tagItem]; - // Others [menu addItem:[NSMenuItem separatorItem]]; @@ -649,6 +642,106 @@ [[branchPopUp cell] setMenu: menu]; } +- (void) updatePullMenuWithRemotes:(NSMutableArray *)remoteBranches +{ + if (!pullItem) + return; + + NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Pull menu"]; + + // Remotes + NSMenu *currentMenu = nil; + for (PBGitRevSpecifier *rev in remoteBranches) + { + NSString *ref = [rev simpleRef]; + NSArray *components = [ref componentsSeparatedByString:@"/"]; + + NSString *remoteName = [components objectAtIndex:2]; + NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"]; + + if (![[currentMenu title] isEqualToString:remoteName]) + { + currentMenu = [[NSMenu alloc] initWithTitle:remoteName]; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""]; + [item setSubmenu:currentMenu]; + [remoteMenu addItem:item]; + } + + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(pullMenuAction:) keyEquivalent:@""]; + [item setTarget:self]; + [item setRepresentedObject:rev]; + [currentMenu addItem:item]; + } + + [pullItem setMenu: remoteMenu]; +} + +- (void) updatePushMenuWithRemotes:(NSMutableArray *)remoteBranches +{ + if (!pushItem) + return; + + NSMenu *remoteMenu = [[NSMenu alloc] initWithTitle:@"Push menu"]; + + // Remotes + NSMenu *currentMenu = nil; + for (PBGitRevSpecifier *rev in remoteBranches) + { + NSString *ref = [rev simpleRef]; + NSArray *components = [ref componentsSeparatedByString:@"/"]; + + NSString *remoteName = [components objectAtIndex:2]; + NSString *branchName = [[components subarrayWithRange:NSMakeRange(3, [components count] - 3)] componentsJoinedByString:@"/"]; + + if (![[currentMenu title] isEqualToString:remoteName]) + { + currentMenu = [[NSMenu alloc] initWithTitle:remoteName]; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:remoteName action:NULL keyEquivalent:@""]; + [item setSubmenu:currentMenu]; + [remoteMenu addItem:item]; + } + + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:branchName action:@selector(pushMenuAction:) keyEquivalent:@""]; + [item setTarget:self]; + [item setRepresentedObject:rev]; + [currentMenu addItem:item]; + } + + [pushItem setMenu: remoteMenu]; +} + +- (void) updateBranchMenus +{ + NSMutableArray *localBranches = [NSMutableArray array]; + NSMutableArray *remoteBranches = [NSMutableArray array]; + NSMutableArray *tags = [NSMutableArray array]; + NSMutableArray *other = [NSMutableArray array]; + + for (PBGitRevSpecifier *rev in historyController.repository.branches) + { + if (![rev isSimpleRef]) + { + [other addObject:rev]; + continue; + } + + NSString *ref = [rev simpleRef]; + + if ([ref hasPrefix:@"refs/heads"]) + [localBranches addObject:rev]; + else if ([ref hasPrefix:@"refs/tags"]) + [tags addObject:rev]; + else if ([ref hasPrefix:@"refs/remote"]) + [remoteBranches addObject:rev]; + } + + [self updateAllBranchesMenuWithLocal:localBranches remote:remoteBranches tag:tags other:other]; + + [self updatePullMenuWithRemotes:remoteBranches]; + + [self updatePushMenuWithRemotes:remoteBranches]; +} + - (void) changeBranch:(NSMenuItem *)sender { PBGitRevSpecifier *rev = [sender representedObject]; @@ -677,6 +770,24 @@ } } +- (void) pullMenuAction:(NSMenuItem *)sender +{ + NSString *ref = [(PBGitRevSpecifier *)[sender representedObject] description]; + NSArray *refComponents = [ref componentsSeparatedByString:@"/"]; + if ([refComponents count] != 2) + return; + [self pullRef:[refComponents objectAtIndex:1] fromRemote:[refComponents objectAtIndex:0]]; +} + +- (void) pushMenuAction:(NSMenuItem *)sender +{ + NSString *ref = [(PBGitRevSpecifier *)[sender representedObject] description]; + NSArray *refComponents = [ref componentsSeparatedByString:@"/"]; + if ([refComponents count] != 2) + return; + [self pushRef:[refComponents objectAtIndex:1] toRemote:[refComponents objectAtIndex:0]]; +} + @end @implementation NSString (PBRefSpecAdditions)