Disable the highlight mechanism after fixing the behavior in File.onPopupClosed_().
[armadillo.git] / web_frontend / actor.js
1 //
2 // Armadillo File Manager
3 // Copyright (c) 2010, Robert Sesek <http://www.bluestatic.org>
4 //
5 // This program is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free Software
7 // Foundation, either version 3 of the License, or any later version.
8 //
9
10 goog.provide('armadillo.Actor');
11 goog.provide('armadillo.Actor.TileControlRenderer_');
12
13 goog.require('armadillo.PathControl');
14 goog.require('goog.array');
15 goog.require('goog.dom');
16 goog.require('goog.events');
17 goog.require('goog.events.EventHandler');
18 goog.require('goog.positioning.ClientPosition');
19 goog.require('goog.positioning.Corner');
20 goog.require('goog.style');
21 goog.require('goog.ui.Container');
22 goog.require('goog.ui.Dialog');
23 goog.require('goog.ui.Popup');
24
25 /**
26 * The Actor is a popup that displays the various actions that can be performed
27 * on a given File.
28 * @param {armadillo.File} file The file to act on.
29 * @param {goog.dom.DomHelper} opt_domHelper
30 * @constructor
31 */
32 armadillo.Actor = function(file, opt_domHelper) {
33 goog.ui.Container.call(this, null, null, opt_domHelper);
34
35 /**
36 * The file object on which this acts.
37 * @type {armadillo.File}
38 */
39 this.file_ = file;
40
41 /**
42 * Registrar for all the Actor's events.
43 * @type {goog.events.EventHandler}
44 */
45 this.eh_ = new goog.events.EventHandler();
46
47 /**
48 * The Container that holds the display element.
49 * @type {goog.ui.Popup}
50 */
51 this.popup_ = new goog.ui.Popup(this.element_);
52 this.eh_.listenOnce(this.popup_, goog.ui.PopupBase.EventType.HIDE,
53 this.onPopupClosed_, false, this);
54
55 /**
56 * The UI element used for a specific action.
57 * @type {goog.Disposable}
58 */
59 this.actionObject_ = null;
60
61 armadillo.Actor.actors_.push(this);
62 }
63 goog.inherits(armadillo.Actor, goog.ui.Container);
64
65 /**
66 * An array of all the Actors that have been created.
67 */
68 armadillo.Actor.actors_ = new Array();
69
70 /**
71 * The different options that the Actor can perform.
72 */
73 armadillo.Actor.options_ = {
74 OPEN : 'open',
75 MOVE : 'move',
76 RENAME : 'rename',
77 DELETE : 'delete'
78 };
79
80 /**
81 * String values for the options.
82 */
83 armadillo.Actor.optionStrings_ = {
84 'open' : 'Open',
85 'move' : 'Move',
86 'rename' : 'Rename',
87 'delete' : 'Delete'
88 };
89
90 /**
91 * A global property that should be checked to see if an actor is present,
92 * creating a modal session.
93 */
94 armadillo.Actor.isModal = function() {
95 var isVisible = false;
96 goog.array.forEach(armadillo.Actor.actors_, function (e) {
97 isVisible |= e.popup_.isVisible();
98 });
99 return isVisible;
100 };
101
102 /**
103 * Disposer
104 * @protected
105 */
106 armadillo.Actor.prototype.disposeInternal = function() {
107 armadillo.Actor.superClass_.disposeInternal.call(this);
108
109 this.eh_.dispose();
110
111 // Remove the actor display element.
112 goog.dom.removeNode(this.element_);
113 this.element_ = null;
114
115 // Kill the popup.
116 this.popup_.dispose();
117 this.popup_ = null;
118
119 if (this.actionObject_) {
120 this.actionObject_.dispose();
121 this.actionObject_ = null;
122 }
123
124 // Remove the actor from the list.
125 goog.array.remove(armadillo.Actor.actors_, this);
126
127 this.file_ = null;
128 };
129
130 /**
131 * Shows the popup.
132 * @param {int} x The X position to show at
133 * @param {int} y The Y position to show at
134 */
135 armadillo.Actor.prototype.show = function(x, y) {
136 if (armadillo.Actor.isModal())
137 return;
138 this.popup_.setPinnedCorner(goog.positioning.Corner.TOP_LEFT);
139 this.popup_.setPosition(new goog.positioning.ClientPosition(x, y));
140 this.popup_.setHideOnEscape(true);
141 this.render();
142 this.popup_.setVisible(true);
143 this.file_.setHighlight(armadillo.File.Highlight.ACTIVE);
144 };
145
146 /**
147 * Hides the popup.
148 */
149 armadillo.Actor.prototype.hide = function() {
150 this.file_.setHighlight(armadillo.File.Highlight.SELECTED);
151 this.popup_.setVisible(false);
152 };
153
154
155 /**
156 * Creates a new path control object.
157 */
158 armadillo.Actor.prototype.createDom = function() {
159 this.decorateInternal(this.dom_.createDom('div', 'actor'));
160 this.popup_.setElement(this.element_);
161 };
162
163 /**
164 * @inheritDoc
165 */
166 armadillo.Actor.prototype.canDecorate = function() {
167 return false;
168 };
169
170 /**
171 * Decorates the given element into a path control.
172 * @param {Element} element
173 */
174 armadillo.Actor.prototype.decorateInternal = function(element) {
175 this.element_ = element;
176 this.dom_.removeChildren(this.element_);
177 for (var option in armadillo.Actor.options_) {
178 var tile = this.createTile_(option);
179 if (tile) {
180 this.addChild(tile, true);
181 }
182 }
183 };
184
185 /**
186 * Creates the DOM Element that is inserted into the popup.
187 * @param {armadillo.Actor.options_} Key of the option to create
188 * @returns {goog.ui.Control}
189 */
190 armadillo.Actor.prototype.createTile_ = function(option) {
191 var value = armadillo.Actor.options_[option];
192
193 // Create the title element.
194 var title = this.dom_.createDom('span', 'title',
195 armadillo.Actor.optionStrings_[value]);
196
197 var tile = new goog.ui.Control(title, new armadillo.Actor.TileControlRenderer_());
198 tile.actorOption = value;
199
200 // Cannot open non-directory files.
201 if (value == armadillo.Actor.options_.OPEN && !this.file_.isDirectory()) {
202 return null;
203 }
204
205 this.eh_.listen(tile, goog.ui.Component.EventType.ACTION,
206 this.tileClickHandler_, false, this);
207 return tile;
208 };
209
210 /**
211 * Click handler for individual tiles.
212 * @param {Event} e
213 */
214 armadillo.Actor.prototype.tileClickHandler_ = function(e) {
215 var option = e.target.actorOption;
216 if (option == armadillo.Actor.options_.OPEN) {
217 // TODO: assert that this.file_.isDirectory().
218 app.navigate(this.file_.getName());
219 } else if (option == armadillo.Actor.options_.MOVE ||
220 option == armadillo.Actor.options_.RENAME) {
221 this.performMove_();
222 } else if (option == armadillo.Actor.options_.DELETE) {
223 this.performDelete_();
224 }
225 this.hide();
226 };
227
228 /**
229 * Subroutine to handle bringing up the move confirmation UI.
230 * @private
231 */
232 armadillo.Actor.prototype.performMove_ = function() {
233 this.actionObject_ = this.createActionDialog_();
234 this.actionObject_.setTitle('Move File');
235
236 var editor = new armadillo.PathControl(this.file_.getFullPath(), true);
237 this.actionObject_.addChild(editor, true);
238
239 var closeCallback = function(e) {
240 if (e.key != goog.ui.Dialog.DefaultButtonKeys.CANCEL) {
241 var newPath = editor.getPath();
242 this.file_.move(newPath);
243 }
244 this.dispose();
245 };
246 // Will be removed when the event source closes.
247 this.eh_.listen(this.actionObject_, goog.ui.Dialog.SELECT_EVENT,
248 closeCallback, false, this);
249
250 this.actionObject_.setVisible(true);
251 var position = goog.style.getPosition(this.actionObject_.getElement());
252 goog.style.setPosition(this.actionObject_.getElement(), position.x, '10%');
253 };
254
255 /**
256 * Subroutine to handle bringing up the delete confirmation UI.
257 * @private
258 */
259 armadillo.Actor.prototype.performDelete_ = function() {
260 this.actionObject_ = this.createActionDialog_();
261 this.actionObject_.setTitle('Confirm Delete');
262
263 var container = this.actionObject_.getContentElement();
264 var content = goog.dom.createDom('span', null,
265 'Are you sure you want to delete:',
266 goog.dom.createElement('br'),
267 goog.dom.createDom('strong', null, this.file_.getName()));
268 goog.dom.appendChild(container, content);
269
270 var closeCallback = function(e) {
271 if (e.key != goog.ui.Dialog.DefaultButtonKeys.CANCEL) {
272 this.file_.remove();
273 }
274 this.dispose();
275 };
276 // Will be removed when the event source closes.
277 this.eh_.listen(this.actionObject_, goog.ui.Dialog.SELECT_EVENT,
278 closeCallback, false, this);
279
280 this.actionObject_.setVisible(true);
281 };
282
283 /**
284 * Creates a new instance of a Dialog that has some basic properties set that
285 * are common to performing actions.
286 * @private
287 */
288 armadillo.Actor.prototype.createActionDialog_ = function() {
289 var confirm = new goog.ui.Dialog();
290 confirm.setDisposeOnHide(true);
291 confirm.setEscapeToCancel(true);
292 confirm.setModal(true);
293 confirm.setDraggable(false);
294 confirm.setHasTitleCloseButton(false);
295 return confirm;
296 };
297
298 /**
299 * Event handler for when this.popup_ closes.
300 * @param {Event} e
301 */
302 armadillo.Actor.prototype.onPopupClosed_ = function(e) {
303 // If an action is not being performed, then dispose the Actor. Otherwise,
304 // this will get cleaned up after the actionObject_ closes.
305 if (!this.actionObject_) {
306 this.file_.setHighlight(armadillo.File.Highlight.SELECTED);
307 this.dispose();
308 }
309 };
310
311 /**
312 * Tile Control Renderer
313 * @constructor
314 */
315 armadillo.Actor.TileControlRenderer_ = function() {
316 goog.ui.ControlRenderer.call(this);
317 };
318 goog.inherits(armadillo.Actor.TileControlRenderer_, goog.ui.ControlRenderer);
319
320 /**
321 * Returns the control's contents wrapped in a DIV, with the renderer's own
322 * CSS class and additional state-specific classes applied to it.
323 * @param {goog.ui.Control} control Control to render.
324 * @return {Element} Root element for the control.
325 */
326 goog.ui.ControlRenderer.prototype.createDom = function(control) {
327 // Create and return DIV wrapping contents.
328 return control.getDomHelper().createDom('div', 'tile', control.getContent());
329 };