How do I update a ColourHighlighter (swingx) when the predicate has changed - java

I have a class called ErrorHighlighter which gets notified anytime a property called errorString is changed. Based on this propertychangeevent I update the HighLighterPredicate to highlight a particular row with a red background.
ErrorHighlighter receives the propertychangeevent, it also changes the HighlighterPredicate, but the table row does not get updated with red background.
I also update the tooltip of the row. That does not get reflected either.
Please see the code below. Could someone please help?
public class ErrorRowHighlighter extends ColorHighlighter implements PropertyChangeListener {
private Map<Integer, String> rowsInError;
private SwingObjTreeTable<ShareholderHistoryTable> treeTable;
public ErrorRowHighlighter(SwingObjTreeTable<ShareholderHistoryTable> treeTable) {
super(CommonConstants.errorColor, null);
this.treeTable = treeTable;
rowsInError=new HashMap<Integer, String>();
setHighlightPredicate(new HighlightPredicate() {
#Override
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
if(rowsInError.containsKey(adapter.row)){
return true;
}
return false;
}
});
this.treeTable.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int row=ErrorRowHighlighter.this.treeTable.rowAtPoint(e.getPoint());
if(rowsInError.containsKey(row)){
ErrorRowHighlighter.this.treeTable.setToolTipText(rowsInError.get(row));
}else{
ErrorRowHighlighter.this.treeTable.setToolTipText(null);
}
}
});
}
public void highlightRowWithModelDataAsError(ShareholderHistoryTable modelData){
int indexForNodeData = treeTable.getIndexForNodeData(modelData);
if(indexForNodeData>-1){
rowsInError.put(indexForNodeData, modelData.getErrorString());
updateHighlighter();
}
}
public void unhighlightRowWithModelDataAsError(ShareholderHistoryTable modelData){
int indexForNodeData = treeTable.getIndexForNodeData(modelData);
if(indexForNodeData>-1){
rowsInError.remove(indexForNodeData);
updateHighlighter();
}
}
public void updateHighlighter(){
treeTable.removeHighlighter(this);
treeTable.addHighlighter(this);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
ShareholderHistoryTable sourceObject= (ShareholderHistoryTable) evt.getSource();
if(StringUtils.isNotEmpty(sourceObject.getErrorString())){
highlightRowWithModelDataAsError(sourceObject);
}else{
unhighlightRowWithModelDataAsError(sourceObject);
}
}
}

This looks like a mistake on my part. The method treeTable.getIndexForNodeData() actually returns back the index of the row by doing a pre-order traversal of the underlying tree data structure. This includes a root node that is not being displayed on the jxtreetable. Hence I needed to minus 1 from the index
int indexForNodeData = treeTable.getIndexForNodeData(modelData)-1;
This fixed the problem for me. I am leaving the post rather than deleting it if anyone wants to look at an example of a ColorHighlighter and a property change listener.

Related

Switch add-on with push, vaadin

I have a simple example of the add-on switch with vaadin, what I want is to keep the state of the switch even when I update the UI, that is, I support multiple tabs, but I can not do it, this push example is very similar to What I want to do but with a textField.
https://github.com/vaadin-marcus/push-example/blob/master/src/main/java/com/vaadin/training/ScrumBoardLayout.java
https://github.com/rucko24/MVP/blob/testingSwitchPushTemu/src/main/java/com/Core/vaadin/pushServer/ejemploPushMarkus/ScrumBoard.java
To my example I add a bulb so that when another accesses the application can see the current state of the bulb. My example in github is this with only 3 classes
https://github.com/rucko24/MVP/tree/testingSwitchPushTemu/src/main/java/com/Core/vaadin/pushServer/ejemploPushMarkus
This is the swithc listener that changes my bulb, but when I get the boolean value (true, or false), I still do not understand the right way to push the other switch
switchTemu.addValueChangeListener(new Property.ValueChangeListener() {
private static final long serialVersionUID = 1L;
#Override
public void valueChange(Property.ValueChangeEvent event) {
boolean estado = (boolean) event.getProperty().getValue();
ScrumBoard.addSwitch(estado);
switchTemu.removeValueChangeListener(this);
if(estado == Boolean.TRUE) {
bombilla.setIcon(bombillaON);
}else {
bombilla.setIcon(bombillaOFF);
}
switchTemu.addValueChangeListener(this);
}
});
Update
In my example github achievement, change the state of all switches to all UI, but I still do not know how to get the state of the switches
I made a couple of changes to your sources (still basic, but it gets you started):
only 1 common shared state
switch value change listeners now just trigger a state changed event
state changed listeners now update the UI elements when triggered
upon registration, a state changed listeners is informed (triggered) about the current state
The main idea is to have just a single shared state and any change is communicated to all the listeners (including the one where the change originated).
Below you can find the code: (P.S. I did not recompile my widgetset so the nice switch icon falls back to the default check box style)
1) SwitchState - represents the state of the switch shared between all the app instances
public enum SwitchState {
ON(true, new ThemeResource("img/on.png")), OFF(false, new ThemeResource("img/off.png"));
private final boolean value;
private final ThemeResource icon;
SwitchState(boolean value, ThemeResource icon) {
this.value = value;
this.icon = icon;
}
public boolean getValue() {
return value;
}
public ThemeResource getIcon() {
return icon;
}
public static SwitchState from(boolean value) {
return value ? ON : OFF;
}
}
2) ScrumBoard common state and listeners manager
public class ScrumBoard {
// list of listeners
private static List<SwitchChangeListener> LISTENERS = new ArrayList<>();
// initial state
private static SwitchState STATE = SwitchState.OFF;
// state change listener contract
public interface SwitchChangeListener {
void handleStateChange(SwitchState state);
}
// handle a a state change request
public static synchronized void updateState(boolean value) {
STATE = SwitchState.from(value);
fireChangeEvent(STATE);
}
// register a new state listener
public static synchronized void addSwitchChangeListener(SwitchChangeListener listener) {
System.out.println("Added listener for " + listener);
LISTENERS.add(listener);
// when a new listener is registered, also inform it of the current state
listener.handleStateChange(STATE);
}
// remove a state listener
public static synchronized void removeSwitchListener(SwitchChangeListener listener) {
LISTENERS.remove(listener);
}
// fire a change event to all registered listeners
private static void fireChangeEvent(SwitchState state) {
for (SwitchChangeListener listener : LISTENERS) {
listener.handleStateChange(state);
}
}
}
3) ScrumBoardLayout - UI layout and components
public class ScrumBoardLayout extends VerticalLayout implements ScrumBoard.SwitchChangeListener {
private Label icon = new Label();
private Switch mySwitch = new Switch();
public ScrumBoardLayout() {
setMargin(true);
setSpacing(true);
addHeader();
// listen for state changes
ScrumBoard.addSwitchChangeListener(this);
}
private void addHeader() {
mySwitch.setImmediate(true);
icon.setSizeUndefined();
// notify of state change
mySwitch.addValueChangeListener((Property.ValueChangeListener) event -> ScrumBoard.updateState((Boolean) event.getProperty().getValue()));
VerticalLayout layout = new VerticalLayout();
layout.setHeight("78%");
layout.addComponents(icon, mySwitch);
layout.setComponentAlignment(icon, Alignment.BOTTOM_CENTER);
layout.setComponentAlignment(mySwitch, Alignment.BOTTOM_CENTER);
layout.setExpandRatio(mySwitch, 1);
addComponents(layout);
}
#Override
public void handleStateChange(SwitchState state) {
// update UI on state change
UI.getCurrent().access(() -> {
mySwitch.setValue(state.getValue());
icon.setIcon(state.getIcon());
Notification.show(state.name(), Type.ASSISTIVE_NOTIFICATION);
});
}
#Override
public void detach() {
super.detach();
ScrumBoard.removeSwitchListener(this);
}
}
4) Result
I could see that with the ThemeResource () class, changing the bulb to its ON / OFF effect is strange, but I solve it as follows
.bombillo-on {
#include valo-animate-in-fade($duration: 1s);
width: 181px;
height: 216px;
background: url(img/on.png) no-repeat;
}
.bombillo-off {
#include valo-animate-in-fade($duration: 1s);
width: 181px;
height: 216px;
background: url(img/off.png) no-repeat;
}
public enum Sstate {
ON(true,"bombillo-on"),
OFF(false,"bombillo-off");
private boolean value;
private String style;
Sstate(boolean value, String style) {
this.value = value;
this.style = style;
}
public boolean getValue() { return value;}
public String getStyle() { return style;}
public static Sstate from(boolean value) { return value ? ON:OFF;}
}
And the handleChangeEvent It stays like this
#Override
public void handleChangeEvent(Sstate state) {
ui.access(() -> {
bombilla.setStyleName(state.getStyle());
s.setValue(state.getValue());
System.out.println(state+" values "+s);
});
}
UPDATE:
I notice an issue, that when I add a new view, or change using the buttonMenuToggle, it loses the synchronization, and update the bulb quite strange, clear with the themeResource does not happen that.
Solution:
to avoid UiDetachedException when using the Navigator try this, It works very well
#Override
public void handleChangeEvent(Sstate state) {
if(!ui.isAttached()) {
BroadcastesSwitch.removeListener(this);
return;
}
ui.access(() -> {
bombilla.setStyleName(state.getStyle());
s.setValue(state.getValue());
System.out.println(state+" values "+s);
});
}

jList not displaying data in specific case

In my program, I have a jList and I can add, delete, modify items in this Jlist.
My problem is, if I click on my add button before selecting an item in my jList, the items inside the jList disapear. (only in apeareance because they are actually still in the jList)
If, before that, I select an item in my list, then everything is working fine. So my guess would be that the "valueChanged()" method from my listener is doing something that I don't do myself.
Here is my list initialisation, which I call at the start of the program:
public final void initList() {
jListPaiement.setModel(new MyListModel(ls.getDb().getListePaiements()));
final DecimalFormat df = new DecimalFormat("###.##");
jListPaiement.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent lse) {
MoyenPaiement mp = (MoyenPaiement) ((MyListModel) jListPaiement.getModel()).getElementAt(jListPaiement.getSelectedIndex());
jTextFieldFF.setText(df.format(mp.getFraisf()));
jTextFieldFV.setText(df.format(mp.getFraisv() * 100));
jTextFieldNomP.setText(mp.getNom());
jTextFieldFF.setVisible(true);
jTextFieldFV.setVisible(true);
jTextFieldNomP.setVisible(true);
jLabel1.setVisible(true);
jLabel6.setVisible(true);
jLabel7.setVisible(true);
jLabel8.setVisible(true);
jLabel11.setVisible(true);
jButtonSaveP.setVisible(true);
}
});
Here is the code from the add button:
private void jButtonAddPActionPerformed(java.awt.event.ActionEvent evt) {
MoyenPaiement mp = new MoyenPaiement("Nouveau", 0, 0);
((MyListModel) jListPaiement.getModel()).addElement(mp);
jListPaiement.setSelectedValue(mp, true);
jListPaiement.repaint();
}
MyListModel code:
public class MyListModel extends AbstractListModel {
ArrayList list;
public MyListModel(ArrayList list) {
this.list = list;
}
#Override
public int getSize() {
return list.size();
}
#Override
public Object getElementAt(int i) {
return list.get(i);
}
public void addElement(Object o){
list.add(o);
}
public void deleteElement(Object o){
list.remove(o);
}
public void setElement(int i,Object o){
list.set(i, o);
}
public ArrayList getList() {
return list;
}
public void setList(ArrayList list) {
this.list = list;
}
}
Any help will be greatly appreciated.
Thanks
Edit: After further research, the problem is when I add item to my model.
It comes exactly on the line:
((MyListModel) jListPaiement.getModel()).addElement(mp);
Even if I add a simple string such as:
((MyListModel) jListPaiement.getModel()).addElement("String");
The problem still occurs.
Look in detail what happens on this line and if you initialize jListPaiement correctly with the right data.
jListPaiement.setModel(new MyListModel(ls.getDb().getListePaiements()));
Seems like on this line setSelectedValue() can't find the element mp
jListPaiement.setSelectedValue(mp, true);
I finally found a solution.
Rather than using my own List Model, I used DefaultListModel and everything works fine. It's been long time since i worked on this project and I don't remember why i chose to make my own list model class.
Even tough it works now, I still don't understand what was missing in my own class (MyListModel) that made it not working..

Error using custom renderer to show value in jComboBox

I have researched and attempted to fill a jcombobox dynamically from an arraylist containing Publisher Objects. I have tried to implement a renderer in order to show the Publishers name using the getName() method. The combobox shows the names when the program is run, however, if a new Publisher is then added to the ArrayList, the combobox becomes blank.
Creating Model:
public class PublisherComboBoxModel implements ComboBoxModel{
protected List<Publisher> publishers;
public PublisherComboBoxModel(List<Publisher> list) {
this.listeners = new ArrayList();
this.publishers = list;
if(list.size() > 0) {
selected = list.get(0);
}
}
protected Object selected;
#Override
public void setSelectedItem(Object item) {
this.selected = item;
}
#Override
public Object getSelectedItem() {
return this.selected;
}
#Override
public Object getElementAt(int index) {
return publishers.get(index);
}
#Override
public int getSize() {
return publishers.size();
}
protected List listeners;
#Override
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
#Override
public void removeListDataListener(ListDataListener l) {
this.listeners.remove(l);
}
}
Creating renderer:
jComboBoxPublisher.setModel(publisherComboModel);
jComboBoxPublisher.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
Publisher publisher = (Publisher)value;
if(value!=null)
{
value = publisher.getName();
}
return super.getListCellRendererComponent(list, value,
index, isSelected, cellHasFocus);
}
This is not necessarily an answer, but highlights a potential problem
While skimming over your code, I noticed that you combo box model is simply maintaining a reference to the original list. This isn't necessarily a problem, but may result in some unexpected and potentially, unwanted behaviour...
The main problem, is that the combo box model has no idea when the list is changed, therefore it can't tell combo box that it should updated.
Generally, what I would normally do is make a new List of the original list. This means that if the original is updated, it won't cause issues for the model and and combo box.
I would then add mutation functionality to the combo box model so it could be updated, for example...
public class PublisherComboBoxModel extends AbstractListModel implements ComboBoxModel {
private List<Publisher> publishers;
private Publisher selectedItem;
public PublisherComboBoxModel(List<Publisher> publishers) {
this.publishers = new ArrayList<>(publishers);
}
public void addPublisher(Publisher pub) {
publishers.add(pub);
fireIntervalAdded(this, publishers.size() - 1, publishers.size() - 1);
}
#Override
public int getSize() {
return publishers.size();
}
#Override
public Object getElementAt(int index) {
return publishers.get(index);
}
#Override
public void setSelectedItem(Object anItem) {
selectedItem = (Publisher) anItem;
}
#Override
public Object getSelectedItem() {
return selectedItem;
}
}
There are several alternatives to this idea. You could create a "general" model, which listed the publishers, but provided event notification to interested parties, so when you added or removed publishers from this model, interested parties, like the combo box model, would be notified and have an opportunity to update themselves and forward appropriate notifications to their interested parties.
Personally, in larger scaled applications, this is my preferred approach.
Another approach would be to provide the combo box model with direct notification...
Thats, you would maintain a reference to the existing list as you are, but the combo box model would have methods that you could call which it could then forward on.

Selecting only leafs in JTree

So I want to be able to only select leafs in JTree. There are some solutions online, but they don't work on multiple selection...
What I would like is to find the part of the code that fires when user clicks on a node and modify that part to suit my needs.
I have found a listener within DefaultTreeCellEditor, but that code seem to apply to when only one node is selected at a time...
The bottom line is, where can I find the code that, when nodes gets clicked, decides if it will select it or not, and will it or not deselect all the other selected nodes?
Fixed it!
public class LeafOnlyTreeSelectionModel extends DefaultTreeSelectionModel
{
private static final long serialVersionUID = 1L;
private TreePath[] augmentPaths(TreePath[] pPaths)
{
ArrayList<TreePath> paths = new ArrayList<TreePath>();
for (int i = 0; i < pPaths.length; i++)
{
if (((DefaultMutableTreeNode) pPaths[i].getLastPathComponent()).isLeaf())
{
paths.add(pPaths[i]);
}
}
return paths.toArray(pPaths);
}
#Override
public void setSelectionPaths(TreePath[] pPaths)
{
super.setSelectionPaths(augmentPaths(pPaths));
}
#Override
public void addSelectionPaths(TreePath[] pPaths)
{
super.addSelectionPaths(augmentPaths(pPaths));
}
}

GWT - Issue with property change member variable being raised while loading the data first time

I have a GWT application that loads a product when the page is loaded. I am using PropertyChangeEvent on the product object (and its sub-objects) to update the values of fields, whenever a change happens.
Of course, I do not want this PropertyChangeEvent to raise when the product is loaded for the first time. For this, I am setting the raisePropertyChange value to false, but it doesn't seem to work. Please find below the code base:
// Class ProductBaseImpl
public abstract class PropChangeImpl {
// The raise property change event, should be turned off conditionally
private boolean raisePropertyChangeEvent = true;
protected boolean getRaisePropertyChangeEvent() {
return this.raisePropertyChangeEvent;
}
protected void setRaisePropertyChangeEvent(final boolean value) {
this.raisePropertyChangeEvent = value;
}
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent ) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}
}
// Class ProductBaseImpl
public abstract class ProductBaseImpl extends PropChangeImpl {
private static HandlerRegistration productChangeBeginRegistration;
private static HandlerRegistration productChangeEndRegistration;
protected E instance;
protected ProductBaseImpl(final E instance) {
this.instance = instance;
// Stop updates when a new product loads
if (ProductBaseImpl.productChangeBeginRegistration == null) {
ProductBaseImpl.productChangeBeginRegistration = Core.getEventBus().addHandler(ProductChangeBeginEvent.TYPE, new ProductChangeBeginEventEventHandler() {
#Override
public void onProductChangeBegin(final ProductChangeBeginEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(false);
}
});
}
if (ProductBaseImpl.productChangeEndRegistration == null) {
ProductBaseImpl.productChangeEndRegistration = Core.getEventBus().addHandler(ProductChangeEndEvent.TYPE, new ProductChangeEndEventtHandler() {
#Override
public void onProductChangeEnd(final ProductChangeEndEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(true);
}
});
}
}
}
// Class ProductSubObj1
public class ProductSubObj1 extends ProductBaseImpl {
public ProductSubObj1 (final E instance) {
super(instance);
// some other operations
}
}
// similar to above, I have classes ProductSubObj1, ProductSubObj2 ...
// Class ProductProvider, that fetches the product from service to UI
public class ProductProvider {
// some properties and members
public void fetchProduct(String productId) {
// Let listeners know the product is about to change
Core.getEventBus().fireEvent(new ProductChangeBeginEvent(productId));
// Call the service to get the product in Json data
// After processing the data to be available for the UI (and scheduleDeferred)
Core.getEventBus().fireEvent(new ProductChangeEndEvent(productId));
}
}
As commented inline in the code, the control always goes within the
if (this.raiseDataChangeEvent)
block which I don't want to happen when the product is loaded for the first time.
Could you please advise what am I doing wrong?
Thanks.
Can you just do this:?
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent && oldValue != null /*Or whatever your default unloaded value is*/) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}

Categories

Resources