View Javadoc
1   /*
2   
3       dsh-eventlist-view  Views for event lists.
4       Copyright (c) 2010-2019 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.eventlist.view;
25  
26  import static org.dishevelled.iconbundle.tango.TangoProject.EDIT_COPY;
27  import static org.dishevelled.iconbundle.tango.TangoProject.EDIT_CUT;
28  import static org.dishevelled.iconbundle.tango.TangoProject.EDIT_PASTE;
29  import static org.dishevelled.iconbundle.tango.TangoProject.EDIT_SELECT_ALL;
30  import static org.dishevelled.iconbundle.tango.TangoProject.EXTRA_SMALL;
31  import static org.dishevelled.iconbundle.tango.TangoProject.LIST_ADD;
32  import static org.dishevelled.iconbundle.tango.TangoProject.LIST_REMOVE;
33  import static org.dishevelled.iconbundle.tango.TangoProject.SIZES;
34  
35  import java.awt.event.ActionEvent;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  import javax.swing.AbstractAction;
41  import javax.swing.Box;
42  import javax.swing.BoxLayout;
43  import javax.swing.JCheckBoxMenuItem;
44  import javax.swing.JLabel;
45  import javax.swing.JPanel;
46  import javax.swing.JPopupMenu;
47  import javax.swing.ListSelectionModel;
48  
49  import javax.swing.border.EmptyBorder;
50  
51  import javax.swing.event.EventListenerList;
52  import javax.swing.event.ListSelectionEvent;
53  import javax.swing.event.ListSelectionListener;
54  
55  import ca.odell.glazedlists.EventList;
56  import ca.odell.glazedlists.ListSelection;
57  
58  import ca.odell.glazedlists.event.ListEvent;
59  import ca.odell.glazedlists.event.ListEventListener;
60  
61  import org.dishevelled.iconbundle.IconSize;
62  
63  import org.dishevelled.identify.ContextMenuButton;
64  import org.dishevelled.identify.ContextMenuListener;
65  import org.dishevelled.identify.IdPopupMenu;
66  import org.dishevelled.identify.IdToolBar;
67  import org.dishevelled.identify.IdentifiableAction;
68  
69  /**
70   * Abstract event list view.
71   *
72   * @param <E> model element type
73   * @author  Michael Heuer
74   */
75  public abstract class AbstractEventListView<E>
76      extends JPanel
77      implements EventListView<E>
78  {
79      /** Event list view support. */
80      private final EventListViewSupport<E> eventListViewSupport;
81  
82      /** Label. */
83      private final JLabel label;
84  
85      /** Tool bar. */
86      private final IdToolBar toolBar;
87  
88      /** Context menu. */
89      private final IdPopupMenu contextMenu;
90  
91      /** Tool bar context menu. */
92      private final JPopupMenu toolBarContextMenu;
93  
94      /** Tool bar context menu button. */
95      private final ContextMenuButton contextMenuButton;
96  
97      /** List selection model adapter. */
98      private final ListSelectionModelAdapter listSelectionModelAdapter;
99  
100     /** Select all action. */
101     private final IdentifiableAction selectAllAction = new IdentifiableAction("Select all", EDIT_SELECT_ALL)
102         {
103                 @Override
104                 public void actionPerformed(final ActionEvent event)
105                 {
106                     selectAll();
107                 }
108         };
109 
110     /** Clear selection action. */
111     private final AbstractAction clearSelectionAction = new AbstractAction("Clear selection")
112         {
113                 @Override
114                 public void actionPerformed(final ActionEvent event)
115                 {
116                     clearSelection();
117                 }
118         };
119 
120     /** Invert selection action. */
121     private final AbstractAction invertSelectionAction = new AbstractAction("Invert selection")
122         {
123                 @Override
124                 public void actionPerformed(final ActionEvent event)
125                 {
126                     invertSelection();
127                 }
128         };
129 
130     /** Cut action. */
131     private final IdentifiableAction cutAction = new IdentifiableAction("Cut", EDIT_CUT)
132         {
133                 @Override
134                 public void actionPerformed(final ActionEvent event)
135                 {
136                     cut();
137                 }
138         };
139 
140     /** Copy action. */
141     private final IdentifiableAction copyAction = new IdentifiableAction("Copy", EDIT_COPY)
142         {
143                 @Override
144                 public void actionPerformed(final ActionEvent event)
145                 {
146                     copy();
147                 }
148         };
149 
150     /** Paste action. */
151     private final IdentifiableAction pasteAction = new IdentifiableAction("Paste", EDIT_PASTE)
152         {
153                 @Override
154                 public void actionPerformed(final ActionEvent event)
155                 {
156                     paste();
157                 }
158         };
159 
160     /** Add action. */
161     private final IdentifiableAction addAction = new IdentifiableAction("Add", LIST_ADD)
162         {
163                 @Override
164                 public void actionPerformed(final ActionEvent event)
165                 {
166                     add();
167                 }
168         };
169 
170     /** Remove action. */
171     private final IdentifiableAction removeAction = new IdentifiableAction("Remove", LIST_REMOVE)
172         {
173                 @Override
174                 public void actionPerformed(final ActionEvent event)
175                 {
176                     remove();
177                 }
178         };
179 
180     /** Remove all action. */
181     private final AbstractAction removeAllAction = new AbstractAction("Remove all")
182         {
183                 @Override
184                 public void actionPerformed(final ActionEvent event)
185                 {
186                     clear();
187                 }
188         };
189 
190     /** Listener. */
191     private ListEventListener<E> listener = new ListEventListener<E>()
192         {
193             @Override
194             public void listChanged(final ListEvent<E> event)
195             {
196                 updateListActions();
197             }
198         };
199 
200     /** Selection listener. */
201     private ListSelection.Listener selectionListener = new ListSelection.Listener()
202         {
203             @Override
204             public void selectionChanged(final int index0, final int index1)
205             {
206                 updateSelectionActions();
207             }
208         };
209 
210 
211     /**
212      * Create a new abstract event list view with the specified model.
213      *
214      * @param model model, must not be null
215      */
216     protected AbstractEventListView(final EventList<E> model)
217     {
218         super();
219         setOpaque(false);
220         eventListViewSupport = new EventListViewSupport<E>(model);
221         getModel().addListEventListener(listener);
222         getSelectionModel().addSelectionListener(selectionListener);
223         updateListActions();
224         updateSelectionActions();
225 
226         label = new JLabel();
227         label.setAlignmentY(-1.0f);
228         label.setBorder(new EmptyBorder(0, 2, 2, 0));
229 
230         contextMenu = new IdPopupMenu();
231         contextMenu.add(getCutAction(), EXTRA_SMALL);
232         contextMenu.add(getCopyAction(), EXTRA_SMALL);
233         contextMenu.add(getPasteAction(), EXTRA_SMALL);
234         contextMenu.addSeparator();
235         contextMenu.add(getSelectAllAction(), EXTRA_SMALL);
236         contextMenu.add(getClearSelectionAction());
237         contextMenu.add(getInvertSelectionAction());
238         contextMenu.addSeparator();
239         contextMenu.add(getAddAction(), EXTRA_SMALL);
240         contextMenu.add(getRemoveAction(), EXTRA_SMALL);
241         contextMenu.add(getRemoveAllAction());
242 
243         toolBar = new IdToolBar();
244         toolBar.setOpaque(false);
245         toolBar.setBorder(new EmptyBorder(0, 0, 0, 0));
246         toolBar.add(getAddAction());
247         toolBar.add(getRemoveAction());
248         contextMenuButton = toolBar.add(contextMenu);
249 
250         toolBarContextMenu = new JPopupMenu();
251         for (Object menuItem : toolBar.getDisplayMenuItems())
252         {
253             toolBarContextMenu.add((JCheckBoxMenuItem) menuItem);
254         }
255         toolBarContextMenu.addSeparator();
256         for (Object iconSize : SIZES)
257         {
258             toolBarContextMenu.add(toolBar.createIconSizeMenuItem((IconSize) iconSize));
259         }
260         toolBar.setIconSize(EXTRA_SMALL);
261         toolBar.addMouseListener(new ContextMenuListener(toolBarContextMenu));
262 
263         listSelectionModelAdapter = new ListSelectionModelAdapter();
264     }
265 
266 
267     /**
268      * Update list actions.
269      */
270     private void updateListActions()
271     {
272         selectAllAction.setEnabled(!isEmpty());
273         invertSelectionAction.setEnabled(!isEmpty());
274         removeAllAction.setEnabled(!isEmpty());
275     }
276 
277     /**
278      * Update selection actions.
279      */
280     private void updateSelectionActions()
281     {
282         copyAction.setEnabled(!isSelectionEmpty());
283         cutAction.setEnabled(!isSelectionEmpty());
284         removeAction.setEnabled(!isSelectionEmpty());
285         clearSelectionAction.setEnabled(!isSelectionEmpty());
286     }
287 
288     /**
289      * Return true if the model is empty.
290      *
291      * @return true if the model is empty.
292      */
293     public final boolean isEmpty()
294     {
295         return getModel().isEmpty();
296     }
297 
298     /**
299      * Return true if the selection is empty.
300      *
301      * @return true if the selection is empty.
302      */
303     public final boolean isSelectionEmpty()
304     {
305         return getSelectionModel().getSelected().isEmpty();
306     }
307 
308     /**
309      * Select all.
310      */
311     public final void selectAll()
312     {
313         getSelectionModel().selectAll();
314     }
315 
316     /**
317      * Clear selection.
318      */
319     public final void clearSelection()
320     {
321         getSelectionModel().deselectAll();
322     }
323 
324     /**
325      * Invert selection.
326      */
327     public final void invertSelection()
328     {
329         getSelectionModel().invertSelection();
330     }
331 
332     /**
333      * Cut.
334      */
335     public final void cut()
336     {
337         if (!isSelectionEmpty())
338         {
339             cut(new ArrayList<E>(getSelectionModel().getSelected()));
340         }
341     }
342 
343     /**
344      * Copy.
345      */
346     public final void copy()
347     {
348         if (!isSelectionEmpty())
349         {
350             copy(new ArrayList<E>(getSelectionModel().getSelected()));
351         }
352     }
353 
354     /**
355      * Cut the specific list of elements to the clipboard.
356      *
357      * @param toCut list of elements to cut, must not be null
358      */
359     protected abstract void cut(List<E> toCut);
360 
361     /**
362      * Copy the specific list of elements to the clipboard.
363      *
364      * @param toCopy list of elements to copy, must not be null
365      */
366     protected abstract void copy(List<E> toCopy);
367 
368     /**
369      * Add.
370      */
371     public abstract void add();
372 
373     /**
374      * Paste.
375      */
376     public abstract void paste();
377 
378     /**
379      * Remove.
380      */
381     public final void remove()
382     {
383         getSelectionModel().getSelected().clear();
384     }
385 
386     /**
387      * Remove all/clear.
388      */
389     public final void clear()
390     {
391         getModel().clear();
392     }
393 
394     /**
395      * Return the select all action.
396      *
397      * @return the select all action
398      */
399     protected final IdentifiableAction getSelectAllAction()
400     {
401         return selectAllAction;
402     }
403 
404     /**
405      * Return the clear selection action.
406      *
407      * @return the clear selection action
408      */
409     protected final AbstractAction getClearSelectionAction()
410     {
411         return clearSelectionAction;
412     }
413 
414     /**
415      * Return the invert selection action.
416      *
417      * @return the invert selection action
418      */
419     protected final AbstractAction getInvertSelectionAction()
420     {
421         return invertSelectionAction;
422     }
423 
424     /**
425      * Return the cut action.
426      *
427      * @return the cut action
428      */
429     protected final IdentifiableAction getCutAction()
430     {
431         return cutAction;
432     }
433 
434     /**
435      * Return the copy action.
436      *
437      * @return the copy action
438      */
439     protected final IdentifiableAction getCopyAction()
440     {
441         return copyAction;
442     }
443 
444     /**
445      * Return the paste action.
446      *
447      * @return the paste action
448      */
449     protected final IdentifiableAction getPasteAction()
450     {
451         return pasteAction;
452     }
453 
454     /**
455      * Return the add action.
456      *
457      * @return the add action
458      */
459     protected final IdentifiableAction getAddAction()
460     {
461         return addAction;
462     }
463 
464     /**
465      * Return the remove action.
466      *
467      * @return the remove action
468      */
469     protected final IdentifiableAction getRemoveAction()
470     {
471         return removeAction;
472     }
473 
474     /**
475      * Return the remove all action.
476      *
477      * @return the remove all action
478      */
479     protected final AbstractAction getRemoveAllAction()
480     {
481         return removeAllAction;
482     }
483 
484     /**
485      * Return the label.
486      *
487      * @return the label
488      */
489     protected final JLabel getLabel()
490     {
491         return label;
492     }
493 
494     /**
495      * Return the tool bar.
496      *
497      * @return the tool bar
498      */
499     protected final IdToolBar getToolBar()
500     {
501         return toolBar;
502     }
503 
504     /**
505      * Return the context menu.
506      *
507      * @return the context menu
508      */
509     protected final IdPopupMenu getContextMenu()
510     {
511         return contextMenu;
512     }
513 
514     /**
515      * Return the tool bar context menu.
516      *
517      * @return the tool bar context menu
518      */
519     protected final JPopupMenu getToolBarContextMenu()
520     {
521         return toolBarContextMenu;
522     }
523 
524     /**
525      * Return the context menu button.
526      *
527      * @return the context menu button
528      */
529     protected final ContextMenuButton getContextMenuButton()
530     {
531         return contextMenuButton;
532     }
533 
534     /**
535      * Create and return a new tool bar panel.
536      *
537      * @return a new tool bar panel
538      */
539     protected final JPanel createToolBarPanel()
540     {
541         JPanel panel = new JPanel();
542         panel.setOpaque(false);
543         panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
544         panel.add(getLabel());
545         panel.add(Box.createGlue());
546         panel.add(Box.createGlue());
547         panel.add(getToolBar());
548         panel.addMouseListener(new ContextMenuListener(getToolBarContextMenu()));
549         return panel;
550     }
551 
552     /**
553      * Return the list selection model adapter.
554      *
555      * @return the list selection model adapter
556      */
557     protected ListSelectionModelAdapter getListSelectionModelAdapter()
558     {
559         return listSelectionModelAdapter;
560     }
561 
562     @Override
563     public final EventList<E> getModel()
564     {
565         return eventListViewSupport.getModel();
566     }
567 
568     @Override
569     public final ListSelection<E> getSelectionModel()
570     {
571         return eventListViewSupport.getSelectionModel();
572     }
573 
574     /**
575      * Release the resources consumed by this abstract event list view
576      * so that it may eventually be garbage collected.  Subclasses that override
577      * this method should call <code>super.dispose()</code>.
578      */
579     public void dispose()
580     {
581         getModel().removeListEventListener(listener);
582         getSelectionModel().removeSelectionListener(selectionListener);
583         getSelectionModel().removeSelectionListener(listSelectionModelAdapter.getSelectionListener());
584     }
585 
586     /**
587      * List selection model that delegates to {@link #getSelectionModel}.
588      */
589     protected final class ListSelectionModelAdapter implements ListSelectionModel
590     {
591         /** Event listener list. */
592         private final EventListenerList listenerList;
593 
594         /** True if the selection is undergoing a series of changes. */
595         private boolean valueIsAdjusting = false;
596 
597         /** Full start index. */
598         private int fullIndex0 = -1;
599 
600         /** Full end index. */
601         private int fullIndex1 = -1;
602 
603         /** List selection listener. */
604         private final ListSelection.Listener listSelectionListener = new ListSelection.Listener()
605             {
606                 @Override
607                 public void selectionChanged(final int index0, final int index1)
608                 {
609                     fireSelectionChanged(index0, index1);
610                 }
611             };
612 
613 
614         /**
615          * Create a new list selection model adapter.
616          */
617         private ListSelectionModelAdapter()
618         {
619             listenerList = new EventListenerList();
620             getSelectionModel().addSelectionListener(listSelectionListener);
621         }
622 
623 
624         @Override
625         public void setSelectionInterval(final int index0, final int index1)
626         {
627             getSelectionModel().setSelection(index0, index1);
628         }
629 
630         @Override
631         public void addSelectionInterval(final int index0, final int index1)
632         {
633             getSelectionModel().select(index0, index1);
634         }
635 
636         @Override
637         public void removeSelectionInterval(final int index0, final int index1)
638         {
639             getSelectionModel().deselect(index0, index1);
640         }
641 
642         @Override
643         public int getMinSelectionIndex()
644         {
645             return getSelectionModel().getMinSelectionIndex();
646         }
647 
648         @Override
649         public int getMaxSelectionIndex()
650         {
651             return getSelectionModel().getMaxSelectionIndex();
652         }
653 
654         @Override
655         public boolean isSelectedIndex(final int index)
656         {
657             return getSelectionModel().isSelected(index);
658         }
659 
660         @Override
661         public int getAnchorSelectionIndex()
662         {
663             return getSelectionModel().getAnchorSelectionIndex();
664         }
665 
666         @Override
667         public void setAnchorSelectionIndex(final int index)
668         {
669             getSelectionModel().setAnchorSelectionIndex(index);
670         }
671 
672         @Override
673         public int getLeadSelectionIndex()
674         {
675             return getSelectionModel().getLeadSelectionIndex();
676         }
677 
678         @Override
679         public void setLeadSelectionIndex(final int index)
680         {
681             getSelectionModel().setLeadSelectionIndex(index);
682         }
683 
684         @Override
685         public void clearSelection()
686         {
687             getSelectionModel().deselectAll();
688         }
689 
690         @Override
691         public boolean isSelectionEmpty()
692         {
693             return getSelectionModel().getSelected().isEmpty();
694         }
695 
696         @Override
697         public void insertIndexInterval(final int index, final int length, final boolean before)
698         {
699             // empty
700         }
701 
702         @Override
703         public void removeIndexInterval(final int index0, final int index1)
704         {
705             // empty
706         }
707 
708         @Override
709         public void setValueIsAdjusting(final boolean valueIsAdjusting)
710         {
711             if (!valueIsAdjusting)
712             {
713                 if ((fullIndex0 != -1) && (fullIndex1 != -1))
714                 {
715                     fireSelectionChanged(fullIndex0, fullIndex1);
716                     fullIndex0 = -1;
717                     fullIndex1 = -1;
718                 }
719             }
720             this.valueIsAdjusting = valueIsAdjusting;
721         }
722 
723         @Override
724         public boolean getValueIsAdjusting()
725         {
726             return valueIsAdjusting;
727         }
728 
729         @Override
730         public void setSelectionMode(final int selectionMode)
731         {
732             getSelectionModel().setSelectionMode(selectionMode);
733         }
734 
735         @Override
736         public int getSelectionMode()
737         {
738             return getSelectionModel().getSelectionMode();
739         }
740 
741         @Override
742         public void addListSelectionListener(final ListSelectionListener listener)
743         {
744             listenerList.add(ListSelectionListener.class, listener);
745         }
746 
747         @Override
748         public void removeListSelectionListener(final ListSelectionListener listener)
749         {
750             listenerList.remove(ListSelectionListener.class, listener);
751         }
752 
753         /**
754          * Fire a selection changed event to all registered list selection listeners.
755          *
756          * @param index0 first index
757          * @param index1 second index
758          */
759         private void fireSelectionChanged(final int index0, final int index1)
760         {
761             if (valueIsAdjusting)
762             {
763                 if ((fullIndex0 == -1) || (index0 < fullIndex0))
764                 {
765                     fullIndex0 = index0;
766                 }
767                 if ((fullIndex1 == -1) || (index1 > fullIndex1))
768                 {
769                     fullIndex1 = index1;
770                 }
771             }
772             Object[] listeners = listenerList.getListenerList();
773             ListSelectionEvent e = null;
774 
775             for (int i = listeners.length - 2; i >= 0; i -= 2)
776             {
777                 if (listeners[i] == ListSelectionListener.class)
778                 {
779                     // lazily create the event
780                     if (e == null)
781                     {
782                         e = new ListSelectionEvent(this, index0, index1, valueIsAdjusting);
783                     }
784                     ((ListSelectionListener) listeners[i + 1]).valueChanged(e);
785                 }
786             }
787         }
788 
789         /**
790          * Return the selection listener.
791          *
792          * @return the selection listener
793          */
794         private ListSelection.Listener getSelectionListener()
795         {
796             return listSelectionListener;
797         }
798     }
799 }