1 /* ************************************************************************
3 qooxdoo - the new era of web development
8 2004-2007 1&1 Internet AG, Germany, http://www.1and1.org
11 LGPL: http://www.gnu.org/licenses/lgpl.html
12 EPL: http://www.eclipse.org/org/documents/epl-v10.php
13 See the LICENSE file in the project's top-level directory for details.
16 * Sebastian Werner (wpbasti)
17 * Andreas Ecker (ecker)
19 ************************************************************************ */
21 /* ************************************************************************
25 ************************************************************************ */
28 * This class represents a selection and manage incoming events for widgets
29 * which need selection support.
31 * @event changeSelection {qx.event.type.DataEvent} sets the data property of the event object to an arryas of selected items.
33 qx.OO.defineClass("qx.manager.selection.SelectionManager", qx.core.Target,
34 function(vBoundedWidget)
36 qx.core.Target.call(this);
38 this._selectedItems = new qx.type.Selection(this);
40 if (vBoundedWidget != null) {
41 this.setBoundedWidget(vBoundedWidget);
48 ---------------------------------------------------------------------------
50 ---------------------------------------------------------------------------
54 This contains the currently assigned widget (qx.ui.form.List, ...)
56 qx.OO.addProperty({ name : "boundedWidget", type : "object" });
59 Should multiple selection be allowed?
61 qx.OO.addProperty({ name : "multiSelection", type : "boolean", defaultValue : true });
64 Enable drag selection?
66 qx.OO.addProperty({ name : "dragSelection", type : "boolean", defaultValue : true });
69 Should the user be able to select
71 qx.OO.addProperty({ name : "canDeselect", type : "boolean", defaultValue : true });
74 Should a change event be fired?
76 qx.OO.addProperty({ name : "fireChange", type : "boolean", defaultValue : true });
79 The current anchor in range selections.
81 qx.OO.addProperty({ name : "anchorItem", type : "object" });
84 The last selected item
86 qx.OO.addProperty({ name : "leadItem", type : "object" });
91 qx.OO.addProperty({ name : "multiColumnSupport", type : "boolean", defaultValue : false });
99 ---------------------------------------------------------------------------
101 ---------------------------------------------------------------------------
104 qx.Proto._modifyAnchorItem = function(propValue, propOldValue, propData)
107 this.renderItemAnchorState(propOldValue, false);
111 this.renderItemAnchorState(propValue, true);
117 qx.Proto._modifyLeadItem = function(propValue, propOldValue, propData)
120 this.renderItemLeadState(propOldValue, false);
124 this.renderItemLeadState(propValue, true);
136 ---------------------------------------------------------------------------
137 MAPPING TO BOUNDED WIDGET
138 ---------------------------------------------------------------------------
141 qx.Proto._getFirst = function() {
142 return this.getBoundedWidget().getFirstVisibleChild();
145 qx.Proto._getLast = function() {
146 return this.getBoundedWidget().getLastVisibleChild();
149 qx.Proto.getFirst = function()
151 var vItem = this._getFirst();
153 return vItem.isEnabled() ? vItem : this.getNext(vItem);
157 qx.Proto.getLast = function()
159 var vItem = this._getLast();
161 return vItem.isEnabled() ? vItem : this.getPrevious(vItem);
165 qx.Proto.getItems = function() {
166 return this.getBoundedWidget().getChildren();
169 qx.Proto.getNextSibling = function(vItem) {
170 return vItem.getNextSibling();
173 qx.Proto.getPreviousSibling = function(vItem) {
174 return vItem.getPreviousSibling();
177 qx.Proto.getNext = function(vItem)
181 vItem = this.getNextSibling(vItem);
187 if (this.getItemEnabled(vItem)) {
195 qx.Proto.getPrevious = function(vItem)
199 vItem = this.getPreviousSibling(vItem);
205 if (this.getItemEnabled(vItem)) {
213 qx.Proto.isBefore = function(vItem1, vItem2)
215 var cs = this.getItems();
216 return cs.indexOf(vItem1) < cs.indexOf(vItem2);
219 qx.Proto.isEqual = function(vItem1, vItem2) {
220 return vItem1 == vItem2;
226 ---------------------------------------------------------------------------
227 MAPPING TO ITEM PROPERTIES
228 ---------------------------------------------------------------------------
231 qx.Proto.getItemHashCode = function(vItem) {
232 return vItem.toHashCode();
240 ---------------------------------------------------------------------------
241 MAPPING TO ITEM DIMENSIONS
242 ---------------------------------------------------------------------------
245 qx.Proto.scrollItemIntoView = function(vItem, vTopLeft) {
246 vItem.scrollIntoView(vTopLeft);
249 qx.Proto.getItemLeft = function(vItem) {
250 return vItem.getOffsetLeft();
253 qx.Proto.getItemTop = function(vItem) {
254 return vItem.getOffsetTop();
257 qx.Proto.getItemWidth = function(vItem) {
258 return vItem.getOffsetWidth();
261 qx.Proto.getItemHeight = function(vItem) {
262 return vItem.getOffsetHeight();
265 qx.Proto.getItemEnabled = function(vItem) {
266 return vItem.getEnabled();
275 ---------------------------------------------------------------------------
277 ---------------------------------------------------------------------------
280 qx.Proto.renderItemSelectionState = function(vItem, vIsSelected)
282 vIsSelected ? vItem.addState("selected") : vItem.removeState("selected");
284 if (vItem.handleStateChange) {
285 vItem.handleStateChange();
289 qx.Proto.renderItemAnchorState = function(vItem, vIsAnchor)
291 vIsAnchor ? vItem.addState("anchor") : vItem.removeState("anchor");
293 if (vItem.handleStateChange != null) {
294 vItem.handleStateChange();
298 qx.Proto.renderItemLeadState = function(vItem, vIsLead)
300 vIsLead ? vItem.addState("lead") : vItem.removeState("lead");
302 if (vItem.handleStateChange != null) {
303 vItem.handleStateChange();
312 ---------------------------------------------------------------------------
314 ---------------------------------------------------------------------------
317 qx.Proto.getItemSelected = function(vItem) {
318 return this._selectedItems.contains(vItem);
322 Make a single item selected / not selected
324 #param vItem[qx.ui.core.Widget]: Item which should be selected / not selected
325 #param vSelected[Boolean]: Should this item be selected?
327 qx.Proto.setItemSelected = function(vItem, vSelected)
329 var hc = this.getItemHashCode(vItem);
331 switch(this.getMultiSelection())
333 // Multiple item selection is allowed
335 if (!this.getItemEnabled(vItem)) {
339 // If selection state is not to be changed => return
340 if (this.getItemSelected(vItem) == vSelected) {
344 // Otherwise render new state
345 this.renderItemSelectionState(vItem, vSelected);
347 // Add item to selection hash / delete it from there
348 vSelected ? this._selectedItems.add(vItem) : this._selectedItems.remove(vItem);
350 // Dispatch change Event
351 this._dispatchChange();
357 // Multiple item selection is NOT allowed
359 var item0 = this.getSelectedItems()[0];
365 // Precheck for any changes
368 if (this.isEqual(vItem, old)) {
372 // Reset rendering of previous selected item
374 this.renderItemSelectionState(old, false);
377 // Render new item as selected
378 this.renderItemSelectionState(vItem, true);
380 // Reset current selection hash
381 this._selectedItems.removeAll();
384 this._selectedItems.add(vItem);
386 // Dispatch change Event
387 this._dispatchChange();
391 // Pre-check if item is currently selected
392 // Do not allow deselection in single selection mode
393 if (!this.isEqual(item0, vItem))
395 // Reset rendering as selected item
396 this.renderItemSelectionState(vItem, false);
398 // Reset current selection hash
399 this._selectedItems.removeAll();
401 // Dispatch change Event
402 this._dispatchChange();
419 Get the selected items (objects)
421 qx.Proto.getSelectedItems = function() {
422 return this._selectedItems.toArray();
425 qx.Proto.getSelectedItem = function() {
426 return this._selectedItems.getFirst();
432 #param vItems[Array of Widgets]: Items to select
434 qx.Proto.setSelectedItems = function(vItems)
436 var oldVal = this._getChangeValue();
438 // Temporary disabling of event fire
439 var oldFireChange = this.getFireChange();
440 this.setFireChange(false);
442 // Deselect all currently selected items
445 // Apply new selection
447 var vItemLength = vItems.length;
449 for (var i=0; i<vItemLength; i++)
453 if (!this.getItemEnabled(vItem)) {
457 // Add item to selection
458 this._selectedItems.add(vItem);
460 // Render new state for item
461 this.renderItemSelectionState(vItem, true);
464 // Recover change event status
465 this.setFireChange(oldFireChange);
467 // Dispatch change Event
468 if (oldFireChange && this._hasChanged(oldVal)) {
469 this._dispatchChange();
474 qx.Proto.setSelectedItem = function(vItem)
480 if (!this.getItemEnabled(vItem)) {
484 var oldVal = this._getChangeValue();
486 // Temporary disabling of event fire
487 var oldFireChange = this.getFireChange();
488 this.setFireChange(false);
490 // Deselect all currently selected items
493 // Add item to selection
494 this._selectedItems.add(vItem);
496 // Render new state for item
497 this.renderItemSelectionState(vItem, true);
499 // Recover change event status
500 this.setFireChange(oldFireChange);
502 // Dispatch change Event
503 if (oldFireChange && this._hasChanged(oldVal)) {
504 this._dispatchChange();
515 qx.Proto.selectAll = function()
517 var oldVal = this._getChangeValue();
519 // Temporary disabling of event fire
520 var oldFireChange = this.getFireChange();
521 this.setFireChange(false);
523 // Call sub method to select all items
526 // Recover change event status
527 this.setFireChange(oldFireChange);
529 // Dispatch change Event
530 if (oldFireChange && this._hasChanged(oldVal)) {
531 this._dispatchChange();
536 Sub method for selectAll. Handles the real work
539 qx.Proto._selectAll = function()
541 if (!this.getMultiSelection()) {
546 var vItems = this.getItems();
547 var vItemsLength = vItems.length;
549 // Reset current selection hash
550 this._selectedItems.removeAll();
552 for (var i=0; i<vItemsLength; i++)
556 if (!this.getItemEnabled(vItem)) {
560 // Add item to selection
561 this._selectedItems.add(vItem);
563 // Render new state for item
564 this.renderItemSelectionState(vItem, true);
577 qx.Proto.deselectAll = function()
579 var oldVal = this._getChangeValue();
581 // Temporary disabling of event fire
582 var oldFireChange = this.getFireChange();
583 this.setFireChange(false);
585 // Call sub method to deselect all items
588 // Recover change event status
589 this.setFireChange(oldFireChange);
591 // Dispatch change Event
592 if (oldFireChange && this._hasChanged(oldVal))
593 this._dispatchChange();
597 Sub method for deselectAll. Handles the real work
598 to deselect all items.
600 qx.Proto._deselectAll = function()
602 // Render new state for items
603 var items = this._selectedItems.toArray();
604 for (var i = 0; i < items.length; i++) {
605 this.renderItemSelectionState(items[i], false);
608 // Delete all entries in selectedItems hash
609 this._selectedItems.removeAll();
618 Select a range of items.
620 #param vItem1[qx.ui.core.Widget]: Start item
621 #param vItem2[qx.ui.core.Widget]: Stop item
623 qx.Proto.selectItemRange = function(vItem1, vItem2)
625 var oldVal = this._getChangeValue();
627 // Temporary disabling of event fire
628 var oldFireChange = this.getFireChange();
629 this.setFireChange(false);
631 // Call sub method to select the range of items
632 this._selectItemRange(vItem1, vItem2, true);
634 // Recover change event status
635 this.setFireChange(oldFireChange);
637 // Dispatch change Event
638 if (oldFireChange && this._hasChanged(oldVal)) {
639 this._dispatchChange();
647 Sub method for selectItemRange. Handles the real work
648 to select a range of items.
650 #param vItem1[qx.ui.core.Widget]: Start item
651 #param vItem2[qx.ui.core.Widget]: Stop item
652 #param vDelect[Boolean]: Deselect currently selected items first?
654 qx.Proto._selectItemRange = function(vItem1, vItem2, vDeselect)
656 // this.debug("SELECT_RANGE: " + vItem1.toText() + "<->" + vItem2.toText());
657 // this.debug("SELECT_RANGE: " + vItem1.pos + "<->" + vItem2.pos);
659 // Pre-Check a revert call if vItem2 is before vItem1
660 if (this.isBefore(vItem2, vItem1)) {
661 return this._selectItemRange(vItem2, vItem1, vDeselect);
669 var vCurrentItem = vItem1;
671 while (vCurrentItem != null)
673 if (this.getItemEnabled(vCurrentItem))
675 // Add item to selection
676 this._selectedItems.add(vCurrentItem);
678 // Render new state for item
679 this.renderItemSelectionState(vCurrentItem, true);
682 // Stop here if we reached target item
683 if (this.isEqual(vCurrentItem, vItem2)) {
688 vCurrentItem = this.getNext(vCurrentItem);
695 Internal method for deselection of ranges.
697 #param vItem1[qx.ui.core.Widget]: Start item
698 #param vItem2[qx.ui.core.Widget]: Stop item
700 qx.Proto._deselectItemRange = function(vItem1, vItem2)
702 // Pre-Check a revert call if vItem2 is before vItem1
703 if (this.isBefore(vItem2, vItem1)) {
704 return this._deselectItemRange(vItem2, vItem1);
707 var vCurrentItem = vItem1;
709 while (vCurrentItem != null)
711 // Add item to selection
712 this._selectedItems.remove(vCurrentItem);
714 // Render new state for item
715 this.renderItemSelectionState(vCurrentItem, false);
717 // Stop here if we reached target item
718 if (this.isEqual(vCurrentItem, vItem2)) {
723 vCurrentItem = this.getNext(vCurrentItem);
729 ---------------------------------------------------------------------------
731 ---------------------------------------------------------------------------
734 qx.Proto._activeDragSession = false;
736 qx.Proto.handleMouseDown = function(vItem, e)
738 // Only allow left and right button
739 if (!e.isLeftButtonPressed() && !e.isRightButtonPressed()) {
743 // Keep selection on right click on already selected item
744 if (e.isRightButtonPressed() && this.getItemSelected(vItem)) {
750 // Click on an unseleted item (without Strg)
751 if (e.isShiftPressed() || this.getDragSelection() || (!this.getItemSelected(vItem) && !e.isCtrlPressed()))
754 this._onmouseevent(vItem, e);
759 this.setLeadItem(vItem);
764 this._activeDragSession = this.getDragSelection();
766 if (this._activeDragSession)
768 // Add mouseup listener and register as capture widget
769 this.getBoundedWidget().addEventListener("mouseup", this._ondragup, this);
770 this.getBoundedWidget().setCapture(true);
774 qx.Proto._ondragup = function(e)
776 this.getBoundedWidget().removeEventListener("mouseup", this._ondragup, this);
777 this.getBoundedWidget().setCapture(false);
778 this._activeDragSession = false;
781 qx.Proto.handleMouseUp = function(vItem, e)
783 if (!e.isLeftButtonPressed()) {
787 if (e.isCtrlPressed() || this.getItemSelected(vItem) && !this._activeDragSession) {
788 this._onmouseevent(vItem, e);
791 if (this._activeDragSession)
793 this._activeDragSession = false;
794 this.getBoundedWidget().setCapture(false);
798 qx.Proto.handleMouseOver = function(oItem, e)
800 if (! this.getDragSelection() || !this._activeDragSession) {
804 this._onmouseevent(oItem, e, true);
807 // currently unused placeholder
808 qx.Proto.handleClick = function(vItem, e) {}
810 // currently unused placeholder
811 qx.Proto.handleDblClick = function(vItem, e) {}
815 Internal handler for all mouse events bound to this manager.
817 qx.Proto._onmouseevent = function(oItem, e, bOver)
819 if (!this.getItemEnabled(oItem)) {
823 // ********************************************************************
825 // ********************************************************************
827 // Cache current (old) values
828 var oldVal = this._getChangeValue();
829 var oldLead = this.getLeadItem();
831 // Temporary disabling of event fire
832 var oldFireChange = this.getFireChange();
833 this.setFireChange(false);
835 // Cache selection and count
836 var selectedItems = this.getSelectedItems();
837 var selectedCount = selectedItems.length;
840 this.setLeadItem(oItem);
842 // Cache current anchor item
843 var currentAnchorItem = this.getAnchorItem();
845 // Cache keys pressed
846 var vCtrlKey = e.isCtrlPressed();
847 var vShiftKey = e.isShiftPressed();
850 // ********************************************************************
851 // Do we need to update the anchor?
852 // ********************************************************************
854 if (!currentAnchorItem || selectedCount == 0 || (vCtrlKey && !vShiftKey && this.getMultiSelection() && !this.getDragSelection()))
856 this.setAnchorItem(oItem);
857 currentAnchorItem = oItem;
862 // ********************************************************************
863 // Mode #1: Replace current selection with new one
864 // ********************************************************************
865 if ((!vCtrlKey && !vShiftKey && !this._activeDragSession || !this.getMultiSelection()))
867 if (!this.getItemEnabled(oItem)) {
871 // Remove current selection
874 // Update anchor item
875 this.setAnchorItem(oItem);
877 if (this._activeDragSession)
879 // a little bit hacky, but seems to be a fast way to detect if we slide to top or to bottom
880 this.scrollItemIntoView((this.getBoundedWidget().getScrollTop() > (this.getItemTop(oItem)-1) ? this.getPrevious(oItem) : this.getNext(oItem)) || oItem);
883 if (!this.getItemSelected(oItem)) {
884 this.renderItemSelectionState(oItem, true);
887 // Clear up and add new one
888 //this._selectedItems.removeAll();
889 this._selectedItems.add(oItem);
891 this._addToCurrentSelection = true;
895 // ********************************************************************
896 // Mode #2: (De-)Select item range in mouse drag session
897 // ********************************************************************
898 else if (this._activeDragSession && bOver)
901 this._deselectItemRange(currentAnchorItem, oldLead);
905 if (this.isBefore(currentAnchorItem, oItem))
907 if (this._addToCurrentSelection)
909 this._selectItemRange(currentAnchorItem, oItem, false);
913 this._deselectItemRange(currentAnchorItem, oItem);
920 if (this._addToCurrentSelection)
922 this._selectItemRange(oItem, currentAnchorItem, false);
926 this._deselectItemRange(oItem, currentAnchorItem);
930 // a little bit hacky, but seems to be a fast way to detect if we slide to top or to bottom
931 this.scrollItemIntoView((this.getBoundedWidget().getScrollTop() > (this.getItemTop(oItem)-1) ? this.getPrevious(oItem) : this.getNext(oItem)) || oItem);
935 // ********************************************************************
936 // Mode #3: Add new item to current selection (ctrl pressed)
937 // ********************************************************************
938 else if (this.getMultiSelection() && vCtrlKey && !vShiftKey)
940 if (!this._activeDragSession) {
941 this._addToCurrentSelection = !(this.getCanDeselect() && this.getItemSelected(oItem));
944 this.setItemSelected(oItem, this._addToCurrentSelection);
945 this.setAnchorItem(oItem);
949 // ********************************************************************
950 // Mode #4: Add new (or continued) range to selection
951 // ********************************************************************
952 else if (this.getMultiSelection() && vCtrlKey && vShiftKey)
954 if (!this._activeDragSession) {
955 this._addToCurrentSelection = !(this.getCanDeselect() && this.getItemSelected(oItem));
958 if (this._addToCurrentSelection)
960 this._selectItemRange(currentAnchorItem, oItem, false);
964 this._deselectItemRange(currentAnchorItem, oItem);
968 // ********************************************************************
969 // Mode #5: Replace selection with new range selection
970 // ********************************************************************
971 else if (this.getMultiSelection() && !vCtrlKey && vShiftKey)
973 if (this.getCanDeselect())
975 this._selectItemRange(currentAnchorItem, oItem, true);
981 this._deselectItemRange(currentAnchorItem, oldLead);
984 this._selectItemRange(currentAnchorItem, oItem, false);
990 // Recover change event status
991 this.setFireChange(oldFireChange);
993 // Dispatch change Event
994 if(oldFireChange && this._hasChanged(oldVal)) {
995 this._dispatchChange();
1003 ---------------------------------------------------------------------------
1005 ---------------------------------------------------------------------------
1008 qx.Proto.handleKeyDown = function(vDomEvent) {
1010 "qx.manager.selection.SelectionManager.handleKeyDown is deprecated! " +
1011 "Use keypress insted and bind it to the onkeypress event."
1013 this.handleKeyPress(vDomEvent);
1018 * Handles key event to perform selection and navigation
1020 * @param vDomEvent {qx.event.type.KeyEvent} event object
1022 qx.Proto.handleKeyPress = function(vDomEvent)
1024 var oldVal = this._getChangeValue();
1026 // Temporary disabling of event fire
1027 var oldFireChange = this.getFireChange();
1028 this.setFireChange(false);
1030 // Ctrl+A: Select all
1031 if (vDomEvent.getKeyIdentifier() == "A" && vDomEvent.isCtrlPressed())
1033 if (this.getMultiSelection())
1037 // Update lead item to this new last
1038 // (or better here: first) selected item
1039 this.setLeadItem(this.getFirst());
1043 // Default operation
1046 var aIndex = this.getAnchorItem();
1047 var itemToSelect = this.getItemToSelect(vDomEvent);
1049 // this.debug("Anchor: " + (aIndex ? aIndex.getLabel() : "null"));
1050 // this.debug("ToSelect: " + (itemToSelect ? itemToSelect.getLabel() : "null"));
1052 if (itemToSelect && this.getItemEnabled(itemToSelect))
1054 // Update lead item to this new last selected item
1055 this.setLeadItem(itemToSelect);
1057 // Scroll new item into view
1058 this.scrollItemIntoView(itemToSelect);
1060 // Stop event handling
1061 vDomEvent.preventDefault();
1064 if (vDomEvent.isShiftPressed() && this.getMultiSelection())
1066 // Make it a little bit more failsafe:
1067 // Set anchor if not given already. Allows us to select
1068 // a range without any previous selection.
1069 if (aIndex == null) {
1070 this.setAnchorItem(itemToSelect);
1073 // Select new range (and clear up current selection first)
1074 this._selectItemRange(this.getAnchorItem(), itemToSelect, true);
1076 else if (!vDomEvent.isCtrlPressed())
1078 // Clear current selection
1079 this._deselectAll();
1081 // Update new item to be selected
1082 this.renderItemSelectionState(itemToSelect, true);
1084 // Add item to new selection
1085 this._selectedItems.add(itemToSelect);
1087 // Update anchor to this new item
1088 // (allows following shift range selection)
1089 this.setAnchorItem(itemToSelect);
1091 else if (vDomEvent.getKeyIdentifier() == "Space")
1093 if (this._selectedItems.contains(itemToSelect))
1095 // Update new item to be selected
1096 this.renderItemSelectionState(itemToSelect, false);
1098 // Add item to new selection
1099 this._selectedItems.remove(itemToSelect);
1102 this.setAnchorItem(this._selectedItems.getFirst());
1106 // Clear current selection
1107 if (!vDomEvent.isCtrlPressed() || !this.getMultiSelection()) {
1108 this._deselectAll();
1111 // Update new item to be selected
1112 this.renderItemSelectionState(itemToSelect, true);
1114 // Add item to new selection
1115 this._selectedItems.add(itemToSelect);
1117 // Update anchor to this new item
1118 // (allows following shift range selection)
1119 this.setAnchorItem(itemToSelect);
1125 // Recover change event status
1126 this.setFireChange(oldFireChange);
1128 // Dispatch change Event
1129 if (oldFireChange && this._hasChanged(oldVal)) {
1130 this._dispatchChange();
1134 qx.Proto.getItemToSelect = function(vKeyboardEvent)
1136 // Don't handle ALT here
1137 if (vKeyboardEvent.isAltPressed()) {
1141 // Handle event by keycode
1142 switch (vKeyboardEvent.getKeyIdentifier())
1145 return this.getHome(this.getLeadItem());
1148 return this.getEnd(this.getLeadItem());
1152 return this.getDown(this.getLeadItem());
1155 return this.getUp(this.getLeadItem());
1159 return this.getLeft(this.getLeadItem());
1162 return this.getRight(this.getLeadItem());
1166 return this.getPageUp(this.getLeadItem()) || this.getHome(this.getLeadItem());
1169 return this.getPageDown(this.getLeadItem()) || this.getEnd(this.getLeadItem());
1173 if (vKeyboardEvent.isCtrlPressed()) {
1174 return this.getLeadItem();
1185 ---------------------------------------------------------------------------
1187 ---------------------------------------------------------------------------
1190 qx.Proto._dispatchChange = function()
1192 if (!this.getFireChange()) {
1196 if (this.hasEventListeners("changeSelection")) {
1197 this.dispatchEvent(new qx.event.type.DataEvent("changeSelection", this.getSelectedItems()), true);
1201 qx.Proto._hasChanged = function(sOldValue) {
1202 return sOldValue != this._getChangeValue();
1205 qx.Proto._getChangeValue = function() {
1206 return this._selectedItems.getChangeValue();
1215 ---------------------------------------------------------------------------
1217 ---------------------------------------------------------------------------
1220 qx.Proto.getHome = function() {
1221 return this.getFirst();
1224 qx.Proto.getEnd = function() {
1225 return this.getLast();
1228 qx.Proto.getDown = function(vItem)
1231 return this.getFirst();
1234 return this.getMultiColumnSupport() ? (this.getUnder(vItem) || this.getLast()) : this.getNext(vItem);
1237 qx.Proto.getUp = function(vItem)
1240 return this.getLast();
1243 return this.getMultiColumnSupport() ? (this.getAbove(vItem) || this.getFirst()) : this.getPrevious(vItem);
1246 qx.Proto.getLeft = function(vItem)
1248 if (!this.getMultiColumnSupport()) {
1252 return !vItem ? this.getLast() : this.getPrevious(vItem);
1255 qx.Proto.getRight = function(vItem)
1257 if (!this.getMultiColumnSupport()) {
1261 return !vItem ? this.getFirst() : this.getNext(vItem);
1264 qx.Proto.getAbove = function(vItem)
1266 throw new Error("getAbove(): Not implemented yet");
1269 qx.Proto.getUnder = function(vItem)
1271 throw new Error("getUnder(): Not implemented yet");
1281 ---------------------------------------------------------------------------
1283 ---------------------------------------------------------------------------
1289 #param vItem[qx.ui.core.Widget]: Relative to this widget
1291 qx.Proto.getPageUp = function(vItem)
1293 var vBoundedWidget = this.getBoundedWidget();
1294 var vParentScrollTop = vBoundedWidget.getScrollTop();
1295 var vParentClientHeight = vBoundedWidget.getClientHeight();
1299 var nextItem = this.getLeadItem();
1301 nextItem = this.getFirst();
1304 // Normally we should reach the status "lead" for the
1305 // nextItem after two iterations.
1307 while (tryLoops < 2)
1309 while (nextItem && (this.getItemTop(nextItem) - this.getItemHeight(nextItem) >= vParentScrollTop)) {
1310 nextItem = this.getUp(nextItem);
1313 // This should never occour after the fix above
1314 if (nextItem == null) {
1318 // If the nextItem is not anymore the leadItem
1319 // Means: There has occured a change.
1320 // We break here. This is normally the second step.
1321 if (nextItem != this.getLeadItem())
1323 // be sure that the top is reached
1324 this.scrollItemIntoView(nextItem, true);
1328 // Update scrolling (this is normally the first step)
1329 // this.debug("Scroll-Up: " + (vParentScrollTop + vParentClientHeight - 2 * this.getItemHeight(nextItem)));
1330 vBoundedWidget.setScrollTop(vParentScrollTop - vParentClientHeight - this.getItemHeight(nextItem));
1332 // Use the real applied value instead of the calulated above
1333 vParentScrollTop = vBoundedWidget.getScrollTop();
1335 // Increment counter
1345 #param vItem[qx.ui.core.Widget]: Relative to this widget
1347 qx.Proto.getPageDown = function(vItem)
1349 var vBoundedWidget = this.getBoundedWidget();
1350 var vParentScrollTop = vBoundedWidget.getScrollTop();
1351 var vParentClientHeight = vBoundedWidget.getClientHeight();
1353 // this.debug("Bound: " + (vBoundedWidget._getTargetNode() != vBoundedWidget.getElement()));
1355 // this.debug("ClientHeight-1: " + vBoundedWidget._getTargetNode().clientHeight);
1356 // this.debug("ClientHeight-2: " + vBoundedWidget.getElement().clientHeight);
1360 var nextItem = this.getLeadItem();
1362 nextItem = this.getFirst();
1365 // Normally we should reach the status "lead" for the
1366 // nextItem after two iterations.
1368 while (tryLoops < 2)
1370 // this.debug("Loop: " + tryLoops);
1371 // this.debug("Info: " + nextItem + " :: " + (this.getItemTop(nextItem) + (2 * this.getItemHeight(nextItem))) + " <> " + (vParentScrollTop + vParentClientHeight));
1372 // this.debug("Detail: " + vParentScrollTop + ", " + vParentClientHeight);
1375 while (nextItem && ((this.getItemTop(nextItem) + (2 * this.getItemHeight(nextItem))) <= (vParentScrollTop + vParentClientHeight))) {
1376 nextItem = this.getDown(nextItem);
1379 // This should never occour after the fix above
1380 if (nextItem == null) {
1384 // If the nextItem is not anymore the leadItem
1385 // Means: There has occured a change.
1386 // We break here. This is normally the second step.
1387 if (nextItem != this.getLeadItem()) {
1391 // Update scrolling (this is normally the first step)
1392 // this.debug("Scroll-Down: " + (vParentScrollTop + vParentClientHeight - 2 * this.getItemHeight(nextItem)));
1393 vBoundedWidget.setScrollTop(vParentScrollTop + vParentClientHeight - 2 * this.getItemHeight(nextItem));
1395 // Use the real applied value instead of the calulated above
1396 vParentScrollTop = vBoundedWidget.getScrollTop();
1398 // Increment counter
1402 //this.debug("Select: " + nextItem._labelObject.getHtml());
1417 ---------------------------------------------------------------------------
1419 ---------------------------------------------------------------------------
1422 qx.Proto.dispose = function()
1424 if (this.getDisposed()) {
1428 if (this._selectedItems)
1430 this._selectedItems.dispose();
1431 this._selectedItems = null;
1434 return qx.core.Target.prototype.dispose.call(this);