com.archimatetool.zest.ZestView.java Source code

Java tutorial

Introduction

Here is the source code for com.archimatetool.zest.ZestView.java

Source

/**
 * This program and the accompanying materials
 * are made available under the terms of the License
 * which accompanies this distribution in the file LICENSE.txt
 */
package com.archimatetool.zest;

import java.beans.PropertyChangeEvent;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.help.HelpSystem;
import org.eclipse.help.IContext;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.zest.layouts.LayoutStyles;
import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm;

import com.archimatetool.editor.model.IEditorModelManager;
import com.archimatetool.editor.ui.ArchimateLabelProvider;
import com.archimatetool.editor.ui.IArchimateImages;
import com.archimatetool.editor.utils.StringUtils;
import com.archimatetool.editor.views.AbstractModelView;
import com.archimatetool.editor.views.tree.actions.IViewerAction;
import com.archimatetool.editor.views.tree.actions.PropertiesAction;
import com.archimatetool.model.IArchimateElement;
import com.archimatetool.model.IArchimateModel;
import com.archimatetool.model.IArchimateModelElement;
import com.archimatetool.model.IArchimatePackage;

/**
 * Zest View
 * 
 * @author Phillip Beauvoir
 */
public class ZestView extends AbstractModelView implements IZestView, ISelectionListener {

    private ZestGraphViewer fGraphViewer;

    private CLabel fLabel;

    private IAction fActionLayout;
    private IViewerAction fActionProperties;
    private IAction fActionPinContent;
    private IAction fActionCopyImageToClipboard;
    private IAction fActionExportImageToFile;

    // Depth Actions
    private IAction[] fDepthActions;

    private DrillDownManager fDrillDownManager;

    @Override
    protected void doCreatePartControl(Composite parent) {
        GridLayout layout = new GridLayout();
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.verticalSpacing = 0;
        parent.setLayout(layout);

        fLabel = new CLabel(parent, SWT.NONE);
        fLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        fGraphViewer = new ZestGraphViewer(parent, SWT.NONE);
        fGraphViewer.getGraphControl().setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));

        fGraphViewer.setLayoutAlgorithm(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
        //fGraphViewer.setLayoutAlgorithm(new TreeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
        //fGraphViewer.setLayoutAlgorithm(new RadialLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);
        //fGraphViewer.setLayoutAlgorithm(new HorizontalTreeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), true);

        // Graph selection listener
        fGraphViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                // Update actions
                updateActions();
                // Need to do this in order for Tabbed Properties View to update on Selection
                getSite().getSelectionProvider().setSelection(event.getSelection());
            }
        });

        // Double-click
        fGraphViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                fDrillDownManager.goInto();
            }
        });

        fDrillDownManager = new DrillDownManager(this);

        makeActions();
        hookContextMenu();
        registerGlobalActions();
        makeLocalToolBar();

        // This will update previous Undo/Redo text if View was closed before
        updateActions();

        // Register selections
        getSite().setSelectionProvider(getViewer());

        // Listen to global selections to update the viewer
        getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);

        // Help
        PlatformUI.getWorkbench().getHelpSystem().setHelp(fGraphViewer.getControl(), HELP_ID);

        // Initialise with whatever is selected in the workbench
        ISelection selection = getSite().getWorkbenchWindow().getSelectionService().getSelection();
        selectionChanged(null, selection);
    }

    @Override
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (part == this) {
            return;
        }

        if (fActionPinContent.isChecked()) {
            return;
        }

        if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
            Object object = ((IStructuredSelection) selection).getFirstElement();
            setElement(object);
        }
    }

    @Override
    protected void selectAll() {
        fGraphViewer.getGraphControl().selectAll();
    }

    private void setElement(Object object) {
        IArchimateElement element = null;

        if (object instanceof IArchimateElement) {
            element = (IArchimateElement) object;
        } else if (object instanceof IAdaptable) {
            element = (IArchimateElement) ((IAdaptable) object).getAdapter(IArchimateElement.class);
        }

        fDrillDownManager.setNewInput(element);
        updateActions();

        updateLabel();
    }

    void refresh() {
        getViewer().refresh();
        updateActions();
        updateLabel();
    }

    /**
     * Update local label
     */
    void updateLabel() {
        String text = ArchimateLabelProvider.INSTANCE.getLabel(fDrillDownManager.getCurrentElement());
        text = StringUtils.escapeAmpersandsInText(text);
        fLabel.setText(text);
        fLabel.setImage(ArchimateLabelProvider.INSTANCE.getImage(fDrillDownManager.getCurrentElement()));
    }

    /**
     * Update the Local Actions depending on the selection, and undo/redo actions
     */
    void updateActions() {
        IStructuredSelection selection = (IStructuredSelection) getViewer().getSelection();
        fActionProperties.update(selection);
        updateUndoActions();
    }

    /**
     * Populate the ToolBar
     */
    private void makeLocalToolBar() {
        IActionBars bars = getViewSite().getActionBars();

        IToolBarManager manager = bars.getToolBarManager();

        fDrillDownManager.addNavigationActions(manager);
        manager.add(new Separator());
        manager.add(fActionPinContent);
        manager.add(new Separator());
        manager.add(fActionLayout);

        final IMenuManager menuManager = bars.getMenuManager();

        IMenuManager depthMenuManager = new MenuManager(Messages.ZestView_3);
        menuManager.add(depthMenuManager);

        // Depth Actions
        fDepthActions = new Action[6];
        for (int i = 0; i < fDepthActions.length; i++) {
            fDepthActions[i] = new Action(Messages.ZestView_3 + " " + (i + 1), IAction.AS_RADIO_BUTTON) { //$NON-NLS-1$
                @Override
                public void run() {
                    IStructuredSelection selection = (IStructuredSelection) fGraphViewer.getSelection();
                    // set depth
                    int depth = Integer.valueOf(getId());
                    ((ZestViewerContentProvider) fGraphViewer.getContentProvider()).setDepth(depth);
                    // store in prefs
                    ArchimateZestPlugin.INSTANCE.getPreferenceStore()
                            .setValue(IPreferenceConstants.VISUALISER_DEPTH, depth);
                    // update viewer
                    fGraphViewer.setInput(fGraphViewer.getInput());
                    fGraphViewer.setSelection(selection);
                    fGraphViewer.doApplyLayout();
                }
            };

            fDepthActions[i].setId(Integer.toString(i));
            depthMenuManager.add(fDepthActions[i]);
        }

        // Set depth from prefs
        int depth = ArchimateZestPlugin.INSTANCE.getPreferenceStore().getInt(IPreferenceConstants.VISUALISER_DEPTH);
        ((ZestViewerContentProvider) fGraphViewer.getContentProvider()).setDepth(depth);
        fDepthActions[depth].setChecked(true);

        menuManager.add(new Separator());
        menuManager.add(fActionCopyImageToClipboard);
        menuManager.add(fActionExportImageToFile);
    }

    @Override
    public void setFocus() {
        if (fGraphViewer != null) {
            fGraphViewer.getControl().setFocus();
        }
    }

    @Override
    public ZestGraphViewer getViewer() {
        return fGraphViewer;
    }

    /**
     * Make local actions
     */
    private void makeActions() {
        fActionProperties = new PropertiesAction(getViewer());

        fActionLayout = new Action(Messages.ZestView_0) {
            @Override
            public void run() {
                fGraphViewer.doApplyLayout();
            }

            @Override
            public String getToolTipText() {
                return getText();
            }

            @Override
            public ImageDescriptor getImageDescriptor() {
                return AbstractUIPlugin.imageDescriptorFromPlugin(ArchimateZestPlugin.PLUGIN_ID, "img/layout.gif"); //$NON-NLS-1$
            }
        };

        fActionPinContent = new Action(Messages.ZestView_4, IAction.AS_CHECK_BOX) {
            {
                setToolTipText(Messages.ZestView_1);
                setImageDescriptor(IArchimateImages.ImageFactory.getImageDescriptor(IArchimateImages.ICON_PIN_16));
            }
        };

        fActionCopyImageToClipboard = new CopyZestViewAsImageToClipboardAction(fGraphViewer);
        fActionExportImageToFile = new ExportAsImageAction(fGraphViewer);
    }

    /**
     * Register Global Action Handlers
     */
    private void registerGlobalActions() {
        IActionBars actionBars = getViewSite().getActionBars();

        // Register our interest in the global menu actions
        actionBars.setGlobalActionHandler(ActionFactory.PROPERTIES.getId(), fActionProperties);
    }

    /**
     * Hook into a right-click menu
     */
    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("#ZestViewPopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);

        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                fillContextMenu(manager);
            }
        });

        Menu menu = menuMgr.createContextMenu(getViewer().getControl());
        getViewer().getControl().setMenu(menu);

        getSite().registerContextMenu(menuMgr, getViewer());
    }

    /**
     * Fill context menu when user right-clicks
     * @param manager
     */
    private void fillContextMenu(IMenuManager manager) {
        Object selected = ((IStructuredSelection) getViewer().getSelection()).getFirstElement();
        boolean isEmpty = selected == null;

        fDrillDownManager.addNavigationActions(manager);
        manager.add(new Separator());
        manager.add(fActionLayout);

        manager.add(new Separator());
        IMenuManager depthMenuManager = new MenuManager(Messages.ZestView_3);
        manager.add(depthMenuManager);

        for (int i = 0; i < fDepthActions.length; i++) {
            depthMenuManager.add(fDepthActions[i]);
        }

        manager.add(new Separator());
        manager.add(fActionCopyImageToClipboard);
        manager.add(fActionExportImageToFile);

        if (!isEmpty) {
            manager.add(new Separator());
            manager.add(fActionProperties);
        }

        // Other plug-ins can contribute their actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    @Override
    protected IArchimateModel getActiveArchimateModel() {
        IArchimateElement element = fDrillDownManager.getCurrentElement();
        return element != null ? element.getArchimateModel() : null;
    }

    @Override
    public void dispose() {
        super.dispose();

        // Explicit dispose seems to be needed if the GraphViewer is displaying scrollbars
        // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=373191
        // In fact, Graph.dispose() seems never to be called
        // fGraphViewer.getControl().dispose();

        // Unregister selection listener
        getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
    }

    // =================================================================================
    //                       Listen to Editor Model Changes
    // =================================================================================

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        String propertyName = evt.getPropertyName();
        Object newValue = evt.getNewValue();

        // Model Closed
        if (propertyName == IEditorModelManager.PROPERTY_MODEL_REMOVED) {
            Object input = getViewer().getInput();
            if (input instanceof IArchimateModelElement
                    && ((IArchimateModelElement) input).getArchimateModel() == newValue) {
                fDrillDownManager.reset();
            }
        }
        // Command Stack - update Actions
        else if (propertyName == IEditorModelManager.COMMAND_STACK_CHANGED) {
            updateActions();
        } else {
            super.propertyChange(evt);
        }
    }

    // =================================================================================
    //                       React to ECore Model Changes
    // =================================================================================

    @Override
    protected void eCoreChanged(Notification msg) {
        int type = msg.getEventType();

        if (type == Notification.ADD || type == Notification.ADD_MANY || type == Notification.REMOVE
                || type == Notification.REMOVE_MANY || type == Notification.MOVE) {
            refresh();
        }

        // Attribute set
        else if (type == Notification.SET) {
            Object feature = msg.getFeature();
            Object notifier = msg.getNotifier();

            // Relationship/Connection changed - requires full refresh
            if (feature == IArchimatePackage.Literals.RELATIONSHIP__SOURCE
                    || feature == IArchimatePackage.Literals.RELATIONSHIP__TARGET) {
                refresh();
            } else {
                super.eCoreChanged(msg);
            }

            if (notifier == fDrillDownManager.getCurrentElement()) {
                updateLabel();
            }
        } else {
            super.eCoreChanged(msg);
        }
    }

    @Override
    protected void refreshElementsFromBufferedNotifications() {
        refresh();
    }

    // =================================================================================
    //                       Contextual Help support
    // =================================================================================

    public int getContextChangeMask() {
        return NONE;
    }

    public IContext getContext(Object target) {
        return HelpSystem.getContext(HELP_ID);
    }

    public String getSearchExpression(Object target) {
        return Messages.ZestView_2;
    }
}