44 |
import java.awt.Graphics; |
import java.awt.Graphics; |
45 |
import java.awt.Point; |
import java.awt.Point; |
46 |
import java.awt.Rectangle; |
import java.awt.Rectangle; |
47 |
|
import java.awt.event.ActionEvent; |
48 |
|
import java.awt.event.ActionListener; |
49 |
import java.awt.event.ComponentAdapter; |
import java.awt.event.ComponentAdapter; |
50 |
import java.awt.event.ComponentEvent; |
import java.awt.event.ComponentEvent; |
51 |
import java.awt.event.ComponentListener; |
import java.awt.event.ComponentListener; |
52 |
import java.awt.event.FocusEvent; |
import java.awt.event.FocusEvent; |
53 |
import java.awt.event.FocusListener; |
import java.awt.event.FocusListener; |
|
import java.awt.event.InputEvent; |
|
|
import java.awt.event.KeyAdapter; |
|
54 |
import java.awt.event.KeyEvent; |
import java.awt.event.KeyEvent; |
55 |
import java.awt.event.MouseEvent; |
import java.awt.event.MouseEvent; |
56 |
import java.beans.PropertyChangeEvent; |
import java.beans.PropertyChangeEvent; |
57 |
import java.beans.PropertyChangeListener; |
import java.beans.PropertyChangeListener; |
58 |
|
|
59 |
|
import javax.swing.AbstractAction; |
60 |
|
import javax.swing.ActionMap; |
61 |
import javax.swing.CellRendererPane; |
import javax.swing.CellRendererPane; |
62 |
|
import javax.swing.InputMap; |
63 |
import javax.swing.JComponent; |
import javax.swing.JComponent; |
64 |
import javax.swing.JList; |
import javax.swing.JList; |
65 |
import javax.swing.JViewport; |
import javax.swing.JViewport; |
66 |
|
import javax.swing.KeyStroke; |
67 |
import javax.swing.ListCellRenderer; |
import javax.swing.ListCellRenderer; |
68 |
import javax.swing.ListModel; |
import javax.swing.ListModel; |
69 |
import javax.swing.ListSelectionModel; |
import javax.swing.ListSelectionModel; |
76 |
import javax.swing.event.ListSelectionListener; |
import javax.swing.event.ListSelectionListener; |
77 |
import javax.swing.event.MouseInputListener; |
import javax.swing.event.MouseInputListener; |
78 |
import javax.swing.plaf.ComponentUI; |
import javax.swing.plaf.ComponentUI; |
79 |
|
import javax.swing.plaf.InputMapUIResource; |
80 |
import javax.swing.plaf.ListUI; |
import javax.swing.plaf.ListUI; |
81 |
|
|
82 |
/** |
/** |
192 |
} |
} |
193 |
} |
} |
194 |
|
|
|
|
|
195 |
/** |
/** |
196 |
* A helper class which listens for {@link KeyEvents}s |
* This class is used to mimmic the behaviour of the JDK when registering |
197 |
* from the {@link JList}. |
* keyboard actions. It is the same as the private class used in JComponent |
198 |
|
* for the same reason. This class receives an action event and dispatches |
199 |
|
* it to the true receiver after altering the actionCommand property of the |
200 |
|
* event. |
201 |
*/ |
*/ |
202 |
// FIXME: This should be handled somehow by the L&F key bindings. |
private static class ActionListenerProxy |
203 |
private class KeyHandler extends KeyAdapter |
extends AbstractAction |
204 |
{ |
{ |
205 |
public KeyHandler() |
ActionListener target; |
206 |
|
String bindingCommandName; |
207 |
|
|
208 |
|
public ActionListenerProxy(ActionListener li, |
209 |
|
String cmd) |
210 |
{ |
{ |
211 |
|
target = li; |
212 |
|
bindingCommandName = cmd; |
213 |
} |
} |
214 |
|
|
215 |
public void keyPressed( KeyEvent evt ) |
public void actionPerformed(ActionEvent e) |
216 |
{ |
{ |
217 |
int lead = BasicListUI.this.list.getLeadSelectionIndex(); |
ActionEvent derivedEvent = new ActionEvent(e.getSource(), |
218 |
int max = BasicListUI.this.list.getModel().getSize() - 1; |
e.getID(), |
219 |
|
bindingCommandName, |
220 |
|
e.getModifiers()); |
221 |
|
target.actionPerformed(derivedEvent); |
222 |
|
} |
223 |
|
} |
224 |
|
|
225 |
|
class ListAction extends AbstractAction |
226 |
|
{ |
227 |
|
public void actionPerformed (ActionEvent e) |
228 |
|
{ |
229 |
|
int lead = list.getLeadSelectionIndex(); |
230 |
|
int max = list.getModel().getSize() - 1; |
231 |
// Do nothing if list is empty |
// Do nothing if list is empty |
232 |
if (max == -1) |
if (max == -1) |
233 |
return; |
return; |
234 |
|
|
235 |
// Process the key event. Bindings can be found in |
if (e.getActionCommand().equals("selectNextRow")) |
|
// javax.swing.plaf.basic.BasicLookAndFeel.java |
|
|
if ((evt.getKeyCode() == KeyEvent.VK_DOWN) |
|
|
|| (evt.getKeyCode() == KeyEvent.VK_KP_DOWN)) |
|
236 |
{ |
{ |
237 |
if (evt.getModifiers() == 0) |
selectNextIndex(); |
238 |
{ |
} |
239 |
BasicListUI.this.list.clearSelection(); |
else if (e.getActionCommand().equals("selectPreviousRow")) |
240 |
BasicListUI.this.list.setSelectedIndex(Math.min(lead+1,max)); |
{ |
241 |
} |
selectPreviousIndex(); |
242 |
else if (evt.getModifiers() == InputEvent.SHIFT_MASK) |
} |
243 |
selectNextIndex(); |
else if (e.getActionCommand().equals("clearSelection")) |
244 |
|
{ |
245 |
|
list.clearSelection(); |
246 |
} |
} |
247 |
else if ((evt.getKeyCode() == KeyEvent.VK_UP) |
else if (e.getActionCommand().equals("selectAll")) |
|
|| (evt.getKeyCode() == KeyEvent.VK_KP_UP)) |
|
248 |
{ |
{ |
249 |
if (evt.getModifiers() == 0) |
list.setSelectionInterval(0, max); |
250 |
|
// this next line is to restore the lead selection index to the old |
251 |
|
// position, because select-all should not change the lead index |
252 |
|
list.addSelectionInterval(lead, lead); |
253 |
|
} |
254 |
|
else if (e.getActionCommand().equals("selectLastRow")) |
255 |
|
{ |
256 |
|
list.setSelectedIndex(list.getModel().getSize() - 1); |
257 |
|
} |
258 |
|
else if (e.getActionCommand().equals("scrollDownExtendSelection")) |
259 |
|
{ |
260 |
|
int target; |
261 |
|
if (lead == list.getLastVisibleIndex()) |
262 |
{ |
{ |
263 |
BasicListUI.this.list.clearSelection(); |
target = Math.min |
264 |
BasicListUI.this.list.setSelectedIndex(Math.max(lead-1,0)); |
(max, lead + (list.getLastVisibleIndex() - |
265 |
|
list.getFirstVisibleIndex() + 1)); |
266 |
} |
} |
267 |
else if (evt.getModifiers() == InputEvent.SHIFT_MASK) |
else |
268 |
selectPreviousIndex(); |
target = list.getLastVisibleIndex(); |
269 |
|
list.getSelectionModel().setLeadSelectionIndex(target); |
270 |
} |
} |
271 |
else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP) |
else if (e.getActionCommand().equals("scrollUpExtendSelection")) |
272 |
{ |
{ |
273 |
int target; |
int target; |
274 |
if (lead == BasicListUI.this.list.getFirstVisibleIndex()) |
if (lead == list.getFirstVisibleIndex()) |
275 |
{ |
{ |
276 |
target = Math.max |
target = Math.max |
277 |
(0, lead - (BasicListUI.this.list.getLastVisibleIndex() - |
(0, lead - (list.getLastVisibleIndex() - |
278 |
BasicListUI.this.list.getFirstVisibleIndex() + 1)); |
list.getFirstVisibleIndex() + 1)); |
279 |
} |
} |
280 |
else |
else |
281 |
{ |
target = list.getFirstVisibleIndex(); |
282 |
target = BasicListUI.this.list.getFirstVisibleIndex(); |
list.getSelectionModel().setLeadSelectionIndex(target); |
283 |
} |
} |
284 |
if (evt.getModifiers() == 0) |
else if (e.getActionCommand().equals("selectNextRowExtendSelection")) |
285 |
BasicListUI.this.list.setSelectedIndex(target); |
{ |
286 |
else if (evt.getModifiers() == InputEvent.SHIFT_MASK) |
list.getSelectionModel(). |
287 |
BasicListUI.this.list.getSelectionModel(). |
setLeadSelectionIndex(Math.min(lead + 1,max)); |
288 |
setLeadSelectionIndex(target); |
} |
289 |
|
else if (e.getActionCommand().equals("selectFirstRow")) |
290 |
|
{ |
291 |
|
list.setSelectedIndex(0); |
292 |
} |
} |
293 |
else if (evt.getKeyCode() == KeyEvent.VK_PAGE_DOWN) |
else if (e.getActionCommand().equals("selectFirstRowExtendSelection")) |
294 |
|
{ |
295 |
|
list.getSelectionModel().setLeadSelectionIndex(0); |
296 |
|
} |
297 |
|
else if (e.getActionCommand().equals("selectPreviousRowExtendSelection")) |
298 |
|
{ |
299 |
|
list.getSelectionModel().setLeadSelectionIndex(Math.max(0,lead - 1)); |
300 |
|
} |
301 |
|
else if (e.getActionCommand().equals("scrollUp")) |
302 |
{ |
{ |
303 |
int target; |
int target; |
304 |
if (lead == BasicListUI.this.list.getLastVisibleIndex()) |
if (lead == list.getFirstVisibleIndex()) |
305 |
{ |
{ |
306 |
target = Math.min |
target = Math.max |
307 |
(max, lead + (BasicListUI.this.list.getLastVisibleIndex() - |
(0, lead - (list.getLastVisibleIndex() - |
308 |
BasicListUI.this.list.getFirstVisibleIndex() + 1)); |
list.getFirstVisibleIndex() + 1)); |
309 |
} |
} |
310 |
else |
else |
311 |
{ |
target = list.getFirstVisibleIndex(); |
312 |
target = BasicListUI.this.list.getLastVisibleIndex(); |
list.setSelectedIndex(target); |
|
} |
|
|
if (evt.getModifiers() == 0) |
|
|
BasicListUI.this.list.setSelectedIndex(target); |
|
|
else if (evt.getModifiers() == InputEvent.SHIFT_MASK) |
|
|
BasicListUI.this.list.getSelectionModel(). |
|
|
setLeadSelectionIndex(target); |
|
313 |
} |
} |
314 |
else if (evt.getKeyCode() == KeyEvent.VK_BACK_SLASH |
else if (e.getActionCommand().equals("selectLastRowExtendSelection")) |
|
&& (evt.getModifiers() == InputEvent.CTRL_MASK)) |
|
315 |
{ |
{ |
316 |
BasicListUI.this.list.clearSelection(); |
list.getSelectionModel(). |
317 |
|
setLeadSelectionIndex(list.getModel().getSize() - 1); |
318 |
} |
} |
319 |
else if ((evt.getKeyCode() == KeyEvent.VK_HOME) |
else if (e.getActionCommand().equals("scrollDown")) |
|
|| evt.getKeyCode() == KeyEvent.VK_END) |
|
320 |
{ |
{ |
321 |
if (evt.getModifiers() != 0 && |
int target; |
322 |
evt.getModifiers() != InputEvent.SHIFT_MASK) |
if (lead == list.getLastVisibleIndex()) |
323 |
return; |
{ |
324 |
// index is either 0 for HOME, or last cell for END |
target = Math.min |
325 |
int index = (evt.getKeyCode() == KeyEvent.VK_HOME) ? 0 : max; |
(max, lead + (list.getLastVisibleIndex() - |
326 |
|
list.getFirstVisibleIndex() + 1)); |
327 |
if (!evt.isShiftDown() ||(BasicListUI.this.list.getSelectionMode() |
} |
|
== ListSelectionModel.SINGLE_SELECTION)) |
|
|
BasicListUI.this.list.setSelectedIndex(index); |
|
|
else if (BasicListUI.this.list.getSelectionMode() == |
|
|
ListSelectionModel.SINGLE_INTERVAL_SELECTION) |
|
|
BasicListUI.this.list.setSelectionInterval |
|
|
(BasicListUI.this.list.getAnchorSelectionIndex(), index); |
|
328 |
else |
else |
329 |
BasicListUI.this.list.getSelectionModel(). |
target = list.getLastVisibleIndex(); |
330 |
setLeadSelectionIndex(index); |
list.setSelectedIndex(target); |
331 |
} |
} |
332 |
else if ((evt.getKeyCode() == KeyEvent.VK_A || evt.getKeyCode() |
else |
|
== KeyEvent.VK_SLASH) && (evt.getModifiers() == |
|
|
InputEvent.CTRL_MASK)) |
|
333 |
{ |
{ |
334 |
BasicListUI.this.list.setSelectionInterval(0, max); |
// DEBUG: uncomment the following line to print out |
335 |
// this next line is to restore the lead selection index to the old |
// key bindings that aren't implemented yet |
336 |
// position, because select-all should not change the lead index |
|
337 |
BasicListUI.this.list.addSelectionInterval(lead, lead); |
// System.out.println ("not implemented: "+e.getActionCommand()); |
|
} |
|
|
else if (evt.getKeyCode() == KeyEvent.VK_SPACE && |
|
|
(evt.getModifiers() == InputEvent.CTRL_MASK)) |
|
|
{ |
|
|
BasicListUI.this.list.getSelectionModel(). |
|
|
setLeadSelectionIndex(Math.min(lead+1,max)); |
|
338 |
} |
} |
339 |
|
|
340 |
BasicListUI.this.list.ensureIndexIsVisible |
list.ensureIndexIsVisible(list.getLeadSelectionIndex()); |
|
(BasicListUI.this.list.getLeadSelectionIndex()); |
|
341 |
} |
} |
342 |
} |
} |
343 |
|
|
344 |
/** |
/** |
345 |
* A helper class which listens for {@link MouseEvent}s |
* A helper class which listens for {@link MouseEvent}s |
346 |
* from the {@link JList}. |
* from the {@link JList}. |
561 |
/** The mouse listener listening to the list. */ |
/** The mouse listener listening to the list. */ |
562 |
protected MouseInputListener mouseInputListener; |
protected MouseInputListener mouseInputListener; |
563 |
|
|
|
/** The key listener listening to the list */ |
|
|
private KeyHandler keyListener; |
|
|
|
|
564 |
/** The property change listener listening to the list. */ |
/** The property change listener listening to the list. */ |
565 |
protected PropertyChangeListener propertyChangeListener; |
protected PropertyChangeListener propertyChangeListener; |
566 |
|
|
604 |
* The {@link CellRendererPane} that is used for painting. |
* The {@link CellRendererPane} that is used for painting. |
605 |
*/ |
*/ |
606 |
protected CellRendererPane rendererPane; |
protected CellRendererPane rendererPane; |
607 |
|
|
608 |
|
/** The action bound to KeyStrokes. */ |
609 |
|
ListAction action; |
610 |
|
|
611 |
/** |
/** |
612 |
* Calculate the height of a particular row. If there is a fixed {@link |
* Calculate the height of a particular row. If there is a fixed {@link |
835 |
list.addPropertyChangeListener(propertyChangeListener); |
list.addPropertyChangeListener(propertyChangeListener); |
836 |
|
|
837 |
// FIXME: Are these two really needed? At least they are not documented. |
// FIXME: Are these two really needed? At least they are not documented. |
838 |
keyListener = new KeyHandler(); |
//keyListener = new KeyHandler(); |
839 |
list.addComponentListener(componentListener); |
list.addComponentListener(componentListener); |
840 |
componentListener = new ComponentHandler(); |
componentListener = new ComponentHandler(); |
841 |
list.addKeyListener(keyListener); |
//list.addKeyListener(keyListener); |
842 |
} |
} |
843 |
|
|
844 |
/** |
/** |
850 |
list.getModel().removeListDataListener(listDataListener); |
list.getModel().removeListDataListener(listDataListener); |
851 |
list.removeListSelectionListener(listSelectionListener); |
list.removeListSelectionListener(listSelectionListener); |
852 |
list.removeMouseListener(mouseInputListener); |
list.removeMouseListener(mouseInputListener); |
853 |
list.removeKeyListener(keyListener); |
//list.removeKeyListener(keyListener); |
854 |
list.removeMouseMotionListener(mouseInputListener); |
list.removeMouseMotionListener(mouseInputListener); |
855 |
list.removePropertyChangeListener(propertyChangeListener); |
list.removePropertyChangeListener(propertyChangeListener); |
856 |
} |
} |
857 |
|
|
858 |
|
private int convertModifiers(int mod) |
859 |
|
{ |
860 |
|
if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0) |
861 |
|
{ |
862 |
|
mod |= KeyEvent.SHIFT_MASK; |
863 |
|
mod &= ~KeyEvent.SHIFT_DOWN_MASK; |
864 |
|
} |
865 |
|
if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0) |
866 |
|
{ |
867 |
|
mod |= KeyEvent.CTRL_MASK; |
868 |
|
mod &= ~KeyEvent.CTRL_DOWN_MASK; |
869 |
|
} |
870 |
|
if ((mod & KeyEvent.META_DOWN_MASK) != 0) |
871 |
|
{ |
872 |
|
mod |= KeyEvent.META_MASK; |
873 |
|
mod &= ~KeyEvent.META_DOWN_MASK; |
874 |
|
} |
875 |
|
if ((mod & KeyEvent.ALT_DOWN_MASK) != 0) |
876 |
|
{ |
877 |
|
mod |= KeyEvent.ALT_MASK; |
878 |
|
mod &= ~KeyEvent.ALT_DOWN_MASK; |
879 |
|
} |
880 |
|
if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0) |
881 |
|
{ |
882 |
|
mod |= KeyEvent.ALT_GRAPH_MASK; |
883 |
|
mod &= ~KeyEvent.ALT_GRAPH_DOWN_MASK; |
884 |
|
} |
885 |
|
return mod; |
886 |
|
} |
887 |
|
|
888 |
/** |
/** |
889 |
* Installs keyboard actions for this UI in the {@link JList}. |
* Installs keyboard actions for this UI in the {@link JList}. |
890 |
*/ |
*/ |
891 |
protected void installKeyboardActions() |
protected void installKeyboardActions() |
892 |
{ |
{ |
893 |
|
UIDefaults defaults = UIManager.getLookAndFeelDefaults(); |
894 |
|
InputMap focusInputMap = (InputMap)defaults.get("List.focusInputMap"); |
895 |
|
InputMapUIResource parentInputMap = new InputMapUIResource(); |
896 |
|
// FIXME: The JDK uses a LazyActionMap for parentActionMap |
897 |
|
ActionMap parentActionMap = new ActionMap(); |
898 |
|
action = new ListAction(); |
899 |
|
Object keys[] = focusInputMap.allKeys(); |
900 |
|
// Register key bindings in the UI InputMap-ActionMap pair |
901 |
|
// Note that we register key bindings with both the old and new modifier |
902 |
|
// masks: InputEvent.SHIFT_MASK and InputEvent.SHIFT_DOWN_MASK and so on. |
903 |
|
for (int i = 0; i < keys.length; i++) |
904 |
|
{ |
905 |
|
parentInputMap.put(KeyStroke.getKeyStroke |
906 |
|
(((KeyStroke)keys[i]).getKeyCode(), convertModifiers |
907 |
|
(((KeyStroke)keys[i]).getModifiers())), |
908 |
|
(String)focusInputMap.get((KeyStroke)keys[i])); |
909 |
|
|
910 |
|
parentInputMap.put(KeyStroke.getKeyStroke |
911 |
|
(((KeyStroke)keys[i]).getKeyCode(), |
912 |
|
((KeyStroke)keys[i]).getModifiers()), |
913 |
|
(String)focusInputMap.get((KeyStroke)keys[i])); |
914 |
|
|
915 |
|
parentActionMap.put |
916 |
|
((String)focusInputMap.get((KeyStroke)keys[i]), new ActionListenerProxy |
917 |
|
(action, (String)focusInputMap.get((KeyStroke)keys[i]))); |
918 |
|
} |
919 |
|
parentInputMap.setParent(list.getInputMap().getParent()); |
920 |
|
parentActionMap.setParent(list.getActionMap().getParent()); |
921 |
|
list.getInputMap().setParent(parentInputMap); |
922 |
|
list.getActionMap().setParent(parentActionMap); |
923 |
} |
} |
924 |
|
|
925 |
/** |
/** |
1250 |
*/ |
*/ |
1251 |
protected void selectNextIndex() |
protected void selectNextIndex() |
1252 |
{ |
{ |
1253 |
int index = list.getSelectedIndex(); |
int index = list.getSelectionModel().getLeadSelectionIndex(); |
1254 |
index++; |
if (index < list.getModel().getSize() - 1) |
1255 |
list.setSelectedIndex(index); |
{ |
1256 |
|
index++; |
1257 |
|
list.setSelectedIndex(index); |
1258 |
|
} |
1259 |
list.ensureIndexIsVisible(index); |
list.ensureIndexIsVisible(index); |
1260 |
} |
} |
1261 |
|
|
1264 |
*/ |
*/ |
1265 |
protected void selectPreviousIndex() |
protected void selectPreviousIndex() |
1266 |
{ |
{ |
1267 |
int index = list.getSelectedIndex(); |
int index = list.getSelectionModel().getLeadSelectionIndex(); |
1268 |
index--; |
if (index > 0) |
1269 |
list.setSelectedIndex(index); |
{ |
1270 |
|
index--; |
1271 |
|
list.setSelectedIndex(index); |
1272 |
|
} |
1273 |
list.ensureIndexIsVisible(index); |
list.ensureIndexIsVisible(index); |
1274 |
} |
} |
1275 |
} |
} |