001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings.plaf.css;
014:
015: import org.wings.SComponent;
016: import org.wings.SContainer;
017: import org.wings.SGridBagLayout;
018: import org.wings.SLayoutManager;
019: import org.wings.io.Device;
020: import org.wings.plaf.LayoutCG;
021: import org.wings.plaf.css.PaddingVoodoo;
022: import org.wings.session.BrowserType;
023: import org.wings.session.SessionManager;
024: import org.wings.style.CSSProperty;
025: import org.wings.util.SStringBuilder;
026:
027: import java.awt.*;
028: import java.io.IOException;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: /**
033: * Abstract super class for layout CGs using invisible tables to arrange their contained components.
034: *
035: * @author bschmid
036: */
037: public abstract class AbstractLayoutCG implements LayoutCG {
038:
039: /**
040: * Print HTML table element declaration of a typical invisible layouter table.
041: */
042: protected void openLayouterBody(Device d, SLayoutManager layout)
043: throws IOException {
044: Utils.printDebugNewline(d, layout.getContainer());
045: Utils.printDebug(d, "<!-- START LAYOUT: " + name(layout)
046: + " -->");
047: d.print("<tbody>");
048: }
049:
050: private String name(SLayoutManager layout) {
051: String name = layout.getClass().getName();
052: int pos = name.lastIndexOf('.');
053: if (pos != -1) {
054: name = name.substring(pos + 1);
055: }
056: return name;
057: }
058:
059: /**
060: * Counterpart to {@link #openLayouterBody}
061: */
062: protected void closeLayouterBody(Device d, SLayoutManager layout)
063: throws IOException {
064: d.print("</tbody>");
065: Utils.printDebugNewline(d, layout.getContainer());
066: Utils
067: .printDebug(d, "<!-- END LAYOUT: " + name(layout)
068: + " -->");
069: }
070:
071: /**
072: * Render passed list of components to a table body.
073: * Use {@link #openLayouterBody(org.wings.io.Device,org.wings.SLayoutManager)} in front
074: * and {@link #closeLayouterBody(org.wings.io.Device,org.wings.SLayoutManager)} afterwards!
075: *
076: * @param d The device to write to
077: * @param renderedContainer The (container) component rendered
078: * @param cols Wrap after this amount of columns
079: * @param components The components to layout in this table
080: * @param cellStyle Style attributes for the table cells.
081: */
082: protected void printLayouterTableBody(final Device d,
083: final SContainer renderedContainer, int cols,
084: final List components, final TableCellStyle cellStyle)
085: throws IOException {
086: final int componentCount = components.size();
087: final boolean isMSIE = SessionManager.getSession()
088: .getUserAgent().getBrowserType() == BrowserType.IE;
089: final TableCellStyle origCellStyle = cellStyle.makeACopy();
090:
091: int col = 0;
092: int row = 0;
093: int idx = 0;
094:
095: for (Iterator iter = components.iterator(); iter.hasNext();) {
096: final SComponent c = (SComponent) iter.next();
097:
098: if (col == 0) {
099: Utils.printNewline(d, c.getParent());
100: d.print("<tr>");
101: } else if (col % cols == 0) {
102: row += 1;
103: col = 0;
104: d.print("</tr>");
105: Utils.printNewline(d, c.getParent());
106: d.print("<tr>");
107: }
108:
109: cellStyle.renderAsTH = row == 0 && cellStyle.renderAsTH;
110: cellStyle.defaultLayoutCellHAlignment = getDefaultLayoutCellHAlignment();
111: cellStyle.defaultLayoutCellVAlignment = getDefaultLayoutCellVAlignment();
112:
113: if (PaddingVoodoo.hasPaddingInsets(renderedContainer)) {
114: final Insets patchedInsets = (Insets) origCellStyle
115: .getInsets().clone();
116: final boolean isFirstRow = row == 0;
117: final boolean isLastRow = ((componentCount - (idx + 1))
118: + col < cols);
119: final boolean isFirstCol = (col == 0);
120: final boolean isLastCol = ((col % cols) == 0);
121: PaddingVoodoo.doBorderPaddingsWorkaround(
122: renderedContainer.getBorder(), patchedInsets,
123: isFirstRow, isFirstCol, isLastCol, isLastRow);
124: cellStyle.setInsets(patchedInsets);
125: }
126:
127: openLayouterCell(d, c, cellStyle);
128:
129: c.write(d); // Render component
130:
131: closeLayouterCell(d, c, row == 0 && cellStyle.renderAsTH);
132:
133: col++;
134: idx++;
135:
136: if (!iter.hasNext()) {
137: d.print("</tr>");
138: Utils.printNewline(d, c.getParent());
139: }
140: }
141: }
142:
143: /**
144: * The default horizontal alignment for components inside a layout cell.
145: */
146: public abstract int getDefaultLayoutCellHAlignment();
147:
148: /**
149: * The default vertical alignment for components inside a layout cell.
150: */
151: public abstract int getDefaultLayoutCellVAlignment();
152:
153: /**
154: * Converts a hgap/vgap in according inset declaration.
155: * If a gapp is odd, the overlapping additonal pixel is added to the right/bottom inset.
156: *
157: * @param hgap Horizontal gap between components in px
158: * @param vgap Vertical gap between components in px
159: * @return An inset equal to the gap declarations
160: */
161: protected static Insets convertGapsToInset(int hgap, int vgap) {
162: Insets insets = null;
163: if (hgap > -1 || vgap > -1) {
164: final int paddingTop = (int) Math.round((vgap < 0 ? 0
165: : vgap) / 2.0);
166: final int paddingBottom = (int) Math.round((vgap < 0 ? 0
167: : vgap) / 2.0 + 0.1); // round up
168: final int paddingLeft = (int) Math.round((hgap < 0 ? 0
169: : hgap) / 2.0);
170: final int paddingRight = (int) Math.round((hgap < 0 ? 0
171: : hgap) / 2.0 + 0.1); // round up
172: insets = new Insets(paddingTop, paddingLeft, paddingBottom,
173: paddingRight);
174: }
175: return insets;
176: }
177:
178: public static void openLayouterRow(final Device d, String height)
179: throws IOException {
180: d.print("<tr");
181: Utils.optAttribute(d, "height", height);
182: d.print(">");
183: }
184:
185: /**
186: * Closes a TR.
187: */
188: public static void closeLayouterRow(final Device d)
189: throws IOException {
190: d.print("</tr>");
191: }
192:
193: /**
194: * Opens a TD or TH cell of an invisible layouter table. This method also does component alignment.
195: * <b>Attention:</b> As you want to attach more attributes you need to close the tag with > on your own!
196: */
197: public static void openLayouterCell(final Device d,
198: final SComponent component, final TableCellStyle cellStyle)
199: throws IOException {
200: Utils.printNewline(d, component);
201: if (cellStyle.renderAsTH) {
202: d.print("<th");
203: } else {
204: d.print("<td");
205: }
206:
207: int oversizePadding = Utils.calculateHorizontalOversize(
208: component, true);
209: oversizePadding += cellStyle.getInsets().left;
210: oversizePadding += cellStyle.getInsets().right;
211: Utils.optAttribute(d, "oversize", oversizePadding);
212:
213: Utils.printTableCellAlignment(d, component,
214: cellStyle.defaultLayoutCellHAlignment,
215: cellStyle.defaultLayoutCellVAlignment);
216: Utils.optAttribute(d, "colspan", cellStyle.colspan);
217: Utils.optAttribute(d, "rowspan", cellStyle.rowspan);
218: Utils.optAttribute(d, "width", cellStyle.width);
219: Utils.optAttribute(d, "class", cellStyle.optionalStyleClass);
220: // render optional style attribute
221: if (cellStyle.hasAdditionalCellStyles()
222: || cellStyle.hasInsets()) {
223: SStringBuilder styleString = new SStringBuilder();
224: Utils.createInlineStylesForInsets(styleString, cellStyle
225: .getInsets());
226: styleString.append(cellStyle.getAdditionalCellStyles()
227: .toString());
228: Utils.optAttribute(d, "style", styleString);
229: }
230: d.print(">");
231: }
232:
233: /**
234: * Closes a TD or TH cell of an invisible layouter table.
235: *
236: * @param renderAsHeader Print TH instead of TD
237: */
238: public static void closeLayouterCell(final Device d,
239: final SComponent component, final boolean renderAsHeader)
240: throws IOException {
241: Utils.printNewline(d, component);
242: d.print(renderAsHeader ? "</th>" : "</td>");
243: }
244:
245: protected abstract int getLayoutHGap(SLayoutManager layout);
246:
247: protected abstract int getLayoutVGap(SLayoutManager layout);
248:
249: protected abstract int getLayoutBorder(SLayoutManager layout);
250:
251: protected final TableCellStyle cellLayoutStyle(SLayoutManager layout) {
252: final Insets insets = convertGapsToInset(getLayoutHGap(layout),
253: getLayoutVGap(layout));
254: final int layoutBorder = getLayoutBorder(layout);
255: final TableCellStyle cellStyle = new TableCellStyle();
256:
257: cellStyle.setInsets(insets);
258: if (layoutBorder > 0) {
259: cellStyle.getAdditionalCellStyles().put(CSSProperty.BORDER,
260: layoutBorder + "px solid black");
261: }
262: return cellStyle;
263: }
264:
265: protected abstract int layoutOversize(SLayoutManager layout);
266:
267: protected final int cellOversize(SGridBagLayout layout,
268: Insets insets) {
269: return insets.top + insets.bottom + layout.getBorder();
270: }
271:
272: /**
273: * @return true if current browser is microsoft exploder
274: */
275: protected final boolean isMSIE(final SComponent component) {
276: return component.getSession().getUserAgent().getBrowserType() == BrowserType.IE;
277: }
278: }
|