Commit 64264e41 authored by Tuukka Lehtonen's avatar Tuukka Lehtonen

Support multiple selection for OpenWithMenuContribution

Added IDynamicMenuContribution2 interface to allow
DynamicMenuContribution implementations to avoid unnecessarily
converting input ISelection to anything else before editor adapter
testing.

gitlab #682
parent 22f5eb44
......@@ -26,9 +26,10 @@ 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.function.DbFunction;
import org.simantics.db.management.ISessionContext;
import org.simantics.ui.SimanticsUI;
import org.simantics.utils.ui.ErrorLogger;
import org.slf4j.LoggerFactory;
/**
* A more or less carbon copy of CompoundContributionItem with the exception of
......@@ -51,7 +52,13 @@ import org.simantics.utils.ui.ErrorLogger;
* parameter, override {@link #getSelectedObjects()}
* </p>
*
* <p>
* Optionally implement {@link IDynamicMenuContribution2} to avoid ISelection
* preprocessing.
* </p>
*
* @author Tuukka Lehtonen
* @see IDynamicMenuContribution2
*/
public abstract class DynamicMenuContribution extends CompoundContributionItem {
......@@ -84,28 +91,23 @@ public abstract class DynamicMenuContribution extends CompoundContributionItem {
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<IContributionItem[]>() {
@Override
public IContributionItem[] perform(ReadGraph graph) throws DatabaseException {
return getContributionItems(graph, selection);
}
@Override
public String toString() {
return DynamicMenuContribution.this.toString();
}
});
if (this instanceof IDynamicMenuContribution2) {
IDynamicMenuContribution2 dmc = (IDynamicMenuContribution2) this;
ISelection selection = getSelection();
return dmc.preAcceptSelection2(selection)
? trySyncRequest(graph -> dmc.getContributionItems2(graph, selection))
: NONE;
} else {
final Object[] selection = getSelectedObjects();
//System.out.println(getClass().getSimpleName() + "@" + System.identityHashCode(this) + "( " + System.identityHashCode(selection) + ": " + Arrays.toString(selection) + " )");
return preAcceptSelection(selection)
? trySyncRequest(graph -> getContributionItems(graph, selection))
: NONE;
}
} catch (DatabaseException | InterruptedException e) {
ErrorLogger.defaultLogError(e);
LoggerFactory.getLogger(getClass()).error("Failed to get contribution items", e);
}
}
return NONE;
......@@ -119,6 +121,24 @@ public abstract class DynamicMenuContribution extends CompoundContributionItem {
return true;
}
private IContributionItem[] trySyncRequest(DbFunction<ReadGraph, IContributionItem[]> f) throws DatabaseException, InterruptedException {
return RequestUtil.trySyncRequest(
Simantics.getSession(),
SimanticsUI.UI_THREAD_REQUEST_START_TIMEOUT,
SimanticsUI.UI_THREAD_REQUEST_EXECUTION_TIMEOUT_LONG,
NONE,
new UniqueRead<IContributionItem[]>() {
@Override
public IContributionItem[] perform(ReadGraph graph) throws DatabaseException {
return f.apply(graph);
}
@Override
public String toString() {
return DynamicMenuContribution.this.toString();
}
});
}
//////////////////////////////////////////////////////////////////////////
protected ISelection getSelection() {
......@@ -131,6 +151,9 @@ public abstract class DynamicMenuContribution extends CompoundContributionItem {
ISelection sel = getSelection();
if (!(sel instanceof IStructuredSelection))
return NO_OBJECTS;
IStructuredSelection iss = (IStructuredSelection) sel;
if (iss.size() == 0)
return NO_OBJECTS;
return ((IStructuredSelection) sel).toArray();
}
......@@ -139,7 +162,7 @@ public abstract class DynamicMenuContribution extends CompoundContributionItem {
return resources.length == 1 ? resources[0] : null;
}
protected IContributionItem[] toContributionItems(IAction... actions) {
protected static IContributionItem[] toContributionItems(IAction... actions) {
if (actions == null)
return NONE;
......
package org.simantics.ui.contribution;
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.simantics.db.ReadGraph;
import org.simantics.db.exception.DatabaseException;
/**
* A replacement interface that a {@link DynamicMenuContribution} implementation
* can implement to optimize away unnecessary ISelection conversions.
*
* <p>
* This interface exists to keep backwards compatible with old
* {@link DynamicMenuContribution} implementations but allow for optimizations
* where needed.
*
* @author TuukkaLehtonen
* @since 1.48.0
*/
public interface IDynamicMenuContribution2 {
/**
* 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.
*
* <p>
* 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 <code>true</code> if selection is {@link IStructuredSelection} and
* non-empty
*/
default boolean preAcceptSelection2(ISelection selection) {
return selection instanceof IStructuredSelection
? !((IStructuredSelection) selection).isEmpty()
: false;
}
default IContributionItem[] getContributionItems2(ReadGraph graph, ISelection selection) throws DatabaseException {
return DynamicMenuContribution.toContributionItems(getActions2(graph, selection));
}
default IAction[] getActions2(ReadGraph graph, ISelection selection) throws DatabaseException {
throw new UnsupportedOperationException("Not implemented");
}
}
\ No newline at end of file
......@@ -13,7 +13,6 @@ package org.simantics.ui.contribution;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
......@@ -26,6 +25,8 @@ import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
......@@ -45,7 +46,7 @@ import org.simantics.utils.ui.workbench.WorkbenchUtils;
/**
* @author Tuukka Lehtonen
*/
public class OpenWithMenuContribution extends DynamicMenuContribution implements IExecutableExtension{
public class OpenWithMenuContribution extends DynamicMenuContribution implements IExecutableExtension, IDynamicMenuContribution2 {
private static final boolean DEBUG_ADAPTERS = false;
......@@ -104,65 +105,92 @@ public class OpenWithMenuContribution extends DynamicMenuContribution implements
return "Open With";
}
@Override
public boolean preAcceptSelection2(ISelection selection) {
return selection instanceof IStructuredSelection
? !((IStructuredSelection) selection).isEmpty()
: false;
}
@Override
public IContributionItem[] getContributionItems2(ReadGraph graph, ISelection selection) throws DatabaseException {
IStructuredSelection ss = (IStructuredSelection) selection;
Object input = selection;
if (ss.size() == 1) {
// For backwards compatibility single-selections are pre-processed to resources.
input = extractResource(graph, ss.getFirstElement());
if (input == null)
return NONE;
}
EditorAdapter[] editorAdapters = resolveEditorAdapters(graph, input);
if (editorAdapters.length == 0)
return NONE;
return new IContributionItem[] { openWithItem(editorAdapters, input) };
}
@Override
protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException {
final Object r = extractResource(graph, selection[0]);
if (r == null)
Object input = extractResource(graph, selection[0]);
if (input == null)
return NONE;
EditorAdapter[] _editorAdapters = EditorRegistry.getInstance().getAdaptersFor(graph, r);
final EditorAdapter[] editorAdapters = filter(_editorAdapters, editorId);
EditorAdapter[] editorAdapters = resolveEditorAdapters(graph, input);
if (editorAdapters.length == 0)
return NONE;
return new IContributionItem[] {
new ContributionItem() {
@Override
public void fill(Menu menu, int index) {
MenuItem openWith = new MenuItem(menu, SWT.CASCADE, index);
openWith.setText(getText());
openWith.setEnabled(editorAdapters.length > 0);
Menu subMenu = new Menu(menu);
openWith.setMenu(subMenu);
if (editorAdapters.length > 0) {
// Sort the open with actions in descending priority order.
Adapter[] adapters = toAdapters(editorAdapters, r);
for (Adapter a : adapters)
addMenuItem(subMenu, a, r);
}
}
return new IContributionItem[] { openWithItem(editorAdapters, input) };
}
protected ContributionItem openWithItem(EditorAdapter[] editorAdapters, Object input) {
return new ContributionItem() {
@Override
public void fill(Menu menu, int index) {
MenuItem openWith = new MenuItem(menu, SWT.CASCADE, index);
openWith.setText(getText());
openWith.setEnabled(editorAdapters.length > 0);
Menu subMenu = new Menu(menu);
openWith.setMenu(subMenu);
if (editorAdapters.length > 0) {
// Sort the open with actions in descending priority order.
Adapter[] adapters = toAdapters(editorAdapters, input);
for (Adapter a : adapters)
addMenuItem(subMenu, a, input);
}
}
};
}
protected Adapter[] toAdapters(EditorAdapter[] editorAdapters, Object r) {
Adapter[] adapters = new Adapter[editorAdapters.length];
for (int i = 0; i < editorAdapters.length; i++)
adapters[i] = new Adapter(editorAdapters[i], r, true);
Arrays.sort(adapters, new Comparator<Adapter>() {
@Override
public int compare(Adapter o1, Adapter o2) {
int delta = o2.getPriority() - o1.getPriority();
if (delta != 0)
return delta;
return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getText(), o2.getText());
}
});
return adapters;
Adapter[] adapters = new Adapter[editorAdapters.length];
for (int i = 0; i < editorAdapters.length; i++)
adapters[i] = new Adapter(editorAdapters[i], r, true);
Arrays.sort(adapters, (o1, o2) -> {
int delta = o2.getPriority() - o1.getPriority();
if (delta != 0)
return delta;
return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getText(), o2.getText());
});
return adapters;
}
private EditorAdapter[] resolveEditorAdapters(ReadGraph graph, Object input) throws DatabaseException {
return filter(EditorRegistry.getInstance().getAdaptersFor(graph, input), editorId);
}
protected EditorAdapter[] filter(EditorAdapter[] adapters, String id) {
if (id == null)
return adapters;
List<EditorAdapter> editorAdapters = new ArrayList<EditorAdapter>();
for (EditorAdapter a : adapters) {
if (id.equals(a.getEditorId()))
continue;
editorAdapters.add(a);
}
return editorAdapters.toArray(new EditorAdapter[editorAdapters.size()]);
if (id == null)
return adapters;
List<EditorAdapter> editorAdapters = new ArrayList<>(adapters.length);
for (EditorAdapter a : adapters) {
if (id.equals(a.getEditorId()))
continue;
editorAdapters.add(a);
}
return editorAdapters.toArray(new EditorAdapter[editorAdapters.size()]);
}
private void addMenuItem(Menu subMenu, Adapter adapter, Object r) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment