Swing DataBinding Rollback changes when cancel button is pressed - java

I have a simple JFrame with several jtextfields inside, the text property of each jtextfield is bound with a field of an object through databinding (i used window builder to setup the binding), when the user change something on the JTextField the changes are automatically reflected to the bound object property, i have the needs that when the user press a JButton (Cancel Button) every changes done by the user will be discarded.
So i want that when the user start editing the field like a transaction will be started and depending on the user action (OK or Cancel Button) the transaction being Committed or RollBacked .
Is it possible with Swing Data Binding framework ? How ?
Here the code that initialize data bindings :
/**
* Data bindings initialization
*/
protected void initDataBindings() {
//Title field
BeanProperty<Script, String> scriptBeanProperty = BeanProperty.create("description");
BeanProperty<JTextField, String> jTextFieldBeanProperty = BeanProperty.create("text");
AutoBinding<Script, String, JTextField, String> autoBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, script, scriptBeanProperty, textFieldName, jTextFieldBeanProperty, "ScriptTitleBinding");
autoBinding.bind();
//Id field
BeanProperty<Script, Long> scriptBeanProperty_1 = BeanProperty.create("id");
BeanProperty<JLabel, String> jLabelBeanProperty = BeanProperty.create("text");
AutoBinding<Script, Long, JLabel, String> autoBinding_1 = Bindings.createAutoBinding(UpdateStrategy.READ, script, scriptBeanProperty_1, labelScriptNo, jLabelBeanProperty, "ScriptIdBinding");
autoBinding_1.bind();
}

nothing out off the box, you have to implement the buffering logic yourself. An example is in my swinglabs incubator section, look at the AlbumModel. Basically
the bean is Album
AlbumModel is a wrapper (aka: buffer) around the bean with the same properties as the wrapped: the view is bound to the properties of this wrapper
internally, it uses a read-once binding to the wrappee properties
in addition, the wrapper has a property "buffering" which is true once any of its buffered properties is different from the wrappee. In this state, the changes can be either committed or canceled
Below is an excerpt of AlbumModel (nearly all minus validation) which might give you an idea. Note that BindingGroupBean is a slightly modified BindingGroup which maps internal state to a bean property "dirty" to allow binding of "buffering". You can find it in the incubator as well as a complete application BAlbumBrowser (an implementation of Fowler's classical example in terms of BeansBinding)
/**
* Buffered presentation model of Album.
*
*/
#SuppressWarnings("rawtypes")
public class AlbumModel extends Album {
#SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(AlbumModel.class
.getName());
private Album wrappee;
private BindingGroupBean context;
private boolean buffering;
public AlbumModel() {
super();
initBinding();
}
#Action (enabledProperty = "buffering")
public void apply() {
if ((wrappee == null))
return;
context.saveAndNotify();
}
#Action (enabledProperty = "buffering")
public void discard() {
if (wrappee == null) return;
context.unbind();
context.bind();
}
private void initBinding() {
initPropertyBindings();
initBufferingControl();
}
private void initBufferingControl() {
BindingGroup bufferingContext = new BindingGroup();
// needs change-on-type in main binding to be effective
bufferingContext.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ,
context, BeanProperty.create("dirty"),
this, BeanProperty.create("buffering")));
bufferingContext.bind();
}
/**
* Buffer wrappee's properties to this.
*/
private void initPropertyBindings() {
context = new BindingGroupBean(true);
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("artist"),
this, BeanProperty.create("artist")));
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("title"),
this, BeanProperty.create("title")));
// binding ... hmm .. was some problem with context cleanup
// still a problem in revised binding? Yes - because
// it has the side-effect of changing the composer property
// need to bind th composer later
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("classical"),
this, BeanProperty.create("classical")));
context.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_ONCE,
wrappee, BeanProperty.create("composer"),
this, BeanProperty.create("composer")));
context.bind();
}
public void setAlbum(Album wrappee) {
Object old = getAlbum();
boolean oldEditEnabled = isEditEnabled();
this.wrappee = wrappee;
context.setSourceObject(wrappee);
firePropertyChange("album", old, getAlbum());
firePropertyChange("editEnabled", oldEditEnabled, isEditEnabled());
}
public boolean isEditEnabled() {
return (wrappee != null); // && (wrappee != nullWrappee);
}
public boolean isComposerEnabled() {
return isClassical();
}
/**
* Overridden to fire a composerEnabled for the sake of the view.
*/
#Override
public void setClassical(boolean classical) {
boolean old = isComposerEnabled();
super.setClassical(classical);
firePropertyChange("composerEnabled", old, isComposerEnabled());
}
public boolean isBuffering() {
return buffering;
}
public void setBuffering(boolean buffering) {
boolean old = isBuffering();
this.buffering = buffering;
firePropertyChange("buffering", old, isBuffering());
}
/**
* Public as an implementation artefact - binding cannot handle
* write-only properrties? fixed in post-0.61
* #return
*/
public Album getAlbum() {
return wrappee;
}
}

Related

How to dynamically reference resources in the R.drawable folder in Android?

I have the following code for my Adapter:
#Override
public void onBindViewHolder(GameViewHolder holder, int position) {
final Games game = gameList.get(position);
holder.awayTeamImageView.setBackgroundResource(R.drawable.fortyers);
}
The above works perfectly but I am hard coding the image that will be displayed. What I really need is to get the background image from the game list and I am looking to do something like this:
holder.awayTeamImageView.setBackgroundResource(R.drawable.game.getaBackground());
But that causes an error
How can I dynamically set the imageView's background resource?
UPDATE:
I have attached a screenshot of the desired effect.
Each week the schedule will change so the list will always be different depending on the week selected.
Game constructor:
public Games(DataSnapshot game) {
this.AwayTeam = game.child("AwayTeam").getValue().toString();
this.AwayId = Integer.parseInt(game.child("AwayId").getValue().toString());
this.HomeTeam = game.child("HomeTeam").getValue().toString();
this.HomeId = Integer.parseInt(game.child("HomeId").getValue().toString());
this.aBackground = game.child("aBackground").getValue().toString();
this.hBackground = game.child("hBackground").getValue().toString();
}
Create a list of String that will contain the id of your drawables, keep them in the order synced with the position and then get the id as per the position from the list to pass while setting the drawable.
If it's something reused in multiple places and the order is consistent, you can also go for an Enum to get the drawable id in terms of a certain key of your choice. You might need to figure the key using a when condition as per position if it's really dependant on the position.
Hope that works!
Assuming Games class should be a model/data class; so you can have an int member field that can store drawable id for each instance of this class, and create getter & setter:
public class Games {
private int myDrawable;
public int getMyDrawable() {
return myDrawable;
}
public void setMyDrawable(int myDrawable) {
this.myDrawable = myDrawable;
}
}
Whenever you construct the Games objects, you can set the drawable image id using setMyDrawable(R.drawabe.foo).. Probably you can modify the Games constructor to accept an int parameter that you can set myDrawable to.
Then in RecyclerView:
#Override
public void onBindViewHolder(GameViewHolder holder, int position) {
final Games game = gameList.get(position);
holder.awayTeamImageView.setBackgroundResource(game.getMyDrawable());
}
UPDATE:
public class Games {
private int homeTeamImage, awayTeamImage;
public int getHomeTeamImage() {
return homeTeamImage;
}
public void setHomeTeamImage(int homeTeamImage) {
this.homeTeamImage = homeTeamImage;
}
public int getAwayTeamImage() {
return awayTeamImage;
}
public void setAwayTeamImage(int awayTeamImage) {
this.awayTeamImage = awayTeamImage;
}
public Games(DataSnapshot game, int homeTeamImage, int awayTeamImage) {
//... rest of code
this.homeTeamImage = homeTeamImage;
this.awayTeamImage = awayTeamImage;
}
}
But in terms of OOP, I'd suggest that you can have a Team class that has either team properties, like image, name, .... etc. and the Games class can be the controller of the game, not building the team instances.
Another approach that might help you is using getIdentifier from the string. For this what you have make a background string name same as the one in your resource. e.g if you want to show ic_menu_camera then your aBackground should be the same string.
Example game class:
public class Game {
String aBackground;
public Game(String aBackground) {
this.aBackground = aBackground;
}
public String getaBackground() {
return aBackground;
}
public void setaBackground(String aBackground) {
this.aBackground = aBackground;
}
}
Then use it like this. Read inline comments to understand in detail.
//set the background name that you want same as name in your drawable folder
// without any extension .png or .xml. Just name should be there
Game game = new Game("ic_menu_camera");
ImageView imageView = findViewById(R.id.imageView);
//get the id by name using this
/* #param name The name of the desired resource.
* #param defType Optional default resource type to find, if "type/" is
* not included in the name. Can be null to require an
* explicit type.
* #param defPackage Optional default package to find, if "package:" is
* not included in the name. Can be null to require an
* explicit package.
*
* #return int The associated resource identifier. Returns 0 if no such
* resource was found. (0 is not a valid resource ID.)
* */
int resId = getResources().getIdentifier(game.getaBackground(), "drawable",getPackageName());
// then set that id to your image
imageView.setBackgroundResource(resId);
Do not make things too complicated. Just use a simple approch. Using HashMap should do just fine.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Cincinnati", R.drawable.red_striped_helmet);
map.put("Cleveland", R.drawable.red_helmet);
map.put("New York", R.drawable.blue_helmet);
map.put("Chicago", R.drawable.dark_blue_helmet);
map.put(homeTeam, homeId);
...
holder.awayTeamImageView.setBackgroundResource(map.get("New York"));
// holder.awayTeamImageView.setBackgroundResource(map.get(homeTeam));

How to create global events in wicket?

The case is simple: I have several ajax components and I want to update them when some ajax action is
happened. It is easy when all of these components are in the same place and they can be reached one by another.
But if the page has a huge hierarchy this can be not so trivial and to solve this problem I would like to send some global event (which will contain an IPartialPageRequestHandler) and all these components should catch it and update himself.
How can I do this in wicket?
Actualy I see onEvent method in the component class and I can access IPartialPageRequestHandler inside of it:
public void onEvent(IEvent<?> event){
Object payload = event.getPayload();
if (payload instanceof IPartialPageRequestHandler) {
...
}
}
but how can I create the global event that should be catched by this method?
Create a custom event, for example:
public class CounterUpdate
{
private final AjaxRequestTarget target;
/**
* Constructor
*
* #param target
*/
public CounterUpdate(AjaxRequestTarget target)
{
this.target = target;
}
/** #return ajax request target */
public AjaxRequestTarget getTarget()
{
return target;
}
}
In your Ajax callback method broadcast it:
send(getPage(), Broadcast.BREADTH, new CounterUpdate(target));
In any Component/Behavior that is interested for this event do:
#Override
public void onEvent(IEvent<?> event)
{
super.onEvent(event);
// check if this is a counter update event and if so repaint self
if (event.getPayload() instanceof CounterUpdate)
{
CounterUpdate update = (CounterUpdate)event.getPayload();
update.getTarget().add(this);
}
}
you could notify the entire page or application 'page.send(...)' or 'application.send(...)'. Wicket already does it for every AJAX event to notify the entire page hierarchy. See the end of this paragraph from user guide:
https://ci.apache.org/projects/wicket/guide/8.x/single.html#_how_to_use_ajax_components_and_behaviors

What parent to set when calling FieldEditor#setEnabled?

I am building a preference page in Eclipse by extending the FieldEditorPreferencePage class. this page contains 2 fields : 1 BooleanFieldEditor (checkbox) and 1 FileFieldEditor. I would like to disable/enable the file field following the checkbox value.
I went up to something like this (some obvious code is not displayed):
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
public static final String PREF_KEY_1 = "checkBoxPref";
public static final String PREF_KEY_2 = "filePref";
private FileFieldEditor pathField;
private BooleanFieldEditor yesOrNoField;
private Composite pathFieldParent;
#Override
protected void createFieldEditors() {
this.yesOrNoField = new BooleanFieldEditor(PREF_KEY_1, "Check this box!", getFieldEditorParent());
this.pathFieldParent = getFieldEditorParent();
this.pathField = new FileFieldEditor(PREF_KEY_2, "Path:", this.pathFieldParent);
addField(this.yesOrNoField);
addField(this.pathField);
boolean isChecked = getPreferenceStore().getBoolean(PREF_KEY_1);
updatePathFieldEnablement(! isChecked);
}
/**
* Updates the fields according to entered values
*/
private void updatePathFieldEnablement(boolean enabled) {
this.pathField.setEnabled(enabled, this.pathFieldParent);
}
#SuppressWarnings("boxing")
#Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(FieldEditor.VALUE) && event.getSource() == this.yesOrNoField) {
updatePathFieldEnablement(! (boolean) event.getNewValue());
}
super.propertyChange(event);
}
}
My question is about this second parameter in FieldEditor#setEnabled. This parameter is the parent composite of the FieldEditor's controls ("Used to create the controls if required" says the javadoc) . At first, I set the value with the return of getFieldEditorParent but then I got an exception "Different parent". So I ended storing it (cf. this.pathFieldParent) and give it back to setEnabled and it works (or it seems to work).
But I am not sure I am doing well, especially because I had to create a member in my class that means nothing to it (and I would have to create many of them if I had many fields to enable/disable).
Do you think I am doing well or is there a better way to provide this parent ? And could you explain to me why *setEnabled" needs it ?
Thanks.
You are using the default FLAT layout for the preference page. When this layout is used each call to getFieldEditorParent generates a new Composite so you have to make just one call and remember the correct parent. Using the GRID layout getFieldEditorParent always returns the same parent. This is the actual code:
protected Composite getFieldEditorParent() {
if (style == FLAT) {
// Create a new parent for each field editor
Composite parent = new Composite(fieldEditorParent, SWT.NULL);
parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return parent;
}
// Just return the parent
return fieldEditorParent;
}
setEnabled does sometimes create a new Label control so it needs to know the correct parent Composite.

Create CellFactory based on row item

I try to create TableColumn with ChoiceBoxTableCell. Choices in this ChoiceBox are dynamically generated (and changed over time) based on item associate with current row. I tried different approaches, but nothing seems to work.
I would like to have something like this:
private DataProvider dataProvider;
private TableColumn<Phone, String> testColumn;
public void initialize() {
testColumn.setCellFactory(param, phone -> new ChoiceBoxTableCell<Phone, String>(dataProvicer.get(phone)));
}
Where:
public interface DataProvider {
ObservableList<String> get(Phone phone);
}
This is my ideal code I would like to have, but as you know setCallFactory takes Callback with TableColumn<S,T> as function parameter and there is no way to access it within CellFactory. I could probably do some dirty and ugly hacks to get why I want, but I would love to have some nice solution.
A reminder of the basic mechanism: a cellFactory is used to create any cell of for the given column. The calling code (that is the VirtualFlow deep inside the implementation of table's skin) isn't interested in or don't event know which row the cell is created for. Also, it will be re-used - that is setting a new item - quite often. In all, the moment of creating the cell is not the right time to configure the cell with row-related data. This has to be done later, once the row is known: the most obvious candidate is updateItem(T, boolean).
Now back to the concrete ChoiceBoxTableCell: unfortunately, its implementation is too dumb and simply doesn't support dynamic updates of its choice items. So you need a custom extension which does support the dynamics. On the bright side: ChoiceBoxTableCell exposes its items, thus allowing to change its contents as needed.
As noted in the code comment, it turned out that the obvious hook didn't work out nicely. So had to move the config into the startEdit method.
Some code:
public interface ChoiceItemProvider<S, T> {
ObservableList<T> getItems(S source);
}
public class DynamicChoiceBoxTableCell<S, T> extends ChoiceBoxTableCell<S, T> {
private ChoiceItemProvider<S, T> provider;
public DynamicChoiceBoxTableCell(ChoiceItemProvider<S, T> provider) {
super();
this.provider = provider;
}
/**
* Not so obvious hook: overridden to update the items of the
* choiceBox.
*/
#Override
public void startEdit() {
super.startEdit();
updateItems();
}
/**
* Obvious hook: override to update the items of the choiceBox.
* Not fully working - for some reason, the current item isn't
* selected after starting the edit.
*/
#Override
public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
// updateItems();
}
/**
* Dynamically updates the items to current rowItem.
*/
#SuppressWarnings("unchecked")
protected void updateItems() {
TableRow<S> tableRow = getTableRow();
S rowItem = tableRow != null ? tableRow.getItem() : null;
if (provider == null || rowItem == null) return;
if (provider != null) {
getItems().setAll(provider.getItems(rowItem));
}
}
}
Addendum re:
no ideal, because items won't be updated when it is already expanded
If you need that, you can bind the choiceBox' items to the items returned by the provider, that is instead of calling setAll(provider.getItems()) do:
Bindings.bindContent(getItems(), provider.getItems());
testColumn.setCellFactory(ChoiceBoxTableCell.forTableCoulmn(<dynamic list>));
This should work.

Object oriented programming - question about design and accessing relevant data

Consider you have the following code in a Timer:
It's main goal is to count down the seconds and to show it on GUI, which is Swing based.
The Timer is part of the game and is used for a decision of who is the winner by taking the user who reached a solution first.
Action updateClockAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JLabel secLabel = m_GameApplet.GetJpanelStartNetGame().GetJlabelSeconds();
secLabel.setFont(new java.awt.Font("Lucida Handwriting", 1, 36));
secLabel.setForeground(Color.red);
secLabel.setText(Integer.toString(m_TimerSeconds));
if (m_TimerSeconds > 0) {
m_TimerSeconds--;
} else if (m_TimerSeconds == 0) {
m_Timer.stop();
m_GameApplet.GetJpanelStartNetGame().GetJlabelSeconds().setText("0");
m_GameApplet.GetJpanelStartNetGame().GetJbuttonFinish().setVisible(false);
//Checking whether time ended for both players and no solution was recieved
if (!m_WasGameDecisived) {
System.out.println("Tie - No one had a solution in the given time");
}
}
}
};
m_Timer = new Timer(1000, updateClockAction);
Now, I have two main packages in the client implementation
A.GUI - hold all the swing Jpanels etc.
B.LogicEngine
Now in LogicEngine I have a class that is called GameManager- that should manage and store information about the game.
My Current implementation in general is like this:
I have a JpanelMainGame which is on the GUI Package
JpanelMainGame contains JPanelGameBoard which has reference(or in other word contains) an instance of GameManger, which is on different package - The LogicEngine package.
So my questions are these:
Where does all of the timer definition code above should be placed?
A. In the JpanelMainGame? (JpanelMainGame should have a reference to it).
B. In GameManger as a part of a data of the game
In each solution you give, How should I access all the label info from within the anonymous inner class? As I can only access member from the outer class with command: OuterClass.this.member.
Any comments about current design.
Thank you very much
I repeat the answer I gave on another question:
I strongly discourage from organizing packages from an implementational point of view, like controllers, data, etc. I prefer grouping them by functionality, that is, feature1, feature2, etc. If a feature is reasonably complex and requires a large number of classes, then (and only then) I create subpackages like above, that is, feature1.controllers, feature1.data, etc.
Well not to answer questions with questions, but...
How often is Timer used?
Is Timer coded to be general use, or specific to a certain class?
If the Timer is general use, then I would say it belongs in the LogicEngine package tree somewhere. However, if Timer can only be used in a GUI element, then it belongs in the GUI package tree.
As a general rule of thumb (not to bang on the Agile drum too hard), you should only code what makes sense now but not be afraid to change it later.
Example: You are making an anonymous inner class of the type AbstractAction() in your code. This is just fine (although I shy away from anon classes) if the updateClockAction variable is used simply. But once your coding leads you to the need to cross the boundries of updateClockAction (via OuterClass.this.member) then I assert its time for a bit of refactoring: extract this inner class and make it a fully qualified class. This way, you can have the proper amount of control over the updateClockAction object's internal state (using the cstr, getters, etc)
EDIT: Added requested example
public class Testing {
private Timer timer; /* INIT this from somewhere.... */
public void myFunction() {
/* 60 Seconds */
long countDownTimeSEC = 60;
/* convert to Miliseconds */
long countDownTimeMS = 1000 * countDownTimeSEC;
/* Get ref to label */
JLabel label = m_GameApplet.GetJpanelStartNetGame().GetJlabelSeconds();
/* Set once */
label.setFont(new java.awt.Font("Lucida Handwriting", 1, 36));
label.setForeground(Color.red);
/* Set initial time */
label.setText(Long.toString(countDownTimeSEC));
/* Get ref to button */
JButton button = m_GameApplet.GetJpanelStartNetGame().GetJbuttonFinish();
/* Set up post Count Down list */
ArrayList<AbstractAction> actsWhenComplete = new ArrayList<AbstractAction>();
/* instantiate countdown object */
CountDownClockAction cdca = new CountDownClockAction(label, countDownTimeMS, actsWhenComplete);
this.timer = new Timer(1000, cdca);
/* Now that we have a timer, add the post Count Down action(s) to the post Count Down list */
actsWhenComplete.add(new CountDownFinishAction(label, button, this.timer));
/* Finally, kick off the timer */
this.timer.start();
}
public static class CountDownClockAction extends AbstractAction {
private static final long serialVersionUID = 1L;
private final JLabel labelToUpdate;
private final long startMS;
private long currentMS;
private long timeMark;
private final ArrayList<AbstractAction> actionsToExeWhenComplete;
private boolean actionsExedFlag;
public CountDownClockAction(
final JLabel labelToUpdate,
final long startMS,
ArrayList<AbstractAction> actionsToExeWhenComplete
) {
super();
this.labelToUpdate = labelToUpdate;
this.startMS = startMS;
this.currentMS = startMS;
this.timeMark = 0;
this.actionsExedFlag = false;
this.actionsToExeWhenComplete = actionsToExeWhenComplete;
}
#Override
public void actionPerformed(final ActionEvent e) {
/* First time firing */
if (this.timeMark == 0)
this.timeMark = System.currentTimeMillis();
/* Although the Timer object was set to 1000ms intervals,
* the UpdateClockAction doesn't know this, nor should it
* since > or < 1000ms intervals could happen to do the fact that
* the JVM nor the OS have perfectly accurate timing, nor do they
* have instantaneous code execution properties.
* So, we should see what the *real* time diff is...
*/
long timeDelta = System.currentTimeMillis() - this.timeMark;
/* Allow for the label to be null */
if (this.labelToUpdate != null)
labelToUpdate.setText(Long.toString((long)(currentMS / 1000)));
if (currentMS > 0) {
currentMS -= timeDelta;
} else if (currentMS <= 0 && this.actionsExedFlag == false) {
/* Ensure actions only fired once */
this.actionsExedFlag = true;
/* Allow for the label to be null */
if (this.actionsToExeWhenComplete != null)
for (AbstractAction aa: this.actionsToExeWhenComplete)
aa.actionPerformed(e);
}
/* Finally, update timeMark for next calls */
this.timeMark = System.currentTimeMillis();
}
}
public static class CountDownFinishAction extends AbstractAction {
private final JLabel labelToUpdate;
private final JButton buttonToUpdate;
private final Timer timerToStop;
public CountDownFinishAction(
JLabel labelToUpdate,
JButton buttonToUpdate,
Timer timerToStop
) {
super();
this.labelToUpdate = labelToUpdate;
this.buttonToUpdate = buttonToUpdate;
this.timerToStop = timerToStop;
}
#Override
public void actionPerformed(final ActionEvent e) {
/* Perform actions, allowing for items to be null */
if (this.labelToUpdate != null)
this.labelToUpdate.setText("0");
if (this.buttonToUpdate != null)
this.buttonToUpdate.setVisible(false);
if (this.timerToStop != null)
this.timerToStop.stop();
}
}
}

Categories