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 View 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
38 * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
40 FileGrid.decorate = function(self, metadataCache, volumeManager) {
41 cr.ui.Grid.decorate(self);
42 self.__proto__ = FileGrid.prototype;
43 self.metadataCache_ = metadataCache;
44 self.volumeManager_ = volumeManager;
46 self.scrollBar_ = new MainPanelScrollBar();
47 self.scrollBar_.initialize(self.parentNode, self);
48 self.setBottomMarginForPanel(0);
50 self.itemConstructor = function(entry) {
51 var item = self.ownerDocument.createElement('LI');
52 FileGrid.Item.decorate(item, entry, self);
56 self.relayoutRateLimiter_ =
57 new AsyncUtil.RateLimiter(self.relayoutImmediately_.bind(self));
61 * Updates items to reflect metadata changes.
62 * @param {string} type Type of metadata changed.
63 * @param {Array.<Entry>} entries Entries whose metadata changed.
65 FileGrid.prototype.updateListItemsMetadata = function(type, entries) {
66 var urls = util.entriesToURLs(entries);
67 var boxes = this.querySelectorAll('.img-container');
68 for (var i = 0; i < boxes.length; i++) {
70 var listItem = this.getListItemAncestor(box);
71 var entry = listItem && this.dataModel.item(listItem.listIndex);
72 if (!entry || urls.indexOf(entry.toURL()) === -1)
75 FileGrid.decorateThumbnailBox(box,
79 ThumbnailLoader.FillMode.FIT,
80 FileGrid.ThumbnailQuality.LOW);
85 * Redraws the UI. Skips multiple consecutive calls.
87 FileGrid.prototype.relayout = function() {
88 this.relayoutRateLimiter_.run();
92 * Redraws the UI immediately.
95 FileGrid.prototype.relayoutImmediately_ = function() {
96 this.startBatchUpdates();
99 this.endBatchUpdates();
100 cr.dispatchSimpleEvent(this, 'relayout');
104 * Decorates thumbnail.
105 * @param {HTMLElement} li List item.
106 * @param {Entry} entry Entry to render a thumbnail for.
107 * @param {MetadataCache} metadataCache To retrieve metadata.
108 * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
110 FileGrid.decorateThumbnail = function(li, entry, metadataCache, volumeManager) {
111 li.className = 'thumbnail-item';
113 filelist.decorateListItem(li, entry, metadataCache);
115 var frame = li.ownerDocument.createElement('div');
116 frame.className = 'thumbnail-frame';
117 li.appendChild(frame);
119 var box = li.ownerDocument.createElement('div');
121 FileGrid.decorateThumbnailBox(box,
125 ThumbnailLoader.FillMode.AUTO,
126 FileGrid.ThumbnailQuality.LOW);
128 frame.appendChild(box);
130 var bottom = li.ownerDocument.createElement('div');
131 bottom.className = 'thumbnail-bottom';
132 bottom.appendChild(filelist.renderFileNameLabel(li.ownerDocument, entry));
133 frame.appendChild(bottom);
137 * Decorates the box containing a centered thumbnail image.
139 * @param {HTMLDivElement} box Box to decorate.
140 * @param {Entry} entry Entry which thumbnail is generating for.
141 * @param {MetadataCache} metadataCache To retrieve metadata.
142 * @param {VolumeManagerWrapper} volumeManager Volume manager instance.
143 * @param {ThumbnailLoader.FillMode} fillMode Fill mode.
144 * @param {FileGrid.ThumbnailQuality} quality Thumbnail quality.
145 * @param {function(HTMLElement)=} opt_imageLoadCallback Callback called when
146 * the image has been loaded before inserting it into the DOM.
148 FileGrid.decorateThumbnailBox = function(
149 box, entry, metadataCache, volumeManager, fillMode, quality,
150 opt_imageLoadCallback) {
151 var locationInfo = volumeManager.getLocationInfo(entry);
152 box.className = 'img-container';
154 if (entry.isDirectory) {
155 box.setAttribute('generic-thumbnail', 'folder');
156 if (locationInfo && locationInfo.isDriveBased) {
157 metadataCache.getOne(entry, 'external', function(metadata) {
159 box.classList.add('shared');
162 if (opt_imageLoadCallback)
163 setTimeout(opt_imageLoadCallback, 0, null /* callback parameter */);
167 var metadataTypes = 'thumbnail|filesystem|external|media';
169 // Drive provides high quality thumbnails via USE_EMBEDDED, however local
170 // images usually provide very tiny thumbnails, therefore USE_EMBEDDE can't
171 // be used to obtain high quality output.
174 case FileGrid.ThumbnailQuality.LOW:
175 useEmbedded = ThumbnailLoader.UseEmbedded.USE_EMBEDDED;
177 case FileGrid.ThumbnailQuality.HIGH:
178 // TODO(mtomasz): Use Entry instead of paths.
179 useEmbedded = (locationInfo && locationInfo.isDriveBased) ?
180 ThumbnailLoader.UseEmbedded.USE_EMBEDDED :
181 ThumbnailLoader.UseEmbedded.NO_EMBEDDED;
185 metadataCache.getOne(entry, metadataTypes,
187 new ThumbnailLoader(entry,
188 ThumbnailLoader.LoaderType.IMAGE,
190 undefined, // opt_mediaType
194 ThumbnailLoader.OptimizationMode.DISCARD_DETACHED,
195 opt_imageLoadCallback);
200 * Item for the Grid View.
203 FileGrid.Item = function() {
208 * Inherits from cr.ui.ListItem.
210 FileGrid.Item.prototype.__proto__ = cr.ui.ListItem.prototype;
212 Object.defineProperty(FileGrid.Item.prototype, 'label', {
214 * @this {FileGrid.Item}
215 * @return {string} Label of the item.
218 return this.querySelector('filename-label').textContent;
223 * @param {Element} li List item element.
224 * @param {Entry} entry File entry.
225 * @param {FileGrid} grid Owner.
227 FileGrid.Item.decorate = function(li, entry, grid) {
228 li.__proto__ = FileGrid.Item.prototype;
229 // TODO(mtomasz): Pass the metadata cache and the volume manager directly
230 // instead of accessing private members of grid.
231 FileGrid.decorateThumbnail(
232 li, entry, grid.metadataCache_, grid.volumeManager_, true);
234 // Override the default role 'listitem' to 'option' to match the parent's
236 li.setAttribute('role', 'option');
240 * Sets the margin height for the transparent preview panel at the bottom.
241 * @param {number} margin Margin to be set in px.
243 FileGrid.prototype.setBottomMarginForPanel = function(margin) {
244 // +20 bottom margin is needed to match the bottom margin size with the
245 // margin between its items.
246 this.style.paddingBottom = (margin + 20) + 'px';
247 this.scrollBar_.setBottomMarginForPanel(margin);
251 * Obtains if the drag selection should be start or not by referring the mouse
253 * @param {MouseEvent} event Drag start event.
254 * @return {boolean} True if the mouse is hit to the background of the list.
256 FileGrid.prototype.shouldStartDragSelection = function(event) {
257 var pos = DragSelector.getScrolledPosition(this, event);
258 return this.getHitElements(pos.x, pos.y).length === 0;
262 * Obtains the column/row index that the coordinate points.
263 * @param {number} coordinate Vertical/horizontal coordinate value that points
265 * @param {number} step Length from a column/row to the next one.
266 * @param {number} threshold Threshold that determines whether 1 offset is added
267 * to the return value or not. This is used in order to handle the margin of
269 * @return {number} Index of hit column/row.
272 FileGrid.prototype.getHitIndex_ = function(coordinate, step, threshold) {
273 var index = ~~(coordinate / step);
274 return (coordinate % step >= threshold) ? index + 1 : index;
278 * Obtains the index list of elements that are hit by the point or the
281 * We should match its argument interface with FileList.getHitElements.
283 * @param {number} x X coordinate value.
284 * @param {number} y Y coordinate value.
285 * @param {number=} opt_width Width of the coordinate.
286 * @param {number=} opt_height Height of the coordinate.
287 * @return {Array.<number>} Index list of hit elements.
289 FileGrid.prototype.getHitElements = function(x, y, opt_width, opt_height) {
290 var currentSelection = [];
291 var right = x + (opt_width || 0);
292 var bottom = y + (opt_height || 0);
293 var itemMetrics = this.measureItem();
294 var horizontalStartIndex = this.getHitIndex_(
295 x, itemMetrics.width, itemMetrics.width - itemMetrics.marginRight);
296 var horizontalEndIndex = Math.min(this.columns, this.getHitIndex_(
297 right, itemMetrics.width, itemMetrics.marginLeft));
298 var verticalStartIndex = this.getHitIndex_(
299 y, itemMetrics.height, itemMetrics.height - itemMetrics.bottom);
300 var verticalEndIndex = this.getHitIndex_(
301 bottom, itemMetrics.height, itemMetrics.marginTop);
302 for (var verticalIndex = verticalStartIndex;
303 verticalIndex < verticalEndIndex;
305 var indexBase = this.getFirstItemInRow(verticalIndex);
306 for (var horizontalIndex = horizontalStartIndex;
307 horizontalIndex < horizontalEndIndex;
309 var index = indexBase + horizontalIndex;
310 if (0 <= index && index < this.dataModel.length)
311 currentSelection.push(index);
314 return currentSelection;