001: /*
002: * MyGWT Widget Library
003: * Copyright(c) 2007, MyGWT.
004: * licensing@mygwt.net
005: *
006: * http://mygwt.net/license
007: */
008: package net.mygwt.ui.client.widget.treetable;
009:
010: import net.mygwt.ui.client.MyDOM;
011: import net.mygwt.ui.client.Style;
012: import net.mygwt.ui.client.event.BaseEvent;
013: import net.mygwt.ui.client.event.Listener;
014: import net.mygwt.ui.client.util.DelayedTask;
015: import net.mygwt.ui.client.util.StyleTemplate;
016: import net.mygwt.ui.client.widget.table.ITable;
017: import net.mygwt.ui.client.widget.table.TableColumn;
018: import net.mygwt.ui.client.widget.table.TableColumnModel;
019: import net.mygwt.ui.client.widget.table.TableHeader;
020: import net.mygwt.ui.client.widget.table.TableItem;
021: import net.mygwt.ui.client.widget.tree.MultiSelectionModel;
022: import net.mygwt.ui.client.widget.tree.Tree;
023: import net.mygwt.ui.client.widget.tree.TreeSelectionModel;
024:
025: import com.google.gwt.user.client.DOM;
026: import com.google.gwt.user.client.Element;
027: import com.google.gwt.user.client.Event;
028: import com.google.gwt.user.client.ui.WidgetHelper;
029:
030: /**
031: * A hierarchical tree widget with support for additional columns. The tree
032: * contains a hierarchy of <code>TreeTableItems</code> that the user can open,
033: * close, and select.
034: *
035: * <dl>
036: * <dt><b>Styles:</b></dt>
037: * <dd>SINGLE, MULTI, CHECK, HORIZONTAL</dd>
038: *
039: * <dt><b>Events:</b></dt>
040: *
041: * <dd><b>BeforeAdd</b> : (widget, item, index)<br>
042: * <div>Fires before a item is added or inserted. Listeners can set the
043: * <code>doit</code> field to <code>false</code> to cancel the action.</div>
044: * <ul>
045: * <li>widget : the parent item</li>
046: * <li>item : the item being added</li>
047: * <li>index : the index at which the item will be added</li>
048: * </ul>
049: * </dd>
050: *
051: * <dd><b>BeforeRemove</b> : (widget, item)<br>
052: * <div>Fires before a item is removed. Listeners can set the <code>doit</code>
053: * field to <code>false</code> to cancel the action.</div>
054: * <ul>
055: * <li>widget : the parent item</li>
056: * <li>item : the item being removed</li>
057: * </ul>
058: * </dd>
059: *
060: * <dd><b>BeforeExpand</b> : (widget, item)<br>
061: * <div>Fires before a item is expanded. Listeners can set the <code>doit</code>
062: * field to <code>false</code> to cancel the expand.</div>
063: * <ul>
064: * <li>widget : the parent item</li>
065: * <li>item : the item being expanded</li>
066: * </ul>
067: * </dd>
068: *
069: * <dd><b>BeforeCollapse</b> : (widget, item)<br>
070: * <div>Fires before a item is collapsed. Listeners can set the
071: * <code>doit</code> field to <code>false</code> to cancel the collapse.</div>
072: * <ul>
073: * <li>widget : the parent item</li>
074: * <li>item : the item being expanded</li>
075: * </ul>
076: * </dd>
077: *
078: * <dd><b>Add</b> : (widget, item, index)<br>
079: * <div>Fires after a item has been added or inserted.</div>
080: * <ul>
081: * <li>widget : the parent item</li>
082: * <li>item : the item that was added</li>
083: * <li>index : the index at which the item will be added</li>
084: * </ul>
085: * </dd>
086: *
087: * <dd><b>Remove</b> : (widget, item)<br>
088: * <div>Fires after a item has been removed.</div>
089: * <ul>
090: * <li>widget : the parent item</li>
091: * <li>item : the item being removed</li>
092: * </ul>
093: * </dd>
094: *
095: * <dd><b>Expand</b> : (widget, item)<br>
096: * <div>Fires after a item has been expanded.</div>
097: * <ul>
098: * <li>widget : the parent item</li>
099: * <li>item : the item being expanded</li>
100: * </ul>
101: * </dd>
102: *
103: * <dd><b>Collapse</b> : (widget, item)<br>
104: * <div>Fires ater a item is collapsed.</div>
105: * <ul>
106: * <li>widget : the parent item</li>
107: * <li>item : the item being collapsed</li>
108: * </ul>
109: * </dd>
110: *
111: * <dd><b>CheckChange</b> : (widget)<br>
112: * <div>Fires after a check state change.</div>
113: * <ul>
114: * <li>widget : the parent item</li>
115: * </ul>
116: * </dd>
117: * </dd>
118: *
119: * <dd><b>ContextMenu</b> : (widget)<br>
120: * <div>Fires before the tree's context menu is shown.</div>
121: * <ul>
122: * <li>widget : this</li>
123: * </ul>
124: * </dd>
125: *
126: * <dd><b>CellClick</b> : (widget, item, index)<br>
127: * <div>Fires after a cell has been clicked.</div>
128: * <ul>
129: * <li>widget : tree table</li>
130: * <li>item : item represented by the cell</li>
131: * <li>index : cell column index</li>
132: * </ul>
133: * </dd>
134: *
135: * <dd><b>CellDoubleClick</b> : (widget, item, index)<br>
136: * <div>Fires after a cell has been double clicked.</div>
137: * <ul>
138: * <li>widget : tree table</li>
139: * <li>item : item represented by the cell</li>
140: * <li>index : cell column index</li>
141: * </ul>
142: * </dd>
143: *
144: * <dd><b>RowClick</b> : (widget, item, index)<br>
145: * <div>Fires after a cell has been clicked.</div>
146: * <ul>
147: * <li>widget : tree table</li>
148: * <li>item : item that represents the row</li>
149: * <li>index : cell column index</li>
150: * </ul>
151: * </dd>
152: *
153: * <dd><b>RowDoubleClick</b> : (widget, item, index)<br>
154: * <div>Fires after a cell has been double clicked.</div>
155: * <ul>
156: * <li>widget : tree table</li>
157: * <li>item : item that represents the row</li>
158: * <li>index : cell column index</li>
159: * </ul>
160: * </dd>
161: *
162: * <dt><b>CSS:</b></dt>
163: * <dd>.my-treetbl (the containing table)</dd>
164: * <dd>.my-treetbl-data (the table data)</dd>
165: * <dd>.my-treetbl-item (a row in the table)</dd>
166: * <dd>.my-treetbl-tree (the tree itself)</dd>
167: * <dd>.my-treetbl-item (a node within the tree)</dd>
168: * <dd>.my-treetbl-item-text span (the tree item text)</dd>
169: * </dl>
170: */
171: public class TreeTable extends Tree implements ITable {
172: /**
173: * disableColumnContextMenu specifies if the column context menu should be
174: * disabled. Default value is <code>false</code>.
175: */
176: boolean disableColumnContextMenu;
177:
178: private TreeTableHeader header;
179: private TreeTableColumnModel cm;
180: private TreeTableView view;
181: private int lastLeft;
182:
183: StyleTemplate styleTemplate = null;
184:
185: private DelayedTask scrollTask = new DelayedTask(new Listener() {
186: public void handleEvent(BaseEvent be) {
187: header.updateSplitBars();
188: }
189: });
190:
191: /**
192: * Creates a new single select tree table. A column model must be set before
193: * the table is rendered.
194: */
195: public TreeTable() {
196: this .style = Style.SINGLE;
197: }
198:
199: /**
200: * Creates a new tree table with the given column model.
201: *
202: * @param cm the tree table column model
203: */
204: public TreeTable(TreeTableColumnModel cm) {
205: this (Style.NONE, cm);
206: }
207:
208: /**
209: * Creates a new tree table with the given style and column model.
210: *
211: * @param style the style
212: * @param cm the tree table column model
213: */
214: public TreeTable(int style, TreeTableColumnModel cm) {
215: super (style);
216: this .cm = cm;
217: }
218:
219: /**
220: * Returns the column at the specified index.
221: *
222: * @param index the column index
223: * @return the column
224: */
225: public TableColumn getColumn(int index) {
226: return cm.getColumn(index);
227: }
228:
229: /**
230: * Returns the column with the given id.
231: *
232: * @param id the column id
233: * @return the column
234: */
235: public TableColumn getColumn(String id) {
236: return cm.getColumn(id);
237: }
238:
239: /**
240: * Returns the column context menu enabed state.
241: *
242: * @return <code>true</code> if enabled, <code>false</code> otherwise.
243: */
244: public boolean getColumnContextMenu() {
245: return !disableColumnContextMenu;
246: }
247:
248: /**
249: * Returns the number of columns contained in the table.
250: *
251: * @return the number of columns
252: */
253: public int getColumnCount() {
254: return cm.getColumnCount();
255: }
256:
257: /**
258: * Returns the table's column model.
259: *
260: * @return the column model
261: */
262: public TableColumnModel getColumnModel() {
263: return cm;
264: }
265:
266: /**
267: * Returns the tree table's header.
268: *
269: * @return the table header
270: */
271: public TableHeader getTableHeader() {
272: if (header == null) {
273: header = new TreeTableHeader(this );
274: }
275: return header;
276: }
277:
278: /**
279: * Returns <code>true</code> if vertical lines are enabled.
280: *
281: * @return the vertical line state
282: */
283: // public boolean getVeritcalLines() {
284: // return verticalLines;
285: // }
286: /**
287: * Returns the tree table's view.
288: *
289: * @return the view
290: */
291: protected TreeTableView getView() {
292: if (view == null) {
293: view = new TreeTableView();
294: }
295: return view;
296: }
297:
298: public void onBrowserEvent(Event event) {
299: super .onBrowserEvent(event);
300: int type = DOM.eventGetType(event);
301:
302: if (type == Event.ONSCROLL) {
303: int left = MyDOM.getScrollLeft(view.getScrollElement());
304: if (left == lastLeft) {
305: return;
306: }
307: lastLeft = left;
308: MyDOM.setLeft(header.getElement(), -left);
309: scrollTask.delay(400);
310: }
311: }
312:
313: /**
314: * Recalculates the ui based on the table's current size.
315: */
316: public void recalculate() {
317: if (isRendered()) {
318: // onResize(getWidth(), getHeight());
319: header.resizeColumns(false, true);
320: }
321: }
322:
323: /**
324: * Scrolls the item into view.
325: *
326: * @param item the item
327: */
328: public void scrollIntoView(TableItem item) {
329: MyDOM.scrollIntoView(item.getElement(),
330: view.getScrollElement(), false);
331: }
332:
333: /**
334: * Sets whether the column context menu is enabled. Initial value is
335: * <code>true</code>.
336: *
337: * @param enabled the enabled state
338: */
339: public void setColumnContextMenu(boolean enabled) {
340: this .disableColumnContextMenu = !enabled;
341: }
342:
343: /**
344: * Sets the tree table's header. Should only be called when providing a custom
345: * tree table header. Has no effect if called after the table has been
346: * rendered.
347: *
348: * @param header the table header
349: */
350: public void setTableHeader(TreeTableHeader header) {
351: if (!isRendered()) {
352: this .header = header;
353: }
354: }
355:
356: /**
357: * Sets whether cells should have have a horizontal border. Default value is
358: * <code>false</code>.
359: *
360: * @param show <code>true</code> to display horizontal borders
361: */
362: // public void setVerticalLines(boolean show) {
363: // this.verticalLines = show;
364: // }
365: /**
366: * Sets the tree table's view. Provides a way to provide specialized views.
367: * table views.
368: *
369: * @param view the view
370: */
371: public void setView(TreeTableView view) {
372: this .view = view;
373: }
374:
375: /**
376: * Sorts the tree table using the specified column index.
377: *
378: * @param index the column index
379: * @param direction the direction to sort (NONE, ASC, DESC)
380: */
381: public void sort(int index, int direction) {
382: // TODO Auto-generated method stub
383:
384: }
385:
386: protected void doAttachChildren() {
387: WidgetHelper.doAttach(header);
388: }
389:
390: protected void doDetachChildren() {
391: WidgetHelper.doDetach(header);
392: }
393:
394: protected String getRenderedValue(int column, Object value) {
395: TreeTableColumn col = (TreeTableColumn) cm.getColumn(column);
396: if (col.getRenderer() != null) {
397: return col.getRenderer().render(col.getID(), value);
398: } else {
399: if (value != null) {
400: return value.toString();
401: }
402: return null;
403: }
404: }
405:
406: protected void onRender() {
407: setElement(DOM.createDiv());
408: setStyleName("my-treetbl");
409:
410: if (getSelectionMode() == Style.MULTI) {
411: sm = new MultiSelectionModel();
412: } else {
413: sm = new TreeSelectionModel();
414: }
415:
416: this .sm.init(this );
417:
418: DOM.appendChild(getElement(), root.getElement());
419:
420: ((RootTreeTableItem) root).renderChildren();
421:
422: cm.setTable(this );
423: ((TreeTableItem) root).setValues(new String[getColumnCount()]);
424:
425: MyDOM.removeChildren(getElement());
426:
427: header = (TreeTableHeader) getTableHeader();
428: header.init(this );
429:
430: DOM.appendChild(getElement(), header.getElement());
431:
432: if (styleTemplate == null) {
433: Element style = DOM.createElement("style");
434: DOM
435: .setElementProperty(style, "id", getId()
436: + "-cols-style");
437: DOM.appendChild(MyDOM.getHead(), style);
438: styleTemplate = new StyleTemplate(style);
439: }
440:
441: for (int i = 0, n = cm.getColumnCount(); i < n; i++) {
442: TreeTableColumn c = (TreeTableColumn) cm.getColumn(i);
443: int w = cm.getWidthInPixels(c.getIndex());
444: styleTemplate.set("." + getId() + "-col-" + i, "width:" + w
445: + "px;");
446: }
447:
448: view = getView();
449: view.init(this );
450: view.render();
451:
452: disableTextSelection(true);
453:
454: sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS
455: | Event.KEYEVENTS | Event.ONSCROLL);
456: }
457:
458: protected void onResize(int width, int height) {
459: header.resizeColumns(false, true);
460: }
461:
462: protected void onShowContextMenu(int x, int y) {
463: super .onShowContextMenu(x, y);
464: getView().clearHoverStyles();
465: }
466:
467: /*
468: * (non-Javadoc)
469: *
470: * @see net.mygwt.ui.client.widget.tree.Tree#createRootItem()
471: */
472: protected void createRootItem() {
473: root = new RootTreeTableItem(this);
474: }
475:
476: }
|