From 51fae123394764396b3246079f6569893e63de1b Mon Sep 17 00:00:00 2001 From: Alex Lebrun Date: Wed, 18 Feb 2015 17:49:21 -0800 Subject: [PATCH 01/15] Added support for Mail.app 8.2 Just added the UUID in Info.plist. --- GMailinator/Info.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index cb7fb11..d948658 100644 --- a/GMailinator/Info.plist +++ b/GMailinator/Info.plist @@ -26,6 +26,7 @@ Copyright © 2013 Michael Lai. All rights reserved. SupportedPluginCompatibilityUUIDs + 60D52D22-7491-4CA7-95BA-88215BD88F8E B61772F2-9975-4EC0-B22F-9A277C46ADD2 0A13A9ED-4864-4F07-AE70-60FB2F7EA63D DAFFB2B4-77BC-4C25-8CE1-2405E652D54B From 0134918052d3a2b52919be509b86ed038d559b20 Mon Sep 17 00:00:00 2001 From: Alex Lebrun Date: Thu, 19 Feb 2015 08:56:47 -0800 Subject: [PATCH 02/15] Added send email (tab, then enter) shortcut to email composer --- GMailinator/GMailinator.m | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index d6fd5af..f066357 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -8,6 +8,9 @@ } @implementation GMailinator +{ + NSDate *_tabDate; +} + (void)initialize { [GMailinator registerBundle]; @@ -46,6 +49,16 @@ + (void)load { class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); + + // Add shortcuts to the message editor + c = NSClassFromString(@"MessageWebHTMLView"); + originalSelector = @selector(keyDown:); + overrideSelector = @selector(overrideMessageEditorKeyDown:); + originalMethod = class_getInstanceMethod(c, originalSelector); + overrideMethod = class_getInstanceMethod(self, overrideSelector); + + class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); + class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); } + (void)registerBundle @@ -190,4 +203,36 @@ - (void)overrideMessagesKeyDown:(NSEvent*)event { } } +- (void)overrideMessageEditorKeyDown:(NSEvent*)event { + unichar key = [[event characters] characterAtIndex:0]; + + switch (key) { + case '\t': { + _tabDate = [NSDate date]; + [self overrideMessageEditorKeyDown:event]; + break; + } + case '\r': { + if (_tabDate) { + double timePassed_ms = [_tabDate timeIntervalSinceNow] * -1000.0; + if (timePassed_ms < 500) { + CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 2, true); // D + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); + NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; + [self overrideMessageEditorKeyDown: newEvent]; + break; + } else { + _tabDate = nil; // avoid unnecessary calculations later + } + } + [self overrideMessageEditorKeyDown:event]; + break; + } + default: + [self overrideMessageEditorKeyDown:event]; + break; + } +} + + @end From 4aa308689716dfc97f6599c865bb7243f06bf0e9 Mon Sep 17 00:00:00 2001 From: Alex Lebrun Date: Thu, 19 Feb 2015 11:20:08 -0800 Subject: [PATCH 03/15] added Flagged (aka Star on Gmail, S) shortcut --- GMailinator/GMailinator.m | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index f066357..945cdd0 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -141,6 +141,13 @@ - (void)overrideMailKeyDown:(NSEvent*)event { [self overrideMailKeyDown: newEvent]; break; } + case 's': { + CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 0x25, true); // l + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); + NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; + [self overrideMailKeyDown: newEvent]; + break; + } default: [self overrideMailKeyDown:event]; break; @@ -196,6 +203,13 @@ - (void)overrideMessagesKeyDown:(NSEvent*)event { [self overrideMessagesKeyDown: newEvent]; break; } + case 's': { + CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 0x25, true); // l + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); + NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; + [self overrideMessagesKeyDown: newEvent]; + break; + } default: [self overrideMessagesKeyDown:event]; break; From 2fa0366b37f78dc03feac855936b36f47827e164 Mon Sep 17 00:00:00 2001 From: Alexandre Lebrun Date: Sat, 21 Feb 2015 19:08:01 -0800 Subject: [PATCH 04/15] Fixed Move to Folder on 10.10.2 --- .gitignore | 1 + GMailinator/SearchManager.m | 9 ++++++++- GMailinator/SearchPopup.m | 13 +++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index e9f3e7b..051b3c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store xcuserdata/ +GMailinator.xcodeproj/project.xcworkspace/xcshareddata/ \ No newline at end of file diff --git a/GMailinator/SearchManager.m b/GMailinator/SearchManager.m index f7747fd..85dbf37 100644 --- a/GMailinator/SearchManager.m +++ b/GMailinator/SearchManager.m @@ -95,10 +95,17 @@ - (NSMenuItem *) newMenuItemWithTitle:(NSString *)title action:(SEL)action andKe - (void) setContextMenu:(NSMenu *)menu { - NSMenuItem* moveFolderItem = [self newMenuItemWithTitle: @"Move to Folder..." action: @selector(moveToFolder:) andKeyEquivalent: @"l" inMenu: [[NSApplication sharedApplication] mainMenu] withTitle: @"Move Again" offset: 1]; + // create "Move to Folder..." menu item just below the existing "Move Again" menuitem + NSMenuItem* moveFolderItem = [self newMenuItemWithTitle: @"Move to Folder..." + action: @selector(moveToFolder:) + andKeyEquivalent: @"l" + inMenu: [[NSApplication sharedApplication] mainMenu] + withTitle: @"Move Again" + offset: 1]; [moveFolderItem setTarget: self]; [moveFolderItem setKeyEquivalentModifierMask: 0]; + // lookup existing "Move to" and "Copy to" menuitems NSMenu* messagesMenu = [moveFolderItem menu]; NSArray *items = [messagesMenu itemArray]; for (int iI = 0; iI < [items count]; iI++) { diff --git a/GMailinator/SearchPopup.m b/GMailinator/SearchPopup.m index 6e87912..1b56cb4 100644 --- a/GMailinator/SearchPopup.m +++ b/GMailinator/SearchPopup.m @@ -18,7 +18,6 @@ - (void)setSubmenu:(NSMenu*)sm { - (void)addMenu:(NSMenu *)menu toDictionary:(NSMutableDictionary *)dict withPath:(NSMutableArray *)path atLevel:(int)depth { NSArray *items = [menu itemArray]; - for (int i = 0; i < [items count]; ++i) { NSMenuItem *menuItem = [items objectAtIndex:i]; @@ -57,9 +56,13 @@ - (void)showWithSender: sender andTitle: (NSString *)title { // update menu items [[submenu delegate] menuNeedsUpdate: submenu ]; + // set message handling to copy / move - //[submenu _sendMenuOpeningNotification]; - [submenu performSelector:@selector(_sendMenuOpeningNotification)]; + if ([submenu respondsToSelector:@selector(_sendMenuOpeningNotification:)]) { // Yosemite 10.10.2 + [submenu performSelector:@selector(_sendMenuOpeningNotification:)]; + } else if ([submenu respondsToSelector:@selector(_sendMenuOpeningNotification)]) { + [submenu performSelector:@selector(_sendMenuOpeningNotification)]; + } // if ([p lastFolder] != nil) // { @@ -137,7 +140,9 @@ - (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySe { // [parent setLastFolder: selectedResult objectAtIndex:0]]; NSMenuItem *menuItem = [selectedResult objectForKey:@"menuItem"]; - [[menuItem menu] performActionForItemAtIndex:[[menuItem menu] indexOfItem:menuItem]]; + NSMenu *menu = [menuItem menu]; + NSInteger index = [menu indexOfItem:menuItem]; + [menu performActionForItemAtIndex:index]; } [searchWindow orderOut:nil]; From ac4872f6d2d56371e69624a5267c8d06eb89618e Mon Sep 17 00:00:00 2001 From: Alex Lebrun Date: Sun, 22 Feb 2015 06:39:44 -0800 Subject: [PATCH 05/15] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 76fe2da..ed72a18 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ in progress. Tested with Mail for OS X 10.8.4. kGo to next message/thread /Mailbox search lMove to folder (opens dialog) + sFlag + tab, then enterSend message ## How to install From 4fff641ae7b034e3ecb618335463b5c38b77eaa3 Mon Sep 17 00:00:00 2001 From: Alex Lebrun Date: Thu, 19 Mar 2015 13:57:41 -0700 Subject: [PATCH 06/15] added compiled bundle --- GMailinator.mailbundle.zip | Bin 0 -> 29261 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 GMailinator.mailbundle.zip diff --git a/GMailinator.mailbundle.zip b/GMailinator.mailbundle.zip new file mode 100644 index 0000000000000000000000000000000000000000..8d732dd83155bd2c5c38a932b107600e7585e698 GIT binary patch literal 29261 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%0AwoMPXvee|d3A_QPL7gsW+D}-`3^k=~zw9xYc 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 literal 0 HcmV?d00001 From aa63febbafafde71d3eeadef9954f811905d3865 Mon Sep 17 00:00:00 2001 From: Filip Krikava Date: Sun, 6 Sep 2015 13:08:23 +0200 Subject: [PATCH 07/15] Added 'u' to toggle unread/read mark --- GMailinator/GMailinator.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index 945cdd0..ccbf171 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -1,5 +1,6 @@ #import "GMailinator.h" #import +#import #import NSBundle *GetGMailinatorBundle(void) @@ -148,6 +149,13 @@ - (void)overrideMailKeyDown:(NSEvent*)event { [self overrideMailKeyDown: newEvent]; break; } + case 'u': { + CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); + NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; + [self overrideMailKeyDown: newEvent]; + break; + } default: [self overrideMailKeyDown:event]; break; From 8e6a0dcc1ca59f471e5f58ccc7682639c8dd5908 Mon Sep 17 00:00:00 2001 From: Filip Krikava Date: Sun, 6 Sep 2015 13:08:38 +0200 Subject: [PATCH 08/15] Resized and resizable search dialog --- GMailinator/SearchPopup.xib | 534 ++++++++++++++++++++---------------- 1 file changed, 301 insertions(+), 233 deletions(-) diff --git a/GMailinator/SearchPopup.xib b/GMailinator/SearchPopup.xib index fa0c190..5aef530 100644 --- a/GMailinator/SearchPopup.xib +++ b/GMailinator/SearchPopup.xib @@ -1,17 +1,16 @@ - + 1050 - 12E55 - 3084 - 1187.39 - 626.00 + 14F27 + 7706 + 1348.17 + 758.70 com.apple.InterfaceBuilder.CocoaPlugin - 3084 + 7706 - - YES + NSCustomObject NSImageCell NSScrollView @@ -23,17 +22,15 @@ NSTextFieldCell NSView NSWindowTemplate - - - YES + + com.apple.InterfaceBuilder.CocoaPlugin - + PluginDependencyRecalculationVersion - - YES + SearchPopup @@ -44,10 +41,10 @@ NSApplication - 81 + 89 2 - {{673, 431}, {249, 307}} - 1685586944 + {{673, 431}, {670, 205}} + 1685591040 Search Mailbox NSPanel @@ -55,22 +52,22 @@ 256 - - YES + 266 - {{20, 265}, {209, 22}} + {{20, 163}, {630, 22}} + YES YES 342884417 268436480 - LucidaGrande + YES 13 1044 @@ -81,7 +78,7 @@ 6 System textBackgroundColor - + 3 MQA @@ -112,22 +109,12 @@ 0 0 clear - - YES - - YES - - YES - AXDescription - NSAccessibilityEncodedAttributesValueType - - - YES - cancel - - - - + + + cancel + + + _searchFieldCancel: @@ -138,27 +125,26 @@ 75 255 - CAAAAA + DgAAAA NO + 1 274 - - YES + - 2304 - - YES + 2322 + 256 - {207, 228} + {628, 133} - + YES NO YES @@ -167,8 +153,7 @@ -2147483392 {{209, 0}, {16, 17}} - - YES + image 40 @@ -179,7 +164,7 @@ 201328640 Image - LucidaGrande + YES 11 3100 @@ -206,7 +191,7 @@ title - 161 + 582 40 1000 @@ -238,10 +223,10 @@ YES - + 3 2 - + 6 System @@ -262,14 +247,15 @@ 0 1 - - {{1, 1}, {207, 228}} + + {{1, 1}, {628, 133}} 4 + YES @@ -278,6 +264,8 @@ NO + _doScroller: + _doScroller: 0.99212599999999995 @@ -290,16 +278,18 @@ NO + _doScroller: + 1 _doScroller: 0.81632660000000001 - - {{20, 20}, {209, 230}} + + {{20, 20}, {630, 135}} - + 133650 @@ -309,20 +299,19 @@ 4 1 - - {249, 307} + + {670, 205} - {{0, 0}, {1920, 1178}} + {{0, 0}, {1680, 1027}} {10000000000000, 10000000000000} YES - + - - YES + searchField @@ -379,15 +368,12 @@ 32 - + - - YES + 0 - - YES - + @@ -412,29 +398,26 @@ 1 - - YES + - + 2 - - YES - + - + + 4 - - YES + - + @@ -445,12 +428,11 @@ 20 - - YES + - + @@ -466,29 +448,26 @@ 23 - - YES + - + 25 - - YES + - + 26 - - YES + - + @@ -501,178 +480,267 @@ - - - - YES - - YES - -1.IBPluginDependency - -2.IBPluginDependency - -3.IBPluginDependency - 1.IBNSWindowAutoPositionCentersHorizontal - 1.IBNSWindowAutoPositionCentersVertical - 1.IBPluginDependency - 1.IBWindowTemplateEditedContentRect - 1.NSWindowTemplate.visibleAtLaunch - 2.IBPluginDependency - 2.IBViewIntegration.shadowBlurRadius - 2.IBViewIntegration.shadowColor - 2.IBViewIntegration.shadowOffsetHeight - 2.IBViewIntegration.shadowOffsetWidth - 20.IBPluginDependency - 21.IBPluginDependency - 22.IBPluginDependency - 23.IBPluginDependency - 23.IBViewIntegration.shadowBlurRadius - 23.IBViewIntegration.shadowColor - 23.IBViewIntegration.shadowOffsetHeight - 23.IBViewIntegration.shadowOffsetWidth - 25.IBPluginDependency - 26.IBPluginDependency - 27.IBPluginDependency - 29.IBPluginDependency - 4.IBPluginDependency - 5.IBPluginDependency - - - YES - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - com.apple.InterfaceBuilder.CocoaPlugin - {{703, 547}, {265, 198}} - - com.apple.InterfaceBuilder.CocoaPlugin - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - YES - - + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + {497, 285.5} + com.apple.InterfaceBuilder.CocoaPlugin + {{703, 547}, {265, 198}} + + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + - - YES - - - + 36 - - YES + SearchPopup NSObject - - YES - - YES - changeSelection: - doSearch: - - - YES - id - id + + id + id + + + + changeSelection: + id - - - YES - - YES - changeSelection: - doSearch: + + doSearch: + id - - YES - - changeSelection: - id - - - doSearch: - id - + + + NSTableView + NSSearchField + NSWindow + + + + resultViewer + NSTableView - - - YES - - YES - resultViewer - searchField - searchWindow + + searchField + NSSearchField - - YES - NSTableView - NSSearchField - NSWindow + + searchWindow + NSWindow + + + IBProjectSource + ../GMailinator/SearchPopup.h - - YES - - YES - resultViewer - searchField - searchWindow + + + SearchPopup + + id + id + + + + changeSelection: + id - - YES - - resultViewer - NSTableView - - - searchField - NSSearchField - - - searchWindow - NSWindow - + + doSearch: + id - + IBProjectSource - ./Classes/SearchPopup.h + ../GMailinator/SearchPopup.m - + + + + NSActionCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSActionCell.h + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSCell + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSCell.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSImageCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSImageCell.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSPanel + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSPanel.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSScrollView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSScrollView.h + + + + NSScroller + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSScroller.h + + + + NSSearchField + NSTextField + + IBFrameworkSource + AppKit.framework/Headers/NSSearchField.h + + + + NSSearchFieldCell + NSTextFieldCell + + IBFrameworkSource + AppKit.framework/Headers/NSSearchFieldCell.h + + + + NSTableColumn + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableColumn.h + + + + NSTableView + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSTextField + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSTextField.h + + + + NSTextFieldCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSTextFieldCell.h + + + + NSView + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + 0 IBCocoaFramework + NO com.apple.InterfaceBuilder.CocoaPlugin.macosx com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - + YES 3 From 9bf7b10c8673567cdbbf3cd97a094064217c384d Mon Sep 17 00:00:00 2001 From: Filip Krikava Date: Mon, 21 Mar 2016 21:41:06 +0100 Subject: [PATCH 09/15] Added support to Mail.app 9.2 (OSX 10.11.3) --- GMailinator/Info.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index d948658..92a5eb8 100644 --- a/GMailinator/Info.plist +++ b/GMailinator/Info.plist @@ -26,6 +26,7 @@ Copyright © 2013 Michael Lai. All rights reserved. SupportedPluginCompatibilityUUIDs + 1550C683-EA48-4036-B7CE-FB6F5D13EE02 60D52D22-7491-4CA7-95BA-88215BD88F8E B61772F2-9975-4EC0-B22F-9A277C46ADD2 0A13A9ED-4864-4F07-AE70-60FB2F7EA63D From 05222952f0bfabd6f22d208a553659e589992415 Mon Sep 17 00:00:00 2001 From: Filip Krikava Date: Sat, 21 May 2016 18:39:59 +0200 Subject: [PATCH 10/15] Added support to Mail.app 9.3 (OSX 10.11.4) --- GMailinator/Info.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index 92a5eb8..da24c66 100644 --- a/GMailinator/Info.plist +++ b/GMailinator/Info.plist @@ -26,6 +26,7 @@ Copyright © 2013 Michael Lai. All rights reserved. SupportedPluginCompatibilityUUIDs + 71562B89-0D90-4588-8E94-A75B701D6443 1550C683-EA48-4036-B7CE-FB6F5D13EE02 60D52D22-7491-4CA7-95BA-88215BD88F8E B61772F2-9975-4EC0-B22F-9A277C46ADD2 From 8c584df77dae21e3997d9e8411f5e1aeec71ee45 Mon Sep 17 00:00:00 2001 From: Thiago Alves Date: Sun, 17 Jul 2016 14:40:31 -0700 Subject: [PATCH 11/15] Refactor GMailinator.m and other fixes. The goal here is to make easier to understand and add new shortcuts to GMailinator. Any new addition should be straightforward on the main file. Also I gather several fixes for different thing over everyone's fork to get the following: Auto UUID on the build script; Most of the compilation warnings are gone, with exception of the deprecation to load the NIB. If I fix that, the Move To dialog simply does not show up; Also I got rid of a memory leak on the event creation. It as a tiny one and would take forever to impact real users but nevertheless it was a good catch. --- GMailinator.xcodeproj/project.pbxproj | 26 +- GMailinator/GMailinator.m | 329 +++++----- GMailinator/Info.plist | Bin 2118 -> 1499 bytes GMailinator/SearchManager.m | 6 + GMailinator/SearchPopup.m | 2 +- GMailinator/SearchPopup.xib | 842 +++----------------------- README.md | 35 +- 7 files changed, 304 insertions(+), 936 deletions(-) diff --git a/GMailinator.xcodeproj/project.pbxproj b/GMailinator.xcodeproj/project.pbxproj index 42fe4dc..baad0b0 100644 --- a/GMailinator.xcodeproj/project.pbxproj +++ b/GMailinator.xcodeproj/project.pbxproj @@ -75,6 +75,7 @@ 499015DB1615FE5300991F6C /* Products */, ); sourceTree = ""; + wrapsLines = 0; }; 499015DB1615FE5300991F6C /* Products */ = { isa = PBXGroup; @@ -130,6 +131,7 @@ isa = PBXNativeTarget; buildConfigurationList = 499015E81615FE5300991F6C /* Build configuration list for PBXNativeTarget "GMailinator" */; buildPhases = ( + A718F5961A681B6700545966 /* Save current UUID to Info.plist */, 499015D61615FE5300991F6C /* Sources */, 499015D71615FE5300991F6C /* Frameworks */, 499015D81615FE5300991F6C /* Resources */, @@ -150,7 +152,7 @@ 499015D11615FE5300991F6C /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0450; + LastUpgradeCheck = 0610; ORGANIZATIONNAME = nompute; }; buildConfigurationList = 499015D41615FE5300991F6C /* Build configuration list for PBXProject "GMailinator" */; @@ -181,6 +183,23 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + A718F5961A681B6700545966 /* Save current UUID to Info.plist */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Save current UUID to Info.plist"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"Grabbing the mail app uuid for your version....\"\necho \"the Source_ROOT ${SOURCE_ROOT}\"\n\ndefaults write ${SOURCE_ROOT}/GMailinator/Info.plist SupportedPluginCompatibilityUUIDs -array-add \"`${SOURCE_ROOT}/find_uuid.sh`\""; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 499015D61615FE5300991F6C /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -201,13 +220,13 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -232,7 +251,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; @@ -261,6 +279,7 @@ GCC_PREFIX_HEADER = "GMailinator/GMailinator-Prefix.pch"; INFOPLIST_FILE = GMailinator/Info.plist; INSTALL_PATH = "$(HOME)/Library/Mail/Bundles"; + PRODUCT_BUNDLE_IDENTIFIER = "com.nompute.gmailinator.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = GMailinator; WRAPPER_EXTENSION = mailbundle; }; @@ -275,6 +294,7 @@ GCC_PREFIX_HEADER = "GMailinator/GMailinator-Prefix.pch"; INFOPLIST_FILE = GMailinator/Info.plist; INSTALL_PATH = "$(HOME)/Library/Mail/Bundles"; + PRODUCT_BUNDLE_IDENTIFIER = "com.nompute.gmailinator.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = GMailinator; WRAPPER_EXTENSION = mailbundle; }; diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index ccbf171..2da14bd 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -9,252 +9,241 @@ } @implementation GMailinator -{ - NSDate *_tabDate; -} + (void)initialize { [GMailinator registerBundle]; SearchManager* sm = [[SearchManager alloc] init]; [sm setContextMenu: nil]; - objc_setAssociatedObject(GetGMailinatorBundle(), @"searchManager", sm, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(GetGMailinatorBundle(), + @"searchManager", + sm, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -+ (void)load { - // Add shortcuts to the mailbox list - Class c = NSClassFromString(@"MailTableView"); - SEL originalSelector = @selector(keyDown:); - SEL overrideSelector = @selector(overrideMailKeyDown:); - Method originalMethod = class_getInstanceMethod(c, originalSelector); - Method overrideMethod = class_getInstanceMethod(self, overrideSelector); ++ (void)logAllSelectorsFromClass:(Class)cls { + unsigned int methodCount = 0; + Method * methodlist = class_copyMethodList(cls, &methodCount); - class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); + NSLog(@"Class '%s' has %d methods", class_getName(cls), methodCount); + for(int i = 0; i < methodCount; ++i) { + NSLog(@"Method no #%d: %s", i, sel_getName(method_getName(methodlist[i]))); + } +} - // Add shortcuts to the messages list - c = NSClassFromString(@"MessagesTableView"); - originalSelector = @selector(keyDown:); - overrideSelector = @selector(overrideMessagesKeyDown:); - originalMethod = class_getInstanceMethod(c, originalSelector); - overrideMethod = class_getInstanceMethod(self, overrideSelector); +/** + * Helper method to setup a class from Mail to use our custom methods instead of they common + * keyDown:. + */ ++ (void)setupClass:(Class)cls swappingKeyDownWith:(SEL)overrideSelector { + if (DEBUG) { + [self logAllSelectorsFromClass:cls]; + } - class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); + if (cls == nil) return; - // Add shortcuts to the messages list - c = NSClassFromString(@"MessageViewer"); - originalSelector = @selector(keyDown:); - overrideSelector = @selector(overrideMessagesKeyDown:); - originalMethod = class_getInstanceMethod(c, originalSelector); - overrideMethod = class_getInstanceMethod(self, overrideSelector); + // Helper methods + SEL performSelector = @selector(performSelectorOnMessageViewer:basedOnEvent:); + SEL getShortcutSelector = @selector(getShortcutRemappedEventFor:); + Method performMethod = class_getInstanceMethod(self, performSelector); + Method getShortcutMethod = class_getInstanceMethod(self, getShortcutSelector); - class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); + // Swapped methods + SEL originalSelector = @selector(keyDown:); + Method originalMethod = class_getInstanceMethod(cls, originalSelector); + Method overrideMethod = class_getInstanceMethod(self, overrideSelector); - // Add shortcuts to the message editor - c = NSClassFromString(@"MessageWebHTMLView"); - originalSelector = @selector(keyDown:); - overrideSelector = @selector(overrideMessageEditorKeyDown:); - originalMethod = class_getInstanceMethod(c, originalSelector); - overrideMethod = class_getInstanceMethod(self, overrideSelector); - - class_addMethod(c, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); - class_replaceMethod(c, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); + // Swap keyDow with the given method + class_addMethod(cls, + overrideSelector, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + class_replaceMethod(cls, + originalSelector, + method_getImplementation(overrideMethod), + method_getTypeEncoding(overrideMethod)); + // Add helper methods + class_addMethod(cls, + performSelector, + method_getImplementation(performMethod), + method_getTypeEncoding(performMethod)); + class_addMethod(cls, + getShortcutSelector, + method_getImplementation(getShortcutMethod), + method_getTypeEncoding(getShortcutMethod)); } -+ (void)registerBundle -{ - if(class_getClassMethod(NSClassFromString(@"MVMailBundle"), @selector(registerBundle))) - [NSClassFromString(@"MVMailBundle") performSelector:@selector(registerBundle)]; ++ (void)load { + [self setupClass:NSClassFromString(@"MailTableView") + swappingKeyDownWith:@selector(overrideMailKeyDown:)]; + + // this class does not exist on newer versions of Mail +// [self setupClass:NSClassFromString(@"MessagesTableView") +// swappingKeyDownWith:@selector(overrideMessagesKeyDown:)]; - //[[self class] load]; + [self setupClass:NSClassFromString(@"MessageViewer") + swappingKeyDownWith:@selector(overrideMessagesKeyDown:)]; } ++ (void)registerBundle { + Class mailBundleClass = NSClassFromString(@"MVMailBundle"); + if(class_getClassMethod(mailBundleClass, @selector(registerBundle))) + [mailBundleClass performSelector:@selector(registerBundle)]; +} -- (void)overrideMailKeyDown:(NSEvent*)event { +/** + * This method is where we perform known selectors on the message viewer. This is prefferable + * over the shortcut proxy since a user could change their shortcuts, unfortunately there is no + * documentation on which selectors the MessageViewer on Mail we could use. + */ +- (BOOL)performSelectorOnMessageViewer:(id)messageViewer basedOnEvent:(NSEvent*)event { unichar key = [[event characters] characterAtIndex:0]; - id messageViewer = [[self performSelector:@selector(delegate)] performSelector:@selector(delegate)]; + BOOL performed = YES; switch (key) { - case 'e': - case 'y': { - [messageViewer performSelector:@selector(archiveMessages:) withObject:nil]; - break; - } -// case 'h': { -// NSEvent *newEvent = [NSEvent eventWithCGEvent: CGEventCreateKeyboardEvent(NULL, 115, true)]; -// [self overrideMailKeyDown: newEvent]; -// break; -// } -// case 'l': { -// NSEvent *newEvent = [NSEvent eventWithCGEvent: CGEventCreateKeyboardEvent(NULL, 119, true)]; -// [self overrideMailKeyDown: newEvent]; -// break; -// } - case 'k': { - NSEvent *newEvent = [NSEvent eventWithCGEvent: CGEventCreateKeyboardEvent(NULL, 126, true)]; - [self overrideMailKeyDown: newEvent]; - break; - } - case 'K': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 126, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMailKeyDown: newEvent]; - break; - } - case 'j': { - NSEvent *newEvent = [NSEvent eventWithCGEvent: CGEventCreateKeyboardEvent(NULL, 125, true)]; - [self overrideMailKeyDown: newEvent]; - break; - } - case 'J': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 125, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMailKeyDown: newEvent]; - break; - } case '#': { [messageViewer performSelector:@selector(deleteMessages:) withObject:nil]; break; } + case 'a': { + [messageViewer performSelector:@selector(replyAllMessage:) withObject:nil]; + break; + } case 'c': { [messageViewer performSelector:@selector(showComposeWindow:) withObject:nil]; break; } - case 'r': { - [messageViewer performSelector:@selector(replyMessage:) withObject:nil]; + case 'e': + case 'y': { + [messageViewer performSelector:@selector(archiveMessages:) withObject:nil]; break; } case 'f': { [messageViewer performSelector:@selector(forwardMessage:) withObject:nil]; break; } - case 'a': { - [messageViewer performSelector:@selector(replyAllMessage:) withObject:nil]; + case 'o': { + [messageViewer performSelector:@selector(openMessages:) withObject:nil]; break; } - case '/': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 3, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskAlternate); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMailKeyDown: newEvent]; + case 'R': { + [messageViewer performSelector:@selector(checkNewMail:) withObject:nil]; break; } - case 's': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 0x25, true); // l - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMailKeyDown: newEvent]; + case 'r': { + [messageViewer performSelector:@selector(replyMessage:) withObject:nil]; break; } - case 'u': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMailKeyDown: newEvent]; + case 's': { + [messageViewer performSelector:@selector(toggleFlaggedStatus:) withObject:nil]; break; } default: - [self overrideMailKeyDown:event]; - break; - + performed = NO; } + return performed; } -- (void)overrideMessagesKeyDown:(NSEvent*)event { +/** + * This method is a proxy for shortcuts. We receive the Gmail key presses and translate it to normal + * Mail shortcuts. Althoug this is the easiest way to remap shortcuts it shouldn't be the primary + * way since a user could remap the entire set of shortcuts and have weird behavior using this + * plugin. Also there is the fact that some modifiers cannot be remapped, for instanse Alt+Up/Down + * can be used to go to next and previous message on a thread, but when we remap them here, the + * generated shortcut is the same as going to the beginning or end of the message list. + */ +-(NSEvent*)getShortcutRemappedEventFor:(NSEvent*)event { unichar key = [[event characters] characterAtIndex:0]; + NSEvent *newEvent = event; + CGEventRef cgEvent = NULL; switch (key) { - case 'e': - case 'y': { - [self performSelector:@selector(archiveMessages:) withObject:nil]; + case '!': { // mark message as Spam + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_J, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - // These don't work so well, but it looks like this is a Mail bug; the - // menu option for Select next/previous message in conversation just jumps - // to the next/previous thread instead. Also, it looks like capturing left/ - // right doesn't work for MessageViewer for some reason. -// case 'k': { -// [self performSelector:@selector(selectNextInThread:) withObject:nil]; -// break; -// } -// case 'j': { -// [self performSelector:@selector(selectPreviousInThread:) withObject:nil]; -// break; -// } - case '#': { - [self performSelector:@selector(deleteMessages:) withObject:nil]; + case '/': { // go to search field + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_F, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskAlternate); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 'c': { - [self performSelector:@selector(showComposeWindow:) withObject:nil]; + case 'g': { // go to the beginning of the list + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_UpArrow, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskAlternate | kCGEventFlagMaskControl); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 'r': { - [self performSelector:@selector(replyMessage:) withObject:nil]; + case 'G': { // go to the end of the list + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_DownArrow, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskAlternate | kCGEventFlagMaskControl); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 'f': { - [self performSelector:@selector(forwardMessage:) withObject:nil]; + case 'j': { // next message (down) + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_DownArrow, true); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 'a': { - [self performSelector:@selector(replyAllMessage:) withObject:nil]; + case 'J': { // expand selection to next message (down) + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_DownArrow, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskShift); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case '/': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 3, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskAlternate); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMessagesKeyDown: newEvent]; + case 'k': { // previous message (up) + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_UpArrow, true); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 's': { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 0x25, true); // l + case 'K': { // expand selection to previous message (up) + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_UpArrow, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskShift); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; + break; + } + case 'u': { // mark selected messages as unread + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMessagesKeyDown: newEvent]; + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - default: - [self overrideMessagesKeyDown:event]; + case 'v': { // view raw message + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskAlternate); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; + break; + } + case 'z': { // undo + cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_Z, true); + CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand); + newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; + } + } + if (cgEvent != NULL) { + // prevent memory leak from the temporary CGEvent + CFRelease(cgEvent); } + + return newEvent; } -- (void)overrideMessageEditorKeyDown:(NSEvent*)event { - unichar key = [[event characters] characterAtIndex:0]; +- (void)overrideMailKeyDown:(NSEvent*)event { + id messageViewer = [[self performSelector:@selector(delegate)] + performSelector:@selector(delegate)]; - switch (key) { - case '\t': { - _tabDate = [NSDate date]; - [self overrideMessageEditorKeyDown:event]; - break; - } - case '\r': { - if (_tabDate) { - double timePassed_ms = [_tabDate timeIntervalSinceNow] * -1000.0; - if (timePassed_ms < 500) { - CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 2, true); // D - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); - NSEvent *newEvent = [NSEvent eventWithCGEvent: cgEvent]; - [self overrideMessageEditorKeyDown: newEvent]; - break; - } else { - _tabDate = nil; // avoid unnecessary calculations later - } - } - [self overrideMessageEditorKeyDown:event]; - break; - } - default: - [self overrideMessageEditorKeyDown:event]; - break; + if (! [self performSelectorOnMessageViewer:messageViewer basedOnEvent:event]) { + [self overrideMailKeyDown:[self getShortcutRemappedEventFor:event]]; } } +- (void)overrideMessagesKeyDown:(NSEvent*)event { + if (! [self performSelectorOnMessageViewer:self basedOnEvent:event]) { + [self overrideMessagesKeyDown:[self getShortcutRemappedEventFor:event]]; + } +} @end diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index da24c6690db922b8d110298515b4e07646a643e9..ccc4213de32ad58ae1658d04a004768dbe1f0a36 100644 GIT binary patch delta 764 zcmYk4&ubG=5Xa~3kEU8{UmL4QZM3OdZK}qkf(Jojve`wW zDJ54?dXV77g9x5HXc6=e@K6te$9kzJK|ELwf>*&Yd5aD_KJ(){^X4u4p6(9JEjKOG z^%9BCgyRLVFVMel|G`y3} zHCy*u4$6amuBcaT>or4NYhpJ1=yjOaXsC0J54ANq=8H=8hU2mOOCFA|;a<{uaq()l zEUQ`}omVzAIxbE7nM}EmomaG6Rw<~txty{XD_w{u7LvHUw65GxI)g%w3gZUjTgD%ZQ({kdi}5kzM@F%) zSHHyAVtmQ?l`#_N>B@|ojN6RA8F9b%(v`e!S|)zmhNGs?)C@lVx?vkgpT+}+S+D23% zUP{Ij&<{~;l5tcE9>6Pj2Rrbc1jr;wkp2lb6nw6~f)(<5#JewnV&kr@0ml(yxoDpXMq*?l)B=jKV+8%6{zkv7M|R-4 z3Z(}@%bhcGX68zhi?6G;xUaiivsu3z(J>hn^?JTpH0$NN(d{gQ8C{&8O@59a!t9S} zDzx=2Fzq$;?2+GoJw{2aPaTbf|)#c5sz!_zku1Ce_uJ5;R%ktsjVO$+fjpv)y z;oz>EcAIV8^^aFL3=lA0^otSFJWv0h9Q<_A%=`1RpC(`G$McZ=e!XbxSl`#}X1l7_ z{l|KV;wRv>+y0`HEpx5`b{@}^KJU^L%a>Mr@?Q#&vQz+XkO-= zrdogAM6>}Ht-8nGp7T0pRbz5W>UFgmSoF1+lho5{{-s*hv&ZcZ6a00&I>mT%x9R%t zLT{cTIhKpLp9D8cG;H7fz<7a=Q=k*B(Bt#hH&blLyxCT5Xsg}sSl2J_Ujq9P`s;pG ztv}Y);!vHi**@^{(#j>&5t( z>Sn*)qJ#Bf+U}RlI$)cszWIbz>K|`!FXMl*tFEgK&3~!BrcyC)ERbjk2x$zMWCb`a zy(Tm&A^0o07aWz61Z6lRCkzO}6?h#IWUsP}l&3`45KfUOS!4{fuoOga8m#mVjA2yz zXiPR~2yoY6&6*0-mu9)-kPvP%g$nKH?~MHboFd2@siRa7hzTBzu;1a*D?x zzUVR@@B*~<0d+D7CL{_>5SbLNAcY^oF>g&|F#;jPqo0g|;mL#cIe6=-qQMN|6v6R? zWQ1sS@Fjxhib3Wfcpa08N)O>E#oY<9khta`qF3O&z;$(!*5MP$gZnXRxMu-bQbbbc z5ErNcM?HSR66aD3@>wr=vJ!|r`01R-=&}VBSvFpKMfjjjVg*jN85pOBC0PW=6u#0< zaHDvz_Vwj^=@}7GVb~5N%Xk{afi+e^AYNu~Bo1{$I - - - 1050 - 14F27 - 7706 - 1348.17 - 758.70 - - com.apple.InterfaceBuilder.CocoaPlugin - 7706 - - - NSCustomObject - NSImageCell - NSScrollView - NSScroller - NSSearchField - NSSearchFieldCell - NSTableColumn - NSTableView - NSTextFieldCell - NSView - NSWindowTemplate - - - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - - SearchPopup - - - FirstResponder - - - NSApplication - - - 89 - 2 - {{673, 431}, {670, 205}} - 1685591040 - Search Mailbox - NSPanel - - - - - 256 - - - - 266 - {{20, 163}, {630, 22}} - - - - YES - YES - - 342884417 - 268436480 - - - YES - 13 - 1044 - - - YES - 1 - - 6 - System - textBackgroundColor - - 3 - MQA - - - - 6 - System - controlTextColor - - 3 - MAA - - - - 0 - 0 - search - - _searchFieldSearch: - - 138690560 - 0 - - 400 - 75 - - - 0 - 0 - clear - - - cancel - - - - - _searchFieldCancel: - - 138690560 - 0 - - 400 - 75 - - 255 - DgAAAA - - NO - 1 - - - - 274 - - - - 2322 - - - - 256 - {628, 133} - - - - YES - NO - YES - - - -2147483392 - {{209, 0}, {16, 17}} - - - - image - 40 - 40 - 1000 - - 75497536 - 201328640 - Image - - YES - 11 - 3100 - - - 3 - MC4zMzMzMzI5OQA - - - 6 - System - headerTextColor - - - - - 134217728 - 33685504 - 0 - 0 - 0 - NO - - - - - title - 582 - 40 - 1000 - - 75497536 - 2048 - Title - - - - - - 337641536 - 2048 - Text Cell - - - - 6 - System - controlBackgroundColor - - 3 - MC42NjY2NjY2NjY3AA - - - - - 1 - YES - - - - 3 - 2 - - - 6 - System - gridColor - - 3 - MC41AA - - - 17 - 37748736 - - - 4 - 15 - 0 - NO - 0 - 1 - - - {{1, 1}, {628, 133}} - - - - - - 4 - YES - - - - -2147483392 - {{209, 1}, {15, 126}} - - - NO - _doScroller: - - - _doScroller: - 0.99212599999999995 - - - - -2147483392 - {{-100, -100}, {208, 15}} - - - - NO - _doScroller: - - 1 - - _doScroller: - 0.81632660000000001 - - - {{20, 20}, {630, 135}} - - - - 133650 - - - - QSAAAEEgAABBmAAAQZgAAA - 0.25 - 4 - 1 - - - {670, 205} - - - - - {{0, 0}, {1680, 1027}} - {10000000000000, 10000000000000} - YES - - - - - - - searchField - - - - 7 - - - - searchWindow - - - - 8 - - - - doSearch: - - - - 9 - - - - resultViewer - - - - 33 - - - - changeSelection: - - - - 34 - - - - delegate - - - - 10 - - - - dataSource - - - - 32 - - - - - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 1 - - - - - - - - 2 - - - - - - - - - 4 - - - - - - - - 5 - - - - - 20 - - - - - - - - - - 21 - - - - - 22 - - - - - 23 - - - - - - - - - 25 - - - - - - - - 26 - - - - - - - - 27 - - - - - 29 - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - {497, 285.5} - com.apple.InterfaceBuilder.CocoaPlugin - {{703, 547}, {265, 198}} - - - com.apple.InterfaceBuilder.CocoaPlugin - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - 36 - - - - - SearchPopup - NSObject - - id - id - - - - changeSelection: - id - - - doSearch: - id - - - - NSTableView - NSSearchField - NSWindow - - - - resultViewer - NSTableView - - - searchField - NSSearchField - - - searchWindow - NSWindow - - - - IBProjectSource - ../GMailinator/SearchPopup.h - - - - SearchPopup - - id - id - - - - changeSelection: - id - - - doSearch: - id - - - - IBProjectSource - ../GMailinator/SearchPopup.m - - - - - - NSActionCell - NSCell - - IBFrameworkSource - AppKit.framework/Headers/NSActionCell.h - - - - NSApplication - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSApplication.h - - - - NSCell - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSCell.h - - - - NSControl - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSControl.h - - - - NSImageCell - NSCell - - IBFrameworkSource - AppKit.framework/Headers/NSImageCell.h - - - - NSMenu - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSMenu.h - - - - NSPanel - NSWindow - - IBFrameworkSource - AppKit.framework/Headers/NSPanel.h - - - - NSResponder - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSResponder.h - - - - NSScrollView - NSView - - IBFrameworkSource - AppKit.framework/Headers/NSScrollView.h - - - - NSScroller - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSScroller.h - - - - NSSearchField - NSTextField - - IBFrameworkSource - AppKit.framework/Headers/NSSearchField.h - - - - NSSearchFieldCell - NSTextFieldCell - - IBFrameworkSource - AppKit.framework/Headers/NSSearchFieldCell.h - - - - NSTableColumn - NSObject - - IBFrameworkSource - AppKit.framework/Headers/NSTableColumn.h - - - - NSTableView - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSTableView.h - - - - NSTextField - NSControl - - IBFrameworkSource - AppKit.framework/Headers/NSTextField.h - - - - NSTextFieldCell - NSActionCell - - IBFrameworkSource - AppKit.framework/Headers/NSTextFieldCell.h - - - - NSView - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSView.h - - - - NSWindow - NSResponder - - IBFrameworkSource - AppKit.framework/Headers/NSWindow.h - - - - - 0 - IBCocoaFramework - NO - - com.apple.InterfaceBuilder.CocoaPlugin.macosx - - - - com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 - - - YES - 3 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index ed72a18..4b8c290 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,27 @@ in progress. Tested with Mail for OS X 10.8.4. ## Supported Shortcuts - - - - - - - - - - - - - -
KeyAction
cCompose new message
rReply
aReply All
y, eArchive
#Delete
jGo to previous message/thread
kGo to next message/thread
/Mailbox search
lMove to folder (opens dialog)
sFlag
tab, then enterSend message
+| Key | Action | +| :----: | ------------------------------ | +| # | Delete | +| / | Mailbox search | +| ! | Toggle message as Junk | +| a | Reply All | +| c | Compose new message | +| e, y | Archive | +| f | Forward message | +| G | Go to the last message | +| g | Go to the first message | +| j | Go to next message/thread | +| k | Go to previous message/thread | +| l | Move to folder (opens dialog) | +| o | Open selected message | +| R | Get new mail (Refresh) | +| r | Reply | +| s | Flag | +| u | Toggle message as read | +| v | View raw message dialog | +| z | Undo | ## How to install From 9c0a80e606e17b5cfcba882a77b1b2cdd4c08ae2 Mon Sep 17 00:00:00 2001 From: Thiago Alves Date: Sun, 17 Jul 2016 14:48:44 -0700 Subject: [PATCH 12/15] Fix the macOS version where I test this. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b8c290..22c6b5d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GMailinator Adds Gmail-esque keyboard shorcuts to Mail.app. This is still very much a work -in progress. Tested with Mail for OS X 10.8.4. +in progress. Tested with Mail for OS X 10.11.5. ## Supported Shortcuts From e259f22f46123ebcc05266d61c3c395cfc9871a7 Mon Sep 17 00:00:00 2001 From: Adrian Rollett Date: Tue, 4 Oct 2016 08:57:08 -0600 Subject: [PATCH 13/15] Mac OS Sierra updates --- GMailinator.xcodeproj/project.pbxproj | 2 +- GMailinator/GMailinator.m | 10 ++++++++-- GMailinator/Info.plist | Bin 1499 -> 1633 bytes 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/GMailinator.xcodeproj/project.pbxproj b/GMailinator.xcodeproj/project.pbxproj index baad0b0..bd27f59 100644 --- a/GMailinator.xcodeproj/project.pbxproj +++ b/GMailinator.xcodeproj/project.pbxproj @@ -131,7 +131,7 @@ isa = PBXNativeTarget; buildConfigurationList = 499015E81615FE5300991F6C /* Build configuration list for PBXNativeTarget "GMailinator" */; buildPhases = ( - A718F5961A681B6700545966 /* Save current UUID to Info.plist */, + A718F5961A681B6700545966 /* Save current UUID to Info.plist */, 499015D61615FE5300991F6C /* Sources */, 499015D71615FE5300991F6C /* Frameworks */, 499015D81615FE5300991F6C /* Resources */, diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index 2da14bd..7a04a4b 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -232,8 +232,14 @@ -(NSEvent*)getShortcutRemappedEventFor:(NSEvent*)event { } - (void)overrideMailKeyDown:(NSEvent*)event { - id messageViewer = [[self performSelector:@selector(delegate)] - performSelector:@selector(delegate)]; + id tableViewManager = [self performSelector:@selector(delegate)]; + id messageListViewController = [tableViewManager performSelector:@selector(delegate)]; + + // NOTE: backwards compatibility. In 10.11 and earlier, tableViewManager.delegate.delegate was already the message viewer. + id messageViewer + = [messageListViewController respondsToSelector:@selector(messageViewer)] + ? [messageListViewController performSelector:@selector(messageViewer)] + : messageListViewController; if (! [self performSelectorOnMessageViewer:messageViewer basedOnEvent:event]) { [self overrideMailKeyDown:[self getShortcutRemappedEventFor:event]]; diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index ccc4213de32ad58ae1658d04a004768dbe1f0a36..790e858d2667509a81db06f3c0129750fda5cb74 100644 GIT binary patch delta 376 zcmcc3{g6jFsURn_xWvHVE+Z2&3o9Et2PYRd4=*3TfS{1DsE)3lzJcLH#cXx8;L?JE z{GyW76hi|&L!*G4()7$c=ltA)#FEUU%$&@U%Fs|xm*S0GjEpV|MdAfijLn>#oh+Q3 zbd8J*%ymsnO@ES;Qnoh)6|nXi%Eqe88p4{un#!8RTEu#j0SuTJAvA*#l!j7FtakzG@m8n+ delta 204 zcmaFJbDLW+sURn_xWvHV4kHsY3o9Et2PYRd4=*3TfS{0urk1vj?nLG6iQE56tQQa! z77-N_mync_mXVc{S5Q<^R#8<`-~5Y_pLz0MMwQ7StUZ;A40;UC49N_o3|$QC7!ERA zWq8fV&Zxv_<#9z*xdq#@NlcnDG?jM Date: Wed, 12 Oct 2016 10:45:25 -0600 Subject: [PATCH 14/15] Fix mark as read/unread shortcuts --- GMailinator/GMailinator.m | 14 ++++++++------ GMailinator/Info.plist | Bin 1633 -> 1843 bytes README.md | 5 +++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/GMailinator/GMailinator.m b/GMailinator/GMailinator.m index 7a04a4b..71cbae1 100644 --- a/GMailinator/GMailinator.m +++ b/GMailinator/GMailinator.m @@ -137,6 +137,14 @@ - (BOOL)performSelectorOnMessageViewer:(id)messageViewer basedOnEvent:(NSEvent*) [messageViewer performSelector:@selector(toggleFlaggedStatus:) withObject:nil]; break; } + case 'u': { + [messageViewer performSelector:@selector(markAsRead:) withObject:nil]; + break; + } + case 'U': { + [messageViewer performSelector:@selector(markAsUnread:) withObject:nil]; + break; + } default: performed = NO; } @@ -203,12 +211,6 @@ -(NSEvent*)getShortcutRemappedEventFor:(NSEvent*)event { newEvent = [NSEvent eventWithCGEvent: cgEvent]; break; } - case 'u': { // mark selected messages as unread - cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); - CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskShift); - newEvent = [NSEvent eventWithCGEvent: cgEvent]; - break; - } case 'v': { // view raw message cgEvent = CGEventCreateKeyboardEvent(NULL, kVK_ANSI_U, true); CGEventSetFlags(cgEvent, kCGEventFlagMaskCommand | kCGEventFlagMaskAlternate); diff --git a/GMailinator/Info.plist b/GMailinator/Info.plist index 790e858d2667509a81db06f3c0129750fda5cb74..7f796fdcbb31f32f13d482c7106deb09b2bf3aee 100644 GIT binary patch delta 323 zcmaFJvzc##G>4wPp^>qPso7*-M)`?G?i07GOsuqi7RfY8evce*wV&W2#QqnTAa`Fm_O3Es#YQpO3 z8k$<#I=Y*GF-9>@)@4=4zzZ407!81;CY(-wECo)cE+{}23iIGWz z$)Bl=X)els zEGaEYo%ly$vIe86e!PGbTrMNOs3a`4s5mn}FSw*AGcP?}fM2CLAjsb()Hx*H&(YVl zc5)u0>f{ToVvHh__p_)=uNROI6PJ*bl9rK`lUGnwQdUt_Q`gWG)6&x3yqPhIc`_dx zFQ+{aggE)R_)Jz|%dZbM)H7h{`1EUzD5u-0-7Gn+L zY{vPF3mLaD?qIyn_=`!A$%84MX&TdBrYFqY%tp+S%ni)Tna?qQWszZVX31ii$g+dw kJ}VomE^7#D0&6O37HbjfO$IPvVua8PMo^j)O0(Vt0O Date: Sat, 28 Oct 2017 18:30:26 +0530 Subject: [PATCH 15/15] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9499888..72526c9 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ in progress. Tested with Mail for OS X Sierra. ## Credits -A lot of this was built with heavy use of of the +A lot of this was built with heavy use of the [BindDeleteKeyToArchive](https://github.com/benlenarts/BindDeleteKeyToArchive) project by Ben Lenarts. The Xcode project and interface skeleton were all from that project, and for the most part, renamed. I added the keybinding code.