/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.ui.contribution; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.CompoundContributionItem; import org.simantics.DatabaseJob; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.RequestUtil; import org.simantics.db.exception.DatabaseException; import org.simantics.db.management.ISessionContext; import org.simantics.ui.SimanticsUI; import org.simantics.utils.ui.ErrorLogger; /** * A more or less carbon copy of CompoundContributionItem with the exception of * adding Simantics Graph database traits to the menu filling process. * *

* The simplest way to use this class is to override the * {@link #getActions(ReadGraph, Object[])} method which should return any * actions that you want to be performed on the specified selection. Another way * is to override {@link #getContributionItems(ReadGraph, Object[])} that by * default simply invokes the simplest method * {@link #getActions(ReadGraph, Object[])}. Overriding it allows you to create * e.g. submenus, which you cannot do with actions. *

* *

* To customize what gets passed to either * {@link #getActions(ReadGraph, Object[])} or * {@link #getContributionItems(ReadGraph, Object[])} as the selection * parameter, override {@link #getSelectedObjects()} *

* * @author Tuukka Lehtonen */ public abstract class DynamicMenuContribution extends CompoundContributionItem { protected static final Object[] NO_OBJECTS = {}; protected static final IAction[] NO_ACTIONS = {}; protected static final IContributionItem[] NONE = {}; /** * Creates a contribution item with a null id. */ protected DynamicMenuContribution() { super(); } /** * Creates a contribution item with the given (optional) id. * * @param id the contribution item identifier, or null */ protected DynamicMenuContribution(String id) { super(id); } /* (non-Javadoc) * @see org.eclipse.ui.actions.CompoundContributionItem#getContributionItems() */ @Override protected final IContributionItem[] getContributionItems() { if (DatabaseJob.inProgress()) return NONE; ISessionContext ctx = Simantics.getSessionContext(); if (ctx != null) { final Object[] selection = getSelectedObjects(); //System.out.println(getClass().getSimpleName() + "@" + System.identityHashCode(this) + "( " + System.identityHashCode(selection) + ": " + Arrays.toString(selection) + " )"); if (!preAcceptSelection(selection)) return NONE; try { return RequestUtil.trySyncRequest( Simantics.getSession(), SimanticsUI.UI_THREAD_REQUEST_START_TIMEOUT, SimanticsUI.UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG, NONE, new UniqueRead() { @Override public IContributionItem[] perform(ReadGraph graph) throws DatabaseException { return getContributionItems(graph, selection); } @Override public String toString() { return DynamicMenuContribution.this.toString(); } }); } catch (DatabaseException | InterruptedException e) { ErrorLogger.defaultLogError(e); } } return NONE; } /* (non-Javadoc) * @see org.eclipse.ui.actions.CompoundContributionItem#isDynamic() */ @Override public boolean isDynamic() { return true; } ////////////////////////////////////////////////////////////////////////// protected ISelection getSelection() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); ISelectionService service = window.getSelectionService(); return service.getSelection(); } protected Object[] getSelectedObjects() { ISelection sel = getSelection(); if (!(sel instanceof IStructuredSelection)) return NO_OBJECTS; return ((IStructuredSelection) sel).toArray(); } protected Object getSingleSelectedObject() { Object[] resources = getSelectedObjects(); return resources.length == 1 ? resources[0] : null; } protected IContributionItem[] toContributionItems(IAction... actions) { if (actions == null) return NONE; IContributionItem[] ret = new IContributionItem[actions.length]; for (int i = 0; i < actions.length; ++i) ret[i] = new ActionContributionItem(actions[i]); return ret; } ////////////////////////////////////////////////////////////////////////// // Override these where needed /** * Tests the input selection for whether it can produce any meaningful * contribution items in the first place. This is a filter that is invoked * before performing a database request to find out more about the possible * contributions. * *

* The default implementation checks that the input selection is not empty. * To be able to provide contributions for empty selection, you must * override this method. * * @param selection * @return */ protected boolean preAcceptSelection(Object[] selection) { return selection != null && selection.length > 0; } protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException { return toContributionItems( getActions(graph, selection) ); } protected IAction[] getActions(ReadGraph graph, Object[] selection) throws DatabaseException { return new IAction[0]; } }