001 /*
002 * $Id: JXTableHeader.java,v 1.12 2006/03/15 11:58:49 kleopatra Exp $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021 package org.jdesktop.swingx;
022
023 import java.awt.event.MouseEvent;
024
025 import javax.swing.JComponent;
026 import javax.swing.JTable;
027 import javax.swing.SwingUtilities;
028 import javax.swing.event.MouseInputListener;
029 import javax.swing.table.JTableHeader;
030 import javax.swing.table.TableCellRenderer;
031 import javax.swing.table.TableColumn;
032 import javax.swing.table.TableColumnModel;
033
034 import org.jdesktop.swingx.table.ColumnHeaderRenderer;
035
036 /**
037 * TableHeader with extended functionality if associated Table is of
038 * type JXTable.<p>
039 *
040 * The enhancements:
041 * <ul>
042 * <li> supports pluggable handler to control user interaction for sorting
043 * <li> uses ColumnHeaderRenderer which can show the sort icon
044 * <li> triggers column pack (== auto-resize to exactly fit the contents)
045 * on double-click in resize region.
046 * </ul>
047 *
048 *
049 * @author Jeanette Winzenburg
050 */
051 public class JXTableHeader extends JTableHeader {
052
053 private SortGestureRecognizer sortGestureRecognizer;
054
055 public JXTableHeader() {
056 super();
057 }
058
059 public JXTableHeader(TableColumnModel columnModel) {
060 super(columnModel);
061 }
062
063 /**
064 * Sets the associated JTable. Enables enhanced header
065 * features if table is of type JXTable.<p>
066 *
067 * PENDING: who is responsible for synching the columnModel?
068 */
069 public void setTable(JTable table) {
070 super.setTable(table);
071 // setColumnModel(table.getColumnModel());
072 // the additional listening option makes sense only if the table
073 // actually is a JXTable
074 if (getXTable() != null) {
075 installHeaderListener();
076 } else {
077 uninstallHeaderListener();
078 }
079 }
080
081 public JXTable getXTable() {
082 if (!(getTable() instanceof JXTable))
083 return null;
084 return (JXTable) getTable();
085 }
086
087
088 public void updateUI() {
089 super.updateUI();
090 if (getDefaultRenderer() instanceof JComponent) {
091 ((JComponent) getDefaultRenderer()).updateUI();
092
093 }
094 }
095 /**
096 * returns the (visible) view index for the given column
097 * or -1 if not visible or not contained in this header's
098 * columnModel.
099 *
100 *
101 * @param aColumn
102 * @return
103 */
104 private int getViewIndexForColumn(TableColumn aColumn) {
105 if (aColumn == null)
106 return -1;
107 TableColumnModel cm = getColumnModel();
108 for (int column = 0; column < cm.getColumnCount(); column++) {
109 if (cm.getColumn(column) == aColumn) {
110 return column;
111 }
112 }
113 return -1;
114 }
115
116 protected TableCellRenderer createDefaultRenderer() {
117 return ColumnHeaderRenderer.createColumnHeaderRenderer();
118 }
119
120 /**
121 * Lazily creates and returns the SortGestureRecognizer.
122 *
123 * @return the SortGestureRecognizer used in Headerlistener.
124 */
125 public SortGestureRecognizer getSortGestureRecognizer() {
126 if (sortGestureRecognizer == null) {
127 sortGestureRecognizer = createSortGestureRecognizer();
128 }
129 return sortGestureRecognizer;
130
131 }
132
133 /**
134 * Set the SortGestureRecognizer for use in the HeaderListener.
135 *
136 * @param recognizer the recognizer to use in HeaderListener.
137 */
138 public void setSortGestureRecognizer(SortGestureRecognizer recognizer) {
139 this.sortGestureRecognizer = recognizer;
140 }
141
142 /**
143 * creates and returns the default SortGestureRecognizer.
144 * @return the SortGestureRecognizer used in Headerlistener.
145 *
146 */
147 protected SortGestureRecognizer createSortGestureRecognizer() {
148 return new SortGestureRecognizer();
149 }
150
151 protected void installHeaderListener() {
152 if (headerListener == null) {
153 headerListener = new HeaderListener();
154 addMouseListener(headerListener);
155 addMouseMotionListener(headerListener);
156
157 }
158 }
159
160 protected void uninstallHeaderListener() {
161 if (headerListener != null) {
162 removeMouseListener(headerListener);
163 removeMouseMotionListener(headerListener);
164 headerListener = null;
165 }
166 }
167
168 private MouseInputListener headerListener;
169
170 private class HeaderListener implements MouseInputListener {
171 private TableColumn cachedResizingColumn;
172
173 public void mouseClicked(MouseEvent e) {
174 if (shouldIgnore(e)) {
175 return;
176 }
177 if (isInResizeRegion(e)) {
178 doResize(e);
179 } else {
180 doSort(e);
181 }
182 }
183
184 private boolean shouldIgnore(MouseEvent e) {
185 return !SwingUtilities.isLeftMouseButton(e)
186 || !table.isEnabled();
187 }
188
189 private void doSort(MouseEvent e) {
190 JXTable table = getXTable();
191 if (!table.isSortable())
192 return;
193 if (getSortGestureRecognizer().isResetSortOrderGesture(e)) {
194 table.resetSortOrder();
195 repaint();
196 } else if (getSortGestureRecognizer().isToggleSortOrderGesture(e)){
197 int column = columnAtPoint(e.getPoint());
198 if (column >= 0) {
199 table.toggleSortOrder(column);
200 }
201 uncacheResizingColumn();
202 repaint();
203 }
204
205 }
206
207 private void doResize(MouseEvent e) {
208 if (e.getClickCount() != 2)
209 return;
210 int column = getViewIndexForColumn(cachedResizingColumn);
211 if (column >= 0) {
212 (getXTable()).packColumn(column, 5);
213 }
214 uncacheResizingColumn();
215
216 }
217
218
219 public void mouseReleased(MouseEvent e) {
220 cacheResizingColumn(e);
221 }
222
223 public void mousePressed(MouseEvent e) {
224 cacheResizingColumn(e);
225 }
226
227 private void cacheResizingColumn(MouseEvent e) {
228 if (!getSortGestureRecognizer().isSortOrderGesture(e))
229 return;
230 TableColumn column = getResizingColumn();
231 if (column != null) {
232 cachedResizingColumn = column;
233 }
234 }
235
236 private void uncacheResizingColumn() {
237 cachedResizingColumn = null;
238 }
239
240 private boolean isInResizeRegion(MouseEvent e) {
241 return cachedResizingColumn != null; // inResize;
242 }
243
244 public void mouseEntered(MouseEvent e) {
245 }
246
247 public void mouseExited(MouseEvent e) {
248 uncacheResizingColumn();
249 }
250
251 public void mouseDragged(MouseEvent e) {
252 uncacheResizingColumn();
253 }
254
255 public void mouseMoved(MouseEvent e) {
256 }
257 }
258
259 /**
260 * Encapsulates decision about which MouseEvents should
261 * trigger sort/unsort events.
262 *
263 * Here: a single left click for toggling sort order, a
264 * single SHIFT-left click for unsorting.
265 *
266 */
267 public static class SortGestureRecognizer {
268 public boolean isResetSortOrderGesture(MouseEvent e) {
269 return isSortOrderGesture(e) && isResetModifier(e);
270 }
271
272 protected boolean isResetModifier(MouseEvent e) {
273 return ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) == MouseEvent.SHIFT_DOWN_MASK);
274 }
275
276 public boolean isToggleSortOrderGesture(MouseEvent e) {
277 return isSortOrderGesture(e) && !isResetModifier(e);
278 }
279
280 public boolean isSortOrderGesture(MouseEvent e) {
281 return e.getClickCount() == 1;
282 }
283 }
284
285
286 }