OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 25 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.ui.component;
15
 
16
import org.openconcerto.utils.CollectionUtils;
17
import org.openconcerto.utils.checks.EmptyChangeSupport;
18
import org.openconcerto.utils.checks.EmptyListener;
19
import org.openconcerto.utils.checks.EmptyObj;
20
import org.openconcerto.utils.checks.MutableValueObject;
21
 
22
import java.awt.event.ItemEvent;
23
import java.awt.event.ItemListener;
24
import java.awt.event.MouseListener;
25
import java.beans.PropertyChangeListener;
26
import java.beans.PropertyChangeSupport;
27
import java.util.ArrayList;
28
import java.util.Collection;
29
import java.util.Iterator;
30
import java.util.LinkedHashMap;
31
import java.util.List;
32
import java.util.Map;
33
 
34
import javax.swing.ButtonGroup;
35
import javax.swing.JPanel;
36
import javax.swing.JRadioButton;
37
 
38
/**
39
 * A group of radio buttons (only one can be selected at any time). The buttons are identified by an
40
 * ID and are created when {@link #init()} is called. A special empty button is not shown, it is
41
 * selected when init() finishes.
42
 *
43
 * @author Sylvain CUAZ
44
 * @param <V> type of value
45
 */
46
public class JRadioButtons<V> extends JPanel implements MutableValueObject<V>, EmptyObj {
47
 
48
    public static final class JStringRadioButtons extends JRadioButtons<String> {
49
        public JStringRadioButtons(final Collection<String> choices) {
50
            // keep order even if the passed collection is a set (it might still be ordered)
51
            super(CollectionUtils.fillMap(new LinkedHashMap<String, String>(), choices));
52
        }
53
    }
54
 
55
    private final PropertyChangeSupport supp;
56
    private final EmptyChangeSupport emptySupp;
57
    // {id => JRadioButton}
58
    private final Map<V, JRadioButton> choices;
59
    private final ButtonGroup group;
60
    // listeners to add to each of our buttons
61
    private final List<MouseListener> mouseListeners;
62
 
63
    private V emptyID;
64
    private V value;
65
 
66
    public JRadioButtons() {
67
        this(null);
68
    }
69
 
70
    public JRadioButtons(final Map<V, String> choices) {
71
        super();
72
        this.supp = new PropertyChangeSupport(this);
73
        this.emptySupp = new EmptyChangeSupport(this);
74
        // record the order of the buttons
75
        this.choices = new LinkedHashMap<V, JRadioButton>();
76
        this.group = new ButtonGroup();
77
        this.emptyID = null;
78
        this.mouseListeners = new ArrayList<MouseListener>();
79
        this.value = null;
80
 
81
        this.setOpaque(false);
82
 
83
        if (choices != null)
84
            this.init(null, choices);
85
    }
86
 
87
    /**
88
     * Init this component. If a value of <code>choices</code> is <code>null</code> the ID will be
89
     * used as the label.
90
     *
91
     * @param emptyID the id meaning that no button (on screen) is selected, can be
92
     *        <code>null</code>.
93
     * @param choices the map from id to label of the buttons to display, ids cannot be
94
     *        <code>null</code>.
95
     */
96
    public final void init(final V emptyID, final Map<V, String> choices) {
97
        this.emptyID = emptyID;
98
        for (final Map.Entry<V, String> e : choices.entrySet()) {
99
            final V choice = e.getKey();
100
            if (choice == null)
101
                throw new IllegalArgumentException("A key is null in " + choices);
102
            final String choiceLabel = e.getValue();
103
 
104
            this.addBtn(choiceLabel == null ? choice.toString() : choiceLabel, choice);
105
        }
106
        // pour pouvoir "déselectionner"
107
        this.addBtn("--- ??? ---", this.emptyID);
108
 
109
        // Group the radio buttons.
110
        final Iterator<V> choicesIter = this.choices.keySet().iterator();
111
        while (choicesIter.hasNext()) {
112
            final V id = choicesIter.next();
113
            final JRadioButton btn = this.choices.get(id);
114
            this.group.add(btn);
115
            // maintain the value, otherwise we'd have to create a map from ButtonModel to ID
116
            // since this.group.getSelection() returns a ButtonModel
117
            btn.addItemListener(new ItemListener() {
118
                public void itemStateChanged(ItemEvent e) {
119
                    // ATTN we get 2 events for each change : first DESELECTED then SELECTED
120
                    // MAYBE ignore one but ATTN DESELECTED can be alone with
121
                    // ButtonGroup#clearSelection()
122
                    if (e.getStateChange() == ItemEvent.SELECTED)
123
                        valueChanged(id);
124
                    // DESELECTED
125
                    else if (JRadioButtons.this.value == id)
126
                        valueChanged(null);
127
                }
128
            });
129
            // ne pas mettre l'indéfini dans l'interface
130
            if (id != this.emptyID) {
131
                this.add(btn);
132
            }
133
        }
134
 
135
        // initialise la valeur à indéfini
136
        this.resetValue();
137
    }
138
 
139
    private void valueChanged(V id) {
140
        this.value = id;
141
        this.supp.firePropertyChange("value", null, id);
142
        this.emptySupp.fireEmptyChange(isEmpty());
143
    }
144
 
145
    private final void addBtn(String btnLabel, V id) {
146
        final JRadioButton btn = new JRadioButton(btnLabel);
147
        for (final MouseListener l : this.mouseListeners) {
148
            btn.addMouseListener(l);
149
        }
150
        this.choices.put(id, btn);
151
    }
152
 
153
    public final void setEnabled(boolean b) {
154
        super.setEnabled(b);
155
        for (final JRadioButton btn : this.choices.values()) {
156
            btn.setEnabled(b);
157
        }
158
    }
159
 
160
    // ** valueObject
161
 
162
    /**
163
     * Set the selected button.
164
     *
165
     * @param id the id of the button to select, <code>null</code> means the empty ID.
166
     */
167
    public void setValue(V id) {
168
        // treat unknown value as empty
169
        // MAYBE add a boolean to throw an exception
170
        if (id == null || !this.choices.containsKey(id))
171
            id = this.emptyID;
172
        this.choices.get(id).setSelected(true);
173
    }
174
 
175
    public final V getValue() {
176
        return this.value;
177
    }
178
 
179
    /**
180
     * Selects the empty ID.
181
     */
182
    public final void resetValue() {
183
        this.setValue(null);
184
    }
185
 
186
    public final void addValueListener(PropertyChangeListener l) {
187
        this.supp.addPropertyChangeListener(l);
188
    }
189
 
190
    public final void rmValueListener(PropertyChangeListener l) {
191
        this.supp.removePropertyChangeListener(l);
192
    }
193
 
194
    // ** emptyObj
195
 
196
    public boolean isEmpty() {
197
        return this.getValue() == null || this.getValue().equals(this.emptyID);
198
    }
199
 
200
    public void addEmptyListener(EmptyListener l) {
201
        this.emptySupp.addEmptyListener(l);
202
    }
203
 
204
    // ** mouseListeners
205
 
206
    @Override
207
    public void addMouseListener(MouseListener l) {
208
        this.mouseListeners.add(l);
209
        for (final JRadioButton radio : this.choices.values()) {
210
            radio.addMouseListener(l);
211
        }
212
    }
213
 
214
    @Override
215
    public void removeMouseListener(MouseListener l) {
216
        this.mouseListeners.remove(l);
217
        for (final JRadioButton radio : this.choices.values()) {
218
            radio.removeMouseListener(l);
219
        }
220
    }
221
}