001: /*
002: * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.awt;
027:
028: import java.awt.peer.ButtonPeer;
029: import java.util.EventListener;
030: import java.awt.event.*;
031: import java.io.ObjectOutputStream;
032: import java.io.ObjectInputStream;
033: import java.io.IOException;
034: import javax.accessibility.*;
035:
036: /**
037: * This class creates a labeled button. The application can cause
038: * some action to happen when the button is pushed. This image
039: * depicts three views of a "<code>Quit</code>" button as it appears
040: * under the Solaris operating system:
041: * <p>
042: * <img src="doc-files/Button-1.gif" alt="The following context describes the graphic"
043: * ALIGN=center HSPACE=10 VSPACE=7>
044: * <p>
045: * The first view shows the button as it appears normally.
046: * The second view shows the button
047: * when it has input focus. Its outline is darkened to let the
048: * user know that it is an active object. The third view shows the
049: * button when the user clicks the mouse over the button, and thus
050: * requests that an action be performed.
051: * <p>
052: * The gesture of clicking on a button with the mouse
053: * is associated with one instance of <code>ActionEvent</code>,
054: * which is sent out when the mouse is both pressed and released
055: * over the button. If an application is interested in knowing
056: * when the button has been pressed but not released, as a separate
057: * gesture, it can specialize <code>processMouseEvent</code>,
058: * or it can register itself as a listener for mouse events by
059: * calling <code>addMouseListener</code>. Both of these methods are
060: * defined by <code>Component</code>, the abstract superclass of
061: * all components.
062: * <p>
063: * When a button is pressed and released, AWT sends an instance
064: * of <code>ActionEvent</code> to the button, by calling
065: * <code>processEvent</code> on the button. The button's
066: * <code>processEvent</code> method receives all events
067: * for the button; it passes an action event along by
068: * calling its own <code>processActionEvent</code> method.
069: * The latter method passes the action event on to any action
070: * listeners that have registered an interest in action
071: * events generated by this button.
072: * <p>
073: * If an application wants to perform some action based on
074: * a button being pressed and released, it should implement
075: * <code>ActionListener</code> and register the new listener
076: * to receive events from this button, by calling the button's
077: * <code>addActionListener</code> method. The application can
078: * make use of the button's action command as a messaging protocol.
079: *
080: * @version 1.89 05/05/07
081: * @author Sami Shaio
082: * @see java.awt.event.ActionEvent
083: * @see java.awt.event.ActionListener
084: * @see java.awt.Component#processMouseEvent
085: * @see java.awt.Component#addMouseListener
086: * @since JDK1.0
087: */
088: public class Button extends Component implements Accessible {
089:
090: /**
091: * The button's label. This value may be null.
092: * @serial
093: * @see #getLabel()
094: * @see #setLabel(String)
095: */
096: String label;
097:
098: /**
099: * The action to be performed once a button has been
100: * pressed. This value may be null.
101: * @serial
102: * @see #getActionCommand()
103: * @see #setActionCommand(String)
104: */
105: String actionCommand;
106:
107: transient ActionListener actionListener;
108:
109: private static final String base = "button";
110: private static int nameCounter = 0;
111:
112: /*
113: * JDK 1.1 serialVersionUID
114: */
115: private static final long serialVersionUID = -8774683716313001058L;
116:
117: static {
118: /* ensure that the necessary native libraries are loaded */
119: Toolkit.loadLibraries();
120: if (!GraphicsEnvironment.isHeadless()) {
121: initIDs();
122: }
123: }
124:
125: /**
126: * Initialize JNI field and method IDs for fields that may be
127: * accessed from C.
128: */
129: private static native void initIDs();
130:
131: /**
132: * Constructs a button with an empty string for its label.
133: *
134: * @exception HeadlessException if GraphicsEnvironment.isHeadless()
135: * returns true
136: * @see java.awt.GraphicsEnvironment#isHeadless
137: */
138: public Button() throws HeadlessException {
139: this ("");
140: }
141:
142: /**
143: * Constructs a button with the specified label.
144: *
145: * @param label a string label for the button, or
146: * <code>null</code> for no label
147: * @exception HeadlessException if GraphicsEnvironment.isHeadless()
148: * returns true
149: * @see java.awt.GraphicsEnvironment#isHeadless
150: */
151: public Button(String label) throws HeadlessException {
152: GraphicsEnvironment.checkHeadless();
153: this .label = label;
154: }
155:
156: /**
157: * Construct a name for this component. Called by getName() when the
158: * name is null.
159: */
160: String constructComponentName() {
161: synchronized (Button.class) {
162: return base + nameCounter++;
163: }
164: }
165:
166: /**
167: * Creates the peer of the button. The button's peer allows the
168: * application to change the look of the button without changing
169: * its functionality.
170: *
171: * @see java.awt.Toolkit#createButton(java.awt.Button)
172: * @see java.awt.Component#getToolkit()
173: */
174: public void addNotify() {
175: synchronized (getTreeLock()) {
176: if (peer == null)
177: peer = getToolkit().createButton(this );
178: super .addNotify();
179: }
180: }
181:
182: /**
183: * Gets the label of this button.
184: *
185: * @return the button's label, or <code>null</code>
186: * if the button has no label.
187: * @see java.awt.Button#setLabel
188: */
189: public String getLabel() {
190: return label;
191: }
192:
193: /**
194: * Sets the button's label to be the specified string.
195: *
196: * @param label the new label, or <code>null</code>
197: * if the button has no label.
198: * @see java.awt.Button#getLabel
199: */
200: public void setLabel(String label) {
201: boolean testvalid = false;
202:
203: synchronized (this ) {
204: if (label != this .label
205: && (this .label == null || !this .label.equals(label))) {
206: this .label = label;
207: ButtonPeer peer = (ButtonPeer) this .peer;
208: if (peer != null) {
209: peer.setLabel(label);
210: }
211: testvalid = true;
212: }
213: }
214:
215: // This could change the preferred size of the Component.
216: if (testvalid && valid) {
217: invalidate();
218: }
219: }
220:
221: /**
222: * Sets the command name for the action event fired
223: * by this button. By default this action command is
224: * set to match the label of the button.
225: *
226: * @param command a string used to set the button's
227: * action command.
228: * If the string is <code>null</code> then the action command
229: * is set to match the label of the button.
230: * @see java.awt.event.ActionEvent
231: * @since JDK1.1
232: */
233: public void setActionCommand(String command) {
234: actionCommand = command;
235: }
236:
237: /**
238: * Returns the command name of the action event fired by this button.
239: * If the command name is <code>null</code> (default) then this method
240: * returns the label of the button.
241: */
242: public String getActionCommand() {
243: return (actionCommand == null ? label : actionCommand);
244: }
245:
246: /**
247: * Adds the specified action listener to receive action events from
248: * this button. Action events occur when a user presses or releases
249: * the mouse over this button.
250: * If l is null, no exception is thrown and no action is performed.
251: * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
252: * >AWT Threading Issues</a> for details on AWT's threading model.
253: *
254: * @param l the action listener
255: * @see #removeActionListener
256: * @see #getActionListeners
257: * @see java.awt.event.ActionListener
258: * @since JDK1.1
259: */
260: public synchronized void addActionListener(ActionListener l) {
261: if (l == null) {
262: return;
263: }
264: actionListener = AWTEventMulticaster.add(actionListener, l);
265: newEventsOnly = true;
266: }
267:
268: /**
269: * Removes the specified action listener so that it no longer
270: * receives action events from this button. Action events occur
271: * when a user presses or releases the mouse over this button.
272: * If l is null, no exception is thrown and no action is performed.
273: * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
274: * >AWT Threading Issues</a> for details on AWT's threading model.
275: *
276: * @param l the action listener
277: * @see #addActionListener
278: * @see #getActionListeners
279: * @see java.awt.event.ActionListener
280: * @since JDK1.1
281: */
282: public synchronized void removeActionListener(ActionListener l) {
283: if (l == null) {
284: return;
285: }
286: actionListener = AWTEventMulticaster.remove(actionListener, l);
287: }
288:
289: /**
290: * Returns an array of all the action listeners
291: * registered on this button.
292: *
293: * @return all of this button's <code>ActionListener</code>s
294: * or an empty array if no action
295: * listeners are currently registered
296: *
297: * @see #addActionListener
298: * @see #removeActionListener
299: * @see java.awt.event.ActionListener
300: * @since 1.4
301: */
302: public synchronized ActionListener[] getActionListeners() {
303: return (ActionListener[]) (getListeners(ActionListener.class));
304: }
305:
306: /**
307: * Returns an array of all the objects currently registered
308: * as <code><em>Foo</em>Listener</code>s
309: * upon this <code>Button</code>.
310: * <code><em>Foo</em>Listener</code>s are registered using the
311: * <code>add<em>Foo</em>Listener</code> method.
312: *
313: * <p>
314: * You can specify the <code>listenerType</code> argument
315: * with a class literal, such as
316: * <code><em>Foo</em>Listener.class</code>.
317: * For example, you can query a
318: * <code>Button</code> <code>b</code>
319: * for its action listeners with the following code:
320: *
321: * <pre>ActionListener[] als = (ActionListener[])(b.getListeners(ActionListener.class));</pre>
322: *
323: * If no such listeners exist, this method returns an empty array.
324: *
325: * @param listenerType the type of listeners requested; this parameter
326: * should specify an interface that descends from
327: * <code>java.util.EventListener</code>
328: * @return an array of all objects registered as
329: * <code><em>Foo</em>Listener</code>s on this button,
330: * or an empty array if no such
331: * listeners have been added
332: * @exception ClassCastException if <code>listenerType</code>
333: * doesn't specify a class or interface that implements
334: * <code>java.util.EventListener</code>
335: *
336: * @see #getActionListeners
337: * @since 1.3
338: */
339: public <T extends EventListener> T[] getListeners(
340: Class<T> listenerType) {
341: EventListener l = null;
342: if (listenerType == ActionListener.class) {
343: l = actionListener;
344: } else {
345: return super .getListeners(listenerType);
346: }
347: return AWTEventMulticaster.getListeners(l, listenerType);
348: }
349:
350: // REMIND: remove when filtering is done at lower level
351: boolean eventEnabled(AWTEvent e) {
352: if (e.id == ActionEvent.ACTION_PERFORMED) {
353: if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0
354: || actionListener != null) {
355: return true;
356: }
357: return false;
358: }
359: return super .eventEnabled(e);
360: }
361:
362: /**
363: * Processes events on this button. If an event is
364: * an instance of <code>ActionEvent</code>, this method invokes
365: * the <code>processActionEvent</code> method. Otherwise,
366: * it invokes <code>processEvent</code> on the superclass.
367: * <p>Note that if the event parameter is <code>null</code>
368: * the behavior is unspecified and may result in an
369: * exception.
370: *
371: * @param e the event
372: * @see java.awt.event.ActionEvent
373: * @see java.awt.Button#processActionEvent
374: * @since JDK1.1
375: */
376: protected void processEvent(AWTEvent e) {
377: if (e instanceof ActionEvent) {
378: processActionEvent((ActionEvent) e);
379: return;
380: }
381: super .processEvent(e);
382: }
383:
384: /**
385: * Processes action events occurring on this button
386: * by dispatching them to any registered
387: * <code>ActionListener</code> objects.
388: * <p>
389: * This method is not called unless action events are
390: * enabled for this button. Action events are enabled
391: * when one of the following occurs:
392: * <p><ul>
393: * <li>An <code>ActionListener</code> object is registered
394: * via <code>addActionListener</code>.
395: * <li>Action events are enabled via <code>enableEvents</code>.
396: * </ul>
397: * <p>Note that if the event parameter is <code>null</code>
398: * the behavior is unspecified and may result in an
399: * exception.
400: *
401: * @param e the action event
402: * @see java.awt.event.ActionListener
403: * @see java.awt.Button#addActionListener
404: * @see java.awt.Component#enableEvents
405: * @since JDK1.1
406: */
407: protected void processActionEvent(ActionEvent e) {
408: ActionListener listener = actionListener;
409: if (listener != null) {
410: listener.actionPerformed(e);
411: }
412: }
413:
414: /**
415: * Returns a string representing the state of this <code>Button</code>.
416: * This method is intended to be used only for debugging purposes, and the
417: * content and format of the returned string may vary between
418: * implementations. The returned string may be empty but may not be
419: * <code>null</code>.
420: *
421: * @return the parameter string of this button
422: */
423: protected String paramString() {
424: return super .paramString() + ",label=" + label;
425: }
426:
427: /* Serialization support.
428: */
429:
430: /*
431: * Button Serial Data Version.
432: * @serial
433: */
434: private int buttonSerializedDataVersion = 1;
435:
436: /**
437: * Writes default serializable fields to stream. Writes
438: * a list of serializable <code>ActionListeners</code>
439: * as optional data. The non-serializable
440: * <code>ActionListeners</code> are detected and
441: * no attempt is made to serialize them.
442: *
443: * @serialData <code>null</code> terminated sequence of 0 or
444: * more pairs: the pair consists of a <code>String</code>
445: * and an <code>Object</code>; the <code>String</code>
446: * indicates the type of object and is one of the following:
447: * <code>actionListenerK</code> indicating an
448: * <code>ActionListener</code> object
449: *
450: * @param s the <code>ObjectOutputStream</code> to write
451: * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
452: * @see java.awt.Component#actionListenerK
453: * @see #readObject(ObjectInputStream)
454: */
455: private void writeObject(ObjectOutputStream s) throws IOException {
456: s.defaultWriteObject();
457:
458: AWTEventMulticaster.save(s, actionListenerK, actionListener);
459: s.writeObject(null);
460: }
461:
462: /**
463: * Reads the <code>ObjectInputStream</code> and if
464: * it isn't <code>null</code> adds a listener to
465: * receive action events fired by the button.
466: * Unrecognized keys or values will be ignored.
467: *
468: * @param s the <code>ObjectInputStream</code> to read
469: * @exception HeadlessException if
470: * <code>GraphicsEnvironment.isHeadless</code> returns
471: * <code>true</code>
472: * @serial
473: * @see #removeActionListener(ActionListener)
474: * @see #addActionListener(ActionListener)
475: * @see java.awt.GraphicsEnvironment#isHeadless
476: * @see #writeObject(ObjectOutputStream)
477: */
478: private void readObject(ObjectInputStream s)
479: throws ClassNotFoundException, IOException,
480: HeadlessException {
481: GraphicsEnvironment.checkHeadless();
482: s.defaultReadObject();
483:
484: Object keyOrNull;
485: while (null != (keyOrNull = s.readObject())) {
486: String key = ((String) keyOrNull).intern();
487:
488: if (actionListenerK == key)
489: addActionListener((ActionListener) (s.readObject()));
490:
491: else
492: // skip value for unrecognized key
493: s.readObject();
494: }
495: }
496:
497: /////////////////
498: // Accessibility support
499: ////////////////
500:
501: /**
502: * Gets the <code>AccessibleContext</code> associated with
503: * this <code>Button</code>. For buttons, the
504: * <code>AccessibleContext</code> takes the form of an
505: * <code>AccessibleAWTButton</code>.
506: * A new <code>AccessibleAWTButton</code> instance is
507: * created if necessary.
508: *
509: * @return an <code>AccessibleAWTButton</code> that serves as the
510: * <code>AccessibleContext</code> of this <code>Button</code>
511: * @beaninfo
512: * expert: true
513: * description: The AccessibleContext associated with this Button.
514: * @since 1.3
515: */
516: public AccessibleContext getAccessibleContext() {
517: if (accessibleContext == null) {
518: accessibleContext = new AccessibleAWTButton();
519: }
520: return accessibleContext;
521: }
522:
523: /**
524: * This class implements accessibility support for the
525: * <code>Button</code> class. It provides an implementation of the
526: * Java Accessibility API appropriate to button user-interface elements.
527: * @since 1.3
528: */
529: protected class AccessibleAWTButton extends AccessibleAWTComponent
530: implements AccessibleAction, AccessibleValue {
531: /*
532: * JDK 1.3 serialVersionUID
533: */
534: private static final long serialVersionUID = -5932203980244017102L;
535:
536: /**
537: * Get the accessible name of this object.
538: *
539: * @return the localized name of the object -- can be null if this
540: * object does not have a name
541: */
542: public String getAccessibleName() {
543: if (accessibleName != null) {
544: return accessibleName;
545: } else {
546: if (getLabel() == null) {
547: return super .getAccessibleName();
548: } else {
549: return getLabel();
550: }
551: }
552: }
553:
554: /**
555: * Get the AccessibleAction associated with this object. In the
556: * implementation of the Java Accessibility API for this class,
557: * return this object, which is responsible for implementing the
558: * AccessibleAction interface on behalf of itself.
559: *
560: * @return this object
561: */
562: public AccessibleAction getAccessibleAction() {
563: return this ;
564: }
565:
566: /**
567: * Get the AccessibleValue associated with this object. In the
568: * implementation of the Java Accessibility API for this class,
569: * return this object, which is responsible for implementing the
570: * AccessibleValue interface on behalf of itself.
571: *
572: * @return this object
573: */
574: public AccessibleValue getAccessibleValue() {
575: return this ;
576: }
577:
578: /**
579: * Returns the number of Actions available in this object. The
580: * default behavior of a button is to have one action - toggle
581: * the button.
582: *
583: * @return 1, the number of Actions in this object
584: */
585: public int getAccessibleActionCount() {
586: return 1;
587: }
588:
589: /**
590: * Return a description of the specified action of the object.
591: *
592: * @param i zero-based index of the actions
593: */
594: public String getAccessibleActionDescription(int i) {
595: if (i == 0) {
596: // [[[PENDING: WDW -- need to provide a localized string]]]
597: return new String("click");
598: } else {
599: return null;
600: }
601: }
602:
603: /**
604: * Perform the specified Action on the object
605: *
606: * @param i zero-based index of actions
607: * @return true if the the action was performed; else false.
608: */
609: public boolean doAccessibleAction(int i) {
610: if (i == 0) {
611: // Simulate a button click
612: Toolkit.getEventQueue().postEvent(
613: new ActionEvent(Button.this ,
614: ActionEvent.ACTION_PERFORMED,
615: Button.this .getActionCommand()));
616: return true;
617: } else {
618: return false;
619: }
620: }
621:
622: /**
623: * Get the value of this object as a Number.
624: *
625: * @return An Integer of 0 if this isn't selected or an Integer of 1 if
626: * this is selected.
627: * @see javax.swing.AbstractButton#isSelected()
628: */
629: public Number getCurrentAccessibleValue() {
630: return Integer.valueOf(0);
631: }
632:
633: /**
634: * Set the value of this object as a Number.
635: *
636: * @return True if the value was set.
637: */
638: public boolean setCurrentAccessibleValue(Number n) {
639: return false;
640: }
641:
642: /**
643: * Get the minimum value of this object as a Number.
644: *
645: * @return An Integer of 0.
646: */
647: public Number getMinimumAccessibleValue() {
648: return Integer.valueOf(0);
649: }
650:
651: /**
652: * Get the maximum value of this object as a Number.
653: *
654: * @return An Integer of 0.
655: */
656: public Number getMaximumAccessibleValue() {
657: return Integer.valueOf(0);
658: }
659:
660: /**
661: * Get the role of this object.
662: *
663: * @return an instance of AccessibleRole describing the role of the
664: * object
665: * @see AccessibleRole
666: */
667: public AccessibleRole getAccessibleRole() {
668: return AccessibleRole.PUSH_BUTTON;
669: }
670: } // inner class AccessibleAWTButton
671:
672: }
|