Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: chrome/browser/resources/file_manager/js/directory_model.js

Issue 10146008: Remember current directory for each volume. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/resources/file_manager/js/file_manager.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // If directory files changes too often, don't rescan directory more than once 5 // If directory files changes too often, don't rescan directory more than once
6 // per specified interval 6 // per specified interval
7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000; 7 var SIMULTANEOUS_RESCAN_INTERVAL = 1000;
8 // Used for operations that require almost instant rescan. 8 // Used for operations that require almost instant rescan.
9 var SHORT_RESCAN_INTERVAL = 100; 9 var SHORT_RESCAN_INTERVAL = 100;
10 10
(...skipping 23 matching lines...) Expand all
34 this.scanFailures_ = 0; 34 this.scanFailures_ = 0;
35 35
36 // DirectoryEntry representing the current directory of the dialog. 36 // DirectoryEntry representing the current directory of the dialog.
37 this.currentDirEntry_ = root; 37 this.currentDirEntry_ = root;
38 38
39 this.fileList_.prepareSort = this.prepareSort_.bind(this); 39 this.fileList_.prepareSort = this.prepareSort_.bind(this);
40 this.autoSelectIndex_ = 0; 40 this.autoSelectIndex_ = 0;
41 41
42 this.rootsList_ = new cr.ui.ArrayDataModel([]); 42 this.rootsList_ = new cr.ui.ArrayDataModel([]);
43 this.rootsListSelection_ = new cr.ui.ListSingleSelectionModel(); 43 this.rootsListSelection_ = new cr.ui.ListSingleSelectionModel();
44 this.rootsListSelection_.addEventListener( 44
45 'change', this.onRootsSelectionChanged_.bind(this)); 45 /**
46 * A map root.fullPath -> currentDirectory.fullPath.
47 * @private
48 * @type {Object.<string, string>}
49 */
50 this.currentDirByRoot_ = {};
46 51
47 // The map 'name' -> callback. Callbacks are function(entry) -> boolean. 52 // The map 'name' -> callback. Callbacks are function(entry) -> boolean.
48 this.filters_ = {}; 53 this.filters_ = {};
49 this.filterHidden = true; 54 this.filterHidden = true;
50 55
51 // Readonly status. 56 // Readonly status.
52 this.readonly_ = false; 57 this.readonly_ = false;
53 this.currentVolumeMetadata_ = {rootPath: '/'}; 58 this.currentVolumeMetadata_ = {rootPath: '/'};
54 this.offline_ = false; 59 this.offline_ = false;
55 } 60 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 }, 104 },
100 105
101 /** 106 /**
102 * Selection in the fileList. 107 * Selection in the fileList.
103 * @type {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} 108 * @type {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel}
104 */ 109 */
105 get fileListSelection() { 110 get fileListSelection() {
106 return this.fileListSelection_; 111 return this.fileListSelection_;
107 }, 112 },
108 113
109 /**
110 * Top level Directories from user perspective.
111 * @type {cr.ui.ArrayDataModel}
112 */
113 get rootsList() {
114 return this.rootsList_;
115 },
116
117 /**
118 * Selection in the rootsList.
119 * @type {cr.ui.ListSingleSelectionModel}
120 */
121 get rootsListSelection() {
122 return this.rootsListSelection_;
123 },
124
125 /**
126 * Root path for the current directory (parent directory is not navigatable
127 * for the user).
128 * @type {string}
129 */
130 get rootPath() {
131 return DirectoryModel.getRootPath(this.currentEntry.fullPath);
132 },
133
134 get rootType() { 114 get rootType() {
135 return DirectoryModel.getRootType(this.currentEntry.fullPath); 115 return DirectoryModel.getRootType(this.currentEntry.fullPath);
136 }, 116 },
137 117
138 get rootName() { 118 get rootName() {
139 return DirectoryModel.getRootName(this.currentEntry.fullPath); 119 return DirectoryModel.getRootName(this.currentEntry.fullPath);
140 }, 120 },
141 121
142 get rootEntry() {
143 return this.rootsList.item(this.rootsListSelection.selectedIndex);
144 },
145
146 /** 122 /**
147 * True if current directory is read only. 123 * True if current directory is read only.
148 * @type {boolean} 124 * @type {boolean}
149 */ 125 */
150 get readonly() { 126 get readonly() {
151 return this.readonly_; 127 return this.readonly_;
152 }, 128 },
153 129
154 get offline() { 130 get offline() {
155 return this.offline_; 131 return this.offline_;
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
244 for (var i = 0; i < this.fileList_.length; i++) { 220 for (var i = 0; i < this.fileList_.length; i++) {
245 if (this.fileList_.item(i).name == value) { 221 if (this.fileList_.item(i).name == value) {
246 this.fileListSelection_.leadIndex = i; 222 this.fileListSelection_.leadIndex = i;
247 return; 223 return;
248 } 224 }
249 } 225 }
250 } 226 }
251 }; 227 };
252 228
253 /** 229 /**
230 * @return {cr.ui.ArrayDataModel} The list of roots.
231 */
232 DirectoryModel.prototype.getRootsList = function() {
233 return this.rootsList_;
234 };
235
236 /**
237 * @return {Entry} Directory entry of the current root.
238 */
239 DirectoryModel.prototype.getCurrentRootDirEntry = function() {
240 return this.rootsList_.item(this.rootsListSelection_.selectedIndex);
241 };
242
243 /**
244 * @return {string} Root path for the current directory (parent directory is
245 * not navigatable for the user).
246 */
247 DirectoryModel.prototype.getCurrentRootPath = function() {
248 return DirectoryModel.getRootPath(this.currentDirEntry_.fullPath);
249 };
250
251 /**
252 * @return {cr.ui.ListSingleSelectionModel} Root list selection model.
253 */
254 DirectoryModel.prototype.getRootsListSelectionModel = function() {
255 return this.rootsListSelection_;
256 };
257
258 /**
254 * Add a filter for directory contents. 259 * Add a filter for directory contents.
255 * @param {string} name An identifier of the filter (used when removing it). 260 * @param {string} name An identifier of the filter (used when removing it).
256 * @param {function(Entry):boolean} filter Hide file if false. 261 * @param {function(Entry):boolean} filter Hide file if false.
257 */ 262 */
258 DirectoryModel.prototype.addFilter = function(name, filter) { 263 DirectoryModel.prototype.addFilter = function(name, filter) {
259 this.filters_[name] = filter; 264 this.filters_[name] = filter;
260 this.rescanSoon(); 265 this.rescanSoon();
261 }; 266 };
262 267
263 /** 268 /**
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 return; 329 return;
325 } 330 }
326 331
327 this.runningScan_ = this.createScanner_(fileList, successCallback); 332 this.runningScan_ = this.createScanner_(fileList, successCallback);
328 this.runningScan_.run(); 333 this.runningScan_.run();
329 }; 334 };
330 335
331 /** 336 /**
332 * @private 337 * @private
333 * @param {Array.<Entry>|cr.ui.ArrayDataModel} list 338 * @param {Array.<Entry>|cr.ui.ArrayDataModel} list
334 * @param {function} successCallback 339 * @param {function} successCallback Callback on success.
335 * @return {DirectoryModel.Scanner} 340 * @return {DirectoryModel.Scanner} New Scanner instance.
336 */ 341 */
337 DirectoryModel.prototype.createScanner_ = function(list, successCallback) { 342 DirectoryModel.prototype.createScanner_ = function(list, successCallback) {
338 var self = this; 343 var self = this;
339 function onSuccess() { 344 function onSuccess() {
340 self.scanFailures_ = 0; 345 self.scanFailures_ = 0;
341 successCallback(); 346 successCallback();
342 if (self.pendingScan_) { 347 if (self.pendingScan_) {
343 self.runningScan_ = self.pendingScan_; 348 self.runningScan_ = self.pendingScan_;
344 self.pendingScan_ = null; 349 self.pendingScan_ = null;
345 self.runningScan_.run(); 350 self.runningScan_.run();
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 } 604 }
600 605
601 this.currentEntry.getDirectory(name, {create: true, exclusive: true}, 606 this.currentEntry.getDirectory(name, {create: true, exclusive: true},
602 onSuccess, errorCallback); 607 onSuccess, errorCallback);
603 }; 608 };
604 609
605 /** 610 /**
606 * Changes directory. Causes 'directory-change' event. 611 * Changes directory. Causes 'directory-change' event.
607 * 612 *
608 * @param {string} path New current directory path. 613 * @param {string} path New current directory path.
614 * @param {function} opt_OnError Called if failed.
609 */ 615 */
610 DirectoryModel.prototype.changeDirectory = function(path) { 616 DirectoryModel.prototype.changeDirectory = function(path, opt_OnError) {
611 var onDirectoryResolved = function(dirEntry) { 617 var onDirectoryResolved = function(dirEntry) {
612 var autoSelect = this.selectIndex.bind(this, this.autoSelectIndex_); 618 var autoSelect = this.selectIndex.bind(this, this.autoSelectIndex_);
613 this.changeDirectoryEntry_(dirEntry, autoSelect, false); 619 this.changeDirectoryEntry_(dirEntry, autoSelect, false);
614 }.bind(this); 620 }.bind(this);
615 621
616 if (this.unmountedGDataEntry_ && 622 if (this.unmountedGDataEntry_ &&
617 DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) { 623 DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
618 // TODO(kaznacheeev): Currently if path points to some GData subdirectory 624 // TODO(kaznacheeev): Currently if path points to some GData subdirectory
619 // and GData is not mounted we will change to the fake GData root and 625 // and GData is not mounted we will change to the fake GData root and
620 // ignore the rest of the path. Consider remembering the path and 626 // ignore the rest of the path. Consider remembering the path and
621 // changing to it once GDdata is mounted. This is only relevant for cases 627 // changing to it once GDdata is mounted. This is only relevant for cases
622 // when we open the File Manager with an URL pointing to GData (e.g. via 628 // when we open the File Manager with an URL pointing to GData (e.g. via
623 // a bookmark). 629 // a bookmark).
624 onDirectoryResolved(this.unmountedGDataEntry_); 630 onDirectoryResolved(this.unmountedGDataEntry_);
625 return; 631 return;
626 } 632 }
627 633
628 if (path == '/') { 634 if (path == '/') {
629 onDirectoryResolved(this.root_); 635 onDirectoryResolved(this.root_);
630 return; 636 return;
631 } 637 }
632 638
633 this.root_.getDirectory( 639 var onError = opt_OnError || function(error) {
634 path, {create: false}, 640 // TODO(serya): We should show an alert.
635 onDirectoryResolved, 641 console.error('Error changing directory to: ' + path + ', ' + error);
636 function(error) { 642 };
637 // TODO(serya): We should show an alert. 643
638 console.error('Error changing directory to: ' + path + ', ' + error); 644 this.root_.getDirectory(path, {create: false}, onDirectoryResolved, onError);
639 });
640 }; 645 };
641 646
642 /** 647 /**
643 * Change the current directory to the directory represented by a 648 * Change the current directory to the directory represented by a
644 * DirectoryEntry. 649 * DirectoryEntry.
645 * 650 *
646 * Dispatches the 'directory-changed' event when the directory is successfully 651 * Dispatches the 'directory-changed' event when the directory is successfully
647 * changed. 652 * changed.
648 * 653 *
649 * @private 654 * @private
(...skipping 11 matching lines...) Expand all
661 function onRescanComplete() { 666 function onRescanComplete() {
662 action(); 667 action();
663 // For tests that open the dialog to empty directories, everything 668 // For tests that open the dialog to empty directories, everything
664 // is loaded at this point. 669 // is loaded at this point.
665 chrome.test.sendMessage('directory-change-complete'); 670 chrome.test.sendMessage('directory-change-complete');
666 } 671 }
667 this.updateReadonlyStatus_(); 672 this.updateReadonlyStatus_();
668 this.updateVolumeMetadata_(); 673 this.updateVolumeMetadata_();
669 this.updateRootsListSelection_(); 674 this.updateRootsListSelection_();
670 this.scan_(onRescanComplete); 675 this.scan_(onRescanComplete);
676 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath;
671 677
672 var e = new cr.Event('directory-changed'); 678 var e = new cr.Event('directory-changed');
673 e.previousDirEntry = previous; 679 e.previousDirEntry = previous;
674 e.newDirEntry = dirEntry; 680 e.newDirEntry = dirEntry;
675 e.initial = initial; 681 e.initial = initial;
676 this.dispatchEvent(e); 682 this.dispatchEvent(e);
677 }; 683 };
678 684
679 /** 685 /**
680 * Change the state of the model to reflect the specified path (either a 686 * Change the state of the model to reflect the specified path (either a
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after
964 } 970 }
965 }; 971 };
966 972
967 /** 973 /**
968 * @param {function} opt_callback Called when all roots are resolved. 974 * @param {function} opt_callback Called when all roots are resolved.
969 * @param {boolean} opt_resolveGData If true GData should be resolved for real, 975 * @param {boolean} opt_resolveGData If true GData should be resolved for real,
970 * If false a stub entry should be created. 976 * If false a stub entry should be created.
971 */ 977 */
972 DirectoryModel.prototype.updateRoots = function(opt_callback, 978 DirectoryModel.prototype.updateRoots = function(opt_callback,
973 opt_resolveGData) { 979 opt_resolveGData) {
974 console.log('resolving roots');
975 var self = this; 980 var self = this;
976 this.resolveRoots_(function(rootEntries) { 981 this.resolveRoots_(function(rootEntries) {
977 console.log('resolved roots:', rootEntries);
978 var dm = self.rootsList_; 982 var dm = self.rootsList_;
979 var args = [0, dm.length].concat(rootEntries); 983 var args = [0, dm.length].concat(rootEntries);
980 dm.splice.apply(dm, args); 984 dm.splice.apply(dm, args);
981 985
982 self.updateRootsListSelection_(); 986 self.updateRootsListSelection_();
983 987
984 if (opt_callback) 988 if (opt_callback)
985 opt_callback(); 989 opt_callback();
986 }, opt_resolveGData); 990 }, opt_resolveGData);
987 }; 991 };
988 992
989 /** 993 /**
990 * @private 994 * Change root. In case user already opened the root, the last selected
991 * @param {Event} event 995 * directory will be selected.
996 * @param {string} rootPath The path of the root.
992 */ 997 */
993 DirectoryModel.prototype.onRootsSelectionChanged_ = function(event) { 998 DirectoryModel.prototype.changeRoot = function(rootPath) {
994 var root = this.rootsList.item(this.rootsListSelection.selectedIndex); 999 if (this.currentDirByRoot_[rootPath]) {
995 if (root && this.rootPath != root.fullPath) 1000 var onFail = function() {
996 this.changeDirectory(root.fullPath); 1001 this.changeDirectory(rootPath);
1002 };
1003 this.changeDirectory(this.currentDirByRoot_[rootPath], onFail);
1004 } else {
1005 this.changeDirectory(rootPath);
1006 }
997 }; 1007 };
998 1008
999 /** 1009 /**
1000 * Find roots list item by root path. 1010 * Find roots list item by root path.
1001 * 1011 *
1002 * @param {string} path Root path. 1012 * @param {string} path Root path.
1003 * @return {number} Index of the item. 1013 * @return {number} Index of the item.
1004 * @private 1014 * @private
1005 */ 1015 */
1006 DirectoryModel.prototype.findRootsListItem_ = function(path) { 1016 DirectoryModel.prototype.findRootsListItem_ = function(path) {
1007 var roots = this.rootsList_; 1017 var roots = this.rootsList_;
1008 for (var index = 0; index < roots.length; index++) { 1018 for (var index = 0; index < roots.length; index++) {
1009 if (roots.item(index).fullPath == path) 1019 if (roots.item(index).fullPath == path)
1010 return index; 1020 return index;
1011 } 1021 }
1012 return -1; 1022 return -1;
1013 }; 1023 };
1014 1024
1015 /** 1025 /**
1016 * @private 1026 * @private
1017 */ 1027 */
1018 DirectoryModel.prototype.updateRootsListSelection_ = function() { 1028 DirectoryModel.prototype.updateRootsListSelection_ = function() {
1019 this.rootsListSelection.selectedIndex = 1029 this.rootsListSelection_.selectedIndex =
1020 this.findRootsListItem_(this.rootPath); 1030 this.findRootsListItem_(this.rootPath);
1021 }; 1031 };
1022 1032
1023 /** 1033 /**
1024 * @private 1034 * @private
1025 */ 1035 */
1026 DirectoryModel.prototype.updateReadonlyStatus_ = function() { 1036 DirectoryModel.prototype.updateReadonlyStatus_ = function() {
1027 switch (this.rootType) { 1037 switch (this.rootType) {
1028 case DirectoryModel.RootType.REMOVABLE: 1038 case DirectoryModel.RootType.REMOVABLE:
1029 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly; 1039 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly;
(...skipping 10 matching lines...) Expand all
1040 default: 1050 default:
1041 this.readonly_ = true; 1051 this.readonly_ = true;
1042 break; 1052 break;
1043 } 1053 }
1044 }; 1054 };
1045 1055
1046 /** 1056 /**
1047 * @private 1057 * @private
1048 */ 1058 */
1049 DirectoryModel.prototype.updateVolumeMetadata_ = function() { 1059 DirectoryModel.prototype.updateVolumeMetadata_ = function() {
1050 var rootPath = this.rootPath; 1060 var rootPath = this.getCurrentRootPath();
1051 if (this.currentVolumeMetadata_.rootPath != rootPath) { 1061 if (this.currentVolumeMetadata_.rootPath != rootPath) {
1052 var metadata = this.currentVolumeMetadata_ = {rootPath: rootPath}; 1062 var metadata = this.currentVolumeMetadata_ = {rootPath: rootPath};
1053 if (DirectoryModel.getRootType(rootPath) == 1063 if (DirectoryModel.getRootType(rootPath) ==
1054 DirectoryModel.RootType.REMOVABLE) { 1064 DirectoryModel.RootType.REMOVABLE) {
1055 var self = this; 1065 var self = this;
1056 this.root_.getDirectory(rootPath, {}, function(entry) { 1066 this.root_.getDirectory(rootPath, {}, function(entry) {
1057 chrome.fileBrowserPrivate.getVolumeMetadata(entry.toURL(), 1067 chrome.fileBrowserPrivate.getVolumeMetadata(entry.toURL(),
1058 function(systemMetadata) { 1068 function(systemMetadata) {
1059 if (systemMetadata) { 1069 if (systemMetadata) {
1060 metadata.isReadOnly = systemMetadata.isReadOnly; 1070 metadata.isReadOnly = systemMetadata.isReadOnly;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1121 * @param {string} path 1131 * @param {string} path
1122 * @return {string} 1132 * @return {string}
1123 */ 1133 */
1124 DirectoryModel.getRootName = function(path) { 1134 DirectoryModel.getRootName = function(path) {
1125 var root = DirectoryModel.getRootPath(path); 1135 var root = DirectoryModel.getRootPath(path);
1126 var index = root.lastIndexOf('/'); 1136 var index = root.lastIndexOf('/');
1127 return index == -1 ? root : root.substring(index + 1); 1137 return index == -1 ? root : root.substring(index + 1);
1128 }; 1138 };
1129 1139
1130 /** 1140 /**
1131 * @param {string} path 1141 * @param {string} path Path.
1132 * @return {string} 1142 * @return {string} A root type.
1133 */ 1143 */
1134 DirectoryModel.getRootType = function(path) { 1144 DirectoryModel.getRootType = function(path) {
1135 function isTop(dir) { 1145 function isTop(dir) {
1136 return path.substr(1, dir.length) == dir; 1146 return path.substr(1, dir.length) == dir;
1137 } 1147 }
1138 1148
1139 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY)) 1149 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY))
1140 return DirectoryModel.RootType.DOWNLOADS; 1150 return DirectoryModel.RootType.DOWNLOADS;
1141 else if (isTop(DirectoryModel.GDATA_DIRECTORY)) 1151 if (isTop(DirectoryModel.GDATA_DIRECTORY))
1142 return DirectoryModel.RootType.GDATA; 1152 return DirectoryModel.RootType.GDATA;
1143 else if (isTop(DirectoryModel.ARCHIVE_DIRECTORY)) 1153 if (isTop(DirectoryModel.ARCHIVE_DIRECTORY))
1144 return DirectoryModel.RootType.ARCHIVE; 1154 return DirectoryModel.RootType.ARCHIVE;
1145 else if (isTop(DirectoryModel.REMOVABLE_DIRECTORY)) 1155 if (isTop(DirectoryModel.REMOVABLE_DIRECTORY))
1146 return DirectoryModel.RootType.REMOVABLE; 1156 return DirectoryModel.RootType.REMOVABLE;
1147 return ''; 1157 return '';
1148 }; 1158 };
1149 1159
1150 /** 1160 /**
1151 * @param {string} path 1161 * @param {string} path
1152 * @return {boolean} 1162 * @return {boolean}
1153 */ 1163 */
1154 DirectoryModel.isRootPath = function(path) { 1164 DirectoryModel.isRootPath = function(path) {
1155 if (path[path.length - 1] == '/') 1165 if (path[path.length - 1] == '/')
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 /** 1257 /**
1248 * @private 1258 * @private
1249 */ 1259 */
1250 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { 1260 DirectoryModel.Scanner.prototype.recordMetrics_ = function() {
1251 metrics.recordInterval('DirectoryScan'); 1261 metrics.recordInterval('DirectoryScan');
1252 if (this.dir_.fullPath == 1262 if (this.dir_.fullPath ==
1253 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { 1263 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) {
1254 metrics.recordMediumCount('DownloadsCount', this.list_.length); 1264 metrics.recordMediumCount('DownloadsCount', this.list_.length);
1255 } 1265 }
1256 }; 1266 };
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/file_manager/js/file_manager.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698