summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd3
-rw-r--r--chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc1
-rw-r--r--chrome/browser/resources/file_manager/css/file_manager.css88
-rw-r--r--chrome/browser/resources/file_manager/js/directory_model.js47
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager.js45
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager_commands.js25
-rw-r--r--chrome/browser/resources/file_manager/js/main_scripts.js1
-rw-r--r--chrome/browser/resources/file_manager/js/sidebar.js377
-rw-r--r--chrome/browser/resources/file_manager/js/test_util.js4
-rw-r--r--chrome/browser/resources/file_manager/js/volume_list.js196
-rw-r--r--chrome/browser/resources/file_manager/js/volume_manager.js1
-rw-r--r--chrome/browser/resources/file_manager/main.html2
-rw-r--r--chrome/browser/resources/file_manager/main_new_ui.html42
13 files changed, 692 insertions, 140 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index fdfba12..839a0e5 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12172,6 +12172,9 @@ Some features may be unavailable. Please check that the profile exists and you
<message name="IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL" desc="/drive directory label.">
Google Drive
</message>
+ <message name="IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL" desc="A label for the 'My Drive' collection of Google Drive.">
+ My Drive
+ </message>
<message name="IDS_FILE_BROWSER_DRIVE_SHARED_WITH_ME_COLLECTION_LABEL" desc="A label for the 'shared with me' collection of Google Drive.">
Shared with me
</message>
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
index f689691..2cf52f9 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
@@ -1855,6 +1855,7 @@ bool FileDialogStringsFunction::RunImpl() {
SET_STRING("DOWNLOADS_DIRECTORY_LABEL",
IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL);
SET_STRING("DRIVE_DIRECTORY_LABEL", IDS_FILE_BROWSER_DRIVE_DIRECTORY_LABEL);
+ SET_STRING("DRIVE_MY_DRIVE_LABEL", IDS_FILE_BROWSER_DRIVE_MY_DRIVE_LABEL);
SET_STRING("DRIVE_OFFLINE_COLLECTION_LABEL",
IDS_FILE_BROWSER_DRIVE_OFFLINE_COLLECTION_LABEL);
SET_STRING("DRIVE_SHARED_WITH_ME_COLLECTION_LABEL",
diff --git a/chrome/browser/resources/file_manager/css/file_manager.css b/chrome/browser/resources/file_manager/css/file_manager.css
index 62250d1..eaf8e0f 100644
--- a/chrome/browser/resources/file_manager/css/file_manager.css
+++ b/chrome/browser/resources/file_manager/css/file_manager.css
@@ -284,7 +284,7 @@ body[new-ui] .dialog-sidebar-footer {
/* A vertical splitter between the roots list and the file list. It is actually
a transparent area centered on the roots list right border.*/
-div.sidebar-splitter {
+div.splitter {
-webkit-box-flex: 0;
cursor: col-resize;
margin-left: -3px;
@@ -294,6 +294,66 @@ div.sidebar-splitter {
z-index: 100;
}
+body[new-ui] #volume-list {
+ -webkit-box-flex: 1;
+ -webkit-box-orient: vertical;
+ display: -webkit-box;
+}
+
+body[new-ui] #volume-list > * {
+ background-color: rgb(240, 240, 240);
+ background-image: none;
+ border: none;
+ height: 40px;
+ margin: 0;
+ padding: 0 5px;
+}
+
+body[new-ui] #volume-list > :hover {
+ background-color: rgba(0, 0, 0, 0.05);
+ border-color: rgba(0, 0, 0, 0.05);
+}
+
+body[new-ui] #volume-list > .accepts,
+body[new-ui] #volume-list > [lead][selected],
+body[new-ui] #volume-list > [lead],
+body[new-ui] #volume-list > [selected],
+body[new-ui] #volume-list > [anchor] {
+ background-color: rgb(225, 225, 225);
+}
+
+body[new-ui] #volume-list:focus > .accepts,
+body[new-ui] #volume-list:focus > [lead][selected],
+body[new-ui] #volume-list:focus > [lead],
+body[new-ui] #volume-list:focus > [selected],
+body[new-ui] #volume-list:focus > [anchor] {
+ background-color: rgb(77, 144, 254);
+ color: white;
+}
+
+body[new-ui] #volume-list li.root-item {
+ -webkit-box-align: center;
+ display: -webkit-box;
+ line-height: 22px; /* To accomodate for icons. */
+ padding-left: 11px;
+}
+
+body[new-ui] #volume-list li.root-item > .root-label {
+ -webkit-box-flex: 1;
+ margin: 0 2px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+body[new-ui] #volume-list .volume-icon {
+ background-position: center 2px;
+ background-repeat: no-repeat;
+ height: 24px;
+ width: 24px;
+}
+
+
#directory-tree {
bottom: 0;
left: 0;
@@ -304,6 +364,10 @@ div.sidebar-splitter {
top: 0;
}
+body[new-ui] #directory-tree {
+ border-right: 1px solid rgb(212, 212, 212);
+}
+
#directory-tree .tree-row {
-webkit-border-radius: 0;
background-image: none;
@@ -316,6 +380,7 @@ div.sidebar-splitter {
body[new-ui] #directory-tree .tree-row {
cursor: pointer;
+ line-height: 29px;
}
/* For rows of subitems (non-top items) */
@@ -323,6 +388,10 @@ body[new-ui] #directory-tree .tree-row {
line-height: 31px;
}
+body[new-ui] #directory-tree .tree-children .tree-row {
+ line-height: 29px;
+}
+
#directory-tree .tree-row > .expand-icon {
height: 37px;
left: 3px;
@@ -352,10 +421,6 @@ body[new-ui] #directory-tree .tree-row {
background-color: rgba(0, 0, 0, 0.05);
}
-body[new-ui] #directory-tree .tree-row:hover {
- background-color: transparent;
-}
-
#directory-tree .tree-item.accepts > .tree-row,
#directory-tree .tree-row[lead][selected],
#directory-tree .tree-row[lead],
@@ -502,6 +567,19 @@ body[maximized][type='full-page'] .dialog-header {
position: relative;
}
+.main-panel {
+ -webkit-box-flex: 1;
+ display: -webkit-box;
+}
+
+body[new-ui] .dialog-middlebar-contents {
+ display: -webkit-box;
+ max-width: 50%;
+ min-width: 45px;
+ position: relative;
+ width: 180px;
+}
+
/* Container for the ok/cancel buttons. */
.dialog-footer {
-webkit-box-align: center;
diff --git a/chrome/browser/resources/file_manager/js/directory_model.js b/chrome/browser/resources/file_manager/js/directory_model.js
index 52c1038..4ce915f 100644
--- a/chrome/browser/resources/file_manager/js/directory_model.js
+++ b/chrome/browser/resources/file_manager/js/directory_model.js
@@ -784,7 +784,8 @@ DirectoryModel.prototype.changeDirectory = function(path, opt_errorCallback) {
};
/**
- * Resolves absolute directory path. Handles Drive stub.
+ * Resolves absolute directory path. Handles Drive stub. If the drive is
+ * mounting, callbacks will be called after the mount is completed.
* @param {string} path Path to the directory.
* @param {function(DirectoryEntry} successCallback Success callback.
* @param {function(FileError} errorCallback Error callback.
@@ -792,7 +793,9 @@ DirectoryModel.prototype.changeDirectory = function(path, opt_errorCallback) {
DirectoryModel.prototype.resolveDirectory = function(path, successCallback,
errorCallback) {
if (PathUtil.getRootType(path) == RootType.DRIVE) {
- if (!this.isDriveMounted()) {
+ var driveStatus = this.volumeManager_.getDriveStatus();
+ if (!this.isDriveMounted() &&
+ driveStatus != VolumeManager.DriveStatus.MOUNTING) {
if (path == DirectoryModel.fakeDriveEntry_.fullPath)
successCallback(DirectoryModel.fakeDriveEntry_);
else // Subdirectory.
@@ -1059,19 +1062,26 @@ DirectoryModel.prototype.resolveRoots_ = function(callback) {
drive: null,
driveSpecialSearchRoots: null
};
+ if (util.platform.newUI()) {
+ groups = {
+ downloads: null,
+ archives: null,
+ removables: null,
+ drive: null
+ };
+ }
var self = this;
metrics.startInterval('Load.Roots');
var done = function() {
- for (var i in groups)
+ var roots = [];
+ for (var i in groups) {
if (!groups[i])
return;
+ roots = roots.concat(groups[i]);
+ }
- callback(groups.downloads.
- concat(groups.drive).
- concat(groups.driveSpecialSearchRoots).
- concat(groups.archives).
- concat(groups.removables));
+ callback(roots);
metrics.recordInterval('Load.Roots');
};
@@ -1105,17 +1115,16 @@ DirectoryModel.prototype.resolveRoots_ = function(callback) {
append.bind(this, 'removables'));
if (this.driveEnabled_) {
- groups.driveSpecialSearchRoots = this.showSpecialSearchRoots_ ?
- DirectoryModel.fakeDriveSpecialSearchEntries_ : [];
- var fake = [DirectoryModel.fakeDriveEntry_];
- if (this.isDriveMounted()) {
- readSingle(DirectoryModel.fakeDriveEntry_.fullPath, 'drive', fake);
- } else {
- groups.drive = fake;
- done();
+ if (!util.platform.newUI()) {
+ groups.driveSpecialSearchRoots = this.showSpecialSearchRoots_ ?
+ DirectoryModel.fakeDriveSpecialSearchEntries_ : [];
}
+ // Use a fake instead to return a list as fast as possible.
+ groups.drive = [DirectoryModel.fakeDriveEntry_];
+ done();
} else {
- groups.driveSpecialSearchRoots = [];
+ if (!util.platform.newUI())
+ groups.driveSpecialSearchRoots = [];
groups.drive = [];
done();
}
@@ -1142,8 +1151,8 @@ DirectoryModel.prototype.updateRoots_ = function(opt_callback) {
* @return {boolean} True if DRIVE is fully mounted.
*/
DirectoryModel.prototype.isDriveMounted = function() {
- return this.volumeManager_.getDriveStatus() ==
- VolumeManager.DriveStatus.MOUNTED;
+ var driveStatus = this.volumeManager_.getDriveStatus();
+ return driveStatus == VolumeManager.DriveStatus.MOUNTED;
};
/**
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index 1e8c40e..715284e 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -342,7 +342,7 @@ DialogType.isModal = function(type) {
metrics.startInterval('Load.FileSystem');
- var downcount = 3;
+ var downcount = 4;
var viewOptions = {};
var done = function() {
if (--downcount == 0)
@@ -378,6 +378,7 @@ DialogType.isModal = function(type) {
this.getPreferences_(function() {
if (this.isDriveEnabled())
this.volumeManager_.mountDrive(function() {}, function() {});
+ done();
}.bind(this));
};
@@ -543,7 +544,10 @@ DialogType.isModal = function(type) {
this.dialogDom_.querySelector('#roots-context-menu');
cr.ui.Menu.decorate(this.rootsContextMenu_);
- this.directoryTree_.setContextMenu(this.rootsContextMenu_);
+ if (util.platform.newUI())
+ this.volumeList_.setContextMenu(this.rootsContextMenu_);
+ else
+ this.directoryTree_.setContextMenu(this.rootsContextMenu_);
this.textContextMenu_ =
this.dialogDom_.querySelector('#text-context-menu');
@@ -611,16 +615,24 @@ DialogType.isModal = function(type) {
CommandUtil.registerCommand(doc, 'change-default-app',
Commands.changeDefaultAppCommand, this);
- CommandUtil.registerCommand(this.directoryTree_, 'unmount',
- Commands.unmountCommand, this.directoryTree_, this);
+ if (!util.platform.newUI()) {
+ CommandUtil.registerCommand(this.directoryTree_, 'unmount',
+ Commands.unmountCommand, this.directoryTree_, this);
+
+ CommandUtil.registerCommand(this.directoryTree_, 'import-photos',
+ Commands.importCommand, this.directoryTree_);
+ } else {
+ CommandUtil.registerCommand(this.volumeList_, 'unmount',
+ Commands.unmountCommand, this.volumeList_, this);
+
+ CommandUtil.registerCommand(this.volumeList_, 'import-photos',
+ Commands.importCommand, this.volumeList_);
+ }
CommandUtil.registerCommand(doc, 'format',
Commands.formatCommand, this.directoryTree_, this,
this.directoryModel_);
- CommandUtil.registerCommand(this.directoryTree_, 'import-photos',
- Commands.importCommand, this.directoryTree_);
-
CommandUtil.registerCommand(doc, 'delete',
Commands.deleteFileCommand, this);
@@ -786,8 +798,15 @@ DialogType.isModal = function(type) {
this.onCancelBound_ = this.onCancel_.bind(this);
this.cancelButton_.addEventListener('click', this.onCancelBound_);
- this.decorateSplitter(
- this.dialogDom_.querySelector('div.sidebar-splitter'));
+ if (util.platform.newUI()) {
+ this.decorateSplitter(
+ this.dialogDom_.querySelector('div#sidebar-splitter'));
+ this.decorateSplitter(
+ this.dialogDom_.querySelector('div#middlebar-splitter'));
+ } else {
+ this.decorateSplitter(
+ this.dialogDom_.querySelector('div.sidebar-splitter'));
+ }
this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
@@ -1029,6 +1048,10 @@ DialogType.isModal = function(type) {
FileManager.prototype.initSidebar_ = function() {
this.directoryTree_ = this.dialogDom_.querySelector('#directory-tree');
DirectoryTree.decorate(this.directoryTree_, this.directoryModel_);
+ if (util.platform.newUI()) {
+ this.volumeList_ = this.dialogDom_.querySelector('#volume-list');
+ VolumeList.decorate(this.volumeList_, this.directoryModel_);
+ }
};
/**
@@ -1839,7 +1862,8 @@ DialogType.isModal = function(type) {
FileManager.prototype.onDriveConnectionChanged_ = function() {
var connection = this.volumeManager_.getDriveConnectionState();
- this.dialogContainer_.setAttribute('connection', connection.type);
+ if (this.dialogContainer_)
+ this.dialogContainer_.setAttribute('connection', connection.type);
};
/**
@@ -2204,7 +2228,6 @@ DialogType.isModal = function(type) {
this.closeOnUnmount_ = false;
}
- this.directoryTree_.selectPath(this.getCurrentDirectory());
this.updateUnformattedDriveStatus_();
this.updateTitle_();
this.updateGearMenu_();
diff --git a/chrome/browser/resources/file_manager/js/file_manager_commands.js b/chrome/browser/resources/file_manager/js/file_manager_commands.js
index 88e9351..ef40f60 100644
--- a/chrome/browser/resources/file_manager/js/file_manager_commands.js
+++ b/chrome/browser/resources/file_manager/js/file_manager_commands.js
@@ -10,16 +10,24 @@ var CommandUtil = {};
* Extracts root on which command event was dispatched.
*
* @param {Event} event Command event for which to retrieve root to operate on.
- * @param {DirectoryTree} directoryTree Directory tree to extract root node.
+ * @param {DirectoryTree|VolumeList} list Directory tree or volume list to
+ * extract root node.
* @return {DirectoryEntry} Found root.
*/
-CommandUtil.getCommandRoot = function(event, directoryTree) {
- var entry = directoryTree.selectedItem;
-
- if (entry && PathUtil.isRootPath(entry.fullPath))
- return entry;
- else
- return null;
+CommandUtil.getCommandRoot = function(event, list) {
+ if (util.platform.newUI() && list instanceof VolumeList) {
+ var result = list.dataModel.item(
+ list.getIndexOfListItem(event.target)) ||
+ list.selectedItem;
+ return result;
+ } else {
+ var entry = list.selectedItem;
+
+ if (entry && PathUtil.isRootPath(entry.fullPath))
+ return entry;
+ else
+ return null;
+ }
};
/**
@@ -29,7 +37,6 @@ CommandUtil.getCommandRoot = function(event, directoryTree) {
*/
CommandUtil.getCommandRootType = function(event, directoryTree) {
var root = CommandUtil.getCommandRoot(event, directoryTree);
-
return root && PathUtil.getRootType(root.fullPath);
};
diff --git a/chrome/browser/resources/file_manager/js/main_scripts.js b/chrome/browser/resources/file_manager/js/main_scripts.js
index 4521379..06f0e0d 100644
--- a/chrome/browser/resources/file_manager/js/main_scripts.js
+++ b/chrome/browser/resources/file_manager/js/main_scripts.js
@@ -89,6 +89,7 @@
//<include src="file_type.js"/>
//<include src="scrollbar.js"/>
//<include src="sidebar.js"/>
+//<include src="volume_list.js"/>
//<include src="volume_manager.js"/>
//<include src="media/media_util.js"/>
//<include src="metadata/metadata_cache.js"/>
diff --git a/chrome/browser/resources/file_manager/js/sidebar.js b/chrome/browser/resources/file_manager/js/sidebar.js
index 2753b07..9904366 100644
--- a/chrome/browser/resources/file_manager/js/sidebar.js
+++ b/chrome/browser/resources/file_manager/js/sidebar.js
@@ -4,6 +4,11 @@
'use strict';
+// TODO(yoshiki): rename this sidebar.js to directory_tree.js.
+
+////////////////////////////////////////////////////////////////////////////////
+// DirectoryTreeUtil
+
/**
* Utility methods. They are intended for use only in this file.
*/
@@ -99,7 +104,7 @@ DirectoryTreeUtil.isDummyEntry = function(dirEntry) {
DirectoryTreeUtil.searchAndSelectPath = function(items, path) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
- if (PathUtil.isParentPath(item.fullPath, path)) {
+ if (PathUtil.isParentPath(item.entry.fullPath, path)) {
item.selectPath(path);
return true;
}
@@ -108,6 +113,123 @@ DirectoryTreeUtil.searchAndSelectPath = function(items, path) {
};
/**
+ * Modifies a list of the directory entries to match the new UI sepc.
+ *
+ * TODO(yoshiki): remove this after the old UI is removed.
+ *
+ * @param {Array.<DirectoryEntry>} entries The list of entty.
+ * @return {Array.<DirectoryEntries>} Modified entries.
+ */
+DirectoryTreeUtil.addAndRemoveDriveSpecialDirs = function(entries) {
+ if (!util.platform.newUI()) {
+ console.error('This function should be used only in new ui.');
+ return [];
+ }
+ var modifiedEntries = [];
+ for (var i in entries) {
+ // Removes '/drive/other'.
+ var entry = entries[i];
+ if (entry.fullPath ==
+ (RootDirectory.DRIVE + '/' + DriveSubRootDirectory.OTHER)) {
+ continue;
+ }
+
+ // Changes the label of '/drive/root' to 'My Drive'.
+ if (entry.fullPath == DirectoryModel.fakeDriveEntry_.fullPath) {
+ entry.label = str('DRIVE_MY_DRIVE_LABEL');
+ }
+
+ modifiedEntries.push(entry);
+ }
+
+ // Adds the special directories.
+ var specialDirs = [DirectoryModel.fakeDriveSharedWithMeEntry_,
+ DirectoryModel.fakeDriveRecentEntry_,
+ DirectoryModel.fakeDriveOfflineEntry_];
+ for (var i in specialDirs) {
+ var dir = specialDirs[i];
+ dir['label'] = PathUtil.getRootLabel(dir.fullPath);
+ modifiedEntries.push(dir);
+ }
+ return modifiedEntries;
+};
+
+/**
+ * Retrieves the file list with the latest information.
+ *
+ * @param {DirectoryTree|DirectoryItem} item Parent to be reloaded.
+ * @param {DirectoryModel} dm The directory model.
+ * @param {boolean} recursive True if the update is recursively.
+ * @param {function(Array.<Entry>)} successCallback Callback on success.
+ * @param {function()=} opt_errorCallback Callback on failure.
+ */
+DirectoryTreeUtil.updateSubDirectories = function(
+ item, dm, recursive, successCallback, opt_errorCallback) {
+ // Tries to retrieve new entry if the cached entry is dummy.
+ if (DirectoryTreeUtil.isDummyEntry(item.entry)) {
+ // Fake Drive root.
+ dm.resolveDirectory(
+ item.fullPath,
+ function(entry) {
+ item.dirEntry_ = entry;
+
+ // If the retrieved entry is dummy again, returns with an error.
+ if (DirectoryTreeUtil.isDummyEntry(entry)) {
+ if (opt_errorCallback)
+ opt_errorCallback();
+ return;
+ }
+
+ DirectoryTreeUtil.updateSubDirectories(
+ item, dm, recursive, successCallback, opt_errorCallback);
+ },
+ opt_errorCallback);
+ return;
+ }
+
+ var reader = item.entry.createReader();
+ var entries = [];
+ var readEntry = function() {
+ reader.readEntries(function(results) {
+ if (!results.length) {
+ if (item.entry.fullPath == RootDirectory.DRIVE)
+ successCallback(
+ DirectoryTreeUtil.addAndRemoveDriveSpecialDirs(entries));
+ else
+ successCallback(
+ DirectoryTreeUtil.sortEntries(item.fileFilter_, entries));
+ return;
+ }
+
+ for (var i = 0; i < results.length; i++) {
+ var entry = results[i];
+ if (entry.isDirectory)
+ entries.push(entry);
+ }
+ readEntry();
+ });
+ };
+ readEntry();
+};
+
+/**
+ * Sorts a list of entries.
+ *
+ * @param {FileFilter} fileFilter The file filter.
+ * @param {Array.<Entries>} entries Entries to be sorted.
+ * @return {Array.<Entries>} Sorted entries.
+ */
+DirectoryTreeUtil.sortEntries = function(fileFilter, entries) {
+ entries.sort(function(a, b) {
+ return (a.name.toLowerCase() > b.name.toLowerCase()) ? 1 : -1;
+ });
+ return entries.filter(fileFilter.filter.bind(fileFilter));
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// DirectoryItem
+
+/**
* A directory in the tree. Each element represents one directory.
*
* @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
@@ -166,8 +288,13 @@ DirectoryItem.prototype = {
DirectoryItem.prototype.decorate = function(
dirEntry, parentDirItem, directoryModel) {
var path = dirEntry.fullPath;
- var label = PathUtil.isRootPath(path) ?
- PathUtil.getRootLabel(path) : dirEntry.name;
+ var label;
+ if (!util.platform.newUI()) {
+ label = PathUtil.isRootPath(path) ?
+ PathUtil.getRootLabel(path) : dirEntry.name;
+ } else {
+ label = dirEntry.label ? dirEntry.label : dirEntry.name;
+ }
this.className = 'tree-item';
this.innerHTML =
@@ -195,13 +322,22 @@ DirectoryItem.prototype.decorate = function(
this.addEventListener('expand', this.onExpand_.bind(this), false);
var volumeManager = VolumeManager.getInstance();
var icon = this.querySelector('.icon');
- if (PathUtil.isRootPath(path)) {
- icon.classList.add('volume-icon');
- var iconType = PathUtil.getRootType(path);
- icon.setAttribute('volume-type-icon', iconType);
-
- if (iconType == RootType.REMOVABLE)
- icon.setAttribute('volume-subtype', volumeManager.getDeviceType(path));
+ if (!util.platform.newUI()) {
+ if (PathUtil.isRootPath(path)) {
+ icon.classList.add('volume-icon');
+ var iconType = PathUtil.getRootType(path);
+ icon.setAttribute('volume-type-icon', iconType);
+
+ if (iconType == RootType.REMOVABLE)
+ icon.setAttribute('volume-subtype', volumeManager.getDeviceType(path));
+ }
+ } else {
+ icon.classList.add('volume-icon');
+ var iconType = PathUtil.getRootType(path);
+ if (iconType && PathUtil.isRootPath(path))
+ icon.setAttribute('volume-type-icon', iconType);
+ else
+ icon.setAttribute('file-type-icon', 'folder');
}
var eject = this.querySelector('.root-eject');
@@ -252,49 +388,16 @@ DirectoryItem.prototype.onExpand_ = function(e) {
*/
DirectoryItem.prototype.updateSubDirectories = function(
recursive, opt_successCallback, opt_errorCallback) {
- // Tries to retrieve new entry if the cached entry is dummy.
- if (DirectoryTreeUtil.isDummyEntry(this.dirEntry_)) {
- // Fake Drive root.
- this.directoryModel_.resolveDirectory(
- this.fullPath,
- function(entry) {
- this.dirEntry_ = entry;
-
- // If the retrieved entry is dummy again, returns with an error.
- if (DirectoryTreeUtil.isDummyEntry(entry)) {
- if (opt_errorCallback)
- opt_errorCallback();
- return;
- }
-
- this.updateSubDirectories(
- recursive, opt_successCallback, opt_errorCallback);
- }.bind(this),
- opt_errorCallback);
- return;
- }
-
- var reader = this.dirEntry_.createReader();
- var entries = [];
-
- var readEntry = function() {
- reader.readEntries(function(results) {
- if (!results.length) {
- this.entries_ = entries.sort();
+ DirectoryTreeUtil.updateSubDirectories(
+ this,
+ this.directoryModel_,
+ recursive,
+ function(entries) {
+ this.entries_ = entries;
this.redrawSubDirectoryList_(recursive);
opt_successCallback && opt_successCallback();
- return;
- }
-
- for (var i = 0; i < results.length; i++) {
- var entry = results[i];
- if (entry.isDirectory)
- entries.push(entry);
- }
- readEntry();
- }.bind(this));
- }.bind(this);
- readEntry();
+ }.bind(this),
+ opt_errorCallback);
};
/**
@@ -351,6 +454,9 @@ DirectoryItem.prototype.doAction = function() {
this.directoryModel_.changeDirectory(this.fullPath);
};
+////////////////////////////////////////////////////////////////////////////////
+// DirectoryTree
+
/**
* Tree of directories on the sidebar. This element is also the root of items,
* in other words, this is the parent of the top-level items.
@@ -378,7 +484,17 @@ DirectoryTree.prototype = {
/**
* @param {boolean} value Not used.
*/
- set expanded(value) {}
+ set expanded(value) {},
+
+ /**
+ * The DirectoryEntry corresponding to this DirectoryItem. This may be
+ * a dummy DirectoryEntry.
+ * @type {DirectoryEntry|Object}
+ * @override
+ **/
+ get entry() {
+ return this.dirEntry_;
+ }
};
/**
@@ -389,36 +505,53 @@ DirectoryTree.prototype.decorate = function(directoryModel) {
cr.ui.Tree.prototype.decorate.call(this);
this.directoryModel_ = directoryModel;
+ this.entries_ = [];
this.fileFilter_ = this.directoryModel_.getFileFilter();
this.fileFilter_.addEventListener('changed',
this.onFilterChanged_.bind(this));
+ /**
+ * The path of the root directory.
+ * @type {string}
+ */
+ this.fullPath = '/';
+ this.dirEntry_ = null;
+ if (!util.platform.newUI()) {
+ this.rootsList_ = this.directoryModel_.getRootsList();
+ this.rootsList_.addEventListener('change',
+ this.onRootsListChanged_.bind(this));
+ this.rootsList_.addEventListener('permuted',
+ this.onRootsListChanged_.bind(this));
+ }
+
+ /**
+ * The path of the current directory.
+ * @type {string}
+ */
+ this.currentPath_ = null;
- this.rootsList_ = this.directoryModel_.getRootsList();
- this.rootsList_.addEventListener('change',
- this.onRootsListChanged_.bind(this));
- this.rootsList_.addEventListener('permuted',
- this.onRootsListChanged_.bind(this));
- this.onRootsListChanged_();
+ this.directoryModel_.addEventListener('directory-changed',
+ this.onCurrentDirectoryChanged_.bind(this));
// Add a handler for directory change.
this.addEventListener('change', function() {
- if (this.selectedItem) {
+ if (this.selectedItem && this.currentPath_ != this.selectedItem.fullPath) {
+ this.currentPath_ = this.selectedItem;
this.selectedItem.doAction();
return;
}
-
- // Fallback to the default directory.
- this.directoryModel_.changeDirectory(
- this.directoryModel_.getDefaultDirectory());
}.bind(this));
- this.privateOnDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
+ this.privateOnDirectoryChangedBound_ =
+ this.onDirectoryContentChanged_.bind(this);
chrome.fileBrowserPrivate.onDirectoryChanged.addListener(
this.privateOnDirectoryChangedBound_);
if (util.platform.newUI())
ScrollBar.createVertical(this.parentNode, this);
+
+ if (!util.platform.newUI())
+ this.onRootsListChanged_();
};
/**
@@ -428,8 +561,10 @@ DirectoryTree.prototype.decorate = function(directoryModel) {
* @param {cr.ui.Menu} menu Context menu.
*/
DirectoryTree.prototype.setContextMenu = function(menu) {
- this.contextMenu_ = menu;
+ if (util.platform.newUI())
+ return;
+ this.contextMenu_ = menu;
for (var i = 0; i < this.rootsList_.length; i++) {
var item = this.rootsList_.item(i);
var type = PathUtil.getRootType(item.fullPath);
@@ -446,10 +581,82 @@ DirectoryTree.prototype.setContextMenu = function(menu) {
* @param {string} path Path to be selected.
*/
DirectoryTree.prototype.selectPath = function(path) {
- if (this.selectedItem && path == this.selectedItem.fullPath)
+ if ((this.entry && this.entry.fullPath == path) || this.currentPath_ == path)
return;
+ this.currentPath_ = path;
+ this.selectPathInternal_(path);
+};
- DirectoryTreeUtil.searchAndSelectPath(this.items, path);
+/**
+ * Select the item corresponding to the given path. This method is used
+ * internally.
+ * @param {string} path Path to be selected.
+ * @private
+ */
+DirectoryTree.prototype.selectPathInternal_ = function(path) {
+ var rootDirPath = PathUtil.getRootPath(path);
+
+ if (PathUtil.isSpecialSearchRoot(rootDirPath) ||
+ PathUtil.getRootType(rootDirPath) == RootType.DRIVE) {
+ rootDirPath = RootDirectory.DRIVE;
+ }
+
+ if (this.fullPath != rootDirPath) {
+ this.fullPath = rootDirPath;
+
+ // Clears the list
+ this.dirEntry_ = [];
+ this.entries_ = [];
+ this.redraw(false);
+
+ this.directoryModel_.resolveDirectory(
+ rootDirPath,
+ function(entry) {
+ if (this.fullPath != rootDirPath)
+ return;
+
+ this.dirEntry_ = entry;
+ this.selectPathInternal_(path);
+ }.bind(this),
+ function() {});
+ } else {
+ if (this.selectedItem && path == this.selectedItem.fullPath)
+ return;
+
+ if (DirectoryTreeUtil.searchAndSelectPath(this.items, path))
+ return;
+
+ this.selectedItem = null;
+ this.updateSubDirectories(
+ false /* recursive */,
+ function() {
+ if (!DirectoryTreeUtil.searchAndSelectPath(
+ this.items, this.currentPath_))
+ this.selectedItem = null;
+ }.bind(this));
+ }
+};
+
+/**
+ * Retrieves the latest subdirectories and update them on the tree.
+ * @param {boolean} recursive True if the update is recursively.
+ * @param {function()=} opt_successCallback Callback called on success.
+ */
+DirectoryTree.prototype.updateSubDirectories = function(
+ recursive, opt_successCallback) {
+ if (!this.currentPath_)
+ return;
+
+ DirectoryTreeUtil.updateSubDirectories(
+ this,
+ this.directoryModel_,
+ recursive,
+ function(entries) {
+ this.entries_ = entries;
+ this.redraw(recursive);
+ if (opt_successCallback)
+ opt_successCallback();
+ }.bind(this));
};
/**
@@ -458,8 +665,8 @@ DirectoryTree.prototype.selectPath = function(path) {
* @private
*/
DirectoryTree.prototype.onRootsListChanged_ = function() {
- this.redraw(false /* recursive */);
- this.selectPath(this.directoryModel_.getCurrentDirPath());
+ if (!util.platform.newUI())
+ this.redraw(false /* recursive */);
};
/**
@@ -468,12 +675,21 @@ DirectoryTree.prototype.onRootsListChanged_ = function() {
* only root items are updated.
*/
DirectoryTree.prototype.redraw = function(recursive) {
- var rootsList = this.rootsList_;
- DirectoryTreeUtil.updateSubElementsFromList(this,
- rootsList.item.bind(rootsList),
- this.directoryModel_,
- recursive);
- this.setContextMenu(this.contextMenu_);
+ if (!util.platform.newUI()) {
+ var rootsList = this.rootsList_;
+ DirectoryTreeUtil.updateSubElementsFromList(
+ this,
+ rootsList.item.bind(rootsList),
+ this.directoryModel_,
+ recursive);
+ } else {
+ DirectoryTreeUtil.updateSubElementsFromList(
+ this,
+ function(i) { return this.entries_[i]; }.bind(this),
+ this.directoryModel_,
+ recursive);
+ this.setContextMenu(this.contextMenu_);
+ }
};
/**
@@ -489,7 +705,7 @@ DirectoryTree.prototype.onFilterChanged_ = function() {
* @param {!UIEvent} event Event.
* @private
*/
-DirectoryTree.prototype.onDirectoryChanged_ = function(event) {
+DirectoryTree.prototype.onDirectoryContentChanged_ = function(event) {
if (event.eventType == 'changed') {
var path = util.extractFilePath(event.directoryUrl);
DirectoryTreeUtil.updateChangedDirectoryItem(path, this);
@@ -497,6 +713,15 @@ DirectoryTree.prototype.onDirectoryChanged_ = function(event) {
};
/**
+ * Invoked when the current directory is changed.
+ * @param {!UIEvent} event Event.
+ * @private
+ */
+DirectoryTree.prototype.onCurrentDirectoryChanged_ = function(event) {
+ this.selectPath(event.newDirEntry.fullPath);
+};
+
+/**
* Returns the path of the selected item.
* @return {string} The current path.
*/
diff --git a/chrome/browser/resources/file_manager/js/test_util.js b/chrome/browser/resources/file_manager/js/test_util.js
index aa1ca01..c3a4b46 100644
--- a/chrome/browser/resources/file_manager/js/test_util.js
+++ b/chrome/browser/resources/file_manager/js/test_util.js
@@ -113,7 +113,9 @@ test.util.waitForFileListChange = function(
var notReadyRows = files.filter(function(row) {
return row.filter(function(cell) { return cell == '...'; }).length;
});
- if (notReadyRows.length === 0 && files.length !== lengthBefore)
+ if (notReadyRows.length === 0 &&
+ files.length !== lengthBefore &&
+ files.length !== 0)
callback(files);
else
window.setTimeout(helper, 50);
diff --git a/chrome/browser/resources/file_manager/js/volume_list.js b/chrome/browser/resources/file_manager/js/volume_list.js
new file mode 100644
index 0000000..5910fdf
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/volume_list.js
@@ -0,0 +1,196 @@
+// 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.
+
+'use strict';
+
+/**
+ * A volume list.
+ */
+function VolumeList() {
+}
+
+/**
+ * VolumeList inherits cr.ui.List.
+ */
+VolumeList.prototype.__proto__ = cr.ui.List.prototype;
+
+/**
+ * @param {HTMLElement} el Element to be DirectoryItem.
+ * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ */
+VolumeList.decorate = function(el, directoryModel) {
+ el.__proto__ = VolumeList.prototype;
+ el.decorate(directoryModel);
+};
+
+/**
+ * @param {DirectoryModel} directoryModel Current DirectoryModel.
+ */
+VolumeList.prototype.decorate = function(directoryModel) {
+ cr.ui.List.decorate(this);
+ this.__proto__ = VolumeList.prototype;
+
+ this.directoryModel_ = directoryModel;
+ this.volumeManager_ = VolumeManager.getInstance();
+ this.selectionModel = new cr.ui.ListSingleSelectionModel();
+
+ this.directoryModel_.addEventListener('directory-changed',
+ this.onCurrentDirectoryChanged_.bind(this));
+ this.selectionModel.addEventListener(
+ 'change', this.onSelectionChange_.bind(this));
+ this.selectionModel.addEventListener(
+ 'beforeChange', this.onBeforeSelectionChange_.bind(this));
+ this.currentVolume_ = null;
+
+ // Overriding default role 'list' set by cr.ui.List.decorate() to 'listbox'
+ // role for better accessibility on ChromeOS.
+ this.setAttribute('role', 'listbox');
+
+ var self = this;
+ this.itemConstructor = function(entry) {
+ return self.renderRoot_(entry);
+ };
+
+ //this.rootsList_.selectionModel =
+ // this.directoryModel_.getRootsListSelectionModel();
+ this.dataModel = this.directoryModel_.getRootsList();
+};
+
+/**
+ * Creates an element of a volume. This method is called from cr.ui.List
+ * internally.
+ * @param {DirectoryEntry} entry Entry of the directory to be rendered.
+ * @return {HTMLElement} Rendered element.
+ * @private
+ */
+VolumeList.prototype.renderRoot_ = function(entry) {
+ var path = entry.fullPath;
+ var li = cr.doc.createElement('li');
+ li.className = 'root-item';
+ li.setAttribute('role', 'option');
+ var dm = this.directoryModel_;
+ var handleClick = function() {
+ if (li.selected && path !== dm.getCurrentDirPath()) {
+ this.directoryModel_.changeDirectory(path);
+ }
+ }.bind(this);
+ li.addEventListener('click', handleClick);
+ li.addEventListener(cr.ui.TouchHandler.EventType.TOUCH_START, handleClick);
+
+ var rootType = PathUtil.getRootType(path);
+
+ var iconDiv = cr.doc.createElement('div');
+ iconDiv.className = 'volume-icon';
+ iconDiv.setAttribute('volume-type-icon', rootType);
+ if (rootType === RootType.REMOVABLE) {
+ iconDiv.setAttribute('volume-subtype',
+ this.volumeManager_.getDeviceType(path));
+ }
+ li.appendChild(iconDiv);
+
+ var div = cr.doc.createElement('div');
+ div.className = 'root-label';
+
+ div.textContent = PathUtil.getRootLabel(path);
+ li.appendChild(div);
+
+ if (rootType === RootType.ARCHIVE || rootType === RootType.REMOVABLE) {
+ var eject = cr.doc.createElement('div');
+ eject.className = 'root-eject';
+ eject.addEventListener('click', function(event) {
+ event.stopPropagation();
+ var unmountCommand = cr.doc.querySelector('command#unmount');
+ // Let's make sure 'canExecute' state of the command is properly set for
+ // the root before executing it.
+ unmountCommand.canExecuteChange(li);
+ unmountCommand.execute(li);
+ }.bind(this));
+ // Block other mouse handlers.
+ eject.addEventListener('mouseup', function(e) { e.stopPropagation() });
+ eject.addEventListener('mousedown', function(e) { e.stopPropagation() });
+ li.appendChild(eject);
+ }
+
+ if (this.contextMenu_ &&
+ rootType != RootType.DRIVE && rootType != RootType.DOWNLOADS)
+ cr.ui.contextMenuHandler.setContextMenu(li, this.contextMenu_);
+
+ cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
+ cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
+
+ // If the current directory is already set.
+ if (this.currentVolume_ == path) {
+ setTimeout(function() {
+ this.selectedItem = entry;
+ }.bind(this), 0);
+ }
+
+ return li;
+};
+
+/**
+ * Sets a context menu. Context menu is enabled only on archive and removable
+ * volumes as of now.
+ *
+ * @param {cr.ui.Menu} menu Context menu.
+ */
+VolumeList.prototype.setContextMenu = function(menu) {
+ this.contextMenu_ = menu;
+
+ for (var i = 0; i < this.dataModel.length; i++) {
+ var item = this.rootsList_.item(i);
+ var type = PathUtil.getRootType(item.fullPath);
+ // Context menu is set only to archive and removable volumes.
+ if (type == RootType.ARCHIVE || type == RootType.REMOVABLE) {
+ cr.ui.contextMenuHandler.setContextMenu(this.getListItemByIndex(i),
+ this.contextMenu_);
+ }
+ }
+};
+
+/**
+ * Handler before root item change.
+ * @param {Event} event The event.
+ * @private
+ */
+VolumeList.prototype.onBeforeSelectionChange_ = function(event) {
+ if (event.changes.length == 1 && !event.changes[0].selected)
+ event.preventDefault();
+};
+
+/**
+ * Handler for root item being clicked.
+ * @param {Event} event The event.
+ * @private
+ */
+VolumeList.prototype.onSelectionChange_ = function(event) {
+ var newRootDir = this.dataModel.item(this.selectionModel.selectedIndex);
+ if (newRootDir && this.currentVolume_ != newRootDir.fullPath) {
+ this.currentVolume_ = newRootDir.fullPath;
+ this.directoryModel_.changeDirectory(this.currentVolume_);
+ }
+};
+
+/**
+ * Invoked when the current directory is changed.
+ * @param {Event} event The event.
+ * @private
+ */
+VolumeList.prototype.onCurrentDirectoryChanged_ = function(event) {
+ var path = event.newDirEntry.fullPath || dm.getCurrentDirPath();
+ var newRootPath = PathUtil.getRootPath(path);
+
+ // Sets |this.currentVolume_| in advance to prevent |onSelectionChange_()|
+ // from calling |DirectoryModel.ChangeDirectory()| again.
+ this.currentVolume_ = newRootPath;
+
+ for (var i = 0; i < this.dataModel.length; i++) {
+ var item = this.dataModel.item(i);
+ if (PathUtil.getRootPath(item.fullPath) == newRootPath) {
+
+ this.selectionModel.selectedIndex = i;
+ return;
+ }
+ }
+};
diff --git a/chrome/browser/resources/file_manager/js/volume_manager.js b/chrome/browser/resources/file_manager/js/volume_manager.js
index c329d87..41d72fa 100644
--- a/chrome/browser/resources/file_manager/js/volume_manager.js
+++ b/chrome/browser/resources/file_manager/js/volume_manager.js
@@ -375,6 +375,7 @@ VolumeManager.prototype.mountDrive = function(successCallback, errorCallback) {
if (this.getDriveStatus() == VolumeManager.DriveStatus.ERROR) {
this.setDriveStatus_(VolumeManager.DriveStatus.UNMOUNTED);
}
+ this.setDriveStatus_(VolumeManager.DriveStatus.MOUNTING);
var self = this;
this.mount_('', 'drive', function(mountPath) {
this.waitDriveLoaded_(mountPath, function(success, error) {
diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html
index 9afdc11..a0cb7c663 100644
--- a/chrome/browser/resources/file_manager/main.html
+++ b/chrome/browser/resources/file_manager/main.html
@@ -218,7 +218,7 @@
<div class=dialog-sidebar>
<tree id="directory-tree" tabindex="8"></tree>
</div>
- <div class=sidebar-splitter></div>
+ <div class="sidebar-splitter splitter"></div>
<div class=dialog-main>
<div class=dialog-header>
<div class="offline-icon"></div>
diff --git a/chrome/browser/resources/file_manager/main_new_ui.html b/chrome/browser/resources/file_manager/main_new_ui.html
index 3720302..ecc5d3e 100644
--- a/chrome/browser/resources/file_manager/main_new_ui.html
+++ b/chrome/browser/resources/file_manager/main_new_ui.html
@@ -103,6 +103,7 @@
<script src="js/scrollbar.js"></script>
<script src="js/sidebar.js"></script>
<script src="js/volume_manager.js"></script>
+ <script src="js/volume_list.js"></script>
<script src="js/media/media_util.js"></script>
<script src="js/metadata/metadata_cache.js"></script>
<script src="js/default_action_dialog.js"></script>
@@ -236,7 +237,7 @@
<span id="app-name"></span>
</div>
<div class=dialog-sidebar-contents>
- <tree id="directory-tree" tabindex="8"></tree>
+ <list id="volume-list" tabindex="8"></list>
</div>
<div class=dialog-sidebar-footer>
<div id="butter-bar-container">
@@ -252,7 +253,7 @@
</div>
</div>
</div>
- <div class=sidebar-splitter></div>
+ <div class="splitter" id="sidebar-splitter"></div>
<div class=dialog-main>
<div class=dialog-header>
<input id="search-box" type="search" tabindex="9"
@@ -266,27 +267,32 @@
<button id="close-button" visibleif="full-page" tabindex="-1">
</button>
</div>
-
- </div>
- <div class="drive-welcome header"></div>
- <div class="volume-warning" id="volume-space-warning" hidden></div>
- <div class="volume-warning" id="drive-auth-failed-warning" hidden>
- <div class="drive-icon"></div>
- <div class="drive-text" id="drive-auth-failed-warning-text"></div>
</div>
<div class=dialog-body>
- <div class=filelist-panel>
- <div id="list-container">
- <div class=detail-table id="detail-table" tabindex=1 autofocus>
+ <div class="main-panel">
+ <div class="dialog-middlebar-contents">
+ <tree id="directory-tree" tabindex="8"></tree>
+ </div>
+ <div class="splitter" id="middlebar-splitter"></div>
+ <div class=filelist-panel>
+ <div class="drive-welcome header"></div>
+ <div class="volume-warning" id="volume-space-warning" hidden></div>
+ <div class="volume-warning" id="drive-auth-failed-warning" hidden>
+ <div class="drive-icon"></div>
+ <div class="drive-text" id="drive-auth-failed-warning-text"></div>
</div>
- <grid class=thumbnail-grid tabindex=1></grid>
- <div id="spinner-container">
- <div id="spinner-with-text"></div>
+ <div id="list-container">
+ <div class=detail-table id="detail-table" tabindex=1 autofocus>
+ </div>
+ <grid class=thumbnail-grid tabindex=1></grid>
+ <div id="spinner-container">
+ <div id="spinner-with-text"></div>
+ </div>
+ <div class="drive-welcome page"></div>
+ <div id="no-search-results"></div>
</div>
- <div class="drive-welcome page"></div>
- <div id="no-search-results"></div>
+ <div class=downloads-warning hidden></div>
</div>
- <div class=downloads-warning hidden></div>
</div>
<div class="preview-panel" visibility="hidden">
<div>