r20517: re-add cleaned-up webapps
[kai/samba.git] / webapps / qooxdoo-0.6.3-sdk / frontend / framework / source / class / qx / ui / splitpane / SplitPane.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
10    License:
11      LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
12
13    Authors:
14      * Volker Pauli (vpauli)
15      * Sebastian Werner (wpbasti)
16      * Carsten Lergenmueller (carstenL)
17
18  ************************************************************************ */
19
20 /* ************************************************************************
21
22 #module(ui_splitpane)
23
24  ************************************************************************ */
25
26
27 /**
28  * Creates a new instance of a SplitPane. It allows the user to dynamically resize
29  * the areas dropping the border between.
30  *
31  * new qx.ui.splitpane.SplitPane(orientation)
32  * new qx.ui.splitpane.SplitPane(orientation, firstSize, secondSize)
33  *
34  * @param orientation {string} The orientation of the splitpane control. Allowed values are "horizontal" (default) and "vertical". This is the same type as used in {@link qx.ui.layout.BoxLayout#orientation}.
35  * @param firstSize {string} The size of the left (top) pane. Allowed values are any by {@link qx.ui.core.Widget} supported unit.
36  * @param secondSize {string} The size of the right (bottom) pane. Allowed values are any by {@link qx.ui.core.Widget} supported unit.
37  */
38 qx.OO.defineClass("qx.ui.splitpane.SplitPane", qx.ui.layout.CanvasLayout,
39 function(orientation, firstSize, secondSize)
40 {
41   qx.ui.layout.CanvasLayout.call(this);
42
43   // CREATE INNER BOX LAYOUT
44   var box = this._box = new qx.ui.layout.BoxLayout;
45   box.setEdge(0);
46   this.add(box);
47
48   /*
49
50   the splitpane itself is a boxlayout resides on top of a canvas for easier computing of positional values
51
52   ---------------------------------------------------------------------------------------
53   |  canvas                                                                               |
54   |  -----------------------------------------------------------------------------------  |
55   | | box                                                                               | |
56   | | ---------------------------  ---  ----------------------------------------------- | |
57   | | |                         |  | |  |                                             | | |
58   | | | firstArea               |  |s|  | secondArea                                  | | |
59   | | |                         |  |p|  |                                             | | |
60   | | |                         |  |l|  |                                             | | |
61   | | |                         |  |i|  |                                             | | |
62   | | |                         |  |t|  |                                             | | |
63   | | |                         |  |t|  |                                             | | |
64   | | |                         |  |e|  |                                             | | |
65   | | |                         |  |r|  |                                             | | |
66   | | |                         |  | |  |                                             | | |
67   | | ---------------------------  ---  ----------------------------------------------- | |
68   |  -----------------------------------------------------------------------------------  |
69   |                                                                                       |
70   ---------------------------------------------------------------------------------------
71
72   */
73
74   // CREATE SLIDER
75   this._slider = new qx.ui.layout.CanvasLayout;
76   this._slider.setAppearance("splitpane-slider");
77   this._slider.setStyleProperty("fontSize", "0px");
78   this._slider.setStyleProperty("lineHeight", "0px");
79   this._slider.hide();
80   this._slider._pane = this;
81   this.add(this._slider);
82
83   // CREATE SPLITTER
84   this._splitter = new qx.ui.layout.CanvasLayout;
85   this._splitter.setStyleProperty("fontSize", "0px");
86   this._splitter.setStyleProperty("lineHeight", "0px");
87   this._splitter.setAppearance("splitpane-splitter");
88   this._splitter._pane = this;
89
90   // PATCH METHODS
91   this._slider._applyRuntimeLeft = this._splitter._applyRuntimeLeft = this._applyRuntimeLeftWrapper;
92   this._slider._applyRuntimeTop = this._splitter._applyRuntimeTop = this._applyRuntimeTopWrapper;
93
94   // CREATE KNOB
95   this._knob = new qx.ui.basic.Image;
96   this._knob.setAppearance("splitpane-knob");
97   this._knob.setVisibility(false);
98   this.add(this._knob);
99
100   // CREATE AREAS
101   this._firstArea = new qx.ui.layout.CanvasLayout;
102   this._secondArea = new qx.ui.layout.CanvasLayout;
103
104   // FILL BOX
105   box.add(this._firstArea, this._splitter, this._secondArea);
106
107   // APPLY DIMENSIONS
108   this.setFirstSize(firstSize || "1*");
109   this.setSecondSize(secondSize || "1*");
110
111   // APPLY ORIENTATION
112   this.setOrientation(orientation || "horizontal");
113 });
114
115
116
117
118
119
120
121
122
123
124 /*
125 ---------------------------------------------------------------------------
126   PROPERTIES
127 ---------------------------------------------------------------------------
128  */
129
130 /**
131  * Appearance change
132  */
133 qx.OO.changeProperty({ name : "appearance", defaultValue : "splitpane" });
134
135 /**
136  * Show the knob
137  */
138 qx.OO.addProperty({ name : "showKnob", type : "boolean", allowNull : false, defaultValue : false });
139
140 /**
141  * The layout method for the splitpane. If true, the content will updated immediatly.
142  */
143 qx.OO.addProperty({ name : "liveResize", type : "boolean", allowNull : false, defaultValue : false, getAlias : "isLiveResize"});
144
145 /**
146  * The orientation of the splitpane control. Allowed values are "horizontal" (default) and "vertical".
147  */
148 qx.OO.addProperty({ name : "orientation", type : "string", possibleValues : [ "horizontal", "vertical" ] });
149
150 /**
151  * The size of the first (left/top) area.
152  */
153 qx.OO.addProperty({ name : "firstSize" });
154
155 /**
156  * The size of the second (right/bottom) area.
157  */
158 qx.OO.addProperty({ name : "secondSize" });
159
160 /**
161  * Size of the splitter
162  */
163 qx.OO.addProperty({ name : "splitterSize", defaultValue : 4 });
164
165
166
167
168
169
170
171 /*
172 ---------------------------------------------------------------------------
173   PUBLIC METHODS
174 ---------------------------------------------------------------------------
175 */
176
177
178 /**
179  * adds one or more widget(s) to the left pane
180  *
181  *@param widget (qx.ui.core.Parent)
182  */
183 qx.Proto.addLeft = function() {
184   var c = this.getFirstArea();
185   return c.add.apply(c, arguments);
186 }
187
188 /**
189  * adds one or more widget(s) to the top pane
190  *
191  *@param widget (qx.ui.core.Parent)
192  */
193 qx.Proto.addTop = function() {
194   var c = this.getFirstArea();
195   return c.add.apply(c, arguments);
196 }
197
198 /**
199  * adds one or more widget(s) to the right pane
200  *
201  *@param widget (qx.ui.core.Parent)
202  */
203 qx.Proto.addRight = function() {
204   var c = this.getSecondArea();
205   return c.add.apply(c, arguments);
206 }
207
208 /**
209  * adds one or more widget(s) to the bottom pane
210  *
211  *@param widget (qx.ui.core.Parent)
212  */
213 qx.Proto.addBottom = function() {
214   var c = this.getSecondArea();
215   return c.add.apply(c, arguments);
216 }
217
218 /**
219  * Returns the splitter.
220  *
221  * @return {qx.ui.core.Widget} The splitter.
222  */
223 qx.Proto.getSplitter = function() {
224   return this._splitter;
225 }
226
227 /**
228  * Returns the knob.
229  *
230  * @return {qx.ui.core.Widget} The knob.
231  */
232 qx.Proto.getKnob = function() {
233   return this._knob;
234 }
235
236
237
238
239
240
241 /**
242  * Returns the left area (CanvasLayout)
243  *
244  * @return {qx.ui.layout.CanvasLayout}
245  */
246 qx.Proto.getLeftArea = function() {
247   return this.getFirstArea();
248 }
249
250 /**
251  * Returns the top area (CanvasLayout)
252  *
253  * @return {qx.ui.layout.CanvasLayout}
254  */
255 qx.Proto.getTopArea = function() {
256   return this.getFirstArea();
257 }
258
259 /**
260  * Returns the right area (CanvasLayout)
261  *
262  * @return {qx.ui.layout.CanvasLayout}
263  */
264 qx.Proto.getRightArea = function() {
265   return this.getSecondArea();
266 }
267
268 /**
269  * Returns the bottom area (CanvasLayout)
270  *
271  * @return {qx.ui.layout.CanvasLayout}
272  */
273 qx.Proto.getBottomArea = function() {
274   return this.getSecondArea();
275 }
276
277 /**
278  * Returns the first area (CanvasLayout)
279  *
280  * @return {qx.ui.layout.CanvasLayout}
281  */
282 qx.Proto.getFirstArea = function() {
283   return this._firstArea;
284 }
285
286 /**
287  * Returns the second area (CanvasLayout)
288  *
289  * @return {qx.ui.layout.CanvasLayout}
290  */
291 qx.Proto.getSecondArea = function() {
292   return this._secondArea;
293 }
294
295
296
297
298
299
300
301
302
303 /*
304 ---------------------------------------------------------------------------
305   MODIFIER
306 ---------------------------------------------------------------------------
307 */
308
309 qx.Proto._modifyShowKnob = function(propValue, propOldValue, propData)
310 {
311   this._knob.setVisibility(propValue);
312   return true;
313 }
314
315 qx.Proto._modifyOrientation = function(propValue, propOldValue, propData)
316 {
317   // sync orientation to layout
318   this._box.setOrientation(propValue);
319
320   switch(propOldValue)
321   {
322     case "horizontal":
323       // remove old listeners
324       this._splitter.removeEventListener("mousedown", this._onSplitterMouseDownX, this);
325       this._splitter.removeEventListener("mousemove", this._onSplitterMouseMoveX, this);
326       this._splitter.removeEventListener("mouseup", this._onSplitterMouseUpX, this);
327       this._knob.removeEventListener("mousedown", this._onSplitterMouseDownX, this);
328       this._knob.removeEventListener("mousemove", this._onSplitterMouseMoveX, this);
329       this._knob.removeEventListener("mouseup", this._onSplitterMouseUpX, this);
330
331       // reconfigure states
332       this._splitter.removeState("horizontal");
333       this._knob.removeState("horizontal");
334
335       // reset old dimensions
336       this._firstArea.setWidth(null);
337       this._secondArea.setWidth(null);
338       this._splitter.setWidth(null);
339
340       break;
341
342     case "vertical":
343       // remove old listeners
344       this._splitter.removeEventListener("mousedown", this._onSplitterMouseDownY, this);
345       this._splitter.removeEventListener("mousemove", this._onSplitterMouseMoveY, this);
346       this._splitter.removeEventListener("mouseup", this._onSplitterMouseUpY, this);
347       this._knob.removeEventListener("mousedown", this._onSplitterMouseDownY, this);
348       this._knob.removeEventListener("mousemove", this._onSplitterMouseMoveY, this);
349       this._knob.removeEventListener("mouseup", this._onSplitterMouseUpY, this);
350
351       // reconfigure states
352       this._splitter.removeState("vertical");
353       this._knob.removeState("vertical");
354
355       // reset old dimensions
356       this._firstArea.setHeight(null);
357       this._secondArea.setHeight(null);
358       this._splitter.setHeight(null);
359
360       break;
361   }
362
363   switch(propValue)
364   {
365     case "horizontal":
366       // add new listeners
367       this._splitter.addEventListener("mousemove", this._onSplitterMouseMoveX, this);
368       this._splitter.addEventListener("mousedown", this._onSplitterMouseDownX, this);
369       this._splitter.addEventListener("mouseup", this._onSplitterMouseUpX, this);
370       this._knob.addEventListener("mousemove", this._onSplitterMouseMoveX, this);
371       this._knob.addEventListener("mousedown", this._onSplitterMouseDownX, this);
372       this._knob.addEventListener("mouseup", this._onSplitterMouseUpX, this);
373
374       // reconfigure states
375       this._splitter.addState("horizontal");
376       this._knob.addState("horizontal");
377
378       // apply images
379       this._knob.setSource("widget/splitpane/knob-horizontal.png");
380
381       break;
382
383     case "vertical":
384       // add new listeners
385       this._splitter.addEventListener("mousedown", this._onSplitterMouseDownY, this);
386       this._splitter.addEventListener("mousemove", this._onSplitterMouseMoveY, this);
387       this._splitter.addEventListener("mouseup", this._onSplitterMouseUpY, this);
388       this._knob.addEventListener("mousedown", this._onSplitterMouseDownY, this);
389       this._knob.addEventListener("mousemove", this._onSplitterMouseMoveY, this);
390       this._knob.addEventListener("mouseup", this._onSplitterMouseUpY, this);
391
392       // reconfigure states
393       this._splitter.addState("vertical");
394       this._knob.addState("vertical");
395
396       // apply images
397       this._knob.setSource("widget/splitpane/knob-vertical.png");
398
399       break;
400   }
401
402   // apply new dimensions
403   this._syncFirstSize();
404   this._syncSecondSize();
405   this._syncSplitterSize();
406
407   return true;
408 };
409
410 qx.Proto._modifyFirstSize = function(propValue, propOldValue, propData)
411 {
412   this._syncFirstSize();
413   return true;
414 }
415
416 qx.Proto._modifySecondSize = function(propValue, propOldValue, propData)
417 {
418   this._syncSecondSize();
419   return true;
420 }
421
422 qx.Proto._modifySplitterSize = function(propValue, propOldValue, propData)
423 {
424   this._syncSplitterSize();
425   return true;
426 }
427
428 qx.Proto._syncFirstSize = function()
429 {
430   switch(this.getOrientation())
431   {
432     case "horizontal":
433       this._firstArea.setWidth(this.getFirstSize());
434       break;
435
436     case "vertical":
437       this._firstArea.setHeight(this.getFirstSize());
438       break;
439   }
440 }
441
442 qx.Proto._syncSecondSize = function()
443 {
444   switch(this.getOrientation())
445   {
446     case "horizontal":
447       this._secondArea.setWidth(this.getSecondSize());
448       break;
449
450     case "vertical":
451       this._secondArea.setHeight(this.getSecondSize());
452       break;
453   }
454 }
455
456 qx.Proto._syncSplitterSize = function()
457 {
458   switch(this.getOrientation())
459   {
460     case "horizontal":
461       this._splitter.setWidth(this.getSplitterSize());
462       break;
463
464     case "vertical":
465       this._splitter.setHeight(this.getSplitterSize());
466       break;
467   }
468 }
469
470
471
472
473
474
475
476 /*
477 ---------------------------------------------------------------------------
478   EVENTS
479 ---------------------------------------------------------------------------
480 */
481
482 /**
483  * Initializes drag session in case of a mousedown event on splitter in a horizontal splitpane.
484  *
485  * @param e {qx.event.MouseEvent} The event itself.
486  */
487 qx.Proto._onSplitterMouseDownX = function(e)
488 {
489   if (!e.isLeftButtonPressed()) {
490     return;
491   }
492
493   this._commonMouseDown();
494
495   // activate global cursor
496   this.getTopLevelWidget().setGlobalCursor("col-resize");
497   this._slider.addState("dragging");
498   this._knob.addState("dragging");
499
500   // initialize the drag session
501   this._dragMin = qx.dom.Location.getPageInnerLeft(this._box.getElement());
502   this._dragMax = this._dragMin + this._box.getInnerWidth() - this._splitter.getBoxWidth();
503   this._dragOffset = e.getPageX() - qx.dom.Location.getPageBoxLeft(this._splitter.getElement());
504 }
505
506 /**
507  * Initializes drag session in case of a mousedown event on splitter in a vertical splitpane.
508  *
509  * @param e {qx.event.MouseEvent} The event itself.
510  */
511 qx.Proto._onSplitterMouseDownY = function(e)
512 {
513   if (!e.isLeftButtonPressed()) {
514     return;
515   }
516
517   this._commonMouseDown();
518
519   // activate global cursor
520   this.getTopLevelWidget().setGlobalCursor("row-resize");
521   this._slider.addState("dragging");
522   this._knob.addState("dragging");
523
524   // initialize the drag session
525   // dragStart = position of layout + mouse offset on splitter
526   this._dragMin = qx.dom.Location.getPageInnerTop(this._box.getElement());
527   this._dragMax = this._dragMin + this._box.getInnerHeight() - this._splitter.getBoxHeight();
528   this._dragOffset = e.getPageY() - qx.dom.Location.getPageBoxTop(this._splitter.getElement());
529 }
530
531 qx.Proto._commonMouseDown = function()
532 {
533   // enable capturing
534   this._splitter.setCapture(true);
535
536   // initialize the slider
537   if(!this.isLiveResize())
538   {
539     this._slider.setLeft(this._splitter.getOffsetLeft());
540     this._slider.setTop(this._splitter.getOffsetTop());
541     this._slider.setWidth(this._splitter.getBoxWidth());
542     this._slider.setHeight(this._splitter.getBoxHeight());
543
544     this._slider.show();
545   }
546 }
547
548
549
550
551
552
553
554
555 /**
556  * Move the splitter in case of a mousemove event on splitter in a horizontal splitpane.
557  *
558  * @param e {qx.event.MouseEvent} The event itself.
559  */
560 qx.Proto._onSplitterMouseMoveX = function(e)
561 {
562   if (!this._splitter.getCapture()) {
563     return;
564   }
565
566   this.isLiveResize() ? this._syncX(e) : this._slider._applyRuntimeLeft(this._normalizeX(e));
567   e.preventDefault();
568 }
569
570 /**
571  * Move the splitter in case of a mousemove event on splitter in a vertical splitpane.
572  *
573  * @param e {qx.event.MouseEvent} The event itself.
574  */
575 qx.Proto._onSplitterMouseMoveY = function(e)
576 {
577   if (!this._splitter.getCapture()) {
578     return;
579   }
580
581   this.isLiveResize() ? this._syncY(e) : this._slider._applyRuntimeTop(this._normalizeY(e));
582   e.preventDefault();
583 }
584
585
586
587
588
589
590
591 /**
592  * Ends the drag session and computes the new dimensions of panes in case of a mouseup event on splitter in a horizontal splitpane.
593  *
594  * @param e {qx.event.MouseEvent} The event itself.
595  */
596 qx.Proto._onSplitterMouseUpX = function(e)
597 {
598   if (!this._splitter.getCapture()) {
599     return;
600   }
601
602   if(!this.isLiveResize()) {
603     this._syncX(e);
604   }
605
606   this._commonMouseUp();
607 }
608
609 /**
610  * Ends the drag session and computes the new dimensions of panes in case of a mouseup event on splitter in a vertical splitpane.
611  *
612  * @param e {qx.event.MouseEvent} The event itself.
613  */
614 qx.Proto._onSplitterMouseUpY = function(e)
615 {
616   if (!this._splitter.getCapture()) {
617     return;
618   }
619
620   if(!this.isLiveResize()) {
621     this._syncY(e);
622   }
623
624   this._commonMouseUp();
625 }
626
627 qx.Proto._commonMouseUp = function()
628 {
629   // hide helpers
630   this._slider.hide();
631
632   // disable capturing
633   this._splitter.setCapture(false);
634
635   // reset the global cursor
636   this.getTopLevelWidget().setGlobalCursor(null);
637
638   // cleanup dragsession
639   this._slider.removeState("dragging");
640   this._knob.removeState("dragging");
641 }
642
643 qx.Proto._syncX = function(e)
644 {
645   var first = this._normalizeX(e);
646   var second = this._box.getInnerWidth() - this._splitter.getBoxWidth() - first;
647
648   this._syncCommon(first, second);
649 }
650
651 qx.Proto._syncY = function(e)
652 {
653   var first = this._normalizeY(e);
654   var second = this._box.getInnerHeight() - this._splitter.getBoxHeight() - first;
655
656   this._syncCommon(first, second);
657 }
658
659 qx.Proto._syncCommon = function(first, second)
660 {
661   this.setFirstSize(first + "*");
662   this.setSecondSize(second + "*");
663 }
664
665 qx.Proto._normalizeX = function(e) {
666   return qx.lang.Number.limit(e.getPageX() - this._dragOffset, this._dragMin, this._dragMax) - this._dragMin;
667 }
668
669 qx.Proto._normalizeY = function(e) {
670   return qx.lang.Number.limit(e.getPageY() - this._dragOffset, this._dragMin, this._dragMax) - this._dragMin;
671 }
672
673 qx.Proto._applyRuntimeLeftWrapper = function(v)
674 {
675   if (this._pane.getOrientation() == "horizontal") {
676     this._pane._knob._applyRuntimeLeft(v);
677   }
678
679   return this.constructor.prototype._applyRuntimeLeft.call(this, v);
680 }
681
682 qx.Proto._applyRuntimeTopWrapper = function(v)
683 {
684   if (this._pane.getOrientation() == "vertical") {
685     this._pane._knob._applyRuntimeTop(v);
686   }
687
688   return this.constructor.prototype._applyRuntimeTop.call(this, v);
689 }
690
691
692
693
694
695 /*
696 ------------------------------------------------------------------------------------
697   DISPOSER
698 ------------------------------------------------------------------------------------
699  */
700
701 /**
702  * Garbage collection
703  */
704 qx.Proto.dispose = function()
705 {
706   if (this.getDisposed()) {
707     return true;
708   }
709
710   if(this._firstArea)
711   {
712     this._firstArea.dispose();
713     this._firstArea = null;
714   }
715
716   if(this._secondArea)
717   {
718     this._secondArea.dispose();
719     this._secondArea = null;
720   }
721
722   if (this._splitter)
723   {
724     this._splitter.removeEventListener("mousedown", this._onSplitterMouseDownX, this);
725     this._splitter.removeEventListener("mouseup", this._onSplitterMouseMoveX, this);
726     this._splitter.removeEventListener("mousemove", this._onSplitterMouseUpX, this);
727
728     this._splitter.removeEventListener("mousedown", this._onSplitterMouseDownY, this);
729     this._splitter.removeEventListener("mouseup", this._onSplitterMouseMoveY, this);
730     this._splitter.removeEventListener("mousemove", this._onSplitterMouseUpY, this);
731
732     this._splitter.dispose();
733     this._splitter._pane = null;
734     this._splitter = null;
735   }
736
737   if (this._slider)
738   {
739     this._slider.dispose();
740     this._slider._pane = null;
741     this._slider = null;
742   }
743
744   if (this._knob)
745   {
746     this._knob.removeEventListener("mousedown", this._onSplitterMouseDownX, this);
747     this._knob.removeEventListener("mouseup", this._onSplitterMouseMoveX, this);
748     this._knob.removeEventListener("mousemove", this._onSplitterMouseUpX, this);
749
750     this._knob.removeEventListener("mousedown", this._onSplitterMouseDownY, this);
751     this._knob.removeEventListener("mouseup", this._onSplitterMouseMoveY, this);
752     this._knob.removeEventListener("mousemove", this._onSplitterMouseUpY, this);
753
754     this._knob.dispose();
755     this._knob = null;
756   }
757
758   return qx.ui.layout.BoxLayout.prototype.dispose.call(this);
759 }