001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.extensions.markup.html.tree.table;
018:
019: import java.text.NumberFormat;
020: import java.util.ArrayList;
021: import java.util.Arrays;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Locale;
025:
026: import javax.swing.tree.TreeNode;
027:
028: import org.apache.wicket.Component;
029: import org.apache.wicket.RequestCycle;
030: import org.apache.wicket.Response;
031: import org.apache.wicket.extensions.markup.html.tree.table.ColumnLocation.Unit;
032: import org.apache.wicket.markup.MarkupStream;
033: import org.apache.wicket.markup.html.WebMarkupContainer;
034: import org.apache.wicket.response.NullResponse;
035:
036: /**
037: * Class that renders cells of columns aligned in the middle. This class also
038: * takes care of counting their widths and of column spans.
039: *
040: * @author Matej Knopp
041: */
042: final class MiddleColumnsView extends WebMarkupContainer {
043: private static final long serialVersionUID = 1L;
044:
045: private final List columns = new ArrayList();
046:
047: private final List components = new ArrayList();
048:
049: private TreeNode node;
050:
051: private final List renderables = new ArrayList();
052:
053: private boolean treeHasLeftColumn;
054:
055: /**
056: * Constructor.
057: *
058: * @param id
059: * The component id
060: * @param node
061: * The tree node
062: * @param treeHasLeftColumn
063: * Whether there is a column aligned to left in the tree table
064: */
065: public MiddleColumnsView(String id, TreeNode node,
066: boolean treeHasLeftColumn) {
067: super (id);
068: this .node = node;
069: this .treeHasLeftColumn = treeHasLeftColumn;
070: }
071:
072: /**
073: * Adds a column to be rendered.
074: *
075: * @param column
076: * The column to render
077: * @param component
078: * The component
079: * @param renderable
080: * The renderer
081: */
082: public void addColumn(IColumn column, Component component,
083: IRenderable renderable) {
084: if (column.isVisible()) {
085: columns.add(column);
086: components.add(component);
087: renderables.add(renderable);
088: }
089: }
090:
091: /**
092: * Computes the percentagle widths of columns. If a column spans over other
093: * columns, the widths of those columns will be zero.
094: *
095: * @return widths of columns
096: */
097: protected double[] computeColumnWidths() {
098: // initialize the columns array
099: double result[] = new double[columns.size()];
100: Arrays.fill(result, 0d);
101:
102: // the sum of weights of all columns
103: double sum = 0d;
104: double whole = 99.8d;
105:
106: // go over all columns, check their alignment and count sum of their
107: // weights
108: for (Iterator i = columns.iterator(); i.hasNext();) {
109: IColumn column = (IColumn) i.next();
110: // check if the unit is right
111: if (column.getLocation().getUnit() != Unit.PROPORTIONAL) {
112: throw new IllegalStateException(
113: "Middle columns must have PROPORTIONAL unit set.");
114: }
115: sum += column.getLocation().getSize();
116: }
117:
118: int index = 0; // index of currently processed column
119:
120: int spanColumn = 0; // index of column that is spanning over currently
121: // processed column (if any)
122: int spanLeft = 0; // over how many columns does the spanning column
123: // span
124:
125: for (Iterator i = columns.iterator(); i.hasNext();) {
126: IColumn column = (IColumn) i.next();
127: int ix = index; // to which column should we append the size
128: if (spanLeft > 0) // is there a column spanning over current
129: // column?
130: {
131: ix = spanColumn; // the size should be appended to the
132: // spanning
133: // column
134: --spanLeft;
135: }
136: // add the percentage size to the column
137: result[ix] += Math.round((column.getLocation().getSize())
138: / sum * whole);
139:
140: // wants this column to span and no other column is spanning over
141: // this column?
142: if (spanLeft == 0 && column.getSpan(node) > 1) {
143: int maxSpan = columns.size() - columns.indexOf(column); // how
144: // many
145: // columns
146: // left
147: int span = column.getSpan(node) - 1; // how much columns want
148: // the column to span
149: // over
150: spanColumn = index; // index of column that is spanning
151: spanLeft = span < maxSpan ? span : maxSpan; // set the number of
152: // columns spanned
153: // over
154: }
155: ++index;
156: }
157:
158: // count the sum
159: double together = 0d;
160:
161: for (int i = 0; i < result.length; i++) {
162: together += result[i];
163: }
164:
165: // is it bigger than 99.8? that can cause layout problems in IE
166: if (together > 99.8d) {
167: // this can happen - rounding error. just decrease the last one
168: for (int i = result.length - 1; i >= 0; --i) {
169: if (result[i] != 0d) {
170: result[i] -= together - 99.8d;
171: break;
172: }
173: }
174:
175: }
176:
177: return result;
178: }
179:
180: /**
181: * Renders all columns.
182: *
183: * @param markupStream
184: * The markup stream of this component
185: */
186: protected void onRender(final MarkupStream markupStream) {
187: final int markupStart = markupStream.getCurrentIndex();
188: Response response = RequestCycle.get().getResponse();
189: double widths[] = computeColumnWidths();
190:
191: boolean rendered = false; // has been at least one column (component,
192: // not renderable) rendered?
193:
194: NumberFormat nf = NumberFormat
195: .getNumberInstance(Locale.ENGLISH);
196: nf.setMaximumFractionDigits(0);
197: nf.setMaximumFractionDigits(2);
198:
199: for (int i = 0; i < columns.size(); ++i) {
200: Component component = (Component) components.get(i);
201: IRenderable renderable = (IRenderable) renderables.get(i);
202: IColumn column = (IColumn) columns.get(i);
203:
204: // write the wrapping column markup
205: response.write("<span class=\"b_\" style=\"width:"
206: + nf.format(widths[i]) + "%\">");
207:
208: // determine whether we should render the left border
209: if (!treeHasLeftColumn && i == 0)
210: response.write("<span class=\"d_\">");
211: else
212: response.write("<span class=\"c_\">");
213:
214: if (component != null) // is there a component for current column?
215: {
216: // render the component
217: markupStream.setCurrentIndex(markupStart);
218: component.render(markupStream);
219: rendered = true;
220: } else if (renderable != null) // no component - try to render
221: // renderable
222: {
223: renderable.render(node, response);
224: } else {
225: // no renderable or component. fail
226: throw new IllegalStateException(
227: "Either renderable or cell component must be created for this noode");
228: }
229:
230: // end of wrapping markup
231: response.write("</span></span>\n");
232:
233: // does this component span over other columns
234: int span = column.getSpan(node);
235: if (span > 1) {
236: // iterate through the columns and if any of them has a
237: // component,
238: // render the component to null response (otherwise the
239: // component will
240: // complain that it hasn't been rendered
241: for (int j = 1; j < span && i < components.size(); ++j) {
242: ++i;
243: if (components.get(i) != null) {
244: Response old = RequestCycle.get().setResponse(
245: NullResponse.getInstance());
246: markupStream.setCurrentIndex(markupStart);
247: ((Component) components.get(i))
248: .render(markupStream);
249: RequestCycle.get().setResponse(old);
250: rendered = true;
251: }
252:
253: }
254: }
255: }
256:
257: // if no component was rendered just advance in the markup stream
258: if (rendered == false) {
259: markupStream.skipComponent();
260: }
261: }
262: }
|