summaryrefslogtreecommitdiff
path: root/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table
diff options
context:
space:
mode:
Diffstat (limited to 'webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table')
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractDataCellRenderer.js127
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractTableModel.js150
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/BooleanDataCellRenderer.js48
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CellEditorFactory.js62
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CheckBoxCellEditorFactory.js43
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataCellRenderer.js80
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataRowRenderer.js54
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataCellRenderer.js189
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataRowRenderer.js106
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultHeaderCellRenderer.js63
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/HeaderCellRenderer.js69
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconDataCellRenderer.js182
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconHeaderCellRenderer.js84
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/RemoteTableModel.js435
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionManager.js163
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionModel.js427
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SimpleTableModel.js335
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/Table.js1062
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableColumnModel.js399
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableModel.js243
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePane.js486
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneHeader.js276
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneModel.js179
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneScroller.js1331
-rw-r--r--webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TextFieldCellEditorFactory.js58
25 files changed, 6651 insertions, 0 deletions
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractDataCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractDataCellRenderer.js
new file mode 100644
index 0000000000..d3d7950bd5
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractDataCellRenderer.js
@@ -0,0 +1,127 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * An abstract data cell renderer that does the basic coloring
+ * (borders, selected look, ...).
+ */
+qx.OO.defineClass("qx.ui.table.AbstractDataCellRenderer", qx.ui.table.DataCellRenderer,
+function() {
+ qx.ui.table.DataCellRenderer.call(this);
+});
+
+
+// overridden
+qx.Proto.createDataCellHtml = function(cellInfo) {
+ var AbstractDataCellRenderer = qx.ui.table.AbstractDataCellRenderer;
+ return AbstractDataCellRenderer.MAIN_DIV_START + this._getCellStyle(cellInfo)
+ + AbstractDataCellRenderer.MAIN_DIV_START_END
+ + this._getContentHtml(cellInfo) + AbstractDataCellRenderer.MAIN_DIV_END;
+}
+
+
+// overridden
+qx.Proto.updateDataCellElement = function(cellInfo, cellElement) {
+ cellElement.innerHTML = this._getContentHtml(cellInfo);
+}
+
+
+/**
+ * Returns the CSS styles that should be applied to the main div of this cell.
+ *
+ * @param cellInfo {Map} The information about the cell.
+ * See {@link #createDataCellHtml}.
+ * @return the CSS styles of the main div.
+ */
+qx.Proto._getCellStyle = function(cellInfo) {
+ return cellInfo.style + qx.ui.table.AbstractDataCellRenderer.MAIN_DIV_STYLE;
+}
+
+
+/**
+ * Returns the HTML that should be used inside the main div of this cell.
+ *
+ * @param cellInfo {Map} The information about the cell.
+ * See {@link #createDataCellHtml}.
+ * @return {string} the inner HTML of the main div.
+ */
+qx.Proto._getContentHtml = function(cellInfo) {
+ return cellInfo.value;
+}
+
+
+qx.Proto.createDataCellHtml_array_join = function(cellInfo, htmlArr) {
+ var AbstractDataCellRenderer = qx.ui.table.AbstractDataCellRenderer;
+
+ if (qx.ui.table.TablePane.USE_TABLE) {
+ htmlArr.push(AbstractDataCellRenderer.TABLE_TD);
+ htmlArr.push(cellInfo.styleHeight);
+ htmlArr.push("px");
+ } else {
+ htmlArr.push(AbstractDataCellRenderer.ARRAY_JOIN_MAIN_DIV_LEFT);
+ htmlArr.push(cellInfo.styleLeft);
+ htmlArr.push(AbstractDataCellRenderer.ARRAY_JOIN_MAIN_DIV_WIDTH);
+ htmlArr.push(cellInfo.styleWidth);
+ htmlArr.push(AbstractDataCellRenderer.ARRAY_JOIN_MAIN_DIV_HEIGHT);
+ htmlArr.push(cellInfo.styleHeight);
+ htmlArr.push("px");
+ }
+
+ this._createCellStyle_array_join(cellInfo, htmlArr);
+
+ htmlArr.push(AbstractDataCellRenderer.ARRAY_JOIN_MAIN_DIV_START_END);
+
+ this._createContentHtml_array_join(cellInfo, htmlArr);
+
+ if (qx.ui.table.TablePane.USE_TABLE) {
+ htmlArr.push(AbstractDataCellRenderer.TABLE_TD_END);
+ } else {
+ htmlArr.push(AbstractDataCellRenderer.ARRAY_JOIN_MAIN_DIV_END);
+ }
+}
+
+
+qx.Proto._createCellStyle_array_join = function(cellInfo, htmlArr) {
+ htmlArr.push(qx.ui.table.AbstractDataCellRenderer.MAIN_DIV_STYLE);
+}
+
+
+qx.Proto._createContentHtml_array_join = function(cellInfo, htmlArr) {
+ htmlArr.push(cellInfo.value);
+}
+
+
+qx.Class.MAIN_DIV_START = '<div style="';
+qx.Class.MAIN_DIV_START_END = '">';
+qx.Class.MAIN_DIV_END = '</div>';
+qx.Class.MAIN_DIV_STYLE = ';overflow:hidden;white-space:nowrap;border-right:1px solid #eeeeee;border-bottom:1px solid #eeeeee;padding-left:2px;padding-right:2px;cursor:default'
+ + (qx.sys.Client.getInstance().isMshtml() ? '' : ';-moz-user-select:none;');
+
+qx.Class.ARRAY_JOIN_MAIN_DIV_LEFT = '<div style="position:absolute;left:';
+qx.Class.ARRAY_JOIN_MAIN_DIV_WIDTH = 'px;top:0px;width:';
+qx.Class.ARRAY_JOIN_MAIN_DIV_HEIGHT = 'px;height:';
+qx.Class.ARRAY_JOIN_MAIN_DIV_START_END = '">';
+qx.Class.ARRAY_JOIN_MAIN_DIV_END = '</div>';
+
+qx.Class.TABLE_TD = '<td style="height:';
+qx.Class.TABLE_TD_END = '</td>'; \ No newline at end of file
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractTableModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractTableModel.js
new file mode 100644
index 0000000000..99470e9361
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/AbstractTableModel.js
@@ -0,0 +1,150 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * An abstract table model that performs the column handling, so subclasses only
+ * need to care for row handling.
+ */
+qx.OO.defineClass("qx.ui.table.AbstractTableModel", qx.ui.table.TableModel,
+function() {
+ qx.ui.table.TableModel.call(this);
+
+ this._columnIdArr = [];
+ this._columnNameArr = [];
+ this._columnIndexMap = {};
+});
+
+
+// overridden
+qx.Proto.getColumnCount = function() {
+ return this._columnIdArr.length;
+}
+
+
+// overridden
+qx.Proto.getColumnIndexById = function(columnId) {
+ return this._columnIndexMap[columnId];
+}
+
+
+// overridden
+qx.Proto.getColumnId = function(columnIndex) {
+ return this._columnIdArr[columnIndex];
+}
+
+
+// overridden
+qx.Proto.getColumnName = function(columnIndex) {
+ return this._columnNameArr[columnIndex];
+}
+
+
+/**
+ * Sets the column IDs. These IDs may be used internally to identify a column.
+ * <p>
+ * Note: This will clear previously set column names.
+ * </p>
+ *
+ * @param columnIdArr {string[]} the IDs of the columns.
+ * @see #setColumns
+ */
+qx.Proto.setColumnIds = function(columnIdArr) {
+ this._columnIdArr = columnIdArr;
+
+ // Create the reverse map
+ this._columnIndexMap = {};
+ for (var i = 0; i < columnIdArr.length; i++) {
+ this._columnIndexMap[columnIdArr[i]] = i;
+ }
+ this._columnNameArr = new Array(columnIdArr.length);
+
+ // Inform the listeners
+ if (!this._internalChange) {
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+ }
+}
+
+
+/**
+ * Sets the column names. These names will be shown to the user.
+ * <p>
+ * Note: The column IDs have to be defined before.
+ * </p>
+ *
+ * @param columnNameArr {string[]} the names of the columns.
+ * @see #setColumnIds
+ */
+qx.Proto.setColumnNamesByIndex = function(columnNameArr) {
+ if (this._columnIdArr.length != columnNameArr.length) {
+ throw new Error("this._columnIdArr and columnNameArr have different length: "
+ + this._columnIdArr.length + " != " + columnNameArr.length);
+ }
+ this._columnNameArr = columnNameArr;
+
+ // Inform the listeners
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+}
+
+
+/**
+ * Sets the column names. These names will be shown to the user.
+ * <p>
+ * Note: The column IDs have to be defined before.
+ * </p>
+ *
+ * @param columnNameMap {Map} a map containing the column IDs as keys and the
+ * column name as values.
+ * @see #setColumnIds
+ */
+qx.Proto.setColumnNamesById = function(columnNameMap) {
+ this._columnNameArr = new Array(this._columnIdArr.length);
+ for (var i = 0; i < this._columnIdArr.length; ++i) {
+ this._columnNameArr[i] = columnNameMap[this._columnIdArr[i]];
+ }
+}
+
+
+/**
+ * Sets the columns.
+ *
+ * @param columnNameArr {string[]} The column names. These names will be shown to
+ * the user.
+ * @param columnIdArr {string[] ? null} The column IDs. These IDs may be used
+ * internally to identify a column. If null, the column names are used as
+ * IDs.
+ */
+qx.Proto.setColumns = function(columnNameArr, columnIdArr) {
+ if (columnIdArr == null) {
+ columnIdArr = columnNameArr;
+ }
+
+ if (columnIdArr.length != columnNameArr.length) {
+ throw new Error("columnIdArr and columnNameArr have different length: "
+ + columnIdArr.length + " != " + columnNameArr.length);
+ }
+
+ this._internalChange = true;
+ this.setColumnIds(columnIdArr);
+ this._internalChange = false;
+ this.setColumnNamesByIndex(columnNameArr);
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/BooleanDataCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/BooleanDataCellRenderer.js
new file mode 100644
index 0000000000..13df2cd2f4
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/BooleanDataCellRenderer.js
@@ -0,0 +1,48 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+ * Carsten Lergenmueller (carstenl)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A data cell renderer for boolean values.
+ */
+qx.OO.defineClass("qx.ui.table.BooleanDataCellRenderer", qx.ui.table.IconDataCellRenderer,
+function() {
+ qx.ui.table.IconDataCellRenderer.call(this);
+
+ this._iconUrlTrue = qx.manager.object.AliasManager.getInstance().resolvePath("widget/table/boolean-true.png");
+ this._iconUrlFalse = qx.manager.object.AliasManager.getInstance().resolvePath("widget/table/boolean-false.png");
+ this._iconUrlNull = qx.manager.object.AliasManager.getInstance().resolvePath("static/image/blank.gif");
+
+});
+
+//overridden
+qx.Proto._identifyImage = function(cellInfo) {
+ var IconDataCellRenderer = qx.ui.table.IconDataCellRenderer;
+ var imageHints = { imageWidth:11, imageHeight:11 };
+ switch (cellInfo.value) {
+ case true: imageHints.url = this._iconUrlTrue; break;
+ case false: imageHints.url = this._iconUrlFalse; break;
+ default: imageHints.url = this._iconUrlNull; break;
+ }
+ return imageHints;
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CellEditorFactory.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CellEditorFactory.js
new file mode 100644
index 0000000000..817954f40f
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CellEditorFactory.js
@@ -0,0 +1,62 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A factory creating widgets to use for editing table cells.
+ */
+qx.OO.defineClass("qx.ui.table.CellEditorFactory", qx.core.Object,
+function() {
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * Creates a cell editor.
+ * <p>
+ * The cellInfo map contains the following properties:
+ * <ul>
+ * <li>value (var): the cell's value.</li>
+ * <li>row (int): the model index of the row the cell belongs to.</li>
+ * <li>col (int): the model index of the column the cell belongs to.</li>
+ * <li>xPos (int): the x position of the cell in the table pane.</li>
+ * </ul>
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create.
+ * @return {qx.ui.core.Widget} the widget that should be used as cell editor.
+ */
+qx.Proto.createCellEditor = function(cellInfo) {
+ throw new Error("createCellEditor is abstract");
+}
+
+
+/**
+ * Returns the current value of a cell editor.
+ *
+ * @param cellEditor {qx.ui.core.Widget} The cell editor formally created by
+ * {@link #createCellEditor}.
+ * @return {var} the current value from the editor.
+ */
+qx.Proto.getCellEditorValue = function(cellEditor) {
+ throw new Error("getCellEditorValue is abstract");
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CheckBoxCellEditorFactory.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CheckBoxCellEditorFactory.js
new file mode 100644
index 0000000000..d5609a4b77
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/CheckBoxCellEditorFactory.js
@@ -0,0 +1,43 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by David Perez
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * David Perez (david-perez)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * For editing boolean data in a checkbox. It is advisable to use this in conjuntion with BooleanDataCellRenderer.
+ */
+qx.OO.defineClass("qx.ui.table.CheckBoxCellEditorFactory", qx.ui.table.CellEditorFactory, function() {
+ qx.ui.table.CellEditorFactory.call(this);
+});
+
+// overridden
+qx.Proto.createCellEditor = function(cellInfo) {
+ var editor = new qx.ui.form.CheckBox;
+ with (editor) {
+ setChecked(cellInfo.value);
+ }
+ return editor;
+}
+
+// overridden
+qx.Proto.getCellEditorValue = function(cellEditor) {
+ return cellEditor.getChecked();
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataCellRenderer.js
new file mode 100644
index 0000000000..46f808df32
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataCellRenderer.js
@@ -0,0 +1,80 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A cell renderer for data cells.
+ */
+qx.OO.defineClass("qx.ui.table.DataCellRenderer", qx.core.Object,
+function() {
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * Creates the HTML for a data cell.
+ * <p>
+ * The cellInfo map contains the following properties:
+ * <ul>
+ * <li>value (var): the cell's value.</li>
+ * <li>rowData (var): contains the row data for the row, the cell belongs to.
+ * The kind of this object depends on the table model, see
+ * {@link TableModel#getRowData()}</li>
+ * <li>row (int): the model index of the row the cell belongs to.</li>
+ * <li>col (int): the model index of the column the cell belongs to.</li>
+ * <li>table (qx.ui.table.Table): the table the cell belongs to.</li>
+ * <li>xPos (int): the x position of the cell in the table pane.</li>
+ * <li>selected (boolean): whether the cell is selected.</li>
+ * <li>focusedCol (boolean): whether the cell is in the same column as the
+ * focused cell.</li>
+ * <li>focusedRow (boolean): whether the cell is in the same row as the
+ * focused cell.</li>
+ * <li>editable (boolean): whether the cell is editable.</li>
+ * <li>style (string): The CSS styles that should be applied to the outer HTML
+ * element.</li>
+ * </ul>
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create.
+ * @return {string} the HTML of the data cell.
+ */
+qx.Proto.createDataCellHtml = function(cellInfo) {
+ throw new Error("createDataCellHtml is abstract");
+}
+
+
+/**
+ * Updates a data cell.
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create. This map has the same structure as in {@link #createDataCell}.
+ * @param cellElement {element} the DOM element that renders the data cell. This
+ * is the same element formally created by the HTML from {@link #createDataCell}.
+ */
+qx.Proto.updateDataCellElement = function(cellInfo, cellElement) {
+ throw new Error("updateDataCellElement is abstract");
+}
+
+
+qx.Proto.createDataCellHtml_array_join = function(cellInfo, htmlArr) {
+ throw new Error("createDataCellHtml_array_join is abstract");
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataRowRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataRowRenderer.js
new file mode 100644
index 0000000000..9cd4c86961
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DataRowRenderer.js
@@ -0,0 +1,54 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A cell renderer for data rows.
+ */
+qx.OO.defineClass("qx.ui.table.DataRowRenderer", qx.core.Object,
+function() {
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * Updates a data row.
+ * <p>
+ * The rowInfo map contains the following properties:
+ * <ul>
+ * <li>rowData (var): contains the row data for the row.
+ * The kind of this object depends on the table model, see
+ * {@link TableModel#getRowData()}</li>
+ * <li>row (int): the model index of the row.</li>
+ * <li>selected (boolean): whether a cell in this row is selected.</li>
+ * <li>focusedRow (boolean): whether the focused cell is in this row.</li>
+ * <li>table (qx.ui.table.Table): the table the row belongs to.</li>
+ * </ul>
+ *
+ * @param rowInfo {Map} A map containing the information about the row to
+ * update. This map has the same structure as in {@link #createDataCell}.
+ * @param cellElement {element} the DOM element that renders the data rot. This
+ * is the same element formally created by the HTML from {@link #createDataCell}.
+ */
+qx.Proto.updateDataRowElement = function(rowInfo, rowElement) {
+ throw new Error("updateDataRowElement is abstract");
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataCellRenderer.js
new file mode 100644
index 0000000000..4de4341037
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataCellRenderer.js
@@ -0,0 +1,189 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+// This is needed because of the instantiation at the end of this file.
+// I don't think this is a good idea. (wpbasti)
+#require(qx.util.format.NumberFormat)
+
+************************************************************************ */
+
+/**
+ * The default data cell renderer.
+ */
+qx.OO.defineClass("qx.ui.table.DefaultDataCellRenderer", qx.ui.table.AbstractDataCellRenderer,
+function() {
+ qx.ui.table.AbstractDataCellRenderer.call(this);
+});
+
+
+/**
+ * Whether the alignment should automatically be set according to the cell value.
+ * If true numbers will be right-aligned.
+ */
+qx.OO.addProperty({ name:"useAutoAlign", type:"boolean", defaultValue:true, allowNull:false });
+
+
+// overridden
+qx.Proto._getCellStyle = function(cellInfo) {
+ var style = qx.ui.table.AbstractDataCellRenderer.prototype._getCellStyle(cellInfo);
+
+ var stylesToApply = this._getStyleFlags(cellInfo);
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ALIGN_RIGHT){
+ style += ";text-align:right";
+ }
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_BOLD){
+ style += ";font-weight:bold";
+ }
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ITALIC){
+ style += ";font-style:italic";
+ }
+
+ return style;
+}
+
+/**
+ * Determines the styles to apply to the cell
+ *
+ * @param cellInfo {Object} cellInfo of the cell
+ * @return the sum of any of the STYLEFLAGS defined below
+ */
+qx.Proto._getStyleFlags = function(cellInfo) {
+ if (this.getUseAutoAlign()) {
+ if (typeof cellInfo.value == "number") {
+ return qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ALIGN_RIGHT;
+ }
+ }
+}
+
+
+// overridden
+qx.Proto._getContentHtml = function(cellInfo) {
+ return qx.ui.table.DefaultDataCellRenderer.escapeHtml(this._formatValue(cellInfo));
+}
+
+
+// overridden
+qx.Proto.updateDataCellElement = function(cellInfo, cellElement) {
+ var style = qx.ui.table.AbstractDataCellRenderer.prototype._getCellStyle(cellInfo);
+
+ var stylesToApply = this._getStyleFlags(cellInfo);
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ALIGN_RIGHT){
+ cellElement.style.textAlign = "right";
+ } else {
+ cellElement.style.textAlign = "";
+ }
+
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_BOLD){
+ cellElement.style.fontWeight = "bold";
+ } else {
+ cellElement.style.fontWeight = "";
+ }
+
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ITALIC){
+ cellElement.style.fontStyle = "ital";
+ } else {
+ cellElement.style.fontStyle = "";
+ }
+
+ var textNode = cellElement.firstChild;
+ if (textNode != null) {
+ textNode.nodeValue = this._formatValue(cellInfo);
+ } else {
+ cellElement.innerHTML = qx.ui.table.DefaultDataCellRenderer.escapeHtml(this._formatValue(cellInfo));
+ }
+}
+
+
+/**
+ * Formats a value.
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create. This map has the same structure as in
+ * {@link DataCellRenderer#createDataCell}.
+ * @return {string} the formatted value.
+ */
+qx.Proto._formatValue = function(cellInfo) {
+ var value = cellInfo.value;
+ if (value == null) {
+ return "";
+ } else if (typeof value == "number") {
+ return qx.ui.table.DefaultDataCellRenderer._numberFormat.format(value);
+ } else if (value instanceof Date) {
+ return qx.util.format.DateFormat.getDateInstance().format(value);
+ } else {
+ return value;
+ }
+}
+
+
+qx.Proto._createCellStyle_array_join = function(cellInfo, htmlArr) {
+ qx.ui.table.AbstractDataCellRenderer.prototype._createCellStyle_array_join(cellInfo, htmlArr);
+
+ var stylesToApply = this._getStyleFlags(cellInfo);
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ALIGN_RIGHT){
+ htmlArr.push(";text-align:right");
+ }
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_BOLD){
+ htmlArr.push(";font-weight:bold");
+ }
+ if (stylesToApply & qx.ui.table.DefaultDataCellRenderer.STYLEFLAG_ITALIC){
+ htmlArr.push(";font-style:italic");
+ }
+}
+
+
+qx.Proto._createContentHtml_array_join = function(cellInfo, htmlArr) {
+ htmlArr.push(qx.ui.table.DefaultDataCellRenderer.escapeHtml(this._formatValue(cellInfo)));
+}
+
+
+/**
+ * Escapes special HTML characters by their entities.
+ *
+ * @param html {string} The HTML to escape.
+ * @return {string} The escaped string showing HTML code as plain text.
+ */
+qx.Class.escapeHtml = function(html) {
+ return html.replace(/[<>&]/gi, qx.ui.table.DefaultDataCellRenderer._escapeHtmlReplacer);
+}
+
+
+/**
+ * Helper method for {@link #escapeHtml}.
+ */
+qx.Class._escapeHtmlReplacer = function(str) {
+ switch(str) {
+ case "<": return "&lt;";
+ case ">": return "&gt;";
+ case "&": return "&amp;";
+ }
+}
+
+
+qx.Class._numberFormat = new qx.util.format.NumberFormat();
+qx.Class._numberFormat.setMaximumFractionDigits(2);
+
+qx.Class.STYLEFLAG_ALIGN_RIGHT = 1;
+qx.Class.STYLEFLAG_BOLD = 2;
+qx.Class.STYLEFLAG_ITALIC = 4;
+
+
+
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataRowRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataRowRenderer.js
new file mode 100644
index 0000000000..8fd2198cd4
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultDataRowRenderer.js
@@ -0,0 +1,106 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * The default data row renderer.
+ */
+qx.OO.defineClass("qx.ui.table.DefaultDataRowRenderer", qx.ui.table.DataRowRenderer,
+function() {
+ qx.ui.table.DataRowRenderer.call(this);
+});
+
+
+/** Whether the focused row should be highlighted. */
+qx.OO.addProperty({ name:"highlightFocusRow", type:"boolean", allowNull:false, defaultValue:true});
+
+/**
+ * Whether the focused row and the selection should be grayed out when the table
+ * hasn't the focus.
+ */
+qx.OO.addProperty({ name:"visualizeFocusedState", type:"boolean", allowNull:false, defaultValue:true});
+
+
+// overridden
+qx.Proto.updateDataRowElement = function(rowInfo, rowElem) {
+ var clazz = qx.ui.table.DefaultDataRowRenderer;
+
+ if (rowInfo.focusedRow && this.getHighlightFocusRow()) {
+ if (rowInfo.table.getFocused() || !this.getVisualizeFocusedState()) {
+ rowElem.style.backgroundColor = rowInfo.selected ? clazz.BGCOL_FOCUSED_SELECTED : clazz.BGCOL_FOCUSED;
+ } else {
+ rowElem.style.backgroundColor = rowInfo.selected ? clazz.BGCOL_FOCUSED_SELECTED_BLUR : clazz.BGCOL_FOCUSED_BLUR;
+ }
+ } else {
+ if (rowInfo.selected) {
+ if (rowInfo.table.getFocused() || !this.getVisualizeFocusedState()) {
+ rowElem.style.backgroundColor = clazz.BGCOL_SELECTED;
+ } else {
+ rowElem.style.backgroundColor = clazz.BGCOL_SELECTED_BLUR;
+ }
+ } else {
+ rowElem.style.backgroundColor = (rowInfo.row % 2 == 0) ? clazz.BGCOL_EVEN : clazz.BGCOL_ODD;
+ }
+ }
+ rowElem.style.color = rowInfo.selected ? clazz.COL_SELECTED : clazz.COL_NORMAL;
+}
+
+
+qx.Proto._createRowStyle_array_join = function(rowInfo, htmlArr) {
+ var clazz = qx.ui.table.DefaultDataRowRenderer;
+
+ htmlArr.push(clazz.ARRAY_JOIN_BG_COLOR);
+ if (rowInfo.focusedRow && this.getHighlightFocusRow()) {
+ if (rowInfo.table.getFocused() || !this.getVisualizeFocusedState()) {
+ htmlArr.push(rowInfo.selected ? clazz.BGCOL_FOCUSED_SELECTED : clazz.BGCOL_FOCUSED);
+ } else {
+ htmlArr.push(rowInfo.selected ? clazz.BGCOL_FOCUSED_SELECTED_BLUR : clazz.BGCOL_FOCUSED_BLUR);
+ }
+ } else {
+ if (rowInfo.selected) {
+ if (rowInfo.table.getFocused() || !this.getVisualizeFocusedState()) {
+ htmlArr.push(clazz.BGCOL_SELECTED);
+ } else {
+ htmlArr.push(clazz.BGCOL_SELECTED_BLUR);
+ }
+ } else {
+ htmlArr.push((rowInfo.row % 2 == 0) ? clazz.BGCOL_EVEN : clazz.BGCOL_ODD);
+ }
+ }
+ htmlArr.push(clazz.ARRAY_JOIN_COLOR);
+ htmlArr.push(rowInfo.selected ? clazz.COL_SELECTED : clazz.COL);
+}
+
+
+qx.Class.BGCOL_FOCUSED_SELECTED = "#5a8ad3";
+qx.Class.BGCOL_FOCUSED_SELECTED_BLUR = "#b3bac6";
+qx.Class.BGCOL_FOCUSED = "#ddeeff";
+qx.Class.BGCOL_FOCUSED_BLUR = "#dae0e7";
+qx.Class.BGCOL_SELECTED = "#335ea8";
+qx.Class.BGCOL_SELECTED_BLUR = "#989ea8";
+qx.Class.BGCOL_EVEN = "#faf8f3";
+qx.Class.BGCOL_ODD = "white";
+qx.Class.COL_SELECTED = "white";
+qx.Class.COL_NORMAL = "black";
+
+qx.Class.ARRAY_JOIN_BG_COLOR = ";background-color:";
+qx.Class.ARRAY_JOIN_COLOR = ';color:';
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultHeaderCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultHeaderCellRenderer.js
new file mode 100644
index 0000000000..060b095a14
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/DefaultHeaderCellRenderer.js
@@ -0,0 +1,63 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * The default header cell renderer.
+ */
+qx.OO.defineClass("qx.ui.table.DefaultHeaderCellRenderer", qx.ui.table.HeaderCellRenderer,
+function() {
+ qx.ui.table.HeaderCellRenderer.call(this);
+});
+
+
+// overridden
+qx.Proto.createHeaderCell = function(cellInfo) {
+ var widget = new qx.ui.basic.Atom();
+ widget.setAppearance("table-header-cell");
+
+ this.updateHeaderCell(cellInfo, widget);
+
+ return widget;
+}
+
+
+// overridden
+qx.Proto.updateHeaderCell = function(cellInfo, cellWidget) {
+ var DefaultHeaderCellRenderer = qx.ui.table.DefaultHeaderCellRenderer;
+
+ cellWidget.setLabel(cellInfo.name);
+
+ cellWidget.setIcon(cellInfo.sorted ? (cellInfo.sortedAscending ? "widget/table/ascending.png" : "widget/table/descending.png") : null);
+ cellWidget.setState(DefaultHeaderCellRenderer.STATE_SORTED, cellInfo.sorted);
+ cellWidget.setState(DefaultHeaderCellRenderer.STATE_SORTED_ASCENDING, cellInfo.sortedAscending);
+}
+
+/**
+ * (string) The state which will be set for header cells of sorted columns.
+ */
+qx.Class.STATE_SORTED = "sorted";
+
+/**
+ * (string) The state which will be set when sorting is ascending.
+ */
+qx.Class.STATE_SORTED_ASCENDING = "sortedAscending";
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/HeaderCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/HeaderCellRenderer.js
new file mode 100644
index 0000000000..2108778efb
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/HeaderCellRenderer.js
@@ -0,0 +1,69 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A cell renderer for header cells.
+ */
+qx.OO.defineClass("qx.ui.table.HeaderCellRenderer", qx.core.Object,
+function() {
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * Creates a header cell.
+ * <p>
+ * The cellInfo map contains the following properties:
+ * <ul>
+ * <li>col (int): the model index of the column.</li>
+ * <li>xPos (int): the x position of the column in the table pane.</li>
+ * <li>name (string): the name of the column.</li>
+ * <li>editable (boolean): whether the column is editable.</li>
+ * <li>sorted (boolean): whether the column is sorted.</li>
+ * <li>sortedAscending (boolean): whether sorting is ascending.</li>
+ * </ul>
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create.
+ * @return {qx.ui.core.Widget} the widget that renders the header cell.
+ */
+qx.Proto.createHeaderCell = function(cellInfo) {
+ throw new Error("createHeaderCell is abstract");
+}
+
+
+/**
+ * Updates a header cell.
+ *
+ * @param cellInfo {Map} A map containing the information about the cell to
+ * create. This map has the same structure as in {@link #createHeaderCell}.
+ * @param cellWidget {qx.ui.core.Widget} the widget that renders the header cell. This is
+ * the same widget formally created by {@link #createHeaderCell}.
+ */
+qx.Proto.updateHeaderCell = function(cellInfo, cellWidget) {
+ throw new Error("updateHeaderCell is abstract");
+}
+
+
+/** The preferred height of cells created by this header renderer. */
+qx.OO.addProperty({ name:"prefferedCellHeight", type:"number", defaultValue:16, allowNull:false });
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconDataCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconDataCellRenderer.js
new file mode 100644
index 0000000000..b4a717527b
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconDataCellRenderer.js
@@ -0,0 +1,182 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+ * Carsten Lergenmueller (carstenl)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A data cell renderer for boolean values.
+ */
+qx.OO.defineClass("qx.ui.table.IconDataCellRenderer", qx.ui.table.AbstractDataCellRenderer,
+function() {
+ qx.ui.table.AbstractDataCellRenderer.call(this);
+ this.IMG_BLANK_URL = qx.manager.object.AliasManager.getInstance().resolvePath("static/image/blank.gif");
+});
+
+
+/**
+ * Identifies the Image to show.
+ *
+ * @param cellInfo {Map} The information about the cell.
+ * See {@link #createDataCellHtml}.
+ * @return {Map} A map having the following attributes:
+ * <ul>
+ * <li>"url": (type string) must be the URL of the image to show.</li>
+ * <li>"imageWidth": (type int) the width of the image in pixels.</li>
+ * <li>"imageHeight": (type int) the height of the image in pixels.</li>
+ * <li>"tooltip": (type string) must be the image tooltip text.</li>
+ * </ul>
+ */
+qx.Proto._identifyImage = function(cellInfo) {
+ throw new Error("_identifyImage is abstract");
+}
+
+
+/**
+ * Retrieves the image infos.
+ *
+ * @param cellInfo {Map} The information about the cell.
+ * See {@link #createDataCellHtml}.
+ * @return {Map} Map with an "url" attribute (type string)
+ * holding the URL of the image to show
+ * and a "tooltip" attribute
+ * (type string) being the tooltip text (or null if none was specified)
+ *
+ */
+qx.Proto._getImageInfos= function(cellInfo) {
+ // Query the subclass about image and tooltip
+ var urlAndTooltipMap = this._identifyImage(cellInfo);
+
+ // If subclass refuses to give map, construct it
+ if (urlAndTooltipMap == null || typeof urlAndTooltipMap == "string"){
+ urlAndTooltipMap = {url:urlAndTooltipMap, tooltip:null};
+ }
+
+ // If subclass gave null as url, replace with url to empty image
+ if (urlAndTooltipMap.url == null){
+ urlAndTooltipMap.url = this.IMG_BLANK_URL;
+ }
+
+ return urlAndTooltipMap;
+}
+
+// overridden
+qx.Proto._getCellStyle = function(cellInfo) {
+ var style = qx.ui.table.AbstractDataCellRenderer.prototype._getCellStyle(cellInfo);
+ style += qx.ui.table.IconDataCellRenderer.MAIN_DIV_STYLE;
+ return style;
+}
+
+
+// overridden
+qx.Proto._getContentHtml = function(cellInfo) {
+ var IconDataCellRenderer = qx.ui.table.IconDataCellRenderer;
+
+ var urlAndToolTip = this._getImageInfos(cellInfo);
+ var html = IconDataCellRenderer.IMG_START;
+ if (qx.sys.Client.getInstance().isMshtml() && /\.png$/i.test(urlAndToolTip.url)) {
+ html += qx.manager.object.AliasManager.getInstance().resolvePath("static/image/blank.gif")
+ + '" style="filter:' + "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + urlAndToolTip.url + "',sizingMethod='scale')";
+ } else {
+ html += urlAndToolTip.url + '" style="';
+ }
+
+ if (urlAndToolTip.imageWidth && urlAndToolTip.imageHeight) {
+ html += ';width:' + urlAndToolTip.imageWidth + 'px'
+ + ';height:' + urlAndToolTip.imageHeight + 'px';
+ }
+
+ var tooltip = urlAndToolTip.tooltip;
+ if (tooltip != null){
+ html += IconDataCellRenderer.IMG_TITLE_START + tooltip;
+ }
+ html += IconDataCellRenderer.IMG_END;
+ return html;
+}
+
+
+// overridden
+qx.Proto.updateDataCellElement = function(cellInfo, cellElement) {
+ // Set image and tooltip text
+ var urlAndToolTip = this._getImageInfos(cellInfo);
+ var img = cellElement.firstChild;
+ if (qx.sys.Client.getInstance().isMshtml()) {
+ if (/\.png$/i.test(urlAndToolTip.url)) {
+ img.src = qx.manager.object.AliasManager.getInstance().resolvePath("static/image/blank.gif");
+ img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + urlAndToolTip.url + "',sizingMethod='scale')";
+ } else {
+ img.src = urlAndToolTip.url;
+ img.style.filter = "";
+ }
+ } else {
+ img.src = urlAndToolTip.url;
+ }
+
+ if (urlAndToolTip.imageWidth && urlAndToolTip.imageHeight) {
+ img.style.width = urlAndToolTip.imageWidth + "px";
+ img.style.height = urlAndToolTip.imageHeight + "px";
+ }
+
+ if (urlAndToolTip.tooltip != null){
+ img.text = urlAndToolTip.tooltip;
+ }
+}
+
+
+// overridden
+qx.Proto._createCellStyle_array_join = function(cellInfo, htmlArr) {
+ qx.ui.table.AbstractDataCellRenderer.prototype._createCellStyle_array_join(cellInfo, htmlArr);
+
+ htmlArr.push(qx.ui.table.IconDataCellRenderer.MAIN_DIV_STYLE);
+}
+
+qx.Proto._createContentHtml_array_join = function(cellInfo, htmlArr) {
+ var IconDataCellRenderer = qx.ui.table.IconDataCellRenderer;
+
+ if (qx.ui.table.TablePane.USE_TABLE) {
+ htmlArr.push(IconDataCellRenderer.TABLE_DIV);
+ htmlArr.push(cellInfo.styleHeight - 2); // -1 for the border, -1 for the padding
+ htmlArr.push(IconDataCellRenderer.TABLE_DIV_CLOSE);
+ }
+
+ htmlArr.push(IconDataCellRenderer.IMG_START);
+ var urlAndToolTip = this._getImageInfos(cellInfo);
+ htmlArr.push(urlAndToolTip.url);
+ var tooltip = urlAndToolTip.tooltip;
+ if (tooltip != null){
+ IconDataCellRenderer.IMG_TITLE_START;
+ htmlArr.push(tooltip);
+ }
+ htmlArr.push(IconDataCellRenderer.IMG_END);
+
+ if (qx.ui.table.TablePane.USE_TABLE) {
+ htmlArr.push(IconDataCellRenderer.TABLE_DIV_END);
+ }
+}
+
+qx.Class.MAIN_DIV_STYLE = ';text-align:center;padding-top:1px;';
+qx.Class.IMG_START = '<img src="';
+qx.Class.IMG_END = '"/>';
+qx.Class.IMG_TITLE_START = '" title="';
+qx.Class.TABLE_DIV = '<div style="overflow:hidden;height:';
+qx.Class.TABLE_DIV_CLOSE = 'px">';
+qx.Class.TABLE_DIV_END = '</div>';
+
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconHeaderCellRenderer.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconHeaderCellRenderer.js
new file mode 100644
index 0000000000..51e653f5c4
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/IconHeaderCellRenderer.js
@@ -0,0 +1,84 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+ * Carsten Lergenmueller (carstenl)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A header cell renderer which renders an icon (only). The icon cannot be combined
+ * with text.
+ *
+ * @param iconUrl {string} URL to the icon to show
+ * @param tooltip {string ? ""} Text of the tooltip to show if the mouse hovers over the
+ * icon
+ *
+ */
+qx.OO.defineClass("qx.ui.table.IconHeaderCellRenderer", qx.ui.table.DefaultHeaderCellRenderer,
+function(iconUrl, tooltip) {
+ qx.ui.table.DefaultHeaderCellRenderer.call(this);
+ if (iconUrl == null){
+ iconUrl = "";
+ }
+ this.setIconUrl(iconUrl);
+ this.setToolTip(tooltip);
+});
+
+/**
+ * URL of the icon to show
+ */
+qx.OO.addProperty({ name:"iconUrl", type:"string", defaultValue:"", allowNull:false });
+
+/**
+ * ToolTip to show if the mouse hovers of the icon
+ */
+qx.OO.addProperty({ name:"toolTip", type:"string", defaultValue:null, allowNull:true });
+
+// overridden
+qx.Proto.updateHeaderCell = function(cellInfo, cellWidget) {
+ qx.ui.table.DefaultHeaderCellRenderer.prototype.updateHeaderCell.call(this, cellInfo, cellWidget);
+
+ // Set URL to icon
+ var img = cellWidget.getUserData("qx_ui_table_IconHeaderCellRenderer_icon");
+ if (img == null){
+ img = new qx.ui.basic.Image();
+ cellWidget.setUserData("qx_ui_table_IconHeaderCellRenderer_icon", img);
+ cellWidget.addAtBegin(img);
+ }
+ img.setSource(this.getIconUrl());
+
+ // Set image tooltip if given
+ var widgetToolTip = cellWidget.getToolTip();
+ if (this.getToolTip() != null){
+
+ //Create tooltip if necessary
+ if (true || widgetToolTip == null ){
+ widgetToolTip = new qx.ui.popup.ToolTip(this.getToolTip());
+ cellWidget.setToolTip(widgetToolTip);
+ //this.debug("Creating tooltip");
+ }
+
+ //Set tooltip text
+ widgetToolTip.getAtom().setLabel(this.getToolTip());
+ //this.debug("Setting tooltip text " + this.getToolTip());
+ }
+
+}
+
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/RemoteTableModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/RemoteTableModel.js
new file mode 100644
index 0000000000..ebd1be8f53
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/RemoteTableModel.js
@@ -0,0 +1,435 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A table model that loads its data from a backend.
+ * <p>
+ * Only those rows are loaded that are near the area the user is currently
+ * viewing. If the user scrolls, the rows he will see soon are loaded
+ * asynchroniously in the background. All loaded data is managed in a cache that
+ * automatically removes the last resently used rows when it gets full.
+ * <p>
+ * This class is abstract: The actual loading of row data must be done by
+ * subclasses.
+ */
+qx.OO.defineClass("qx.ui.table.RemoteTableModel", qx.ui.table.AbstractTableModel,
+function() {
+ qx.ui.table.AbstractTableModel.call(this);
+
+ this._sortColumnIndex = -1;
+ this._sortAscending = true;
+ this._rowCount = -1;
+
+ this._lruCounter = 0;
+ this._firstLoadingBlock = -1;
+ this._firstRowToLoad = -1;
+ this._lastRowToLoad = -1;
+ this._ignoreCurrentRequest = false;
+
+ this._rowBlockCache = {};
+ this._rowBlockCount = 0;
+});
+
+
+/** The number of rows that are stored in one cache block. */
+qx.OO.addProperty({ name:"blockSize", type:"number", defaultValue:50, allowNull:false });
+
+/** The maximum number of row blocks kept in the cache. */
+qx.OO.addProperty({ name:"maxCachedBlockCount", type:"number", defaultValue:15, allowNull:false });
+
+/**
+ * Whether to clear the cache when some rows are removed.
+ * If false the rows are removed locally in the cache.
+ */
+qx.OO.addProperty({ name:"clearCacheOnRemove", type:"boolean", defaultValue:false, allowNull:false });
+
+
+// overridden
+qx.Proto.getRowCount = function() {
+ if (this._rowCount == -1) {
+ this._loadRowCount();
+
+ // NOTE: _loadRowCount may set this._rowCount
+ return (this._rowCount == -1) ? 0 : this._rowCount;
+ } else {
+ return this._rowCount;
+ }
+}
+
+
+/**
+ * Loads the row count from the server.
+ * <p>
+ * Implementing classes have to call {@link _onRowDataLoaded()} when the server
+ * response arrived. That method has to be called! Even when there was an error.
+ */
+qx.Proto._loadRowCount = function() {
+ throw new Error("_loadRowCount is abstract");
+};
+
+
+/**
+ * Sets the row count.
+ * <p>
+ * Has to be called by {@link _loadRowCount()}.
+ *
+ * @param rowCount {int} the number of rows in this model or null if loading.
+ */
+qx.Proto._onRowCountLoaded = function(rowCount) {
+ this.debug("row count loaded: " + rowCount);
+ if (rowCount == null) {
+ rowCount = 0;
+ }
+ this._rowCount = rowCount;
+
+ // Inform the listeners
+ var data = { firstRow:0, lastRow:rowCount - 1, firstColumn:0, lastColumn:this.getColumnCount() - 1 };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+};
+
+
+/**
+ * Reloads the model and clears the local cache.
+ */
+qx.Proto.reloadData = function() {
+ this.clearCache();
+
+ // If there is currently a request on its way, then this request will bring
+ // obsolete data -> Ignore it
+ if (this._firstLoadingBlock != -1) {
+ this._ignoreCurrentRequest = true;
+ }
+
+ // NOTE: This will inform the listeners as soon as the new row count is known
+ this._loadRowCount();
+};
+
+
+/**
+ * Clears the cache.
+ */
+qx.Proto.clearCache = function() {
+ this._rowBlockCache = {};
+ this._rowBlockCount = 0;
+};
+
+
+// overridden
+qx.Proto.prefetchRows = function(firstRowIndex, lastRowIndex) {
+ // this.debug("Prefetch wanted: " + firstRowIndex + ".." + lastRowIndex);
+ if (this._firstLoadingBlock == -1) {
+ var blockSize = this.getBlockSize();
+ var totalBlockCount = Math.ceil(this._rowCount / blockSize);
+
+ // There is currently no request running -> Start a new one
+ // NOTE: We load one more block above and below to have a smooth
+ // scrolling into the next block without blank cells
+ var firstBlock = parseInt(firstRowIndex / blockSize) - 1;
+ if (firstBlock < 0) {
+ firstBlock = 0;
+ }
+ var lastBlock = parseInt(lastRowIndex / blockSize) + 1;
+ if (lastBlock >= totalBlockCount) {
+ lastBlock = totalBlockCount - 1;
+ }
+
+ // Check which blocks we have to load
+ var firstBlockToLoad = -1;
+ var lastBlockToLoad = -1;
+ for (var block = firstBlock; block <= lastBlock; block++) {
+ if (this._rowBlockCache[block] == null || this._rowBlockCache[block].isDirty) {
+ // We don't have this block
+ if (firstBlockToLoad == -1) {
+ firstBlockToLoad = block;
+ }
+ lastBlockToLoad = block;
+ }
+ }
+
+ // Load the blocks
+ if (firstBlockToLoad != -1) {
+ this._firstRowToLoad = -1;
+ this._lastRowToLoad = -1;
+
+ this._firstLoadingBlock = firstBlockToLoad;
+
+ this.debug("Starting server request. rows: " + firstRowIndex + ".." + lastRowIndex + ", blocks: " + firstBlockToLoad + ".." + lastBlockToLoad);
+ this._loadRowData(firstBlockToLoad * blockSize, (lastBlockToLoad + 1) * blockSize - 1);
+ }
+ } else {
+ // There is already a request running -> Remember this request
+ // so it can be executed after the current one is finished.
+ this._firstRowToLoad = firstRowIndex;
+ this._lastRowToLoad = lastRowIndex;
+ }
+};
+
+
+/**
+ * Loads some row data from the server.
+ * <p>
+ * Implementing classes have to call {@link _onRowDataLoaded()} when the server
+ * response arrived. That method has to be called! Even when there was an error.
+ *
+ * @param firstRow {int} The index of the first row to load.
+ * @param lastRow {int} The index of the last row to load.
+ */
+qx.Proto._loadRowData = function(firstRow, lastRow) {
+ throw new Error("_loadRowCount is abstract");
+};
+
+
+/**
+ * Sets row data.
+ * <p>
+ * Has to be called by {@link _loadRowData()}.
+ *
+ * @param rowDataArr {Map[]} the loaded row data or null if there was an error.
+ */
+qx.Proto._onRowDataLoaded = function(rowDataArr) {
+ if (rowDataArr != null && ! this._ignoreCurrentRequest) {
+ var blockSize = this.getBlockSize();
+ var blockCount = Math.ceil(rowDataArr.length / blockSize);
+ if (blockCount == 1) {
+ // We got one block -> Use the rowData directly
+ this._setRowBlockData(this._firstLoadingBlock, rowDataArr);
+ } else {
+ // We got more than one block -> We've to split the rowData
+ for (var i = 0; i < blockCount; i++) {
+ var rowOffset = i * blockSize;
+ var blockRowData = [];
+ var mailCount = Math.min(blockSize, rowDataArr.length - rowOffset);
+ for (var row = 0; row < mailCount; row++) {
+ blockRowData.push(rowDataArr[rowOffset + row]);
+ }
+
+ this._setRowBlockData(this._firstLoadingBlock + i, blockRowData);
+ }
+ }
+ this.debug("Got server answer. blocks: " + this._firstLoadingBlock + ".." + (this._firstLoadingBlock + blockCount - 1) + ". mail count: " + rowDataArr.length + " block count:" + blockCount);
+
+ // Inform the listeners
+ var data = {
+ firstRow:this._firstLoadingBlock * blockSize,
+ lastRow:(this._firstLoadingBlock + blockCount + 1) * blockSize - 1,
+ firstColumn:0,
+ lastColumn:this.getColumnCount() - 1
+ };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ }
+
+ // We're not loading any blocks any more
+ this._firstLoadingBlock = -1;
+ this._ignoreCurrentRequest = false;
+
+ // Check whether we have to start a new request
+ if (this._firstRowToLoad != -1) {
+ this.prefetchRows(this._firstRowToLoad, this._lastRowToLoad);
+ }
+};
+
+
+/**
+ * Sets the data of one block.
+ *
+ * @param block {int} the index of the block.
+ * @param rowDataArr {var[][]} the data to set.
+ */
+qx.Proto._setRowBlockData = function(block, rowDataArr) {
+ if (this._rowBlockCache[block] == null) {
+ // This is a new block -> Check whether we have to remove another block first
+ this._rowBlockCount++;
+
+ while (this._rowBlockCount > this.getMaxCachedBlockCount()) {
+ // Find the last recently used block
+ // NOTE: We never remove block 0 and 1
+ var lruBlock;
+ var minLru = this._lruCounter;
+ for (var currBlock in this._rowBlockCache) {
+ var currLru = this._rowBlockCache[currBlock].lru;
+ if (currLru < minLru && currBlock > 1) {
+ minLru = currLru;
+ lruBlock = currBlock;
+ }
+ }
+
+ // Remove that block
+ this.debug("Removing block: " + lruBlock + ". current LRU: " + this._lruCounter);
+ delete this._rowBlockCache[lruBlock];
+ this._rowBlockCount--;
+ }
+ }
+
+ this._rowBlockCache[block] = { lru:++this._lruCounter, rowDataArr:rowDataArr };
+};
+
+
+/**
+ * Removes a rows from the model.
+ *
+ * @param rowIndex {int} the index of the row to remove.
+ */
+qx.Proto.removeRow = function(rowIndex) {
+ if (this.getClearCacheOnRemove()) {
+ this.clearCache();
+
+ // Inform the listeners
+ var data = { firstRow:0, lastRow:rowCount - 1, firstColumn:0, lastColumn:this.getColumnCount() - 1 };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ } else {
+ var blockSize = this.getBlockSize();
+ var blockCount = Math.ceil(this.getRowCount() / blockSize);
+ var startBlock = parseInt(rowIndex / blockSize);
+
+ // Remove the row and move the rows of all following blocks
+ for (var block = startBlock; block <= blockCount; block++) {
+ var blockData = this._rowBlockCache[block];
+ if (blockData != null) {
+ // Remove the row in the start block
+ // NOTE: In the other blocks the first row is removed
+ // (This is the row that was)
+ var removeIndex = 0;
+ if (block == startBlock) {
+ removeIndex = rowIndex - block * blockSize;
+ }
+ blockData.rowDataArr.splice(removeIndex, 1);
+
+ if (block == blockCount - 1) {
+ // This is the last block
+ if (blockData.rowDataArr.length == 0) {
+ // It is empty now -> Remove it
+ delete this._rowBlockCache[block];
+ }
+ } else {
+ // Try to copy the first row of the next block to the end of this block
+ // so this block can stays clean
+ var nextBlockData = this._rowBlockCache[block + 1];
+ if (nextBlockData != null) {
+ blockData.rowDataArr.push(nextBlockData.rowDataArr[0]);
+ } else {
+ // There is no row to move -> Mark this block as dirty
+ blockData.isDirty = true;
+ }
+ }
+ }
+ }
+
+ if (this._rowCount != -1) {
+ this._rowCount--;
+ }
+
+ // Inform the listeners
+ if (this.hasEventListeners(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED)) {
+ var data = { firstRow:rowIndex, lastRow:this.getRowCount() - 1, firstColumn:0, lastColumn:this.getColumnCount() - 1 };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ }
+ }
+};
+
+
+/**
+ * <p>See overridden method for details.</p>
+ *
+ * @param rowIndex {int} the model index of the row.
+ * @return {Object} Map containing a value for each column.
+ */
+qx.Proto.getRowData = function(rowIndex) {
+ var blockSize = this.getBlockSize();
+ var block = parseInt(rowIndex / blockSize);
+ var blockData = this._rowBlockCache[block];
+ if (blockData == null) {
+ // This block is not (yet) loaded
+ return null;
+ } else {
+ var rowData = blockData.rowDataArr[rowIndex - (block * blockSize)];
+
+ // Update the last recently used counter
+ if (blockData.lru != this._lruCounter) {
+ blockData.lru = ++this._lruCounter;
+ }
+
+ return rowData;
+ }
+};
+
+
+// overridden
+qx.Proto.getValue = function(columnIndex, rowIndex) {
+ var rowData = this.getRowData(rowIndex);
+ if (rowData == null) {
+ return null;
+ } else {
+ var columnId = this.getColumnId(columnIndex);
+ return rowData[columnId];
+ }
+};
+
+
+/**
+ * Sets whether a column is sortable.
+ *
+ * @param columnIndex {int} the column of which to set the sortable state.
+ * @param sortable {boolean} whether the column should be sortable.
+ */
+qx.Proto.setColumnSortable = function(columnIndex, sortable) {
+ if (sortable != this.isColumnSortable(columnIndex)) {
+ if (this._sortableColArr == null) {
+ this._sortableColArr = [];
+ }
+ this._sortableColArr[columnIndex] = sortable;
+
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+ }
+}
+
+
+// overridden
+qx.Proto.isColumnSortable = function(columnIndex) {
+ return this._sortableColArr ? (this._sortableColArr[columnIndex] == true) : false;
+}
+
+
+// overridden
+qx.Proto.sortByColumn = function(columnIndex, ascending) {
+ if (this._sortColumnIndex != columnIndex || this._sortAscending != ascending) {
+ this._sortColumnIndex = columnIndex;
+ this._sortAscending = ascending;
+
+ this.clearCache();
+
+ // Inform the listeners
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+ }
+};
+
+
+// overridden
+qx.Proto.getSortColumnIndex = function() {
+ return this._sortColumnIndex;
+}
+
+
+// overridden
+qx.Proto.isSortAscending = function() {
+ return this._sortAscending;
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionManager.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionManager.js
new file mode 100644
index 0000000000..715b0d9d96
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionManager.js
@@ -0,0 +1,163 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A selection manager. This is a helper class that handles all selection
+ * related events and updates a SelectionModel.
+ * <p>
+ * Widgets that support selection should use this manager. This way the only
+ * thing the widget has to do is mapping mouse or key events to indexes and
+ * call the corresponding handler method.
+ *
+ * @see SelectionModel
+ */
+qx.OO.defineClass("qx.ui.table.SelectionManager", qx.core.Object,
+function() {
+ qx.core.Object.call(this);
+});
+
+
+/**
+ * The selection model where to set the selection changes.
+ */
+qx.OO.addProperty({ name:"selectionModel", type:"object", instance:"qx.ui.table.SelectionModel" });
+
+
+/**
+ * Handles the mouse down event.
+ *
+ * @param index {int} the index the mouse is pointing at.
+ * @param evt {Map} the mouse event.
+ */
+qx.Proto.handleMouseDown = function(index, evt) {
+ if (evt.isLeftButtonPressed()) {
+ var selectionModel = this.getSelectionModel();
+ if (!selectionModel.isSelectedIndex(index)) {
+ // This index is not selected -> We react when the mouse is pressed (because of drag and drop)
+ this._handleSelectEvent(index, evt);
+ this._lastMouseDownHandled = true;
+ } else {
+ // This index is already selected -> We react when the mouse is released (because of drag and drop)
+ this._lastMouseDownHandled = false;
+ }
+ } else if (evt.isRightButtonPressed() && evt.getModifiers() == 0) {
+ var selectionModel = this.getSelectionModel();
+ if (!selectionModel.isSelectedIndex(index)) {
+ // This index is not selected -> Set the selection to this index
+ selectionModel.setSelectionInterval(index, index);
+ }
+ }
+}
+
+
+/**
+ * Handles the mouse up event.
+ *
+ * @param index {int} the index the mouse is pointing at.
+ * @param evt {Map} the mouse event.
+ */
+qx.Proto.handleMouseUp = function(index, evt) {
+ if (evt.isLeftButtonPressed() && !this._lastMouseDownHandled) {
+ this._handleSelectEvent(index, evt);
+ }
+}
+
+
+/**
+ * Handles the mouse click event.
+ *
+ * @param index {int} the index the mouse is pointing at.
+ * @param evt {Map} the mouse event.
+ */
+qx.Proto.handleClick = function(index, evt) {
+}
+
+
+/**
+ * Handles the key down event that is used as replacement for mouse clicks
+ * (Normally space).
+ *
+ * @param index {int} the index that is currently focused.
+ * @param evt {Map} the key event.
+ */
+qx.Proto.handleSelectKeyDown = function(index, evt) {
+ this._handleSelectEvent(index, evt);
+};
+
+
+/**
+ * Handles a key down event that moved the focus (E.g. up, down, home, end, ...).
+ *
+ * @param index {int} the index that is currently focused.
+ * @param evt {Map} the key event.
+ */
+qx.Proto.handleMoveKeyDown = function(index, evt) {
+ var selectionModel = this.getSelectionModel();
+ switch (evt.getModifiers()) {
+ case 0:
+ selectionModel.setSelectionInterval(index, index);
+ break;
+ case qx.event.type.DomEvent.SHIFT_MASK:
+ var anchor = selectionModel.getAnchorSelectionIndex();
+ if (anchor == -1) {
+ selectionModel.setSelectionInterval(index, index);
+ } else {
+ selectionModel.setSelectionInterval(anchor, index);
+ }
+ break;
+ }
+}
+
+
+/**
+ * Handles a select event.
+ *
+ * @param index {int} the index the event is pointing at.
+ * @param evt {Map} the mouse event.
+ */
+qx.Proto._handleSelectEvent = function(index, evt) {
+ var selectionModel = this.getSelectionModel();
+ if (evt.getShiftKey()) {
+ var leadIndex = selectionModel.getLeadSelectionIndex();
+ if (index != leadIndex || selectionModel.isSelectionEmpty()) {
+ // The lead selection index was changed
+ var anchorIndex = selectionModel.getAnchorSelectionIndex();
+ if (anchorIndex == -1) {
+ anchorIndex = index;
+ }
+ if (evt.isCtrlOrCommandPressed()) {
+ selectionModel.addSelectionInterval(anchorIndex, index);
+ } else {
+ selectionModel.setSelectionInterval(anchorIndex, index);
+ }
+ }
+ } else if (evt.isCtrlOrCommandPressed()) {
+ if (selectionModel.isSelectedIndex(index)) {
+ selectionModel.removeSelectionInterval(index, index);
+ } else {
+ selectionModel.addSelectionInterval(index, index);
+ }
+ } else {
+ selectionModel.setSelectionInterval(index, index);
+ }
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionModel.js
new file mode 100644
index 0000000000..fb0f6b7317
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SelectionModel.js
@@ -0,0 +1,427 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A selection model.
+ *
+ * @event changeSelection {qx.event.type.Event} Fired when the selection has
+ * changed.
+ */
+qx.OO.defineClass("qx.ui.table.SelectionModel", qx.core.Target,
+function() {
+ qx.core.Target.call(this);
+
+ this._selectedRangeArr = [];
+ this._anchorSelectionIndex = -1;
+ this._leadSelectionIndex = -1;
+ this.hasBatchModeRefCount = 0;
+ this._hadChangeEventInBatchMode = false;
+});
+
+
+/** {int} The selection mode "none". Nothing can ever be selected. */
+qx.Class.NO_SELECTION = 1;
+
+/** {int} The selection mode "single". This mode only allows one selected item. */
+qx.Class.SINGLE_SELECTION = 2;
+
+/**
+ * (int) The selection mode "single interval". This mode only allows one
+ * continuous interval of selected items.
+ */
+qx.Class.SINGLE_INTERVAL_SELECTION = 3;
+
+/**
+ * (int) The selection mode "multiple interval". This mode only allows any
+ * selection.
+ */
+qx.Class.MULTIPLE_INTERVAL_SELECTION = 4;
+
+
+/**
+ * (int) the selection mode.
+ */
+qx.OO.addProperty({ name:"selectionMode", type:"number",
+ defaultValue:qx.Class.SINGLE_SELECTION,
+ allowNull:false,
+ possibleValues:[ qx.Class.NO_SELECTION,
+ qx.Class.SINGLE_SELECTION,
+ qx.Class.SINGLE_INTERVAL_SELECTION,
+ qx.Class.MULTIPLE_INTERVAL_SELECTION ] });
+
+// selectionMode property modifier
+qx.Proto._modifySelectionMode = function(selectionMode) {
+ if (selectionMode == qx.ui.table.SelectionModel.NO_SELECTION) {
+ this.clearSelection();
+ }
+ return true;
+}
+
+
+/**
+ * <p>Activates / Deactivates batch mode. In batch mode, no change events will be thrown but
+ * will be collected instead. When batch mode is turned off again and any events have
+ * been collected, one event is thrown to inform the listeners.</p>
+ *
+ * <p>This method supports nested calling, i. e. batch mode can be turned more than once.
+ * In this case, batch mode will not end until it has been turned off once for each
+ * turning on.</p>
+ *
+ * @param batchMode {boolean} true to activate batch mode, false to deactivate
+ * @return {boolean} true if batch mode is active, false otherwise
+ * @throws Error if batch mode is turned off once more than it has been turned on
+ */
+qx.Proto.setBatchMode = function(batchMode) {
+ if (batchMode){
+ this.hasBatchModeRefCount += 1;
+ } else {
+ if (this.hasBatchModeRefCount == 0){
+ throw new Error("Try to turn off batch mode althoug it was not turned on.")
+ }
+ this.hasBatchModeRefCount -= 1;
+ if (this._hadChangeEventInBatchMode){
+ this._hadChangeEventInBatchMode = false;
+ this._fireChangeSelection();
+ }
+ }
+ return this.hasBatchMode();
+}
+
+
+/**
+ * <p>Returns whether batch mode is active. See setter for a description of batch mode.</p>
+ *
+ * @return {boolean} true if batch mode is active, false otherwise
+ */
+qx.Proto.hasBatchMode = function() {
+ return this.hasBatchModeRefCount > 0;
+}
+
+
+/**
+ * Returns the first argument of the last call to {@link #setSelectionInterval()},
+ * {@link #addSelectionInterval()} or {@link #removeSelectionInterval()}.
+ *
+ * @return {int} the ancor selection index.
+ */
+qx.Proto.getAnchorSelectionIndex = function() {
+ return this._anchorSelectionIndex;
+}
+
+
+/**
+ * Returns the second argument of the last call to {@link #setSelectionInterval()},
+ * {@link #addSelectionInterval()} or {@link #removeSelectionInterval()}.
+ *
+ * @return {int} the lead selection index.
+ */
+qx.Proto.getLeadSelectionIndex = function() {
+ return this._leadSelectionIndex;
+}
+
+
+/**
+ * Clears the selection.
+ */
+qx.Proto.clearSelection = function() {
+ if (! this.isSelectionEmpty()) {
+ this._clearSelection();
+ this._fireChangeSelection();
+ }
+}
+
+
+/**
+ * Returns whether the selection is empty.
+ *
+ * @return {boolean} whether the selection is empty.
+ */
+qx.Proto.isSelectionEmpty = function() {
+ return this._selectedRangeArr.length == 0;
+}
+
+
+/**
+ * Returns the number of selected items.
+ *
+ * @return {int} the number of selected items.
+ */
+qx.Proto.getSelectedCount = function() {
+ var selectedCount = 0;
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ var range = this._selectedRangeArr[i];
+ selectedCount += range.maxIndex - range.minIndex + 1;
+ }
+
+ return selectedCount;
+}
+
+
+/**
+ * Returns whether a index is selected.
+ *
+ * @param index {int} the index to check.
+ * @return {boolean} whether the index is selected.
+ */
+qx.Proto.isSelectedIndex = function(index) {
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ var range = this._selectedRangeArr[i];
+
+ if (index >= range.minIndex && index <= range.maxIndex) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Returns the selected ranges as an array. Each array element has a
+ * <code>minIndex</code> and a <code>maxIndex</code> property.
+ *
+ * @return {Map[]} the selected ranges.
+ */
+qx.Proto.getSelectedRanges = function() {
+ // clone the selection array and the individual elements - this prevents the
+ // caller from messing with the internal model
+ var retVal = [];
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ retVal.push({minIndex: this._selectedRangeArr[i].minIndex,
+ maxIndex: this._selectedRangeArr[i].maxIndex});
+ }
+ return retVal;
+}
+
+
+/**
+ * Calls a iterator function for each selected index.
+ * <p>
+ * Usage Example:
+ * <pre>
+ * var selectedRowData = [];
+ * mySelectionModel.iterateSelection(function(index) {
+ * selectedRowData.push(myTableModel.getRowData(index));
+ * });
+ * </pre>
+ *
+ * @param iterator {Function} the function to call for each selected index.
+ * Gets the current index as parameter.
+ * @param object {var ? null} the object to use when calling the handler.
+ * (this object will be available via "this" in the iterator)
+ */
+qx.Proto.iterateSelection = function(iterator, object) {
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ for (var j = this._selectedRangeArr[i].minIndex; j <= this._selectedRangeArr[i].maxIndex; j++) {
+ iterator.call(object, j);
+ }
+ }
+};
+
+
+/**
+ * Sets the selected interval. This will clear the former selection.
+ *
+ * @param fromIndex {int} the first index of the selection (including).
+ * @param toIndex {int} the last index of the selection (including).
+ */
+qx.Proto.setSelectionInterval = function(fromIndex, toIndex) {
+ var SelectionModel = qx.ui.table.SelectionModel;
+
+ switch(this.getSelectionMode()) {
+ case SelectionModel.NO_SELECTION:
+ return;
+ case SelectionModel.SINGLE_SELECTION:
+ fromIndex = toIndex;
+ break;
+ }
+
+ this._clearSelection();
+ this._addSelectionInterval(fromIndex, toIndex);
+
+ this._fireChangeSelection();
+}
+
+
+/**
+ * Adds a selection interval to the current selection.
+ *
+ * @param fromIndex {int} the first index of the selection (including).
+ * @param toIndex {int} the last index of the selection (including).
+ */
+qx.Proto.addSelectionInterval = function(fromIndex, toIndex) {
+ var SelectionModel = qx.ui.table.SelectionModel;
+ switch (this.getSelectionMode()) {
+ case SelectionModel.NO_SELECTION:
+ return;
+ case SelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ this._addSelectionInterval(fromIndex, toIndex);
+ this._fireChangeSelection();
+ break;
+ default:
+ this.setSelectionInterval(fromIndex, toIndex);
+ break;
+ }
+}
+
+
+/**
+ * Removes a interval from the current selection.
+ *
+ * @param fromIndex {int} the first index of the interval (including).
+ * @param toIndex {int} the last index of the interval (including).
+ */
+qx.Proto.removeSelectionInterval = function(fromIndex, toIndex) {
+ this._anchorSelectionIndex = fromIndex;
+ this._leadSelectionIndex = toIndex;
+
+ var minIndex = Math.min(fromIndex, toIndex);
+ var maxIndex = Math.max(fromIndex, toIndex);
+
+ // Crop the affected ranges
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ var range = this._selectedRangeArr[i];
+
+ if (range.minIndex > maxIndex) {
+ // We are done
+ break;
+ } else if (range.maxIndex >= minIndex) {
+ // This range is affected
+ var minIsIn = (range.minIndex >= minIndex) && (range.minIndex <= maxIndex);
+ var maxIsIn = (range.maxIndex >= minIndex) && (range.maxIndex <= maxIndex);
+
+ if (minIsIn && maxIsIn) {
+ // This range is removed completely
+ this._selectedRangeArr.splice(i, 1);
+
+ // Check this index another time
+ i--;
+ } else if (minIsIn) {
+ // The range is cropped from the left
+ range.minIndex = maxIndex + 1;
+ } else if (maxIsIn) {
+ // The range is cropped from the right
+ range.maxIndex = minIndex - 1;
+ } else {
+ // The range is split
+ var newRange = { minIndex:maxIndex + 1, maxIndex:range.maxIndex }
+ this._selectedRangeArr.splice(i + 1, 0, newRange);
+
+ range.maxIndex = minIndex - 1;
+
+ // We are done
+ break;
+ }
+ }
+ }
+
+ //this._dumpRanges();
+
+ this._fireChangeSelection();
+}
+
+
+/**
+ * Clears the selection, but doesn't inform the listeners.
+ */
+qx.Proto._clearSelection = function() {
+ this._selectedRangeArr = [];
+}
+
+
+/**
+ * Adds a selection interval to the current selection, but doesn't inform
+ * the listeners.
+ *
+ * @param fromIndex {int} the first index of the selection (including).
+ * @param toIndex {int} the last index of the selection (including).
+ */
+qx.Proto._addSelectionInterval = function(fromIndex, toIndex) {
+ this._anchorSelectionIndex = fromIndex;
+ this._leadSelectionIndex = toIndex;
+
+ var minIndex = Math.min(fromIndex, toIndex);
+ var maxIndex = Math.max(fromIndex, toIndex);
+
+ // Find the index where the new range should be inserted
+ var newRangeIndex = 0;
+ for (; newRangeIndex < this._selectedRangeArr.length; newRangeIndex++) {
+ var range = this._selectedRangeArr[newRangeIndex];
+ if (range.minIndex > minIndex) {
+ break;
+ }
+ }
+
+ // Add the new range
+ this._selectedRangeArr.splice(newRangeIndex, 0, { minIndex:minIndex, maxIndex:maxIndex });
+
+ // Merge overlapping ranges
+ var lastRange = this._selectedRangeArr[0];
+ for (var i = 1; i < this._selectedRangeArr.length; i++) {
+ var range = this._selectedRangeArr[i];
+
+ if (lastRange.maxIndex + 1 >= range.minIndex) {
+ // The ranges are overlapping -> merge them
+ lastRange.maxIndex = Math.max(lastRange.maxIndex, range.maxIndex);
+
+ // Remove the current range
+ this._selectedRangeArr.splice(i, 1);
+
+ // Check this index another time
+ i--;
+ } else {
+ lastRange = range;
+ }
+ }
+
+ //this._dumpRanges();
+}
+
+
+/**
+ * Logs the current ranges for debug perposes.
+ */
+qx.Proto._dumpRanges = function() {
+ var text = "Ranges:";
+ for (var i = 0; i < this._selectedRangeArr.length; i++) {
+ var range = this._selectedRangeArr[i];
+ text += " [" + range.minIndex + ".." + range.maxIndex + "]";
+ }
+ this.debug(text);
+}
+
+
+/**
+ * Fires the "changeSelection" event to all registered listeners. If the selection model
+ * currently is in batch mode, only one event will be thrown when batch mode is ended.
+ */
+qx.Proto._fireChangeSelection = function() {
+ //In batch mode, remember event but do not throw (yet)
+ if (this.hasBatchMode()){
+ this._hadChangeEventInBatchMode = true;
+
+ //If not in batch mode, throw event
+ } else if (this.hasEventListeners("changeSelection")) {
+ this.dispatchEvent(new qx.event.type.Event("changeSelection"), true);
+ }
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SimpleTableModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SimpleTableModel.js
new file mode 100644
index 0000000000..ef6ef2fecc
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/SimpleTableModel.js
@@ -0,0 +1,335 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A simple table model that provides an API for changing the model data.
+ */
+qx.OO.defineClass("qx.ui.table.SimpleTableModel", qx.ui.table.AbstractTableModel,
+function() {
+ qx.ui.table.AbstractTableModel.call(this);
+
+ this._rowArr = [];
+ this._sortColumnIndex = -1;
+ this._sortAscending;
+
+ this._editableColArr = null;
+});
+
+
+/**
+ * <p>See overridden method for details.</p>
+ *
+ * @param rowIndex {int} the model index of the row.
+ * @return {Array} Array containing a value for each column.
+ */
+qx.Proto.getRowData = function(rowIndex) {
+ return this._rowArr[rowIndex];
+};
+
+
+/**
+ * Returns the data of one row as map containing the column IDs as key and the
+ * cell values as value.
+ *
+ * @param rowIndex {int} the model index of the row.
+ * @return {Map} a Map containing the column values.
+ */
+qx.Proto.getRowDataAsMap = function(rowIndex) {
+ var columnArr = this._rowArr[rowIndex];
+ var map = {};
+ for (var col = 0; col < this.getColumnCount(); col++) {
+ map[this.getColumnId(col)] = columnArr[col];
+ }
+ return map;
+};
+
+
+/**
+ * Sets all columns editable or not editable.
+ *
+ * @param editable {boolean} whether all columns are editable.
+ */
+qx.Proto.setEditable = function(editable) {
+ this._editableColArr = [];
+ for (var col = 0; col < this.getColumnCount(); col++) {
+ this._editableColArr[col] = editable;
+ }
+
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+}
+
+
+/**
+ * Sets whether a column is editable.
+ *
+ * @param columnIndex {int} the column of which to set the editable state.
+ * @param editable {boolean} whether the column should be editable.
+ */
+qx.Proto.setColumnEditable = function(columnIndex, editable) {
+ if (editable != this.isColumnEditable(columnIndex)) {
+ if (this._editableColArr == null) {
+ this._editableColArr = [];
+ }
+ this._editableColArr[columnIndex] = editable;
+
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+ }
+}
+
+
+// overridden
+qx.Proto.isColumnEditable = function(columnIndex) {
+ return this._editableColArr ? (this._editableColArr[columnIndex] == true) : false;
+}
+
+
+// overridden
+qx.Proto.isColumnSortable = function(columnIndex) {
+ return true;
+}
+
+
+// overridden
+qx.Proto.sortByColumn = function(columnIndex, ascending) {
+ // NOTE: We use different comperators for ascending and descending,
+ // because comperators should be really fast.
+ var comperator;
+ if (ascending) {
+ comperator = function(row1, row2) {
+ var obj1 = row1[columnIndex];
+ var obj2 = row2[columnIndex];
+ return (obj1 > obj2) ? 1 : ((obj1 == obj2) ? 0 : -1);
+ }
+ } else {
+ comperator = function(row1, row2) {
+ var obj1 = row1[columnIndex];
+ var obj2 = row2[columnIndex];
+ return (obj1 < obj2) ? 1 : ((obj1 == obj2) ? 0 : -1);
+ }
+ }
+
+ this._rowArr.sort(comperator);
+
+ this._sortColumnIndex = columnIndex;
+ this._sortAscending = ascending;
+
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+}
+
+
+/**
+ * Clears the sorting.
+ */
+qx.Proto._clearSorting = function() {
+ if (this._sortColumnIndex != -1) {
+ this._sortColumnIndex = -1;
+ this._sortAscending = true;
+
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED);
+ }
+}
+
+
+// overridden
+qx.Proto.getSortColumnIndex = function() {
+ return this._sortColumnIndex;
+}
+
+
+// overridden
+qx.Proto.isSortAscending = function() {
+ return this._sortAscending;
+}
+
+
+// overridden
+qx.Proto.getRowCount = function() {
+ return this._rowArr.length;
+}
+
+
+// overridden
+qx.Proto.getValue = function(columnIndex, rowIndex) {
+ if (rowIndex < 0 || rowIndex >= this._rowArr.length) {
+ throw new Error("this._rowArr out of bounds: " + rowIndex + " (0.." + this._rowArr.length + ")");
+ }
+
+ return this._rowArr[rowIndex][columnIndex];
+}
+
+
+// overridden
+qx.Proto.setValue = function(columnIndex, rowIndex, value) {
+ if (this._rowArr[rowIndex][columnIndex] != value) {
+ this._rowArr[rowIndex][columnIndex] = value;
+
+ // Inform the listeners
+ if (this.hasEventListeners(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED)) {
+ var data = { firstRow:rowIndex, lastRow:rowIndex,
+ firstColumn:columnIndex, lastColumn:columnIndex }
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ }
+
+ if (columnIndex == this._sortColumnIndex) {
+ this._clearSorting();
+ }
+ }
+}
+
+
+/**
+ * Sets the whole data in a bulk.
+ *
+ * @param rowArr {var[][]} An array containing an array for each row. Each
+ * row-array contains the values in that row in the order of the columns
+ * in this model.
+ */
+qx.Proto.setData = function(rowArr) {
+ this._rowArr = rowArr;
+
+ // Inform the listeners
+ if (this.hasEventListeners(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED)) {
+ this.createDispatchEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED);
+ }
+
+ this._clearSorting();
+}
+
+
+/**
+ * Returns the data of this model.
+ * <p>
+ * Warning: Do not alter this array! If you want to change the data use
+ * {@link #setData}, {@link #setDataAsMapArray} or {@link #setValue} instead.
+ *
+ * @return {var[][]} An array containing an array for each row. Each
+ * row-array contains the values in that row in the order of the columns
+ * in this model.
+ */
+qx.Proto.getData = function() {
+ return this._rowArr;
+};
+
+
+/**
+ * Sets the whole data in a bulk.
+ *
+ * @param mapArr {Map[]} An array containing a map for each row. Each
+ * row-map contains the column IDs as key and the cell values as value.
+ */
+qx.Proto.setDataAsMapArray = function(mapArr) {
+ this.setData(this._mapArray2RowArr(mapArr));
+};
+
+
+/**
+ * Adds some rows to the model.
+ * <p>
+ * Warning: The given array will be altered!
+ *
+ * @param rowArr {var[][]} An array containing an array for each row. Each
+ * row-array contains the values in that row in the order of the columns
+ * in this model.
+ * @param startIndex {int ? null} The index where to insert the new rows. If null,
+ * the rows are appended to the end.
+ */
+qx.Proto.addRows = function(rowArr, startIndex) {
+ if (startIndex == null) {
+ startIndex = this._rowArr.length;
+ }
+
+ // Prepare the rowArr so it can be used for apply
+ rowArr.splice(0, 0, startIndex, 0);
+
+ // Insert the new rows
+ Array.prototype.splice.apply(this._rowArr, rowArr);
+
+ // Inform the listeners
+ if (this.hasEventListeners(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED)) {
+ var data = { firstRow:startIndex, lastRow:this._rowArr.length - 1, firstColumn:0, lastColumn:this.getColumnCount() - 1 };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ }
+
+ this._clearSorting();
+};
+
+
+/**
+ * Adds some rows to the model.
+ * <p>
+ * Warning: The given array (mapArr) will be altered!
+ *
+ * @param mapArr {Map[]} An array containing a map for each row. Each
+ * row-map contains the column IDs as key and the cell values as value.
+ * @param startIndex {int ? null} The index where to insert the new rows. If null,
+ * the rows are appended to the end.
+ */
+qx.Proto.addRowsAsMapArray = function(mapArr, startIndex) {
+ this.addRows(this._mapArray2RowArr(mapArr), startIndex);
+};
+
+
+/**
+ * Removes some rows from the model.
+ *
+ * @param startIndex {int} the index of the first row to remove.
+ * @param howMany {int} the number of rows to remove.
+ */
+qx.Proto.removeRows = function(startIndex, howMany) {
+ this._rowArr.splice(startIndex, howMany);
+
+ // Inform the listeners
+ if (this.hasEventListeners(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED)) {
+ var data = { firstRow:startIndex, lastRow:this._rowArr.length - 1, firstColumn:0, lastColumn:this.getColumnCount() - 1 };
+ this.dispatchEvent(new qx.event.type.DataEvent(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, data), true);
+ }
+
+ this._clearSorting();
+};
+
+
+/**
+ * Creates an array of maps to an array of arrays.
+ *
+ * @param mapArr {Map[]} An array containing a map for each row. Each
+ * row-map contains the column IDs as key and the cell values as value.
+ * @return {var[][]} An array containing an array for each row. Each
+ * row-array contains the values in that row in the order of the columns
+ * in this model.
+ */
+qx.Proto._mapArray2RowArr = function(mapArr) {
+ var rowCount = mapArr.length;
+ var columnCount = this.getColumnCount();
+ var dataArr = new Array(rowCount);
+ var columnArr;
+ var j;
+ for (var i = 0; i < rowCount; ++i) {
+ columnArr = new Array(columnCount);
+ for (var j = 0; j < columnCount; ++j) {
+ columnArr[j] = mapArr[i][this.getColumnId(j)];
+ }
+ dataArr[i] = columnArr;
+ }
+
+ return dataArr;
+};
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/Table.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/Table.js
new file mode 100644
index 0000000000..360662e718
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/Table.js
@@ -0,0 +1,1062 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+#require(qx.ui.table.DefaultDataRowRenderer)
+
+************************************************************************ */
+
+/**
+ * A table.
+ *
+ * @param tableModel {qx.ui.table.TableModel} The table
+ * model to read the data from.
+ */
+qx.OO.defineClass("qx.ui.table.Table", qx.ui.layout.VerticalBoxLayout,
+function(tableModel) {
+ qx.ui.layout.VerticalBoxLayout.call(this);
+
+ // Create the child widgets
+ this._scrollerParent = new qx.ui.layout.HorizontalBoxLayout;
+ this._scrollerParent.setDimension("100%", "1*");
+ this._scrollerParent.setSpacing(1);
+
+ this._statusBar = new qx.ui.basic.Label;
+ this._statusBar.setAppearance("table-focus-statusbar");
+ this._statusBar.setDimension("100%", "auto");
+
+ this.add(this._scrollerParent, this._statusBar);
+
+ this._columnVisibilityBt = new qx.ui.toolbar.Button(null, "widget/table/selectColumnOrder.png");
+ this._columnVisibilityBt.addEventListener("execute", this._onColumnVisibilityBtExecuted, this);
+
+ // Create the models
+ this._selectionManager = new qx.ui.table.SelectionManager;
+
+ this.setSelectionModel(new qx.ui.table.SelectionModel);
+ this.setTableColumnModel(new qx.ui.table.TableColumnModel);
+ this.setTableModel(tableModel);
+
+ // Update the status bar
+ this._updateStatusBar();
+
+ // create the main meta column
+ this.setMetaColumnCounts([ -1 ]);
+
+ // Make focusable
+ this.setTabIndex(1);
+ this.addEventListener("keydown", this._onkeydown);
+ this.addEventListener("keypress", this._onkeypress);
+ this.addEventListener("changeFocused", this._onFocusChanged);
+
+ this._focusedCol = 0;
+ this._focusedRow = 0;
+});
+
+
+/** The default row renderer to use when {@link #dataRowRenderer} is null. */
+qx.Class.DEFAULT_DATA_ROW_RENDERER = new qx.ui.table.DefaultDataRowRenderer();
+
+
+/** The selection model. */
+qx.OO.addProperty({ name:"selectionModel", type:"object", instance : "qx.ui.table.SelectionModel" });
+
+/** The table model. */
+qx.OO.addProperty({ name:"tableModel", type:"object", instance : "qx.ui.table.TableModel" });
+
+/** The table column model. */
+qx.OO.addProperty({ name:"tableColumnModel", type:"object", instance : "qx.ui.table.TableColumnModel" });
+
+/** The height of the table rows. */
+qx.OO.addProperty({ name:"rowHeight", type:"number", defaultValue:15 });
+
+/** Whether to show the status bar */
+qx.OO.addProperty({ name:"statusBarVisible", type:"boolean", defaultValue:true });
+
+/** Whether to show the column visibility button */
+qx.OO.addProperty({ name:"columnVisibilityButtonVisible", type:"boolean", defaultValue:true });
+
+/**
+ * {int[]} The number of columns per meta column. If the last array entry is -1,
+ * this meta column will get the remaining columns.
+ */
+qx.OO.addProperty({ name:"metaColumnCounts", type:"object" });
+
+/**
+ * Whether the focus should moved when the mouse is moved over a cell. If false
+ * the focus is only moved on mouse clicks.
+ */
+qx.OO.addProperty({ name:"focusCellOnMouseMove", type:"boolean", defaultValue:false });
+
+/**
+ * Whether the table should keep the first visible row complete. If set to false,
+ * the first row may be rendered partial, depending on the vertical scroll value.
+ */
+qx.OO.addProperty({ name:"keepFirstVisibleRowComplete", type:"boolean", defaultValue:true });
+
+/**
+ * Whether the table cells should be updated when only the selection or the
+ * focus changed. This slows down the table update but allows to react on a
+ * changed selection or a changed focus in a cell renderer.
+ */
+qx.OO.addProperty({ name:"alwaysUpdateCells", type:"boolean", defaultValue:false });
+
+/** The height of the header cells. */
+qx.OO.addProperty({ name:"headerCellHeight", type:"number", defaultValue:16, allowNull:false });
+
+/** The renderer to use for styling the rows. */
+qx.OO.addProperty({ name:"dataRowRenderer", type:"object", instance:"qx.ui.table.DataRowRenderer", defaultValue:qx.Class.DEFAULT_DATA_ROW_RENDERER, allowNull:false });
+
+
+// property modifier
+qx.Proto._modifySelectionModel = function(propValue, propOldValue, propData) {
+ this._selectionManager.setSelectionModel(propValue);
+
+ if (propOldValue != null) {
+ propOldValue.removeEventListener("changeSelection", this._onSelectionChanged, this);
+ }
+ propValue.addEventListener("changeSelection", this._onSelectionChanged, this);
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyTableModel = function(propValue, propOldValue, propData) {
+ this.getTableColumnModel().init(propValue.getColumnCount());
+
+ if (propOldValue != null) {
+ propOldValue.removeEventListener(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED, this._onTableModelMetaDataChanged, this);
+ propOldValue.removeEventListener(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, this._onTableModelDataChanged, this);
+ }
+ propValue.addEventListener(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED, this._onTableModelMetaDataChanged, this);
+ propValue.addEventListener(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, this._onTableModelDataChanged, this);
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyTableColumnModel = function(propValue, propOldValue, propData) {
+ if (propOldValue != null) {
+ propOldValue.removeEventListener("visibilityChanged", this._onColVisibilityChanged, this);
+ propOldValue.removeEventListener("widthChanged", this._onColWidthChanged, this);
+ propOldValue.removeEventListener("orderChanged", this._onColOrderChanged, this);
+ }
+ propValue.addEventListener("visibilityChanged", this._onColVisibilityChanged, this);
+ propValue.addEventListener("widthChanged", this._onColWidthChanged, this);
+ propValue.addEventListener("orderChanged", this._onColOrderChanged, this);
+
+ return true;
+};
+
+
+// property modifier
+qx.Proto._modifyStatusBarVisible = function(propValue, propOldValue, propData) {
+ this._statusBar.setDisplay(propValue);
+
+ if (propValue) {
+ this._updateStatusBar();
+ }
+ return true;
+};
+
+
+// property modifier
+qx.Proto._modifyColumnVisibilityButtonVisible = function(propValue, propOldValue, propData) {
+ this._columnVisibilityBt.setDisplay(propValue);
+
+ return true;
+};
+
+
+// property modifier
+qx.Proto._modifyMetaColumnCounts = function(propValue, propOldValue, propData) {
+ var metaColumnCounts = propValue;
+ var scrollerArr = this._getPaneScrollerArr();
+
+ // Remove the panes not needed any more
+ this._cleanUpMetaColumns(metaColumnCounts.length);
+
+ // Update the old panes
+ var leftX = 0;
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var paneScroller = scrollerArr[i];
+ var paneModel = paneScroller.getTablePaneModel();
+ paneModel.setFirstColumnX(leftX);
+ paneModel.setMaxColumnCount(metaColumnCounts[i]);
+ leftX += metaColumnCounts[i];
+ }
+
+ // Add the new panes
+ if (metaColumnCounts.length > scrollerArr.length) {
+ var selectionModel = this.getSelectionModel();
+ var tableModel = this.getTableModel();
+ var columnModel = this.getTableColumnModel();
+
+ for (var i = scrollerArr.length; i < metaColumnCounts.length; i++) {
+ var paneModel = new qx.ui.table.TablePaneModel(columnModel);
+ paneModel.setFirstColumnX(leftX);
+ paneModel.setMaxColumnCount(metaColumnCounts[i]);
+ leftX += metaColumnCounts[i];
+
+ var paneScroller = new qx.ui.table.TablePaneScroller(this);
+ paneScroller.setTablePaneModel(paneModel);
+
+ // Register event listener for vertical scrolling
+ paneScroller.addEventListener("changeScrollY", this._onScrollY, this);
+
+ this._scrollerParent.add(paneScroller);
+ }
+ }
+
+ // Update all meta columns
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var paneScroller = scrollerArr[i];
+ var isLast = (i == (scrollerArr.length - 1));
+
+ // Set the right header height
+ paneScroller.getHeader().setHeight(this.getHeaderCellHeight());
+
+ // Put the _columnVisibilityBt in the top right corner of the last meta column
+ paneScroller.setTopRightWidget(isLast ? this._columnVisibilityBt : null);
+ }
+
+ this._updateScrollerWidths();
+ this._updateScrollBarVisibility();
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyFocusCellOnMouseMove = function(propValue, propOldValue, propData) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i].setFocusCellOnMouseMove(propValue);
+ }
+ return true;
+};
+
+
+// property modifier
+qx.Proto._modifyKeepFirstVisibleRowComplete = function(propValue, propOldValue, propData) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onKeepFirstVisibleRowCompleteChanged();
+ }
+ return true;
+};
+
+
+// property modifier
+qx.Proto._modifyHeaderCellHeight = function(propValue, propOldValue, propData) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i].getHeader().setHeight(propValue);
+ }
+ return true;
+};
+
+
+/**
+ * Returns the selection manager.
+ *
+ * @return {SelectionManager} the selection manager.
+ */
+qx.Proto._getSelectionManager = function() {
+ return this._selectionManager;
+};
+
+
+/**
+ * Returns an array containing all TablePaneScrollers in this table.
+ *
+ * @return {TablePaneScroller[]} all TablePaneScrollers in this table.
+ */
+qx.Proto._getPaneScrollerArr = function() {
+ return this._scrollerParent.getChildren();
+}
+
+
+/**
+ * Returns a TablePaneScroller of this table.
+ *
+ * @param metaColumn {int} the meta column to get the TablePaneScroller for.
+ * @return {TablePaneScroller} the TablePaneScroller.
+ */
+qx.Proto.getPaneScroller = function(metaColumn) {
+ return this._getPaneScrollerArr()[metaColumn];
+}
+
+
+/**
+ * Cleans up the meta columns.
+ *
+ * @param fromMetaColumn {int} the first meta column to clean up. All following
+ * meta columns will be cleaned up, too. All previous meta columns will
+ * stay unchanged. If 0 all meta columns will be cleaned up.
+ */
+qx.Proto._cleanUpMetaColumns = function(fromMetaColumn) {
+ var scrollerArr = this._getPaneScrollerArr();
+ if (scrollerArr != null) {
+ for (var i = scrollerArr.length - 1; i >= fromMetaColumn; i--) {
+ var paneScroller = scrollerArr[i];
+ paneScroller.removeEventListener("changeScrollY", this._onScrollY, this);
+ this._scrollerParent.remove(paneScroller);
+ paneScroller.dispose();
+ }
+ }
+}
+
+
+/**
+ * Event handler. Called when the selection has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onSelectionChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onSelectionChanged(evt);
+ }
+
+ this._updateStatusBar();
+}
+
+
+/**
+ * Event handler. Called when the table model meta data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelMetaDataChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onTableModelMetaDataChanged(evt);
+ }
+
+ this._updateStatusBar();
+}
+
+
+/**
+ * Event handler. Called when the table model data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelDataChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onTableModelDataChanged(evt);
+ }
+
+ var rowCount = this.getTableModel().getRowCount();
+ if (rowCount != this._lastRowCount) {
+ this._lastRowCount = rowCount;
+
+ this._updateScrollBarVisibility();
+ this._updateStatusBar();
+ }
+};
+
+
+/**
+ * Event handler. Called when a TablePaneScroller has been scrolled vertically.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onScrollY = function(evt) {
+ if (! this._internalChange) {
+ this._internalChange = true;
+
+ // Set the same scroll position to all meta columns
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i].setScrollY(evt.getData());
+ }
+
+ this._internalChange = false;
+ }
+}
+
+
+/**
+ * Event handler. Called when a key was pressed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onkeydown = function(evt) {
+ var identifier = evt.getKeyIdentifier();
+
+ var consumed = false;
+ var oldFocusedRow = this._focusedRow;
+ if (this.isEditing()) {
+ // Editing mode
+ if (evt.getModifiers() == 0) {
+ consumed = true;
+ switch (identifier) {
+ case "Enter":
+ this.stopEditing();
+ var oldFocusedRow = this._focusedRow;
+ this.moveFocusedCell(0, 1);
+ if (this._focusedRow != oldFocusedRow) {
+ this.startEditing();
+ }
+ break;
+ case "Escape":
+ this.cancelEditing();
+ this.focus();
+ break;
+ default:
+ consumed = false;
+ break;
+ }
+ }
+ } else {
+ // No editing mode
+
+ // Handle keys that are independant from the modifiers
+ consumed = true;
+ switch (identifier) {
+ case "Home":
+ this.setFocusedCell(this._focusedCol, 0, true);
+ break;
+ case "End":
+ var rowCount = this.getTableModel().getRowCount();
+ this.setFocusedCell(this._focusedCol, rowCount - 1, true);
+ break;
+ default:
+ consumed = false;
+ break;
+ }
+
+ // Handle keys that depend on modifiers
+ if (evt.getModifiers() == 0) {
+ consumed = true;
+ switch (identifier) {
+ case "F2":
+ case "Enter":
+ this.startEditing();
+ break;
+ default:
+ consumed = false;
+ break;
+ }
+ } else if (evt.getCtrlKey()) {
+ consumed = true;
+ switch (identifier) {
+ case "A": // Ctrl + A
+ var rowCount = this.getTableModel().getRowCount();
+ if (rowCount > 0) {
+ this.getSelectionModel().setSelectionInterval(0, rowCount - 1);
+ }
+ break;
+ default:
+ consumed = false;
+ break;
+ }
+ }
+ }
+
+ if (oldFocusedRow != this._focusedRow) {
+ // The focus moved -> Let the selection manager handle this event
+ this._selectionManager.handleMoveKeyDown(this._focusedRow, evt);
+ }
+
+ if (consumed) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+};
+
+
+qx.Proto._onkeypress = function(evt)
+{
+ if (this.isEditing()) { return }
+ // No editing mode
+ var oldFocusedRow = this._focusedRow;
+ var consumed = true;
+
+ // Handle keys that are independant from the modifiers
+ var identifier = evt.getKeyIdentifier();
+ switch (identifier) {
+ case "Space":
+ this._selectionManager.handleSelectKeyDown(this._focusedRow, evt);
+ break;
+
+ case "Left":
+ this.moveFocusedCell(-1, 0);
+ break;
+
+ case "Right":
+ this.moveFocusedCell(1, 0);
+ break;
+
+ case "Up":
+ this.moveFocusedCell(0, -1);
+ break;
+
+ case "Down":
+ this.moveFocusedCell(0, 1);
+ break;
+
+ case "PageUp":
+ case "PageDown":
+ var scroller = this.getPaneScroller(0);
+ var pane = scroller.getTablePane();
+ var rowCount = pane.getVisibleRowCount() - 1;
+ var rowHeight = this.getRowHeight();
+ var direction = (identifier == "PageUp") ? -1 : 1;
+ scroller.setScrollY(scroller.getScrollY() + direction * rowCount * rowHeight);
+ this.moveFocusedCell(0, direction * rowCount);
+ break;
+
+ default:
+ consumed = false;
+ }
+ if (oldFocusedRow != this._focusedRow) {
+ // The focus moved -> Let the selection manager handle this event
+ this._selectionManager.handleMoveKeyDown(this._focusedRow, evt);
+ }
+
+ if (consumed) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+};
+
+
+/**
+ * Event handler. Called when the table gets the focus.
+ */
+qx.Proto._onFocusChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onFocusChanged(evt);
+ }
+};
+
+
+/**
+ * Event handler. Called when the visibility of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColVisibilityChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onColVisibilityChanged(evt);
+ }
+
+ this._updateScrollerWidths();
+ this._updateScrollBarVisibility();
+}
+
+
+/**
+ * Event handler. Called when the width of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColWidthChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onColWidthChanged(evt);
+ }
+
+ this._updateScrollerWidths();
+ this._updateScrollBarVisibility();
+}
+
+
+/**
+ * Event handler. Called when the column order has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColOrderChanged = function(evt) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i]._onColOrderChanged(evt);
+ }
+
+ // A column may have been moved between meta columns
+ this._updateScrollerWidths();
+ this._updateScrollBarVisibility();
+}
+
+
+/**
+ * Gets the TablePaneScroller at a certain x position in the page. If there is
+ * no TablePaneScroller at this postion, null is returned.
+ *
+ * @param pageX {int} the position in the page to check (in pixels).
+ * @return {TablePaneScroller} the TablePaneScroller or null.
+ *
+ * @see TablePaneScrollerPool
+ */
+qx.Proto.getTablePaneScrollerAtPageX = function(pageX) {
+ var metaCol = this._getMetaColumnAtPageX(pageX);
+ return (metaCol != -1) ? this.getPaneScroller(metaCol) : null;
+}
+
+
+/**
+ * Sets the currently focused cell.
+ *
+ * @param col {int} the model index of the focused cell's column.
+ * @param row {int} the model index of the focused cell's row.
+ * @param scrollVisible {boolean ? false} whether to scroll the new focused cell
+ * visible.
+ *
+ * @see TablePaneScrollerPool
+ */
+qx.Proto.setFocusedCell = function(col, row, scrollVisible) {
+ if (!this.isEditing() && (col != this._focusedCol || row != this._focusedRow)) {
+ this._focusedCol = col;
+ this._focusedRow = row;
+
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ scrollerArr[i].setFocusedCell(col, row);
+ }
+
+ if (scrollVisible) {
+ this.scrollCellVisible(col, row);
+ }
+ }
+}
+
+
+/**
+ * Returns the column of the currently focused cell.
+ *
+ * @return {int} the model index of the focused cell's column.
+ */
+qx.Proto.getFocusedColumn = function() {
+ return this._focusedCol;
+};
+
+
+/**
+ * Returns the row of the currently focused cell.
+ *
+ * @return {int} the model index of the focused cell's column.
+ */
+qx.Proto.getFocusedRow = function() {
+ return this._focusedRow;
+};
+
+
+/**
+ * Moves the focus.
+ *
+ * @param deltaX {int} The delta by which the focus should be moved on the x axis.
+ * @param deltaY {int} The delta by which the focus should be moved on the y axis.
+ */
+qx.Proto.moveFocusedCell = function(deltaX, deltaY) {
+ var col = this._focusedCol;
+ var row = this._focusedRow;
+
+ if (deltaX != 0) {
+ var columnModel = this.getTableColumnModel();
+ var x = columnModel.getVisibleX(col);
+ var colCount = columnModel.getVisibleColumnCount();
+ x = qx.lang.Number.limit(x + deltaX, 0, colCount - 1);
+ col = columnModel.getVisibleColumnAtX(x);
+ }
+
+ if (deltaY != 0) {
+ var tableModel = this.getTableModel();
+ row = qx.lang.Number.limit(row + deltaY, 0, tableModel.getRowCount() - 1);
+ }
+
+ this.setFocusedCell(col, row, true);
+}
+
+
+/**
+ * Scrolls a cell visible.
+ *
+ * @param col {int} the model index of the column the cell belongs to.
+ * @param row {int} the model index of the row the cell belongs to.
+ */
+qx.Proto.scrollCellVisible = function(col, row) {
+ var columnModel = this.getTableColumnModel();
+ var x = columnModel.getVisibleX(col);
+
+ var metaColumn = this._getMetaColumnAtColumnX(x);
+ if (metaColumn != -1) {
+ this.getPaneScroller(metaColumn).scrollCellVisible(col, row);
+ }
+}
+
+
+/**
+ * Returns whether currently a cell is editing.
+ *
+ * @return whether currently a cell is editing.
+ */
+qx.Proto.isEditing = function() {
+ if (this._focusedCol != null) {
+ var x = this.getTableColumnModel().getVisibleX(this._focusedCol);
+ var metaColumn = this._getMetaColumnAtColumnX(x);
+ return this.getPaneScroller(metaColumn).isEditing();
+ }
+}
+
+
+/**
+ * Starts editing the currently focused cell. Does nothing if already editing
+ * or if the column is not editable.
+ *
+ * @return {boolean} whether editing was started
+ */
+qx.Proto.startEditing = function() {
+ if (this._focusedCol != null) {
+ var x = this.getTableColumnModel().getVisibleX(this._focusedCol);
+ var metaColumn = this._getMetaColumnAtColumnX(x);
+ return this.getPaneScroller(metaColumn).startEditing();
+ }
+ return false;
+}
+
+
+/**
+ * Stops editing and writes the editor's value to the model.
+ */
+qx.Proto.stopEditing = function() {
+ if (this._focusedCol != null) {
+ var x = this.getTableColumnModel().getVisibleX(this._focusedCol);
+ var metaColumn = this._getMetaColumnAtColumnX(x);
+ this.getPaneScroller(metaColumn).stopEditing();
+ }
+}
+
+
+/**
+ * Stops editing without writing the editor's value to the model.
+ */
+qx.Proto.cancelEditing = function() {
+ if (this._focusedCol != null) {
+ var x = this.getTableColumnModel().getVisibleX(this._focusedCol);
+ var metaColumn = this._getMetaColumnAtColumnX(x);
+ this.getPaneScroller(metaColumn).cancelEditing();
+ }
+}
+
+
+/**
+ * Gets the meta column at a certain x position in the page. If there is no
+ * meta column at this postion, -1 is returned.
+ *
+ * @param pageX {int} the position in the page to check (in pixels).
+ * @return {int} the index of the meta column or -1.
+ */
+qx.Proto._getMetaColumnAtPageX = function(pageX) {
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var elem = scrollerArr[i].getElement();
+ if (pageX >= qx.dom.Location.getPageBoxLeft(elem)
+ && pageX <= qx.dom.Location.getPageBoxRight(elem))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * Returns the meta column a column is shown in. If the column is not shown at
+ * all, -1 is returned.
+ *
+ * @param visXPos {int} the visible x position of the column.
+ * @return {int} the meta column the column is shown in.
+ */
+qx.Proto._getMetaColumnAtColumnX = function(visXPos) {
+ var metaColumnCounts = this.getMetaColumnCounts();
+ var rightXPos = 0;
+ for (var i = 0; i < metaColumnCounts.length; i++) {
+ var counts = metaColumnCounts[i];
+ rightXPos += counts;
+
+ if (counts == -1 || visXPos < rightXPos) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * Updates the text shown in the status bar.
+ */
+qx.Proto._updateStatusBar = function() {
+ if (this.getStatusBarVisible()) {
+ var selectedRowCount = this.getSelectionModel().getSelectedCount();
+ var rowCount = this.getTableModel().getRowCount();
+
+ var text;
+ if (selectedRowCount == 0) {
+ text = rowCount + ((rowCount == 1) ? " row" : " rows");
+ } else {
+ text = selectedRowCount + " of " + rowCount
+ + ((rowCount == 1) ? " row" : " rows") + " selected";
+ }
+ this._statusBar.setHtml(text);
+ }
+}
+
+
+/**
+ * Updates the widths of all scrollers.
+ */
+qx.Proto._updateScrollerWidths = function() {
+/* no longer needed, per Til, and removing it does not appear to add problems.
+ * qx.ui.core.Widget.flushGlobalQueues();
+ */
+
+ // Give all scrollers except for the last one the wanted width
+ // (The last one has a flex with)
+ var scrollerArr = this._getPaneScrollerArr();
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var isLast = (i == (scrollerArr.length - 1));
+ var width = isLast ? "1*" : scrollerArr[i].getTablePaneModel().getTotalWidth();
+ scrollerArr[i].setWidth(width);
+ }
+}
+
+
+/**
+ * Updates the visibility of the scrollbars in the meta columns.
+ */
+qx.Proto._updateScrollBarVisibility = function() {
+ if (this.isSeeable()) {
+ var horBar = qx.ui.table.TablePaneScroller.HORIZONTAL_SCROLLBAR;
+ var verBar = qx.ui.table.TablePaneScroller.VERTICAL_SCROLLBAR;
+ var scrollerArr = this._getPaneScrollerArr();
+
+ // Check which scroll bars are needed
+ var horNeeded = false;
+ var verNeeded = false;
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var isLast = (i == (scrollerArr.length - 1));
+
+ // Only show the last vertical scrollbar
+ var bars = scrollerArr[i].getNeededScrollBars(horNeeded, !isLast);
+
+ if (bars & horBar) {
+ horNeeded = true;
+ }
+ if (isLast && (bars & verBar)) {
+ verNeeded = true;
+ }
+ }
+
+ // Set the needed scrollbars
+ for (var i = 0; i < scrollerArr.length; i++) {
+ var isLast = (i == (scrollerArr.length - 1));
+
+ // Only show the last vertical scrollbar
+ scrollerArr[i].setHorizontalScrollBarVisible(horNeeded);
+ scrollerArr[i].setVerticalScrollBarVisible(isLast && verNeeded);
+ }
+ }
+}
+
+
+/**
+ * Event handler. Called when the column visibiliy button was executed.
+ */
+qx.Proto._onColumnVisibilityBtExecuted = function() {
+ if ((this._columnVisibilityMenuCloseTime == null)
+ || (new Date().getTime() > this._columnVisibilityMenuCloseTime + 200))
+ {
+ this._toggleColumnVisibilityMenu();
+ }
+}
+
+
+/**
+ * Toggels the visibility of the menu used to change the visibility of columns.
+ */
+qx.Proto._toggleColumnVisibilityMenu = function() {
+ if (this._columnVisibilityMenu == null || !this._columnVisibilityMenu.isSeeable()) {
+ // Show the menu
+
+ // Create the new menu
+ var menu = new qx.ui.menu.Menu;
+
+ menu.addEventListener("disappear", function(evt) {
+ this._columnVisibilityMenuCloseTime = new Date().getTime();
+ }, this);
+
+ var tableModel = this.getTableModel();
+ var columnModel = this.getTableColumnModel();
+ for (var x = 0; x < columnModel.getOverallColumnCount(); x++) {
+ var col = columnModel.getOverallColumnAtX(x);
+ var visible = columnModel.isColumnVisible(col);
+ var cmd = { col:col }
+ var bt = new qx.ui.menu.CheckBox(tableModel.getColumnName(col), null, visible);
+
+ var handler = this._createColumnVisibilityCheckBoxHandler(col);
+ bt._handler = handler;
+ bt.addEventListener("execute", handler, this);
+
+ menu.add(bt);
+ }
+
+ menu.setParent(this.getTopLevelWidget());
+
+ this._columnVisibilityMenu = menu;
+
+ // Show the menu
+ var btElem = this._columnVisibilityBt.getElement();
+ menu.setRestrictToPageOnOpen(false);
+ menu.setTop(qx.dom.Location.getClientBoxBottom(btElem));
+ menu.setLeft(-1000);
+
+ // NOTE: We have to show the menu in a timeout, otherwise it won't be shown
+ // at all.
+ window.setTimeout(function() {
+ menu.show();
+ qx.ui.core.Widget.flushGlobalQueues();
+
+ menu.setLeft(qx.dom.Location.getClientBoxRight(btElem) - menu.getOffsetWidth());
+ qx.ui.core.Widget.flushGlobalQueues();
+ }, 0);
+ } else {
+ // hide the menu
+ menu.hide();
+ this._cleanupColumnVisibilityMenu();
+ }
+}
+
+
+/**
+ * Cleans up the column visibility menu.
+ */
+qx.Proto._cleanupColumnVisibilityMenu = function() {
+ if (this._columnVisibilityMenu != null && ! this._columnVisibilityMenu.getDisposed()) {
+ this._columnVisibilityMenu.dispose();
+ this._columnVisibilityMenu = null;
+ }
+}
+
+
+/**
+ * Creates a handler for a check box of the column visibility menu.
+ *
+ * @param col {int} the model index of column to create the handler for.
+ */
+qx.Proto._createColumnVisibilityCheckBoxHandler = function(col) {
+ return function(evt) {
+ var columnModel = this.getTableColumnModel();
+ columnModel.setColumnVisible(col, !columnModel.isColumnVisible(col));
+ }
+}
+
+
+/**
+ * Sets the width of a column.
+ *
+ * @param col {int} the model index of column.
+ * @param width {int} the new width in pixels.
+ */
+qx.Proto.setColumnWidth = function(col, width) {
+ this.getTableColumnModel().setColumnWidth(col, width);
+}
+
+
+// overridden
+qx.Proto._changeInnerWidth = function(newValue, oldValue) {
+ var self = this;
+ window.setTimeout(function() {
+ self._updateScrollBarVisibility();
+ qx.ui.core.Widget.flushGlobalQueues();
+ }, 0);
+
+ return qx.ui.layout.VerticalBoxLayout.prototype._changeInnerWidth.call(this, newValue, oldValue);
+}
+
+
+// overridden
+qx.Proto._changeInnerHeight = function(newValue, oldValue) {
+ var self = this;
+ window.setTimeout(function() {
+ self._updateScrollBarVisibility();
+ qx.ui.core.Widget.flushGlobalQueues();
+ }, 0);
+
+ return qx.ui.layout.VerticalBoxLayout.prototype._changeInnerHeight.call(this, newValue, oldValue);
+}
+
+
+// overridden
+qx.Proto._afterAppear = function() {
+ qx.ui.layout.VerticalBoxLayout.prototype._afterAppear.call(this);
+
+ this._updateScrollBarVisibility();
+}
+
+
+// overridden
+qx.Proto.dispose = function() {
+ if (this.getDisposed()) {
+ return true;
+ }
+
+ if (this._tableModel) {
+ this._tableModel.removeEventListener(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED, this._onTableModelMetaDataChanged, this);
+ }
+
+ this._columnVisibilityBt.removeEventListener("execute", this._onColumnVisibilityBtExecuted, this);
+ this._columnVisibilityBt.dispose();
+
+ this._cleanupColumnVisibilityMenu();
+
+ this._cleanUpMetaColumns(0);
+
+ var selectionModel = this.getSelectionModel();
+ if (selectionModel != null) {
+ selectionModel.removeEventListener("changeSelection", this._onSelectionChanged, this);
+ }
+
+ var tableModel = this.getTableModel();
+ if (tableModel != null) {
+ tableModel.removeEventListener(qx.ui.table.TableModel.EVENT_TYPE_META_DATA_CHANGED, this._onTableModelMetaDataChanged, this);
+ tableModel.removeEventListener(qx.ui.table.TableModel.EVENT_TYPE_DATA_CHANGED, this._onTableModelDataChanged, this);
+ }
+
+ var tableColumnModel = this.getTableColumnModel();
+ if (tableColumnModel) {
+ tableColumnModel.removeEventListener("visibilityChanged", this._onColVisibilityChanged, this);
+ tableColumnModel.removeEventListener("widthChanged", this._onColWidthChanged, this);
+ tableColumnModel.removeEventListener("orderChanged", this._onColOrderChanged, this);
+ }
+
+ this.removeEventListener("keydown", this._onkeydown);
+ this.removeEventListener("keypress", this._onkeypress);
+
+ return qx.ui.layout.VerticalBoxLayout.prototype.dispose.call(this);
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableColumnModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableColumnModel.js
new file mode 100644
index 0000000000..334187a268
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableColumnModel.js
@@ -0,0 +1,399 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+// These are needed because of their instantiation at bottom. I don't think this
+// is a good idea. (wpbasti)
+#require(qx.ui.table.DefaultHeaderCellRenderer)
+#require(qx.ui.table.DefaultDataCellRenderer)
+#require(qx.ui.table.TextFieldCellEditorFactory)
+
+************************************************************************ */
+
+/**
+ * A model that contains all meta data about columns, such as width, renderers,
+ * visibility and order.
+ *
+ * @event widthChanged {qx.event.type.DataEvent} Fired when the width of a
+ * column has changed. The data property of the event is a map having the
+ * following attributes:
+ * <ul>
+ * <li>col: The model index of the column the width of which has changed.</li>
+ * <li>newWidth: The new width of the column in pixels.</li>
+ * <li>oldWidth: The old width of the column in pixels.</li>
+ * </ul>
+ * @event visibilityChangedPre {qx.event.type.DataEvent} Fired when the
+ * visibility of a column has changed. This event is equal to
+ * "visibilityChanged", but is fired right before.
+ * @event visibilityChanged {qx.event.type.DataEvent} Fired when the
+ * visibility of a column has changed. The data property of the
+ * event is a map having the following attributes:
+ * <ul>
+ * <li>col: The model index of the column the visibility of which has changed.</li>
+ * <li>visible: Whether the column is now visible.</li>
+ * </ul>
+ * @event orderChanged {qx.event.type.DataEvent} Fired when the column order
+ * has changed. The data property of the
+ * event is a map having the following attributes:
+ * <ul>
+ * <li>col: The model index of the column that was moved.</li>
+ * <li>fromOverXPos: The old overall x position of the column.</li>
+ * <li>toOverXPos: The new overall x position of the column.</li>
+ * </ul>
+ *
+ * @see com.ptvag.webcomponent.ui.table.TableModel
+ */
+qx.OO.defineClass("qx.ui.table.TableColumnModel", qx.core.Target,
+function() {
+ qx.core.Target.call(this);
+});
+
+
+/**
+ * Initializes the column model.
+ *
+ * @param colCount {int} the number of columns the model should have.
+ */
+qx.Proto.init = function(colCount) {
+ this._columnDataArr = [];
+
+ var width = qx.ui.table.TableColumnModel.DEFAULT_WIDTH;
+ var headerRenderer = qx.ui.table.TableColumnModel.DEFAULT_HEADER_RENDERER;
+ var dataRenderer = qx.ui.table.TableColumnModel.DEFAULT_DATA_RENDERER;
+ var editorFactory = qx.ui.table.TableColumnModel.DEFAULT_EDITOR_FACTORY;
+ this._overallColumnArr = [];
+ this._visibleColumnArr = [];
+ for (var col = 0; col < colCount; col++) {
+ this._columnDataArr[col] = { width:width, headerRenderer:headerRenderer,
+ dataRenderer:dataRenderer, editorFactory:editorFactory }
+ this._overallColumnArr[col] = col;
+ this._visibleColumnArr[col] = col;
+ }
+
+ this._colToXPosMap = null;
+}
+
+
+/**
+ * Sets the width of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @param width {int} the new width the column should get in pixels.
+ */
+qx.Proto.setColumnWidth = function(col, width) {
+ var oldWidth = this._columnDataArr[col].width;
+ if (oldWidth != width) {
+ this._columnDataArr[col].width = width;
+ if (this.hasEventListeners("widthChanged")) {
+ var data = { col:col, newWidth:width, oldWidth:oldWidth }
+ this.dispatchEvent(new qx.event.type.DataEvent("widthChanged", data), true);
+ }
+ }
+}
+
+
+/**
+ * Returns the width of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {int} the width of the column in pixels.
+ */
+qx.Proto.getColumnWidth = function(col) {
+ return this._columnDataArr[col].width;
+}
+
+
+/**
+ * Sets the header renderer of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @param renderer {HeaderCellRenderer} the new header renderer the column
+ * should get.
+ */
+qx.Proto.setHeaderCellRenderer = function(col, renderer) {
+ this._columnDataArr[col].headerRenderer = renderer;
+}
+
+
+/**
+ * Returns the header renderer of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {HeaderCellRenderer} the header renderer of the column.
+ */
+qx.Proto.getHeaderCellRenderer = function(col) {
+ return this._columnDataArr[col].headerRenderer;
+}
+
+
+/**
+ * Sets the data renderer of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @param renderer {DataCellRenderer} the new data renderer the column should get.
+ */
+qx.Proto.setDataCellRenderer = function(col, renderer) {
+ this._columnDataArr[col].dataRenderer = renderer;
+}
+
+
+/**
+ * Returns the data renderer of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {DataCellRenderer} the data renderer of the column.
+ */
+qx.Proto.getDataCellRenderer = function(col) {
+ return this._columnDataArr[col].dataRenderer;
+}
+
+
+/**
+ * Sets the cell editor factory of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @param factory {CellEditorFactory} the new cell editor factory the column should get.
+ */
+qx.Proto.setCellEditorFactory = function(col, factory) {
+ this._columnDataArr[col].editorFactory = factory;
+}
+
+
+/**
+ * Returns the cell editor factory of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {CellEditorFactory} the cell editor factory of the column.
+ */
+qx.Proto.getCellEditorFactory = function(col) {
+ return this._columnDataArr[col].editorFactory;
+}
+
+
+/**
+ * Returns the map that translates model indexes to x positions.
+ * <p>
+ * The returned map contains for a model index (int) a map having two
+ * properties: overX (the overall x position of the column, int) and
+ * visX (the visible x position of the column, int). visX is missing for
+ * hidden columns.
+ *
+ * @return the "column to x postion" map.
+ */
+qx.Proto._getColToXPosMap = function() {
+ if (this._colToXPosMap == null) {
+ this._colToXPosMap = {};
+ for (var overX = 0; overX < this._overallColumnArr.length; overX++) {
+ var col = this._overallColumnArr[overX];
+ this._colToXPosMap[col] = { overX:overX }
+ }
+ for (var visX = 0; visX < this._visibleColumnArr.length; visX++) {
+ var col = this._visibleColumnArr[visX];
+ this._colToXPosMap[col].visX = visX;
+ }
+ }
+ return this._colToXPosMap;
+}
+
+
+/**
+ * Returns the number of visible columns.
+ *
+ * @return {int} the number of visible columns.
+ */
+qx.Proto.getVisibleColumnCount = function() {
+ return this._visibleColumnArr.length;
+}
+
+
+/**
+ * Returns the model index of a column at a certain visible x position.
+ *
+ * @param visXPos {int} the visible x position of the column.
+ * @return {int} the model index of the column.
+ */
+qx.Proto.getVisibleColumnAtX = function(visXPos) {
+ return this._visibleColumnArr[visXPos];
+}
+
+
+/**
+ * Returns the visible x position of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {int} the visible x position of the column.
+ */
+qx.Proto.getVisibleX = function(col) {
+ return this._getColToXPosMap()[col].visX;
+}
+
+
+/**
+ * Returns the overall number of columns (including hidden columns).
+ *
+ * @return {int} the overall number of columns.
+ */
+qx.Proto.getOverallColumnCount = function() {
+ return this._overallColumnArr.length;
+}
+
+
+/**
+ * Returns the model index of a column at a certain overall x position.
+ *
+ * @param overXPos {int} the overall x position of the column.
+ * @return {int} the model index of the column.
+ */
+qx.Proto.getOverallColumnAtX = function(overXPos) {
+ return this._overallColumnArr[overXPos];
+}
+
+
+/**
+ * Returns the overall x position of a column.
+ *
+ * @param col {int} the model index of the column.
+ * @return {int} the overall x position of the column.
+ */
+qx.Proto.getOverallX = function(col) {
+ return this._getColToXPosMap()[col].overX;
+}
+
+
+/**
+ * Returns whether a certain column is visible.
+ *
+ * @param col {int} the model index of the column.
+ * @return {boolean} whether the column is visible.
+ */
+qx.Proto.isColumnVisible = function(col) {
+ return (this._getColToXPosMap()[col].visX != null);
+}
+
+
+/**
+ * Sets whether a certain column is visible.
+ *
+ * @param col {int} the model index of the column.
+ * @param visible {boolean} whether the column should be visible.
+ */
+qx.Proto.setColumnVisible = function(col, visible) {
+ if (visible != this.isColumnVisible(col)) {
+ if (visible) {
+ var colToXPosMap = this._getColToXPosMap();
+
+ var overX = colToXPosMap[col].overX;
+ if (overX == null) {
+ throw new Error("Showing column failed: " + col
+ + ". The column is not added to this TablePaneModel.");
+ }
+
+ // get the visX of the next visible column after the column to show
+ var nextVisX;
+ for (var x = overX + 1; x < this._overallColumnArr.length; x++) {
+ var currCol = this._overallColumnArr[x];
+ var currVisX = colToXPosMap[currCol].visX;
+ if (currVisX != null) {
+ nextVisX = currVisX;
+ break;
+ }
+ }
+
+ // If there comes no visible column any more, then show the column
+ // at the end
+ if (nextVisX == null) {
+ nextVisX = this._visibleColumnArr.length;
+ }
+
+ // Add the column to the visible columns
+ this._visibleColumnArr.splice(nextVisX, 0, col);
+ } else {
+ var visX = this.getVisibleX(col);
+ this._visibleColumnArr.splice(visX, 1);
+ }
+
+ // Invalidate the _colToXPosMap
+ this._colToXPosMap = null;
+
+ // Inform the listeners
+ if (! this._internalChange) {
+ if (this.hasEventListeners("visibilityChangedPre")) {
+ var data = { col:col, visible:visible }
+ this.dispatchEvent(new qx.event.type.DataEvent("visibilityChangedPre", data), true);
+ }
+ if (this.hasEventListeners("visibilityChanged")) {
+ var data = { col:col, visible:visible }
+ this.dispatchEvent(new qx.event.type.DataEvent("visibilityChanged", data), true);
+ }
+ }
+
+ //this.debug("setColumnVisible col:"+col+",visible:"+visible+",this._overallColumnArr:"+this._overallColumnArr+",this._visibleColumnArr:"+this._visibleColumnArr);
+ }
+}
+
+
+/**
+ * Moves a column.
+ *
+ * @param fromOverXPos {int} the overall x postion of the column to move.
+ * @param toOverXPos {int} the overall x postion of where the column should be
+ * moved to.
+ */
+qx.Proto.moveColumn = function(fromOverXPos, toOverXPos) {
+ this._internalChange = true;
+
+ var col = this._overallColumnArr[fromOverXPos];
+ var visible = this.isColumnVisible(col);
+
+ if (visible) {
+ this.setColumnVisible(col, false);
+ }
+
+ this._overallColumnArr.splice(fromOverXPos, 1);
+ this._overallColumnArr.splice(toOverXPos, 0, col);
+
+ // Invalidate the _colToXPosMap
+ this._colToXPosMap = null;
+
+ if (visible) {
+ this.setColumnVisible(col, true);
+ }
+
+ this._internalChange = false;
+
+ // Inform the listeners
+ if (this.hasEventListeners("orderChanged")) {
+ var data = { col:col, fromOverXPos:fromOverXPos, toOverXPos:toOverXPos }
+ this.dispatchEvent(new qx.event.type.DataEvent("orderChanged", data), true);
+ }
+}
+
+
+/** {int} the default width of a column in pixels. */
+qx.Class.DEFAULT_WIDTH = 100;
+
+/** {DefaultDataCellRenderer} the default header cell renderer. */
+qx.Class.DEFAULT_HEADER_RENDERER = new qx.ui.table.DefaultHeaderCellRenderer;
+
+/** {DefaultDataCellRenderer} the default data cell renderer. */
+qx.Class.DEFAULT_DATA_RENDERER = new qx.ui.table.DefaultDataCellRenderer;
+
+/** {TextFieldCellEditorFactory} the default editor factory. */
+qx.Class.DEFAULT_EDITOR_FACTORY = new qx.ui.table.TextFieldCellEditorFactory;
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableModel.js
new file mode 100644
index 0000000000..6bf4a55291
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TableModel.js
@@ -0,0 +1,243 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * The data model of a table.
+ *
+ * @event dataChanged {qx.event.type.DataEvent} Fired when the table data changed
+ * (the stuff shown in the table body). The data property of the event
+ * may be null or a map having the following attributes:
+ * <ul>
+ * <li>firstRow: The index of the first row that has changed.</li>
+ * <li>lastRow: The index of the last row that has changed.</li>
+ * <li>firstColumn: The model index of the first column that has changed.</li>
+ * <li>lastColumn: The model index of the last column that has changed.</li>
+ * </ul>
+ * @event metaDataChanged {qx.event.type.Event} Fired when the meta data changed
+ * (the stuff shown in the table header).
+ */
+qx.OO.defineClass("qx.ui.table.TableModel", qx.core.Target,
+function() {
+ qx.core.Target.call(this);
+});
+
+
+/**
+ * Returns the number of rows in the model.
+ *
+ * @return {int} the number of rows.
+ */
+qx.Proto.getRowCount = function() {
+ throw new Error("getRowCount is abstract");
+}
+
+
+/**
+ * <p>Returns the data of one row. This function may be overriden by models which hold
+ * all data of a row in one object. By using this function, clients have a way of
+ * quickly retrieving the entire row data.</p>
+ *
+ * <p><b>Important:</b>Models which do not have their row data accessible in one object
+ * may return null.</p>
+ *
+ * @param rowIndex {int} the model index of the row.
+ * @return {Object} the row data as an object or null if the model does not support row data
+ * objects. The details on the object returned are determined by the model
+ * implementation only.
+ */
+qx.Proto.getRowData = function(rowIndex) {
+ return null;
+}
+
+
+/**
+ * Returns the number of columns in the model.
+ *
+ * @return {int} the number of columns.
+ */
+qx.Proto.getColumnCount = function() {
+ throw new Error("getColumnCount is abstract");
+}
+
+
+/**
+ * Returns the ID of column. The ID may be used to identify columns
+ * independent from their index in the model. E.g. for being aware of added
+ * columns when saving the width of a column.
+ *
+ * @param columnIndex {int} the index of the column.
+ * @return {string} the ID of the column.
+ */
+qx.Proto.getColumnId = function(columnIndex) {
+ throw new Error("getColumnId is abstract");
+}
+
+
+/**
+ * Returns the index of a column.
+ *
+ * @param columnId {string} the ID of the column.
+ * @return {int} the index of the column.
+ */
+qx.Proto.getColumnIndexById = function(columnId) {
+ throw new Error("getColumnIndexById is abstract");
+}
+
+
+/**
+ * Returns the name of a column. This name will be shown to the user in the
+ * table header.
+ *
+ * @param columnIndex {int} the index of the column.
+ * @return {string} the name of the column.
+ */
+qx.Proto.getColumnName = function(columnIndex) {
+ throw new Error("getColumnName is abstract");
+}
+
+
+/**
+ * Returns whether a column is editable.
+ *
+ * @param columnIndex {int} the column to check.
+ * @return {boolean} whether the column is editable.
+ */
+qx.Proto.isColumnEditable = function(columnIndex) {
+ return false;
+}
+
+
+/**
+ * Returns whether a column is sortable.
+ *
+ * @param columnIndex {int} the column to check.
+ * @return {boolean} whether the column is sortable.
+ */
+qx.Proto.isColumnSortable = function(columnIndex) {
+ return false;
+}
+
+
+/**
+ * Sorts the model by a column.
+ *
+ * @param columnIndex {int} the column to sort by.
+ * @param ascending {boolean} whether to sort ascending.
+ */
+qx.Proto.sortByColumn = function(columnIndex, ascending) {
+}
+
+
+/**
+ * Returns the column index the model is sorted by. If the model is not sorted
+ * -1 is returned.
+ *
+ * @return {int} the column index the model is sorted by.
+ */
+qx.Proto.getSortColumnIndex = function() {
+ return -1;
+}
+
+
+/**
+ * Returns whether the model is sorted ascending.
+ *
+ * @return {boolean} whether the model is sorted ascending.
+ */
+qx.Proto.isSortAscending = function() {
+ return true;
+}
+
+
+/**
+ * Prefetches some rows. This is a hint to the model that the specified rows
+ * will be read soon.
+ *
+ * @param firstRowIndex {int} the index of first row.
+ * @param lastRowIndex {int} the index of last row.
+ */
+qx.Proto.prefetchRows = function(firstRowIndex, lastRowIndex) {
+}
+
+
+/**
+ * Returns a cell value by column index.
+ *
+ * @param columnIndex {int} the index of the column.
+ * @param rowIndex {int} the index of the row.
+ * @return {var} The value of the cell.
+ * @see #getValueById{}
+ */
+qx.Proto.getValue = function(columnIndex, rowIndex) {
+ throw new Error("getValue is abstract");
+}
+
+
+/**
+ * Returns a cell value by column ID.
+ * <p>
+ * Whenever you have the choice, use {@link #getValue()} instead,
+ * because this should be faster.
+ *
+ * @param columnId {string} the ID of the column.
+ * @param rowIndex {int} the index of the row.
+ * @return {var} the value of the cell.
+ */
+qx.Proto.getValueById = function(columnId, rowIndex) {
+ return this.getValue(this.getColumnIndexById(columnId), rowIndex);
+}
+
+
+/**
+ * Sets a cell value by column index.
+ *
+ * @param columnIndex {int} The index of the column.
+ * @param rowIndex {int} the index of the row.
+ * @param value {var} The new value.
+ * @see #setValueById{}
+ */
+qx.Proto.setValue = function(columnIndex, rowIndex, value) {
+ throw new Error("setValue is abstract");
+}
+
+
+/**
+ * Sets a cell value by column ID.
+ * <p>
+ * Whenever you have the choice, use {@link #setValue()} instead,
+ * because this should be faster.
+ *
+ * @param columnId {string} The ID of the column.
+ * @param rowIndex {int} The index of the row.
+ * @param value {var} The new value.
+ */
+qx.Proto.setValueById = function(columnId, rowIndex, value) {
+ return this.setValue(this.getColumnIndexById(columnId), rowIndex, value);
+}
+
+
+/** {string} The type of the event fired when the data changed. */
+qx.Class.EVENT_TYPE_DATA_CHANGED = "dataChanged";
+
+/** {string} The type of the event fired when the meta data changed. */
+qx.Class.EVENT_TYPE_META_DATA_CHANGED = "metaDataChanged";
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePane.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePane.js
new file mode 100644
index 0000000000..41db2ab274
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePane.js
@@ -0,0 +1,486 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * The table pane that shows a certain section from a table. This class handles
+ * the display of the data part of a table and is therefore the base for virtual
+ * scrolling.
+ *
+ * @param paneScroller {TablePaneScroller} the TablePaneScroller the header belongs to.
+ */
+qx.OO.defineClass("qx.ui.table.TablePane", qx.ui.basic.Terminator,
+function(paneScroller) {
+ qx.ui.basic.Terminator.call(this);
+
+ this._paneScroller = paneScroller;
+
+ this.debug("USE_ARRAY_JOIN:" + qx.ui.table.TablePane.USE_ARRAY_JOIN + ", USE_TABLE:" + qx.ui.table.TablePane.USE_TABLE);
+
+ this._lastColCount = 0;
+ this._lastRowCount = 0;
+});
+
+/** The index of the first row to show. */
+qx.OO.addProperty({ name:"firstVisibleRow", type:"number", defaultValue:0 });
+
+/** The number of rows to show. */
+qx.OO.addProperty({ name:"visibleRowCount", type:"number", defaultValue:0 });
+
+
+// property modifier
+qx.Proto._modifyFirstVisibleRow = function(propValue, propOldValue, propData) {
+ this._updateContent();
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyVisibleRowCount = function(propValue, propOldValue, propData) {
+ this._updateContent();
+ return true;
+}
+
+
+// overridden
+qx.Proto._afterAppear = function() {
+ qx.ui.basic.Terminator.prototype._afterAppear.call(this);
+
+ if (this._updateWantedWhileInvisible) {
+ // We are visible now and an update was wanted while we were invisible
+ // -> Do the update now
+ this._updateContent();
+ this._updateWantedWhileInvisible = false;
+ }
+};
+
+
+/**
+ * Returns the TablePaneScroller this pane belongs to.
+ *
+ * @return {TablePaneScroller} the TablePaneScroller.
+ */
+qx.Proto.getPaneScroller = function() {
+ return this._paneScroller;
+};
+
+
+/**
+ * Returns the table this pane belongs to.
+ *
+ * @return {Table} the table.
+ */
+qx.Proto.getTable = function() {
+ return this._paneScroller.getTable();
+};
+
+
+/**
+ * Sets the currently focused cell.
+ *
+ * @param col {int} the model index of the focused cell's column.
+ * @param row {int} the model index of the focused cell's row.
+ * @param massUpdate {boolean ? false} Whether other updates are planned as well.
+ * If true, no repaint will be done.
+ */
+qx.Proto.setFocusedCell = function(col, row, massUpdate) {
+ if (col != this._focusedCol || row != this._focusedRow) {
+ var oldCol = this._focusedCol;
+ var oldRow = this._focusedRow;
+ this._focusedCol = col;
+ this._focusedRow = row;
+
+ // Update the focused row background
+ if (row != oldRow && !massUpdate) {
+ // NOTE: Only the old and the new row need update
+ this._updateContent(false, oldRow, true);
+ this._updateContent(false, row, true);
+ }
+ }
+}
+
+
+/**
+ * Event handler. Called when the selection has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onSelectionChanged = function(evt) {
+ this._updateContent(false, null, true);
+}
+
+
+/**
+ * Event handler. Called when the table gets or looses the focus.
+ */
+qx.Proto._onFocusChanged = function(evt) {
+ this._updateContent(false, null, true);
+};
+
+
+/**
+ * Event handler. Called when the width of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColWidthChanged = function(evt) {
+ this._updateContent(true);
+}
+
+
+/**
+ * Event handler. Called the column order has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColOrderChanged = function(evt) {
+ this._updateContent(true);
+}
+
+
+/**
+ * Event handler. Called when the pane model has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onPaneModelChanged = function(evt) {
+ this._updateContent(true);
+}
+
+
+/**
+ * Event handler. Called when the table model data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelDataChanged = function(evt) {
+ var data = evt.getData ? evt.getData() : null;
+
+ var firstRow = this.getFirstVisibleRow();
+ var rowCount = this.getVisibleRowCount();
+ if (data == null || data.lastRow == -1
+ || data.lastRow >= firstRow && data.firstRow < firstRow + rowCount)
+ {
+ // The change intersects this pane
+ this._updateContent();
+ }
+}
+
+
+/**
+ * Event handler. Called when the table model meta data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelMetaDataChanged = function(evt) {
+ this._updateContent();
+}
+
+
+/**
+ * Updates the content of the pane.
+ *
+ * @param completeUpdate {boolean ? false} if true a complete update is performed.
+ * On a complete update all cell widgets are recreated.
+ * @param onlyRow {int ? null} if set only the specified row will be updated.
+ * @param onlySelectionOrFocusChanged {boolean ? false} if true, cell values won't
+ * be updated. Only the row background will.
+ */
+qx.Proto._updateContent = function(completeUpdate, onlyRow,
+ onlySelectionOrFocusChanged)
+{
+ if (! this.isSeeable()) {
+ this._updateWantedWhileInvisible = true;
+ return;
+ }
+
+ if (qx.ui.table.TablePane.USE_ARRAY_JOIN) {
+ this._updateContent_array_join(completeUpdate, onlyRow, onlySelectionOrFocusChanged);
+ } else {
+ this._updateContent_orig(completeUpdate, onlyRow, onlySelectionOrFocusChanged);
+ }
+}
+
+
+qx.Proto._updateContent_array_join = function(completeUpdate, onlyRow,
+ onlySelectionOrFocusChanged)
+{
+ var TablePane = qx.ui.table.TablePane;
+
+ var table = this.getTable();
+
+ var selectionModel = table.getSelectionModel();
+ var tableModel = table.getTableModel();
+ var columnModel = table.getTableColumnModel();
+ var paneModel = this.getPaneScroller().getTablePaneModel();
+ var rowRenderer = table.getDataRowRenderer();
+
+ var colCount = paneModel.getColumnCount();
+ var rowHeight = table.getRowHeight();
+
+ var firstRow = this.getFirstVisibleRow();
+ var rowCount = this.getVisibleRowCount();
+ var modelRowCount = tableModel.getRowCount();
+ if (firstRow + rowCount > modelRowCount) {
+ rowCount = Math.max(0, modelRowCount - firstRow);
+ }
+
+ var cellInfo = { table:table };
+ cellInfo.styleHeight = rowHeight;
+
+ var htmlArr = [];
+ var rowWidth = paneModel.getTotalWidth();
+
+ if (TablePane.USE_TABLE) {
+ htmlArr.push('<table cellspacing\="0" cellpadding\="0" style\="table-layout:fixed;font-family:\'Segoe UI\', Corbel, Calibri, Tahoma, \'Lucida Sans Unicode\', sans-serif;font-size:11px;width:');
+ htmlArr.push(rowWidth);
+ htmlArr.push('px"><colgroup>');
+
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+
+ htmlArr.push();
+ htmlArr.push(columnModel.getColumnWidth(col));
+ htmlArr.push('"/>');
+ }
+
+ htmlArr.push('</colgroup><tbody>');
+ }
+
+ tableModel.prefetchRows(firstRow, firstRow + rowCount - 1);
+ for (var y = 0; y < rowCount; y++) {
+ var row = firstRow + y;
+
+ cellInfo.row = row;
+ cellInfo.selected = selectionModel.isSelectedIndex(row);
+ cellInfo.focusedRow = (this._focusedRow == row);
+ cellInfo.rowData = tableModel.getRowData(row);
+
+ // Update this row
+ if (TablePane.USE_TABLE) {
+ htmlArr.push('<tr style\="height:');
+ htmlArr.push(rowHeight);
+ } else {
+ htmlArr.push('<div style\="position:absolute;font-family:\'Segoe UI\', Corbel, Calibri, Tahoma, \'Lucida Sans Unicode\', sans-serif;font-size:11px;left:0px;top:');
+ htmlArr.push(y * rowHeight);
+ htmlArr.push('px;width:');
+ htmlArr.push(rowWidth);
+ htmlArr.push('px;height:');
+ htmlArr.push(rowHeight);
+ htmlArr.push('px;background-color:');
+ }
+
+ rowRenderer._createRowStyle_array_join(cellInfo, htmlArr);
+
+ htmlArr.push('">');
+
+ var left = 0;
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+ cellInfo.xPos = x;
+ cellInfo.col = col;
+ cellInfo.editable = tableModel.isColumnEditable(col);
+ cellInfo.focusedCol = (this._focusedCol == col);
+ cellInfo.value = tableModel.getValue(col, row);
+ var cellWidth = columnModel.getColumnWidth(col);
+
+ cellInfo.styleLeft = left;
+ cellInfo.styleWidth = cellWidth;
+
+ var cellRenderer = columnModel.getDataCellRenderer(col);
+ cellRenderer.createDataCellHtml_array_join(cellInfo, htmlArr);
+
+ left += cellWidth;
+ }
+
+ if (TablePane.USE_TABLE) {
+ htmlArr.push('</tr>');
+ } else {
+ htmlArr.push('</div>');
+ }
+ }
+
+ if (TablePane.USE_TABLE) {
+ htmlArr.push('</tbody></table>');
+ }
+
+ var elem = this.getElement();
+ // this.debug(">>>" + htmlArr.join("") + "<<<")
+ elem.innerHTML = htmlArr.join("");
+
+ this.setHeight(rowCount * rowHeight);
+
+ this._lastColCount = colCount;
+ this._lastRowCount = rowCount;
+}
+
+
+qx.Proto._updateContent_orig = function(completeUpdate, onlyRow,
+ onlySelectionOrFocusChanged)
+{
+ var TablePane = qx.ui.table.TablePane;
+
+ var table = this.getTable();
+
+ var alwaysUpdateCells = table.getAlwaysUpdateCells();
+
+ var selectionModel = table.getSelectionModel();
+ var tableModel = table.getTableModel();
+ var columnModel = table.getTableColumnModel();
+ var paneModel = this.getPaneScroller().getTablePaneModel();
+ var rowRenderer = table.getDataRowRenderer();
+
+ var colCount = paneModel.getColumnCount();
+ var rowHeight = table.getRowHeight();
+
+ var firstRow = this.getFirstVisibleRow();
+ var rowCount = this.getVisibleRowCount();
+ var modelRowCount = tableModel.getRowCount();
+ if (firstRow + rowCount > modelRowCount) {
+ rowCount = Math.max(0, modelRowCount - firstRow);
+ }
+
+ // Remove the rows that are not needed any more
+ if (completeUpdate || this._lastRowCount > rowCount) {
+ var firstRowToRemove = completeUpdate ? 0 : rowCount;
+ this._cleanUpRows(firstRowToRemove);
+ }
+
+ if (TablePane.USE_TABLE) {
+ throw new Error("Combination of USE_TABLE==true and USE_ARRAY_JOIN==false is not yet implemented");
+ }
+
+ var elem = this.getElement();
+ var childNodes = elem.childNodes;
+ var cellInfo = { table:table };
+ tableModel.prefetchRows(firstRow, firstRow + rowCount - 1);
+ for (var y = 0; y < rowCount; y++) {
+ var row = firstRow + y;
+ if ((onlyRow != null) && (row != onlyRow)) {
+ continue;
+ }
+
+ cellInfo.row = row;
+ cellInfo.selected = selectionModel.isSelectedIndex(row);
+ cellInfo.focusedRow = (this._focusedRow == row);
+ cellInfo.rowData = tableModel.getRowData(row);
+
+ // Update this row
+ var rowElem;
+ var recyleRowElem;
+ if (y < childNodes.length) {
+ rowElem = childNodes[y];
+ recyleRowElem = true
+ } else {
+ var rowElem = document.createElement("div");
+
+ //rowElem.style.position = "relative";
+ rowElem.style.position = "absolute";
+ rowElem.style.left = "0px";
+ rowElem.style.top = (y * rowHeight) + "px";
+
+ rowElem.style.height = rowHeight + "px";
+ rowElem.style.fontFamily = TablePane.CONTENT_ROW_FONT_FAMILY;
+ rowElem.style.fontSize = TablePane.CONTENT_ROW_FONT_SIZE;
+ elem.appendChild(rowElem);
+ recyleRowElem = false;
+ }
+
+ rowRenderer.updateDataRowElement(cellInfo, rowElem);
+
+ if (alwaysUpdateCells || !recyleRowElem || !onlySelectionOrFocusChanged) {
+ var html = "";
+ var left = 0;
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+ cellInfo.xPos = x;
+ cellInfo.col = col;
+ cellInfo.editable = tableModel.isColumnEditable(col);
+ cellInfo.focusedCol = (this._focusedCol == col);
+ cellInfo.value = tableModel.getValue(col, row);
+ var width = columnModel.getColumnWidth(col);
+ cellInfo.style = 'position:absolute;left:' + left
+ + 'px;top:0px;width:' + width
+ + 'px; height:' + rowHeight + "px";
+
+ var cellRenderer = columnModel.getDataCellRenderer(col);
+ if (recyleRowElem) {
+ var cellElem = rowElem.childNodes[x];
+ cellRenderer.updateDataCellElement(cellInfo, cellElem);
+ } else {
+ html += cellRenderer.createDataCellHtml(cellInfo);
+ }
+
+ left += width;
+ }
+ if (! recyleRowElem) {
+ rowElem.style.width = left + "px";
+ rowElem.innerHTML = html;
+ }
+ }
+ }
+
+ this.setHeight(rowCount * rowHeight);
+
+ this._lastColCount = colCount;
+ this._lastRowCount = rowCount;
+}
+
+
+/**
+ * Cleans up the row widgets.
+ *
+ * @param firstRowToRemove {int} the visible index of the first row to clean up.
+ * All following rows will be cleaned up, too.
+ */
+qx.Proto._cleanUpRows = function(firstRowToRemove) {
+ var elem = this.getElement();
+ if (elem) {
+ var childNodes = this.getElement().childNodes;
+ var paneModel = this.getPaneScroller().getTablePaneModel();
+ var colCount = paneModel.getColumnCount();
+ for (var y = childNodes.length - 1; y >= firstRowToRemove; y--) {
+ elem.removeChild(childNodes[y]);
+ }
+ }
+}
+
+
+// overridden
+qx.Proto.dispose = function() {
+ if (this.getDisposed()) {
+ return true;
+ }
+
+ this._cleanUpRows(0);
+
+ return qx.ui.basic.Terminator.prototype.dispose.call(this);
+}
+
+
+qx.Class.USE_ARRAY_JOIN = false;
+qx.Class.USE_TABLE = false;
+
+
+qx.Class.CONTENT_ROW_FONT_FAMILY = '"Segoe UI", Corbel, Calibri, Tahoma, "Lucida Sans Unicode", sans-serif';
+qx.Class.CONTENT_ROW_FONT_SIZE = "11px";
+
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneHeader.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneHeader.js
new file mode 100644
index 0000000000..657950293f
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneHeader.js
@@ -0,0 +1,276 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * Shows the header of a table.
+ *
+ * @param paneScroller {TablePaneScroller} the TablePaneScroller the header belongs to.
+ */
+qx.OO.defineClass("qx.ui.table.TablePaneHeader", qx.ui.layout.HorizontalBoxLayout,
+function(paneScroller) {
+ qx.ui.layout.HorizontalBoxLayout.call(this);
+
+ this._paneScroller = paneScroller;
+});
+
+
+/**
+ * Returns the TablePaneScroller this header belongs to.
+ *
+ * @return {TablePaneScroller} the TablePaneScroller.
+ */
+qx.Proto.getPaneScroller = function() {
+ return this._paneScroller;
+};
+
+
+/**
+ * Returns the table this header belongs to.
+ *
+ * @return {Table} the table.
+ */
+qx.Proto.getTable = function() {
+ return this._paneScroller.getTable();
+};
+
+
+/**
+ * Event handler. Called when the width of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColWidthChanged = function(evt) {
+ var data = evt.getData();
+ this.setColumnWidth(data.col, data.newWidth);
+}
+
+
+/**
+ * Event handler. Called the column order has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColOrderChanged = function(evt) {
+ this._updateContent(true);
+}
+
+
+/**
+ * Event handler. Called when the pane model has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onPaneModelChanged = function(evt) {
+ this._updateContent(true);
+}
+
+
+/**
+ * Event handler. Called when the table model meta data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelMetaDataChanged = function(evt) {
+ this._updateContent();
+}
+
+
+/**
+ * Sets the column width. This overrides the width from the column model.
+ *
+ * @param col {int} the column to change the width for.
+ * @param width {int} the new width.
+ */
+qx.Proto.setColumnWidth = function(col, width) {
+ var x = this.getPaneScroller().getTablePaneModel().getX(col);
+ var children = this.getChildren();
+ if (children[x] != null) {
+ children[x].setWidth(width);
+ }
+}
+
+
+/**
+ * Sets the column the mouse is currently over.
+ *
+ * @param col {int} the model index of the column the mouse is currently over or
+ * null if the mouse is over no column.
+ */
+qx.Proto.setMouseOverColumn = function(col) {
+ if (col != this._lastMouseOverColumn) {
+ var paneModel = this.getPaneScroller().getTablePaneModel();
+ var children = this.getChildren();
+
+ if (this._lastMouseOverColumn != null) {
+ var widget = children[paneModel.getX(this._lastMouseOverColumn)];
+ if (widget != null) {
+ widget.removeState("mouseover");
+ }
+ }
+ if (col != null) {
+ children[paneModel.getX(col)].addState("mouseover");
+ }
+
+ this._lastMouseOverColumn = col;
+ }
+}
+
+
+/**
+ * Shows the feedback shown while a column is moved by the user.
+ *
+ * @param col {int} the model index of the column to show the move feedback for.
+ * @param x {int} the x position the left side of the feeback should have
+ * (in pixels, relative to the left side of the header).
+ */
+qx.Proto.showColumnMoveFeedback = function(col, x) {
+ var elem = this.getElement();
+ if (this._moveFeedback == null) {
+ var xPos = this.getPaneScroller().getTablePaneModel().getX(col);
+ var cellWidget = this.getChildren()[xPos];
+
+ // Create the feedback
+ // Workaround: Since a cloned widget throws an exception when it is
+ // added to another component we have to create a new one
+ // using the renderer
+ //this._moveFeedback = cellWidget.clone();
+ var tableModel = this.getTable().getTableModel();
+ var columnModel = this.getTable().getTableColumnModel();
+ var cellInfo = { xPos:xPos, col:col, name:tableModel.getColumnName(col) }
+ var cellRenderer = columnModel.getHeaderCellRenderer(col);
+ this._moveFeedback = cellRenderer.createHeaderCell(cellInfo);
+
+ // Configure the feedback
+ with (this._moveFeedback) {
+ setWidth(cellWidget.getBoxWidth());
+ setHeight(cellWidget.getBoxHeight());
+ setZIndex(1000000);
+ setOpacity(0.8);
+ setTop(qx.dom.Location.getClientBoxTop(elem));
+ }
+ this.getTopLevelWidget().add(this._moveFeedback);
+ }
+
+ this._moveFeedback.setLeft(qx.dom.Location.getClientBoxLeft(elem) + x);
+}
+
+
+/**
+ * Hides the feedback shown while a column is moved by the user.
+ */
+qx.Proto.hideColumnMoveFeedback = function() {
+ if (this._moveFeedback != null) {
+ this.getTopLevelWidget().remove(this._moveFeedback);
+ this._moveFeedback.dispose();
+ this._moveFeedback = null;
+ }
+}
+
+
+/**
+ * Returns whether the column move feedback is currently shown.
+ */
+qx.Proto.isShowingColumnMoveFeedback = function() {
+ return this._moveFeedback != null;
+}
+
+
+/**
+ * Updates the content of the header.
+ *
+ * @param completeUpdate {boolean} if true a complete update is performed. On a
+ * complete update all header widgets are recreated.
+ */
+qx.Proto._updateContent = function(completeUpdate) {
+ var tableModel = this.getTable().getTableModel();
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneModel = this.getPaneScroller().getTablePaneModel();
+
+ var children = this.getChildren();
+ var oldColCount = children.length;
+ var colCount = paneModel.getColumnCount();
+
+ var sortedColum = tableModel.getSortColumnIndex();
+
+ // Remove all widgets on the complete update
+ if (completeUpdate) {
+ this._cleanUpCells();
+ }
+
+ // Update the header
+ var cellInfo = {};
+ cellInfo.sortedAscending = tableModel.isSortAscending();
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+
+ var colWidth = columnModel.getColumnWidth(col);
+
+ // TODO: Get real cell renderer
+ var cellRenderer = columnModel.getHeaderCellRenderer(col);
+
+ cellInfo.xPos = x;
+ cellInfo.col = col;
+ cellInfo.name = tableModel.getColumnName(col);
+ cellInfo.editable = tableModel.isColumnEditable(col);
+ cellInfo.sorted = (col == sortedColum);
+
+ // Get the cached widget
+ var cachedWidget = children[x];
+
+ // Create or update the widget
+ if (cachedWidget == null) {
+ // We have no cached widget -> create it
+ cachedWidget = cellRenderer.createHeaderCell(cellInfo);
+ cachedWidget.set({ width:colWidth, height:"100%" });
+
+ this.add(cachedWidget);
+ } else {
+ // This widget already created before -> recycle it
+ cellRenderer.updateHeaderCell(cellInfo, cachedWidget);
+ }
+ }
+}
+
+
+/**
+ * Cleans up all header cells.
+ */
+qx.Proto._cleanUpCells = function() {
+ var children = this.getChildren();
+ for (var x = children.length - 1; x >= 0; x--) {
+ var cellWidget = children[x];
+ //this.debug("disposed:" + cellWidget.getDisposed() + ",has parent: " + (cellWidget.getParent() != null) + ",x:"+x);
+ this.remove(cellWidget);
+ cellWidget.dispose();
+ }
+}
+
+
+// overridden
+qx.Proto.dispose = function() {
+ if (this.getDisposed()) {
+ return true;
+ }
+
+ return qx.ui.layout.HorizontalBoxLayout.prototype.dispose.call(this);
+}
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneModel.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneModel.js
new file mode 100644
index 0000000000..d53da59251
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneModel.js
@@ -0,0 +1,179 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * The model of a table pane. This model works as proxy to a
+ * {@link TableColumnModel} and manages the visual order of the columns shown in
+ * a {@link TablePane}.
+ *
+ * @param tableColumnModel {TableColumnModel} The TableColumnModel of which this
+ * model is the proxy.
+ *
+ * @event modelChanged {qx.event.type.Event} Fired when the model changed.
+ */
+qx.OO.defineClass("qx.ui.table.TablePaneModel", qx.core.Target,
+function(tableColumnModel) {
+ qx.core.Target.call(this);
+
+ tableColumnModel.addEventListener("visibilityChangedPre", this._onColVisibilityChanged, this);
+
+ this._tableColumnModel = tableColumnModel;
+});
+
+
+/** The visible x position of the first column this model should contain. */
+qx.OO.addProperty({ name : "firstColumnX", type : "number", defaultValue : 0 });
+
+/**
+ * The maximum number of columns this model should contain. If -1 this model will
+ * contain all remaining columns.
+ */
+qx.OO.addProperty({ name : "maxColumnCount", type : "number", defaultValue : -1 });
+
+
+// property modifier
+qx.Proto._modifyFirstColumnX = function(propValue, propOldValue, propData) {
+ this._columnCount = null;
+ this.createDispatchEvent(qx.ui.table.TablePaneModel.EVENT_TYPE_MODEL_CHANGED);
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyMaxColumnCount = function(propValue, propOldValue, propData) {
+ this._columnCount = null;
+ this.createDispatchEvent(qx.ui.table.TablePaneModel.EVENT_TYPE_MODEL_CHANGED);
+ return true;
+}
+
+
+/**
+ * Event handler. Called when the visibility of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColVisibilityChanged = function(evt) {
+ this._columnCount = null;
+
+ // TODO: Check whether the column is in this model (This is a little bit
+ // tricky, because the column could _have been_ in this model, but is
+ // not in it after the change)
+ this.createDispatchEvent(qx.ui.table.TablePaneModel.EVENT_TYPE_MODEL_CHANGED);
+}
+
+
+/**
+ * Returns the number of columns in this model.
+ *
+ * @return {int} the number of columns in this model.
+ */
+qx.Proto.getColumnCount = function() {
+ if (this._columnCount == null) {
+ var firstX = this.getFirstColumnX();
+ var maxColCount = this.getMaxColumnCount();
+ var totalColCount = this._tableColumnModel.getVisibleColumnCount();
+
+ if (maxColCount == -1 || (firstX + maxColCount) > totalColCount) {
+ this._columnCount = totalColCount - firstX;
+ } else {
+ this._columnCount = maxColCount;
+ }
+ }
+ return this._columnCount;
+}
+
+
+/**
+ * Returns the model index of the column at the position <code>xPos</code>.
+ *
+ * @param xPos {int} the x postion in the table pane of the column.
+ * @return {int} the model index of the column.
+ */
+qx.Proto.getColumnAtX = function(xPos) {
+ var firstX = this.getFirstColumnX();
+ return this._tableColumnModel.getVisibleColumnAtX(firstX + xPos);
+}
+
+
+/**
+ * Returns the x position of the column <code>col</code>.
+ *
+ * @param col {int} the model index of the column.
+ * @return {int} the x postion in the table pane of the column.
+ */
+qx.Proto.getX = function(col) {
+ var firstX = this.getFirstColumnX();
+ var maxColCount = this.getMaxColumnCount();
+
+ var x = this._tableColumnModel.getVisibleX(col) - firstX;
+ if (x >= 0 && (maxColCount == -1 || x < maxColCount)) {
+ return x;
+ } else {
+ return -1;
+ }
+}
+
+
+/**
+ * Gets the position of the left side of a column (in pixels, relative to the
+ * left side of the table pane).
+ * <p>
+ * This value corresponds to the sum of the widths of all columns left of the
+ * column.
+ *
+ * @param col {int} the model index of the column.
+ * @return the position of the left side of the column.
+ */
+qx.Proto.getColumnLeft = function(col) {
+ var left = 0;
+ var colCount = this.getColumnCount();
+ for (var x = 0; x < colCount; x++) {
+ var currCol = this.getColumnAtX(x);
+ if (currCol == col) {
+ return left;
+ }
+
+ left += this._tableColumnModel.getColumnWidth(currCol);
+ }
+ return -1;
+}
+
+
+/**
+ * Returns the total width of all columns in the model.
+ *
+ * @return {int} the total width of all columns in the model.
+ */
+qx.Proto.getTotalWidth = function() {
+ var totalWidth = 0;
+ var colCount = this.getColumnCount();
+ for (var x = 0; x < colCount; x++) {
+ var col = this.getColumnAtX(x);
+ totalWidth += this._tableColumnModel.getColumnWidth(col);
+ }
+ return totalWidth;
+}
+
+
+/** {string} The type of the event fired when the model changed. */
+qx.Class.EVENT_TYPE_MODEL_CHANGED = "modelChanged";
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneScroller.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneScroller.js
new file mode 100644
index 0000000000..d6f7773148
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TablePaneScroller.js
@@ -0,0 +1,1331 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * Shows a whole meta column. This includes a {@link TablePaneHeader},
+ * a {@link TablePane} and the needed scroll bars. This class handles the
+ * virtual scrolling and does all the mouse event handling.
+ *
+ * @param table {Table} the table the scroller belongs to.
+ */
+qx.OO.defineClass("qx.ui.table.TablePaneScroller", qx.ui.layout.VerticalBoxLayout,
+function(table) {
+ qx.ui.layout.VerticalBoxLayout.call(this);
+
+ this._table = table;
+
+ // init scrollbars
+ this._verScrollBar = new qx.ui.core.ScrollBar(false);
+ this._horScrollBar = new qx.ui.core.ScrollBar(true);
+
+ var scrollBarWidth = this._verScrollBar.getPreferredBoxWidth();
+
+ this._verScrollBar.setWidth("auto");
+ this._horScrollBar.setHeight("auto");
+ this._horScrollBar.setPaddingRight(scrollBarWidth);
+ //this._verScrollBar.setMergeEvents(true);
+
+ this._horScrollBar.addEventListener("changeValue", this._onScrollX, this);
+ this._verScrollBar.addEventListener("changeValue", this._onScrollY, this);
+
+ // init header
+ this._header = new qx.ui.table.TablePaneHeader(this);
+ this._header.set({ width:"auto", height:"auto" });
+
+ this._headerClipper = new qx.ui.layout.CanvasLayout;
+ with (this._headerClipper) {
+ setDimension("1*", "auto");
+ setOverflow("hidden");
+ add(this._header);
+ }
+
+ this._spacer = new qx.ui.basic.Terminator;
+ this._spacer.setWidth(scrollBarWidth);
+
+ this._top = new qx.ui.layout.HorizontalBoxLayout;
+ with (this._top) {
+ setHeight("auto");
+ add(this._headerClipper, this._spacer);
+ }
+
+ // init pane
+ this._tablePane = new qx.ui.table.TablePane(this);
+ this._tablePane.set({ width:"auto", height:"auto" });
+
+ this._focusIndicator = new qx.ui.layout.HorizontalBoxLayout;
+ this._focusIndicator.setAppearance("table-focus-indicator");
+ this._focusIndicator.hide();
+
+ // Workaround: If the _focusIndicator has no content if always gets a too
+ // high hight in IE.
+ var dummyContent = new qx.ui.basic.Terminator;
+ dummyContent.setWidth(0);
+ this._focusIndicator.add(dummyContent);
+
+ this._paneClipper = new qx.ui.layout.CanvasLayout;
+ with (this._paneClipper) {
+ setWidth("1*");
+ setOverflow("hidden");
+ add(this._tablePane, this._focusIndicator);
+ addEventListener("mousewheel", this._onmousewheel, this);
+ }
+
+ // add all child widgets
+ var scrollerBody = new qx.ui.layout.HorizontalBoxLayout;
+ scrollerBody.setHeight("1*");
+ scrollerBody.add(this._paneClipper, this._verScrollBar);
+
+ this.add(this._top, scrollerBody, this._horScrollBar);
+
+ // init event handlers
+ this.addEventListener("mousemove", this._onmousemove, this);
+ this.addEventListener("mousedown", this._onmousedown, this);
+ this.addEventListener("mouseup", this._onmouseup, this);
+ this.addEventListener("click", this._onclick, this);
+ this.addEventListener("dblclick", this._ondblclick, this);
+ this.addEventListener("mouseout", this._onmouseout, this);
+});
+
+/** Whether to show the horizontal scroll bar */
+qx.OO.addProperty({ name:"horizontalScrollBarVisible", type:"boolean", defaultValue:true });
+
+/** Whether to show the vertical scroll bar */
+qx.OO.addProperty({ name:"verticalScrollBarVisible", type:"boolean", defaultValue:true });
+
+/** The table pane model. */
+qx.OO.addProperty({ name:"tablePaneModel", type:"object", instance:"qx.ui.table.TablePaneModel" });
+
+/** The current position of the the horizontal scroll bar. */
+qx.OO.addProperty({ name:"scrollX", type:"number", allowNull:false, defaultValue:0 });
+
+/** The current position of the the vertical scroll bar. */
+qx.OO.addProperty({ name:"scrollY", type:"number", allowNull:false, defaultValue:0 });
+
+/**
+ * Whether column resize should be live. If false, during resize only a line is
+ * shown and the real resize happens when the user releases the mouse button.
+ */
+qx.OO.addProperty({ name:"liveResize", type:"boolean", defaultValue:false });
+
+/**
+ * Whether the focus should moved when the mouse is moved over a cell. If false
+ * the focus is only moved on mouse clicks.
+ */
+qx.OO.addProperty({ name:"focusCellOnMouseMove", type:"boolean", defaultValue:false });
+
+
+// property modifier
+qx.Proto._modifyHorizontalScrollBarVisible = function(propValue, propOldValue, propData) {
+ // Workaround: We can't use setDisplay, because the scroll bar needs its
+ // correct height in order to check its value. When using
+ // setDisplay(false) the height isn't relayouted any more
+ if (propValue) {
+ this._horScrollBar.setHeight("auto");
+ } else {
+ this._horScrollBar.setHeight(0);
+ }
+ this._horScrollBar.setVisibility(propValue);
+
+ // NOTE: We have to flush the queues before updating the content so the new
+ // layout has been applied and _updateContent is able to work with
+ // correct values.
+ qx.ui.core.Widget.flushGlobalQueues();
+ this._updateContent();
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyVerticalScrollBarVisible = function(propValue, propOldValue, propData) {
+ // Workaround: See _modifyHorizontalScrollBarVisible
+ if (propValue) {
+ this._verScrollBar.setWidth("auto");
+ } else {
+ this._verScrollBar.setWidth(0);
+ }
+ this._verScrollBar.setVisibility(propValue);
+
+ var scrollBarWidth = propValue ? this._verScrollBar.getPreferredBoxWidth() : 0;
+ this._horScrollBar.setPaddingRight(scrollBarWidth);
+ this._spacer.setWidth(scrollBarWidth);
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyTablePaneModel = function(propValue, propOldValue, propData) {
+ if (propOldValue != null) {
+ propOldValue.removeEventListener("modelChanged", this._onPaneModelChanged, this);
+ }
+ propValue.addEventListener("modelChanged", this._onPaneModelChanged, this);
+
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyScrollX = function(propValue, propOldValue, propData) {
+ this._horScrollBar.setValue(propValue);
+ return true;
+}
+
+
+// property modifier
+qx.Proto._modifyScrollY = function(propValue, propOldValue, propData) {
+ this._verScrollBar.setValue(propValue);
+ return true;
+}
+
+
+/**
+ * Returns the table this scroller belongs to.
+ *
+ * @return {Table} the table.
+ */
+qx.Proto.getTable = function() {
+ return this._table;
+};
+
+
+/**
+ * Event handler. Called when the visibility of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColVisibilityChanged = function(evt) {
+ this._updateHorScrollBarMaximum();
+ this._updateFocusIndicator();
+}
+
+
+/**
+ * Event handler. Called when the width of a column has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColWidthChanged = function(evt) {
+ this._header._onColWidthChanged(evt);
+ this._tablePane._onColWidthChanged(evt);
+
+ var data = evt.getData();
+ var paneModel = this.getTablePaneModel();
+ var x = paneModel.getX(data.col);
+ if (x != -1) {
+ // The change was in this scroller
+ this._updateHorScrollBarMaximum();
+ this._updateFocusIndicator();
+ }
+}
+
+
+/**
+ * Event handler. Called when the column order has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onColOrderChanged = function(evt) {
+ this._header._onColOrderChanged(evt);
+ this._tablePane._onColOrderChanged(evt);
+
+ this._updateHorScrollBarMaximum();
+}
+
+
+/**
+ * Event handler. Called when the table model has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelDataChanged = function(evt) {
+ this._tablePane._onTableModelDataChanged(evt);
+
+ var rowCount = this.getTable().getTableModel().getRowCount();
+ if (rowCount != this._lastRowCount) {
+ this._lastRowCount = rowCount;
+
+ this._updateVerScrollBarMaximum();
+ if (this.getFocusedRow() >= rowCount) {
+ if (rowCount == 0) {
+ this.setFocusedCell(null, null);
+ } else {
+ this.setFocusedCell(this.getFocusedColumn(), rowCount - 1);
+ }
+ }
+ }
+}
+
+
+/**
+ * Event handler. Called when the selection has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onSelectionChanged = function(evt) {
+ this._tablePane._onSelectionChanged(evt);
+};
+
+
+/**
+ * Event handler. Called when the table gets or looses the focus.
+ */
+qx.Proto._onFocusChanged = function(evt) {
+ this._focusIndicator.setState("tableHasFocus", this.getTable().getFocused());
+
+ this._tablePane._onFocusChanged(evt);
+};
+
+
+/**
+ * Event handler. Called when the table model meta data has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onTableModelMetaDataChanged = function(evt) {
+ this._header._onTableModelMetaDataChanged(evt);
+ this._tablePane._onTableModelMetaDataChanged(evt);
+};
+
+
+/**
+ * Event handler. Called when the pane model has changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onPaneModelChanged = function(evt) {
+ this._header._onPaneModelChanged(evt);
+ this._tablePane._onPaneModelChanged(evt);
+};
+
+
+/**
+ * Updates the maximum of the horizontal scroll bar, so it corresponds to the
+ * total width of the columns in the table pane.
+ */
+qx.Proto._updateHorScrollBarMaximum = function() {
+ this._horScrollBar.setMaximum(this.getTablePaneModel().getTotalWidth());
+}
+
+
+/**
+ * Updates the maximum of the vertical scroll bar, so it corresponds to the
+ * number of rows in the table.
+ */
+qx.Proto._updateVerScrollBarMaximum = function() {
+ var rowCount = this.getTable().getTableModel().getRowCount();
+ var rowHeight = this.getTable().getRowHeight();
+
+ if (this.getTable().getKeepFirstVisibleRowComplete()) {
+ this._verScrollBar.setMaximum((rowCount + 1) * rowHeight);
+ } else {
+ this._verScrollBar.setMaximum(rowCount * rowHeight);
+ }
+}
+
+
+/**
+ * Event handler. Called when the table property "keepFirstVisibleRowComplete"
+ * changed.
+ */
+qx.Proto._onKeepFirstVisibleRowCompleteChanged = function() {
+ this._updateVerScrollBarMaximum();
+ this._updateContent();
+};
+
+
+// overridden
+qx.Proto._changeInnerHeight = function(newValue, oldValue) {
+ // The height has changed -> Update content
+ this._postponedUpdateContent();
+
+ return qx.ui.layout.VerticalBoxLayout.prototype._changeInnerHeight.call(this, newValue, oldValue);
+}
+
+
+// overridden
+qx.Proto._afterAppear = function() {
+ qx.ui.layout.VerticalBoxLayout.prototype._afterAppear.call(this);
+
+ var self = this;
+ this.getElement().onselectstart = qx.util.Return.returnFalse;
+
+ this._updateContent();
+ this._header._updateContent();
+ this._updateHorScrollBarMaximum();
+ this._updateVerScrollBarMaximum();
+}
+
+
+/**
+ * Event handler. Called when the horizontal scroll bar moved.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onScrollX = function(evt) {
+ // Workaround: See _updateContent
+ this._header.setLeft(-evt.getData());
+
+ this._paneClipper.setScrollLeft(evt.getData());
+ this.setScrollX(evt.getData());
+}
+
+
+/**
+ * Event handler. Called when the vertical scroll bar moved.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onScrollY = function(evt) {
+ this._postponedUpdateContent();
+ this.setScrollY(evt.getData());
+}
+
+
+/**
+ * Event handler. Called when the user moved the mouse wheel.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onmousewheel = function(evt) {
+ this._verScrollBar.setValue(this._verScrollBar.getValue()
+ - evt.getWheelDelta() * this.getTable().getRowHeight());
+
+ // Update the focus
+ if (this._lastMousePageX && this.getFocusCellOnMouseMove()) {
+ this._focusCellAtPagePos(this._lastMousePageX, this._lastMousePageY);
+ }
+}
+
+
+/**
+ * Event handler. Called when the user moved the mouse.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onmousemove = function(evt) {
+ var tableModel = this.getTable().getTableModel();
+ var columnModel = this.getTable().getTableColumnModel();
+
+ var useResizeCursor = false;
+ var mouseOverColumn = null;
+
+ var pageX = evt.getPageX();
+ var pageY = evt.getPageY();
+
+ // Workaround: In onmousewheel the event has wrong coordinates for pageX
+ // and pageY. So we remember the last move event.
+ this._lastMousePageX = pageX;
+ this._lastMousePageY = pageY;
+
+ if (this._resizeColumn != null) {
+ // We are currently resizing -> Update the position
+ var minColumnWidth = qx.ui.table.TablePaneScroller.MIN_COLUMN_WIDTH;
+ var newWidth = Math.max(minColumnWidth, this._lastResizeWidth + pageX - this._lastResizeMousePageX);
+
+ if (this.getLiveResize()) {
+ columnModel.setColumnWidth(this._resizeColumn, newWidth);
+ } else {
+ this._header.setColumnWidth(this._resizeColumn, newWidth);
+
+ var paneModel = this.getTablePaneModel();
+ this._showResizeLine(paneModel.getColumnLeft(this._resizeColumn) + newWidth);
+ }
+
+ useResizeCursor = true;
+ this._lastResizeMousePageX += newWidth - this._lastResizeWidth;
+ this._lastResizeWidth = newWidth;
+ } else if (this._moveColumn != null) {
+ // We are moving a column
+
+ // Check whether we moved outside the click tolerance so we can start
+ // showing the column move feedback
+ // (showing the column move feedback prevents the onclick event)
+ var clickTolerance = qx.ui.table.TablePaneScroller.CLICK_TOLERANCE;
+ if (this._header.isShowingColumnMoveFeedback()
+ || pageX > this._lastMoveMousePageX + clickTolerance
+ || pageX < this._lastMoveMousePageX - clickTolerance)
+ {
+ this._lastMoveColPos += pageX - this._lastMoveMousePageX;
+
+ this._header.showColumnMoveFeedback(this._moveColumn, this._lastMoveColPos);
+
+ // Get the responsible scroller
+ var targetScroller = this._table.getTablePaneScrollerAtPageX(pageX);
+ if (this._lastMoveTargetScroller && this._lastMoveTargetScroller != targetScroller) {
+ this._lastMoveTargetScroller.hideColumnMoveFeedback();
+ }
+ if (targetScroller != null) {
+ this._lastMoveTargetX = targetScroller.showColumnMoveFeedback(pageX);
+ } else {
+ this._lastMoveTargetX = null;
+ }
+
+ this._lastMoveTargetScroller = targetScroller;
+ this._lastMoveMousePageX = pageX;
+ }
+ } else {
+ // This is a normal mouse move
+ var row = this._getRowForPagePos(pageX, pageY);
+ if (row == -1) {
+ // The mouse is over the header
+ var resizeCol = this._getResizeColumnForPageX(pageX);
+ if (resizeCol != -1) {
+ // The mouse is over a resize region -> Show the right cursor
+ useResizeCursor = true;
+ } else {
+ var col = this._getColumnForPageX(pageX);
+ if (col != null && tableModel.isColumnSortable(col)) {
+ mouseOverColumn = col;
+ }
+ }
+ } else if (row != null) {
+ // The mouse is over the data -> update the focus
+ if (this.getFocusCellOnMouseMove()) {
+ this._focusCellAtPagePos(pageX, pageY);
+ }
+ }
+ }
+
+ // Workaround: Setting the cursor to the right widget doesn't work
+ //this._header.setCursor(useResizeCursor ? "e-resize" : null);
+ this.getTopLevelWidget().setGlobalCursor(useResizeCursor ? qx.ui.table.TablePaneScroller.CURSOR_RESIZE_HORIZONTAL : null);
+
+ this._header.setMouseOverColumn(mouseOverColumn);
+}
+
+
+/**
+ * Event handler. Called when the user pressed a mouse button.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onmousedown = function(evt) {
+ var tableModel = this.getTable().getTableModel();
+ var columnModel = this.getTable().getTableColumnModel();
+
+ var pageX = evt.getPageX();
+ var pageY = evt.getPageY();
+ var row = this._getRowForPagePos(pageX, pageY);
+ if (row == -1) {
+ // mouse is in header
+ var resizeCol = this._getResizeColumnForPageX(pageX);
+ if (resizeCol != -1) {
+ // The mouse is over a resize region -> Start resizing
+ this._resizeColumn = resizeCol;
+ this._lastResizeMousePageX = pageX;
+ this._lastResizeWidth = columnModel.getColumnWidth(this._resizeColumn);
+ this.setCapture(true);
+ } else {
+ // The mouse is not in a resize region
+ var col = this._getColumnForPageX(pageX);
+ if (col != null) {
+ // Prepare column moving
+ this._moveColumn = col;
+ this._lastMoveMousePageX = pageX;
+ this._lastMoveColPos = this.getTablePaneModel().getColumnLeft(col);
+ this.setCapture(true);
+ }
+ }
+ } else if (row != null) {
+ // The mouse is over the data -> update the focus
+ if (! this.getFocusCellOnMouseMove()) {
+ this._focusCellAtPagePos(pageX, pageY);
+ }
+
+ this.getTable()._getSelectionManager().handleMouseDown(row, evt);
+ }
+}
+
+
+/**
+ * Event handler. Called when the user released a mouse button.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onmouseup = function(evt) {
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneModel = this.getTablePaneModel();
+
+ if (this._resizeColumn != null) {
+ // We are currently resizing -> Finish resizing
+ if (! this.getLiveResize()) {
+ this._hideResizeLine();
+ columnModel.setColumnWidth(this._resizeColumn, this._lastResizeWidth);
+ }
+
+ this._resizeColumn = null;
+ this.setCapture(false);
+
+ this.getTopLevelWidget().setGlobalCursor(null);
+ } else if (this._moveColumn != null) {
+ // We are moving a column -> Drop the column
+ this._header.hideColumnMoveFeedback();
+ if (this._lastMoveTargetScroller) {
+ this._lastMoveTargetScroller.hideColumnMoveFeedback();
+ }
+
+ if (this._lastMoveTargetX != null) {
+ var fromVisXPos = paneModel.getFirstColumnX() + paneModel.getX(this._moveColumn);
+ var toVisXPos = this._lastMoveTargetX;
+ if (toVisXPos != fromVisXPos && toVisXPos != fromVisXPos + 1) {
+ // The column was really moved to another position
+ // (and not moved before or after itself, which is a noop)
+
+ // Translate visible positions to overall positions
+ var fromCol = columnModel.getVisibleColumnAtX(fromVisXPos);
+ var toCol = columnModel.getVisibleColumnAtX(toVisXPos);
+ var fromOverXPos = columnModel.getOverallX(fromCol);
+ var toOverXPos = (toCol != null) ? columnModel.getOverallX(toCol) : columnModel.getOverallColumnCount();
+
+ if (toOverXPos > fromOverXPos) {
+ // Don't count the column itself
+ toOverXPos--;
+ }
+
+ // Move the column
+ columnModel.moveColumn(fromOverXPos, toOverXPos);
+ }
+ }
+
+ this._moveColumn = null;
+ this._lastMoveTargetX = null;
+ this.setCapture(false);
+ } else {
+ // This is a normal mouse up
+ var row = this._getRowForPagePos(evt.getPageX(), evt.getPageY());
+ if (row != -1 && row != null) {
+ this.getTable()._getSelectionManager().handleMouseUp(row, evt);
+ }
+ }
+}
+
+
+/**
+ * Event handler. Called when the user clicked a mouse button.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onclick = function(evt) {
+ var tableModel = this.getTable().getTableModel();
+
+ var pageX = evt.getPageX();
+ var pageY = evt.getPageY();
+ var row = this._getRowForPagePos(pageX, pageY);
+ if (row == -1) {
+ // mouse is in header
+ var resizeCol = this._getResizeColumnForPageX(pageX);
+ if (resizeCol == -1) {
+ // mouse is not in a resize region
+ var col = this._getColumnForPageX(pageX);
+ if (col != null && tableModel.isColumnSortable(col)) {
+ // Sort that column
+ var sortCol = tableModel.getSortColumnIndex();
+ var ascending = (col != sortCol) ? true : !tableModel.isSortAscending();
+
+ tableModel.sortByColumn(col, ascending);
+ this.getTable().getSelectionModel().clearSelection();
+ }
+ }
+ } else if (row != null) {
+ this.getTable()._getSelectionManager().handleClick(row, evt);
+ }
+}
+
+
+/**
+ * Event handler. Called when the user double clicked a mouse button.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._ondblclick = function(evt) {
+ if (! this.isEditing()) {
+ this._focusCellAtPagePos(evt.getPageX(), evt.getPageY());
+ this.startEditing();
+ }
+}
+
+
+/**
+ * Event handler. Called when the mouse moved out.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onmouseout = function(evt) {
+ /*
+ // Workaround: See _onmousemove
+ this._lastMousePageX = null;
+ this._lastMousePageY = null;
+ */
+
+ // Reset the resize cursor when the mouse leaves the header
+ // If currently a column is resized then do nothing
+ // (the cursor will be reset on mouseup)
+ if (this._resizeColumn == null) {
+ this.getTopLevelWidget().setGlobalCursor(null);
+ }
+
+ this._header.setMouseOverColumn(null);
+}
+
+
+/**
+ * Shows the resize line.
+ *
+ * @param x {int} the position where to show the line (in pixels, relative to
+ * the left side of the pane).
+ */
+qx.Proto._showResizeLine = function(x) {
+ var resizeLine = this._resizeLine;
+ if (resizeLine == null) {
+ resizeLine = new qx.ui.basic.Terminator;
+ resizeLine.setBackgroundColor("#D6D5D9");
+ resizeLine.setWidth(3);
+ this._paneClipper.add(resizeLine);
+ qx.ui.core.Widget.flushGlobalQueues();
+
+ this._resizeLine = resizeLine;
+ }
+
+ resizeLine._applyRuntimeLeft(x - 2); // -1 for the width
+ resizeLine._applyRuntimeHeight(this._paneClipper.getBoxHeight() + this._paneClipper.getScrollTop());
+
+ this._resizeLine.removeStyleProperty("visibility");
+}
+
+
+/**
+ * Hides the resize line.
+ */
+qx.Proto._hideResizeLine = function() {
+ this._resizeLine.setStyleProperty("visibility", "hidden");
+}
+
+
+/**
+ * Shows the feedback shown while a column is moved by the user.
+ *
+ * @param pageX {int} the x position of the mouse in the page (in pixels).
+ * @return {int} the visible x position of the column in the whole table.
+ */
+qx.Proto.showColumnMoveFeedback = function(pageX) {
+ var paneModel = this.getTablePaneModel();
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneLeftX = qx.dom.Location.getClientBoxLeft(this._tablePane.getElement());
+ var colCount = paneModel.getColumnCount();
+
+ var targetXPos = 0;
+ var targetX = 0;
+ var currX = paneLeftX;
+ for (var xPos = 0; xPos < colCount; xPos++) {
+ var col = paneModel.getColumnAtX(xPos);
+ var colWidth = columnModel.getColumnWidth(col);
+
+ if (pageX < currX + colWidth / 2) {
+ break;
+ }
+
+ currX += colWidth;
+ targetXPos = xPos + 1;
+ targetX = currX - paneLeftX;
+ }
+
+ // Ensure targetX is visible
+ var clipperLeftX = qx.dom.Location.getClientBoxLeft(this._paneClipper.getElement());
+ var clipperWidth = this._paneClipper.getBoxWidth();
+ var scrollX = clipperLeftX - paneLeftX;
+ // NOTE: +2/-1 because of feedback width
+ targetX = qx.lang.Number.limit(targetX, scrollX + 2, scrollX + clipperWidth - 1);
+
+ this._showResizeLine(targetX);
+
+ // Return the overall target x position
+ return paneModel.getFirstColumnX() + targetXPos;
+}
+
+
+/**
+ * Hides the feedback shown while a column is moved by the user.
+ */
+qx.Proto.hideColumnMoveFeedback = function() {
+ this._hideResizeLine();
+}
+
+
+/**
+ * Sets the focus to the cell that's located at the page position
+ * <code>pageX</code>/<code>pageY</code>. If there is no cell at that position,
+ * nothing happens.
+ *
+ * @param pageX {int} the x position in the page (in pixels).
+ * @param pageY {int} the y position in the page (in pixels).
+ */
+qx.Proto._focusCellAtPagePos = function(pageX, pageY) {
+ var row = this._getRowForPagePos(pageX, pageY);
+ if (row != -1 && row != null) {
+ // The mouse is over the data -> update the focus
+ var col = this._getColumnForPageX(pageX);
+ if (col != null) {
+ this._table.setFocusedCell(col, row);
+ }
+ }
+}
+
+
+/**
+ * Sets the currently focused cell.
+ *
+ * @param col {int} the model index of the focused cell's column.
+ * @param row {int} the model index of the focused cell's row.
+ */
+qx.Proto.setFocusedCell = function(col, row) {
+ if (!this.isEditing()) {
+ this._tablePane.setFocusedCell(col, row, this._updateContentPlanned);
+
+ this._focusedCol = col;
+ this._focusedRow = row;
+
+ // Move the focus indicator
+ if (! this._updateContentPlanned) {
+ this._updateFocusIndicator();
+ }
+ }
+}
+
+
+/**
+ * Returns the column of currently focused cell.
+ *
+ * @return {int} the model index of the focused cell's column.
+ */
+qx.Proto.getFocusedColumn = function() {
+ return this._focusedCol;
+};
+
+
+/**
+ * Returns the row of currently focused cell.
+ *
+ * @return {int} the model index of the focused cell's column.
+ */
+qx.Proto.getFocusedRow = function() {
+ return this._focusedRow;
+};
+
+
+/**
+ * Scrolls a cell visible.
+ *
+ * @param col {int} the model index of the column the cell belongs to.
+ * @param row {int} the model index of the row the cell belongs to.
+ */
+qx.Proto.scrollCellVisible = function(col, row) {
+ var paneModel = this.getTablePaneModel();
+ var xPos = paneModel.getX(col);
+
+ if (xPos != -1) {
+ var columnModel = this.getTable().getTableColumnModel();
+
+ var colLeft = paneModel.getColumnLeft(col);
+ var colWidth = columnModel.getColumnWidth(col);
+ var rowHeight = this.getTable().getRowHeight();
+ var rowTop = row * rowHeight;
+
+ var scrollX = this.getScrollX();
+ var scrollY = this.getScrollY();
+ var viewWidth = this._paneClipper.getBoxWidth();
+ var viewHeight = this._paneClipper.getBoxHeight();
+
+ // NOTE: We don't use qx.lang.Number.limit, because min should win if max < min
+ var minScrollX = Math.min(colLeft, colLeft + colWidth - viewWidth);
+ var maxScrollX = colLeft;
+ this.setScrollX(Math.max(minScrollX, Math.min(maxScrollX, scrollX)));
+
+ var minScrollY = rowTop + rowHeight - viewHeight;
+ if (this.getTable().getKeepFirstVisibleRowComplete()) {
+ minScrollY += rowHeight - 1;
+ }
+ var maxScrollY = rowTop;
+ this.setScrollY(Math.max(minScrollY, Math.min(maxScrollY, scrollY)));
+ }
+}
+
+
+/**
+ * Returns whether currently a cell is editing.
+ *
+ * @return whether currently a cell is editing.
+ */
+qx.Proto.isEditing = function() {
+ return this._cellEditor != null;
+}
+
+
+/**
+ * Starts editing the currently focused cell. Does nothing if already editing
+ * or if the column is not editable.
+ *
+ * @return {boolean} whether editing was started
+ */
+qx.Proto.startEditing = function() {
+ var tableModel = this.getTable().getTableModel();
+ var col = this._focusedCol;
+
+ if (!this.isEditing() && (col != null) && tableModel.isColumnEditable(col)) {
+ var row = this._focusedRow;
+ var xPos = this.getTablePaneModel().getX(col);
+ var value = tableModel.getValue(col, row);
+
+ this._cellEditorFactory = this.getTable().getTableColumnModel().getCellEditorFactory(col);
+ var cellInfo = { col:col, row:row, xPos:xPos, value:value }
+ this._cellEditor = this._cellEditorFactory.createCellEditor(cellInfo);
+ this._cellEditor.set({ width:"100%", height:"100%" });
+
+ this._focusIndicator.add(this._cellEditor);
+ this._focusIndicator.addState("editing");
+
+ this._cellEditor.addEventListener("changeFocused", this._onCellEditorFocusChanged, this);
+
+ // Workaround: Calling focus() directly has no effect
+ var editor = this._cellEditor;
+ window.setTimeout(function() {
+ editor.focus();
+ }, 0);
+
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Stops editing and writes the editor's value to the model.
+ */
+qx.Proto.stopEditing = function() {
+ this.flushEditor();
+ this.cancelEditing();
+}
+
+
+/**
+ * Writes the editor's value to the model.
+ */
+qx.Proto.flushEditor = function() {
+ if (this.isEditing()) {
+ var value = this._cellEditorFactory.getCellEditorValue(this._cellEditor);
+ this.getTable().getTableModel().setValue(this._focusedCol, this._focusedRow, value);
+
+ this._table.focus();
+ }
+}
+
+
+/**
+ * Stops editing without writing the editor's value to the model.
+ */
+qx.Proto.cancelEditing = function() {
+ if (this.isEditing()) {
+ this._focusIndicator.remove(this._cellEditor);
+ this._focusIndicator.removeState("editing");
+ this._cellEditor.dispose();
+
+ this._cellEditor.removeEventListener("changeFocused", this._onCellEditorFocusChanged, this);
+ this._cellEditor = null;
+ this._cellEditorFactory = null;
+ }
+}
+
+
+/**
+ * Event handler. Called when the focused state of the cell editor changed.
+ *
+ * @param evt {Map} the event.
+ */
+qx.Proto._onCellEditorFocusChanged = function(evt) {
+ if (!this._cellEditor.getFocused()) {
+ this.stopEditing();
+ }
+}
+
+
+/**
+ * Returns the model index of the column the mouse is over or null if the mouse
+ * is not over a column.
+ *
+ * @param pageX {int} the x position of the mouse in the page (in pixels).
+ * @return {int} the model index of the column the mouse is over.
+ */
+qx.Proto._getColumnForPageX = function(pageX) {
+ var headerLeftX = qx.dom.Location.getClientBoxLeft(this._header.getElement());
+
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneModel = this.getTablePaneModel();
+ var colCount = paneModel.getColumnCount();
+ var currX = headerLeftX;
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+ var colWidth = columnModel.getColumnWidth(col);
+ currX += colWidth;
+
+ if (pageX < currX) {
+ return col;
+ }
+ }
+
+ return null;
+}
+
+
+/**
+ * Returns the model index of the column that should be resized when dragging
+ * starts here. Returns -1 if the mouse is in no resize region of any column.
+ *
+ * @param pageX {int} the x position of the mouse in the page (in pixels).
+ * @return {int} the column index.
+ */
+qx.Proto._getResizeColumnForPageX = function(pageX) {
+ var headerLeftX = qx.dom.Location.getClientBoxLeft(this._header.getElement());
+
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneModel = this.getTablePaneModel();
+ var colCount = paneModel.getColumnCount();
+ var currX = headerLeftX;
+ var regionRadius = qx.ui.table.TablePaneScroller.RESIZE_REGION_RADIUS;
+ for (var x = 0; x < colCount; x++) {
+ var col = paneModel.getColumnAtX(x);
+ var colWidth = columnModel.getColumnWidth(col);
+ currX += colWidth;
+
+ if (pageX >= (currX - regionRadius) && pageX <= (currX + regionRadius)) {
+ return col;
+ }
+ }
+
+ return -1;
+}
+
+
+/**
+ * Returns the model index of the row the mouse is currently over. Returns -1 if
+ * the mouse is over the header. Returns null if the mouse is not over any
+ * column.
+ *
+ * @param pageX {int} the mouse x position in the page.
+ * @param pageY {int} the mouse y position in the page.
+ * @return {int} the model index of the row the mouse is currently over.
+ */
+qx.Proto._getRowForPagePos = function(pageX, pageY) {
+ var paneClipperElem = this._paneClipper.getElement();
+ var paneClipperLeftX = qx.dom.Location.getClientBoxLeft(paneClipperElem);
+ var paneClipperRightX = qx.dom.Location.getClientBoxRight(paneClipperElem);
+ if (pageX < paneClipperLeftX || pageX > paneClipperRightX) {
+ // There was no cell or header cell hit
+ return null;
+ }
+
+ var paneClipperTopY = qx.dom.Location.getClientBoxTop(paneClipperElem);
+ var paneClipperBottomY = qx.dom.Location.getClientBoxBottom(paneClipperElem);
+ if (pageY >= paneClipperTopY && pageY <= paneClipperBottomY) {
+ // This event is in the pane -> Get the row
+ var rowHeight = this.getTable().getRowHeight();
+
+ var scrollY = this._verScrollBar.getValue();
+ if (this.getTable().getKeepFirstVisibleRowComplete()) {
+ scrollY = Math.floor(scrollY / rowHeight) * rowHeight;
+ }
+
+ var tableY = scrollY + pageY - paneClipperTopY;
+ var row = Math.floor(tableY / rowHeight);
+
+ var rowCount = this.getTable().getTableModel().getRowCount();
+ return (row < rowCount) ? row : null;
+ }
+
+ var headerElem = this._headerClipper.getElement();
+ if (pageY >= qx.dom.Location.getClientBoxTop(headerElem)
+ && pageY <= qx.dom.Location.getClientBoxBottom(headerElem)
+ && pageX <= qx.dom.Location.getClientBoxRight(headerElem))
+ {
+ // This event is in the pane -> Return -1 for the header
+ return -1;
+ }
+
+ return null;
+}
+
+
+/**
+ * Sets the widget that should be shown in the top right corner.
+ * <p>
+ * The widget will not be disposed, when this table scroller is disposed. So the
+ * caller has to dispose it.
+ *
+ * @param widget {qx.ui.core.Widget} The widget to set. May be null.
+ */
+qx.Proto.setTopRightWidget = function(widget) {
+ var oldWidget = this._topRightWidget;
+ if (oldWidget != null) {
+ this._top.remove(oldWidget);
+ }
+
+ if (widget != null) {
+ this._top.remove(this._spacer);
+ this._top.add(widget);
+ } else if (oldWidget != null) {
+ this._top.add(this._spacer);
+ }
+
+ this._topRightWidget = widget;
+}
+
+
+/**
+ * Returns the header.
+ *
+ * @return {TablePaneHeader} the header.
+ */
+qx.Proto.getHeader = function() {
+ return this._header;
+}
+
+
+/**
+ * Returns the table pane.
+ *
+ * @return {TablePane} the table pane.
+ */
+qx.Proto.getTablePane = function() {
+ return this._tablePane;
+}
+
+
+/**
+ * Returns which scrollbars are needed.
+ *
+ * @param forceHorizontal {boolean ? false} Whether to show the horizontal
+ * scrollbar always.
+ * @param preventVertical {boolean ? false} Whether tp show the vertical scrollbar
+ * never.
+ * @return {int} which scrollbars are needed. This may be any combination of
+ * {@link #HORIZONTAL_SCROLLBAR} or {@link #VERTICAL_SCROLLBAR}
+ * (combined by OR).
+ */
+qx.Proto.getNeededScrollBars = function(forceHorizontal, preventVertical) {
+ var barWidth = this._verScrollBar.getPreferredBoxWidth();
+
+ // Get the width and height of the view (without scroll bars)
+ var viewWidth = this._paneClipper.getInnerWidth();
+ if (this.getVerticalScrollBarVisible()) {
+ viewWidth += barWidth;
+ }
+ var viewHeight = this._paneClipper.getInnerHeight();
+ if (this.getHorizontalScrollBarVisible()) {
+ viewHeight += barWidth;
+ }
+
+ // Get the (virtual) width and height of the pane
+ var paneWidth = this.getTablePaneModel().getTotalWidth();
+ var paneHeight = this.getTable().getRowHeight() * this.getTable().getTableModel().getRowCount();
+
+ // Check which scrollbars are needed
+ var horNeeded = false;
+ var verNeeded = false;
+ if (paneWidth > viewWidth) {
+ horNeeded = true;
+ if (paneHeight > viewHeight - barWidth) {
+ verNeeded = true;
+ }
+ } else if (paneHeight > viewHeight) {
+ verNeeded = true;
+ if (!preventVertical && (paneWidth > viewWidth - barWidth)) {
+ horNeeded = true;
+ }
+ }
+
+ // Create the mask
+ var horBar = qx.ui.table.TablePaneScroller.HORIZONTAL_SCROLLBAR;
+ var verBar = qx.ui.table.TablePaneScroller.VERTICAL_SCROLLBAR;
+ return ((forceHorizontal || horNeeded) ? horBar : 0)
+ | ((preventVertical || !verNeeded) ? 0 : verBar);
+}
+
+
+/**
+ * Does a postponed update of the content.
+ *
+ * @see #_updateContent
+ */
+qx.Proto._postponedUpdateContent = function() {
+ if (! this._updateContentPlanned) {
+ var self = this;
+ window.setTimeout(function() {
+ self._updateContent();
+ self._updateContentPlanned = false;
+ qx.ui.core.Widget.flushGlobalQueues();
+ }, 0);
+ this._updateContentPlanned = true;
+ }
+}
+
+
+/**
+ * Updates the content. Sets the right section the table pane should show and
+ * does the scrolling.
+ */
+qx.Proto._updateContent = function() {
+ var paneHeight = this._paneClipper.getInnerHeight();
+ var scrollX = this._horScrollBar.getValue();
+ var scrollY = this._verScrollBar.getValue();
+ var rowHeight = this.getTable().getRowHeight();
+
+ var firstRow = Math.floor(scrollY / rowHeight);
+ var oldFirstRow = this._tablePane.getFirstVisibleRow();
+ this._tablePane.setFirstVisibleRow(firstRow);
+
+ var rowCount = Math.ceil(paneHeight / rowHeight);
+ var paneOffset = 0;
+ if (! this.getTable().getKeepFirstVisibleRowComplete()) {
+ // NOTE: We don't consider paneOffset, because this may cause alternating
+ // adding and deleting of one row when scolling. Instead we add one row
+ // in every case.
+ rowCount++;
+ paneOffset = scrollY % rowHeight;
+ }
+ this._tablePane.setVisibleRowCount(rowCount);
+
+ if (firstRow != oldFirstRow) {
+ this._updateFocusIndicator();
+ }
+
+ // Workaround: We can't use scrollLeft for the header because IE
+ // automatically scrolls the header back, when a column is
+ // resized.
+ this._header.setLeft(-scrollX);
+ this._paneClipper.setScrollLeft(scrollX);
+ this._paneClipper.setScrollTop(paneOffset);
+
+ //this.debug("paneHeight:"+paneHeight+",rowHeight:"+rowHeight+",firstRow:"+firstRow+",rowCount:"+rowCount+",paneOffset:"+paneOffset);
+}
+
+
+/**
+ * Updates the location and the visibility of the focus indicator.
+ */
+qx.Proto._updateFocusIndicator = function() {
+ if (this._focusedCol == null) {
+ this._focusIndicator.hide();
+ } else {
+ var xPos = this.getTablePaneModel().getX(this._focusedCol);
+ if (xPos == -1) {
+ this._focusIndicator.hide();
+ } else {
+ var columnModel = this.getTable().getTableColumnModel();
+ var paneModel = this.getTablePaneModel();
+
+ var firstRow = this._tablePane.getFirstVisibleRow();
+ var rowHeight = this.getTable().getRowHeight();
+
+ this._focusIndicator.setHeight(rowHeight + 3);
+ this._focusIndicator.setWidth(columnModel.getColumnWidth(this._focusedCol) + 3);
+ this._focusIndicator.setTop((this._focusedRow - firstRow) * rowHeight - 2);
+ this._focusIndicator.setLeft(paneModel.getColumnLeft(this._focusedCol) - 2);
+
+ this._focusIndicator.show();
+ }
+ }
+}
+
+
+// overridden
+qx.Proto.dispose = function() {
+ if (this.getDisposed()) {
+ return true;
+ }
+
+ if (this.getElement() != null) {
+ this.getElement().onselectstart = null;
+ }
+
+ this._verScrollBar.dispose();
+ this._horScrollBar.dispose();
+ this._header.dispose();
+ this._headerClipper.dispose();
+ this._spacer.dispose();
+ this._top.dispose();
+ this._tablePane.dispose();
+ this._paneClipper.dispose();
+
+ if (this._resizeLine != null) {
+ this._resizeLine.dispose();
+ }
+
+ this.removeEventListener("mousemove", this._onmousemove, this);
+ this.removeEventListener("mousedown", this._onmousedown, this);
+ this.removeEventListener("mouseup", this._onmouseup, this);
+ this.removeEventListener("click", this._onclick, this);
+ this.removeEventListener("dblclick", this._ondblclick, this);
+ this.removeEventListener("mouseout", this._onmouseout, this);
+
+ var tablePaneModel = this.getTablePaneModel();
+ if (tablePaneModel != null) {
+ tablePaneModel.removeEventListener("modelChanged", this._onPaneModelChanged, this);
+ }
+
+ return qx.ui.layout.VerticalBoxLayout.prototype.dispose.call(this);
+}
+
+
+/** {int} The minimum width a colum could get in pixels. */
+qx.Class.MIN_COLUMN_WIDTH = 10;
+
+/** {int} The radius of the resize region in pixels. */
+qx.Class.RESIZE_REGION_RADIUS = 5;
+
+/**
+ * (int) The number of pixels the mouse may move between mouse down and mouse up
+ * in order to count as a click.
+ */
+qx.Class.CLICK_TOLERANCE = 5;
+
+/**
+ * (int) The mask for the horizontal scroll bar.
+ * May be combined with {@link #VERTICAL_SCROLLBAR}.
+ *
+ * @see #getNeededScrollBars
+ */
+qx.Class.HORIZONTAL_SCROLLBAR = 1;
+
+/**
+ * (int) The mask for the vertical scroll bar.
+ * May be combined with {@link #HORIZONTAL_SCROLLBAR}.
+ *
+ * @see #getNeededScrollBars
+ */
+qx.Class.VERTICAL_SCROLLBAR = 2;
+
+/**
+ * (string) The correct value for the CSS style attribute "cursor" for the
+ * horizontal resize cursor.
+ */
+qx.Class.CURSOR_RESIZE_HORIZONTAL = (qx.sys.Client.getInstance().isGecko() && (qx.sys.Client.getInstance().getMajor() > 1 || qx.sys.Client.getInstance().getMinor() >= 8)) ? "ew-resize" : "e-resize";
diff --git a/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TextFieldCellEditorFactory.js b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TextFieldCellEditorFactory.js
new file mode 100644
index 0000000000..6878ce7470
--- /dev/null
+++ b/webapps/qooxdoo-0.6.3-sdk/frontend/framework/source/class/qx/ui/table/TextFieldCellEditorFactory.js
@@ -0,0 +1,58 @@
+/* ************************************************************************
+
+ qooxdoo - the new era of web development
+
+ http://qooxdoo.org
+
+ Copyright:
+ 2006 by STZ-IDA, Germany, http://www.stz-ida.de
+
+ License:
+ LGPL 2.1: http://www.gnu.org/licenses/lgpl.html
+
+ Authors:
+ * Til Schneider (til132)
+
+************************************************************************ */
+
+/* ************************************************************************
+
+#module(ui_table)
+
+************************************************************************ */
+
+/**
+ * A cell editor factory creating text fields.
+ */
+qx.OO.defineClass("qx.ui.table.TextFieldCellEditorFactory", qx.ui.table.CellEditorFactory,
+function() {
+ qx.ui.table.CellEditorFactory.call(this);
+});
+
+
+// overridden
+qx.Proto.createCellEditor = function(cellInfo) {
+ var cellEditor = new qx.ui.form.TextField;
+ cellEditor.setAppearance("table-editor-textfield");
+ cellEditor.originalValue = cellInfo.value;
+ cellEditor.setValue("" + cellInfo.value);
+
+ cellEditor.addEventListener("appear", function() {
+ this.selectAll();
+ });
+
+ return cellEditor;
+}
+
+
+// overridden
+qx.Proto.getCellEditorValue = function(cellEditor) {
+ // Workaround: qx.ui.form.TextField.getValue() delivers the old value, so we use the
+ // value property of the DOM element directly
+ var value = cellEditor.getElement().value;
+
+ if (typeof cellEditor.originalValue == "number") {
+ value = parseFloat(value);
+ }
+ return value;
+}