001: /*
002: * $Id$ $Revision$ $Date$
003: *
004: * ==============================================================================
005: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
006: * use this file except in compliance with the License. You may obtain a copy of
007: * 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, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations under
015: * the License.
016: */
017: package wicket.extensions.markup.html.tree.table;
018:
019: import java.io.Serializable;
020:
021: import javax.swing.tree.TreeModel;
022: import javax.swing.tree.TreeNode;
023:
024: import wicket.Component;
025: import wicket.MarkupContainer;
026: import wicket.behavior.AbstractBehavior;
027: import wicket.extensions.markup.html.tree.AbstractTree;
028: import wicket.extensions.markup.html.tree.DefaultAbstractTree;
029: import wicket.extensions.markup.html.tree.table.ColumnLocation.Alignment;
030: import wicket.markup.ComponentTag;
031: import wicket.markup.html.PackageResourceReference;
032: import wicket.markup.html.WebMarkupContainer;
033: import wicket.markup.html.basic.Label;
034: import wicket.markup.html.panel.Fragment;
035: import wicket.model.AbstractReadOnlyModel;
036: import wicket.model.IModel;
037:
038: /**
039: * TreeTable is a component that represents a grid with a tree. It's divided
040: * into columns. One of the columns has to be column derived from
041: * {@link AbstractTreeColumn}.
042: *
043: * @author Matej Knopp
044: */
045: public class TreeTable extends DefaultAbstractTree {
046: /**
047: * Callback for rendering tree node text.
048: */
049: public static interface IRenderNodeCallback extends Serializable {
050: /**
051: * Renders the tree node to text.
052: *
053: * @param node
054: * The tree node to render
055: * @return the tree node as text
056: */
057: public String renderNode(TreeNode node);
058: }
059:
060: /**
061: * Represents a content of a cell in TreeColumn (column containing the
062: * actual tree).
063: *
064: * @author Matej Knopp
065: */
066: private class TreeFragment extends Fragment {
067: private static final long serialVersionUID = 1L;
068:
069: /**
070: * Constructor.
071: *
072: *
073: * @param id
074: * @param node
075: * @param level
076: * @param renderNodeCallback
077: * The call back for rendering nodes
078: */
079: public TreeFragment(String id, final TreeNode node, int level,
080: final IRenderNodeCallback renderNodeCallback) {
081: super (id, "fragment");
082:
083: add(newIndentation(this , "indent", node, level));
084:
085: add(newJunctionLink(this , "link", "image", node));
086:
087: MarkupContainer nodeLink = newNodeLink(this , "nodeLink",
088: node);
089: add(nodeLink);
090:
091: nodeLink.add(newNodeIcon(nodeLink, "icon", node));
092:
093: nodeLink.add(new Label("label",
094: new AbstractReadOnlyModel() {
095: private static final long serialVersionUID = 1L;
096:
097: /**
098: * @see wicket.model.AbstractReadOnlyModel#getObject(wicket.Component)
099: */
100: public Object getObject(Component c) {
101: return renderNodeCallback.renderNode(node);
102: }
103: }));
104: }
105: }
106:
107: /** Reference to the css file. */
108: private static final PackageResourceReference CSS = new PackageResourceReference(
109: DefaultAbstractTree.class, "res/tree-table.css");
110:
111: private static final long serialVersionUID = 1L;
112:
113: /**
114: * Creates a tree cell for given node. This method is supposed to be used by
115: * TreeColumns (columns that draw the actual tree).
116: *
117: * @param parent
118: * Parent component
119: *
120: * @param id
121: * Component ID
122: *
123: * @param node
124: * Tree node for the row
125: *
126: * @param level
127: * How deep is the node nested (for convenience)
128: *
129: * @param callback
130: * Used to get the display string
131: *
132: * @param table
133: * Tree table
134: *
135: * @return The tree cell
136: */
137: public static Component newTreeCell(MarkupContainer parent,
138: String id, TreeNode node, int level,
139: IRenderNodeCallback callback, TreeTable table) {
140: return table.newTreePanel(parent, id, node, level, callback);
141: }
142:
143: // columns of the TreeTable
144: private IColumn columns[];
145:
146: /**
147: * Creates the TreeTable for the given array of columns.
148: *
149: * @param id
150: * @param columns
151: */
152: public TreeTable(String id, IColumn columns[]) {
153: super (id);
154: init(columns);
155: }
156:
157: /**
158: * Creates the TreeTable for the given model and array of columns.
159: *
160: * @param id
161: * The component id
162: * @param model
163: * The tree model
164: * @param columns
165: * The columns
166: */
167: public TreeTable(String id, IModel model, IColumn columns[]) {
168: super (id, model);
169: init(columns);
170: }
171:
172: /**
173: * Creates the TreeTable for the given TreeModel and array of columns.
174: *
175: * @param id
176: * The component id
177: * @param model
178: * The tree model
179: * @param columns
180: * The columns
181: */
182: public TreeTable(String id, TreeModel model, IColumn columns[]) {
183: super (id, model);
184: init(columns);
185: }
186:
187: /**
188: * Adds the header to the TreeTable.
189: */
190: protected void addHeader() {
191: // create the view for side columns
192: SideColumnsView sideColumns = new SideColumnsView(
193: "sideColumns", null);
194: add(sideColumns);
195: if (columns != null)
196: for (int i = 0; i < columns.length; i++) {
197: IColumn column = columns[i];
198: if (column.getLocation().getAlignment() == Alignment.LEFT
199: || column.getLocation().getAlignment() == Alignment.RIGHT) {
200: Component component = column.newHeader(sideColumns,
201: "" + i);
202: sideColumns.add(component);
203: sideColumns.addColumn(column, component, null);
204: }
205: }
206:
207: // create the view for middle columns
208: MiddleColumnsView middleColumns = new MiddleColumnsView(
209: "middleColumns", null);
210: add(middleColumns);
211: if (columns != null)
212: for (int i = 0; i < columns.length; i++) {
213: IColumn column = columns[i];
214: if (column.getLocation().getAlignment() == Alignment.MIDDLE) {
215: Component component = column.newHeader(
216: middleColumns, "" + i);
217: middleColumns.add(component);
218: middleColumns.addColumn(column, component, null);
219: }
220: }
221: };
222:
223: /**
224: * @see wicket.extensions.markup.html.tree.DefaultAbstractTree#getCSS()
225: */
226: protected PackageResourceReference getCSS() {
227: return CSS;
228: }
229:
230: /**
231: * Creates a new instance of the TreeFragment.
232: *
233: * @param parent
234: * The parent component
235: * @param id
236: * The component id
237: * @param node
238: * The tree node
239: * @param level
240: * The level of the tree row
241: * @param renderNodeCallback
242: * The node call back
243: * @return The tree panel
244: */
245: protected Component newTreePanel(MarkupContainer parent, String id,
246: final TreeNode node, int level,
247: IRenderNodeCallback renderNodeCallback) {
248: return new TreeFragment(id, node, level, renderNodeCallback);
249: }
250:
251: /**
252: * @see AbstractTree#onBeforeAttach()
253: */
254: protected void onBeforeAttach() {
255: // has the header been added yet?
256: if (get("sideColumns") == null) {
257: // no. initialize columns first
258: if (columns != null)
259: for (int i = 0; i < columns.length; i++) {
260: IColumn column = columns[i];
261: column.setTreeTable(this );
262: }
263:
264: // add the tree table header
265: addHeader();
266: }
267:
268: super .onAttach();
269: }
270:
271: /**
272: * Populates one row of the tree.
273: *
274: * @param item
275: * the tree node component
276: * @param level
277: * the current level
278: */
279: protected void populateTreeItem(WebMarkupContainer item, int level) {
280: final TreeNode node = (TreeNode) item.getModelObject();
281:
282: // add side columns
283: SideColumnsView sideColumns = new SideColumnsView(
284: "sideColumns", node);
285: item.add(sideColumns);
286: if (columns != null)
287: for (int i = 0; i < columns.length; i++) {
288: IColumn column = columns[i];
289: if (column.getLocation().getAlignment() == Alignment.LEFT
290: || column.getLocation().getAlignment() == Alignment.RIGHT) {
291: Component component;
292: // first try to create a renderable
293: IRenderable renderable = column
294: .newCell(node, level);
295:
296: if (renderable == null) {
297: // if renderable failed, try to create a regular
298: // component
299: component = column.newCell(sideColumns, "" + i,
300: node, level);
301: sideColumns.add(component);
302: } else {
303: component = null;
304: }
305:
306: sideColumns
307: .addColumn(column, component, renderable);
308: }
309: }
310:
311: // add middle columns
312: MiddleColumnsView middleColumns = new MiddleColumnsView(
313: "middleColumns", node);
314: if (columns != null)
315: for (int i = 0; i < columns.length; i++) {
316: IColumn column = columns[i];
317: if (column.getLocation().getAlignment() == Alignment.MIDDLE) {
318: Component component;
319: // first try to create a renderable
320: IRenderable renderable = column
321: .newCell(node, level);
322:
323: if (renderable == null) {
324: // if renderable failed, try to create a regular
325: // component
326: component = column.newCell(middleColumns, ""
327: + i, node, level);
328: middleColumns.add(component);
329: } else {
330: component = null;
331: }
332:
333: middleColumns.addColumn(column, component,
334: renderable);
335: }
336: }
337: item.add(middleColumns);
338:
339: // do distinguish between selected and unselected rows we add an
340: // behavior
341: // that modifies row css class.
342: item.add(new AbstractBehavior() {
343: private static final long serialVersionUID = 1L;
344:
345: public void onComponentTag(Component component,
346: ComponentTag tag) {
347: super .onComponentTag(component, tag);
348: if (getTreeState().isNodeSelected(node)) {
349: tag.put("class", "row-selected");
350: } else {
351: tag.put("class", "row");
352: }
353: }
354: });
355: }
356:
357: /**
358: * Internal initialization. Also checks if at least one of the columns is
359: * derived from AbstractTreeColumn.
360: *
361: * @param columns
362: * The columns
363: */
364: private void init(IColumn columns[]) {
365: boolean found = false;
366: if (columns != null)
367: for (int i = 0; i < columns.length; i++) {
368: IColumn column = columns[i];
369: if (column instanceof AbstractTreeColumn) {
370: found = true;
371: break;
372: }
373: }
374: if (found == false) {
375: throw new IllegalArgumentException(
376: "At least one column in TreeTable must be derived from AbstractTreeColumn.");
377: }
378:
379: this.columns = columns;
380: }
381: }
|