Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 44 additions & 38 deletions src/CommandManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,55 @@
* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
*/

/**
* Manages global application commands that can be called from menu items, key bindings, or subparts
* of the application.
*/
var CommandManager = {};

CommandManager._commands = {};
/**
* Manages global application commands that can be called from menu items, key bindings, or subparts
* of the application.
*/
define(function(require, exports, module) {

var _commands = {};

/**
* Registers a global command.
*
* @param {string} id The ID of the command.
* @param {function} command The function to call when the command is executed. Any arguments passed to
* execute() (after the id) are passed as arguments to the function. If the function is asynchronous,
* it must return a jQuery Deferred that is resolved when the command completes. Otherwise, the
* CommandManager will assume it is synchronous, and return a Deferred that is already resolved.
*/
CommandManager.register = function(id, command) {
if (CommandManager._commands[id]) {
throw new Error("Attempting to register an already-registered command: " + id);
/**
* Registers a global command.
*
* @param {string} id The ID of the command.
* @param {function} command The function to call when the command is executed. Any arguments passed to
* execute() (after the id) are passed as arguments to the function. If the function is asynchronous,
* it must return a jQuery Deferred that is resolved when the command completes. Otherwise, the
* CommandManager will assume it is synchronous, and return a Deferred that is already resolved.
*/
function register(id, command) {
if (_commands[id]) {
throw new Error("Attempting to register an already-registered command: " + id);
}
_commands[id] = command;
}
CommandManager._commands[id] = command;
}

/**
* Runs a global command. Additional arguments are passed to the command.
*
* @param {string} id The ID of the command to run.
* @return {Deferred} a jQuery Deferred that will be resolved when the command completes.
*/
CommandManager.execute = function(id) {
var command = CommandManager._commands[id];
if (command) {
var result = command.apply(null, Array.prototype.slice.call(arguments, 1));
if (result === undefined) {
return (new $.Deferred()).resolve();
/**
* Runs a global command. Additional arguments are passed to the command.
*
* @param {string} id The ID of the command to run.
* @return {Deferred} a jQuery Deferred that will be resolved when the command completes.
*/
function execute(id) {
var command = _commands[id];
if (command) {
var result = command.apply(null, Array.prototype.slice.call(arguments, 1));
if (result === undefined) {
return (new $.Deferred()).resolve();
}
else {
return result;
}
}
else {
return result;
console.log("Attempted to call unregistered command: " + id);
return (new $.Deferred()).reject();
}
}
else {
console.log("Attempted to call unregistered command: " + id);
return (new $.Deferred()).reject();
}
}

// Define public API
exports.register = register;
exports.execute = execute;
});
19 changes: 10 additions & 9 deletions src/Commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
*/

/**
* List of constants for global command IDs.
*/
var Commands = {
FILE_OPEN: "file.open",
FILE_NEW: "file.new",
FILE_SAVE: "file.save",
FILE_CLOSE: "file.close"
};
define(function(require, exports, module) {
/**
* List of constants for global command IDs.
*/
exports.FILE_OPEN = "file.open";
exports.FILE_NEW = "file.new";
exports.FILE_SAVE = "file.save";
exports.FILE_CLOSE = "file.close";
});

54 changes: 30 additions & 24 deletions src/FileCommandHandlers.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
/*
* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
*/

/**
* Handlers for commands related to file handling (opening, saving, etc.)
*/
var FileCommandHandlers = (function() {
// TODO: remove this and use the real exports variable when we switch to modules.
var exports = {};

define(function(require, exports, module) {
// Load dependent modules
var CommandManager = require("CommandManager")
, Commands = require("Commands")
, NativeFileSystem = require("NativeFileSystem").NativeFileSystem
, ProjectManager = require("ProjectManager")
, Strings = require("strings");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have the capitalization disagree like this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my fault for not capitalizing the source file correctly. When we finalize our directory structure, the strings.js file should be capitalized at its new location.

;

/**
* Handlers for commands related to file handling (opening, saving, etc.)
*/

var _editor, _title, _currentFilePath, _currentTitlePath,
_isDirty = false,
_savedUndoPosition = 0;

exports.init = function init(editor, title) {
function init(editor, title) {
_editor = editor;
_title = title;

Expand Down Expand Up @@ -99,7 +104,7 @@ var FileCommandHandlers = (function() {
if (!fullPath) {
// Prompt the user with a dialog
// TODO: we're relying on this to not be asynchronous--is that safe?
NativeFileSystem.showOpenDialog(false, false, brackets.strings.OPEN_FILE, ProjectManager.getProjectRoot().fullPath,
NativeFileSystem.showOpenDialog(false, false, Strings.OPEN_FILE, ProjectManager.getProjectRoot().fullPath,
["htm", "html", "js", "css"], function(files) {
if (files.length > 0) {
result = doOpen(files[0]);
Expand Down Expand Up @@ -234,8 +239,8 @@ var FileCommandHandlers = (function() {
if (_currentFilePath && _isDirty) {
brackets.showModalDialog(
brackets.DIALOG_ID_SAVE_CLOSE
, brackets.strings.SAVE_CLOSE_TITLE
, brackets.strings.format(brackets.strings.SAVE_CLOSE_MESSAGE, _currentTitlePath)
, Strings.SAVE_CLOSE_TITLE
, Strings.format(Strings.SAVE_CLOSE_MESSAGE, _currentTitlePath)
).done(function(id) {
if (id === brackets.DIALOG_BTN_CANCEL) {
result.reject();
Expand Down Expand Up @@ -287,9 +292,9 @@ var FileCommandHandlers = (function() {
function showFileOpenError(code, path) {
brackets.showModalDialog(
brackets.DIALOG_ID_ERROR
, brackets.strings.ERROR_OPENING_FILE_TITLE
, brackets.strings.format(
brackets.strings.ERROR_OPENING_FILE
, Strings.ERROR_OPENING_FILE_TITLE
, Strings.format(
Strings.ERROR_OPENING_FILE
, path
, getErrorString(code))
);
Expand All @@ -298,9 +303,9 @@ var FileCommandHandlers = (function() {
function showSaveFileError(code, path) {
brackets.showModalDialog(
brackets.DIALOG_ID_ERROR
, brackets.strings.ERROR_SAVING_FILE_TITLE
, brackets.strings.format(
brackets.strings.ERROR_SAVING_FILE
, Strings.ERROR_SAVING_FILE_TITLE
, Strings.format(
Strings.ERROR_SAVING_FILE
, path
, getErrorString(code))
);
Expand All @@ -312,17 +317,18 @@ var FileCommandHandlers = (function() {
var result;

if (code == FileError.NOT_FOUND_ERR)
result = brackets.strings.NOT_FOUND_ERR;
result = Strings.NOT_FOUND_ERR;
else if (code == FileError.NOT_READABLE_ERR)
result = brackets.strings.NOT_READABLE_ERR;
result = Strings.NOT_READABLE_ERR;
else if (code == FileError.NO_MODIFICATION_ALLOWED_ERR)
result = brackets.strings.NO_MODIFICATION_ALLOWED_ERR;
result = Strings.NO_MODIFICATION_ALLOWED_ERR;
else
result = brackets.strings.format(brackets.strings.GENERIC_ERROR, code);
result = Strings.format(Strings.GENERIC_ERROR, code);

return result;
}

return exports;
})();
// Define public API
exports.init = init;
});

111 changes: 61 additions & 50 deletions src/KeyBindingManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,70 @@
* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
*/

/**
* Manages the mapping of keyboard inputs to commands.
*/
var KeyBindingManager = {
/**
* The currently installed keymap.
*/
_keymap: null,
define(function(require, exports, module) {
CommandManager = require("CommandManager");

// TODO: Move KeyMap into separate module

/**
* Install the specified keymap as the current keymap, overwriting the existing keymap.
*
* @param {KeyMap} keymap The keymap to install.
* Manages the mapping of keyboard inputs to commands.
*/
installKeymap: function(keymap) {
this._keymap = keymap;
},
var KeyBindingManager = {
/**
* The currently installed keymap.
*/
_keymap: null,

/**
* Process the keybinding for the current key.
/**
* Install the specified keymap as the current keymap, overwriting the existing keymap.
*
* @param {KeyMap} keymap The keymap to install.
*/
installKeymap: function(keymap) {
this._keymap = keymap;
},

/**
* Process the keybinding for the current key.
*
* @param {string} A key-description string.
* @return {boolean} true if the key was processed, false otherwise
*/
handleKey: function(key) {
if (this._keymap && this._keymap.map[key]) {
CommandManager.execute(this._keymap.map[key]);
return true;
}
return false;
}
};

/** class Keymap
*
* A keymap specifies how keys are mapped to commands. This currently just holds the map, but in future
* it will likely be extended to include other metadata about the keymap.
*
* Keys are described by strings of the form "[modifier-modifier-...-]key", where modifier is one of
* Ctrl, Alt, or Shift. If multiple modifiers are specified, they must be specified in that order
* (i.e. "Ctrl-Alt-Shift-P" is legal, "Alt-Ctrl-Shift-P" is not).
* (TODO: the above restriction is to simplify mapping--is it too onerous?)
* -- Ctrl maps to Cmd on Mac. (This means that you can't specifically bind to the Ctrl key on Mac.)
* -- Alt maps to the Option key on Mac.
* -- Letters must be uppercase, but do not require Shift by default. To indicate that Shift must be held
* down, you must specifically include Shift.
*
* @param {string} A key-description string.
* @return {boolean} true if the key was processed, false otherwise
* @constructor
* @param {map} map An object mapping key-description strings to command IDs.
*/
handleKey: function(key) {
if (this._keymap && this._keymap.map[key]) {
CommandManager.execute(this._keymap.map[key]);
return true;
}
return false;
}
};

/** class Keymap
*
* A keymap specifies how keys are mapped to commands. This currently just holds the map, but in future
* it will likely be extended to include other metadata about the keymap.
*
* Keys are described by strings of the form "[modifier-modifier-...-]key", where modifier is one of
* Ctrl, Alt, or Shift. If multiple modifiers are specified, they must be specified in that order
* (i.e. "Ctrl-Alt-Shift-P" is legal, "Alt-Ctrl-Shift-P" is not).
* (TODO: the above restriction is to simplify mapping--is it too onerous?)
* -- Ctrl maps to Cmd on Mac. (This means that you can't specifically bind to the Ctrl key on Mac.)
* -- Alt maps to the Option key on Mac.
* -- Letters must be uppercase, but do not require Shift by default. To indicate that Shift must be held
* down, you must specifically include Shift.
*
* @constructor
* @param {map} map An object mapping key-description strings to command IDs.
*/
var KeyMap = function(map) {
if (map === undefined) {
throw new Error("All parameters to the KeyMap constructor must be specified");
}
this.map = map;
};
var KeyMap = function(map) {
if (map === undefined) {
throw new Error("All parameters to the KeyMap constructor must be specified");
}
this.map = map;
};

// Define public API
// TODO: Once KeyMap is moved into separate module, export KeyBindingManager methods instead of the entire object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure I understand this right: if we made KeyMap an "inner class" of KeyBindingManager, then this wouldn't be an issue, right? We'd assign public members of KeyBindingManager directly to the exports object, and one of those members would happen to be the KeyMap ctor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could do that (export the KeyMap ctor as a public property of KeyBindingManager), or we could make KeyMap a separate module. Either one would be fine.

exports.KeyBindingManager = KeyBindingManager;
exports.KeyMap = KeyMap;
});
Loading