| OLD | NEW |
| 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 /** | 5 /** |
| 6 * The current selection object. | 6 * The current selection object. |
| 7 * | 7 * |
| 8 * @param {!FileManager} fileManager FileManager instance. | 8 * @param {FileManager} fileManager FileManager instance. |
| 9 * @param {!Array.<number>} indexes Selected indexes. | 9 * @param {Array.<number>} indexes Selected indexes. |
| 10 * @constructor | 10 * @constructor |
| 11 * @struct | |
| 12 */ | 11 */ |
| 13 function FileSelection(fileManager, indexes) { | 12 function FileSelection(fileManager, indexes) { |
| 14 /** | |
| 15 * @type {!FileManager} | |
| 16 * @private | |
| 17 * @const | |
| 18 */ | |
| 19 this.fileManager_ = fileManager; | 13 this.fileManager_ = fileManager; |
| 20 | |
| 21 /** | |
| 22 * @type {number} | |
| 23 * @private | |
| 24 */ | |
| 25 this.computeBytesSequence_ = 0; | 14 this.computeBytesSequence_ = 0; |
| 26 | |
| 27 /** | |
| 28 * @type {!Array.<number>} | |
| 29 * @const | |
| 30 */ | |
| 31 this.indexes = indexes; | 15 this.indexes = indexes; |
| 32 | |
| 33 /** | |
| 34 * @type {!Array.<!Entry>} | |
| 35 * @const | |
| 36 */ | |
| 37 this.entries = []; | 16 this.entries = []; |
| 38 | |
| 39 /** | |
| 40 * @type {number} | |
| 41 */ | |
| 42 this.totalCount = 0; | 17 this.totalCount = 0; |
| 43 | |
| 44 /** | |
| 45 * @type {number} | |
| 46 */ | |
| 47 this.fileCount = 0; | 18 this.fileCount = 0; |
| 48 | |
| 49 /** | |
| 50 * @type {number} | |
| 51 */ | |
| 52 this.directoryCount = 0; | 19 this.directoryCount = 0; |
| 53 | |
| 54 /** | |
| 55 * @type {number} | |
| 56 */ | |
| 57 this.bytes = 0; | 20 this.bytes = 0; |
| 58 | |
| 59 /** | |
| 60 * @type {boolean} | |
| 61 */ | |
| 62 this.showBytes = false; | 21 this.showBytes = false; |
| 63 | 22 this.allDriveFilesPresent = false, |
| 64 /** | |
| 65 * @type {boolean} | |
| 66 */ | |
| 67 this.allDriveFilesPresent = false; | |
| 68 | |
| 69 /** | |
| 70 * @type {?string} | |
| 71 */ | |
| 72 this.iconType = null; | 23 this.iconType = null; |
| 73 | |
| 74 /** | |
| 75 * @type {boolean} | |
| 76 */ | |
| 77 this.bytesKnown = false; | 24 this.bytesKnown = false; |
| 78 | |
| 79 /** | |
| 80 * @type {boolean} | |
| 81 * @private | |
| 82 */ | |
| 83 this.mustBeHidden_ = false; | 25 this.mustBeHidden_ = false; |
| 84 | |
| 85 /** | |
| 86 * @type {Array.<string>} | |
| 87 */ | |
| 88 this.mimeTypes = null; | 26 this.mimeTypes = null; |
| 89 | 27 |
| 90 /** | |
| 91 * @type {!FileTasks} | |
| 92 */ | |
| 93 this.tasks = new FileTasks(this.fileManager_); | |
| 94 | |
| 95 /** | |
| 96 * @type {Promise} | |
| 97 * @private | |
| 98 */ | |
| 99 this.asyncInitPromise_ = null; | |
| 100 | |
| 101 // Synchronously compute what we can. | 28 // Synchronously compute what we can. |
| 102 for (var i = 0; i < this.indexes.length; i++) { | 29 for (var i = 0; i < this.indexes.length; i++) { |
| 103 var entry = /** @type {!Entry} */ | 30 var entry = /** @type {!Entry} */ |
| 104 (fileManager.getFileList().item(this.indexes[i])); | 31 (fileManager.getFileList().item(this.indexes[i])); |
| 105 if (!entry) | 32 if (!entry) |
| 106 continue; | 33 continue; |
| 107 | 34 |
| 108 this.entries.push(entry); | 35 this.entries.push(entry); |
| 109 | 36 |
| 110 if (this.iconType == null) { | 37 if (this.iconType == null) { |
| 111 this.iconType = FileType.getIcon(entry); | 38 this.iconType = FileType.getIcon(entry); |
| 112 } else if (this.iconType != 'unknown') { | 39 } else if (this.iconType != 'unknown') { |
| 113 var iconType = FileType.getIcon(entry); | 40 var iconType = FileType.getIcon(entry); |
| 114 if (this.iconType != iconType) | 41 if (this.iconType != iconType) |
| 115 this.iconType = 'unknown'; | 42 this.iconType = 'unknown'; |
| 116 } | 43 } |
| 117 | 44 |
| 118 if (entry.isFile) { | 45 if (entry.isFile) { |
| 119 this.fileCount += 1; | 46 this.fileCount += 1; |
| 120 } else { | 47 } else { |
| 121 this.directoryCount += 1; | 48 this.directoryCount += 1; |
| 122 } | 49 } |
| 123 this.totalCount++; | 50 this.totalCount++; |
| 124 } | 51 } |
| 52 |
| 53 this.tasks = new FileTasks(this.fileManager_); |
| 54 |
| 55 Object.seal(this); |
| 125 } | 56 } |
| 126 | 57 |
| 127 /** | 58 /** |
| 128 * Computes data required to get file tasks and requests the tasks. | 59 * Computes data required to get file tasks and requests the tasks. |
| 129 * @return {!Promise} | 60 * |
| 61 * @param {function()} callback The callback. |
| 130 */ | 62 */ |
| 131 FileSelection.prototype.completeInit = function() { | 63 FileSelection.prototype.createTasks = function(callback) { |
| 132 if (!this.asyncInitPromise_) { | 64 if (!this.fileManager_.isOnDrive()) { |
| 133 if (!this.fileManager_.isOnDrive()) { | 65 this.tasks.init(this.entries); |
| 134 this.asyncInitPromise_ = Promise.resolve(); | 66 callback(); |
| 135 this.tasks.init(this.entries); | 67 return; |
| 136 this.allDriveFilesPresent = true; | 68 } |
| 137 } else { | 69 |
| 138 this.asyncInitPromise_ = new Promise(function(fulfill) { | 70 this.fileManager_.metadataCache_.get( |
| 139 this.fileManager_.metadataCache.get(this.entries, 'external', fulfill); | 71 this.entries, 'external', function(props) { |
| 140 }.bind(this)).then(function(props) { | |
| 141 var present = props.filter(function(p) { | 72 var present = props.filter(function(p) { |
| 142 return p && p.availableOffline; | 73 return p && p.availableOffline; |
| 143 }); | 74 }); |
| 144 this.allDriveFilesPresent = present.length == props.length; | 75 this.allDriveFilesPresent = present.length == props.length; |
| 145 // Collect all of the mime types and push that info into the | 76 |
| 146 // selection. | 77 // Collect all of the mime types and push that info into the selection. |
| 147 this.mimeTypes = props.map(function(value) { | 78 this.mimeTypes = props.map(function(value) { |
| 148 return (value && value.contentMimeType) || ''; | 79 return (value && value.contentMimeType) || ''; |
| 149 }); | 80 }); |
| 81 |
| 150 this.tasks.init(this.entries, this.mimeTypes); | 82 this.tasks.init(this.entries, this.mimeTypes); |
| 83 callback(); |
| 151 }.bind(this)); | 84 }.bind(this)); |
| 152 } | |
| 153 } | |
| 154 return this.asyncInitPromise_; | |
| 155 }; | 85 }; |
| 156 | 86 |
| 157 /** | 87 /** |
| 158 * Computes the total size of selected files. | 88 * Computes the total size of selected files. |
| 159 * | 89 * |
| 160 * @param {function()} callback Completion callback. Not called when cancelled, | 90 * @param {function()} callback Completion callback. Not called when cancelled, |
| 161 * or a new call has been invoked in the meantime. | 91 * or a new call has been invoked in the meantime. |
| 162 */ | 92 */ |
| 163 FileSelection.prototype.computeBytes = function(callback) { | 93 FileSelection.prototype.computeBytes = function(callback) { |
| 164 if (this.entries.length == 0) { | 94 if (this.entries.length == 0) { |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 * | 143 * |
| 214 * @private | 144 * @private |
| 215 */ | 145 */ |
| 216 FileSelection.prototype.cancelComputing_ = function() { | 146 FileSelection.prototype.cancelComputing_ = function() { |
| 217 this.computeBytesSequence_++; | 147 this.computeBytesSequence_++; |
| 218 }; | 148 }; |
| 219 | 149 |
| 220 /** | 150 /** |
| 221 * This object encapsulates everything related to current selection. | 151 * This object encapsulates everything related to current selection. |
| 222 * | 152 * |
| 223 * @param {!FileManager} fileManager File manager instance. | 153 * @param {FileManager} fileManager File manager instance. |
| 224 * @extends {cr.EventTarget} | 154 * @extends {cr.EventTarget} |
| 225 * @constructor | 155 * @constructor |
| 226 * @struct | 156 * @struct |
| 227 * @suppress {checkStructDictInheritance} | 157 * @suppress {checkStructDictInheritance} |
| 228 */ | 158 */ |
| 229 function FileSelectionHandler(fileManager) { | 159 function FileSelectionHandler(fileManager) { |
| 230 cr.EventTarget.call(this); | 160 cr.EventTarget.call(this); |
| 231 | 161 |
| 232 this.fileManager_ = fileManager; | 162 this.fileManager_ = fileManager; |
| 233 // TODO(dgozman): create a shared object with most of UI elements. | 163 // TODO(dgozman): create a shared object with most of UI elements. |
| 234 this.previewPanel_ = fileManager.ui.previewPanel; | 164 this.previewPanel_ = fileManager.ui.previewPanel; |
| 235 this.taskMenuButton_ = fileManager.ui.taskMenuButton; | 165 this.taskMenuButton_ = fileManager.ui.taskMenuButton; |
| 236 this.selection = new FileSelection(this.fileManager_, []); | 166 this.selection = new FileSelection(this.fileManager_, []); |
| 237 | 167 |
| 238 /** | 168 /** |
| 239 * @private | 169 * @private |
| 240 * @type {number} | 170 * @type {number} |
| 241 */ | 171 */ |
| 242 this.selectionUpdateTimer_ = 0; | 172 this.selectionUpdateTimer_ = 0; |
| 243 | 173 |
| 244 /** | 174 /** |
| 245 * @private | 175 * @private |
| 246 * @type {!Date} | 176 * @type {!Date} |
| 247 */ | 177 */ |
| 248 this.lastFileSelectionTime_ = new Date(); | 178 this.lastFileSelectionTime_ = new Date(); |
| 249 } | 179 } |
| 250 | 180 |
| 251 /** | 181 /** |
| 252 * @enum {string} | |
| 253 */ | |
| 254 FileSelectionHandler.EventType = { | |
| 255 /** | |
| 256 * Dispatched every time when selection is changed. | |
| 257 */ | |
| 258 CHANGE: 'change', | |
| 259 | |
| 260 /** | |
| 261 * Dispatched 200ms later after the selecton is changed. | |
| 262 * If multiple changes are happened during the term, only one CHANGE_THROTTLED | |
| 263 * event is dispatched. | |
| 264 */ | |
| 265 CHANGE_THROTTLED: 'changethrottled' | |
| 266 }; | |
| 267 | |
| 268 /** | |
| 269 * Create the temporary disabled action item. | 182 * Create the temporary disabled action item. |
| 270 * @return {Object} Created disabled item. | 183 * @return {Object} Created disabled item. |
| 271 * @private | 184 * @private |
| 272 */ | 185 */ |
| 273 FileSelectionHandler.createTemporaryDisabledActionItem_ = function() { | 186 FileSelectionHandler.createTemporaryDisabledActionItem_ = function() { |
| 274 if (!FileSelectionHandler.cachedDisabledActionItem_) { | 187 if (!FileSelectionHandler.cachedDisabledActionItem_) { |
| 275 FileSelectionHandler.cachedDisabledActionItem_ = { | 188 FileSelectionHandler.cachedDisabledActionItem_ = { |
| 276 title: str('ACTION_OPEN'), | 189 title: str('ACTION_OPEN'), |
| 277 disabled: true, | 190 disabled: true, |
| 278 taskId: null | 191 taskId: null |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 347 this.fileManager_.updateContextMenuActionItems( | 260 this.fileManager_.updateContextMenuActionItems( |
| 348 [FileSelectionHandler.createTemporaryDisabledActionItem_()]); | 261 [FileSelectionHandler.createTemporaryDisabledActionItem_()]); |
| 349 } else { | 262 } else { |
| 350 // Update context menu. | 263 // Update context menu. |
| 351 this.fileManager_.updateContextMenuActionItems(); | 264 this.fileManager_.updateContextMenuActionItems(); |
| 352 } | 265 } |
| 353 | 266 |
| 354 this.selectionUpdateTimer_ = setTimeout(function() { | 267 this.selectionUpdateTimer_ = setTimeout(function() { |
| 355 this.selectionUpdateTimer_ = null; | 268 this.selectionUpdateTimer_ = null; |
| 356 if (this.selection == selection) | 269 if (this.selection == selection) |
| 357 this.updateFileSelectionAsync_(selection); | 270 this.updateFileSelectionAsync(selection); |
| 358 }.bind(this), updateDelay); | 271 }.bind(this), updateDelay); |
| 359 | 272 |
| 360 cr.dispatchSimpleEvent(this, FileSelectionHandler.EventType.CHANGE); | 273 cr.dispatchSimpleEvent(this, 'change'); |
| 361 }; | 274 }; |
| 362 | 275 |
| 363 /** | 276 /** |
| 364 * Calculates async selection stats and updates secondary UI elements. | 277 * Calculates async selection stats and updates secondary UI elements. |
| 365 * | 278 * |
| 366 * @param {FileSelection} selection The selection object. | 279 * @param {FileSelection} selection The selection object. |
| 367 * @private | |
| 368 */ | 280 */ |
| 369 FileSelectionHandler.prototype.updateFileSelectionAsync_ = function(selection) { | 281 FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) { |
| 370 if (this.selection !== selection) | 282 if (this.selection != selection) return; |
| 371 return; | |
| 372 | 283 |
| 373 // Update the file tasks. | 284 // Update the file tasks. |
| 374 if (this.fileManager_.dialogType === DialogType.FULL_PAGE && | 285 if (this.fileManager_.dialogType === DialogType.FULL_PAGE && |
| 375 selection.directoryCount === 0 && selection.fileCount > 0) { | 286 selection.directoryCount === 0 && selection.fileCount > 0) { |
| 376 selection.completeInit().then(function() { | 287 selection.createTasks(function() { |
| 377 if (this.selection !== selection) | 288 if (this.selection != selection) |
| 378 return; | 289 return; |
| 379 selection.tasks.display(this.taskMenuButton_); | 290 selection.tasks.display(this.taskMenuButton_); |
| 380 selection.tasks.updateMenuItem(); | 291 selection.tasks.updateMenuItem(); |
| 381 }.bind(this)); | 292 }.bind(this)); |
| 382 } else { | 293 } else { |
| 383 this.taskMenuButton_.hidden = true; | 294 this.taskMenuButton_.hidden = true; |
| 384 } | 295 } |
| 385 | 296 |
| 386 // Update preview panels. | 297 // Update preview panels. |
| 387 var wasVisible = this.previewPanel_.visible; | 298 var wasVisible = this.previewPanel_.visible; |
| 388 this.previewPanel_.setSelection(selection); | 299 this.previewPanel_.setSelection(selection); |
| 389 | 300 |
| 390 // Scroll to item | 301 // Scroll to item |
| 391 if (!wasVisible && this.selection.totalCount == 1) { | 302 if (!wasVisible && this.selection.totalCount == 1) { |
| 392 var list = this.fileManager_.getCurrentList(); | 303 var list = this.fileManager_.getCurrentList(); |
| 393 list.scrollIndexIntoView(list.selectionModel.selectedIndex); | 304 list.scrollIndexIntoView(list.selectionModel.selectedIndex); |
| 394 } | 305 } |
| 395 | 306 |
| 396 // Sync the commands availability. | 307 // Sync the commands availability. |
| 397 if (this.fileManager_.commandHandler) | 308 if (this.fileManager_.commandHandler) |
| 398 this.fileManager_.commandHandler.updateAvailability(); | 309 this.fileManager_.commandHandler.updateAvailability(); |
| 399 | 310 |
| 400 cr.dispatchSimpleEvent(this, FileSelectionHandler.EventType.CHANGE_THROTTLED); | 311 // Inform tests it's OK to click buttons now. |
| 312 if (selection.totalCount > 0) |
| 313 util.testSendMessage('selection-change-complete'); |
| 401 }; | 314 }; |
| 402 | |
| 403 /** | |
| 404 * Returns whether all the selected files are available currently or not. | |
| 405 * Should be called after the selection initialized. | |
| 406 * @return {boolean} | |
| 407 */ | |
| 408 FileSelectionHandler.prototype.isAvailable = function() { | |
| 409 return !this.fileManager_.isOnDrive() || | |
| 410 this.fileManager_.volumeManager.getDriveConnectionState().type !== | |
| 411 VolumeManagerCommon.DriveConnectionType.OFFLINE || | |
| 412 this.selection.allDriveFilesPresent; | |
| 413 }; | |
| OLD | NEW |