Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"className": "org.jhotdraw.action.edit.AbstractUndoRedoActionBDDTest",
"name": "Abstract Undo Redo Action BDD",
"scenarios": [
{
"className": "org.jhotdraw.action.edit.AbstractUndoRedoActionBDDTest",
"testMethodName": "undo_action_should_delegate_to_real_action",
"description": "undo action should delegate to real action",
"tagIds": [],
"explicitParameters": [],
"derivedParameters": [],
"scenarioCases": [
{
"caseNr": 1,
"steps": [
{
"name": "an application is running",
"words": [
{
"value": "Given",
"isIntroWord": true
},
{
"value": "an application is running"
}
],
"status": "PASSED",
"durationInNanos": 914291900,
"depth": 0,
"parentFailed": false
},
{
"name": "a real action is set",
"words": [
{
"value": "and",
"isIntroWord": true
},
{
"value": "a real action is set"
}
],
"status": "PASSED",
"durationInNanos": 4300600,
"depth": 0,
"parentFailed": false
},
{
"name": "the undo action is performed",
"words": [
{
"value": "When",
"isIntroWord": true
},
{
"value": "the undo action is performed"
}
],
"status": "PASSED",
"durationInNanos": 323900,
"depth": 0,
"parentFailed": false
},
{
"name": "the real action should be invoked",
"words": [
{
"value": "Then",
"isIntroWord": true
},
{
"value": "the real action should be invoked"
}
],
"status": "PASSED",
"durationInNanos": 9822700,
"depth": 0,
"parentFailed": false
}
],
"explicitArguments": [],
"derivedArguments": [],
"status": "SUCCESS",
"durationInNanos": 952868100
}
],
"casesAsTable": false,
"durationInNanos": 952868100
},
{
"className": "org.jhotdraw.action.edit.AbstractUndoRedoActionBDDTest",
"testMethodName": "undo_action_should_do_nothing_if_no_real_action",
"description": "undo action should do nothing if no real action",
"tagIds": [],
"explicitParameters": [],
"derivedParameters": [],
"scenarioCases": [
{
"caseNr": 1,
"steps": [
{
"name": "an application is running",
"words": [
{
"value": "Given",
"isIntroWord": true
},
{
"value": "an application is running"
}
],
"status": "PASSED",
"durationInNanos": 1127300,
"depth": 0,
"parentFailed": false
},
{
"name": "the undo action is performed",
"words": [
{
"value": "When",
"isIntroWord": true
},
{
"value": "the undo action is performed"
}
],
"status": "PASSED",
"durationInNanos": 391000,
"depth": 0,
"parentFailed": false
},
{
"name": "the real action should not be invoked",
"words": [
{
"value": "Then",
"isIntroWord": true
},
{
"value": "the real action should not be invoked"
}
],
"status": "PASSED",
"durationInNanos": 197300,
"depth": 0,
"parentFailed": false
}
],
"explicitArguments": [],
"derivedArguments": [],
"status": "SUCCESS",
"durationInNanos": 2158700
}
],
"casesAsTable": false,
"durationInNanos": 2158700
}
],
"tagMap": {}
}
30 changes: 30 additions & 0 deletions jhotdraw-actions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,35 @@
<artifactId>jhotdraw-datatransfer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tngtech.jgiven</groupId>
<artifactId>jgiven-junit</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package org.jhotdraw.action;

import java.beans.*;
import java.util.Objects;
import javax.swing.*;
import org.jhotdraw.api.app.Application;
import org.jhotdraw.api.app.View;
Expand Down Expand Up @@ -44,24 +45,18 @@ public abstract class AbstractViewAction extends AbstractAction {
* the enabled state of the view and the application.
*/
private boolean combinedEnabled = true;
private PropertyChangeListener applicationListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ((evt.getPropertyName() == null && Application.ACTIVE_VIEW_PROPERTY == null) || (evt.getPropertyName() != null && evt.getPropertyName().equals(Application.ACTIVE_VIEW_PROPERTY))) { // Strings get interned
updateView((View) evt.getOldValue(), (View) evt.getNewValue());
}
private final PropertyChangeListener applicationListener = evt -> {
if (Objects.equals(evt.getPropertyName(), Application.ACTIVE_VIEW_PROPERTY)) {
updateView((View) evt.getOldValue(), (View) evt.getNewValue());
}
};
private PropertyChangeListener viewListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
private final PropertyChangeListener viewListener = evt -> {
String name = evt.getPropertyName();
if ("enabled".equals(name)) {
if (Objects.equals(name, "enabled")) {
updateEnabled();
} else if ((name == null && propertyName == null) || (name != null && name.equals(propertyName))) {
} else if (Objects.equals(name, propertyName)) {
updateView();
}
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package org.jhotdraw.action.edit;

import org.jhotdraw.action.AbstractViewAction;
import org.jhotdraw.api.app.Application;
import org.jhotdraw.api.app.View;
import org.jhotdraw.util.ResourceBundleUtil;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.util.Objects;

/**
* An abstract base class for undo and redo actions that centralizes common logic.
* <p>
* This class uses an {@code actionID} to differentiate between various actions,
* configures resource bundles, manages property change listeners, and delegates
* action invocation to the corresponding real action from the view.
* </p>
*/
public abstract class AbstractUndoRedoAction extends AbstractViewAction {
protected final String actionID;
protected final ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.action.Labels");
private final PropertyChangeListener actionPropertyListener = evt -> {
String name = evt.getPropertyName();
if (Objects.equals(name, AbstractAction.NAME)) {
putValue(AbstractAction.NAME, evt.getNewValue());
} else if ("enabled".equals(name)) {
updateEnabledState();
}
};

protected AbstractUndoRedoAction(Application app, View view, String id) {
super(app, view);
this.actionID = id;
labels.configureAction(this, id);
}

private Action getRealAction() {
return (getActiveView() == null) ? null : getActiveView().getActionMap().get(actionID);
}

protected void updateEnabledState() {
boolean isEnabled = false;
Action realAction = getRealAction();
if (realAction != null && realAction != this) {
isEnabled = realAction.isEnabled();
}
setEnabled(isEnabled);
}

@Override
public void actionPerformed(ActionEvent e) {
Action realAction = getRealAction();
if (realAction != null && realAction != this) {
realAction.actionPerformed(e);
}
}

/**
* Installs listeners on the view object.
*/
@Override
protected void installViewListeners(View p) {
super.installViewListeners(p);
Action actionInView = p.getActionMap().get(actionID);
if (actionInView != null && actionInView != this) {
actionInView.addPropertyChangeListener(actionPropertyListener);
}
}


/**
* Installs listeners on the view object.
*/
@Override
protected void uninstallViewListeners(View p) {
super.uninstallViewListeners(p);
Action actionInView = p.getActionMap().get(actionID);
if (actionInView != null && actionInView != this) {
actionInView.removePropertyChangeListener(actionPropertyListener);
}
}


@Override
protected void updateView(View oldValue, View newValue) {
super.updateView(oldValue, newValue);
if (newValue == null || newValue.getActionMap().get(actionID) == null) {
return;
}
Action actionInView = newValue.getActionMap().get(actionID);
if (actionInView != this) {
putValue(AbstractAction.NAME, actionInView.getValue(AbstractAction.NAME));
updateEnabledState();
}
}


}

Loading