How to implement undo/redo in Java for MVC model? - java

I am having trouble understanding the undo/redo functions using UndoManager, and integrating it with the MVC model.
I am not sure where to put the various methods(in model, view or control)
and I am still not sure how to use the undo manager.
My control class implements UndoableEditListener
It creates:
private UndoManager manager = new UndoManager();
and in:
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Undo")) {
try {
manager.undo();
} catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
}
I understand up to here, but the rest I am not sure what to do. I know I will have to add more in the model and view class, but not sure where.
DO I have to have the following classes?
public class UndoAction extends AbstractAction {}
public void undoableEditHappened(UndoableEditEvent e) {
I am simply placing an integer number in a textfield, and then I want to be able to undo this.I set the number in the textfield in the view class.I want to acheive this the simplest way possible, no fancy coding! This is a minor part of my assg but I just cant get it working!!
==========================================================
Here is a more detailed description of my code, maybe it will help:
I have a model, view and control package.
Contol has:
ButtonGUIControl.java, which implements both
ActionListener and
UndoableEditListener.
final UndoManager manager = new UndoManager();
In the actionPerformed method, it calls
if (e.getActionCommand().equals("Undo")){
try {
manager.undo();
}
and in:
public void undoableEditHappened(UndoableEditEvent evt) {
manager.addEdit(evt.getEdit());
}
In the View:
Grid.java , which extends JTextField will add the following, wherever it needs to display a number on the GUI:(model is simply an instance of my Model class)
getDocument().addUndoableEditListener(new ButtonGUIControl(model));
Could it be because the UndoManager is being created in a different package? I really have no idea how to debug this anymore!!
I could post my entire code if that helps. I guess Im not sure how to integrate this with my mvc model structure.

Take a step back for a second. The whole idea here is that a user will use your app and will make a series of changes to something. A text editor is a good example. You can insert characters and lines, delete them again, replace text with other text, scroll the text, etc. In order to support this with MVC you have a model that holds state and a View that displays it.
Your first instinct might be to have the view directly access the model, and then refresh the view every time the user makes a change, but it's very hard to undo those changes with that implementation. Instead, you encode every kind of change the user can make in classes that are able to perform that change and can later undo that change.
For example, an action that inserts text would be implemented by a class that knows the character offset of the insertion point and the string of characters that is to be inserted. The perform action would insert the string at the offset and the undo action would remove the right number of characters after that insertion point. You'd have a different class that would handle deletion, another to handle scrolling etc.
Every time the user takes some action, the view would construct one of these UndoableEdit classes and would tell the instance to run itself (redo()). Once executed, you put that UndoableEdit at the end of a list of UndoableEdit instances that represent all of the actions the user has taken so far. This list makes it very easy to support any sequence of undo requests, redo requests and actual edit actions (resulting in more UndoableEdit's being put on the list).
So back to your question. If your app needs to support undo and redo, then you'll need to implement an UndoManager which simply manages the list of UndoableEdit's and performs undo and redo as necessary. You also have to implement a whole bunch of UndoableEdits, one for each kind of thing your user will do against the UI. As for a listener, I can't see that you really need to do that one.

If you need only simple undo/redo, you can use UndoManager as it is, you don't need to subclass or customize it in any way.
JTextField (more specifically its model, the Document) has some built-in support for undo, which means you don't need to write UndoableEdit implementations either, the UndoableEdit objects will be automagically created for you (actually AbstractDocument.DefaultDocumentEvent implements UndoableEdit).
Full simple working example is here

Related

How to trigger cell events after pasting data in a JavaFX grid?

I defined a paste method in a controller class, based on the solution given here: How to copy/paste table cells in a TableView. Everything went well, except for one detail: some cells where data is pasted have events that should be triggered but are not.
For example:
public class MyController {
private TableColumn<MyBean, String> valueColumn;
...
valueColumn.setOnEditCommit(e -> doSomeStuff(e));
private void doSomeStuff(CellEditEvent<MyBean, String> event) {
...
}
In this example, after user hits ENTER, the doSomeStuff method is called, which is expected behavior.
The problem with the paste method I implemented is that it does not affect the cell, only its content (its ObservableValue). This means of course that after data is pasted, no event is triggered.
My question : is there a way to trigger the same event, or a similar one that will call my doSomeStuff method after pasting data?
Table View doesn't work as you think. If you want to have a fully customizable structure, use a grid pane. It is hard to create one, but after you make it look like a table, you have many more options to customize.
I did that I a recent project where I was needed to insert a table inside a cell. It was much more easier with a gridpane and textfields.

Add (hover) listener for grid cell after grid first attach

I have something like this.
Grid<Stock> mygrid = new Grid<Stock>(store, cm)
{
#Override
protected void onAfterFirstAttach()
{
super.onAfterFirstAttach();
//here do the binding for the cell elements that have myclass
}
};
And for the view of mygrid I have this
view.setViewConfig(new GridViewConfig<Stock>()
{
#Override
public String getRowStyle(Stock model, int rowIndex)
{
return "";
}
#Override
public String getColStyle(Stock model, ValueProvider<? super Stock, ?> valueProvider, int rowIndex, int colIndex)
{
String style = "";
if(SOME_CASE)
{
style = "myclass";
}
return style;
}
});
What I want to do is onAfterFirstAttach I want to add some listener/handler that during hover will do something, let's say for the sake of simplicity, give an alert.
Is what I'm trying to do possible? Can I accomplish what I want without using JSNI or a library like gwtquery? Is it sure that onAfterFirstAttach all the cells that should have the class myclass will be available and accessible? If yes how can I attach the listener/handler that I want?
The questions seem a lot, but I believe they are all interconnected.
I'm using GWT 2.6 with GXT 3.1 on top of it.
Update
I forgot to mention that a feasible solution would be to add a listener for the mouse move on the whole grid, and then get each cell and check if it has the required class, but this seems such an overkill. There must be a simpler way.
A browser fires native events (click, focus, blur, etc.) regardless of whether you need them. These events "bubble up" the DOM tree giving you a chance to "listen" to them at the right level.
In your example you can create 30 listeners at the cell level, waiting for something to happen in "their" cell. This is a heavy (30 objects) and slow (it takes time to build and bind all of them) solution. Alternatively, you can create a single listener at the grid level, which listens to all events that bubble up from the cells and decides if something needs to be done or an event should be ignored. A single listener is lighter and can be created faster. On the flip side, it needs to examine events, although it's usually a fast and efficient operation (e.g. checking of an event source has a particular class name). Note also that this operation takes time when a browser typically does not have much to do anyway, so examining events only to ignore them does not impact user experience.
In my experience a single listener is almost always a better solution, although modern browsers and computers are so fast that the difference is rarely noticeable.

Java swing toggle button to filter jtable rows

I have a JTable, it contains a custom AbstractTableModel that return an object when getValueAt is called. And of course I have a custom TableCellRenderer that is capable of getting/constructing the text from the object so that it can be displayed.
However now I would like to write a filter. Filter will be a simple toggle button. When it is turned on I would like the filter to be applied and when it is turned off I would like filter to be removed.
I have two problems due to that.
First one is that I have absolutely no idea how to write a filter when you have to filter by object rather than a primitive.
Second is I have no idea how to attached the said filter to the toggle button to be able to turn it on and off.
I am sorry if this is a retarded question but http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#sorting was the most useless documentation I saw since explanation was not in depth.
Thanks to anyone for their help.
EDIT:
The object contains multiple fields, but I am interested in two filter toggles specifically. One returns a boolean value when I say isSuper, and the second return a string when I call getName. If first toggle is turned on it should show all entries that return true on isSuper, and the second toggle should show all entries where name is compromised of two words (space is present) when I call getName.
To be honest, the JavaDocs spell it out quite well...
With such little information to go on, the best you can hope for is an overview...
TableRowSorter<TableModel> trs = new TableRowSorter<TableModel>();
table.setRowSorter(trs);
// Create the row filder...
trs.setRowFilter(new RowFilter<TableModel, Integer>() {
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
boolean include = false;
// Returns the value for the specific column...
CustomObject value = (CustomObject)entry.getValue(filterColumn);
if (filterBySuper) {
include = value.isSuper();
} else {
include = value.getName().startsWith(fistPart) && value.getName().endWith(lastPart);
}
return include;
}
});
When you want to update the filter, you simply need to call...
trs.sort();
First one is that I have absolutely no idea how to write a filter when you have to filter by object rather than a primitive.
Did you read the RowFilter API? It shows an example of how to create a custom filter based on a custom Object.
Second is I have no idea how to attached the said filter to the toggle button to be able to turn it on and off.
Did you read the tutorial and try running the demo? The tutorial uses a DocumentFilter to change the filter dynamically every time the user changes the text in the text field. So the tutorial shows you how to dynamically changes the filter based on user input. You can modify the code to change the filter every time the toggle button is pressed.
it contains a custom AbstractTableModel that return an object when getValueAt is called. And of course I have a custom TableCellRenderer that is capable of getting/constructing the text from the object so that it can be displayed.
Unrelated to my answer, but I don't really understand that statement. Are you saying every cell in the model returns a differently object, or does every cell return the same object but you just display a different property of the object for column1, column2, column3 etc? Either way it sounds like a weird design. We can probably suggest something better. Post your SSCCE that demonstrates the problem.

The Elegant way to handle Cyclic Event in Java?

i think this not a specific problem to me; everybody might have encountered this issue before.
To properly illustrate it, here's a simple UI:
As you can see, those two spinners are controlling a single variable -- "A". The only difference is that they control it using different views.
Since these two spinners' displaying values are synchronized, cyclic event shows up.
If i change the top spinner, "A" will be changed and the bottom spinner's value will also be updated accordingly. However, updating the bottom spinner's call (such as setValue) will also trigger another event instructing the top spinner to update based on the bottom spinner's value. Thus creates a bad cycle which can eventually cause a StackOverFlow exception.
My previously solution is kinda cumbersome: i placed a guarding boolean to indicate whether the 2nd updating call should be performed.
Now i'd like to ask "how can i handle such situation elegantly? ( in general, not specific to spinners )"
thx
Update:
Since i've got 2 answers suggesting me to utilize the observer structure, i have to say something about it.
Like what i've said, it's great but far from being perfect. Not only because of its inherent complexity, but also Its inability to solve the problem.
Why? To see the reason, you must realize the tight coupling of the View and Model-Controller in Java Swing. Lets take my spinner UI for an example. Suppose the variable A is actually an Observer object. Then, after firing the first state change event from the top spinner, the Observer "A" will update its value and fire a PropertyChange event to notify the bottom spinner. Then comes the 2nd updating which updates the bottom spinner's View. However, changing bottom spinner's view inevitably triggers a redundant event that will try to set "A"'s value again. Afterwards, the deadly loop is fully constructed and the stack overflow will be thrown.
In theory, the Observer model tries to solve the direct cycle by introducing 2 independent feedback paths. The chained updating odds(in event-response codes) implicitly form a bridge connecting both paths, making a cycle again.
Going back to Model-View-Controller, think about what your Model is, and what your View is.
In your current implementation, you have two models (one for each Spinner control), and they're being synced through the View layer.
What you should be doing though is share the same backing model. For the spinner with a subtracted value, create a proxy to the original model. ie:
class ProxySpinnerModel implements SpinnerModel {
getValue() { return originalSpinner.getValue() - 10 }
setValue(v) { originalSpinner.setValue(v+10) }
}
spinnerA = new JSpinner()
spinnerB = new JSpinner( new ProxySpinnerModel( spinnerA.getModel() ) )
Now, you don't need to add listeners, since they're both working off the same model and the default implementation (the originalModel) already has change listeners which it fires to the view.
Problem Solved
I've got many different suggestions. Particularly,
i want to thank Marc W & Reverend Gonzo. I'm here to make a summary for these ideas; this can save your time navigating thru big chunk of texts.
This problem can be easily bypassed if you carefully decouple the View and Model-Controller.
The dead cycle is caused by dependent writes: write_1 -> write_2 -> write_1 ->.... Intuitively, breaking the dependency can solve the problem elegantly.
If we look into the problem in depth, we can find updating the corresponding views doesn't necessarily involves an external write call. Actually, a view only depends on the data it's representing. Known this, we can then re-write the logic as follow: write_1 -> read_2 & write_2 -> read_1.
To illustrate this idea, lets compare the 3 methods mentioned by different posters:
alt text http://www.freeimagehosting.net/uploads/2707f1b483.png
As you can see, only the proxied view can solve all the dependency thus it's the generic solution for this knid of problem.
In practice, it can be implemented as something like this (in your event-response codes):
setValue(newValue);
anotherSyncUI.parse(); // not anotherSyncUI.setValue() any more
anotherSyncUI.repaint();
No more loops. Solved.
It's a bit complicated, but you could make A actually be an object that's observable. Both spinners (or whatever needs to update itself based on A's value) would then observe A. Whenever A changes, the spinners (or again, whatever object) update themselves to reflect the new value of A. This decouples the spinners' logic from one another. In your example here, the spinners should not be coupled to one another because they really have nothing to do with each other. Instead, they should both simply be bound to A and take care of their own view updating individually.
Whenever the value in the first spinner is changed, you would simply update A's value to match it. Whenever the value in the second spinner is changed, you would of course add 10 to its value before assigning it to A.
Update
In response to the update to your original question, my answer is that the spinners do not listen to one another's change events. Have a separate event handling method for each spinner. A user clicking the up or down arrows in the spinner generates a different event than calling setValue on the spinner programmatically, correct? If the spinners are completely independent of one another, there will be no infinite loop.
E.g. for the second spinner, calculate A-10 and then compare it to the current value of the spinner. If it's the same, do nothing, ending the infinite loop. Similarly for the first spinner.
I think there are also ways to update the spinner's model in a way that doesn't fire an event, but I don't know them off the top of my head.
Use a single SpinnerModel for both JSpinners. See the following code:
Note that the call to setValue() is only made once each time a new value is defined by one of the JSpinners.
import java.awt.BorderLayout;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame jf = new JFrame();
SpinnerModel spinModel = new MySpinnerModel();
JSpinner jspin1 = new JSpinner(spinModel);
JSpinner jspin2 = new JSpinner(spinModel);
jf.setLayout(new BorderLayout());
jf.add(jspin1, BorderLayout.NORTH);
jf.add(jspin2, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(3);
}
}
class MySpinnerModel extends AbstractSpinnerModel {
private int _value = 0;
private int _min = 0;
private int _max = 10;
#Override
public Object getNextValue() {
if (_value == _max) {
return null;
}
return _value + 1;
}
#Override
public Object getPreviousValue() {
if (_value == _min) {
return null;
}
return _value - 1;
}
#Override
public Object getValue() {
return _value;
}
#Override
public void setValue(Object value) {
System.out.println("setValue(" + value + ")");
if (value instanceof Integer) {
_value = (Integer) value;
fireStateChanged();
}
}
}
It seems you're really observing the wrong thing. From the example given I presume what you want to detect is the user's actions on the controls, not the changes in the values themselves. As you've outlined, changes in your model are reflected in the values of the spinners, and it is this which forms the infinite loop of events.
However, diving further into the UI implementation may not be the answer you want. In that case I'd say the best you can do is either your current guard solution, or to better extract the logic into your model (similar to what Marc and William have said). How that can be done will depend on the 'real world' model behind a particular implementation of the provided puzzle.
As a rule, your model should not be defined by your GUI. Ie, the SpinnerModel that backs each JSpinner should not be your value A. (That would be a horribly inelegant tightly coupled dependency on a particular view.)
Instead, your value A should either be a POJO or a property of an object. In which case, you can add PropertyChangeSupport to it. (And presumably have already done so in any case, as you want your spinners to update automatically if A is changed by other parts of your program).
I realise this is similar to Marc W's answer, and you were concerned that it's "complicated", but PropertyChangeSupport does almost all of it for you.
In fact, for trivially simple cases, you can just use a single class that wires a "setProperty" method through to a "firePropertyChange" call (as well as storing the value in a HashMap for any "getProperty" calls).
I don't really want to solve your problem but I find it interesting. I have already been confront to it and solved it each time a different way. But when I think about the 'why ?' and not about the 'how ?' am staying perplexed.
This problem only exists because I am using an automatism (MVC) which had to help me, and exactly in that way. The art how the components are used make this automatism a barrier to a beautiful code.
Why do set #setEvent() has to produce the same event as a GUI action?
Though, my opinion is also pretty close to Observer pattern but it is a bit lighter than that!!!
Have A as a variable with a setter
private Integer A;
setA(int A)
{
this.A = A;
refreshSpinners();
}
refreshSpinners()
{
setSpinnerA();
setSpinnerAMinus10();
}
setSpinnerA()
{
// show value of A
}
setSpinnerAMinus10()
{
// show value of A-10
}

Action commands vs Action classes?

A shout out to the Swing gurus out there!!
I've been doing Swing programming for several years but have always been unclear on this.
As you know Swing/AWT gives you several ways to execute a particular action when a button is clicked. I've seen it done several different ways in the applications I've worked on. The project I'm currently working on tends to follow this approach:
someButton.setActionCommand("mycommand");
someButton.addActionListener(listener);
--snip--
public void actionPerformed(ActionEvent event) {
String command = event.getActionCommand();
if (command.equals("mycommand"))
doThis();
else if (command.equals("someothercommand"))
doThat();
etc.
This seems kind of clunky to me - is there any benefit to this style of programming, or is it better to use Swing Actions?
Or are there different situations where the different approaches are better/worse?
IMO, it is better to use separate listeners for Actions.
That way you leave the delegating of what action should happen up to Swing. You don't have to compare Strings to decide what to do.
Having one huge ActionListener for more than one action feels like breaking the pattern to me.
From a design point of view, I think it is better to have one class to handle one specific action for a component as opposed to one class that is a "lets handle everything for all components here" type of design.
Also, if you use Action you can a) apply it to more than one component (e.g. a button and a menu item) b) call setEnable to enable/disable it for all components its attached to and c) Also use it to define various settings on the components its attached to (namely, the text label, the tooltip text, the accelerator key, icon, etc.). This last one is done via the putValue method and calling this method again will change the settings for all components its attached to.
Specifically, I would advise to subclass AbstractAction for your needs.
I know that's demo code but since you're working on this stuff I thought I'd mention that swing tends to be really repetitive if you're not careful.
Using Action classes tends to let you refactor better. In swing, one of the best ways to start is to ensure that NO strings are in your code. Nearly every "New" will be in a loop of some sort, reading from a dataset (Often the dataset is as simple as an array)--Once you start reading from a dataset like that, actions can help you a lot.
You use data to create your action, data to create your control, and data to associate the two--in this way you can end up very close to (or at) 0 lines of code for a new control.
Once you start programming this way and can see the patterns, it's at least as quick as the repetitive way and much less error prone.
its Useful if you have several buttons or components that perform the same action (ie. several exit buttons on the same page will use the same code)
Set them all to the same action command and they will all use the same code in the listener
JButton.addActionListener(this);
JButton2.addActionListener(this);
JButton.setActionCommand("exit");
JButton2.setActionCommand("exit");
public void ActionPerformed(ActionEvent e){
if(e.getActionCommand=="exit")
System.exit(0);
}

Categories

Resources