View Javadoc

1   /*
2   
3       dsh-venn  Lightweight components for venn diagrams.
4       Copyright (c) 2009-2013 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.venn.swing;
25  
26  import java.awt.GridLayout;
27  
28  import java.beans.PropertyChangeEvent;
29  import java.beans.PropertyChangeListener;
30  
31  import java.util.List;
32  import java.util.Set;
33  
34  import javax.swing.Box;
35  import javax.swing.JList;
36  import javax.swing.JPanel;
37  import javax.swing.JScrollPane;
38  import javax.swing.ListSelectionModel;
39  
40  import javax.swing.event.ListSelectionEvent;
41  import javax.swing.event.ListSelectionListener;
42  
43  import ca.odell.glazedlists.swing.EventListModel;
44  
45  import org.dishevelled.layout.LabelFieldPanel;
46  
47  import org.dishevelled.observable.event.SetChangeEvent;
48  import org.dishevelled.observable.event.SetChangeListener;
49  
50  import org.dishevelled.venn.TernaryVennModel;
51  
52  /**
53   * Tertiary venn diagram list.
54   *
55   * @param <E> value type
56   * @author  Michael Heuer
57   * @version $Revision$ $Date$
58   */
59  public final class TernaryVennList<E>
60      extends AbstractTernaryVennDiagram<E>
61  {
62      /** Contents of the first set. */
63      private final JList first = new JList();
64  
65      /** Contents of the second set. */
66      private final JList second = new JList();
67  
68      /** Contents of the third set. */
69      private final JList third = new JList();
70  
71      /** Contents of the first only view. */
72      private final JList firstOnly = new JList();
73  
74      /** Contents for the second only view. */
75      private final JList secondOnly = new JList();
76  
77      /** Contents for the third only view. */
78      private final JList thirdOnly = new JList();
79  
80      /** Contents for the first second view. */
81      private final JList firstSecond = new JList();
82  
83      /** Contents for the first third view. */
84      private final JList firstThird = new JList();
85  
86      /** Contents for the second third view. */
87      private final JList secondThird = new JList();
88  
89      /** Contents of the intersection view. */
90      private final JList intersection = new JList();
91  
92      /** Contents of the union view. */
93      private final JList union = new JList();
94  
95      /** Adapter for the first list model. */
96      private ObservableSetEventListAdapter<E> firstAdapter;
97  
98      /** Adapter for the second list model. */
99      private ObservableSetEventListAdapter<E> secondAdapter;
100 
101     /** Adapter for the third list model. */
102     private ObservableSetEventListAdapter<E> thirdAdapter;
103 
104     /** Adapter for the first only list model. */
105     private SetEventListAdapter<E> firstOnlyAdapter;
106 
107     /** Adapter for the second only list model. */
108     private SetEventListAdapter<E> secondOnlyAdapter;
109 
110     /** Adapter for the third only list model. */
111     private SetEventListAdapter<E> thirdOnlyAdapter;
112 
113     /** Adapter for the first second list model. */
114     private SetEventListAdapter<E> firstSecondAdapter;
115 
116     /** Adapter for the first third list model. */
117     private SetEventListAdapter<E> firstThirdAdapter;
118 
119     /** Adapter for the second third list model. */
120     private SetEventListAdapter<E> secondThirdAdapter;
121 
122     /** Adapter for the intersection list model. */
123     private SetEventListAdapter<E> intersectionAdapter;
124 
125     /** Adapter for the union list model. */
126     private SetEventListAdapter<E> unionAdapter;
127 
128     /** Model change listener. */
129     private final PropertyChangeListener modelChange = new PropertyChangeListener()
130     {
131         /** {@inheritDoc} */
132         public void propertyChange(final PropertyChangeEvent event)
133         {
134             uninstallListModels((TernaryVennModel<E>) event.getOldValue());
135             installListModels();
136         }
137     };
138 
139     /** Update list models from model. */
140     private final SetChangeListener<E> updateListModels = new SetChangeListener<E>()
141     {
142         /** {@inheritDoc} */
143         public void setChanged(final SetChangeEvent<E> event)
144         {
145             updateListModels();
146         }
147     };
148 
149     /** Update list selection from model. */
150     private final SetChangeListener<E> updateSelection = new SetChangeListener<E>()
151     {
152         /** {@inheritDoc} */
153         public void setChanged(final SetChangeEvent<E> event)
154         {
155             updateSelection();
156         }
157     };
158 
159 
160     /**
161      * Create a new empty ternary venn list.
162      */
163     public TernaryVennList()
164     {
165         super();
166         installListModels();
167         installSelectionListeners();
168         layoutComponents();
169         addPropertyChangeListener("model", modelChange);
170     }
171 
172     /**
173      * Create a new ternary venn list with the specified sets.
174      *
175      * @param firstLabelText label text for the first set
176      * @param first first set, must not be null
177      * @param secondLabelText label text for the second set
178      * @param second second set, must not be null
179      * @param thirdLabelText label text for the third set
180      * @param third third set, must not be null
181      */
182     public TernaryVennList(final String firstLabelText, final Set<? extends E> first,
183                              final String secondLabelText, final Set<? extends E> second,
184                              final String thirdLabelText, final Set<? extends E> third)
185     {
186         super(firstLabelText, first, secondLabelText, second, thirdLabelText, third);
187         installListModels();
188         installSelectionListeners();
189         layoutComponents();
190         addPropertyChangeListener("model", modelChange);
191     }
192 
193     /**
194      * Create a new ternary venn list with the specified model.
195      *
196      * @param model model for this ternary venn list, must not be null
197      */
198     public TernaryVennList(final TernaryVennModel<E> model)
199     {
200         super(model);
201         installListModels();
202         installSelectionListeners();
203         layoutComponents();
204         addPropertyChangeListener("model", modelChange);
205     }
206 
207 
208     /**
209      * Clear selection.
210      */
211     public void clearSelection()
212     {
213         union.requestFocusInWindow();
214         getModel().selection().clear();
215     }
216 
217     /**
218      * Select all.
219      */
220     public void selectAll()
221     {
222         union.requestFocusInWindow();
223         // todo:  dreadfully inefficient
224         getModel().selection().addAll(getModel().union());
225     }
226 
227     /**
228      * Install list models.
229      */
230     private void installListModels()
231     {
232         firstAdapter = new ObservableSetEventListAdapter<E>(getModel().first());
233         first.setModel(new EventListModel<E>(firstAdapter));
234         secondAdapter = new ObservableSetEventListAdapter<E>(getModel().second());
235         second.setModel(new EventListModel<E>(secondAdapter));
236         thirdAdapter = new ObservableSetEventListAdapter<E>(getModel().third());
237         third.setModel(new EventListModel<E>(thirdAdapter));
238 
239         firstOnlyAdapter = new SetEventListAdapter<E>(getModel().firstOnly());
240         firstOnly.setModel(new EventListModel<E>(firstOnlyAdapter));
241         secondOnlyAdapter = new SetEventListAdapter<E>(getModel().secondOnly());
242         secondOnly.setModel(new EventListModel<E>(secondOnlyAdapter));
243         thirdOnlyAdapter = new SetEventListAdapter<E>(getModel().thirdOnly());
244         thirdOnly.setModel(new EventListModel<E>(thirdOnlyAdapter));
245         firstSecondAdapter = new SetEventListAdapter<E>(getModel().firstSecond());
246         firstSecond.setModel(new EventListModel<E>(firstSecondAdapter));
247         firstThirdAdapter = new SetEventListAdapter<E>(getModel().firstThird());
248         firstThird.setModel(new EventListModel<E>(firstThirdAdapter));
249         secondThirdAdapter = new SetEventListAdapter<E>(getModel().secondThird());
250         secondThird.setModel(new EventListModel<E>(secondThirdAdapter));
251         intersectionAdapter = new SetEventListAdapter<E>(getModel().intersection());
252         intersection.setModel(new EventListModel<E>(intersectionAdapter));
253         unionAdapter = new SetEventListAdapter<E>(getModel().union());
254         union.setModel(new EventListModel<E>(unionAdapter));
255 
256         getModel().first().addSetChangeListener(updateListModels);
257         getModel().second().addSetChangeListener(updateListModels);
258         getModel().third().addSetChangeListener(updateListModels);
259         getModel().first().addSetChangeListener(updateSelection);
260         getModel().second().addSetChangeListener(updateSelection);
261         getModel().third().addSetChangeListener(updateSelection);
262         getModel().selection().addSetChangeListener(updateSelection);
263     }
264 
265     /**
266      * Update list models.
267      */
268     private void updateListModels()
269     {
270         firstOnlyAdapter.updateEventList();
271         secondOnlyAdapter.updateEventList();
272         thirdOnlyAdapter.updateEventList();
273         firstSecondAdapter.updateEventList();
274         firstThirdAdapter.updateEventList();
275         secondThirdAdapter.updateEventList();
276         intersectionAdapter.updateEventList();
277         unionAdapter.updateEventList();
278     }
279 
280     /**
281      * Uninstall list models.
282      *
283      * @param oldModel old model
284      */
285     private void uninstallListModels(final TernaryVennModel<E> oldModel)
286     {
287         firstAdapter.dispose();
288         secondAdapter.dispose();
289         thirdAdapter.dispose();
290         ((EventListModel<E>) first.getModel()).dispose();
291         ((EventListModel<E>) second.getModel()).dispose();
292         ((EventListModel<E>) third.getModel()).dispose();
293         ((EventListModel<E>) firstOnly.getModel()).dispose();
294         ((EventListModel<E>) secondOnly.getModel()).dispose();
295         ((EventListModel<E>) thirdOnly.getModel()).dispose();
296         ((EventListModel<E>) firstSecond.getModel()).dispose();
297         ((EventListModel<E>) firstThird.getModel()).dispose();
298         ((EventListModel<E>) secondThird.getModel()).dispose();
299         ((EventListModel<E>) intersection.getModel()).dispose();
300         ((EventListModel<E>) union.getModel()).dispose();
301         oldModel.first().removeSetChangeListener(updateListModels);
302         oldModel.second().removeSetChangeListener(updateListModels);
303         oldModel.third().removeSetChangeListener(updateListModels);
304         oldModel.first().removeSetChangeListener(updateSelection);
305         oldModel.second().removeSetChangeListener(updateSelection);
306         oldModel.third().removeSetChangeListener(updateSelection);
307         oldModel.selection().removeSetChangeListener(updateSelection);
308     }
309 
310      /**
311      * Install selection listeners.
312      */
313     private void installSelectionListeners()
314     {
315         first.addListSelectionListener(new UpdateSelectionView());
316         second.addListSelectionListener(new UpdateSelectionView());
317         third.addListSelectionListener(new UpdateSelectionView());
318         firstOnly.addListSelectionListener(new UpdateSelectionView());
319         secondOnly.addListSelectionListener(new UpdateSelectionView());
320         thirdOnly.addListSelectionListener(new UpdateSelectionView());
321         firstSecond.addListSelectionListener(new UpdateSelectionView());
322         firstThird.addListSelectionListener(new UpdateSelectionView());
323         secondThird.addListSelectionListener(new UpdateSelectionView());
324         intersection.addListSelectionListener(new UpdateSelectionView());
325         union.addListSelectionListener(new UpdateSelectionView());
326     }
327 
328    /** {@inheritDoc} */
329     protected void updateContents()
330     {
331         // empty
332     }
333 
334     /**
335      * Layout components.
336      */
337     private void layoutComponents()
338     {
339         addFinalField(createMainPanel());
340     }
341 
342     /**
343      * Create and return the main panel.
344      *
345      * @return the main panel
346      */
347     private JPanel createMainPanel()
348     {
349         JPanel panel = new JPanel();
350         panel.setLayout(new GridLayout(3, 5, 12, 12));
351 
352         LabelFieldPanel f = new LabelFieldPanel();
353         f.addLabel(getFirstLabel());
354         f.addFinalField(new JScrollPane(first));
355         panel.add(f);
356 
357         LabelFieldPanel s = new LabelFieldPanel();
358         s.addLabel(getSecondLabel());
359         s.addFinalField(new JScrollPane(second));
360         panel.add(s);
361 
362         LabelFieldPanel t = new LabelFieldPanel();
363         t.addLabel(getThirdLabel());
364         t.addFinalField(new JScrollPane(third));
365         panel.add(t);
366 
367         panel.add(Box.createGlue());
368         panel.add(Box.createGlue());
369 
370         LabelFieldPanel fo = new LabelFieldPanel();
371         fo.addLabel(getFirstOnlyLabel());
372         fo.addFinalField(new JScrollPane(firstOnly));
373         panel.add(fo);
374 
375         LabelFieldPanel so = new LabelFieldPanel();
376         so.addLabel(getSecondOnlyLabel());
377         so.addFinalField(new JScrollPane(secondOnly));
378         panel.add(so);
379 
380         LabelFieldPanel to = new LabelFieldPanel();
381         to.addLabel(getThirdOnlyLabel());
382         to.addFinalField(new JScrollPane(thirdOnly));
383         panel.add(to);
384 
385         panel.add(Box.createGlue());
386         panel.add(Box.createGlue());
387 
388         LabelFieldPanel fs = new LabelFieldPanel();
389         fs.addLabel(getFirstSecondLabel());
390         fs.addFinalField(new JScrollPane(firstSecond));
391         panel.add(fs);
392 
393         LabelFieldPanel ft = new LabelFieldPanel();
394         ft.addLabel(getFirstThirdLabel());
395         ft.addFinalField(new JScrollPane(firstThird));
396         panel.add(ft);
397 
398         LabelFieldPanel st = new LabelFieldPanel();
399         st.addLabel(getSecondThirdLabel());
400         st.addFinalField(new JScrollPane(secondThird));
401         panel.add(st);
402 
403         LabelFieldPanel n = new LabelFieldPanel();
404         n.addLabel(getIntersectionLabel());
405         n.addFinalField(new JScrollPane(intersection));
406         panel.add(n);
407 
408         LabelFieldPanel u = new LabelFieldPanel();
409         u.addLabel(getUnionLabel());
410         u.addFinalField(new JScrollPane(union));
411         panel.add(u);
412         return panel;
413     }
414 
415     /**
416      * Update list selection from the selection view in the model.
417      */
418     private void updateSelection()
419     {
420         if (getModel().selection().isEmpty())
421         {
422             clearSelection(first);
423             clearSelection(second);
424             clearSelection(third);
425             clearSelection(firstOnly);
426             clearSelection(secondOnly);
427             clearSelection(thirdOnly);
428             clearSelection(firstSecond);
429             clearSelection(firstThird);
430             clearSelection(secondThird);
431             clearSelection(intersection);
432             clearSelection(union);
433         }
434         else
435         {
436             // todo:  need element(s) that were added from set change event
437             for (E e : getModel().selection())
438             {
439                 addToSelection(getModel().first(), first, firstAdapter, e);
440                 addToSelection(getModel().second(), second, secondAdapter, e);
441                 addToSelection(getModel().third(), third, thirdAdapter, e);
442                 addToSelection(getModel().firstOnly(), firstOnly, firstOnlyAdapter, e);
443                 addToSelection(getModel().secondOnly(), secondOnly, secondOnlyAdapter, e);
444                 addToSelection(getModel().thirdOnly(), thirdOnly, thirdOnlyAdapter, e);
445                 addToSelection(getModel().firstSecond(), firstSecond, firstSecondAdapter, e);
446                 addToSelection(getModel().firstThird(), firstThird, firstThirdAdapter, e);
447                 addToSelection(getModel().secondThird(), secondThird, secondThirdAdapter, e);
448                 addToSelection(getModel().intersection(), intersection, intersectionAdapter, e);
449                 addToSelection(getModel().union(), union, unionAdapter, e);
450             }
451 
452             removeFromSelection(getModel().first(), first, firstAdapter);
453             removeFromSelection(getModel().second(), second, secondAdapter);
454             removeFromSelection(getModel().third(), third, thirdAdapter);
455             removeFromSelection(getModel().firstOnly(), firstOnly, firstOnlyAdapter);
456             removeFromSelection(getModel().secondOnly(), secondOnly, secondOnlyAdapter);
457             removeFromSelection(getModel().thirdOnly(), thirdOnly, thirdOnlyAdapter);
458             removeFromSelection(getModel().firstSecond(), firstSecond, firstSecondAdapter);
459             removeFromSelection(getModel().firstThird(), firstThird, firstThirdAdapter);
460             removeFromSelection(getModel().secondThird(), secondThird, secondThirdAdapter);
461             removeFromSelection(getModel().intersection(), intersection, intersectionAdapter);
462             removeFromSelection(getModel().union(), union, unionAdapter);
463         }
464     }
465 
466     /**
467      * Clear the selection for the specified list if it is not a focus owner.
468      *
469      * @param list list
470      */
471     private void clearSelection(final JList list)
472     {
473         if (!list.isFocusOwner())
474         {
475             list.clearSelection();
476         }
477     }
478 
479     /**
480      * Add the specified element to the list selection if it is contained
481      * in the specified model and the list is not a focus owner.
482      *
483      * @param model model
484      * @param list list
485      * @param adapter adapter
486      * @param e element
487      */
488     private void addToSelection(final Set<E> model, final JList list, final List<E> adapter, final E e)
489     {
490         if (!list.isFocusOwner() && model.contains(e))
491         {
492             int index = adapter.indexOf(e);
493             list.getSelectionModel().addSelectionInterval(index, index);
494         }
495     }
496 
497     /**
498      * Remove elements from the list selection if they are not present in
499      * the specified model and the list is not a focus owner.
500      *
501      * @param model model
502      * @param list list
503      * @param adapter adapter
504      */
505     private void removeFromSelection(final Set<E> model, final JList list, final List<E> adapter)
506     {
507         if (!list.isFocusOwner())
508         {
509             // todo:  need element(s) that were removed from set change event
510             for (E e : model)
511             {
512                 if (!getModel().selection().contains(e))
513                 {
514                     int index = adapter.indexOf(e);
515                     list.getSelectionModel().removeSelectionInterval(index, index);
516                 }
517             }
518         }
519     }
520 
521     /**
522      * Return the contents of the first set.  The model for the returned
523      * JList should not be changed, as the current model implementation is
524      * synchronized to the ternary venn model backing this venn diagram.
525      *
526      * @return the contents of the first set
527      */
528     public JList getFirst()
529     {
530         return first;
531     }
532 
533     /**
534      * Return the contents of the second set.  The model for the returned
535      * JList should not be changed, as the current model implementation is
536      * synchronized to the ternary venn model backing this venn diagram.
537      *
538      * @return the contents of the second set
539      */
540     public JList getSecond()
541     {
542         return second;
543     }
544 
545     /**
546      * Return the contents of the third set.  The model for the returned
547      * JList should not be changed, as the current model implementation is
548      * synchronized to the ternary venn model backing this venn diagram.
549      *
550      * @return the contents of the thid set
551      */
552     public JList getThird()
553     {
554         return third;
555     }
556 
557     /**
558      * Return the contents of the first only view.  The model for the returned
559      * JList should not be changed, as the current model implementation is
560      * synchronized to the ternary venn model backing this venn diagram.
561      *
562      * @return the contents of the first only view
563      */
564     public JList getFirstOnly()
565     {
566         return firstOnly;
567     }
568 
569     /**
570      * Return the contents of the second only view.  The model for the returned
571      * JList should not be changed, as the current model implementation is
572      * synchronized to the ternary venn model backing this venn diagram.
573      *
574      * @return the contents of the second only view
575      */
576     public JList getSecondOnly()
577     {
578         return secondOnly;
579     }
580 
581     /**
582      * Return the contents of the third only view.  The model for the returned
583      * JList should not be changed, as the current model implementation is
584      * synchronized to the ternary venn model backing this venn diagram.
585      *
586      * @return the contents of the third only view
587      */
588     public JList getThirdOnly()
589     {
590         return thirdOnly;
591     }
592 
593     /**
594      * Return the contents of the first second view.  The model for the returned
595      * JList should not be changed, as the current model implementation is
596      * synchronized to the ternary venn model backing this venn diagram.
597      *
598      * @return the contents of the first second view
599      */
600     public JList getFirstSecond()
601     {
602         return firstSecond;
603     }
604 
605     /**
606      * Return the contents of the first third view.  The model for the returned
607      * JList should not be changed, as the current model implementation is
608      * synchronized to the ternary venn model backing this venn diagram.
609      *
610      * @return the contents of the first third view
611      */
612     public JList getFirstThird()
613     {
614         return firstThird;
615     }
616 
617     /**
618      * Return the contents of the second third view.  The model for the returned
619      * JList should not be changed, as the current model implementation is
620      * synchronized to the ternary venn model backing this venn diagram.
621      *
622      * @return the contents of the second third view
623      */
624     public JList getSecondThird()
625     {
626         return secondThird;
627     }
628 
629     /**
630      * Return the contents of the intersection view.  The model for the returned
631      * JList should not be changed, as the current model implementation is
632      * synchronized to the ternary venn model backing this venn diagram.
633      *
634      * @return the contents of the intersection view
635      */
636     public JList getIntersection()
637     {
638         return intersection;
639     }
640 
641     /**
642      * Return the contents of the union view.  The model for the returned
643      * JList should not be changed, as the current model implementation is
644      * synchronized to the ternary venn model backing this venn diagram.
645      *
646      * @return the contents of the union view
647      */
648     public JList getUnion()
649     {
650         return union;
651     }
652 
653     /**
654      * Update selection view.
655      */
656     private class UpdateSelectionView implements ListSelectionListener
657     {
658         /** {@inheritDoc} */
659         public void valueChanged(final ListSelectionEvent event)
660         {
661             JList list = (JList) event.getSource();
662             if (list.isFocusOwner() && !event.getValueIsAdjusting())
663             {
664                 ListSelectionModel selectionModel = list.getSelectionModel();
665                 for (int index = event.getFirstIndex(); index < (event.getLastIndex() + 1); index++)
666                 {
667                     E e = (E) list.getModel().getElementAt(index);
668                     if (selectionModel.isSelectedIndex(index))
669                     {
670                         if (!getModel().selection().contains(e))
671                         {
672                             getModel().selection().add(e);
673                         }
674                     }
675                     else
676                     {
677                         if (getModel().selection().contains(e))
678                         {
679                             getModel().selection().remove(e);
680                         }
681                     }
682                 }
683             }
684             // todo:  may need to remove from selection view those
685             //    elements not present in model for focused list
686             //    e.g.  say "bar" is selected, select "foo" in First only
687         }
688     }
689 }