001: package net.sourceforge.squirrel_sql.fw.gui;
002:
003: /*
004: * Copyright (C) 2002 Johan Compagner
005: * jcompagner@j-com.nl
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: import java.awt.*;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.event.MouseAdapter;
024: import java.awt.event.MouseEvent;
025: import java.awt.event.MouseMotionListener;
026: import java.util.prefs.Preferences;
027:
028: import javax.swing.*;
029: import javax.swing.event.*;
030: import javax.swing.table.JTableHeader;
031: import javax.swing.table.TableCellRenderer;
032: import javax.swing.table.TableColumn;
033: import javax.swing.table.TableModel;
034:
035: import net.sourceforge.squirrel_sql.fw.resources.LibraryResources;
036: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
037: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
038: import net.sourceforge.squirrel_sql.fw.datasetviewer.RowNumberTableColumn;
039: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.SquirrelTableCellRenderer;
040:
041: /**
042: * @version $Id: ButtonTableHeader.java,v 1.8 2006/09/16 21:08:43 gerdwagner Exp $
043: * @author Johan Compagner
044: */
045: public class ButtonTableHeader extends JTableHeader {
046: /** Logger for this class. */
047: private static ILogger s_log = LoggerController
048: .createLogger(ButtonTableHeader.class);
049:
050: private static final String PREF_KEY_ALWAYS_ADOPT_ALL_COLUMN_HEADERS = "Squirrel.alwaysAdoptAllColumnHeaders";
051:
052: /** Icon for "Sorted ascending". */
053: private static Icon s_ascIcon;
054:
055: /** Icon for "Sorted descending". */
056: private static Icon s_descIcon;
057:
058: /** Listens for changes in the underlying data. */
059: private TableDataListener _dataListener = new TableDataListener();
060:
061: /** If <TT>true</TT> then the mouse button is currently pressed. */
062: private boolean _pressed;
063:
064: /**
065: * If <TT>true</TT> then the mouse is being dragged. This is only relevant
066: * while the mouse is pressed.
067: */
068: private boolean _dragged;
069:
070: /**
071: * if <tt>_pressed</tt> is <tt>true</tt> then this is the physical column
072: * that the mouse was pressed in.
073: */
074: private int _pressedColumnIdx;
075:
076: /** Icon for the currently sorted column. */
077: private Icon _currentSortedColumnIcon;
078:
079: /** Physical (as opposed to model) index of the currently sorted column. */
080: private int _currentlySortedColumnIdx = -1;
081:
082: static {
083: try {
084: LibraryResources rsrc = new LibraryResources();
085: s_descIcon = rsrc
086: .getIcon(LibraryResources.IImageNames.TABLE_DESCENDING);
087: s_ascIcon = rsrc
088: .getIcon(LibraryResources.IImageNames.TABLE_ASCENDING);
089: } catch (Exception ex) {
090: s_log.error("Error retrieving icons", ex);
091: }
092: }
093:
094: /**
095: * Constructor for ButtonTableHeader.
096: */
097: public ButtonTableHeader() {
098: super ();
099: _pressed = false;
100: _dragged = false;
101: _pressedColumnIdx = -1;
102:
103: setDefaultRenderer(new ButtonTableRenderer(getFont()));
104:
105: HeaderListener hl = new HeaderListener();
106: addMouseListener(hl);
107: addMouseMotionListener(hl);
108: }
109:
110: public void setTable(JTable table) {
111: JTable oldTable = getTable();
112: if (oldTable != null) {
113: Object obj = oldTable.getModel();
114: if (obj instanceof SortableTableModel) {
115: SortableTableModel model = (SortableTableModel) obj;
116: model.getActualModel().removeTableModelListener(
117: _dataListener);
118: }
119: }
120:
121: super .setTable(table);
122:
123: if (table != null) {
124: Object obj = table.getModel();
125: if (obj instanceof SortableTableModel) {
126: SortableTableModel model = (SortableTableModel) obj;
127: model.getActualModel().addTableModelListener(
128: _dataListener);
129: }
130: }
131: _currentSortedColumnIcon = null;
132: _currentlySortedColumnIdx = -1;
133: }
134:
135: // SS: Display complete column header as tooltip if the column isn't wide enough to display it
136: public String getToolTipText(MouseEvent e) {
137: int col = columnAtPoint(e.getPoint());
138: //int modelCol = getTable().convertColumnIndexToModel(col);
139: String retStr = null;
140:
141: if (col >= 0) {
142: TableColumn tcol = getColumnModel().getColumn(col);
143: int colWidth = tcol.getWidth();
144: TableCellRenderer h = tcol.getHeaderRenderer();
145:
146: if (h == null) {
147: h = getDefaultRenderer();
148: }
149:
150: Component c = h.getTableCellRendererComponent(table, tcol
151: .getHeaderValue(), false, false, -1, col);
152:
153: int prefWidth = c.getPreferredSize().width;
154: if (prefWidth > colWidth) {
155: retStr = tcol.getHeaderValue().toString();
156: }
157: }
158: return retStr;
159: }
160:
161: /**
162: * @return The currently sorted column index. If no column is sorted -1.
163: */
164: public int getCurrentlySortedColumnIdx() {
165: return _currentlySortedColumnIdx;
166: }
167:
168: /**
169: *
170: * @return The direction of the currently sorted column. If no column is sorted false.
171: */
172: public boolean isAscending() {
173: return _currentSortedColumnIcon == s_ascIcon;
174: }
175:
176: public void columnIndexWillBeRemoved(int colIx) {
177: if (colIx < _currentlySortedColumnIdx) {
178: --_currentlySortedColumnIdx;
179: } else if (colIx == _currentlySortedColumnIdx) {
180: _currentlySortedColumnIdx = -1;
181: }
182: }
183:
184: public void columnIndexWillBeAdded(int colIx) {
185: if (colIx <= _currentlySortedColumnIdx) {
186: ++_currentlySortedColumnIdx;
187: }
188: }
189:
190: public void adoptAllColWidths(boolean includeColHeaders) {
191: for (int i = 0; i < getTable().getColumnModel()
192: .getColumnCount(); ++i) {
193: adoptColWidth(i, includeColHeaders);
194: }
195: }
196:
197: public static boolean isAlwaysAdoptAllColWidths() {
198: return Preferences.userRoot().getBoolean(
199: PREF_KEY_ALWAYS_ADOPT_ALL_COLUMN_HEADERS, false);
200: }
201:
202: public static void setAlwaysAdoptAllColWidths(boolean b) {
203: Preferences.userRoot().putBoolean(
204: PREF_KEY_ALWAYS_ADOPT_ALL_COLUMN_HEADERS, b);
205: }
206:
207: public void initColWidths() {
208: if (isAlwaysAdoptAllColWidths()) {
209: SwingUtilities.invokeLater(new Runnable() {
210: public void run() {
211: adoptAllColWidths(true);
212: }
213: });
214: }
215: }
216:
217: private final class TableDataListener implements TableModelListener {
218: public void tableChanged(TableModelEvent evt) {
219: _currentSortedColumnIcon = null;
220: _currentlySortedColumnIdx = -1;
221: }
222:
223: }
224:
225: private void adoptColWidth(int colIx, boolean includeColHeaders) {
226: int modelIx = getTable().convertColumnIndexToModel(colIx);
227:
228: int rowCount = getTable().getModel().getRowCount();
229:
230: int newWidth = 20;
231:
232: if (includeColHeaders) {
233: TableColumn column = getTable().getColumnModel().getColumn(
234: colIx);
235: TableCellRenderer headerRenderer = column
236: .getHeaderRenderer();
237: if (null == headerRenderer) {
238: headerRenderer = getTable().getTableHeader()
239: .getDefaultRenderer();
240: }
241:
242: Component headerComp = headerRenderer
243: .getTableCellRendererComponent(getTable(), null,
244: false, false, 0, colIx);
245: FontMetrics headerFontMetrics = headerComp
246: .getFontMetrics(headerComp.getFont());
247:
248: Rectangle2D bounds = headerFontMetrics
249: .getStringBounds("" + column.getHeaderValue(),
250: headerComp.getGraphics());
251:
252: newWidth = Math.max(newWidth, bounds.getBounds().width);
253: }
254:
255: if (0 == rowCount) {
256: if (includeColHeaders) {
257: getColumnModel().getColumn(colIx).setPreferredWidth(
258: newWidth + 10);
259: //getTable().doLayout();
260: }
261:
262: return;
263: }
264:
265: TableCellRenderer cellRenderer = getTable().getCellRenderer(0,
266: colIx);
267: Component cellComp = cellRenderer
268: .getTableCellRendererComponent(getTable(), null, false,
269: false, rowCount - 1, colIx);
270: FontMetrics cellFontMetrics = cellComp.getFontMetrics(cellComp
271: .getFont());
272:
273: for (int i = 0; i < rowCount; i++) {
274: Object value = getTable().getModel().getValueAt(i, modelIx);
275:
276: String stringVal = "";
277: if (getTable().getCellRenderer(i, colIx) instanceof SquirrelTableCellRenderer) {
278: stringVal += ((SquirrelTableCellRenderer) getTable()
279: .getCellRenderer(i, colIx)).renderValue(value);
280: } else {
281: stringVal += value;
282: }
283:
284: Rectangle2D bounds = cellFontMetrics.getStringBounds(
285: stringVal, cellComp.getGraphics());
286:
287: newWidth = Math.max(newWidth, bounds.getBounds().width);
288: }
289:
290: getColumnModel().getColumn(colIx).setPreferredWidth(
291: newWidth + 10);
292: //getTable().doLayout();
293: }
294:
295: class HeaderListener extends MouseAdapter implements
296: MouseMotionListener {
297: /*
298: * @see MouseListener#mousePressed(MouseEvent)
299: */
300: public void mousePressed(MouseEvent e) {
301: if (getCursor().getType() == Cursor.E_RESIZE_CURSOR) {
302: return;
303: }
304:
305: _pressed = true;
306: if (RowNumberTableColumn.ROW_NUMBER_MODEL_INDEX == table
307: .convertColumnIndexToModel(columnAtPoint(e
308: .getPoint()))) {
309: return;
310: }
311:
312: _pressedColumnIdx = columnAtPoint(e.getPoint());
313: repaint();
314: }
315:
316: public void mouseClicked(MouseEvent e) {
317: if (2 == e.getClickCount()
318: && getCursor().getType() == Cursor.E_RESIZE_CURSOR) {
319: int colIx = columnAtPoint(e.getPoint());
320: Rectangle headerRect = getHeaderRect(colIx);
321:
322: int distToColBegin = e.getPoint().x - headerRect.x;
323: int distToColEnd = headerRect.x + headerRect.width
324: - e.getPoint().x;
325: if (distToColBegin < distToColEnd && 0 < colIx
326: && colIx < getColumnModel().getColumnCount()) {
327: --colIx;
328: }
329:
330: adoptColWidth(colIx, true);
331: }
332: }
333:
334: /*
335: * @see MouseListener#mouseReleased(MouseEvent)
336: */
337: public void mouseReleased(MouseEvent e) {
338: if (getCursor().getType() == Cursor.E_RESIZE_CURSOR) {
339: return;
340: }
341:
342: if (RowNumberTableColumn.ROW_NUMBER_MODEL_INDEX == table
343: .convertColumnIndexToModel(columnAtPoint(e
344: .getPoint()))) {
345: _pressed = false;
346: _dragged = false;
347: return;
348: }
349:
350: _pressed = false;
351: if (!_dragged) {
352: _currentSortedColumnIcon = null;
353: int column = getTable().convertColumnIndexToModel(
354: _pressedColumnIdx);
355: TableModel tm = table.getModel();
356:
357: if (column > -1 && column < tm.getColumnCount()
358: && tm instanceof SortableTableModel) {
359: ((SortableTableModel) tm).sortByColumn(column);
360: if (((SortableTableModel) tm).isSortedAscending()) {
361: _currentSortedColumnIcon = s_ascIcon;
362: } else {
363: _currentSortedColumnIcon = s_descIcon;
364: }
365: _currentlySortedColumnIdx = _pressedColumnIdx;
366: }
367: repaint();
368: }
369: _dragged = false;
370: }
371:
372: /*
373: * @see MouseMotionListener#mouseDragged(MouseEvent)
374: */
375: public void mouseDragged(MouseEvent e) {
376: _dragged = true;
377: if (_pressed) {
378: _currentSortedColumnIcon = null;
379: _currentlySortedColumnIdx = -1;
380: _pressed = false;
381: repaint();
382: }
383: }
384:
385: /*
386: * @see MouseMotionListener#mouseMoved(MouseEvent)
387: */
388: public void mouseMoved(MouseEvent e) {
389: _dragged = false;
390: }
391:
392: }
393:
394: protected class ButtonTableRenderer implements TableCellRenderer {
395: JButton _buttonRaised;
396: JButton _buttonLowered;
397:
398: ButtonTableRenderer(Font font) {
399: _buttonRaised = new JButton();
400: _buttonRaised.setMargin(new Insets(0, 0, 0, 0));
401: _buttonRaised.setFont(font);
402: _buttonLowered = new JButton();
403: _buttonLowered.setMargin(new Insets(0, 0, 0, 0));
404: _buttonLowered.setFont(font);
405: _buttonLowered.getModel().setArmed(true);
406: _buttonLowered.getModel().setPressed(true);
407:
408: _buttonLowered.setMinimumSize(new Dimension(50, 25));
409: _buttonRaised.setMinimumSize(new Dimension(50, 25));
410: }
411:
412: /*
413: * @see TableCellRenderer#getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
414: */
415: public Component getTableCellRendererComponent(JTable table,
416: Object value, boolean isSelected, boolean hasFocus,
417: int row, int column) {
418:
419: if (value == null) {
420: value = "";
421: }
422:
423: // Rendering the column that the mouse has been pressed in.
424: if (_pressedColumnIdx == column && _pressed) {
425: _buttonLowered.setText(value.toString());
426:
427: // If this is the column that the table is currently is
428: // currently sorted by then display the sort icon.
429: if (column == _currentlySortedColumnIdx
430: && _currentSortedColumnIcon != null) {
431: _buttonLowered.setIcon(_currentSortedColumnIcon);
432: } else {
433: _buttonLowered.setIcon(null);
434: }
435: return _buttonLowered;
436: }
437:
438: // This is not the column that the mouse has been pressed in.
439: _buttonRaised.setText(value.toString());
440: if (_currentSortedColumnIcon != null
441: && column == _currentlySortedColumnIdx) {
442: _buttonRaised.setIcon(_currentSortedColumnIcon);
443: } else {
444: _buttonRaised.setIcon(null);
445: }
446: return _buttonRaised;
447: }
448: }
449: }
|