org.eclipse.richbeans.widgets.shuffle.ShuffleViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.richbeans.widgets.shuffle.ShuffleViewer.java

Source

/*-
 *******************************************************************************
 * Copyright (c) 2011, 2016 Diamond Light Source Ltd.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Matthew Gerring - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.richbeans.widgets.shuffle;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.richbeans.api.reflection.RichBeanUtils;
import org.eclipse.richbeans.widgets.internal.GridUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * A class for shuffling between lists
 * 
 * @author Matthew Gerring
 * @param T type of items in the ShuffleViewer, for instance String
 */
public class ShuffleViewer<T> {

    private static final Logger logger = LoggerFactory.getLogger(ShuffleViewer.class);

    private ShuffleConfiguration<T> conf;
    private TableViewer fromTable, toTable;

    private Composite control;
    private List<IShuffleListener> shuffleListeners;

    public ShuffleViewer(ShuffleConfiguration<T> data) {
        this.conf = data;
    }

    /**
     * Please call dispose which will clean up the listeners
     * and should be treated as with an SWT Displose.
     */
    public void dispose() {
        conf.clearListeners();
        control.dispose();
    }

    public Composite createPartControl(Composite parent) {

        this.control = new Composite(parent, SWT.NONE);
        control.setLayout(new GridLayout(3, false));
        control.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        GridUtils.removeMargins(control);

        this.fromTable = createTable(control, conf.getFromToolipText(), conf.getFromList(), "fromList",
                conf.getFromLabel(), false, conf.isFromReorder());
        createButtons(control);
        this.toTable = createTable(control, conf.getToToolipText(), conf.getToList(), "toList", conf.getToLabel(),
                true, conf.isToReorder());

        return control;
    }

    public void addShuffleListener(IShuffleListener l) {
        if (shuffleListeners == null)
            shuffleListeners = Collections.synchronizedList(new ArrayList<>(7));
        shuffleListeners.add(l);
    }

    public void removeShuffleListener(IShuffleListener l) {
        if (shuffleListeners == null)
            return;
        shuffleListeners.remove(l);
    }

    /**
     * 
     * @param evt
     * @return items if one or more listeners set them, the items unchanged if no listener changed them.
     */
    protected List<T> firePreShuffle(ShuffleEvent evt) {
        if (shuffleListeners == null)
            return (List<T>) evt.getItems();
        IShuffleListener[] la = shuffleListeners.toArray(new IShuffleListener[shuffleListeners.size()]);
        List<T> ret = (List<T>) evt.getItems();
        for (IShuffleListener iShuffleListener : la) {
            boolean ok = iShuffleListener.preShuffle(evt);
            if (!ok)
                throw new RuntimeException("Unable to call preshuffle, aborting event!");
            if (evt.isItemsSet())
                ret = (List<T>) evt.getItems(); // Ret can be overwritten, Javadoc on IShuffleListener explains this.
        }
        return ret;
    }

    /**
     * 
     * @param evt
     * @return items if one or more listeners set them, null if no listener changed them.
     */
    protected void firePostShuffle(ShuffleEvent evt) {
        if (shuffleListeners == null)
            return;
        IShuffleListener[] la = shuffleListeners.toArray(new IShuffleListener[shuffleListeners.size()]);
        for (IShuffleListener iShuffleListener : la)
            iShuffleListener.postShuffle(evt);
    }

    private final Composite createButtons(Composite content) {

        final Composite ret = new Composite(content, SWT.NONE);
        ret.setLayout(new GridLayout(1, false));
        ret.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, true));
        ret.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        GridUtils.removeMargins(ret, 0, 20);

        final Button right = new Button(ret, SWT.ARROW | SWT.RIGHT);
        right.setToolTipText("Move item right");
        right.setEnabled(!conf.getFromList().isEmpty());
        right.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                moveRight();
            }
        });
        right.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
        gd.heightHint = 50;
        right.setLayoutData(gd);
        PropertyChangeListener pcRight = evt -> {
            final boolean enabled = evt.getNewValue() != null && evt.getNewValue() instanceof List
                    && ((List) evt.getNewValue()).size() > 0;
            fromTable.getControl().getDisplay().syncExec(() -> right.setEnabled(enabled));
        };
        conf.addPropertyChangeListener("fromList", pcRight);

        final Button left = new Button(ret, SWT.ARROW | SWT.LEFT);
        left.setToolTipText("Move item left");
        left.setEnabled(!conf.getToList().isEmpty());
        left.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                moveLeft();
            }
        });
        left.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        left.setLayoutData(gd);
        PropertyChangeListener pcLeft = evt -> {
            final boolean enabled = evt.getNewValue() != null && evt.getNewValue() instanceof List
                    && ((List) evt.getNewValue()).size() > 0;
            fromTable.getControl().getDisplay().syncExec(() -> left.setEnabled(enabled));
        };
        conf.addPropertyChangeListener("toList", pcLeft);

        return ret;
    }

    private void moveRight() {
        ShuffleBean<T> from = new ShuffleBean<>(fromTable, "fromList", conf.getFromList(), ShuffleDirection.DELETE);
        ShuffleBean<T> to = new ShuffleBean<>(toTable, "toList", conf.getToList(), ShuffleDirection.LEFT_TO_RIGHT);
        moveHorizontal(from, to);
    }

    private void moveLeft() {
        ShuffleBean<T> from = new ShuffleBean<>(toTable, "toList", conf.getToList(), ShuffleDirection.DELETE);
        ShuffleBean<T> to = new ShuffleBean<>(fromTable, "fromList", conf.getFromList(),
                ShuffleDirection.RIGHT_TO_LEFT);
        moveHorizontal(from, to);
    }

    private void moveHorizontal(ShuffleBean<T> abean, ShuffleBean<T> bbean) {

        T sel = getSelection(abean.getTable());
        if (sel == null)
            return;

        List<T> a = abean.getList();
        int aindex = a.indexOf(sel);
        List<T> rem = new ArrayList<>(a);
        rem.remove(sel);

        List<T> add = new ArrayList<>(bbean.getList());
        T existing = getSelection(bbean.getTable());
        int bindex = add.size();
        if (existing != null) {
            bindex = add.indexOf(existing);
        }
        try {
            add.add(bindex + 1, sel);
        } catch (IndexOutOfBoundsException ne) {
            add.add(sel);
        }

        try {
            rem = firePreShuffle(new ShuffleEvent(this, abean.getDirection(), rem));
            RichBeanUtils.setBeanValue(conf, abean.getName(), rem); // Set the value of the rem list and property name 'aName' in object conf
            firePostShuffle(new ShuffleEvent(this, abean.getDirection(), rem));

            add = firePreShuffle(new ShuffleEvent(this, bbean.getDirection(), add));
            RichBeanUtils.setBeanValue(conf, bbean.getName(), add); // Set the value of the add list and property name 'bName' in object conf

            bbean.getTable().setSelection(new StructuredSelection(sel));
            if (!rem.isEmpty()) {
                aindex--;
                if (aindex < 0)
                    aindex = 0;
                abean.getTable().setSelection(new StructuredSelection(rem.get(aindex)));
            }
            firePostShuffle(new ShuffleEvent(this, bbean.getDirection(), add));

        } catch (Exception ne) {
            logger.error("Cannot set the values after move!", ne);
        }
    }

    private T getSelection(TableViewer viewer) {
        ISelection sel = viewer.getSelection();
        if (sel instanceof StructuredSelection) {
            return (T) ((StructuredSelection) sel).getFirstElement();
        }
        return null;
    }

    private final TableViewer createTable(Composite parent, String tooltip, List<T> items, String propName,
            String slabel, boolean selectFromEnd, boolean allowReorder) {

        Composite content = new Composite(parent, SWT.NONE);
        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        content.setLayout(new GridLayout(1, false));
        content.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));

        if (slabel != null) {
            final Label label = new Label(content, SWT.NONE);
            label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
            label.setText(slabel);
            label.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        }

        TableViewer ret = new TableViewer(content,
                SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
        ret.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        ((Table) ret.getControl()).setToolTipText(tooltip); // NOTE This can get clobbered if we used tooltips inside the table.

        final ShuffleContentProvider prov = new ShuffleContentProvider(conf, propName, selectFromEnd);
        ret.setContentProvider(prov);

        ret.setInput(items);

        Composite buttons = new Composite(content, SWT.NONE);
        buttons.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        buttons.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false));
        buttons.setLayout(new GridLayout(2, true));
        GridUtils.removeMargins(buttons, 20, 0);

        Button down = new Button(buttons, SWT.ARROW | SWT.DOWN);
        down.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        down.setEnabled(false);
        GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
        gd.widthHint = 50;
        down.setLayoutData(gd);
        down.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                ShuffleDirection direction = propName.startsWith("from") ? ShuffleDirection.LEFT_DOWN
                        : ShuffleDirection.RIGHT_DOWN;
                moveVertical(ret, propName, 1, direction);
            }
        });

        Button up = new Button(buttons, SWT.ARROW | SWT.UP);
        up.setBackground(content.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        up.setEnabled(false);
        up.setLayoutData(gd);
        up.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                ShuffleDirection direction = propName.startsWith("from") ? ShuffleDirection.LEFT_UP
                        : ShuffleDirection.RIGHT_UP;
                moveVertical(ret, propName, -1, direction);
            }
        });
        if (!allowReorder) { // This cannot be changed later
            down.setVisible(false);
            up.setVisible(false);
        }

        PropertyChangeListener pcLeft = evt -> {
            final boolean enabled = evt.getNewValue() != null && evt.getNewValue() instanceof List
                    && ((List) evt.getNewValue()).size() > 1;
            fromTable.getControl().getDisplay().syncExec(() -> {
                down.setEnabled(enabled);
                up.setEnabled(enabled);
            });
        };
        conf.addPropertyChangeListener(propName, pcLeft);

        // pcLeft only triggered when list is changed.
        // check whether you should be able to reorder as soon as widget is created
        boolean enabled = items.size() > 1;
        down.setEnabled(enabled);
        up.setEnabled(enabled);

        return ret;
    }

    private void moveVertical(TableViewer viewer, String propName, int moveAmount, ShuffleDirection direction) {

        try {
            T item = getSelection(viewer);
            if (item == null)
                return;

            List<T> items = new ArrayList<>((Collection<T>) RichBeanUtils.getBeanValue(conf, propName));
            final int index = items.indexOf(item);
            item = items.remove(index);

            int newIndex = index + moveAmount;
            if (newIndex < 0)
                newIndex = 0;
            if (newIndex > items.size())
                newIndex = items.size();
            items.add(newIndex, item);
            if (items.equals(RichBeanUtils.getBeanValue(conf, propName))) {
                // The list is the same
                return;
            }

            items = firePreShuffle(new ShuffleEvent(this, direction, items));
            RichBeanUtils.setBeanValue(conf, propName, items);
            viewer.setSelection(new StructuredSelection(item));
            firePostShuffle(new ShuffleEvent(this, direction, items));

        } catch (Exception ne) {
            logger.error("Problem moving veritcally!", ne);
        }
    }

    public Control getControl() {
        return control;
    }

    public void setLayoutData(GridData data) {
        control.setLayoutData(data);
    }

    public void setFocus() {
        fromTable.getControl().setFocus();
    }

    /**
     * Sets this menu on the 
     * @param rightClick
     */
    public void setMenu(MenuManager rightClick) {
        control.setMenu(rightClick.createContextMenu(control));
        fromTable.getControl().setMenu(rightClick.createContextMenu(fromTable.getControl()));
        toTable.getControl().setMenu(rightClick.createContextMenu(toTable.getControl()));
    }

    /**
     * 
     * @return the mutable model, changing items in the config causes the UI to change
     */
    public ShuffleConfiguration<T> getShuffleConfiguration() {
        return conf;
    }
}