]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/editor/ModelUpdateEditor.java
efe67a76d96a66360baf7959e40d5def3c7a9da3
[simantics/interop.git] / org.simantics.interop.update / src / org / simantics / interop / update / editor / ModelUpdateEditor.java
1 package org.simantics.interop.update.editor;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Stack;
8
9 import org.eclipse.jface.resource.ImageDescriptor;
10 import org.eclipse.jface.resource.JFaceResources;
11 import org.eclipse.jface.resource.LocalResourceManager;
12 import org.eclipse.jface.viewers.CellEditor;
13 import org.eclipse.jface.viewers.CheckStateChangedEvent;
14 import org.eclipse.jface.viewers.CheckboxCellEditor;
15 import org.eclipse.jface.viewers.CheckboxTreeViewer;
16 import org.eclipse.jface.viewers.ColumnLabelProvider;
17 import org.eclipse.jface.viewers.ColumnViewer;
18 import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
19 import org.eclipse.jface.viewers.EditingSupport;
20 import org.eclipse.jface.viewers.ICheckStateListener;
21 import org.eclipse.jface.viewers.IStructuredContentProvider;
22 import org.eclipse.jface.viewers.ITreeContentProvider;
23 import org.eclipse.jface.viewers.ITreeViewerListener;
24 import org.eclipse.jface.viewers.TableViewer;
25 import org.eclipse.jface.viewers.TableViewerColumn;
26 import org.eclipse.jface.viewers.TreeExpansionEvent;
27 import org.eclipse.jface.viewers.TreeViewerColumn;
28 import org.eclipse.jface.viewers.Viewer;
29 import org.eclipse.swt.SWT;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Image;
34 import org.eclipse.swt.graphics.RGB;
35 import org.eclipse.swt.layout.FillLayout;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Button;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Label;
42 import org.eclipse.swt.widgets.Text;
43 import org.simantics.Simantics;
44 import org.simantics.db.ReadGraph;
45 import org.simantics.db.Resource;
46 import org.simantics.db.Session;
47 import org.simantics.db.Statement;
48 import org.simantics.db.WriteGraph;
49 import org.simantics.db.common.request.WriteRequest;
50 import org.simantics.db.exception.DatabaseException;
51 import org.simantics.db.request.Read;
52 import org.simantics.interop.test.GraphChanges;
53 import org.simantics.interop.update.Activator;
54 import org.simantics.interop.update.model.ModelUpdate;
55 import org.simantics.interop.update.model.ModelUpdate.WarningListener;
56 import org.simantics.interop.update.model.PropertyChange;
57 import org.simantics.interop.update.model.UpdateList;
58 import org.simantics.interop.update.model.UpdateNode;
59 import org.simantics.interop.update.model.UpdateOp;
60 import org.simantics.interop.update.model.UpdateStatus;
61 import org.simantics.interop.update.model.UpdateTree;
62 import org.simantics.interop.utils.TableUtils;
63 import org.simantics.utils.datastructures.Pair;
64 import org.simantics.utils.ui.ExceptionUtils;
65
66
67 /**
68  * Editor for updating models.
69  * 
70  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
71  *
72  */
73 public abstract class ModelUpdateEditor extends Composite implements WarningListener{
74
75         private Composite errorComposite;
76         
77         private CheckboxTreeViewer changeBrowser;
78         private TableViewer changeViewer;
79         
80         private Button updateAllButton;
81         private Button updateSelectedButton;
82         
83         private LocalResourceManager manager = new LocalResourceManager(JFaceResources.getResources());
84         
85         private Image checked;
86         private Image unchecked;
87         private Image warning;
88         
89         private Color containsColor;
90         private Color deletedColor;
91         private Color addedColor;
92         private Color disabledColor;
93
94         private ModelUpdate update;
95         
96         
97         private HashSet<UpdateNode> selectedStructure = new HashSet<UpdateNode>();
98         
99         public ModelUpdateEditor(Composite parent) {
100                 super(parent,SWT.NONE);
101                 checked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/tick.png"));
102                 unchecked = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/cross.png"));
103                 warning = manager.createImage(Activator.imageDescriptorFromPlugin("com.famfamfam.silk", "icons/error.png"));
104
105                 
106                 containsColor = new Color(parent.getDisplay(), new RGB(255,255,220));
107                 deletedColor = new Color(parent.getDisplay(), new RGB(255,220,220));
108                 addedColor = new Color(parent.getDisplay(), new RGB(220,255,220));
109                 disabledColor = new Color(parent.getDisplay(), new RGB(128,128,128));
110                 
111                 this.setLayout(new GridLayout(1,false));
112                 
113                 errorComposite = new Composite(this, SWT.BORDER);
114                 GridData data = new GridData();
115                 data.grabExcessHorizontalSpace = true;
116                 data.grabExcessVerticalSpace = false;
117                 data.horizontalAlignment = SWT.FILL;
118                 data.verticalAlignment = SWT.TOP;
119                 errorComposite.setLayoutData(data);
120                 errorComposite.setLayout(new GridLayout(2, false));
121                 
122                 errorComposite.setVisible(false);
123
124 //              IEditorInput input = getEditorInput();
125 //              if (!(input instanceof UpdateEditorInput)) {
126 //                      Label label = new Label(composite, SWT.NONE);
127 //                      label.setText("Unknown input.");
128 //                      return;
129 //              }
130                 
131                 Composite fillComposite = new Composite(this, SWT.NONE);
132                 data = new GridData();
133                 data.grabExcessHorizontalSpace = true;
134                 data.grabExcessVerticalSpace = true;
135                 data.horizontalAlignment = SWT.FILL;
136                 data.verticalAlignment = SWT.FILL;
137                 fillComposite.setLayoutData(data);
138                 fillComposite.setLayout(new FillLayout(SWT.VERTICAL));
139
140                 {
141                         changeBrowser = new CheckboxTreeViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION  );
142                         
143                         changeBrowser.setContentProvider(new UpdateTreeContentProvider());
144                         
145                         changeBrowser.getTree().setHeaderVisible(true);
146                         
147                         ColumnViewerToolTipSupport.enableFor(changeBrowser);
148
149                         
150                         TreeViewerColumn dataColumn = TableUtils.addColumn(changeBrowser, "Data", true, 600);
151
152                         dataColumn.setLabelProvider(new UpdateNodeLabelProvider());
153                         
154                         changeBrowser.addCheckStateListener(new ICheckStateListener() {
155                                 
156                                 @Override
157                                 public void checkStateChanged(CheckStateChangedEvent event) {
158                                         UpdateNode node = (UpdateNode) event.getElement();
159                                         if (node.getOp() != null) {
160                                                 node.getOp().select(Boolean.TRUE.equals(event.getChecked()));
161                                                 
162                                         }
163                                         refreshChecked();
164                                         
165                                 }
166                         });
167                         changeBrowser.addTreeListener(new ITreeViewerListener() {
168                                 
169                                 @Override
170                                 public void treeExpanded(TreeExpansionEvent event) {
171                                         event.getTreeViewer().getControl().getDisplay().asyncExec(new Runnable() {
172                                                 
173                                                 @Override
174                                                 public void run() {
175                                                         // TreeViewer uses lazy load, checked states must be updated when the tree is expanded. 
176                                                         refreshChecked();
177                                                 }
178                                         });
179                                         
180                                 }
181                                 
182                                 @Override
183                                 public void treeCollapsed(TreeExpansionEvent event) {
184                                         
185                                 }
186                         });
187                         changeBrowser.setUseHashlookup(true);
188                         
189                 }
190                 {
191                         changeViewer = new TableViewer(fillComposite,SWT.MULTI|SWT.V_SCROLL|SWT.BORDER|SWT.FULL_SELECTION);
192                         
193                         changeViewer.getTable().setHeaderVisible(true);
194                         changeViewer.getTable().setLinesVisible(true);
195                         
196                         changeViewer.setContentProvider(new ModificationListContentProvider());
197                         
198                         changeViewer.setUseHashlookup(true);
199                         
200                         TableViewerColumn cols[] = new TableViewerColumn[getChangeListColumnCount()];
201                         TableViewerColumn selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, getChangeListColumnWidth(0));
202                         cols[0] = selection;
203                         for (int i = 1 ; i < getChangeListColumnCount(); i++) {
204                                 TableViewerColumn column = TableUtils.addColumn(changeViewer, getColumntTitle(i), true, getChangeListColumnSortable(i), getChangeListColumnWidth(i));
205                                 cols[i] = column;
206                                 column.setLabelProvider(getLabelProvider(i));
207                                 configureChangeListColumn(i, column);
208                         }
209                         
210                         selection.setLabelProvider(new SelectionLabelProvider());
211                         selection.getColumn().addSelectionListener(new SelectionAdapter() {
212                                 @Override
213                                 public void widgetSelected(SelectionEvent e) {
214                                         if (update.getUpdateList().getChanges().size() > 0) {
215                                                 if (update.getUpdateList().getSelected().size() > 0) {
216                                                         update.getUpdateList().clearSelected();
217                                                 } else {
218                                                         for (PropertyChange nr : update.getUpdateList().getChanges())
219                                                                 nr.select(true);
220                                                 }
221                                                 changeViewer.refresh();
222                                         }
223                                 }
224                         });
225                         selection.setEditingSupport(new SelectionEditingSupport(changeViewer));
226                 
227                 }
228                 Composite buttonComposite = new Composite(this, SWT.NONE);
229                 
230                 data = new GridData();
231                 data.grabExcessHorizontalSpace = true;
232                 data.grabExcessVerticalSpace = false;
233                 data.horizontalAlignment = SWT.FILL;
234                 data.verticalAlignment = SWT.BOTTOM;
235                 
236                 buttonComposite.setLayoutData(data);
237                 
238                 buttonComposite.setLayout(new GridLayout(3, false));
239                 
240                 Label label = new Label(buttonComposite, SWT.NONE);
241                 data = new GridData();
242                 data.grabExcessHorizontalSpace = true;
243                 data.grabExcessVerticalSpace = false;
244                 data.horizontalAlignment = SWT.FILL;
245                 data.verticalAlignment = SWT.CENTER;
246                 label.setLayoutData(data);
247                 
248                 updateAllButton = new Button(buttonComposite, SWT.PUSH);
249                 updateAllButton.setText("Update All");
250                 updateAllButton.addSelectionListener(new SelectionAdapter() {
251                         @Override
252                         public void widgetSelected(SelectionEvent e) {
253                                 applyAll();
254                         }
255                 });
256                 updateSelectedButton = new Button(buttonComposite, SWT.PUSH);
257                 updateSelectedButton.setText("Update Selected");
258                 updateSelectedButton.addSelectionListener(new SelectionAdapter() {
259                         @Override
260                         public void widgetSelected(SelectionEvent e) {
261                                 applySelected();
262                         }
263                 });
264         }
265         
266         protected int getChangeListColumnCount() {
267                 return 6;
268         }
269         
270         protected int getChangeListColumnWidth(int col) {
271                 if (col == 0)
272                         return 20;
273                 return 100;
274         }
275         
276         protected boolean getChangeListColumnSortable(int col) {
277                 if (col == 0)
278                         return false;
279                 return true;
280         }
281         
282         protected void configureChangeListColumn(int col, TableViewerColumn column) {
283                 
284         }
285         
286         protected Session getSession() {
287                 return Simantics.getSession();
288         }
289         
290         protected String getColumntTitle(int i) {
291                 switch (i) {
292                 case 0:
293                         return "!";
294                 case 1:
295                         return "Diagram";
296                 case 2:
297                         return "Symbol";
298                 case 3:
299                         return "Property";
300                 case 4:
301                         return "Old Value";
302                 case 5:
303                         return "New Value";
304                 default:
305                         throw new RuntimeException("Unknown column index" + i);
306                         
307                 }
308         }
309         
310         protected abstract ColumnLabelProvider getLabelProvider(int i);
311         
312         public GraphChanges getChanges() {
313                 return update.getChanges();
314         }
315         
316         public UpdateTree getUpdateTree() {
317                 return update.getUpdateTree();
318         }
319         
320         public UpdateList getUpdateList() {
321                 return update.getUpdateList();
322         }
323         
324         public CheckboxTreeViewer getChangeBrowser() {
325                 return changeBrowser;
326         }
327         
328         public TableViewer getChangeViewer() {
329                 return changeViewer;
330         }
331         
332         public void showWarning(ModelUpdate update, String text) {
333                 errorComposite.setVisible(true);
334                 
335                 Label label = new Label(errorComposite, SWT.NONE);
336                 label.setImage(warning);
337                 label = new Label(errorComposite, SWT.NONE);
338                 label.setText(text);
339                 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
340                 this.layout(true);
341         }
342         
343         
344         private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
345         
346         
347         public void addCheckStateListener(ICheckStateListener listener) {
348                 checkStateListeners.add(listener);
349         }
350         
351         public void removeCheckStateListener(ICheckStateListener listener) {
352                 checkStateListeners.remove(listener);
353         }
354         
355         public void refreshChecked() {
356                 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
357                 nodeStack.push((UpdateNode)update.getUpdateTree().getRootNode());
358                 while (!nodeStack.isEmpty()) {
359                         UpdateNode n = nodeStack.pop();
360                         if (n.getOp() != null) {
361                                 UpdateOp op = n.getOp();
362                                 if (!op.isChange()) {
363                                         changeBrowser.setGrayed(n, true);
364                                         changeBrowser.setChecked(n, true);
365                                 } else {
366                                         boolean applied = op.applied();
367                                         if (applied) {
368                                                 changeBrowser.setChecked(n, true);
369                                                 changeBrowser.setGrayed(n, true);
370                                                 selectedStructure.remove(n);
371                                         } else {
372                                                 boolean sel = op.selected();
373                                                 if (sel) {
374                                                         selectedStructure.add(n);
375                                                         
376                                                 } else {
377                                                         selectedStructure.remove(n);
378                                                 }
379                                                 changeBrowser.setChecked(n, sel);
380                                                 changeBrowser.setGrayed(n, !op.enabled());
381                                         }
382                                 }
383                         } else {
384                                 changeBrowser.setGrayed(n, true);
385                                 changeBrowser.setChecked(n, true);
386                         }
387                         for (UpdateNode c : n.getChildren()) {
388                                 nodeStack.add((UpdateNode)c);
389                         }
390                 }
391                 
392                 changeBrowser.refresh();
393                 for (ICheckStateListener l : checkStateListeners) {
394                         l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
395                 }
396                 changeViewer.refresh();
397         }
398         
399         protected abstract ModelUpdate createUpdate();
400         
401         
402         
403         public void load(UpdateEditorInput uei) {
404
405                 Resource oldModel = uei.getR1(); // old model that is being updated, contains user made changes
406                 Resource newModel = uei.getR2(); // new model, 
407                 Resource originalModel = uei.getR3(); // original old model without user made changes 
408                 boolean newDistinct = uei.isNewDistinct();
409                 try {
410                         if (update != null)
411                                 update.removeListener(this);
412                         update = createUpdate();
413                         update.addListener(this);
414                         update.setInput(oldModel, newModel, originalModel, newDistinct);                        
415                 } catch (DatabaseException e) {
416                         Text text = new Text(this, SWT.MULTI);
417                         text.setText(e.getMessage());
418                         e.printStackTrace();
419                         return;
420                 }
421         
422                 setInputs();
423                 
424                 refreshChecked();
425         }
426         
427         protected void setInputs() {
428                 changeViewer.setInput(update.getUpdateList().getChanges());
429                 changeBrowser.setInput(update.getUpdateTree());
430         }
431         
432         private void applyAll() {
433                 updateAllButton.setEnabled(false);
434                 updateSelectedButton.setEnabled(false);
435                 getSession().asyncRequest(new WriteRequest(){
436                         @Override
437                         public void perform(WriteGraph graph) throws DatabaseException {
438                                 update.applyAll(graph);
439                                 
440                                 Display.getDefault().asyncExec(new Runnable() {
441                                         
442                                         @Override
443                                         public void run() {
444                                                 
445                                                 updateAllButton.setEnabled(true);
446                                                 updateSelectedButton.setEnabled(true);
447                                                 refreshChecked();
448                                                 changeViewer.refresh();
449                                         }
450                                 });
451                         }
452                         
453                         
454                 }, e -> {
455                         if (e != null)
456                                 ExceptionUtils.logAndShowError("Cannot update model", e);
457                 });
458         }
459         
460         private void applySelected() {
461                 updateAllButton.setEnabled(false);
462                 updateSelectedButton.setEnabled(false);
463                 getSession().asyncRequest(new WriteRequest(){
464                         @Override
465                         public void perform(WriteGraph graph) throws DatabaseException {
466                                 update.applySelected(graph);
467                                 
468                                 Display.getDefault().asyncExec(new Runnable() {
469                                         
470                                         @Override
471                                         public void run() {
472                                                 changeViewer.refresh();
473                                                 updateAllButton.setEnabled(true);
474                                                 updateSelectedButton.setEnabled(true);
475                                                 refreshChecked();
476                                         }
477                                 });
478                         }
479                 });
480         }       
481         
482         private class ModificationListContentProvider implements IStructuredContentProvider {
483                 
484                 @SuppressWarnings("unchecked")
485                 @Override
486                 public Object[] getElements(Object inputElement) {
487                         if (inputElement == null)
488                                 return null;
489                         Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
490                         return coll.toArray();
491                 }
492                 
493                 @Override
494                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
495                         
496                 }
497                 
498                 @Override
499                 public void dispose() {
500                         
501                 }
502         }
503         
504         private class UpdateTreeContentProvider implements ITreeContentProvider {
505                 @Override
506                 public Object[] getElements(Object inputElement) {
507                         if (inputElement instanceof UpdateTree)
508                                 return new Object[]{((UpdateTree)inputElement).getRootNode()};
509                         if (inputElement instanceof UpdateNode) {
510                                 UpdateNode node = (UpdateNode)inputElement;
511                                 return node.getChildren().toArray();
512                         }
513                         return new Object[0];
514                 }
515                 
516                 @Override
517                 public Object getParent(Object element) {
518                         return null;
519                 }
520                 
521                 @Override
522                 public Object[] getChildren(Object parentElement) {
523                         UpdateNode node = (UpdateNode)parentElement;
524                         return node.getChildren().toArray();
525                 }
526                 
527                 @Override
528                 public boolean hasChildren(Object element) {
529                         UpdateNode node = (UpdateNode)element;
530                         return node.getChildren().size() > 0;
531                 }
532                 
533                 @Override
534                 public void dispose() {
535                         
536                 }
537                 
538                 @Override
539                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
540                         
541                 }
542         }
543         
544         private class SelectionLabelProvider extends ColumnLabelProvider {
545                 
546                 public SelectionLabelProvider() {
547                         
548                 }
549                 @Override
550                 public String getText(Object element) {
551                         return "";
552                 }
553                 
554                 @Override
555                 public Image getImage(Object element) {
556                         if (update == null || !update.isInit())
557                                 return null;
558                         PropertyChange pc = (PropertyChange)element;
559                         if (pc.selected())
560                                 return checked;
561                         else
562                                 return unchecked;
563                 }
564                 
565                 @Override
566                 public Color getForeground(Object element) {
567                         PropertyChange pc = (PropertyChange)element;
568                         if (!pc.enabled())
569                                 return disabledColor;
570                         return null;
571                 }
572         }
573         
574         protected abstract class PropertyChangeLabelProvider extends ColumnLabelProvider {
575                 
576                 public PropertyChangeLabelProvider() {
577                         
578                 }
579                 
580                 @Override
581                 public Color getForeground(Object element) {
582                         PropertyChange pc = (PropertyChange)element;
583                         if (!pc.enabled())
584                                 return disabledColor;
585                         return null;
586                 }
587         }
588         
589         private class UpdateNodeLabelProvider extends ColumnLabelProvider {
590                 
591                 @Override
592                 public String getText(Object element) {
593                         final UpdateNode node = (UpdateNode)element;
594                         return node.getLabel();
595                 }
596                 
597                 @Override
598                 public Image getImage(Object element) {
599                         final UpdateNode node = (UpdateNode)element;
600                         try  {
601                                 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
602                                         @Override
603                                         public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
604                                                 return node.getImage(graph);
605                                         }
606                                 });
607                                 return manager.createImage(id);
608                         } catch (Exception e) {
609                                 return null;
610                         }
611                 }
612                 
613                 @Override
614                 public String getToolTipText(Object element) {
615                         final UpdateNode node = (UpdateNode)element;
616                         if (node.getOp() != null) {
617                                 return node.getOp().toString();
618                         } else {
619                                 return null;
620                         }
621                 }
622                 
623                 @Override
624                 public int getToolTipDisplayDelayTime(Object object) {
625                         return 1000;
626                 }
627                 
628                 @Override
629                 public int getToolTipTimeDisplayed(Object object) {
630                         return 10000;
631                 }
632
633                 @Override
634                 public Color getBackground(Object element) {
635                         final UpdateNode node = (UpdateNode)element;
636                         UpdateStatus status = node.getStatus();
637                         if (status == UpdateStatus.CONTAINS)
638                                 return containsColor;
639                         if (status == UpdateStatus.DELETED)
640                                 return deletedColor;
641                         if (status == UpdateStatus.NEW)
642                                 return addedColor;
643                         return null;            
644                 }
645                 
646                 @Override
647                 public Color getForeground(Object element) {
648                         final UpdateNode node = (UpdateNode)element;
649                         if (node.getOp() != null && !node.getOp().enabled())
650                                 return disabledColor;
651                         return null;
652                 }
653         }
654         
655         private class SelectionEditingSupport extends EditingSupport {
656                 
657                 
658                 public SelectionEditingSupport(ColumnViewer viewer) {
659                         super(viewer);
660                 }
661
662                 @Override
663                 protected boolean canEdit(Object element) {
664                         return true;
665                 }
666                 
667                 @Override
668                 protected CellEditor getCellEditor(Object element) {
669                         return new CheckboxCellEditor(null, SWT.CHECK);
670                 }
671                 
672                 @Override
673                 protected Object getValue(Object element) {
674                         if (update == null || !update.isInit())
675                                 return false;
676                         PropertyChange pc = (PropertyChange)element;
677                         return pc.selected();
678                 }
679                 
680                 @Override
681                 protected void setValue(Object element, Object value) {
682                         if (update == null || !update.isInit())
683                                 return;
684                         PropertyChange pc = (PropertyChange)element;
685                         if (Boolean.TRUE.equals(value))
686                                 pc.select(true);
687                         else
688                                 pc.select(false);
689                         
690                         getViewer().refresh(element);
691                 }
692                 
693                 
694         }
695
696
697 }