org.csstudio.display.builder.rcp.run.ContextMenuSupport.java Source code

Java tutorial

Introduction

Here is the source code for org.csstudio.display.builder.rcp.run.ContextMenuSupport.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Oak Ridge National Laboratory.
 * 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
 *******************************************************************************/
package org.csstudio.display.builder.rcp.run;

import static org.csstudio.display.builder.model.properties.CommonWidgetProperties.propPVName;
import static org.csstudio.display.builder.rcp.Plugin.logger;

import java.net.URL;
import java.util.Optional;
import java.util.logging.Level;

import org.csstudio.csdata.ProcessVariable;
import org.csstudio.display.builder.model.ModelPlugin;
import org.csstudio.display.builder.model.Preferences;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.WidgetProperty;
import org.csstudio.display.builder.model.properties.ActionInfo;
import org.csstudio.display.builder.model.properties.ActionInfo.ActionType;
import org.csstudio.display.builder.model.properties.OpenDisplayActionInfo;
import org.csstudio.display.builder.model.properties.OpenDisplayActionInfo.Target;
import org.csstudio.display.builder.rcp.RuntimeViewPart;
import org.csstudio.display.builder.representation.ToolkitListener;
import org.csstudio.display.builder.representation.javafx.widgets.JFXBaseRepresentation;
import org.csstudio.display.builder.runtime.ActionUtil;
import org.csstudio.display.builder.runtime.RuntimeAction;
import org.csstudio.display.builder.runtime.RuntimeUtil;
import org.csstudio.display.builder.runtime.WidgetRuntime;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import javafx.scene.Node;
import javafx.scene.Scene;

/** Context menu
 *
 *  <p>Helper for creating the RCP/SWT context menu
 *  for a widget.
 *
 *  <p>Adds the runtime actions and widget actions,
 *  and allows object contributions from RCP
 *  for the PV of the widget.
 *
 *  @author Kay Kasemir
 */
@SuppressWarnings("nls")
public class ContextMenuSupport {
    private static final boolean support_standalone = Preferences.isStandaloneWindowSupported();

    private final RuntimeViewPart view;
    private Shell shell;

    /** SWT/JFace Action for a model's ActionInfo
     *
     *  <p>Shows the ActionInfo's description and icon,
     *  invokes it.
     */
    private static class ActionInfoWrapper extends Action {
        private final Widget widget;
        private final ActionInfo info;

        public ActionInfoWrapper(final Widget widget, final ActionInfo info) {
            super(info.toString(),
                    AbstractUIPlugin.imageDescriptorFromPlugin(ModelPlugin.ID, info.getType().getIconPath()));
            this.widget = widget;
            this.info = info;
        }

        @Override
        public void run() {
            ActionUtil.handleAction(widget, info);
        }
    }

    /** SWT/JFace Action for a model's RuntimeAction
     *
     *  <p>Shows the description and icon,
     *  invokes it.
     */
    private static class RuntimeActionWrapper extends Action {
        private final RuntimeAction info;

        public RuntimeActionWrapper(final Widget widget, final RuntimeAction info) {
            super(info.getDescription(), getIcon(info));
            this.info = info;
        }

        @Override
        public void run() {
            info.run();
        }
    }

    private static ImageDescriptor getIcon(final RuntimeAction info) {
        try {
            return ImageDescriptor.createFromURL(new URL(info.getIconPath()));
        } catch (Exception ex) {
            logger.log(Level.WARNING, "Cannot obtain icon " + info.getIconPath(), ex);
            return null;
        }
    }

    /** Create SWT context menu
     *  @param site RCP site
     *  @param parent Parent SWT widget
     *  @param representation Representation
     */
    public ContextMenuSupport(final RuntimeViewPart view, final Control parent,
            final RCP_JFXRepresentation representation) {
        this.view = view;
        final IWorkbenchPartSite site = view.getSite();
        shell = site.getShell();

        // Tried to use a JFX context menu on the individual items,
        // but adding the existing PV contributions requires parsing
        // the registry and creating suitable JFX menu entries.
        // Finally, it was unclear how to set the "activeMenuSelection"
        // required by existing object contributions.
        //
        // So using SWT context menu, automatically populated with PV contributions.

        // Selection provider to inform RCP about PV for the context menu
        final ISelectionProvider sel_provider = new RCPSelectionProvider();
        site.setSelectionProvider(sel_provider);

        // RCP context menu w/ "additions" placeholder for contributions
        final MenuManager mm = new MenuManager();
        mm.setRemoveAllWhenShown(true);
        mm.addMenuListener(manager -> fillContextMenu(manager));
        site.registerContextMenu(mm, sel_provider);

        // Create menu ..
        final Menu menu = mm.createContextMenu(parent);
        // .. but _don't_ attach to SWT control
        //     parent.setMenu(menu);

        // Menu is shown by representation listener _after_
        // setting the selection to widget's PV
        final ToolkitListener tkl = new ToolkitListener() {
            @Override
            public void handleContextMenu(final Widget widget) {
                IStructuredSelection sel = StructuredSelection.EMPTY;
                final Optional<WidgetProperty<String>> name_prop = widget.checkProperty(propPVName);
                if (name_prop.isPresent()) {
                    final String pv_name = name_prop.get().getValue();
                    if (!pv_name.isEmpty())
                        sel = new StructuredSelection(new ProcessVariable(pv_name));
                }
                sel_provider.setSelection(sel);

                // Show the menu
                view.setActiveWidget(widget);
                menu.setVisible(true);
            }

            @Override
            public void handleClick(final Widget widget, final boolean with_control) {
                // Track the widget that was last clicked (for DnD hack)
                view.setActiveWidget(widget);
            }
        };
        representation.addListener(tkl);
        parent.addDisposeListener(event -> representation.removeListener(tkl));
    }

    private void fillContextMenu(final IMenuManager manager) {
        final Widget context_menu_widget = view.getActiveWidget();
        if (context_menu_widget == null) {
            logger.log(Level.WARNING, "Missing context_menu_widget");
            manager.add(new Action("No widget") {
            });
        } else {
            // Widget info
            manager.add(new WidgetInfoAction(context_menu_widget));

            // Actions of the widget
            for (ActionInfo info : context_menu_widget.propActions().getValue().getActions()) {
                if (info.getType() == ActionType.OPEN_DISPLAY) {
                    // Add variant for all the available Target types: Replace, new Tab, ...
                    final OpenDisplayActionInfo open_info = (OpenDisplayActionInfo) info;
                    for (Target target : Target.values()) {
                        // STANDALONE can be achieved via StandaloneAction on new display
                        if (target == Target.STANDALONE)
                            continue;
                        final String desc = target == Target.REPLACE ? open_info.getDescription()
                                : open_info.getDescription() + " (" + target + ")";
                        manager.add(new ActionInfoWrapper(context_menu_widget, new OpenDisplayActionInfo(desc,
                                open_info.getFile(), open_info.getMacros(), target)));
                    }
                } else
                    manager.add(new ActionInfoWrapper(context_menu_widget, info));
            }

            // Actions of the widget runtime
            final WidgetRuntime<Widget> runtime = RuntimeUtil.getRuntime(context_menu_widget);
            if (runtime == null)
                throw new NullPointerException("Missing runtime for " + context_menu_widget);
            for (RuntimeAction info : runtime.getRuntimeActions())
                manager.add(new RuntimeActionWrapper(context_menu_widget, info));
        }

        // Placeholder for ProcessVariable object contributions
        manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));

        manager.add(new Separator());

        if (context_menu_widget != null) {
            final Node node = JFXBaseRepresentation.getJFXNode(context_menu_widget);
            final Scene scene = node.getScene();

            manager.add(new SaveSnapshotAction(shell, scene));
            manager.add(new PrintAction(shell, scene));
            manager.add(new SendEMailAction(shell, scene));
            manager.add(new SendLogbookAction(shell, scene));
            manager.add(new FullScreenAction(view.getSite().getPage()));

            if (support_standalone)
                manager.add(new StandaloneAction(view));
        }

        // Placeholder for the display editor.
        // If editor.rcp plugin is included, it adds "Open in editor"
        manager.add(new Separator("display_editor"));
        manager.add(new ReloadDisplayAction());
    };
}