/******************************************************************************* * Copyright 2015 xWic group (http://www.xwic.de) * * Licensed under the Apache License, Version 2.0 (the "License"). * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *******************************************************************************/ package de.jwic.controls.tableviewer; import java.io.IOException; import java.io.ObjectInputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.LogFactory; import de.jwic.base.Control; import de.jwic.base.Field; import de.jwic.base.IControlRenderer; import de.jwic.base.ImageRef; import de.jwic.base.JWicRuntime; import de.jwic.base.RenderContext; import de.jwic.data.IContentProvider; import de.jwic.data.Range; /** * Default implementation of the ITableRenderer interface. The default renderer * generates HTML "by code", without using templates. The reason for this is * because its faster, costs less resources and the generated HTML is quite * complex so that templates would be quite ugly anyway. * * @author Jens Bornemann */ public class FixColumnTableRenderer extends DefaultTableRenderer { /** * */ private static final long serialVersionUID = 1L; protected int fixColumn = 0; protected boolean reuseContent = true; /* (non-Javadoc) * @see de.jwic.ecolib.tableviewer.ITableRenderer#renderTable(de.jwic.base.RenderContext, de.jwic.ecolib.tableviewer.TableViewer, de.jwic.ecolib.tableviewer.TableModel) */ @SuppressWarnings("unused") public void renderTable(RenderContext renderContext, TableViewer viewer, TableModel model, ITableLabelProvider labelProvider) { if (!viewer.isScrollable() || fixColumn < 0) { super.renderTable(renderContext, viewer, model, labelProvider); return; } String tblvWebPath = JWicRuntime.getJWicRuntime().getContextPath() + "/ecolib/tblviewer/"; PrintWriter writer = renderContext.getWriter(); IContentProvider<?> contentProvider = model.getContentProvider(); int tblWidth = 0; for (Iterator<TableColumn> it = model.getColumnIterator(); it.hasNext(); ) { TableColumn tc = it.next(); if (!tc.isVisible()) { continue; } tblWidth += tc.getWidth(); } int defaultColumnWidth = 150; // TODO use defined css border width instead of this fixed one... int leftWidth = (fixColumn + 1) * 2; { int c = 0; for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext(); ) { TableColumn column = itC.next(); if (c++ > fixColumn || !column.isVisible()) { break; } int width = column.getWidth() != 0 ? column.getWidth() : defaultColumnWidth; leftWidth += width; } } int rightWidth = viewer.getWidth() != 0 ? viewer.getWidth() - leftWidth : 0; // Add resizer div if (viewer.isResizeableColumns()) { writer.print("<DIV id=\"tblViewResizer_" + viewer.getControlID() + "\" class=\"tblViewResizer\" "); /*writer.print("onMouseUp=\"tblViewer_resizeColumnDone()\"" + " onMouseMove=\"tblViewer_resizeColumMove()\"");*/ writer.print(" style=\"height: " + (viewer.getHeight() != 0 ? viewer.getHeight() : 20) + "px\""); writer.println("></DIV>"); } // render main table. writer.print("<table cellspacing=\"0\" cellpadding=\"0\" class=\"" + viewer.getCssClass() + "\""); if (viewer.isFillWidth()) { writer.print(" width=\"100%\""); } else { if (viewer.getWidth() != 0) { writer.print(" width=\"" + viewer.getWidth() + "\""); } } writer.println(">"); writer.print("<tr><td>"); // render data table. String divHeight = viewer.getHeight() != 0 ? (viewer.isShowStatusBar() ? viewer.getHeight() - 18 : viewer.getHeight() ) + "px" : "100%"; String divWidth = viewer.getWidth() != 0 ? viewer.getWidth() + "px" : "100%"; // main table content DIV writer.print("<div class=\"tblContent\""); writer.print(" id=\"tblContent_" + viewer.getControlID() + "\""); writer.print(" style=\"height: " + divHeight + "; width: " + divWidth + "; overflow: hidden\">"); StringWriter leftHtml = new StringWriter(); PrintWriter leftWriter = new PrintWriter(leftHtml); StringWriter rightHtml = new StringWriter(); PrintWriter rightWriter = new PrintWriter(rightHtml); writer = leftWriter; String tblView = "tblViewLeft"; for (int i = 0; i < 2; i++) { boolean right = i == 1; int dataWidth = leftWidth; if (right && writer != rightWriter) { writer = rightWriter; tblView = "tblView"; dataWidth = rightWidth; } if (dataWidth == 0) { dataWidth = 300; } if (viewer.isShowHeader()) { writer.print("<DIV id=\"" + tblView + "Head_" + viewer.getControlID() + "\""); writer.print("style=\"width: " + dataWidth + "px; "); writer.print("height: 20px; overflow: hidden;"); writer.print("\">"); } // create required table attributes. StringBuffer sbTblSelAttrs = new StringBuffer(); switch (model.getSelectionMode()) { case TableModel.SELECTION_SINGLE: { String clearKey = model.getFirstSelectedKey(); if (clearKey == null) { clearKey = ""; } sbTblSelAttrs.append(" tbvSelKey=\"" + clearKey + "\""); sbTblSelAttrs.append(" tbvSelMode=\"single\""); break; } case TableModel.SELECTION_MULTI: { sbTblSelAttrs.append(" tbvSelKey=\"\""); sbTblSelAttrs.append(" tbvSelMode=\"multi\""); break; } default: { sbTblSelAttrs.append(" tbvSelKey=\"\""); sbTblSelAttrs.append(" tbvSelMode=\"none\""); } } if (viewer.isShowHeader()) { writer.print("<table"); writer.print(" tbvctrlid=\"" + viewer.getControlID() + "\""); writer.print(" id=\"" + tblView + "Data_" + viewer.getControlID() + "\""); writer.print(" class=\"tblData\" cellspacing=\"0\" cellpadding=\"0\" "); if (viewer.isScrollable()) { // must add a width attribute, otherwise table-layout: fixed isnt working on Mozilla writer.print(" width=\"" + dataWidth + "\" "); } writer.print(sbTblSelAttrs); writer.println(">"); } // render HEADER columns if (viewer.isShowHeader()) { renderHeader(writer, model, viewer, tblvWebPath, right, dataWidth); } // if scrollable, seperate the data table from the header // and render it within its own, scrollable DIV. Will only // look proper if the columns have a fixed width. int dataHeight = viewer.getHeight() != 0 ? viewer.getHeight() - (viewer.isShowHeader() ? 20 : 0): 200; if (viewer.isShowStatusBar()) { dataHeight -= 18; } if (viewer.isShowHeader()) { writer.println("</TABLE></DIV>"); } if (!right) { writer.print("<DIV style=\""); writer.print("width: " + dataWidth + "px; height: " + dataHeight + "px; overflow: hidden;\""); writer.print("\" id=\"tblViewLeftDataLayer_" + viewer.getControlID() + "\""); writer.print(">"); writer.print("<table"); writer.print(" tbvctrlid=\"" + viewer.getControlID() + "\""); writer.print(" id=\"tblViewLeftDataTbl_" + viewer.getControlID() + "\""); writer.print(" class=\"tblData\" cellspacing=\"0\" cellpadding=\"0\" width=\"" + dataWidth + "\""); writer.print(sbTblSelAttrs); writer.print(">"); } else { writer.print("<DIV onscroll=\"JWic.controls.TableViewer.handleScroll(event, '" + viewer.getControlID() + "')\" style=\""); writer.print("width: " + dataWidth + "px; height: " + dataHeight + "px; overflow: auto;"); writer.print("\" id=\"tblViewDataLayer_" + viewer.getControlID() + "\""); writer.print(">"); writer.print("<table"); writer.print(" tbvctrlid=\"" + viewer.getControlID() + "\""); writer.print(" id=\"tblViewDataTbl_" + viewer.getControlID() + "\""); writer.print(" class=\"tblData\" cellspacing=\"0\" cellpadding=\"0\" width=\"" + dataWidth + "\""); writer.print(sbTblSelAttrs); writer.print(">"); } } // render table BODY leftWriter.print("<TBODY class=\"tblData\">"); rightWriter.print("<TBODY class=\"tblData\">"); int count = 0; try { writer = leftWriter; tblView = "tblViewLeft"; Range range = model.getRange(); if (range.getMax() == 0) { // = Auto int max = -1; int rowSpace = viewer.getHeight() - (35 + 20); // - header height + scroll bar height if (rowSpace != 0) { if (viewer.isShowStatusBar()) { rowSpace -= 18; } max = rowSpace / viewer.getRowHeightHint(); // if (max < 1) { max = 1; } model.setLastRenderedPageSize(max); } range = new Range(range.getStart(), max); } List<Object> list = null; if (reuseContent) { list = new ArrayList<Object>(); for (Iterator<?> it = contentProvider.getContentIterator(range); it.hasNext();) { list.add(it.next()); } } for (int i = 0; i < 2; i++) { boolean right = i == 1; if (right && writer != rightWriter) { writer = rightWriter; tblView = "tblView"; } Iterator<?> it = list != null ? list.iterator() : contentProvider.getContentIterator(model.getRange()); count = renderRows(0, false, writer, it, viewer, labelProvider, right, tblView); } } catch (Exception e) { writer.println("Error reading data from ContentProvider: " + e); log.error("Error reading data from ContentProvider", e); } model.setLastRowRenderCount(count); writer = leftWriter; for (int i = 0; i < 2; i++) { boolean right = i == 1; if (right && writer != rightWriter) { writer = rightWriter; } writer.print("</TBODY>"); writer.print("</table>"); writer.print("</DIV>"); } // finish left and right... leftWriter.close(); rightWriter.close(); writer = renderContext.getWriter(); writer.print("<table cellspacing=\"0\" cellpadding=\"0\"><tr><td valign=top>"); writer.print(leftHtml.toString()); writer.print("</td><td valign=top>"); writer.print(rightHtml.toString()); writer.print("</td></tr></table>"); writer.println("</div></td></tr>"); // render STATUS BAR if (viewer.isShowStatusBar()) { writer.println("<tr><td>"); // render context Control sb = viewer.getControl("statusBar"); IControlRenderer renderer = JWicRuntime.getRenderer(sb.getRendererId()); renderer.renderControl(sb, renderContext); writer.print("</td></tr>"); } writer.println("</table>"); // div to fix IE6 scrolling writer.print("<div id=\"ie6fixscroll_" + viewer.getControlID() + "\" style=display:none;;width:0px;height:0px\"></div>"); // add scroll fields Field fldLeft = viewer.getField("left"); Field fldTop = viewer.getField("top"); writer.println("<INPUT TYPE=\"HIDDEN\" NAME=\"" + fldLeft.getId() + "\" VALUE=\"" + fldLeft.getValue() + "\">"); writer.println("<INPUT TYPE=\"HIDDEN\" NAME=\"" + fldTop.getId() + "\" VALUE=\"" + fldTop.getValue() + "\">"); writer.print("<script language=\"javascript\">"); writer.print("window.setTimeout('var h = document.getElementById(\"tblViewDataLayer_" + viewer.getControlID() + "\").clientHeight;"); writer.print("if (h > 0) document.getElementById(\"tblViewLeftDataLayer_" + viewer.getControlID() + "\").style.height=h + \"px\";"); writer.print("JWic.restoreScrolling(\"" + viewer.getControlID() + "\", \"tblViewDataLayer_" + viewer.getControlID() + "\");', 0);"); writer.print("</script>"); } /** * */ protected void renderHeader(PrintWriter writer, TableModel model, TableViewer viewer, String tblvWebPath, boolean right, int dataWidth) { boolean isResizable = viewer.isResizeableColumns() && viewer.isEnabled(); boolean isColSelectable = viewer.isSelectableColumns() && viewer.isEnabled(); writer.println("<THEAD>"); writer.println("<tr>"); int c = -1; for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext(); ) { TableColumn column = itC.next(); c++; if (!right) { if (c > fixColumn) { break; } } else { if (c <= fixColumn) { continue; } } if (!column.isVisible()) { continue; } if (isResizable && column.getWidth() == 0) { // must set a default width if resizeable columns is activated column.setWidth(150); } writer.print("<th"); int innerWidth = 0; if (column.getWidth() > 0) { writer.print(" width=\"" + column.getWidth() + "\""); innerWidth = column.getWidth() - (viewer.isResizeableColumns() ? 5 : 0); innerWidth = innerWidth - (column.getSortIcon() != TableColumn.SORT_ICON_NONE ? 8 : 0); if (innerWidth < 3) { innerWidth = 3; } } writer.print(" colIdx=\"" + column.getIndex() + "\" "); //header tooltip if (column.getToolTip() != null && column.getToolTip().length() > 0) { writer.print(" title=\"" + column.getToolTip() + "\""); } writer.println(">"); // create cell table writer.print("<TABLE class=\"tbvColHeader\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><TR>"); writer.print("<TD class=\"" + getHeaderCssClass(column) + "\" width=\"" + innerWidth + "\""); if (isColSelectable) { writer.print(" onClick=\"JWic.fireAction('" + viewer.getControlID() + "', 'columnSelection', '" + column.getIndex() + "')\""); writer.print(" onMouseDown=\"JWic.controls.TableViewer.pushColumn(" + column.getIndex() + ", '" + viewer.getControlID() + "', " + (right ? "false" : "true") + ")\""); writer.print(" onMouseUp=\"JWic.controls.TableViewer.releaseColumn()\""); writer.print(" onMouseOut=\"JWic.controls.TableViewer.releaseColumn()\""); } writer.print(">"); writer.print("<NOBR>"); if (column.getImage() != null) { writer.print("<IMG SRC=\"" + column.getImage().getPath() + "\" border=0/>"); } writer.print(column.getTitle()); writer.print("</NOBR>"); writer.print("</TD>"); if (column.getSortIcon() != TableColumn.SORT_ICON_NONE) { ImageRef imgSort = null; switch (column.getSortIcon()) { case TableColumn.SORT_ICON_UP: imgSort = ICON_SORTUP; break; case TableColumn.SORT_ICON_DOWN: imgSort = ICON_SORTDOWN; break; } if (imgSort != null) { writer.print("<TD class=\"tbvColHeadCell\" width=\"8\">"); writer.print("<IMG SRC=\"" + imgSort.getPath() + "\" border=0>"); writer.print("</TD>"); } } if (isResizable) { writer.print("<TD class=\"tbvColHeadCellPoint\" width=\"3\"><IMG SRC=\"" + tblvWebPath + "resizer.gif\" width=\"3\" height=\"13\""); writer.print(" colIdx=\"" + column.getIndex() + "\""); writer.print(" onMouseDown=\"JWic.controls.TableViewer.resizeColumn(event, '" + viewer.getControlID() + "', " + (right ? "false" : "true") + ")\" class=\"tblResize\" border=0>"); writer.print("</TD>"); } writer.print("</TR></TABLE>"); writer.println("</th>"); } // if the width is fixed, we must render an empty column at the end so that the // browser will not adjust the columns width if (dataWidth != 0 && right) { writer.print("<TH width=\"" + dataWidth + "\"> </TH>"); } writer.println("</tr>"); writer.println("</THEAD>"); } /** * @param writer * @param rootIterator */ @SuppressWarnings({ "unchecked", "rawtypes" }) protected int renderRows(int level, boolean hasNext, PrintWriter writer, Iterator<?> it, TableViewer viewer, ITableLabelProvider labelProvider, boolean right, String tblView) { int expandIconWidth = getExpandIconWidth(); int expandIconHeight = getExpandIconHeight(); TableModel model = viewer.getModel(); IContentProvider contentProvider = model.getContentProvider(); int count = 0; while(it.hasNext()) { Object row = it.next(); count++; String key = contentProvider.getUniqueKey(row) ; boolean expanded = model.isExpanded(key); writer.print("<tr"); String rowCssClass = ""; if (!(it.hasNext() || hasNext || contentProvider.hasChildren(row))) { rowCssClass="lastRow"; } // handle selection writer.print(" tbvRowKey=\"" + key + "\""); if (model.getSelectionMode() != TableModel.SELECTION_NONE) { if (viewer.isEnabled()) { writer.print(" onClick=\"JWic.controls.TableViewer.clickRow(this, event)\""); writer.print(" onDblClick=\"JWic.controls.TableViewer.clickRow(this, event, true)\""); } if (model.isSelected(key)) { rowCssClass = rowCssClass + "selected"; } } if (rowCssClass.length() != 0) { writer.print(" class=\"" + rowCssClass + "\""); } writer.println(">"); int c = -1; for (Iterator<TableColumn> itC = model.getColumnIterator(); itC.hasNext(); ) { TableColumn column = itC.next(); c++; if (!right) { if (c > fixColumn) { break; } } else { if (c <= fixColumn) { continue; } } if (!column.isVisible()) { continue; } CellLabel cell = labelProvider.getCellLabel(row, column, new RowContext(expanded, level)); writer.print("<td"); if (column.getWidth() > 0) { writer.print(" width=\"" + column.getWidth() + "\""); } if (cell.cssClass != null) { writer.print(" class=\"" + cell.cssClass + "\""); } if (cell.toolTip != null && cell.toolTip.length() > 0) { writer.print(" title=\"" + cell.toolTip + "\""); } if (cell.colspan != null && cell.colspan.intValue() > 0) { writer.print(" colspan=\"" + cell.colspan + "\""); } writer.print(">"); boolean innerTable = column.getIndex() == viewer.getExpandableColumn() || cell.image != null && cell.text != null; //innerTable = true; if (innerTable) { writer.print("<table class=\"inner\" cellspacing=0 cellpadding=0 border=0><tr><td>"); //writer.print("</p>"); } // handle exp/collapse if (column.getIndex() == viewer.getExpandableColumn()) { // indent for (int i = 0; i < level; i++) { writer.print(ICON_CLEAR.toImgTag(expandIconWidth, expandIconHeight)); } if (contentProvider.hasChildren(row)) { if (viewer.isEnabled()) { writer.print("<a href=\"#\" onClick=\"return "); writer.print(expanded ? "trV_Collapse(event)" : "trV_Expand(event)"); writer.print("\";return false;\">"); writer.print((expanded ? ICON_COLLAPSE : ICON_EXPAND).toImgTag(expandIconWidth, expandIconHeight)); writer.print("</A>"); } else { writer.print((expanded ? ICON_COLLAPSE : ICON_EXPAND).toImgTag(expandIconWidth, expandIconHeight)); } } else { if (isExpandLinkSpacing()) { writer.print(ICON_CLEAR.toImgTag(expandIconWidth, expandIconHeight)); } } //writer.print("</td><td class=\"content\">"); } // print cell value if (cell.image != null) { writer.print(cell.image.toImgTag()); if (cell.text != null) { //writer.print("</td><td class=\"content\">"); } } if (cell.text != null) { writer.print("<NOBR>"); if (cell.text.length() == 0 && cell.image == null) { writer.print(" "); } else { writer.print(cell.text); } writer.print("</NOBR>"); } if (innerTable) { writer.print("</td></tr></table>"); //writer.print("</p>"); } writer.println("</td>"); } // if its a fixed width, must render an empty column that fills up the space. if (viewer.getWidth() != 0 && right) { writer.println("<TD> </TD>"); } writer.println(" </tr>"); if (contentProvider.hasChildren(row) && expanded) { renderRows(level + 1, hasNext || it.hasNext(), writer, contentProvider.getChildren(row), viewer, labelProvider, right, tblView); } } return count; } /** * Returns CssClass used for table headers. * @param column * @return */ protected String getHeaderCssClass(TableColumn column) { return "tbvColHeadCell"; } /** * Get new logger after deserialization. * @param s * @throws IOException */ private void readObject(ObjectInputStream s) throws IOException { try { s.defaultReadObject(); } catch (ClassNotFoundException e) { throw new IOException("ClassNotFound in readObject"); } log = LogFactory.getLog(getClass()); } /** * @return the fixColumn */ public int getFixColumn() { return fixColumn; } /** * @param fixColumn the fixColumn to set */ public void setFixColumn(int fixColumn) { this.fixColumn = fixColumn; } /** * @return the reuseContent */ public boolean isReuseContent() { return reuseContent; } /** * @param reuseContent the reuseContent to set */ public void setReuseContent(boolean reuseContent) { this.reuseContent = reuseContent; } }