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 }