1 /*
2 * Copyright 2002-2004 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16 package org.springframework.richclient.command.config;
17
18 import javax.swing.AbstractButton;
19 import javax.swing.JMenu;
20 import javax.swing.JMenuItem;
21 import javax.swing.KeyStroke;
22
23 import org.springframework.core.style.ToStringCreator;
24 import org.springframework.richclient.core.LabelInfo;
25 import org.springframework.richclient.factory.ButtonConfigurer;
26 import org.springframework.richclient.util.Assert;
27 import org.springframework.util.ObjectUtils;
28 import org.springframework.util.StringUtils;
29
30
31 /**
32 * An immutable parameter object consisting of the text, mnemonic character, mnemonic character
33 * index and keystroke accelerator that may be associated with a labeled command button.
34 *
35 * <p>
36 * This class also acts as a factory for creating instances of itself based on a string descriptor.
37 * The syntax used for this descriptor is described in the javadoc for the {@link #valueOf(String)}
38 * method.
39 *
40 *
41 * @author Keith Donald
42 * @author Kevin Stembridge
43 *
44 * @see LabelInfo
45 * @see KeyStroke
46 */
47 public final class CommandButtonLabelInfo implements ButtonConfigurer {
48
49 /** A default instance to be used for command buttons with no label information. */
50 public static final CommandButtonLabelInfo BLANK_BUTTON_LABEL = new CommandButtonLabelInfo("");
51
52 private final LabelInfo labelInfo;
53
54 private final KeyStroke accelerator;
55
56 /**
57 * Return an instance of this class, created by parsing the information in the given label
58 * descriptor string. The expected format of this descriptor is the same as that used by
59 * the {@link LabelInfo} class, with the following additions:
60 *
61 * <ul>
62 * <li>The @ symbol is an escapable character.</li>
63 * <li>Everything after the first unescaped @ symbol will be treated as the textual representation
64 * of the keystroke accelerator.</li>
65 * </ul>
66 *
67 * The expected format of the keystroke accelerator string is as described in the javadocs for the
68 * {@link KeyStroke#getKeyStroke(String)} method.
69 *
70 *
71 * @param labelDescriptor The label descriptor. May be null or empty, in which case, a default
72 * blank label info will be returned.
73 * @return A CommandButtonLabelInfo instance, never null.
74 *
75 * @throws IllegalArgumentException if {@code labelDescriptor} contains invalid syntax.
76 *
77 * @see LabelInfo
78 * @see KeyStroke
79 */
80 public static CommandButtonLabelInfo valueOf(String labelDescriptor) {
81
82 if (!StringUtils.hasText(labelDescriptor)) {
83 return BLANK_BUTTON_LABEL;
84 }
85
86 StringBuffer labelInfoBuffer = new StringBuffer();
87 char currentChar;
88 KeyStroke keyStroke = null;
89
90 for (int i = 0, textCharsIndex = 0; i < labelDescriptor.length(); i++, textCharsIndex++) {
91 currentChar = labelDescriptor.charAt(i);
92
93 if (currentChar == '\\') {
94 int nextCharIndex = i + 1;
95 //if this backslash is escaping an @ symbol, we remove the backslash and
96 //continue with the next char
97 if (nextCharIndex < labelDescriptor.length()) {
98 char nextChar = labelDescriptor.charAt(nextCharIndex);
99
100 if (nextChar == '@') {
101 labelInfoBuffer.append(nextChar);
102 i++;
103 } else {
104 labelInfoBuffer.append(currentChar);
105 labelInfoBuffer.append(nextChar);
106 i++;
107 }
108
109 }
110
111 }
112 else if (currentChar == '@') {
113 //we've found the accelerator indicator
114
115 if (i + 1 == labelDescriptor.length()) {
116 throw new IllegalArgumentException(
117 "The label descriptor ["
118 + labelDescriptor
119 + "] does not specify a valid accelerator after the last "
120 + "non-espaced @ symbol.");
121 }
122
123 String acceleratorStr = labelDescriptor.substring(i + 1);
124 keyStroke = KeyStroke.getKeyStroke(acceleratorStr);
125
126 if (keyStroke == null) {
127 throw new IllegalArgumentException(
128 "The keystroke accelerator string ["
129 + acceleratorStr
130 + "] from the label descriptor ["
131 + labelDescriptor
132 + "] is not a valid keystroke format.");
133 }
134
135 break;
136
137 }
138 else {
139 labelInfoBuffer.append(currentChar);
140 }
141
142 }
143
144 LabelInfo info = LabelInfo.valueOf(labelInfoBuffer.toString());
145
146 return new CommandButtonLabelInfo(info, keyStroke);
147
148 }
149
150 /**
151 * Creates a new {@code CommandButtonLabelInfo} that will display the given text on its label.
152 * There will be no associated mnemonic character and no keystroke accelerator.
153 *
154 * @param text The label text to be displayed. Must not be null.
155 *
156 * @throws IllegalArgumentException if {@code text} is null.
157 */
158 public CommandButtonLabelInfo(String text) {
159 this(new LabelInfo(text), null);
160 }
161
162 /**
163 * Creates a new {@code CommandButtonLabelInfo} with the given label information and keystroke
164 * accelerator.
165 *
166 * @param labelInfo The label information. Must not be null.
167 * @param accelerator The keystroke accelerator. May be null.
168 *
169 * @throws IllegalArgumentException if {@code labelInfo} is null.
170 */
171 public CommandButtonLabelInfo(LabelInfo labelInfo, KeyStroke accelerator) {
172 Assert.required(labelInfo, "labelInfo");
173 this.labelInfo = labelInfo;
174 this.accelerator = accelerator;
175 }
176
177 /**
178 * Returns the displayable text.
179 *
180 * @return The label text. Maybe an empty string but never null.
181 */
182 public String getText() {
183 return this.labelInfo.getText();
184 }
185
186 /**
187 * Returns the mnemonic for the label.
188 *
189 * @return The mnemonic for the label.
190 */
191 public int getMnemonic() {
192 return this.labelInfo.getMnemonic();
193 }
194
195 /**
196 * Returns the zero-based index for the mnemonic character within the label text.
197 *
198 * @return The mnemonic index or -1 to indicate that there is no associated mnemonic character.
199 */
200 public int getMnemonicIndex() {
201 return this.labelInfo.getMnemonicIndex();
202 }
203
204 /**
205 * Returns the keystroke accelerator for the label.
206 *
207 * @return The keystroke accelerator, or null.
208 */
209 public KeyStroke getAccelerator() {
210 return this.accelerator;
211 }
212
213 /**
214 * {@inheritDoc}
215 */
216 public int hashCode() {
217 return this.labelInfo.hashCode() + (this.accelerator != null ? this.accelerator.hashCode() : 0);
218 }
219
220 /**
221 * {@inheritDoc}
222 */
223 public boolean equals(Object obj) {
224
225 if (obj == this) {
226 return true;
227 }
228
229 if (!(obj instanceof CommandButtonLabelInfo)) {
230 return false;
231 }
232
233 CommandButtonLabelInfo other = (CommandButtonLabelInfo)obj;
234
235 if (!this.labelInfo.equals(other.labelInfo)) {
236 return false;
237 }
238
239 if (!ObjectUtils.nullSafeEquals(this.accelerator, other.accelerator)) {
240 return false;
241 }
242
243 return true;
244
245 }
246
247 /**
248 * Configures an existing button appropriately based on this label info's
249 * properties.
250 *
251 * @param button
252 */
253 public AbstractButton configure(AbstractButton button) {
254 Assert.notNull(button);
255 button.setText(this.labelInfo.getText());
256 button.setMnemonic(this.labelInfo.getMnemonic());
257 button.setDisplayedMnemonicIndex(this.labelInfo.getMnemonicIndex());
258 configureAccelerator(button, getAccelerator());
259 return button;
260 }
261
262 /**
263 * Sets the given keystroke accelerator on the given button.
264 *
265 * @param button The button. May be null.
266 * @param keystrokeAccelerator The accelerator. May be null.
267 */
268 protected void configureAccelerator(AbstractButton button, KeyStroke keystrokeAccelerator) {
269 if ((button instanceof JMenuItem) && !(button instanceof JMenu)) {
270 ((JMenuItem)button).setAccelerator(keystrokeAccelerator);
271 }
272 }
273
274 /**
275 * {@inheritDoc}
276 */
277 public String toString() {
278 return new ToStringCreator(this)
279 .append("labelInfo", this.labelInfo)
280 .append("accelerator", this.accelerator)
281 .toString();
282 }
283
284 }