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