diff options
12 files changed, 305 insertions, 22 deletions
diff --git a/chrome/browser/chromeos/extensions/file_browser_private_api.cc b/chrome/browser/chromeos/extensions/file_browser_private_api.cc index 7a597f8..9e60661 100644 --- a/chrome/browser/chromeos/extensions/file_browser_private_api.cc +++ b/chrome/browser/chromeos/extensions/file_browser_private_api.cc @@ -632,6 +632,26 @@ void ExecuteTasksFileBrowserFunction::OnTaskExecuted(bool success) { SendResponse(success); } +SetDefaultTaskFileBrowserFunction::SetDefaultTaskFileBrowserFunction() {} + +SetDefaultTaskFileBrowserFunction::~SetDefaultTaskFileBrowserFunction() {} + +bool SetDefaultTaskFileBrowserFunction::RunImpl() { + // First param is task id that was to the extension with setDefaultTask call. + std::string task_id; + if (!args_->GetString(0, &task_id) || !task_id.size()) + return false; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind( + &file_handler_util::UpdateFileHandlerUsageStats, + profile_, task_id)); + + result_.reset(new base::FundamentalValue(true)); + return true; +} + FileBrowserFunction::FileBrowserFunction() { } diff --git a/chrome/browser/chromeos/extensions/file_browser_private_api.h b/chrome/browser/chromeos/extensions/file_browser_private_api.h index e252894..f1a38fd 100644 --- a/chrome/browser/chromeos/extensions/file_browser_private_api.h +++ b/chrome/browser/chromeos/extensions/file_browser_private_api.h @@ -113,6 +113,19 @@ class ExecuteTasksFileBrowserFunction : public AsyncExtensionFunction { DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.executeTask"); }; +// Implements the chrome.fileBrowserPrivate.setDefaultTask method. +class SetDefaultTaskFileBrowserFunction : public SyncExtensionFunction { + public: + SetDefaultTaskFileBrowserFunction(); + virtual ~SetDefaultTaskFileBrowserFunction(); + + protected: + // AsyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.setDefaultTask"); +}; + // Parent class for the chromium extension APIs for the file dialog. class FileBrowserFunction : public AsyncExtensionFunction { diff --git a/chrome/browser/chromeos/extensions/file_handler_util.cc b/chrome/browser/chromeos/extensions/file_handler_util.cc index 9802c25..6aab112 100644 --- a/chrome/browser/chromeos/extensions/file_handler_util.cc +++ b/chrome/browser/chromeos/extensions/file_handler_util.cc @@ -76,18 +76,6 @@ int ExtractProcessFromExtensionId(const std::string& extension_id, return process->GetID(); } -// Update file handler usage stats. -void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) { - if (!profile || !profile->GetPrefs()) - return; - DictionaryPrefUpdate prefs_usage_update(profile->GetPrefs(), - prefs::kLastUsedFileBrowserHandlers); - prefs_usage_update->SetWithoutPathExpansion(task_id, - new base::FundamentalValue( - static_cast<int>(base::Time::Now().ToInternalValue()/ - base::Time::kMicrosecondsPerSecond))); -} - URLPatternSet GetAllMatchingPatterns(const FileBrowserHandler* handler, const std::vector<GURL>& files_list) { URLPatternSet matching_patterns; @@ -201,6 +189,18 @@ void SortLastUsedHandlerList(LastUsedHandlerList *list) { } // namespace +// Update file handler usage stats. +void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) { + if (!profile || !profile->GetPrefs()) + return; + DictionaryPrefUpdate prefs_usage_update(profile->GetPrefs(), + prefs::kLastUsedFileBrowserHandlers); + prefs_usage_update->SetWithoutPathExpansion(task_id, + new base::FundamentalValue( + static_cast<int>(base::Time::Now().ToInternalValue()/ + base::Time::kMicrosecondsPerSecond))); +} + int GetReadWritePermissions() { return kReadWriteFilePermissions; } @@ -635,8 +635,6 @@ void FileTaskExecutor::SetupPermissionsAndDispatchEvent( details->SetInteger("tab_id", ExtensionTabUtil::GetTabId(contents)); } - UpdateFileHandlerUsageStats(profile_, MakeTaskID(extension_id_, action_id_)); - std::string json_args; base::JSONWriter::Write(event_args.get(), &json_args); event_router->DispatchEventToExtension( diff --git a/chrome/browser/chromeos/extensions/file_handler_util.h b/chrome/browser/chromeos/extensions/file_handler_util.h index 6ef6760..84b0e2f 100644 --- a/chrome/browser/chromeos/extensions/file_handler_util.h +++ b/chrome/browser/chromeos/extensions/file_handler_util.h @@ -20,6 +20,8 @@ class Profile; namespace file_handler_util { +void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id); + // Gets read-write file access permission flags. int GetReadWritePermissions(); // Gets read-only file access permission flags. diff --git a/chrome/browser/extensions/extension_function_registry.cc b/chrome/browser/extensions/extension_function_registry.cc index e60a7ff..4e29f7e 100644 --- a/chrome/browser/extensions/extension_function_registry.cc +++ b/chrome/browser/extensions/extension_function_registry.cc @@ -338,6 +338,7 @@ void ExtensionFunctionRegistry::ResetFunctions() { // the extension-based file picker on Aura. crbug.com/97424 RegisterFunction<CancelFileDialogFunction>(); RegisterFunction<ExecuteTasksFileBrowserFunction>(); + RegisterFunction<SetDefaultTaskFileBrowserFunction>(); RegisterFunction<FileDialogStringsFunction>(); RegisterFunction<GetFileTasksFileBrowserFunction>(); RegisterFunction<GetVolumeMetadataFunction>(); diff --git a/chrome/browser/resources/file_manager/css/file_manager.css b/chrome/browser/resources/file_manager/css/file_manager.css index ba926e0..653e289 100644 --- a/chrome/browser/resources/file_manager/css/file_manager.css +++ b/chrome/browser/resources/file_manager/css/file_manager.css @@ -571,33 +571,61 @@ div.img-container > img { position: absolute; } +/* Padding counterweights negative margins of items, thus eliminating scroll + bar when it's not needed. Max height is set to fit 8 items before showing + scroll bar. */ +#default-actions-list { + max-height: 328px; + padding: 1px 0; +} + +#default-actions-list > li { + display: -webkit-box; +} + +#default-actions-list > li > * { + display: block; + padding: 0 5px; +} + +#default-actions-list > li > img { + height: 16px; + padding-top: 12px; +} + + #list-container list > *, -#list-container grid > * { +#list-container grid > *, +#default-actions-list > * { background-image: none; border-radius: 0; line-height: 30px; } #list-container list > [selected], -#list-container grid > [selected] { +#list-container grid > [selected], +#default-actions-list > [selected] { background-color: rgb(203, 219, 241); } -#list-container list > li.table-row[selected] { +#list-container list > li.table-row[selected], +#default-actions-list > li[selected] { border-top: 1px solid rgb(224, 233, 247); } #list-container list > .accepts[selected], #list-container grid > .accepts[selected], #list-container list > [selected]:hover, -#list-container grid > [selected]:hover { +#list-container grid > [selected]:hover, +#default-actions-list > [selected]:hover { background-color: rgb(193, 211, 236); } #list-container list > .accepts, #list-container grid > .accepts, #list-container list > :hover, -#list-container grid > :hover { +#list-container grid > :hover, +#default-actions-list > :hover { background-color: #f1f1f1; } @@ -845,7 +873,8 @@ input.rename { opacity: 1 !important; } -#list-container li.table-row { +#list-container li.table-row, +#default-actions-list li { border: none; border-top: 1px solid transparent; line-height: 39px; diff --git a/chrome/browser/resources/file_manager/js/combobutton.js b/chrome/browser/resources/file_manager/js/combobutton.js index 62ee57d..7c2fcf1 100644 --- a/chrome/browser/resources/file_manager/js/combobutton.js +++ b/chrome/browser/resources/file_manager/js/combobutton.js @@ -35,6 +35,13 @@ cr.define('cr.ui', function() { }, /** + * Adds separator to drop-down list. + */ + addSeparator: function() { + this.menu.addSeparator(); + }, + + /** * Default item to fire on combobox click */ get defaultItem() { diff --git a/chrome/browser/resources/file_manager/js/default_action_dialog.js b/chrome/browser/resources/file_manager/js/default_action_dialog.js new file mode 100644 index 0000000..348ac4a --- /dev/null +++ b/chrome/browser/resources/file_manager/js/default_action_dialog.js @@ -0,0 +1,137 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +/** + * DefaultActionDialog contains a message, a list box, an ok button, and a + * cancel button. + * This dialog should be used as action picker for file operations. + */ +cr.define('cr.filebrowser', function() { + + /** + * Creates dialog in DOM tree. + * + * @param {HTMLElement} parentNode Node to be parent for this dialog. + */ + function DefaultActionDialog(parentNode) { + cr.ui.dialogs.BaseDialog.call(this, parentNode); + + this.list_ = new cr.ui.List(); + this.list_.id = 'default-actions-list'; + this.frame_.insertBefore(this.list_, this.text_.nextSibling); + + this.selectionModel_ = this.list_.selectionModel = + new cr.ui.ListSingleSelectionModel(); + this.dataModel_ = this.list_.dataModel = new cr.ui.ArrayDataModel([]); + + // List has max-height defined at css, so that list grows automatically, + // but doesn't exceed predefined size. + this.list_.autoExpands = true; + this.list_.activateItemAtIndex = this.activateItemAtIndex_.bind(this); + + this.initialFocusElement_ = this.list_; + + var self = this; + + // Binding stuff doesn't work with constructors, so we have to create + // closure here. + this.list_.itemConstructor = function(item) { + return self.renderItem(item); + } + } + + DefaultActionDialog.prototype = { + __proto__: cr.ui.dialogs.BaseDialog.prototype + }; + + /** + * Overrides BaseDialog::onInputFocus + */ + DefaultActionDialog.prototype.onInputFocus = function() { + this.list_.select(); + }; + + /** + * Renders item for list. + * @param {Object} item Item to render. + */ + DefaultActionDialog.prototype.renderItem = function(item) { + var result = this.document_.createElement('li'); + + var iconNode = this.document_.createElement('img'); + iconNode.src = item.iconUrl; + result.appendChild(iconNode); + + var labelNode = this.document_.createElement('span'); + labelNode.textContent = item.label; + result.appendChild(labelNode); + + cr.defineProperty(result, 'lead', cr.PropertyKind.BOOL_ATTR); + cr.defineProperty(result, 'selected', cr.PropertyKind.BOOL_ATTR); + + return result; + } + + /** + * Shows dialog. + * + * @param {String} message Message in dialog caption. + * @param {Array} items Items to render in list + * @param {int} defaultIndex Item to select by default. + * @param {Function} onOk Callback function. + * @param {Function} onCancel Callback function. + * @param {Function} onShow Callback function. + */ + DefaultActionDialog.prototype.show = function(message, items, defaultIndex, + onOk, onCancel, onShow) { + + cr.ui.dialogs.BaseDialog.prototype.show.apply(this, + [message, onOk, onCancel, onShow]); + + this.list_.startBatchUpdates(); + + this.dataModel_.splice(0, this.dataModel_.length); + + for (var i = 0; i < items.length; i++) { + this.dataModel_.push(items[i]); + } + + this.selectionModel_.selectedIndex = defaultIndex; + + this.list_.endBatchUpdates(); + }; + + /** + * List activation handler. Closes dialog and calls 'ok' callback. + * + * @param {int} index Activated index. + */ + DefaultActionDialog.prototype.activateItemAtIndex_ = function(index) { + this.hide(); + if (this.onOk_) + this.onOk_(this.dataModel_.item(index).task); + } + + /** + * Closes dialog and invokes callback with currently-selected item. + */ + DefaultActionDialog.prototype.onOkClick_ = function() { + this.activateItemAtIndex_(this.selectionModel_.selectedIndex); + }; + + // Overrides BaseDialog::onContainerKeyDown_; + DefaultActionDialog.prototype.onContainerKeyDown_ = function(event) { + // Handle Escape. + if (event.keyCode == 27) { + this.onCancelClick_(event); + event.preventDefault(); + } else if (event.keyCode == 32 || event.keyCode == 13) { + this.onOkClick_(); + event.preventDefault(); + } + }; + + return {DefaultActionDialog: DefaultActionDialog}; +}); diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js index 7425dac..ba499c5 100644 --- a/chrome/browser/resources/file_manager/js/file_manager.js +++ b/chrome/browser/resources/file_manager/js/file_manager.js @@ -590,6 +590,8 @@ FileManager.prototype = { this.alert = new d.AlertDialog(this.dialogDom_); this.confirm = new d.ConfirmDialog(this.dialogDom_); this.prompt = new d.PromptDialog(this.dialogDom_); + this.defaultTaskPicker = + new cr.filebrowser.DefaultActionDialog(this.dialogDom_); }; /** @@ -2591,6 +2593,7 @@ FileManager.prototype = { } } + var defaultIdx = 0; this.taskItems_.hidden = dropDownItems.length == 0; if (dropDownItems.length > 1) { @@ -2600,7 +2603,17 @@ FileManager.prototype = { for (var j = 0; j < dropDownItems.length; j++) { this.taskItems_.addDropDownItem(dropDownItems[j]); + if (dropDownItems[j].task.taskId == defaultTask.taskId) { + defaultIdx = j; + } } + + this.taskItems_.addSeparator(); + this.taskItems_.addDropDownItem({ + label: str('CHANGE_DEFAULT_MENU_ITEM'), + items: dropDownItems, + defaultIdx: defaultIdx + }); } selection.tasksList = tasksList; @@ -2622,10 +2635,55 @@ FileManager.prototype = { } }; + /** + * Task combobox handler. + * + * @param {Object} event Event containing task which was clicked. + */ FileManager.prototype.onTaskItemClicked_ = function(event) { - this.dispatchFileTask_(event.item.task.taskId, this.selection.urls); + if (event.item.task) { + // Task field doesn't exist on change-default dropdown item. + this.dispatchFileTask_(event.item.task.taskId, this.selection.urls); + } else { + var extensions = []; + + for (var i = 0; i < this.selection.urls.length; i++) { + var match = /\.(\w+)$/g.exec(this.selection.urls[i]); + if (match) { + var ext = match[1].toUpperCase(); + if (extensions.indexOf(ext) == -1) { + extensions.push(ext); + } + } + } + + var format = ''; + + if (extensions.length != 1) { + format = extensions[0]; + } + + // Change default was clicked. We should open "change default" dialog. + this.defaultTaskPicker.show( + strf('CHANGE_DEFAULT_CAPTION', format), + event.item.items, event.item.defaultIdx, + this.onDefaultTaskDone_.bind(this)); + } }; + + /** + * Set's given task as default, when this task is applicable. + * @param {Object} task Task to set as default. + */ + FileManager.prototype.onDefaultTaskDone_ = function(task) { + chrome.fileBrowserPrivate.setDefaultTask(task.taskId); + + chrome.fileBrowserPrivate.getFileTasks( + this.selection.urls, + this.onTasksFound_.bind(this, this.selection)); + } + /** * Dispatches default task for the current selection. If tasks are not ready * yet, dispatches after task are available. diff --git a/chrome/browser/resources/file_manager/js/main_scripts.js b/chrome/browser/resources/file_manager/js/main_scripts.js index 09b3c46..7fecb37 100644 --- a/chrome/browser/resources/file_manager/js/main_scripts.js +++ b/chrome/browser/resources/file_manager/js/main_scripts.js @@ -46,7 +46,6 @@ //<include src="../../shared/js/cr/ui/menu.js"/> //<include src="../../shared/js/cr/ui/menu_button.js"/> //<include src="../../shared/js/cr/ui/context_menu_handler.js"/> -// //<include src="combobutton.js"/> // //<include src="util.js"/> @@ -58,6 +57,7 @@ //<include src="file_type.js"/> //<include src="file_transfer_controller.js"/> //<include src="metadata/metadata_cache.js"/> +//<include src="default_action_dialog.js"/> // // For accurate load performance tracking place main.js should be // // the last include to include. //<include src="main.js"/> diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html index 13bc8ae..410f9ee 100644 --- a/chrome/browser/resources/file_manager/main.html +++ b/chrome/browser/resources/file_manager/main.html @@ -76,6 +76,7 @@ <script src="js/file_transfer_controller.js"></script> <script src="js/file_type.js"></script> <script src="js/metadata/metadata_cache.js"></script> + <script src="js/default_action_dialog.js"></script> <!-- For accurate load performance tracking main.js should be the last script to include. --> <script src="js/main.js"></script> diff --git a/chrome/common/extensions/api/file_browser_private.json b/chrome/common/extensions/api/file_browser_private.json index 3b28fd8..fefcc25 100644 --- a/chrome/common/extensions/api/file_browser_private.json +++ b/chrome/common/extensions/api/file_browser_private.json @@ -388,6 +388,23 @@ ] }, { + "name": "setDefaultTask", + "description": "Sets default task.", + "parameters": [ + { + "name": "taskId", + "type": "string", + "description": "The unique identifier of task to mark as default." + }, + { + "name": "callback", + "type": "function", + "optional": true, + "parameters": [] + } + ] + }, + { "name": "getFileTasks", "description": "Gets the list of tasks that can be performed over selected files.", "parameters": [ |