1 /***
2 * Copyright (c) 2003 held jointly by the individual authors.
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; either version 2.1 of the License, or
7 * (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; with out even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software Foundation,
16 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17 *
18 * > http://www.gnu.org/copyleft/lesser.html
19 * > http://www.opensource.org/licenses/lgpl-license.php
20 */
21 package net.mlw.vlh.swing.support;
22
23 import java.awt.Color;
24 import java.awt.Component;
25 import java.awt.Graphics;
26 import java.awt.event.ActionEvent;
27 import java.awt.event.ActionListener;
28 import java.awt.event.MouseAdapter;
29 import java.awt.event.MouseEvent;
30 import java.awt.event.MouseListener;
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import javax.swing.Icon;
35 import javax.swing.JLabel;
36 import javax.swing.JTable;
37 import javax.swing.event.TableModelEvent;
38 import javax.swing.event.TableModelListener;
39 import javax.swing.table.AbstractTableModel;
40 import javax.swing.table.JTableHeader;
41 import javax.swing.table.TableCellRenderer;
42 import javax.swing.table.TableColumnModel;
43 import javax.swing.table.TableModel;
44
45 /***
46 * @author Matthew L. Wilson
47 * @version $Revision: 1.1 $ $Date: 2005/03/16 12:16:23 $
48 */
49 public class DelegateToServiceTableSorter extends AbstractTableModel
50 {
51 protected TableModel tableModel;
52
53 public static final int DESCENDING = -1;
54
55 public static final int NOT_SORTED = 0;
56
57 public static final int ASCENDING = 1;
58
59 private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
60
61 private JTableHeader tableHeader;
62
63 private MouseListener mouseListener;
64
65 private TableModelListener tableModelListener;
66
67 private List sortingColumns = new ArrayList();
68
69 public DelegateToServiceTableSorter()
70 {
71 this.mouseListener = new MouseHandler();
72 this.tableModelListener = new TableModelHandler();
73 }
74
75 public DelegateToServiceTableSorter(TableModel tableModel)
76 {
77 this();
78 setTableModel(tableModel);
79 }
80
81 public DelegateToServiceTableSorter(TableModel tableModel, JTableHeader tableHeader)
82 {
83 this();
84 setTableHeader(tableHeader);
85 setTableModel(tableModel);
86 }
87
88 private void clearSortingState()
89 {
90 }
91
92 public TableModel getTableModel()
93 {
94 return tableModel;
95 }
96
97 public void setTableModel(TableModel tableModel)
98 {
99 if (this.tableModel != null)
100 {
101 this.tableModel.removeTableModelListener(tableModelListener);
102 }
103
104 this.tableModel = tableModel;
105 if (this.tableModel != null)
106 {
107 this.tableModel.addTableModelListener(tableModelListener);
108 }
109
110 clearSortingState();
111 fireTableStructureChanged();
112 }
113
114 public JTableHeader getTableHeader()
115 {
116 return tableHeader;
117 }
118
119 public void setTableHeader(JTableHeader tableHeader)
120 {
121 if (this.tableHeader != null)
122 {
123 this.tableHeader.removeMouseListener(mouseListener);
124 TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
125 if (defaultRenderer instanceof SortableHeaderRenderer)
126 {
127 this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
128 }
129 }
130 this.tableHeader = tableHeader;
131 if (this.tableHeader != null)
132 {
133 this.tableHeader.addMouseListener(mouseListener);
134 this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
135 }
136 }
137
138 public boolean isSorting()
139 {
140 return sortingColumns.size() != 0;
141 }
142
143 private Directive getDirective(int column)
144 {
145 for (int i = 0; i < sortingColumns.size(); i++)
146 {
147 Directive directive = (Directive) sortingColumns.get(i);
148 if (directive.column == column)
149 {
150 return directive;
151 }
152 }
153 return EMPTY_DIRECTIVE;
154 }
155
156 public int getSortingStatus(int column)
157 {
158 return getDirective(column).direction;
159 }
160
161 private void sortingStatusChanged()
162 {
163 clearSortingState();
164 fireTableDataChanged();
165 if (tableHeader != null)
166 {
167 tableHeader.repaint();
168 }
169 }
170
171 public void setSortingStatus(int column, int status)
172 {
173 Directive directive = getDirective(column);
174 if (directive != EMPTY_DIRECTIVE)
175 {
176 sortingColumns.remove(directive);
177 }
178 if (status != NOT_SORTED)
179 {
180 sortingColumns.add(new Directive(column, status));
181 }
182 sortingStatusChanged();
183 }
184
185 protected Icon getHeaderRendererIcon(int column, int size)
186 {
187 Directive directive = getDirective(column);
188 if (directive == EMPTY_DIRECTIVE)
189 {
190 return null;
191 }
192 return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive));
193 }
194
195 private void cancelSorting()
196 {
197 sortingColumns.clear();
198 sortingStatusChanged();
199 }
200
201
202
203 public int getRowCount()
204 {
205 return (tableModel == null) ? 0 : tableModel.getRowCount();
206 }
207
208 public int getColumnCount()
209 {
210 return (tableModel == null) ? 0 : tableModel.getColumnCount();
211 }
212
213 public String getColumnName(int column)
214 {
215 return tableModel.getColumnName(column);
216 }
217
218 public Class getColumnClass(int column)
219 {
220 return tableModel.getColumnClass(column);
221 }
222
223 public boolean isCellEditable(int row, int column)
224 {
225 return tableModel.isCellEditable(row, column);
226 }
227
228 public Object getValueAt(int row, int column)
229 {
230 return tableModel.getValueAt(row, column);
231 }
232
233 public void setValueAt(Object aValue, int row, int column)
234 {
235 tableModel.setValueAt(aValue, row, column);
236 }
237
238 private ActionListener actionListener;
239
240 public void addActionListener(ActionListener actionListener)
241 {
242 this.actionListener = actionListener;
243 }
244
245 /***
246 * @return Returns the sortingColumns.
247 */
248 public List getSortingColumns()
249 {
250 return sortingColumns;
251 }
252
253
254
255 private class TableModelHandler implements TableModelListener
256 {
257 public void tableChanged(TableModelEvent e)
258 {
259
260 if (!isSorting())
261 {
262 clearSortingState();
263 fireTableChanged(e);
264 return;
265 }
266
267
268
269
270 if (e.getFirstRow() == TableModelEvent.HEADER_ROW)
271 {
272 cancelSorting();
273 fireTableChanged(e);
274 return;
275 }
276
277
278 clearSortingState();
279 fireTableDataChanged();
280 return;
281 }
282 }
283
284 private class MouseHandler extends MouseAdapter
285 {
286 public void mouseClicked(MouseEvent e)
287 {
288 JTableHeader h = (JTableHeader) e.getSource();
289 TableColumnModel columnModel = h.getColumnModel();
290 int viewColumn = columnModel.getColumnIndexAtX(e.getX());
291 int column = columnModel.getColumn(viewColumn).getModelIndex();
292 if (column != -1)
293 {
294 int status = getSortingStatus(column);
295 if (!e.isControlDown())
296 {
297 cancelSorting();
298 }
299
300
301 status = status + (e.isShiftDown() ? -1 : 1);
302 status = (status + 4) % 3 - 1;
303 setSortingStatus(column, status);
304 if (actionListener != null)
305 {
306 actionListener.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "sort", e.getModifiers()));
307 }
308 }
309 }
310 }
311
312 private static class Arrow implements Icon
313 {
314 private boolean descending;
315
316 private int size;
317
318 private int priority;
319
320 public Arrow(boolean descending, int size, int priority)
321 {
322 this.descending = descending;
323 this.size = size;
324 this.priority = priority;
325 }
326
327 public void paintIcon(Component c, Graphics g, int x, int y)
328 {
329 Color color = c == null ? Color.GRAY : c.getBackground();
330
331
332 int dx = (int) (size / 2 * Math.pow(0.8, priority));
333 int dy = descending ? dx : -dx;
334
335 y = y + 5 * size / 6 + (descending ? -dy : 0);
336 int shift = descending ? 1 : -1;
337 g.translate(x, y);
338
339
340 g.setColor(color.darker());
341 g.drawLine(dx / 2, dy, 0, 0);
342 g.drawLine(dx / 2, dy + shift, 0, shift);
343
344
345 g.setColor(color.brighter());
346 g.drawLine(dx / 2, dy, dx, 0);
347 g.drawLine(dx / 2, dy + shift, dx, shift);
348
349
350 if (descending)
351 {
352 g.setColor(color.darker().darker());
353 }
354 else
355 {
356 g.setColor(color.brighter().brighter());
357 }
358 g.drawLine(dx, 0, 0, 0);
359
360 g.setColor(color);
361 g.translate(-x, -y);
362 }
363
364 public int getIconWidth()
365 {
366 return size;
367 }
368
369 public int getIconHeight()
370 {
371 return size;
372 }
373 }
374
375 private class SortableHeaderRenderer implements TableCellRenderer
376 {
377 private TableCellRenderer tableCellRenderer;
378
379 public SortableHeaderRenderer(TableCellRenderer tableCellRenderer)
380 {
381 this.tableCellRenderer = tableCellRenderer;
382 }
383
384 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
385 int row, int column)
386 {
387 Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
388 if (c instanceof JLabel)
389 {
390 JLabel l = (JLabel) c;
391 l.setHorizontalTextPosition(JLabel.LEFT);
392 int modelColumn = table.convertColumnIndexToModel(column);
393 l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));
394 }
395 return c;
396 }
397 }
398
399 public static class Directive
400 {
401 private int column;
402
403 private int direction;
404
405 public Directive(int column, int direction)
406 {
407 this.column = column;
408 this.direction = direction;
409 }
410
411 /***
412 * @return Returns the column.
413 */
414 public int getColumn()
415 {
416 return column;
417 }
418
419 /***
420 * @return Returns the direction.
421 */
422 public int getDirection()
423 {
424 return direction;
425 }
426 }
427 }