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 }