1 /* 2 * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package test.java.awt.event.helpers.lwcomponents; 25 26 import java.awt.*; 27 import java.awt.event.*; 28 29 /** 30 * Lightweight <i>Button</i> component with some nice features. This 31 * component provides the capabilities of Buttons, namely that you it 32 * displays a label string and, when clicked, causes the 33 * ActionListener method to be called.<p> 34 * 35 * The look of the button is a little unusual. There are three 36 * rectangles drawn at the border that indicate various states 37 * of the button. These are (listed from outside in)<p> 38 * <ol> 39 * <li><b>Focus</b>: Indicates that the LWButton has the focus. 40 * <li><b>Mouse Over</b>: Indicates that the mouse is over the component. 41 * <li><b>Mouse Pressed</b>: Indicates that the mouse has been pressed. 42 * </ol> 43 * 44 * In addition, when the button has been activated (mouse clicked or 45 * via keyboard activation) the button flashes briefly. 46 */ 47 48 public class LWButton extends LWComponent { 49 50 /* 51 * The button's Label. 52 * If Label is not specified it will default to "". 53 * @serial 54 * @see getLabel() 55 * @see setLabel() 56 */ 57 private String label; 58 private boolean isInClick = false; 59 60 private static final String base = "LWButton"; 61 private static int nameCounter = 0; 62 63 private transient ActionListener actionListener; 64 65 /* 66 * The action to be performaed once a button has been 67 * pressed. 68 * actionCommand can be null. 69 * @serial 70 * @see getActionCommand() 71 * @see setActionCommand() 72 */ 73 String actionCommand; 74 75 Color colMousePressed; 76 77 public LWButton() { this(""); } 78 79 public LWButton(String label) { 80 this(label, Color.red, Color.green, Color.white); 81 } 82 83 /** 84 * Initialize the LWButton, fully specifying all parameters. 85 * @param label The string to display. 86 * @param fgnd The color to draw the label in. 87 * @param bkgnd The color of the button itself. 88 * @param mousePressed The Color of the MousePressed rectangle. 89 */ 90 public LWButton(String label, Color fgnd, Color bkgnd, Color mousePressed) { 91 super(); 92 this.label = label; 93 setBackground(fgnd); 94 setForeground(bkgnd); 95 colMousePressed = mousePressed; 96 setName(makeComponentName()); 97 98 enableEvents( AWTEvent.MOUSE_EVENT_MASK 99 | AWTEvent.KEY_EVENT_MASK 100 | AWTEvent.ACTION_EVENT_MASK); 101 setEnabled(true); 102 } 103 104 /** 105 * Make the component flash briefly. 106 */ 107 public void flash() { 108 isInClick = true; 109 repaint(); 110 111 class unClicker implements Runnable { 112 @Override 113 public void run() { 114 try { Thread.sleep(100); } catch (InterruptedException ee) {} 115 isInClick = false; 116 repaint(); 117 } 118 } 119 try { 120 unClicker uc = new unClicker(); 121 new Thread(uc).start(); 122 } catch (Exception e) { 123 // In case we're in an applet and the security has not been 124 // turned off (in which case we can't start a new thread) 125 // we can catch that and set the flag back to how it should be. 126 isInClick = false; 127 repaint(); 128 } 129 } 130 131 /** 132 * Set the MousePressed color (the color shown in the MousePressed rectangle 133 * when the mouse is over the component). 134 * @param c The color of the MousePressed rectangle. 135 */ 136 public void setMousePressedColor(Color c) { colMousePressed = c; } 137 138 /** 139 * Get the MousePressed color. 140 * @return The color of the MousePressed rectangle. 141 */ 142 public Color getMousePressedColor() { return colMousePressed; } 143 144 /** 145 * Used to dispatch out the ActionEvent for a corresponding InputEvent. 146 * @param e The InputEvent that is causing the ActionEvent dispatch. 147 */ 148 private void sendActionEvent(InputEvent e) { 149 150 int modifiers = e.getModifiers(); 151 int aModifiers = 0; 152 153 if ((modifiers & MouseEvent.SHIFT_MASK) != 0) { 154 aModifiers |= ActionEvent.SHIFT_MASK; 155 } 156 if ((modifiers & MouseEvent.CTRL_MASK) != 0) { 157 aModifiers |= ActionEvent.CTRL_MASK; 158 } 159 if ((modifiers & MouseEvent.META_MASK) != 0) { 160 aModifiers |= ActionEvent.META_MASK; 161 } 162 if ((modifiers & MouseEvent.ALT_MASK) != 0) { 163 aModifiers |= ActionEvent.ALT_MASK; 164 } 165 166 ActionEvent ae = new ActionEvent(this, 167 ActionEvent.ACTION_PERFORMED, 168 actionCommand, 169 aModifiers); 170 // XXX: What's the right way to send out the ActionEvent? 171 // My assumption was to put it into the system event queue 172 // and the it will be dispatched back into <i>processEvent</i> 173 // for us. However this doesn't happen...? 174 if (actionListener != null) { 175 actionListener.actionPerformed(ae); 176 } 177 //Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(ae); 178 } 179 180 /** 181 * Set whether the component is enabled ({@code true}) or not. 182 * @param enabled If {@code true}, the component is to be enabled. 183 */ 184 @Override 185 public void setEnabled(boolean enabled) { 186 super.setEnabled(enabled); 187 188 if (enabled) { 189 enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); 190 } else { 191 disableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); 192 } 193 repaint(1); 194 } 195 196 /** 197 * Indicates that LWButton component can receive focus. 198 * @return {@code true} if the LWButton component can receive focus 199 */ 200 @Override 201 public boolean isFocusTraversable() { return true; } 202 203 /** 204 * Construct a name for this component. Called by getName() when the 205 * name is null. 206 */ 207 String makeComponentName() { 208 synchronized (getClass()) { 209 return base + nameCounter++; 210 } 211 } 212 213 /** 214 * Handle painting the enabled version of the component. 215 * 216 * ASSUMES: g.color may be changed 217 */ 218 @Override 219 public void paint(Graphics g) { 220 221 super.paint(g); 222 restrictGraphicsToClientArea(g); 223 224 Dimension dim = getClientSize(); 225 226 int s = Math.min(dim.width - 1, dim.height - 1); 227 228 if (isInClick) { 229 g.setColor(Color.white); 230 } else { 231 g.setColor(getBackground()); 232 } 233 234 // In jdk 1.2 (pre-release) there was a bug using clearRect 235 // to paint the background of a lightweight. 236 //g.clearRect(loc.x, loc.y, dim.width, dim.height); 237 g.fillRect(0, 0, dim.width, dim.height); 238 239 if (mouseB1Pressed) { 240 g.setColor(colMousePressed); 241 //LWComponent.traceMsg("paint mousePressed " + this.toString()); 242 g.drawRect(1, 1, dim.width - 3, dim.height - 3); 243 } 244 245 Font f = getFont(); 246 if (f != null) { 247 FontMetrics fm = getFontMetrics(f); 248 g.setColor(getForeground()); 249 g.drawString(label, 250 s/2 - fm.stringWidth(label)/2, 251 s/2 + fm.getMaxDescent()); 252 } 253 254 unrestrictGraphicsFromClientArea(g); 255 } 256 257 @Override 258 public Dimension getPreferredSize() { 259 Font f = getFont(); 260 if (f != null) { 261 FontMetrics fm = getFontMetrics(f); 262 int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40); 263 return new Dimension(max, max); 264 } else { 265 return new Dimension(100, 100); 266 } 267 } 268 269 @Override 270 public Dimension getMinimumSize() { 271 return getPreferredSize(); 272 } 273 274 /** 275 * Get the text displayed in the LWButton. 276 * @return the text displayed in the LWButton 277 */ 278 public String getText() { return label; } 279 280 /** 281 * Set the text displayed in the LWButton. 282 * @param s The text to be displayed. 283 */ 284 public void setText(String s) { 285 Font f = getFont(); 286 int oWidth = 0; 287 int oHeight = 0; 288 int nWidth = 0; 289 int nHeight = 0; 290 int invalidated = 0; 291 FontMetrics fm = null; 292 293 if (f != null) { 294 fm = getFontMetrics(f); 295 oWidth = fm.stringWidth(label); 296 oHeight = fm.getHeight(); 297 } 298 299 this.label = s; 300 301 if (f != null) { 302 nWidth = fm.stringWidth(label); 303 nHeight = fm.getHeight(); 304 305 if ((nWidth > oWidth) || (nHeight > oHeight)) { 306 invalidate(); 307 invalidated = 1; 308 } 309 } 310 311 if (invalidated == 0) { 312 repaint(); 313 } 314 } 315 316 /** 317 * Set the command name for the action event fired 318 * by this button. By default this action command is 319 * set to match the label of the button. 320 * @param command A string used to set the button's 321 * action command. 322 * If the string is <code>null</code> then the action command 323 * is set to match the label of the button. 324 * @see java.awt.event.ActionEvent 325 * @since JDK1.1 326 */ 327 public void setActionCommand(String command) { 328 actionCommand = command; 329 } 330 331 /** 332 * Returns the command name of the action event fired by this button. 333 * If the command name is {@code null} (default) then this method 334 * returns the label of the button. 335 * 336 * @return the command name of the action event fired by this button 337 * or the label of the button (in case of {@code null}) 338 */ 339 public String getActionCommand() { 340 return (actionCommand == null? label : actionCommand); 341 } 342 343 /** 344 * Add the specified action listener to receive action events from 345 * this button. Action events occur when a user presses or releases 346 * the mouse over this button. 347 * @param l the action listener. 348 * @see java.awt.event.ActionListener 349 * @see #removeActionListener 350 * @since JDK1.1 351 */ 352 public synchronized void addActionListener(ActionListener l) { 353 actionListener = AWTEventMulticaster.add(actionListener, l); 354 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 355 } 356 357 /** 358 * Remove the specified action listener so that it no longer 359 * receives action events from this button. Action events occur 360 * when a user presses or releases the mouse over this button. 361 * @param l the action listener. 362 * @see java.awt.event.ActionListener 363 * @see #addActionListener 364 * @since JDK1.1 365 */ 366 public synchronized void removeActionListener(ActionListener l) { 367 actionListener = AWTEventMulticaster.remove(actionListener, l); 368 } 369 370 @Override 371 protected void processKeyEvent(KeyEvent e) { 372 super.processKeyEvent(e); 373 if (!isEnabled()) { return; } 374 switch(e.getID()) { 375 case KeyEvent.KEY_TYPED: 376 switch (e.getKeyCode()) { 377 case KeyEvent.VK_ENTER: 378 case KeyEvent.VK_SPACE: 379 flash(); 380 sendActionEvent(e); 381 break; 382 } 383 break; 384 } 385 } 386 387 @Override 388 protected void processMouseEvent(MouseEvent e) { 389 super.processMouseEvent(e); 390 if (!isEnabled()) { return; } 391 switch(e.getID()) { 392 case MouseEvent.MOUSE_PRESSED: 393 requestFocus(); 394 repaint(); 395 break; 396 case MouseEvent.MOUSE_RELEASED: 397 repaint(); 398 break; 399 case MouseEvent.MOUSE_CLICKED: 400 if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { 401 flash(); 402 sendActionEvent(e); 403 } 404 break; 405 } 406 } 407 408 /** 409 * Returns the parameter string representing the state of this 410 * button. This string is useful for debugging. 411 * @return the parameter string of this button. 412 */ 413 @Override 414 protected String paramString() { 415 return super.paramString() + ", label = " + label; 416 } 417 418 }