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.ButtonGroup;
037: import javax.swing.JToggleButton;
038:
039: import com.jgoodies.binding.BindingUtils;
040: import com.jgoodies.binding.value.ValueModel;
041:
042: /**
043: * Converts ValueModels to the ToggleButtonModel interface. Useful to bind
044: * JRadioButtons and JRadioButtonMenuItems to a ValueModel.<p>
045: *
046: * This adapter holds a <em>choice</em> object that is used to determine
047: * the selection state if the underlying subject ValueModel changes its value.
048: * This model is selected if the subject's value equals the choice object.
049: * And if the selection is set, the choice object is set to the subject.<p>
050: *
051: * <strong>Note:</strong> You must not use a ButtonGroup with this adapter.
052: * The RadioButtonAdapter ensures that only one choice is selected by sharing
053: * a single subject ValueModel - at least if all choice values differ.
054: * See also the example below.<p>
055: *
056: * <strong>Example:</strong><pre>
057: * // Recommended binding style using a factory
058: * PresentationModel presentationModel = new PresentationModel(printerSettings);
059: * ValueModel orientationModel =
060: * presentationModel.getModel(PrinterSettings.PROPERTYNAME_ORIENTATION);
061: * JRadioButton landscapeButton = BasicComponentFactory.createRadioButton(
062: * orientationModel, PrinterSettings.LANDSCAPE, "Landscape");
063: * JRadioButton portraitButton = BasicComponentFactory.createRadioButton(
064: * orientationModel, PrinterSettings.PORTRAIT, "Portrait");
065: *
066: * // Binding using the Bindings class
067: * ValueModel orientationModel =
068: * presentationModel.getModel(PrinterSettings.PROPERTYNAME_ORIENTATION);
069: * JRadioButton landscapeButton = new JRadioButton("Landscape");
070: * Bindings.bind(landscapeButton, orientationModel, "landscape");
071: *
072: * JRadioButton portraitButton = new JRadioButton("Portrait");
073: * Bindings.bind(portraitButton, orientationModel, "portrait");
074: *
075: * // Hand-made style
076: * ValueModel orientationModel =
077: * presentationModel.getModel(PrinterSettings.PROPERTYNAME_ORIENTATION);
078: * JRadioButton landscapeButton = new JRadioButton("Landscape");
079: * landscapeButton.setModel(new RadioButtonAdapter(model, "landscape");
080: *
081: * JRadioButton portraitButton = new JRadioButton("Portrait");
082: * portraitButton.setModel(new RadioButtonAdapter(model, "portrait");
083: * </pre>
084: *
085: * @author Karsten Lentzsch
086: * @version $Revision: 1.5 $
087: *
088: * @see javax.swing.ButtonModel
089: * @see javax.swing.JRadioButton
090: * @see javax.swing.JRadioButtonMenuItem
091: */
092:
093: public final class RadioButtonAdapter extends
094: JToggleButton.ToggleButtonModel {
095:
096: /**
097: * Refers to the underlying ValueModel that stores the state.
098: */
099: private final ValueModel subject;
100:
101: /**
102: * Holds the object that is compared with the subject's value
103: * to determine whether this adapter is selected or not.
104: */
105: private final Object choice;
106:
107: // Instance Creation ****************************************************
108:
109: /**
110: * Constructs a RadioButtonAdapter on the given subject ValueModel
111: * for the specified choice.
112: * The created adapter will be selected if and only if the
113: * subject's initial value equals the given <code>choice</code>.
114: *
115: * @param subject the subject that holds the value
116: * @param choice the choice that indicates that this adapter is selected
117: *
118: * @throws NullPointerException if the subject is <code>null</code>
119: */
120: public RadioButtonAdapter(ValueModel subject, Object choice) {
121: if (subject == null)
122: throw new NullPointerException(
123: "The subject must not be null.");
124: this .subject = subject;
125: this .choice = choice;
126: subject.addValueChangeListener(new SubjectValueChangeHandler());
127: updateSelectedState();
128: }
129:
130: // ToggleButtonModel Implementation ***********************************
131:
132: /**
133: * First, the subject value is set to this adapter's choice value if
134: * the argument is <code>true</code>. Second, this adapter's state is set
135: * to the then current subject value. The latter ensures that the selection
136: * state is synchronized with the subject - even if the subject rejects
137: * the change.<p>
138: *
139: * Does nothing if the boolean argument is <code>false</code>,
140: * or if this adapter is already selected.
141: *
142: * @param b <code>true</code> sets the choice value as subject value,
143: * and is intended to select this adapter (although it may not happen);
144: * <code>false</code> does nothing
145: */
146: @Override
147: public void setSelected(boolean b) {
148: if (!b || isSelected())
149: return;
150: subject.setValue(choice);
151: updateSelectedState();
152: }
153:
154: // Safety Check ***********************************************************
155:
156: /**
157: * Throws an UnsupportedOperationException if the group
158: * is not <code>null</code>. You need not and must not
159: * use a ButtonGroup with a set of RadioButtonAdapters.
160: * RadioButtonAdapters form a group by sharing the same
161: * subject ValueModel.
162: *
163: * @param group the <code>ButtonGroup</code> that will be rejected
164: *
165: * @throws UnsupportedOperationException if the group is not <code>null</code>.
166: */
167: @Override
168: public void setGroup(ButtonGroup group) {
169: if (group != null)
170: throw new UnsupportedOperationException(
171: "You need not and must not use a ButtonGroup "
172: + "with a set of RadioButtonAdapters. These form "
173: + "a group by sharing the same subject ValueModel.");
174: }
175:
176: /**
177: * Updates this adapter's selected state to reflect
178: * whether the subject holds the selected value or not.
179: * Does not modify the subject value.
180: */
181: private void updateSelectedState() {
182: boolean subjectHoldsChoiceValue = BindingUtils.equals(choice,
183: subject.getValue());
184: super .setSelected(subjectHoldsChoiceValue);
185: }
186:
187: // Event Handling *********************************************************
188:
189: /**
190: * Handles changes in the subject's value.
191: */
192: private final class SubjectValueChangeHandler implements
193: PropertyChangeListener {
194:
195: /**
196: * The subject value has changed. Updates this adapter's selected
197: * state to reflect whether the subject holds the choice value or not.
198: *
199: * @param evt the property change event fired by the subject
200: */
201: public void propertyChange(PropertyChangeEvent evt) {
202: updateSelectedState();
203: }
204:
205: }
206:
207: }
|