/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2007 1&1 Internet AG, Germany, http://www.1and1.org
License:
LGPL: http://www.gnu.org/licenses/lgpl.html
EPL: http://www.eclipse.org/org/documents/epl-v10.php
See the LICENSE file in the project's top-level directory for details.
Authors:
* David Perez Carmona (david-perez), based on qx.ui.form.ComboBox
************************************************************************ */
/* ************************************************************************
#module(ui_comboboxex)
#require(qx.ui.table.Table)
#embed(qx.widgettheme/arrows/down.gif)
#embed(qx.icontheme/16/actions/edit-find.png)
#embed(qx.icontheme/16/actions/dialog-ok.png)
#embed(qx.icontheme/16/actions/dialog-cancel.png)
************************************************************************ */
/**
* An enhanced combo-box for qooxdoo.
*
*
Features:
*
* - Editable text field
* - Complete key-navigation
* - Mouse wheel navigation
* - Multicolumn display in list
* - If more than one column, headers are automatically shown
* - Can show the ID and/or description of each list item
* - Automatically calculating needed width
* - Popup list always shows full contents, and can be wider than text field
* - Search values through popup dialog
* - Internationalization support of messages
*
* Pending features:
*
* - Images inside the list
* - Autocomplete on key input
*
*
* @event beforeInitialOpen {qx.event.type.Event}
*/
qx.OO.defineClass('qx.ui.form.ComboBoxEx', qx.ui.layout.HorizontalBoxLayout, function() {
qx.ui.layout.HorizontalBoxLayout.call(this);
// ************************************************************************
// POPUP
// ************************************************************************
var p = this._popup = new qx.ui.popup.Popup;
p.setAppearance('combo-box-ex-popup');
// ************************************************************************
// LIST
// ************************************************************************
this._createList([ this.tr("ID"), this.tr("Description") ]);
// ************************************************************************
// FIELD
// ************************************************************************
var f = this._field = new qx.ui.form.TextField;
f.setAppearance('combo-box-ex-text-field');
f.addEventListener("input", this._oninput, this);
this.add(f);
this.setEditable(false);
// ************************************************************************
// BUTTON
// ************************************************************************
// Use qx.ui.basic.Atom instead of qx.ui.form.Button here to omit the registration
// of the unneeded and complex button events.
var b = this._button = new qx.ui.basic.Atom(null, "widget/arrows/down.gif");
b.set({
appearance: "combo-box-button",
tabIndex: -1
});
this.add(b);
// ************************************************************************
// BEHAVIOR
// ************************************************************************
this.setTabIndex(1);
// ************************************************************************
// WIDGET MOUSE EVENTS
// ************************************************************************
this.addEventListener("mousedown", this._onmousedown);
this.addEventListener("mouseup", this._onmouseup);
this.addEventListener("mousewheel", this._onmousewheel);
this.addEventListener("dblclick", function() {
if (this.getAllowSearch()) {
this.openSearchDialog();
}
});
// ************************************************************************
// WIDGET KEY EVENTS
// ************************************************************************
this.addEventListener("keydown", this._onkeydown);
this.addEventListener("keypress", this._onkeypress);
// ************************************************************************
// WIDGET STATE EVENTS
// ************************************************************************
this.addEventListener("beforeDisappear", this._testClosePopup);
// ************************************************************************
// CHILDREN EVENTS
// ************************************************************************
this._popup.addEventListener("appear", this._onpopupappear, this);
});
/*
---------------------------------------------------------------------------
PROPERTIES
---------------------------------------------------------------------------
*/
qx.OO.changeProperty({ name : "appearance", type : "string", defaultValue : "combo-box-ex" });
/*!Is the text field component editable or the user can only select from the list?*/
qx.OO.addProperty({ name: "editable", type: "boolean", getAlias: "isEditable" });
/*!0 based. -1 means no selected index. It retrieves always the value column of the selection, not the description.*/
qx.OO.addProperty({ name: "value", type : "string" });
/*!How many items to transverse with PageUp and PageDn.*/
qx.OO.addProperty({ name: "pagingInterval", type: "number", defaultValue: 10 });
/*!Show the ID column (column 0) of the selection data?*/
qx.OO.addProperty({ name: "idColumnVisible", type: "boolean", getAlias: "isIdColumnVisible", defaultValue: false });
/*!Only used when editable is false. It determines what to show in the text field of the combo box.*/
qx.OO.addProperty({ name: "showOnTextField", type: "string", defaultValue: 'description', possibleValues : [ 'description', 'idAndDescription' ] });
/*!Only used when editable is false and showOnTextField=='idAndDescription'.*/
qx.OO.addProperty({ name: "idDescriptionSeparator", type: "string", defaultValue: '- ' });
/*!Ensures that always an item is selected (in case the selection isn't empty). Only used when editable is false.*/
qx.OO.addProperty({ name: 'ensureSomethingSelected', type: "boolean", defaultValue: true });
/*!Allow the search dialog when double clicking the combo, or pressing special keys?.*/
qx.OO.addProperty({ name: 'allowSearch', type: "boolean", defaultValue: true });
/*!Maximum number of visible rows in the popup list.*/
qx.OO.addProperty({ name: 'maxVisibleRows', type: "number", defaultValue: 10 });
/*
---------------------------------------------------------------------------
UTILITIES
---------------------------------------------------------------------------
*/
qx.Proto.getPopup = function() {
return this._popup;
}
qx.Proto.getList = function() {
return this._list;
}
qx.Proto.getField = function() {
return this._field;
}
qx.Proto.getButton = function() {
return this._button;
}
qx.Proto._getComboSetting = function(key) {
return qx.Settings.getValueOfClass('qx.ui.form.ComboBoxEx', key);
}
/**Gets the current selected row of the selection.
* @return null if nothing selected or an array*/
qx.Proto.getSelectedRow = function() {
var ind = this.getSelectedIndex();
return ind < 0 ? null : this._model.getData()[ind];
}
/**Creates the list component.*/
qx.Proto._createList = function(columns) {
this._model = new qx.ui.table.SimpleTableModel;
// Default column titles
this._model.setColumns(columns);
var l = this._list = new qx.ui.table.Table(this._model);
l.setFocusedCell = function() {}
l.setAppearance('combo-box-ex-list');
// We receive this: Modification of property "keepFirstVisibleRowComplete" failed with exception: TypeError - vCurrentChild has no properties or
// this: Modification of property "keepFirstVisibleRowComplete" failed with exception: TypeError - this.getParent() has no properties
l.forceKeepFirstVisibleRowComplete(false);
var selMan = l._getSelectionManager();
var oldHandle = selMan.handleMouseUp, me = this;
selMan.handleMouseUp = function(vItem, e) {
oldHandle.apply(selMan, arguments);
if (e.isLeftButtonPressed()) {
me._testClosePopup();
}
}
this._modifyIdColumnVisible(this.getIdColumnVisible());
this._manager = l.getSelectionModel();
this._manager.addEventListener('changeSelection', this._onChangeSelection, this);
// Avoid deselection from user
this._manager.removeSelectionInterval = function() {};
this._manager.setSelectionMode(qx.ui.table.SelectionModel.SINGLE_SELECTION);
this._popup.add(l);
// Invalidate calculation of column widths
delete this._calcDimensions;
}
/*
---------------------------------------------------------------------------
PSEUDO-PROPERTIES
---------------------------------------------------------------------------
*/
/**Sets the header for each column.
* @param columns {String[]}*/
qx.Proto.setColumnHeaders = function(columns) {
if (!this._list || columns.length != this._model.getColumnCount()) {
if (this._list) {
var data = this._model.getData();
this._list.setParent(null);
this._list.dispose();
this._list = null;
}
this._createList(columns);
if (data && data.length) {
this._model.setData(data);
}
} else {
this._model.setColumns(columns);
this._list.getTableColumnModel().init(columns.length);
delete this._calcDimensions;
}
this._modifyIdColumnVisible(this.getIdColumnVisible());
}
/**Getter for {@link #setColumnHeaders}.
* @return {String[]}*/
qx.Proto.getColumnHeaders = function(propVal) {
var cols = [];
cols.length = this._model.getColumnCount();
for (var col = 0; col < cols.length; col++) {
cols[col] = this._model.getColumnName(col);
}
return cols;
}
/**Sets the list of selectable items.
* @param data {var[][]} Array of values. Its value is an array, with the following info:.
* - Column 0 represents the ID, i.e. the value that is stored internally and used by the app.
* - Column 1 represents the description, the text that the end user normally sees.
* - Columns > 1 will also be shown in the popup list, it you have set the appropiate column headers with {@link #setColumnHeaders}.
*
*/
qx.Proto.setSelection = function(data) {
// Invalidate calculation of column widths
delete this._calcDimensions;
this._model.setData(data);
// Try to preserve currently selected value
if (!this.getEditable()) {
this._modifyValue(this.getValue());
}
}
/**Getter for {@link #setSelection}.
* @return {Array}*/
qx.Proto.getSelection = function() {
return this._model.getData();
}
/**Sets the index of the currently selected item in the list.
* @param index {number} -1 means no selected index*/
qx.Proto.setSelectedIndex = function(index) {
var items = this.getSelection().length;
if (items >= 0) {
if (index < 0 && !this.getEditable() && this.getEnsureSomethingSelected()) {
index = 0;
}
if (index >= 0) {
index = qx.lang.Number.limit(index, 0, items-1);
this._manager.setSelectionInterval(index, index);
if (this._popup.isSeeable()) {
this._list.scrollCellVisible(0, index);
}
} else {
this._manager.clearSelection();
}
}
return true;
}
/**Getter for {@link #setSelectedIndex}.*/
qx.Proto.getSelectedIndex = function() {
var index = this._manager.getAnchorSelectionIndex();
return this._manager.isSelectedIndex(index) ? index:-1;
}
/*
---------------------------------------------------------------------------
MODIFIER
---------------------------------------------------------------------------
*/
qx.Proto._modifyShowOnTextField = function(propVal) {
if (!this.getEditable()) {
this.setSelectedIndex(this.getSelectedIndex());
delete this._calcDimensions; // Invalidate this._neededTextFieldWidth
}
return true;
}
qx.Proto._modifyMaxVisibleRows = function() {
delete this._calcDimensions; // Invalidate this._list.height
return true;
}
qx.Proto._checkIdDescriptionSeparator = function(propVal) {
// For measuring widths, it is better to replace spaces with non-breakable spaces
return String(propVal).replace(/ /g, '\u00A0')
}
qx.Proto._modifyIdDescriptionSeparator = function(propVal) {
if (!this.getEditable() && this.getShowOnTextField() == 'idAndDescription') {
this.setSelectedIndex(this.getSelectedIndex());
delete this._calcDimensions; // Invalidate this._neededTextFieldWidth
}
return true;
}
qx.Proto._modifyIdColumnVisible = function(propVal) {
this._list.getTableColumnModel().setColumnVisible(0, propVal);
delete this._calcDimensions;
return true;
}
qx.Proto._modifyEditable = function(propValue/*, propOldValue, propData*/) {
var f = this._field;
f.setReadOnly(!propValue);
f.setCursor(propValue ? null : "default");
f.setSelectable(propValue);
return true;
}
qx.Proto._modifyValue = function(propValue/*, propOldValue, propData*/) {
this._fromValue = true;
var values = this._model.getData();
var i = -1;
if (propValue != null) {
for (var i = 0; i < values.length; i++) {
if (propValue == values[i][0]) {
break;
}
}
if (i == values.length) {
i = -1;
}
}
if (this.getEditable()) {
this._field.setValue(propValue);
}
// only do this if we called setValue separately
// and not from the property "selected".
if (!this._fromSelected) {
this.setSelectedIndex(i);
}
// reset hint
delete this._fromValue;
return true;
}
qx.Proto._modifyEnabled = function(propValue/*, propOldValue, propData*/) {
if (this._button) {
this._button.setEnabled(propValue);
}
if (this._field) {
this._field.setEnabled(propValue);
}
return qx.ui.layout.HorizontalBoxLayout.prototype._modifyEnabled.apply(this, arguments);
}
/*
---------------------------------------------------------------------------
POPUP HELPER
---------------------------------------------------------------------------
*/
qx.Proto._oldSelected = null;
qx.Proto._openPopup = function() {
if (this.isSearchInProgress()) {
return;
}
var p = this._popup;
p.setAutoHide(false);
var el = this.getElement();
if (!p.isCreated()) {
this.createDispatchEvent("beforeInitialOpen");
}
if (!this.getSelection().length) {
return;
}
p.positionRelativeTo(el, 1, qx.html.Dimension.getBoxHeight(el));
this._calculateDimensions();
p.setParent(this.getTopLevelWidget());
p.auto();
p.show();
this._oldSelected = this.getSelectedIndex();
window.setInterval(function() {
p.setAutoHide(true);
}, 0);
}
/**Hide the popup list.*/
qx.Proto._closePopup = function() {
this._popup.hide();
}
/**Hide the popup list only when needed.*/
qx.Proto._testClosePopup = function() {
if (this._popup.isSeeable()) {
this._closePopup();
}
}
/**Toggle the visibility of the popup list.*/
qx.Proto._togglePopup = function() {
this._popup.isSeeable() ? this._closePopup() : this._openPopup();
}
/*
---------------------------------------------------------------------------
DIMENSIONING
---------------------------------------------------------------------------
*/
/**Sizes the width of the text field component to the needed value to show any selection item.*/
qx.Proto.sizeTextFieldToContent = function() {
this._calculateDimensions();
this._field.setWidth(this._neededTextFieldWidth);
}
/**Calculates the needed dimensions for the text field and list components*/
qx.Proto._calculateDimensions = function() {
if (this._calcDimensions) {
// Already calculated
return;
}
var data = this.getSelection();
var cols = this.getColumnHeaders(), nCols = cols.length;
var columnWidths = [];
this._neededTextFieldWidth = 0;
columnWidths.length = cols.length;
for (var col = 0; col < cols.length; col++) {
columnWidths[col] = 0;
}
var withDescript = this.getShowOnTextField() == 'idAndDescription';
for (var row = 0, rows = Math.min(data.length, 50); row < rows; row++) {
var r = data[row], wi0, wi1;
for (col = 0; col < nCols; col++) {
var wi = this._getTextWidth(r[col]);
if (col == 0) {
wi0 = wi;
} else if (col == 1) {
wi1 = wi;
}
columnWidths[col] = Math.max(wi, columnWidths[col]);
}
this._neededTextFieldWidth = Math.max(this._neededTextFieldWidth,
wi1+(withDescript ? wi0:0));
}
if (this.getShowOnTextField() == 'idAndDescription') {
this._neededTextFieldWidth += this._getTextWidth(this.getIdDescriptionSeparator());
}
this._neededTextFieldWidth += 8; /*Extra margins*/
var maxRows = this.getMaxVisibleRows(),
// Only assign room for the vertical scrollbar when needed
width = data.length > maxRows ? (new qx.ui.core.ScrollBar)._getScrollBarWidth():0,
colModel = this._list.getTableColumnModel(),
countVisible = 0;
// ##Only show headers if we have more than 1 column visible
for (col = 0; col < nCols; col++) {
if (colModel.isColumnVisible(col)) {
countVisible++;
}
}
var hasHeaders = countVisible > 1;
this._list.getPaneScroller(0).getHeader().setHeight(hasHeaders ? 'auto' : 1);
// ##Size each column
for (col = 0; col < nCols; col++) {
if (colModel.isColumnVisible(col)) {
var w = columnWidths[col];
if (hasHeaders) {
w = Math.max(w, this._getTextWidth(cols[col]));
}
w += 8;
this._list.setColumnWidth(col, w);
width += w;
}
}
// ##Final width and height
this._list.set({
width: width,
height: this._list.getRowHeight()*
Math.min(maxRows, (hasHeaders ? 1:0)+data.length)+2+(hasHeaders ? 2:0)
});
// This denotes dimensions are already calculated
this._calcDimensions = true;
}
/**Calculates the width of the given text.
* The default font is used.
* @return {Integer}*/
qx.Proto._getTextWidth = function(text) {
var lab = new qx.ui.basic.Label(text);
var res = lab.getPreferredBoxWidth();
lab.dispose();
return res;
}
/*
---------------------------------------------------------------------------
SEARCHING
---------------------------------------------------------------------------
*/
/**Does this combo have the searched dialog open?*/
qx.Proto.isSearchInProgress = function() {
return !this._popup.contains(this._list);
}
/**Searches the given text. Called from the search dialog.
* @param startIndex {number} Start index, 0 based
* @param txt {String} Text to find
* @param caseSens {Boolean} Case sensivity flag.*/
qx.Proto._search = function(startIndex, txt, caseSens) {
if (txt == null || !txt.length) {
return;
}
var row = startIndex,
nCols = this._model.getColumnCount(),
nRows = this.getSelection().length,
data = this._model.getData();
if (!caseSens) {
txt = txt.toLowerCase();
}
var colModel = this._list.getTableColumnModel();
while (true) {
var dataRow = data[row];
if (dataRow) {
for (var col = 0; col < nCols; col++) {
if (colModel.isColumnVisible(col)) {
var txtCol = dataRow[col];
if (!caseSens) {
txtCol = txtCol.toLowerCase();
}
if (txtCol.indexOf(txt) >= 0) {
this._manager.setSelectionInterval(row, row);
this._list.scrollCellVisible(1, row);
return;
}
}
}
}
row = (row+1)% nRows;
if (row == startIndex) {
break;
}
}
}
/**Opens a popup search dialog, useful when the combo has a lot of items.
* This dialog is triggered by double clicking the combo, pressing F3 or Ctrl+F.*/
qx.Proto.openSearchDialog = function() {
var sel = this.getSelection();
if (!sel || !sel.length || this.isSearchInProgress()) {
return;
}
this._testClosePopup();
var me = this,
oldSelectedIndex = this.getSelectedIndex(),
startIndex = oldSelectedIndex;
//###searchField
function search() {
me._search(startIndex, searchField.getComputedValue(), checkCase.isChecked());
}
var searchField = new qx.ui.form.TextField;
searchField.set({
minWidth: this._field.getWidth(),
width: '100%'
});
searchField.addEventListener("input", function() {
search();
});
//###checkCase
var checkCase = new qx.ui.form.CheckBox(this.tr("Case sensitive"));
checkCase.set({
horizontalAlign: 'center',
marginBottom: 4
});
//###vbox
var vbox = new qx.ui.layout.VerticalBoxLayout;
vbox.set({
spacing: 6,
horizontalChildrenAlign: 'center',
height: '100%'
});
vbox.auto();
vbox.add(searchField, checkCase);
//###list, we reuse the same list in the popup
this._calculateDimensions();
var border = qx.renderer.border.BorderPresets.getInstance().inset;
var newListSettings = {
/*minHeight: border.getTopWidth()+this._list.getHeight()+border.getBottomWidth(),
height: '1*',*/
height: border.getTopWidth()+this._list.getHeight()+border.getBottomWidth(),
width: border.getLeftWidth()+this._list.getWidth()+border.getRightWidth(),
border: border,
parent: vbox
};
// Save old list settings
var oldListSettings = {};
for (var prop in newListSettings) {
oldListSettings[prop] = this._list[qx.OO.getter[prop]]();
}
this._list.set(newListSettings);
//###buttons
var butNext = new qx.ui.form.Button('', 'icon/16/actions/edit-find.png');
butNext.set({
toolTip: new qx.ui.popup.ToolTip(this.tr("Search next occurrence"))
});
butNext.addEventListener("execute", function() {
startIndex = (this.getSelectedIndex()+1) % sel.length;
search();
}, this);
var butOk = new qx.ui.form.Button('', 'icon/16/actions/dialog-ok.png');
butOk.addEventListener('execute', function() {
oldSelectedIndex = null;
win.close();
}, this);
var butCancel = new qx.ui.form.Button('', 'icon/16/actions/dialog-cancel.png');
butCancel.addEventListener('execute', function() {
win.close();
}, this);
var butBox = new qx.ui.layout.VerticalBoxLayout;
butBox.auto();
butBox.set({
spacing: 10
});
butBox.add(butNext, butOk, butCancel);
//###hbox
var hbox = new qx.ui.layout.BoxLayout;
hbox.auto();
hbox.setPadding(10);
hbox.set({
spacing: 8,
minHeight: 'auto',
height: '100%'
});
hbox.add(vbox, butBox);
//###Window
var win = new qx.ui.window.Window(this.tr("Search items in list"), 'icon/16/actions/edit-find.png');
win.add(hbox);
win.positionRelativeTo(this);
win.set({
autoHide: true,
allowMaximize: false,
showMaximize: false,
allowMinimize: false,
showMinimize: false
});
win.addEventListener("appear", function() {
searchField.focus();
});
win.addEventListener("disappear", function() {
if (oldSelectedIndex != null) {
// Hit Cancel button
this.setSelectedIndex(oldSelectedIndex);
}
this._list.set(oldListSettings);
this.focus();
}, this);
win.addEventListener("keydown", function(e) {
switch (e.getKeyIdentifier()) {
case "Enter":
butOk.createDispatchEvent('execute');
break;
case "Escape":
butCancel.createDispatchEvent('execute');
break;
case "F3":
butNext.createDispatchEvent('execute');
break;
default:
return;
}
e.preventDefault();
}, this);
win.auto();
win.addToDocument();
win.open();
}
/*
---------------------------------------------------------------------------
OTHER EVENT HANDLER
---------------------------------------------------------------------------
*/
qx.Proto._onChangeSelection = function(e) {
this._fromSelected = true;
// only do this if we called setValue separately
// and not from the event "input".
if (!this._fromInput) {
var index = this.getSelectedIndex();
if (index >= 0) {
var row = this._model.getData()[index];
}
if (row || !this.getEditable()) {
this.setValue(row && row[0]);
}
// In case of editable, this.setValue() already calls this._field.setValue()
if (!this.getEditable()) {
var val = "";
if (row) {
val = this.getShowOnTextField() == 'description' ?
row[1] :
(row[0] != null && row[0] != '' ? row[0] + this.getIdDescriptionSeparator() + row[1]:row[1]);
}
this._field.setValue(val);
}
}
// reset hint
delete this._fromSelected;
}
qx.Proto._onpopupappear = function(e) {
var index = this.getSelectedIndex();
if (index >= 0) {
this._list.scrollCellVisible(0, index);
}
}
qx.Proto._oninput = function(e) {
// Hint for modifier
this._fromInput = true;
this.setValue(this._field.getComputedValue());
delete this._fromInput;
}
/*
---------------------------------------------------------------------------
MOUSE EVENT HANDLER
---------------------------------------------------------------------------
*/
qx.Proto._onmousedown = function(e) {
switch(e.getTarget()) {
case this._field:
if (this.getEditable()) {
break;
}
// no break here
case this._button:
this._button.addState("pressed");
this._togglePopup();
// Assure we receive the mouse up event
this.setCapture(true);
break;
}
}
qx.Proto._onmouseup = function(e) {
switch(e.getTarget()) {
case this._field:
if (this.getEditable()) {
break;
}
// no break here
default:
this._button.removeState("pressed");
break;
}
this.setCapture(false);
}
qx.Proto._onmousewheel = function(e) {
if (!this._popup.isSeeable()) {
this.setSelectedIndex(Math.max(0, this.getSelectedIndex()+(e.getWheelDelta() < 0 ? -1:1)));
}
}
/*
---------------------------------------------------------------------------
KEY EVENT HANDLER
---------------------------------------------------------------------------
*/
qx.Proto._onkeydown = function(e) {
var vVisible = this._popup.isSeeable();
switch (e.getKeyIdentifier()) {
case "Enter":
if (vVisible) {
this._closePopup();
this.setFocused(true);
} else {
this._openPopup();
}
break;
case "Escape":
if (vVisible) {
this.setSelectedIndex(this._oldSelected);
this._closePopup();
this.setFocused(true);
}
break;
case "Home":
this.setSelectedIndex(0);
break;
case "End":
var items = this.getSelection().length;
if (items) {
this.setSelectedIndex(items-1);
}
break;
case "Down":
if (e.isAltPressed()) {
this._togglePopup();
}
break;
case "F3":
if (this.getAllowSearch()) {
this.openSearchDialog();
}
break;
case "F":
if (e.isCtrlPressed()) {
if (this.getAllowSearch()) {
this.openSearchDialog();
}
break;
}
return;
default:
if (vVisible) {
this._list.dispatchEvent(e);
}
return;
}
e.preventDefault();
}
qx.Proto._onkeypress = function(e) {
var vVisible = this._popup.isSeeable();
switch (e.getKeyIdentifier()) {
case "Up":
this.setSelectedIndex(Math.max(0, this.getSelectedIndex()-1));
break;
case "Down":
this.setSelectedIndex(Math.max(0, this.getSelectedIndex()+1));
break;
case "PageUp":
this.setSelectedIndex(Math.max(0, this.getSelectedIndex()-this.getPagingInterval()));
break;
case "PageDown":
this.setSelectedIndex(this.getSelectedIndex()+this.getPagingInterval());
break;
default:
if (vVisible) {
this._list.dispatchEvent(e);
}
return;
}
e.preventDefault();
if (!this.isEditable() && this._list.isSeeable()) {
this._list.dispatchEvent(e);
}
}
/*
---------------------------------------------------------------------------
FOCUS HANDLING
---------------------------------------------------------------------------
*/
qx.Proto._visualizeBlur = function() {
// Force blur, even if mouseFocus is not active because we
// need to be sure that the previous focus rect gets removed.
// But this only needs to be done, if there is no new focused element.
if (qx.core.Client.getInstance().isMshtml()) {
if (this.getEnableElementFocus() && !this.getFocusRoot().getFocusedChild()) {
try {
if (this.getEditable()) {
this.getField().getElement().blur();
} else {
this.getElement().blur();
}
}
catch(ex) {};
}
} else {
if (this.getEnableElementFocus()) {
try {
if (this.getEditable()) {
this.getField().getElement().blur();
} else if (!this.getFocusRoot().getFocusedChild()) {
this.getElement().blur();
}
}
catch(ex) {};
}
}
this.removeState("focused");
return true;
}
qx.Proto._visualizeFocus = function() {
if (!qx.event.handler.FocusHandler.mouseFocus && this.getEnableElementFocus()) {
try {
if (this.getEditable()) {
this.getField().getElement().focus();
this.getField()._ontabfocus();
} else {
this.getElement().focus();
}
} catch(ex) {
}
}
this.addState("focused");
return true;
}
/*
---------------------------------------------------------------------------
DISPOSE
---------------------------------------------------------------------------
*/
qx.Proto.dispose = function() {
if (this.getDisposed()) {
return;
}
// ************************************************************************
// WIDGET MOUSE EVENTS
// ************************************************************************
this.removeEventListener("mousedown", this._onmousedown);
this.removeEventListener("mouseup", this._onmouseup);
this.removeEventListener("mousewheel", this._onmousewheel);
// ************************************************************************
// WIDGET KEY EVENTS
// ************************************************************************
this.removeEventListener("keydown", this._onkeydown);
this.removeEventListener("keypress", this._onkeypress);
this._model = null;
if (this._manager) {
this._manager.removeEventListener('changeSelection', this._onChangeSelection);
this._manager = null;
}
if (this._list) {
this._list.dispose();
this._list = null;
}
if (this._popup) {
this._popup.removeEventListener("appear", this._onpopupappear, this);
this._popup.dispose();
this._popup = null;
}
if (this._field) {
if (this.getEditable()) {
this._field.removeEventListener("input", this._oninput, this);
}
this._field.dispose();
this._field = null;
}
if (this._button) {
this._button.dispose();
this._button = null;
}
return qx.ui.layout.HorizontalBoxLayout.prototype.dispose.call(this);
}