@qooxdoo/framework
Version:
The JS Framework for Coders
573 lines (468 loc) • 16.4 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2008 Derrell Lipman
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Derrell Lipman (derrell)
************************************************************************ */
/**
* Table Row renderer for Progressive.
*/
qx.Class.define("qx.ui.progressive.renderer.table.Row",
{
extend : qx.ui.progressive.renderer.Abstract,
/**
* @param columnWidths {qx.ui.progressive.renderer.table.Widths}
* Information that indicates how to resize each of the column widths
*/
construct : function(columnWidths)
{
this.base(arguments);
// Save the column widths
this.__columnWidths = columnWidths;
// Create space to store renderers for each column
this.__renderers = { };
// We need a default cell renderer to use if none is specified
this.__defaultCellRenderer =
new qx.ui.progressive.renderer.table.cell.Default();
// We don't yet know who our Progressive will be
this.__progressive = null;
this.__colors = {};
this.__linkColors();
// This layout is not connected to a widget but to this class. This class
// must implement the method "getLayoutChildren", which must return all
// columns (LayoutItems) which should be recalculated. The call
// "layout.renderLayout" will call the method "renderLayout" on each
// column data object The advantage of the use of the normal layout
// manager is that the semantics of flex and percent are exactly the same
// as in the widget code.
this.__layout = new qx.ui.layout.HBox();
this.__layout.connectToWidget(this);
// dynamic theme switch
if (qx.core.Environment.get("qx.dyntheme")) {
qx.theme.manager.Meta.getInstance().addListener(
"changeTheme", this.__linkColors, this
);
}
},
statics :
{
/**
* Storage for each progressive.
*
* @internal
*/
__clazz : null,
/**
* Default row padding.
*
* @internal
*/
__padding : 6, // modify padding parameter below too if this changes
/**
* Default style sheet for table cells.
*
* @internal
*/
__tableCellStyleSheet :
" position: absolute;" +
" top: 0px;" +
" height: 100%;" +
" overflow:hidden;" +
(qx.core.Environment.get("css.textoverflow") ?
qx.bom.Style.getCssName(qx.core.Environment.get("css.textoverflow")) +
':ellipsis;' : "") +
" white-space:nowrap;" +
" border-right:1px solid #f2f2f2;" +
" border-bottom:1px solid #eeeeee;" +
" padding : 0px 6px 0px 6px;" +
" cursor:default;" +
" font-size: 11px;" +
" font-family: 'Segoe UI', Corbel, Calibri, Tahoma, 'Lucida Sans Unicode', sans-serif;" +
(qx.core.Environment.get("css.userselect") ?
qx.bom.Style.getCssName(qx.core.Environment.get("css.userselect")) +
':' + qx.core.Environment.get("css.userselect.none") + ';'
: '')
},
properties :
{
/** The default height of a row, if not altered by a cell renderer. */
defaultRowHeight :
{
init : 16
}
},
members :
{
__progressive : null,
__name : null,
__hash : null,
__columnWidths : null,
__renderers : null,
__defaultCellRenderer : null,
__colors : null,
__layout : null,
/**
* Helper to link the theme colors to the current class
*/
__linkColors : function() {
// link to color theme
var colorMgr = qx.theme.manager.Color.getInstance();
this.__colors.bgcol = [];
this.__colors.bgcol[0] =
colorMgr.resolve("progressive-table-row-background-even");
this.__colors.bgcol[1] =
colorMgr.resolve("progressive-table-row-background-odd");
},
// overridden
join : function(progressive, name)
{
// Are we already joined?
if (this.__progressive)
{
// Yup. Let 'em know they can't do that.
throw new Error("Renderer is already joined to a Progressive.");
}
// Save the Progressive to which we're joined
this.__progressive = progressive;
// Save the name that Progressive knows us by
this.__name = name;
// If we haven't created style sheets for this table yet...
var tr = qx.ui.progressive.renderer.table.Row;
if (!tr.__clazz)
{
tr.__clazz = { };
}
var hash = progressive.toHashCode();
if (!tr.__clazz[hash])
{
// ... then do it now.
tr.__clazz[hash] =
{
rowstylesheet : null,
cellstylesheet : [ ]
};
var stylesheet =
".qx-progressive-" + hash + "-row {" +
" width : 100%;" +
"}";
tr.__clazz[hash].rowstylesheet =
qx.bom.Stylesheet.createElement(stylesheet);
var columnData = this.__columnWidths.getData();
for (var i = 0; i < columnData.length; i++)
{
var stylesheet =
".qx-progressive-" + hash + "-col-" + i + " {" +
tr.__tableCellStyleSheet +
"}";
tr.__clazz[hash].cellstylesheet[i] =
qx.bom.Stylesheet.createElement(stylesheet);
}
// Save the hash too
this.__hash = hash;
// Arrange to be called when the window appears or is resized, so we
// can set each style sheet's left and width field appropriately.
var pane = progressive.getStructure().getPane();
pane.addListener("resize", this._resizeColumns, this);
}
},
/**
* Add a cell renderer for use within a row rendered by this row
* renderer.
*
* @param column {Integer}
* The column number for which the cell renderer applies
*
* @param renderer {qx.ui.progressive.renderer.table.cell.Abstract}
* The cell renderer for the specified column.
*
*/
addRenderer : function(column, renderer)
{
var columnData = this.__columnWidths.getData();
if (column < 0 || column >= columnData.length)
{
throw new Error("Column " +
column +
" out of range (max: " +
(columnData.length - 1) +
")");
}
this.__renderers[column] = renderer;
},
/**
* Remove a cell renderer previously added with {@link #addRenderer}.
*
* @param column {Integer}
* The column for which the cell renderer is to be removed.
*
*/
removeRenderer : function(column)
{
var columnData = this.__columnWidths.getData();
if (column < 0 || column >= columnData.length)
{
throw new Error("Column " +
column +
" out of range (max: " +
(columnData.length - 1) +
")");
}
if (! this.__renderers[column])
{
throw new Error("No existing renderer for column " + column);
}
delete this.__renderers[column];
},
// overridden
render : function(state, element)
{
var data = element.data;
var html = [ ];
var cellInfo;
var renderer;
var height = 0;
// Initialize row counter, if necessary. We'll use this for shading
// alternate rows.
if (state.getRendererData()[this.__name].end === undefined)
{
state.getRendererData()[this.__name] =
{
end : 0,
start : 1,
rows : 0,
totalHeight : 0
};
}
// Create the div for this row
var div = document.createElement("div");
// For each cell...
for (var i = 0; i < data.length; i++)
{
var stylesheet = "qx-progressive-" + this.__hash + "-col-" + i;
// Determine what renderer to use for this column
renderer = this.__renderers[i] || this.__defaultCellRenderer;
// Specify information that cell renderer will need
cellInfo =
{
state : state,
rowDiv : div,
stylesheet : stylesheet,
element : element,
dataIndex : i,
cellData : data[i],
height : height
};
// Render this cell
html.push(renderer.render(cellInfo));
// If this cell's height was greater than our current maximum...
if (cellInfo.height > height)
{
// ... then it becomes our row height
height = cellInfo.height;
}
}
height = (height > 0 ? height : this.getDefaultRowHeight());
// Get a reference to our renderer data
var rendererData = state.getRendererData()[this.__name];
// Track total height so we can determine if there's a vertical scrollbar
rendererData.totalHeight += height;
// Set properties for the row div
div.style.position = "relative";
div.style.height = height + "px";
div.className = "qx-progressive-" + this.__hash + "-row";
div.innerHTML = html.join("");
// Add this row to the table
switch(element.location)
{
case "end":
// Determine color of row based on state of last added row
var index = rendererData.end || 0;
// Set the background color of this row
div.style.backgroundColor = this.__colors.bgcol[index];
// Update state for next time
rendererData.end = (index == 0 ? 1 : 0);
// Append our new row to the pane.
state.getPane().getContentElement().getDomElement().appendChild(div);
break;
case "start":
// Get the pane element
var elem = state.getPane().getContentElement().getDomElement();
// Get its children array
var children = elem.childNodes;
// Are there any children?
if (children.length > 0)
{
// Yup. Determine color of row based on state of last added row
var index = rendererData.start;
// Set the background color of this row
div.style.backgroundColor = this.__colors.bgcol[index];
// Update state for next time
rendererData.start = (index == 0 ? 1 : 0);
// Insert our new row before the first child.
elem.insertBefore(div, children[0]);
break;
}
else
{
/* No children yet. We can append our new row. */
elem.appendChild(div);
}
break;
default:
throw new Error("Invalid location: " + element.location);
}
// Increment row count
++rendererData.rows;
},
/**
* This method is required by the box layout. If returns an array of items
* to relayout.
* @return {Array} Array of column data.
*/
getLayoutChildren : function()
{
return this.__columnWidths.getData();
},
/**
* Event handler for the "resize" event. We recalculate the
* widths of each of the columns, and modify the stylesheet rule
* applicable to each column, to apply the new widths.
*
* @param e {qx.event.type.Event}
* Ignored
*
*/
_resizeColumns : function(e)
{
var pane = this.__progressive.getStructure().getPane();
var width =
pane.getBounds().width - qx.bom.element.Scroll.getScrollbarWidth();
// Get the style sheet rule name for this row
var stylesheet = ".qx-progressive-" + this.__hash + "-row";
// Remove the style rule for this row
var tr = qx.ui.progressive.renderer.table.Row;
qx.bom.Stylesheet.removeRule(tr.__clazz[this.__hash].rowstylesheet,
stylesheet);
// Create the new rule for this row
var rule = "width: " + width + "px;";
// Apply the new rule
qx.bom.Stylesheet.addRule(tr.__clazz[this.__hash].rowstylesheet,
stylesheet,
rule);
// Compute the column widths
this.__layout.renderLayout(width, 100, {top: 0, right: 0, bottom: 0, left: 0});
// Get the column data
var columnData = this.__columnWidths.getData();
// Reset each of the column style sheets to deal with width changes
for (var i = 0,
left = 0;
i < columnData.length;
i++,
left += width)
{
// Get the style sheet rule name for this cell
var stylesheet = ".qx-progressive-" + this.__hash + "-col-" + i;
// Remove the style rule for this column
var tr = qx.ui.progressive.renderer.table.Row;
qx.bom.Stylesheet.removeRule(tr.__clazz[this.__hash].cellstylesheet[i],
stylesheet);
// Get this column width.
width = columnData[i].getComputedWidth();
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.tableResizeDebug"))
{
this.debug("col " + i + ": width=" + width);
}
}
// Make our width calculations box-model independent
var inset;
if (qx.core.Environment.get("css.boxmodel") == "content")
{
inset = qx.ui.progressive.renderer.table.Row.__padding * 2;
}
else
{
inset = -1;
}
// Create the new rule, based on calculated widths
var widthRule = (width - inset) + "px;";
var paddingRule =
"0px " + qx.ui.progressive.renderer.table.Row.__padding + "px " +
"0px " + qx.ui.progressive.renderer.table.Row.__padding + "px;";
var leftRule = left + "px;";
var rule =
tr.__tableCellStyleSheet +
"width: " + widthRule +
"left: " + leftRule +
"padding: " + paddingRule;
// Apply the new rule
qx.bom.Stylesheet.addRule(tr.__clazz[this.__hash].cellstylesheet[i],
stylesheet,
rule);
}
}
},
destruct : function()
{
// remove dynamic theme listener
qx.theme.manager.Meta.getInstance().removeListener(
"changeTheme", this.__linkColors, this
);
var name;
for (name in this.__renderers)
{
this.__renderers[name] = null;
}
// Remove any style sheets that we had added
var tr = qx.ui.progressive.renderer.table.Row;
var hash = this.__progressive.toHashCode();
if (tr.__clazz && tr.__clazz[hash])
{
// Remove the row stylesheet
if (tr.__clazz[hash].rowstylesheet)
{
// Get the style sheet rule name for this row
var stylesheet = ".qx-progressive-" + this.__hash + "-row";
// Remove the style rule for this row
var tr = qx.ui.progressive.renderer.table.Row;
qx.bom.Stylesheet.removeRule(tr.__clazz[this.__hash].rowstylesheet,
stylesheet);
}
// Remove each of the column style sheets
if (tr.__clazz[hash].cellstylesheet)
{
for (var i = tr.__clazz[hash].cellstylesheet.length - 1; i >= 0; i--)
{
// Get the style sheet rule name for this cell
var stylesheet = ".qx-progressive-" + this.__hash + "-col-" + i;
var rule = tr.__clazz[this.__hash].cellstylesheet[i];
// Remove the style rule for this column
var tr = qx.ui.progressive.renderer.table.Row;
qx.bom.Stylesheet.removeRule(rule, stylesheet);
}
}
}
if (this.__progressive && this.__progressive.getRendererData)
{
var rendererData = this.__progressive.getRendererData();
if (rendererData &&
rendererData[this.__name] &&
rendererData[this.__name].end !== undefined)
{
rendererData[this.__name] = null;
}
}
this.__colors = this.__renderers = this.__progressive =
this.__columnWidths = null;
this._disposeObjects(
"__layout",
"__defaultCellRenderer",
"__columnData");
}
});