+package org.simantics.team.ui;\r
+\r
+import java.lang.ref.WeakReference;\r
+import java.nio.ByteBuffer;\r
+import java.nio.CharBuffer;\r
+import java.nio.charset.CharacterCodingException;\r
+import java.nio.charset.Charset;\r
+import java.nio.charset.CharsetDecoder;\r
+import java.nio.charset.CodingErrorAction;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.Vector;\r
+\r
+import org.eclipse.jface.action.Action;\r
+import org.eclipse.jface.layout.TreeColumnLayout;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.viewers.ColumnWeightData;\r
+import org.eclipse.jface.viewers.TreeViewer;\r
+import org.eclipse.jface.viewers.TreeViewerColumn;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.simantics.db.Operation;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.UndoContext;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.UndoRedoSupport;\r
+import org.simantics.db.service.UndoRedoSupport.ChangeListener;\r
+import org.simantics.team.Activator;\r
+import org.simantics.team.internal.Images;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.utils.ui.dialogs.ShowError;\r
+\r
+/**\r
+ * @author Kalle Kondelin\r
+ */\r
+public class UndoView extends TreeView {\r
+ @Override\r
+ public void createPartControl(Composite parent) {\r
+ this.parent = parent;\r
+ this.treeViewer = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION);\r
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay()), treeViewer.getTree());\r
+ Images.getInstance(JFaceResources.getResources());\r
+ TreeColumnLayout ad = new TreeColumnLayout();\r
+ parent.setLayout(ad);\r
+ treeViewer.getTree().setHeaderVisible(true);\r
+ //treeViewer.getTree().setLinesVisible(true);\r
+ //treeViewer.setUseHashlookup(true);\r
+ //treeViewer.setAutoExpandLevel(3);\r
+\r
+ TreeViewerColumn idColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);\r
+ TreeViewerColumn dateColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);\r
+ TreeViewerColumn commentColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);\r
+\r
+ idColumn.setLabelProvider(new IdColumnLabelProvider());\r
+ dateColumn.setLabelProvider(new DateColumnLabelProvider());\r
+ commentColumn.setLabelProvider(new CommentColumnLabelProvider());\r
+\r
+ idColumn.getColumn().setText("Id");\r
+ idColumn.getColumn().setWidth(20);\r
+ ad.setColumnData(idColumn.getColumn(), new ColumnWeightData(50, 20));\r
+ dateColumn.getColumn().setText("Date");\r
+ dateColumn.getColumn().setWidth(20);\r
+ ad.setColumnData(dateColumn.getColumn(), new ColumnWeightData(50, 40));\r
+ commentColumn.getColumn().setText("Comment");\r
+ commentColumn.getColumn().setWidth(20);\r
+ ad.setColumnData(commentColumn.getColumn(), new ColumnWeightData(50, 50));\r
+\r
+ final UndoContentProvider contentProvider = new UndoContentProvider(SimanticsUI.getSession());\r
+ treeViewer.setContentProvider(contentProvider);\r
+ treeViewer.setInput(this);\r
+ getViewSite().getActionBars().getToolBarManager().add(new Action("Remove All", Activator.REMOVE_ALL_ICON) {\r
+ @Override\r
+ public void run() {\r
+ contentProvider.removeAll();\r
+ }\r
+ });\r
+ getViewSite().getActionBars().getToolBarManager().add(new Action("Get Undo History", Activator.REFRESH_ICON) {\r
+ @Override\r
+ public void run() {\r
+ treeViewer.setContentProvider(contentProvider);\r
+ }\r
+ });\r
+ new ItemDetailToolTip(treeViewer, treeViewer.getTree(), null);\r
+ }\r
+}\r
+\r
+abstract class UndoViewElement extends TreeElement {\r
+ protected Session session;\r
+ UndoViewElement(Session session) {\r
+ this.session = session;\r
+ }\r
+ protected static final Charset UTF8 = Charset.forName("UTF-8");\r
+ protected String toString(byte[] data) {\r
+ if (data == null)\r
+ return "null";\r
+ CharsetDecoder decoder = UTF8.newDecoder();\r
+ ByteBuffer bbuf = ByteBuffer.wrap(data);\r
+ CharBuffer cbuf;\r
+ String s = null;\r
+ try {\r
+ cbuf = decoder.decode(bbuf);\r
+ s = cbuf.toString();\r
+ } catch (CharacterCodingException e) {\r
+ bbuf.rewind();\r
+ try {\r
+ cbuf = UTF8\r
+ .newDecoder()\r
+ .onMalformedInput(CodingErrorAction.REPLACE)\r
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)\r
+ .decode(bbuf);\r
+ s = cbuf.toString();\r
+ } catch (CharacterCodingException e1) {\r
+ return "String conversion error.";\r
+ }\r
+ }\r
+ return s;\r
+ }\r
+ @Override\r
+ boolean hasChildren() {\r
+ return false;\r
+ }\r
+ @Override\r
+ Object[] getChildren() {\r
+ return new Object[0];\r
+ }\r
+}\r
+\r
+class UndoContextElement extends UndoViewElement {\r
+ protected String name = "Undo";\r
+ protected WeakReference<UndoContext> contextRef;\r
+ UndoContextElement(Session session, UndoContext context) {\r
+ super(session);\r
+ this.contextRef = new WeakReference<UndoContext>(context);\r
+ }\r
+ @Override\r
+ protected Image getIdImage() {\r
+ return Images.getInstance().OTHER_IMAGE;\r
+ }\r
+ @Override\r
+ protected String getIdText() {\r
+ String s = toString();\r
+ return s.substring(0, Math.min(10, s.length()));\r
+ }\r
+ @Override\r
+ boolean hasChildren() {\r
+ UndoContext c = contextRef.get();\r
+ if (null == c)\r
+ return false;\r
+ try {\r
+ return c.getAll().size() > 0;\r
+ } catch (DatabaseException e) {\r
+ Logger.defaultLogError(e);\r
+ return false;\r
+ }\r
+ }\r
+ @Override\r
+ public String toString() {\r
+ UndoContext c = contextRef.get();\r
+ if (null == c)\r
+ return name + "@no context";\r
+ return name + "@" + c;\r
+ }\r
+ @Override\r
+ Object[] getChildren() {\r
+ UndoContext c = contextRef.get();\r
+ if (null == c)\r
+ return new Object[0];\r
+ Collection<Operation> operations;\r
+ try {\r
+ operations = c.getAll();\r
+ } catch (DatabaseException e) {\r
+ Logger.defaultLogError(e);\r
+ return new Object[0];\r
+ }\r
+ if (operations.size() < 1)\r
+ return new Object[0];\r
+ Object[] objects = new Object[operations.size()];\r
+ Iterator<Operation> it = operations.iterator();\r
+ int i = operations.size();\r
+ while (it.hasNext()) {\r
+ Operation op = it.next();\r
+ objects[--i] = new UndoCombinedElement(session, op);\r
+ }\r
+ assert(0==i);\r
+ return objects;\r
+ }\r
+}\r
+\r
+class RedoContextElement extends UndoContextElement {\r
+ RedoContextElement(Session session, UndoContext context) {\r
+ super(session, context);\r
+ this.name = "Redo";\r
+ }\r
+ @Override\r
+ boolean hasChildren() {\r
+ UndoContext c = contextRef.get();\r
+ if (null == c)\r
+ return false;\r
+ try {\r
+ return c.getRedoList().size() > 0;\r
+ } catch (DatabaseException e) {\r
+ Logger.defaultLogError(e);\r
+ return false;\r
+ }\r
+ }\r
+ @Override\r
+ Object[] getChildren() {\r
+ UndoContext c = contextRef.get();\r
+ if (null == c)\r
+ return new Object[0];\r
+ Collection<Operation> operations;\r
+ try {\r
+ operations = c.getRedoList();\r
+ } catch (DatabaseException e) {\r
+ Logger.defaultLogError(e);\r
+ operations = null;\r
+ }\r
+ if (operations.size() < 1)\r
+ return new Object[0];\r
+ Object[] objects = new Object[operations.size()];\r
+ Iterator<Operation> it = operations.iterator();\r
+ int i = operations.size();\r
+ while (it.hasNext()) {\r
+ Operation op = it.next();\r
+ objects[--i] = new UndoCombinedElement(session, op); \r
+ }\r
+ assert(0 == i);\r
+ return objects;\r
+ }\r
+}\r
+\r
+class UndoCombinedElement extends UndoViewElement {\r
+ private final Operation operation;\r
+ private final Vector<ChangeSetElement> elements = new Vector<ChangeSetElement>();\r
+ private String comment = null;\r
+ UndoCombinedElement(Session session, Operation op) {\r
+ super(session);\r
+ operation = op;\r
+ }\r
+ @Override\r
+ protected Image getIdImage() {\r
+ return Images.getInstance().UNDO_IMAGE;\r
+ }\r
+ @Override\r
+ protected String getIdText() {\r
+ return "" + operation.getId();\r
+ }\r
+ @Override\r
+ protected String getDateText() {\r
+ if (elements.size() == 0)\r
+ getChildren();\r
+ if (elements.size() > 0)\r
+ return elements.firstElement().getDateText();\r
+ else\r
+ return null; \r
+ }\r
+ @Override\r
+ protected String getCommentText() {\r
+ if (elements.size() == 0)\r
+ getChildren();\r
+ if (elements.size() > 0)\r
+ return elements.firstElement().getCommentText();\r
+ else\r
+ return null; \r
+ }\r
+ @Override\r
+ boolean hasChildren() {\r
+ return operation.getOperations().size() > 0;\r
+ }\r
+ @Override\r
+ Object[] getChildren() {\r
+ Collection<Operation> ops = operation.getOperations();\r
+ final int SIZE = ops.size(); \r
+ if (SIZE < 1)\r
+ return new Object[0];\r
+ elements.clear();\r
+ Iterator<Operation> it = ops.iterator();\r
+ for (int i=0; i<SIZE; ++i) {\r
+ Operation op = it.next();\r
+ ChangeSetElement e = new ChangeSetElement(session, op.getCSId());\r
+ elements.add(e);\r
+ }\r
+ if (elements.size() == 1)\r
+ return elements.firstElement().getChildren();\r
+ return elements.toArray();\r
+ }\r
+ @Override\r
+ public String toString() {\r
+ getChildren(); // This initializes elements vector.\r
+ for (ChangeSetElement e : elements) {\r
+ comment = e.toString();\r
+ if (null != comment)\r
+ return comment;\r
+ }\r
+ return "no name";\r
+ }\r
+}\r
+\r
+class UndoContentProvider extends ChangeSetProvider implements ChangeListener {\r
+ private UndoContextElement uce;\r
+ protected UndoRedoSupport undoRedoSupport;\r
+ private boolean subscribed = false;\r
+ UndoContentProvider(Session session) {\r
+ super(session);\r
+ undoRedoSupport = session.getService(UndoRedoSupport.class);\r
+ subscribe();\r
+ }\r
+ @Override\r
+ public Object[] getElements(Object inputElement) {\r
+ return getElements(session);\r
+ }\r
+ @Override\r
+ public void dispose() {\r
+ managementSupport.cancel(this);\r
+ this.subscribed = false;\r
+ super.dispose();\r
+ }\r
+ @Override\r
+ public void onChanged() {\r
+ if (null != viewer)\r
+ refresh();\r
+ }\r
+ protected Object[] getElements(Session session) {\r
+ try {\r
+ UndoRedoSupport undoSupport = session.getService(UndoRedoSupport.class);\r
+ UndoContext undoContext = undoSupport.getUndoContext(session);\r
+ if (null != undoContext) {\r
+ uce = new UndoContextElement(session, undoContext);\r
+ return uce.getChildren();\r
+ }\r
+ } catch (Exception e) {\r
+ Logger.defaultLogError(e);\r
+ if (DEBUG)\r
+ ShowError.showError("Error", e.getMessage(), e);\r
+ }\r
+ return new Object[0];\r
+ }\r
+ protected void subscribe() {\r
+ super.subscribe();\r
+ if (this.subscribed)\r
+ return;\r
+ if (null == undoRedoSupport)\r
+ undoRedoSupport = session.getService(UndoRedoSupport.class);\r
+ undoRedoSupport.subscribe(this);\r
+ this.subscribed = true;\r
+ }\r
+ void removeAll() {\r
+ UndoRedoSupport us = session.getService(UndoRedoSupport.class);\r
+ UndoContext uc = us.getUndoContext(session);\r
+ if (uc != null) {\r
+ uc.clear();\r
+ refresh();\r
+ }\r
+ }\r
+}\r
+\r
+class RedoContentProvider extends UndoContentProvider {\r
+ private RedoContextElement rce;\r
+ RedoContentProvider(Session session) {\r
+ super(session);\r
+ }\r
+ @Override\r
+ protected Object[] getElements(Session session) {\r
+ try {\r
+ UndoRedoSupport undoSupport = session.getService(UndoRedoSupport.class);\r
+ UndoContext undoContext = undoSupport.getUndoContext(session);\r
+ if (null != undoContext) {\r
+ rce = new RedoContextElement(session, undoContext);\r
+ return rce.getChildren();\r
+ }\r
+ } catch (Exception e) {\r
+ Logger.defaultLogError(e);\r
+ if (DEBUG)\r
+ ShowError.showError("Error", e.getMessage(), e);\r
+ }\r
+ return new Object[0];\r
+ }\r
+}\r