diff --git a/Cocoa/Classes/AtomApp.m b/Cocoa/Classes/AtomApp.m index cb39620c1..9a300777d 100644 --- a/Cocoa/Classes/AtomApp.m +++ b/Cocoa/Classes/AtomApp.m @@ -17,29 +17,22 @@ } // Overridden -- (void)sendEvent:(NSEvent *)event { - switch ([event type]) { - case NSKeyDown: - { - BOOL handeled = NO; - id controller = [[self keyWindow] windowController]; - - // The keyWindow could be a Cocoa Dialog or something, ignore them. - if ([controller isKindOfClass:[AtomController class]]) { - handeled = [controller handleKeyEvent:event]; - } - - if (!handeled) { - [super sendEvent:event]; - } - - } - break; - default: - [super sendEvent:event]; - break; - } -} +//- (void)sendEvent:(NSEvent *)event { +// if ([event type] == NSKeyDown) { +// BOOL handeled = NO; +// id controller = [[self keyWindow] windowController]; +// +// // The keyWindow could be a Cocoa Dialog or something, ignore that. +// if ([controller isKindOfClass:[AtomController class]]) { +// handeled = [controller handleKeyEvent:event]; +// } +// +// if (!handeled) [super sendEvent:event]; +// } +// else { +// [super sendEvent:event]; +// } +//} // AppDelegate - (void)applicationWillFinishLaunching:(NSNotification *)aNotification { diff --git a/Cocoa/Classes/AtomController.m b/Cocoa/Classes/AtomController.m index 1dc7375d7..8e7003b27 100644 --- a/Cocoa/Classes/AtomController.m +++ b/Cocoa/Classes/AtomController.m @@ -20,10 +20,10 @@ [jscocoa unlinkAllReferences]; [jscocoa garbageCollect]; [jscocoa release]; jscocoa = nil; - + [webView release]; [URL release]; - + [super dealloc]; } @@ -37,7 +37,7 @@ [super windowDidLoad]; [webView setUIDelegate:self]; - + [self setShouldCascadeWindows:YES]; [self setWindowFrameAutosaveName:@"atomController"]; @@ -48,14 +48,19 @@ jscocoa = [[JSCocoa alloc] initWithGlobalContext:[[webView mainFrame] globalContext]]; [jscocoa setObject:self withName:@"atomController"]; - NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; - NSString *requirePath = [resourcePath stringByAppendingString:@"/src/require.js"]; - [jscocoa evalJSFile:requirePath]; +// NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; +// NSString *requirePath = [resourcePath stringByAppendingString:@"/src/require.js"]; +// [jscocoa evalJSFile:requirePath]; NSURL *resourceURL = [[NSBundle mainBundle] resourceURL]; NSURL *indexURL = [resourceURL URLByAppendingPathComponent:@"index.html"]; - NSURLRequest *request = [NSURLRequest requestWithURL:indexURL]; + NSURLRequest *request = [NSURLRequest requestWithURL:indexURL]; [[webView mainFrame] loadRequest:request]; + + NSString *resourcePath = [[NSBundle mainBundle] resourcePath]; + NSString *requirePath = [resourcePath stringByAppendingString:@"/src/watcher.js"]; + [jscocoa evalJSFile:requirePath]; + } } @@ -72,7 +77,7 @@ // WebUIDelegate Protocol - (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems { - return [NSArray array]; + return defaultMenuItems; } diff --git a/Cocoa/UKKQueue/UKMainThreadProxy.m b/Cocoa/UKKQueue/UKMainThreadProxy.m index c3517980e..93ded660c 100644 --- a/Cocoa/UKKQueue/UKMainThreadProxy.m +++ b/Cocoa/UKKQueue/UKMainThreadProxy.m @@ -1,4 +1,4 @@ -/* ============================================================================= + /* ============================================================================= FILE: UKMainThreadProxy.h PROJECT: UKMainThreadProxy diff --git a/docs/sample-conf.coffee b/docs/sample-conf.coffee new file mode 100644 index 000000000..ba4d0dab6 --- /dev/null +++ b/docs/sample-conf.coffee @@ -0,0 +1,24 @@ +# ~/.atomicity/settings.coffee +# ---------------------------- + +editor: + tabSize: 2 + spaces: true + +# ~/.atomicity/keybindings.coffee + +app: + "cmd-q": (app) -> app.quit() + "cmd-q": "quit" # optional way? +editor: + "ctrl-p": (editor) -> editor.moveUp() + "ctrl-a": (editor) -> + position = editor.cursorPosition() + position.column = 0 + editor.setCursorPosition(position) +tree: + "cmd-ctrl-n": (tree) -> tree.toggle() +window: + 'Command-O' : @open + 'Command-Ctrl-K' : @showConsole + diff --git a/index.html b/index.html index 877f7f001..c617b573b 100644 --- a/index.html +++ b/index.html @@ -65,12 +65,12 @@ diff --git a/src/app.coffee b/src/app.coffee index bef5d194f..f8282b61d 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -1,13 +1,18 @@ -_ = require 'underscore' - File = require 'fs' -Window = require 'window' +KeyBinder = require 'key-binder' +require 'window' module.exports = class App @root: OSX.NSBundle.mainBundle.resourcePath - @start: -> - @window = new Window - controller : AtomController - path : localStorage.lastOpenedPath ? File.workingDirectory() + @startup: -> + KeyBinder.register "app", @ + + window.startup() + + KeyBinder.load "#{@root}/static/key-bindings.coffee" + KeyBinder.load "~/.atomicity/key-bindings.coffee" + + @quit: -> + OSX.NSApp.terminate OSX.NSApp diff --git a/src/chrome.coffee b/src/chrome.coffee deleted file mode 100644 index 99368bc36..000000000 --- a/src/chrome.coffee +++ /dev/null @@ -1,34 +0,0 @@ -module.exports = -class Chrome - # path - Optional. The String path to the file to base it on. - @newWindow: (path) -> - c = OSX.AtomWindowController.alloc.initWithWindowNibName "AtomWindow" - c.window - c.window.makeKeyAndOrderFront null - - # Returns null or a file path. - @openPanel: -> - panel = OSX.NSOpenPanel.openPanel - panel.setCanChooseDirectories true - if panel.runModal isnt OSX.NSFileHandlingPanelOKButton - return null - filename = panel.filenames.lastObject - localStorage.lastOpenedPath = filename - filename - - @openURL: (url) -> - window.location = url - App = require 'app' - App.activeWindow.setTitle _.last url.replace(/\/$/,'').split '/' - - # Returns null or a file path. - @savePanel: -> - panel = OSX.NSSavePanel.savePanel - if panel.runModal isnt OSX.NSFileHandlingPanelOKButton - return null - panel.filenames.lastObject - - @writeToPasteboard: (text) -> - pb = OSX.NSPasteboard.generalPasteboard - pb.declareTypes_owner [OSX.NSStringPboardType], null - pb.setString_forType text, OSX.NSStringPboardType diff --git a/src/key-binder.coffee b/src/key-binder.coffee new file mode 100644 index 000000000..c2d18a427 --- /dev/null +++ b/src/key-binder.coffee @@ -0,0 +1,124 @@ +_ = require 'underscore' +fs = require 'fs' +Watcher = require 'watcher' +{CoffeeScript} = require 'coffee-script' + +module.exports = +class KeyBinder + @bindings: {} + + @scopes: {} + + @register: (name, scope) -> + @scopes[name] = scope + + @load: (path) -> + try + Watcher.watch path, -> + # Should we keep track of which file bindings are associated with? That + # way we could clear bindings when the file is changed or deleted. I + # think the answer is yes, but I don't want to do this right now. + console.log "#{@name}: Reloading #{path}" + @load path + + json = CoffeeScript.eval "return " + (fs.read path) + for scopeName, bindings of json + @create scopeName, binding, method for binding, method of bindings + catch error + console.error "#{@class}: Could not load key bindings at `#{path}`. #{error}" + + @create: (scope, binding, method) -> + if typeof scope is "string" + throw "#{@name}: Unknown scope `#{scope}`" unless @scopes[scope] + scope = @scopes[scope] + + callback = if _.isFunction method + -> method scope + else if scope[method] + -> scope[method] scope + else + throw "#{@name}: '#{method}' not found found in scope #{scope}" + + callbacks = @bindings[@bindingParser binding] ?= [] + + callbacks.push callback + + @handleEvent: (event) -> + keys = [] + keys.push @modifierKeys.command if event.modifierFlags & OSX.NSCommandKeyMask + keys.push @modifierKeys.shift if event.modifierFlags & OSX.NSShiftKeyMask + keys.push @modifierKeys.control if event.modifierFlags & OSX.NSControlKeyMask + keys.push @modifierKeys.alt if event.modifierFlags & OSX.NSAlternateKeyMask + keys.push event.charactersIgnoringModifiers.toLowerCase().charCodeAt 0 + + binding = keys.sort().join "-" + + callbacks = @bindings[binding] + return false if not callbacks + + callback() for callback in callbacks + true + + @bindingParser: (binding) -> + keys = binding.trim().split '-' + + modifiers = [] + key = null + + for k in keys + k = k.toLowerCase() + if @modifierKeys[k] + modifiers.push @modifierKeys[k] + else if key + throw "#{@name}: #{binding} specifies TWO keys, we don't handle that yet." + else if @namedKeys[k] + key = @namedKeys[k] + else if k.length > 1 + throw "#{@name}: #{binding} uses an unknown key #{k}." + else + key = k.charCodeAt 0 + + modifiers.concat(key).sort().join "-" + + @modifierKeys: + '⇧': 16 + shift: 16 + alt: 18 + option: 18 + '⌥': 18 + control: 17 + ctrl: 17 + command: 91 + cmd: 91 + '⌘': 91 + + @namedKeys: + backspace: 8 + tab: 9 + clear: 12 + enter: 13 + return: 13 + esc: 27 + escape: 27 + space: 32 + left: 37 + up: 38 + right: 39 + down: 40 + del: 46 + delete: 46 + home: 36 + end: 35 + pageup: 33 + pagedown: 34 + ',': 188 + '.': 190 + '/': 191 + '`': 192 + '-': 189 + '=': 187 + ';': 186 + '\'': 222 + '[': 219 + ']': 221 + '\\': 220 diff --git a/src/keybinder.coffee b/src/keybinder.coffee deleted file mode 100644 index 7ab22c612..000000000 --- a/src/keybinder.coffee +++ /dev/null @@ -1,95 +0,0 @@ -_ = require 'underscore' - -modifierKeys = - '⇧': 16 - shift: 16 - alt: 18 - option: 18 - '⌥': 18 - control: 17 - ctrl: 17 - command: 91 - cmd: 91 - '⌘': 91 - -namedKeys = - backspace: 8 - tab: 9 - clear: 12 - enter: 13 - return: 13 - esc: 27 - escape: 27 - space: 32 - left: 37 - up: 38 - right: 39 - down: 40 - del: 46 - delete: 46 - home: 36 - end: 35 - pageup: 33 - pagedown: 34 - ',': 188 - '.': 190 - '/': 191 - '`': 192 - '-': 189 - '=': 187 - ';': 186 - '\'': 222 - '[': 219 - ']': 221 - '\\': 220 - -bindings = {} - -shortcutParser = (shortcut) -> - keys = shortcut.trim().split '-' - - modifiers = [] - key = null - - for k in keys - k = k.toLowerCase() - if modifierKeys[k] - modifiers.push modifierKeys[k] - else if key - throw "THIS KEYBINDING #{shortcut} specifies TWO keys, we don't handle that yet." - else if namedKeys[k] - key = namedKeys[k] - else if k.length > 1 - throw "THIS KEYBINDING #{shortcut} uses an unknown key #{k}." - else - key = k.charCodeAt 0 - - modifiers.concat(key).sort().join "-" - -exports.bindKey = (scope, shortcut, method) -> - callback = if _.isFunction method - -> method.apply scope - else if scope[method] - -> scope[method]() - else - console.error "keymap: no '#{method}' method found" - -> console.error "keymap: #{shortcut} failed to bind" - - callbacks = bindings[shortcutParser shortcut] ?= [] - callbacks.push callback - -window.handleKeyEvent = (event) -> - keys = [] - keys.push modifierKeys.command if event.modifierFlags & OSX.NSCommandKeyMask - keys.push modifierKeys.shift if event.modifierFlags & OSX.NSShiftKeyMask - keys.push modifierKeys.control if event.modifierFlags & OSX.NSControlKeyMask - keys.push modifierKeys.alt if event.modifierFlags & OSX.NSAlternateKeyMask - keys.push event.charactersIgnoringModifiers.charCodeAt 0 - - shortcut = keys.sort().join "-" - - callbacks = bindings[shortcut] - return false if not callbacks - - callback() for callback in callbacks - true diff --git a/src/pane.coffee b/src/pane.coffee deleted file mode 100644 index b0c7c9c56..000000000 --- a/src/pane.coffee +++ /dev/null @@ -1,44 +0,0 @@ -$ = require 'jquery' - -module.exports = -class Pane - position: null - - html: null - - showing: false - - constructor: (@window) -> - - add: -> - verticalDiv = $('#app-vertical') - horizontalDiv = $('#app-horizontal') - - el = $ "
" - el.addClass "pane " + @position - el.append @html - - switch @position - when 'main' - $('.main').replaceWith el - when 'top' - verticalDiv.prepend el - when 'left' - horizontalDiv.prepend el - when 'bottom' - verticalDiv.append el - when 'right' - horizontalDiv.append el - else - throw "I DON'T KNOW HOW TO DEAL WITH #{@position}" - - toggle: -> - if @showing - @html.parent().detach() - else - @add this - - @showing = not @showing - - # Override these in your subclass - initialize: -> diff --git a/src/plugin.coffee b/src/plugin.coffee deleted file mode 100644 index 8ce674890..000000000 --- a/src/plugin.coffee +++ /dev/null @@ -1,43 +0,0 @@ -{bindKey} = require 'keybinder' - -module.exports = -class Plugin - constructor: (@window) -> - console.log "Loaded Plugin: " + @.constructor.name - - for shortcut, method of @keymap() - bindKey @, shortcut, method - - # Called after all plugins are loaded - load: -> - - pane: -> - - keymap: -> - - storageNamespace: -> @.constructor.name - - get: (key, defaultValue) -> - try - object = JSON.parse(localStorage[@storageNamespace()] ? "{}") - catch error - error.message += "\nGetting #{key}" - console.log(error) - - object[key] ? defaultValue - - set: (key, value) -> - try - object = JSON.parse(localStorage[@storageNamespace()] ? "{}") - catch error - error.message += "\nSetting #{key}: #{value}" - console.log(error) - - # Putting data in - if value == undefined - delete object[key] - else - object[key] = value - localStorage[@storageNamespace()] = JSON.stringify(object) - - diff --git a/src/startup.coffee b/src/startup.coffee index 1522d84dc..352a96d44 100644 --- a/src/startup.coffee +++ b/src/startup.coffee @@ -1,3 +1,3 @@ # Like sands through the hourglass, so are the days of our lives. App = require 'app' -App.start() +App.startup() diff --git a/src/system.coffee b/src/system.coffee deleted file mode 100644 index 677a7aaa5..000000000 --- a/src/system.coffee +++ /dev/null @@ -1,7 +0,0 @@ -# commonjs system module -# http://ringojs.org/api/v0.8/system/ - -module.exports = - # An object containing our environment variables. - env: -> - OSX.NSProcess.processInfo.environment diff --git a/src/watcher.coffee b/src/watcher.coffee index 299d03681..f0e5aa1d6 100644 --- a/src/watcher.coffee +++ b/src/watcher.coffee @@ -1,4 +1,4 @@ -module.exports = +#module.exports = class Watcher @watchedPaths: {} @@ -18,8 +18,7 @@ class Watcher (@watchedPaths[path] ?= []).push callback - # Used to unwatch a path - callback + callback # Handy for anonymous functions. @unwatch: (path, callback=null) -> return unless @watchedPaths[path] @@ -30,15 +29,22 @@ class Watcher @queue.removePathFromQueue path # Delegate method for AAWatcher - change the name of this method @watcher_receivedNotification_forPath = (queue, notification, path) => callbacks = @watchedPaths[path] - switch notification - when "UKFileWatcherRenameNotification" + switch notification.toString() + when "UKKQueueFileRenamedNotification" raise "Doesn't handle this yet" - when "UKFileWatcherDeleteNotification" + when "UKKQueueFileDeletedNotification" @watchedPaths[path] = null @queue.removePathFromQueue path + when "UKKQueueFileWrittenToNotification" + callback notification, path, callback for callback in callbacks + when "UKKQueueFileAttributesChangedNotification" + # Just ignore this + else + console.error "I HAVE NO IDEA WHEN #{notification} IS TRIGGERED" + +Watcher.watch "~/tmp/what.txt", -> + OSX.NSLog "HI" - callback notification, path, callback for callback in callbacks diff --git a/src/window.coffee b/src/window.coffee index c1abf3bb4..729fbaf66 100644 --- a/src/window.coffee +++ b/src/window.coffee @@ -1,98 +1,21 @@ -$ = require 'jquery' - File = require 'fs' -Chrome = require 'chrome' +KeyBinder = require 'key-binder' -{bindKey} = require 'keybinder' -oop = require "pilot/oop" -{EventEmitter} = require "pilot/event_emitter" +windowAdditions = + extensions: [] -module.exports = -class Window - controller: null + startup: -> + KeyBinder.register "window", window - nswindow: null + @path = localStorage.lastOpenedPath ? File.workingDirectory() + @appPath = OSX.NSBundle.mainBundle.resourcePath - panes: [] - - keymap: -> - 'Command-N' : @new - 'Command-O' : @open - 'Command-Shift-O' : @openURL - 'Command-Ctrl-K' : @showConsole - 'Command-Ctrl-M' : @reload - - constructor: (options={}) -> - oop.implement @, EventEmitter - - for option, value of options - @[option] = value - - for shortcut, method of @keymap() - bindKey @, shortcut, method - - @nswindow = @controller?.window - @loadPlugins() - @._emit "loaded" - - loadPlugins: -> - @plugins = [] - - # Ewwww, don't do this - App = require 'app' - for pluginPath in File.list(App.root + "/plugins") - if File.isDirectory pluginPath - try - plugin = require pluginPath - @plugins.push new plugin(@) - catch error - console.warn "Plugin Failed: #{File.base pluginPath}" - console.warn error - - @open @path if @path? - - # After all the plugins are created, load them. - for plugin in @plugins - try - plugin.load() - catch error - console.warn "Plugin Loading Failed: #{plugin.constructor.name}" - console.warn error - - reload: -> - Chrome.newWindow() - @controller.close - - inspector: -> - @_inspector ?= WindowController.webView.inspector - - new: -> - Chrome.newWindow() + handleKeyEvent: -> + KeyBinder.handleEvent.apply KeyBinder, arguments showConsole: -> - @inspector().showConsole(1) + atomController.webView.inspector.showConsole true - title: -> - @nswindow.title - - setTitle: (title) -> - @nswindow.title = title - - # Do these get moved into document? - isDirty: -> - @nswindow.isDocumentEdited() - - # Set the active window's dirty status. - setDirty: (bool) -> - @nswindow.setDocumentEdited bool - - open: (path) -> - @_emit 'open', { filename: path } - - close: (path) -> - @_emit 'close', { filename: path } - - openURL: (url) -> - if url = prompt "Enter URL:" - Chrome = require 'app' - Chrome.openURL url +for key, value of windowAdditions + raise "DOMWindow already has a key named #{key}" if window[key] + window[key] = value diff --git a/static/key-bindings.coffee b/static/key-bindings.coffee new file mode 100644 index 000000000..ba487a448 --- /dev/null +++ b/static/key-bindings.coffee @@ -0,0 +1,2 @@ +app: + "cmd-q": (app) -> app.quit()