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.
8 * FileGrid constructor.
10 * Represents grid for the Grid Vew in the File Manager.
12 * @extends {cr.ui.Grid}
16 throw new Error('Use FileGrid.decorate');
23 FileGrid.ThumbnailQuality = {
29 * Inherits from cr.ui.Grid.
31 FileGrid.prototype.__proto__ = cr.ui.Grid.prototype;
34 * Decorates an HTML element to be a FileGrid.
35 * @param {HTMLElement} self The grid to decorate.
36 * @param {MetadataCache} metadataCache Metadata cache to find entries
39 FileGrid.decorate = function(self, metadataCache) {
40 cr.ui.Grid.decorate(self);
41 self.__proto__ = FileGrid.prototype;
42 self.metadataCache_ = metadataCache;
44 self.scrollBar_ = new MainPanelScrollBar();
45 self.scrollBar_.initialize(self.parentNode, self);
46 self.setBottomMarginForPanel(0);
48 self.itemConstructor = function(entry) {
49 var item = self.ownerDocument.createElement('LI');
50 FileGrid.Item.decorate(item, entry, self);
54 self.relayoutAggregation_ =
55 new AsyncUtil.Aggregation(self.relayoutImmediately_.bind(self));
59 * Updates items to reflect metadata changes.
60 * @param {string} type Type of metadata changed.
61 * @param {Object.<string, Object>} props Map from entry URLs to metadata props.
63 FileGrid.prototype.updateListItemsMetadata = function(type, props) {
64 var boxes = this.querySelectorAll('.img-container');
65 for (var i = 0; i < boxes.length; i++) {
67 var entry = this.dataModel.item(this.getListItemAncestor(box));
68 if (!entry || !(entry.toURL() in props))
71 FileGrid.decorateThumbnailBox(box,
74 ThumbnailLoader.FillMode.FIT,
75 FileGrid.ThumbnailQuality.HIGH);
80 * Redraws the UI. Skips multiple consecutive calls.
82 FileGrid.prototype.relayout = function() {
83 this.relayoutAggregation_.run();
87 * Redraws the UI immediately.
90 FileGrid.prototype.relayoutImmediately_ = function() {
91 this.startBatchUpdates();
94 this.endBatchUpdates();
95 cr.dispatchSimpleEvent(this, 'relayout');
99 * Decorates thumbnail.
100 * @param {HTMLElement} li List item.
101 * @param {Entry} entry Entry to render a thumbnail for.
102 * @param {MetadataCache} metadataCache To retrieve metadata.
104 FileGrid.decorateThumbnail = function(li, entry, metadataCache) {
105 li.className = 'thumbnail-item';
107 filelist.decorateListItem(li, entry, metadataCache);
109 var frame = li.ownerDocument.createElement('div');
110 frame.className = 'thumbnail-frame';
111 li.appendChild(frame);
113 var box = li.ownerDocument.createElement('div');
115 FileGrid.decorateThumbnailBox(box,
118 ThumbnailLoader.FillMode.AUTO,
119 FileGrid.ThumbnailQuality.HIGH);
121 frame.appendChild(box);
123 var bottom = li.ownerDocument.createElement('div');
124 bottom.className = 'thumbnail-bottom';
125 bottom.appendChild(filelist.renderFileNameLabel(li.ownerDocument, entry));
126 frame.appendChild(bottom);
130 * Decorates the box containing a centered thumbnail image.
132 * @param {HTMLDivElement} box Box to decorate.
133 * @param {Entry} entry Entry which thumbnail is generating for.
134 * @param {MetadataCache} metadataCache To retrieve metadata.
135 * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
136 * @param {FileGrid.ThumbnailQuality} quality Thumbnail quality.
137 * @param {function(HTMLElement)=} opt_imageLoadCallback Callback called when
138 * the image has been loaded before inserting it into the DOM.
140 FileGrid.decorateThumbnailBox = function(
141 box, entry, metadataCache, fillMode, quality, opt_imageLoadCallback) {
142 box.className = 'img-container';
143 if (entry.isDirectory) {
144 box.setAttribute('generic-thumbnail', 'folder');
145 if (opt_imageLoadCallback)
146 setTimeout(opt_imageLoadCallback, 0, null /* callback parameter */);
150 var imageUrl = entry.toURL();
151 var metadataTypes = 'thumbnail|filesystem';
153 if (FileType.isOnDrive(imageUrl)) {
154 metadataTypes += '|drive';
156 // TODO(dgozman): If we ask for 'media' for a Drive file we fall into an
158 metadataTypes += '|media';
161 // Drive provides high quality thumbnails via USE_EMBEDDED, however local
162 // images usually provide very tiny thumbnails, therefore USE_EMBEDDE can't
163 // be used to obtain high quality output.
166 case FileGrid.ThumbnailQuality.LOW:
167 useEmbedded = ThumbnailLoader.UseEmbedded.USE_EMBEDDED;
169 case FileGrid.ThumbnailQuality.HIGH:
170 useEmbedded = FileType.isOnDrive(imageUrl) ?
171 ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
172 ThumbnailLoader.UseEmbedded.NO_EMBEDDED;
176 metadataCache.get(imageUrl, metadataTypes,
178 new ThumbnailLoader(imageUrl,
179 ThumbnailLoader.LoaderType.IMAGE,
181 undefined, // opt_mediaType
185 ThumbnailLoader.OptimizationMode.DISCARD_DETACHED,
186 opt_imageLoadCallback);
191 * Item for the Grid View.
194 FileGrid.Item = function() {
199 * Inherits from cr.ui.ListItem.
201 FileGrid.Item.prototype.__proto__ = cr.ui.ListItem.prototype;
203 Object.defineProperty(FileGrid.Item.prototype, 'label', {
205 * @this {FileGrid.Item}
206 * @return {string} Label of the item.
209 return this.querySelector('filename-label').textContent;
214 * @param {Element} li List item element.
215 * @param {Entry} entry File entry.
216 * @param {FileGrid} grid Owner.
218 FileGrid.Item.decorate = function(li, entry, grid) {
219 li.__proto__ = FileGrid.Item.prototype;
220 FileGrid.decorateThumbnail(li, entry, grid.metadataCache_, true);
222 // Override the default role 'listitem' to 'option' to match the parent's
224 li.setAttribute('role', 'option');
228 * Sets the margin height for the transparent preview panel at the bottom.
229 * @param {number} margin Margin to be set in px.
231 FileGrid.prototype.setBottomMarginForPanel = function(margin) {
232 // +20 bottom margin is needed to match the bottom margin size with the
233 // margin between its items.
234 this.style.paddingBottom = (margin + 20) + 'px';
235 this.scrollBar_.setBottomMarginForPanel(margin);
239 * Obtains if the drag selection should be start or not by referring the mouse
241 * @param {MouseEvent} event Drag start event.
242 * @return {boolean} True if the mouse is hit to the background of the list.
244 FileGrid.prototype.shouldStartDragSelection = function(event) {
245 var pos = DragSelector.getScrolledPosition(this, event);
246 return this.getHitElements(pos.x, pos.y).length == 0;
250 * Obtains the column/row index that the coordinate points.
251 * @param {number} coordinate Vertical/horizontal coodinate value that points
253 * @param {number} step Length from a column/row to the next one.
254 * @param {number} threshold Threshold that determinds whether 1 offset is added
255 * to the return value or not. This is used in order to handle the margin of
257 * @return {number} Index of hit column/row.
260 FileGrid.prototype.getHitIndex_ = function(coordinate, step, threshold) {
261 var index = ~~(coordinate / step);
262 return (coordinate % step >= threshold) ? index + 1 : index;
266 * Obtains the index list of elements that are hit by the point or the
269 * We should match its argument interface with FileList.getHitElements.
271 * @param {number} x X coordinate value.
272 * @param {number} y Y coordinate value.
273 * @param {=number} opt_width Width of the coordinate.
274 * @param {=number} opt_height Height of the coordinate.
275 * @return {Array.<number>} Index list of hit elements.
277 FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) {
278 var currentSelection = [];
279 var right = x + (opt_width || 0);
280 var bottom = y + (opt_height || 0);
281 var itemMetrics = this.measureItem();
282 var horizontalStartIndex = this.getHitIndex_(
283 x, itemMetrics.width, itemMetrics.width - itemMetrics.marginRight);
284 var horizontalEndIndex = Math.min(this.columns, this.getHitIndex_(
285 right, itemMetrics.width, itemMetrics.marginLeft));
286 var verticalStartIndex = this.getHitIndex_(
287 y, itemMetrics.height, itemMetrics.height - itemMetrics.bottom);
288 var verticalEndIndex = this.getHitIndex_(
289 bottom, itemMetrics.height, itemMetrics.marginTop);
290 for (var verticalIndex = verticalStartIndex;
291 verticalIndex < verticalEndIndex;
293 var indexBase = this.getFirstItemInRow(verticalIndex);
294 for (var horizontalIndex = horizontalStartIndex;
295 horizontalIndex < horizontalEndIndex;
297 var index = indexBase + horizontalIndex;
298 if (0 <= index && index < this.dataModel.length)
299 currentSelection.push(index);
302 return currentSelection;