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