97 |
|
|
98 |
class KeyHandler implements KeyListener |
class KeyHandler implements KeyListener |
99 |
{ |
{ |
100 |
|
|
101 |
|
/** |
102 |
|
* A helper method for the keyPressed event. Used because the actions |
103 |
|
* for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. |
104 |
|
* |
105 |
|
* Selects the next (previous if SHIFT pressed) column for TAB, or row for |
106 |
|
* ENTER from within the currently selected cells. |
107 |
|
* |
108 |
|
* @param firstModel the ListSelectionModel for columns (TAB) or |
109 |
|
* rows (ENTER) |
110 |
|
* @param firstMin the first selected index in firstModel |
111 |
|
* @param firstMax the last selected index in firstModel |
112 |
|
* @param secondModel the ListSelectionModel for rows (TAB) or |
113 |
|
* columns (ENTER) |
114 |
|
* @param secondMin the first selected index in secondModel |
115 |
|
* @param secondMax the last selected index in secondModel |
116 |
|
* @param reverse true if shift was held for the event |
117 |
|
* @param eventIsTab true if TAB was pressed, false if ENTER pressed |
118 |
|
*/ |
119 |
|
void advanceMultipleSelection (ListSelectionModel firstModel, int firstMin, |
120 |
|
int firstMax, ListSelectionModel secondModel, |
121 |
|
int secondMin, int secondMax, boolean reverse, |
122 |
|
boolean eventIsTab) |
123 |
|
{ |
124 |
|
// If eventIsTab, all the "firsts" correspond to columns, otherwise, to rows |
125 |
|
// "seconds" correspond to the opposite |
126 |
|
int firstLead = firstModel.getLeadSelectionIndex(); |
127 |
|
int secondLead = secondModel.getLeadSelectionIndex(); |
128 |
|
int numFirsts = eventIsTab ? |
129 |
|
table.getModel().getColumnCount() : table.getModel().getRowCount(); |
130 |
|
int numSeconds = eventIsTab ? |
131 |
|
table.getModel().getRowCount() : table.getModel().getColumnCount(); |
132 |
|
|
133 |
|
// check if we have to wrap the "firsts" around, going to the other side |
134 |
|
if ((firstLead == firstMax && !reverse) || |
135 |
|
(reverse && firstLead == firstMin)) |
136 |
|
{ |
137 |
|
firstModel.addSelectionInterval(reverse ? firstMax : firstMin, |
138 |
|
reverse ? firstMax : firstMin); |
139 |
|
|
140 |
|
// check if we have to wrap the "seconds" |
141 |
|
if ((secondLead == secondMax && !reverse) || |
142 |
|
(reverse && secondLead == secondMin)) |
143 |
|
secondModel.addSelectionInterval(reverse ? secondMax : secondMin, |
144 |
|
reverse ? secondMax : secondMin); |
145 |
|
|
146 |
|
// if we're not wrapping the seconds, we have to find out where we |
147 |
|
// are within the secondModel and advance to the next cell (or |
148 |
|
// go back to the previous cell if reverse == true) |
149 |
|
else |
150 |
|
{ |
151 |
|
int[] secondsSelected; |
152 |
|
if (eventIsTab && table.getRowSelectionAllowed() || |
153 |
|
!eventIsTab && table.getColumnSelectionAllowed()) |
154 |
|
secondsSelected = eventIsTab ? |
155 |
|
table.getSelectedRows() : table.getSelectedColumns(); |
156 |
|
else |
157 |
|
{ |
158 |
|
// if row selection is not allowed, then the entire column gets |
159 |
|
// selected when you click on it, so consider ALL rows selected |
160 |
|
secondsSelected = new int[numSeconds]; |
161 |
|
for (int i = 0; i < numSeconds; i++) |
162 |
|
secondsSelected[i] = i; |
163 |
|
} |
164 |
|
|
165 |
|
// and now find the "next" index within the model |
166 |
|
int secondIndex = reverse ? secondsSelected.length - 1 : 0; |
167 |
|
if (!reverse) |
168 |
|
while (secondsSelected[secondIndex] <= secondLead) |
169 |
|
secondIndex++; |
170 |
|
else |
171 |
|
while (secondsSelected[secondIndex] >= secondLead) |
172 |
|
secondIndex--; |
173 |
|
|
174 |
|
// and select it - updating the lead selection index |
175 |
|
secondModel.addSelectionInterval(secondsSelected[secondIndex], |
176 |
|
secondsSelected[secondIndex]); |
177 |
|
} |
178 |
|
} |
179 |
|
// We didn't have to wrap the firsts, so just find the "next" first |
180 |
|
// and select it, we don't have to change "seconds" |
181 |
|
else |
182 |
|
{ |
183 |
|
int[] firstsSelected; |
184 |
|
if (eventIsTab && table.getColumnSelectionAllowed() || |
185 |
|
!eventIsTab && table.getRowSelectionAllowed()) |
186 |
|
firstsSelected = eventIsTab ? |
187 |
|
table.getSelectedColumns() : table.getSelectedRows(); |
188 |
|
else |
189 |
|
{ |
190 |
|
// if selection not allowed, consider ALL firsts to be selected |
191 |
|
firstsSelected = new int[numFirsts]; |
192 |
|
for (int i = 0; i < numFirsts; i++) |
193 |
|
firstsSelected[i] = i; |
194 |
|
} |
195 |
|
int firstIndex = reverse ? firstsSelected.length - 1 : 0; |
196 |
|
if (!reverse) |
197 |
|
while (firstsSelected[firstIndex] <= firstLead) |
198 |
|
firstIndex++; |
199 |
|
else |
200 |
|
while (firstsSelected[firstIndex] >= firstLead) |
201 |
|
firstIndex--; |
202 |
|
firstModel.addSelectionInterval(firstsSelected[firstIndex], |
203 |
|
firstsSelected[firstIndex]); |
204 |
|
secondModel.addSelectionInterval(secondLead, secondLead); |
205 |
|
} |
206 |
|
} |
207 |
|
|
208 |
|
/** |
209 |
|
* A helper method for the keyPressed event. Used because the actions |
210 |
|
* for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. |
211 |
|
* |
212 |
|
* Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER) |
213 |
|
* in the table, changing the current selection. All cells in the table |
214 |
|
* are eligible, not just the ones that are currently selected. |
215 |
|
* @param firstModel the ListSelectionModel for columns (TAB) or rows |
216 |
|
* (ENTER) |
217 |
|
* @param firstMax the last index in firstModel |
218 |
|
* @param secondModel the ListSelectionModel for rows (TAB) or columns |
219 |
|
* (ENTER) |
220 |
|
* @param secondMax the last index in secondModel |
221 |
|
* @param reverse true if SHIFT was pressed for the event |
222 |
|
*/ |
223 |
|
|
224 |
|
void advanceSingleSelection (ListSelectionModel firstModel, int firstMax, |
225 |
|
ListSelectionModel secondModel, int secondMax, |
226 |
|
boolean reverse) |
227 |
|
{ |
228 |
|
// for TABs, "first" corresponds to columns and "seconds" to rows. |
229 |
|
// the opposite is true for ENTERs |
230 |
|
int firstLead = firstModel.getLeadSelectionIndex(); |
231 |
|
int secondLead = secondModel.getLeadSelectionIndex(); |
232 |
|
|
233 |
|
// if we are going backwards subtract 2 because we later add 1 |
234 |
|
// for a net change of -1 |
235 |
|
if (reverse && (firstLead == 0)) |
236 |
|
{ |
237 |
|
// check if we have to wrap around |
238 |
|
if (secondLead == 0) |
239 |
|
secondLead += secondMax + 1; |
240 |
|
secondLead -= 2; |
241 |
|
} |
242 |
|
|
243 |
|
// do we have to wrap the "seconds"? |
244 |
|
if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax)) |
245 |
|
secondModel.setSelectionInterval((secondLead + 1)%(secondMax + 1), |
246 |
|
(secondLead + 1)%(secondMax + 1)); |
247 |
|
// if not, just reselect the current lead |
248 |
|
else |
249 |
|
secondModel.setSelectionInterval(secondLead, secondLead); |
250 |
|
|
251 |
|
// if we are going backwards, subtract 2 because we add 1 later |
252 |
|
// for net change of -1 |
253 |
|
if (reverse) |
254 |
|
{ |
255 |
|
// check for wraparound |
256 |
|
if (firstLead == 0) |
257 |
|
firstLead += firstMax + 1; |
258 |
|
firstLead -= 2; |
259 |
|
} |
260 |
|
// select the next "first" |
261 |
|
firstModel.setSelectionInterval ((firstLead + 1)%(firstMax + 1), |
262 |
|
(firstLead + 1)%(firstMax + 1)); |
263 |
|
} |
264 |
|
|
265 |
public void keyPressed(KeyEvent evt) |
public void keyPressed(KeyEvent evt) |
266 |
{ |
{ |
267 |
ListSelectionModel rowModel = table.getSelectionModel(); |
ListSelectionModel rowModel = table.getSelectionModel(); |
400 |
{ |
{ |
401 |
// FIXME: implement, need JList.ensureIndexIsVisible to work |
// FIXME: implement, need JList.ensureIndexIsVisible to work |
402 |
} |
} |
403 |
else if (evt.getKeyCode() == KeyEvent.VK_TAB) |
else if (evt.getKeyCode() == KeyEvent.VK_TAB |
404 |
{ |
|| evt.getKeyCode() == KeyEvent.VK_ENTER) |
|
// FIXME: Implement select next column |
|
|
// NOTE: TAB doesn't get recognized by this KeyHandler |
|
|
// something must be intercepting it and using it to change |
|
|
// focus |
|
|
} |
|
|
else if (evt.getKeyCode() == KeyEvent.VK_ENTER) |
|
405 |
{ |
{ |
406 |
// If nothing is selected, select the first cell in the table |
// If nothing is selected, select the first cell in the table |
407 |
if (table.getSelectedRowCount() == 0 && |
if (table.getSelectedRowCount() == 0 && |
424 |
return; |
return; |
425 |
} |
} |
426 |
|
|
427 |
// If there is just one selection, select the next row, and wrap |
// multRowsSelected and multColsSelected tell us if multiple rows or |
428 |
// when you get to the edges of the table. |
// columns are selected, respectively |
429 |
boolean multRowsSelected, multColsSelected; |
boolean multRowsSelected, multColsSelected; |
430 |
multRowsSelected = (table.getSelectedRowCount() > 1) || |
multRowsSelected = (table.getSelectedRowCount() > 1) || |
431 |
(!table.getRowSelectionAllowed() && |
(!table.getRowSelectionAllowed() && |
433 |
multColsSelected = (table.getSelectedColumnCount() > 1) || |
multColsSelected = (table.getSelectedColumnCount() > 1) || |
434 |
(!table.getColumnSelectionAllowed() && |
(!table.getColumnSelectionAllowed() && |
435 |
table.getSelectedRowCount() > 0); |
table.getSelectedRowCount() > 0); |
436 |
|
|
437 |
|
// If there is just one selection, select the next cell, and wrap |
438 |
|
// when you get to the edges of the table. |
439 |
if (!multColsSelected || !multRowsSelected) |
if (!multColsSelected || !multRowsSelected) |
440 |
{ |
{ |
441 |
rowModel.setSelectionInterval((rowLead + 1)%(rowMax + 1), |
if (evt.getKeyCode() == KeyEvent.VK_TAB) |
442 |
(rowLead + 1)%(rowMax + 1)); |
advanceSingleSelection(colModel, colMax, rowModel, rowMax, evt.isShiftDown()); |
|
if (rowLead == rowMax) |
|
|
colModel.setSelectionInterval((colLead + 1)%(colMax + 1), |
|
|
(colLead + 1)%(colMax + 1)); |
|
443 |
else |
else |
444 |
colModel.setSelectionInterval(colLead, colLead); |
advanceSingleSelection(rowModel, rowMax, colModel, colMax, evt.isShiftDown()); |
445 |
return; |
return; |
446 |
} |
} |
447 |
|
|
|
// Otherwise select the next row and wrap when you get to the edges |
|
|
// of the selection. So you only move the lead indices around |
|
|
// amongst the already selected cells. |
|
448 |
|
|
449 |
// If the row lead index is at the end of the selection, wrap it around |
// rowMinSelected and rowMaxSelected are the minimum and maximum |
450 |
|
// values respectively of selected cells in the row selection model |
451 |
|
// Similarly for colMinSelected and colMaxSelected. |
452 |
int rowMaxSelected = table.getRowSelectionAllowed() ? |
int rowMaxSelected = table.getRowSelectionAllowed() ? |
453 |
rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1; |
rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1; |
454 |
int rowMinSelected = table.getRowSelectionAllowed() ? |
int rowMinSelected = table.getRowSelectionAllowed() ? |
458 |
table.getModel().getColumnCount() - 1; |
table.getModel().getColumnCount() - 1; |
459 |
int colMinSelected = table.getColumnSelectionAllowed() ? |
int colMinSelected = table.getColumnSelectionAllowed() ? |
460 |
colModel.getMinSelectionIndex() : 0; |
colModel.getMinSelectionIndex() : 0; |
461 |
if (rowLead == rowMaxSelected) |
|
462 |
{ |
// If there are multiple rows and columns selected, select the next |
463 |
rowModel.addSelectionInterval(rowMinSelected, rowMinSelected); |
// cell and wrap at the edges of the selection. |
464 |
|
if (evt.getKeyCode() == KeyEvent.VK_TAB) |
465 |
// And select the next column (from within the selection) |
advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, rowModel, rowMinSelected, rowMaxSelected, evt.isShiftDown(), true); |
|
if (colLead == colMaxSelected) |
|
|
colModel.addSelectionInterval(colMinSelected, colMinSelected); |
|
|
else |
|
|
{ |
|
|
int[] colsSelected; |
|
|
if (table.getColumnSelectionAllowed()) |
|
|
colsSelected = table.getSelectedColumns(); |
|
|
else |
|
|
{ |
|
|
colsSelected = new int[table.getModel().getColumnCount()]; |
|
|
for (int i = 0; i < table.getModel().getColumnCount(); i++) |
|
|
colsSelected[i] = i; |
|
|
} |
|
|
int colIndex = 0; |
|
|
while (colsSelected[colIndex] <= colLead) |
|
|
colIndex++; |
|
|
colModel.addSelectionInterval(colsSelected[colIndex], |
|
|
colsSelected[colIndex]); |
|
|
} |
|
|
} |
|
|
// If the row lead index isn't at the end, just advance it |
|
|
// and you don't have to update the column index |
|
466 |
else |
else |
467 |
{ |
advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, colModel, colMinSelected, colMaxSelected, evt.isShiftDown(), false); |
468 |
int[] rowsSelected; |
|
|
if (table.getRowSelectionAllowed()) |
|
|
rowsSelected = table.getSelectedRows(); |
|
|
else |
|
|
{ |
|
|
rowsSelected = new int[table.getModel().getRowCount()]; |
|
|
for (int i = 0; i < table.getModel().getRowCount(); i++) |
|
|
rowsSelected[i] = i; |
|
|
} |
|
|
int rowIndex = 0; |
|
|
while (rowsSelected[rowIndex] <= rowLead) |
|
|
rowIndex++; |
|
|
rowModel.addSelectionInterval(rowsSelected[rowIndex], |
|
|
rowsSelected[rowIndex]); |
|
|
colModel.addSelectionInterval(colLead, colLead); |
|
|
} |
|
469 |
table.repaint(); |
table.repaint(); |
470 |
} |
} |
471 |
else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) |
else if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) |
778 |
} |
} |
779 |
|
|
780 |
} |
} |
|
|
|
781 |
} |
} |