From b69c5172763b4c233173298fe60b3512bf5c6793 Mon Sep 17 00:00:00 2001 From: Thomas Rosenstein Date: Wed, 17 Jul 2013 09:38:02 +0200 Subject: [PATCH 01/97] Create default config Moved the default config creation to awakeFromNib, because if neither ssh_config nor config exist, it never get's created --- Shuttle/AppDelegate.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 607a9e5..dafc2c3 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -13,7 +13,13 @@ - (void) awakeFromNib { // Load the menu content // [self loadMenu]; - + + // if the config file does not exist, create a default one + if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { + NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; + [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; + } + // Create the status bar item statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:25.0]; [statusItem setMenu:menu]; @@ -140,12 +146,6 @@ - (void) loadMenu { for (int i=0;i Date: Wed, 17 Jul 2013 10:55:12 +0200 Subject: [PATCH 02/97] Added capability to open urls of all kinds Before opening a command in a terminal, the command is checked and if it's a url it's opened. Useful for custom url schemes. --- Shuttle/AppDelegate.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 607a9e5..3fd20b5 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -265,8 +265,14 @@ - (void) loadMenu { - (void) openHost:(NSMenuItem *) sender { //NSLog(@"sender: %@", sender); //NSLog(@"Command: %@",[sender representedObject]); - - if ( [terminalPref isEqualToString: @"iterm"] ) { + + // Check if Url + NSURL* url = [NSURL URLWithString:[sender representedObject]]; + if(url) + { + [[NSWorkspace sharedWorkspace] openURL:url]; + } + else if ( [terminalPref isEqualToString: @"iterm"] ) { NSAppleScript* iTerm2 = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"on ApplicationIsRunning(appName) \n" From 9073c0c528dc697ea7117b1b1d42e0d36d3943c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Bo=CC=88decs?= Date: Wed, 17 Jul 2013 12:40:33 +0200 Subject: [PATCH 03/97] - fix: " character escaping issue in cmd scripts --- Shuttle/AppDelegate.m | 92 ++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 607a9e5..f02c8a5 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -13,14 +13,14 @@ - (void) awakeFromNib { // Load the menu content // [self loadMenu]; - + // Create the status bar item statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:25.0]; [statusItem setMenu:menu]; [statusItem setHighlightMode:YES]; [statusItem setImage:[NSImage imageNamed:@"StatusIcon"]]; [statusItem setAlternateImage:[NSImage imageNamed:@"StatusIconAlt"]]; - + launchAtLoginController = [[LaunchAtLoginController alloc] init]; // Needed to trigger the menuWillOpen event @@ -114,7 +114,7 @@ - (NSDictionary*) parseSSHConfigFile { } else { components = [cleanedLine componentsSeparatedByCharactersInSet: - [NSCharacterSet whitespaceCharacterSet]]; + [NSCharacterSet whitespaceCharacterSet]]; } NSString* host = [[components objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; @@ -122,7 +122,7 @@ - (NSDictionary*) parseSSHConfigFile { } } - return servers; + return servers; } // Replaces Underscores with Spaces for better readable names @@ -140,7 +140,7 @@ - (void) loadMenu { for (int i=0;i Date: Wed, 17 Jul 2013 12:21:23 -0400 Subject: [PATCH 04/97] Extremely small OCD fix Just had to do it. --- Shuttle/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 607a9e5..56f3bf5 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -178,7 +178,7 @@ - (void) loadMenu { NSDictionary* data = [servers objectForKey:key]; // Ignore entrys that contain wildcard characters - NSString* host= [data valueForKey:@"Host"]; + NSString* host = [data valueForKey:@"Host"]; if ([host rangeOfString:@"*"].length != 0) continue; From e5b09777232a8a3cf02404be7ef4ab2dc3e922cd Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Wed, 17 Jul 2013 20:47:46 -0400 Subject: [PATCH 05/97] Fix iTerm split behavior If using splits inside iTerm, Shuttle was openining hosts into the last open split and disregarding the newly-created tab. See #5. --- Shuttle/AppDelegate.m | 2 +- apple-scripts/iterm2.applescript | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 607a9e5..ecf5998 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -280,7 +280,7 @@ - (void) openHost:(NSMenuItem *) sender { @" tell the current terminal \n" @" if isRunning then \n" @" set newSession to (launch session \"Default Session\") \n" - @" tell newSession \n" + @" tell the last session \n" @" write text \"clear\" \n" @" write text \"%1$@\" \n" @" end tell \n" diff --git a/apple-scripts/iterm2.applescript b/apple-scripts/iterm2.applescript index f7acb2b..126fc84 100644 --- a/apple-scripts/iterm2.applescript +++ b/apple-scripts/iterm2.applescript @@ -9,7 +9,7 @@ tell application "iTerm" tell the current terminal if isRunning then set newSession to (launch session "Default Session") - tell newSession + tell the last session write text "clear" write text "%1$@" end tell From 534cd446e553ffd82857d6bd715b2984633cdbca Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 18 Jul 2013 00:48:09 -0400 Subject: [PATCH 06/97] Add contributors --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index c59f29a..21f6d6b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,18 @@ A simple SSH shortcut menu for OS X * Open menu * Select host option within menu +## Contributors + +This project was created by Trevor Fitzgerald. I owe many thanks to the following people who have helped make Shuttle even better. + +(In alphabetical order) + +* Dmitry Filimonov +* Marco Aurélio +* Martin Grund +* Ryan Cohen +* Thomas Rosenstein + ## Credits Shuttle was inspired by [SSHMenu](http://sshmenu.sourceforge.net/), the GNOME applet for Linux. From ef367810a9ac496669396ed0040ec4126e9bf333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tibor=20Bo=CC=88decs?= Date: Thu, 18 Jul 2013 12:52:21 +0200 Subject: [PATCH 07/97] - unnecessary nslog removed --- Shuttle/AppDelegate.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index f02c8a5..5099a7e 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -265,11 +265,8 @@ - (void) loadMenu { - (void) openHost:(NSMenuItem *) sender { - NSString *escapedObject = [[sender representedObject] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - NSLog(@"%@", escapedObject); - if ( [terminalPref isEqualToString: @"iterm"] ) { NSAppleScript* iTerm2 = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @@ -299,7 +296,8 @@ - (void) openHost:(NSMenuItem *) sender @"end tell \n" , escapedObject]]; [iTerm2 executeAndReturnError:nil]; - } else { + } + else { NSAppleScript* terminalapp = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"on ApplicationIsRunning(appName) \n" From a8d2ed80b7f654f0433433ea8fb7ee4aaf61cbf5 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 19:46:22 -0400 Subject: [PATCH 08/97] add license. closes #27 --- LICENSE | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7130522 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2013 Trevor Fitzgerald + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 0118713690b68461da8fae6194e828523a2177b8 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 20:01:24 -0400 Subject: [PATCH 09/97] Menu not being created at awakeFromNib. Undo #35 --- Shuttle/AppDelegate.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index b4ae7f4..fa7b374 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -14,12 +14,6 @@ - (void) awakeFromNib { // Load the menu content // [self loadMenu]; - // if the config file does not exist, create a default one - if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { - NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; - [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; - } - // Create the status bar item statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:25.0]; [statusItem setMenu:menu]; @@ -137,6 +131,12 @@ - (NSString*) humanize: (NSString*) val{ } - (void) loadMenu { + + // if the config file does not exist, create a default one + if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { + NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; + [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; + } // System configuration NSDictionary* servers = [self parseSSHConfigFile]; From a8c2670b66e43323d53ae55c66a6b7109239a951 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 21:17:47 -0400 Subject: [PATCH 10/97] add test configs --- tests/README.md | 4 ++++ tests/shuttle.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ tests/ssh_config | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 tests/README.md create mode 100644 tests/shuttle.json create mode 100644 tests/ssh_config diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..d99848c --- /dev/null +++ b/tests/README.md @@ -0,0 +1,4 @@ +Sample `.shuttle.json` and `.ssh/config` file for testing functionality of application. + +1. Copy `shuttle.json` to `~/.shuttle.json` +2. Copy `ssh_config` to `~/.ssh/config` diff --git a/tests/shuttle.json b/tests/shuttle.json new file mode 100644 index 0000000..05d1853 --- /dev/null +++ b/tests/shuttle.json @@ -0,0 +1,45 @@ +{ + "_comment": "Valid terminals include: 'Terminal.app' or 'iTerm'", + "_comment": "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", + "_comment": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", + "terminal": "Terminal.app", + "launch_at_login": false, + "hosts": [ + { + "name": "My Dev Server", + "cmd": "ssh username@dev.example.com" + }, + { + "Personal": [ + { + "name": "My blog", + "cmd": "ssh username@blog.example.com" + } + ] + }, + { + "Work": [ + { + "name": "dev.example.net", + "cmd": "ssh username@dev.example.net -p 3000" + }, + { + "name": "staging.example.net", + "cmd": "ssh username@staging.example.net -p 3000" + }, + { + "name": "production.example.net", + "cmd": "ssh username@example.net -p 3000" + } + ] + }, + { + "Vagrant": [ + { + "name": "precise32", + "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" + } + ] + } + ] +} diff --git a/tests/ssh_config b/tests/ssh_config new file mode 100644 index 0000000..5540885 --- /dev/null +++ b/tests/ssh_config @@ -0,0 +1,43 @@ +# +# A couple of Github accounts +# Prefixed with `.` so they are ignored from the Shuttle menu +# +Host .github.amrom.workers.dev-user1 + User user1 + Hostname github.com + IdentityFile ~/.ssh/github-1.key + +Host .github.amrom.workers.dev-user2 + User user2 + Hostname github.com + IdentityFile ~/.ssh/github-2.key + +# +# Main menu items +# +Host blog.example.com + User username + Hostname blog.example.com + +# +# Grouped menu items +# +Host Group1/web01.example.com + User username + Hostname web01.example.com + +Host Group1/web02.example.com + User username + Hostname web02.example.com + +Host Group1/web03.example.com + User username + Hostname web03.example.com + +Host Group2/mysql01.example.com + User username + Hostname mysql01.example.com + +Host Group2/mysql02.example.com + User username + Hostname mysql02.example.com From 0953a8750a792a6cd99003e20b2b700fe1ccbcf7 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 21:34:37 -0400 Subject: [PATCH 11/97] add better/more checks to sample shuttle.json --- tests/shuttle.json | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/shuttle.json b/tests/shuttle.json index 05d1853..044bf1d 100644 --- a/tests/shuttle.json +++ b/tests/shuttle.json @@ -1,42 +1,47 @@ { - "_comment": "Valid terminals include: 'Terminal.app' or 'iTerm'", - "_comment": "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", - "_comment": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", "terminal": "Terminal.app", "launch_at_login": false, "hosts": [ { - "name": "My Dev Server", + "name": "Test escaping characters", + "cmd": "echo \"Hello\"; echo 'world'; " + }, + { + "name": "Test opening URL", + "cmd": "http://fitztrev.github.io/shuttle" + }, + { + "name": "Main Item", "cmd": "ssh username@dev.example.com" }, { - "Personal": [ + "Submenu 1": [ { - "name": "My blog", + "name": "Submenu Item #1.1", "cmd": "ssh username@blog.example.com" } ] }, { - "Work": [ + "Submenu 2": [ { - "name": "dev.example.net", + "name": "Submenu Item #2.1", "cmd": "ssh username@dev.example.net -p 3000" }, { - "name": "staging.example.net", + "name": "Submenu Item #2.2", "cmd": "ssh username@staging.example.net -p 3000" }, { - "name": "production.example.net", + "name": "Submenu Item #2.3", "cmd": "ssh username@example.net -p 3000" } ] }, { - "Vagrant": [ + "Submenu 3": [ { - "name": "precise32", + "name": "Submenu Item #3.1", "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" } ] From 77d3b2692410a9d4fc8213c806ba65498d571421 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 21:36:57 -0400 Subject: [PATCH 12/97] =?UTF-8?q?Add=20Tibor=20B=C3=B6decs=20to=20contribu?= =?UTF-8?q?tors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 21f6d6b..6738daa 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This project was created by Trevor Fitzgerald. I owe many thanks to the followin * Martin Grund * Ryan Cohen * Thomas Rosenstein +* Tibor Bödecs ## Credits From e591136789bd34dd111967396991c03303d715cc Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 21:49:55 -0400 Subject: [PATCH 13/97] Ignore .ssh/config entries that start with `.` --- Shuttle/AppDelegate.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index fa7b374..03b7fda 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -176,12 +176,16 @@ - (void) loadMenu { // First add all the system serves we know for (id key in servers) { NSDictionary* data = [servers objectForKey:key]; - - // Ignore entrys that contain wildcard characters NSString* host = [data valueForKey:@"Host"]; + + // Ignore entries that contain wildcard characters if ([host rangeOfString:@"*"].length != 0) continue; + // Ignore entries that start with `.` + if ([host hasPrefix:@"."]) + continue; + // Parse hosts... NSRange ns = [host rangeOfString:@"/"]; if (ns.length == 0) { From 1e2c770f98d09815cf080404d2dd067b228ee051 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 22:32:38 -0400 Subject: [PATCH 14/97] v1.1.1 --- Shuttle/Shuttle-Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index ae86995..56e89fd 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.0 + 1.1.1 CFBundleSignature ???? CFBundleVersion - 1.1.0 + 1.1.1 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From 5788786654dfd81b8a1f09ae156300b785d77234 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 22:47:02 -0400 Subject: [PATCH 15/97] move default config setup back to awakeFromNib (#35) --- Shuttle/AppDelegate.m | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 03b7fda..df59aae 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -11,6 +11,12 @@ - (void) awakeFromNib { // The path for the configuration file (by default: ~/.shuttle.json) shuttleConfigFile = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json"]; + // if the config file does not exist, create a default one + if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { + NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; + [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; + } + // Load the menu content // [self loadMenu]; @@ -131,12 +137,6 @@ - (NSString*) humanize: (NSString*) val{ } - (void) loadMenu { - - // if the config file does not exist, create a default one - if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { - NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; - [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; - } // System configuration NSDictionary* servers = [self parseSSHConfigFile]; From 53013126e1e321331deaf40c4a6dece314be83ff Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 19 Jul 2013 23:00:55 -0400 Subject: [PATCH 16/97] add /etc/ssh_config test config file --- tests/{shuttle.json => .shuttle.json} | 0 tests/{ssh_config => .ssh/config} | 0 tests/README.md | 5 +--- tests/etc/ssh_config | 43 +++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) rename tests/{shuttle.json => .shuttle.json} (100%) rename tests/{ssh_config => .ssh/config} (100%) create mode 100644 tests/etc/ssh_config diff --git a/tests/shuttle.json b/tests/.shuttle.json similarity index 100% rename from tests/shuttle.json rename to tests/.shuttle.json diff --git a/tests/ssh_config b/tests/.ssh/config similarity index 100% rename from tests/ssh_config rename to tests/.ssh/config diff --git a/tests/README.md b/tests/README.md index d99848c..5d11634 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,4 +1 @@ -Sample `.shuttle.json` and `.ssh/config` file for testing functionality of application. - -1. Copy `shuttle.json` to `~/.shuttle.json` -2. Copy `ssh_config` to `~/.ssh/config` +Sample config files for testing various functionalities of the application. diff --git a/tests/etc/ssh_config b/tests/etc/ssh_config new file mode 100644 index 0000000..9aaec17 --- /dev/null +++ b/tests/etc/ssh_config @@ -0,0 +1,43 @@ +# +# A couple of Github accounts +# Prefixed with `.` so they are ignored from the Shuttle menu +# +Host .github.amrom.workers.dev-user1.global + User user1 + Hostname github.com + IdentityFile ~/.ssh/github-1.key + +Host .github.amrom.workers.dev-user2.global + User user2 + Hostname github.com + IdentityFile ~/.ssh/github-2.key + +# +# Main menu items +# +Host global-blog.example.com + User username + Hostname blog.example.com + +# +# Grouped menu items +# +Host Global-Group1/web01.example.com + User username + Hostname web01.example.com + +Host Global-Group1/web02.example.com + User username + Hostname web02.example.com + +Host Global-Group1/web03.example.com + User username + Hostname web03.example.com + +Host Global-Group2/mysql01.example.com + User username + Hostname mysql01.example.com + +Host Global-Group2/mysql02.example.com + User username + Hostname mysql02.example.com From 694f34b5207d843637388c8c63ecb74492b45f3e Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Tue, 23 Jul 2013 16:39:57 -0400 Subject: [PATCH 17/97] Fix JSON parsing issues with duplicate keys. Fixes #43 --- Shuttle/shuttle.default.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 05d1853..10da386 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -1,7 +1,7 @@ { - "_comment": "Valid terminals include: 'Terminal.app' or 'iTerm'", - "_comment": "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", - "_comment": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", + "_comment1": "Valid terminals include: 'Terminal.app' or 'iTerm'", + "_comment2": "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", + "_comment3": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", "terminal": "Terminal.app", "launch_at_login": false, "hosts": [ From a0da7d504825e51c6f5f6941c75434958cc80e58 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Tue, 23 Jul 2013 16:44:07 -0400 Subject: [PATCH 18/97] v1.1.2 --- Shuttle/Shuttle-Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 56e89fd..5903134 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.1 + 1.1.2 CFBundleSignature ???? CFBundleVersion - 1.1.1 + 1.1.2 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From 48b86db5f7380a9592a3949155ebd2c75a3aae0e Mon Sep 17 00:00:00 2001 From: Jack Weeden Date: Wed, 24 Jul 2013 11:27:29 +0100 Subject: [PATCH 19/97] Include option to show/hide servers from SSH config files Added a property to the JSON config to enable/disable the automatic adding of hosts in user's SSH config files. Default is to show them. --- Shuttle/AppDelegate.m | 52 +++++++++++++++++++----------------- Shuttle/shuttle.default.json | 1 + 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index df59aae..4aa0317 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -173,34 +173,38 @@ - (void) loadMenu { NSMutableDictionary* fullMenu = [NSMutableDictionary dictionary]; - // First add all the system serves we know - for (id key in servers) { - NSDictionary* data = [servers objectForKey:key]; - NSString* host = [data valueForKey:@"Host"]; - - // Ignore entries that contain wildcard characters - if ([host rangeOfString:@"*"].length != 0) - continue; - - // Ignore entries that start with `.` - if ([host hasPrefix:@"."]) - continue; + // First add all the system servers we know + BOOL showSshConfigHosts = [json[@"show_ssh_config_hosts"] boolValue]; + + if (showSshConfigHosts) { + for (id key in servers) { + NSDictionary* data = [servers objectForKey:key]; + NSString* host = [data valueForKey:@"Host"]; - // Parse hosts... - NSRange ns = [host rangeOfString:@"/"]; - if (ns.length == 0) { - [fullMenu setObject:[NSString stringWithFormat:@"ssh %@", host] forKey:[self humanize:host]]; + // Ignore entries that contain wildcard characters + if ([host rangeOfString:@"*"].length != 0) + continue; - } else { - NSString *part = [host substringToIndex: ns.location]; - host = [host substringFromIndex:ns.location + 1]; + // Ignore entries that start with `.` + if ([host hasPrefix:@"."]) + continue; - if ([fullMenu objectForKey:part] == nil) { - NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; - [fullMenu setObject:tmp forKey:part]; - } + // Parse hosts... + NSRange ns = [host rangeOfString:@"/"]; + if (ns.length == 0) { + [fullMenu setObject:[NSString stringWithFormat:@"ssh %@", host] forKey:[self humanize:host]]; + + } else { + NSString *part = [host substringToIndex: ns.location]; + host = [host substringFromIndex:ns.location + 1]; - [[fullMenu objectForKey:part] setObject:[NSString stringWithFormat:@"ssh %@", [data valueForKey:@"Host"]] forKey:host]; + if ([fullMenu objectForKey:part] == nil) { + NSMutableDictionary *tmp = [NSMutableDictionary dictionary]; + [fullMenu setObject:tmp forKey:part]; + } + + [[fullMenu objectForKey:part] setObject:[NSString stringWithFormat:@"ssh %@", [data valueForKey:@"Host"]] forKey:host]; + } } } diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 10da386..8f736cf 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -4,6 +4,7 @@ "_comment3": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", "terminal": "Terminal.app", "launch_at_login": false, + "show_ssh_config_hosts": true, "hosts": [ { "name": "My Dev Server", From 4aacd46a14a842ca857c4418fd5461f6bf8aedd6 Mon Sep 17 00:00:00 2001 From: Jack Weeden Date: Thu, 25 Jul 2013 10:40:18 +0100 Subject: [PATCH 20/97] Show ssh config hosts if isn't set in the config --- Shuttle/AppDelegate.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 4aa0317..6458fd3 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -173,8 +173,13 @@ - (void) loadMenu { NSMutableDictionary* fullMenu = [NSMutableDictionary dictionary]; + // First add all the system servers we know - BOOL showSshConfigHosts = [json[@"show_ssh_config_hosts"] boolValue]; + + BOOL showSshConfigHosts = YES; + if ([[json allKeys] containsObject:(@"show_ssh_config_hosts")] && [json[@"show_ssh_config_hosts"] boolValue] == NO) { + showSshConfigHosts = NO; + } if (showSshConfigHosts) { for (id key in servers) { From 043b9564219c7bfefb5b220f1bc28b0c98e7dbb3 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 25 Jul 2013 10:05:39 -0400 Subject: [PATCH 21/97] Add show_ssh_config_hosts option to test config --- tests/.shuttle.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.shuttle.json b/tests/.shuttle.json index 044bf1d..7bf20b7 100644 --- a/tests/.shuttle.json +++ b/tests/.shuttle.json @@ -1,6 +1,7 @@ { "terminal": "Terminal.app", "launch_at_login": false, + "show_ssh_config_hosts": true, "hosts": [ { "name": "Test escaping characters", From c35fe369e1c98b51ae0295cb67665ac8d9fb36b1 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 25 Jul 2013 10:06:56 -0400 Subject: [PATCH 22/97] Add Jack Weeden to contributors --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6738daa..675f7a9 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ This project was created by Trevor Fitzgerald. I owe many thanks to the followin (In alphabetical order) * Dmitry Filimonov +* Jack Weeden * Marco Aurélio * Martin Grund * Ryan Cohen From 29906ad4bbf66ff4c0bedc9369a26b81fa12bc3d Mon Sep 17 00:00:00 2001 From: Rui Rodrigues Date: Sat, 27 Jul 2013 01:54:56 +0100 Subject: [PATCH 23/97] Settings sub menu created with 3 options (Edtit, Export and Import) --- Shuttle/AppDelegate.m | 45 ++++++++++ Shuttle/en.lproj/MainMenu.xib | 161 +++++++++++++++++++++++++++++----- Shuttle/main.m | 2 +- 3 files changed, 186 insertions(+), 22 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 6458fd3..cc51ca5 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -343,6 +343,51 @@ - (void) openHost:(NSMenuItem *) sender { } } +- (IBAction)showImportPanel:(id)sender +{ + NSLog(@"doOpen"); + NSOpenPanel * openPanelObj = [NSOpenPanel openPanel]; + NSInteger tvarNSInteger = [openPanelObj runModal]; + if(tvarNSInteger == NSOKButton){ + NSLog(@"doOpen OK button clicked"); + + //Backup the current configuration + NSLog(@"doOpen Backup the current config"); + [[NSFileManager defaultManager] moveItemAtPath:shuttleConfigFile toPath: [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.bkp"] error: nil]; + + NSURL * selectedFileUrl = [openPanelObj URL]; + //Import the selected file + NSLog(@"copy filename from %@ to %@",selectedFileUrl.path,shuttleConfigFile); + [[NSFileManager defaultManager] copyItemAtPath:selectedFileUrl.path toPath:shuttleConfigFile error:nil]; + //Delete the old configuration file + NSLog(@"doOpen deleteing the old config"); + [[NSFileManager defaultManager] removeItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.bkp"] error: nil]; + } else if(tvarNSInteger == NSCancelButton) { + NSLog(@"doOpen Cancel button clicked"); + return; + } else { + NSLog(@"doOpen do nothing"); + return; + } + +} + +- (IBAction)showExportPanel:(id)sender +{ + NSLog(@"doSave"); + NSSavePanel * savePanelObj = [NSSavePanel savePanel]; + //Display the Save Panel + NSInteger result = [savePanelObj runModal]; + if (result == NSFileHandlingPanelOKButton) { + + NSURL *saveURL = [savePanelObj URL]; + + // then copy a previous file to the new location + [[NSFileManager defaultManager] copyItemAtPath:shuttleConfigFile toPath:saveURL.path error:nil]; + } +} + + - (IBAction)configure:(id)sender { [[NSWorkspace sharedWorkspace] openFile:shuttleConfigFile]; } diff --git a/Shuttle/en.lproj/MainMenu.xib b/Shuttle/en.lproj/MainMenu.xib index c9a6284..b337e50 100644 --- a/Shuttle/en.lproj/MainMenu.xib +++ b/Shuttle/en.lproj/MainMenu.xib @@ -108,15 +108,42 @@ - + - Configure - + Settings + S 2147483647 - - + submenuAction: + + Settings + + + + Edit + + 2147483647 + + + + + + Export + + 2147483647 + + + + + + Import + + 2147483647 + + + + @@ -150,6 +177,20 @@ YES YES + + Quit + + 2147483647 + + + + + Edit + + 2147483647 + + + @@ -207,15 +248,39 @@ - 642 + 645 + + + + quit: + + + + 650 configure: - + - 643 + 654 + + + + showImportPanel: + + + + 656 + + + + showExportPanel: + + + + 657 @@ -299,10 +364,10 @@ 542 - - + + @@ -312,25 +377,63 @@ - 551 - + 561 + - 543 - + 638 + - 561 - + 644 + - 638 - + 646 + + + + + + 647 + + + + + + + + + + 648 + + + + + 649 + + + + + 651 + + + + + 652 + + + + + 653 + + + @@ -343,20 +446,26 @@ 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 + 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 - 643 + 657 @@ -367,6 +476,8 @@ id id id + id + id @@ -381,6 +492,14 @@ showAbout: id + + showExportPanel: + id + + + showImportPanel: + id + NSArrayController diff --git a/Shuttle/main.m b/Shuttle/main.m index 8b3f762..cdcc869 100644 --- a/Shuttle/main.m +++ b/Shuttle/main.m @@ -1,7 +1,7 @@ // // main.m // Shuttle -// +//dafsadfsdaf #import From cbf02c41afd4a575804ff4d1265f56a7c36829b3 Mon Sep 17 00:00:00 2001 From: Rui Rodrigues Date: Mon, 29 Jul 2013 14:29:52 +0100 Subject: [PATCH 24/97] Clean --- Shuttle/main.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/main.m b/Shuttle/main.m index cdcc869..8b3f762 100644 --- a/Shuttle/main.m +++ b/Shuttle/main.m @@ -1,7 +1,7 @@ // // main.m // Shuttle -//dafsadfsdaf +// #import From 11d0a21a5e77e03342aac2212bca3406973a839d Mon Sep 17 00:00:00 2001 From: Dave Eddy Date: Wed, 28 Aug 2013 20:11:46 -0700 Subject: [PATCH 25/97] Update shuttle.default.json comments are now stored in an array to not clobber the key space. --- Shuttle/shuttle.default.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 8f736cf..d80f68d 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -1,7 +1,9 @@ { - "_comment1": "Valid terminals include: 'Terminal.app' or 'iTerm'", - "_comment2": "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", - "_comment3": "For more information on how to configure, please see http://fitztrev.github.io/shuttle/", + "_comments": [ + "Valid terminals include: 'Terminal.app' or 'iTerm'", + "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", + "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" + ], "terminal": "Terminal.app", "launch_at_login": false, "show_ssh_config_hosts": true, From a5a86058a335a060d2dad1ab1aac04866a626885 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Wed, 30 Oct 2013 13:43:10 -0500 Subject: [PATCH 26/97] Remove the status icon from the status bar on quit. --- Shuttle/AppDelegate.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 6458fd3..bc7a989 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -352,7 +352,8 @@ - (IBAction)showAbout:(id)sender { } - (IBAction)quit:(id)sender { - [NSApp terminate:nil]; + [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; + [NSApp terminate:NSApp]; } @end From fc2849bf46a12e7779bef328659edd312ab1b555 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Wed, 30 Oct 2013 14:03:19 -0500 Subject: [PATCH 27/97] Fixing whitespace. --- Shuttle/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index bc7a989..f89c2ba 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -352,7 +352,7 @@ - (IBAction)showAbout:(id)sender { } - (IBAction)quit:(id)sender { - [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; + [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; [NSApp terminate:NSApp]; } From 89e05b8c073d406b81f611c85ec52d41f9bfd73b Mon Sep 17 00:00:00 2001 From: Frank Enderle Date: Sun, 17 Nov 2013 03:44:03 +0100 Subject: [PATCH 28/97] Add nested menus (and other stuff) It is now possible to have menus within menus. The nesting depth is not restricted (except for memory and useability). Configuration support has been added for JSON and ssh/config configurations. JSON configurations simply nest the host entries as deep as required. For the ssh/config configuration the Host name can now have multiple '/' separators. The name is splitted among the separator, resulting in the menu levels. The last level corresponds to the name of the final menu entry for the actual connection. However you can now also configure more data into the ssh/config file. It is possible to add key/value pairs within a Host section using comments: Host work/servers/gandalf HostName gandalf@example.com could also be written as: Host gandalf # shuttle.name = work/servers/gandalf (webserver) HostName gandalf@example.com The resulting menustructure would then be work -> servers -> gandalf (webserver) Currently shuttle.name is the only supported key/value pair. --- Shuttle/AppDelegate.m | 251 +++++++++++++++++++---------------- Shuttle/shuttle.default.json | 7 + 2 files changed, 146 insertions(+), 112 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index f89c2ba..3d7c85b 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -92,39 +92,46 @@ - (NSDictionary*) parseSSHConfigFile { // Get file contents into fh. NSString *fh = [NSString stringWithContentsOfFile:configFile encoding:NSUTF8StringEncoding error:nil]; - // Initialize our server list as an empty dictionary variable. - NSMutableDictionary *servers = [NSMutableDictionary dictionaryWithObjects:nil forKeys:nil]; + + // build the regex for matching + NSError* error = NULL; + NSRegularExpression* rx = [NSRegularExpression regularExpressionWithPattern:@"^(#?)[ \\t]*([^ \\t=]+)[ \\t=]+(.*)$" + options:0 + error:&error]; + + // create data store + NSMutableDictionary* servers = [[NSMutableDictionary alloc] init]; + NSString* key = nil; // Loop through each line and parse the file. for (NSString *line in [fh componentsSeparatedByString:@"\n"]) { // Strip line - NSString *cleanedLine = [line stringByTrimmingCharactersInSet:[ NSCharacterSet whitespaceCharacterSet]]; + NSString *trimmed = [line stringByTrimmingCharactersInSet:[ NSCharacterSet whitespaceCharacterSet]]; - // Empty lines and lines starting with `#' are comments. - if ([cleanedLine length] == 0 || [line characterAtIndex:0] == '#') + // run the regex against the line + NSTextCheckingResult* matches = [rx firstMatchInString:trimmed + options:0 + range:NSMakeRange(0, [trimmed length])]; + if ([matches numberOfRanges] != 4) continue; - // Since there might be the possibility that someone thought it might be useful to use = for separating properties - // we have to check that. And of course for now, we are only looking into the host - // section and gently ignore the rest - NSError* error = NULL; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^Host\\b" options:0 error: &error]; - NSUInteger num = [regex numberOfMatchesInString:cleanedLine options:0 range:NSMakeRange(0, [cleanedLine length])]; - if (num == 1) { - - // Somebody really used = - NSArray* components = nil; - if ([cleanedLine rangeOfString:@"="].length != 0) { - components = [cleanedLine componentsSeparatedByString:@"="]; - - } else { - components = [cleanedLine componentsSeparatedByCharactersInSet: - [NSCharacterSet whitespaceCharacterSet]]; - } - NSString* host = [[components objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - [servers setObject:[NSDictionary dictionaryWithObject: host forKey:@"Host"] forKey:host] ; + BOOL isComment = [[trimmed substringWithRange:[matches rangeAtIndex:1]] isEqualToString:@"#"]; + NSString* first = [trimmed substringWithRange:[matches rangeAtIndex:2]]; + NSString* second = [trimmed substringWithRange:[matches rangeAtIndex:3]]; + + // check for special comment key/value pairs + if (isComment && key && [first hasPrefix:@"shuttle."]) + servers[key][[first substringFromIndex:8]] = second; + + // other comments must be skipped + if (isComment) + continue; + + if ([first isEqualToString:@"Host"]) { + // a new host section + key = second; + servers[key] = [[NSMutableDictionary alloc] init]; } } @@ -137,10 +144,6 @@ - (NSString*) humanize: (NSString*) val{ } - (void) loadMenu { - - // System configuration - NSDictionary* servers = [self parseSSHConfigFile]; - // Clear out the hosts so we can start over NSUInteger n = [[menu itemArray] count]; for (int i=0;i Date: Sun, 17 Nov 2013 16:36:49 -0500 Subject: [PATCH 29/97] Update test config files for new supported syntax --- tests/.shuttle.json | 10 +++++++++- tests/.ssh/config | 42 ++++++++++++++++++++++++++++++------------ tests/etc/ssh_config | 43 ------------------------------------------- 3 files changed, 39 insertions(+), 56 deletions(-) delete mode 100644 tests/etc/ssh_config diff --git a/tests/.shuttle.json b/tests/.shuttle.json index 7bf20b7..d970360 100644 --- a/tests/.shuttle.json +++ b/tests/.shuttle.json @@ -42,7 +42,15 @@ { "Submenu 3": [ { - "name": "Submenu Item #3.1", + "Nested Menu - #3.1": [ + { + "name": "Submenu Item #3.1.1", + "cmd": "echo \"OK\";" + } + ] + }, + { + "name": "Submenu Item #3.2", "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" } ] diff --git a/tests/.ssh/config b/tests/.ssh/config index 5540885..6d2f4be 100644 --- a/tests/.ssh/config +++ b/tests/.ssh/config @@ -2,16 +2,11 @@ # A couple of Github accounts # Prefixed with `.` so they are ignored from the Shuttle menu # -Host .github.amrom.workers.dev-user1 +Host github.com User user1 Hostname github.com IdentityFile ~/.ssh/github-1.key -Host .github.amrom.workers.dev-user2 - User user2 - Hostname github.com - IdentityFile ~/.ssh/github-2.key - # # Main menu items # @@ -20,24 +15,47 @@ Host blog.example.com Hostname blog.example.com # -# Grouped menu items +# Grouped menu items (separated by /) # -Host Group1/web01.example.com +Host Clients/client-a.example.com + User username + Hostname client-a.example.com + +Host Clients/client-b.example.com + User username + Hostname client-b.example.com + +Host Group1/web/web01.example.com User username Hostname web01.example.com -Host Group1/web02.example.com +Host Group1/web/web02.example.com User username Hostname web02.example.com -Host Group1/web03.example.com +Host Group1/web/web03.example.com User username Hostname web03.example.com -Host Group2/mysql01.example.com +Host Group1/db/mysql01.example.com User username Hostname mysql01.example.com -Host Group2/mysql02.example.com +Host Group1/db/mysql02.example.com User username Hostname mysql02.example.com + +# +# Grouped menu items (by Shuttle comment syntax) +# +Host dev01.example.net + # shuttle.name = Work/dev01.example.net (my dev box) + Hostname dev01.example.net + +Host test01.example.net + # shuttle.name = Work/Production/test01.example.net (webserver) + Hostname test01.example.net + +Host test02.example.net + # shuttle.name = Work/Production/test02.example.net (database) + Hostname test02.example.net diff --git a/tests/etc/ssh_config b/tests/etc/ssh_config deleted file mode 100644 index 9aaec17..0000000 --- a/tests/etc/ssh_config +++ /dev/null @@ -1,43 +0,0 @@ -# -# A couple of Github accounts -# Prefixed with `.` so they are ignored from the Shuttle menu -# -Host .github.amrom.workers.dev-user1.global - User user1 - Hostname github.com - IdentityFile ~/.ssh/github-1.key - -Host .github.amrom.workers.dev-user2.global - User user2 - Hostname github.com - IdentityFile ~/.ssh/github-2.key - -# -# Main menu items -# -Host global-blog.example.com - User username - Hostname blog.example.com - -# -# Grouped menu items -# -Host Global-Group1/web01.example.com - User username - Hostname web01.example.com - -Host Global-Group1/web02.example.com - User username - Hostname web02.example.com - -Host Global-Group1/web03.example.com - User username - Hostname web03.example.com - -Host Global-Group2/mysql01.example.com - User username - Hostname mysql01.example.com - -Host Global-Group2/mysql02.example.com - User username - Hostname mysql02.example.com From b8957419981ece7ddcbbd2c7a448fafbda48314e Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Sun, 17 Nov 2013 16:38:36 -0500 Subject: [PATCH 30/97] Add missing ] to default json config file --- Shuttle/shuttle.default.json | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 51bb145..7c05ae8 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -14,18 +14,19 @@ }, { "Personal": [ - { - "name": "My blog", - "cmd": "ssh username@blog.example.com" - }, - { - "Spouse": [ - { - "name": "Her blog", - "cmd": "ssh username@blog2.example.com" - } - } - ] + { + "name": "My blog", + "cmd": "ssh username@blog.example.com" + }, + { + "Spouse": [ + { + "name": "Her blog", + "cmd": "ssh username@blog2.example.com" + } + ] + } + ] }, { "Work": [ From 089edce88f8012e4b8eec50387320ac9608f0be5 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Sun, 17 Nov 2013 17:16:49 -0500 Subject: [PATCH 31/97] Remove debug logging --- Shuttle/AppDelegate.m | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 5682ed7..0805f62 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -370,45 +370,31 @@ - (void) openHost:(NSMenuItem *) sender { } } -- (IBAction)showImportPanel:(id)sender -{ - NSLog(@"doOpen"); +- (IBAction)showImportPanel:(id)sender { NSOpenPanel * openPanelObj = [NSOpenPanel openPanel]; NSInteger tvarNSInteger = [openPanelObj runModal]; if(tvarNSInteger == NSOKButton){ - NSLog(@"doOpen OK button clicked"); - //Backup the current configuration - NSLog(@"doOpen Backup the current config"); - [[NSFileManager defaultManager] moveItemAtPath:shuttleConfigFile toPath: [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.bkp"] error: nil]; + [[NSFileManager defaultManager] moveItemAtPath:shuttleConfigFile toPath: [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.backup"] error: nil]; NSURL * selectedFileUrl = [openPanelObj URL]; //Import the selected file - NSLog(@"copy filename from %@ to %@",selectedFileUrl.path,shuttleConfigFile); + //NSLog(@"copy filename from %@ to %@",selectedFileUrl.path,shuttleConfigFile); [[NSFileManager defaultManager] copyItemAtPath:selectedFileUrl.path toPath:shuttleConfigFile error:nil]; //Delete the old configuration file - NSLog(@"doOpen deleteing the old config"); - [[NSFileManager defaultManager] removeItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.bkp"] error: nil]; - } else if(tvarNSInteger == NSCancelButton) { - NSLog(@"doOpen Cancel button clicked"); - return; + [[NSFileManager defaultManager] removeItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.backup"] error: nil]; } else { - NSLog(@"doOpen do nothing"); return; } } -- (IBAction)showExportPanel:(id)sender -{ - NSLog(@"doSave"); +- (IBAction)showExportPanel:(id)sender { NSSavePanel * savePanelObj = [NSSavePanel savePanel]; //Display the Save Panel NSInteger result = [savePanelObj runModal]; if (result == NSFileHandlingPanelOKButton) { - NSURL *saveURL = [savePanelObj URL]; - // then copy a previous file to the new location [[NSFileManager defaultManager] copyItemAtPath:shuttleConfigFile toPath:saveURL.path error:nil]; } From a90df97787c7a2f2c176a02fd4e4ac603a26094f Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 26 Nov 2013 16:19:23 -0600 Subject: [PATCH 32/97] Adding options for ignoring hosts based on name or keyword --- Shuttle/AppDelegate.h | 2 ++ Shuttle/AppDelegate.m | 23 ++++++++++++++++++++++- Shuttle/shuttle.default.json | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index db8ec99..155123d 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -20,6 +20,8 @@ NSString *terminalPref; NSMutableArray* shuttleHosts; + NSMutableArray* ignoreHosts; + NSMutableArray* ignoreKeywords; LaunchAtLoginController *launchAtLoginController; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 0805f62..25a2490 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -169,6 +169,8 @@ - (void) loadMenu { terminalPref = [json[@"terminal"] lowercaseString]; launchAtLoginController.launchAtLogin = [json[@"launch_at_login"] boolValue]; shuttleHosts = json[@"hosts"]; + ignoreHosts = json[@"ssh_config_ignore_hosts"]; + ignoreKeywords = json[@"ssh_config_ignore_keywords"]; // Should we merge ssh config hosts? BOOL showSshConfigHosts = YES; @@ -180,6 +182,7 @@ - (void) loadMenu { // Read configuration from ssh config NSDictionary* servers = [self parseSSHConfigFile]; for (NSString* key in servers) { + BOOL skipCurrent = NO; NSDictionary* cfg = [servers objectForKey:key]; // get special name from config if set, fallback to the key @@ -187,11 +190,29 @@ - (void) loadMenu { // Ignore entries that contain wildcard characters if ([name rangeOfString:@"*"].length != 0) - continue; + skipCurrent = YES; // Ignore entries that start with `.` if ([name hasPrefix:@"."]) + skipCurrent = YES; + + // Ignore entries whose name matches exactly any of the values in ignoreHosts + for (NSString* ignore in ignoreHosts) { + if ([name isEqualToString:ignore]) { + skipCurrent = YES; + } + } + + // Ignore entries whose name contains any of the values in ignoreKeywords + for (NSString* ignore in ignoreKeywords) { + if ([name rangeOfString:ignore].location != NSNotFound) { + skipCurrent = YES; + } + } + + if (skipCurrent) { continue; + } // Split the host into parts separated by / - the last part is the name for the leaf in the tree NSMutableArray* path = [NSMutableArray arrayWithArray:[name componentsSeparatedByString:@"/"]]; diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 7c05ae8..456d965 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -7,6 +7,8 @@ "terminal": "Terminal.app", "launch_at_login": false, "show_ssh_config_hosts": true, + "ssh_config_ignore_hosts": [], + "ssh_config_ignore_keywords": [], "hosts": [ { "name": "My Dev Server", From 6d7b81f6e1f316cb3545cf2af23fed30b7c60d87 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 26 Nov 2013 16:21:42 -0600 Subject: [PATCH 33/97] Fixing whitespace --- Shuttle/shuttle.default.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 456d965..a3538ec 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -7,8 +7,8 @@ "terminal": "Terminal.app", "launch_at_login": false, "show_ssh_config_hosts": true, - "ssh_config_ignore_hosts": [], - "ssh_config_ignore_keywords": [], + "ssh_config_ignore_hosts": [], + "ssh_config_ignore_keywords": [], "hosts": [ { "name": "My Dev Server", From c694630120fccd02e523c95317cef396589b68de Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Mon, 2 Dec 2013 12:00:35 -0500 Subject: [PATCH 34/97] update list of contributors --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 675f7a9..5dadc99 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,15 @@ This project was created by Trevor Fitzgerald. I owe many thanks to the followin (In alphabetical order) +* Dave Eddy * Dmitry Filimonov +* Frank Enderle * Jack Weeden +* Justin Swanson * Marco Aurélio * Martin Grund +* Michael Davis +* Rui Rodrigues * Ryan Cohen * Thomas Rosenstein * Tibor Bödecs From 1e1bacaef685898873ff2d94ceadb803f7d92ce0 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Mon, 2 Dec 2013 12:13:38 -0500 Subject: [PATCH 35/97] add docs for new features around ~/.ssh/config --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index 5dadc99..db58bb9 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,49 @@ A simple SSH shortcut menu for OS X 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) 2. Copy to Applications +## Customization + +The default, out-of-the-box configuration should be good enough to get started. However, if you're looking to customize the appearance further, here are a few advanced tips. + +### Disabling `~/.ssh/config` hosts + +By default, Shuttle will parse your `~/.ssh/config` file for hosts. + +##### To disable all ~/.ssh/config entries: + +``` +"show_ssh_config_hosts": false, +``` + +#### Disable specific hosts: + +``` +"ssh_config_ignore_hosts": ["github.com", "git.example.com"], +``` + +#### Disable hosts that contain a keyword: + +``` +"ssh_config_ignore_keywords": ["git"], +``` + +### Nested menus for `~/.ssh/config` hosts + +#### Create a menu item at "work" > "servers" > "web01" + +``` +Host work/servers/web01 + HostName user@web01.example.com +``` +\- *or* - + +``` +Host gandalf + # shuttle.name = work/servers/web01 (webserver) + HostName user@web01.example.com +``` + + ## Roadmap * Cloud hosting integration From c59b0410c7f3271d35499d3a74181244f42fb4b0 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Mon, 2 Dec 2013 12:14:54 -0500 Subject: [PATCH 36/97] v1.2.0 --- Shuttle/Shuttle-Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 5903134..b63263a 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2.0 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From c51588cc09afbce84d84ca89934afb93b426ea0d Mon Sep 17 00:00:00 2001 From: blazeworx Date: Fri, 24 Oct 2014 13:56:22 -0400 Subject: [PATCH 37/97] Added Yosemite OSX Dark Mode menu icon support. --- Shuttle/AppDelegate.h | 3 +++ Shuttle/AppDelegate.m | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index 155123d..d35dc0a 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -10,6 +10,9 @@ IBOutlet NSMenu *menu; IBOutlet NSArrayController *arrayController; + NSImage *regularIcon; + NSImage *altIcon; + NSStatusItem *statusItem; NSString *shuttleConfigFile; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 25a2490..3c784dc 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -19,13 +19,28 @@ - (void) awakeFromNib { // Load the menu content // [self loadMenu]; + + // Define Icons + regularIcon = [NSImage imageNamed:@"StatusIcon"]; + altIcon = [NSImage imageNamed:@"StatusIconAlt"]; + + // Check for AppKit Version, add support for darkmode if > 10.9 + BOOL oldAppKitVersion = (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9); + + if (!oldAppKitVersion) + { + // 10.10 or higher, add support to icon for auto detection of Regular/Dark mode + [regularIcon setTemplate:YES]; + [altIcon setTemplate:YES]; + } // Create the status bar item statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:25.0]; + [statusItem setMenu:menu]; [statusItem setHighlightMode:YES]; - [statusItem setImage:[NSImage imageNamed:@"StatusIcon"]]; - [statusItem setAlternateImage:[NSImage imageNamed:@"StatusIconAlt"]]; + [statusItem setImage: regularIcon]; + [statusItem setAlternateImage: altIcon]; launchAtLoginController = [[LaunchAtLoginController alloc] init]; From 48dfe7f24ad0af2b226913da821387b7cdd55834 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Fri, 24 Oct 2014 14:47:58 -0400 Subject: [PATCH 38/97] v1.2.1 --- README.md | 1 + Shuttle/Shuttle-Info.plist | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db58bb9..b896522 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ This project was created by Trevor Fitzgerald. I owe many thanks to the followin (In alphabetical order) +* Alex Carter * Dave Eddy * Dmitry Filimonov * Frank Enderle diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index b63263a..7e4db8b 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.0 + 1.2.1 CFBundleSignature ???? CFBundleVersion - 1.2.0 + 1.2.1 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From 4fddb8c3ac3f4a92f40251fc1d206b3e8afda70a Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 28 Oct 2014 01:07:28 +0100 Subject: [PATCH 39/97] Reuse getMTimeFor method --- Shuttle/AppDelegate.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 3c784dc..4316de7 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -56,9 +56,7 @@ - (BOOL) needUpdateFor: (NSString*) file with: (NSDate*) old { if (old == NULL) return true; - NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[file stringByExpandingTildeInPath] - error:nil]; - NSDate *date = [attributes fileModificationDate]; + NSDate *date = [self getMTimeFor:file]; return [date compare: old] == NSOrderedDescending; } From da1778c219077b6fa45d72107f463947d929f46d Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Sat, 1 Nov 2014 11:00:22 -0400 Subject: [PATCH 40/97] NSAppKitVersionNumber10_9 is missing. Hardcoded the number instead. --- Shuttle/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 3c784dc..6b31a2c 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -25,7 +25,7 @@ - (void) awakeFromNib { altIcon = [NSImage imageNamed:@"StatusIconAlt"]; // Check for AppKit Version, add support for darkmode if > 10.9 - BOOL oldAppKitVersion = (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9); + BOOL oldAppKitVersion = (floor(NSAppKitVersionNumber) <= 1265); if (!oldAppKitVersion) { From 350df8944b09b795d45713aa18b61d248c46bbd5 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Sat, 1 Nov 2014 11:02:08 -0400 Subject: [PATCH 41/97] v1.2.2 --- Shuttle/Shuttle-Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 7e4db8b..706ed77 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.1 + 1.2.2 CFBundleSignature ???? CFBundleVersion - 1.2.1 + 1.2.2 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From d9e8396b741dfbe2214db14eecab7963d4a633a8 Mon Sep 17 00:00:00 2001 From: welsonla Date: Wed, 10 Dec 2014 00:04:15 +0800 Subject: [PATCH 42/97] fix iterm open check --- Shuttle/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 6b31a2c..52cbf3c 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -350,7 +350,7 @@ - (void) openHost:(NSMenuItem *) sender { { [[NSWorkspace sharedWorkspace] openURL:url]; } - else if ( [terminalPref isEqualToString: @"iterm"] ) { + else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound) { NSAppleScript* iTerm2 = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"on ApplicationIsRunning(appName) \n" From 36688a26275d58bf43a27d396b2b207c586b927a Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 7 Jan 2015 13:58:15 -0700 Subject: [PATCH 43/97] About Dialog Window When the user clicks the About button on the menu a new about window is opened. Data for the window is populated from values in the info.plist file and shows the program name, Version, copyright, and a home page button. --- Shuttle.xcodeproj/project.pbxproj | 10 ++++ Shuttle/AboutWindowController.h | 21 ++++++++ Shuttle/AboutWindowController.m | 79 +++++++++++++++++++++++++++++++ Shuttle/AboutWindowController.xib | 78 ++++++++++++++++++++++++++++++ Shuttle/AppDelegate.m | 12 ++++- Shuttle/Shuttle-Info.plist | 2 + 6 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 Shuttle/AboutWindowController.h create mode 100644 Shuttle/AboutWindowController.m create mode 100644 Shuttle/AboutWindowController.xib diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index 962b1ec..406abde 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; }; 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */; }; + A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; }; + A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */; }; C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; }; C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C149EC0615D5214600B1F558 /* InfoPlist.strings */; }; C149EC0A15D5214600B1F558 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C149EC0915D5214600B1F558 /* main.m */; }; @@ -31,6 +33,9 @@ 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; + A1D700051A5DCDF4003563E4 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = ""; }; + A1D700061A5DCE8D003563E4 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = ""; }; + A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindowController.xib; sourceTree = ""; }; C149EBF915D5214600B1F558 /* Shuttle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shuttle.app; sourceTree = BUILT_PRODUCTS_DIR; }; C149EBFD15D5214600B1F558 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; C149EC0015D5214600B1F558 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -119,6 +124,9 @@ C149EC1215D5214600B1F558 /* MainMenu.xib */, 0ADB3B10178EF8E2004E9BB9 /* Images */, C149EC0415D5214600B1F558 /* Supporting Files */, + A1D700051A5DCDF4003563E4 /* AboutWindowController.h */, + A1D700061A5DCE8D003563E4 /* AboutWindowController.m */, + A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */, ); path = Shuttle; sourceTree = ""; @@ -189,6 +197,7 @@ 0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */, 0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */, C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */, + A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */, 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */, 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */, C149EC0E15D5214600B1F558 /* Credits.rtf in Resources */, @@ -206,6 +215,7 @@ buildActionMask = 2147483647; files = ( 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */, + A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */, C149EC0A15D5214600B1F558 /* main.m in Sources */, C149EC1115D5214600B1F558 /* AppDelegate.m in Sources */, ); diff --git a/Shuttle/AboutWindowController.h b/Shuttle/AboutWindowController.h new file mode 100644 index 0000000..4e5fbe5 --- /dev/null +++ b/Shuttle/AboutWindowController.h @@ -0,0 +1,21 @@ +// +// AboutWindowController.h +// Shuttle +// +// Created by Matthew Turner on 1/7/15. +// Copyright (c) 2015 fitztrev. All rights reserved. +// + +#import + +@interface AboutWindowController : NSWindowController + +@property (strong) NSWindowController *aboutWindow; + +@property (strong) IBOutlet NSTextField *appName; +@property (strong) IBOutlet NSTextField *appVersion; +@property (strong) IBOutlet NSTextField *appCopyright; + +- (IBAction)btnHomepage:(id)sender; + +@end diff --git a/Shuttle/AboutWindowController.m b/Shuttle/AboutWindowController.m new file mode 100644 index 0000000..08b3a2a --- /dev/null +++ b/Shuttle/AboutWindowController.m @@ -0,0 +1,79 @@ +// +// AboutWindowController.m +// Shuttle +// +// Created by Matthew Turner on 1/7/15. +// Copyright (c) 2015 fitztrev. All rights reserved. +// + +#import "AboutWindowController.h" + +@interface AboutWindowController () + +@end + +@implementation AboutWindowController +@synthesize aboutWindow; +@synthesize appName; +@synthesize appVersion; +@synthesize appCopyright; + +NSDictionary *plistDict; + +- (id)initWithWindow:(NSWindow *)window +{ + aboutWindow = [super initWithWindow:window]; + if (self) { + // Initialization code here. + } + return self; + } + +- (void)windowDidLoad +{ + [super windowDidLoad]; + //Prevent the window from changing positions after multiple opens. + [aboutWindow setShouldCascadeWindows:NO]; + + //Load the plist so we can get current info for the about box. + plistDict = [[NSBundle mainBundle] infoDictionary]; + + //Get the application name. + id applicationName = [plistDict objectForKey:@"CFBundleName"]; + //Get the build version. + id applicationVersion = [plistDict objectForKey:@"CFBundleVersion"]; + //Get the copyright. + id applicationCopyright = [plistDict objectForKey:@"NSHumanReadableCopyright"]; + + //Build the string for the windows title. + NSString *aboutTitle = [NSString stringWithFormat:@"%@%@", @"About ", applicationName]; + [aboutWindow.window setTitle:aboutTitle]; + + //Build the string for the application name. appName - tagline + NSString *progName = [NSString stringWithFormat:@"%@%@", applicationName, @" - A simple SSH shortcut menu."]; + [appName setStringValue:progName]; + + //Build the string for the version. Version: $build + NSString *progVersion = [NSString stringWithFormat:@"%@%@", @"Version: ", applicationVersion]; + [appVersion setStringValue:progVersion]; + + //Make the copyright font smaller. + [appCopyright setFont:[NSFont systemFontOfSize:10]]; + [appCopyright setStringValue:applicationCopyright]; + + } + +- (IBAction)btnHomepage:(id)sender { + + //Get the homepage from the plist + id applicationHomepage = [plistDict objectForKey:@"Product Homepage"]; + //Build the homepage's URL. + NSURL *homeURL = [NSURL URLWithString:applicationHomepage]; + + //Go to the website. + [[NSWorkspace sharedWorkspace] openURL:homeURL]; + + //Close the about box. + [aboutWindow close]; + } +@end diff --git a/Shuttle/AboutWindowController.xib b/Shuttle/AboutWindowController.xib new file mode 100644 index 0000000..1348a07 --- /dev/null +++ b/Shuttle/AboutWindowController.xib @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 52cbf3c..71314ee 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -4,6 +4,7 @@ // #import "AppDelegate.h" +#import "AboutWindowController.h" @implementation AppDelegate @@ -442,8 +443,15 @@ - (IBAction)configure:(id)sender { } - (IBAction)showAbout:(id)sender { - [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString:@"http://fitztrev.github.io/shuttle"]]; -} + + //Call the windows controller + AboutWindowController *aboutWindow = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindowController"]; + + //Set the window to stay on top + [aboutWindow.window setLevel:NSFloatingWindowLevel]; + + //Show the window + [aboutWindow showWindow:self];} - (IBAction)quit:(id)sender { [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 706ed77..a6d5ed9 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -34,5 +34,7 @@ MainMenu NSPrincipalClass NSApplication + Product Homepage + http://fitztrev.github.io/shuttle/ From f6d528311254e968994168a99108beb0dab90afa Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 7 Jan 2015 14:01:10 -0700 Subject: [PATCH 44/97] About Dialog Window minor syntax formatting change --- Shuttle/AppDelegate.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 71314ee..e4590d4 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -451,7 +451,8 @@ - (IBAction)showAbout:(id)sender { [aboutWindow.window setLevel:NSFloatingWindowLevel]; //Show the window - [aboutWindow showWindow:self];} + [aboutWindow showWindow:self]; +} - (IBAction)quit:(id)sender { [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; From 035c0d99d981550487f655816c702546f915f33c Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 7 Jan 2015 16:04:19 -0700 Subject: [PATCH 45/97] JSON Settings editor option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the JSON settings file modifying the new option “editor”: “default”, will open the settings file from the Settings > edit menu in that editor. Set the editor to 'nano', 'vi', or any terminal based editor. Delete the editor line or keep it 'default' to use your systems default editor --- Shuttle/AppDelegate.h | 1 + Shuttle/AppDelegate.m | 22 ++++++- Shuttle/shuttle.default.json | 114 ++++++++++++++++++----------------- 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index d35dc0a..69b62f5 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -22,6 +22,7 @@ NSDate *sshConfigSystem; NSString *terminalPref; + NSString *editorPref; NSMutableArray* shuttleHosts; NSMutableArray* ignoreHosts; NSMutableArray* ignoreKeywords; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 52cbf3c..3ec6364 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -182,6 +182,7 @@ - (void) loadMenu { } terminalPref = [json[@"terminal"] lowercaseString]; + editorPref = [json[@"editor"] lowercaseString]; launchAtLoginController.launchAtLogin = [json[@"launch_at_login"] boolValue]; shuttleHosts = json[@"hosts"]; ignoreHosts = json[@"ssh_config_ignore_hosts"]; @@ -438,9 +439,28 @@ - (IBAction)showExportPanel:(id)sender { - (IBAction)configure:(id)sender { - [[NSWorkspace sharedWorkspace] openFile:shuttleConfigFile]; + + //if the editor setting is omitted or contains 'default' open using the default editor. + if([editorPref rangeOfString:@"default"].location != NSNotFound) { + + [[NSWorkspace sharedWorkspace] openFile:shuttleConfigFile]; + } + else{ + //build the editor command + NSString *editorCommand = [NSString stringWithFormat:@"%@ %@", editorPref, shuttleConfigFile]; + + //make a menu item for the command selector(openHost:) runs in a new terminal window. + NSMenuItem *editorMenu = [[NSMenuItem alloc] initWithTitle:@"editJSONconfig" action:@selector(openHost:) keyEquivalent:(@"")]; + + //set the command for the menu item + [editorMenu setRepresentedObject:editorCommand]; + + //open the JSON file in the terminal editor. + [self openHost:editorMenu]; + } } + - (IBAction)showAbout:(id)sender { [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString:@"http://fitztrev.github.io/shuttle"]]; } diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index a3538ec..ded837e 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -1,58 +1,60 @@ { - "_comments": [ - "Valid terminals include: 'Terminal.app' or 'iTerm'", - "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", - "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" - ], - "terminal": "Terminal.app", - "launch_at_login": false, - "show_ssh_config_hosts": true, - "ssh_config_ignore_hosts": [], - "ssh_config_ignore_keywords": [], - "hosts": [ - { - "name": "My Dev Server", - "cmd": "ssh username@dev.example.com" - }, - { - "Personal": [ - { - "name": "My blog", - "cmd": "ssh username@blog.example.com" - }, - { - "Spouse": [ - { - "name": "Her blog", - "cmd": "ssh username@blog2.example.com" - } - ] - } - ] - }, - { - "Work": [ - { - "name": "dev.example.net", - "cmd": "ssh username@dev.example.net -p 3000" - }, - { - "name": "staging.example.net", - "cmd": "ssh username@staging.example.net -p 3000" - }, - { - "name": "production.example.net", - "cmd": "ssh username@example.net -p 3000" - } - ] - }, - { - "Vagrant": [ - { - "name": "precise32", - "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" - } - ] - } - ] + "_comments": [ + "Valid terminals include: 'Terminal.app' or 'iTerm'", + "In the editor value change 'default' to 'nano', 'vi', or another terminal based editor.", + "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", + "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" + ], + "terminal": "Terminal.app", + "editor": "default", + "launch_at_login": false, + "show_ssh_config_hosts": true, + "ssh_config_ignore_hosts": [], + "ssh_config_ignore_keywords": [], + "hosts": [ + { + "name": "My Dev Server", + "cmd": "ssh username@dev.example.com" + }, + { + "Personal": [ + { + "name": "My blog", + "cmd": "ssh username@blog.example.com" + }, + { + "Spouse": [ + { + "name": "Her blog", + "cmd": "ssh username@blog2.example.com" + } + ] + } + ] + }, + { + "Work": [ + { + "name": "dev.example.net", + "cmd": "ssh username@dev.example.net -p 3000" + }, + { + "name": "staging.example.net", + "cmd": "ssh username@staging.example.net -p 3000" + }, + { + "name": "production.example.net", + "cmd": "ssh username@example.net -p 3000" + } + ] + }, + { + "Vagrant": [ + { + "name": "precise32", + "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" + } + ] + } + ] } From 6f443aafed9042bbb1a0c002234b61c5745cb7cb Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Wed, 18 Mar 2015 11:07:39 -0400 Subject: [PATCH 46/97] Add terminal setup info to README I get a lot of emails asking about it so this should help --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b896522..41bb3f6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A simple SSH shortcut menu for OS X ![How Shuttle works](https://raw.github.com/fitztrev/shuttle/gh-pages/img/how-shuttle-works.gif) +***Sidenote***: *Many people ask, so here's how I have [my terminal setup](https://github.com/fitztrev/shuttle/wiki/My-Terminal-Prompt).* + ## Installation 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) From 835a36353bf14908736df5c98b2f9c9edf721640 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 11 Oct 2015 16:50:20 -0700 Subject: [PATCH 47/97] Terminal title and theme support Allows for setting the theme and title for the terminal command. In the JSON settings for your cmd add theme and title like so: "cmd": "ssh username@dev.example.com", "name": "My Dev Server", "theme": "Homebrew", "title": "Dev Server - SSH" Added some examples in shuttle.default.json --- Shuttle.xcodeproj/project.pbxproj | 4 +- Shuttle/AppDelegate.m | 110 +++++++++++++++++++-------- Shuttle/shuttle.default.json | 120 +++++++++++++++--------------- 3 files changed, 143 insertions(+), 91 deletions(-) diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index 406abde..fc7b327 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ C149EBF015D5214600B1F558 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0440; + LastUpgradeCheck = 0630; ORGANIZATIONNAME = fitztrev; }; buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "Shuttle" */; @@ -255,7 +255,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -283,7 +282,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 485c8ac..a833c78 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -331,8 +331,19 @@ - (void) buildMenu:(NSArray*)data addToMenu:(NSMenu *)m { for (NSString *key in leafKeys) { NSDictionary* cfg = leafs[key]; NSMenuItem* menuItem = [[NSMenuItem alloc] init]; + + //Get the command we are going to run in termainal + NSString *menuCmd = cfg[@"cmd"]; + //Get the theme for this terminal session + NSString *termTheme = cfg[@"theme"]; + //Get the name for the terminal session + NSString *termTitle = cfg[@"title"]; + + //Place the terminal command, theme, and title into an comma delimited string + NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@", menuCmd, termTheme, termTitle]; + [menuItem setTitle:cfg[@"name"]]; - [menuItem setRepresentedObject:cfg[@"cmd"]]; + [menuItem setRepresentedObject:menuRepObj]; [menuItem setAction:@selector(openHost:)]; [m insertItem:menuItem atIndex:pos++]; } @@ -342,7 +353,39 @@ - (void) openHost:(NSMenuItem *) sender { //NSLog(@"sender: %@", sender); //NSLog(@"Command: %@",[sender representedObject]); - NSString *escapedObject = [[sender representedObject] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + //Place the comma delimited string of menu item settings into an array + NSArray *objectsFromJSON = [[sender representedObject] componentsSeparatedByString:(@",")]; + + //This is our command that will be run in the terminal window + NSString *escapedObject; + //The theme for the terminal window + NSString *terminalTheme; + //The title for the terminal window + NSString *terminalTitle; + + //if for some reason we get a representedObject with only one item... + if (objectsFromJSON.count <=1) { + escapedObject = [[sender representedObject] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + } + else { + escapedObject = [[objectsFromJSON objectAtIndex:0] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + //Check if terminalTheme is null + if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ + if( [terminalPref isEqualToString:@"iterm"] ){ + terminalTheme = @"default"; + }else{ + terminalTheme = @"basic"; + } + }else { + terminalTheme = [objectsFromJSON objectAtIndex:1]; + } + //Check if terminalTitle is null + if( [[objectsFromJSON objectAtIndex:2] isEqualToString:@"(null)"]){ + terminalTitle = @""; + }else{ + terminalTitle = [objectsFromJSON objectAtIndex:2]; + } + } // Check if Url NSURL* url = [NSURL URLWithString:[sender representedObject]]; @@ -352,32 +395,36 @@ - (void) openHost:(NSMenuItem *) sender { } else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound) { NSAppleScript* iTerm2 = [[NSAppleScript alloc] initWithSource: - [NSString stringWithFormat: - @"on ApplicationIsRunning(appName) \n" - @" tell application \"System Events\" to set appNameIsRunning to exists (processes where name is appName) \n" - @" return appNameIsRunning \n" - @"end ApplicationIsRunning \n" - @" \n" - @"set isRunning to ApplicationIsRunning(\"iTerm\") \n" - @" \n" - @"tell application \"iTerm\" \n" - @" tell the current terminal \n" - @" if isRunning then \n" - @" set newSession to (launch session \"Default Session\") \n" - @" tell the last session \n" - @" write text \"clear\" \n" - @" write text \"%1$@\" \n" - @" end tell \n" - @" else \n" - @" tell the current session \n" - @" write text \"clear\" \n" - @" write text \"%1$@\" \n" - @" activate \n" - @" end tell \n" - @" end if \n" - @" end tell \n" - @"end tell \n" - , escapedObject]]; + [NSString stringWithFormat: + @"on ApplicationIsRunning(appName) \n" + @" tell application \"System Events\" to set appNameIsRunning to exists (processes where name is appName) \n" + @" return appNameIsRunning \n" + @"end ApplicationIsRunning \n" + @" \n" + @"set isRunning to ApplicationIsRunning(\"iTerm\") \n" + @" \n" + @"tell application \"iTerm\" \n" + @" tell the current terminal \n" + @" if isRunning then \n" + @" set newSession to (launch session \"%2$@\") \n" + @" tell the last session \n" + @" reopen \n" + @" activate \n" + @" write text \"clear\" \n" + @" write text \"%1$@\" \n" + @" set name to \"%3$@\" \n" + @" end tell \n" + @" else \n" + @" tell the current session \n" + @" write text \"clear\" \n" + @" write text \"%1$@\" \n" + @" set name to \"%3$@\" \n" + @" activate \n" + @" end tell \n" + @" end if \n" + @" end tell \n" + @"end tell \n" + , escapedObject, terminalTheme, terminalTitle]]; [iTerm2 executeAndReturnError:nil]; } else { NSAppleScript* terminalapp = [[NSAppleScript alloc] initWithSource: @@ -391,6 +438,7 @@ - (void) openHost:(NSMenuItem *) sender { @" \n" @"tell application \"Terminal\" \n" @" if isRunning then \n" + @" reopen \n" @" activate \n" @" tell application \"System Events\" to tell process \"Terminal.app\" to keystroke \"t\" using command down \n" @" do script \"clear\" in front window \n" @@ -400,9 +448,13 @@ - (void) openHost:(NSMenuItem *) sender { @" do script \"%1$@\" in window 1 \n" @" activate \n" @" end if \n" + @"set current settings of selected tab of front window to settings set \"%2$@\" \n" + @"set title displays custom title of windows to true \n" + @"set custom title of selected tab of front window to \"%3$@\" \n" @"end tell \n" - , escapedObject]]; + , escapedObject, terminalTheme, terminalTitle]]; [terminalapp executeAndReturnError:nil]; + } } diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index ded837e..d4ebc97 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -1,60 +1,62 @@ { - "_comments": [ - "Valid terminals include: 'Terminal.app' or 'iTerm'", - "In the editor value change 'default' to 'nano', 'vi', or another terminal based editor.", - "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", - "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" - ], - "terminal": "Terminal.app", - "editor": "default", - "launch_at_login": false, - "show_ssh_config_hosts": true, - "ssh_config_ignore_hosts": [], - "ssh_config_ignore_keywords": [], - "hosts": [ - { - "name": "My Dev Server", - "cmd": "ssh username@dev.example.com" - }, - { - "Personal": [ - { - "name": "My blog", - "cmd": "ssh username@blog.example.com" - }, - { - "Spouse": [ - { - "name": "Her blog", - "cmd": "ssh username@blog2.example.com" - } - ] - } - ] - }, - { - "Work": [ - { - "name": "dev.example.net", - "cmd": "ssh username@dev.example.net -p 3000" - }, - { - "name": "staging.example.net", - "cmd": "ssh username@staging.example.net -p 3000" - }, - { - "name": "production.example.net", - "cmd": "ssh username@example.net -p 3000" - } - ] - }, - { - "Vagrant": [ - { - "name": "precise32", - "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key" - } - ] - } - ] -} + "_comments": [ + "Valid terminals include: 'Terminal.app' or 'iTerm'", + "In the editor value change 'default' to 'nano', 'vi', or another terminal based editor.", + "Hosts will also be read from your ~/.ssh/config or /etc/ssh_config file, if available", + "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" + ], + "editor": "default", + "launch_at_login": false, + "show_ssh_config_hosts": true, + "ssh_config_ignore_hosts": [ ], + "ssh_config_ignore_keywords": [ ], + "terminal": "iTerm" + "hosts": [ + { + "cmd": "ssh username@dev.example.com", + "name": "My Dev Server", + "theme": "Homebrew", + "title": "Dev Server - SSH" + }, + { + "Personal": [ + { + "cmd": "ssh username@blog.example.com", + "name": "My blog" + }, + { + "Spouse": [ + { + "cmd": "ssh username@blog2.example.com", + "name": "Her blog" + } + ] + } + ] + }, + { + "Work": [ + { + "cmd": "ssh username@dev.example.net -p 4000", + "name": "dev.example.net" + }, + { + "cmd": "ssh username@staging.example.net -p 5000", + "name": "staging.example.net" + }, + { + "cmd": "ssh username@example.net -p 6000", + "name": "production.example.net" + } + ] + }, + { + "Vagrant": [ + { + "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key", + "name": "precise32" + } + ] + } + ] +} \ No newline at end of file From 41c430939f79477493d6c0ca46e2d5c7db311d35 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 14 Oct 2015 18:01:59 -0700 Subject: [PATCH 48/97] iTerm Nightly support - Run commands in new or existing windows - Set custom path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iTerm support for stable and nightly versions. Set “stable” or “nightly” in the JSON settings. Hardcoded applescript has been removed. In testing compiled applescripts were more consistent. Applescripts are configured with a handler and bundled with the app. Commands can be run in a new window or in the current window. Set “inTerminal” to “new” or “current”. Current window will always be the front active window. You can set a custom path for the JSON files. Do to this you would create a new file in your home directory called .shuttle.path ~/.shuttle.path In this file is the path to the JSON settings. Mine currently reads /Users/thshdw/Desktop/shuttle.json If the shuttle.path file does not exist then the system reads the file in its default location ~/shuttle.json. This ensures that this version is backwards compatible with existing installs. --- Shuttle.xcodeproj/project.pbxproj | 44 ++++ Shuttle/AppDelegate.h | 2 + Shuttle/AppDelegate.m | 232 ++++++++++++------ .../iTerm-stable-current-window.scpt | Bin 0 -> 1132 bytes .../iTerm-stable-new-tab-default.scpt | Bin 0 -> 1780 bytes .../apple-scpt/iTerm-stable-new-window.scpt | Bin 0 -> 2414 bytes .../iTerm2-nightly-current-window.scpt | Bin 0 -> 1082 bytes .../iTerm2-nightly-new-tab-default.scpt | Bin 0 -> 1754 bytes .../apple-scpt/iTerm2-nightly-new-window.scpt | Bin 0 -> 1562 bytes .../apple-scpt/terminal-current-window.scpt | Bin 0 -> 1094 bytes .../apple-scpt/terminal-new-tab-default.scpt | Bin 0 -> 2960 bytes Shuttle/apple-scpt/terminal-new-window.scpt | Bin 0 -> 1472 bytes Shuttle/shuttle.default.json | 40 ++- .../iTerm2-nightly-current-window.applescript | 23 ++ ...iTerm2-nightly-new-tab-default.applescript | 63 +++++ .../iTerm2-nightly-new-window.applescript | 34 +++ .../iTerm-stable-current-window.applescript | 31 +++ .../iTerm-stable-new-tab-default.applescript | 44 ++++ .../iTerm-stable-new-window.applescript | 39 +++ apple-scripts/iterm2.applescript | 24 -- apple-scripts/terminal.applescript | 19 -- .../terminal-current-window.applescript | 18 ++ .../terminal-new-tab-default.applescript | 40 +++ .../terminal/terminal-new-window.applescript | 29 +++ 24 files changed, 559 insertions(+), 123 deletions(-) create mode 100644 Shuttle/apple-scpt/iTerm-stable-current-window.scpt create mode 100644 Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt create mode 100644 Shuttle/apple-scpt/iTerm-stable-new-window.scpt create mode 100644 Shuttle/apple-scpt/iTerm2-nightly-current-window.scpt create mode 100644 Shuttle/apple-scpt/iTerm2-nightly-new-tab-default.scpt create mode 100644 Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt create mode 100644 Shuttle/apple-scpt/terminal-current-window.scpt create mode 100644 Shuttle/apple-scpt/terminal-new-tab-default.scpt create mode 100644 Shuttle/apple-scpt/terminal-new-window.scpt create mode 100644 apple-scripts/iTermNightly/iTerm2-nightly-current-window.applescript create mode 100644 apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript create mode 100644 apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript create mode 100644 apple-scripts/iTermStable/iTerm-stable-current-window.applescript create mode 100644 apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript create mode 100644 apple-scripts/iTermStable/iTerm-stable-new-window.applescript delete mode 100644 apple-scripts/iterm2.applescript delete mode 100644 apple-scripts/terminal.applescript create mode 100644 apple-scripts/terminal/terminal-current-window.applescript create mode 100644 apple-scripts/terminal/terminal-new-tab-default.applescript create mode 100644 apple-scripts/terminal/terminal-new-window.applescript diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index fc7b327..77d7556 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -13,6 +13,15 @@ 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; }; 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */; }; + A12D9BF01BCF2C73004F52A6 /* iTerm-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */; }; + A12D9BF11BCF2C73004F52A6 /* iTerm-stable-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */; }; + A12D9BF21BCF2C73004F52A6 /* iTerm-stable-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */; }; + A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */; }; + A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */; }; + A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */; }; + A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BED1BCF2C73004F52A6 /* terminal-current-window.scpt */; }; + A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */; }; + A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */; }; A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; }; A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */; }; C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; }; @@ -33,6 +42,15 @@ 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; + A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-current-window.scpt"; sourceTree = ""; }; + A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-new-tab-default.scpt"; sourceTree = ""; }; + A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-new-window.scpt"; sourceTree = ""; }; + A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-current-window.scpt"; sourceTree = ""; }; + A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-tab-default.scpt"; sourceTree = ""; }; + A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-window.scpt"; sourceTree = ""; }; + A12D9BED1BCF2C73004F52A6 /* terminal-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-current-window.scpt"; sourceTree = ""; }; + A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-new-tab-default.scpt"; sourceTree = ""; }; + A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-new-window.scpt"; sourceTree = ""; }; A1D700051A5DCDF4003563E4 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = ""; }; A1D700061A5DCE8D003563E4 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = ""; }; A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindowController.xib; sourceTree = ""; }; @@ -75,6 +93,22 @@ name = Images; sourceTree = ""; }; + A12D9BE61BCF2C72004F52A6 /* apple-scpt */ = { + isa = PBXGroup; + children = ( + A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */, + A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */, + A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */, + A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */, + A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */, + A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */, + A12D9BED1BCF2C73004F52A6 /* terminal-current-window.scpt */, + A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */, + A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */, + ); + path = "apple-scpt"; + sourceTree = ""; + }; C149EBEE15D5214600B1F558 = { isa = PBXGroup; children = ( @@ -114,6 +148,7 @@ C149EC0315D5214600B1F558 /* Shuttle */ = { isa = PBXGroup; children = ( + A12D9BE61BCF2C72004F52A6 /* apple-scpt */, 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */, 7E72D21E178003ED00A6389C /* Shuttle.entitlements */, C159DC2715D5DE7F00F5DE24 /* shuttle.icns */, @@ -194,15 +229,24 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */, + A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */, + A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */, + A12D9BF21BCF2C73004F52A6 /* iTerm-stable-new-window.scpt in Resources */, 0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */, + A12D9BF11BCF2C73004F52A6 /* iTerm-stable-new-tab-default.scpt in Resources */, 0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */, C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */, A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */, 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */, 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */, + A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */, C149EC0E15D5214600B1F558 /* Credits.rtf in Resources */, + A12D9BF01BCF2C73004F52A6 /* iTerm-stable-current-window.scpt in Resources */, + A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */, C149EC1415D5214600B1F558 /* MainMenu.xib in Resources */, C159DC2815D5DE8000F5DE24 /* shuttle.icns in Resources */, + A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */, 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index 69b62f5..2a4783a 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -15,6 +15,7 @@ NSStatusItem *statusItem; NSString *shuttleConfigFile; + NSString *shuttleJSONPath; // This is for the JSON File NSDate *configModified; @@ -23,6 +24,7 @@ NSString *terminalPref; NSString *editorPref; + NSString *iTermVersion; NSMutableArray* shuttleHosts; NSMutableArray* ignoreHosts; NSMutableArray* ignoreKeywords; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index a833c78..6b5f2cc 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -9,13 +9,29 @@ @implementation AppDelegate - (void) awakeFromNib { - // The path for the configuration file (by default: ~/.shuttle.json) - shuttleConfigFile = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json"]; - // if the config file does not exist, create a default one - if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { - NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; - [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; + // The location for the JSON path file. This is a simple file that contains the hard path to the *.json settings file. + shuttleJSONPath = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.path"]; + + //if file shuttle.path exists in ~/.shuttle.path then read this file as it should contain the custom path to *.json + if( [[NSFileManager defaultManager] fileExistsAtPath:shuttleJSONPath] ) { + + //Read the shuttle.path file which contains the path to the json file + NSString *jsonConfigPath = [NSString stringWithContentsOfFile:shuttleJSONPath encoding:NSUTF8StringEncoding error:NULL]; + + //Remove the white space if any. + jsonConfigPath = [ jsonConfigPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + shuttleConfigFile = jsonConfigPath; + }else{ + // The path for the configuration file (by default: ~/.shuttle.json) + shuttleConfigFile = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json"]; + + // if the config file does not exist, create a default one + if ( ![[NSFileManager defaultManager] fileExistsAtPath:shuttleConfigFile] ) { + NSString *cgFileInResource = [[NSBundle mainBundle] pathForResource:@"shuttle.default" ofType:@"json"]; + [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; + } } // Load the menu content @@ -182,6 +198,7 @@ - (void) loadMenu { terminalPref = [json[@"terminal"] lowercaseString]; editorPref = [json[@"editor"] lowercaseString]; + iTermVersion = [json[@"iTerm_version"] lowercaseString]; launchAtLoginController.launchAtLogin = [json[@"launch_at_login"] boolValue]; shuttleHosts = json[@"hosts"]; ignoreHosts = json[@"ssh_config_ignore_hosts"]; @@ -338,9 +355,11 @@ - (void) buildMenu:(NSArray*)data addToMenu:(NSMenu *)m { NSString *termTheme = cfg[@"theme"]; //Get the name for the terminal session NSString *termTitle = cfg[@"title"]; + //Get the value of setting inTerminal + NSString *termWindow = cfg[@"inTerminal"]; //Place the terminal command, theme, and title into an comma delimited string - NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@", menuCmd, termTheme, termTitle]; + NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@", menuCmd, termTheme, termTitle, termWindow]; [menuItem setTitle:cfg[@"name"]]; [menuItem setRepresentedObject:menuRepObj]; @@ -362,6 +381,8 @@ - (void) openHost:(NSMenuItem *) sender { NSString *terminalTheme; //The title for the terminal window NSString *terminalTitle; + //Are commands run in a new tab (default) a new terminal window (new), or in the current tab of the last used window (current). + NSString *terminalWindow; //if for some reason we get a representedObject with only one item... if (objectsFromJSON.count <=1) { @@ -372,7 +393,7 @@ - (void) openHost:(NSMenuItem *) sender { //Check if terminalTheme is null if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ if( [terminalPref isEqualToString:@"iterm"] ){ - terminalTheme = @"default"; + terminalTheme = @"Default"; }else{ terminalTheme = @"basic"; } @@ -385,79 +406,148 @@ - (void) openHost:(NSMenuItem *) sender { }else{ terminalTitle = [objectsFromJSON objectAtIndex:2]; } + //Check if inTerminal is null + if( [[objectsFromJSON objectAtIndex:3] isEqualToString:@"(null)"]){ + terminalWindow = @"default"; //this is not currently used. + }else{ + terminalWindow = [objectsFromJSON objectAtIndex:3]; + } } - // Check if Url + //Set Paths to iTerm Stable AppleScripts + NSString *iTermStableNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-new-window" ofType:@"scpt"]; + NSString *iTermStableCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-current-window" ofType:@"scpt"]; + NSString *iTermStableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-new-tab-default" ofType:@"scpt"]; + + //Set Paths to iTerm Nightly AppleScripts + NSString *iTerm2NightlyNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-window" ofType:@"scpt"]; + NSString *iTerm2NightlyCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-current-window" ofType:@"scpt"]; + NSString *iTerm2StableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-tab-default" ofType:@"scpt"]; + + //Set Paths to terminalScripts + NSString *terminalNewWindow = [[NSBundle mainBundle] pathForResource:@"terminal-new-window" ofType:@"scpt"]; + NSString *terminalCurrentWindow = [[NSBundle mainBundle] pathForResource:@"terminal-current-window" ofType:@"scpt"]; + NSString *terminalNewTabDefault = [[NSBundle mainBundle] pathForResource:@"terminal-new-tab-default" ofType:@"scpt"]; + + //Set the name of the handler that we are passing parameters too in the apple script + NSString *handlerName = @"scriptRun"; + + //script expects the following order: Command, Theme, Title + NSArray *passParameters = @[escapedObject, terminalTheme, terminalTitle]; + +// Check if Url NSURL* url = [NSURL URLWithString:[sender representedObject]]; if(url) { [[NSWorkspace sharedWorkspace] openURL:url]; } - else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound) { - NSAppleScript* iTerm2 = [[NSAppleScript alloc] initWithSource: - [NSString stringWithFormat: - @"on ApplicationIsRunning(appName) \n" - @" tell application \"System Events\" to set appNameIsRunning to exists (processes where name is appName) \n" - @" return appNameIsRunning \n" - @"end ApplicationIsRunning \n" - @" \n" - @"set isRunning to ApplicationIsRunning(\"iTerm\") \n" - @" \n" - @"tell application \"iTerm\" \n" - @" tell the current terminal \n" - @" if isRunning then \n" - @" set newSession to (launch session \"%2$@\") \n" - @" tell the last session \n" - @" reopen \n" - @" activate \n" - @" write text \"clear\" \n" - @" write text \"%1$@\" \n" - @" set name to \"%3$@\" \n" - @" end tell \n" - @" else \n" - @" tell the current session \n" - @" write text \"clear\" \n" - @" write text \"%1$@\" \n" - @" set name to \"%3$@\" \n" - @" activate \n" - @" end tell \n" - @" end if \n" - @" end tell \n" - @"end tell \n" - , escapedObject, terminalTheme, terminalTitle]]; - [iTerm2 executeAndReturnError:nil]; - } else { - NSAppleScript* terminalapp = [[NSAppleScript alloc] initWithSource: - [NSString stringWithFormat: - @"on ApplicationIsRunning(appName) \n" - @" tell application \"System Events\" to set appNameIsRunning to exists (processes where name is appName) \n" - @" return appNameIsRunning \n" - @"end ApplicationIsRunning \n" - @" \n" - @"set isRunning to ApplicationIsRunning(\"Terminal\") \n" - @" \n" - @"tell application \"Terminal\" \n" - @" if isRunning then \n" - @" reopen \n" - @" activate \n" - @" tell application \"System Events\" to tell process \"Terminal.app\" to keystroke \"t\" using command down \n" - @" do script \"clear\" in front window \n" - @" do script \"%1$@\" in front window \n" - @" else \n" - @" do script \"clear\" in window 1 \n" - @" do script \"%1$@\" in window 1 \n" - @" activate \n" - @" end if \n" - @"set current settings of selected tab of front window to settings set \"%2$@\" \n" - @"set title displays custom title of windows to true \n" - @"set custom title of selected tab of front window to \"%3$@\" \n" - @"end tell \n" - , escapedObject, terminalTheme, terminalTitle]]; - [terminalapp executeAndReturnError:nil]; + //If the JSON file is set to use iTerm + else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound ) { + + //If the JSON file is set to use applescript via iTermVersion then configure for iTerm Stable + if( [iTermVersion isEqualToString: @"stable"] ) { + //run the applescript that works with iTerm Stable + + //if we are running in a new iTerm "Stable" Window + if ( [terminalWindow isEqualToString:@"new"] ) { + [self runScript:iTermStableNewWindow handler:handlerName parameters:passParameters]; + }else { + //if we are running in the current iTerm "Stable" Window + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:iTermStableCurrentWindow handler:handlerName parameters:passParameters]; + }else { + //we are using the default action of shuttle, use the active window in a new Tab + [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; + } + } + } + //iTermVersion is not set to "stable" using applescripts Configured for Nightly + else { + //if we are running in a new iTerm "Nightly" Window + if( [terminalWindow isEqualToString:@"new"] ) { + [self runScript:iTerm2NightlyNewWindow handler:handlerName parameters:passParameters]; + }else { + //if we are running in the current iTerm "Nightly" Window + if( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:iTerm2NightlyCurrentWindow handler:handlerName parameters:passParameters]; + }else { + //we are using the default action of shuttle, use the active window in a new Tab + [self runScript:iTerm2StableNewTabDefault handler:handlerName parameters:passParameters]; + } + } + } + } + //If JSON file is set to use Terminal.app + else { + if ( [terminalWindow isEqualToString:@"new"] ) { + [self runScript:terminalNewWindow handler:handlerName parameters:passParameters]; + }else { + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:terminalCurrentWindow handler:handlerName parameters:passParameters]; + }else { + [self runScript:terminalNewTabDefault handler:handlerName parameters:passParameters]; + } + } } } +- (void) runScript:(NSString *)scriptPath handler:(NSString*)handlerName parameters:(NSArray*)parametersInArray +{ + //special thanks to stackoverflow.com/users/316866/leandro for pointing me the right direction. + //see http://goo.gl/olcpaX + NSAppleScript * appleScript; + NSAppleEventDescriptor * thisApplication, *containerEvent; + NSURL * pathURL = [NSURL fileURLWithPath:scriptPath]; + + NSDictionary * appleScriptCreationError = nil; + appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL error:&appleScriptCreationError]; + + if (handlerName && [handlerName length]) + { + /* If we have a handlerName (and potentially parameters), we build + * an NSAppleEvent to execute the script. */ + + //Get a descriptor + int pid = [[NSProcessInfo processInfo] processIdentifier]; + thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID + bytes:&pid + length:sizeof(pid)]; + + //Create the container event + + //We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework... + #define kASAppleScriptSuite 'ascr' + #define kASSubroutineEvent 'psbr' + #define keyASSubroutineName 'snam' + containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite + eventID:kASSubroutineEvent + targetDescriptor:thisApplication + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + //Set the target handler + [containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:handlerName] + forKeyword:keyASSubroutineName]; + + //Pass parameters - parameters is expecting an NSArray with only NSString objects + if ([parametersInArray count]) + { + + NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor]; + NSString *object; + + for (object in parametersInArray) { + [arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object] + atIndex:([arguments numberOfItems] +1)]; + } + + [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject]; + } + //Execute the event + [appleScript executeAppleEvent:containerEvent error:nil]; + } +} + - (IBAction)showImportPanel:(id)sender { NSOpenPanel * openPanelObj = [NSOpenPanel openPanel]; NSInteger tvarNSInteger = [openPanelObj runModal]; @@ -488,7 +578,6 @@ - (IBAction)showExportPanel:(id)sender { } } - - (IBAction)configure:(id)sender { //if the editor setting is omitted or contains 'default' open using the default editor. @@ -511,7 +600,6 @@ - (IBAction)configure:(id)sender { } } - - (IBAction)showAbout:(id)sender { //Call the windows controller diff --git a/Shuttle/apple-scpt/iTerm-stable-current-window.scpt b/Shuttle/apple-scpt/iTerm-stable-current-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..c99b82fa0c6d1638ef86a8c014bf1cc5d528d386 GIT binary patch literal 1132 zcmb_c%Wl(95Is)PCh-kP8X&3!m9k=Csx)0ys@PN!&@QS-Z6bEs$_+R+RTgxH z?iAE~#WeMDK|0m+1!ixUllh}`N+)zoKhOz39SJ&Zn@yn;)ReL)8;S%UWK_G!a?UXPZ5qoeS17xo10T~S6C z9YM_8|CrI%+h&RXg{RzJgr4Lf2N^9_`7JnTotujixwyDyJM2^F@WppR?V%4>|JLRcT ziRcx_y@A9kEwR@$oV=cQtABeJIg62f>8ey?p4_8Hwp(oQaDFlBm_ZU5OPQ1>ET-q;Kxm+#+o&N{i$ZMzb H^B;Z#eKtPK literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt b/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt new file mode 100644 index 0000000000000000000000000000000000000000..bd4dd006ef8a53a6fdff6846db6b20b15924898b GIT binary patch literal 1780 zcmb_dOK%%h6#m9>5_`t69n)5o5K<&q4N;OVP^Ai~s-l#D09TDIidap@BRZ|ewq_;? z?DzplAhCi~Uu+O85bzrin>J0`q|Y>uKGLLV`l7UH;p5zKEC(uw4P$xcn|og0Ip^Lp zd##{U+%wX%qi@H6`lkWJGbI0lhL}bInqZ{m6`3qzi1SahA&z!U0J4H(e9mVEpJUqq zl7a2Jwa_1}G~oWxios{tJ^@oh2RhKn#|GD0deit+ z`0xp0asG}(o7~#cNC*)=;zK?#_z1ZHbXxw$f$f#*V9+ZWd>Hvu>)|2CbC6W9prFAA zO_>{(8!`oBLQ3-NKnL&h9#?snE4*Xyev2sgnx0;Wvf7mWD~iFpk-u>_JQT53AY#1| z*{Iib)(Wn(zJo0TNXU8^xl^d1P!^riw`GINn-spAkFoXW&`;yu^#Vz-dmQi|2VxigT8eJi`f|=BW%)+UE&5Yyb20c47Z}a%^Fh z-9Wk4`sBoFZTXP{tpZJq{kl9XJ;&5rm87eZ=(B3IR@(ouK07u&xpVl|!N zq5uazJoLdrO@5hZB;+xI1JaXi8}0j><}X3(hhnMo3gUWAW|Pj#f(Aq1(|JkIz95jb zi`d?$s{8KygTrd2o=X!Hsn+7-)7$c#?x{_SgZeWUZN z7zLjO)sj=T8b*p$#XBf2C&kDfcO|bgqGwlq$1YcchF;V8gyJUWt0H-MopfW^?$$oa=S)3U-_Gy*a#LE*vYT5SLM7F1Y!6B8RR2a8eIY TEEY4c z_dC~HOsBVJb9eK`mW^%d8x@hd7=S-TlYj<@Xv7j_S7LDqNIm|LpwvmdG-!kpXD^87 zc!nhd&t+YkM4W8dF1h9YLKSZ7FBo_x$uij7^-1&1lwRhjQ`L~$;D09wa+`s~^zVv&yoV9!!+nTh|{=&DFf3!-mlc~KIP**E(W)( z377FJmvjjiaRKLJ(xiPErds~x=l#sK4=6k_bGD;gYc15A7EE_<_X{*OFtXUi{K&+6 zg@~Q8%J~+vRPwX_hxgM1ljH4O?^LZ=2v`9k&DH+$Z{y}tDQ9O**De+uZanv4Y%ujH zM-vKF{UlFZU6iXeKrA{xgg6K@t`j))yWX-HQQ0UL)fZeqDN)!&Wn1$aR{aQWW=(t2v z=L@IUYvs%p)7?_Y?H*<8`7+NncV+_AIS!1)?QEP;7xoleRd zshJIy_PV^9+Sw?9^c>r|bZUNAD?!62671_qr}hjDQ)?k=twCvR5)(LwU$_-9QVpV5~DbZ5gfr`9Kt~y z!2Xyd|JUn(Os}I9*REnVr{gHmM{YT4}NSe?u&VO-oZ#Kl}wLJj3V! literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm2-nightly-current-window.scpt b/Shuttle/apple-scpt/iTerm2-nightly-current-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..aeebfe15e0abef0f6cce214f42be02d97ab91768 GIT binary patch literal 1082 zcmb_bO-~b16g_WRXgjaAr5ZxwhUmhino@Sijx>I-AWdi+NnDN7Mh837W@h+U`U70} z6YvMP@E5q$#0_ELl915yE#D#(^m-rI*hGpOXEJxry!-B*bI#m(Xn3XKNWOnCJ(y8f z3_vhWL_Z;*2_ir+MvNa3Pe4PEejtD#x x7;AJ!r;^Uln?b}gU90B1Q%(zxPdSoK z(U*a4R&CpGN-C*mFED3g+$@x!CY{hReMdJ!bR?V}nd<@VOFFo8m!$pk^%={5abB$eS0@l+7xpCWT_K}~ zjv(hAyw9cHzP!s`#n7c>?a~fy(^o{G)0U*&D>?|;QNp&BuuaGS2NoukunkEYmr~QX zH4BR{zjaCLm+nbGX8}=Kqg7g=2Gwafj;MGY=5PI^B%ish-+BFz&c0=fJ| zrx(pnf82b`e&Nd_Ct?-KuAMe&wNAVLvA*lbr`9eWW1ingf9v`8JvvgWRV>r+ zt*Ya(dF;ddOzJ6TdlX){e`|=b;IU**%>O3SA8xN1MjO!>Zm|6p3Mj*cjWi6@_>6O} z0CLD<0!j3?*AM(DzYZ|~#Zsp=^remRwC}QhXR1;mdc~PHP*|r`&f1n|8m2$}cXkCq zJ?qXmIyD$QF+E0gMuqaMuhTNuj{2_cIsR;`R(Vl5%6e6dhmP;+) eOCph~bBv@KbCoE%i^XCb2LIh`%(dp?;=5n$EHF9% literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm2-nightly-new-tab-default.scpt b/Shuttle/apple-scpt/iTerm2-nightly-new-tab-default.scpt new file mode 100644 index 0000000000000000000000000000000000000000..822fe6837bf475a0ccab1784d34461ee5b1a02ac GIT binary patch literal 1754 zcmb_d&2JM&6n|?wvAtvLB%~2)RViv!Ra#QVNE|4a3PqvKfv!Ysk*cT7+Q5Q$z1rPi zdgBl1q1X0AXnX9TqMUnds|pAK!bc$_1ZX}8;j4TE+8=K=v1}D>dg)jm|7PBMzxSJY zGvnQcUoPz$?%kH(R*>H;5hZc}{vm~83P7YVrYsLB*FiB#;BSgkg4z@Y)WJB0mv|xY zl6nf1_D#=n18>Y}z`bLRzzf<^Al0ndw&9eeQ5{&rY|S!RvIDDlj%RpE?Uck5fz?Ph z144;zbF;wE}o?n3&CSN!b5?_)K#FQ;T`ZzySxSl?6Sb4kWLt(4Hft9;p<68)$%2^lINSL z#P#8ks6-P-ep&j2YfDofJ87#}@$9_ex>4T$F+cPBp2;0Q|Jv~4=YrpfbZ=w4{+rq5 zx>d_G0;}fuyl1=*6Lsx#wsuN9`QbY|7%P1JSrhAji{pLbKG(1-Gb374pcY zJQ?KjCl_9QorY8%KG*B_Q&f+`4(N>nV z9Ou~Y)1dCEm}q3<(d&L^*>AuLMg`6^4|WWvxjMK*r)Ik7M{TyRqP$H*jC-_08H(w1 z+$VJH_Thd8?IoQp?~gM~>e_r?7rm)1aKHJ4w%E6uUzL-(wsgFjp-ib%`iw@qX*>J( yg`V$e%j0ax>e>oN^n`J_wYX+XX&-$s!Uw05!`^L46Xl*W_}_;QRAzO0`iEB<_$yQZ literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt b/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..0d7208de551b3b341e8dce728c2b1809bd105524 GIT binary patch literal 1562 zcmb_c&2JM&9DQq>*xq&QBv3?)kRmRXmdG(T%B4b4C>+qL#1>RNEo(!Tde^Jn4W>8# zfF62nPe9uvC)#t5ZPg3nBYcO10L=&APzr(e^*1J#tyWGi9m}(C=J%U7Z|2R+Zab=0 zc1;X#FK#cXe;z=pK=LnWNN6OW2}WL;C=4Kh6#qmLDXh~3U_fw!Z}{5a8*D5g8#$pH z#Nm|RhWn>{gRilv1l?(Pp6ypvP#=4XC7Ktq^Z-}*iZA&G`jF-egDXARG`jtAq$=N3@)v~rEyW2vY<@43nM9&m|`eX?r@&B4c_Uoy!kbjce}^(5+X`0 z$;x4z<1BA+#^78J{@FG7Z}s3eI0HkDP!Ki_yK)$0E*<^I@g{GepVxVfS9yh(In7JF z$O{GJv@bGpb^hnFx4eI!+(OuOH&WDEQ7)jCv?dPrn&{lIpO*KCg_!=x&${JW=oRfC z=#~8+{jqJ0<&Yu4#TM(LsgJf3S6j zU`=khXcGOKMgGq2F+0&EG>;A9tBW#f2;rd!8-e@^ts8D(0tYdKjosr%JHyvQ8--G- z^CnVyAWw}hmTf9}Zsgcbe9WJ!*U1x-^Cc9{h^?6qfB8z4uXSFR)adi5QLWYOPU;Ex z-l0(EHKBBxu25c;Jf{)XoO&Z_=c(;z96CCz^NK|G#-Zmon?ZNfl%n&pu#AOGVVM?m zz=;H1Qeu7E(|J))xpq9(d0~|$^5f&(ENYHH-owYUL)dE0jnuR)c-K6dL&BPuI%S!6 zKN}U$_X(1d6-%<-KPAnyWiE^i;XQLv>h-71rIFoo@y}Z3^2vG*xk{x{fGw}LIMu&0 HGjrx|+NJ3y literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/terminal-current-window.scpt b/Shuttle/apple-scpt/terminal-current-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..fcc7472416067caa510306ffed54bcc1b94a2d1c GIT binary patch literal 1094 zcmcIj%TC)+5Ixrkg!o#HL!~OJxY@nsv5Uk*Lh7cLleV|mPGlR>C7;kw zpdV1FkB{gUHn2mL8s3z5DX%geL!m_wcD3d4@tv9DbH>+??XWV`JJ8i#=q|;V2_Tsv zqGJeXf(Q_dVe%s~7BnR37ZOOKO)!APSfe94lyrofC8R?qQ1vJn_0M4cs4wXdxe|1z z=6SYXiIaNYDdw!nWGRadXrK1zC)$ysT}cNm*#zyg9@>MJq#x)=a1bUsSRn04+CgUt zDLZ%>I$q@z^m!GHiCJqqYu%Z?iNp}pm+d_(`uqkQt8XH;Kg`9Zw zv7CQ5a-U~qqAj+yLF=?e-;st+tCBXZVGy()8(WKwtw1I?urV1MTb8tZr8JGH*;tzO zElFCsvc`hW&J9|m1)8ThYGz=F4;}oa|HJW66R=$s#?+#p_lroB2Zmd{Xmae!%2S>e zrnjCOgVn%OzU>z5dc9S{b?Y+^K2IG-pUz5n6Z7s(cbz@|xu=IB<*GSI?kdNQRLu{2aL&<#i>giBf`LI)5x|3j4;wBD z9P4~$m^Xnk1~80H-2IoccurZo$an*yh}elvi^vu1>R1%;fc#O{B^u|WQ%s#!BWa*mz$O6|y1#PHbP~C90(v+pegZ`(q zbBS_yg=t-sJ1ghbL{aE=mao%vTC-UTkj?s5_8u`^&RW3btmjZx10SzDSvrB51PZ~& zn$9XTyP+UqdWiC#`U7fj{V!@?{jX}|+wZMST%oHj>{s%zkGrsk zpm6_}&}F(rzjIL+5k!bCAWY|Jj7DiBh!W{XAMc!(@Oj@OK}(pnpqBRV{*%0F<6gHw zgPH!h4_GUV&VTA}GOVPT(h^ZEop!q+-}>0Q2Zk1%pL3V6DX<$9SLNo*zs1dtnJ^vG z;1oq`VX!mu37-oKJhV9WI%A13XN1w6DXw5E7f`z+olcm#=9uj%yB5zmYVl&$0ldmx z(*y$+k}#2ih6JKKruiFWUN2NMVlPVZ#%j*kbIRC@jGH0FAS#jRG7980JC013P#M*X zjAQYmN_8X>MBRMNR}ffc(JP}#)7CZJ$*esaL_ivT3EJC6#%2Rv(LVFfwcn|R8@p`B zNLJNnjFe;liw3x`@5`aZZX>6)i}wDycFSlqtc?Vwbt!(yXpptR}0)`U(Pgle?-BU9jPBML{`G zL9nVnE@t~fw*%a0(oZN2bCZSEHlt6}{ocUW;`5Enwon={yH$(KK$cG58c?cijGn{avO}CP`bm-IliO9Ep%ZcfN2G8p-pA8KxJg9PKqCY8T$#04ykBfInU3;xPTFdb>4-pjiq{Tj zkSyyXCgA$7UPZORU_AXV`4H@Jzd?P8UoHR=v!L5QQgoS Q)cAdw|04Yh3_z3#BtnTksNNVnTFfCA97gVWBagyCwn&#*LF{(b1WqX$Nu1Pw*!I z>e9rB{syzZCx1@()+SfViz3n#eA@7&-wR3prFgPUd5G=3L9+(012uc!%16O5rojPMIT zbIsrv)TI$N3pa|EoBahB-K@dSB~3N%-^{r=L=?6wG`O}OvwdeTrZ6C;B+qJ8^AlIO z!euUT(cq^ttE}!T{j!s2h)!lWiaM$%B4yREg<9T&y-|#hGafHKhM775?vUC2!bv~fMbnK#% z)~?9@(}J0{&TFvi z^>?3<4&Qcc-_D7}glt2+=uKXeSgqohJ0mBBHDPgt;Y*%ma9R>*x{%L1wq^Quw^wY! zZ;d7#bRENSMA42M3UFb=g9!(X(&ptC7u_H-XhSFJaOzLZ)NjeuLB&JRQc#)be2ToVO-jLHyptgS^Ie9zfL=kEkx{65utrBID i@~p}vrw3B^XBt{FgX*Q(hSrX_DQB#F(&*;s=%b%CCeCO8 literal 0 HcmV?d00001 diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index d4ebc97..7e85b3d 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -6,29 +6,36 @@ "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" ], "editor": "default", + "iTerm_version": "nightly", "launch_at_login": false, "show_ssh_config_hosts": true, "ssh_config_ignore_hosts": [ ], "ssh_config_ignore_keywords": [ ], - "terminal": "iTerm" + "terminal": "iTerm", "hosts": [ { - "cmd": "ssh username@dev.example.com", + "cmd": "ps aux | grep foo", + "inTerminal": "new", "name": "My Dev Server", "theme": "Homebrew", "title": "Dev Server - SSH" }, { - "Personal": [ + "Spouses Servers": [ { - "cmd": "ssh username@blog.example.com", - "name": "My blog" + "cmd": "echo '—->WARNING<-- Running a command in this active terminal! Are you sure? y/n'; read sure; if [ $sure == y ]; then echo running command... && tail -f /var/log/messages; else echo exiting...; fi", + "inTerminal": "current", + "name": "Look at the Logs" }, { - "Spouse": [ + "Jane’s Servers": [ { "cmd": "ssh username@blog2.example.com", - "name": "Her blog" + "name": "Wordpress Box" + }, + { + "cmd": "ssh username@shop1.example.com", + "name": "Shopping Site" } ] } @@ -59,4 +66,21 @@ ] } ] -} \ No newline at end of file +} + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apple-scripts/iTermNightly/iTerm2-nightly-current-window.applescript b/apple-scripts/iTermNightly/iTerm2-nightly-current-window.applescript new file mode 100644 index 0000000..2aefda2 --- /dev/null +++ b/apple-scripts/iTermNightly/iTerm2-nightly-current-window.applescript @@ -0,0 +1,23 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- CommandRun(argsCmd) +--end run + +on scriptRun(argsCmd) + set withCmd to (argsCmd) + CommandRun(withCmd) +end scriptRun + +on CommandRun(withCmd) + tell application "iTerm" + reopen + activate + tell the current window + tell the current session + --set name to theTitle + write text withCmd + end tell + end tell + end tell +end CommandRun diff --git a/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript b/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript new file mode 100644 index 0000000..6498192 --- /dev/null +++ b/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript @@ -0,0 +1,63 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep xcode" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- CommandRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is not running then + tell application "iTerm" + activate + delay 0.2 + close first window + end tell + + tell application "iTerm" + create window with profile withTheme + tell the current window + tell the current session + set name to theTitle + set profile to withTheme + write text withCmd + end tell + end tell + end tell + else + --assume that iTerm is open and open a new tab + try + tell application "iTerm" + tell the current window + create tab with profile withTheme + tell the current tab + tell the current session + set name to theTitle + write text withCmd + end tell + end tell + end tell + end tell + on error msg + --if all iTerm windows are closed the app stays open. In this scenario iTerm has no "current window" and will give an error when trying to create the new tab. + tell application "iTerm" + create window with profile withTheme + tell the current window + tell the current session + set name to theTitle + write text withCmd + end tell + end tell + end tell + end try + end if + end tell +end CommandRun \ No newline at end of file diff --git a/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript b/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript new file mode 100644 index 0000000..de41323 --- /dev/null +++ b/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript @@ -0,0 +1,34 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- CommandRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is not running then + activate + delay 0.2 + close first window + end if + end tell + tell application "iTerm" + create window with profile withTheme + tell the current window + tell the current session + set name to theTitle + set profile to withTheme + write text withCmd + end tell + end tell + end tell +end CommandRun diff --git a/apple-scripts/iTermStable/iTerm-stable-current-window.applescript b/apple-scripts/iTermStable/iTerm-stable-current-window.applescript new file mode 100644 index 0000000..edc62c7 --- /dev/null +++ b/apple-scripts/iTermStable/iTerm-stable-current-window.applescript @@ -0,0 +1,31 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- CommandRun(argsCmd) +--end run + +on scriptRun(argsCmd) + set withCmd to (argsCmd) + CommandRun(withCmd) +end scriptRun + +on CommandRun(withCmd) + tell application "iTerm" + if it is running then + tell the current terminal + tell the current session + reopen + activate + write text withCmd + end tell + end tell + else + tell the current terminal + tell the current session + write text withCmd + activate + end tell + end tell + end if + end tell +end CommandRun \ No newline at end of file diff --git a/apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript b/apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript new file mode 100644 index 0000000..af81763 --- /dev/null +++ b/apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript @@ -0,0 +1,44 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- CommandRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is running then + tell the current terminal + set newSession to (launch session withTheme) + tell the last session + reopen + activate + write text "clear" + write text withCmd + set name to theTitle + end tell + end tell + else + activate + delay 0.2 + close first window + set newTerm to (make new terminal) + tell newTerm + set newSession to (launch session withTheme) + tell newSession + write text withCmd + set name to theTitle + activate + end tell + end tell + end if + end tell +end CommandRun \ No newline at end of file diff --git a/apple-scripts/iTermStable/iTerm-stable-new-window.applescript b/apple-scripts/iTermStable/iTerm-stable-new-window.applescript new file mode 100644 index 0000000..9bdd783 --- /dev/null +++ b/apple-scripts/iTermStable/iTerm-stable-new-window.applescript @@ -0,0 +1,39 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- preLoad(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + preLoad(withCmd, withTheme, theTitle) +end scriptRun + +on preLoad(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is not running then + activate + delay 0.2 + close first window + end if + end tell + CommandRun(withCmd, withTheme, theTitle) +end preLoad + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + set newTerm to (make new terminal) + tell newTerm + set newSession to (launch session withTheme) + tell newSession + write text withCmd + set name to theTitle + activate + end tell + end tell + end tell +end CommandRun \ No newline at end of file diff --git a/apple-scripts/iterm2.applescript b/apple-scripts/iterm2.applescript deleted file mode 100644 index 126fc84..0000000 --- a/apple-scripts/iterm2.applescript +++ /dev/null @@ -1,24 +0,0 @@ -on ApplicationIsRunning(appName) - tell application "System Events" to set appNameIsRunning to exists (processes where name is appName) - return appNameIsRunning -end ApplicationIsRunning - -set isRunning to ApplicationIsRunning("iTerm") - -tell application "iTerm" - tell the current terminal - if isRunning then - set newSession to (launch session "Default Session") - tell the last session - write text "clear" - write text "%1$@" - end tell - else - tell the current session - write text "clear" - write text "%1$@" - activate - end tell - end if - end tell -end tell diff --git a/apple-scripts/terminal.applescript b/apple-scripts/terminal.applescript deleted file mode 100644 index ba61caf..0000000 --- a/apple-scripts/terminal.applescript +++ /dev/null @@ -1,19 +0,0 @@ -on ApplicationIsRunning(appName) - tell application "System Events" to set appNameIsRunning to exists (processes where name is appName) - return appNameIsRunning -end ApplicationIsRunning - -set isRunning to ApplicationIsRunning("Terminal") - -tell application "Terminal" - if isRunning then - activate - tell application "System Events" to tell process "Terminal.app" to keystroke "t" using command down - do script "clear" in front window - do script "%1$@" in front window - else - do script "clear" in window 1 - do script "%1$@" in window 1 - activate - end if -end tell diff --git a/apple-scripts/terminal/terminal-current-window.applescript b/apple-scripts/terminal/terminal-current-window.applescript new file mode 100644 index 0000000..89f3263 --- /dev/null +++ b/apple-scripts/terminal/terminal-current-window.applescript @@ -0,0 +1,18 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- CommandRun(argsCmd) +--end run + +on scriptRun(argsCmd) + set withCmd to (argsCmd) + CommandRun(withCmd) +end scriptRun + +on CommandRun(withCmd) + tell application "Terminal" + reopen + activate + do script withCmd in front window + end tell +end CommandRun diff --git a/apple-scripts/terminal/terminal-new-tab-default.applescript b/apple-scripts/terminal/terminal-new-tab-default.applescript new file mode 100644 index 0000000..8eac612 --- /dev/null +++ b/apple-scripts/terminal/terminal-new-tab-default.applescript @@ -0,0 +1,40 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- preLoad(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + preLoad(withCmd, withTheme, theTitle) +end scriptRun + +on preLoad(withCmd, withTheme, theTitle) + tell application "Terminal" + if it is running then + reopen + activate + tell application "System Events" + tell process "Terminal" + delay 0.2 + keystroke "t" using {command down} + end tell + end tell + end if + my CommandRun(withCmd, withTheme, theTitle) + activate + end tell +end preLoad + +on CommandRun(withCmd, withTheme, theTitle) + tell application "Terminal" + do script withCmd in front window + set current settings of selected tab of front window to settings set withTheme + set title displays custom title of front window to true + set custom title of selected tab of front window to theTitle + end tell +end CommandRun diff --git a/apple-scripts/terminal/terminal-new-window.applescript b/apple-scripts/terminal/terminal-new-window.applescript new file mode 100644 index 0000000..f4a090e --- /dev/null +++ b/apple-scripts/terminal/terminal-new-window.applescript @@ -0,0 +1,29 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- CommandRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "Terminal" + if it is not running then + --if this is the first time Terminal is running you have specify window 1 + --if you dont do this you will get two windows and the title wont be set + set newTerm to do script withCmd in window 1 + else + set newTerm to do script withCmd + end if + activate + set newTerm's current settings to settings set withTheme + set custom title of front window to theTitle + end tell +end CommandRun From e61d19f156445a6a67902c4035ddb80d92fe3f06 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 15 Oct 2015 15:37:15 -0700 Subject: [PATCH 49/97] Changed the build number to 1.2.3 Changed the build Number to 1.2.3 --- Shuttle/Shuttle-Info.plist | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index a6d5ed9..75d3eb7 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.2 + 1.2.3 CFBundleSignature ???? CFBundleVersion - 1.2.2 + 1.2.3 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion @@ -34,7 +34,7 @@ MainMenu NSPrincipalClass NSApplication - Product Homepage - http://fitztrev.github.io/shuttle/ + Product Homepage + http://fitztrev.github.io/shuttle/ From fcbc83c98bda754879cfa0ab6260e8f96bd4f09c Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sat, 17 Oct 2015 23:31:36 -0700 Subject: [PATCH 50/97] bug fixes - Fixed the icon it was not turning white. - Fixed iTerm2 variable - About window on top changes - Setting the name of the menu command as the title, if title is null --- Shuttle.xcodeproj/project.pbxproj | 5 +++- Shuttle/AboutWindowController.xib | 13 ++++++--- Shuttle/AppDelegate.m | 46 ++++++++++++++++--------------- Shuttle/Shuttle-Info.plist | 6 ++-- Shuttle/shuttle.default.json | 6 ++-- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index 77d7556..8489867 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -204,7 +204,7 @@ C149EBF015D5214600B1F558 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0630; + LastUpgradeCheck = 0700; ORGANIZATIONNAME = fitztrev; }; buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "Shuttle" */; @@ -303,6 +303,7 @@ CLANG_ENABLE_OBJC_ARC = 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; @@ -352,6 +353,7 @@ GCC_PREFIX_HEADER = "Shuttle/Shuttle-Prefix.pch"; INFOPLIST_FILE = "Shuttle/Shuttle-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = "shuttle.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -367,6 +369,7 @@ GCC_PREFIX_HEADER = "Shuttle/Shuttle-Prefix.pch"; INFOPLIST_FILE = "Shuttle/Shuttle-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_BUNDLE_IDENTIFIER = "shuttle.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/Shuttle/AboutWindowController.xib b/Shuttle/AboutWindowController.xib index 1348a07..19a5411 100644 --- a/Shuttle/AboutWindowController.xib +++ b/Shuttle/AboutWindowController.xib @@ -1,14 +1,13 @@ - + - + - @@ -20,7 +19,7 @@ - + @@ -29,10 +28,12 @@ + + @@ -41,6 +42,7 @@ + @@ -49,6 +51,7 @@ + @@ -57,6 +60,7 @@ + diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 6b5f2cc..c1e2eb9 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -33,34 +33,32 @@ - (void) awakeFromNib { [[NSFileManager defaultManager] copyItemAtPath:cgFileInResource toPath:shuttleConfigFile error:nil]; } } - - // Load the menu content - // [self loadMenu]; - + // Define Icons + //only regular icon is needed for 10.10 and higher. OS X changes the icon for us. regularIcon = [NSImage imageNamed:@"StatusIcon"]; altIcon = [NSImage imageNamed:@"StatusIconAlt"]; + // Create the status bar item + statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; + [statusItem setMenu:menu]; + [statusItem setImage: regularIcon]; + // Check for AppKit Version, add support for darkmode if > 10.9 BOOL oldAppKitVersion = (floor(NSAppKitVersionNumber) <= 1265); + // 10.10 or higher, dont load the alt image let OS X style it. if (!oldAppKitVersion) { - // 10.10 or higher, add support to icon for auto detection of Regular/Dark mode - [regularIcon setTemplate:YES]; - [altIcon setTemplate:YES]; + regularIcon.template = YES; + } + // Load the alt image for OS X < 10.10 + else{ + [statusItem setHighlightMode:YES]; + [statusItem setAlternateImage: altIcon]; } - // Create the status bar item - statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:25.0]; - - [statusItem setMenu:menu]; - [statusItem setHighlightMode:YES]; - [statusItem setImage: regularIcon]; - [statusItem setAlternateImage: altIcon]; - launchAtLoginController = [[LaunchAtLoginController alloc] init]; - // Needed to trigger the menuWillOpen event [menu setDelegate:self]; } @@ -357,9 +355,11 @@ - (void) buildMenu:(NSArray*)data addToMenu:(NSMenu *)m { NSString *termTitle = cfg[@"title"]; //Get the value of setting inTerminal NSString *termWindow = cfg[@"inTerminal"]; + //Get the menu name will will use this as the title if title is null. + NSString *menuName = cfg[@"name"]; //Place the terminal command, theme, and title into an comma delimited string - NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@", menuCmd, termTheme, termTitle, termWindow]; + NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@,%@", menuCmd, termTheme, termTitle, termWindow, menuName]; [menuItem setTitle:cfg[@"name"]]; [menuItem setRepresentedObject:menuRepObj]; @@ -384,6 +384,7 @@ - (void) openHost:(NSMenuItem *) sender { //Are commands run in a new tab (default) a new terminal window (new), or in the current tab of the last used window (current). NSString *terminalWindow; + //if for some reason we get a representedObject with only one item... if (objectsFromJSON.count <=1) { escapedObject = [[sender representedObject] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; @@ -402,7 +403,7 @@ - (void) openHost:(NSMenuItem *) sender { } //Check if terminalTitle is null if( [[objectsFromJSON objectAtIndex:2] isEqualToString:@"(null)"]){ - terminalTitle = @""; + terminalTitle = [objectsFromJSON objectAtIndex:4]; }else{ terminalTitle = [objectsFromJSON objectAtIndex:2]; } @@ -422,7 +423,7 @@ - (void) openHost:(NSMenuItem *) sender { //Set Paths to iTerm Nightly AppleScripts NSString *iTerm2NightlyNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-window" ofType:@"scpt"]; NSString *iTerm2NightlyCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-current-window" ofType:@"scpt"]; - NSString *iTerm2StableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-tab-default" ofType:@"scpt"]; + NSString *iTerm2NightlyNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-tab-default" ofType:@"scpt"]; //Set Paths to terminalScripts NSString *terminalNewWindow = [[NSBundle mainBundle] pathForResource:@"terminal-new-window" ofType:@"scpt"]; @@ -473,7 +474,7 @@ - (void) openHost:(NSMenuItem *) sender { [self runScript:iTerm2NightlyCurrentWindow handler:handlerName parameters:passParameters]; }else { //we are using the default action of shuttle, use the active window in a new Tab - [self runScript:iTerm2StableNewTabDefault handler:handlerName parameters:passParameters]; + [self runScript:iTerm2NightlyNewTabDefault handler:handlerName parameters:passParameters]; } } } @@ -601,11 +602,12 @@ - (IBAction)configure:(id)sender { } - (IBAction)showAbout:(id)sender { - - //Call the windows controller + + //Call the windows controller AboutWindowController *aboutWindow = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindowController"]; //Set the window to stay on top + [aboutWindow.window makeKeyAndOrderFront:nil]; [aboutWindow.window setLevel:NSFloatingWindowLevel]; //Show the window diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 75d3eb7..2080b6b 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile shuttle CFBundleIdentifier - shuttle.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.3 + 1.2.4 CFBundleSignature ???? CFBundleVersion - 1.2.3 + 1.2.4 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 7e85b3d..a3fa14d 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -16,9 +16,9 @@ { "cmd": "ps aux | grep foo", "inTerminal": "new", - "name": "My Dev Server", - "theme": "Homebrew", - "title": "Dev Server - SSH" + "name": "look for process foo", + "theme": "Default", + "title": "grep foo" }, { "Spouses Servers": [ From 75cccbe14848e52dd4f8a25ee52112acdb903623 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 23 Oct 2015 13:58:46 -0700 Subject: [PATCH 51/97] feature add open_in - bug fix settings editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a new feature called “open_in” this handles the default global settings for how commands open. Do they open in new tabs or new windows? This setting accepts the value of “tab” or “new” The settings editor had a bug that prevented a terminal based editor like vim from opening. Cleaned up the default JSON file and changed the names to reflect the default action. --- Shuttle/AppDelegate.h | 12 ++-- Shuttle/AppDelegate.m | 132 ++++++++++++++++++++--------------- Shuttle/shuttle.default.json | 31 +++----- 3 files changed, 90 insertions(+), 85 deletions(-) diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index 2a4783a..9c6e8f7 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -15,16 +15,20 @@ NSStatusItem *statusItem; NSString *shuttleConfigFile; - NSString *shuttleJSONPath; // This is for the JSON File NSDate *configModified; NSDate *sshConfigUser; NSDate *sshConfigSystem; - NSString *terminalPref; - NSString *editorPref; - NSString *iTermVersion; + //Global settings Pref in the JSON file. + NSString *shuttleJSONPathPref; //alternate path the JSON file + NSString *terminalPref; //which terminal will we be using iTerm or Terminal.app + NSString *editorPref; //what app opens the JSON fiile vi, nano... + NSString *iTermVersionPref; //which version of iTerm nightly or stable + NSString *openInPref; //by default are commands opened in tabs or new windows. + + //used to gather ssh config settings NSMutableArray* shuttleHosts; NSMutableArray* ignoreHosts; NSMutableArray* ignoreKeywords; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index c1e2eb9..7df3aba 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -11,13 +11,13 @@ @implementation AppDelegate - (void) awakeFromNib { // The location for the JSON path file. This is a simple file that contains the hard path to the *.json settings file. - shuttleJSONPath = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.path"]; + shuttleJSONPathPref = [NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.path"]; //if file shuttle.path exists in ~/.shuttle.path then read this file as it should contain the custom path to *.json - if( [[NSFileManager defaultManager] fileExistsAtPath:shuttleJSONPath] ) { + if( [[NSFileManager defaultManager] fileExistsAtPath:shuttleJSONPathPref] ) { //Read the shuttle.path file which contains the path to the json file - NSString *jsonConfigPath = [NSString stringWithContentsOfFile:shuttleJSONPath encoding:NSUTF8StringEncoding error:NULL]; + NSString *jsonConfigPath = [NSString stringWithContentsOfFile:shuttleJSONPathPref encoding:NSUTF8StringEncoding error:NULL]; //Remove the white space if any. jsonConfigPath = [ jsonConfigPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; @@ -196,7 +196,8 @@ - (void) loadMenu { terminalPref = [json[@"terminal"] lowercaseString]; editorPref = [json[@"editor"] lowercaseString]; - iTermVersion = [json[@"iTerm_version"] lowercaseString]; + iTermVersionPref = [json[@"iTerm_version"] lowercaseString]; + openInPref = [json[@"open_in"] lowercaseString]; launchAtLoginController.launchAtLogin = [json[@"launch_at_login"] boolValue]; shuttleHosts = json[@"hosts"]; ignoreHosts = json[@"ssh_config_ignore_hosts"]; @@ -384,35 +385,39 @@ - (void) openHost:(NSMenuItem *) sender { //Are commands run in a new tab (default) a new terminal window (new), or in the current tab of the last used window (current). NSString *terminalWindow; + escapedObject = [[objectsFromJSON objectAtIndex:0] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - //if for some reason we get a representedObject with only one item... - if (objectsFromJSON.count <=1) { - escapedObject = [[sender representedObject] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - } - else { - escapedObject = [[objectsFromJSON objectAtIndex:0] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - //Check if terminalTheme is null - if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ - if( [terminalPref isEqualToString:@"iterm"] ){ - terminalTheme = @"Default"; + //Check if terminalTheme is null + if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ + if( [terminalPref isEqualToString:@"iterm"] ){ + terminalTheme = @"Default"; }else{ terminalTheme = @"basic"; } - }else { - terminalTheme = [objectsFromJSON objectAtIndex:1]; - } - //Check if terminalTitle is null - if( [[objectsFromJSON objectAtIndex:2] isEqualToString:@"(null)"]){ - terminalTitle = [objectsFromJSON objectAtIndex:4]; - }else{ - terminalTitle = [objectsFromJSON objectAtIndex:2]; - } - //Check if inTerminal is null - if( [[objectsFromJSON objectAtIndex:3] isEqualToString:@"(null)"]){ - terminalWindow = @"default"; //this is not currently used. - }else{ - terminalWindow = [objectsFromJSON objectAtIndex:3]; + }else{ + terminalTheme = [objectsFromJSON objectAtIndex:1]; + } + + //Check if terminalTitle is null + if( [[objectsFromJSON objectAtIndex:2] isEqualToString:@"(null)"]){ + //setting the empty title to that of the menu item. + terminalTitle = [objectsFromJSON objectAtIndex:4]; + }else{ + terminalTitle = [objectsFromJSON objectAtIndex:2]; + } + + //Check if inTerminal is null if so then use the default settings of open_in + if( [[objectsFromJSON objectAtIndex:3] isEqualToString:@"(null)"]){ + //check if open_in is null + if([openInPref isEqualToString:@"(null)"]){ + //open_in was null so we are opening the command the active window in a new tab + openInPref = @"tab"; } + //open_in was not null we are passing the settings. + terminalWindow = openInPref; + }else{ + //inTerminal is not null and overrides the default values of open_in + terminalWindow = [objectsFromJSON objectAtIndex:3]; } //Set Paths to iTerm Stable AppleScripts @@ -445,51 +450,59 @@ - (void) openHost:(NSMenuItem *) sender { //If the JSON file is set to use iTerm else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound ) { - //If the JSON file is set to use applescript via iTermVersion then configure for iTerm Stable - if( [iTermVersion isEqualToString: @"stable"] ) { + //If the JSON prefs for iTermVersion are set to "stable" then configure applescripts for iTerm Stable + if( [iTermVersionPref isEqualToString: @"stable"] ) { //run the applescript that works with iTerm Stable //if we are running in a new iTerm "Stable" Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTermStableNewWindow handler:handlerName parameters:passParameters]; - }else { - //if we are running in the current iTerm "Stable" Window - if ( [terminalWindow isEqualToString:@"current"] ) { - [self runScript:iTermStableCurrentWindow handler:handlerName parameters:passParameters]; - }else { - //we are using the default action of shuttle, use the active window in a new Tab - [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; - } - } + } + + //if we are running in the current iTerm "Stable" Window + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:iTermStableCurrentWindow handler:handlerName parameters:passParameters]; + } + + //we are using the default action of shuttle... The active window in a new tab + if ( [terminalWindow isEqualToString:@"tab"] ) { + [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; + } } //iTermVersion is not set to "stable" using applescripts Configured for Nightly else { //if we are running in a new iTerm "Nightly" Window - if( [terminalWindow isEqualToString:@"new"] ) { + if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTerm2NightlyNewWindow handler:handlerName parameters:passParameters]; - - }else { - //if we are running in the current iTerm "Nightly" Window - if( [terminalWindow isEqualToString:@"current"] ) { - [self runScript:iTerm2NightlyCurrentWindow handler:handlerName parameters:passParameters]; - }else { - //we are using the default action of shuttle, use the active window in a new Tab - [self runScript:iTerm2NightlyNewTabDefault handler:handlerName parameters:passParameters]; - } - } + } + + //if we are running in the current iTerm "Nightly" Window + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:iTerm2NightlyCurrentWindow handler:handlerName parameters:passParameters]; + } + + //we are using the default action of shuttle... The active window in a new tab + if ( [terminalWindow isEqualToString:@"tab"] ) { + [self runScript:iTerm2NightlyNewTabDefault handler:handlerName parameters:passParameters]; + } } } //If JSON file is set to use Terminal.app else { + //if we are running in a new terminal Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:terminalNewWindow handler:handlerName parameters:passParameters]; - }else { - if ( [terminalWindow isEqualToString:@"current"] ) { - [self runScript:terminalCurrentWindow handler:handlerName parameters:passParameters]; - }else { - [self runScript:terminalNewTabDefault handler:handlerName parameters:passParameters]; - } - } + } + + //if we are running in the current terminal Window + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:terminalCurrentWindow handler:handlerName parameters:passParameters]; + } + + //we are using the default action of shuttle... The active window in a new tab + if ( [terminalWindow isEqualToString:@"tab"] ) { + [self runScript:terminalNewTabDefault handler:handlerName parameters:passParameters]; + } } } @@ -590,11 +603,14 @@ - (IBAction)configure:(id)sender { //build the editor command NSString *editorCommand = [NSString stringWithFormat:@"%@ %@", editorPref, shuttleConfigFile]; + //build the reprensented object. It's expecting menuCmd, termTheme, termTitle, termWindow, menuName + NSString *editorRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@,%@", editorCommand, nil, @"Editing shuttle JSON", nil, nil]; + //make a menu item for the command selector(openHost:) runs in a new terminal window. NSMenuItem *editorMenu = [[NSMenuItem alloc] initWithTitle:@"editJSONconfig" action:@selector(openHost:) keyEquivalent:(@"")]; //set the command for the menu item - [editorMenu setRepresentedObject:editorCommand]; + [editorMenu setRepresentedObject:editorRepObj]; //open the JSON file in the terminal editor. [self openHost:editorMenu]; diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index a3fa14d..2a59769 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -6,17 +6,18 @@ "For more information on how to configure, please see http://fitztrev.github.io/shuttle/" ], "editor": "default", - "iTerm_version": "nightly", "launch_at_login": false, - "show_ssh_config_hosts": true, + "show_ssh_config_hosts": false, "ssh_config_ignore_hosts": [ ], "ssh_config_ignore_keywords": [ ], "terminal": "iTerm", + "iTerm_version": "nightly", + "open_in": "tab", "hosts": [ { "cmd": "ps aux | grep foo", "inTerminal": "new", - "name": "look for process foo", + "name": "foo - Opens in a new terminal window", "theme": "Default", "title": "grep foo" }, @@ -25,43 +26,27 @@ { "cmd": "echo '—->WARNING<-- Running a command in this active terminal! Are you sure? y/n'; read sure; if [ $sure == y ]; then echo running command... && tail -f /var/log/messages; else echo exiting...; fi", "inTerminal": "current", - "name": "Look at the Logs" + "name": "Logs - Opens in the current active terminal window" }, { "Jane’s Servers": [ { "cmd": "ssh username@blog2.example.com", - "name": "Wordpress Box" + "name": "WP - Opens in active terminal in a new tab" }, { "cmd": "ssh username@shop1.example.com", - "name": "Shopping Site" + "name": "Shop - Opens in active terminal in a new tab" } ] } ] }, - { - "Work": [ - { - "cmd": "ssh username@dev.example.net -p 4000", - "name": "dev.example.net" - }, - { - "cmd": "ssh username@staging.example.net -p 5000", - "name": "staging.example.net" - }, - { - "cmd": "ssh username@example.net -p 6000", - "name": "production.example.net" - } - ] - }, { "Vagrant": [ { "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key", - "name": "precise32" + "name": "SSH - Opens in active terminal in a new tab" } ] } From c720d6cee3f50759596fa6e2795bf53cf5a04fdb Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 25 Oct 2015 19:07:06 -0700 Subject: [PATCH 52/97] Update README.md Initial readme changes that explain all possible settings. --- README.md | 113 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 41bb3f6..efad8b3 100644 --- a/README.md +++ b/README.md @@ -13,48 +13,123 @@ A simple SSH shortcut menu for OS X 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) 2. Copy to Applications + ## Customization The default, out-of-the-box configuration should be good enough to get started. However, if you're looking to customize the appearance further, here are a few advanced tips. -### Disabling `~/.ssh/config` hosts +### JSON Options +#### Global settings +##### ```"editor": "default",``` +_This changes the app that opens settings.json for editing (Global Setting)_ -By default, Shuttle will parse your `~/.ssh/config` file for hosts. +Valid settings are ```default``` ```nano``` ```vi``` ```vim``` or any terminal based editor. +```default``` opens settings.json in whatever app is registered as the default for extension ```.json``` +``` +"editor": "vim", +``` +would open ```~/.shuttle.json``` in vim -##### To disable all ~/.ssh/config entries: +---- -``` -"show_ssh_config_hosts": false, -``` +##### ```"launch_at_login": false,``` +_This allows you to flag the shuttle.app to start automatically (Global Setting)_ -#### Disable specific hosts: +Valid settings are ```true``` or ```false``` -``` -"ssh_config_ignore_hosts": ["github.com", "git.example.com"], -``` +---- -#### Disable hosts that contain a keyword: +##### ```"terminal": "iTerm",``` +_This allows you to set the default terminal (Global Setting)_ -``` -"ssh_config_ignore_keywords": ["git"], -``` +Valid settings are ```Terminal.app``` or ```iTerm``` + +---- + +##### ```"iTerm_version": "nightly",``` +_This changes the applescripts for iTerm (Global Setting)_ + +Valid settings are ```stable``` or ```nightly``` + +**If the terminal is set to ```iTerm``` this setting is mandatory** + +---- + +##### ```"open_in": "tab",``` +_This changes the default action for how commands are opened (Global Setting)_ + +Valid settings are ```tab``` or ```new```. ```tab``` opens the command in the active terminal in a new tab. ```new``` opens the command in window. This setting can be overwritten b$ + +---- +##### ``````"show_ssh_config_hosts": false,```` +_This changes parsing ssh config. By default, Shuttle will parse your `~/.ssh/config` file for hosts. (Global Setting)_ + +Valid settings are ```false``` or ```true```` + +---- + +##### ```"ssh_config_ignore_hosts": ["github.com", "git.example.com"],``` +_This will ignore hosts in the ssh config. (Global Settings)_ -### Nested menus for `~/.ssh/config` hosts +Valid settings are the hosts in your config that you want to ignore. -#### Create a menu item at "work" > "servers" > "web01" +---- + +##### ```"ssh_config_ignore_keywords": ["git"],``` +_This will ignore keywoards in your ssh config. (Global Settings)_ + +Valid settings are the keywords in your ssh config that you want to ignore. + +---- +***Additional ssh config customization** +##### Nested menus for `~/.ssh/config` hosts + +###### Create a menu item at "work" > "servers" > "web01" ``` Host work/servers/web01 - HostName user@web01.example.com + HostName user@web01.example.com ``` \- *or* - ``` Host gandalf - # shuttle.name = work/servers/web01 (webserver) - HostName user@web01.example.com + # shuttle.name = work/servers/web01 (webserver) + HostName user@web01.example.com ``` +#### Command level settings +#### ```"inTerminal": "new",``` +_This sets how command will open in the terminal window_ + +Valid settings are ```new```, ```tab```, or ```current``` + +```new``` opens the command in a new terminal window. + +```tab``` opens the command in the active terminal window in a new tab. + +```current``` opens the command in the active terminal's window. + +When using using ```current``` I recommend that you wrap the command in some user input like this: + +```echo "are you sure y/n"; read sure; if [ "$sure" == "y" ]; then echo "running command" && ps aux | grep [s]sh; else echo "exiting..."; fi``` + +Do this as a precaution as it could be possible to run a command on the wrong host. +---- + +##### ```"theme": "Default",``` +_This sets the theme for the terminal window_ + +Valid settings are the profile names for iTerm or Terminal.app + +--- + +##### ```"title": "grep foo"``` +_This sets the text that will appear in the terminal's title bar_ + +If ```title``` is missing shuttle uses the menu's name and sets this as ```title``` + + ## Roadmap From 135ab337a5859d268f93dc948664986ec14b698a Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 25 Oct 2015 19:32:05 -0700 Subject: [PATCH 53/97] Update README.md --- README.md | 64 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index efad8b3..88b3fe1 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ The default, out-of-the-box configuration should be good enough to get started. ### JSON Options #### Global settings -##### ```"editor": "default",``` +##### ```"editor": "VALUE",``` _This changes the app that opens settings.json for editing (Global Setting)_ -Valid settings are ```default``` ```nano``` ```vi``` ```vim``` or any terminal based editor. +Possible values are ```default```, ```nano```, ```vi```, ```vim``` or any terminal based editor. ```default``` opens settings.json in whatever app is registered as the default for extension ```.json``` ``` "editor": "vim", @@ -32,56 +32,63 @@ would open ```~/.shuttle.json``` in vim ---- -##### ```"launch_at_login": false,``` +##### ```"launch_at_login": VALUE,``` _This allows you to flag the shuttle.app to start automatically (Global Setting)_ -Valid settings are ```true``` or ```false``` +Possible values are ```true``` or ```false``` ---- -##### ```"terminal": "iTerm",``` +##### ```"terminal": "VALUE",``` _This allows you to set the default terminal (Global Setting)_ -Valid settings are ```Terminal.app``` or ```iTerm``` +Possible values are ```Terminal.app``` or ```iTerm``` ---- -##### ```"iTerm_version": "nightly",``` +##### ```"iTerm_version": "VALUE",``` _This changes the applescripts for iTerm (Global Setting)_ -Valid settings are ```stable``` or ```nightly``` +Possible values are ```stable``` or ```nightly``` -**If the terminal is set to ```iTerm``` this setting is mandatory** +**If ```terminal``` is set to ```iTerm``` this setting is mandatory** ---- -##### ```"open_in": "tab",``` +##### ```"open_in": "VALUE",``` _This changes the default action for how commands are opened (Global Setting)_ -Valid settings are ```tab``` or ```new```. ```tab``` opens the command in the active terminal in a new tab. ```new``` opens the command in window. This setting can be overwritten b$ +Possible values are ```tab``` or ```new```. + +```tab``` opens the command in the active terminal in a new tab. + +```new``` opens the command in window. This setting can be overwritten by the command level ```"inTerminal"``` settings ---- -##### ``````"show_ssh_config_hosts": false,```` -_This changes parsing ssh config. By default, Shuttle will parse your `~/.ssh/config` file for hosts. (Global Setting)_ +##### ```"show_ssh_config_hosts": VALUE,``` +_This changes parsing ssh config. By default, Shuttle will parse your ```~/.ssh/config``` file for hosts. (Global Setting)_ -Valid settings are ```false``` or ```true```` +Possbile values are ```false``` or ```true``` ---- -##### ```"ssh_config_ignore_hosts": ["github.com", "git.example.com"],``` +##### ```"ssh_config_ignore_hosts": ["VALUE", "VALUE"],``` _This will ignore hosts in the ssh config. (Global Settings)_ -Valid settings are the hosts in your config that you want to ignore. +Possible values are the hosts in your config that you want to ignore. If you had github.com and git.example.com in your ssh config, to ignor them you set: + +```"ssh_config_ignore_hosts": ["github.com", "git.example.com"],``` ---- -##### ```"ssh_config_ignore_keywords": ["git"],``` +##### ```"ssh_config_ignore_keywords": ["VALUE"],``` _This will ignore keywoards in your ssh config. (Global Settings)_ -Valid settings are the keywords in your ssh config that you want to ignore. +Possible values are the keywords in your ssh config that you want to ignore. ---- -***Additional ssh config customization** + +**Additional ssh config customization** ##### Nested menus for `~/.ssh/config` hosts ###### Create a menu item at "work" > "servers" > "web01" @@ -99,10 +106,10 @@ Host gandalf ``` #### Command level settings -#### ```"inTerminal": "new",``` +#### ```"inTerminal": "VALUE",``` _This sets how command will open in the terminal window_ -Valid settings are ```new```, ```tab```, or ```current``` +Possible values are ```new```, ```tab```, or ```current``` ```new``` opens the command in a new terminal window. @@ -112,24 +119,27 @@ Valid settings are ```new```, ```tab```, or ```current``` When using using ```current``` I recommend that you wrap the command in some user input like this: -```echo "are you sure y/n"; read sure; if [ "$sure" == "y" ]; then echo "running command" && ps aux | grep [s]sh; else echo "exiting..."; fi``` +``` +echo "are you sure y/n"; read sure; if [ "$sure" == "y" ]; then echo "running command" && ps aux | grep [s]sh; else echo "exiting..."; fi +``` Do this as a precaution as it could be possible to run a command on the wrong host. + ---- -##### ```"theme": "Default",``` +##### ```"theme": "VALUE",``` _This sets the theme for the terminal window_ -Valid settings are the profile names for iTerm or Terminal.app +Possbile values are the profile names for iTerm or Terminal.app --- -##### ```"title": "grep foo"``` +##### ```"title": "VALUE"``` _This sets the text that will appear in the terminal's title bar_ -If ```title``` is missing shuttle uses the menu's name and sets this as ```title``` - +Where VALUE is the text you want to set in the terminals title bar. +If ```title``` is missing shuttle uses the menu's name and sets this as ```title``` ## Roadmap From ced6f16e2486db9744885e3ccf26206cd4c126a0 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 29 Oct 2015 20:10:09 -0700 Subject: [PATCH 54/97] Show alert boxes on some errors. Set Global Theme If iTerm_version is missing then show the user an error. if iTerm_version has a bad value show the user that value. If inTerminal has a bad value show the user that value. --- Shuttle/AppDelegate.h | 1 + Shuttle/AppDelegate.m | 94 +++++++++++++++++++++++++++--------- Shuttle/shuttle.default.json | 49 +++++++------------ 3 files changed, 90 insertions(+), 54 deletions(-) diff --git a/Shuttle/AppDelegate.h b/Shuttle/AppDelegate.h index 9c6e8f7..916084c 100644 --- a/Shuttle/AppDelegate.h +++ b/Shuttle/AppDelegate.h @@ -27,6 +27,7 @@ NSString *editorPref; //what app opens the JSON fiile vi, nano... NSString *iTermVersionPref; //which version of iTerm nightly or stable NSString *openInPref; //by default are commands opened in tabs or new windows. + NSString *themePref; //The global theme. //used to gather ssh config settings NSMutableArray* shuttleHosts; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 7df3aba..af7acee 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -198,6 +198,7 @@ - (void) loadMenu { editorPref = [json[@"editor"] lowercaseString]; iTermVersionPref = [json[@"iTerm_version"] lowercaseString]; openInPref = [json[@"open_in"] lowercaseString]; + themePref = json[@"default_theme"]; launchAtLoginController.launchAtLogin = [json[@"launch_at_login"] boolValue]; shuttleHosts = json[@"hosts"]; ignoreHosts = json[@"ssh_config_ignore_hosts"]; @@ -373,6 +374,10 @@ - (void) openHost:(NSMenuItem *) sender { //NSLog(@"sender: %@", sender); //NSLog(@"Command: %@",[sender representedObject]); + NSString *errorMessage; + NSString *errorInfo; + + //Place the comma delimited string of menu item settings into an array NSArray *objectsFromJSON = [[sender representedObject] componentsSeparatedByString:(@",")]; @@ -387,13 +392,21 @@ - (void) openHost:(NSMenuItem *) sender { escapedObject = [[objectsFromJSON objectAtIndex:0] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; - //Check if terminalTheme is null + //if terminalTheme is not set then check for a global setting. if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ - if( [terminalPref isEqualToString:@"iterm"] ){ - terminalTheme = @"Default"; - }else{ - terminalTheme = @"basic"; - } + if(themePref == 0) { + if( [terminalPref isEqualToString:@"iterm"] ){ + //we have no global theme and there is no theme in the command settings. + //Forcing the Default profile for iTerm and the basic profile for Terminal.app + terminalTheme = @"Default"; + }else{ + terminalTheme = @"basic"; + } + //We have a global setting using this as the theme. + }else { + terminalTheme = themePref; + } + //we have command level theme override the Global default_theme settings. }else{ terminalTheme = [objectsFromJSON objectAtIndex:1]; } @@ -408,16 +421,22 @@ - (void) openHost:(NSMenuItem *) sender { //Check if inTerminal is null if so then use the default settings of open_in if( [[objectsFromJSON objectAtIndex:3] isEqualToString:@"(null)"]){ - //check if open_in is null - if([openInPref isEqualToString:@"(null)"]){ - //open_in was null so we are opening the command the active window in a new tab + + //if open_in is not "tab" or "new" then force the default of "tab". + if( ![openInPref isEqualToString:@"tab"] && ![openInPref isEqualToString:@"new"]){ openInPref = @"tab"; } - //open_in was not null we are passing the settings. + //open_in was not empty or bad value we are passing the settings. terminalWindow = openInPref; }else{ //inTerminal is not null and overrides the default values of open_in terminalWindow = [objectsFromJSON objectAtIndex:3]; + if( ![terminalWindow isEqualToString:@"new"] && ![terminalWindow isEqualToString:@"current"] && ![terminalWindow isEqualToString:@"tab"]) + { + errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",terminalWindow,@"'", @"is not a valid value for inTerminal. Please fix this in the JSON file"]; + errorInfo = @"bad \"inTerminal\":\"VALUE\" in the JSON settings"; + [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:NO]; + } } //Set Paths to iTerm Stable AppleScripts @@ -450,44 +469,55 @@ - (void) openHost:(NSMenuItem *) sender { //If the JSON file is set to use iTerm else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound ) { - //If the JSON prefs for iTermVersion are set to "stable" then configure applescripts for iTerm Stable - if( [iTermVersionPref isEqualToString: @"stable"] ) { - //run the applescript that works with iTerm Stable - + //If the JSON prefs for iTermVersion are not stable or nightly throw an error + if( ![iTermVersionPref isEqualToString: @"stable"] && ![iTermVersionPref isEqualToString:@"nightly"] ) { + + if( iTermVersionPref == 0 ) { + errorMessage = @"\"iTerm_version\": \"VALUE\", is missing.\n\"VALUE\" can be \"stable\" or \"nightly\"\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help."; + errorInfo = @"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have iTerm nightly installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle."; + [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:YES]; + iTermVersionPref = @"stable"; + + }else{ + errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",iTermVersionPref,@"'", @"is not a valid value for iTerm_version. Please fix this in the JSON file"]; + errorInfo = @"bad \"iTerm_version\": \"VALUE\" in the JSON settings"; + [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:NO]; + } + } + + if( [iTermVersionPref isEqualToString:@"stable"]) { + + //run the applescript that works with iTerm Stable //if we are running in a new iTerm "Stable" Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTermStableNewWindow handler:handlerName parameters:passParameters]; } - //if we are running in the current iTerm "Stable" Window if ( [terminalWindow isEqualToString:@"current"] ) { [self runScript:iTermStableCurrentWindow handler:handlerName parameters:passParameters]; } - //we are using the default action of shuttle... The active window in a new tab if ( [terminalWindow isEqualToString:@"tab"] ) { - [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; + [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; } } //iTermVersion is not set to "stable" using applescripts Configured for Nightly - else { + if( [iTermVersionPref isEqualToString:@"nightly"]) { //if we are running in a new iTerm "Nightly" Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTerm2NightlyNewWindow handler:handlerName parameters:passParameters]; } - //if we are running in the current iTerm "Nightly" Window if ( [terminalWindow isEqualToString:@"current"] ) { [self runScript:iTerm2NightlyCurrentWindow handler:handlerName parameters:passParameters]; } - //we are using the default action of shuttle... The active window in a new tab if ( [terminalWindow isEqualToString:@"tab"] ) { [self runScript:iTerm2NightlyNewTabDefault handler:handlerName parameters:passParameters]; } } } - //If JSON file is set to use Terminal.app + //If JSON settings are set to use Terminal.app else { //if we are running in a new terminal Window if ( [terminalWindow isEqualToString:@"new"] ) { @@ -506,8 +536,7 @@ - (void) openHost:(NSMenuItem *) sender { } } -- (void) runScript:(NSString *)scriptPath handler:(NSString*)handlerName parameters:(NSArray*)parametersInArray -{ +- (void) runScript:(NSString *)scriptPath handler:(NSString*)handlerName parameters:(NSArray*)parametersInArray { //special thanks to stackoverflow.com/users/316866/leandro for pointing me the right direction. //see http://goo.gl/olcpaX NSAppleScript * appleScript; @@ -581,6 +610,25 @@ - (IBAction)showImportPanel:(id)sender { } +-(void) throwError:(NSString*)errorMessage additionalInfo:(NSString*)errorInfo continueOnErrorOption:(BOOL)continueOption { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setInformativeText:errorInfo]; + [alert setMessageText:errorMessage]; + [alert setAlertStyle:NSWarningAlertStyle]; + + if (continueOption) { + [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:@"Continue"]; + + }else{ + [alert addButtonWithTitle:@"Quit"]; + } + + if ([alert runModal] == NSAlertFirstButtonReturn) { + [NSApp terminate:NSApp]; + } +} + - (IBAction)showExportPanel:(id)sender { NSSavePanel * savePanelObj = [NSSavePanel savePanel]; //Display the Save Panel diff --git a/Shuttle/shuttle.default.json b/Shuttle/shuttle.default.json index 2a59769..5259821 100644 --- a/Shuttle/shuttle.default.json +++ b/Shuttle/shuttle.default.json @@ -7,24 +7,22 @@ ], "editor": "default", "launch_at_login": false, + "terminal": "Terminal.app", + "iTerm_version": "nightly", + "default_theme": "Homebrew", + "open_in": "new", "show_ssh_config_hosts": false, "ssh_config_ignore_hosts": [ ], "ssh_config_ignore_keywords": [ ], - "terminal": "iTerm", - "iTerm_version": "nightly", - "open_in": "tab", "hosts": [ { - "cmd": "ps aux | grep foo", - "inTerminal": "new", - "name": "foo - Opens in a new terminal window", - "theme": "Default", - "title": "grep foo" + "cmd": "ps aux | grep defaults", + "name": "Grep - Opens in Default-window-theme-title" }, { "Spouses Servers": [ { - "cmd": "echo '—->WARNING<-- Running a command in this active terminal! Are you sure? y/n'; read sure; if [ $sure == y ]; then echo running command... && tail -f /var/log/messages; else echo exiting...; fi", + "cmd": "echo '—->WARNING<-- Running a command in this active terminal! Are you sure? y/n'; read sure; if [ $sure == y ]; then echo running command... && tail -f /var/log/current; else echo exiting...; fi", "inTerminal": "current", "name": "Logs - Opens in the current active terminal window" }, @@ -32,11 +30,17 @@ "Jane’s Servers": [ { "cmd": "ssh username@blog2.example.com", - "name": "WP - Opens in active terminal in a new tab" + "inTerminal": "tab", + "name": "SSH blog - Opens in Tab of active window", + "theme": "basic", + "title": "title of tab" }, - { + { "cmd": "ssh username@shop1.example.com", - "name": "Shop - Opens in active terminal in a new tab" + "inTerminal": "new", + "name": "SSH Shop - Opens in New Window", + "theme": "basic", + "title": "title of new window" } ] } @@ -46,26 +50,9 @@ "Vagrant": [ { "cmd": "ssh vagrant@127.0.0.1 -p 2222 -i ~/.vagrant.d/insecure_private_key", - "name": "SSH - Opens in active terminal in a new tab" + "name": "Vagrant - Opens in default-window-theme-title" } ] } ] -} - - - - - - - - - - - - - - - - - \ No newline at end of file +} \ No newline at end of file From 47b6403e76366803eb5de458bf967b67d0f57aec Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 30 Oct 2015 12:19:30 -0700 Subject: [PATCH 55/97] added ```default_theme``` added ```default_theme``` and fixed some spelling. --- README.md | 100 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 88b3fe1..d172448 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,16 @@ A simple SSH shortcut menu for OS X ![How Shuttle works](https://raw.github.com/fitztrev/shuttle/gh-pages/img/how-shuttle-works.gif) -***Sidenote***: *Many people ask, so here's how I have [my terminal setup](https://github.com/fitztrev/shuttle/wiki/My-Terminal-Prompt).* +**Sidenote**: *Many people ask, so here's how I have [my terminal setup](https://github.com/fitztrev/shuttle/wiki/My-Terminal-Prompt).* ## Installation 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) 2. Copy to Applications - -## Customization - -The default, out-of-the-box configuration should be good enough to get started. However, if you're looking to customize the appearance further, here are a few advanced tips. - -### JSON Options -#### Global settings -##### ```"editor": "VALUE",``` +## JSON Options +### Global settings +#### ```"editor": "VALUE",``` _This changes the app that opens settings.json for editing (Global Setting)_ Possible values are ```default```, ```nano```, ```vi```, ```vim``` or any terminal based editor. @@ -32,66 +27,84 @@ would open ```~/.shuttle.json``` in vim ---- -##### ```"launch_at_login": VALUE,``` +#### ```"launch_at_login": VALUE,``` _This allows you to flag the shuttle.app to start automatically (Global Setting)_ Possible values are ```true``` or ```false``` ---- -##### ```"terminal": "VALUE",``` +#### ```"terminal": "VALUE",``` _This allows you to set the default terminal (Global Setting)_ Possible values are ```Terminal.app``` or ```iTerm``` ---- -##### ```"iTerm_version": "VALUE",``` +#### ```"iTerm_version": "VALUE",``` _This changes the applescripts for iTerm (Global Setting)_ Possible values are ```stable``` or ```nightly``` **If ```terminal``` is set to ```iTerm``` this setting is mandatory** +_This setting is ignored if your terminal is set to ```Terminal.app```_ + +---- + +#### ```"default_theme": "Homebrew",``` +_This sets the Terminal theme for all windows. (Global Setting)_ + +Possible values are the Profile names in your terminal preferences. iTerm ships with one Profile named "Default". OS X Terminal ships with several. To see the names see the preferences area of the terminal you are using. + +In iTerm the profile names are case sensitive. + +**Please ensure the theme names you set are valid. If shuttle passes theme "Dagobah" and it does not exist in iTerm or OS X Terminal then your command won't run. This is because the applescripts are not making any checks to see if the theme you passed actually exists within the terminal application.** + +This setting can be overwritten by the command level "theme" settings + ---- -##### ```"open_in": "VALUE",``` +#### ```"open_in": "VALUE",``` _This changes the default action for how commands are opened (Global Setting)_ Possible values are ```tab``` or ```new```. ```tab``` opens the command in the active terminal in a new tab. -```new``` opens the command in window. This setting can be overwritten by the command level ```"inTerminal"``` settings +```new``` opens the command in a new window. + +This setting can be overwritten by the command level ```"inTerminal"``` settings ---- -##### ```"show_ssh_config_hosts": VALUE,``` + +#### ```"show_ssh_config_hosts": VALUE,``` _This changes parsing ssh config. By default, Shuttle will parse your ```~/.ssh/config``` file for hosts. (Global Setting)_ -Possbile values are ```false``` or ```true``` +Possible values are ```false``` or ```true``` ---- -##### ```"ssh_config_ignore_hosts": ["VALUE", "VALUE"],``` -_This will ignore hosts in the ssh config. (Global Settings)_ +#### ```"ssh_config_ignore_hosts": ["VALUE", "VALUE"],``` +_This will ignore hosts in the ssh config. (Global Setting)_ -Possible values are the hosts in your config that you want to ignore. If you had github.com and git.example.com in your ssh config, to ignor them you set: +Possible values are the hosts in your config that you want to ignore. If you had github.com and git.example.com in your ssh config, to ignore them you set: ```"ssh_config_ignore_hosts": ["github.com", "git.example.com"],``` ---- -##### ```"ssh_config_ignore_keywords": ["VALUE"],``` -_This will ignore keywoards in your ssh config. (Global Settings)_ +#### ```"ssh_config_ignore_keywords": ["VALUE"],``` +_This will ignore keywords in your ssh config. (Global Setting)_ Possible values are the keywords in your ssh config that you want to ignore. ---- **Additional ssh config customization** -##### Nested menus for `~/.ssh/config` hosts +#### Nested menus for `~/.ssh/config` hosts -###### Create a menu item at "work" > "servers" > "web01" +##### Create a menu item at "work" > "servers" > "web01" ``` Host work/servers/web01 @@ -105,9 +118,34 @@ Host gandalf HostName user@web01.example.com ``` -#### Command level settings +### Command level settings +_Command level settings unique to your command and will overwrite the Global setting equivalent_ + +#### ```"cmd": "VALUE"``` +_This is the command / script that will be launched in the terminal. (Command setting)_ + +Where Value is a command or script. +``` +"cmd": "ps aux | grep [s]sh" +``` +Would check for ssh processes. + +---- + +#### ```"name": "VALUE"``` +_This sets the text that will appear in shuttles drop down menu. (Command setting)_ + +Were Value is the text you want to see in the drop down menu for this command. +``` +"name": "SSH to my wordpress blog" +``` + +This value can also set the title of the terminal window if ```"title" :"VALUE"``` is not set. + +---- + #### ```"inTerminal": "VALUE",``` -_This sets how command will open in the terminal window_ +_This sets how command will open in the terminal window. (Command setting)_ Possible values are ```new```, ```tab```, or ```current``` @@ -127,15 +165,15 @@ Do this as a precaution as it could be possible to run a command on the wrong ho ---- -##### ```"theme": "VALUE",``` -_This sets the theme for the terminal window_ +#### ```"theme": "VALUE",``` +_This sets the theme for the terminal window. (Command setting)_ -Possbile values are the profile names for iTerm or Terminal.app +Possible values are the profile names for iTerm or OS X Terminal. ---- +---- -##### ```"title": "VALUE"``` -_This sets the text that will appear in the terminal's title bar_ +#### ```"title": "VALUE"``` +_This sets the text that will appear in the terminal's title bar. (Command setting)_ Where VALUE is the text you want to set in the terminals title bar. From 977fb32bcfc1e9779304027c9b6c6e48a577a41f Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 30 Oct 2015 12:28:14 -0700 Subject: [PATCH 56/97] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d172448..5ad0ff9 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ In iTerm the profile names are case sensitive. **Please ensure the theme names you set are valid. If shuttle passes theme "Dagobah" and it does not exist in iTerm or OS X Terminal then your command won't run. This is because the applescripts are not making any checks to see if the theme you passed actually exists within the terminal application.** -This setting can be overwritten by the command level "theme" settings +This setting can be overwritten by the command level ```"theme"``` settings ---- @@ -119,7 +119,7 @@ Host gandalf ``` ### Command level settings -_Command level settings unique to your command and will overwrite the Global setting equivalent_ +_Command level settings are unique to your command and will overwrite the Global setting equivalent_ #### ```"cmd": "VALUE"``` _This is the command / script that will be launched in the terminal. (Command setting)_ @@ -170,6 +170,8 @@ _This sets the theme for the terminal window. (Command setting)_ Possible values are the profile names for iTerm or OS X Terminal. +If ```"theme"``` is not set and ```"default_theme"``` is not set then shuttle passes Profile ```Default``` for iTerm and Profile ```basic``` for OS X terminal. + ---- #### ```"title": "VALUE"``` From ddbd10e5c4df3e86eba1a3a70305b704796ae29e Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 30 Oct 2015 12:58:04 -0700 Subject: [PATCH 57/97] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 5ad0ff9..4535a19 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,16 @@ A simple SSH shortcut menu for OS X 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) 2. Copy to Applications +## JSON Path Change + +In your home directory create a file called ```~/.shuttle.path``` +In this file should be a single line with the path to the JSON settings file. + +``` +/Users/thshdw/Dropbox/shuttle/shuttle.json +``` +shuttle will read ```~/.shuttle.path``` first and use its contents as the path to your JSON file. + ## JSON Options ### Global settings #### ```"editor": "VALUE",``` From addb62ae097af8b8ed27dbed0cea1e8d6831723c Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 30 Oct 2015 14:16:17 -0700 Subject: [PATCH 58/97] v1.2.5 Build v1.2.5 --- Shuttle/Shuttle-Info.plist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 2080b6b..df43b6f 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.4 + 1.2.5 CFBundleSignature ???? CFBundleVersion - 1.2.4 + 1.2.5 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion From 3c3eef8ca3b2c18d605809da19202810d5b89d31 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Sat, 14 Nov 2015 07:16:30 +0000 Subject: [PATCH 59/97] Add Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4535a19..b08204d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Shuttle +[![Join the chat at https://gitter.im/fitztrev/shuttle](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fitztrev/shuttle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + A simple SSH shortcut menu for OS X [http://fitztrev.github.io/shuttle/](http://fitztrev.github.io/shuttle/) From adbb645da29b1cb791a1b35124a20dbebc9b25cc Mon Sep 17 00:00:00 2001 From: keesfransen Date: Mon, 14 Dec 2015 16:04:59 +1300 Subject: [PATCH 60/97] modifies parseSSHConfigFile to only keep first host alias ssh_config allows multiple patterns on the Host argument, which Shuttle parsed into the menu and then passed on to SSH when activated. Allows for hosts defined like: Host prod/host host.prod HostName myserver.local --- Shuttle/AppDelegate.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index af7acee..3a11ff9 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -158,7 +158,11 @@ - (NSDictionary*) parseSSHConfigFile { if ([first isEqualToString:@"Host"]) { // a new host section - key = second; + + // split multiple aliases on space and only save the first + NSArray* hostAliases = [second componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + hostAliases = [hostAliases filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF != ''"]]; + key = [hostAliases firstObject]; servers[key] = [[NSMutableDictionary alloc] init]; } } From 0e53645e91da5b3f722e4b92074ac26c9c47c7b3 Mon Sep 17 00:00:00 2001 From: Morton Fox Date: Fri, 8 Jan 2016 17:08:31 -0500 Subject: [PATCH 61/97] Handle situation where iTerm is running but has no windows. --- .../iTerm-stable-new-tab-default.scpt | Bin 1780 -> 1884 bytes .../iTerm-stable-new-tab-default.applescript | 10 ++++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt b/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt index bd4dd006ef8a53a6fdff6846db6b20b15924898b..f6ab136b4090352471d9aa7f30b6a098ad5651d8 100644 GIT binary patch delta 745 zcmZ9JO-NKx6vzKF?+r6O$8qLmCCJcV(&A+*K`q>5r7=SGLL5w7yqg8v{-BBwy!mH=3tYIY%L???fT8Fn0LN<{G%4p557Q$_-aL}aDUqo3r zXb=)A;inZ^rX^aW1)8Thnxz?!L|bq)@?*l?=sCc(e=ld3i_JdQv!^wMpPLWtn0TQ%Kh`$N9U=ID!mp}N ztX&WtmVIjuZJVy7({WSRvSuooX@cpDUazIo_7}1G)JeWWI}BLRk>fmo2%>1Qx5cwP zD-fl$l}k#r%<5X!O1NkVaML0n(E{)FrS!N&^Q>-hRiZgm)$1wC&@Cgba1<{zh-5C# z+^oZ8GVC{FE1s}x+Kzg{r>D3lVP83%s$%X99f()TpStU@ST}AI0S!S0L#m=d?rUIqTrJsuRNu&`J+yx<q^L_Vx2X4pz!}s#7)#IQ&>d^=4 z(ypjm=d|!7q=H^Gb<@li3j+sVWHEER^v_ka&yq3Ez@yd2pyt9pnU$&4t0V^yvHMzjuu(wfpe=vIzK4 z(5tkYQ?qBq9s8-{jjD2!wGdoDbL!c1W((|nFAs_IXLRhx#^J{yxRFH)It=8IfQAyE z0Kc Date: Wed, 24 Feb 2016 01:53:59 -0700 Subject: [PATCH 62/97] applescript fixes for iTerm Stable, Nightly and Terminal iTerm Stable and Terminal apple scripts were not correctly handling events where the app was open but no windows were open. Fixed an issue were iTerm Nightly applescripts would not open if a theme was not set. Added the script files that compile the applescripts files for inclusion in shuttle.app Corrected an issue where shuttle would not open file system commands. --- Shuttle/AppDelegate.m | 2 +- Shuttle/Shuttle-Info.plist | 4 +- .../iTerm-stable-current-window.scpt | Bin 1132 -> 1158 bytes .../iTerm-stable-new-tab-default.scpt | Bin 1884 -> 2022 bytes .../apple-scpt/iTerm-stable-new-window.scpt | Bin 2414 -> 2468 bytes .../iTerm2-nightly-current-window.scpt | Bin 1082 -> 1108 bytes .../iTerm2-nightly-new-tab-default.scpt | Bin 1754 -> 1934 bytes .../apple-scpt/iTerm2-nightly-new-window.scpt | Bin 1562 -> 1646 bytes .../apple-scpt/terminal-current-window.scpt | Bin 1094 -> 1120 bytes .../apple-scpt/terminal-new-tab-default.scpt | Bin 2960 -> 2422 bytes Shuttle/apple-scpt/terminal-new-window.scpt | Bin 1472 -> 1498 bytes apple-scripts/compile-Terminal.sh | 6 ++ apple-scripts/compile-iTermNightly.sh | 7 ++ apple-scripts/compile-iTermStable.sh | 7 ++ .../iTerm2-nightly-current-window.applescript | 2 +- ...iTerm2-nightly-new-tab-default.applescript | 25 +++++-- .../iTerm2-nightly-new-window.applescript | 11 +-- .../iTerm-stable-current-window.applescript | 2 +- .../iTerm-stable-new-tab-default.applescript | 64 ++++++++++++------ .../iTerm-stable-new-window.applescript | 5 +- .../terminal-current-window.applescript | 2 +- .../terminal-new-tab-default.applescript | 55 +++++++++------ 22 files changed, 133 insertions(+), 59 deletions(-) create mode 100755 apple-scripts/compile-Terminal.sh create mode 100755 apple-scripts/compile-iTermNightly.sh create mode 100755 apple-scripts/compile-iTermStable.sh diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 3a11ff9..aa9a77b 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -465,7 +465,7 @@ - (void) openHost:(NSMenuItem *) sender { NSArray *passParameters = @[escapedObject, terminalTheme, terminalTitle]; // Check if Url - NSURL* url = [NSURL URLWithString:[sender representedObject]]; + NSURL* url = [NSURL URLWithString:escapedObject]; if(url) { [[NSWorkspace sharedWorkspace] openURL:url]; diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index df43b6f..078ef53 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.5 + 1.2.6 CFBundleSignature ???? CFBundleVersion - 1.2.5 + 1.2.6 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/Shuttle/apple-scpt/iTerm-stable-current-window.scpt b/Shuttle/apple-scpt/iTerm-stable-current-window.scpt index c99b82fa0c6d1638ef86a8c014bf1cc5d528d386..e74a33882c9e27145aaa3759e5eaf5dd9154b1db 100644 GIT binary patch delta 224 zcmaFE(Z;#qFC)`3j>(Kn0S*ido*_ZL3`~p+i8-0Y3_!r>!NA18$m5%soS9dWU!0-f z;Q|sy0vCh!$9QN11-f$PP3~uMU}3DTyfFDPlUe=6V8t^)aX|(S28K)KtJVP7j0`Lw zbwD#!kd3r*EGWpyOinDx%+D*fV#o|hEy~qPEGS^$V-R8BWXNO)VMt{tV#sCCV@PBu r078BSDFz+}Um!0TD4WMn!jR8U%#gvLz~I5)!XU&T1~hPT9`io{p3F4+ delta 151 zcmZqUe8aKfFC$YA$7DvP02u}b&yXNr1|~*^#GK4x1|VS6V_;%nWcCk^04k8+xlnfe zwTCuP{F-~-#p}QDMFJ?>OF$5Z&tSOvG3vMoc}-nIp6=ytOhoM&l}w5b#27b zvID4Q(9J-kPpEByz|_=CGi~T4+_jBGT18z8#F4aq*%(ocA>AsIc87@c(cbT_IU0h1 zJ@C;7T7jRIX^9qTf#zwB3Y4c=nxSc$qDh*d_Xq^xbB%fJad(IP)?ID?c88l1sDR6t zRMfO?8D?*N;xu+vzVpq+#9`jMnYm-1@SLi?c6GMUnrO3cc#a&(cO97IqcFTcq4mjA z#x8gqDcd&ub+uPK$7<@}LlO#9q@lxt0TaD;edTefws|0Js)}k2>qD6g(RC2rf`h&A z!+Y}GZKp;BTDE(tqT&)fg35CdXwlA9?b|ER0`K%2YDS=WM(3>TfIxG0t}-eX9B!Jd z&D)PFqq$k;>Fz6*Kr^UqKHJ@y?8#bMITi@PxYSoK&@}4?b%lMVSf?6UP1P+;;@X~0 z2o4r#l6CE+yb0DR+OU;X1_#T2;%X`9TB?Qt=)$G5%717(tD+ zb(fi6zAyb~JR|e<94b<&3mBEwoA`1t{z{ej!Cim7J#FY^CdTYL5g?=KZ9 delta 673 zcmZ9J%WD%+6vn^lOq+O<<}s#HkkC@=W{g%8wXVcRqy^)MG_>Ms97jl-G+~mc-Kc+o z2p17`Q!TnHC|02x!KH0Y-w(BxDj2F%-FI_5u?D4Qary2&zw@2Lx#i$;;90BMY!}^v z<7VJt(Z#|?pWxXKwUEgHK3bMX)Tr`NHmMBs;RPnYH@;@AK&zl^+w#{{{?rbKckrAEIB;R3$mf7O zTH9`F;$*?iD6|CB&>|pc0h;K`+G#=atj=*(&>R}~*;&`IT_s#b{lzqMvlT9% z?-w*9qjk}yX{IgbYQeQ~xe7sa3q)0VO6F>J)(hT@CQ@D33jx&2>AFa}pc2=0cAZaj z^X{v4KnU4Oz0HDmsEJ&^mGL3~@3FV{EAc?kgnU>Vy*bWF`&x=e7^{w&&gQq8gsF_@ zt2%~_&oKv4Tr|GK;xEhyyhHJkZiITELNmUW(|&mPeMrY4W9<>&w#8qX##*XP$1bDS z4`EhLpJ}ZB;AFrwQtN|%#AD-E>~u1D0cSP=3n2!>reQ(vYh$_HtlGaapXzA+Xa7V; YgYm!3W{+et8MN}Y%fIU1SXmkV4G2Esng9R* diff --git a/Shuttle/apple-scpt/iTerm-stable-new-window.scpt b/Shuttle/apple-scpt/iTerm-stable-new-window.scpt index a6c7e3cd439e1b2c485861cc954db6d2601b477c..d118d05c0d6ee5c93936ea317f6bea188ce80a3c 100644 GIT binary patch delta 483 zcmaDSv_yD=Ei=(S90S*ido*_ZL3`~p+i8-0Y3_!r>!NA18$m5%soS9dWU!0-f z;Q|sy0vCh!$9QN11-f$PO+L@;z`|Hvd110Ji&_1}V8t^)aX|(S28K)KtJVP7j0`Lw zbwD#!kd3r*EGWpyOinDx%+D*fV#o|hEy~qPEGS^$V-R8BWXNO)VMt{tV#sCCV@PBu z078BSDFz+}Um!0TD4WMn!jR8U%#gvLz~I5)!XU&T1~hQ;I+k6^h7W`oRJ0!Iq%z7f nNNZ*BFfjQ)29hlPT2Hg}febePfX$t(DU8E11UAQU2rvQwhO=)i delta 264 zcmZ1?{7z_tEi+RO$7Dz5fXUJ9!kl^xObm?7{=pGI5ec3PWyfE8XfrS{TyxKxe4g2X zh2cTj^~v|y%@i+`YefUa1sOOP7%rKwS_5P=GO&Qu0S%A_>9^;QoZQVK&B?*Q%;3)u z%n&j8Aj?5T{Rct}N?H$fQW<3#B(<`57?}JY14$Ntt*6=gKnC09rK~B8^mCEgb%7 diff --git a/Shuttle/apple-scpt/iTerm2-nightly-current-window.scpt b/Shuttle/apple-scpt/iTerm2-nightly-current-window.scpt index aeebfe15e0abef0f6cce214f42be02d97ab91768..b9d07158e694895a9d1f9aa813e2faf6cc67a0f3 100644 GIT binary patch delta 188 zcmdnRafM^UZ$_qN9FrND{3Z)9u}t=764dozU}9k8@l8z5%qz(+&QS1hVL$~JgZ9UG zXamK%a^_9$WpdzPtWNuQ>0{7=$yb?-oGu0{o&m}UGH@_3T>7}x0LW%!U;(KGnyZ3r ztd(OyK~83JVo7FxUa=KJW=LvLu3ln6!DL5fX=y129tK~AM22LBOolv$5{7(+VulO` N1qKfWm&vKjR{@;UGK&BJ delta 122 zcmcb@v5RBFZ$_pbj>(Knev<{5SSI^333BQ&FflMP`v*q=MJ0GHlpTNVq0PX+aLqk$ zaxaqu2g8HYzLzZapO}1=$w=`+xmGk#PLP3vf#H&+k_C{>$iMMNL|vWmmpMUL0cp#WQ=HHgw^W!{o# zl~p=ZWz}L;w#=eLO;*Y50{d35cj%N|DON3e!I~=+h!(k7pfo)jk!YEHXYKj2M8cA@ zxofbdK<#7(Gz9+5Y_NN@Voxi46>JJ>2>xx-=mWmL4d9!;9nA^{FvWU6pOxV<-tj0J z`umGVSnN(2{AD-0hE_M*ZXiL;rfSAVW>1 zQd77dLXrVzcF=aA{Iwga3kck6buDZkRF;i)bOrGvE!Q5lL b#*{9{M5x!<{|G9~YNo<}LjRAgt-C(~pnuES delta 532 zcmZ{h!A}!W5XQfDH*I-cSXv1|(L_-~@~o8@4{#%C6q~5;P&U+fYMW}ncB$Lda+5!R zaM?cq%AG`GJQ9zX7@!oWib@~`A`;?V591ULa4|2L{4(=>Gn08U=2q+Li&`?=k6_7} zcrsSV17Z4(U_Z2C`6VE*;*x4xlk?dw;c|5OzL3s1LmV;3f21S85gcm}?P) zfcjw10J9HzQ_D@dn>}Xr^ViUZ4zyqlBgn%+5piTt;%Bl?gF*^v??_)v32HzW38!KU z+GHBdm66QGn%)8S*-t9#3M()P)zDr0lcgk@Ce)WT+uH@dcn+yR=>Px# diff --git a/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt b/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt index 0d7208de551b3b341e8dce728c2b1809bd105524..d96457f4d1cf51aa4c6bae6c099fd308b6b21e4e 100644 GIT binary patch delta 402 zcmY+9OD_Xq6o%h1)2Q(=Dq_I`iH*9X!K$mGk#3qsX;_e$Op1|qI_XqM`~xe=w-afw zu#=FmBm4lHR^2Z@z%A+4<~TOOIXNfKxhC(H_!Nh2>TqocYSvo>nxJzCdZs5d&?7z2 zJ>AhQ-Jp@KsX|v&rjh}n?EB?}8kh5`PZrfce+)HHMBKD&r;ziOI%84%w^H(aHrC6% zjZ9HKsS&@jzECY!lNYjE8yqc7c3tu;06$Q!o`jecXg|CBxBKtg@QCYXY|AXzImZjb zo=WGl1E%ZBO|8AX4FYkPuwcVM0XcYBLMLK~%46+prk-x`E47A2p^;rVf)Rkh6h#^f ze7}MW3OZdw)5r3wj!qS(Y0G1}Vsafbt5cb2D!s;gCAsbEQ0C;RuO~9p0)2N`pmrK! shUL_~{uGJeTVFF;gYTS;z2HaRcp~_@lMygdsgz-QR-W~Wua}pDZ?_M7Jpcdz delta 295 zcmaFIGmB?~6tfYx0RwwMQGQxxPAUU~00S2TF9Xm2yZ`Sn@czI3|JMJT|8M-i{{Pzl ztN*Y3zs%3T!`Q)yS!0QvJ{r~m)} diff --git a/Shuttle/apple-scpt/terminal-current-window.scpt b/Shuttle/apple-scpt/terminal-current-window.scpt index fcc7472416067caa510306ffed54bcc1b94a2d1c..21073d4f94d9f9e2d29bf35207e8024f0faed2ad 100644 GIT binary patch delta 176 zcmX@c@qlB)Pe!Iw9FrND0wxPEu~|7VF)}3PWEL|30b>pW69XfUZ(?$0UP*p&hJuF+ zNEiuR4B8*#p$!!17N0P=o5_*6JMr=4^Gqgs7lReg0EGn^I2af%{=2ROWHSQUKxGVF zIrD(V8X=oHS%X=9asab}v=jpmgD*oOLo!1qLmop3Lq0<>Lk5Eag9n4lvY69Xf&e{cj)T7u_7+40vN z+CcGZpAsf_GdVI}x7j@TJd=s)g>tQEps*kV2Lr>!f7g{jT7hh!GKOpJc|bEXK}Hxd Yt4|JRR^;ShU}o@V2xf?wJem0{00vef!vFvP diff --git a/Shuttle/apple-scpt/terminal-new-tab-default.scpt b/Shuttle/apple-scpt/terminal-new-tab-default.scpt index 2d347393ffce6de1435f49938d5bec9fe78b37b9..03c2218937b9dbddbb18546129e74ed64d2ec5b0 100644 GIT binary patch literal 2422 zcmcImOHABW6g?gw%-ECp<%d#5rJqfwgdts0Rc%B-NG%#Ez#>vNj>i-Sk7s0?p)8uP zPFgAIu3c35b=gImcGX4Jt;mNognS8~l#uWV`7}*P%I$rgnG8@ss8Wn<&U@GI++9N8m#_{ev=;qe5bU2xA|;pyvv` zz}7kh9L+YeuH9`F;ofdbq35WogREuJY1K*zquh|kYz;A4WrXJG89k-Hu>qCzM4|ar z*(7?#@z7KF%IPoo%lJ_if}aDS$MlFED)bn!I#jCmXO5Om4``0=(=5%(6Gm5={>G)r%}nEje$Zp zvAqsTk71=UJzA#Qa*KloF^E9MRs`t=U8iewm9EebU8X_0M1No-UF1Fr(*PoLfzH!8 zI!paw1f}2o+;y+u`p73iH4R5lOD6ZO6l_s7!*VlDXLa+QWgZ9ip82$ytB)hwjs``p zhNYUdYBsy$(bsN(|*?oY*cc|+lyBv4%(7ybv@UBfh7L+219+`<@WeWRakQ4cQ+ zy~$W&%n{+R`5kBQ-r@x6cW1Mvp{cHsv7CCmB&)}g@I%;+AljhAMj8ezs4!8>&n*9i zJ!=b(G|%QD0|%X`Ml<#xhPM_wNxbxtSmFYkASEDjl<5Y_#2;RS>2{d)W_@kpt+yBGF@S7+Hf>gb8{u@Q3x#45VN>hyF;eS0%;Df z7!-(GUZzV0X3cW-IA8g19D%?x%de_d)pM@R1zpY@#ztxG6==}Wa}FOOB@KSPZVetb z4@Fyh9am3RH|BK9b^eDCzdCq&V4+VhXpO(}iW&?ya5Y+WJ7;Jt!xdCGHt4A^$O=5f z_d&kD;5x51Jr=XpV}(>gc$;J)cZmPP-@Xyow=!>DRkcp9lS%2O zTGCM;tn_|6#H*Pn7Uj0_fyH@&9dxKFi}O7Eir$iPKIXGcrgKap9Cu}Hr${2oly8Ew zp13Vu-!D)~bE7nYGb1tl8Xv9Er5$+Z*jRkLD`5sv&c|2$KoAwl_{8zV$vZVo$rHjf zS<|!+U&hCGvMs>J+o=QCf!7Le@|>J3QS8I}!XOujreb(KK0T9&VcRTEhm$HIDxxZ4 sT{-r+sl9#CTFPx# literal 2960 zcmeHJT~8ck6nWQ%s#!BWa*mz$O6|y1#PHbP~C90(v+pegZ`(q zbBS_yg=t-sJ1ghbL{aE=mao%vTC-UTkj?s5_8u`^&RW3btmjZx10SzDSvrB51PZ~& zn$9XTyP+UqdWiC#`U7fj{V!@?{jX}|+wZMST%oHj>{s%zkGrsk zpm6_}&}F(rzjIL+5k!bCAWY|Jj7DiBh!W{XAMc!(@Oj@OK}(pnpqBRV{*%0F<6gHw zgPH!h4_GUV&VTA}GOVPT(h^ZEop!q+-}>0Q2Zk1%pL3V6DX<$9SLNo*zs1dtnJ^vG z;1oq`VX!mu37-oKJhV9WI%A13XN1w6DXw5E7f`z+olcm#=9uj%yB5zmYVl&$0ldmx z(*y$+k}#2ih6JKKruiFWUN2NMVlPVZ#%j*kbIRC@jGH0FAS#jRG7980JC013P#M*X zjAQYmN_8X>MBRMNR}ffc(JP}#)7CZJ$*esaL_ivT3EJC6#%2Rv(LVFfwcn|R8@p`B zNLJNnjFe;liw3x`@5`aZZX>6)i}wDycFSlqtc?Vwbt!(yXpptR}0)`U(Pgle?-BU9jPBML{`G zL9nVnE@t~fw*%a0(oZN2bCZSEHlt6}{ocUW;`5Enwon={yH$(KK$cG58c?cijGn{avO}CP`bm-IliO9Ep%ZcfN2G8p-pA8KxJg9PKqCY8T$#04ykBfInU3;xPTFdb>4-pjiq{Tj zkSyyXCgA$7UPZORU_AXV`4H@Jzd?P8UoHR=v!L5QQgoS Q)cAdw|04Yh&<{0h1G$*{mFx7#R|CGK(32fH8-GiGh*FH!(RguOz=XL&3uZ zB#Z|6NxCvKfJFpfZN8 zoOwWFjgU>9T*0C~c>#-pv=jpmgD*oOLo!1qLmop3Lq0<>Lk5Eag9n4l&<{0h1G$*~A=}7#R|CGK(32fH8=HiGh*XKR5y?Ex~i4?D%UB zZJ_wIPYIK6GdnV0x7j>dp2bA Date: Thu, 2 Jun 2016 11:26:54 -0400 Subject: [PATCH 63/97] Ignore error from close first window after startup. The problem is in iTerm2 3.0.0, when Preferences/General/Startup is set to "Don't Open Any Windows", it does not open a window when it starts up. So "close first window" fails with an error. To handle this scenario, we can simply wrap this statement in try / end try. --- .../iTermNightly/iTerm2-nightly-new-tab-default.applescript | 4 +++- .../iTermNightly/iTerm2-nightly-new-window.applescript | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript b/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript index e57e737..db2acbd 100644 --- a/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript +++ b/apple-scripts/iTermNightly/iTerm2-nightly-new-tab-default.applescript @@ -19,7 +19,9 @@ on CommandRun(withCmd, withTheme, theTitle) tell application "iTerm" activate delay 0.2 - close first window + try + close first window + end try end tell tell application "iTerm" diff --git a/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript b/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript index e8e755f..eebb2b9 100644 --- a/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript +++ b/apple-scripts/iTermNightly/iTerm2-nightly-new-window.applescript @@ -18,7 +18,9 @@ on CommandRun(withCmd, withTheme, theTitle) if it is not running then activate delay 0.2 - close first window + try + close first window + end try end if end tell tell application "iTerm" @@ -34,4 +36,4 @@ on CommandRun(withCmd, withTheme, theTitle) end tell end tell end tell -end CommandRun \ No newline at end of file +end CommandRun From 5a3bc49461ea27cd526d1b2c0835ae5bbbb30fdf Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 2 Jun 2016 23:54:20 -0700 Subject: [PATCH 64/97] New theme notes to reflect changes in 1.2.6 In 1.2.6 I added some logic in the iTerm applescript to set the theme to Profile Default if he passed theme was missing or spelled wrong. Reflecting those changes in the readmd.md https://github.com/fitztrev/shuttle/commit/99b300b47cc5b7efb552bddd064887900bf2f512 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b08204d..547d878 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,8 @@ Possible values are the Profile names in your terminal preferences. iTerm ships In iTerm the profile names are case sensitive. -**Please ensure the theme names you set are valid. If shuttle passes theme "Dagobah" and it does not exist in iTerm or OS X Terminal then your command won't run. This is because the applescripts are not making any checks to see if the theme you passed actually exists within the terminal application.** +**Please ensure the theme names you set are valid. If shuttle passes theme "Dagobah" and it does not exist in iTerm, shuttle's applescripts fall back to the default profile. In iTerm this profile is called ```Default```. +If you have removed ```Default``` or renamed it shuttle may not open your command.** This setting can be overwritten by the command level ```"theme"``` settings From d5e400db66df8316ff9b7eb9a390b0c46023f2af Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 10 Jun 2016 13:11:30 -0700 Subject: [PATCH 65/97] Updated the Contributors List. Updated the contributors list to include a link back to their github profile.Thanks to all of you that have taken the time to make shuttle better; you all rock! --- README.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 547d878..9fded81 100644 --- a/README.md +++ b/README.md @@ -207,23 +207,29 @@ If ```title``` is missing shuttle uses the menu's name and sets this as ```title ## Contributors -This project was created by Trevor Fitzgerald. I owe many thanks to the following people who have helped make Shuttle even better. +This project was created by [Trevor Fitzgerald](https://github.com/fitztrev). I owe many thanks to the following people who have helped make Shuttle even better. (In alphabetical order) -* Alex Carter -* Dave Eddy -* Dmitry Filimonov -* Frank Enderle -* Jack Weeden -* Justin Swanson +* [Alex Carter](https://github.com/blazeworx) +* [Dave Eddy](https://github.com/bahamas10) +* [Dmitry Filimonov](https://github.com/petethepig) +* [Frank Enderle](https://github.com/fenderle) +* [Jack Weeden](https://github.com/jackbot) +* [Justin Swanson](https://github.com/geeksunny) +* [Kees Fransen](https://github.com/keesfransen) * Marco Aurélio -* Martin Grund -* Michael Davis -* Rui Rodrigues -* Ryan Cohen +* [Martin Grund](https://github.com/grundprinzip) +* [Matt Turner](https://github.com/thshdw) +* [Michael Davis](https://github.com/mpdavis) +* [Morton Fox](https://github.com/mortonfox) +* [Rui Rodrigues](https://github.com/rmrodrigues) +* [Ryan Cohen](https://github.com/imryan) +* [Stefan Jansen](https://github.com/steffex) * Thomas Rosenstein -* Tibor Bödecs +* [Thoro](https://github.com/Thoro) +* [Tibor Bödecs](https://github.com/tib) +* [welsonla](https://github.com/welsonla) ## Credits From 991724a0d9d1f4564fe63b6a6e9a0d4dc63697d0 Mon Sep 17 00:00:00 2001 From: Pluwen Date: Tue, 14 Jun 2016 09:02:40 +0800 Subject: [PATCH 66/97] =?UTF-8?q?=E5=9B=BE=E6=A0=87=E9=AB=98=E6=B8=85?= =?UTF-8?q?=E9=87=8D=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 提供最高 1024*1024 尺寸,小图标进行细节微调,减少虚化的像素点 --- Shuttle/StatusIcon.png | Bin 375 -> 547 bytes Shuttle/StatusIcon@2x.png | Bin 686 -> 1125 bytes Shuttle/StatusIconAlt.png | Bin 374 -> 454 bytes Shuttle/StatusIconAlt@2x.png | Bin 665 -> 911 bytes shuttle.icns | Bin 63118 -> 184340 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Shuttle/StatusIcon.png b/Shuttle/StatusIcon.png index 9b1f75e6ed53ff08f3bc944917276ddfe8f34f18..b8185e8e123f96a109282555c8bd22adf4afb01a 100644 GIT binary patch delta 523 zcmV+m0`&d&0;2?wBYyw{XF*Lt006O%3;baP0005nNkloNnym8}9Hf@JWEAQL}3Z}A2l zu8OUC@VM`tbI;+qtWOB+<&prXmnI6m8LdtMGH`O zRHVnc9*@Uq@IGt_#!9oli=ueM&y#Ush4p&olfUWD`NM-U?5ZYT*F?kmx72K94h=-$}k)bdt0rRml83| zU@$Z&j;s*P7k{eN>Ln%Mn8jjw;pfSyPK|>umum<6VK^KHI7UTTmLDm6sy`Tl5${%| z-|yS0WRBTvwzqsf?-B%I%rPFP9vPFnTV%$BFicZsj8hHqH8|Vt_9=SvX*Qc{jGVO! zg~IwQPVHel9^Z4j-8WXN^`p^fm_wn^IbPgNN+?OvE`NnHOf?J!gKTO~c$6WgY|m!1 zYtd-*uv{*C&y zdG68?#wP4->m0xdg@i34@`Cy){q#3!e3{u&?fa{{R30 N07*qoL3P*+#?6G;XFHK_sdKU@a7xVT&++J8Wxh6rG+e!^uK2fZ#MRE&fF} z(8k8*DmjG%D^?JDKYskUZQZ(cb9e9FJv}xy_7up#prD{#l$17{Kr9Qy;xAsj2mlre v@4C9WN~u*IvQ$-7EeF!_)GT0)@W}%Jllk!3Y9n#{00000NkvXXu0mjfFZ7(> diff --git a/Shuttle/StatusIcon@2x.png b/Shuttle/StatusIcon@2x.png index f7fef90f725696661e6aac58012497449c966e04..8e3fb532d76110e29257d19bd74036d8ddd88138 100644 GIT binary patch delta 1105 zcmV-X1g`t81?32kBYyw{XF*Lt006O%3;baP000CWNkls)A^@W zrqQ-6XQFdvDzUO+SZGGkOBpdAdN2)?z?Yt+Xq3>4NYH~}MA)lAFPY1S96kA<7?GeU z_-Cmg=E`;LaO>`R&(6Jf6MOK$k8{rN_dCDy+3((S?teLpi`y^z8Hh~=>~_1- zWHOzQ8ciW}fK>myygZ%H=c^3_0(A<7!aqJfehPI!Y@A&K+y;9Iuv>tM4+1Hb%4>l9 z66~{TCCfpM-Dou0Ha9na0`3mRhu}yo=o+NNjA;hVX7gc>$Mbp{jK<+d7EdCJziAF! zeate5tZCQR*MGk<;K+P1c~E(F`GI%>f3^74fF z`S}l+=n`u(j=hl6j@XPE(B*Qyz+=50aTcBt+stvEirS3|5bs?sA~#W^(+wGPQ@AB6 zz~toQSIC|!JS4k^F{0-c?Z}@tCnx91+S*zbUoWPQZGXlu#*g^O224pwX%#yO-w%NE zd>ySX3$UW1B5QSZwU`!^rB|s`b1a{rT^3+^dip~Iko^@(OicV91Yp&&0B2`sZ?FPK zvd72Ae~V=2+7h@OR-2qqu!ptI%*>crzBD`Fd{|tcX?DI|bOy0%@8MiGy0NigLlD`I zSEthr4Sx?0FY-ZXeI_9GECt}6qwa*H(gKOq7Zen9iuEOa%uE388Ni-M2}<+L%F3GT z>+3V&HSy4V`1*($%+1Zc4Y%mwj~#V59AyA> zbM>q?HG_V?|7on?2!z}1_DY2#;{-EUSXg+}>wopS04ez(K!Q?=N2Aebv4S@!c0sk^ zKA4e_aer!RN`>$3N_KX(D>F0Gt=H?_-QC^i7Z(?g2u5Nnldw(Y!|ey#^`D0zU`a`d zv9`AMeN9cx1U~E`Z>!bnYG`Pv!$h(&izjj@6ij3z>`F++;Jr(&s;V*<6&1Ph&dfkH z34a-Zc#uua3d+#X5Xpq(f_NYC1V*I{6v`lZgbv6s0m zChorKpj~9WB+@VBCb&pKR$c>;liB!K}*9_=}Z1(F8YQI?jL5-KYzA1o{^{JkJyCvf)n_g~)OxSI-qDj<4D zG2|k|0x?2LA@<(h-geylwjhD(>gq>wScDdd=r~wJx(usr>02+|N()|4VUrN){ z(|Q~_-h?^qbq9bdNK;o=*W1R%#&>(oBRD?*&qD0W%gcF!4tuTK&LUay|8D_gRPXs8 X(e8R+dU`B;00000NkvXXu0mjf4Ko;y delta 663 zcmV;I0%-l^2(AT?BYy&QNkl0S!>2%&T zEszDL)u(h+9*^g>nSnPU#^G|gKARHAVh*g08)&!NyJiK>L+A)xGdz%m_MxXIkbCfl z8G+X!^q85MS${D!kVQ7zY_^Yj17oq+E29JVL+F*sWbPRdsNDqU!Ikef8jVx31J$k5 z9fE>8I9|>juHI#4Zd;K%-i%R@gcBhF>$aS}kg9pf++E2N1_q zjHGwOSX{ x^OF>~1s9>fFFR0Hqz9L~LRigj^002ovPDHLkV1kKKKt=!n diff --git a/Shuttle/StatusIconAlt.png b/Shuttle/StatusIconAlt.png index 613511cf88f63b7793c18631752bc03433015920..cdbb6daf3dfda1d50249198d84ced20bd5f1eba8 100644 GIT binary patch delta 429 zcmV;e0aE_<0>%T7BYyw{XF*Lt006O%3;baP0004fNklQEcAQ=1a-@iXeHt^fGZzo^|pwmDXej>@hA3uI5p?@2K%zyLd%|mPkGGP-0 zYGnNM=~FE>Ihfe(+qW;k*vK>sG6%#48YT^-7Kw_AI-$!U^Us|-w*{Gxl?^nE^WVRJ zw~)PtjeYCZt$RQNuqJF2FaP=Trm?l6(;4W-yYZFjbR_>RTm7S%puRrJI%a^ZlX#$E%;gN$&$pcw( zKn#i~B?SeA_Ctpbo%;<0FqgsTyLa!dgXVyYzo9$l9G~D!1VbkCnv`mMHK}cH6|Y)p9w(ytQ44mp;ZS%1W5n@ Xj`wGK>-LE-00000NkvXXu0mjfx{$jl delta 348 zcmV-i0i*uL1NH)tBYy!sNklJNxeh#mxO5j5r0Dg+Wu4dL2f z&>SuK7gRVDZMh0f>hOjJDcpJwTLdATH0hx{#2?qUEhP1PwDf@w=iR~Q4m^es@~8f@ zU>n$9hUf4TezP-k5PO?dRgK6G!Z5s^DTsBxEX#LNBaY*@-hV-?&Ebia}isBW^vKNlyjGKZ%5WG?a!5&X;lW@qxRho2R+6L}(=?w6ux;C*3O29;*Wnt^^M_Fs ueJF}@Mir!BK~>cUj2l!zN_H906#N1Vg#@j?>d~kG0000=e_TJd3iIlNq5+7Psnrg6IE>G_nRT8_?lRJZ3TU*a|BJZ?bsZ`cGttscZ1m<$N0jt$|UP(qfMrr({wdE4Hwzf8k z0Niy{EEY3{3bfg5L$m?Z>9@4Bl!Xu* zw-}`(0^9tR;HPWE=Y zUBnvo4Syo>s2;ZX^v7_VtU_R~WT^ECP7&`2M*!-3n8Z{)2eaAih}-S%-9%elTwKlP z^F_=RgTde>hr?m#5es1l;CBl6#^SFkFqupq4~0UnXJ%%W>-9R}YXFsKG`bpzL`E$Z z3vXtRvWV+syAKNw06*XX`TO_c$jHpwb}$WfM0ke zyK<1pWbQhhP6x(4xaK~4Jf4fBzgyan-|v5cuf2U$&dtp|-Yvo;9qnD8N~IRac{)5i z%=Lc^IZ}*jdO-o6EBSlq?PxcAP8r)k{IdF_0<|FNLTJZ>aZwsGhmgF P00000NkvXXu0mjfxy+v- delta 641 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

e&PXKrR-%^s`aC| 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

-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 Date: Tue, 14 Jun 2016 11:34:04 +0800 Subject: [PATCH 67/97] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B9=B4=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Shuttle/Shuttle-Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index 078ef53..dc1be57 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -29,7 +29,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2013 Trevor Fitzgerald. All rights reserved. + Copyright © 2016 Trevor Fitzgerald. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass From 02ee7278582e3e328f67ac6601f5f2ef4c70c71a Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Tue, 14 Jun 2016 09:04:33 -0700 Subject: [PATCH 68/97] Updated the Contributors List. Thanks to all that have contributed their time and efforts to make shuttle better --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9fded81..693c276 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ This project was created by [Trevor Fitzgerald](https://github.com/fitztrev). I * [Matt Turner](https://github.com/thshdw) * [Michael Davis](https://github.com/mpdavis) * [Morton Fox](https://github.com/mortonfox) +* [pluwen](https://github.com/pluwen) * [Rui Rodrigues](https://github.com/rmrodrigues) * [Ryan Cohen](https://github.com/imryan) * [Stefan Jansen](https://github.com/steffex) From 9df7c61c91dafde1a03915e2d0534cdb6c3f8851 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 24 Jul 2016 13:50:50 -0700 Subject: [PATCH 69/97] Fixes for release 1.2.7 * Corrected an issue where commas in commands would not be parsed. #173 * Changed the directive names for iTerm_version. Valid names are legacy, stable, nightly. #181 --- Shuttle.xcodeproj/project.pbxproj | 36 +++++--- Shuttle/AppDelegate.m | 44 +++++++--- Shuttle/Shuttle-Info.plist | 4 +- ....scpt => iTerm-legacy-current-window.scpt} | Bin 1158 -> 1158 bytes ...scpt => iTerm-legacy-new-tab-default.scpt} | Bin 2022 -> 2022 bytes ...ndow.scpt => iTerm-legacy-new-window.scpt} | Bin 2468 -> 2468 bytes .../iTerm2-nightly-current-window.scpt | Bin 1108 -> 1108 bytes .../iTerm2-nightly-new-tab-default.scpt | Bin 1934 -> 1952 bytes .../apple-scpt/iTerm2-nightly-new-window.scpt | Bin 1646 -> 1666 bytes .../iTerm2-stable-current-window.scpt | Bin 0 -> 1108 bytes .../iTerm2-stable-new-tab-default.scpt | Bin 0 -> 1952 bytes .../apple-scpt/iTerm2-stable-new-window.scpt | Bin 0 -> 1666 bytes apple-scripts/compile-iTermLegacy.sh | 7 ++ apple-scripts/compile-iTermStable.sh | 6 +- .../iTerm-legacy-current-window.applescript} | 0 .../iTerm-legacy-new-tab-default.applescript} | 0 .../iTerm-legacy-new-window.applescript} | 0 .../iTerm2-stable-current-window.applescript | 23 ++++++ .../iTerm2-stable-new-tab-default.applescript | 78 ++++++++++++++++++ .../iTerm2-stable-new-window.applescript | 39 +++++++++ 20 files changed, 209 insertions(+), 28 deletions(-) rename Shuttle/apple-scpt/{iTerm-stable-current-window.scpt => iTerm-legacy-current-window.scpt} (94%) rename Shuttle/apple-scpt/{iTerm-stable-new-tab-default.scpt => iTerm-legacy-new-tab-default.scpt} (96%) rename Shuttle/apple-scpt/{iTerm-stable-new-window.scpt => iTerm-legacy-new-window.scpt} (94%) create mode 100644 Shuttle/apple-scpt/iTerm2-stable-current-window.scpt create mode 100644 Shuttle/apple-scpt/iTerm2-stable-new-tab-default.scpt create mode 100644 Shuttle/apple-scpt/iTerm2-stable-new-window.scpt create mode 100755 apple-scripts/compile-iTermLegacy.sh rename apple-scripts/{iTermStable/iTerm-stable-current-window.applescript => iTermLegacy/iTerm-legacy-current-window.applescript} (100%) rename apple-scripts/{iTermStable/iTerm-stable-new-tab-default.applescript => iTermLegacy/iTerm-legacy-new-tab-default.applescript} (100%) rename apple-scripts/{iTermStable/iTerm-stable-new-window.applescript => iTermLegacy/iTerm-legacy-new-window.applescript} (100%) create mode 100644 apple-scripts/iTermStable/iTerm2-stable-current-window.applescript create mode 100644 apple-scripts/iTermStable/iTerm2-stable-new-tab-default.applescript create mode 100644 apple-scripts/iTermStable/iTerm2-stable-new-window.applescript diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index 8489867..d3b0fce 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -13,9 +13,12 @@ 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; }; 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */; }; - A12D9BF01BCF2C73004F52A6 /* iTerm-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */; }; - A12D9BF11BCF2C73004F52A6 /* iTerm-stable-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */; }; - A12D9BF21BCF2C73004F52A6 /* iTerm-stable-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */; }; + A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */; }; + A124BA1A1D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */; }; + A124BA1B1D45572B00218F2F /* iTerm2-stable-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */; }; + A124BA1F1D4558E500218F2F /* iTerm-legacy-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA1C1D4558E500218F2F /* iTerm-legacy-current-window.scpt */; }; + A124BA201D4558E500218F2F /* iTerm-legacy-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA1D1D4558E500218F2F /* iTerm-legacy-new-tab-default.scpt */; }; + A124BA211D4558E500218F2F /* iTerm-legacy-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA1E1D4558E500218F2F /* iTerm-legacy-new-window.scpt */; }; A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */; }; A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */; }; A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */; }; @@ -42,9 +45,12 @@ 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; - A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-current-window.scpt"; sourceTree = ""; }; - A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-new-tab-default.scpt"; sourceTree = ""; }; - A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-stable-new-window.scpt"; sourceTree = ""; }; + A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-current-window.scpt"; sourceTree = ""; }; + A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-new-tab-default.scpt"; sourceTree = ""; }; + A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-new-window.scpt"; sourceTree = ""; }; + A124BA1C1D4558E500218F2F /* iTerm-legacy-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-legacy-current-window.scpt"; sourceTree = ""; }; + A124BA1D1D4558E500218F2F /* iTerm-legacy-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-legacy-new-tab-default.scpt"; sourceTree = ""; }; + A124BA1E1D4558E500218F2F /* iTerm-legacy-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm-legacy-new-window.scpt"; sourceTree = ""; }; A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-current-window.scpt"; sourceTree = ""; }; A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-tab-default.scpt"; sourceTree = ""; }; A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-nightly-new-window.scpt"; sourceTree = ""; }; @@ -96,9 +102,12 @@ A12D9BE61BCF2C72004F52A6 /* apple-scpt */ = { isa = PBXGroup; children = ( - A12D9BE71BCF2C72004F52A6 /* iTerm-stable-current-window.scpt */, - A12D9BE81BCF2C72004F52A6 /* iTerm-stable-new-tab-default.scpt */, - A12D9BE91BCF2C72004F52A6 /* iTerm-stable-new-window.scpt */, + A124BA1C1D4558E500218F2F /* iTerm-legacy-current-window.scpt */, + A124BA1D1D4558E500218F2F /* iTerm-legacy-new-tab-default.scpt */, + A124BA1E1D4558E500218F2F /* iTerm-legacy-new-window.scpt */, + A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */, + A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */, + A124BA181D45572B00218F2F /* iTerm2-stable-new-window.scpt */, A12D9BEA1BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt */, A12D9BEB1BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt */, A12D9BEC1BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt */, @@ -232,22 +241,25 @@ A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */, A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */, A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */, - A12D9BF21BCF2C73004F52A6 /* iTerm-stable-new-window.scpt in Resources */, 0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */, - A12D9BF11BCF2C73004F52A6 /* iTerm-stable-new-tab-default.scpt in Resources */, + A124BA1F1D4558E500218F2F /* iTerm-legacy-current-window.scpt in Resources */, 0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */, C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */, A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */, 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */, 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */, + A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */, A12D9BF41BCF2C73004F52A6 /* iTerm2-nightly-new-tab-default.scpt in Resources */, C149EC0E15D5214600B1F558 /* Credits.rtf in Resources */, - A12D9BF01BCF2C73004F52A6 /* iTerm-stable-current-window.scpt in Resources */, A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */, C149EC1415D5214600B1F558 /* MainMenu.xib in Resources */, + A124BA1B1D45572B00218F2F /* iTerm2-stable-new-window.scpt in Resources */, + A124BA1A1D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt in Resources */, C159DC2815D5DE8000F5DE24 /* shuttle.icns in Resources */, A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */, 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */, + A124BA201D4558E500218F2F /* iTerm-legacy-new-tab-default.scpt in Resources */, + A124BA211D4558E500218F2F /* iTerm-legacy-new-window.scpt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index aa9a77b..a76ef51 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -365,7 +365,7 @@ - (void) buildMenu:(NSArray*)data addToMenu:(NSMenu *)m { NSString *menuName = cfg[@"name"]; //Place the terminal command, theme, and title into an comma delimited string - NSString *menuRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@,%@", menuCmd, termTheme, termTitle, termWindow, menuName]; + NSString *menuRepObj = [NSString stringWithFormat:@"%@¬_¬%@¬_¬%@¬_¬%@¬_¬%@", menuCmd, termTheme, termTitle, termWindow, menuName]; [menuItem setTitle:cfg[@"name"]]; [menuItem setRepresentedObject:menuRepObj]; @@ -383,7 +383,7 @@ - (void) openHost:(NSMenuItem *) sender { //Place the comma delimited string of menu item settings into an array - NSArray *objectsFromJSON = [[sender representedObject] componentsSeparatedByString:(@",")]; + NSArray *objectsFromJSON = [[sender representedObject] componentsSeparatedByString:(@"¬_¬")]; //This is our command that will be run in the terminal window NSString *escapedObject; @@ -443,10 +443,15 @@ - (void) openHost:(NSMenuItem *) sender { } } + //Set Paths to iTerm Legacy AppleScripts + NSString *iTermLegacyNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-legacy-new-window" ofType:@"scpt"]; + NSString *iTermLegacyCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-legacy-current-window" ofType:@"scpt"]; + NSString *iTermLegacyNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm-legacy-new-tab-default" ofType:@"scpt"]; + //Set Paths to iTerm Stable AppleScripts - NSString *iTermStableNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-new-window" ofType:@"scpt"]; - NSString *iTermStableCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-current-window" ofType:@"scpt"]; - NSString *iTermStableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm-stable-new-tab-default" ofType:@"scpt"]; + NSString *iTermStableNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-new-window" ofType:@"scpt"]; + NSString *iTermStableCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-current-window" ofType:@"scpt"]; + NSString *iTermStableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-new-tab-default" ofType:@"scpt"]; //Set Paths to iTerm Nightly AppleScripts NSString *iTerm2NightlyNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-window" ofType:@"scpt"]; @@ -474,11 +479,11 @@ - (void) openHost:(NSMenuItem *) sender { else if ( [terminalPref rangeOfString: @"iterm"].location !=NSNotFound ) { //If the JSON prefs for iTermVersion are not stable or nightly throw an error - if( ![iTermVersionPref isEqualToString: @"stable"] && ![iTermVersionPref isEqualToString:@"nightly"] ) { + if( ![iTermVersionPref isEqualToString: @"legacy"] && ![iTermVersionPref isEqualToString: @"stable"] && ![iTermVersionPref isEqualToString:@"nightly"] ) { if( iTermVersionPref == 0 ) { - errorMessage = @"\"iTerm_version\": \"VALUE\", is missing.\n\"VALUE\" can be \"stable\" or \"nightly\"\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help."; - errorInfo = @"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have iTerm nightly installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle."; + errorMessage = @"\"iTerm_version\": \"VALUE\", is missing.\n\n\"VALUE\" can be:\n\"legacy\" targeting iTerm 2.14\n\"stable\" targeting new versions.\n\"nightly\" targeting nightly builds.\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help."; + errorInfo = @"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have another version of iTerm installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle."; [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:YES]; iTermVersionPref = @"stable"; @@ -489,9 +494,26 @@ - (void) openHost:(NSMenuItem *) sender { } } - if( [iTermVersionPref isEqualToString:@"stable"]) { + if( [iTermVersionPref isEqualToString:@"legacy"]) { - //run the applescript that works with iTerm Stable + //run the applescript that works with iTerm Legacy + //if we are running in a new iTerm "Stable" Window + if ( [terminalWindow isEqualToString:@"new"] ) { + [self runScript:iTermLegacyNewWindow handler:handlerName parameters:passParameters]; + } + //if we are running in the current iTerm "Stable" Window + if ( [terminalWindow isEqualToString:@"current"] ) { + [self runScript:iTermLegacyCurrentWindow handler:handlerName parameters:passParameters]; + } + //we are using the default action of shuttle... The active window in a new tab + if ( [terminalWindow isEqualToString:@"tab"] ) { + [self runScript:iTermLegacyNewTabDefault handler:handlerName parameters:passParameters]; + } + } + //iTermVersion is not set to "legacy" using applescripts Configured for Stable + if( [iTermVersionPref isEqualToString:@"stable"]) { + + //run the applescript that works with iTerm Stable //if we are running in a new iTerm "Stable" Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTermStableNewWindow handler:handlerName parameters:passParameters]; @@ -502,7 +524,7 @@ - (void) openHost:(NSMenuItem *) sender { } //we are using the default action of shuttle... The active window in a new tab if ( [terminalWindow isEqualToString:@"tab"] ) { - [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; + [self runScript:iTermStableNewTabDefault handler:handlerName parameters:passParameters]; } } //iTermVersion is not set to "stable" using applescripts Configured for Nightly diff --git a/Shuttle/Shuttle-Info.plist b/Shuttle/Shuttle-Info.plist index dc1be57..3c6d613 100644 --- a/Shuttle/Shuttle-Info.plist +++ b/Shuttle/Shuttle-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.6 + 1.2.7 CFBundleSignature ???? CFBundleVersion - 1.2.6 + 1.2.7 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/Shuttle/apple-scpt/iTerm-stable-current-window.scpt b/Shuttle/apple-scpt/iTerm-legacy-current-window.scpt similarity index 94% rename from Shuttle/apple-scpt/iTerm-stable-current-window.scpt rename to Shuttle/apple-scpt/iTerm-legacy-current-window.scpt index e74a33882c9e27145aaa3759e5eaf5dd9154b1db..f1e2a798d9e6340022bd589e3a04fd48ce2d593a 100644 GIT binary patch delta 16 XcmZqUY~$Q;fr)udaPQ{JOq&@2HA)6- delta 16 XcmZqUY~$Q;fr+`g^1|lJOq&@2HeLp# diff --git a/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt b/Shuttle/apple-scpt/iTerm-legacy-new-tab-default.scpt similarity index 96% rename from Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt rename to Shuttle/apple-scpt/iTerm-legacy-new-tab-default.scpt index ae079b6ddadea5685e92391e8c2d725ca830a4a7..e61df7650bedddc38fd22c7f772cebffc5dfc2c8 100644 GIT binary patch delta 16 XcmaFH|BQb_9}DxE;NHy>SwfisJhTQ} delta 16 XcmaFH|BQb_9}9DJ<%P`?SwfisJ;(-> diff --git a/Shuttle/apple-scpt/iTerm-stable-new-window.scpt b/Shuttle/apple-scpt/iTerm-legacy-new-window.scpt similarity index 94% rename from Shuttle/apple-scpt/iTerm-stable-new-window.scpt rename to Shuttle/apple-scpt/iTerm-legacy-new-window.scpt index d118d05c0d6ee5c93936ea317f6bea188ce80a3c..85e75cc63f3f65bb233591f08b17e513100a4dbc 100644 GIT binary patch delta 29 jcmZ1?yhM0|01NY);NHpi*{^JV&hnKB#MykG{U{>ej7~;b1yKZ>M^HgU*jYRvy)DyD%jznkqA9Iz3ZKoFs6%M`8w1FS6MIveT)7((~n$K($& z9WDG(%;*MxiVd6P&p%E6yq;1~Z&}tP)(nh`{gJmTyyz_q8HjKP4Q1idZRmcWCpd_D xk}22_z9v6ddke0HS~iCK6_MLt?G^Dg2aVNc?*pQKElYE9i7X_jFW1-KUIB#BoS6Us delta 451 zcmZ{ey-Nad7{{OM&bRCI>@SstszVswvI zXdlexQaqN4&p6MNNZdG;ho2`2zU606jB!JRvP+H&FrvKB$|>^e<_ra+s?f=t6Zj(S zOkoh#zzmkRexu0m9tkGo4_Y0^@~3B6DF!YS`Kz!cfT*f!1P+}LqW3EUv%c?lf=$-I zo-fj^u`o!wu%Bok^&^F{gDPFRSR9ClQZ)>I{e*gFhkx1hOatfev;PDlXqjl*3=CL* HyWP`2RYj0E diff --git a/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt b/Shuttle/apple-scpt/iTerm2-nightly-new-window.scpt index d96457f4d1cf51aa4c6bae6c099fd308b6b21e4e..d0cbdaeed5c2653c0a0328305bbeeb82aac2976e 100644 GIT binary patch delta 301 zcmWm6PfG$(6b0}z&e%+^nWjXd7U8PGhge|iHbT&92pYzgB9fI3qQj(_>5X1t&Kw3!CzK3l;D10{idL(Qc9ve5TYKysEhgJR;^NUJ+JH@ z6^leC7>A2YiQnoP6Ap!LV`BN$3Q~+HbvXPXtEY91{sSIWzB%tjA-4r-;o1H||ZQas8H z%t@H66?iDY#UaW#FsKX7Sgsva4eGE8#X76a+ON75gF@E2cg($3FpSUo+iz*i$#=_B zGMJIRK+OK&)NJ{=x4;fY?L=<6r-aEx)Zarsa^+ypTWEsx3$7 TWdYN19Mi2AYfvD52ZO6WI@n=Z diff --git a/Shuttle/apple-scpt/iTerm2-stable-current-window.scpt b/Shuttle/apple-scpt/iTerm2-stable-current-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..956952461689e6cc1475e544b3f202fdfc2dae93 GIT binary patch literal 1108 zcmb_bOHUJF6g}TiXglB7mTCxbRilYXHKlCX5zw>?LQ>keaWhUE8SG4(nc=Z??aHk# z6gRGP;V7k%$;-RKIYsrckY{hxzsdo|K)fEE} ziWAXK2xx)`5R4IH7x4r%gy;v_5JJ0PfCOWWj_6R*5xTO7`j%(c18>}I!NRyJ=@8vn z=vK{fOt-9p`oIb1XpEbM6V#*w+Nba6K$!L)dyBcegHfWu`A_|?>ByC>OLC}U8Y+ViZ1+t9|4<^-MtCChPq^5Ce z9xTfIRwS)ln3I6c0~oYSOSDK0TA=wj4DmL?-}*lv`C?&+*9KK8wdX%#7Ul#L-TLXJ2Y#j0LYHb7KCt&eHEYTil0J#xY#u&tRj73OqQ- zz(k$TIIhFM5PKF!T?DAX$0$@&j~Nid%4);u`MXIZ(hF>(>Be-`Ku4)mio@jpo+Z1|oSS?93zjK66aWAK literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm2-stable-new-tab-default.scpt b/Shuttle/apple-scpt/iTerm2-stable-new-tab-default.scpt new file mode 100644 index 0000000000000000000000000000000000000000..2aeb57fdf3840065c006401d0a9ecc828d9aabf4 GIT binary patch literal 1952 zcmb_dOKcNY6g^`*u|4CAzeEVBQd)IoqQopvB{mTXL5bAVQm_>&bu*a^cv8>Unwive z$GWR7x`9PY=x5a~sAlNk&&Y=I+mZ$d+*G-=e_&h z^+C-YFYZ6owWnuKPJC4Wp*Ru!4G95>2#^><-3zS(O+msn4T)M-cn7X z=j?}`ArPWJ5eae=R1sk#^n@PMBZZzIl|xu_j=H)vz5)8JafKfHnqu558J2;lfCT{+ zdeoRRy>fj{zyO=#b+)6O9?}C^qg7g=WrZHLi1MJJ^jeg)##~brg;ssNn(38%*8=#i zSNwNGchj|io359U$sxkyo-Zd*L7)V}oVO)~mbTgWBt6BBQv*Nbrv`q^pC0%rpZ)Ty zudUsjXBBNCCHLtb-K9Seg-mx8y1$KE+@eLgsnD$!%Uj%L zc{f`uFTkcaB~i4|0?pG6nxpFqEwqR*zfFW2Eg~p1hj0!-%RLGxbR7zxsH#&rV)EJG z&@ws^qigg#I_N50q04lMF46`1jW=PI&eIH?qqB5|enp(7k%%KEeG}ok^*=vDg^^*t zrqIf!E2O1CzM)cVSkq10tGK1E!2?YZXGVVhV32c+Ps*Q1&4S@rJzBNeMEZ|i>D!^z z*%M!^*Xdj~xL!oN>ix~%*#2s@Z0ee4R&1B|j`v~a(3{gdn-qBVWMG1^#ASdzvH!QX z+TXYJ<$Y*4R*~TYV4?s64lMLQLzO>q>_QB~+|zm1hKCAVl+cAi96$=4TLpU?dTB_# z5dLMlhLBw4DkXDzclKDOt82RVkv&;16CG#Hrw}-T4n+EXIU$~tGF@e=`>|UYH_BRr zdK|Vj=E!t~CH0!gl9!pMR~$nxSKK;JY*b}zqD!Jn;BtCVn7F{aAi84HyW;t%ZQ{%Yj8I9^LqZzS0TeD<3$EaW&W8T@# zXs+!Y+Zs){lIaZd-t%fzna}g4EABP(RNi|wl1lO}zh-2bCHft2OyvW>q;wMLW>S*w zNWu9OD7(;YNN>Pk{8n}%hCqIi+fY7(j_mCZdl|QnB665(T%=UYGwYoUC%h~o46GElP`l2c4d zjUwgii2)61F_dSVVZ}~gH@>0U&@7Ik&wqtw_cm;){+Nax4P&hY;u`;l@^y%<&&_@J E54E69M*si- literal 0 HcmV?d00001 diff --git a/Shuttle/apple-scpt/iTerm2-stable-new-window.scpt b/Shuttle/apple-scpt/iTerm2-stable-new-window.scpt new file mode 100644 index 0000000000000000000000000000000000000000..69b7b05b56e9d192f01f59b856d6b39bf6ad7bdd GIT binary patch literal 1666 zcmb_dOKcle6g^|Ru|4DX6GBxXAsGoaCURV{>jqV6s$>yaN^PY`Y^GyVPwD}4gIrWVL2qj3qgoc1d0-9jNWr{=+0fhJhL4>eF6M&@P0H5=j!ROeSL)5bx zcHM8xxNSH&;~IR1T{-Ah&2db(q>TE=D=gNykcE@n;8Q-~U+6=aj}310WYhRm^zaD+ zA^wR-P=;U}5fQ>ie8>j|A7LPeu-Q25Sx)H{7;#DlAGSEvd%se4%7`jhP|)Cmj?MO~ zZJUAt5hZ!{p^x`@kLz6HDpw5N?-Au*hv{EY);qQ>Q4FrN_?os~X{lDArFylsMol+W zE4Zn81%o+6dU&5@D&^vcctTHa^A2c>ToakNK0MKjlx2{+!Qz_4PN-yHe*k zcBqov)xH~Fn>+U9M%!IFgS3Qbx;@|gjqR=1tF~qOcFpx<$7CO7 z54`o8SW^nm9S=+k*5sy(Cei3*FqD3sR}j+c(ui~^>aMI~dzNYWpSUyCDtT0VK8L~) z^ds{AugBDLT<2wR^*;4#rE=BmxE_V;9BSyiB$QUu7Rrm_XVn^It6KBgerls?ho;jS zFF-oxc_reU_>KDw$89$2-F_K)3^`r@9KpEizds<6lGNjpVi_Mv@-wKy*Lharjx?GQ zcTUihz$CVJ{3In^_zxYj5J6%ybNa{F02J z{2=->H$EN~+&6{DL1`_K$_EHmig{V_-XQdoVv0^7c2I6gWLZt}nUx`_LuOUR#Hq~M c(Efa8{X{i}WU*LGz?6RpISu_A^Yh>T4gEy~wEzGB literal 0 HcmV?d00001 diff --git a/apple-scripts/compile-iTermLegacy.sh b/apple-scripts/compile-iTermLegacy.sh new file mode 100755 index 0000000..d1af6f1 --- /dev/null +++ b/apple-scripts/compile-iTermLegacy.sh @@ -0,0 +1,7 @@ +#!/bin/bash +echo "compiling applescripts for iTerm Legacy..." +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-legacy-new-window.scpt -x ~/Git/shuttle/apple-scripts/iTermlegacy/iTerm-legacy-new-window.applescript +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-legacy-current-window.scpt -x ~/Git/shuttle/apple-scripts/iTermlegacy/iTerm-legacy-current-window.applescript +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-legacy-new-tab-default.scpt -x ~/Git/shuttle/apple-scripts/iTermlegacy/iTerm-legacy-new-tab-default.applescript + + diff --git a/apple-scripts/compile-iTermStable.sh b/apple-scripts/compile-iTermStable.sh index f2f5e9e..25d0b91 100755 --- a/apple-scripts/compile-iTermStable.sh +++ b/apple-scripts/compile-iTermStable.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "compiling applescripts for iTerm Stable..." -osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-stable-new-window.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm-stable-new-window.applescript -osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-stable-current-window.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm-stable-current-window.applescript -osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm-stable-new-tab-default.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm2-stable-new-window.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm2-stable-new-window.applescript +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm2-stable-current-window.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm2-stable-current-window.applescript +osacompile -o ~/Git/shuttle/Shuttle/apple-scpt/iTerm2-stable-new-tab-default.scpt -x ~/Git/shuttle/apple-scripts/iTermStable/iTerm2-stable-new-tab-default.applescript diff --git a/apple-scripts/iTermStable/iTerm-stable-current-window.applescript b/apple-scripts/iTermLegacy/iTerm-legacy-current-window.applescript similarity index 100% rename from apple-scripts/iTermStable/iTerm-stable-current-window.applescript rename to apple-scripts/iTermLegacy/iTerm-legacy-current-window.applescript diff --git a/apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript b/apple-scripts/iTermLegacy/iTerm-legacy-new-tab-default.applescript similarity index 100% rename from apple-scripts/iTermStable/iTerm-stable-new-tab-default.applescript rename to apple-scripts/iTermLegacy/iTerm-legacy-new-tab-default.applescript diff --git a/apple-scripts/iTermStable/iTerm-stable-new-window.applescript b/apple-scripts/iTermLegacy/iTerm-legacy-new-window.applescript similarity index 100% rename from apple-scripts/iTermStable/iTerm-stable-new-window.applescript rename to apple-scripts/iTermLegacy/iTerm-legacy-new-window.applescript diff --git a/apple-scripts/iTermStable/iTerm2-stable-current-window.applescript b/apple-scripts/iTermStable/iTerm2-stable-current-window.applescript new file mode 100644 index 0000000..8458376 --- /dev/null +++ b/apple-scripts/iTermStable/iTerm2-stable-current-window.applescript @@ -0,0 +1,23 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- scriptRun(argsCmd) +--end run + +on scriptRun(argsCmd) + set withCmd to (argsCmd) + CommandRun(withCmd) +end scriptRun + +on CommandRun(withCmd) + tell application "iTerm" + reopen + activate + tell the current window + tell the current session + --set name to theTitle + write text withCmd + end tell + end tell + end tell +end CommandRun diff --git a/apple-scripts/iTermStable/iTerm2-stable-new-tab-default.applescript b/apple-scripts/iTermStable/iTerm2-stable-new-tab-default.applescript new file mode 100644 index 0000000..db2acbd --- /dev/null +++ b/apple-scripts/iTermStable/iTerm2-stable-new-tab-default.applescript @@ -0,0 +1,78 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep xcode" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- scriptRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is not running then + tell application "iTerm" + activate + delay 0.2 + try + close first window + end try + end tell + + tell application "iTerm" + try + create window with profile withTheme + on error msg + create window with profile "Default" + end try + tell the current window + tell the current session + set name to theTitle + set profile to withTheme + write text withCmd + end tell + end tell + end tell + else + --assume that iTerm is open and open a new tab + try + tell application "iTerm" + activate + tell the current window + try + create tab with profile withTheme + on error msg + create tab with profile "Default" + end try + tell the current tab + tell the current session + set name to theTitle + write text withCmd + end tell + end tell + end tell + end tell + on error msg + --if all iTerm windows are closed the app stays open. In this scenario iTerm has no "current window" and will give an error when trying to create the new tab. + tell application "iTerm" + try + create window with profile withTheme + on error msg + create window with profile "Default" + end try + tell the current window + tell the current session + set name to theTitle + write text withCmd + end tell + end tell + end tell + end try + end if + end tell +end CommandRun diff --git a/apple-scripts/iTermStable/iTerm2-stable-new-window.applescript b/apple-scripts/iTermStable/iTerm2-stable-new-window.applescript new file mode 100644 index 0000000..eebb2b9 --- /dev/null +++ b/apple-scripts/iTermStable/iTerm2-stable-new-window.applescript @@ -0,0 +1,39 @@ +--for testing uncomment the "on run" block +--on run +-- set argsCmd to "ps aux | grep [s]sh" +-- set argsTheme to "Homebrew" +-- set argsTitle to "Custom title" +-- scriptRun(argsCmd, argsTheme, argsTitle) +--end run + +on scriptRun(argsCmd, argsTheme, argsTitle) + set withCmd to (argsCmd) + set withTheme to (argsTheme) + set theTitle to (argsTitle) + CommandRun(withCmd, withTheme, theTitle) +end scriptRun + +on CommandRun(withCmd, withTheme, theTitle) + tell application "iTerm" + if it is not running then + activate + delay 0.2 + try + close first window + end try + end if + end tell + tell application "iTerm" + try + create window with profile withTheme + on error msg + create window with profile "Default" + end try + tell the current window + tell the current session + set name to theTitle + write text withCmd + end tell + end tell + end tell +end CommandRun From b8d1f3db7b6a9f845cd9703e05cc4394d95b4831 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 24 Jul 2016 14:10:57 -0700 Subject: [PATCH 70/97] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 693c276..6aa9719 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,14 @@ Possible values are ```Terminal.app``` or ```iTerm``` #### ```"iTerm_version": "VALUE",``` _This changes the applescripts for iTerm (Global Setting)_ -Possible values are ```stable``` or ```nightly``` +Possible values are ```legacy``` or ```stable``` or ```nightly``` **If ```terminal``` is set to ```iTerm``` this setting is mandatory** +```"iTerm_version": "legacy",``` targeting iTerm 2.14 +```"iTerm_version": "stable",``` targeting new versions of iTerm +```"iTerm_version": "nightly",```targeting only the nightly build of iTerm + _This setting is ignored if your terminal is set to ```Terminal.app```_ ---- From 25e0920be61861031faa2d594eb30351999d2cd9 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sun, 24 Jul 2016 14:11:35 -0700 Subject: [PATCH 71/97] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6aa9719..0349aa6 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,9 @@ Possible values are ```legacy``` or ```stable``` or ```nightly``` **If ```terminal``` is set to ```iTerm``` this setting is mandatory** ```"iTerm_version": "legacy",``` targeting iTerm 2.14 + ```"iTerm_version": "stable",``` targeting new versions of iTerm + ```"iTerm_version": "nightly",```targeting only the nightly build of iTerm _This setting is ignored if your terminal is set to ```Terminal.app```_ From 347cd2656bda843cd942189ee4687eeaab0d7f38 Mon Sep 17 00:00:00 2001 From: Pluwen Date: Mon, 25 Jul 2016 09:13:35 +0800 Subject: [PATCH 72/97] remove the small icon shadow RT --- Shuttle/StatusIcon.png | Bin 547 -> 480 bytes Shuttle/StatusIcon@2x.png | Bin 1125 -> 929 bytes Shuttle/StatusIconAlt.png | Bin 454 -> 350 bytes Shuttle/StatusIconAlt@2x.png | Bin 911 -> 671 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Shuttle/StatusIcon.png b/Shuttle/StatusIcon.png index b8185e8e123f96a109282555c8bd22adf4afb01a..1a563e41a999a428657233a5f49e720033f2e26c 100644 GIT binary patch delta 438 zcmV;n0ZIO&1mFXZG=G;#L_t(I5zSOROT$nUZDPTu2{;w}STu$tC4-7M1VxJA0 zPOH_b73GkM1|r@tjJ4r#c!b%WqA1Hj5G1^AiYG)p5>;_dMt`GG7xPv8YA}wIBzffZ zGEeY*r_+)9{r)u!j$v4(bKqTy*;<+qW*1m)vr7a^k~E#B={e7jO=z0t2FwolCDMa$ zi1@LFS(cq)N%-K9@auh)C^eLp1w&+|U&_4*bd zxwTKiu?H7>XL}ashAc@iF^{6Cjjzg#EXxi)4hUG5`Po delta 505 zcmVP^KzmOnlVYt_%fR3YhI-D6qIks5lW}2%^?Loj+wGoAir5(yuY$YP;&UtX`~3%< zPUlQ3V)wydAXE5U!(OkKf`}X(D*Rc>FdPnhTdkIt5;4qRFf=HRtPsr?s@3WxCE%FF zVtL``$*4|^gMTiUYX|#bI2;BzMnzecA1QpQKNx}$?^dPX@7t+lj@fLsw|qYD5(Ht) zF&?KL8I!wPWX6OrOjBlzQw{MoINR;^DSGp1Hk)gVoV5yt!ul*u?O{9~-*da&H&(0l zqtR%XL!r<)UffMeC`r;Tg)>Yw31BCqL2%!g%Gk*hENklGXpF7P$+b|)9HKzTn7FB z!4Y4OU6k?ZXa?i)_?C9NeQyND;^-lZyO6~?%Rn!8EQ83J+G@4ln!u6RB%??45WIUw z08^=yA3qwxfR_L~LM=@Ytp?yYUWTCH{i!+MMx z#h}D%W`{w>z#l%%exSU z_!E_eBD4gE7wt)kFJTb?7e$S|qK3q7bV9j_;XNeNPj3sjRiDrIMla*t3j<=+W^t~& z3%JcZhXT4+#bWWdfZfo19#i@p!Jtc4cOe96oWrtHPlw>y%;t64^V>&@si#3<7Qry_|w` zVfAieQg9U13?>qZZ2(-t@cKA?WmRpz->+f?ugY|!zGm=ULFOKTBYWl|p8f~gWVkP* zL{Y3u#D}QPp!^|i$!l5C-r9K(kdHCp$uEpM2q?U5 zdA;5xSwQI_HU=PHb@h6EFXYoGQz*+=P@hrHp+1XRyH?+R}5-%%P?)6bdU?6lAUr|72l0END8JdSZYCqEsq< z0oZfg{!&%-sSKEAJND;E6FON30)aXpzhegxK1DkjU&vP`lbLuTgh2jFqra841Kh(< R|0e(d002ovPDHLkV1oC;sOta# delta 1089 zcmV-H1it&B2jvKmGk*jeNkls)A^@WrqQ-6XQFdvDzUO+SZGGk zOBpdAdN2)?z?Yt+Xq3>4NYH~}MA)lAFPY1S96kA<7?GeU_-Cmg=E`;LaO>`R z&(6Jf6MOK$k8{rN_dCDy+3((S?m3H#+b{bWh)o9UcDvGKGJlmyygZ%H z=c^3_0(A<7!aqJfehPI!Y@A&K+y;9Iuv>tM4+1Hb%4>l966~{TCCfpM-Dou0Ha9na z0`3mRhu}yo=o+NNjA;hVX7gc>$Mbp{jK<+d7EdCJziAF!eate5tZCQR*S|90$lN5O zSGf?8+A#qvD}O7~V3JReGm$0>+P1c~E(F`GI%>f3^74fF`S}l+=n`u(j=hl6j@XPE z(B*Qyz+=50aTcBt+stvEirS3|5bs?sA~#W^(+wGPQ@AB6z~toQSIC|!JS4k^F{0-c z?Z}@tCnx91+S*zbUoWPQZN@LgkNC(2Oi4*;6*~#v4}XC3d>ySX3$UW1B5QSZwU`!^ zrB|s`b1a{rT^3+^dip~Iko^@(OicV91Yp&&0B2`sZ?FPKvd72Ae~V=2+7h@OR-2qq zu!ptI%*>crzBD`Fd{|tcX?DI|bOy0%@8MiGy0NigLlD`ISEthr4G#}5@l~e?tg@&(gKOq7Zen9iuEOa%uE388Ni-M2}<+L%F3GT>+3V&HSy4V`1*($%+1Zc z4Y%mwj~#V59AyA>bM>q?HG_V?|7on?2!z}1 z_DY2#;{-EUSXg+}>-D+-Dfu8kf>Me{qtR%wf`2zCc0sk^KA4e_aer!RN`>$3N_KX( zD>F0Gt=H?_-QC^i7Z(?g2u5Nnldw(Y!|ey#^`D0zU`a`dv9`AMeN9cx1U~E`Z>!bn zYG`Pv!$h(&izjj@6ij3z>`F++;Jr(&s;V*<6&1Ph&dfkH2^oQSkWI}B%Fxgd$%N#B zcz+-91V*I{6v`lZgbv6s0mChorKpj~9WB+@VBCb&pK zR$c>;liB!K}*9_=}Z z1(F8YQI?jL5-KYzA1o{^{JkJyCvf)n_g~)OxSI-qDj<4DG2|k|0x?2LA@<(h-geyl zwjhD(>gq>wScDdd=r~wJx(usr>02+|N()|4VU(?gmdK^05ggNYW2Y@O_Q#w~y z*W1R%#&>(oBRD?*&qD0W%gcF!4tuTK&LUay|8D_gRPXs8(e8R+dU`B;00000NkvXX Hu0mjfQmz!7 diff --git a/Shuttle/StatusIconAlt.png b/Shuttle/StatusIconAlt.png index cdbb6daf3dfda1d50249198d84ced20bd5f1eba8..6bb90832968dfcfe23fee79db7294b55fa321a47 100644 GIT binary patch delta 257 zcmV+c0sj8R1Kt9VGyxTnH8p>NL~ke8z==qPATuA4Y#>m#DG>idHU)|Oog4!XA{l_r zd_|IhK;4SyCZO>zU^9>jn;=jlBT&2$n;eqZE+jT?CeScxAl{4S8f;u^+@?TffVwz= z_$D^pXkz;a8U|7T)YOBf5f?WGmx1sw1nN`(D#`}pb3l9%i17h^T#|JxND@DRY$--Y zMo>QG2C`29u@;g%GE)MXgUp7z5NObIkYON6ApI3c*#Pl8ApQZw=->;w0MUG)YuSKU z3y4L4cmWWDKqk=!;8O@RhzE$D0kI@LbtK6F4bvt`1Eq=qjxBSB642#S00000NkvXX Hu0mjfO^#$- delta 362 zcmV-w0hRvV0>%T7Gy#2)H8p<%qrZRuzL#7BC&COtrhx{&B-ucq0j5Cw57{6X`|sbs zKS?(5+qZ8gU(;G%Km)KQ zY!om5`SYihk&#gqt8GYvM~@!e2IRg&62fExU7`R=3XdN@eg*{3vAGN;_WJef>6j)! zMBpxLYikowR#xtpm6e^PudhGn<;$0^acKgIOW~1&OUVOSazG4-4xRfA z1TdGu=(~6Cu7m3$fuUXnR9ni!!_(y9;W0&3RkafscIQEc9zJ}ylLT{6l(GYPDnM)s z#9ES)l2yR;`6(wS#~MWy1spXWAD;<8{j3z2fuU6gLj*|x0FL)(d+YXzFaQ7m07*qo IM6N<$f?FA*!2kdN diff --git a/Shuttle/StatusIconAlt@2x.png b/Shuttle/StatusIconAlt@2x.png index 32768db6b4ed8ed47d0876bde01ca8baea98e1db..80fb8013c42a28b243e7cdfdbd6521f7a41edc21 100644 GIT binary patch delta 631 zcmV--0*L*O2cHFyGk*eBNklzbScen=Z8QY5^CJW?O+*O^CJ`^cS2!4UXNH}bJ@z0DJlyR2 z@x0Hxw=+A1!sB@CKsFtyR4NUiFB`TDSrAnMtKb}*fG9(He}5{3h_D@S2h=g;&zeyt zYUaU}s!Ex^$*7Dw4I;XLbj>!TqrL9zff*Dnf=@POitlCGat&z%ZE+NC!~YHp+u{`8 zxH7Np6md;pSLsfg>0WJBDeS1(NTVngbD>aJr$J{DMF(I%0?pPcN$1}vik>9RNlWNE zxB;cBI8hN=H-EX3xP|Ub#Qrvy$J;oc={aO2YbMeZ_RAD84^Tm~WUo1*PuI;JV(XM#>HpMTf!3IWe@9djPGgL(zFqBEm8 zp~LJld$flwUFs&~3urN?I(y{%TjUn=$0f7L5M0JC;^dnthw2$P1+Ty$@ENTn4S~-t z$tHoa%1Q8^72j70>8yy|2Xe*t8J#{!PC1Bt5wKBaoQe@_+ay{pI=Y2p0sPbk*o`-K zcZOcN*l+_j!FPv__Pa{Z?qU}b#L0U+3Z4Sl2-2lPAji}asE%JuWEbC64Y~dzyd4}X z-F=;_AYF09jezQ>a#0tPyA6SF!8g#JyBM7SggpbxI$zz`5ZG8ZV*WcJ{{Y2e2P_Ph RkGlW>002ovPDHLkV1k^zAawu$ delta 873 zcmV-v1D5=s1&;@iGk*g{Nkl=e_TJd3iIlNl(vS+slAH8ECayW?)bsHh&#iAj$s7+AAed^nrg6IE>G_nRT8_?lRJZ3TU*a| zBJZ?bsZ`cGttscZ1m<$N0jt$|UP(qfMrr({wdE4Hwzf8k0Niy{EEY3{3bfg5L$m?Z z>9@4Bl!Xu*w-}`(0^9tR;HPWE=YUBnvo4I=TV9=7=O$8emi zLSV0CsDJedP7&`2M*!-3n8Z{)2eaAih}-S%-9%elTwKlP^F_=RgTde>hr?m#5es1l z;CBl6#^SFkFqupq4~0UnXJ%%W>-9R}YXFsKG`bpzL`E$Z3vXtRvWV+syAKNw06*XX z`TO_c$jHpwb}$WfM0keyO7Cb?mC@L2gW_P=01Bo zo??rnzgyan-|v5cuf2U$&dtp|-Yvo;9qnD8N~IRac{)5i%=Lc^IZ}*jdO-o6EBSlq?PxcAP8r)k{IdF_0<|FNLTJZ>aZwsGhmgF00000NkvXXu0mjfg+rS_ From d83e044bf5fb56c8517630b448235257d43e1e6a Mon Sep 17 00:00:00 2001 From: Pluwen Date: Mon, 25 Jul 2016 09:23:58 +0800 Subject: [PATCH 73/97] Add Chinese Language RT --- Shuttle.xcodeproj/project.pbxproj | 3 + Shuttle/zh-Hans.lproj/MainMenu.xib | 100 +++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Shuttle/zh-Hans.lproj/MainMenu.xib diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index d3b0fce..1d297d1 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIconAlt@2x.png"; sourceTree = ""; }; 0ADB3B11178F3DE4004E9BB9 /* LaunchAtLoginController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = ""; }; 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; + 6F356B111D45A0DF00AF6132 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-current-window.scpt"; sourceTree = ""; }; @@ -222,6 +223,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, + "zh-Hans", ); mainGroup = C149EBEE15D5214600B1F558; productRefGroup = C149EBFA15D5214600B1F558 /* Products */; @@ -300,6 +302,7 @@ isa = PBXVariantGroup; children = ( C149EC1315D5214600B1F558 /* en */, + 6F356B111D45A0DF00AF6132 /* zh-Hans */, ); name = MainMenu.xib; sourceTree = ""; diff --git a/Shuttle/zh-Hans.lproj/MainMenu.xib b/Shuttle/zh-Hans.lproj/MainMenu.xib new file mode 100644 index 0000000..cf1ff4e --- /dev/null +++ b/Shuttle/zh-Hans.lproj/MainMenu.xib @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
From 4cd5df20ba36bd79e134bf9fc2835b4c01d37314 Mon Sep 17 00:00:00 2001 From: Pluwen Date: Mon, 25 Jul 2016 12:59:02 +0800 Subject: [PATCH 74/97] Add Chinese translate for dialogs Shuttle I18N, hope you love it --- Shuttle.xcodeproj/project.pbxproj | 22 ++- Shuttle/AboutWindowController.m | 76 ++++---- Shuttle/AppDelegate.m | 178 +++++++++--------- Shuttle/Localizable.strings | 24 +++ .../{ => en.lproj}/AboutWindowController.xib | 14 +- .../zh-Hans.lproj/AboutWindowController.xib | 77 ++++++++ 6 files changed, 250 insertions(+), 141 deletions(-) create mode 100644 Shuttle/Localizable.strings rename Shuttle/{ => en.lproj}/AboutWindowController.xib (92%) create mode 100644 Shuttle/zh-Hans.lproj/AboutWindowController.xib diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index 1d297d1..fec7212 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0A178EF8DB004E9BB9 /* StatusIcon@2x.png */; }; 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; }; 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 6FC541A21D45CF3000A896E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6FC541A11D45CF3000A896E3 /* Localizable.strings */; }; + 6FC541A31D45CF7F00A896E3 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */; }; 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */; }; A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */; }; A124BA1A1D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA171D45572B00218F2F /* iTerm2-stable-new-tab-default.scpt */; }; @@ -26,7 +28,6 @@ A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */; }; A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */; }; A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; }; - A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */; }; C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; }; C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C149EC0615D5214600B1F558 /* InfoPlist.strings */; }; C149EC0A15D5214600B1F558 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C149EC0915D5214600B1F558 /* main.m */; }; @@ -44,6 +45,9 @@ 0ADB3B11178F3DE4004E9BB9 /* LaunchAtLoginController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = ""; }; 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; 6F356B111D45A0DF00AF6132 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; + 6FC541A11D45CF3000A896E3 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 6FC541A41D45CF7F00A896E3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/AboutWindowController.xib; sourceTree = ""; }; + 6FC541A61D45CF8100A896E3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/AboutWindowController.xib"; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-current-window.scpt"; sourceTree = ""; }; @@ -60,7 +64,6 @@ A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-new-window.scpt"; sourceTree = ""; }; A1D700051A5DCDF4003563E4 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = ""; }; A1D700061A5DCE8D003563E4 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = ""; }; - A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindowController.xib; sourceTree = ""; }; C149EBF915D5214600B1F558 /* Shuttle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shuttle.app; sourceTree = BUILT_PRODUCTS_DIR; }; C149EBFD15D5214600B1F558 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; C149EC0015D5214600B1F558 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -171,7 +174,7 @@ C149EC0415D5214600B1F558 /* Supporting Files */, A1D700051A5DCDF4003563E4 /* AboutWindowController.h */, A1D700061A5DCE8D003563E4 /* AboutWindowController.m */, - A1D700081A5DCFE1003563E4 /* AboutWindowController.xib */, + 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */, ); path = Shuttle; sourceTree = ""; @@ -184,6 +187,7 @@ C149EC0915D5214600B1F558 /* main.m */, C149EC0B15D5214600B1F558 /* Shuttle-Prefix.pch */, C149EC0C15D5214600B1F558 /* Credits.rtf */, + 6FC541A11D45CF3000A896E3 /* Localizable.strings */, ); name = "Supporting Files"; sourceTree = ""; @@ -242,12 +246,13 @@ files = ( A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */, A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */, + 6FC541A21D45CF3000A896E3 /* Localizable.strings in Resources */, A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */, 0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */, A124BA1F1D4558E500218F2F /* iTerm-legacy-current-window.scpt in Resources */, 0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */, C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */, - A1D700091A5DCFE1003563E4 /* AboutWindowController.xib in Resources */, + 6FC541A31D45CF7F00A896E3 /* AboutWindowController.xib in Resources */, 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */, 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */, A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */, @@ -282,6 +287,15 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */ = { + isa = PBXVariantGroup; + children = ( + 6FC541A41D45CF7F00A896E3 /* en */, + 6FC541A61D45CF8100A896E3 /* zh-Hans */, + ); + name = AboutWindowController.xib; + sourceTree = ""; + }; C149EC0615D5214600B1F558 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/Shuttle/AboutWindowController.m b/Shuttle/AboutWindowController.m index 08b3a2a..d60d1bc 100644 --- a/Shuttle/AboutWindowController.m +++ b/Shuttle/AboutWindowController.m @@ -22,58 +22,58 @@ @implementation AboutWindowController - (id)initWithWindow:(NSWindow *)window { - aboutWindow = [super initWithWindow:window]; - if (self) { - // Initialization code here. - } - return self; + aboutWindow = [super initWithWindow:window]; + if (self) { + // Initialization code here. } + return self; +} - (void)windowDidLoad { - [super windowDidLoad]; - //Prevent the window from changing positions after multiple opens. - [aboutWindow setShouldCascadeWindows:NO]; + [super windowDidLoad]; + //Prevent the window from changing positions after multiple opens. + [aboutWindow setShouldCascadeWindows:NO]; - //Load the plist so we can get current info for the about box. - plistDict = [[NSBundle mainBundle] infoDictionary]; + //Load the plist so we can get current info for the about box. + plistDict = [[NSBundle mainBundle] infoDictionary]; - //Get the application name. - id applicationName = [plistDict objectForKey:@"CFBundleName"]; - //Get the build version. - id applicationVersion = [plistDict objectForKey:@"CFBundleVersion"]; - //Get the copyright. - id applicationCopyright = [plistDict objectForKey:@"NSHumanReadableCopyright"]; + //Get the application name. + id applicationName = [plistDict objectForKey:@"CFBundleName"]; + //Get the build version. + id applicationVersion = [plistDict objectForKey:@"CFBundleVersion"]; + //Get the copyright. + id applicationCopyright = [plistDict objectForKey:@"NSHumanReadableCopyright"]; - //Build the string for the windows title. - NSString *aboutTitle = [NSString stringWithFormat:@"%@%@", @"About ", applicationName]; - [aboutWindow.window setTitle:aboutTitle]; + //Build the string for the windows title. + NSString *aboutTitle = [NSString stringWithFormat:@"%@%@", NSLocalizedString(@"About ",nil), applicationName]; + [aboutWindow.window setTitle:aboutTitle]; - //Build the string for the application name. appName - tagline - NSString *progName = [NSString stringWithFormat:@"%@%@", applicationName, @" - A simple SSH shortcut menu."]; - [appName setStringValue:progName]; + //Build the string for the application name. appName - tagline + NSString *progName = [NSString stringWithFormat:@"%@%@", applicationName, NSLocalizedString(@" - A simple SSH shortcut menu.",nil)]; + [appName setStringValue:progName]; - //Build the string for the version. Version: $build - NSString *progVersion = [NSString stringWithFormat:@"%@%@", @"Version: ", applicationVersion]; - [appVersion setStringValue:progVersion]; + //Build the string for the version. Version: $build + NSString *progVersion = [NSString stringWithFormat:@"%@%@", NSLocalizedString(@"Version: ",nil), applicationVersion]; + [appVersion setStringValue:progVersion]; - //Make the copyright font smaller. - [appCopyright setFont:[NSFont systemFontOfSize:10]]; - [appCopyright setStringValue:applicationCopyright]; + //Make the copyright font smaller. + [appCopyright setFont:[NSFont systemFontOfSize:10]]; + [appCopyright setStringValue:applicationCopyright]; - } +} - (IBAction)btnHomepage:(id)sender { - //Get the homepage from the plist - id applicationHomepage = [plistDict objectForKey:@"Product Homepage"]; - //Build the homepage's URL. - NSURL *homeURL = [NSURL URLWithString:applicationHomepage]; + //Get the homepage from the plist + id applicationHomepage = [plistDict objectForKey:@"Product Homepage"]; + //Build the homepage's URL. + NSURL *homeURL = [NSURL URLWithString:applicationHomepage]; - //Go to the website. - [[NSWorkspace sharedWorkspace] openURL:homeURL]; + //Go to the website. + [[NSWorkspace sharedWorkspace] openURL:homeURL]; - //Close the about box. - [aboutWindow close]; - } + //Close the about box. + [aboutWindow close]; +} @end diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index a76ef51..7314e7d 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -167,7 +167,7 @@ - (NSDictionary*) parseSSHConfigFile { } } - return servers; + return servers; } // Replaces Underscores with Spaces for better readable names @@ -190,10 +190,10 @@ - (void) loadMenu { // Check valid JSON syntax if ( !json ) { NSMenuItem *menuItem = [menu insertItemWithTitle:@"Error parsing config" - action:false - keyEquivalent:@"" - atIndex:0 - ]; + action:false + keyEquivalent:@"" + atIndex:0 + ]; [menuItem setEnabled:false]; return; } @@ -207,13 +207,13 @@ - (void) loadMenu { shuttleHosts = json[@"hosts"]; ignoreHosts = json[@"ssh_config_ignore_hosts"]; ignoreKeywords = json[@"ssh_config_ignore_keywords"]; - + // Should we merge ssh config hosts? BOOL showSshConfigHosts = YES; if ([[json allKeys] containsObject:(@"show_ssh_config_hosts")] && [json[@"show_ssh_config_hosts"] boolValue] == NO) { showSshConfigHosts = NO; } - + if (showSshConfigHosts) { // Read configuration from ssh config NSDictionary* servers = [self parseSSHConfigFile]; @@ -295,7 +295,7 @@ - (void) loadMenu { itemList = newList; } } - + // if everything worked out we will see a non-nil itemList where the // system should be appended to. part hold the last part of the splitted string (aka hostname). if (itemList) { @@ -343,7 +343,7 @@ - (void) buildMenu:(NSArray*)data addToMenu:(NSMenu *)m { [menuItem setTitle:key]; [menuItem setSubmenu:subMenu]; [m insertItem:menuItem atIndex:pos++]; - + // build submenu [self buildMenu:menus[key] addToMenu:subMenu]; } @@ -380,7 +380,7 @@ - (void) openHost:(NSMenuItem *) sender { NSString *errorMessage; NSString *errorInfo; - + //Place the comma delimited string of menu item settings into an array NSArray *objectsFromJSON = [[sender representedObject] componentsSeparatedByString:(@"¬_¬")]; @@ -403,14 +403,14 @@ - (void) openHost:(NSMenuItem *) sender { //we have no global theme and there is no theme in the command settings. //Forcing the Default profile for iTerm and the basic profile for Terminal.app terminalTheme = @"Default"; - }else{ - terminalTheme = @"basic"; - } - //We have a global setting using this as the theme. + }else{ + terminalTheme = @"basic"; + } + //We have a global setting using this as the theme. }else { terminalTheme = themePref; } - //we have command level theme override the Global default_theme settings. + //we have command level theme override the Global default_theme settings. }else{ terminalTheme = [objectsFromJSON objectAtIndex:1]; } @@ -433,14 +433,14 @@ - (void) openHost:(NSMenuItem *) sender { //open_in was not empty or bad value we are passing the settings. terminalWindow = openInPref; }else{ - //inTerminal is not null and overrides the default values of open_in + //inTerminal is not null and overrides the default values of open_in terminalWindow = [objectsFromJSON objectAtIndex:3]; - if( ![terminalWindow isEqualToString:@"new"] && ![terminalWindow isEqualToString:@"current"] && ![terminalWindow isEqualToString:@"tab"]) - { - errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",terminalWindow,@"'", @"is not a valid value for inTerminal. Please fix this in the JSON file"]; - errorInfo = @"bad \"inTerminal\":\"VALUE\" in the JSON settings"; - [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:NO]; - } + if( ![terminalWindow isEqualToString:@"new"] && ![terminalWindow isEqualToString:@"current"] && ![terminalWindow isEqualToString:@"tab"]) + { + errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",terminalWindow,@"'", NSLocalizedString(@"is not a valid value for inTerminal. Please fix this in the JSON file",nil)]; + errorInfo = NSLocalizedString(@"bad \"inTerminal\":\"VALUE\" in the JSON settings",nil); + [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:NO]; + } } //Set Paths to iTerm Legacy AppleScripts @@ -452,7 +452,7 @@ - (void) openHost:(NSMenuItem *) sender { NSString *iTermStableNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-new-window" ofType:@"scpt"]; NSString *iTermStableCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-current-window" ofType:@"scpt"]; NSString *iTermStableNewTabDefault = [[NSBundle mainBundle] pathForResource:@"iTerm2-stable-new-tab-default" ofType:@"scpt"]; - + //Set Paths to iTerm Nightly AppleScripts NSString *iTerm2NightlyNewWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-new-window" ofType:@"scpt"]; NSString *iTerm2NightlyCurrentWindow = [[NSBundle mainBundle] pathForResource:@"iTerm2-nightly-current-window" ofType:@"scpt"]; @@ -469,7 +469,7 @@ - (void) openHost:(NSMenuItem *) sender { //script expects the following order: Command, Theme, Title NSArray *passParameters = @[escapedObject, terminalTheme, terminalTitle]; -// Check if Url + // Check if Url NSURL* url = [NSURL URLWithString:escapedObject]; if(url) { @@ -480,23 +480,23 @@ - (void) openHost:(NSMenuItem *) sender { //If the JSON prefs for iTermVersion are not stable or nightly throw an error if( ![iTermVersionPref isEqualToString: @"legacy"] && ![iTermVersionPref isEqualToString: @"stable"] && ![iTermVersionPref isEqualToString:@"nightly"] ) { - + if( iTermVersionPref == 0 ) { - errorMessage = @"\"iTerm_version\": \"VALUE\", is missing.\n\n\"VALUE\" can be:\n\"legacy\" targeting iTerm 2.14\n\"stable\" targeting new versions.\n\"nightly\" targeting nightly builds.\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help."; - errorInfo = @"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have another version of iTerm installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle."; + errorMessage = NSLocalizedString(@"\"iTerm_version\": \"VALUE\", is missing.\n\n\"VALUE\" can be:\n\"legacy\" targeting iTerm 2.14\n\"stable\" targeting new versions.\n\"nightly\" targeting nightly builds.\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help.",nil); + errorInfo = NSLocalizedString(@"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have another version of iTerm installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle.",nil); [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:YES]; iTermVersionPref = @"stable"; - + }else{ - errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",iTermVersionPref,@"'", @"is not a valid value for iTerm_version. Please fix this in the JSON file"]; - errorInfo = @"bad \"iTerm_version\": \"VALUE\" in the JSON settings"; + errorMessage = [NSString stringWithFormat:@"%@%@%@ %@",@"'",iTermVersionPref,@"'", NSLocalizedString(@"is not a valid value for iTerm_version. Please fix this in the JSON file",nil)]; + errorInfo = NSLocalizedString(@"bad \"iTerm_version\": \"VALUE\" in the JSON settings",nil); [self throwError:errorMessage additionalInfo:errorInfo continueOnErrorOption:NO]; } } if( [iTermVersionPref isEqualToString:@"legacy"]) { - - //run the applescript that works with iTerm Legacy + + //run the applescript that works with iTerm Legacy //if we are running in a new iTerm "Stable" Window if ( [terminalWindow isEqualToString:@"new"] ) { [self runScript:iTermLegacyNewWindow handler:handlerName parameters:passParameters]; @@ -507,7 +507,7 @@ - (void) openHost:(NSMenuItem *) sender { } //we are using the default action of shuttle... The active window in a new tab if ( [terminalWindow isEqualToString:@"tab"] ) { - [self runScript:iTermLegacyNewTabDefault handler:handlerName parameters:passParameters]; + [self runScript:iTermLegacyNewTabDefault handler:handlerName parameters:passParameters]; } } //iTermVersion is not set to "legacy" using applescripts Configured for Stable @@ -572,49 +572,49 @@ - (void) runScript:(NSString *)scriptPath handler:(NSString*)handlerName paramet NSDictionary * appleScriptCreationError = nil; appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL error:&appleScriptCreationError]; - if (handlerName && [handlerName length]) + if (handlerName && [handlerName length]) + { + /* If we have a handlerName (and potentially parameters), we build + * an NSAppleEvent to execute the script. */ + + //Get a descriptor + int pid = [[NSProcessInfo processInfo] processIdentifier]; + thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID + bytes:&pid + length:sizeof(pid)]; + + //Create the container event + + //We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework... +#define kASAppleScriptSuite 'ascr' +#define kASSubroutineEvent 'psbr' +#define keyASSubroutineName 'snam' + containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite + eventID:kASSubroutineEvent + targetDescriptor:thisApplication + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + //Set the target handler + [containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:handlerName] + forKeyword:keyASSubroutineName]; + + //Pass parameters - parameters is expecting an NSArray with only NSString objects + if ([parametersInArray count]) { - /* If we have a handlerName (and potentially parameters), we build - * an NSAppleEvent to execute the script. */ - - //Get a descriptor - int pid = [[NSProcessInfo processInfo] processIdentifier]; - thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID - bytes:&pid - length:sizeof(pid)]; - //Create the container event + NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor]; + NSString *object; - //We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework... - #define kASAppleScriptSuite 'ascr' - #define kASSubroutineEvent 'psbr' - #define keyASSubroutineName 'snam' - containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite - eventID:kASSubroutineEvent - targetDescriptor:thisApplication - returnID:kAutoGenerateReturnID - transactionID:kAnyTransactionID]; - //Set the target handler - [containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:handlerName] - forKeyword:keyASSubroutineName]; - - //Pass parameters - parameters is expecting an NSArray with only NSString objects - if ([parametersInArray count]) - { - - NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor]; - NSString *object; - - for (object in parametersInArray) { - [arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object] - atIndex:([arguments numberOfItems] +1)]; - } - - [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject]; + for (object in parametersInArray) { + [arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object] + atIndex:([arguments numberOfItems] +1)]; } - //Execute the event - [appleScript executeAppleEvent:containerEvent error:nil]; + + [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject]; } + //Execute the event + [appleScript executeAppleEvent:containerEvent error:nil]; + } } - (IBAction)showImportPanel:(id)sender { @@ -631,7 +631,7 @@ - (IBAction)showImportPanel:(id)sender { //Delete the old configuration file [[NSFileManager defaultManager] removeItemAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@".shuttle.json.backup"] error: nil]; } else { - return; + return; } } @@ -643,11 +643,11 @@ -(void) throwError:(NSString*)errorMessage additionalInfo:(NSString*)errorInfo c [alert setAlertStyle:NSWarningAlertStyle]; if (continueOption) { - [alert addButtonWithTitle:@"Quit"]; - [alert addButtonWithTitle:@"Continue"]; + [alert addButtonWithTitle:NSLocalizedString(@"Quit",nil)]; + [alert addButtonWithTitle:NSLocalizedString(@"Continue",nil)]; }else{ - [alert addButtonWithTitle:@"Quit"]; + [alert addButtonWithTitle:NSLocalizedString(@"Quit",nil)]; } if ([alert runModal] == NSAlertFirstButtonReturn) { @@ -658,12 +658,12 @@ -(void) throwError:(NSString*)errorMessage additionalInfo:(NSString*)errorInfo c - (IBAction)showExportPanel:(id)sender { NSSavePanel * savePanelObj = [NSSavePanel savePanel]; //Display the Save Panel - NSInteger result = [savePanelObj runModal]; - if (result == NSFileHandlingPanelOKButton) { - NSURL *saveURL = [savePanelObj URL]; - // then copy a previous file to the new location - [[NSFileManager defaultManager] copyItemAtPath:shuttleConfigFile toPath:saveURL.path error:nil]; - } + NSInteger result = [savePanelObj runModal]; + if (result == NSFileHandlingPanelOKButton) { + NSURL *saveURL = [savePanelObj URL]; + // then copy a previous file to the new location + [[NSFileManager defaultManager] copyItemAtPath:shuttleConfigFile toPath:saveURL.path error:nil]; + } } - (IBAction)configure:(id)sender { @@ -692,16 +692,16 @@ - (IBAction)configure:(id)sender { } - (IBAction)showAbout:(id)sender { - - //Call the windows controller - AboutWindowController *aboutWindow = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindowController"]; - //Set the window to stay on top - [aboutWindow.window makeKeyAndOrderFront:nil]; - [aboutWindow.window setLevel:NSFloatingWindowLevel]; + //Call the windows controller + AboutWindowController *aboutWindow = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindowController"]; + + //Set the window to stay on top + [aboutWindow.window makeKeyAndOrderFront:nil]; + [aboutWindow.window setLevel:NSFloatingWindowLevel]; - //Show the window - [aboutWindow showWindow:self]; + //Show the window + [aboutWindow showWindow:self]; } - (IBAction)quit:(id)sender { diff --git a/Shuttle/Localizable.strings b/Shuttle/Localizable.strings new file mode 100644 index 0000000..77499bd --- /dev/null +++ b/Shuttle/Localizable.strings @@ -0,0 +1,24 @@ +/* + Localizable.strings + Shuttle + + Created by pluwen on 16/7/25. + Copyright © 2016年 fitztrev. All rights reserved. +*/ +/* NSLocalizedString(@"",nil)*/ + +"About " = "关于 "; +"Version: " = "版本:"; +" - A simple SSH shortcut menu." = " - 简易的 SSH 快捷菜单。"; + +"Quit" = "退出"; +"Continue" = "继续"; + +"is not a valid value for inTerminal. Please fix this in the JSON file" = "不是一个有效的 inTerminal 值。请修改 JSON 文件。"; +"bad \"inTerminal\":\"VALUE\" in the JSON settings" = "错误的 \"inTerminal\":\"VALUE\" 在 JSON 设置中"; + +"\"iTerm_version\": \"VALUE\", is missing.\n\n\"VALUE\" can be:\n\"legacy\" targeting iTerm 2.14\n\"stable\" targeting new versions.\n\"nightly\" targeting nightly builds.\n\nPlease fix your shuttle JSON settings.\nSee readme.md on shuttle's github for help." = "\"iTerm_version\": \"VALUE\", 已丢失。\n\n\"VALUE\" 可以是:\n\"legacy\" 指向 iTerm 2.14\n\"stable\" 指向新版本。\n\"nightly\" 指向内测版本.\n\n请修复你的 Shuttle JSON 设置。\n更多帮助,请浏览 Shuttle 在 GitHub 上的 readme.md 文件。"; +"Press Continue to try iTerm stable applescripts.\n -->(not recommended)<--\nThis will fail if you have another version of iTerm installed.\n\nPlease fix the JSON settings.\nPress Quit to exit shuttle." = "点击“继续”按钮尝试 iTerm 稳定版的 AppleScript。\n -->(不推荐)<--\n如果你有另外一个 iTerm 版本,将会失败。\n\n请修复 JSON 设置。\n点击“退出”按钮退出 Shuttle。"; + +"is not a valid value for iTerm_version. Please fix this in the JSON file" = "不是一个有效的 iTerm_version 值。请修改 JSON 文件。"; +"bad \"iTerm_version\": \"VALUE\" in the JSON settings" = "错误的 \"iTerm_version\": \"VALUE\" 在 JSON 设置中"; \ No newline at end of file diff --git a/Shuttle/AboutWindowController.xib b/Shuttle/en.lproj/AboutWindowController.xib similarity index 92% rename from Shuttle/AboutWindowController.xib rename to Shuttle/en.lproj/AboutWindowController.xib index 19a5411..8de61f3 100644 --- a/Shuttle/AboutWindowController.xib +++ b/Shuttle/en.lproj/AboutWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -19,7 +19,7 @@ - + @@ -28,12 +28,10 @@ - - @@ -42,7 +40,6 @@ - @@ -51,7 +48,6 @@ - @@ -60,7 +56,6 @@ - @@ -78,6 +72,6 @@ - + diff --git a/Shuttle/zh-Hans.lproj/AboutWindowController.xib b/Shuttle/zh-Hans.lproj/AboutWindowController.xib new file mode 100644 index 0000000..c92dbc1 --- /dev/null +++ b/Shuttle/zh-Hans.lproj/AboutWindowController.xib @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a4f4b6c69d8b584f9204caa147709a4566ea855f Mon Sep 17 00:00:00 2001 From: Pluwen Date: Mon, 25 Jul 2016 13:18:53 +0800 Subject: [PATCH 75/97] edit my name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0349aa6..c08af04 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ This project was created by [Trevor Fitzgerald](https://github.com/fitztrev). I * [Matt Turner](https://github.com/thshdw) * [Michael Davis](https://github.com/mpdavis) * [Morton Fox](https://github.com/mortonfox) -* [pluwen](https://github.com/pluwen) +* [Pluwen](https://github.com/pluwen) * [Rui Rodrigues](https://github.com/rmrodrigues) * [Ryan Cohen](https://github.com/imryan) * [Stefan Jansen](https://github.com/steffex) From 50bbb9d2fb2bf3fd295deffa30d643109041a995 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Fri, 29 Jul 2016 20:37:34 -0700 Subject: [PATCH 76/97] Moved the Documentation to the wiki Moved all the documentation the wiki --- README.md | 186 +----------------------------------------------------- 1 file changed, 2 insertions(+), 184 deletions(-) diff --git a/README.md b/README.md index c08af04..99a7ed3 100644 --- a/README.md +++ b/README.md @@ -15,190 +15,8 @@ A simple SSH shortcut menu for OS X 1. Download [Shuttle](http://fitztrev.github.io/shuttle/) 2. Copy to Applications -## JSON Path Change - -In your home directory create a file called ```~/.shuttle.path``` -In this file should be a single line with the path to the JSON settings file. - -``` -/Users/thshdw/Dropbox/shuttle/shuttle.json -``` -shuttle will read ```~/.shuttle.path``` first and use its contents as the path to your JSON file. - -## JSON Options -### Global settings -#### ```"editor": "VALUE",``` -_This changes the app that opens settings.json for editing (Global Setting)_ - -Possible values are ```default```, ```nano```, ```vi```, ```vim``` or any terminal based editor. -```default``` opens settings.json in whatever app is registered as the default for extension ```.json``` -``` -"editor": "vim", -``` -would open ```~/.shuttle.json``` in vim - ----- - -#### ```"launch_at_login": VALUE,``` -_This allows you to flag the shuttle.app to start automatically (Global Setting)_ - -Possible values are ```true``` or ```false``` - ----- - -#### ```"terminal": "VALUE",``` -_This allows you to set the default terminal (Global Setting)_ - -Possible values are ```Terminal.app``` or ```iTerm``` - ----- - -#### ```"iTerm_version": "VALUE",``` -_This changes the applescripts for iTerm (Global Setting)_ - -Possible values are ```legacy``` or ```stable``` or ```nightly``` - -**If ```terminal``` is set to ```iTerm``` this setting is mandatory** - -```"iTerm_version": "legacy",``` targeting iTerm 2.14 - -```"iTerm_version": "stable",``` targeting new versions of iTerm - -```"iTerm_version": "nightly",```targeting only the nightly build of iTerm - -_This setting is ignored if your terminal is set to ```Terminal.app```_ - ----- - -#### ```"default_theme": "Homebrew",``` -_This sets the Terminal theme for all windows. (Global Setting)_ - -Possible values are the Profile names in your terminal preferences. iTerm ships with one Profile named "Default". OS X Terminal ships with several. To see the names see the preferences area of the terminal you are using. - -In iTerm the profile names are case sensitive. - -**Please ensure the theme names you set are valid. If shuttle passes theme "Dagobah" and it does not exist in iTerm, shuttle's applescripts fall back to the default profile. In iTerm this profile is called ```Default```. -If you have removed ```Default``` or renamed it shuttle may not open your command.** - -This setting can be overwritten by the command level ```"theme"``` settings - ----- - -#### ```"open_in": "VALUE",``` -_This changes the default action for how commands are opened (Global Setting)_ - -Possible values are ```tab``` or ```new```. - -```tab``` opens the command in the active terminal in a new tab. - -```new``` opens the command in a new window. - -This setting can be overwritten by the command level ```"inTerminal"``` settings - ----- - -#### ```"show_ssh_config_hosts": VALUE,``` -_This changes parsing ssh config. By default, Shuttle will parse your ```~/.ssh/config``` file for hosts. (Global Setting)_ - -Possible values are ```false``` or ```true``` - ----- - -#### ```"ssh_config_ignore_hosts": ["VALUE", "VALUE"],``` -_This will ignore hosts in the ssh config. (Global Setting)_ - -Possible values are the hosts in your config that you want to ignore. If you had github.com and git.example.com in your ssh config, to ignore them you set: - -```"ssh_config_ignore_hosts": ["github.com", "git.example.com"],``` - ----- - -#### ```"ssh_config_ignore_keywords": ["VALUE"],``` -_This will ignore keywords in your ssh config. (Global Setting)_ - -Possible values are the keywords in your ssh config that you want to ignore. - ----- - -**Additional ssh config customization** -#### Nested menus for `~/.ssh/config` hosts - -##### Create a menu item at "work" > "servers" > "web01" - -``` -Host work/servers/web01 - HostName user@web01.example.com -``` -\- *or* - - -``` -Host gandalf - # shuttle.name = work/servers/web01 (webserver) - HostName user@web01.example.com -``` - -### Command level settings -_Command level settings are unique to your command and will overwrite the Global setting equivalent_ - -#### ```"cmd": "VALUE"``` -_This is the command / script that will be launched in the terminal. (Command setting)_ - -Where Value is a command or script. -``` -"cmd": "ps aux | grep [s]sh" -``` -Would check for ssh processes. - ----- - -#### ```"name": "VALUE"``` -_This sets the text that will appear in shuttles drop down menu. (Command setting)_ - -Were Value is the text you want to see in the drop down menu for this command. -``` -"name": "SSH to my wordpress blog" -``` - -This value can also set the title of the terminal window if ```"title" :"VALUE"``` is not set. - ----- - -#### ```"inTerminal": "VALUE",``` -_This sets how command will open in the terminal window. (Command setting)_ - -Possible values are ```new```, ```tab```, or ```current``` - -```new``` opens the command in a new terminal window. - -```tab``` opens the command in the active terminal window in a new tab. - -```current``` opens the command in the active terminal's window. - -When using using ```current``` I recommend that you wrap the command in some user input like this: - -``` -echo "are you sure y/n"; read sure; if [ "$sure" == "y" ]; then echo "running command" && ps aux | grep [s]sh; else echo "exiting..."; fi -``` - -Do this as a precaution as it could be possible to run a command on the wrong host. - ----- - -#### ```"theme": "VALUE",``` -_This sets the theme for the terminal window. (Command setting)_ - -Possible values are the profile names for iTerm or OS X Terminal. - -If ```"theme"``` is not set and ```"default_theme"``` is not set then shuttle passes Profile ```Default``` for iTerm and Profile ```basic``` for OS X terminal. - ----- - -#### ```"title": "VALUE"``` -_This sets the text that will appear in the terminal's title bar. (Command setting)_ - -Where VALUE is the text you want to set in the terminals title bar. - -If ```title``` is missing shuttle uses the menu's name and sets this as ```title``` +## Help +See the [Wiki](https://github.com/fitztrev/shuttle/wiki) pages. ## Roadmap From fb4bffa31b13241653a75a5db71993c006be94d0 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Tue, 16 Aug 2016 09:35:45 -0700 Subject: [PATCH 77/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99a7ed3..96732ce 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/fitztrev/shuttle](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fitztrev/shuttle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -A simple SSH shortcut menu for OS X +A simple SSH shortcut menu for macOS [http://fitztrev.github.io/shuttle/](http://fitztrev.github.io/shuttle/) From 9cc5b7e45cb61920777d7eb09c722116e32d5e72 Mon Sep 17 00:00:00 2001 From: bihicheng Date: Thu, 6 Oct 2016 13:14:28 +0800 Subject: [PATCH 78/97] Fixed bug that can not edit config file --- Shuttle/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 7314e7d..7fcbfec 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -678,7 +678,7 @@ - (IBAction)configure:(id)sender { NSString *editorCommand = [NSString stringWithFormat:@"%@ %@", editorPref, shuttleConfigFile]; //build the reprensented object. It's expecting menuCmd, termTheme, termTitle, termWindow, menuName - NSString *editorRepObj = [NSString stringWithFormat:@"%@,%@,%@,%@,%@", editorCommand, nil, @"Editing shuttle JSON", nil, nil]; + NSString *editorRepObj = [NSString stringWithFormat:@"%@¬_¬%@¬_¬%@¬_¬%@¬_¬%@", editorCommand, nil, @"Editing shuttle JSON", nil, nil]; //make a menu item for the command selector(openHost:) runs in a new terminal window. NSMenuItem *editorMenu = [[NSMenuItem alloc] initWithTitle:@"editJSONconfig" action:@selector(openHost:) keyEquivalent:(@"")]; From 2bfae1e57b3581dbb8df2a1ef115e8fa95ce80a7 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Tue, 18 Oct 2016 12:51:51 -0700 Subject: [PATCH 79/97] v1.2.8 fixes Menus have been translated to Spanish Added a bash script to the default JSON file that allows writing a command to terminal without execution #200 Fixed an issue that prevented character escapes #194 Fixed an issue that prevented tabs from opening in terminal on macOS #198 Fixed an issue where english was not the default language. --- README.md | 2 + Shuttle.xcodeproj/project.pbxproj | 58 +- Shuttle/AppDelegate.m | 2 +- .../AboutWindowController.xib | 23 +- Shuttle/Base.lproj/Localizable.strings | 4 + Shuttle/{en.lproj => Base.lproj}/MainMenu.xib | 0 Shuttle/Shuttle-Info.plist | 6 +- .../apple-scpt/terminal-new-tab-default.scpt | Bin 2422 -> 2802 bytes .../en.lproj/AboutWindowController.strings | 15 + Shuttle/en.lproj/Localizable.strings | 4 + Shuttle/en.lproj/MainMenu.strings | 42 ++ .../es.lproj/AboutWindowController.strings | 18 + Shuttle/es.lproj/Credits.rtf | 29 + Shuttle/es.lproj/InfoPlist.strings | 2 + Shuttle/es.lproj/Localizable.strings | 9 + Shuttle/es.lproj/MainMenu.strings | 45 ++ Shuttle/es.lproj/MainMenu.xib | 535 ++++++++++++++++++ Shuttle/shuttle.default.json | 8 +- .../AboutWindowController.strings | 15 + .../zh-Hans.lproj/AboutWindowController.xib | 77 --- .../{ => zh-Hans.lproj}/Localizable.strings | 0 Shuttle/zh-Hans.lproj/MainMenu.strings | 42 ++ Shuttle/zh-Hans.lproj/MainMenu.xib | 100 ---- .../terminal-new-tab-default.applescript | 17 +- 24 files changed, 837 insertions(+), 216 deletions(-) rename Shuttle/{en.lproj => Base.lproj}/AboutWindowController.xib (81%) create mode 100644 Shuttle/Base.lproj/Localizable.strings rename Shuttle/{en.lproj => Base.lproj}/MainMenu.xib (100%) create mode 100644 Shuttle/en.lproj/AboutWindowController.strings create mode 100644 Shuttle/en.lproj/Localizable.strings create mode 100644 Shuttle/en.lproj/MainMenu.strings create mode 100644 Shuttle/es.lproj/AboutWindowController.strings create mode 100644 Shuttle/es.lproj/Credits.rtf create mode 100644 Shuttle/es.lproj/InfoPlist.strings create mode 100644 Shuttle/es.lproj/Localizable.strings create mode 100644 Shuttle/es.lproj/MainMenu.strings create mode 100644 Shuttle/es.lproj/MainMenu.xib create mode 100644 Shuttle/zh-Hans.lproj/AboutWindowController.strings delete mode 100644 Shuttle/zh-Hans.lproj/AboutWindowController.xib rename Shuttle/{ => zh-Hans.lproj}/Localizable.strings (100%) create mode 100644 Shuttle/zh-Hans.lproj/MainMenu.strings delete mode 100644 Shuttle/zh-Hans.lproj/MainMenu.xib diff --git a/README.md b/README.md index 96732ce..c64d755 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ This project was created by [Trevor Fitzgerald](https://github.com/fitztrev). I (In alphabetical order) * [Alex Carter](https://github.com/blazeworx) +* [bihicheng](https://github.com/bihicheng) * [Dave Eddy](https://github.com/bahamas10) * [Dmitry Filimonov](https://github.com/petethepig) * [Frank Enderle](https://github.com/fenderle) @@ -48,6 +49,7 @@ This project was created by [Trevor Fitzgerald](https://github.com/fitztrev). I * [Michael Davis](https://github.com/mpdavis) * [Morton Fox](https://github.com/mortonfox) * [Pluwen](https://github.com/pluwen) +* Rebecca Dominguez * [Rui Rodrigues](https://github.com/rmrodrigues) * [Ryan Cohen](https://github.com/imryan) * [Stefan Jansen](https://github.com/steffex) diff --git a/Shuttle.xcodeproj/project.pbxproj b/Shuttle.xcodeproj/project.pbxproj index fec7212..f3dcc0c 100644 --- a/Shuttle.xcodeproj/project.pbxproj +++ b/Shuttle.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0A178EF8DB004E9BB9 /* StatusIcon@2x.png */; }; 0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; }; 0ADB3B13178F3DE4004E9BB9 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 6FC541A21D45CF3000A896E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6FC541A11D45CF3000A896E3 /* Localizable.strings */; }; 6FC541A31D45CF7F00A896E3 /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */; }; 7E74A7C61789CE2F0079E0D2 /* shuttle.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */; }; A124BA191D45572B00218F2F /* iTerm2-stable-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */; }; @@ -27,6 +26,7 @@ A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BED1BCF2C73004F52A6 /* terminal-current-window.scpt */; }; A12D9BF71BCF2C73004F52A6 /* terminal-new-tab-default.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */; }; A12D9BF81BCF2C73004F52A6 /* terminal-new-window.scpt in Resources */ = {isa = PBXBuildFile; fileRef = A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */; }; + A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1B7B9DF1DB53ED200809327 /* Localizable.strings */; }; A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; }; C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; }; C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C149EC0615D5214600B1F558 /* InfoPlist.strings */; }; @@ -44,10 +44,6 @@ 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIconAlt@2x.png"; sourceTree = ""; }; 0ADB3B11178F3DE4004E9BB9 /* LaunchAtLoginController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = ""; }; 0ADB3B12178F3DE4004E9BB9 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = ""; }; - 6F356B111D45A0DF00AF6132 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; - 6FC541A11D45CF3000A896E3 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; - 6FC541A41D45CF7F00A896E3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/AboutWindowController.xib; sourceTree = ""; }; - 6FC541A61D45CF8100A896E3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/AboutWindowController.xib"; sourceTree = ""; }; 7E72D21E178003ED00A6389C /* Shuttle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Shuttle.entitlements; sourceTree = ""; }; 7E74A7C51789CE2F0079E0D2 /* shuttle.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = shuttle.default.json; sourceTree = ""; }; A124BA161D45572B00218F2F /* iTerm2-stable-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTerm2-stable-current-window.scpt"; sourceTree = ""; }; @@ -62,8 +58,22 @@ A12D9BED1BCF2C73004F52A6 /* terminal-current-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-current-window.scpt"; sourceTree = ""; }; A12D9BEE1BCF2C73004F52A6 /* terminal-new-tab-default.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-new-tab-default.scpt"; sourceTree = ""; }; A12D9BEF1BCF2C73004F52A6 /* terminal-new-window.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = "terminal-new-window.scpt"; sourceTree = ""; }; + A1B7B9D31DB5361700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + A1B7B9DE1DB53ED200809327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + A1B7B9E01DB53ED700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + A1B7B9E11DB53EDA00809327 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + A1B7B9E21DB5436700809327 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = ""; }; + A1B7B9E41DB5436700809327 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + A1B7B9E51DB5436700809327 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = es; path = es.lproj/Credits.rtf; sourceTree = ""; }; + A1B7B9E61DB5436700809327 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + A1B7B9ED1DB54D6700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutWindowController.xib; sourceTree = ""; }; + A1B7B9EE1DB54D7B00809327 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; + A1B7B9EF1DB54D7F00809327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; }; + A1B7B9F01DB54D8900809327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AboutWindowController.strings; sourceTree = ""; }; + A1B7B9F11DB54D8C00809327 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AboutWindowController.strings"; sourceTree = ""; }; + A1B7B9F21DB54D9000809327 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AboutWindowController.strings; sourceTree = ""; }; A1D700051A5DCDF4003563E4 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = ""; }; - A1D700061A5DCE8D003563E4 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = ""; }; + A1D700061A5DCE8D003563E4 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AboutWindowController.m; sourceTree = ""; }; C149EBF915D5214600B1F558 /* Shuttle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shuttle.app; sourceTree = BUILT_PRODUCTS_DIR; }; C149EBFD15D5214600B1F558 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; C149EC0015D5214600B1F558 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -76,7 +86,6 @@ C149EC0D15D5214600B1F558 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; C149EC0F15D5214600B1F558 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; C149EC1015D5214600B1F558 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - C149EC1315D5214600B1F558 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; C159DC2715D5DE7F00F5DE24 /* shuttle.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = shuttle.icns; path = ../shuttle.icns; sourceTree = ""; }; /* End PBXFileReference section */ @@ -187,7 +196,7 @@ C149EC0915D5214600B1F558 /* main.m */, C149EC0B15D5214600B1F558 /* Shuttle-Prefix.pch */, C149EC0C15D5214600B1F558 /* Credits.rtf */, - 6FC541A11D45CF3000A896E3 /* Localizable.strings */, + A1B7B9DF1DB53ED200809327 /* Localizable.strings */, ); name = "Supporting Files"; sourceTree = ""; @@ -228,6 +237,8 @@ knownRegions = ( en, "zh-Hans", + Base, + es, ); mainGroup = C149EBEE15D5214600B1F558; productRefGroup = C149EBFA15D5214600B1F558 /* Products */; @@ -246,7 +257,7 @@ files = ( A12D9BF61BCF2C73004F52A6 /* terminal-current-window.scpt in Resources */, A12D9BF31BCF2C73004F52A6 /* iTerm2-nightly-current-window.scpt in Resources */, - 6FC541A21D45CF3000A896E3 /* Localizable.strings in Resources */, + A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */, A12D9BF51BCF2C73004F52A6 /* iTerm2-nightly-new-window.scpt in Resources */, 0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */, A124BA1F1D4558E500218F2F /* iTerm-legacy-current-window.scpt in Resources */, @@ -290,16 +301,30 @@ 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */ = { isa = PBXVariantGroup; children = ( - 6FC541A41D45CF7F00A896E3 /* en */, - 6FC541A61D45CF8100A896E3 /* zh-Hans */, + A1B7B9ED1DB54D6700809327 /* Base */, + A1B7B9F01DB54D8900809327 /* en */, + A1B7B9F11DB54D8C00809327 /* zh-Hans */, + A1B7B9F21DB54D9000809327 /* es */, ); name = AboutWindowController.xib; sourceTree = ""; }; + A1B7B9DF1DB53ED200809327 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + A1B7B9DE1DB53ED200809327 /* en */, + A1B7B9E01DB53ED700809327 /* Base */, + A1B7B9E11DB53EDA00809327 /* zh-Hans */, + A1B7B9E61DB5436700809327 /* es */, + ); + name = Localizable.strings; + sourceTree = ""; + }; C149EC0615D5214600B1F558 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( C149EC0715D5214600B1F558 /* en */, + A1B7B9E41DB5436700809327 /* es */, ); name = InfoPlist.strings; sourceTree = ""; @@ -308,6 +333,7 @@ isa = PBXVariantGroup; children = ( C149EC0D15D5214600B1F558 /* en */, + A1B7B9E51DB5436700809327 /* es */, ); name = Credits.rtf; sourceTree = ""; @@ -315,8 +341,10 @@ C149EC1215D5214600B1F558 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( - C149EC1315D5214600B1F558 /* en */, - 6F356B111D45A0DF00AF6132 /* zh-Hans */, + A1B7B9D31DB5361700809327 /* Base */, + A1B7B9E21DB5436700809327 /* es */, + A1B7B9EE1DB54D7B00809327 /* zh-Hans */, + A1B7B9EF1DB54D7F00809327 /* en */, ); name = MainMenu.xib; sourceTree = ""; @@ -381,7 +409,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Shuttle/Shuttle-Prefix.pch"; INFOPLIST_FILE = "Shuttle/Shuttle-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_BUNDLE_IDENTIFIER = "shuttle.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -397,7 +425,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Shuttle/Shuttle-Prefix.pch"; INFOPLIST_FILE = "Shuttle/Shuttle-Info.plist"; - MACOSX_DEPLOYMENT_TARGET = 10.7; + MACOSX_DEPLOYMENT_TARGET = 10.8; PRODUCT_BUNDLE_IDENTIFIER = "shuttle.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; diff --git a/Shuttle/AppDelegate.m b/Shuttle/AppDelegate.m index 7fcbfec..b637c14 100644 --- a/Shuttle/AppDelegate.m +++ b/Shuttle/AppDelegate.m @@ -394,7 +394,7 @@ - (void) openHost:(NSMenuItem *) sender { //Are commands run in a new tab (default) a new terminal window (new), or in the current tab of the last used window (current). NSString *terminalWindow; - escapedObject = [[objectsFromJSON objectAtIndex:0] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; + escapedObject = [objectsFromJSON objectAtIndex:0]; //if terminalTheme is not set then check for a global setting. if( [[objectsFromJSON objectAtIndex:1] isEqualToString:@"(null)"] ){ diff --git a/Shuttle/en.lproj/AboutWindowController.xib b/Shuttle/Base.lproj/AboutWindowController.xib similarity index 81% rename from Shuttle/en.lproj/AboutWindowController.xib rename to Shuttle/Base.lproj/AboutWindowController.xib index 8de61f3..39437d8 100644 --- a/Shuttle/en.lproj/AboutWindowController.xib +++ b/Shuttle/Base.lproj/AboutWindowController.xib @@ -1,8 +1,8 @@ - - + + - + @@ -19,7 +19,7 @@ - + @@ -27,11 +27,13 @@ - + + - + + @@ -39,7 +41,8 @@ - + + @@ -47,7 +50,8 @@ - + + @@ -55,7 +59,8 @@