Implement the renaming part of moving.
[armadillo.git] / web_frontend / path_control.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.PathControl');
11 goog.provide('armadillo.PathControl.NameControlRenderer_');
12
13 goog.require('goog.array');
14 goog.require('goog.ui.Component');
15 goog.require('goog.ui.FilteredMenu');
16 goog.require('goog.ui.LabelInput');
17 goog.require('goog.ui.MenuButton');
18 goog.require('goog.ui.MenuItem');
19
20 /**
21 * Creates a new path editing control for a given path.
22 * @param {string} path The path to create an editor for
23 * @param {bool} editLastComponent Whether the last component should be shown as an edit box
24 * @param {DomHelper} opt_domHelper Optional DOM helper
25 * @constructor
26 */
27 armadillo.PathControl = function(path, editLastComponent, opt_domHelper) {
28 goog.ui.Component.call(this, opt_domHelper);
29
30 /**
31 * Full path of the control.
32 * @type {string}
33 */
34 this.path_ = null;
35
36 /**
37 * The name of the file at the |path_|.
38 * @type {string}
39 */
40 this.name_ = null;
41 this.setPath(path);
42
43 /**
44 * Whether or not the last component is editable.
45 * @type {bool}
46 */
47 this.editableLastComponent_ = editLastComponent;
48
49 /**
50 * Control UI for the name component of the path.
51 * @type {goog.ui.Control}
52 */
53 this.nameControl_ = null;
54
55 /**
56 * Event Handler
57 * @type {goog.events.EventHandler}
58 */
59 this.eh_ = new goog.events.EventHandler();
60 };
61 goog.inherits(armadillo.PathControl, goog.ui.Component);
62
63 /**
64 * Disposer
65 * @protected
66 */
67 armadillo.PathControl.prototype.disposeInternal = function() {
68 armadillo.PathControl.superClass_.disposeInternal.call(this);
69 this.nameControl_ = null;
70
71 this.eh_.dispose();
72 this.eh_ = null;
73 };
74
75 /**
76 * Sets the path.
77 * @param {string} path
78 */
79 armadillo.PathControl.prototype.setPath = function(path) {
80 this.path_ = app.stripLastPathComponent(path);
81 this.name_ = path.substr(this.path_.length);
82 console.log(this.path_ + ' = ' + this.name_);
83 };
84
85 /**
86 * Gets the new path.
87 * @returns {string}
88 */
89 armadillo.PathControl.prototype.getPath = function() {
90 return app.joinPath(this.path_, this.name_);
91 };
92
93 /**
94 * Gets the name control.
95 * @returns {goog.ui.Control}
96 */
97 armadillo.PathControl.prototype.getNameControl = function() {
98 return this.nameControl_;
99 };
100
101 /**
102 * Creates a new path control object.
103 */
104 armadillo.PathControl.prototype.createDom = function() {
105 this.decorateInternal(this.dom_.createElement('div'));
106 };
107
108 /**
109 * @inheritDoc
110 */
111 armadillo.PathControl.prototype.canDecorate = function() {
112 return true;
113 };
114
115 /**
116 * Decorates the given element into a path control.
117 * @param {Element} element
118 */
119 armadillo.PathControl.prototype.decorateInternal = function(element) {
120 this.element_ = element;
121 var components = this.path_.split('/');
122
123 // If this is an item that lives at the root, generate a special node for
124 // moving between items at the top level.
125 components[0] = '/';
126
127 // If the last component is emtpy, do not use it because it means a directory
128 // is being moved.
129 if (components[components.length - 1] == '') {
130 goog.array.removeAt(components, components.length - 1);
131 }
132
133 var path = '';
134 goog.array.forEach(components, function (part, i) {
135 this.addChild(this.createComponentNode_(path, part), true);
136 path = app.joinPath(path, part);
137 }, this);
138
139 if (this.editableLastComponent_) {
140 var attrs = {
141 'type' : 'text',
142 'name' : 'pathName',
143 'value' : this.name_
144 };
145 this.nameControl_ = new goog.ui.Control(this.dom_.createDom('input', attrs),
146 new armadillo.PathControl.NameControlRenderer_());
147 this.nameControl_.setAllowTextSelection(true);
148 this.nameControl_.setHandleMouseEvents(true);
149 this.addChild(this.nameControl_, true);
150
151 this.eh_.listen(this.nameControl_.getElement(), goog.events.EventType.CHANGE,
152 this.nameChanged_, false, this);
153 this.eh_.listen(this.nameControl_.getElement(), goog.events.EventType.KEYDOWN,
154 this.nameChanged_, false, this);
155 } else {
156 this.nameControl_ = new goog.ui.Control(this.name_);
157 this.addChild(this.nameControl_, true);
158 }
159 goog.dom.classes.add(this.nameControl_.getElement(), 'goog-inline-block');
160 };
161
162 /**
163 * @inheritDoc
164 */
165 armadillo.PathControl.prototype.enterDocument = function() {
166 console.log('enter document ' + this.nameControl_.getElement());
167 this.nameControl_.getElement().focus();
168 };
169
170 /**
171 * Creates a node for a single path component.
172 * @param {string} path The path up to this point.
173 * @param {string} name The current component after |path|.
174 */
175 armadillo.PathControl.prototype.createComponentNode_ = function(path, name) {
176 var menu = new goog.ui.FilteredMenu();
177 menu.setFilterLabel(name);
178 menu.setAllowMultiple(false);
179 menu.setOpenFollowsHighlight(true);
180 goog.events.listen(menu, goog.ui.Component.EventType.ACTION,
181 this.componentChanged_, false, this);
182 this.fetchMenuContents_(path, name, menu);
183
184 var button = new goog.ui.MenuButton(name, menu, null, this.dom_);
185 button.setFocusablePopupMenu(true);
186 button.setScrollOnOverflow(true);
187 button.setVisible(true);
188 return button;
189 };
190
191 /**
192 * Queries the back-end for all the items at a given path and attaches them to
193 * the given menu.
194 * @param {string} path The path to get a list of items in
195 * @param {string} name The name to select
196 * @param {goog.ui.Menu} The menu to attach items to
197 */
198 armadillo.PathControl.prototype.fetchMenuContents_ = function(path, name, menu) {
199 var callback = function(e) {
200 var data = e.target.getResponseJson();
201 if (data['error']) {
202 app.showError(data['message']);
203 return;
204 }
205 if (path == '') {
206 // If this is the root path element, make sure the root is accessible for
207 // moving items.
208 goog.array.insertAt(data, '/', 0);
209 }
210 goog.array.forEach(data, function (caption) {
211 // It only makes sense to be able to move into directories.
212 if (!app.isDirectory(caption)) {
213 return;
214 }
215 var item = new goog.ui.MenuItem(caption);
216 item.setValue(app.joinPath(path, name, caption));
217 menu.addItem(item);
218 if (caption == name) {
219 menu.setHighlighted(item);
220 }
221 });
222 };
223 app.sendRequest('list', {'path' : app.joinPath(path, name)}, callback);
224 };
225
226 /**
227 * Handler for changing a component of the control.
228 * @param {Event} e
229 */
230 armadillo.PathControl.prototype.componentChanged_ = function(e) {
231 console.log(e.target.getValue());
232 this.path_ = e.target.getValue();
233 this.removeChildren(true);
234 this.decorateInternal(this.element_);
235 };
236
237 /**
238 * Handler for changing the editable name component.
239 * @param {Event} e
240 */
241 armadillo.PathControl.prototype.nameChanged_ = function(e) {
242 // TODO: assert(this.editableLastComponent_)
243 console.log('new name = ' + e.target.value);
244 this.name_ = e.target.value;
245 e.stopPropagation();
246 return true;
247 };
248
249 /**
250 * Renderer for the Name Control of the Path Control
251 * @constructor_
252 */
253 armadillo.PathControl.NameControlRenderer_ = function() {
254 goog.ui.ControlRenderer.call(this);
255 };
256 goog.inherits(armadillo.PathControl.NameControlRenderer_, goog.ui.ControlRenderer);
257
258 armadillo.PathControl.NameControlRenderer_.prototype.createDom = function(control) {
259 var content = control.getContent();
260 if (content instanceof HTMLElement) {
261 return content;
262 }
263 return armadillo.PathControl.NameControlRenderer_.superClass_.createDom.call(this, control);
264 };