View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program 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.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.table;
21  
22  import java.awt.event.InputEvent;
23  import java.awt.event.MouseAdapter;
24  import java.awt.event.MouseEvent;
25  import java.awt.event.MouseMotionListener;
26  import java.util.Enumeration;
27  
28  import javax.swing.JPopupMenu;
29  import javax.swing.JTable;
30  import javax.swing.JTree;
31  import javax.swing.table.JTableHeader;
32  import javax.swing.tree.TreePath;
33  
34  import org.xnap.gui.util.GUIHelper;
35  
36  /***
37   * TableHeaderHandler class handles the mouse events invoked by clicking
38   * on the header portion of the JTable.
39   */
40  public class TableHeaderHandler extends MouseAdapter 
41      implements MouseMotionListener {
42  
43      // --- Data Field(s) ---
44  
45      private JTable jta;
46      private JTableHeader header;
47      private JTree jt;
48      private JPopupMenu jpm;
49      private SortButtonRenderer renderer;
50      private SortableModel stm;
51      private TableHeaderListener thl;
52      
53      private TreePath[] selectedPaths;
54      private Enumeration expandedLeafs;
55  
56      /***
57       * Set to true, when the user has pressed a button but not yet
58       * dragged it.
59       */
60      private boolean sorting = false;
61  
62      // --- Constructor(s) ---
63  
64      /***
65       * 
66       */
67      public TableHeaderHandler(JTable jta, SortableModel stm, JPopupMenu jpm)
68      {
69  		this.jta = jta;
70  		this.header = jta.getTableHeader();
71  		this.stm = stm;
72  		this.jpm = jpm;
73  
74  		renderer = new SortButtonRenderer(header.getDefaultRenderer());
75  		header.setDefaultRenderer(renderer);
76  
77  		header.setReorderingAllowed(true);
78  		header.setResizingAllowed(true);
79      }
80  
81      // --- Method(s) ---
82  
83      /***
84       * Installs a TableHeaderHandler to <code>jta</code>. The handler
85       * takes care of sorting and resizing.
86       *
87       * <p>The SortableTableModel parameter is needed for the JTreeTable, 
88       * because it uses a TreeTableModelAdapter that wraps around 
89       * the real TableModel.
90       */
91      public static TableHeaderHandler install(JTable jta, JPopupMenu jpm,
92  											 SortableModel stm)
93      {
94          JTableHeader jth = jta.getTableHeader();
95  		TableHeaderHandler l = new TableHeaderHandler(jta, stm, jpm);
96          jth.addMouseListener(l);
97          jth.addMouseMotionListener(l);
98  
99  		return l;
100     }
101 
102     public static TableHeaderHandler install(JTable jta, JPopupMenu jpm)
103     {
104 		return install(jta, jpm, (SortableModel)jta.getModel());
105     }
106 
107     /***
108      * Installs listener.
109      */
110     public static TableHeaderHandler install(JTable jta)
111     {
112 		return install(jta, null);
113     }
114 
115     public void mousePressed(MouseEvent e)
116     {
117 		if (e.isPopupTrigger()) {
118 			showPopupMenu(e);
119 		}
120 		else {
121 			int col = header.columnAtPoint(e.getPoint());
122 			renderer.selectColumn(col);
123 			header.repaint();
124 	    
125 			sorting = true;
126 		}
127 
128 		setAutoResizeMode(e);
129     }
130 
131     public void mouseReleased(MouseEvent e) 
132     {
133 		if (e.isPopupTrigger()) {
134 			showPopupMenu(e);
135 		}
136 		else if (sorting) {
137 			int col = header.columnAtPoint(e.getPoint());
138 			if (jta.isEditing()) {
139 				jta.getCellEditor().stopCellEditing();
140 			}
141 			sortByColumn(col);
142 
143 			if (thl != null) {
144 				thl.sortedColumnChanged();
145 			}
146 			sorting = false;
147 			renderer.selectColumn(-1);
148 			header.repaint();
149 		}
150 		else {
151 			if (thl != null) {
152 				thl.columnWidthChanged();
153 			}
154 		}
155     }
156 
157     public void mouseDragged(MouseEvent e) 
158     {
159 		if (sorting) {
160 			// abort sorting
161 			sorting = false;
162 	    
163 			renderer.selectColumn(-1);
164 			header.repaint();
165 		}
166 
167 		setAutoResizeMode(e);
168     }
169 
170     public void mouseMoved(MouseEvent e) 
171     {
172     }
173 
174     /***
175      * Sets the auto resize mode of the table depending on a modifier key.
176      */
177     private void setAutoResizeMode(MouseEvent e)
178     {
179 		if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
180 			jta.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
181 		}
182 		else if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) {
183 			jta.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
184 		}
185 		else {
186 			jta.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
187 		}
188     }
189 
190     public void setListener(TableHeaderListener thl)
191     {
192 		this.thl = thl;
193     }
194 
195     public void setTree(JTree jt)
196     {
197 		this.jt = jt;
198     }
199 
200     /***
201      * Displays the popup menu.
202      */
203     public void showPopupMenu(MouseEvent e)
204     {
205 		if (jpm != null) {
206 			header.setDraggedColumn(null);
207 			header.setResizingColumn(null);
208 			GUIHelper.showPopupMenu(jpm, e.getComponent(), e.getX(), e.getY());
209 		}
210     }
211 
212     /***
213      *
214      */
215     public void sortByColumn(int column)
216     {
217 		int modelIndex = jta.convertColumnIndexToModel(column);
218 
219 		boolean ascending;
220 		if (stm.getSortedColumn() == modelIndex) {
221 			// keep old sort order
222 			ascending = stm.isSortedAscending();
223 		}
224 		else if (stm.getColumnClass(modelIndex) == String.class) {
225 			// sort strings ascending
226 			ascending = true;
227 		}
228 		else {
229 			// sort descending by default
230 			ascending = false;
231 		}
232 
233 		// revert if the same column was resorted
234 		boolean revert = (stm.getSortedColumn() == modelIndex);
235 
236 		storeSelections();
237 
238 		ascending = stm.sortByColumn(modelIndex, ascending, revert);
239 		renderer.setSortedColumn(column, ascending);
240 
241 		restoreSelections();
242     }
243 
244     public void storeSelections() 
245     {
246 		if (jt != null) {
247 			selectedPaths = jt.getSelectionPaths();
248 			expandedLeafs = jt.getExpandedDescendants
249 				(new TreePath(jt.getModel().getRoot()));
250 		}
251     }
252 
253     public void restoreSelections() {
254 		if (jt != null) {
255 			if (expandedLeafs != null) {
256 				while (expandedLeafs.hasMoreElements()) {
257 					TreePath path = (TreePath)expandedLeafs.nextElement();
258 					jt.expandPath(path);
259 				}
260 			}
261 			if (selectedPaths != null) {
262 				jt.addSelectionPaths(selectedPaths);
263 			}
264 		}
265     }
266 
267 }