]> gerrit.simantics Code Review - simantics/platform.git/blob
f3ed6365f7c572efd308d109592614dd0519d0cf
[simantics/platform.git] /
1 package org.simantics.modeling.ui.actions.e4;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Comparator;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.concurrent.atomic.AtomicReference;
11
12 import javax.annotation.PostConstruct;
13 import javax.annotation.PreDestroy;
14 import javax.inject.Inject;
15
16 import org.eclipse.core.runtime.IStatus;
17 import org.eclipse.core.runtime.Status;
18 import org.eclipse.e4.ui.di.UISynchronize;
19 import org.eclipse.e4.ui.model.application.ui.menu.MToolControl;
20 import org.eclipse.jface.action.Action;
21 import org.eclipse.jface.action.ActionContributionItem;
22 import org.eclipse.jface.action.IContributionItem;
23 import org.eclipse.jface.action.MenuManager;
24 import org.eclipse.jface.action.Separator;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.widgets.ToolBar;
28 import org.eclipse.swt.widgets.ToolItem;
29 import org.simantics.Simantics;
30 import org.simantics.browsing.ui.NodeContext;
31 import org.simantics.browsing.ui.common.NodeContextBuilder;
32 import org.simantics.browsing.ui.model.InvalidContribution;
33 import org.simantics.browsing.ui.model.actions.ActionBrowseContext;
34 import org.simantics.browsing.ui.model.actions.IActionCategory;
35 import org.simantics.db.ReadGraph;
36 import org.simantics.db.Resource;
37 import org.simantics.db.common.request.BinaryRead;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.procedure.Listener;
40 import org.simantics.modeling.ui.Activator;
41
42 /**
43  * Used like this in E4 model fragments:
44  * 
45  * <pre>
46  * <fragments xsi:type="fragment:StringModelFragment" featurename="trimContributions" parentElementId="org.eclipse.e4.legacy.ide.application">
47  *  <elements xsi:type="menu:TrimContribution" elementId="modeled.trim.id" parentId="org.eclipse.ui.main.toolbar" positionInParent="after=additions">
48  *    <children xsi:type="menu:ToolBar" elementId="toolbar.id">
49  *      <children xsi:type="menu:ToolControl" elementId="toolcontrol.id" contributionURI="bundleclass://org.simantics.modeling.ui/org.simantics.modeling.ui.actions.GlobalModeledToolbarActions">
50  *        <tags>http://www.simantics.org/Project-1.2/MainToolbarActionContext</tags>
51  *      </children>
52  *    </children>
53  *  </elements>
54  * </fragments>
55  * </pre>
56  * 
57  * @author Antti Villberg
58  * @author Jani Simomaa
59  * @author Tuukka Lehtonen
60  */
61 public class GlobalModeledToolbarActions {
62
63     @Inject UISynchronize sync;
64
65     private List<String> browseContexts;
66     private boolean disposed;
67     private Composite composite;
68
69     @PostConstruct
70     protected void create(Composite parent, MToolControl control) {
71         this.composite = parent;
72         browseContexts = new ArrayList<>(control.getTags());
73         Simantics.getSession().asyncRequest(
74                 new GetContributions(Simantics.getProjectResource(), browseContexts),
75                 new ContributionListener());
76     }
77
78     @PreDestroy
79     private void dispose() {
80         disposed = true;
81         if (composite != null) {
82             for (Control c : composite.getChildren())
83                 c.dispose();
84             composite = null;
85         }
86     }
87
88     class ContributionListener implements Listener<List<IContributionItem>>, Runnable {
89
90         AtomicReference<List<IContributionItem>> lastResult = new AtomicReference<>();
91
92         @Override
93         public void execute(List<IContributionItem> result) {
94             if (composite != null) {
95                 lastResult.set(result);
96                 sync.asyncExec(this);
97             }
98         }
99
100         @Override
101         public void run() {
102             List<IContributionItem> result = lastResult.getAndSet(null);
103             if (result == null || composite == null || composite.isDisposed())
104                 return;
105
106             ToolBar tb = (ToolBar) composite.getParent();
107             for (ToolItem item : tb.getItems())
108                 item.dispose();
109             for (IContributionItem item : result)
110                 item.fill(tb, -1);
111
112             Composite tbp = tb.getParent();
113             tbp.layout(true, true);
114         }
115
116         @Override
117         public void exception(Throwable t) {
118             Activator.getDefault().getLog().log(
119                     new Status(IStatus.ERROR, Activator.PLUGIN_ID,
120                             "Global modeled toolbar contribution listener ran into an unexpected exception.",
121                             t));
122         }
123
124         @Override
125         public boolean isDisposed() {
126             return disposed;
127         }
128     }
129
130     static class GetContributions extends BinaryRead<Resource, Collection<String>, List<IContributionItem>> {
131         public GetContributions(Resource from, Collection<String> browseContextNames) {
132             super(from, browseContextNames);
133         }
134         @Override
135         public List<IContributionItem> perform(ReadGraph graph) throws DatabaseException {
136             return getContributionItems(graph, parameter, parameter2);
137         }
138     }
139
140     private static Collection<Resource> getBrowseContextResources(ReadGraph graph, Collection<String> browseContexts) throws DatabaseException {
141         List<Resource> result = new ArrayList<Resource>(browseContexts.size());
142         for (String name : browseContexts)
143             result.add(graph.getResource(name));
144         return result;
145     }
146
147     private static List<IContributionItem> getContributionItems(ReadGraph graph, Resource from, Collection<String> browseContextNames) throws DatabaseException {
148         Collection<Resource> browseContexts = getBrowseContextResources(graph, browseContextNames);
149         NodeContext nodeContext = NodeContextBuilder.buildWithInput(from);
150         try {
151             ActionBrowseContext defaultContext = ActionBrowseContext.create(graph, browseContexts);
152             ActionBrowseContext browseContext = ActionBrowseContext.get(graph, nodeContext, defaultContext);
153             Map<IActionCategory, List<Action>> result = browseContext.getActions(graph, nodeContext, Collections.singletonList(nodeContext));
154             return toContributionItems(result);
155         } catch (InvalidContribution e) {
156             Activator.getDefault().getLog().log(
157                     new Status(IStatus.ERROR, Activator.PLUGIN_ID,
158                             "Encountered invalid modeled contribution(s) while loading global modeled toolbar contributions.",
159                             e));
160         }
161
162         return Collections.emptyList();
163     }
164
165     private static List<IContributionItem> toContributionItems(Map<IActionCategory, List<Action>> map) {
166         if (map.isEmpty())
167             return Collections.emptyList();
168
169         IActionCategory[] categories = map.keySet().toArray(new IActionCategory[map.size()]);
170         Arrays.sort(categories, IActionCategory.ACTION_CATEGORY_COMPARATOR);
171
172         ArrayList<IContributionItem> items = new ArrayList<>();
173         boolean first = true;
174         for (IActionCategory category : categories) {
175             List<Action> actions = map.get(category);
176             Collections.sort(actions, ACTION_COMPARATOR);
177
178             if (category != null && category.isSubmenu()) {
179                 MenuManager manager = new MenuManager(category.getLabel());
180                 for (Action action : actions)
181                     manager.add(new ActionContributionItem(action));
182                 items.add(manager);
183             }
184             else {
185                 if (first)
186                     first = false;
187                 else
188                     items.add(new Separator(category == null ? "" : category.getLabel()));
189                 for (Action action : actions)
190                     items.add(new ActionContributionItem(action));
191             }
192         }
193
194         return items;
195     }
196
197     private static final Comparator<Action> ACTION_COMPARATOR = (o1, o2) -> {
198         String t1 = o1.getText();
199         String t2 = o2.getText();
200         return t1 == null ?
201                 (t2 == null ? 0 : -1)
202                 : (t2 == null ? 1 : t1.compareTo(t2));
203     };
204
205 }