001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.command.config;
017
018 import javax.swing.AbstractButton;
019 import javax.swing.JMenu;
020 import javax.swing.JMenuItem;
021 import javax.swing.KeyStroke;
022
023 import org.springframework.core.style.ToStringCreator;
024 import org.springframework.richclient.core.LabelInfo;
025 import org.springframework.richclient.factory.ButtonConfigurer;
026 import org.springframework.richclient.util.Assert;
027 import org.springframework.util.ObjectUtils;
028 import org.springframework.util.StringUtils;
029
030
031 /**
032 * An immutable parameter object consisting of the text, mnemonic character, mnemonic character
033 * index and keystroke accelerator that may be associated with a labeled command button.
034 *
035 * <p>
036 * This class also acts as a factory for creating instances of itself based on a string descriptor.
037 * The syntax used for this descriptor is described in the javadoc for the {@link #valueOf(String)}
038 * method.
039 *
040 *
041 * @author Keith Donald
042 * @author Kevin Stembridge
043 *
044 * @see LabelInfo
045 * @see KeyStroke
046 */
047 public final class CommandButtonLabelInfo implements ButtonConfigurer {
048
049 /** A default instance to be used for command buttons with no label information. */
050 public static final CommandButtonLabelInfo BLANK_BUTTON_LABEL = new CommandButtonLabelInfo("");
051
052 private final LabelInfo labelInfo;
053
054 private final KeyStroke accelerator;
055
056 /**
057 * Return an instance of this class, created by parsing the information in the given label
058 * descriptor string. The expected format of this descriptor is the same as that used by
059 * the {@link LabelInfo} class, with the following additions:
060 *
061 * <ul>
062 * <li>The @ symbol is an escapable character.</li>
063 * <li>Everything after the first unescaped @ symbol will be treated as the textual representation
064 * of the keystroke accelerator.</li>
065 * </ul>
066 *
067 * The expected format of the keystroke accelerator string is as described in the javadocs for the
068 * {@link KeyStroke#getKeyStroke(String)} method.
069 *
070 *
071 * @param labelDescriptor The label descriptor. May be null or empty, in which case, a default
072 * blank label info will be returned.
073 * @return A CommandButtonLabelInfo instance, never null.
074 *
075 * @throws IllegalArgumentException if {@code labelDescriptor} contains invalid syntax.
076 *
077 * @see LabelInfo
078 * @see KeyStroke
079 */
080 public static CommandButtonLabelInfo valueOf(String labelDescriptor) {
081
082 if (!StringUtils.hasText(labelDescriptor)) {
083 return BLANK_BUTTON_LABEL;
084 }
085
086 StringBuffer labelInfoBuffer = new StringBuffer();
087 char currentChar;
088 KeyStroke keyStroke = null;
089
090 for (int i = 0, textCharsIndex = 0; i < labelDescriptor.length(); i++, textCharsIndex++) {
091 currentChar = labelDescriptor.charAt(i);
092
093 if (currentChar == '\\') {
094 int nextCharIndex = i + 1;
095 //if this backslash is escaping an @ symbol, we remove the backslash and
096 //continue with the next char
097 if (nextCharIndex < labelDescriptor.length()) {
098 char nextChar = labelDescriptor.charAt(nextCharIndex);
099
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 }