diff --git a/src/extensions/default/OpenWithExternalApplication/main.js b/src/extensions/default/OpenWithExternalApplication/main.js new file mode 100644 index 00000000000..535a6333ee7 --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/main.js @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved. + * + * 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. + * + */ + +define(function (require, exports, module) { + "use strict"; + + + var AppInit = brackets.getModule("utils/AppInit"), + PreferencesManager = brackets.getModule("preferences/PreferencesManager"), + Strings = brackets.getModule("strings"), + FileViewController = brackets.getModule("project/FileViewController"), + ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), + NodeDomain = brackets.getModule("utils/NodeDomain"), + FileUtils = brackets.getModule("file/FileUtils"); + + /** + * @private + * @type {string} fullPath of the OpenWithExternalEditor Domain implementation + */ + var _domainPath = ExtensionUtils.getModulePath(module, "node/OpenWithExternalApplicationDomain"); + + /** + * @private + * @type {NodeDomain} + */ + var _nodeDomain = new NodeDomain("OpenWithExternalApplication", _domainPath); + + var extensionToExtApplicationMap = {}; + + function _openWithExternalApplication(event, path) { + _nodeDomain.exec("open", { + path: path, + app: extensionToExtApplicationMap[FileUtils.getFileExtension(path).toLowerCase()] + }); + } + + PreferencesManager.definePreference("externalApplications", "object", {}, { + description: Strings.DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE + }); + + PreferencesManager.on("change", "externalApplications", function () { + extensionToExtApplicationMap = PreferencesManager.get("externalApplications"); + FileUtils.addExtensionToExternalAppList(Object.keys(extensionToExtApplicationMap)); + }); + + FileViewController.on("openWithExternalApplication", _openWithExternalApplication); + + AppInit.appReady(function () { + extensionToExtApplicationMap = PreferencesManager.get("externalApplications"); + FileUtils.addExtensionToExternalAppList(Object.keys(extensionToExtApplicationMap)); + }); +}); diff --git a/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js b/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js new file mode 100644 index 00000000000..8a864689ef6 --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/node/OpenWithExternalApplicationDomain.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. + * + * 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. + * + */ + +/*eslint-env node */ +/*jslint node: true */ +"use strict"; + +var open = require("open"); + +var _domainManager; + +/** + * @private + * + * @param {Object} params Object to use + */ +function _openWithExternalApplication(params) { + var application = "default" === params.app ? "": params.app; + open(params.path, application); +} + + +/** + * Initializes the OpenWithExternalEditor domain with its commands. + * @param {DomainManager} domainManager The DomainManager for the server + */ +function init(domainManager) { + _domainManager = domainManager; + + if (!domainManager.hasDomain("OpenWithExternalApplication")) { + domainManager.registerDomain("OpenWithExternalApplication", {major: 0, minor: 1}); + } + _domainManager.registerCommand( + "OpenWithExternalApplication", + "open", + _openWithExternalApplication, + true, + "open document with External Application.", + [{ + name: "params", + type: "object", + description: "Params Object having document and App Path." + }], + [] + ); +} + +exports.init = init; diff --git a/src/extensions/default/OpenWithExternalApplication/node/package.json b/src/extensions/default/OpenWithExternalApplication/node/package.json new file mode 100644 index 00000000000..65bb049736e --- /dev/null +++ b/src/extensions/default/OpenWithExternalApplication/node/package.json @@ -0,0 +1,6 @@ +{ + "name": "brackets-open-external_application", + "dependencies": { + "open": "0.0.5" + } +} diff --git a/src/file/FileUtils.js b/src/file/FileUtils.js index 308c389cd62..284c839b44e 100644 --- a/src/file/FileUtils.js +++ b/src/file/FileUtils.js @@ -59,6 +59,11 @@ define(function (require, exports, module) { */ var MAX_FILE_SIZE = MAX_FILE_SIZE_MB * 1024 * 1024; + /** + * @const {List} list of File Extensions which will be opened in external Application + */ + var extListToBeOpenedInExtApp = []; + /** * Asynchronously reads a file as UTF-8 encoded text. @@ -526,6 +531,28 @@ define(function (require, exports, module) { return pathArray.join("/"); } + /** + * @param {string} ext extension string a file + * @return {string} returns true If file to be opened in External Application. + * + */ + function shouldOpenInExternalApplication(ext) { + return extListToBeOpenedInExtApp.includes(ext); + } + + /** + * @param {string} ext File Extensions to be added in External App List + * + */ + function addExtensionToExternalAppList(ext) { + + if(Array.isArray(ext)) { + extListToBeOpenedInExtApp = ext; + } else if (typeof ext === 'string'){ + extListToBeOpenedInExtApp.push(ext); + } + } + // Asynchronously load DocumentCommandHandlers // This avoids a temporary circular dependency created // by relocating showFileOpenError() until deprecation is over @@ -568,4 +595,6 @@ define(function (require, exports, module) { exports.comparePaths = comparePaths; exports.MAX_FILE_SIZE = MAX_FILE_SIZE; exports.encodeFilePath = encodeFilePath; + exports.shouldOpenInExternalApplication = shouldOpenInExternalApplication; + exports.addExtensionToExternalAppList = addExtensionToExternalAppList; }); diff --git a/src/language/languages.json b/src/language/languages.json index 1de22f94d48..5c8e9ef0b93 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -291,7 +291,7 @@ "jsz", "lib", "mpeg", "mpg", "mp4", "msi", "node", "o", "obj", "odc", "odb", "odf", "odg", "odp", "ods", "odt", "otf", "pak", "pdb", "pdf", "pdi", "ppt", "pptx", "psd", "rar", "sdf", "so", "sqlite", "suo", "svgz", - "swf", "tar", "tif", "tiff", "ttf", "woff", "xls", "xlsx", "zip" + "swf", "tar", "tif", "tiff", "ttf", "woff", "xls", "xlsx", "zip", "xd" ], "isBinary": true }, diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index bb7c0d42698..e01b2300aeb 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -903,5 +903,8 @@ define({ "REMOTE_DEBUGGING_ENABLED" : "Remote debugging enabled on localhost:", // Remote debugging port argument is invalid - "REMOTE_DEBUGGING_PORT_INVALID" : "Cannot enable remote debugging on port {0}. Port numbers should be between {1} and {2}." + "REMOTE_DEBUGGING_PORT_INVALID" : "Cannot enable remote debugging on port {0}. Port numbers should be between {1} and {2}.", + + //Associate File Type to External App + "DESCRIPTION_EXTERNAL_APPLICATION_ASSOCIATE" : "Add File type association to external App here" }); diff --git a/src/project/FileTreeView.js b/src/project/FileTreeView.js index 4efeef4a665..d7ec88b98c9 100644 --- a/src/project/FileTreeView.js +++ b/src/project/FileTreeView.js @@ -39,7 +39,8 @@ define(function (require, exports, module) { LanguageManager = require("language/LanguageManager"), FileTreeViewModel = require("project/FileTreeViewModel"), ViewUtils = require("utils/ViewUtils"), - KeyEvent = require("utils/KeyEvent"); + KeyEvent = require("utils/KeyEvent"), + PreferencesManager = require("preferences/PreferencesManager"); var DOM = Preact.DOM; @@ -554,7 +555,16 @@ define(function (require, exports, module) { }); } } else { - this.props.actions.setSelected(this.myPath()); + var language = LanguageManager.getLanguageForPath(this.myPath()), + doNotOpen = false; + if (language && language.isBinary() && "image" !== language.getId() && + FileUtils.shouldOpenInExternalApplication( + FileUtils.getFileExtension(this.myPath()).toLowerCase() + ) + ) { + doNotOpen = true; + } + this.props.actions.setSelected(this.myPath(), doNotOpen); } e.stopPropagation(); e.preventDefault(); @@ -569,6 +579,12 @@ define(function (require, exports, module) { if (this.state.clickTimer !== null) { this.clearTimer(); } + if (FileUtils.shouldOpenInExternalApplication( + FileUtils.getFileExtension(this.myPath()).toLowerCase() + )) { + this.props.actions.openWithExternalApplication(this.myPath()); + return; + } this.props.actions.selectInWorkingSet(this.myPath()); } }, diff --git a/src/project/FileViewController.js b/src/project/FileViewController.js index e8079a0ebd5..ac616e943c0 100644 --- a/src/project/FileViewController.js +++ b/src/project/FileViewController.js @@ -226,6 +226,13 @@ define(function (require, exports, module) { return result.promise(); } + /** + * Opens the specified document with its associated external editor, + */ + function openWithExternalApplication(fullPath) { + exports.trigger("openWithExternalApplication", fullPath); + } + /** * Opens the specified document if it's not already open, adds it to the working set, * and selects it in the WorkingSetView @@ -275,4 +282,5 @@ define(function (require, exports, module) { exports.setFileViewFocus = setFileViewFocus; exports.WORKING_SET_VIEW = WORKING_SET_VIEW; exports.PROJECT_MANAGER = PROJECT_MANAGER; + exports.openWithExternalApplication = openWithExternalApplication; }); diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index db0cc7375f6..f6a799adf12 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -280,6 +280,14 @@ define(function (require, exports, module) { this.model.selectInWorkingSet(path); }; + /** + * See `FileViewController.openWithExternalApplication` + */ + ActionCreator.prototype.openWithExternalApplication = function (path) { + FileViewController.openWithExternalApplication(path); + }; + + /** * See `ProjectModel.setContext` */