package org.simantics.modeling.ui.actions.e4;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.model.application.ui.menu.MToolControl;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.simantics.Simantics;
import org.simantics.browsing.ui.NodeContext;
import org.simantics.browsing.ui.common.NodeContextBuilder;
import org.simantics.browsing.ui.model.InvalidContribution;
import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
import org.simantics.browsing.ui.model.actions.IActionCategory;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
import org.simantics.db.common.request.BinaryRead;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.procedure.Listener;
import org.simantics.modeling.ui.Activator;
/**
* Used like this in E4 model fragments:
*
*
*
*
*
*
* http://www.simantics.org/Project-1.2/MainToolbarActionContext
*
*
*
*
*
*
* @author Antti Villberg
* @author Jani Simomaa
* @author Tuukka Lehtonen
*/
public class GlobalModeledToolbarActions {
@Inject UISynchronize sync;
private List browseContexts;
private boolean disposed;
private Composite composite;
@PostConstruct
protected void create(Composite parent, MToolControl control) {
this.composite = parent;
browseContexts = new ArrayList<>(control.getTags());
Simantics.getSession().asyncRequest(
new GetContributions(Simantics.getProjectResource(), browseContexts),
new ContributionListener());
}
@PreDestroy
private void dispose() {
disposed = true;
if (composite != null) {
for (Control c : composite.getChildren())
c.dispose();
composite = null;
}
}
class ContributionListener implements Listener>, Runnable {
AtomicReference> lastResult = new AtomicReference<>();
@Override
public void execute(List result) {
if (composite != null) {
lastResult.set(result);
sync.asyncExec(this);
}
}
@Override
public void run() {
List result = lastResult.getAndSet(null);
if (result == null || composite == null || composite.isDisposed())
return;
ToolBar tb = (ToolBar) composite.getParent();
for (ToolItem item : tb.getItems())
item.dispose();
for (IContributionItem item : result)
item.fill(tb, -1);
Composite tbp = tb.getParent();
tbp.layout(true, true);
}
@Override
public void exception(Throwable t) {
Activator.getDefault().getLog().log(
new Status(IStatus.ERROR, Activator.PLUGIN_ID,
"Global modeled toolbar contribution listener ran into an unexpected exception.",
t));
}
@Override
public boolean isDisposed() {
return disposed;
}
}
static class GetContributions extends BinaryRead, List> {
public GetContributions(Resource from, Collection browseContextNames) {
super(from, browseContextNames);
}
@Override
public List perform(ReadGraph graph) throws DatabaseException {
return getContributionItems(graph, parameter, parameter2);
}
}
private static Collection getBrowseContextResources(ReadGraph graph, Collection browseContexts) throws DatabaseException {
List result = new ArrayList(browseContexts.size());
for (String name : browseContexts)
result.add(graph.getResource(name));
return result;
}
private static List getContributionItems(ReadGraph graph, Resource from, Collection browseContextNames) throws DatabaseException {
Collection browseContexts = getBrowseContextResources(graph, browseContextNames);
NodeContext nodeContext = NodeContextBuilder.buildWithInput(from);
try {
ActionBrowseContext defaultContext = ActionBrowseContext.create(graph, browseContexts);
ActionBrowseContext browseContext = ActionBrowseContext.get(graph, nodeContext, defaultContext);
Map> result = browseContext.getActions(graph, nodeContext, Collections.singletonList(nodeContext));
return toContributionItems(result);
} catch (InvalidContribution e) {
Activator.getDefault().getLog().log(
new Status(IStatus.ERROR, Activator.PLUGIN_ID,
"Encountered invalid modeled contribution(s) while loading global modeled toolbar contributions.",
e));
}
return Collections.emptyList();
}
private static List toContributionItems(Map> map) {
if (map.isEmpty())
return Collections.emptyList();
IActionCategory[] categories = map.keySet().toArray(new IActionCategory[map.size()]);
Arrays.sort(categories, IActionCategory.ACTION_CATEGORY_COMPARATOR);
ArrayList items = new ArrayList<>();
boolean first = true;
for (IActionCategory category : categories) {
List actions = map.get(category);
Collections.sort(actions, ACTION_COMPARATOR);
if (category != null && category.isSubmenu()) {
MenuManager manager = new MenuManager(category.getLabel());
for (Action action : actions)
manager.add(new ActionContributionItem(action));
items.add(manager);
}
else {
if (first)
first = false;
else
items.add(new Separator(category == null ? "" : category.getLabel()));
for (Action action : actions)
items.add(new ActionContributionItem(action));
}
}
return items;
}
private static final Comparator ACTION_COMPARATOR = (o1, o2) -> {
String t1 = o1.getText();
String t2 = o2.getText();
return t1 == null ?
(t2 == null ? 0 : -1)
: (t2 == null ? 1 : t1.compareTo(t2));
};
}