@qooxdoo/framework
Version:
The JS Framework for Coders
769 lines (626 loc) • 23.1 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2006 STZ-IDA, Germany, http://www.stz-ida.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Til Schneider (til132)
************************************************************************ */
/**
* A model that contains all meta data about columns, such as width, renderer,
* visibility and order.
*
* @see qx.ui.table.ITableModel
*/
qx.Class.define("qx.ui.table.columnmodel.Basic",
{
extend : qx.core.Object,
construct : function()
{
this.base(arguments);
this.__overallColumnArr = [];
this.__visibleColumnArr = [];
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
events : {
/**
* 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>
*/
"widthChanged" : "qx.event.type.Data",
/**
* Fired when the visibility of a column has changed. This event is equal to
* "visibilityChanged", but is fired right before.
*/
"visibilityChangedPre" : "qx.event.type.Data",
/**
* 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>
*/
"visibilityChanged" : "qx.event.type.Data",
/**
* 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>
*/
"orderChanged" : "qx.event.type.Data",
/**
* Fired when the cell renderer 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 that was moved.</li>
* </ul>
*/
"headerCellRendererChanged" : "qx.event.type.Data"
},
/*
*****************************************************************************
STATICS
*****************************************************************************
*/
statics :
{
/** @type {Integer} the default width of a column in pixels. */
DEFAULT_WIDTH : 100,
/** @type {qx.ui.table.headerrenderer.Default} the default header cell renderer. */
DEFAULT_HEADER_RENDERER : qx.ui.table.headerrenderer.Default,
/** @type {qx.ui.table.cellrenderer.Default} the default data cell renderer. */
DEFAULT_DATA_RENDERER : qx.ui.table.cellrenderer.Default,
/** @type {qx.ui.table.celleditor.TextField} the default editor factory. */
DEFAULT_EDITOR_FACTORY : qx.ui.table.celleditor.TextField
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__internalChange : null,
__colToXPosMap : null,
__visibleColumnArr : null,
__overallColumnArr : null,
__columnDataArr : null,
__headerRenderer : null,
__dataRenderer : null,
__editorFactory : null,
/**
* Initializes the column model.
*
* @param colCount {Integer}
* The number of columns the model should have.
*
* @param table {qx.ui.table.Table}
* The table to which this column model is attached.
*/
init : function(colCount, table)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(colCount, "Invalid argument 'colCount'.");
}
this.__columnDataArr = [];
var width = qx.ui.table.columnmodel.Basic.DEFAULT_WIDTH;
var headerRenderer = this.__headerRenderer || (this.__headerRenderer = new qx.ui.table.columnmodel.Basic.DEFAULT_HEADER_RENDERER());
var dataRenderer = this.__dataRenderer || (this.__dataRenderer = new qx.ui.table.columnmodel.Basic.DEFAULT_DATA_RENDERER());
var editorFactory = this.__editorFactory || (this.__editorFactory = new qx.ui.table.columnmodel.Basic.DEFAULT_EDITOR_FACTORY());
this.__overallColumnArr = [];
this.__visibleColumnArr = [];
// Get the initially hidden column array, if one was provided. Older
// subclasses may not provide the 'table' argument, so we treat them
// traditionally with no initially hidden columns.
var initiallyHiddenColumns;
// Was a table provided to us?
if (table)
{
// Yup. Get its list of initially hidden columns, if the user provided
// such a list.
initiallyHiddenColumns = table.getInitiallyHiddenColumns();
}
// If no table was specified, or if the user didn't provide a list of
// initially hidden columns, use an empty list.
initiallyHiddenColumns = initiallyHiddenColumns || [];
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;
// If any columns are initially hidden, hide them now. Make it an
// internal change so that events are not generated.
this.__internalChange = true;
for (var hidden=0; hidden<initiallyHiddenColumns.length; hidden++)
{
this.setColumnVisible(initiallyHiddenColumns[hidden], false);
}
this.__internalChange = false;
for (col=0; col<colCount; col++)
{
var data =
{
col : col,
visible : this.isColumnVisible(col)
};
this.fireDataEvent("visibilityChangedPre", data);
this.fireDataEvent("visibilityChanged", data);
}
},
/**
* Return the array of visible columns
*
* @return {Array} List of all visible columns
*/
getVisibleColumns : function() {
return this.__visibleColumnArr != null ? this.__visibleColumnArr : [];
},
/**
* Sets the width of a column.
*
* @param col {Integer}
* The model index of the column.
*
* @param width {Integer}
* The new width the column should get in pixels.
*
* @param isPointerAction {Boolean}
* <i>true</i> if the column width is being changed as a result of a
* pointer drag in the header; false or undefined otherwise.
*
*/
setColumnWidth : function(col, width, isPointerAction)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertInteger(width, "Invalid argument 'width'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
var oldWidth = this.__columnDataArr[col].width;
if (oldWidth != width)
{
this.__columnDataArr[col].width = width;
var data =
{
col : col,
newWidth : width,
oldWidth : oldWidth,
isPointerAction : isPointerAction || false
};
this.fireDataEvent("widthChanged", data);
}
},
/**
* Returns the width of a column.
*
* @param col {Integer} the model index of the column.
* @return {Integer} the width of the column in pixels.
*/
getColumnWidth : function(col)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(col, "Invalid argument 'col'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
return this.__columnDataArr[col].width;
},
/**
* Sets the header renderer of a column.
*
* @param col {Integer} the model index of the column.
* @param renderer {qx.ui.table.IHeaderRenderer} the new header renderer the column
* should get.
*/
setHeaderCellRenderer : function(col, renderer)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertInterface(renderer, qx.ui.table.IHeaderRenderer, "Invalid argument 'renderer'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
var oldRenderer = this.__columnDataArr[col].headerRenderer;
if (oldRenderer !== this.__headerRenderer) {
oldRenderer.dispose();
}
this.__columnDataArr[col].headerRenderer = renderer;
this.fireDataEvent("headerCellRendererChanged", {col:col});
},
/**
* Returns the header renderer of a column.
*
* @param col {Integer} the model index of the column.
* @return {qx.ui.table.IHeaderRenderer} the header renderer of the column.
*/
getHeaderCellRenderer : function(col)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
return this.__columnDataArr[col].headerRenderer;
},
/**
* Sets the data renderer of a column.
*
* @param col {Integer} the model index of the column.
* @param renderer {qx.ui.table.ICellRenderer} the new data renderer
* the column should get.
* @return {qx.ui.table.ICellRenderer?null} If an old renderer was set and
* it was not the default renderer, the old renderer is returned for
* pooling or disposing.
*/
setDataCellRenderer : function(col, renderer)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertInterface(renderer, qx.ui.table.ICellRenderer, "Invalid argument 'renderer'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
var oldRenderer = this.__columnDataArr[col].dataRenderer;
this.__columnDataArr[col].dataRenderer = renderer;
if (oldRenderer !== this.__dataRenderer) {
return oldRenderer;
}
return null;
},
/**
* Returns the data renderer of a column.
*
* @param col {Integer} the model index of the column.
* @return {qx.ui.table.ICellRenderer} the data renderer of the column.
*/
getDataCellRenderer : function(col)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
return this.__columnDataArr[col].dataRenderer;
},
/**
* Sets the cell editor factory of a column.
*
* @param col {Integer} the model index of the column.
* @param factory {qx.ui.table.ICellEditorFactory} the new cell editor factory the column should get.
*/
setCellEditorFactory : function(col, factory)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertInterface(factory, qx.ui.table.ICellEditorFactory, "Invalid argument 'factory'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
var oldFactory = this.__columnDataArr[col].editorFactory;
if (oldFactory === factory) {
return;
}
if (oldFactory !== this.__editorFactory) {
oldFactory.dispose();
}
this.__columnDataArr[col].editorFactory = factory;
},
/**
* Returns the cell editor factory of a column.
*
* @param col {Integer} the model index of the column.
* @return {qx.ui.table.ICellEditorFactory} the cell editor factory of the column.
*/
getCellEditorFactory : function(col)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertNotUndefined(this.__columnDataArr[col], "Column not found in table model");
}
return this.__columnDataArr[col].editorFactory;
},
/**
* Returns the map that translates model indexes to x positions.
*
* 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 {Map} the "column to x position" map.
*/
_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 {Integer} the number of visible columns.
*/
getVisibleColumnCount : function() {
return this.__visibleColumnArr != null ? this.__visibleColumnArr.length : 0;
},
/**
* Returns the model index of a column at a certain visible x position.
*
* @param visXPos {Integer} the visible x position of the column.
* @return {Integer} the model index of the column.
*/
getVisibleColumnAtX : function(visXPos)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(visXPos, "Invalid argument 'visXPos'.");
}
return this.__visibleColumnArr[visXPos];
},
/**
* Returns the visible x position of a column.
*
* @param col {Integer} the model index of the column.
* @return {Integer} the visible x position of the column.
*/
getVisibleX : function(col)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(col, "Invalid argument 'col'.");
}
return this._getColToXPosMap()[col].visX;
},
/**
* Returns the overall number of columns (including hidden columns).
*
* @return {Integer} the overall number of columns.
*/
getOverallColumnCount : function() {
return this.__overallColumnArr.length;
},
/**
* Returns the model index of a column at a certain overall x position.
*
* @param overXPos {Integer} the overall x position of the column.
* @return {Integer} the model index of the column.
*/
getOverallColumnAtX : function(overXPos)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(overXPos, "Invalid argument 'overXPos'.");
}
return this.__overallColumnArr[overXPos];
},
/**
* Returns the overall x position of a column.
*
* @param col {Integer} the model index of the column.
* @return {Integer} the overall x position of the column.
*/
getOverallX : function(col)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(col, "Invalid argument 'col'.");
}
return this._getColToXPosMap()[col].overX;
},
/**
* Returns whether a certain column is visible.
*
* @param col {Integer} the model index of the column.
* @return {Boolean} whether the column is visible.
*/
isColumnVisible : function(col)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertInteger(col, "Invalid argument 'col'.");
}
return (this._getColToXPosMap()[col].visX != null);
},
/**
* Sets whether a certain column is visible.
*
* @param col {Integer} the model index of the column.
* @param visible {Boolean} whether the column should be visible.
*/
setColumnVisible : function(col, visible)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(col, "Invalid argument 'col'.");
this.assertBoolean(visible, "Invalid argument '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)
{
var data =
{
col : col,
visible : visible
};
this.fireDataEvent("visibilityChangedPre", data);
this.fireDataEvent("visibilityChanged", data);
}
}
},
/**
* Moves a column.
*
* @param fromOverXPos {Integer} the overall x position of the column to move.
* @param toOverXPos {Integer} the overall x position of where the column should be
* moved to.
*/
moveColumn : function(fromOverXPos, toOverXPos)
{
if (qx.core.Environment.get("qx.debug"))
{
this.assertInteger(fromOverXPos, "Invalid argument 'fromOverXPos'.");
this.assertInteger(toOverXPos, "Invalid argument '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
var data =
{
col : col,
fromOverXPos : fromOverXPos,
toOverXPos : toOverXPos
};
this.fireDataEvent("orderChanged", data);
},
/**
* Reorders all columns to new overall positions. Will fire one "orderChanged" event
* without data afterwards
*
* @param newPositions {Integer[]} Array mapping the index of a column in table model to its wanted overall
* position on screen (both zero based). If the table models holds
* col0, col1, col2 and col3 and you give [1,3,2,0], the new column order
* will be col3, col0, col2, col1
*/
setColumnsOrder : function(newPositions)
{
if (qx.core.Environment.get("qx.debug")) {
this.assertArray(newPositions, "Invalid argument 'newPositions'.");
}
if (newPositions.length == this.__overallColumnArr.length)
{
this.__internalChange = true;
// Go through each column an switch visible ones to invisible. Reason is unknown,
// this just mimicks the behaviour of moveColumn. Possibly useful because setting
// a column visible later updates a map with its screen coords.
var isVisible = new Array(newPositions.length);
for (var colIdx = 0; colIdx < this.__overallColumnArr.length; colIdx++)
{
var visible = this.isColumnVisible(colIdx);
isVisible[colIdx] = visible; //Remember, as this relies on this.__colToXPosMap which is cleared below
if (visible){
this.setColumnVisible(colIdx, false);
}
}
// Store new position values
this.__overallColumnArr = qx.lang.Array.clone(newPositions);
// Invalidate the __colToXPosMap
this.__colToXPosMap = null;
// Go through each column an switch invisible ones back to visible
for (var colIdx = 0; colIdx < this.__overallColumnArr.length; colIdx++){
if (isVisible[colIdx]) {
this.setColumnVisible(colIdx, true);
}
}
this.__internalChange = false;
// Inform the listeners. Do not add data as all known listeners in qooxdoo
// only take this event to mean "total repaint necesscary". Fabian will look
// after deprecating the data part of the orderChanged - event
this.fireDataEvent("orderChanged");
} else {
throw new Error("setColumnsOrder: Invalid number of column positions given, expected "
+ this.__overallColumnArr.length + ", got " + newPositions.length);
}
}
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function()
{
for (var i=0; i< this.__columnDataArr.length; i++)
{
this.__columnDataArr[i].headerRenderer.dispose();
this.__columnDataArr[i].dataRenderer.dispose();
this.__columnDataArr[i].editorFactory.dispose();
}
this.__overallColumnArr = this.__visibleColumnArr =
this.__columnDataArr = this.__colToXPosMap = null;
this._disposeObjects(
"__headerRenderer",
"__dataRenderer",
"__editorFactory"
);
}
});