RatingPropertyEditorUI Example

package com.escenic.studio.plugin;

import com.escenic.domain.PropertyDescriptor;
import com.escenic.module.Module;
import com.escenic.studio.binding.AbstractPropertyBinding;
import com.escenic.studio.editors.PropertyEditorUI;
import com.escenic.studio.editors.ResourceRecorder;
import com.escenic.swing.DesaturatedIcon;
import com.escenic.swing.IterableButtonGroup;
import com.escenic.swing.SwingHelper;
import com.escenic.swing.binding.BindingEvent;
import com.escenic.swing.binding.BindingListener;
import com.escenic.swing.binding.BindingState;

import com.jgoodies.forms.factories.Borders;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.math.NumberUtils;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;

import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JToggleButton;

import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class RatingPropertyEditorUI implements PropertyEditorUI {

  private static final int DEFAULT_VALUE = 10;

  private final AbstractPropertyBinding mBinding;
  private final List<RatingModel> mRatingModels = new ArrayList<RatingModel>();
  private final IterableButtonGroup mButtonGroup = new IterableButtonGroup();
  private final Icon mUnselectedIcon;
  private final Icon mSelectedIcon;
  private final JPanel mRootPanel;
  private boolean mIgnoreChangeEvents;
  private final ResourceRecorder mResourceRecorder;

  public RatingPropertyEditorUI(final AbstractPropertyBinding pBinding, 
                                      ResourceRecorder pResourceRecorder) {
    Validate.notNull(pBinding, "Binding may not be null");
    Validate.notNull(pResourceRecorder, "Resource recorder may not be null");
    mResourceRecorder = pResourceRecorder;
    mRootPanel = new JPanel();
    mBinding = pBinding;
    ResourceMap resourceMap = Application.getInstance().getContext().getResourceMap(getClass());
    mRootPanel.setName(getClass().getName());
    mSelectedIcon = resourceMap.getIcon("selected-icon");
    mUnselectedIcon = new DesaturatedIcon(mSelectedIcon);
    SwingHelper.initialize(this);
  }

  public PropertyDescriptor getPropertyDescriptor() {
    return mBinding.getPropertyDescriptor();
  }

  public AbstractPropertyBinding getBinding() {
    return mBinding;
  }

  public void dispose() {
    List<BindingListener> listeners = new ArrayList<BindingListener>(mBinding.getBindingListeners());
    for (BindingListener listener : listeners) {
      mBinding.removeBindingListener(listener);
    }
    mResourceRecorder.disposeAll();
  }

  public void initModels() {
    Module ratingEditor = getPropertyDescriptor().getModule(RatingPropertyEditorSpi.PLUGIN_URI, 
                                                            RatingPropertyEditorSpi.RATING_EDITOR, 
                                                            RatingPropertyEditorSpi.ENABLED_ATTRIBUTE, 
                                                            RatingPropertyEditorSpi.ENABLED_ATTRIBUTE_VALUE);
    Module amountModule = ratingEditor.getModule(RatingPropertyEditorSpi.PLUGIN_URI, "amount");
    int amount = NumberUtils.toInt(amountModule.getContent(), DEFAULT_VALUE);
    if (amount > DEFAULT_VALUE) {
      amount = DEFAULT_VALUE;
    }
    for (int i = 1; i < amount + 1; i++) {
      mRatingModels.add(new RatingModel(i));
    }
  }

  public void initCommands() {
  }

  public void initComponents() {
    for (RatingModel ratingModel : mRatingModels) {
      JButton button = new JButton();
      button.setBorder(Borders.EMPTY_BORDER);
      button.setContentAreaFilled(false);
      button.setFocusable(false);
      button.setOpaque(false);
      button.setIcon(mUnselectedIcon);
      button.setDisabledIcon(mUnselectedIcon);
      button.setModel(ratingModel);
      mButtonGroup.add(button);
    }
  }

  public void initListeners() {
    mBinding.addPropertyChangeListener(
        "value",
        new PropertyChangeListener() {
          public void propertyChange(final PropertyChangeEvent pEvent) {
            setValueFromBinding();
          }
        }
    );
    mBinding.addPropertyChangeListener("readOnly", new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent pEvt) {
        setComponentState();
      }
    });

    mBinding.addBindingListener(new BindingListener<Object>() {
      public void bindingUpdated(BindingEvent<Object> pEvent) {
        setComponentState();
      }
    });
    mButtonGroup.addPropertyChangeListener(IterableButtonGroup.SELECTED_PROPERTY, 
                                           new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent pEvent) {
        if (!mIgnoreChangeEvents) {
          updateBindingValue((RatingModel) pEvent.getNewValue());
        }
      }
    });
  }

  public void initLayout() {
    mRootPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    for (AbstractButton button : mButtonGroup) {
      mRootPanel.add(button);
    }
  }

  public void initState() {
    setValueFromBinding();
    setComponentState();
  }

  public JComponent getRootComponent() {
    return mRootPanel;
  }

  private void updateBindingValue(final RatingModel pValue) {
    mIgnoreChangeEvents = true;
    mButtonGroup.setSelected(pValue, true);
    mBinding.setValue(pValue == null ? null : new BigDecimal(pValue.getValue()));
    updateSelectedIcon(pValue);
    mIgnoreChangeEvents = false;
  }

  private void setValueFromBinding() {
    if (!mIgnoreChangeEvents) {
      BigDecimal value = (BigDecimal) mBinding.getValue();
      if (value != null) {
        for (RatingModel ratingModel : mRatingModels) {
          if (ratingModel.getValue() == value.intValueExact()) {
            mButtonGroup.setSelected(ratingModel, true);
            updateSelectedIcon(ratingModel);
            break;
          }
        }
      }
      else {
        updateSelectedIcon(null);
      }
    }
  }

  private void updateSelectedIcon(final RatingModel pRatingModel) {
    long value = pRatingModel == null ? -1 : pRatingModel.getValue();
    for (int i = 0; i < mButtonGroup.getButtonCount(); i++) {
      Icon icon = mUnselectedIcon;
      AbstractButton button = mButtonGroup.get(i);
      if (i <= value - 1) {
        icon = mSelectedIcon;
      }
      button.setIcon(icon);
    }
  }

  private void setComponentState() {
    if (mBinding.getState() == BindingState.UNBOUND) {
      for (AbstractButton button : mButtonGroup) {
        button.setEnabled(false);
        button.setSelected(false);
      }
    }
    for (AbstractButton button : mButtonGroup) {
      button.setEnabled(!mBinding.isReadOnly());
    }
  }


  private class RatingModel extends JToggleButton.ToggleButtonModel { 
    private final int mValue;

    public RatingModel(final int pValue) {
      mValue = pValue;
      setRollover(false);
    }

    public int getValue() {
      return mValue;
    }
  }
}