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

Side by Side Diff: chrome/browser/resources/file_manager/foreground/js/file_selection.js

Issue 247123002: Move Files.app files to ui/file_manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the test failure on non-chromeos Created 6 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
(Empty)
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
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /**
8 * The current selection object.
9 *
10 * @param {FileManager} fileManager FileManager instance.
11 * @param {Array.<number>} indexes Selected indexes.
12 * @constructor
13 */
14 function FileSelection(fileManager, indexes) {
15 this.fileManager_ = fileManager;
16 this.computeBytesSequence_ = 0;
17 this.indexes = indexes;
18 this.entries = [];
19 this.totalCount = 0;
20 this.fileCount = 0;
21 this.directoryCount = 0;
22 this.bytes = 0;
23 this.showBytes = false;
24 this.allDriveFilesPresent = false,
25 this.iconType = null;
26 this.bytesKnown = false;
27 this.mustBeHidden_ = false;
28 this.mimeTypes = null;
29
30 // Synchronously compute what we can.
31 for (var i = 0; i < this.indexes.length; i++) {
32 var entry = fileManager.getFileList().item(this.indexes[i]);
33 if (!entry)
34 continue;
35
36 this.entries.push(entry);
37
38 if (this.iconType == null) {
39 this.iconType = FileType.getIcon(entry);
40 } else if (this.iconType != 'unknown') {
41 var iconType = FileType.getIcon(entry);
42 if (this.iconType != iconType)
43 this.iconType = 'unknown';
44 }
45
46 if (entry.isFile) {
47 this.fileCount += 1;
48 } else {
49 this.directoryCount += 1;
50 }
51 this.totalCount++;
52 }
53
54 this.tasks = new FileTasks(this.fileManager_);
55
56 Object.seal(this);
57 }
58
59 /**
60 * Computes data required to get file tasks and requests the tasks.
61 *
62 * @param {function} callback The callback.
63 */
64 FileSelection.prototype.createTasks = function(callback) {
65 if (!this.fileManager_.isOnDrive()) {
66 this.tasks.init(this.entries);
67 callback();
68 return;
69 }
70
71 this.fileManager_.metadataCache_.get(this.entries, 'drive', function(props) {
72 var present = props.filter(function(p) { return p && p.availableOffline });
73 this.allDriveFilesPresent = present.length == props.length;
74
75 // Collect all of the mime types and push that info into the selection.
76 this.mimeTypes = props.map(function(value) {
77 return (value && value.contentMimeType) || '';
78 });
79
80 this.tasks.init(this.entries, this.mimeTypes);
81 callback();
82 }.bind(this));
83 };
84
85 /**
86 * Computes the total size of selected files.
87 *
88 * @param {function} callback Completion callback. Not called when cancelled,
89 * or a new call has been invoked in the meantime.
90 */
91 FileSelection.prototype.computeBytes = function(callback) {
92 if (this.entries.length == 0) {
93 this.bytesKnown = true;
94 this.showBytes = false;
95 this.bytes = 0;
96 return;
97 }
98
99 var computeBytesSequence = ++this.computeBytesSequence_;
100 var pendingMetadataCount = 0;
101
102 var maybeDone = function() {
103 if (pendingMetadataCount == 0) {
104 this.bytesKnown = true;
105 callback();
106 }
107 }.bind(this);
108
109 var onProps = function(properties) {
110 // Ignore if the call got cancelled, or there is another new one fired.
111 if (computeBytesSequence != this.computeBytesSequence_)
112 return;
113
114 // It may happen that the metadata is not available because a file has been
115 // deleted in the meantime.
116 if (properties)
117 this.bytes += properties.size;
118 pendingMetadataCount--;
119 maybeDone();
120 }.bind(this);
121
122 for (var index = 0; index < this.entries.length; index++) {
123 var entry = this.entries[index];
124 if (entry.isFile) {
125 this.showBytes |= !FileType.isHosted(entry);
126 pendingMetadataCount++;
127 this.fileManager_.metadataCache_.get(entry, 'filesystem', onProps);
128 } else if (entry.isDirectory) {
129 // Don't compute the directory size as it's expensive.
130 // crbug.com/179073.
131 this.showBytes = false;
132 break;
133 }
134 }
135 maybeDone();
136 };
137
138 /**
139 * Cancels any async computation by increasing the sequence number. Results
140 * of any previous call to computeBytes() will be discarded.
141 *
142 * @private
143 */
144 FileSelection.prototype.cancelComputing_ = function() {
145 this.computeBytesSequence_++;
146 };
147
148 /**
149 * This object encapsulates everything related to current selection.
150 *
151 * @param {FileManager} fileManager File manager instance.
152 * @extends {cr.EventTarget}
153 * @constructor
154 */
155 function FileSelectionHandler(fileManager) {
156 this.fileManager_ = fileManager;
157 // TODO(dgozman): create a shared object with most of UI elements.
158 this.okButton_ = fileManager.okButton_;
159 this.filenameInput_ = fileManager.filenameInput_;
160 this.previewPanel_ = fileManager.previewPanel_;
161 this.taskItems_ = fileManager.taskItems_;
162 }
163
164 /**
165 * Create the temporary disabled action menu item.
166 * @return {Object} Created disabled item.
167 * @private
168 */
169 FileSelectionHandler.createTemporaryDisabledActionMenuItem_ = function() {
170 if (!FileSelectionHandler.cachedDisabledActionMenuItem_) {
171 FileSelectionHandler.cachedDisabledActionMenuItem_ = {
172 label: str('ACTION_OPEN'),
173 disabled: true
174 };
175 }
176
177 return FileSelectionHandler.cachedDisabledActionMenuItem_;
178 };
179
180 /**
181 * Cached the temporary disabled action menu item. Used inside
182 * FileSelectionHandler.createTemporaryDisabledActionMenuItem_().
183 * @private
184 */
185 FileSelectionHandler.cachedDisabledActionMenuItem_ = null;
186
187 /**
188 * FileSelectionHandler extends cr.EventTarget.
189 */
190 FileSelectionHandler.prototype.__proto__ = cr.EventTarget.prototype;
191
192 /**
193 * Maximum amount of thumbnails in the preview pane.
194 *
195 * @const
196 * @type {number}
197 */
198 FileSelectionHandler.MAX_PREVIEW_THUMBNAIL_COUNT = 4;
199
200 /**
201 * Maximum width or height of an image what pops up when the mouse hovers
202 * thumbnail in the bottom panel (in pixels).
203 *
204 * @const
205 * @type {number}
206 */
207 FileSelectionHandler.IMAGE_HOVER_PREVIEW_SIZE = 200;
208
209 /**
210 * Update the UI when the selection model changes.
211 *
212 * @param {Event} event The change event.
213 */
214 FileSelectionHandler.prototype.onFileSelectionChanged = function(event) {
215 var indexes =
216 this.fileManager_.getCurrentList().selectionModel.selectedIndexes;
217 if (this.selection) this.selection.cancelComputing_();
218 var selection = new FileSelection(this.fileManager_, indexes);
219 this.selection = selection;
220
221 if (this.fileManager_.dialogType == DialogType.SELECT_SAVEAS_FILE) {
222 // If this is a save-as dialog, copy the selected file into the filename
223 // input text box.
224 if (this.selection.totalCount == 1 &&
225 this.selection.entries[0].isFile &&
226 this.filenameInput_.value != this.selection.entries[0].name) {
227 this.filenameInput_.value = this.selection.entries[0].name;
228 }
229 }
230
231 this.updateOkButton();
232
233 if (this.selectionUpdateTimer_) {
234 clearTimeout(this.selectionUpdateTimer_);
235 this.selectionUpdateTimer_ = null;
236 }
237
238 // The rest of the selection properties are computed via (sometimes lengthy)
239 // asynchronous calls. We initiate these calls after a timeout. If the
240 // selection is changing quickly we only do this once when it slows down.
241
242 var updateDelay = 200;
243 var now = Date.now();
244 if (now > (this.lastFileSelectionTime_ || 0) + updateDelay) {
245 // The previous selection change happened a while ago. Update the UI soon.
246 updateDelay = 0;
247 }
248 this.lastFileSelectionTime_ = now;
249
250 if (this.fileManager_.dialogType === DialogType.FULL_PAGE &&
251 selection.directoryCount === 0 && selection.fileCount > 0) {
252 // Show disabled items for position calculation of the menu. They will be
253 // overridden in this.updateFileSelectionAsync().
254 this.fileManager_.updateContextMenuActionItems(
255 FileSelectionHandler.createTemporaryDisabledActionMenuItem_(), true);
256 } else {
257 // Update context menu.
258 this.fileManager_.updateContextMenuActionItems(null, false);
259 }
260
261 this.selectionUpdateTimer_ = setTimeout(function() {
262 this.selectionUpdateTimer_ = null;
263 if (this.selection == selection)
264 this.updateFileSelectionAsync(selection);
265 }.bind(this), updateDelay);
266 };
267
268 /**
269 * Updates the Ok button enabled state.
270 *
271 * @return {boolean} Whether button is enabled.
272 */
273 FileSelectionHandler.prototype.updateOkButton = function() {
274 var selectable;
275 var dialogType = this.fileManager_.dialogType;
276
277 if (DialogType.isFolderDialog(dialogType)) {
278 // In SELECT_FOLDER mode, we allow to select current directory
279 // when nothing is selected.
280 selectable = this.selection.directoryCount <= 1 &&
281 this.selection.fileCount == 0;
282 } else if (dialogType == DialogType.SELECT_OPEN_FILE) {
283 selectable = (this.isFileSelectionAvailable() &&
284 this.selection.directoryCount == 0 &&
285 this.selection.fileCount == 1);
286 } else if (dialogType == DialogType.SELECT_OPEN_MULTI_FILE) {
287 selectable = (this.isFileSelectionAvailable() &&
288 this.selection.directoryCount == 0 &&
289 this.selection.fileCount >= 1);
290 } else if (dialogType == DialogType.SELECT_SAVEAS_FILE) {
291 if (this.fileManager_.isOnReadonlyDirectory()) {
292 selectable = false;
293 } else {
294 selectable = !!this.filenameInput_.value;
295 }
296 } else if (dialogType == DialogType.FULL_PAGE) {
297 // No "select" buttons on the full page UI.
298 selectable = true;
299 } else {
300 throw new Error('Unknown dialog type');
301 }
302
303 this.okButton_.disabled = !selectable;
304 return selectable;
305 };
306
307 /**
308 * Check if all the files in the current selection are available. The only
309 * case when files might be not available is when the selection contains
310 * uncached Drive files and the browser is offline.
311 *
312 * @return {boolean} True if all files in the current selection are
313 * available.
314 */
315 FileSelectionHandler.prototype.isFileSelectionAvailable = function() {
316 var isDriveOffline =
317 this.fileManager_.volumeManager.getDriveConnectionState().type ===
318 util.DriveConnectionType.OFFLINE;
319 return !this.fileManager_.isOnDrive() || !isDriveOffline ||
320 this.selection.allDriveFilesPresent;
321 };
322
323 /**
324 * Calculates async selection stats and updates secondary UI elements.
325 *
326 * @param {FileSelection} selection The selection object.
327 */
328 FileSelectionHandler.prototype.updateFileSelectionAsync = function(selection) {
329 if (this.selection != selection) return;
330
331 // Update the file tasks.
332 if (this.fileManager_.dialogType === DialogType.FULL_PAGE &&
333 selection.directoryCount === 0 && selection.fileCount > 0) {
334 selection.createTasks(function() {
335 if (this.selection != selection)
336 return;
337 selection.tasks.display(this.taskItems_);
338 selection.tasks.updateMenuItem();
339 }.bind(this));
340 } else {
341 this.taskItems_.hidden = true;
342 }
343
344 // Update preview panels.
345 var wasVisible = this.previewPanel_.visible;
346 this.previewPanel_.setSelection(selection);
347
348 // Scroll to item
349 if (!wasVisible && this.selection.totalCount == 1) {
350 var list = this.fileManager_.getCurrentList();
351 list.scrollIndexIntoView(list.selectionModel.selectedIndex);
352 }
353
354 // Sync the commands availability.
355 if (this.fileManager_.commandHandler)
356 this.fileManager_.commandHandler.updateAvailability();
357
358 // Inform tests it's OK to click buttons now.
359 if (selection.totalCount > 0) {
360 util.testSendMessage('selection-change-complete');
361 }
362 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698