Fixed responder chain issues with ViewControllers

Context menu and copy actions now working (unfinished)
This commit is contained in:
michael starke
2013-03-04 01:24:11 +01:00
parent 4c59a137a2
commit 03bb227c16
13 changed files with 151 additions and 116 deletions

View File

@@ -8,6 +8,14 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
typedef enum {
MPContextMenuCreate = 1<<0,
MPContextMenuDelete = 1<<1,
MPContextMenuCopy = 1<<2,
MPContextMenuMinimal = MPContextMenuCreate | MPContextMenuDelete,
MPContextMenuFull = MPContextMenuMinimal | MPContextMenuCopy,
}MPContextMenuItemsFlags;
@class MPDatabaseDocument; @class MPDatabaseDocument;
@interface MPAppDelegate : NSObject <NSApplicationDelegate> @interface MPAppDelegate : NSObject <NSApplicationDelegate>
@@ -16,4 +24,10 @@
- (NSString *)applicationName; - (NSString *)applicationName;
/*
Creates an array of menuitems to be used as a menu
Automatically sets up actions, so you need to take care of the responder chain
*/
- (NSArray *)contextMenuItemsWithItems:(MPContextMenuItemsFlags)flags;
@end @end

View File

@@ -22,8 +22,7 @@
@implementation MPAppDelegate @implementation MPAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
{
self.mainWindowController = [[[MPMainWindowController alloc] init] autorelease]; self.mainWindowController = [[[MPMainWindowController alloc] init] autorelease];
[self.mainWindowController showWindow:[self.mainWindowController window]]; [self.mainWindowController showWindow:[self.mainWindowController window]];
} }
@@ -51,8 +50,40 @@
[self.settingsController showWindow:_settingsController.window]; [self.settingsController showWindow:_settingsController.window];
} }
- (void)toolbarItemPressed:(id)sender { - (NSArray *)contextMenuItemsWithItems:(MPContextMenuItemsFlags)flags {
NSLog(@"Pressed %@", sender); BOOL insertCreate = (0 != (flags & MPContextMenuCreate));
BOOL insertDelete = (0 != (flags & MPContextMenuDelete));
BOOL insertCopy = (0 != (flags & MPContextMenuCopy));
NSMutableArray *items = [NSMutableArray arrayWithCapacity:7];
if(insertCreate) {
NSMenuItem *newGroup = [[NSMenuItem alloc] initWithTitle:@"New Group" action:@selector(createGroup:) keyEquivalent:@"G"];
NSMenuItem *newEntry = [[NSMenuItem alloc] initWithTitle:@"New Entry" action:@selector(createEntry:) keyEquivalent:@"E"];
[items addObjectsFromArray:@[ newGroup, newEntry ]];
[newEntry release];
[newGroup release];
}
if(insertDelete) {
if([items count] > 0) {
[items addObject:[NSMenuItem separatorItem]];
}
NSMenuItem *delete = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteEntry:) keyEquivalent:@""];
[items addObject:delete];
[delete release];
}
if(insertCopy) {
if([items count] > 0) {
[items addObject:[NSMenuItem separatorItem]];
}
NSMenuItem *copyUsername = [[NSMenuItem alloc] initWithTitle:@"Copy Username" action:@selector(copyUsername:) keyEquivalent:@"C"];
NSMenuItem *copyPassword = [[NSMenuItem alloc] initWithTitle:@"Copy Password" action:@selector(copyPassword:) keyEquivalent:@"c"];
NSMenuItem *openURL = [[NSMenuItem alloc] initWithTitle:@"Open URL" action:@selector(openURL:) keyEquivalent:@"U"];
[items addObjectsFromArray:@[ copyUsername, copyPassword, openURL]];
[copyPassword release];
[copyUsername release];
[openURL release];
}
return items;
} }
@end @end

View File

@@ -34,6 +34,7 @@
[borderGradient drawInRect:drawingRect relativeCenterPosition:NSMakePoint(-1.0, 0)]; [borderGradient drawInRect:drawingRect relativeCenterPosition:NSMakePoint(-1.0, 0)];
drawingRect.origin.x = [self bounds].size.width - 5; drawingRect.origin.x = [self bounds].size.width - 5;
[borderGradient drawInRect:drawingRect relativeCenterPosition:NSMakePoint(1.0, 0)]; [borderGradient drawInRect:drawingRect relativeCenterPosition:NSMakePoint(1.0, 0)];
[borderGradient release];
} }
[super drawRect:dirtyRect]; [super drawRect:dirtyRect];
} }

View File

@@ -14,6 +14,13 @@ APPKIT_EXTERN NSString *const MPEntryTablePasswordColumnIdentifier;
APPKIT_EXTERN NSString *const MPEntryTableParentColumnIdentifier; APPKIT_EXTERN NSString *const MPEntryTableParentColumnIdentifier;
APPKIT_EXTERN NSString *const MPEntryTableURLColumnIdentifier; APPKIT_EXTERN NSString *const MPEntryTableURLColumnIdentifier;
/* Tags to determine what to copy */
typedef enum {
MPCopyUsername,
MPCopyPassword,
MPCopyURL,
MPCopyWholeEntry,
} MPCopyContentTypeTag;
@class KdbGroup; @class KdbGroup;
@class MPOutlineViewDelegate; @class MPOutlineViewDelegate;
@@ -27,4 +34,10 @@ APPKIT_EXTERN NSString *const MPEntryTableURLColumnIdentifier;
/* Clear the Search filter*/ /* Clear the Search filter*/
- (void)clearFilter; - (void)clearFilter;
- (void)copyUsername:(id)sender;
- (void)copyPassword:(id)sender;
//- (void)copyURL:(id)sender;
//- (void)createEntry:(id)sender;
//- (void)deleteEntry:(id)sender;
@end @end

View File

@@ -7,6 +7,7 @@
// //
#import "MPEntryViewController.h" #import "MPEntryViewController.h"
#import "MPAppDelegate.h"
#import "MPOutlineViewDelegate.h" #import "MPOutlineViewDelegate.h"
#import "MPDatabaseController.h" #import "MPDatabaseController.h"
#import "MPDatabaseDocument.h" #import "MPDatabaseDocument.h"
@@ -27,12 +28,6 @@ typedef enum {
MPFilterTitles = 8, MPFilterTitles = 8,
} MPFilterModeType; } MPFilterModeType;
typedef enum {
MPCopyUsername,
MPCopyPassword,
MPCopyURL,
MPCopyWholeEntry,
} MPCopyContentTypeTag;
NSString *const MPEntryTableUserNameColumnIdentifier = @"MPUserNameColumnIdentifier"; NSString *const MPEntryTableUserNameColumnIdentifier = @"MPUserNameColumnIdentifier";
NSString *const MPEntryTableTitleColumnIdentifier = @"MPTitleColumnIdentifier"; NSString *const MPEntryTableTitleColumnIdentifier = @"MPTitleColumnIdentifier";
@@ -83,8 +78,8 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername";
- (void)_showFilterBarAnimated:(BOOL)animate; - (void)_showFilterBarAnimated:(BOOL)animate;
- (void)_hideStatusBarAnimated:(BOOL)animate; - (void)_hideStatusBarAnimated:(BOOL)animate;
- (void)_copyEntryData:(id)sender;
- (void)_quickCopyEntryData:(id)sender; - (void)_quickCopyEntryData:(id)sender;
- (KdbEntry *)_selectedEntry;
@end @end
@@ -333,7 +328,7 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername";
animate = NO; animate = NO;
if(!self.isStatusBarVisible) { if(!self.isStatusBarVisible) {
return; // nothing to do; return; // nothing to do;
} }
@@ -357,84 +352,65 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername";
#pragma mark EntryMenu #pragma mark EntryMenu
- (void)_setupEntryMenu { - (void)_setupEntryMenu {
NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
NSMenuItem *copyUserItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Copy Username"
action:@selector(_copyEntryData:)
keyEquivalent:@"C"];
[copyUserItem setTag:MPCopyUsername];
[copyUserItem setTarget:self];
NSMenuItem *copyPasswordItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Copy Password"
action:@selector(_copyEntryData:)
keyEquivalent:@"c"];
[copyPasswordItem setTag:MPCopyPassword];
[copyPasswordItem setTarget:self];
[menu addItem:copyUserItem];
[menu addItem:copyPasswordItem];
[copyUserItem release];
[copyPasswordItem release];
NSMenu *menu = [[NSMenu alloc] init];
NSArray *items = [(MPAppDelegate *)[NSApp delegate] contextMenuItemsWithItems:MPContextMenuFull];
for(NSMenuItem *item in items) {
[menu addItem:item];
}
[self.entryTable setMenu:menu]; [self.entryTable setMenu:menu];
[menu release]; [menu release];
} }
#pragma makr Action Helper
- (KdbEntry *)_selectedEntry {
NSInteger activeRow = [self.entryTable clickedRow];
/* Fallback to selection e.g. for toolbar actions */
if(activeRow < 0 ) {
activeRow = [self.entryTable selectedRow];
}
if(activeRow >= 0 && activeRow <= [[self.entryArrayController arrangedObjects] count]) {
return [self.entryArrayController arrangedObjects][activeRow];
}
return nil;
}
#pragma mark Actions #pragma mark Actions
- (void)_quickCopyEntryData:(id)sender { - (void)_quickCopyEntryData:(id)sender {
NSInteger clickedRow = [self.entryTable clickedRow];
if(clickedRow < 0 || clickedRow > [[self.entryArrayController arrangedObjects] count]) {
return;
}
KdbEntry *selectedEntry = [self.entryArrayController arrangedObjects][clickedRow];
NSTableColumn *column = [self.entryTable tableColumns][[self.entryTable clickedColumn]]; NSTableColumn *column = [self.entryTable tableColumns][[self.entryTable clickedColumn]];
NSString *identifier = [column identifier]; NSString *identifier = [column identifier];
NSImage *image = nil;
NSString *lable = nil;
if([identifier isEqualToString:MPEntryTablePasswordColumnIdentifier]) { if([identifier isEqualToString:MPEntryTablePasswordColumnIdentifier]) {
[[MPPasteBoardController defaultController] copyObjects:@[ selectedEntry.password ]]; [self copyPassword:nil];
image = [[NSBundle mainBundle] imageForResource:@"00_PasswordTemplate"];
lable = @"Password copied!";
} }
else if([identifier isEqualToString:MPEntryTableUserNameColumnIdentifier]) { else if([identifier isEqualToString:MPEntryTableUserNameColumnIdentifier]) {
[[MPPasteBoardController defaultController] copyObjects:@[ selectedEntry.username ]]; [self copyUsername:nil];
image = [[NSBundle mainBundle] imageForResource:@"09_IdentityTemplate"];
lable = @"Username copied!";
}
if(image || lable) {
[[MPOverlayWindowController sharedController] displayOverlayImage:image label:lable atView:self.view];
} }
} }
- (void)_copyEntryData:(id)sender { - (void)copyPassword:(id)sender {
NSInteger clickedRow = [self.entryTable clickedRow]; KdbEntry *selectedEntry = [self _selectedEntry];
if(clickedRow < 0 || clickedRow > [[self.entryArrayController arrangedObjects] count]) { if(!selectedEntry) {
return; return; // nothing found to work with;
} }
KdbEntry *selectedEntry = [self.entryArrayController arrangedObjects][clickedRow]; [[MPPasteBoardController defaultController] copyObjects:@[ selectedEntry.password ]];
NSImage *image = [[NSBundle mainBundle] imageForResource:@"00_PasswordTemplate"];
if([sender respondsToSelector:@selector(tag)]) { NSString *lable = @"Password copied!";
MPCopyContentTypeTag contentTag = (MPCopyContentTypeTag)[sender tag]; [[MPOverlayWindowController sharedController] displayOverlayImage:image label:lable atView:self.view];
SEL contentTypeSelector = @selector(description); }
switch (contentTag) {
case MPCopyPassword:
contentTypeSelector = @selector(password);
break;
case MPCopyUsername:
contentTypeSelector = @selector(username);
break;
case MPCopyURL:
contentTypeSelector = @selector(URL);
break;
case MPCopyWholeEntry:
default:
break;
}
[[MPPasteBoardController defaultController] copyObjects:@[ [selectedEntry performSelector:contentTypeSelector] ]];
}
- (void)copyUsername:(id)sender {
KdbEntry *selectedEntry = [self _selectedEntry];
if(!selectedEntry) {
return; // No entry to work with;
}
[[MPPasteBoardController defaultController] copyObjects:@[ selectedEntry.username ] ];
[[MPPasteBoardController defaultController] copyObjects:@[ selectedEntry.username ]];
NSImage *image = [[NSBundle mainBundle] imageForResource:@"09_IdentityTemplate"];
NSString *lable = @"Username copied!";
[[MPOverlayWindowController sharedController] displayOverlayImage:image label:lable atView:self.view];
} }
- (void)_toggleFilterSpace:(id)sender { - (void)_toggleFilterSpace:(id)sender {
@@ -453,7 +429,6 @@ NSString *const _toggleFilterUsernameButton = @"SearchUsername";
default: default:
break; break;
} }
} }
- (void)setFilterMode:(MPFilterModeType)newFilterMode { - (void)setFilterMode:(MPFilterModeType)newFilterMode {

View File

@@ -20,4 +20,5 @@
Clears the Search filter Clears the Search filter
*/ */
- (void)clearFilter:(id)sender; - (void)clearFilter:(id)sender;
@end @end

View File

@@ -19,6 +19,7 @@
@interface MPMainWindowController () @interface MPMainWindowController ()
@property (assign) IBOutlet NSView *outlineView; @property (assign) IBOutlet NSView *outlineView;
@property (assign) IBOutlet NSSplitView *splitView; @property (assign) IBOutlet NSSplitView *splitView;
@property (assign) IBOutlet NSView *contentView; @property (assign) IBOutlet NSView *contentView;
@@ -84,7 +85,7 @@
{ {
[super windowDidLoad]; [super windowDidLoad];
[self _updateWindowTitle]; [self _updateWindowTitle];
[[self.welcomeText cell] setBackgroundStyle:NSBackgroundStyleRaised]; [[self.welcomeText cell] setBackgroundStyle:NSBackgroundStyleRaised];
const CGFloat minimumWindowWidth = MPMainWindowSplitViewDelegateMinimumContentWidth + MPMainWindowSplitViewDelegateMinimumOutlineWidth + [self.splitView dividerThickness]; const CGFloat minimumWindowWidth = MPMainWindowSplitViewDelegateMinimumContentWidth + MPMainWindowSplitViewDelegateMinimumOutlineWidth + [self.splitView dividerThickness];

View File

@@ -10,8 +10,8 @@
@interface MPOutlineViewController : MPViewController @interface MPOutlineViewController : MPViewController
@property (retain, readonly) NSMenu *menu;
- (void)clearSelection; - (void)clearSelection;
- (void)createGroup:(id)sender;
- (void)deleteEntry:(id)sender;
@end @end

View File

@@ -11,6 +11,7 @@
#import "MPOutlineDataSource.h" #import "MPOutlineDataSource.h"
#import "MPDatabaseController.h" #import "MPDatabaseController.h"
#import "MPDatabaseDocument.h" #import "MPDatabaseDocument.h"
#import "MPAppDelegate.h"
@interface MPOutlineViewController () @interface MPOutlineViewController ()
@@ -22,8 +23,7 @@
- (void)_didOpenDocument:(NSNotification *)notification; - (void)_didOpenDocument:(NSNotification *)notification;
- (void)_setupMenu; - (NSMenu *)_contextMenu;
- (void)_addEntry:(id)sender;
@end @end
@@ -43,7 +43,6 @@
selector:@selector(_didOpenDocument:) selector:@selector(_didOpenDocument:)
name:MPDatabaseControllerDidLoadDatabaseNotification name:MPDatabaseControllerDidLoadDatabaseNotification
object:nil]; object:nil];
[self _setupMenu];
} }
return self; return self;
@@ -61,7 +60,7 @@
- (void)didLoadView { - (void)didLoadView {
[self.outlineView setDataSource:self.datasource]; [self.outlineView setDataSource:self.datasource];
[self.outlineView setDelegate:self.outlineDelegate]; [self.outlineView setDelegate:self.outlineDelegate];
[self.outlineView setMenu:self.menu]; [self.outlineView setMenu:[self _contextMenu]];
[self.outlineView setAllowsEmptySelection:YES]; [self.outlineView setAllowsEmptySelection:YES];
} }
@@ -83,21 +82,23 @@
[self.outlineView deselectAll:nil]; [self.outlineView deselectAll:nil];
} }
- (void)_setupMenu { - (NSMenu *)_contextMenu {
NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init]; NSMenu *menu = [[NSMenu alloc] init];
[menu addItemWithTitle:@"Add Group" action:@selector(_addEntry:) keyEquivalent:@""]; NSArray *items = [(MPAppDelegate *)[NSApp delegate] contextMenuItemsWithItems:MPContextMenuMinimal];
[menu addItem: [NSMenuItem separatorItem]]; for(NSMenuItem *item in items) {
[menu addItemWithTitle:@"Delete" action:NULL keyEquivalent:@""]; [menu addItem:item];
for(NSMenuItem *item in [menu itemArray]) {
[item setTarget:self];
} }
return [menu autorelease];
self.menu = menu;
[menu release];
} }
- (void)_addEntry:(id)sender { - (void)createGroup:(id)sender {
NSLog(@"Add Entry"); NSLog(@"%@: Create Group", [self class]);
} }
- (void)deleteEntry:(id)sender {
NSLog(@"%@: Delete Entry", [self class]);
}
@end @end

View File

@@ -8,8 +8,7 @@
#import "MPToolbarDelegate.h" #import "MPToolbarDelegate.h"
#import "MPIconHelper.h" #import "MPIconHelper.h"
#import "MPMainWindowController.h" #import "MPAppDelegate.h"
#import "MPPathBar.h"
#import "MPToolbarButton.h" #import "MPToolbarButton.h"
NSString *const MPToolbarItemAddGroup = @"AddGroup"; NSString *const MPToolbarItemAddGroup = @"AddGroup";
@@ -30,13 +29,12 @@ NSString *const MPToolbarItemSearch = @"Search";
@implementation MPToolbarDelegate @implementation MPToolbarDelegate
- (id)init - (id)init {
{
self = [super init]; self = [super init];
if (self) { if (self) {
self.toolbarIdentifiers = @[ MPToolbarItemAddEntry, MPToolbarItemDelete, MPToolbarItemEdit, MPToolbarItemAddGroup, MPToolbarItemAction, NSToolbarFlexibleSpaceItemIdentifier, MPToolbarItemSearch ]; _toolbarIdentifiers = [@[ MPToolbarItemAddEntry, MPToolbarItemDelete, MPToolbarItemEdit, MPToolbarItemAddGroup, MPToolbarItemAction, NSToolbarFlexibleSpaceItemIdentifier, MPToolbarItemSearch ] retain];
self.toolbarImages = [self createToolbarImages]; _toolbarImages = [[self createToolbarImages] retain];
self.toolbarItems = [[NSMutableDictionary alloc] initWithCapacity:[self.toolbarIdentifiers count]]; _toolbarItems = [[NSMutableDictionary alloc] initWithCapacity:[self.toolbarIdentifiers count]];
} }
return self; return self;
} }
@@ -53,7 +51,6 @@ NSString *const MPToolbarItemSearch = @"Search";
NSToolbarItem *item = self.toolbarItems[itemIdentifier]; NSToolbarItem *item = self.toolbarItems[itemIdentifier];
if(!item) { if(!item) {
item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
[item setAction:@selector(toolbarItemPressed:)];
NSString *label = NSLocalizedString(itemIdentifier, @""); NSString *label = NSLocalizedString(itemIdentifier, @"");
[item setLabel:label]; [item setLabel:label];
@@ -73,25 +70,25 @@ NSString *const MPToolbarItemSearch = @"Search";
[[popupButton cell] setImageScaling:NSImageScaleProportionallyDown]; [[popupButton cell] setImageScaling:NSImageScaleProportionallyDown];
[popupButton setTitle:@""]; [popupButton setTitle:@""];
[popupButton sizeToFit]; [popupButton sizeToFit];
/*
Built menu
*/
NSMenu *menu = [NSMenu allocWithZone:[NSMenu menuZone]];
NSMenuItem *menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"" action:NULL keyEquivalent:@""];
[menuItem setImage:self.toolbarImages[itemIdentifier]];
[menu addItem:menuItem];
[menu addItemWithTitle:@"Foo" action:NULL keyEquivalent:@""];
[menu addItemWithTitle:@"Bar" action:NULL keyEquivalent:@""];
NSRect newFrame = [popupButton frame]; NSRect newFrame = [popupButton frame];
newFrame.size.width += 20; newFrame.size.width += 20;
NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
NSMenuItem *actionImageItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"" action:NULL keyEquivalent:@""];
[actionImageItem setImage:self.toolbarImages[MPToolbarItemAction]];
[menu addItem:actionImageItem];
[actionImageItem release];
NSArray *menuItems = [(MPAppDelegate *)[NSApp delegate] contextMenuItemsWithItems:MPContextMenuFull];
for(NSMenuItem *item in menuItems) {
[menu addItem:item];
}
[popupButton setFrame:newFrame]; [popupButton setFrame:newFrame];
[popupButton setMenu:menu]; [popupButton setMenu:menu];
/*
Cleanup
*/
[menuItem release];
[menu release]; [menu release];
[item setView:popupButton]; [item setView:popupButton];
[popupButton release]; [popupButton release];
} }
@@ -142,5 +139,4 @@ NSString *const MPToolbarItemSearch = @"Search";
return imageDict; return imageDict;
} }
@end @end

View File

@@ -12,6 +12,7 @@
- (void)didLoadView; - (void)didLoadView;
- (NSResponder *)reconmendedFirstResponder; - (NSResponder *)reconmendedFirstResponder;
- (void)updateResponderChain; - (void)updateResponderChain;
@end @end

View File

@@ -26,6 +26,7 @@
- (void)updateResponderChain { - (void)updateResponderChain {
if(self.view) { if(self.view) {
NSLog(@"Updated responder chain");
NSResponder *nextResponder = [[self view] nextResponder]; NSResponder *nextResponder = [[self view] nextResponder];
[[self view] setNextResponder:self]; [[self view] setNextResponder:self];
[self setNextResponder:nextResponder]; [self setNextResponder:nextResponder];

View File

@@ -21,7 +21,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>3C8</string> <string>3E4</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string> <string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>