Move the move/rename dialog into the Actor UI to reudce modal feelings.
[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('armadillo.TVRenamer');
15 goog.require('goog.array');
16 goog.require('goog.dom');
17 goog.require('goog.events');
18 goog.require('goog.events.EventHandler');
19 goog.require('goog.style');
20 goog.require('goog.ui.Button');
21 goog.require('goog.ui.Container');
22 goog.require('goog.ui.Dialog');
23
24 /**
25 * The Actor is a popup that displays the various actions that can be performed
26 * on a given File.
27 * @param {armadillo.File} file The file to act on.
28 * @param {goog.dom.DomHelper} opt_domHelper
29 * @constructor
30 */
31 armadillo.Actor = function(file, opt_domHelper) {
32 goog.ui.Container.call(this, null, null, opt_domHelper);
33
34 /**
35 * The file object on which this acts.
36 * @type {armadillo.File}
37 */
38 this.file_ = file;
39
40 /**
41 * Registrar for all the Actor's events.
42 * @type {goog.events.EventHandler}
43 */
44 this.eh_ = new goog.events.EventHandler();
45
46 /**
47 * The UI element used for a specific action.
48 * @type {goog.Disposable}
49 */
50 this.actionObject_ = null;
51
52 /**
53 * Controls for the current action.
54 * @type {goog.ui.Control}
55 */
56 this.controlContainer_ = null;
57 }
58 goog.inherits(armadillo.Actor, goog.ui.Container);
59
60 /**
61 * The different options that the Actor can perform.
62 */
63 armadillo.Actor.options_ = {
64 OPEN : 'open',
65 MOVE : 'move',
66 RENAME : 'rename',
67 DELETE : 'delete',
68 TV_RENAME : 'tv-rename'
69 };
70
71 /**
72 * String values for the options.
73 */
74 armadillo.Actor.optionStrings_ = {
75 'open' : 'Open',
76 'move' : 'Move',
77 'rename' : 'Rename',
78 'delete' : 'Delete',
79 'tv-rename' : 'Rename TV Episode'
80 };
81
82 /**
83 * Disposer
84 * @protected
85 */
86 armadillo.Actor.prototype.disposeInternal = function() {
87 armadillo.Actor.superClass_.disposeInternal.call(this);
88
89 this.eh_.dispose();
90
91 if (this.controlContainer_)
92 this.controlContainer_.dispose();
93 this.controlContainer_ = null;
94
95 // Remove the actor display element.
96 goog.dom.removeNode(this.element_);
97 this.element_ = null;
98
99 if (this.actionObject_) {
100 this.actionObject_.dispose();
101 this.actionObject_ = null;
102 }
103
104 this.file_ = null;
105 };
106
107 armadillo.Actor.prototype.createDom = function() {
108 this.setElementInternal(this.dom_.createDom('div'));
109 this.decorate(this.getElement());
110 };
111
112 /**
113 * Decorates the given element into a path control.
114 * @param {Element} element
115 */
116 armadillo.Actor.prototype.decorateInternal = function(element) {
117 this.element_ = element;
118 goog.dom.classes.add(this.element_, 'actor');
119 this.dom_.removeChildren(this.element_);
120 for (var option in armadillo.Actor.options_) {
121 var tile = this.createTile_(option);
122 if (tile) {
123 this.addChild(tile, true);
124 }
125 }
126 this.controlContainer_ = new goog.ui.Control();
127 this.addChild(this.controlContainer_, true);
128 };
129
130 /**
131 * Creates the DOM Element that is inserted into the popup.
132 * @param {armadillo.Actor.options_} Key of the option to create
133 * @returns {goog.ui.Control}
134 */
135 armadillo.Actor.prototype.createTile_ = function(option) {
136 var value = armadillo.Actor.options_[option];
137
138 // Create the title element.
139 var title = this.dom_.createDom('span', 'title',
140 armadillo.Actor.optionStrings_[value]);
141
142 var tile = new goog.ui.Control(title, new armadillo.Actor.TileControlRenderer_());
143 tile.actorOption = value;
144
145 // Cannot open non-directory files.
146 if (value == armadillo.Actor.options_.OPEN && !this.file_.isDirectory()) {
147 return null;
148 }
149
150 this.eh_.listen(tile, goog.ui.Component.EventType.ACTION,
151 this.tileClickHandler_, false, this);
152 return tile;
153 };
154
155 /**
156 * Click handler for individual tiles.
157 * @param {Event} e
158 */
159 armadillo.Actor.prototype.tileClickHandler_ = function(e) {
160 var option = e.target.actorOption;
161 this.controlContainer_.removeChildren(true);
162 this.controlContainer_.setVisible(true);
163 if (option == armadillo.Actor.options_.OPEN) {
164 // TODO: assert that this.file_.isDirectory().
165 app.navigate(this.file_.getName());
166 } else if (option == armadillo.Actor.options_.MOVE ||
167 option == armadillo.Actor.options_.RENAME) {
168 this.performMove_();
169 } else if (option == armadillo.Actor.options_.DELETE) {
170 this.performDelete_();
171 } else if (option == armadillo.Actor.options_.TV_RENAME) {
172 this.performTVRename_();
173 }
174 };
175
176 /**
177 * Subroutine to handle bringing up the move confirmation UI.
178 * @private
179 */
180 armadillo.Actor.prototype.performMove_ = function() {
181 var editor = new armadillo.PathControl(this.file_.getFullPath(), true);
182 this.controlContainer_.addChild(editor, true);
183
184 var okCallback = function(e) {
185 var newPath = editor.getPath();
186 this.file_.move(newPath);
187 };
188 this.createOkCancel_(goog.bind(okCallback, this), null);
189 };
190
191 /**
192 * Subroutine to handle bringing up the delete confirmation UI.
193 * @private
194 */
195 armadillo.Actor.prototype.performDelete_ = function() {
196 this.actionObject_ = this.createActionDialog_();
197 this.actionObject_.setTitle('Confirm Delete');
198
199 var container = this.actionObject_.getContentElement();
200 var content = goog.dom.createDom('span', null,
201 'Are you sure you want to delete:',
202 goog.dom.createElement('br'),
203 goog.dom.createDom('strong', null, this.file_.getName()));
204 goog.dom.appendChild(container, content);
205
206 var closeCallback = function(e) {
207 if (e.key != goog.ui.Dialog.DefaultButtonKeys.CANCEL) {
208 this.file_.remove();
209 }
210 };
211 // Will be removed when the event source closes.
212 this.eh_.listen(this.actionObject_, goog.ui.Dialog.SELECT_EVENT,
213 closeCallback, false, this);
214
215 this.actionObject_.setVisible(true);
216 };
217
218 /**
219 * Subroutine that renames a file to it's title based on season and episode.
220 * @private
221 */
222 armadillo.Actor.prototype.performTVRename_ = function() {
223 var renamer = new armadillo.TVRenamer(this.file_);
224 renamer.run();
225 };
226
227 /**
228 * Creates a new instance of a Dialog that has some basic properties set that
229 * are common to performing actions.
230 * @private
231 */
232 armadillo.Actor.prototype.createActionDialog_ = function() {
233 var confirm = new goog.ui.Dialog();
234 confirm.setDisposeOnHide(true);
235 confirm.setEscapeToCancel(true);
236 confirm.setModal(true);
237 confirm.setDraggable(false);
238 confirm.setHasTitleCloseButton(false);
239 return confirm;
240 };
241
242 /**
243 * Creates two buttons: one for OK one for Cancel and attahes them to the
244 * |controlContainer_|.
245 * @param {function(Event)?} okCallback
246 * @param {function(Event)?} cancelCallback
247 */
248 armadillo.Actor.prototype.createOkCancel_ = function(okCallback, cancelCallback) {
249 var ok = new goog.ui.Button('OK');
250 if (okCallback)
251 this.eh_.listen(ok, goog.ui.Component.EventType.ACTION, okCallback);
252 var cancel = new goog.ui.Button('Cancel');
253 if (!cancelCallback)
254 cancelCallback = goog.bind(this.defaultCancelCallback_, this);
255 this.eh_.listen(cancel, goog.ui.Component.EventType.ACTION, cancelCallback);
256 this.controlContainer_.addChild(ok, true);
257 this.controlContainer_.addChild(cancel, true);
258 };
259
260 /**
261 * The default cancel callback for the above createOkCancel_().
262 * @param {event} e
263 * @private
264 */
265 armadillo.Actor.prototype.defaultCancelCallback_ = function(e) {
266 this.controlContainer_.removeChildren(true);
267 };
268
269 /**
270 * Tile Control Renderer
271 * @constructor
272 */
273 armadillo.Actor.TileControlRenderer_ = function() {
274 goog.ui.ControlRenderer.call(this);
275 };
276 goog.inherits(armadillo.Actor.TileControlRenderer_, goog.ui.ControlRenderer);
277
278 /**
279 * Returns the control's contents wrapped in a DIV, with the renderer's own
280 * CSS class and additional state-specific classes applied to it.
281 * @param {goog.ui.Control} control Control to render.
282 * @return {Element} Root element for the control.
283 */
284 armadillo.Actor.TileControlRenderer_.prototype.createDom = function(control) {
285 // Create and return DIV wrapping contents.
286 return control.getDomHelper().createDom('div', 'tile', control.getContent());
287 };