diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 000000000..7381b9bc6 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,39 @@ +name: Java CI with Maven + +on: + push: + branches: [ feature/arrange-send-to-front-or-back ] + + pull_request: + branches: [ develop ] + + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + - name: Build with Maven + run: mvn -B -DskipTests package -s .maven-settings.xml + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run tests + run: mvn -B test -s .maven-settings.xml + env: + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.maven-settings.xml b/.maven-settings.xml new file mode 100644 index 000000000..2edc8a0e9 --- /dev/null +++ b/.maven-settings.xml @@ -0,0 +1,11 @@ + + + + github + ${env.GITHUB_ACTOR} + ${env.GITHUB_TOKEN} + + + diff --git a/jhotdraw-core/pom.xml b/jhotdraw-core/pom.xml index 7c276da85..7eb55661b 100644 --- a/jhotdraw-core/pom.xml +++ b/jhotdraw-core/pom.xml @@ -35,10 +35,28 @@ 6.8.21 test + + junit + junit + 4.13.2 + test + + + com.tngtech.jgiven + jgiven-junit + 1.3.1 + test + + + org.assertj + assertj-core + 3.26.3 + test + ${project.groupId} jhotdraw-actions ${project.version} - \ No newline at end of file + diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/AbstractZOrderAction.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/AbstractZOrderAction.java new file mode 100644 index 000000000..b60e9843e --- /dev/null +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/AbstractZOrderAction.java @@ -0,0 +1,87 @@ +/* + * @(#)AbstractZOrderAction.java + * + * Copyright (c) 2003-2008 The authors and contributors of JHotDraw. + * You may not use, copy or modify this file, except in compliance with the + * accompanying license terms. + */ +package org.jhotdraw.draw.action; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import org.jhotdraw.draw.Drawing; +import org.jhotdraw.draw.DrawingEditor; +import org.jhotdraw.draw.DrawingView; +import org.jhotdraw.draw.figure.Figure; +import org.jhotdraw.util.ResourceBundleUtil; + +/** + * Defines the common workflow for z-order actions on selected figures. + */ +public abstract class AbstractZOrderAction extends AbstractSelectedAction { + + private static final long serialVersionUID = 1L; + private final String id; + + protected AbstractZOrderAction(DrawingEditor editor, String id) { + super(editor); + this.id = id; + ResourceBundleUtil labels + = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); + labels.configureAction(this, id); + updateEnabledState(); + } + + @Override + public void actionPerformed(ActionEvent e) { + final DrawingView view = Objects.requireNonNull( + getView(), + "A z-order action requires an active drawing view."); + final Drawing drawing = Objects.requireNonNull( + view.getDrawing(), + "A z-order action requires an attached drawing."); + final LinkedList
figures = new LinkedList<>(view.getSelectedFigures()); + assert figures != null : "Selected figures collection must not be null."; + final List
originalOrder = new ArrayList<>(drawing.getChildren()); + reorder(view, figures); + final List
reorderedOrder = new ArrayList<>(drawing.getChildren()); + fireUndoableEditHappened(new AbstractUndoableEdit() { + private static final long serialVersionUID = 1L; + + @Override + public String getPresentationName() { + ResourceBundleUtil labels + = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); + return labels.getTextProperty(id); + } + + @Override + public void redo() throws CannotRedoException { + super.redo(); + restoreOrder(drawing, reorderedOrder); + } + + @Override + public void undo() throws CannotUndoException { + super.undo(); + restoreOrder(drawing, originalOrder); + } + }); + } + + private void restoreOrder(Drawing drawing, List
order) { + drawing.basicRemoveAll(new ArrayList<>(drawing.getChildren())); + drawing.basicAddAll(0, order); + } + + protected abstract void reorder(DrawingView view, Collection
figures); + + protected abstract void reverseReorder(DrawingView view, Collection
figures); +} diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/BringToFrontAction.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/BringToFrontAction.java index c54d29888..8762d47f2 100644 --- a/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/BringToFrontAction.java +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/BringToFrontAction.java @@ -9,9 +9,7 @@ import org.jhotdraw.draw.figure.Figure; import java.util.*; -import javax.swing.undo.*; import org.jhotdraw.draw.*; -import org.jhotdraw.util.ResourceBundleUtil; /** * ToFrontAction. @@ -19,7 +17,7 @@ * @author Werner Randelshofer * @version $Id$ */ -public class BringToFrontAction extends AbstractSelectedAction { +public class BringToFrontAction extends AbstractZOrderAction { private static final long serialVersionUID = 1L; public static final String ID = "edit.bringToFront"; @@ -28,44 +26,25 @@ public class BringToFrontAction extends AbstractSelectedAction { * Creates a new instance. */ public BringToFrontAction(DrawingEditor editor) { - super(editor); - ResourceBundleUtil labels - = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); - labels.configureAction(this, ID); - updateEnabledState(); + super(editor, ID); } @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - final DrawingView view = getView(); - final LinkedList
figures = new LinkedList<>(view.getSelectedFigures()); + protected void reorder(DrawingView view, Collection
figures) { bringToFront(view, figures); - fireUndoableEditHappened(new AbstractUndoableEdit() { - private static final long serialVersionUID = 1L; - - @Override - public String getPresentationName() { - ResourceBundleUtil labels - = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); - return labels.getTextProperty(ID); - } - - @Override - public void redo() throws CannotRedoException { - super.redo(); - BringToFrontAction.bringToFront(view, figures); - } + } - @Override - public void undo() throws CannotUndoException { - super.undo(); - SendToBackAction.sendToBack(view, figures); - } - }); + @Override + protected void reverseReorder(DrawingView view, Collection
figures) { + SendToBackAction.sendToBack(view, figures); } public static void bringToFront(DrawingView view, Collection
figures) { - Drawing drawing = view.getDrawing(); + Objects.requireNonNull(view, "DrawingView must not be null."); + Objects.requireNonNull(figures, "Figures collection must not be null."); + Drawing drawing = Objects.requireNonNull( + view.getDrawing(), + "DrawingView must provide a drawing."); for (Figure figure : drawing.sort(figures)) { drawing.bringToFront(figure); } diff --git a/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/SendToBackAction.java b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/SendToBackAction.java index be14fa523..364b9b35a 100644 --- a/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/SendToBackAction.java +++ b/jhotdraw-core/src/main/java/org/jhotdraw/draw/action/SendToBackAction.java @@ -9,9 +9,8 @@ import org.jhotdraw.draw.figure.Figure; import java.util.*; -import javax.swing.undo.*; import org.jhotdraw.draw.*; -import org.jhotdraw.util.ResourceBundleUtil; +import org.jhotdraw.util.ReversedList; /** * SendToBackAction. @@ -19,7 +18,7 @@ * @author Werner Randelshofer * @version $Id$ */ -public class SendToBackAction extends AbstractSelectedAction { +public class SendToBackAction extends AbstractZOrderAction { private static final long serialVersionUID = 1L; public static final String ID = "edit.sendToBack"; @@ -28,45 +27,27 @@ public class SendToBackAction extends AbstractSelectedAction { * Creates a new instance. */ public SendToBackAction(DrawingEditor editor) { - super(editor); - ResourceBundleUtil labels - = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); - labels.configureAction(this, ID); - updateEnabledState(); + super(editor, ID); } @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - final DrawingView view = getView(); - final LinkedList
figures = new LinkedList<>(view.getSelectedFigures()); + protected void reorder(DrawingView view, Collection
figures) { sendToBack(view, figures); - fireUndoableEditHappened(new AbstractUndoableEdit() { - private static final long serialVersionUID = 1L; - - @Override - public String getPresentationName() { - ResourceBundleUtil labels - = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); - return labels.getTextProperty(ID); - } - - @Override - public void redo() throws CannotRedoException { - super.redo(); - SendToBackAction.sendToBack(view, figures); - } + } - @Override - public void undo() throws CannotUndoException { - super.undo(); - BringToFrontAction.bringToFront(view, figures); - } - }); + @Override + protected void reverseReorder(DrawingView view, Collection
figures) { + BringToFrontAction.bringToFront(view, figures); } public static void sendToBack(DrawingView view, Collection
figures) { - Drawing drawing = view.getDrawing(); - for (Figure figure : figures) { // XXX Shouldn't the figures be sorted here back to front? + Objects.requireNonNull(view, "DrawingView must not be null."); + Objects.requireNonNull(figures, "Figures collection must not be null."); + Drawing drawing = Objects.requireNonNull( + view.getDrawing(), + "DrawingView must provide a drawing."); + List
sorted = drawing.sort(figures); + for (Figure figure : new ReversedList<>(sorted)) { drawing.sendToBack(figure); } } diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/ArrangeTestSupport.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/ArrangeTestSupport.java new file mode 100644 index 000000000..7d1fc1a54 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/ArrangeTestSupport.java @@ -0,0 +1,68 @@ +package org.jhotdraw.draw; + +import java.awt.geom.Point2D; +import java.util.Arrays; +import java.util.List; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.undo.UndoableEdit; +import org.jhotdraw.draw.figure.Figure; +import org.jhotdraw.draw.figure.RectangleFigure; +import static org.junit.Assert.assertEquals; + +public final class ArrangeTestSupport { + + private ArrangeTestSupport() { + } + + public static DefaultDrawingView createDefaultView() { + return createView(new DefaultDrawing()); + } + + public static DefaultDrawingView createQuadTreeView() { + return createView(new QuadTreeDrawing()); + } + + public static DefaultDrawingView createView(Drawing drawing) { + DefaultDrawingView view = new DefaultDrawingView(); + view.setDrawing(drawing); + return view; + } + + public static Figure addFigure(DefaultDrawingView view, double x) { + RectangleFigure figure = new RectangleFigure(); + figure.setBounds(new Point2D.Double(x, 0), new Point2D.Double(x + 5, 5)); + view.getDrawing().add(figure); + return figure; + } + + public static void select(DefaultDrawingView view, Figure... figures) { + view.clearSelection(); + view.addToSelection(Arrays.asList(figures)); + } + + public static void assertOrder(DefaultDrawingView view, Figure... expected) { + List
actual = view.getDrawing().sort(Arrays.asList(expected)); + assertEquals(Arrays.asList(expected), actual); + } + + public static UndoableEdit captureUndoableEdit(Drawing drawing, Runnable trigger) { + final UndoableEdit[] captured = new UndoableEdit[1]; + UndoableEditListener listener = new UndoableEditListener() { + @Override + public void undoableEditHappened(UndoableEditEvent e) { + captured[0] = e.getEdit(); + } + }; + drawing.addUndoableEditListener(listener); + try { + trigger.run(); + } finally { + drawing.removeUndoableEditListener(listener); + } + if (captured[0] == null) { + throw new AssertionError("Expected an undoable edit to be published."); + } + return captured[0]; + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/QuadTreeDrawingArrangeTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/QuadTreeDrawingArrangeTest.java new file mode 100644 index 000000000..fcf4a2323 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/QuadTreeDrawingArrangeTest.java @@ -0,0 +1,88 @@ +package org.jhotdraw.draw; + +import java.util.Arrays; +import java.util.List; +import org.jhotdraw.draw.action.BringToFrontAction; +import org.jhotdraw.draw.action.SendToBackAction; +import org.jhotdraw.draw.figure.Figure; +import static org.jhotdraw.draw.ArrangeTestSupport.addFigure; +import static org.jhotdraw.draw.ArrangeTestSupport.assertOrder; +import static org.jhotdraw.draw.ArrangeTestSupport.createQuadTreeView; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class QuadTreeDrawingArrangeTest { + + @Test + public void sendToBackPreservesRelativeOrderForMultipleSelectedFiguresInQuadTreeDrawing() { + DefaultDrawingView view = createQuadTreeView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + SendToBackAction.sendToBack(view, Arrays.asList(b, d)); + + assertOrder(view, b, d, a, c, e); + } + + @Test + public void bringToFrontPreservesRelativeOrderForMultipleSelectedFiguresInQuadTreeDrawing() { + DefaultDrawingView view = createQuadTreeView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + BringToFrontAction.bringToFront(view, Arrays.asList(b, d)); + + assertOrder(view, a, c, e, b, d); + } + + @Test + public void sendToBackWithAllFiguresSelectedPreservesOrderInQuadTreeDrawing() { + DefaultDrawingView view = createQuadTreeView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + SendToBackAction.sendToBack(view, Arrays.asList(a, b, c, d)); + + assertOrder(view, a, b, c, d); + } + + @Test + public void sortReturnsBackToFrontOrderAfterSendToBack() { + DefaultDrawingView view = createQuadTreeView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + SendToBackAction.sendToBack(view, Arrays.asList(b, d)); + + List
actual = view.getDrawing().sort(Arrays.asList(e, b, c, d)); + + assertEquals(Arrays.asList(b, d, c, e), actual); + } + + @Test + public void getFiguresFrontToBackMatchesInverseOfSortedOrderAfterBringToFront() { + DefaultDrawingView view = createQuadTreeView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + BringToFrontAction.bringToFront(view, Arrays.asList(b, d)); + + List
actual = view.getDrawing().getFiguresFrontToBack(); + + assertEquals(Arrays.asList(d, b, e, c, a), actual); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/AbstractZOrderActionIntegrationTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/AbstractZOrderActionIntegrationTest.java new file mode 100644 index 000000000..6541404eb --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/AbstractZOrderActionIntegrationTest.java @@ -0,0 +1,183 @@ +package org.jhotdraw.draw.action; + +import java.awt.event.ActionEvent; +import javax.swing.undo.UndoableEdit; +import org.jhotdraw.draw.DefaultDrawingEditor; +import org.jhotdraw.draw.DefaultDrawingView; +import org.jhotdraw.draw.figure.Figure; +import static org.jhotdraw.draw.ArrangeTestSupport.addFigure; +import static org.jhotdraw.draw.ArrangeTestSupport.assertOrder; +import static org.jhotdraw.draw.ArrangeTestSupport.captureUndoableEdit; +import static org.jhotdraw.draw.ArrangeTestSupport.createDefaultView; +import static org.jhotdraw.draw.ArrangeTestSupport.select; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class AbstractZOrderActionIntegrationTest { + + @Test + public void sendToBackActionPerformedUsesActiveViewSelection() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView activeView = createDefaultView(); + DefaultDrawingView inactiveView = createDefaultView(); + editor.add(activeView); + editor.add(inactiveView); + + Figure a = addFigure(activeView, 0); + Figure b = addFigure(activeView, 10); + Figure c = addFigure(activeView, 20); + Figure d = addFigure(activeView, 30); + + Figure x = addFigure(inactiveView, 0); + Figure y = addFigure(inactiveView, 10); + Figure z = addFigure(inactiveView, 20); + + select(activeView, c); + select(inactiveView, y); + editor.setActiveView(activeView); + + SendToBackAction action = new SendToBackAction(editor); + action.actionPerformed(actionEvent()); + + assertOrder(activeView, c, a, b, d); + assertOrder(inactiveView, x, y, z); + } + + @Test + public void bringToFrontActionPerformedUsesActiveViewSelection() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView activeView = createDefaultView(); + DefaultDrawingView inactiveView = createDefaultView(); + editor.add(activeView); + editor.add(inactiveView); + + Figure a = addFigure(activeView, 0); + Figure b = addFigure(activeView, 10); + Figure c = addFigure(activeView, 20); + Figure d = addFigure(activeView, 30); + + Figure x = addFigure(inactiveView, 0); + Figure y = addFigure(inactiveView, 10); + Figure z = addFigure(inactiveView, 20); + + select(activeView, b); + select(inactiveView, y); + editor.setActiveView(activeView); + + BringToFrontAction action = new BringToFrontAction(editor); + action.actionPerformed(actionEvent()); + + assertOrder(activeView, a, c, d, b); + assertOrder(inactiveView, x, y, z); + } + + @Test + public void sendToBackActionPublishesUndoableEditThatRestoresOriginalOrderOnUndo() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView view = createDefaultView(); + editor.add(view); + editor.setActiveView(view); + + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + select(view, b, d); + SendToBackAction action = new SendToBackAction(editor); + + UndoableEdit edit = captureUndoableEdit( + view.getDrawing(), + () -> action.actionPerformed(actionEvent())); + + assertOrder(view, b, d, a, c); + + edit.undo(); + + assertOrder(view, a, b, c, d); + } + + @Test + public void bringToFrontActionPublishesUndoableEditThatRestoresOriginalOrderOnUndo() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView view = createDefaultView(); + editor.add(view); + editor.setActiveView(view); + + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + select(view, a, c); + BringToFrontAction action = new BringToFrontAction(editor); + + UndoableEdit edit = captureUndoableEdit( + view.getDrawing(), + () -> action.actionPerformed(actionEvent())); + + assertOrder(view, b, d, a, c); + + edit.undo(); + + assertOrder(view, a, b, c, d); + } + + @Test + public void sendToBackUndoableEditReappliesOrderOnRedo() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView view = createDefaultView(); + editor.add(view); + editor.setActiveView(view); + + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + select(view, b, d); + SendToBackAction action = new SendToBackAction(editor); + + UndoableEdit edit = captureUndoableEdit( + view.getDrawing(), + () -> action.actionPerformed(actionEvent())); + + edit.undo(); + edit.redo(); + + assertOrder(view, b, d, a, c); + } + + @Test + public void actionIsDisabledWhenSelectionIsEmpty() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView view = createDefaultView(); + editor.add(view); + editor.setActiveView(view); + + addFigure(view, 0); + BringToFrontAction action = new BringToFrontAction(editor); + + assertFalse(action.isEnabled()); + } + + @Test + public void actionBecomesEnabledWhenFiguresAreSelected() { + DefaultDrawingEditor editor = new DefaultDrawingEditor(); + DefaultDrawingView view = createDefaultView(); + editor.add(view); + editor.setActiveView(view); + + Figure a = addFigure(view, 0); + BringToFrontAction action = new BringToFrontAction(editor); + + select(view, a); + + assertTrue(action.isEnabled()); + } + + private ActionEvent actionEvent() { + return new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "test"); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/ArrangeZOrderActionTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/ArrangeZOrderActionTest.java new file mode 100644 index 000000000..4ded0df91 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/ArrangeZOrderActionTest.java @@ -0,0 +1,79 @@ +package org.jhotdraw.draw.action; + +import java.util.Arrays; +import java.util.Collections; +import org.jhotdraw.draw.DefaultDrawingView; +import org.jhotdraw.draw.figure.Figure; +import static org.jhotdraw.draw.ArrangeTestSupport.addFigure; +import static org.jhotdraw.draw.ArrangeTestSupport.assertOrder; +import static org.jhotdraw.draw.ArrangeTestSupport.createDefaultView; +import org.junit.Test; + +public class ArrangeZOrderActionTest { + + @Test + public void sendToBackPreservesRelativeOrderForMultipleSelectedFigures() { + DefaultDrawingView view = createDefaultView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + SendToBackAction.sendToBack(view, Arrays.asList(b, d)); + + assertOrder(view, b, d, a, c, e); + } + + @Test + public void bringToFrontPreservesRelativeOrderForMultipleSelectedFigures() { + DefaultDrawingView view = createDefaultView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + Figure e = addFigure(view, 40); + + BringToFrontAction.bringToFront(view, Arrays.asList(b, d)); + + assertOrder(view, a, c, e, b, d); + } + + @Test + public void sendToBackMovesSingleSelectedFigureToBack() { + DefaultDrawingView view = createDefaultView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + SendToBackAction.sendToBack(view, Collections.singletonList(c)); + + assertOrder(view, c, a, b, d); + } + + @Test + public void sendToBackWithEmptySelectionDoesNotChangeOrder() { + DefaultDrawingView view = createDefaultView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + + SendToBackAction.sendToBack(view, Collections.emptyList()); + + assertOrder(view, a, b, c); + } + + @Test + public void sendToBackWithAllFiguresSelectedPreservesOrder() { + DefaultDrawingView view = createDefaultView(); + Figure a = addFigure(view, 0); + Figure b = addFigure(view, 10); + Figure c = addFigure(view, 20); + Figure d = addFigure(view, 30); + + SendToBackAction.sendToBack(view, Arrays.asList(a, b, c, d)); + + assertOrder(view, a, b, c, d); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeBehaviorTest.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeBehaviorTest.java new file mode 100644 index 000000000..4b5843bff --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeBehaviorTest.java @@ -0,0 +1,51 @@ +package org.jhotdraw.draw.action.bdd; + +import com.tngtech.jgiven.junit.ScenarioTest; +import org.junit.Test; + +public class ArrangeBehaviorTest extends ScenarioTest< + ArrangeGivenStage, + ArrangeWhenStage, + ArrangeThenStage> { + + @Test + public void send_selected_figures_to_back_preserving_relative_order() { + given().a_default_drawing_with_five_figures() + .and().the_figures_b_and_d_are_selected(); + + when().the_user_sends_the_selection_to_the_back(); + + then().the_order_is_b_d_a_c_e(); + } + + @Test + public void bring_selected_figures_to_front_preserving_relative_order() { + given().a_default_drawing_with_five_figures() + .and().the_figures_b_and_d_are_selected(); + + when().the_user_brings_the_selection_to_the_front(); + + then().the_order_is_a_c_e_b_d(); + } + + @Test + public void undo_restores_the_original_order_after_send_to_back() { + given().a_default_drawing_with_five_figures() + .and().the_figures_b_and_d_are_selected(); + + when().the_user_sends_the_selection_to_the_back() + .and().the_user_undoes_the_arrange_action(); + + then().the_order_is_a_b_c_d_e(); + } + + @Test + public void send_to_back_preserves_relative_order_in_a_quad_tree_drawing() { + given().a_quad_tree_drawing_with_five_figures() + .and().the_figures_b_and_d_are_selected(); + + when().the_user_sends_the_selection_to_the_back(); + + then().the_order_is_b_d_a_c_e(); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeGivenStage.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeGivenStage.java new file mode 100644 index 000000000..90c33bd8d --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeGivenStage.java @@ -0,0 +1,51 @@ +package org.jhotdraw.draw.action.bdd; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ProvidedScenarioState; +import org.jhotdraw.draw.DefaultDrawingEditor; +import static org.jhotdraw.draw.ArrangeTestSupport.addFigure; +import static org.jhotdraw.draw.ArrangeTestSupport.createDefaultView; +import static org.jhotdraw.draw.ArrangeTestSupport.createQuadTreeView; +import static org.jhotdraw.draw.ArrangeTestSupport.select; + +public class ArrangeGivenStage extends Stage { + + @ProvidedScenarioState + private final ArrangeScenarioState state = new ArrangeScenarioState(); + + public ArrangeGivenStage a_default_drawing_with_five_figures() { + state.editor = new DefaultDrawingEditor(); + state.view = createDefaultView(); + state.editor.add(state.view); + state.editor.setActiveView(state.view); + addFiveFigures(); + return self(); + } + + public ArrangeGivenStage a_quad_tree_drawing_with_five_figures() { + state.editor = new DefaultDrawingEditor(); + state.view = createQuadTreeView(); + state.editor.add(state.view); + state.editor.setActiveView(state.view); + addFiveFigures(); + return self(); + } + + public ArrangeGivenStage the_figures_b_and_d_are_selected() { + select(state.view, state.b, state.d); + return self(); + } + + public ArrangeGivenStage the_figures_a_and_c_are_selected() { + select(state.view, state.a, state.c); + return self(); + } + + private void addFiveFigures() { + state.a = addFigure(state.view, 0); + state.b = addFigure(state.view, 10); + state.c = addFigure(state.view, 20); + state.d = addFigure(state.view, 30); + state.e = addFigure(state.view, 40); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeScenarioState.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeScenarioState.java new file mode 100644 index 000000000..5209ce1d7 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeScenarioState.java @@ -0,0 +1,18 @@ +package org.jhotdraw.draw.action.bdd; + +import javax.swing.undo.UndoableEdit; +import org.jhotdraw.draw.DefaultDrawingEditor; +import org.jhotdraw.draw.DefaultDrawingView; +import org.jhotdraw.draw.figure.Figure; + +final class ArrangeScenarioState { + + DefaultDrawingEditor editor; + DefaultDrawingView view; + Figure a; + Figure b; + Figure c; + Figure d; + Figure e; + UndoableEdit capturedEdit; +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeThenStage.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeThenStage.java new file mode 100644 index 000000000..889f6fc42 --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeThenStage.java @@ -0,0 +1,34 @@ +package org.jhotdraw.draw.action.bdd; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import java.util.Arrays; +import java.util.List; +import org.jhotdraw.draw.figure.Figure; +import static org.assertj.core.api.Assertions.assertThat; + +public class ArrangeThenStage extends Stage { + + @ExpectedScenarioState + private ArrangeScenarioState state; + + public ArrangeThenStage the_order_is_b_d_a_c_e() { + assertThat(currentOrder()).containsExactly(state.b, state.d, state.a, state.c, state.e); + return self(); + } + + public ArrangeThenStage the_order_is_a_c_e_b_d() { + assertThat(currentOrder()).containsExactly(state.a, state.c, state.e, state.b, state.d); + return self(); + } + + public ArrangeThenStage the_order_is_a_b_c_d_e() { + assertThat(currentOrder()).containsExactly(state.a, state.b, state.c, state.d, state.e); + return self(); + } + + private List
currentOrder() { + return state.view.getDrawing().sort( + Arrays.asList(state.a, state.b, state.c, state.d, state.e)); + } +} diff --git a/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeWhenStage.java b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeWhenStage.java new file mode 100644 index 000000000..43e74834a --- /dev/null +++ b/jhotdraw-core/src/test/java/org/jhotdraw/draw/action/bdd/ArrangeWhenStage.java @@ -0,0 +1,39 @@ +package org.jhotdraw.draw.action.bdd; + +import com.tngtech.jgiven.Stage; +import com.tngtech.jgiven.annotation.ExpectedScenarioState; +import java.awt.event.ActionEvent; +import org.jhotdraw.draw.action.BringToFrontAction; +import org.jhotdraw.draw.action.SendToBackAction; +import static org.jhotdraw.draw.ArrangeTestSupport.captureUndoableEdit; + +public class ArrangeWhenStage extends Stage { + + @ExpectedScenarioState + private ArrangeScenarioState state; + + public ArrangeWhenStage the_user_sends_the_selection_to_the_back() { + SendToBackAction action = new SendToBackAction(state.editor); + state.capturedEdit = captureUndoableEdit( + state.view.getDrawing(), + () -> action.actionPerformed(actionEvent())); + return self(); + } + + public ArrangeWhenStage the_user_brings_the_selection_to_the_front() { + BringToFrontAction action = new BringToFrontAction(state.editor); + state.capturedEdit = captureUndoableEdit( + state.view.getDrawing(), + () -> action.actionPerformed(actionEvent())); + return self(); + } + + public ArrangeWhenStage the_user_undoes_the_arrange_action() { + state.capturedEdit.undo(); + return self(); + } + + private ActionEvent actionEvent() { + return new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "bdd"); + } +}