]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.network.ui/src/org/simantics/district/network/ui/breakdown/DistrictNetworkBreakdownPanel.java
Add refresh to context menu in district network breakdown view
[simantics/district.git] / org.simantics.district.network.ui / src / org / simantics / district / network / ui / breakdown / DistrictNetworkBreakdownPanel.java
1 package org.simantics.district.network.ui.breakdown;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7 import java.util.concurrent.ConcurrentHashMap;
8 import java.util.concurrent.ConcurrentMap;
9
10 import org.eclipse.core.runtime.IProgressMonitor;
11 import org.eclipse.core.runtime.IStatus;
12 import org.eclipse.core.runtime.Status;
13 import org.eclipse.core.runtime.SubMonitor;
14 import org.eclipse.jface.action.Action;
15 import org.eclipse.jface.action.IMenuListener;
16 import org.eclipse.jface.action.IMenuManager;
17 import org.eclipse.jface.action.MenuManager;
18 import org.eclipse.jface.layout.GridDataFactory;
19 import org.eclipse.jface.layout.GridLayoutFactory;
20 import org.eclipse.jface.layout.TreeColumnLayout;
21 import org.eclipse.jface.resource.ImageDescriptor;
22 import org.eclipse.jface.resource.JFaceResources;
23 import org.eclipse.jface.resource.LocalResourceManager;
24 import org.eclipse.jface.resource.ResourceManager;
25 import org.eclipse.jface.viewers.CellLabelProvider;
26 import org.eclipse.jface.viewers.ColumnLabelProvider;
27 import org.eclipse.jface.viewers.ColumnWeightData;
28 import org.eclipse.jface.viewers.ITreeContentProvider;
29 import org.eclipse.jface.viewers.TreeViewer;
30 import org.eclipse.jface.viewers.TreeViewerColumn;
31 import org.eclipse.jface.viewers.Viewer;
32 import org.eclipse.jface.viewers.ViewerComparator;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.events.KeyAdapter;
36 import org.eclipse.swt.events.KeyEvent;
37 import org.eclipse.swt.events.MouseAdapter;
38 import org.eclipse.swt.events.MouseEvent;
39 import org.eclipse.swt.events.SelectionAdapter;
40 import org.eclipse.swt.events.SelectionEvent;
41 import org.eclipse.swt.events.SelectionListener;
42 import org.eclipse.swt.graphics.Image;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.TreeColumn;
46 import org.simantics.DatabaseJob;
47 import org.simantics.NameLabelMode;
48 import org.simantics.NameLabelUtil;
49 import org.simantics.ObjectIdentitySchedulingRule;
50 import org.simantics.Simantics;
51 import org.simantics.db.ReadGraph;
52 import org.simantics.db.Resource;
53 import org.simantics.db.common.procedure.adapter.DisposableListener;
54 import org.simantics.db.common.request.UnaryRead;
55 import org.simantics.db.exception.DatabaseException;
56 import org.simantics.db.layer0.adapter.Instances;
57 import org.simantics.db.layer0.request.ActiveModels;
58 import org.simantics.district.network.ontology.DistrictNetworkResource;
59 import org.simantics.district.network.ui.DistrictNetworkUIUtil;
60 import org.simantics.district.network.ui.breakdown.Input.NetworkDiagram;
61 import org.simantics.district.network.ui.breakdown.Input.NetworkDiagrams;
62 import org.simantics.district.network.ui.breakdown.Input.Subgraph;
63 import org.simantics.district.network.ui.internal.Activator;
64 import org.simantics.modeling.ModelingResources;
65 import org.simantics.utils.strings.AlphanumComparator;
66 import org.simantics.utils.strings.StringUtils;
67 import org.simantics.utils.ui.BundleUtils;
68 import org.simantics.utils.ui.ISelectionUtils;
69 import org.simantics.utils.ui.SWTUtils;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 /**
74  * @author Tuukka Lehtonen
75  * @since 1.35.0
76  */
77 public class DistrictNetworkBreakdownPanel extends Composite {
78
79     private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkBreakdownPanel.class);
80
81     private DisposableListener<NetworkDiagrams> inputListener;
82     //private NetworkDiagrams currentInput;
83     private ConcurrentMap<NetworkDiagram, Subgraph[]> subgraphs = new ConcurrentHashMap<>();
84
85     private ResourceManager rm;
86     private TreeViewer tree;
87
88     private TreeComparator treeComparator;
89
90     private ImageDescriptor mapImg;
91
92     public DistrictNetworkBreakdownPanel(Composite parent, int style) {
93         super(parent, style);
94         this.rm = new LocalResourceManager(JFaceResources.getResources(), this);
95
96         this.mapImg = BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/map.png");
97
98         addDisposeListener(e -> DistrictNetworkBreakdownPanel.this.widgetDisposed());
99         createUI(parent);
100         trackInput();
101     }
102
103     protected void widgetDisposed() {
104         if (inputListener != null)
105             inputListener.dispose();
106     }
107
108     private void createUI(Composite parent) {
109         GridLayoutFactory.fillDefaults().applyTo(this);
110
111         Composite treeParent = new Composite(this, SWT.NONE);
112         TreeColumnLayout treeLayout = new TreeColumnLayout();
113         treeParent.setLayout(treeLayout);
114         GridDataFactory.fillDefaults().grab(true, true).span(1, 1).applyTo(treeParent);
115
116         treeComparator = new TreeComparator();
117
118         tree = new TreeViewer(treeParent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
119         tree.setUseHashlookup(true);
120         tree.getTree().setHeaderVisible(true);
121         tree.setContentProvider(new ContentProvider());
122         tree.setComparator(treeComparator);
123         createColumns(treeLayout);
124
125         tree.getTree().addKeyListener(new KeyAdapter() {
126             @Override
127             public void keyPressed(KeyEvent e) {
128                 if (e.keyCode == SWT.F5) {
129                     e.doit = false;
130                     refreshSelection();
131                 }
132             }
133         });
134         tree.getTree().addMouseListener(new MouseAdapter() {
135             @Override
136             public void mouseDoubleClick(MouseEvent e) {
137                 new ShowSubnetworkAction(selectedSubgraphs()).run();
138             }
139         });
140
141         MenuManager menuManager = new MenuManager("District Network Breakdown Actions", "#DistrictNetworkBreakdownPopup");
142         menuManager.setRemoveAllWhenShown(true);
143         Menu menu = menuManager.createContextMenu(tree.getTree());
144         tree.getTree().setMenu(menu);
145         menuManager.addMenuListener(new IMenuListener() {
146             @Override
147             public void menuAboutToShow(IMenuManager manager) {
148                 List<Subgraph> sel = selectedSubgraphs();
149                 if (sel.size() > 0 && fromSameDiagram(sel)) {
150                     manager.add(new ShowSubnetworkAction(sel));
151                 }
152                 Set<NetworkDiagram> diagrams = new HashSet<>();
153                 if (sel.size() > 0) {
154                     for (Subgraph graph : sel) {
155                         diagrams.add(graph.parent);
156                     }
157                 }
158                 if (diagrams.isEmpty())
159                     diagrams.addAll(subgraphs.keySet());
160                 manager.add(new RefreshAction(diagrams));
161                 //manager.add(new DeleteAction());
162             }
163
164             private boolean fromSameDiagram(List<Subgraph> list) {
165                 NetworkDiagram d = null;
166                 for (Subgraph sg : list) {
167                     if (d == null)
168                         d = sg.parent;
169                     else if (!d.equals(sg.parent))
170                         return false;
171                 }
172                 return true;
173             }
174         });
175     }
176
177     protected void refreshSelection() {
178         for (Object obj : tree.getStructuredSelection().toArray()) {
179             if (obj instanceof NetworkDiagram) {
180                 subgraphs.remove(obj);
181                 tree.refresh(obj);
182             }
183         }
184     }
185
186     protected void scheduleRefresh(NetworkDiagram diagram) {
187         SWTUtils.asyncExec(tree.getTree(), () -> {
188             if (!tree.getTree().isDisposed())
189                 tree.refresh(diagram);
190         });
191     }
192
193     private void createColumns(TreeColumnLayout layout) {
194         TreeViewerColumn nameCol = createColumn(0, layout, "Diagram", "Diagram / Subgraph Number", new NameLabeler(), 1, 100, SWT.LEFT);
195         createColumn(1, layout, "Nodes", "Node Count of Subnetwork", new NodeCountLabeler(), 0, 100, SWT.LEFT);
196         createColumn(2, layout, "Edges", "Edge Count of Subnetwork", new EdgeCountLabeler(), 0, 100, SWT.LEFT);
197         setSortColumn(nameCol.getColumn(), 1);
198     }
199
200     private TreeViewerColumn createColumn(int index, TreeColumnLayout layout, String text, String tooltip,
201             CellLabelProvider labelProvider, int weight, int minimumWidth, int style) {
202         TreeViewerColumn column = new TreeViewerColumn(tree, style);
203         column.getColumn().setText(text);
204         column.getColumn().setToolTipText(StringUtils.safeString(tooltip));
205         column.getColumn().setResizable(true);
206         column.getColumn().setMoveable(true);
207         column.getColumn().addSelectionListener(getSelectionAdapter(column.getColumn(), index));
208         column.setLabelProvider(labelProvider);
209         layout.setColumnData(column.getColumn(), new ColumnWeightData(weight, minimumWidth));
210         return column;
211     }
212
213     private List<Subgraph> selectedSubgraphs() {
214         return ISelectionUtils.filterSelection(tree.getStructuredSelection(), Subgraph.class);
215     }
216
217     public class TreeComparator extends ViewerComparator {
218         private static final int DESCENDING = 1;
219
220         private int propertyIndex = -1;
221         private int direction = 0;
222
223         public int getDirection() {
224             return direction == 1 ? SWT.DOWN : SWT.UP;
225         }
226
227         public void setColumn(int column) {
228             if (column == this.propertyIndex) {
229                 // Same column as last sort; toggle the direction
230                 direction = 1 - direction;
231             } else {
232                 // New column; do an ascending sort
233                 this.propertyIndex = column;
234                 direction = 0;
235             }
236         }
237
238         @Override
239         public int compare(Viewer viewer, Object e1, Object e2) {
240             int rc = 0;
241             if (e1 instanceof NetworkDiagram) {
242                 NetworkDiagram nd1 = (NetworkDiagram) e1;
243                 NetworkDiagram nd2 = (NetworkDiagram) e2;
244                 rc = AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(nd1.name, nd2.name);
245             } else if (e1 instanceof Subgraph) {
246                 Subgraph sg1 = (Subgraph) e1;
247                 Subgraph sg2 = (Subgraph) e2;
248                 switch (propertyIndex) {
249                     case 0:
250                         break;
251                     case 1:
252                         rc = Integer.compare(sg1.vertices.size(), sg2.vertices.size());
253                         break;
254                     case 2:
255                         rc = Integer.compare(sg1.edges.size(), sg2.edges.size());
256                         break;
257                     default:
258                         rc = 0;
259                 }
260             }
261             if (direction == DESCENDING)
262                 rc = -rc;
263             return rc;
264         }
265     }
266
267     private void setSortColumn(TreeColumn column, int index) {
268         treeComparator.setColumn(index);
269         int dir = treeComparator.getDirection();
270         tree.getTree().setSortDirection(dir);
271         tree.getTree().setSortColumn(column);
272     }
273
274     private SelectionListener getSelectionAdapter(TreeColumn column, int index) {
275         return new SelectionAdapter() {
276             @Override
277             public void widgetSelected(SelectionEvent e) {
278                 setSortColumn(column, index);
279                 tree.refresh(true);
280             }
281         };
282     }
283
284     private Runnable setInput(Input.NetworkDiagrams input) {
285         return () -> {
286             if (!tree.getTree().isDisposed())
287                 tree.setInput(input);
288         };
289     }
290
291     private void configureInput(Input.NetworkDiagrams input) {
292         SWTUtils.asyncExec(DistrictNetworkBreakdownPanel.this, setInput(input));
293     }
294
295     private static final String PENDING = "Pending...";
296     private static final Subgraph[] PENDING_RESULT = {};
297
298     private class ContentProvider implements ITreeContentProvider {
299         @Override
300         public void dispose() {
301         }
302
303         @Override
304         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
305         }
306
307         @Override
308         public Object[] getElements(Object inputElement) {
309             return ((NetworkDiagrams) inputElement).diagrams.toArray();
310         }
311
312         @Override
313         public Object getParent(Object element) {
314             if (element instanceof Subgraph)
315                 return ((Subgraph) element).parent;
316             return null;
317         }
318
319         @Override
320         public Object[] getChildren(Object parentElement) {
321             if (parentElement instanceof NetworkDiagram) {
322                 NetworkDiagram nd = (NetworkDiagram) parentElement;
323                 Subgraph[] sgs = subgraphs.get(nd);
324                 if (sgs != null)
325                     return sgs;
326                 subgraphs.put(nd, PENDING_RESULT);
327                 new CalculateSubgraphs(nd).schedule();
328                 return new Object[] { PENDING };
329             }
330             return new Object[0];
331         }
332
333         @Override
334         public boolean hasChildren(Object element) {
335             if (element instanceof NetworkDiagram) {
336                 NetworkDiagram nd = (NetworkDiagram) element;
337                 Subgraph[] sgs = subgraphs.get(nd);
338                 if (sgs != null)
339                     return sgs.length > 0;
340                 // We don't know yet so there might be children
341                 return true;
342             }
343             return false;
344         }
345     }
346
347     private class NameLabeler extends ColumnLabelProvider {
348         @Override
349         public String getText(Object element) {
350             if (element instanceof NetworkDiagram) {
351                 return ((Input.NetworkDiagram) element).name;
352             } else if (element instanceof Subgraph) {
353                 return "" + ((Subgraph) element).index;
354             }
355             return element.toString();
356         }
357         @Override
358         public Image getImage(Object element) {
359             if (element instanceof NetworkDiagram) {
360                 return (Image) rm.get(mapImg);
361             }
362             return null;
363         }
364     }
365
366     private class NodeCountLabeler extends ColumnLabelProvider {
367         @Override
368         public String getText(Object element) {
369             if (element instanceof Subgraph) {
370                 Subgraph sg = (Subgraph) element;
371                 return Integer.toString(sg.vertices.size());
372             }
373             return "";
374         }
375     }
376
377     private class EdgeCountLabeler extends ColumnLabelProvider {
378         @Override
379         public String getText(Object element) {
380             if (element instanceof Subgraph) {
381                 Subgraph sg = (Subgraph) element;
382                 return Integer.toString(sg.edges.size());
383             }
384             return "";
385         }
386     }
387
388     private void trackInput() {
389         this.inputListener = new DisposableListener<NetworkDiagrams>() {
390             @Override
391             public void execute(NetworkDiagrams input) {
392                 //currentInput = input;
393                 configureInput(input);
394             }
395             @Override
396             public void exception(Throwable t) {
397                 LOGGER.error("Problems resolving active models", t);
398             }
399         };
400         Simantics.getSession().asyncRequest(new ResolveInput(Simantics.getProjectResource()), inputListener);
401     }
402
403     private static class ResolveInput extends UnaryRead<Resource, NetworkDiagrams> {
404
405         public ResolveInput(Resource parameter) {
406             super(parameter);
407         }
408
409         @Override
410         public NetworkDiagrams perform(ReadGraph graph) throws DatabaseException {
411             DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
412             ModelingResources MOD = ModelingResources.getInstance(graph);
413             Instances index = graph.getPossibleAdapter(DN.Diagram, Instances.class);
414             NetworkDiagrams result = new NetworkDiagrams();
415
416             for (Resource model : graph.syncRequest(new ActiveModels(parameter))) {
417                 for (Resource diagram : index.find(graph, model)) {
418                     Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
419                     String name = NameLabelUtil.modalName(graph, composite != null ? composite : diagram, NameLabelMode.NAME_AND_LABEL);
420                     result.diagrams.add(new NetworkDiagram(name, diagram));
421                 }
422             }
423
424             return result;
425         }
426
427     }
428
429     private class CalculateSubgraphs extends DatabaseJob {
430
431         private NetworkDiagram diagram;
432
433         public CalculateSubgraphs(NetworkDiagram diagram) {
434             super("Calculate subgraphs");
435             this.diagram = diagram;
436             setUser(true);
437             setRule(new ObjectIdentitySchedulingRule(diagram));
438         }
439
440         @Override
441         protected IStatus run(IProgressMonitor monitor) {
442             try {
443                 SubgraphProvider[] sgps = Activator.getInstance().getSubgraphProviders();
444                 SubMonitor mon = SubMonitor.convert(monitor, NLS.bind("Calculating district network breakdown for {0}", diagram.name), sgps.length);
445                 List<Subgraph> result = new ArrayList<>();
446                 for (SubgraphProvider sgp : sgps)
447                     for (Subgraph sg : sgp.getProvider(mon.split(1), diagram))
448                         result.add(sg);
449                 subgraphs.put(diagram, result.toArray(new Subgraph[result.size()]));
450                 scheduleRefresh(diagram);
451                 return Status.OK_STATUS;
452             } finally {
453                 monitor.done();
454             }
455         }
456
457     }
458
459     private class RefreshAction extends Action {
460
461         private Set<NetworkDiagram> diagram;
462
463         public RefreshAction(Set<NetworkDiagram> diagrams) {
464             super("Refresh");
465             this.diagram = diagrams;
466         }
467
468         @Override
469         public void run() {
470             diagram.forEach(d -> {
471                 subgraphs.remove(d);
472                 tree.refresh();
473             });
474         }
475     }
476
477     private static class ShowSubnetworkAction extends Action {
478
479         private final List<Subgraph> subgraphs;
480
481         public ShowSubnetworkAction(List<Subgraph> subgraphs) {
482             super("Show Subnetwork on Diagram");
483             this.subgraphs = subgraphs;
484         }
485
486         @Override
487         public void run() {
488             try {
489                 openDiagram(subgraphs);
490             } catch (DatabaseException e) {
491                 LOGGER.error("Failed to show selected subnetwork", e);
492             }
493         }
494
495     }
496
497     public static boolean openDiagram(List<Subgraph> subgraphs) throws DatabaseException {
498         if (subgraphs.isEmpty())
499             return false;
500         Subgraph subgraph = subgraphs.get(0);
501         Resource dhElement = subgraph.vertices.size() > 0
502                 ? subgraph.vertices.get(0)
503                 : subgraph.edges.size() > 0 ? subgraph.edges.get(0) : null;
504         DistrictNetworkUIUtil.Input in = dhElement != null
505                 ? Simantics.getSession().syncRequest(new DistrictNetworkUIUtil.ElementToInput(dhElement)) : null;
506
507         if (in != null) {
508             List<Resource> selection = new ArrayList<>();
509             subgraphs.forEach(sg -> {
510                 sg.vertices.forEach(selection::add);
511                 sg.edges.forEach(selection::add);
512             });
513             DistrictNetworkUIUtil.openDNDiagramEditorWithSelection(in, selection.toArray());
514             return true;
515         }
516         return false;
517     }
518
519 }