era.foss.objecteditor.specobject.SpecObjectCompositeViewer.java Source code

Java tutorial

Introduction

Here is the source code for era.foss.objecteditor.specobject.SpecObjectCompositeViewer.java

Source

/**************************************************************************
 * ERA - Eclipse Requirements Analysis
 * ==============================================
 * Copyright (C) 2009-2013 by Georg Blaschke, Christoph P. Neumann
 * and Bernd Haberstumpf (http://era.origo.ethz.ch)
 **************************************************************************
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.eclipse.org/org/documents/epl-v10.html
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **************************************************************************
 */
package era.foss.objecteditor.specobject;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.IEMFListProperty;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.ViewerProperties;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IInputSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.nebula.widgets.compositetable.CompositeTable;
import org.eclipse.swt.nebula.widgets.compositetable.IRowContentProvider;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

import era.foss.erf.ERF;
import era.foss.erf.EraToolExtension;
import era.foss.erf.ErfPackage;
import era.foss.erf.SpecObject;
import era.foss.erf.SpecType;
import era.foss.erf.ToolExtension;
import era.foss.erf.contrib.HierachicalSpecObjectProvider;
import era.foss.objecteditor.contrib.IAllowViewerSchemaChange;
import era.foss.ui.contrib.NotifyingListSizeProperty;

/**
 * The Class SpecObjectCompositeViewer.
 */
public class SpecObjectCompositeViewer extends Viewer implements IInputSelectionProvider, IAllowViewerSchemaChange {

    /** Master observable referring to the currently selected view. */
    IViewerObservableValue viewMaster;

    /** ERF Model. */
    ERF erfModel;

    /** ERA specific extensions. */
    EraToolExtension toolExtension;

    /** top level composite of this viewer. */
    Composite topLevelComposite;

    /** composite table showing the spec objects. */
    CompositeTable compositeTable;

    /** Button bar for various buttons (adding elements, selecting views,... */
    Composite buttonBarComposite;

    /** editing Domain. */
    EditingDomain editingDomain;

    /** Databinding context for this viewer. */
    DataBindingContext dbc;

    /** The current selected SpecObjects. */
    protected LinkedHashMap<Integer, SpecObject> selectedSpecObjectMap = new LinkedHashMap<Integer, SpecObject>();

    /** List of selection changed listeners of this viewer. */
    List<ISelectionChangedListener> selectionChangedListeners = new LinkedList<ISelectionChangedListener>();

    HierachicalSpecObjectProvider specObjectProvider;

    /** The spec type master. */
    protected IObservableValue specTypeMaster;

    /**
     * Create a Viewer for the SpecObjects.
     * 
     * @param parent the parent
     * @param editingDomain the editing domain
     * @param erfModel the erf model
     */
    public SpecObjectCompositeViewer(Composite parent, AdapterFactoryEditingDomain editingDomain, ERF erfModel) {
        this.editingDomain = editingDomain;
        this.erfModel = erfModel;
        this.dbc = new DataBindingContext();

        // find Era specific tool extensions
        for (ToolExtension toolExtension : this.erfModel.getToolExtensions()) {
            if (toolExtension.eClass().getClassifierID() == ErfPackage.ERA_TOOL_EXTENSION) {
                this.toolExtension = (EraToolExtension) toolExtension;
            }
        }
        assert (this.toolExtension != null);

        // TODO: don't rely on the existence of a specification
        specObjectProvider = new HierachicalSpecObjectProvider(
                this.erfModel.getCoreContent().getSpecifications().get(0));
        doLayout(parent);

        // binding the UI with the model
        binding();
    }

    /**
     * layout the viewer gui elements
     * 
     * @param parent
     */
    void doLayout(Composite parent) {
        topLevelComposite = new Composite(parent, SWT.NONE);
        topLevelComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        topLevelComposite.setLayout(new GridLayout(1, false));

        // button bar
        createButtonBar();

        createCompositeTable();
    }

    /**
     * Create the composite table
     */
    private void createCompositeTable() {
        // composite table
        this.compositeTable = new CompositeTable(topLevelComposite, SWT.NULL | SWT.NO_SCROLL);
        this.compositeTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 0, 0));

        SpecObjectViewerRow.setViewMaster(viewMaster);
        SpecObjectViewerRow.setEditingDomain(editingDomain);
        SpecObjectViewerRow.setErfModel(erfModel);
        new SpecObjectViewerRow(compositeTable, SWT.NULL);

        compositeTable.setRunTime(true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.Viewer#getControl()
     */
    @Override
    public Control getControl() {
        return topLevelComposite;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.Viewer#refresh()
     */
    @Override
    public void refresh() {
        compositeTable.refreshAllRows();
    }

    @Override
    public void recreateViewerSchema() {

        dbc.dispose();
        viewMaster.dispose();

        buttonBarComposite.dispose();
        createButtonBar();

        compositeTable.dispose();
        createCompositeTable();

        binding();

        topLevelComposite.layout();
        // buttonBarComposite.layout();
        // viewerComposite.update();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.Viewer#getInput()
     */
    @Override
    public Object getInput() {
        // TODO implement getInput(..) which stems from IInputProvider
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object)
     */
    @Override
    public void setInput(Object input) {
        // TODO implement setInput(..)
    }

    /**
     * A row of the composite table
     */

    /**
     * Create button bar showing:
     * <ul>
     * <li>combo box for the view</li>
     * <li>button for adding new SpecObjects</li>
     * </ul>
     * 
     * 
     * @param viewerComposite the parent composite
     * @return
     */
    private void createButtonBar() {
        buttonBarComposite = new Composite(topLevelComposite, SWT.NONE);
        buttonBarComposite.setLayout(new GridLayout(4, true));
        buttonBarComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 0, 0));

        /*
         * create combo box showing the availible views
         */
        ComboViewer viewComboViewer = new ComboViewer(buttonBarComposite, SWT.READ_ONLY) {
            @Override
            protected void doUpdateItem(Widget data, Object element, boolean fullMap) {
                // memorize the selection before updating the item, as the
                // update routine removes the selection...
                ISelection currentSelection = this.getSelection();
                super.doUpdateItem(data, element, fullMap);
                // set the selection to the previous value
                this.setSelection(currentSelection);
            }
        };
        ObservableListContentProvider contentProvider = new ObservableListContentProvider();
        viewComboViewer.setContentProvider(contentProvider);
        viewComboViewer.setLabelProvider(
                new ObservableMapLabelProvider(EMFProperties.value(ErfPackage.Literals.IDENTIFIABLE__LONG_NAME)
                        .observeDetail(contentProvider.getKnownElements())));
        IEMFListProperty dataTypeDefinitions = EMFProperties.list(ErfPackage.Literals.ERA_TOOL_EXTENSION__VIEWS);
        IObservableList observableList = dataTypeDefinitions.observe(toolExtension);
        viewComboViewer.setInput(observableList);
        // use first view available
        // TODO: use a dedicated default view if available
        if (toolExtension.getViews().size() > 0) {
            viewComboViewer.setSelection(new StructuredSelection(toolExtension.getViews().get(0)));
        }
        viewComboViewer.getControl().setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1));

        viewMaster = ViewerProperties.singleSelection().observe(viewComboViewer);

        // refresh composite table in case view has been changed
        viewMaster.addChangeListener(new IChangeListener() {

            @Override
            public void handleChange(ChangeEvent event) {
                dbc.dispose();

                compositeTable.dispose();
                createCompositeTable();

                binding();

                topLevelComposite.layout();
            }
        });

        /*
         * Create Combo box for selecting the SpecType
         */
        final ComboViewer specTypecomboViewer = new ComboViewer(buttonBarComposite, SWT.READ_ONLY) {
            @Override
            protected void doUpdateItem(Widget data, Object element, boolean fullMap) {
                // memorize the selection before updating the item, as the
                // update routine removes the selection...
                ISelection currentSelection = this.getSelection();
                super.doUpdateItem(data, element, fullMap);
                // set the selection to the previous value
                this.setSelection(currentSelection);
            }
        };
        ObservableListContentProvider comboViewercontentProvider = new ObservableListContentProvider();
        specTypecomboViewer.getControl().setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));
        // set content provider
        specTypecomboViewer.setContentProvider(comboViewercontentProvider);
        // set label provider
        specTypecomboViewer.setLabelProvider(
                new ObservableMapLabelProvider(EMFProperties.value(ErfPackage.Literals.IDENTIFIABLE__LONG_NAME)
                        .observeDetail(comboViewercontentProvider.getKnownElements())));
        // set input
        IEMFListProperty specTypeProperty = EMFProperties.list(ErfPackage.Literals.CONTENT__SPEC_TYPES);
        specTypecomboViewer.setInput(specTypeProperty.observe(this.erfModel.getCoreContent()));

        // TODO: use a dedicated default type if available
        if (erfModel.getCoreContent().getSpecTypes().size() > 0) {
            specTypecomboViewer
                    .setSelection(new StructuredSelection(erfModel.getCoreContent().getSpecTypes().get(0)));
        }
        specTypeMaster = ViewerProperties.singleSelection().observe(specTypecomboViewer);

        /*
         * create add button
         */
        final Button addButton = new Button(buttonBarComposite, SWT.NONE);
        addButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD));
        addButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false));
        addButton.setEnabled(specTypeMaster.getValue() != null);
        addButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                SpecObjectHandler.createNewSpecObject(editingDomain, erfModel.getCoreContent(),
                        (SpecType) SpecObjectCompositeViewer.this.specTypeMaster.getValue(),
                        erfModel.getCoreContent().getSpecifications().get(0));
            }
        });

        specTypeMaster.addValueChangeListener(new IValueChangeListener() {
            @Override
            public void handleValueChange(ValueChangeEvent event) {
                addButton.setEnabled(event.getObservableValue().getValue() != null);
            }
        });

    }

    /**
     * Bind composite table and rows of composite table to data model. Also handle selection events from the composite
     * table
     */
    private void binding() {

        dbc.bindValue(PojoObservables.observeValue(compositeTable, "numRowsInCollection"),
                new NotifyingListSizeProperty().observe(specObjectProvider.getSpecObjectList()),
                new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy());

        compositeTable.addRowContentProvider(new SpecObjectRowContentProvider());
    }

    @Override
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        if (!selectionChangedListeners.contains(listener)) {
            selectionChangedListeners.add(listener);
        }
    }

    @Override
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        selectionChangedListeners.remove(listener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.Viewer#getSelection()
     */
    @Override
    public ISelection getSelection() {
        if (selectedSpecObjectMap.size() == 0) {
            return StructuredSelection.EMPTY;
        }
        return new StructuredSelection(selectedSpecObjectMap.values().toArray());
    }

    @SuppressWarnings("unchecked")
    @Override
    public void setSelection(ISelection selection, boolean reveal) {
        if (selection.isEmpty()) {
            return;
        }

        // unpack ISelection into SpecObject
        assert (selection instanceof StructuredSelection);

        // remove all Spec Objects
        selectedSpecObjectMap.clear();

        Integer selectedSpecOjectOffset = null;

        // set selectedSpecObject to given one
        for (SpecObject specObject : ((List<SpecObject>) ((StructuredSelection) selection).toList())) {
            int specOjectOffset = specObjectProvider.getSpecObjectList().indexOf(specObject);
            if (selectedSpecOjectOffset == null) {
                selectedSpecOjectOffset = specOjectOffset;
            }

            selectedSpecObjectMap.put(specOjectOffset, specObject);
        }

        // if specObject is not yet displayed in the composite table show it in the first row
        if (selectedSpecOjectOffset < compositeTable.getTopRow()
                || selectedSpecOjectOffset > (compositeTable.getTopRow() + compositeTable.getNumRowsVisible())) {
            compositeTable.setTopRow(selectedSpecOjectOffset);
            updateRowSelectionStatus(true);
        } else {
            updateRowSelectionStatus(false);
        }

    }

    /**
     * Update the selection status of all rows controls
     */
    private void updateRowSelectionStatus(boolean setFocus) {
        for (Control control : compositeTable.getRowControls()) {
            SpecObjectViewerRow row = (SpecObjectViewerRow) control;
            row.setSelected(selectedSpecObjectMap.containsKey(row.getSpecObjectOffset()), setFocus);
        }

        // send event to selectionChangedListener
        SelectionChangedEvent selectionChangeEvent = new SelectionChangedEvent(SpecObjectCompositeViewer.this,
                new StructuredSelection(selectedSpecObjectMap.values().toArray()));
        for (ISelectionChangedListener listener : selectionChangedListeners) {
            listener.selectionChanged(selectionChangeEvent);
        }
    }

    /**
     * Content Provider for Row in the Composite table.
     */
    private class SpecObjectRowContentProvider implements IRowContentProvider {

        /**
         * Keeps a reference of each delete listener for each row, so we can remove a listener when compositetable
         * associate the row to another SpecObject.
         */
        private Map<SpecObjectViewerRow, MouseListener> deleteListenerMap = new HashMap<SpecObjectViewerRow, MouseListener>();

        /**
         * Keeps a reference of each selection listener for each row, so we can remove a listener when compositetable
         * associate the row to another SpecObject.
         */
        private Map<SpecObjectViewerRow, SelectionListener> selectionListenerMap = new HashMap<SpecObjectViewerRow, SelectionListener>();

        /**
         * remove and add selectionlistener bind new SpecObject to Row
         */
        public void refresh(CompositeTable sender, final int currentObjectOffset, Control rowControl) {

            final SpecObjectViewerRow currentRow = (SpecObjectViewerRow) rowControl;
            final SpecObject currentSpecObject = specObjectProvider.getSpecObjectList().get(currentObjectOffset);

            // set offset of the current specObject
            currentRow.setSpecObject(currentSpecObject, currentObjectOffset);

            // bind the new SpecObject
            currentRow.bind(currentSpecObject);

            // in case the specObject is selected set the selected status of the row
            currentRow.setSelected(selectedSpecObjectMap.containsKey(currentObjectOffset), false);

            // listener for the delete button of the row
            MouseListener deleteListener = new MouseAdapter() {
                @Override
                public void mouseDown(MouseEvent e) {
                    SpecObjectHandler.deleteSpecObject(editingDomain, currentSpecObject);
                }
            };

            // listener for selection events of a row
            SelectionListener selectionListener = new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    // SHIFT is not pressed: set selection to the SpecObject associated with the current row
                    if ((e.stateMask & SWT.SHIFT) == 0
                            || SpecObjectCompositeViewer.this.selectedSpecObjectMap.isEmpty()) {
                        SpecObjectCompositeViewer.this.selectedSpecObjectMap.clear();
                        SpecObjectCompositeViewer.this.selectedSpecObjectMap.put(currentObjectOffset,
                                currentSpecObject);
                    }
                    // SHIFT is pressed: add all elements between the selected SpecObject and the SpecObject
                    // associated with the current row
                    else {
                        int selectedSpecObjectOffset = (Integer) selectedSpecObjectMap.keySet().toArray()[0];

                        int objectOffset = Math.min(currentObjectOffset, selectedSpecObjectOffset);
                        int objectEndOffset = Math.max(currentObjectOffset, selectedSpecObjectOffset);
                        while (objectOffset <= objectEndOffset) {
                            SpecObjectCompositeViewer.this.selectedSpecObjectMap.put(objectOffset,
                                    specObjectProvider.getSpecObjectList().get(objectOffset));
                            objectOffset++;
                        }
                    }

                    // Only set the focus in case the event is sent by a composite
                    boolean setFocus = e.widget instanceof Composite;

                    // update the selection status of the row controls
                    SpecObjectCompositeViewer.this.updateRowSelectionStatus(setFocus);
                }
            };

            // add listener to row
            currentRow.addSelectionListener(selectionListener);

            // keep a reference to the listeners to be able to remove it
            // during the next refresh
            deleteListenerMap.put(currentRow, deleteListener);
            selectionListenerMap.put(currentRow, selectionListener);
        }
    }
}