001: /*
002: * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.binding.adapter;
032:
033: import java.beans.PropertyChangeEvent;
034: import java.beans.PropertyChangeListener;
035:
036: import javax.swing.JToggleButton;
037:
038: import com.jgoodies.binding.BindingUtils;
039: import com.jgoodies.binding.value.ValueModel;
040:
041: /**
042: * Converts ValueModels to the ToggleButtonModel interface. Useful to bind
043: * JToggleButton, JCheckBox and JCheckBoxMenuItem to a ValueModel.<p>
044: *
045: * This adapter holds two values that represent the selected and the deselected
046: * state. These are used to determine the selection state if the underlying
047: * subject ValueModel changes its value. If the selection is set, the
048: * corresponding representant is written to the underlying ValueModel.<p>
049: *
050: * <strong>Constraints:</strong> The subject ValueModel must allow
051: * read-access to its value. Also, it is strongly recommended (though not
052: * required) that the underlying ValueModel provides only two values, for
053: * example Boolean.TRUE and Boolean.FALSE. This is so because the toggle button
054: * component may behave "strangely" when it is used with ValueModels that
055: * provide more than two elements.<p>
056: *
057: * <strong>Examples:</strong><pre>
058: * // Recommended binding style using a factory
059: * ValueModel model = presentationModel.getModel(MyBean.PROPERTYNAME_VISIBLE);
060: * JCheckBox visibleBox = BasicComponentFactory.createCheckBox(model, "Visible");
061: *
062: * // Binding using the Bindings class
063: * ValueModel model = presentationModel.getModel(MyBean.PROPERTYNAME_VISIBLE);
064: * JCheckBox visibleBox = new JCheckBox("Visible");
065: * Bindings.bind(visibleBox, model);
066: *
067: * // Hand-made binding
068: * ValueModel model = presentationModel.getModel(MyBean.PROPERTYNAME_VISIBLE);
069: * JCheckBox visibleBox = new JCheckBox("Visible");
070: * visibleBox.setModel(new ToggleButtonAdapter(model));
071: * </pre>
072: *
073: * @author Karsten Lentzsch
074: * @version $Revision: 1.6 $
075: *
076: * @see javax.swing.ButtonModel
077: * @see javax.swing.JCheckBox
078: * @see javax.swing.JCheckBoxMenuItem
079: */
080: public final class ToggleButtonAdapter extends
081: JToggleButton.ToggleButtonModel {
082:
083: /**
084: * Refers to the underlying ValueModel that is used to read and write values.
085: */
086: private final ValueModel subject;
087:
088: /**
089: * The value that represents the selected state.
090: */
091: private final Object selectedValue;
092:
093: /**
094: * The value that represents the deselected state.
095: */
096: private final Object deselectedValue;
097:
098: // Instance Creation *****************************************************
099:
100: /**
101: * Constructs a ToggleButtonAdapter on the given subject ValueModel.
102: * The created adapter will be selected if and only if the
103: * subject's initial value is <code>Boolean.TRUE</code>.
104: *
105: * @param subject the subject that holds the value
106: * @throws NullPointerException if the subject is <code>null</code>.
107: */
108: public ToggleButtonAdapter(ValueModel subject) {
109: this (subject, Boolean.TRUE, Boolean.FALSE);
110: }
111:
112: /**
113: * Constructs a ToggleButtonAdapter on the given subject ValueModel
114: * using the specified values for the selected and deselected state.
115: * The created adapter will be selected if and only if the
116: * subject's initial value equals the given <code>selectedValue</code>.
117: *
118: * @param subject the subject that holds the value
119: * @param selectedValue the value that will be set if this is selected
120: * @param deselectedValue the value that will be set if this is deselected
121: *
122: * @throws NullPointerException if the subject is <code>null</code>.
123: * @throws IllegalArgumentException if the selected and deselected values
124: * are equal
125: */
126: public ToggleButtonAdapter(ValueModel subject,
127: Object selectedValue, Object deselectedValue) {
128: if (subject == null)
129: throw new NullPointerException(
130: "The subject must not be null.");
131: if (BindingUtils.equals(selectedValue, deselectedValue))
132: throw new IllegalArgumentException(
133: "The selected value must not equal the deselected value.");
134:
135: this .subject = subject;
136: this .selectedValue = selectedValue;
137: this .deselectedValue = deselectedValue;
138: subject.addValueChangeListener(new SubjectValueChangeHandler());
139: updateSelectedState();
140: }
141:
142: // ToggleButtonModel Implementation ***********************************
143:
144: /**
145: * First, the subject value is set to this adapter's selected value if
146: * the argument is <code>true</code>, to the deselected value otherwise.
147: * Second, this adapter's state is set to the then current subject value.
148: * This ensures that the selected state is synchronized with the subject
149: * - even if the subject rejects the change.
150: *
151: * @param b <code>true</code> sets the selected value as subject value,
152: * <code>false</code> sets the deselected value as subject value
153: */
154: @Override
155: public void setSelected(boolean b) {
156: subject.setValue(b ? selectedValue : deselectedValue);
157: updateSelectedState();
158: }
159:
160: /**
161: * Updates this adapter's selected state to reflect
162: * whether the subject holds the selected value or not.
163: * Does not modify the subject value.
164: */
165: private void updateSelectedState() {
166: boolean subjectHoldsChoiceValue = BindingUtils.equals(
167: selectedValue, subject.getValue());
168: super .setSelected(subjectHoldsChoiceValue);
169: }
170:
171: // Event Handling *********************************************************
172:
173: /**
174: * Handles changes in the subject's value.
175: */
176: private final class SubjectValueChangeHandler implements
177: PropertyChangeListener {
178:
179: /**
180: * The subject value has changed. Updates this adapter's selected
181: * state to reflect whether the subject holds the selected value or not.
182: *
183: * @param evt the property change event fired by the subject
184: */
185: public void propertyChange(PropertyChangeEvent evt) {
186: updateSelectedState();
187: }
188:
189: }
190:
191: }
|