Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #4406 from adobe/glenn/drag-and-drop
Browse files Browse the repository at this point in the history
Add drag and drop support for opening files.
  • Loading branch information
RaymondLim committed Jul 10, 2013
2 parents 97b957e + b851d18 commit 73ca611
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/brackets.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ define(function (require, exports, module) {
LiveDevelopmentMain = require("LiveDevelopment/main"),
NodeConnection = require("utils/NodeConnection"),
ExtensionUtils = require("utils/ExtensionUtils"),
DragAndDrop = require("utils/DragAndDrop"),
ColorUtils = require("utils/ColorUtils");

// Load modules that self-register and just need to get included in the main project
Expand Down Expand Up @@ -284,16 +285,25 @@ define(function (require, exports, module) {
// handle drops.
$(window.document.body)
.on("dragover", function (event) {
var dropEffect = "none";
if (event.originalEvent.dataTransfer.files) {
event.stopPropagation();
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = "none";
if (DragAndDrop.isValidDrop(event.originalEvent.dataTransfer.items)) {
dropEffect = "copy";
}
event.originalEvent.dataTransfer.dropEffect = dropEffect;
}
})
.on("drop", function (event) {
if (event.originalEvent.dataTransfer.files) {
event.stopPropagation();
event.preventDefault();
brackets.app.getDroppedFiles(function (err, files) {
if (!err) {
DragAndDrop.openDroppedFiles(files);
}
});
}
});

Expand Down
26 changes: 26 additions & 0 deletions src/file/FileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ define(function (require, exports, module) {

var NativeFileSystem = require("file/NativeFileSystem").NativeFileSystem,
NativeFileError = require("file/NativeFileError"),
LanguageManager = require("language/LanguageManager"),
PerfUtils = require("utils/PerfUtils"),
Dialogs = require("widgets/Dialogs"),
DefaultDialogs = require("widgets/DefaultDialogs"),
Expand Down Expand Up @@ -361,6 +362,30 @@ define(function (require, exports, module) {
return fullPath.substr(0, fullPath.lastIndexOf("/") + 1);
}

/**
* Determine if a file is a text file. This function is only a quick best-guess.
* @param {string} path Path to check
* @return {boolean} True if the path is a text file, false if not.
*/
function isTextFile(path) {
// We don't have an accurate way to quickly check if a file is a text file.
// To make a good guess, start by looking at the language of the file.
var language = LanguageManager.getLanguageForPath(path);

if (language.getId() !== "unknown") {
// Language is known to Brackets, this must be a text file.
return true;
}

// All other files end up with language === "unknown", including .txt files.
var extension = _getFileExtension(path),
textExtensionRegEx = /^(txt|gyp[i]?)$/;

// If there is no extension, or the extension is known text extension, assume it is a text file.
// Files with other extensions, like .png or .jpg, will return false.
return (extension === path || textExtensionRegEx.test(extension.toLowerCase()));
}

// Define public API
exports.LINE_ENDINGS_CRLF = LINE_ENDINGS_CRLF;
exports.LINE_ENDINGS_LF = LINE_ENDINGS_LF;
Expand All @@ -381,4 +406,5 @@ define(function (require, exports, module) {
exports.isStaticHtmlFileExt = isStaticHtmlFileExt;
exports.isServerHtmlFileExt = isServerHtmlFileExt;
exports.getDirectoryPath = getDirectoryPath;
exports.isTextFile = isTextFile;
});
1 change: 1 addition & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ define({
// File open/save error string
"ERROR_OPENING_FILE_TITLE" : "Error opening file",
"ERROR_OPENING_FILE" : "An error occurred when trying to open the file <span class='dialog-filename'>{0}</span>. {1}",
"ERROR_OPENING_FILES" : "An error occurred when trying to open the following files:",
"ERROR_RELOADING_FILE_TITLE" : "Error reloading changes from disk",
"ERROR_RELOADING_FILE" : "An error occurred when trying to reload the file <span class='dialog-filename'>{0}</span>. {1}",
"ERROR_SAVING_FILE_TITLE" : "Error saving file",
Expand Down
117 changes: 117 additions & 0 deletions src/utils/DragAndDrop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2013 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.
*
*/


/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window, brackets */

define(function (require, exports, module) {
"use strict";

var Async = require("utils/Async"),
CommandManager = require("command/CommandManager"),
Commands = require("command/Commands"),
Dialogs = require("widgets/Dialogs"),
DefaultDialogs = require("widgets/DefaultDialogs"),
FileUtils = require("file/FileUtils"),
ProjectManager = require("project/ProjectManager"),
Strings = require("strings"),
StringUtils = require("utils/StringUtils");

/**
* Returns true if the drag and drop items contains valid drop objects.
* @param {Array.<DataTransferItem>} items Array of items being dragged
* @return {boolean} True if one or more items can be dropped.
*/
function isValidDrop(items) {
var i;

for (i = 0; i < items.length; i++) {
if (items[i].kind === "file") {
var entry = items[i].webkitGetAsEntry();

if (entry.isFile && FileUtils.isTextFile(entry.fullPath)) {
return true;
}
}
}

// No valid entries found
return false;
}

/**
* Open dropped files
* @param {Array.<string>} files Array of files dropped on the application.
* @return {Promise} Promise that is resolved if all files are opened, or rejected
* if there was an error.
*/
function openDroppedFiles(files) {
var errorFiles = [];

return Async.doInParallel(files, function (file) {
var result = new $.Deferred();

// Only open text files
brackets.fs.stat(file, function (err, stat) {
if (!err && stat.isFile() && FileUtils.isTextFile(file)) {
CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET,
{fullPath: file})
.done(function () {
result.resolve();
})
.fail(function () {
errorFiles.push(file);
result.reject();
});
} else {
errorFiles.push(file);
result.reject();
}
});

return result.promise();
}, false)
.fail(function () {
var message = Strings.ERROR_OPENING_FILES;

message += "<ul>";
errorFiles.forEach(function (file) {
message += "<li><span class='dialog-filename'>" +
StringUtils.breakableUrl(ProjectManager.makeProjectRelativeIfPossible(file)) +
"</span></li>";
});
message += "</ul>";

Dialogs.showModalDialog(
DefaultDialogs.DIALOG_ID_ERROR,
Strings.ERROR_OPENING_FILE_TITLE,
message
);
});
}

// Export public API
exports.isValidDrop = isValidDrop;
exports.openDroppedFiles = openDroppedFiles;
});

0 comments on commit 73ca611

Please sign in to comment.