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

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
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 = function(error) {
SeRya 2012/04/23 14:23:43 var onError = opt_onError || function(error) { ...
Oleg Eterevsky 2012/04/23 14:33:04 Good idea. Done.
634 path, {create: false}, 640 if (opt_OnError) {
635 onDirectoryResolved, 641 opt_OnError();
636 function(error) { 642 } else {
637 // TODO(serya): We should show an alert. 643 // TODO(serya): We should show an alert.
638 console.error('Error changing directory to: ' + path + ', ' + error); 644 console.error('Error changing directory to: ' + path + ', ' + error);
639 }); 645 }
646 };
647
648 this.root_.getDirectory(path, {create: false}, onDirectoryResolved, onError);
640 }; 649 };
641 650
642 /** 651 /**
643 * Change the current directory to the directory represented by a 652 * Change the current directory to the directory represented by a
644 * DirectoryEntry. 653 * DirectoryEntry.
645 * 654 *
646 * Dispatches the 'directory-changed' event when the directory is successfully 655 * Dispatches the 'directory-changed' event when the directory is successfully
647 * changed. 656 * changed.
648 * 657 *
649 * @private 658 * @private
(...skipping 11 matching lines...) Expand all
661 function onRescanComplete() { 670 function onRescanComplete() {
662 action(); 671 action();
663 // For tests that open the dialog to empty directories, everything 672 // For tests that open the dialog to empty directories, everything
664 // is loaded at this point. 673 // is loaded at this point.
665 chrome.test.sendMessage('directory-change-complete'); 674 chrome.test.sendMessage('directory-change-complete');
666 } 675 }
667 this.updateReadonlyStatus_(); 676 this.updateReadonlyStatus_();
668 this.updateVolumeMetadata_(); 677 this.updateVolumeMetadata_();
669 this.updateRootsListSelection_(); 678 this.updateRootsListSelection_();
670 this.scan_(onRescanComplete); 679 this.scan_(onRescanComplete);
680 this.currentDirByRoot_[this.getCurrentRootPath()] = dirEntry.fullPath;
SeRya 2012/04/23 14:23:43 Is it OK this is a page-wide state, not applicatio
Oleg Eterevsky 2012/04/23 14:33:04 You mean that we lose the state when we close the
671 681
672 var e = new cr.Event('directory-changed'); 682 var e = new cr.Event('directory-changed');
673 e.previousDirEntry = previous; 683 e.previousDirEntry = previous;
674 e.newDirEntry = dirEntry; 684 e.newDirEntry = dirEntry;
675 e.initial = initial; 685 e.initial = initial;
676 this.dispatchEvent(e); 686 this.dispatchEvent(e);
677 }; 687 };
678 688
679 /** 689 /**
680 * Change the state of the model to reflect the specified path (either a 690 * 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 } 974 }
965 }; 975 };
966 976
967 /** 977 /**
968 * @param {function} opt_callback Called when all roots are resolved. 978 * @param {function} opt_callback Called when all roots are resolved.
969 * @param {boolean} opt_resolveGData If true GData should be resolved for real, 979 * @param {boolean} opt_resolveGData If true GData should be resolved for real,
970 * If false a stub entry should be created. 980 * If false a stub entry should be created.
971 */ 981 */
972 DirectoryModel.prototype.updateRoots = function(opt_callback, 982 DirectoryModel.prototype.updateRoots = function(opt_callback,
973 opt_resolveGData) { 983 opt_resolveGData) {
974 console.log('resolving roots');
975 var self = this; 984 var self = this;
976 this.resolveRoots_(function(rootEntries) { 985 this.resolveRoots_(function(rootEntries) {
977 console.log('resolved roots:', rootEntries);
978 var dm = self.rootsList_; 986 var dm = self.rootsList_;
979 var args = [0, dm.length].concat(rootEntries); 987 var args = [0, dm.length].concat(rootEntries);
980 dm.splice.apply(dm, args); 988 dm.splice.apply(dm, args);
981 989
982 self.updateRootsListSelection_(); 990 self.updateRootsListSelection_();
983 991
984 if (opt_callback) 992 if (opt_callback)
985 opt_callback(); 993 opt_callback();
986 }, opt_resolveGData); 994 }, opt_resolveGData);
987 }; 995 };
988 996
989 /** 997 /**
990 * @private 998 * Change root. In case user already opened the root, the last selected
991 * @param {Event} event 999 * directory will be selected.
1000 * @param {string} rootPath The path of the root.
992 */ 1001 */
993 DirectoryModel.prototype.onRootsSelectionChanged_ = function(event) { 1002 DirectoryModel.prototype.changeRoot = function(rootPath) {
994 var root = this.rootsList.item(this.rootsListSelection.selectedIndex); 1003 if (this.currentDirByRoot_[rootPath]) {
995 if (root && this.rootPath != root.fullPath) 1004 var onFail = function() {
996 this.changeDirectory(root.fullPath); 1005 this.changeDirectory(rootPath);
SeRya 2012/04/23 14:23:43 The same logic makes sense in setupPath. If user h
Oleg Eterevsky 2012/04/23 14:33:04 I'm not sure about it. I'd say if user just change
1006 };
1007 this.changeDirectory(this.currentDirByRoot_[rootPath], onFail);
1008 } else {
1009 this.changeDirectory(rootPath);
1010 }
997 }; 1011 };
998 1012
999 /** 1013 /**
1000 * Find roots list item by root path. 1014 * Find roots list item by root path.
1001 * 1015 *
1002 * @param {string} path Root path. 1016 * @param {string} path Root path.
1003 * @return {number} Index of the item. 1017 * @return {number} Index of the item.
1004 * @private 1018 * @private
1005 */ 1019 */
1006 DirectoryModel.prototype.findRootsListItem_ = function(path) { 1020 DirectoryModel.prototype.findRootsListItem_ = function(path) {
1007 var roots = this.rootsList_; 1021 var roots = this.rootsList_;
1008 for (var index = 0; index < roots.length; index++) { 1022 for (var index = 0; index < roots.length; index++) {
1009 if (roots.item(index).fullPath == path) 1023 if (roots.item(index).fullPath == path)
1010 return index; 1024 return index;
1011 } 1025 }
1012 return -1; 1026 return -1;
1013 }; 1027 };
1014 1028
1015 /** 1029 /**
1016 * @private 1030 * @private
1017 */ 1031 */
1018 DirectoryModel.prototype.updateRootsListSelection_ = function() { 1032 DirectoryModel.prototype.updateRootsListSelection_ = function() {
1019 this.rootsListSelection.selectedIndex = 1033 this.rootsListSelection_.selectedIndex =
1020 this.findRootsListItem_(this.rootPath); 1034 this.findRootsListItem_(this.rootPath);
1021 }; 1035 };
1022 1036
1023 /** 1037 /**
1024 * @private 1038 * @private
1025 */ 1039 */
1026 DirectoryModel.prototype.updateReadonlyStatus_ = function() { 1040 DirectoryModel.prototype.updateReadonlyStatus_ = function() {
1027 switch (this.rootType) { 1041 switch (this.rootType) {
1028 case DirectoryModel.RootType.REMOVABLE: 1042 case DirectoryModel.RootType.REMOVABLE:
1029 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly; 1043 this.readonly_ = !!this.currentVolumeMetadata_.isReadOnly;
(...skipping 10 matching lines...) Expand all
1040 default: 1054 default:
1041 this.readonly_ = true; 1055 this.readonly_ = true;
1042 break; 1056 break;
1043 } 1057 }
1044 }; 1058 };
1045 1059
1046 /** 1060 /**
1047 * @private 1061 * @private
1048 */ 1062 */
1049 DirectoryModel.prototype.updateVolumeMetadata_ = function() { 1063 DirectoryModel.prototype.updateVolumeMetadata_ = function() {
1050 var rootPath = this.rootPath; 1064 var rootPath = this.getCurrentRootPath();
1051 if (this.currentVolumeMetadata_.rootPath != rootPath) { 1065 if (this.currentVolumeMetadata_.rootPath != rootPath) {
1052 var metadata = this.currentVolumeMetadata_ = {rootPath: rootPath}; 1066 var metadata = this.currentVolumeMetadata_ = {rootPath: rootPath};
1053 if (DirectoryModel.getRootType(rootPath) == 1067 if (DirectoryModel.getRootType(rootPath) ==
1054 DirectoryModel.RootType.REMOVABLE) { 1068 DirectoryModel.RootType.REMOVABLE) {
1055 var self = this; 1069 var self = this;
1056 this.root_.getDirectory(rootPath, {}, function(entry) { 1070 this.root_.getDirectory(rootPath, {}, function(entry) {
1057 chrome.fileBrowserPrivate.getVolumeMetadata(entry.toURL(), 1071 chrome.fileBrowserPrivate.getVolumeMetadata(entry.toURL(),
1058 function(systemMetadata) { 1072 function(systemMetadata) {
1059 if (systemMetadata) { 1073 if (systemMetadata) {
1060 metadata.isReadOnly = systemMetadata.isReadOnly; 1074 metadata.isReadOnly = systemMetadata.isReadOnly;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1121 * @param {string} path 1135 * @param {string} path
1122 * @return {string} 1136 * @return {string}
1123 */ 1137 */
1124 DirectoryModel.getRootName = function(path) { 1138 DirectoryModel.getRootName = function(path) {
1125 var root = DirectoryModel.getRootPath(path); 1139 var root = DirectoryModel.getRootPath(path);
1126 var index = root.lastIndexOf('/'); 1140 var index = root.lastIndexOf('/');
1127 return index == -1 ? root : root.substring(index + 1); 1141 return index == -1 ? root : root.substring(index + 1);
1128 }; 1142 };
1129 1143
1130 /** 1144 /**
1131 * @param {string} path 1145 * @param {string} path Path.
1132 * @return {string} 1146 * @return {string} A root type.
1133 */ 1147 */
1134 DirectoryModel.getRootType = function(path) { 1148 DirectoryModel.getRootType = function(path) {
1135 function isTop(dir) { 1149 function isTop(dir) {
1136 return path.substr(1, dir.length) == dir; 1150 return path.substr(1, dir.length) == dir;
1137 } 1151 }
1138 1152
1139 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY)) 1153 if (isTop(DirectoryModel.DOWNLOADS_DIRECTORY))
1140 return DirectoryModel.RootType.DOWNLOADS; 1154 return DirectoryModel.RootType.DOWNLOADS;
1141 else if (isTop(DirectoryModel.GDATA_DIRECTORY)) 1155 if (isTop(DirectoryModel.GDATA_DIRECTORY))
1142 return DirectoryModel.RootType.GDATA; 1156 return DirectoryModel.RootType.GDATA;
1143 else if (isTop(DirectoryModel.ARCHIVE_DIRECTORY)) 1157 if (isTop(DirectoryModel.ARCHIVE_DIRECTORY))
1144 return DirectoryModel.RootType.ARCHIVE; 1158 return DirectoryModel.RootType.ARCHIVE;
1145 else if (isTop(DirectoryModel.REMOVABLE_DIRECTORY)) 1159 if (isTop(DirectoryModel.REMOVABLE_DIRECTORY))
1146 return DirectoryModel.RootType.REMOVABLE; 1160 return DirectoryModel.RootType.REMOVABLE;
1147 return ''; 1161 return '';
1148 }; 1162 };
1149 1163
1150 /** 1164 /**
1151 * @param {string} path 1165 * @param {string} path
1152 * @return {boolean} 1166 * @return {boolean}
1153 */ 1167 */
1154 DirectoryModel.isRootPath = function(path) { 1168 DirectoryModel.isRootPath = function(path) {
1155 if (path[path.length - 1] == '/') 1169 if (path[path.length - 1] == '/')
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 /** 1261 /**
1248 * @private 1262 * @private
1249 */ 1263 */
1250 DirectoryModel.Scanner.prototype.recordMetrics_ = function() { 1264 DirectoryModel.Scanner.prototype.recordMetrics_ = function() {
1251 metrics.recordInterval('DirectoryScan'); 1265 metrics.recordInterval('DirectoryScan');
1252 if (this.dir_.fullPath == 1266 if (this.dir_.fullPath ==
1253 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) { 1267 '/' + DirectoryModel.DOWNLOADS_DIRECTORY) {
1254 metrics.recordMediumCount('DownloadsCount', this.list_.length); 1268 metrics.recordMediumCount('DownloadsCount', this.list_.length);
1255 } 1269 }
1256 }; 1270 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698