UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

471 lines (377 loc) 11.7 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2011 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Christian Hagendorn (chris_schmidt) ************************************************************************ */ /** * Implements the different selection modes single, multi, additive and one * selection with there drag and quick selection. * * Example how to use selection: * <pre class="javascript"> * var rawData = []; * for (var i = 0; i < 2500; i++) { * rawData[i] = "Item No " + i; * } * * var model = qx.data.marshal.Json.createModel(rawData); * var list = new qx.ui.list.List(model); * * // Pre-Select "Item No 20" * list.getSelection().push(model.getItem(20)); * * // log change selection * list.getSelection().addListener("change", function(e) { * this.debug("Selection: " + list.getSelection().getItem(0)); * }, this); * </pre> * * @internal */ qx.Mixin.define("qx.ui.virtual.selection.MModel", { construct : function() { this._initSelectionManager(); this.__defaultSelection = new qx.data.Array(); this.initSelection(this.__defaultSelection); }, properties : { /** Current selected items */ selection : { check : "qx.data.Array", event : "changeSelection", apply : "_applySelection", nullable : false, deferredInit : true }, /** * The selection mode to use. * * For further details please have a look at: * {@link qx.ui.core.selection.Abstract#mode} */ selectionMode : { check : ["single", "multi", "additive", "one"], init : "single", apply : "_applySelectionMode" }, /** * Enable drag selection (multi selection of items through * dragging the pointer in pressed states). * * Only possible for the selection modes <code>multi</code> and <code>additive</code> */ dragSelection : { check : "Boolean", init : false, apply : "_applyDragSelection" }, /** * Enable quick selection mode, where no tap is needed to change the selection. * * Only possible for the modes <code>single</code> and <code>one</code>. */ quickSelection : { check : "Boolean", init : false, apply : "_applyQuickSelection" } }, events : { /** * This event is fired as soon as the content of the selection property changes, but * this is not equal to the change of the selection of the widget. If the selection * of the widget changes, the content of the array stored in the selection property * changes. This means you have to listen to the change event of the selection array * to get an event as soon as the user changes the selected item. * <pre class="javascript">obj.getSelection().addListener("change", listener, this);</pre> */ "changeSelection" : "qx.event.type.Data", /** Fires after the value was modified */ "changeValue" : "qx.event.type.Data" }, members : { /** @type {qx.ui.virtual.selection.Row} selection manager */ _manager : null, /** @type {Boolean} flag to ignore the selection change from {@link #selection} */ __ignoreChangeSelection : false, /** @type {Boolean} flag to ignore the selection change from <code>_manager</code> */ __ignoreManagerChangeSelection : false, __defaultSelection : null, /** * setValue implements part of the {@link qx.ui.form.IField} interface. * * @param selection {qx.data.IListData|null} List data to select as value. * @return {null} The status of this operation. */ setValue : function(selection) { if (null === selection) { this.resetSelection(); } else { this.setSelection(selection); } return null; }, /** * getValue implements part of the {@link qx.ui.form.IField} interface. * * @return {qx.data.IListData} The current selection. */ getValue : function() { return this.getSelection(); }, /** * resetValue implements part of the {@link qx.ui.form.IField} interface. */ resetValue : function() { this.resetSelection(); }, /** * Initialize the selection manager with his delegate. */ _initSelectionManager : function() { var self = this; var selectionDelegate = { isItemSelectable : function(row) { return self._provider.isSelectable(row); }, styleSelectable : function(row, type, wasAdded) { if (type != "selected") { return; } if (wasAdded) { self._provider.styleSelectabled(row); } else { self._provider.styleUnselectabled(row); } } }; this._manager = new qx.ui.virtual.selection.Row( this.getPane(), selectionDelegate ); this._manager.attachPointerEvents(this.getPane()); this._manager.attachKeyEvents(this); this._manager.addListener("changeSelection", this._onManagerChangeSelection, this); }, /** * Determines, if automatically scrolling of selected item is active. * Set <code>false</code> to suspend auto scrolling. * * @param value {Boolean} Set <code>false</code> to suspend auto scrolling. */ setAutoScrollIntoView : function(value) { this._manager._autoScrollIntoView = value; }, /** * Returns true, if automatically scrolling of selected item is active. * * @return {Boolean} Returns <code>false</code> if auto scrolling is suspended. */ getAutoScrollIntoView : function() { return this._manager._autoScrollIntoView; }, /** * Method to update the selection, this method can be used when the model has * changes. */ _updateSelection : function() { if (this._manager == null) { return; } this._onChangeSelection(); }, /* --------------------------------------------------------------------------- APPLY ROUTINES --------------------------------------------------------------------------- */ // apply method _applySelection : function(value, old) { value.addListener("change", this._onChangeSelection, this); if (old != null) { old.removeListener("change", this._onChangeSelection, this); } this._onChangeSelection(); }, // apply method _applySelectionMode : function(value, old) { this._manager.setMode(value); }, // apply method _applyDragSelection : function(value, old) { this._manager.setDrag(value); }, // apply method _applyQuickSelection : function(value, old) { this._manager.setQuick(value); }, /* --------------------------------------------------------------------------- SELECTION HANDLERS --------------------------------------------------------------------------- */ /** * Event handler for the internal selection change {@link #selection}. * * @param e {qx.event.type.Data} the change event. */ _onChangeSelection : function(e) { if (this.__ignoreManagerChangeSelection == true) { return; } this.__ignoreChangeSelection = true; var selection = this.getSelection(); var newSelection = []; for (var i = 0; i < selection.getLength(); i++) { var item = selection.getItem(i); var selectables = this._getSelectables(); var index = -1; if (selectables != null) { index = selectables.indexOf(item); } var row = this._reverseLookup(index); if (row >= 0) { newSelection.push(row); } } if (this._beforeApplySelection != null && qx.lang.Type.isFunction(this._beforeApplySelection)) { this._beforeApplySelection(newSelection); } try { if (!qx.lang.Array.equals(newSelection, this._manager.getSelection())) { this._manager.replaceSelection(newSelection); } } catch(ex) { this._manager.selectItem(newSelection[newSelection.length - 1]); } this.__synchronizeSelection(); if (this._afterApplySelection != null && qx.lang.Type.isFunction(this._afterApplySelection)) { this._afterApplySelection(); } this.__ignoreChangeSelection = false; }, /** * Event handler for the selection change from the <code>_manager</code>. * * @param e {qx.event.type.Data} the change event. */ _onManagerChangeSelection : function(e) { if (this.__ignoreChangeSelection == true) { return; } this.__ignoreManagerChangeSelection = true; this.__synchronizeSelection(); this.__ignoreManagerChangeSelection = false; this.fireDataEvent("changeValue", e.getData(), e.getOldData()); }, /** * Synchronized the selection form the manager with the local one. */ __synchronizeSelection : function() { if (this.__isSelectionEquals()) { return; } var managerSelection = this._manager.getSelection(); var newSelection = []; for (var i = 0; i < managerSelection.length; i++) { var item = this._getDataFromRow(managerSelection[i]); if (item != null) { newSelection.push(item); } } this.__replaceSelection(newSelection); }, /** * Replace the current selection with the passed selection Array. * * @param newSelection {qx.data.Array} The new selection. */ __replaceSelection : function(newSelection) { var selection = this.getSelection(); if (newSelection.length > 0) { var args = [0, selection.getLength()]; args = args.concat(newSelection); // dispose data array returned by splice to avoid memory leak var temp = selection.splice.apply(selection, args); temp.dispose(); } else { selection.removeAll(); } }, /** * Checks whether the local and the manager selection are equal. * * @return {Boolean} <code>true</code> if the selections are equal, * <code>false</code> otherwise. */ __isSelectionEquals : function() { var selection = this.getSelection(); var managerSelection = this._manager.getSelection(); if (selection.getLength() !== managerSelection.length) { return false; } for (var i = 0; i < selection.getLength(); i++) { var item = selection.getItem(i); var selectables = this._getSelectables(); var index = -1; if (selectables != null) { index = selectables.indexOf(item); } var row = this._reverseLookup(index); if (row !== managerSelection[i]) { return false; }; } return true; }, /** * Helper Method to select default item. */ _applyDefaultSelection : function() { if (this._manager != null) { this._manager._applyDefaultSelection(); } } }, destruct : function() { this._manager.dispose(); this._manager = null; if (this.__defaultSelection) { this.__defaultSelection.dispose(); } } });