From f252a08363e33fb00ad02f776972763c67169a63 Mon Sep 17 00:00:00 2001 From: hneemann Date: Fri, 26 May 2017 07:37:16 +0200 Subject: [PATCH] first attempt to implement an undo --- .../core/element/AttributeListener.java | 4 +- .../core/element/ElementAttributes.java | 24 +- .../digital/draw/elements/Circuit.java | 19 ++ .../digital/draw/elements/ElementOrder.java | 33 +-- .../digital/draw/elements/VisualElement.java | 3 +- .../java/de/neemann/digital/gui/Main.java | 7 +- .../neemann/digital/gui/NumberingWizard.java | 5 +- .../java/de/neemann/digital/gui/Settings.java | 2 +- .../gui/components/CircuitComponent.java | 113 ++++++++- .../components/modification/Modification.java | 12 + .../ModificationOfVisualElement.java | 27 ++ .../modification/Modifications.java | 39 +++ .../modification/ModifyAttribute.java | 26 ++ .../modification/ModifyAttributes.java | 24 ++ src/main/resources/edit-redo.png | Bin 0 -> 1490 bytes src/main/resources/edit-redo_hi.png | Bin 0 -> 3207 bytes src/main/resources/edit-undo.png | Bin 0 -> 1419 bytes src/main/resources/edit-undo_hi.png | Bin 0 -> 3102 bytes src/main/svg/edit-redo.svg | 231 +++++++++++++++++ src/main/svg/edit-undo.svg | 232 ++++++++++++++++++ src/main/svg/exp.sh | 2 + 21 files changed, 769 insertions(+), 34 deletions(-) create mode 100644 src/main/java/de/neemann/digital/gui/components/modification/Modification.java create mode 100644 src/main/java/de/neemann/digital/gui/components/modification/ModificationOfVisualElement.java create mode 100644 src/main/java/de/neemann/digital/gui/components/modification/Modifications.java create mode 100644 src/main/java/de/neemann/digital/gui/components/modification/ModifyAttribute.java create mode 100644 src/main/java/de/neemann/digital/gui/components/modification/ModifyAttributes.java create mode 100644 src/main/resources/edit-redo.png create mode 100644 src/main/resources/edit-redo_hi.png create mode 100644 src/main/resources/edit-undo.png create mode 100644 src/main/resources/edit-undo_hi.png create mode 100644 src/main/svg/edit-redo.svg create mode 100644 src/main/svg/edit-undo.svg diff --git a/src/main/java/de/neemann/digital/core/element/AttributeListener.java b/src/main/java/de/neemann/digital/core/element/AttributeListener.java index 488edca03..8cc824838 100644 --- a/src/main/java/de/neemann/digital/core/element/AttributeListener.java +++ b/src/main/java/de/neemann/digital/core/element/AttributeListener.java @@ -8,8 +8,6 @@ package de.neemann.digital.core.element; public interface AttributeListener { /** * Is called if an attribute changes - * - * @param key the key which value has changed */ - void attributeChanged(Key key); + void attributeChanged(); } diff --git a/src/main/java/de/neemann/digital/core/element/ElementAttributes.java b/src/main/java/de/neemann/digital/core/element/ElementAttributes.java index d88541d99..20c4596eb 100644 --- a/src/main/java/de/neemann/digital/core/element/ElementAttributes.java +++ b/src/main/java/de/neemann/digital/core/element/ElementAttributes.java @@ -90,15 +90,15 @@ public class ElementAttributes { attributes = new HashMap<>(); attributes.put(key.getKey(), value); } - fireValueChanged(key); + fireValueChanged(); } return this; } - private void fireValueChanged(Key key) { + private void fireValueChanged() { if (listeners != null) for (AttributeListener l : listeners) - l.attributeChanged(key); + l.attributeChanged(); } /** @@ -221,4 +221,22 @@ public class ElementAttributes { attributes.put(fileKey, file.getPath()); } + /** + * Apply the given attributes to this set + * + * @param elementAttributes the attributes to use + */ + public void getValuesFrom(ElementAttributes elementAttributes) { + if (attributes != null) + attributes.clear(); + else + attributes = new HashMap<>(); + + if (elementAttributes.attributes != null) + attributes.putAll(elementAttributes.attributes); + + if (attributes.isEmpty()) + attributes = null; + fireValueChanged(); + } } diff --git a/src/main/java/de/neemann/digital/draw/elements/Circuit.java b/src/main/java/de/neemann/digital/draw/elements/Circuit.java index 3f14f1c77..bcbe05ddd 100644 --- a/src/main/java/de/neemann/digital/draw/elements/Circuit.java +++ b/src/main/java/de/neemann/digital/draw/elements/Circuit.java @@ -146,6 +146,25 @@ public class Circuit { wires = new ArrayList<>(); } + /** + * Creates a copy of the given circuit + * + * @param original the original + */ + public Circuit(Circuit original) { + this(); + for (VisualElement ve : original.visualElements) + visualElements.add(new VisualElement(ve)); + for (Wire w : original.wires) + wires.add(new Wire(w)); + if (original.attributes != null) + attributes = new ElementAttributes(original.attributes); + + measurementOrdering = new ArrayList<>(); + if (original.measurementOrdering != null) + measurementOrdering.addAll(original.measurementOrdering); + } + /** * returns the elements attributes * diff --git a/src/main/java/de/neemann/digital/draw/elements/ElementOrder.java b/src/main/java/de/neemann/digital/draw/elements/ElementOrder.java index 0967b9ae9..47e34526a 100644 --- a/src/main/java/de/neemann/digital/draw/elements/ElementOrder.java +++ b/src/main/java/de/neemann/digital/draw/elements/ElementOrder.java @@ -1,7 +1,9 @@ package de.neemann.digital.draw.elements; import de.neemann.digital.core.element.ElementTypeDescription; +import de.neemann.digital.gui.components.CircuitComponent; import de.neemann.digital.gui.components.ElementOrderer; +import de.neemann.digital.gui.components.modification.Modification; import java.util.ArrayList; @@ -15,16 +17,16 @@ public class ElementOrder implements ElementOrderer.OrderInterface { private final ArrayList entries; private final ArrayList elements; - private final Circuit circuit; + private final CircuitComponent circuitComponent; /** * Creates a new instance * - * @param circuit the circuit witch components are to order - * @param description the description of the elements to order + * @param circuitComponent the circuit witch components are to order + * @param description the description of the elements to order */ - public ElementOrder(Circuit circuit, ElementTypeDescription description) { - this(circuit, element -> { + public ElementOrder(CircuitComponent circuitComponent, ElementTypeDescription description) { + this(circuitComponent, element -> { return element.equalsDescription(description); }); } @@ -32,12 +34,12 @@ public class ElementOrder implements ElementOrderer.OrderInterface { /** * Creates a new instance * - * @param circuit the circuit witch components are to order - * @param filter the filter to select the entries to order + * @param circuitComponent the circuitComponent witch components are to order + * @param filter the filter to select the entries to order */ - public ElementOrder(Circuit circuit, ElementFilter filter) { - this.circuit = circuit; - this.elements = circuit.getElements(); + public ElementOrder(CircuitComponent circuitComponent, ElementFilter filter) { + this.circuitComponent = circuitComponent; + this.elements = circuitComponent.getCircuit().getElements(); entries = new ArrayList<>(); for (int i = 0; i < elements.size(); i++) if (filter.accept(elements.get(i))) { @@ -59,9 +61,8 @@ public class ElementOrder implements ElementOrderer.OrderInterface { @Override public void swap(int i, int j) { - VisualElement y = elements.get(entries.get(i).i); - elements.set(entries.get(i).i, elements.get(entries.get(j).i)); - elements.set(entries.get(j).i, y); + int index1 = entries.get(i).i; + int index2 = entries.get(j).i; int z = entries.get(i).i; entries.get(i).i = entries.get(j).i; @@ -71,7 +72,11 @@ public class ElementOrder implements ElementOrderer.OrderInterface { entries.set(i, entries.get(j)); entries.set(j, x); - circuit.modified(); + circuitComponent.modify(circuit -> { + VisualElement y = elements.get(index1); + elements.set(index1, elements.get(index2)); + elements.set(index2, y); + }); } private final static class Entry { diff --git a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java index ec54d2634..b7bfbd69f 100644 --- a/src/main/java/de/neemann/digital/draw/elements/VisualElement.java +++ b/src/main/java/de/neemann/digital/draw/elements/VisualElement.java @@ -61,6 +61,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { this.elementName = proto.elementName; this.elementAttributes = new ElementAttributes(proto.elementAttributes); setPos(new Vector(proto.pos)); + this.shapeFactory=proto.shapeFactory; } /** @@ -82,7 +83,7 @@ public class VisualElement implements Drawable, Movable, AttributeListener { } @Override - public void attributeChanged(Key key) { + public void attributeChanged() { shape = null; resetGeometry(); } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 7fa73703b..50fbf1c17 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -104,6 +104,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS private static final Icon ICON_ZOOM_IN = IconCreator.create("View-zoom-in.png"); private static final Icon ICON_ZOOM_OUT = IconCreator.create("View-zoom-out.png"); private static final Icon ICON_HELP = IconCreator.create("help.png"); + private final CircuitComponent circuitComponent; private final ToolTipAction save; private final ElementLibrary library; @@ -193,6 +194,8 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS createEditMenu(menuBar); + toolBar.add(circuitComponent.getUndoAction().createJButtonNoText()); + toolBar.add(circuitComponent.getRedoAction().createJButtonNoText()); toolBar.add(circuitComponent.getDeleteAction().createJButtonNoText()); toolBar.addSeparator(); @@ -473,7 +476,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS ToolTipAction orderInputs = new ToolTipAction(Lang.get("menu_orderInputs")) { @Override public void actionPerformed(ActionEvent e) { - ElementOrder o = new ElementOrder(circuitComponent.getCircuit(), + ElementOrder o = new ElementOrder(circuitComponent, element -> element.equalsDescription(In.DESCRIPTION) || element.equalsDescription(Clock.DESCRIPTION)); new ElementOrderer<>(Main.this, Lang.get("menu_orderInputs"), o).showDialog(); @@ -483,7 +486,7 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS ToolTipAction orderOutputs = new ToolTipAction(Lang.get("menu_orderOutputs")) { @Override public void actionPerformed(ActionEvent e) { - ElementOrder o = new ElementOrder(circuitComponent.getCircuit(), + ElementOrder o = new ElementOrder(circuitComponent, element -> element.equalsDescription(Out.DESCRIPTION) || element.equalsDescription(Out.LEDDESCRIPTION)); new ElementOrderer<>(Main.this, Lang.get("menu_orderOutputs"), o).showDialog(); diff --git a/src/main/java/de/neemann/digital/gui/NumberingWizard.java b/src/main/java/de/neemann/digital/gui/NumberingWizard.java index 19275af5f..af677956f 100644 --- a/src/main/java/de/neemann/digital/gui/NumberingWizard.java +++ b/src/main/java/de/neemann/digital/gui/NumberingWizard.java @@ -6,6 +6,7 @@ import de.neemann.digital.core.io.Out; import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.gui.components.CircuitComponent; +import de.neemann.digital.gui.components.modification.ModifyAttribute; import de.neemann.digital.lang.Lang; import de.neemann.gui.Screen; @@ -71,11 +72,9 @@ public class NumberingWizard extends JDialog implements CircuitComponent.WizardN if (clicked.equalsDescription(In.DESCRIPTION) || clicked.equalsDescription(Clock.DESCRIPTION) || clicked.equalsDescription(Out.DESCRIPTION)) { - clicked.getElementAttributes().set(Keys.PINNUMBER, pinNumber); + circuitComponent.modify(new ModifyAttribute<>(clicked, Keys.PINNUMBER, pinNumber)); pinNumber++; setPinNumber(pinNumber); - circuitComponent.hasChanged(); - circuitComponent.getCircuit().modified(); } } diff --git a/src/main/java/de/neemann/digital/gui/Settings.java b/src/main/java/de/neemann/digital/gui/Settings.java index 8571b9a20..bc352dd52 100644 --- a/src/main/java/de/neemann/digital/gui/Settings.java +++ b/src/main/java/de/neemann/digital/gui/Settings.java @@ -89,7 +89,7 @@ public final class Settings implements AttributeListener { } @Override - public void attributeChanged(Key key) { + public void attributeChanged() { XStream xStream = Circuit.getxStream(); try (Writer out = new OutputStreamWriter(new FileOutputStream(filename), "utf-8")) { out.write("\n"); diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java index 33c356793..3cdafe6e5 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -8,6 +8,8 @@ import de.neemann.digital.core.element.ImmutableList; import de.neemann.digital.core.element.Key; import de.neemann.digital.core.element.Keys; import de.neemann.digital.draw.elements.*; +import de.neemann.digital.gui.components.modification.Modification; +import de.neemann.digital.gui.components.modification.ModifyAttribute; import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementNotFoundException; @@ -16,6 +18,7 @@ import de.neemann.digital.draw.library.LibraryNode; import de.neemann.digital.draw.shapes.Drawable; import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.gui.Main; +import de.neemann.digital.gui.components.modification.ModifyAttributes; import de.neemann.digital.gui.sync.NoSync; import de.neemann.digital.gui.sync.Sync; import de.neemann.digital.lang.Lang; @@ -53,6 +56,9 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe * The delete icon, also used from {@link de.neemann.digital.gui.components.terminal.TerminalDialog} */ public static final Icon ICON_DELETE = IconCreator.create("delete.png"); + private static final Icon ICON_UNDO = IconCreator.create("edit-undo.png"); + private static final Icon ICON_REDO = IconCreator.create("edit-redo.png"); + private static final String DEL_ACTION = "myDelAction"; private static final String ESC_ACTION = "myEscAction"; private static final int MOUSE_BORDER_SMALL = 10; @@ -76,6 +82,8 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe private final AbstractAction copyAction; private final AbstractAction pasteAction; private final AbstractAction rotateAction; + private final ToolTipAction undoAction; + private final ToolTipAction redoAction; private Circuit circuit; private MouseController activeMouseController; @@ -89,6 +97,10 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe private boolean lockMessageShown = false; private boolean antiAlias = true; + private ArrayList modifications; + private Circuit initialCircuit; + private int undoPosition; + /** * Creates a new instance @@ -162,6 +174,20 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe } }.setToolTip(Lang.get("menu_delete_tt")); + undoAction = new ToolTipAction(Lang.get("menu_undo"), ICON_UNDO) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + undo(); + } + }.setToolTip(Lang.get("menu_undo_tt")); + + redoAction = new ToolTipAction(Lang.get("menu_redo"), ICON_REDO) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + redo(); + } + }.setToolTip(Lang.get("menu_redo_tt")); + Action escapeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { @@ -244,12 +270,36 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe VisualElement ve = circuit.getElementAt(pos); if (ve != null && library.isProgrammable(ve.getElementName())) { boolean blown = ve.getElementAttributes().get(Keys.BLOWN); - ve.getElementAttributes().set(Keys.BLOWN, !blown); - circuit.modified(); - hasChanged(); + modify(new ModifyAttribute<>(ve, Keys.BLOWN, !blown)); } } + /** + * Apply a modification + * + * @param modification the modification + */ + public void modify(Modification modification) { + modification.modify(circuit); + addModificationAlreadyMade(modification); + } + + /** + * Add a modification already made + * + * @param modification the modification + */ + public void addModificationAlreadyMade(Modification modification) { + while (modifications.size() > undoPosition) + modifications.remove(modifications.size() - 1); + redoAction.setEnabled(false); + modifications.add(modification); + undoPosition = modifications.size(); + undoAction.setEnabled(true); + circuit.modified(); + hasChanged(); + } + /** * invalidates the image buffer and calls repaint(); */ @@ -258,6 +308,38 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe repaint(); } + /** + * undo last action + */ + public void undo() { + if (undoPosition > 0) { + circuit = new Circuit(initialCircuit); + undoPosition--; + for (int i = 0; i < undoPosition; i++) + modifications.get(i).modify(circuit); + redoAction.setEnabled(true); + if (undoPosition == 0) + undoAction.setEnabled(false); + circuit.modified(); + hasChanged(); + } + } + + /** + * redo last undo + */ + public void redo() { + if (undoPosition < modifications.size()) { + modifications.get(undoPosition).modify(circuit); + undoPosition++; + if (undoPosition == modifications.size()) + redoAction.setEnabled(false); + undoAction.setEnabled(true); + hasChanged(); + } + } + + /** * @return the main frame */ @@ -559,6 +641,11 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe } this.circuit = circuit; + modifications = new ArrayList<>(); + initialCircuit = new Circuit(circuit); + undoPosition = 0; + undoAction.setEnabled(false); + redoAction.setEnabled(false); circuit.addListener(this); @@ -646,10 +733,8 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe } } }.setToolTip(Lang.get("attr_help_tt"))); - if (attributeDialog.showDialog()) { - circuit.modified(); - hasChanged(); - } + if (attributeDialog.showDialog()) + addModificationAlreadyMade(new ModifyAttributes(vp)); } } catch (ElementNotFoundException ex) { // do nothing if element not found! @@ -679,6 +764,20 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe return locked; } + /** + * @return undo action + */ + public ToolTipAction getUndoAction() { + return undoAction; + } + + /** + * @return redo action + */ + public ToolTipAction getRedoAction() { + return redoAction; + } + private class MouseDispatcher extends MouseAdapter implements MouseMotionListener { private Vector pos; private boolean isMoved; diff --git a/src/main/java/de/neemann/digital/gui/components/modification/Modification.java b/src/main/java/de/neemann/digital/gui/components/modification/Modification.java new file mode 100644 index 000000000..5e3c14c32 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/modification/Modification.java @@ -0,0 +1,12 @@ +package de.neemann.digital.gui.components.modification; + +import de.neemann.digital.draw.elements.Circuit; + +/** + * Created by hneemann on 25.05.17. + */ +public interface Modification { + + void modify(Circuit circuit); + +} diff --git a/src/main/java/de/neemann/digital/gui/components/modification/ModificationOfVisualElement.java b/src/main/java/de/neemann/digital/gui/components/modification/ModificationOfVisualElement.java new file mode 100644 index 000000000..d3a890aad --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/modification/ModificationOfVisualElement.java @@ -0,0 +1,27 @@ +package de.neemann.digital.gui.components.modification; + +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.VisualElement; +import de.neemann.digital.draw.graphics.Vector; + +/** + * Created by hneemann on 25.05.17. + */ +public abstract class ModificationOfVisualElement implements Modification { + + private final Vector pos; + private final String name; + + public ModificationOfVisualElement(VisualElement ve) { + pos = ve.getPos(); + name = ve.getElementName(); + } + + public VisualElement getVisualElement(Circuit circuit) { + for (VisualElement ve : circuit.getElements()) { + if (ve.getPos().equals(pos) && ve.getElementName().equals(name)) + return ve; + } + throw new RuntimeException("internal error: Element not found!"); + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/modification/Modifications.java b/src/main/java/de/neemann/digital/gui/components/modification/Modifications.java new file mode 100644 index 000000000..7b8496c84 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/modification/Modifications.java @@ -0,0 +1,39 @@ +package de.neemann.digital.gui.components.modification; + +import de.neemann.digital.draw.elements.Circuit; + +import java.util.ArrayList; + +/** + * Created by hneemann on 25.05.17. + */ +public final class Modifications implements Modification { + private final ArrayList modifications; + + private Modifications(ArrayList modifications) { + this.modifications = modifications; + } + + @Override + public void modify(Circuit circuit) { + for (Modification m : modifications) + m.modify(circuit); + } + + public static final class Builder { + private final ArrayList list; + + public Builder() { + list = new ArrayList<>(); + } + + public Builder add(Modification m) { + list.add(m); + return this; + } + + public Modification build() { + return new Modifications(list); + } + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttribute.java b/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttribute.java new file mode 100644 index 000000000..830c9a6a3 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttribute.java @@ -0,0 +1,26 @@ +package de.neemann.digital.gui.components.modification; + +import de.neemann.digital.core.element.Key; +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.VisualElement; + +/** + * Created by hneemann on 25.05.17. + */ +public class ModifyAttribute extends ModificationOfVisualElement { + + private final Key key; + private final VALUE value; + + public ModifyAttribute(VisualElement ve, Key key, VALUE value) { + super(ve); + this.key = key; + this.value = value; + } + + @Override + public void modify(Circuit circuit) { + VisualElement ve = getVisualElement(circuit); + ve.getElementAttributes().set(key, value); + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttributes.java b/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttributes.java new file mode 100644 index 000000000..f5143fbca --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/modification/ModifyAttributes.java @@ -0,0 +1,24 @@ +package de.neemann.digital.gui.components.modification; + +import de.neemann.digital.core.element.ElementAttributes; +import de.neemann.digital.draw.elements.Circuit; +import de.neemann.digital.draw.elements.VisualElement; + +/** + * Created by hneemann on 25.05.17. + */ +public class ModifyAttributes extends ModificationOfVisualElement { + + private final ElementAttributes attributes; + + public ModifyAttributes(VisualElement ve) { + super(ve); + attributes = new ElementAttributes(ve.getElementAttributes()); + } + + @Override + public void modify(Circuit circuit) { + VisualElement ve = getVisualElement(circuit); + ve.getElementAttributes().getValuesFrom(attributes); + } +} diff --git a/src/main/resources/edit-redo.png b/src/main/resources/edit-redo.png new file mode 100644 index 0000000000000000000000000000000000000000..a5adb95df9c47ea3cd46e95252edf7080587b4f1 GIT binary patch literal 1490 zcmV;@1ugoCP)b%78FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H101tFUSaeirbZlh+MPzAoAW~&yZ=MZV2mk;86m&&cbU}4=Xm4@= zN?~htVjxp=Wod3@a_0N@F8}}lA#_DpbW?A2a${uxXmoUNIxjD3X>Dy`V=irVb7^B} zVQg$JV|r<3<6Zy&09bTISad^gaCvfRXJ~W)LqjkiP<3K#X=5NnZ*5^|ZXiTuWNBkz zbZKvHAZT=Sa5^t9V{&C-bZK^FV{dJ3Z*FrgZ*pfZaCKsAX=7w>ZDDC{FM4HiZ!a+} zFfYdAz4-tD1cOOLK~zYI)s$ImR8<(q|L5M@=`5w&bXuV;)Q$w|0%55oZQO=vD~V#_ z7QzFs#zdl}fVM&!C}3Hl5P7gZ5EG0h8WODwY8Pn{LAFXEm1tY0K+6mrX1#Og-aGdk zA3Aq(oz7S>0ppkabI!x}eZOxxIl%wLHts@0%U;a8-8k;w0xc%4rZ9Kzsg`y;d{=+~ zu0E4T(#Dr#-tY(ExXaz--8J~ zXT}{i?SZYbvhKL+kqry|;Y&nOgWw1cLI^+rLdeuTVUegCF~tV}&}a-J6Q5(*(wED* zZ2q7zuaF67x7t5`v~IoA8#tQ)aiB~~bObQQz!(DnFi?b}W64;Xr4$v#&uc$)x1SIq zTEmRMjoaa@tA5Sp4__pd%3wqQAq0#uP^Li;EXYC*;#w4v?1d;@hel-pK%UKs*@X>i zPyha|YuVA&9j#Dj1X^ubZAFfGv%LOaz;OZyC`>{=@^cm-7LBNZ(0E7`y{M>IVZZA6 z8bR@osj1{WgaT_#toPV^pS8ZBtOEf{&7BrlI%mluS@nT36^v5k=gdc36=I#=Y(6vT zzq-XN<2aqgwUm`U*y;-nLXC?kDXf8oTOc0$>0rmU#&{zd)2}Km&{1$-fh2oDsRFBc zHY717ecQct=k_Mrru%H&2ZY2c2uIy0D_#jr$_@1oe6X`^18q-_nzFR7lyD|4u1V0S zid?G`U46THjWFHfnuLnk&q56Hx6YjVbrx?Q2__K7;IQX9acPGyecD>lP zNjr+EPlUq^zgM~)60d(^H>hP1ShhdMR^%|5FbjZeNJb(ddf78Rw(7(E7y^(DfF(o? zr>>+Kzc!L6K=`>EH*23D_A2Ld4vy#JBV%7&IDZdr~1&j%bn)O)^W z`I6C!@*0y8@7FF2NYR9kzRPsUjm8Y;v<`I{t0YH`d>^F?o3hUjP6A07*qoM6N<$f}Zb{MF0Q* literal 0 HcmV?d00001 diff --git a/src/main/resources/edit-redo_hi.png b/src/main/resources/edit-redo_hi.png new file mode 100644 index 0000000000000000000000000000000000000000..a44afb037b9be8c3ab27283054eaeeb42053607c GIT binary patch literal 3207 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edit-undo.png b/src/main/resources/edit-undo.png new file mode 100644 index 0000000000000000000000000000000000000000..684b517a54be8564248fe93c488b103a5d61048a GIT binary patch literal 1419 zcmV;61$6p}P)b%78FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H101tFUSaeirbZlh+MPzAoAXRQ;Zx4@-Pyhe`6m&&cbU}4=Xm4@= zN?~htVjxp=Wod3@a_0N@F8}}lA#_DpbW?A2a${uxXmoUNIxjD3X>Dy`V=irVb7^B} zVQg$JV|r<3<6Zy&09bTISad^gaCvfRXJ~W)LqjkiP<3K#X=5NnZ*5^|ZXiTuWNBkz zbZKvHAZT=Sa5^t9V{&C-bZK^FV{dJ3Z*FrgZ*pfZaCKsAX=7w>ZDDC{FM4HiZ!a+} zFfYdAz4-tD1UyMZK~zYI&6R6R990y@|99@}uv?Hfv=myPY}BDT=h*ark#pG`?A zH5yalYmG)zO{_+ZelRLARbtx@Caut3u~l;EJaFaf#tEw&g{(2 zy?)qTIxM?(!8GYfPVUUT=bqm==iWI3-0RZRfE5>yplqT0h4~n_eU0_8aha`x359nZ z*l`>cX?Z(Ott@^od7&@>5F;_VbPTU8&ENY@Txxc#?j8F-y#EGr4$}T@RGqc>gFR-8 zX7zhz4hFaD5y^NC4xB7$3IZa$G-iLgCu0>nkBg47^m2l9a5+8`JY%NYg!+ z0Wg3waIOMe1!qop4K#AoC`l3KvYCPm!NIz{{59Uh!1i?%-~G+ zs~g(%Z{Tbye66?#>f}LWomFw zj`k9t{^;%gv?i)YrE`Zr>eQxeSTCP(utzI&ivi2JHD({^#WNiW%`2B1`!pLku+~00IaRT`vbQ6eUpyZap}HiLfWj^ zg|`N54J~5y6TS(qLZDK}rV>$~D&CEQgFPo}Mu(LVDrJVWQIzSom1!yi0HKM{6i3Jg za3>(~+t77Y_^zkxr`>%Wbq>u73czGgv84l@sz;YWBnZ+fxF))E zzn49bi>FM|6s1yy`-bhTZPSbR)t@~Bpa*Ql5HK194i3T?H3z`0R?xI%=@o*={~5Ob z={)&k&-4C$Mq3>KC;mx_@X+Arz{mmU@$n$XKM|{~xf!b`9WLekh0)Jn{}erbfNsc8 z2fzXmgmW6M%me_QEk#Uq1xi!WkTf}pk-8%|eYzRWV2ELy0g^DzsR1x^j$2ExsB9xL zmM=z%Tf@}Fs~9+R3}XSOMnKT}oLgip?;MapjlvDe45JY?3lL5MFyloD26Dp~4CS~N ZDy`V=irVb7^B} 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 literal 0 HcmV?d00001 diff --git a/src/main/svg/edit-redo.svg b/src/main/svg/edit-redo.svg new file mode 100644 index 000000000..bc4d52af7 --- /dev/null +++ b/src/main/svg/edit-redo.svg @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Edit Redo + + + edit + redo + again + reapply + + + + + + + + + + + + + + + + + diff --git a/src/main/svg/edit-undo.svg b/src/main/svg/edit-undo.svg new file mode 100644 index 000000000..f482565f0 --- /dev/null +++ b/src/main/svg/edit-undo.svg @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Edit Undo + + + edit + undo + revert + + + + + + + + + + + + + + + + + diff --git a/src/main/svg/exp.sh b/src/main/svg/exp.sh index ec6f0d6ff..adae63d02 100755 --- a/src/main/svg/exp.sh +++ b/src/main/svg/exp.sh @@ -16,6 +16,8 @@ ./expicon.sh View-zoom-fit ./expicon.sh View-zoom-in ./expicon.sh View-zoom-out +./expicon.sh edit-redo +./expicon.sh edit-undo ./exptest.sh testFailed ./exptest.sh testPassed