View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans-Common.
5    *
6    * CosyBeans-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.components;
21  
22  import com.cosylab.gui.components.introspection.EditorsHelper;
23  import com.cosylab.gui.components.table.QueueTable;
24  import com.cosylab.gui.components.table.cells.DeviceCell;
25  import com.cosylab.gui.components.table.cells.DoubleCell;
26  import com.cosylab.gui.components.table.cells.LongCell;
27  import com.cosylab.gui.components.table.cells.LongEnumCell;
28  import com.cosylab.gui.components.table.cells.PatternCell;
29  import com.cosylab.gui.components.table.cells.StringCell;
30  import com.cosylab.gui.components.table.cells.TableCell;
31  import com.cosylab.gui.components.table.renderers.DateRenderer;
32  import com.cosylab.gui.components.table.renderers.DefaultTableCellRenderer;
33  import com.cosylab.gui.components.table.renderers.DeviceCellRenderer;
34  import com.cosylab.gui.components.table.renderers.DoubleCellEditor;
35  import com.cosylab.gui.components.table.renderers.DoubleCellRenderer;
36  import com.cosylab.gui.components.table.renderers.EmptyCellRenderer;
37  import com.cosylab.gui.components.table.renderers.LongCellEditor;
38  import com.cosylab.gui.components.table.renderers.LongCellRenderer;
39  import com.cosylab.gui.components.table.renderers.LongEnumCellEditor;
40  import com.cosylab.gui.components.table.renderers.LongEnumCellRenderer;
41  import com.cosylab.gui.components.table.renderers.PatternCellRenderer;
42  import com.cosylab.gui.components.table.renderers.StringCellRenderer;
43  
44  import java.awt.event.ActionEvent;
45  import java.awt.event.ActionListener;
46  import java.awt.event.MouseAdapter;
47  import java.awt.event.MouseEvent;
48  
49  import java.util.ArrayList;
50  import java.util.Date;
51  
52  import javax.swing.JCheckBoxMenuItem;
53  import javax.swing.JMenuItem;
54  import javax.swing.JPopupMenu;
55  import javax.swing.table.TableCellEditor;
56  import javax.swing.table.TableCellRenderer;
57  import javax.swing.table.TableColumn;
58  import javax.swing.table.TableColumnModel;
59  import javax.swing.table.TableModel;
60  
61  
62  /**
63   * <code>ObjectTable</code> includes number of usefull features over standard
64   * <code>JTable</code> for users, which try to create tabel for monitoring
65   * dynamic values. ObjectTable can be used same way as standard
66   * <code>JTable</code>.  Following features are implemented in this table:
67   * 
68   * <ul>
69   * <li>
70   * Performance improvement with table cells are randomly updated with high
71   * frequency. <code>com.cosylab.gui.components.table.QueueTable</code> is used
72   * for this purpose.
73   * </li>
74   * <li>
75   * Column hiding, user can select which columns will be displayed and which
76   * not.
77   * </li>
78   * <li>
79   * Renderers and editors are choosen for each cell individually based on cell
80   * class, rather then on column calass as in JTable.
81   * </li>
82   * <li>
83   * Renderers and editors are prepared for cells derived from
84   * <code>com.cosylab.gui.components.table.cells.TableCell</code>.
85   * </li>
86   * </ul>
87   * 
88   *
89   * @author <a href="mailto:ales.pucelj@cosylab.com">Ales Pucelj</a>
90   * @author <a href="mailto:igor.kriznar@cosylab.com">Igor Kriznar</a>
91   * @version $id$
92   */
93  public class ObjectTable extends QueueTable
94  {
95  	private static final long serialVersionUID = 1L;
96  	private ArrayList<TableColumn> hiddenColumns = new ArrayList<TableColumn>();
97  	private boolean allowColumnHide = true;
98  	private TableCellRenderer emptyCellRenderer = new EmptyCellRenderer();
99  	private boolean lastSortDescending = false;
100 	private int lastComlumnSorted = 1;
101 
102 	private class HeaderMouseListenerImpl extends MouseAdapter
103 	{
104 		/**
105 		 * Checks whether the event triggers popup.
106 		 *
107 		 * @param e MouseEvent
108 		 */
109 		private void performPopup(MouseEvent e)
110 		{
111 			if (e.isPopupTrigger()) {
112 				(new ColumnChooser()).showPopup(e);
113 				e.consume();
114 			}
115 		}
116 
117 		/**
118 		 * @see java.awt.event.MouseListener#mouseReleased(MouseEvent)
119 		 */
120 		public void mouseReleased(MouseEvent e)
121 		{
122 			performPopup(e);
123 		}
124 
125 		/**
126 		 * @see java.awt.event.MouseListener#mouseClicked(MouseEvent)
127 		 */
128 		public void mouseClicked(MouseEvent e)
129 		{
130 			performPopup(e);
131 		}
132 
133 		/**
134 		 * @see java.awt.event.MouseListener#mousePressed(MouseEvent)
135 		 */
136 		public void mousePressed(MouseEvent e)
137 		{
138 			performPopup(e);
139 		}
140 	}
141 
142 	private class ColumnChooser extends JPopupMenu
143 	{
144 		private static final long serialVersionUID = 1L;
145 
146 		/**
147 		 * Changes visibility of a column.
148 		 */
149 		private class ActionListenerImpl implements ActionListener
150 		{
151 			private int index;
152 			private ObjectTable table;
153 			private boolean newState;
154 
155 			/**
156 			 * Creates a new ActionListenerImpl object.
157 			 *
158 			 * @param mi Model index of column.
159 			 * @param ot ObjectTable.
160 			 * @param how If true, column will be shown, otherwise hidded.
161 			 */
162 			public ActionListenerImpl(int mi, ObjectTable ot, boolean how)
163 			{
164 				table = ot;
165 				index = mi;
166 				newState = how;
167 			}
168 
169 			/**
170 			 * Changes visibiliy of a column.
171 			 *
172 			 * @param e event.
173 			 */
174 			public void actionPerformed(ActionEvent e)
175 			{
176 				if (newState) {
177 					table.showColumn(index);
178 				} else {
179 					table.hideColumn(index);
180 				}
181 			}
182 		}
183 
184 		private int col;
185 
186 		/**
187 		 * Creates a new ColumnChooser object.
188 		 */
189 		public ColumnChooser()
190 		{
191 			ObjectTable owner = ObjectTable.this;
192 			TableModel model = owner.getModel();
193 
194 			if (model instanceof ObjectTableModel) {
195 				JMenuItem item = new JMenuItem("Sort ascending");
196 				item.addActionListener(new ActionListener() {
197 						public void actionPerformed(ActionEvent e)
198 						{
199 							TableModel model = getModel();
200 
201 							if (col > -1 && (model instanceof ObjectTableModel)) {
202 								((ObjectTableModel)model).getRowModel()
203 								.sortRows(col, false);
204 							}
205 
206 							lastSortDescending = false;
207 							lastComlumnSorted = col;
208 						}
209 					});
210 				add(item);
211 				item = new JMenuItem("Sort decending");
212 				item.addActionListener(new ActionListener() {
213 						public void actionPerformed(ActionEvent e)
214 						{
215 							TableModel model = getModel();
216 
217 							if (col > -1 && (model instanceof ObjectTableModel)) {
218 								((ObjectTableModel)model).getRowModel()
219 								.sortRows(col, true);
220 							}
221 
222 							lastSortDescending = true;
223 							lastComlumnSorted = col;
224 						}
225 					});
226 				add(item);
227 				add(new Separator());
228 			}
229 
230 			int n = model.getColumnCount();
231 
232 			for (int i = 0; i < n; i++) {
233 				JCheckBoxMenuItem item = new JCheckBoxMenuItem(model
234 					    .getColumnName(i));
235 
236 				boolean isHidden = (owner.findHiddenColumn(i) != null);
237 
238 				item.setSelected(!isHidden);
239 
240 				item.addActionListener(new ActionListenerImpl(i, owner, isHidden));
241 				add(item);
242 			}
243 		}
244 
245 		/**
246 		 * DOCUMENT ME!
247 		 *
248 		 * @param ev DOCUMENT ME!
249 		 */
250 		public void showPopup(MouseEvent ev)
251 		{
252 			col = getTableHeader().columnAtPoint(ev.getPoint());
253 
254 			if (col > -1) {
255 				col = getTableHeader().getColumnModel().getColumn(col)
256 					.getModelIndex();
257 			}
258 
259 			show(ev.getComponent(), ev.getX(), ev.getY());
260 		}
261 	}
262 
263 	/**
264 	 * Constructor for ObjectTable.
265 	 */
266 	public ObjectTable()
267 	{
268 		super();
269 		getTableHeader().addMouseListener(new HeaderMouseListenerImpl());
270 	}
271 
272 	/**
273 	 * Constructor for ObjectTable.
274 	 *
275 	 * @param dm
276 	 */
277 	public ObjectTable(TableModel dm)
278 	{
279 		super(dm);
280 		getTableHeader().addMouseListener(new HeaderMouseListenerImpl());
281 	}
282 
283 	/**
284 	 * Constructor for ObjectTable.
285 	 *
286 	 * @param dm
287 	 * @param cm
288 	 */
289 	public ObjectTable(TableModel dm, TableColumnModel cm)
290 	{
291 		super(dm, cm);
292 		getTableHeader().addMouseListener(new HeaderMouseListenerImpl());
293 	}
294 
295 	/**
296 	 * Creates default renderers as defined in renderers package. This only
297 	 * provides basic rendering support. Custom renderers should be added for
298 	 * displaying more complex types.
299 	 *
300 	 * @see javax.swing.JTable#createDefaultRenderers()
301 	 */
302 	@SuppressWarnings("unchecked")
303 	protected void createDefaultRenderers()
304 	{
305 		super.createDefaultRenderers();
306 
307 		defaultRenderersByColumnClass.put(DoubleCell.class,
308 		    new DoubleCellRenderer());
309 
310 		defaultRenderersByColumnClass.put(LongCell.class, new LongCellRenderer());
311 
312 		defaultRenderersByColumnClass.put(LongEnumCell.class,
313 		    new LongEnumCellRenderer());
314 
315 		defaultRenderersByColumnClass.put(StringCell.class,
316 		    new StringCellRenderer());
317 
318 		defaultRenderersByColumnClass.put(PatternCell.class,
319 		    new PatternCellRenderer());
320 
321 		defaultRenderersByColumnClass.put(DeviceCell.class,
322 		    new DeviceCellRenderer());
323 
324 		defaultRenderersByColumnClass.put(Date.class, new DateRenderer());
325 
326 		defaultRenderersByColumnClass.put(TableCell.class,
327 		    new DefaultTableCellRenderer());
328 
329 		//defaultRenderersByColumnClass.remove(Boolean.class);
330 	}
331 
332 	/**
333 	 * Creates default editors as defined in editors package. This only
334 	 * provides basic editing support. Custom editors should be added for
335 	 * editing more complex types.
336 	 *
337 	 * @see javax.swing.JTable#createDefaultEditors()
338 	 */
339 	@SuppressWarnings("unchecked")
340 	protected void createDefaultEditors()
341 	{
342 		super.createDefaultEditors();
343 
344 		defaultEditorsByColumnClass.put(DoubleCell.class, new DoubleCellEditor());
345 
346 		defaultEditorsByColumnClass.put(LongCell.class, new LongCellEditor());
347 
348 		defaultEditorsByColumnClass.put(LongEnumCell.class,
349 		    new LongEnumCellEditor());
350 
351 		defaultEditorsByColumnClass.put(DeviceCell.class,
352 		    new DeviceCellRenderer());
353 	}
354 
355 	/**
356 	 * Trys to return cell renderer on cell class, if this fails then try to
357 	 * find rendere by default JTable procedure.
358 	 *
359 	 * @see javax.swing.JTable#getCellRenderer(int, int)
360 	 */
361 	public TableCellRenderer getCellRenderer(int row, int column)
362 	{
363         TableColumn tableColumn = getColumnModel().getColumn(column);
364         
365         // renderer defined for the column
366         TableCellRenderer renderer = tableColumn.getCellRenderer();
367         
368         if (renderer!=null) {
369         	return renderer;
370         }
371         
372         // checks for default renderer only if model has specified some particular
373         // column class, Object.class is assumed to be default choice, thus ignored.
374 
375         Class cl= getColumnClass(column);
376         if (cl!=Object.class) {
377         	renderer= getDefaultRenderer(cl);
378         }
379         
380         if (renderer!=null) {
381         	return renderer;
382         }
383 
384         // tries to find renderer based on cell value class
385         int c = convertColumnIndexToModel(column);
386 		Object cell = getModel().getValueAt(row, c);
387 
388 		if (cell == null) {
389 			return emptyCellRenderer;
390 		}
391 
392 		renderer = getDefaultRenderer(cell.getClass());
393 
394 		// tryes to use helper class to produce simple renderer for special class
395 		if (renderer == null) {
396 			renderer = EditorsHelper.getTableCellRendererForClass(cell.getClass());
397 		}
398 
399 		if (renderer == null) {
400 			renderer = super.getCellRenderer(row, column);
401 		}
402 
403 		return renderer;
404 	}
405 
406 	/**
407 	 * Trys to return cell editor on cell class, if this fails then try to find
408 	 * editor by default JTable procedure.
409 	 *
410 	 * @see javax.swing.JTable#getCellRenderer(int, int)
411 	 */
412 	public TableCellEditor getCellEditor(int row, int column)
413 	{
414         TableColumn tableColumn = getColumnModel().getColumn(column);
415         
416         // renderer defined for the column
417         TableCellEditor editor = tableColumn.getCellEditor();
418         
419         if (editor!=null) {
420         	return editor;
421         }
422         
423         // checks for default renderer only if model has specified some particular
424         // column class, Object.class is assumed to be default choice, thus ignored.
425 
426         Class cl= getColumnClass(column);
427         if (cl!=Object.class) {
428         	editor= getDefaultEditor(cl);
429         }
430         
431         if (editor!=null) {
432         	return editor;
433         }
434 
435         // tries to find renderer based on cell value class
436         int c = convertColumnIndexToModel(column);
437 		Object cell = getModel().getValueAt(row, c);
438 
439 		if (cell == null) {
440 			return super.getCellEditor(row, column);
441 		}
442 
443 		editor = getDefaultEditor(cell.getClass());
444 
445 		// tryes to use helper class to produce simple renderer for special class
446 		if (editor == null) {
447 			editor = EditorsHelper.getTableCellEditorForClass(cell.getClass());
448 		}
449 
450 		if (editor == null) {
451 			editor = super.getCellEditor(row, column);
452 		}
453 
454 		return editor;
455 
456 	}
457 
458 	protected boolean validColumnIndex(int index)
459 	{
460 		return ((index >= 0) && (index < getModel().getColumnCount()));
461 	}
462 
463 	/**
464 	 * Hides column at specified model index.
465 	 *
466 	 * @param index of column to hide.
467 	 */
468 	public void hideColumn(int index)
469 	{
470 		if (validColumnIndex(index) && allowColumnHide) {
471 			TableColumn tc = getColumn(getModel().getColumnName(index));
472 			getColumnModel().removeColumn(tc);
473 			hiddenColumns.add(tc);
474 		}
475 	}
476 
477 	/**
478 	 * Helper method to find hidden column for given model index.
479 	 *
480 	 * @param modelIndex of the column.
481 	 *
482 	 * @return Instance of TableColumn or null if not found.
483 	 */
484 	protected TableColumn findHiddenColumn(int modelIndex)
485 	{
486 		int n = hiddenColumns.size();
487 
488 		for (int i = 0; i < n; i++) {
489 			TableColumn tc = hiddenColumns.get(i);
490 
491 			if (tc.getModelIndex() == modelIndex) {
492 				return tc;
493 			}
494 		}
495 
496 		return null;
497 	}
498 
499 	/**
500 	 * Shows column at given model index.
501 	 *
502 	 * @param index index of column to show.
503 	 */
504 	public void showColumn(int index)
505 	{
506 		if (validColumnIndex(index) && allowColumnHide) {
507 			TableColumn tc = findHiddenColumn(index);
508 
509 			if (tc == null) {
510 				return;
511 			}
512 
513 			getColumnModel().addColumn(tc);
514 			hiddenColumns.remove(tc);
515 			getColumnModel().moveColumn(getColumnCount() - 1, index);
516 		}
517 	}
518 
519 	/**
520 	 * Shows all hidden columns.
521 	 */
522 	protected synchronized void showHiddenColumns()
523 	{
524 		for (int i = 0; i < hiddenColumns.size(); i++) {
525 			getColumnModel().addColumn(hiddenColumns.get(i));
526 		}
527 
528 		hiddenColumns.clear();
529 	}
530 
531 	/**
532 	 * Returns whether table allows hiding of columns.
533 	 *
534 	 * @return true if hiding is allowed.
535 	 */
536 	public boolean getAllowColumnHide()
537 	{
538 		return allowColumnHide;
539 	}
540 
541 	/**
542 	 * Sets whether table allows hiding of columns.
543 	 *
544 	 * @param value True to allow hiding of columns.
545 	 */
546 	public void setAllowColumnHide(boolean value)
547 	{
548 		allowColumnHide = value;
549 	}
550 
551 	/* (non-Javadoc)
552 	 * @see javax.swing.JTable#createDefaultColumnsFromModel()
553 	 */
554 	public void createDefaultColumnsFromModel()
555 	{
556 		if (hiddenColumns != null) {
557 			hiddenColumns.clear();
558 		}
559 
560 		super.createDefaultColumnsFromModel();
561 	}
562 
563 	/**
564 	 * Repeats the sorting that was applied last.
565 	 *
566 	 */
567 	public void repeatLastSort()
568 	{
569 		TableModel model = getModel();
570 
571 		if (lastComlumnSorted > -1 && (model instanceof ObjectTableModel)) {
572 			((ObjectTableModel)model).getRowModel().sortRows(lastComlumnSorted,
573 			    lastSortDescending);
574 		}
575 	}
576 }
577 
578 /* __oOo__ */