All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dishevelled.venn.swing.BinaryVennList Maven / Gradle / Ivy

There is a newer version: 1.2
Show newest version
/*

    dsh-venn  Lightweight components for venn diagrams.
    Copyright (c) 2009-2013 held jointly by the individual authors.

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
    License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library;  if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.

    > http://www.fsf.org/licensing/licenses/lgpl.html
    > http://www.opensource.org/licenses/lgpl-license.php

*/
package org.dishevelled.venn.swing;

import java.awt.GridLayout;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.util.List;
import java.util.Set;

import javax.swing.Box;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import ca.odell.glazedlists.swing.EventListModel;

import org.dishevelled.layout.LabelFieldPanel;

import org.dishevelled.observable.event.SetChangeEvent;
import org.dishevelled.observable.event.SetChangeListener;

import org.dishevelled.venn.BinaryVennModel;

/**
 * Binary venn diagram list.
 *
 * @param  value type
 * @author  Michael Heuer
 * @version $Revision$ $Date$
 */
public final class BinaryVennList
    extends AbstractBinaryVennDiagram
{
    /** Contents of the first set. */
    private final JList first = new JList();

    /** Contents of the second set. */
    private final JList second = new JList();

    /** Contents of the first only view. */
    private final JList firstOnly = new JList();

    /** Contents for the second only view. */
    private final JList secondOnly = new JList();

    /** Contents of the intersection view. */
    private final JList intersection = new JList();

    /** Contents of the union view. */
    private final JList union = new JList();

    /** Adapter for the first list model. */
    private ObservableSetEventListAdapter firstAdapter;

    /** Adapter for the second list model. */
    private ObservableSetEventListAdapter secondAdapter;

    /** Adapter for the first only list model. */
    private SetEventListAdapter firstOnlyAdapter;

    /** Adapter for the second only list model. */
    private SetEventListAdapter secondOnlyAdapter;

    /** Adapter for the intersection list model. */
    private SetEventListAdapter intersectionAdapter;

    /** Adapter for the union list model. */
    private SetEventListAdapter unionAdapter;

    /** Model change listener. */
    private final PropertyChangeListener modelChange = new PropertyChangeListener()
    {
        /** {@inheritDoc} */
        public void propertyChange(final PropertyChangeEvent event)
        {
            uninstallListModels((BinaryVennModel) event.getOldValue());
            installListModels();
        }
    };

    /** Update list models from model. */
    private final SetChangeListener updateListModels = new SetChangeListener()
    {
        /** {@inheritDoc} */
        public void setChanged(final SetChangeEvent event)
        {
            updateListModels();
        }
    };

    /** Update list selection from model. */
    private final SetChangeListener updateSelection = new SetChangeListener()
    {
        /** {@inheritDoc} */
        public void setChanged(final SetChangeEvent event)
        {
            updateSelection();
        }
    };


    /**
     * Create a new empty binary venn list.
     */
    public BinaryVennList()
    {
        super();
        installListModels();
        installSelectionListeners();
        layoutComponents();
        addPropertyChangeListener("model", modelChange);
    }

    /**
     * Create a new binary venn list with the specified sets.
     *
     * @param firstLabelText label text for the first set
     * @param first first set, must not be null
     * @param secondLabelText label text for the second set
     * @param second second set, must not be null
     */
    public BinaryVennList(final String firstLabelText, final Set first,
        final String secondLabelText, final Set second)
    {
        super(firstLabelText, first, secondLabelText, second);
        installListModels();
        installSelectionListeners();
        layoutComponents();
        addPropertyChangeListener("model", modelChange);
    }

    /**
     * Create a new binary venn list with the specified model.
     *
     * @param model model for this binary venn list, must not be null
     */
    public BinaryVennList(final BinaryVennModel model)
    {
        super(model);
        installListModels();
        installSelectionListeners();
        layoutComponents();
        addPropertyChangeListener("model", modelChange);
    }


    // todo:  keyboard copy & paste does not work as expected always

    /**
     * Clear selection.
     */
    public void clearSelection()
    {
        union.requestFocusInWindow();
        getModel().selection().clear();
    }

    /**
     * Select all.
     */
    public void selectAll()
    {
        union.requestFocusInWindow();
        // todo:  dreadfully inefficient
        getModel().selection().addAll(getModel().union());
    }

    /**
     * Return the contents of the first set.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the first set
     */
    public JList getFirst()
    {
        return first;
    }

    /**
     * Return the contents of the second set.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the second set
     */
    public JList getSecond()
    {
        return second;
    }

    /**
     * Return the contents of the first only view.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the first only view
     */
    public JList getFirstOnly()
    {
        return firstOnly;
    }

    /**
     * Return the contents of the second only view.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the second only view
     */
    public JList getSecondOnly()
    {
        return secondOnly;
    }

    /**
     * Return the contents of the intersection view.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the intersection view
     */
    public JList getIntersection()
    {
        return intersection;
    }

    /**
     * Return the contents of the union view.  The model for the returned
     * JList should not be changed, as the current model implementation is
     * synchronized to the binary venn model backing this venn diagram.
     *
     * @return the contents of the union view
     */
    public JList getUnion()
    {
        return union;
    }

    /**
     * Install list models.
     */
    private void installListModels()
    {
        firstAdapter = new ObservableSetEventListAdapter(getModel().first());
        first.setModel(new EventListModel(firstAdapter));
        secondAdapter = new ObservableSetEventListAdapter(getModel().second());
        second.setModel(new EventListModel(secondAdapter));

        firstOnlyAdapter = new SetEventListAdapter(getModel().firstOnly());
        firstOnly.setModel(new EventListModel(firstOnlyAdapter));
        secondOnlyAdapter = new SetEventListAdapter(getModel().secondOnly());
        secondOnly.setModel(new EventListModel(secondOnlyAdapter));
        intersectionAdapter = new SetEventListAdapter(getModel().intersection());
        intersection.setModel(new EventListModel(intersectionAdapter));
        unionAdapter = new SetEventListAdapter(getModel().union());
        union.setModel(new EventListModel(unionAdapter));

        getModel().first().addSetChangeListener(updateListModels);
        getModel().second().addSetChangeListener(updateListModels);
        getModel().first().addSetChangeListener(updateSelection);
        getModel().second().addSetChangeListener(updateSelection);
        getModel().selection().addSetChangeListener(updateSelection);
    }

    /**
     * Update list models.
     */
    private void updateListModels()
    {
        firstOnlyAdapter.updateEventList();
        secondOnlyAdapter.updateEventList();
        intersectionAdapter.updateEventList();
        unionAdapter.updateEventList();
    }

    /**
     * Uninstall list models.
     *
     * @param oldModel old model
     */
    private void uninstallListModels(final BinaryVennModel oldModel)
    {
        firstAdapter.dispose();
        secondAdapter.dispose();
        ((EventListModel) first.getModel()).dispose();
        ((EventListModel) second.getModel()).dispose();
        ((EventListModel) firstOnly.getModel()).dispose();
        ((EventListModel) secondOnly.getModel()).dispose();
        ((EventListModel) intersection.getModel()).dispose();
        ((EventListModel) union.getModel()).dispose();
        oldModel.first().removeSetChangeListener(updateListModels);
        oldModel.second().removeSetChangeListener(updateListModels);
        oldModel.first().removeSetChangeListener(updateSelection);
        oldModel.second().removeSetChangeListener(updateSelection);
        oldModel.selection().removeSetChangeListener(updateSelection);
    }

    /**
     * Install selection listeners.
     */
    private void installSelectionListeners()
    {
        first.addListSelectionListener(new UpdateSelectionView());
        second.addListSelectionListener(new UpdateSelectionView());
        firstOnly.addListSelectionListener(new UpdateSelectionView());
        secondOnly.addListSelectionListener(new UpdateSelectionView());
        intersection.addListSelectionListener(new UpdateSelectionView());
        union.addListSelectionListener(new UpdateSelectionView());
    }

    /** {@inheritDoc} */
    protected void updateContents()
    {
        // empty
    }

    /**
     * Layout components.
     */
    private void layoutComponents()
    {
        addFinalField(createMainPanel());
    }

    /**
     * Create and return the main panel.
     *
     * @return the main panel
     */
    private JPanel createMainPanel()
    {
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 4, 12, 12));

        LabelFieldPanel f = new LabelFieldPanel();
        f.addLabel(getFirstLabel());
        f.addFinalField(new JScrollPane(first));
        panel.add(f);

        LabelFieldPanel s = new LabelFieldPanel();
        s.addLabel(getSecondLabel());
        s.addFinalField(new JScrollPane(second));
        panel.add(s);

        panel.add(Box.createGlue());
        panel.add(Box.createGlue());

        LabelFieldPanel fo = new LabelFieldPanel();
        fo.addLabel(getFirstOnlyLabel());
        fo.addFinalField(new JScrollPane(firstOnly));
        panel.add(fo);

        LabelFieldPanel so = new LabelFieldPanel();
        so.addLabel(getSecondOnlyLabel());
        so.addFinalField(new JScrollPane(secondOnly));
        panel.add(so);

        LabelFieldPanel n = new LabelFieldPanel();
        n.addLabel(getIntersectionLabel());
        n.addFinalField(new JScrollPane(intersection));
        panel.add(n);

        LabelFieldPanel u = new LabelFieldPanel();
        u.addLabel(getUnionLabel());
        u.addFinalField(new JScrollPane(union));
        panel.add(u);

        return panel;
    }

    /**
     * Update list selection from the selection view in the model.
     */
    private void updateSelection()
    {
        if (getModel().selection().isEmpty())
        {
            clearSelection(first);
            clearSelection(second);
            clearSelection(firstOnly);
            clearSelection(secondOnly);
            clearSelection(intersection);
            clearSelection(union);
        }
        else
        {
            // todo:  need element(s) that were added from set change event
            for (E e : getModel().selection())
            {
                addToSelection(getModel().first(), first, firstAdapter, e);
                addToSelection(getModel().second(), second, secondAdapter, e);
                addToSelection(getModel().firstOnly(), firstOnly, firstOnlyAdapter, e);
                addToSelection(getModel().secondOnly(), secondOnly, secondOnlyAdapter, e);
                addToSelection(getModel().intersection(), intersection, intersectionAdapter, e);
                addToSelection(getModel().union(), union, unionAdapter, e);
            }

            removeFromSelection(getModel().first(), first, firstAdapter);
            removeFromSelection(getModel().second(), second, secondAdapter);
            removeFromSelection(getModel().firstOnly(), firstOnly, firstOnlyAdapter);
            removeFromSelection(getModel().secondOnly(), secondOnly, secondOnlyAdapter);
            removeFromSelection(getModel().intersection(), intersection, intersectionAdapter);
            removeFromSelection(getModel().union(), union, unionAdapter);
        }
    }

    /**
     * Clear the selection for the specified list if it is not a focus owner.
     *
     * @param list list
     */
    private void clearSelection(final JList list)
    {
        if (!list.isFocusOwner())
        {
            list.clearSelection();
        }
    }

    /**
     * Add the specified element to the list selection if it is contained
     * in the specified model and the list is not a focus owner.
     *
     * @param model model
     * @param list list
     * @param adapter adapter
     * @param e element
     */
    private void addToSelection(final Set model, final JList list, final List adapter, final E e)
    {
        if (!list.isFocusOwner() && model.contains(e))
        {
            int index = adapter.indexOf(e);
            list.getSelectionModel().addSelectionInterval(index, index);
        }
    }

    /**
     * Remove elements from the list selection if they are not present in
     * the specified model and the list is not a focus owner.
     *
     * @param model model
     * @param list list
     * @param adapter adapter
     */
    private void removeFromSelection(final Set model, final JList list, final List adapter)
    {
        if (!list.isFocusOwner())
        {
            // todo:  need element(s) that were removed from set change event
            for (E e : model)
            {
                if (!getModel().selection().contains(e))
                {
                    int index = adapter.indexOf(e);
                    list.getSelectionModel().removeSelectionInterval(index, index);
                }
            }
        }
    }

    /**
     * Update selection view.
     */
    private class UpdateSelectionView implements ListSelectionListener
    {
        /** {@inheritDoc} */
        public void valueChanged(final ListSelectionEvent event)
        {
            JList list = (JList) event.getSource();
            if (list.isFocusOwner() && !event.getValueIsAdjusting())
            {
                ListSelectionModel selectionModel = list.getSelectionModel();
                for (int index = event.getFirstIndex(); index < (event.getLastIndex() + 1); index++)
                {
                    E e = (E) list.getModel().getElementAt(index);
                    if (selectionModel.isSelectedIndex(index))
                    {
                        if (!getModel().selection().contains(e))
                        {
                            getModel().selection().add(e);
                        }
                    }
                    else
                    {
                        if (getModel().selection().contains(e))
                        {
                            getModel().selection().remove(e);
                        }
                    }
                }
            }
            // todo:  may need to remove from selection view those
            //    elements not present in model for focused list
            //    e.g.  say "bar" is selected, select "foo" in First only
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy