1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.dishevelled.curate.impl;
25
26 import java.awt.GridLayout;
27
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.KeyAdapter;
31 import java.awt.event.KeyEvent;
32 import java.awt.event.MouseAdapter;
33 import java.awt.event.MouseEvent;
34
35 import java.util.Collection;
36 import java.util.Collections;
37
38 import javax.swing.AbstractAction;
39 import javax.swing.Action;
40 import javax.swing.JLabel;
41 import javax.swing.JList;
42 import javax.swing.JPanel;
43 import javax.swing.JPopupMenu;
44 import javax.swing.JScrollPane;
45 import javax.swing.KeyStroke;
46 import javax.swing.Timer;
47
48 import javax.swing.event.ListSelectionEvent;
49 import javax.swing.event.ListSelectionListener;
50
51 import ca.odell.glazedlists.EventList;
52 import ca.odell.glazedlists.GlazedLists;
53
54 import ca.odell.glazedlists.event.ListEvent;
55 import ca.odell.glazedlists.event.ListEventListener;
56
57 import ca.odell.glazedlists.swing.EventListModel;
58 import ca.odell.glazedlists.swing.EventSelectionModel;
59
60 import org.dishevelled.curate.CullView;
61
62 import org.dishevelled.iconbundle.tango.TangoProject;
63
64 import org.dishevelled.identify.IdentifiableAction;
65 import org.dishevelled.identify.IdMenuItem;
66
67 import org.dishevelled.layout.LabelFieldPanel;
68
69
70
71
72
73
74
75
76 public final class CullPanel<E>
77 extends JPanel
78 implements CullView<E>
79 {
80
81 private static final int DEFAULT_ASSIST_RATE = 1000;
82
83
84 private Collection<E> input;
85
86
87 private EventList<E> remaining;
88
89
90 private EventList<E> selectedRemaining;
91
92
93 private final JList remainingList;
94
95
96 private final JLabel remainingLabel;
97
98
99 private final ListEventListener<E> remainingLabelListener;
100
101
102 private EventList<E> removed;
103
104
105 private final JList removedList;
106
107
108 private final JLabel removedLabel;
109
110
111 private final ListEventListener<E> removedLabelListener;
112
113
114 private final Assist assist;
115
116
117 private final IdentifiableAction removeAction;
118
119
120 private final Action removeAllAction;
121
122
123 private final IdentifiableAction startAssistAction;
124
125
126 private final IdentifiableAction stopAssistAction;
127
128
129 private final Action toggleAssistAction;
130
131
132 private final JPopupMenu contextMenu;
133
134
135
136
137
138 public CullPanel()
139 {
140 super();
141
142 input = Collections.emptyList();
143 remaining = GlazedLists.eventList(input);
144 removed = GlazedLists.eventList(input);
145
146 remainingLabel = new JLabel(" - ");
147 remainingLabelListener = new ListEventListener<E>()
148 {
149
150 public void listChanged(final ListEvent<E> event)
151 {
152 StringBuffer sb = new StringBuffer();
153 sb.append(remaining.size());
154 sb.append(" of ");
155 sb.append(input.size());
156 remainingLabel.setText(sb.toString());
157 }
158 };
159 remainingList = new JList(new EventListModel<E>(remaining));
160
161 EventSelectionModel<E> remainingSelectionModel = new EventSelectionModel<E>(remaining);
162 remainingSelectionModel.setSelectionMode(EventSelectionModel.MULTIPLE_INTERVAL_SELECTION);
163 remainingList.setSelectionModel(remainingSelectionModel);
164 selectedRemaining = remainingSelectionModel.getSelected();
165
166 removedLabel = new JLabel(" - ");
167 removedLabelListener = new ListEventListener<E>()
168 {
169
170 public void listChanged(final ListEvent<E> event)
171 {
172 StringBuffer sb = new StringBuffer();
173 sb.append(removed.size());
174 sb.append(" of ");
175 sb.append(input.size());
176 removedLabel.setText(sb.toString());
177 }
178 };
179 removedList = new JList(new EventListModel<E>(removed));
180
181 removeAction = new IdentifiableAction("Remove", TangoProject.LIST_REMOVE)
182 {
183
184 public void actionPerformed(final ActionEvent event)
185 {
186 EventSelectionModel<E> remainingSelectionModel = (EventSelectionModel<E>) remainingList.getSelectionModel();
187 int maxSelectionIndex = Math.min(remainingSelectionModel.getMaxSelectionIndex() + 1, remaining.size() - 1);
188 int selectionSize = selectedRemaining.size();
189 int newSelectionIndex = maxSelectionIndex - selectionSize;
190 removed.addAll(selectedRemaining);
191 remaining.removeAll(selectedRemaining);
192 remainingSelectionModel.setSelectionInterval(newSelectionIndex, newSelectionIndex);
193 remainingList.ensureIndexIsVisible(newSelectionIndex);
194 if (assist.isRunning())
195 {
196 assist.stop();
197 assist.start();
198 }
199 }
200 };
201
202 removeAllAction = new AbstractAction("Remove all")
203 {
204
205 public void actionPerformed(final ActionEvent event)
206 {
207 if (assist.isRunning())
208 {
209 assist.stop();
210 }
211 removed.addAll(remaining);
212 remaining.clear();
213 }
214 };
215
216 assist = new Assist(DEFAULT_ASSIST_RATE);
217
218 startAssistAction = new IdentifiableAction("Start assist", TangoProject.MEDIA_PLAYBACK_START)
219 {
220
221 public void actionPerformed(final ActionEvent event)
222 {
223 if (!assist.isRunning())
224 {
225 assist.start();
226 }
227 }
228 };
229
230
231 stopAssistAction = new IdentifiableAction("Stop assist", TangoProject.MEDIA_PLAYBACK_STOP)
232 {
233
234 public void actionPerformed(final ActionEvent event)
235 {
236 if (assist.isRunning())
237 {
238 assist.stop();
239 }
240 }
241 };
242 stopAssistAction.setEnabled(false);
243
244 toggleAssistAction = new AbstractAction("Toggle assist")
245 {
246
247 public void actionPerformed(final ActionEvent event)
248 {
249 if (assist.isRunning())
250 {
251 assist.stop();
252 }
253 else
254 {
255 assist.start();
256 }
257 }
258 };
259
260 remainingList.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "remove");
261 remainingList.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "remove");
262 remainingList.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0), "remove");
263 remainingList.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "remove");
264 remainingList.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "toggleAssist");
265 remainingList.getActionMap().put("remove", removeAction);
266 remainingList.getActionMap().put("toggleAssist", toggleAssistAction);
267
268
269 remainingList.addKeyListener(new KeyAdapter()
270 {
271
272 public void keyPressed(final KeyEvent event)
273 {
274 if (assist.isRunning())
275 {
276 if (KeyEvent.VK_UP == event.getKeyCode())
277 {
278 assist.stop();
279 }
280 }
281 }
282 });
283
284
285 remainingList.addListSelectionListener(new ListSelectionListener()
286 {
287
288 public void valueChanged(final ListSelectionEvent event)
289 {
290 if (assist.isRunning())
291 {
292 if (remainingList.getSelectedIndices().length > 1)
293 {
294 assist.stop();
295 }
296 }
297 }
298 });
299
300
301
302 contextMenu = new JPopupMenu();
303 contextMenu.add(new IdMenuItem(removeAction));
304 contextMenu.add(removeAllAction);
305 contextMenu.addSeparator();
306 contextMenu.add(new IdMenuItem(startAssistAction));
307
308
309
310 remainingList.addMouseListener(new MouseAdapter()
311 {
312
313 public void mousePressed(final MouseEvent event) {
314 if (assist.isRunning())
315 {
316 assist.stop();
317 }
318 if (event.isPopupTrigger())
319 {
320 showContextMenu(event);
321 }
322 }
323
324
325 public void mouseReleased(final MouseEvent event) {
326 if (event.isPopupTrigger())
327 {
328 showContextMenu(event);
329 }
330 }
331
332
333
334
335 private void showContextMenu(final MouseEvent event)
336 {
337 contextMenu.show(event.getComponent(), event.getX(), event.getY());
338 }
339 });
340
341 layoutComponents();
342 }
343
344
345
346
347
348 private void layoutComponents()
349 {
350 LabelFieldPanel west = new LabelFieldPanel();
351 west.addField("Remaining:", remainingLabel);
352 west.addFinalField(new JScrollPane(remainingList));
353
354 LabelFieldPanel east = new LabelFieldPanel();
355 east.addField("Removed:", removedLabel);
356 east.addFinalField(new JScrollPane(removedList));
357
358 setLayout(new GridLayout(1, 2, 10, 0));
359 add(west);
360 add(east);
361
362 }
363
364
365 public void setInput(final Collection<E> input)
366 {
367 if (input == null)
368 {
369 throw new IllegalArgumentException("input must not be null");
370 }
371 this.input = input;
372
373
374 try
375 {
376 remaining.removeListEventListener(remainingLabelListener);
377 }
378 catch (IllegalArgumentException e)
379 {
380
381 }
382 try
383 {
384 removed.removeListEventListener(removedLabelListener);
385 }
386 catch (IllegalArgumentException e)
387 {
388
389 }
390
391 remaining = GlazedLists.eventList(input);
392 removed = GlazedLists.eventList(Collections.<E>emptyList());
393
394 remainingList.setModel(new EventListModel<E>(remaining));
395 removedList.setModel(new EventListModel<E>(removed));
396
397 EventSelectionModel<E> remainingSelectionModel = new EventSelectionModel<E>(remaining);
398 remainingList.setSelectionModel(remainingSelectionModel);
399 remainingSelectionModel.setSelectionMode(EventSelectionModel.MULTIPLE_INTERVAL_SELECTION);
400 selectedRemaining = remainingSelectionModel.getSelected();
401
402 remaining.addListEventListener(remainingLabelListener);
403 removed.addListEventListener(removedLabelListener);
404
405
406 remainingLabelListener.listChanged(null);
407 removedLabelListener.listChanged(null);
408 }
409
410
411 public Collection<E> getRemaining()
412 {
413 return Collections.unmodifiableList(remaining);
414 }
415
416
417 public Collection<E> getRemoved()
418 {
419 return Collections.unmodifiableList(removed);
420 }
421
422
423
424
425
426
427
428 IdentifiableAction getRemoveAction()
429 {
430 return removeAction;
431 }
432
433
434
435
436
437
438
439 Action getRemoveAllAction()
440 {
441 return removeAllAction;
442 }
443
444
445
446
447
448
449
450 IdentifiableAction getStartAssistAction()
451 {
452 return startAssistAction;
453 }
454
455
456
457
458
459
460
461 IdentifiableAction getStopAssistAction()
462 {
463 return stopAssistAction;
464 }
465
466
467
468
469 private class Assist
470 {
471
472 private int rate;
473
474
475 private boolean running;
476
477
478 private Timer timer;
479
480
481
482
483
484
485
486 Assist(final int rate)
487 {
488 this.rate = rate;
489 this.running = false;
490 timer = new Timer(0, new ActionListener()
491 {
492
493 public void actionPerformed(final ActionEvent event)
494 {
495 EventSelectionModel<E> remainingSelectionModel = (EventSelectionModel<E>) remainingList.getSelectionModel();
496 int maxSelectionIndex = Math.min(remaining.size() - 1, remainingSelectionModel.getMaxSelectionIndex() + 1);
497 remainingSelectionModel.setSelectionInterval(maxSelectionIndex, maxSelectionIndex);
498 remainingList.ensureIndexIsVisible(maxSelectionIndex);
499 }
500 });
501 timer.setRepeats(true);
502 }
503
504
505
506
507
508
509
510 void setRate(final int rate)
511 {
512 this.rate = rate;
513 }
514
515
516
517
518
519
520 boolean isRunning()
521 {
522 return running;
523 }
524
525
526
527
528 void start()
529 {
530 running = true;
531 timer.setDelay(rate);
532 timer.setInitialDelay(rate);
533 startAssistAction.setEnabled(false);
534 stopAssistAction.setEnabled(true);
535 timer.start();
536 }
537
538
539
540
541 void stop()
542 {
543 running = false;
544 startAssistAction.setEnabled(true);
545 stopAssistAction.setEnabled(false);
546 timer.stop();
547 }
548 }
549 }