001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018: package org.columba.mail.gui.table;
019:
020: import javax.swing.JOptionPane;
021: import javax.swing.table.TableCellRenderer;
022: import javax.swing.table.TableColumn;
023: import javax.swing.tree.TreePath;
024:
025: import org.columba.api.plugin.IExtension;
026: import org.columba.api.plugin.IExtensionHandler;
027: import org.columba.api.plugin.PluginHandlerNotFoundException;
028: import org.columba.core.logging.Logging;
029: import org.columba.core.plugin.PluginManager;
030: import org.columba.mail.gui.table.model.HeaderTableModel;
031: import org.columba.mail.gui.table.model.MessageNode;
032: import org.columba.mail.gui.table.model.TableModelSorter;
033: import org.columba.mail.gui.table.plugins.BasicHeaderRenderer;
034: import org.columba.mail.gui.table.plugins.BasicRenderer;
035: import org.columba.mail.gui.table.plugins.BooleanHeaderRenderer;
036: import org.columba.mail.plugin.IExtensionHandlerKeys;
037: import org.columba.mail.resourceloader.MailImageLoader;
038: import org.frapuccino.treetable.CustomTreeTableCellRenderer;
039: import org.frapuccino.treetable.TreeTable;
040:
041: /**
042: * This widget is a mix between a JTable and a JTree ( we need the JTree for the
043: * Threaded viewing of mailing lists )
044: *
045: * @version 0.9.1
046: * @author fdietz
047: */
048: public class TableView extends TreeTable {
049:
050: private HeaderTableModel headerTableModel;
051:
052: private IExtensionHandler handler;
053:
054: private TableModelSorter sorter;
055:
056: private int defaultRowHeight;
057:
058: public TableView(HeaderTableModel headerTableModel,
059: TableModelSorter sorter) {
060: super ();
061:
062: this .sorter = sorter;
063: this .headerTableModel = headerTableModel;
064:
065: defaultRowHeight = getRowHeight();
066:
067: setModel(headerTableModel);
068:
069: // load plugin handler used for the columns
070: try {
071: handler = PluginManager
072: .getInstance()
073: .getExtensionHandler(
074: IExtensionHandlerKeys.ORG_COLUMBA_MAIL_TABLERENDERER);
075: } catch (PluginHandlerNotFoundException ex) {
076: ex.printStackTrace();
077: }
078:
079: getTree().setCellRenderer(new SubjectTreeRenderer(this ));
080:
081: getTree().setLargeModel(true);
082: }
083:
084: public boolean getScrollableTracksViewportHeight() {
085: return getPreferredSize().height < getParent().getHeight();
086: }
087:
088: public void resetRowHeight() {
089: setRowHeight(defaultRowHeight);
090: }
091:
092: /**
093: * Enable/Disable tree renderer for the subject column.
094: * <p>
095: * Note, that this works because {@link TreeTable}sets its
096: * {@link CustomTreeTableCellRenderer}as default renderer for this class.
097: * <br>
098: * When calling TableModel.getColumnClass(column), the model returns
099: * CustomTreeTableCellRenderer.class, if the threaded- view is enabled.
100: * <p>
101: * JTable automatically falls back to the default renderers, if no custom
102: * renderer is applied. <br>
103: * For this reason, we just remove the custom cell renderer for the
104: * "Subject" column.
105: *
106: * @param b
107: * if true, enable tree renderer. False, otherwise
108: */
109: public void enableThreadedView(boolean b) {
110: if (b) {
111: TableColumn tc = null;
112: tc = getColumn("Subject");
113:
114: // disable subject column renderer, use tree-cellrenderer instead
115: tc.setCellRenderer(null);
116:
117: // tc.setCellEditor(new CustomTreeTableCellEditor());
118: } else {
119: TableColumn tc = null;
120: try {
121: tc = getColumn("Subject");
122: // change subject column renderer back to default
123: tc
124: .setCellRenderer(new BasicRenderer(
125: "columba.subject"));
126: } catch (IllegalArgumentException e) {
127:
128: }
129:
130: }
131: }
132:
133: /**
134: * Create table column using plugin extension point
135: * <b>org.columba.mail.tablerenderer </b>.
136: *
137: * @param name
138: * name of plugin ID
139: * @param size
140: * size of table column
141: * @return table column object
142: */
143: public TableColumn createTableColumn(String name, int size) {
144: TableColumn c = new TableColumn();
145:
146: // set name of column
147: c.setHeaderValue(name);
148: c.setIdentifier(name);
149:
150: TableCellRenderer r = null;
151:
152: if (handler.exists(name)) {
153: // load plugin
154: try {
155: IExtension extension = handler.getExtension(name);
156: r = (TableCellRenderer) extension
157: .instanciateExtension(null);
158: } catch (Exception e) {
159: if (Logging.DEBUG) {
160: e.printStackTrace();
161: }
162:
163: JOptionPane.showMessageDialog(this ,
164: "Error while loading column: " + name + "\n"
165: + e.getMessage());
166: }
167: }
168:
169: if (r == null) {
170: // no specific renderer found
171: // -> use default renderer
172: r = new BasicRenderer(name);
173:
174: registerRenderer(c, name, r, new BasicHeaderRenderer(name,
175: sorter), size, false);
176: } else {
177: IExtension extension = handler.getExtension(name);
178:
179: String image = extension.getMetadata().getAttribute("icon");
180: String fixed = extension.getMetadata().getAttribute("size");
181: boolean lockSize = false;
182:
183: if (fixed != null) {
184: if (fixed.equals("fixed")) {
185: size = 23;
186: lockSize = true;
187: }
188: }
189:
190: if (lockSize) {
191: registerRenderer(c, name, r, new BooleanHeaderRenderer(
192: MailImageLoader.getSmallIcon(image)), size,
193: lockSize);
194: } else {
195: registerRenderer(c, name, r, new BasicHeaderRenderer(
196: name, sorter), size, lockSize);
197: }
198: }
199:
200: return c;
201: }
202:
203: /**
204: * Set properties of this column.
205: *
206: * @param tc
207: * table column
208: * @param name
209: * name of table column
210: * @param cell
211: * cell renderer
212: * @param header
213: * header renderer
214: * @param size
215: * width of column
216: * @param lockSize
217: * is this a fixed size column?
218: */
219: protected void registerRenderer(TableColumn tc, String name,
220: TableCellRenderer cell, TableCellRenderer header, int size,
221: boolean lockSize) {
222: if (tc == null) {
223: return;
224: }
225:
226: // this is a hack for the multiline column
227: if (name.equals("MultiLine")) {
228: setRowHeight(getRowHeight() * 2);
229: }
230:
231: if (cell != null) {
232: tc.setCellRenderer(cell);
233: }
234:
235: if (header != null) {
236: tc.setHeaderRenderer(header);
237: }
238:
239: if (lockSize) {
240: tc.setMaxWidth(size);
241: tc.setMinWidth(size);
242: } else {
243: // Logging.log.info("setting size =" + size);
244: tc.setPreferredWidth(size);
245: }
246: }
247:
248: /**
249: * Get selected message node.
250: *
251: * @return selected message node
252: */
253: public MessageNode getSelectedNode() {
254: MessageNode node = (MessageNode) getTree()
255: .getLastSelectedPathComponent();
256:
257: return node;
258: }
259:
260: /**
261: * Get array of selected message nodes.
262: *
263: * @return arrary of selected message nodes
264: */
265: public MessageNode[] getSelectedNodes() {
266: int[] rows = null;
267: MessageNode[] nodes = null;
268:
269: rows = getSelectedRows();
270: nodes = new MessageNode[rows.length];
271:
272: for (int i = 0; i < rows.length; i++) {
273: TreePath treePath = getTree().getPathForRow(rows[i]);
274:
275: if (treePath == null) {
276: continue;
277: }
278:
279: nodes[i] = (MessageNode) treePath.getLastPathComponent();
280: }
281:
282: return nodes;
283: }
284:
285: /**
286: * Get message node with UID
287: *
288: * @param uid
289: * UID of message node
290: *
291: * @return message node
292: */
293: public MessageNode getMessagNode(Object uid) {
294: return headerTableModel.getMessageNode(uid);
295: }
296:
297: /**
298: * Select first row and make it visible.
299: *
300: * @return uid of selected row
301: */
302: public Object selectFirstRow() {
303:
304: Object uid = null;
305:
306: // if there are entries in the table
307: if (getRowCount() > 0) {
308: // changing the selection to the first row
309: changeSelection(0, 0, true, false);
310:
311: // getting the node
312: MessageNode selectedNode = (MessageNode) getValueAt(0, 0);
313:
314: // and getting the uid for this node
315: uid = selectedNode.getUid();
316:
317: // scrolling to the first row
318: scrollRectToVisible(getCellRect(0, 0, false));
319: // @author: fdietz never request focus
320: //requestFocus();
321:
322: return uid;
323: }
324:
325: return null;
326: }
327:
328: /**
329: * Select last row and make it visible
330: *
331: * @return uid of selected row
332: */
333: public Object selectLastRow() {
334:
335: Object uid = null;
336:
337: // if there are entries in the table
338: if (getRowCount() > 0) {
339: // changing the selection to the first row
340: changeSelection(getRowCount() - 1, 0, true, false);
341:
342: // getting the node
343: MessageNode selectedNode = (MessageNode) getValueAt(
344: getRowCount() - 1, 0);
345:
346: // and getting the uid for this node
347: uid = selectedNode.getUid();
348:
349: // scrolling to the first row
350: scrollRectToVisible(getCellRect(getRowCount() - 1, 0, false));
351:
352: // @author: fdietz never request focus
353: //requestFocus();
354:
355: return uid;
356: }
357:
358: return null;
359: }
360:
361: /**
362: * Overwritten, because selectAll doesn't also select the nodes of the
363: * underlying JTree, which aren't expanded and therefore not visible.
364: * <p>
365: * Go through all nodes and expand them. Afterwards select all rows in the
366: * JTable.
367: *
368: * @see javax.swing.JTable#selectAll()
369: */
370: public void selectAll() {
371: // expand all rows
372: for (int i = 0; i < getRowCount(); i++) {
373: TreePath path = getTree().getPathForRow(i);
374: getTree().expandPath(path);
375: }
376: // select all rows
377: super .selectAll();
378: }
379:
380: /**
381: * Scroll table to row and request focus.
382: *
383: * @param row
384: * selected row
385: */
386: public void makeRowVisible(int row) {
387: scrollRectToVisible(getCellRect(row, 0, false));
388:
389: // @author: fdietz never request focus
390: //requestFocus();
391: }
392:
393: /**
394: * Change the selection to the specified row
395: *
396: * @param row
397: * row to selected
398: */
399: public void selectRow(int row) {
400:
401: if (getRowCount() > 0) {
402: if (row < 0) {
403: row = 0;
404: }
405:
406: if (row >= getRowCount()) {
407: row = getRowCount() - 1;
408: }
409:
410: // changing the selection to the specified row
411: // changeSelection(row, 0, true, false);
412: changeSelection(row, 0, false, false);
413:
414: makeRowVisible(row);
415: /*
416: * // scrolling to the first row
417: * scrollRectToVisible(getCellRect(row, 0, false)); requestFocus();
418: */
419: }
420: }
421:
422: /**
423: * Yes, this was overwritten on purpose. Updating the table-model (swing's
424: * internals - not Columba related ) always triggers an additional call to
425: * clearSelection. This was the easiest place to circumvent this behaviour.
426: *
427: * @see javax.swing.JTable#clearSelection()
428: */
429: public void clearSelection() {
430: // don't clear selection
431: }
432: }
|