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 
LWButton()77   public LWButton() { this(""); }
78 
LWButton(String label)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    */
LWButton(String label, Color fgnd, Color bkgnd, Color mousePressed)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    */
flash()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    */
setMousePressedColor(Color c)136   public void setMousePressedColor(Color c) { colMousePressed = c; }
137 
138   /**
139    * Get the MousePressed color.
140    * @return The color of the MousePressed rectangle.
141    */
getMousePressedColor()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    */
sendActionEvent(InputEvent e)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
setEnabled(boolean enabled)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
isFocusTraversable()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    */
makeComponentName()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
paint(Graphics g)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
getPreferredSize()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
getMinimumSize()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    */
getText()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    */
setText(String s)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    */
setActionCommand(String command)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    */
getActionCommand()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    */
addActionListener(ActionListener l)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    */
removeActionListener(ActionListener l)366   public synchronized void removeActionListener(ActionListener l) {
367     actionListener = AWTEventMulticaster.remove(actionListener, l);
368   }
369 
370   @Override
processKeyEvent(KeyEvent e)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
processMouseEvent(MouseEvent e)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
paramString()414   protected String paramString() {
415     return super.paramString() + ", label = " + label;
416   }
417 
418 }
419