001: /*
002: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License version
007: * 2 only, as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * General Public License version 2 for more details (a copy is
013: * included at /legal/license.txt).
014: *
015: * You should have received a copy of the GNU General Public License
016: * version 2 along with this work; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
018: * 02110-1301 USA
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
021: * Clara, CA 95054 or visit www.sun.com if you need additional
022: * information or have any questions.
023: */
024:
025: /**
026: * Copyright(c) 1997 DTAI, Incorporated (http://www.dtai.com)
027: *
028: * All rights reserved
029: *
030: * Permission to use, copy, modify and distribute this material for
031: * any purpose and without fee is hereby granted, provided that the
032: * above copyright notice and this permission notice appear in all
033: * copies, and that the name of DTAI, Incorporated not be used in
034: * advertising or publicity pertaining to this material without the
035: * specific, prior written permission of an authorized representative of
036: * DTAI, Incorporated.
037: *
038: * DTAI, INCORPORATED MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES,
039: * EXPRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT
040: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
041: * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST
042: * INFRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS. THE
043: * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL DTAI, INCORPORATED OR
044: * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY
045: * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELATING
046: * TO THE SOFTWARE.
047: */package com.sun.jumpimpl.presentation.simplebasis;
048:
049: import java.awt.AWTEventMulticaster;
050: import java.awt.Color;
051: import java.awt.Component;
052: import java.awt.Dimension;
053: import java.awt.Font;
054: import java.awt.FontMetrics;
055: import java.awt.Graphics;
056: import java.awt.Image;
057: import java.awt.Insets;
058: import java.awt.MediaTracker;
059: import java.awt.event.ActionEvent;
060: import java.awt.event.ActionListener;
061: import java.awt.event.FocusEvent;
062: import java.awt.event.FocusListener;
063: import java.awt.event.MouseEvent;
064: import java.awt.event.MouseListener;
065: import java.awt.image.FilteredImageSource;
066: import java.awt.image.RGBImageFilter;
067:
068: public class SimpleBasisAMSImageButton extends Component implements
069: FocusListener, MouseListener {
070:
071: public static final int UNPRESSED = 0;
072: public static final int DEPRESSED = 1;
073: public static final int OVER = 2;
074: public static final int DISABLED = 3;
075:
076: private static Font defaultFont = new Font("Serif", Font.BOLD, 12);
077: private static final SimpleBasisAMSBorder defaultUnpressedBorder = new DefaultJUMPGuiAMSImageButtonBorder(
078: false);
079: private static final SimpleBasisAMSBorder defaultArmedBorder = new DefaultJUMPGuiAMSImageButtonBorder(
080: true);
081:
082: private String label = null;
083: private int labelX = -1;
084: private int labelY = -1;
085: private Dimension prefSize = null;
086: private boolean paintBorders = false;
087: private boolean labelDisplay = true;
088: private MediaTracker tracker = null;
089: private Image images[] = new Image[4];
090: private SimpleBasisAMSBorder borders[] = new SimpleBasisAMSBorder[4];
091: private boolean generatedDisabled = false;
092: private int buttonState = UNPRESSED;
093: private ActionListener actionListener = null;
094: private Color textColor = Color.white;
095: private Font currentFont = null;
096: private boolean textShadow = false;
097: private boolean mousedown = false;
098: private boolean lastFocused = false;
099: private boolean focusable = true;
100:
101: /**
102: * Constructs an SimpleBasisAMSImageButton
103: */
104: public SimpleBasisAMSImageButton() {
105: tracker = new MediaTracker(this );
106: setUnpressedBorder(defaultUnpressedBorder);
107: setDepressedBorder(defaultArmedBorder);
108: addFocusListener(this );
109: addMouseListener(this );
110: }
111:
112: /**
113: * Constructs an SimpleBasisAMSImageButton with the given image.
114: *
115: * @param image the image for all states of the button
116: * (until other images are assigned)
117: */
118: public SimpleBasisAMSImageButton(Image image) {
119: this ();
120: setUnpressedImage(image);
121: }
122:
123: public SimpleBasisAMSImageButton(String label) {
124: this ();
125: setLabel(label);
126: }
127:
128: public SimpleBasisAMSImageButton(Image image, String label) {
129: this (image);
130: setLabel(label);
131: }
132:
133: public void doAction() {
134: ActionEvent ae = new ActionEvent(this ,
135: ActionEvent.ACTION_PERFORMED, "");
136: actionListener.actionPerformed(ae);
137: }
138:
139: public void setFocusable(boolean val) {
140: focusable = val;
141: }
142:
143: public boolean isFocusable() {
144: return focusable;
145: }
146:
147: public boolean isEnabled() {
148: return true;
149: }
150:
151: public boolean lastFocused() {
152: return lastFocused;
153: }
154:
155: public void focusGained(FocusEvent e) {
156: //paintBorders = true;
157: repaint();
158: lastFocused = true;
159: }
160:
161: public void focusLost(FocusEvent e) {
162: //paintBorders = false;
163: repaint();
164: }
165:
166: /*
167: public SimpleBasisAMSImageButton(Image image, int width, int height) {
168: this(image);
169: prefSize = new Dimension(width, height);
170: }
171: */
172:
173: /*
174: public SimpleBasisAMSImageButton(Image image, String label, int width, int height) {
175: this(image);
176: setLabel(label);
177: prefSize = new Dimension(width, height);
178: }
179: */
180:
181: /**
182: * Used internally to add the Image to the array and the MediaTracker,
183: * start loading the image if necessary via the tracker's "checkID", and
184: * repaint if necessary.
185: *
186: * @param id the buttonState id (also used as image id for the MediaTracker)
187: * @param image the image, which is not supposed to be null
188: */
189: private synchronized void setImage(int id, Image image) {
190: if (images[id] != image) {
191: images[id] = image;
192: if (image != null) {
193: tracker.addImage(image, id);
194: tracker.checkID(id, true);
195: }
196: if (buttonState == id) {
197: repaint();
198: }
199: }
200: }
201:
202: public void setTextShadow(boolean val) {
203: textShadow = val;
204: }
205:
206: public void setFont(Font f) {
207: currentFont = f;
208: }
209:
210: public void setTextColor(Color c) {
211: textColor = c;
212: repaint();
213: }
214:
215: public void setPaintBorders(boolean val) {
216: paintBorders = val;
217: }
218:
219: public void setLabelDisplay(boolean val) {
220: labelDisplay = val;
221: }
222:
223: /**
224: * Sets the image to display when the button is not pressed or hilited
225: * because of a mouse-over. This image is also used in those other cases
226: * if no alternative image is requested.
227: *
228: * @param image the unarmed image
229: */
230: public void setUnpressedImage(Image image) {
231: setImage(UNPRESSED, image);
232: if (images[DEPRESSED] == null) {
233: setDepressedImage(image);
234: }
235: if (images[OVER] == null) {
236: setOverImage(image);
237: }
238: if ((images[DISABLED] == null) || generatedDisabled) {
239: setDisabledImage(null);
240: }
241: }
242:
243: /**
244: * Sets the image to display when the button is pressed and the mouse
245: * is still over the button.
246: *
247: * @param image the armed image
248: */
249: public void setDepressedImage(Image image) {
250: if (image != null) {
251: setImage(DEPRESSED, image);
252: } else {
253: setImage(DEPRESSED, images[UNPRESSED]);
254: }
255: }
256:
257: /**
258: * Sets the image to display when the button is not pressed and the mouse
259: * is over the button.
260: *
261: * @param image the over image
262: */
263: public void setOverImage(Image image) {
264: if (image != null) {
265: setImage(OVER, image);
266: } else {
267: setImage(OVER, images[UNPRESSED]);
268: }
269: }
270:
271: /**
272: * Sets the image to display when the button is disabled.
273: *
274: * @param image the disabled image
275: */
276: public void setDisabledImage(Image image) {
277: generatedDisabled = false;
278: if ((image == null) && (images[UNPRESSED] != null)) {
279: generatedDisabled = true;
280: image = createImage(new FilteredImageSource(
281: images[UNPRESSED].getSource(),
282: new DisableImageFilter()));
283: }
284: setImage(DISABLED, image);
285: }
286:
287: /**
288: * Gets the image to display when the button is not pressed or hilited
289: * because of a mouse-over. This image is also used in those other cases
290: * if no alternative image is requested.
291: *
292: * @return the unarmed image
293: */
294: /*
295: public Image getUnpressedImage() {
296: return (images[UNPRESSED]);
297: }
298: */
299:
300: /**
301: * Gets the image to display when the button is pressed and the mouse
302: * is still over the button.
303: *
304: * @return the armed image
305: */
306: /*
307: public Image getDepressedImage() {
308: return (images[DEPRESSED]);
309: }
310: */
311:
312: /**
313: * Gets the image to display when the button is not pressed and the mouse
314: * is over the button.
315: *
316: * @return the over image
317: */
318: /*
319: public Image getOverImage() {
320: return (images[OVER]);
321: }
322: */
323:
324: /**
325: * Gets the image to display when the button is disabled.
326: *
327: * @return the armed image
328: */
329: /*
330: public Image getDisabledImage() {
331: return (images[DISABLED]);
332: }
333: */
334:
335: /**
336: * Used internally to add the Border to the array and repaint if necessary.
337: *
338: * @param id the buttonState, used to index the array
339: * @param border the Border, which is not supposed to be null
340: */
341: private synchronized void setBorder(int id,
342: SimpleBasisAMSBorder border) {
343: if (borders[id] != border) {
344: borders[id] = border;
345: if (buttonState == id) {
346: repaint();
347: }
348: }
349: }
350:
351: /**
352: * Sets the border to display when the button is not pressed or hilited
353: * because of a mouse-over. This border is also used in those other cases
354: * if no alternative border is requested.
355: *
356: * @param border the unarmed border
357: */
358: public void setUnpressedBorder(SimpleBasisAMSBorder border) {
359: setBorder(UNPRESSED, border);
360: if (borders[DEPRESSED] == null) {
361: setDepressedBorder(border);
362: }
363: if (borders[OVER] == null) {
364: setOverBorder(border);
365: }
366: if (borders[DISABLED] == null) {
367: setDisabledBorder(border);
368: }
369: }
370:
371: /**
372: * Sets the border to display when the button is pressed and the mouse
373: * is still over the button.
374: *
375: * @param border the armed border
376: */
377: public void setDepressedBorder(SimpleBasisAMSBorder border) {
378: if (border != null) {
379: setBorder(DEPRESSED, border);
380: } else {
381: setBorder(DEPRESSED, borders[UNPRESSED]);
382: }
383: }
384:
385: /**
386: * Sets the border to display when the button is not pressed and the mouse
387: * is over the button.
388: *
389: * @param border the over border
390: */
391: public void setOverBorder(SimpleBasisAMSBorder border) {
392: if (border != null) {
393: setBorder(OVER, border);
394: } else {
395: setBorder(OVER, borders[UNPRESSED]);
396: }
397: setBorder(OVER, border);
398: }
399:
400: /**
401: * Sets the border to display when the button is disabled.
402: *
403: * @param border the disabled border
404: */
405: public void setDisabledBorder(SimpleBasisAMSBorder border) {
406: if (border != null) {
407: setBorder(DISABLED, border);
408: } else {
409: setBorder(DISABLED, borders[UNPRESSED]);
410: }
411: if (buttonState == DISABLED) {
412: repaint();
413: }
414: }
415:
416: /**
417: * Gets the border to display when the button is not pressed or hilited
418: * because of a mouse-over. This border is also used in those other cases
419: * if no alternative border is requested.
420: *
421: * @return the unarmed border
422: */
423: /*
424: public Border getUnpressedBorder() {
425: return (borders[UNPRESSED]);
426: }
427: */
428:
429: /**
430: * Gets the border to display when the button is pressed and the mouse
431: * is still over the button.
432: *
433: * @return the armed border
434: */
435: /*
436: public Border getDepressedBorder() {
437: return (borders[DEPRESSED]);
438: }
439: */
440:
441: /**
442: * Gets the border to display when the button is not pressed and the mouse
443: * is over the button.
444: *
445: * @return the over border
446: */
447: /*
448: public Border getOverBorder() {
449: return (borders[OVER]);
450: }
451: */
452:
453: /**
454: * Gets the border to display when the button is disabled.
455: *
456: * @return the armed border
457: */
458: /*
459: public Border getDisabledBorder() {
460: return (borders[DISABLED]);
461: }
462: */
463:
464: /**
465: * Gets the current buttonState id for the button
466: *
467: * @return the button state integer id
468: */
469: /*
470: public int getButtonState() {
471: return buttonState;
472: }
473: */
474:
475: /**
476: * Sets the current buttonState id for the button
477: *
478: * @param buttonState the button state integer id
479: */
480: protected void setButtonState(int buttonState) {
481: if (buttonState != this .buttonState) {
482: this .buttonState = buttonState;
483: repaint();
484: }
485: }
486:
487: public void setLabel(String str) {
488: label = str;
489: }
490:
491: public void setLabel(String str, int x, int y) {
492: label = str;
493: labelX = x;
494: labelY = y;
495: }
496:
497: public String getLabel() {
498: return label;
499: }
500:
501: /**
502: * Overrides awt.Component.disable() to also set the button state.
503: */
504: /*
505: public void disable() {
506: setButtonState(DISABLED);
507: super.disable();
508: }
509: */
510:
511: /**
512: * Overrides awt.Component.enable() to also set the button state.
513: */
514: /*
515: public void enable() {
516: setButtonState(UNPRESSED);
517: super.enable();
518: }
519: */
520:
521: /**
522: * Overrides awt.Component.paint() to paint the current border and image.
523: *
524: * @param g The Graphics in which to draw
525: */
526: public void paint(Graphics g) {
527:
528: Dimension size = getSize();
529: if (hasFocus()) {
530: g.setColor(Color.black);
531: } else {
532: g.setColor(getForeground());
533: }
534: g.fillRect(0, 0, size.width, size.height);
535:
536: try {
537: if (!tracker.checkID(buttonState)) {
538: tracker.waitForID(buttonState);
539: }
540: if (!tracker.isErrorID(buttonState)) {
541: Insets insets = borders[buttonState].getInsets();
542: if (images[buttonState] != null) {
543: int imageWidth = images[buttonState].getWidth(this );
544: int imageHeight = images[buttonState]
545: .getHeight(this );
546: int x = insets.left
547: + (((size.width - (insets.left + insets.right)) - imageWidth) / 2);
548: int y = insets.top
549: + (((size.height - (insets.top + insets.bottom)) - imageHeight) / 2)
550: - 5;
551: g.drawImage(images[buttonState], x, y, this );
552: }
553: } else {
554: trace("ERROR: Invalid image used for button: " + label);
555: }
556: } catch (Exception e) {
557: trace(e.getMessage());
558: }
559:
560: // for label
561: if (label != null) {
562: FontMetrics fm = null;
563: if (currentFont != null) {
564: g.setFont(currentFont);
565: fm = getFontMetrics(currentFont);
566: } else {
567: g.setFont(defaultFont);
568: fm = getFontMetrics(defaultFont);
569: }
570:
571: int labelWidth = (int) fm.stringWidth(label);
572: int buttonWidth = size.width;
573: String displayLabel = label;
574: String tmpLabel = null;
575: while (labelWidth > size.width) {
576: displayLabel = displayLabel.substring(0, displayLabel
577: .length() - 1);
578: tmpLabel = displayLabel + "...";
579: labelWidth = (int) fm.stringWidth(tmpLabel);
580: }
581:
582: if (tmpLabel != null) {
583: displayLabel = tmpLabel;
584: }
585:
586: int height = (int) fm.getHeight();
587: if (labelX == -1) {
588: labelX = ((size.width - labelWidth) / 2);
589: if (labelX < 0) {
590: labelX = 0;
591: }
592: }
593: if (labelY == -1) {
594: if (images[buttonState] != null
595: && !tracker.isErrorID(buttonState)) {
596: labelY = (int) (size.height - 5);
597: } else {
598: labelY = (int) (size.height / 2) + (height / 2);
599: }
600: }
601:
602: g.setColor(textColor);
603: if (labelDisplay) {
604: if (textShadow) {
605: drawTextShadowString(g, displayLabel, labelX,
606: labelY);
607: } else {
608: g.drawString(displayLabel, labelX, labelY);
609: }
610: }
611: }
612:
613: if (paintBorders) {
614: borders[buttonState].paint(g, getBackground(), 0, 0,
615: size.width, size.height);
616: }
617: }
618:
619: private void trace(String str) {
620: SimpleBasisAMS.trace(str);
621: }
622:
623: private void drawTextShadowString(Graphics g, String str, int x,
624: int y) {
625: Color origColor = g.getColor();
626: g.setColor(Color.BLACK);
627: g.drawString(str, x + 1, y + 1);
628: g.setColor(origColor);
629: g.drawString(str, x, y);
630: }
631:
632: /*
633: public Dimension getMinimumSize() {
634: return getPreferredSize();
635: }
636: */
637:
638: /*
639: public Dimension getMaximumSize() {
640: return getPreferredSize();
641: }
642: */
643:
644: /**
645: * Overrides awt.Component.preferredSize() to return the preferred size of the button.
646: * This assumes the images (if more than one) are all the same size. It also calculates
647: * the maximum insets from all borders and adds them to the image dimensions.
648: *
649: * @param g The Graphics in which to draw
650: */
651: public Dimension getPreferredSize() {
652:
653: if (prefSize != null) {
654: return prefSize;
655: } else {
656: return new Dimension(120, 70);
657: }
658: /*
659: if (prefSize != null) {
660: return prefSize;
661: }
662:
663: Dimension pref = new Dimension();
664: try {
665: if (!tracker.checkID(buttonState)) {
666: tracker.waitForID(buttonState);
667: }
668: if (!tracker.isErrorID(buttonState)) {
669: Dimension size = size();
670: pref.width = images[buttonState].getWidth(this);
671: pref.height = images[buttonState].getHeight(this);
672: }
673: int maxWidthAdd = 0;
674: int maxHeightAdd = 0;
675: for (int i = 0; i < DISABLED; i++) {
676: Insets insets = borders[i].getInsets();
677: maxWidthAdd = Math.max(maxWidthAdd, insets.left + insets.right);
678: maxHeightAdd = Math.max(maxHeightAdd,
679: insets.top + insets.bottom);
680: }
681: pref.width += maxWidthAdd;
682: pref.height += maxHeightAdd;
683: }
684: catch (InterruptedException ie) {
685: }
686: return pref;
687: */
688: }
689:
690: public void processMouseEvent(MouseEvent e) {
691: if (actionListener != null) {
692: if (e.getID() == MouseEvent.MOUSE_RELEASED) {
693: ActionEvent ae = new ActionEvent(this ,
694: ActionEvent.ACTION_PERFORMED, "");
695: actionListener.actionPerformed(ae);
696: }
697: }
698:
699: }
700:
701: public synchronized void addActionListener(ActionListener l) {
702: actionListener = AWTEventMulticaster.add(actionListener, l);
703: }
704:
705: public synchronized void removeActionListener(ActionListener l) {
706: actionListener = AWTEventMulticaster.remove(actionListener, l);
707: }
708:
709: /*
710: public synchronized ActionListener getActionListener() {
711: return actionListener;
712: }
713: */
714:
715: public void mouseClicked(MouseEvent e) {
716: requestFocusInWindow();
717: }
718:
719: public void mousePressed(MouseEvent e) {
720: }
721:
722: public void mouseReleased(MouseEvent e) {
723: }
724:
725: public void mouseEntered(MouseEvent e) {
726: }
727:
728: public void mouseExited(MouseEvent e) {
729: }
730:
731: }
732:
733: /**
734: * DisableImageFilter - an RGBImageFilter that "greys out" an image by "blanking out"
735: * every other pixel.
736: */
737: class DisableImageFilter extends RGBImageFilter {
738: /**
739: * Constructs a DisableImageFilter. The canFilterIndexColorModel is set to false
740: * because the pixel index is important during filtering.
741: */
742: public DisableImageFilter() {
743: canFilterIndexColorModel = false;
744: }
745:
746: /**
747: * Called when a disabled image is created to alter the pixels to be blanked out.
748: *
749: * @param x the x position of the pixel
750: * @param y the y position of the pixel
751: * @param rgb the rgb value of the pixel
752: */
753: public int filterRGB(int x, int y, int rgb) {
754: if (((x % 2) ^ (y % 2)) == 1) {
755: return (rgb & 0xffffff);
756: } else {
757: return rgb;
758: }
759: }
760: }
761:
762: /**
763: * DefaultJUMPGuiAMSImageButtonBorder - a Border, subclassed to set the default border values.
764: */
765: class DefaultJUMPGuiAMSImageButtonBorder extends SimpleBasisAMSBorder {
766:
767: public DefaultJUMPGuiAMSImageButtonBorder(boolean armed) {
768: setJUMPGuiAMSBorderThickness(2);
769: if (armed) {
770: setType(THREED_IN);
771: setMargins(4, 4, 2, 2);
772: } else {
773: setType(THREED_OUT);
774: setMargins(3);
775: }
776: }
777: }
|