r20517: re-add cleaned-up webapps
[sfrench/samba-autobuild/.git] / webapps / qooxdoo-0.6.3-sdk / frontend / framework / source / class / qx / ui / treefullcontrol / AbstractTreeElement.js
1 /* ************************************************************************
2
3    qooxdoo - the new era of web development
4
5    http://qooxdoo.org
6
7    Copyright:
8      2004-2006 by 1&1 Internet AG, Germany, http://www.1and1.org
9      2006 by Derrell Lipman
10
11    License:
12      LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
13
14    Authors:
15      * Sebastian Werner (wpbasti)
16      * Andreas Ecker (ecker)
17      * Derrell Lipman (derrell)
18
19 ************************************************************************ */
20
21 /* ************************************************************************
22
23 #module(ui_treefullcontrol)
24
25 ************************************************************************ */
26
27 qx.OO.defineClass("qx.ui.treefullcontrol.AbstractTreeElement", qx.ui.layout.BoxLayout,
28 function(treeRowStructure)
29 {
30   if (this.classname == qx.ui.treefullcontrol.AbstractTreeElement.ABSTRACT_CLASS) {
31     throw new Error("Please omit the usage of qx.ui.treefullcontrol.AbstractTreeElement directly. Choose between qx.ui.treefullcontrol.TreeFolder, qx.ui.treefullcontrol.TreeFolderSimple, qx.ui.treefullcontrol.TreeFile and qx.ui.treefullcontrol.TreeFileSimple instead!");
32   }
33
34   if (treeRowStructure !== qx.ui.treefullcontrol.TreeRowStructure.getInstance())
35   {
36     throw new Error("A qx.ui.treefullcontrol.TreeRowStructure parameter is required.");
37   }
38
39   // Precreate subwidgets
40   this._indentObject = treeRowStructure._indentObject;
41   this._iconObject = treeRowStructure._iconObject;
42   this._labelObject = treeRowStructure._labelObject;
43
44   // Make anonymous
45   this._indentObject.setAnonymous(true);
46   this._iconObject.setAnonymous(true);
47   this._labelObject.setAnonymous(true);
48
49   // Behaviour and Hard Styling
50   this._labelObject.setSelectable(false);
51   this._labelObject.setStyleProperty("lineHeight",
52                                      "100%");
53
54   qx.ui.layout.BoxLayout.call(this, "horizontal");
55
56   if (qx.util.Validation.isValid(treeRowStructure._label)) {
57     this.setLabel(treeRowStructure._label);
58   }
59
60   // Prohibit selection
61   this.setSelectable(false);
62
63   // Base URL used for indent images
64   this.BASE_URI = qx.manager.object.AliasManager.getInstance().resolvePath("widget/tree/");
65
66   /*
67    * Add all of the objects which are to be in the horizontal layout.
68    */
69   for (var i = 0; i < treeRowStructure._fields.length; i++)
70   {
71     this.add(treeRowStructure._fields[i]);
72   }
73
74   // Set Icons
75   if ((treeRowStructure._icons.unselected != null) &&
76       (qx.util.Validation.isValidString(treeRowStructure._icons.unselected))) {
77     this.setIcon(treeRowStructure._icons.unselected);
78     this.setIconSelected(treeRowStructure._icons.unselected);
79   }
80   if ((treeRowStructure._icons.selected != null) &&
81       (qx.util.Validation.isValidString(treeRowStructure._icons.selected))) {
82     this.setIconSelected(treeRowStructure._icons.selected);
83   }
84
85   // Setup initial icon
86   this._iconObject.setSource(this._evalCurrentIcon());
87
88   // Set Appearance
89   this._iconObject.setAppearance("tree-element-icon");
90   this._labelObject.setAppearance("tree-element-label");
91
92   // Register event listeners
93   this.addEventListener("mousedown", this._onmousedown);
94   this.addEventListener("mouseup", this._onmouseup);
95 });
96
97 qx.ui.treefullcontrol.AbstractTreeElement.ABSTRACT_CLASS = "qx.ui.treefullcontrol.AbstractTreeElement";
98
99
100
101
102 /*
103 ---------------------------------------------------------------------------
104   PROPERTIES
105 ---------------------------------------------------------------------------
106 */
107
108 qx.OO.changeProperty({ name : "appearance",
109                        type : "string",
110                        defaultValue : "tree-element"
111                      });
112
113 /*!
114   The icons
115 */
116 qx.OO.addProperty({ name : "icon",
117                     type : "string"
118                   });
119
120 qx.OO.addProperty({ name : "iconSelected",
121                     type : "string"
122                   });
123
124 /*!
125   The label/caption/text of the qx.ui.basic.Atom instance
126 */
127 qx.OO.addProperty({ name : "label",
128                     type : "string"
129                   });
130
131 /*!
132   Selected property
133 */
134 qx.OO.addProperty({ name : "selected",
135                     type : "boolean",
136                     defaultValue : false
137                   });
138
139
140
141
142
143
144 /*
145 ---------------------------------------------------------------------------
146   MODIFIER
147 ---------------------------------------------------------------------------
148 */
149
150 qx.Proto._modifyLabel = function(propValue, propOldValue, propData)
151 {
152   if (this._labelObject) {
153     this._labelObject.setHtml(propValue);
154   }
155
156   return true;
157 }
158
159 qx.Proto._modifySelected = function(propValue, propOldValue, propData)
160 {
161   if (propValue) {
162     this.addState("selected");
163     this._labelObject.addState("selected");
164   } else {
165     this.removeState("selected");
166     this._labelObject.removeState("selected");
167   }
168
169   var vTree = this.getTree();
170   if (!vTree._fastUpdate ||
171       (propOldValue && vTree._oldItem == this)) {
172     this._iconObject.setSource(this._evalCurrentIcon());
173
174     if (propValue) {
175       this._iconObject.addState("selected");
176     } else {
177       this._iconObject.removeState("selected");
178     }
179   }
180
181   var vManager = this.getTree().getManager();
182
183   if (propOldValue && vManager.getSelectedItem() == this)
184   {
185     vManager.deselectAll();
186   }
187   else if (propValue && vManager.getSelectedItem() != this)
188   {
189     vManager.setSelectedItem(this);
190   }
191
192   return true;
193 }
194
195 qx.Proto._evalCurrentIcon = function()
196 {
197   if (this.getSelected() && this.getIconSelected()) {
198     return this.getIconSelected();
199   } else {
200     return this.getIcon() || "icon/16/file-new.png";
201   }
202 }
203
204
205
206
207
208 /*
209 ---------------------------------------------------------------------------
210   UTILITIES
211 ---------------------------------------------------------------------------
212 */
213
214 qx.Proto.getParentFolder = function()
215 {
216   try {
217     return this.getParent().getParent();
218   } catch(ex) {}
219
220   return null;
221 }
222
223 qx.Proto.getLevel = function()
224 {
225   var vParentFolder = this.getParentFolder();
226   return vParentFolder ? vParentFolder.getLevel() + 1 : null;
227 }
228
229 qx.Proto.getTree = function()
230 {
231   var vParentFolder = this.getParentFolder();
232   return vParentFolder ? vParentFolder.getTree() : null;
233 }
234
235 qx.Proto.getIndentObject = function() {
236   return this._indentObject;
237 }
238
239 qx.Proto.getIconObject = function() {
240   return this._iconObject;
241 }
242
243 qx.Proto.getLabelObject = function() {
244   return this._labelObject;
245 }
246
247 /**
248  * Obtain the entire hierarchy of labels from the root down to the current
249  * node.
250  *
251  * @param
252  *   vArr -
253  *     When called by the user, arr should typically be an empty array.  Each
254  *     level from the current node upwards will push its label onto the array.
255  */
256 qx.Proto.getHierarchy = function(vArr) {
257   // Add our label to the array
258   if (this._labelObject) {
259     vArr.unshift(this._labelObject.getHtml());
260   }
261
262   // Get the parent folder
263   var parent = this.getParentFolder();
264
265   // If it exists...
266   if (parent) {
267     // ... then add it and its ancestors' labels to the array.
268     parent.getHierarchy(vArr);
269   }
270
271   // Give 'em what they came for
272   return vArr;
273 }
274
275
276
277
278 /*
279 ---------------------------------------------------------------------------
280   QUEUE HANDLING
281 ---------------------------------------------------------------------------
282 */
283
284 qx.Proto.addToTreeQueue = function()
285 {
286   var vTree = this.getTree();
287   if (vTree) {
288     vTree.addChildToTreeQueue(this);
289   }
290 }
291
292 qx.Proto.removeFromTreeQueue = function()
293 {
294   var vTree = this.getTree();
295   if (vTree) {
296     vTree.removeChildFromTreeQueue(this);
297   }
298 }
299
300 qx.Proto.addToCustomQueues = function(vHint)
301 {
302   this.addToTreeQueue();
303
304   qx.ui.layout.BoxLayout.prototype.addToCustomQueues.call(this, vHint);
305 }
306
307 qx.Proto.removeFromCustomQueues = function(vHint)
308 {
309   this.removeFromTreeQueue();
310
311   qx.ui.layout.BoxLayout.prototype.removeFromCustomQueues.call(this, vHint);
312 }
313
314
315
316
317
318
319
320
321 /*
322 ---------------------------------------------------------------------------
323   DISPLAYBLE HANDLING
324 ---------------------------------------------------------------------------
325 */
326
327 qx.Proto._modifyParent = function(propValue, propOldValue, propData)
328 {
329   qx.ui.layout.BoxLayout.prototype._modifyParent.call(this, propValue, propOldValue, propData);
330
331   // Be sure to update previous folder also if it is closed currently
332   // (plus/minus symbol)
333   if (propOldValue &&
334       !propOldValue.isDisplayable() &&
335       propOldValue.getParent() &&
336       propOldValue.getParent().isDisplayable()) {
337     propOldValue.getParent().addToTreeQueue();
338   }
339
340   // Be sure to update new folder also if it is closed currently
341   // (plus/minus symbol)
342   if (propValue &&
343       !propValue.isDisplayable() &&
344       propValue.getParent() &&
345       propValue.getParent().isDisplayable()) {
346     propValue.getParent().addToTreeQueue();
347   }
348
349   return true;
350 }
351
352 qx.Proto._handleDisplayableCustom = function(vDisplayable, vParent, vHint)
353 {
354   qx.ui.layout.BoxLayout.prototype._handleDisplayableCustom.call(this,
355                                                                  vDisplayable,
356                                                                  vParent,
357                                                                  vHint);
358
359   if (vHint)
360   {
361     var vParentFolder = this.getParentFolder();
362     var vPreviousParentFolder = this._previousParentFolder;
363
364     if (vPreviousParentFolder)
365     {
366       if (this._wasLastVisibleChild)
367       {
368         vPreviousParentFolder._updateIndent();
369       }
370       else if (!vPreviousParentFolder.hasContent())
371       {
372         vPreviousParentFolder.addToTreeQueue();
373       }
374     }
375
376     if (vParentFolder &&
377         vParentFolder.isDisplayable() &&
378         vParentFolder._initialLayoutDone) {
379       vParentFolder.addToTreeQueue();
380     }
381
382     if (this.isLastVisibleChild())
383     {
384       var vPrev = this.getPreviousVisibleSibling();
385
386       if (vPrev &&
387           vPrev instanceof qx.ui.treefullcontrol.AbstractTreeElement) {
388         vPrev._updateIndent();
389       }
390     }
391
392     if (vDisplayable) {
393       this._updateIndent();
394     }
395   }
396 }
397
398
399
400
401
402
403
404 /*
405 ---------------------------------------------------------------------------
406   EVENT LISTENERS
407 ---------------------------------------------------------------------------
408 */
409
410 qx.Proto._onmousedown = function(e)
411 {
412   this.getTree().getManager().handleMouseDown(this, e);
413   e.stopPropagation();
414 }
415
416 qx.Proto._onmouseup = qx.util.Return.returnTrue;
417
418
419
420
421
422 /*
423 ---------------------------------------------------------------------------
424   TREE FLUSH
425 ---------------------------------------------------------------------------
426 */
427
428 qx.Proto.flushTree = function()
429 {
430   // store information for update process
431   this._previousParentFolder = this.getParentFolder();
432   this._wasLastVisibleChild = this.isLastVisibleChild();
433
434   // generate html for indent area
435   var vLevel = this.getLevel();
436   var vTree = this.getTree();
437   var vImage;
438   var vHtml = [];
439   var vCurrentObject = this;
440   var vMinLevel = 0;
441   var vMaxLevel = vLevel;
442
443   // If we're displaying the open/close button for the root node (normal)...
444   if (vTree.getRootOpenClose()) {
445     // ... then we need one more level
446     vMaxLevel = vLevel + 1;
447   }
448
449   // If we're not displaying the root node (creating virtual roots)...
450   if (vTree.hideNode()) {
451     // ... then start one level higher
452     vMinLevel = 1;
453   }
454
455   for (var i=vMinLevel; i<vMaxLevel; i++)
456   {
457     vImage = vCurrentObject.getIndentSymbol(vTree.getUseTreeLines(),
458                                             i,
459                                             vMinLevel,
460                                             vMaxLevel);
461
462     if (vImage)
463     {
464       vHtml.push("<img style=\"position:absolute;top:0px;left:");
465
466       // location of image; Root's image could be left of margin (invisible)
467       vHtml.push((vMaxLevel-i-1) * 19);
468
469       vHtml.push("px\" src=\"");
470       vHtml.push(this.BASE_URI);
471       vHtml.push(vImage);
472       vHtml.push(".");
473       vHtml.push("gif");
474       vHtml.push("\" />");
475     }
476
477     vCurrentObject = vCurrentObject.getParentFolder();
478   }
479
480   this._indentObject.setHtml(vHtml.join(""));
481   this._indentObject.setWidth((vMaxLevel - vMinLevel) * 19);
482 }
483
484
485
486
487
488
489
490
491
492
493 /*
494 ---------------------------------------------------------------------------
495   DISPOSER
496 ---------------------------------------------------------------------------
497 */
498
499 qx.Proto.dispose = function()
500 {
501   if (this.getDisposed()) {
502     return true;
503   }
504
505   if (this._indentObject)
506   {
507     this._indentObject.dispose();
508     this._indentObject = null;
509   }
510
511   if (this._iconObject)
512   {
513     this._iconObject.dispose();
514     this._iconObject = null;
515   }
516
517   if (this._labelObject)
518   {
519     this._labelObject.dispose();
520     this._labelObject = null;
521   }
522
523   this._previousParentFolder = null;
524
525   this.removeEventListener("mousedown", this._onmousedown);
526   this.removeEventListener("mouseup", this._onmouseup);
527
528   return qx.ui.layout.BoxLayout.prototype.dispose.call(this);
529 }