]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.interop.update/src/org/simantics/interop/update/editor/ModelUpdateEditor.java
5a6646edc66521422f6170404960de1dc861a7d3
[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.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.Session;
46 import org.simantics.db.Statement;
47 import org.simantics.db.WriteGraph;
48 import org.simantics.db.common.request.WriteRequest;
49 import org.simantics.db.exception.DatabaseException;
50 import org.simantics.db.request.Read;
51 import org.simantics.interop.test.GraphChanges;
52 import org.simantics.interop.update.Activator;
53 import org.simantics.interop.update.model.ModelUpdate;
54 import org.simantics.interop.update.model.ModelUpdate.WarningListener;
55 import org.simantics.interop.update.model.UpdateList;
56 import org.simantics.interop.update.model.UpdateNode;
57 import org.simantics.interop.update.model.UpdateOp;
58 import org.simantics.interop.update.model.UpdateStatus;
59 import org.simantics.interop.update.model.UpdateTree;
60 import org.simantics.interop.utils.TableUtils;
61 import org.simantics.ui.SimanticsUI;
62 import org.simantics.utils.datastructures.Callback;
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 selection = TableUtils.addColumn(changeViewer, getColumntTitle(0), false, false, 20);
199                         TableViewerColumn diagram = TableUtils.addColumn(changeViewer, getColumntTitle(1), true, true, 100);
200                         TableViewerColumn symbol = TableUtils.addColumn(changeViewer, getColumntTitle(2), true, true, 100);
201                         TableViewerColumn property = TableUtils.addColumn(changeViewer, getColumntTitle(3), true, true, 100);
202                         TableViewerColumn oldValue = TableUtils.addColumn(changeViewer, getColumntTitle(4), true, true, 100);
203                         TableViewerColumn newValue = TableUtils.addColumn(changeViewer, getColumntTitle(5), true, true, 100);
204                         
205                         diagram.setLabelProvider(getLabelProvider(1));
206                         symbol.setLabelProvider(getLabelProvider(2));
207                         property.setLabelProvider(getLabelProvider(3));
208                         oldValue.setLabelProvider(getLabelProvider(4));
209                         newValue.setLabelProvider(getLabelProvider(5));
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 (Pair<Statement, Statement> nr : update.getUpdateList().getChanges())
220                                                                 update.getUpdateList().addSelected(nr);
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 Session getSession() {
268                 return SimanticsUI.getSession();
269         }
270         
271         protected String getColumntTitle(int i) {
272                 switch (i) {
273                 case 0:
274                         return "!";
275                 case 1:
276                         return "Diagram";
277                 case 2:
278                         return "Symbol";
279                 case 3:
280                         return "Property";
281                 case 4:
282                         return "Old Value";
283                 case 5:
284                         return "New Value";
285                 default:
286                         throw new RuntimeException("Unknown column index" + i);
287                         
288                 }
289         }
290         
291         protected abstract ColumnLabelProvider getLabelProvider(int i);
292         
293         public GraphChanges getChanges() {
294                 return update.getChanges();
295         }
296         
297         public UpdateTree getUpdateTree() {
298                 return update.getUpdateTree();
299         }
300         
301         public UpdateList getUpdateList() {
302                 return update.getUpdateList();
303         }
304         
305         public CheckboxTreeViewer getChangeBrowser() {
306                 return changeBrowser;
307         }
308         
309         public TableViewer getChangeViewer() {
310                 return changeViewer;
311         }
312         
313         public void showWarning(ModelUpdate update, String text) {
314                 errorComposite.setVisible(true);
315                 
316                 Label label = new Label(errorComposite, SWT.NONE);
317                 label.setImage(warning);
318                 label = new Label(errorComposite, SWT.NONE);
319                 label.setText(text);
320                 //this.setStatusMessage("Update contains structural changes (new or deleted symbols), please create a new model.");
321                 this.layout(true);
322         }
323         
324         
325         private List<ICheckStateListener> checkStateListeners = new ArrayList<>();
326         
327         
328         public void addCheckStateListener(ICheckStateListener listener) {
329                 checkStateListeners.add(listener);
330         }
331         
332         public void removeCheckStateListener(ICheckStateListener listener) {
333                 checkStateListeners.remove(listener);
334         }
335         
336         public void refreshChecked() {
337                 Stack<UpdateNode> nodeStack = new Stack<UpdateNode>();
338                 nodeStack.push((UpdateNode)update.getUpdateTree().getRootNode());
339                 while (!nodeStack.isEmpty()) {
340                         UpdateNode n = nodeStack.pop();
341                         if (n.getOp() != null) {
342                                 UpdateOp op = n.getOp();
343                                 if (!op.isChange()) {
344                                         changeBrowser.setGrayed(n, true);
345                                         changeBrowser.setChecked(n, true);
346                                 } else {
347                                         boolean applied = op.applied();
348                                         if (applied) {
349                                                 changeBrowser.setChecked(n, true);
350                                                 changeBrowser.setGrayed(n, true);
351                                                 selectedStructure.remove(n);
352                                         } else {
353                                                 boolean sel = op.selected();
354                                                 if (sel) {
355                                                         selectedStructure.add(n);
356                                                         
357                                                 } else {
358                                                         selectedStructure.remove(n);
359                                                 }
360                                                 changeBrowser.setChecked(n, sel);
361                                                 changeBrowser.setGrayed(n, false);
362                                         }
363                                 }
364                         } else {
365                                 changeBrowser.setGrayed(n, true);
366                                 changeBrowser.setChecked(n, true);
367                         }
368                         for (UpdateNode c : n.getChildren()) {
369                                 nodeStack.add((UpdateNode)c);
370                         }
371                 }
372                 
373                 changeBrowser.refresh();
374                 for (ICheckStateListener l : checkStateListeners) {
375                         l.checkStateChanged(new CheckStateChangedEvent(changeBrowser, null, false));
376                 }
377                 changeViewer.refresh();
378         }
379         
380         protected abstract ModelUpdate createUpdate();
381         
382         
383         
384         public void load(UpdateEditorInput uei) {
385
386                 Resource oldModel = uei.getR1(); // old model that is being updated, contains user made changes
387                 Resource newModel = uei.getR2(); // new model, 
388                 Resource originalModel = uei.getR3(); // original old model without user made changes 
389                 boolean newDistinct = uei.isNewDistinct();
390                 try {
391                         if (update != null)
392                                 update.removeListener(this);
393                         update = createUpdate();
394                         update.addListener(this);
395                         update.setInput(oldModel, newModel, originalModel, newDistinct);                        
396                 } catch (DatabaseException e) {
397                         Text text = new Text(this, SWT.MULTI);
398                         text.setText(e.getMessage());
399                         e.printStackTrace();
400                         return;
401                 }
402         
403                 setInputs();
404                 
405                 refreshChecked();
406         }
407         
408         protected void setInputs() {
409                 changeViewer.setInput(update.getUpdateList().getChanges());
410                 changeBrowser.setInput(update.getUpdateTree());
411         }
412         
413         private void applyAll() {
414                 updateAllButton.setEnabled(false);
415                 updateSelectedButton.setEnabled(false);
416                 getSession().asyncRequest(new WriteRequest(){
417                         @Override
418                         public void perform(WriteGraph graph) throws DatabaseException {
419                                 update.applyAll(graph);
420                                 
421                                 Display.getDefault().asyncExec(new Runnable() {
422                                         
423                                         @Override
424                                         public void run() {
425                                                 
426                                                 updateAllButton.setEnabled(true);
427                                                 updateSelectedButton.setEnabled(true);
428                                                 refreshChecked();
429                                                 changeViewer.refresh();
430                                         }
431                                 });
432                         }
433                         
434                         
435                 }, new Callback<DatabaseException>() {
436                         @Override
437                         public void run(DatabaseException parameter) {
438                                 if (parameter != null)
439                                         ExceptionUtils.logAndShowError("Cannot update model", parameter);
440                         }
441                 });
442         }
443         
444         private void applySelected() {
445                 updateAllButton.setEnabled(false);
446                 updateSelectedButton.setEnabled(false);
447                 getSession().asyncRequest(new WriteRequest(){
448                         @Override
449                         public void perform(WriteGraph graph) throws DatabaseException {
450                                 update.applySelected(graph);
451                                 
452                                 Display.getDefault().asyncExec(new Runnable() {
453                                         
454                                         @Override
455                                         public void run() {
456                                                 changeViewer.refresh();
457                                                 updateAllButton.setEnabled(true);
458                                                 updateSelectedButton.setEnabled(true);
459                                                 refreshChecked();
460                                         }
461                                 });
462                         }
463                 });
464         }       
465         
466         private class ModificationListContentProvider implements IStructuredContentProvider {
467                 
468                 @SuppressWarnings("unchecked")
469                 @Override
470                 public Object[] getElements(Object inputElement) {
471                         if (inputElement == null)
472                                 return null;
473                         Collection<Pair<Statement, Statement>> coll = (Collection<Pair<Statement, Statement>>)inputElement;
474                         return coll.toArray();
475                 }
476                 
477                 @Override
478                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
479                         
480                 }
481                 
482                 @Override
483                 public void dispose() {
484                         
485                 }
486         }
487         
488         private class UpdateTreeContentProvider implements ITreeContentProvider {
489                 @Override
490                 public Object[] getElements(Object inputElement) {
491                         if (inputElement instanceof UpdateTree)
492                                 return new Object[]{((UpdateTree)inputElement).getRootNode()};
493                         if (inputElement instanceof UpdateNode) {
494                                 UpdateNode node = (UpdateNode)inputElement;
495                                 return node.getChildren().toArray();
496                         }
497                         return new Object[0];
498                 }
499                 
500                 @Override
501                 public Object getParent(Object element) {
502                         return null;
503                 }
504                 
505                 @Override
506                 public Object[] getChildren(Object parentElement) {
507                         UpdateNode node = (UpdateNode)parentElement;
508                         return node.getChildren().toArray();
509                 }
510                 
511                 @Override
512                 public boolean hasChildren(Object element) {
513                         UpdateNode node = (UpdateNode)element;
514                         return node.getChildren().size() > 0;
515                 }
516                 
517                 @Override
518                 public void dispose() {
519                         
520                 }
521                 
522                 @Override
523                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
524                         
525                 }
526         }
527         
528         private class SelectionLabelProvider extends ColumnLabelProvider {
529                 
530                 public SelectionLabelProvider() {
531                         
532                 }
533                 @Override
534                 public String getText(Object element) {
535                         return "";
536                 }
537                 
538                 @Override
539                 public Image getImage(Object element) {
540                         if (update == null || !update.isInit())
541                                 return null;
542 //                      if (update.getUpdateList() == null)
543 //                              return null;
544                         if (update.getUpdateList().isSelected((Pair<Statement, Statement>) element))
545                                 return checked;
546                         else
547                                 return unchecked;
548                 }
549         }
550         
551         private class UpdateNodeLabelProvider extends ColumnLabelProvider {
552                 
553                 @Override
554                 public String getText(Object element) {
555                         final UpdateNode node = (UpdateNode)element;
556                         return node.getLabel();
557                 }
558                 
559                 @Override
560                 public Image getImage(Object element) {
561                         final UpdateNode node = (UpdateNode)element;
562                         try  {
563                                 ImageDescriptor id = getSession().syncRequest(new Read<ImageDescriptor>() {
564                                         @Override
565                                         public ImageDescriptor perform(ReadGraph graph) throws DatabaseException {
566                                                 return node.getImage(graph);
567                                         }
568                                 });
569                                 return manager.createImage(id);
570                         } catch (Exception e) {
571                                 return null;
572                         }
573                 }
574                 
575                 @Override
576                 public String getToolTipText(Object element) {
577                         final UpdateNode node = (UpdateNode)element;
578                         if (node.getOp() != null) {
579                                 return node.getOp().toString();
580                         } else {
581                                 return null;
582                         }
583                 }
584                 
585                 @Override
586                 public int getToolTipDisplayDelayTime(Object object) {
587                         return 1000;
588                 }
589                 
590                 @Override
591                 public int getToolTipTimeDisplayed(Object object) {
592                         return 10000;
593                 }
594
595                 @Override
596                 public Color getBackground(Object element) {
597                         final UpdateNode node = (UpdateNode)element;
598                         UpdateStatus status = node.getStatus();
599                         if (status == UpdateStatus.CONTAINS)
600                                 return containsColor;
601                         if (status == UpdateStatus.DELETED)
602                                 return deletedColor;
603                         if (status == UpdateStatus.NEW)
604                                 return addedColor;
605                         return null;            
606                 }
607         }
608         
609         private class SelectionEditingSupport extends EditingSupport {
610                 
611                 
612                 @SuppressWarnings("rawtypes")
613                 public SelectionEditingSupport(ColumnViewer viewer) {
614                         super(viewer);
615                         
616                 }
617
618                 @Override
619                 protected boolean canEdit(Object element) {
620                         return true;
621                 }
622                 
623                 @Override
624                 protected CellEditor getCellEditor(Object element) {
625                         return new CheckboxCellEditor(null, SWT.CHECK);
626                 }
627                 
628                 @Override
629                 protected Object getValue(Object element) {
630                         if (update == null || !update.isInit())
631                                 return false;
632                         return update.getUpdateList().isSelected((Pair<Statement, Statement>) element);
633                 }
634                 
635                 @SuppressWarnings("unchecked")
636                 @Override
637                 protected void setValue(Object element, Object value) {
638                         if (update == null || !update.isInit())
639                                 return;
640                         if (Boolean.TRUE.equals(value))
641                                 update.getUpdateList().addSelected((Pair<Statement, Statement>) element);
642                         else
643                                 update.getUpdateList().removeSelected((Pair<Statement, Statement>) element);
644                         
645                         getViewer().refresh(element);
646                 }
647                 
648                 
649         }
650
651
652 }