0001: /*
0002: * Javu WingS - Lightweight Java Component Set
0003: * Copyright (c) 2005-2007 Krzysztof A. Sadlocha
0004: * e-mail: ksadlocha@programics.com
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or (at your option) any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: */
0020:
0021: package com.javujavu.javux.wings;
0022:
0023: import java.applet.Applet;
0024: import java.awt.AWTEvent;
0025: import java.awt.AWTEventMulticaster;
0026: import java.awt.Color;
0027: import java.awt.Component;
0028: import java.awt.Container;
0029: import java.awt.Dimension;
0030: import java.awt.EventQueue;
0031: import java.awt.Font;
0032: import java.awt.Frame;
0033: import java.awt.Graphics;
0034: import java.awt.Image;
0035: import java.awt.Point;
0036: import java.awt.Rectangle;
0037: import java.awt.Toolkit;
0038: import java.awt.Window;
0039: import java.awt.datatransfer.Clipboard;
0040: import java.awt.datatransfer.DataFlavor;
0041: import java.awt.datatransfer.StringSelection;
0042: import java.awt.datatransfer.Transferable;
0043: import java.awt.event.ActionEvent;
0044: import java.awt.event.ActionListener;
0045: import java.awt.event.ComponentEvent;
0046: import java.awt.event.ItemEvent;
0047: import java.awt.event.ItemListener;
0048: import java.awt.event.KeyEvent;
0049: import java.awt.event.MouseEvent;
0050: import java.util.Vector;
0051:
0052: import com.javujavu.javux.wings.item.DefaultItemRenderer;
0053: import com.javujavu.javux.wings.item.ItemRenderer;
0054:
0055: /**
0056: * The base class for all WingS components except top-level containers
0057: * (<code>WingFrame, WingWindow, WingDialog and WingApplet</code>).
0058: * <p>
0059: * The <code>WingComponent</code> class provides:
0060: * <ul>
0061: * <li>The base class for both standard and custom components
0062: * that use the WingS architecture.
0063: * <li>Highly customizable look based on hierarchical skin style sheets.
0064: * <li>Interactive keyboard shortcut handling.
0065: * <li>Thread and deadlock safe layout revalidation with <code>revalidateAndRepaint</code> method.
0066: * <li>Posting events on event thread with <code>postOnEventThread</code> method.
0067: * <li>An infrastructure for painting
0068: * that includes double buffering and support for item renderers.
0069: * </ul>
0070: * <br>
0071: * <b>This is one of the core WingS classes required by all the components</b><br>
0072: * <br>
0073: * <b>This class is thread safe.</b>
0074: **/
0075: public class WingComponent extends Container implements WingConst {
0076: public static final String WINGS_VERSION = "1.2.1";
0077: public static final int WINGSKIN_VERSION = 1;
0078:
0079: public static final int POET_EVENT = AWTEvent.RESERVED_ID_MAX + 158;
0080:
0081: private static final int REVALIDATE_REPAINT = 0;
0082: private static final int REVALIDATE = 1;
0083: private static final int INVALID = 2;
0084: private static final int VALIDATING = 3;
0085: private static final int VALID = 4;
0086:
0087: protected static boolean doubleBuffering = true;
0088: protected static int debugDelay = 0;
0089: protected static boolean heavyPopups = true;
0090: protected static boolean lightPopups = true;
0091:
0092: private static WingComponent popup;
0093: /**
0094: * <pre>
0095: * 10 - no popup no click
0096: * 20 - click
0097: * 40 - popup open during click
0098: * 50 - popup
0099: * 60 - popup+click
0100: *
0101: * state transition diagram
0102: * 10 > processMouseEvent begin > 20
0103: * 10 > open popup > 50
0104: * 20 > processMouseEvent end > 10
0105: * 20 > open popup > 40
0106: * 40 > processMouseEvent end > 50
0107: * 40 > close popup > 20
0108: * 40 > open popup > 40
0109: * 50 > close popup > 10
0110: * 50 > open popup > 50
0111: * 50 > processMouseEvent begin > 60
0112: * 60 > open popup > 40
0113: * 60 > processMouseEvent end > 50
0114: * 60 > close popup > 20
0115: * </pre>
0116: */
0117: private static int popupClick = 10;
0118:
0119: /**
0120: * Enables/disables double buffering for all WingS components.
0121: * @param doubleBuffering whether or not double buffering is enabled
0122: */
0123: public static void setDoubleBuffering(boolean doubleBuffering) {
0124: WingComponent.doubleBuffering = doubleBuffering;
0125: }
0126:
0127: /**
0128: * Sets debug delay time for debug purpose
0129: * @param debugDelay delay time
0130: */
0131: public static void setDebugDelay(int debugDelay) {
0132: WingComponent.debugDelay = debugDelay;
0133: }
0134:
0135: /**
0136: * Enables/disables light and heavy weight popups for combo, menu, tooltip popups.
0137: * @param enableLightweightPopups whether or not light weight popups are enabled
0138: * @param enableHeavyweightPopups whether or not heavy weight popups are enabled
0139: */
0140: public static void enablePopups(boolean enableLightweightPopups,
0141: boolean enableHeavyweightPopups) {
0142: lightPopups = enableLightweightPopups;
0143: heavyPopups = enableHeavyweightPopups;
0144: }
0145:
0146: /**
0147: * default item renderer used by all components without specified item renderer
0148: */
0149: protected static ItemRenderer defaultRenderer = new DefaultItemRenderer();
0150:
0151: protected String styleId;
0152: protected Style stNormal;
0153: protected Style stDisabled;
0154: protected Style stTop;
0155: protected ItemRenderer renderer;
0156:
0157: protected Dimension wingPrefSize;
0158: protected boolean wingPrefSizeSet;
0159: protected boolean wingFocusable;
0160: protected int wingValid;
0161:
0162: protected ActionListener actionListener;
0163: protected ItemListener itemListener;
0164: protected Vector shortcuts;
0165: protected WingComponent popupOwner;
0166:
0167: protected Object tooltip;
0168:
0169: private static Object lastTip;
0170: private static Container tipHandle;
0171: protected static final WingTimer tipStartTimer = new WingTimer(800,
0172: false, null);
0173: protected static final WingTimer tipStopTimer = new WingTimer(5000,
0174: false, null);
0175:
0176: /**
0177: * Default <code>WingComponent</code> constructor.
0178: */
0179: public WingComponent() {
0180: enableEvents(AWTEvent.MOUSE_EVENT_MASK
0181: | AWTEvent.MOUSE_MOTION_EVENT_MASK
0182: | AWTEvent.KEY_EVENT_MASK);
0183: }
0184:
0185: /**
0186: * Sets custom style ID.
0187: * <br><br><strong>This method acquire TreeLock</strong>
0188: * @param styleId custom style ID; use <code>null</code> to
0189: * clear custom style ID
0190: */
0191: public void setStyleId(String styleId) {
0192: if ((styleId == null && this .styleId == null)
0193: || (styleId != null && styleId.equals(this .styleId))) {
0194: return;
0195: }
0196: this .styleId = styleId;
0197:
0198: if (stNormal != null)
0199: WingComponent.updateSkin(this );
0200: }
0201:
0202: /**
0203: * A skin change: ask each node in the tree
0204: * to <code>loadSkin()</code> -- that is, to initialize its
0205: * with the current skin.
0206: *
0207: * <strong>This method acquire TreeLock</strong>
0208: * @param c container being at the top of the tree
0209: */
0210: public static void updateSkin(Container c) {
0211: synchronized (c.getTreeLock()) {
0212: if (c instanceof WingComponent) {
0213: WingComponent wc = (WingComponent) c;
0214:
0215: if (wc.stNormal == null)
0216: return;
0217:
0218: wc.loadSkin();
0219: wc.revalidateAndRepaint();
0220: } else
0221: c.repaint();
0222:
0223: if (c instanceof Container) {
0224: for (int i = 0; i < c.getComponentCount(); i++) {
0225: Component cc = c.getComponent(i);
0226: if (cc instanceof Container) {
0227: updateSkin((Container) cc);
0228: }
0229: }
0230: }
0231: }
0232: }
0233:
0234: /**
0235: * Sets overriding at runtime style settings loaded from style sheets.
0236: * <code>topStyle</code> is merged with all component styles
0237: * matching following expression <code>style.state & topStyle.state != 0</code>
0238: * @param topStyle top style;use <code>null</code> to clear top style
0239: */
0240: public void setTopStyle(Style topStyle) {
0241: stTop = topStyle;
0242: updateSkin(this );
0243: }
0244:
0245: /**
0246: * Loads skin resources.<br>
0247: * <pre>
0248: * styles:
0249: * [optional styleID.]normal
0250: * [optional styleID.]disabled
0251: * </pre>
0252: * @see Style
0253: * @see WingSkin
0254: */
0255: public void loadSkin() {
0256: stNormal = WingSkin.getStyle(styleId, null, NORMAL, null);
0257: stDisabled = WingSkin.getStyle(styleId, null, DISABLED,
0258: stNormal);
0259: }
0260:
0261: public void addNotify() {
0262: loadSkin();
0263: super .addNotify();
0264: }
0265:
0266: /**
0267: * Returns the style representing current component state
0268: * @return current style representing component state
0269: */
0270: public Style getStyle() {
0271: return (isEnabled()) ? stNormal : stDisabled;
0272: }
0273:
0274: /**
0275: * Overrides <code>java.awt.Component.getBackground()</code>
0276: * to provide background color from the current component style
0277: * @see java.awt.Component#getBackground()
0278: */
0279: public Color getBackground() {
0280: Color r = getStyle().background;
0281: if (r == null)
0282: r = super .getBackground();
0283: return r;
0284: }
0285:
0286: /**
0287: * Overrides <code>java.awt.Component.getForeground()</code>
0288: * to provide foreground color from the current component style
0289: * @see java.awt.Component#getForeground()
0290: */
0291: public Color getForeground() {
0292: Color r = getStyle().foreground;
0293: if (r == null)
0294: r = super .getForeground();
0295: return r;
0296: }
0297:
0298: /**
0299: * Gets the <code>WingFont</code> font of this component.
0300: * It's analogous to <code>getFont()</code>
0301: * @return the current component WingFont
0302: * @see WingFont
0303: */
0304: public WingFont getWingFont() {
0305: Style st = getStyle();
0306: if (st != null && st.font != null)
0307: return st.font;
0308: Container c = getParent();
0309: if (c instanceof WingComponent)
0310: return ((WingComponent) c).getWingFont();
0311: Font f = super .getFont();
0312: return (f != null) ? new WingFont(f) : null;
0313: }
0314:
0315: /**
0316: * Overrides <code>java.awt.Component.getFont()</code>
0317: * to provide font color from the current component style
0318: * @see java.awt.Component#getFont()
0319: */
0320: public Font getFont() {
0321: return getWingFont().getFont();
0322: }
0323:
0324: /**
0325: * Returns the renderer used to display items in this component.
0326: * @return the current <code>{@link ItemRenderer}</code> of the component.
0327: * @see ItemRenderer
0328: * @see DefaultItemRenderer
0329: */
0330: public ItemRenderer getRenderer() {
0331: ItemRenderer r = renderer;
0332: if (r != null)
0333: return r;
0334: return defaultRenderer;
0335: }
0336:
0337: /**
0338: * Sets the renderer that paints items in this component.
0339: * @param renderer the <code>ItemRenderer</code> of the component;
0340: * use null to reset to default
0341: * @see ItemRenderer
0342: * @see DefaultItemRenderer
0343: */
0344: public void setRenderer(ItemRenderer renderer) {
0345: this .renderer = renderer;
0346: revalidateAndRepaint();
0347: }
0348:
0349: /**
0350: * Registers the tool tip for this component.
0351: * It can be an instance of <code>WingComponent</code>
0352: * or a label acceptable by the item renderer of this component.
0353: * @param tooltip the tool tip to display; if the value is <code>null</code>,
0354: * the tool tip is turned off for this component
0355: */
0356: public void setTooltip(Object tooltip) {
0357: this .tooltip = tooltip;
0358: }
0359:
0360: /**
0361: * Returns the object to be used as the tooltip.
0362: * By default this returns any object set using
0363: * <code>setToolTip</code>. If a component provides
0364: * more extensive API to support differing tooltips at different locations,
0365: * this method should be overridden.
0366: * @param x
0367: * @param y
0368: * @return the object to be used as the tooltip
0369: */
0370: public Object getTooltipAt(int x, int y) {
0371: return tooltip;
0372: }
0373:
0374: /**
0375: * Shows the tool tip at the specified location
0376: * <br><br><strong>This method acquire TreeLock</strong>
0377: * @param point location of the tool tip
0378: */
0379: protected void showTooltip(Point point) {
0380: Container oldHandle, newHandle;
0381:
0382: Object tooltip = getTooltipAt(point.x, point.y);
0383: if (tooltip == null)
0384: return;
0385:
0386: lastTip = tooltip;
0387:
0388: WingComponent tip, label = null;
0389: if (tooltip instanceof WingComponent)
0390: tip = (WingComponent) tooltip;
0391: else {
0392: tip = new WingLabel(tooltip, CENTER);
0393: tip.setStyleId(WingSkin.catKeys(styleId, "light.tooltip"));
0394: label = tip;
0395: }
0396: newHandle = WingToolkit.showPopup(this , point.x, point.y - 3,
0397: 0, 23, HORIZONTAL, 0, 0, tip, null, lightPopups, false,
0398: !heavyPopups, false);
0399: if (newHandle == null) {
0400: if (label != null)
0401: tip.setStyleId(WingSkin.catKeys(styleId,
0402: "heavy.tooltip"));
0403: newHandle = WingToolkit.showPopup(this , point.x,
0404: point.y - 3, 0, 23, HORIZONTAL, 0, 0, tip, null,
0405: false, heavyPopups, false, false);
0406: }
0407: if (newHandle != null) {
0408: tipStopTimer.setTarget(this );
0409: tipStopTimer.start();
0410: synchronized (WingComponent.class) {
0411: oldHandle = tipHandle;
0412: tipHandle = newHandle;
0413: }
0414: if (oldHandle != null)
0415: WingToolkit.hidePopup(oldHandle);
0416: }
0417: }
0418:
0419: /**
0420: * Hides the tool tip
0421: * <br><br><b>This method acquire TreeLock via invokeAndWait</b>
0422: */
0423: protected void hideTooltip() {
0424: Container handle = null;
0425: synchronized (WingComponent.class) {
0426: if (tipHandle == null)
0427: return;
0428: handle = tipHandle;
0429: tipHandle = null;
0430: }
0431: WingToolkit.hidePopup(handle);
0432: }
0433:
0434: /**
0435: * Sets the preferred size of this component.
0436: * If <code>preferredSize</code> is <code>null</code>, the component will
0437: * be asked for the preferred size.
0438: * @param prefSize The preferred size of the component
0439: */
0440: public void setPreferredSize(Dimension prefSize) {
0441: this .wingPrefSize = prefSize;
0442: wingPrefSizeSet = (prefSize != null);
0443: }
0444:
0445: /**
0446: * <br><br><strong>This method acquire TreeLock</strong>
0447: */
0448: public Dimension getPreferredSize() {
0449: Dimension prefSize = wingPrefSize;
0450: if (prefSize == null) {
0451: wingPrefSize = prefSize = super .getPreferredSize();
0452: }
0453: return prefSize;
0454: }
0455:
0456: /**
0457: * <br><br><strong>This method acquire TreeLock</strong>
0458: */
0459: public Dimension getMinimumSize() {
0460: return getPreferredSize();
0461: }
0462:
0463: /**
0464: * Returns the <code>WingRootPane</code> ancestor for this component.
0465: *
0466: * @return the <code>WingRootPane</code> that contains this component,
0467: * or <code>null</code> if no <code>WingRootPane</code> is found
0468: */
0469: public WingRootPane getRootPane() {
0470: for (Component c = this ; c instanceof WingComponent; c = c
0471: .getParent()) {
0472: if (c instanceof WingRootPane)
0473: return (WingRootPane) c;
0474: }
0475: return null;
0476: }
0477:
0478: // protected String wingValid()
0479: // {
0480: // return wingValid==REVALIDATE_REPAINT ? "RR"
0481: // : wingValid==REVALIDATE? "R"
0482: // : wingValid==INVALID ? "I"
0483: // : wingValid==VALIDATING ? "VG" : "V";
0484: // }
0485: /**
0486: * Provide thread and deadlock safe way to revalidate component layout.
0487: * Calls repaint and then invalidate+validate on the painting thread
0488: * just before painting<br>
0489: * deadlock and thread safe method,
0490: * does not acquire TreeLock
0491: *
0492: * <pre>
0493: * wingValid states
0494: * VALID
0495: * INVALID
0496: * REVALIDATE
0497: * REVALIDATE_REPAINT
0498: * VALIDATING
0499: *
0500: * wingValid state transition diagram
0501: * VALID,VALIDATING > invalidate > INVALID
0502: * VALID,INVALID,VALIDATING > child.revalidatAndRepaint > REVALIDATE
0503: * VALID,INVALID,REVALIDATE,VALIDATING > revalidatAndRepaint > REVALIDATE_REPAINT
0504: * INVALID,REVALIDATE,REVALIDATE_REPAINT > wingInvalidateTree > VALIDATING
0505: * VALIDATING > validateTree > VALID
0506: *
0507: * </pre>
0508: */
0509: public void revalidateAndRepaint() {
0510: boolean doRevalidate;
0511: synchronized (this ) {
0512: doRevalidate = (wingValid != REVALIDATE_REPAINT && wingValid != REVALIDATE);
0513: if (doRevalidate)
0514: wingValid = REVALIDATE_REPAINT;
0515: }
0516: if (!doRevalidate) {
0517: repaintVisible();
0518: return;
0519: }
0520:
0521: int x, y, width, height;
0522: Dimension size = getSize();
0523: x = y = 0;
0524: width = size.width;
0525: height = size.height;
0526: if (width <= 0)
0527: width = 1;
0528: if (height <= 0)
0529: height = 1;
0530: Component c = this ;
0531: while (true) {
0532: if (c != this && c instanceof WingComponent) {
0533: synchronized (c) {
0534: if (((WingComponent) c).wingValid != REVALIDATE_REPAINT)
0535: ((WingComponent) c).wingValid = REVALIDATE;
0536: else
0537: return;
0538: }
0539: }
0540: if (!c.isVisible())
0541: return;
0542: Rectangle bounds = c.getBounds();
0543: if (x < 0) {
0544: width += x;
0545: x = 0;
0546: }
0547: if (y < 0) {
0548: height += y;
0549: y = 0;
0550: }
0551: if (x + width > bounds.width)
0552: width = bounds.width - x;
0553: if (y + height > bounds.height)
0554: height = bounds.height - y;
0555: if (width <= 0 || height <= 0) {
0556: x = y = 0;
0557: width = height = 1;
0558: }
0559: if (bounds.height < 1)
0560: bounds.height = 1;
0561: if (bounds.width < 1)
0562: bounds.width = 1;
0563: if (c instanceof Window || c instanceof Applet)
0564: break;
0565: c = c.getParent();
0566: if (c == null)
0567: return;
0568: x += bounds.x;
0569: y += bounds.y;
0570: }
0571: c.repaint(x, y, width, height);
0572: }
0573:
0574: /**
0575: * validate now if invalidated with revalidateAndRepaint
0576: *
0577: * <br><br><strong>This method acquire TreeLock</strong>
0578: * Synchronization with TreeLock should be provided by the method that calls this one
0579: */
0580: protected void wingValidate() {
0581: if (wingValid == VALID && isValid())
0582: return;
0583:
0584: Container p = getParent();
0585: if (p instanceof WingComponent
0586: && (((WingComponent) p).wingValid != VALID || !p
0587: .isValid())) {
0588: ((WingComponent) p).wingValidate();
0589: return;
0590: }
0591:
0592: wingInvalidateTree();
0593:
0594: if (isValid())
0595: return;
0596:
0597: //validate topmost parent
0598: Container c = this ;
0599: while (true) {
0600: if (c instanceof Window || c instanceof Applet) {
0601: c.validate();
0602: break;
0603: }
0604: p = c.getParent();
0605: if (p == null)
0606: break;
0607: if (p.isValid()) {
0608: c.validate();
0609: break;
0610: }
0611: c = p;
0612: }
0613: }
0614:
0615: /**
0616: * <br><br><strong>This method acquire TreeLock</strong>
0617: */
0618: private void wingInvalidateTree() {
0619: if (!isVisible())
0620: return;
0621:
0622: synchronized (this ) {
0623: if (wingValid == VALID)
0624: return;
0625: wingValid = VALIDATING;
0626: }
0627: invalidate();
0628: Component c;
0629: for (int i = 0; i < getComponentCount(); i++) {
0630: c = getComponent(i);
0631: if (c instanceof WingComponent && c.isVisible())
0632: ((WingComponent) c).wingInvalidateTree();
0633: }
0634: }
0635:
0636: protected void validateTree() {
0637: super .validateTree();
0638: synchronized (this ) {
0639: if (wingValid == VALIDATING)
0640: wingValid = VALID;
0641: }
0642: }
0643:
0644: /**
0645: * <br><br><strong>This method acquire TreeLock</strong>
0646: */
0647: public void invalidate() {
0648: synchronized (this ) {
0649: if (!wingPrefSizeSet) {
0650: wingPrefSize = null;
0651: }
0652:
0653: if (wingValid == VALID) {
0654: wingValid = INVALID;
0655: }
0656: }
0657: super .invalidate();
0658: }
0659:
0660: public void update(Graphics g) {
0661: paint(g);
0662: }
0663:
0664: public void paint(Graphics g) {
0665: mrRoot = this ;
0666: if (dispatchEventsOET()) {
0667: // skip repaint areas 1x1 called by postOnEventThread
0668: Rectangle cb = g.getClipBounds();
0669: if (cb != null && cb.height <= 1 || cb.width <= 1)
0670: return;
0671: }
0672: synchronized (getTreeLock()) {
0673: wingValidate();
0674: WingToolkit.paintBuffered(this , g);
0675: }
0676: }
0677:
0678: public void paintComponent(Graphics g) {
0679: paintBackground(g);
0680: boolean delay = WingComponent.debugDelay > 0;
0681: if (delay) {
0682: synchronized (this ) {
0683: try {
0684: wait(WingComponent.debugDelay);
0685: } catch (InterruptedException e) {
0686: }
0687: }
0688: }
0689: wingPaint(g);
0690: WingToolkit.paintContainer(this , g);
0691: }
0692:
0693: /**
0694: * Paints the component background.
0695: * Uses current item renderer and style to paint the background.
0696: * @param g
0697: */
0698: public void paintBackground(Graphics g) {
0699: Dimension size = getSize();
0700: Style st = getStyle();
0701: getRenderer().drawItem(g, 0, 0, size.width, size.height, null,
0702: this , st, st.margin, CENTER, RIGHT, null);
0703: }
0704:
0705: /**
0706: * Paints the component content.
0707: * Override this method instead of <code>paint</code> to paint
0708: * component content.
0709: * @param g
0710: */
0711: public void wingPaint(Graphics g) {
0712: }
0713:
0714: public boolean imageUpdate(Image img, int infoflags, int x, int y,
0715: int w, int h) {
0716: if (!isShowing())
0717: return false;
0718: Dimension size = getSize();
0719: return repaintVisible(0, 0, size.width, size.height)
0720: && (infoflags & (ALLBITS | ABORT)) == 0;
0721: }
0722:
0723: /**
0724: * Repaints this component.
0725: **/
0726: public void repaintVisible() {
0727: synchronized (this ) {
0728: if (wingValid == REVALIDATE_REPAINT)
0729: return;
0730: if (wingValid == REVALIDATE)
0731: wingValid = REVALIDATE_REPAINT;
0732: }
0733: Dimension size = getSize();
0734: repaintVisible(0, 0, size.width, size.height);
0735: }
0736:
0737: /**
0738: * Repaints visible area of this component.
0739: **/
0740: public boolean repaintVisible(int x, int y, int width, int height) {
0741: Component c = this ;
0742: while (true) {
0743: if (c == null || !c.isVisible())
0744: return false;
0745: Dimension size = c.getSize();
0746: if (x + width > size.width)
0747: width = size.width - x;
0748: if (y + height > size.height)
0749: height = size.height - y;
0750: if (x < 0) {
0751: width += x;
0752: x = 0;
0753: }
0754: if (y < 0) {
0755: height += y;
0756: y = 0;
0757: }
0758: if (width <= 0 || height <= 0)
0759: return false;
0760: if (c instanceof Window || c instanceof Applet)
0761: break;
0762: Point pos = c.getLocation();
0763: x += pos.x;
0764: y += pos.y;
0765: c = c.getParent();
0766: }
0767: c.repaint(x, y, width, height);
0768: return true;
0769: }
0770:
0771: ////////////////////////////////////////////////////////
0772: // ScrollView support
0773:
0774: /**
0775: * Returns result of <code>{@link #getViewSize()}</code> or <code>getSize()</code>
0776: * @return result of <code>{@link #getViewSize()}</code> or if it was <code>null getSize()</code>
0777: */
0778: public Dimension getViewOrSize() {
0779: Dimension r = getViewSize();
0780: if (r != null)
0781: return r;
0782: return getSize();
0783: }
0784:
0785: /**
0786: * Returns the size of <code>WingScrollView</code> containing this component or <code>null</code>
0787: * if this component is not contained in <code>WingScrollPane</code>.
0788: * @return the size of <code>WingScrollView</code> containing this component or <code>null</code>
0789: */
0790: public Dimension getViewSize() {
0791: Container p = getParent();
0792: if (p instanceof WingComponent) {
0793: return ((WingComponent) p).getViewSize(this );
0794: }
0795: return null;
0796: }
0797:
0798: protected Dimension getViewSize(WingComponent child) {
0799: return null;
0800: }
0801:
0802: /**
0803: * Sets scroll increments for this component.
0804: * Called by <code>WingScrollPane</code> to obtain component scroll increments
0805: * @param unit unit increments
0806: * @param block block increments
0807: */
0808: public void getScrollIncrements(Point unit, Point block) {
0809: unit.x = unit.y = 1;
0810: Dimension viewSize = getViewOrSize();
0811: block.x = (viewSize.width * 8) / 10;
0812: if (block.x <= 0)
0813: block.x = 1;
0814: block.y = (viewSize.height * 8) / 10;
0815: if (block.y <= 0)
0816: block.y = 1;
0817: }
0818:
0819: /**
0820: * Scrolls the view so that Rectangle within the view becomes visible.
0821: *
0822: * <br><br><strong>This method acquire TreeLock</strong>
0823: * @param r
0824: */
0825: public void scrollRectToVisible(Rectangle r) {
0826: if (r == null)
0827: return;
0828: Container p = getParent();
0829: if (p instanceof WingComponent) {
0830: ((WingComponent) p).doScrollRectToVisible(r);
0831: }
0832: }
0833:
0834: /**
0835: * <br><br><strong>This method acquire TreeLock</strong>
0836: */
0837: protected void doScrollRectToVisible(Rectangle r) {
0838: }
0839:
0840: ////////////////////////////////////////////////////////
0841:
0842: public void setEnabled(boolean b) {
0843: super .setEnabled(b);
0844: repaint();
0845: }
0846:
0847: public Frame getParentFrame() {
0848: for (Component c = this ; c != null; c = c.getParent()) {
0849: if (c instanceof Frame)
0850: return (Frame) c;
0851: }
0852: return null;
0853: }
0854:
0855: //////////////////////////////////////////////////////////
0856: // cancel popup method
0857:
0858: protected static void setPopup(WingComponent popup) {
0859: WingComponent old;
0860: synchronized (WingComponent.class) {
0861: old = WingComponent.popup;
0862: if (old == popup)
0863: return;
0864: WingComponent.popup = popup;
0865: popupClick = (popupClick == 20 || popupClick == 40 || popupClick == 60) ? 40
0866: : 50;
0867: }
0868: if (old != null)
0869: old.cancelPopup(popup);
0870: }
0871:
0872: protected static void clrPopup(WingComponent popup) {
0873: synchronized (WingComponent.class) {
0874: if (WingComponent.popup == popup) {
0875: WingComponent.popup = null;
0876: popupClick = (popupClick == 20 || popupClick == 40 || popupClick == 60) ? 20
0877: : 10;
0878: }
0879: }
0880: }
0881:
0882: protected void cancelPopup(WingComponent src) {
0883: }
0884:
0885: //////////////////////////////////////////////////////////
0886:
0887: protected void processMouseEvent(MouseEvent e) {
0888: if (e.getSource() != this )
0889: return;
0890:
0891: popupClick = (popupClick == 50) ? 60 : 20;
0892:
0893: tipStartTimer.stop();
0894: hideTooltip();
0895:
0896: wingProcessMouseEvent(e);
0897: super .processMouseEvent(e);
0898:
0899: int id = e.getID();
0900:
0901: if (id == MouseEvent.MOUSE_PRESSED && popupClick == 60) {
0902: WingComponent p = popup;
0903: if (p != null)
0904: p.cancelPopup(this );
0905: } else if (id == MouseEvent.MOUSE_ENTERED) {
0906: lastTip = null;
0907: }
0908: popupClick = (popupClick == 20) ? 10 : 50;
0909: }
0910:
0911: protected void processMouseMotionEvent(MouseEvent e) {
0912: if (e.getID() == MouseEvent.MOUSE_MOVED) {
0913: Point p = e.getPoint();
0914: Object tip = getTooltipAt(p.x, p.y);
0915: if (tip != lastTip) {
0916: hideTooltip();
0917: tipStartTimer.stop();
0918: if (tip != null) {
0919: tipStartTimer.setTarget(this );
0920: tipStartTimer.setData(p);
0921: tipStartTimer.start();
0922: }
0923: }
0924: }
0925: wingProcessMouseEvent(e);
0926: super .processMouseMotionEvent(e);
0927: }
0928:
0929: /**
0930: * receives both MouseMotion and Mouse events<br>
0931: * @param e
0932: */
0933: protected void wingProcessMouseEvent(MouseEvent e) {
0934: }
0935:
0936: //////////////////////////////////////////////////////////
0937: // mouse wheel support
0938: public void mouseWheelMoved(Object source, int wheelRotation) {
0939: }
0940:
0941: //////////////////////////////////////////////////////////
0942:
0943: public void wingHierarchyChanged(boolean showing) {
0944: }
0945:
0946: //////////////////////////////////////////////////////////
0947:
0948: public void wingRequestFocusInWindow() {
0949: WingToolkit.the().requestFocusInWindow(this );
0950: }
0951:
0952: /**
0953: * Sets the focusable state of this WingComponent to the specified value. This
0954: * value overrides the Component default focusability.
0955: *
0956: * @param focusable indicates whether this Component is focusable
0957: **/
0958: public void setWingFocusable(boolean focusable) {
0959: wingFocusable = focusable;
0960: }
0961:
0962: public boolean isFocusTraversable() {
0963: return isEnabled() && wingFocusable;
0964: }
0965:
0966: protected void processKeyEvent(KeyEvent e) {
0967: super .processKeyEvent(e);
0968: if (!e.isConsumed()) {
0969: wingProcessKeyEvent(e, null);
0970: }
0971: }
0972:
0973: //////////////////////////////////////////////////////////
0974:
0975: /**
0976: * Adds the specified action listener to receive action events from
0977: * this component. Action events occur when a user presses or releases
0978: * the mouse over a button, checkbox, menu item and so on.
0979: * In WingS action events are dispatched by the component posting the event
0980: * and then by its parents.
0981: * If l is null, no exception is thrown and no action is performed.
0982: * @param l the action listener
0983: * @see #wingProcessActionEvent(ActionEvent)
0984: */
0985: public void addActionListener(ActionListener l) {
0986: actionListener = AWTEventMulticaster.add(actionListener, l);
0987: }
0988:
0989: /**
0990: * Removes the specified action listener so that it no longer
0991: * receives action events from this component.
0992: * @param l the action listener
0993: */
0994: public void removeActionListener(ActionListener l) {
0995: actionListener = AWTEventMulticaster.remove(actionListener, l);
0996: }
0997:
0998: /**
0999: * Processes action events occurring on this component
1000: * by dispatching them to any registered
1001: * <code>ActionListener</code> objects
1002: * and then invokes <code>getParent().wingProcessActionEvent(e)</code>
1003: * if the parent of this component is instance of <code>WingComponent</code>
1004: * @param e the action event
1005: */
1006: protected void wingProcessActionEvent(ActionEvent e) {
1007: Object src = e.getSource();
1008: if (src == tipStartTimer) {
1009: showTooltip((Point) tipStartTimer.getData());
1010: } else if (src == tipStopTimer) {
1011: hideTooltip();
1012: } else {
1013: ActionListener l = actionListener;
1014: if (l != null)
1015: l.actionPerformed(e);
1016:
1017: WingComponent et = popupOwner;
1018: Container p;
1019: if (et != null)
1020: et.wingProcessActionEvent(e);
1021: else if ((p = getParent()) instanceof WingComponent) {
1022: ((WingComponent) p).wingProcessActionEvent(e);
1023: }
1024: }
1025: }
1026:
1027: /**
1028: * Adds the specified item listener to receive item events from
1029: * this component.
1030: * In WingS item events are dispatched by the component posting the event
1031: * and then by its parents.
1032: * If l is <code>null</code>, no exception is thrown and no action
1033: * is performed.
1034: * @param l the item listener
1035: * @see #wingProcessItemEvent(ItemEvent)
1036: */
1037: public void addItemListener(ItemListener l) {
1038: itemListener = AWTEventMulticaster.add(itemListener, l);
1039: }
1040:
1041: /**
1042: * Removes the specified item listener so that it no longer receives
1043: * item events from this component.
1044: * If l is <code>null</code>, no exception is thrown and no
1045: * action is performed.
1046: * @param l the item listener
1047: */
1048: public void removeItemListener(ItemListener l) {
1049: itemListener = AWTEventMulticaster.remove(itemListener, l);
1050: }
1051:
1052: /**
1053: * Processes item events occurring on this component
1054: * by dispatching them to any registered
1055: * <code>ItemListener</code> objects
1056: * and then invokes <code>getParent().wingProcessItemEvent(e)</code>
1057: * if the parent of this component is instance of <code>WingComponent</code>
1058: * @param e the action event
1059: */
1060: protected void wingProcessItemEvent(ItemEvent e) {
1061: ItemListener l = itemListener;
1062: if (l != null)
1063: l.itemStateChanged(e);
1064:
1065: WingComponent et = popupOwner;
1066: Container p;
1067: if (et != null)
1068: et.wingProcessItemEvent(e);
1069: else if ((p = getParent()) instanceof WingComponent) {
1070: ((WingComponent) p).wingProcessItemEvent(e);
1071: }
1072: }
1073:
1074: protected void wingProcessKeyEvent(KeyEvent e,
1075: WingComponent redirecting) {
1076: dispatchShortcuts(e);
1077: if (!e.isConsumed()) {
1078: WingComponent et = popupOwner;
1079: Container p;
1080: if (et != null)
1081: et.wingProcessKeyEvent(e, redirecting);
1082: else if ((p = getParent()) instanceof WingComponent)
1083: ((WingComponent) p).wingProcessKeyEvent(e, redirecting);
1084: }
1085: }
1086:
1087: protected void dispatchShortcuts(KeyEvent e) {
1088: int i = 0, keyCode = e.getKeyCode(), modifiers = e
1089: .getModifiers();
1090: while (shortcuts != null && !e.isConsumed()
1091: && e.getID() != KeyEvent.KEY_TYPED) {
1092: Shortcut s = null;
1093: synchronized (shortcuts) {
1094: if (i >= shortcuts.size())
1095: break;
1096: s = (Shortcut) shortcuts.elementAt(i);
1097: i++;
1098: }
1099: if (s.getShortcutCode() == keyCode
1100: && s.getShortcutModifiers() == modifiers) {
1101: s.processShortcut(e);
1102: }
1103: }
1104: }
1105:
1106: /**
1107: * Adds a keyboard shortcut to this component.
1108: * @param s
1109: */
1110: public void addShortcut(Shortcut s) {
1111: if (s.getShortcutCode() == KeyEvent.VK_UNDEFINED)
1112: return;
1113: if (shortcuts == null)
1114: shortcuts = new Vector();
1115: if (!shortcuts.contains(s))
1116: shortcuts.addElement(s);
1117: }
1118:
1119: /**
1120: * Removes a keyboard shortcut from this component.
1121: * @param s
1122: */
1123: public void removeShortcut(Shortcut s) {
1124: if (shortcuts != null)
1125: shortcuts.removeElement(s);
1126: }
1127:
1128: /**
1129: * Adds every component in the tree that implements
1130: * <code>Shortcut</code> to this component as a shortcut.
1131: * @param source
1132: */
1133: public void addShortcuts(WingComponent source) {
1134: source.updateShortcuts(this , true);
1135: }
1136:
1137: /**
1138: * Removes every component in the tree that implements
1139: * <code>Shortcut</code> added to this component as a shortcut.
1140: * @param source
1141: */
1142: public void removeShortcuts(WingComponent source) {
1143: source.updateShortcuts(this , false);
1144: }
1145:
1146: protected void updateShortcuts(WingComponent target, boolean add) {
1147: if (target == null)
1148: target = this ;
1149: if (this instanceof Shortcut) {
1150: if (add)
1151: target.addShortcut((Shortcut) this );
1152: else
1153: target.removeShortcut((Shortcut) this );
1154: }
1155: synchronized (getTreeLock()) {
1156: for (int i = 0; i < getComponentCount(); i++) {
1157: if (getComponent(i) instanceof WingComponent) {
1158: ((WingComponent) getComponent(i)).updateShortcuts(
1159: target, add);
1160: }
1161: }
1162: }
1163: }
1164:
1165: ///////////////////////////////////////////////////////////
1166: // process on event thread
1167:
1168: protected static final Vector oetEvents = new Vector();
1169: protected static final Vector oetTargets = new Vector();
1170: protected static Thread eventThread;
1171: protected static boolean oetNoQueue = false;
1172: protected static WingComponent mrRoot;
1173:
1174: /**
1175: * Posts an event on the event thread.
1176: * @param e an instance of <code>java.awt.AWTEvent</code>
1177: */
1178: public void postOnEventThread(AWTEvent e) {
1179: if (Thread.currentThread() == eventThread) {
1180: dispatchEventOET(e);
1181: } else {
1182: // boolean invoke;
1183: synchronized (oetEvents) {
1184: // invoke= (oetEvents.size()==0);
1185: oetEvents.addElement(e);
1186: oetTargets.addElement(this );
1187: }
1188: if (/*invoke &&*/!oetNoQueue) {
1189: try {
1190: Toolkit t = Toolkit.getDefaultToolkit();
1191: if (t != null) {
1192: EventQueue queue = t.getSystemEventQueue();
1193: if (queue != null) {
1194: queue.postEvent(new ComponentEvent(this ,
1195: POET_EVENT));
1196: return;
1197: // invoke= false;
1198: }
1199: }
1200: } catch (SecurityException ex) {
1201: oetNoQueue = true;
1202: }
1203: }
1204: // if(invoke)
1205: {
1206: WingComponent c = mrRoot;
1207: if (c == null)
1208: c = this ;
1209: c.repaint(0, 0, 1, 1);
1210: }
1211: }
1212: }
1213:
1214: protected boolean dispatchEventsOET() {
1215: int n = oetEvents.size();
1216: if (n == 0)
1217: return false;
1218: Thread ct = Thread.currentThread();
1219: if (eventThread != ct) {
1220: if (ct.getClass().getName().indexOf("EventDispatchThread") != -1) {
1221: eventThread = ct;
1222: } else
1223: return false;
1224: }
1225:
1226: for (int i = 0; i < n; i++) {
1227: AWTEvent e;
1228: WingComponent target;
1229: synchronized (oetEvents) {
1230: if (oetEvents.size() == 0)
1231: break;
1232: e = (AWTEvent) oetEvents.elementAt(0);
1233: oetEvents.removeElementAt(0);
1234: target = (WingComponent) oetTargets.elementAt(0);
1235: oetTargets.removeElementAt(0);
1236: }
1237: target.dispatchEventOET(e);
1238: }
1239: return true;
1240: }
1241:
1242: protected void dispatchEventOET(AWTEvent e) {
1243: if (e instanceof ActionEvent)
1244: wingProcessActionEvent((ActionEvent) e);
1245: else if (e instanceof ItemEvent)
1246: wingProcessItemEvent((ItemEvent) e);
1247: }
1248:
1249: protected void processEvent(AWTEvent e) {
1250: if (e.getID() == POET_EVENT) {
1251: dispatchEventsOET();
1252: return;
1253: }
1254: super .processEvent(e);
1255: }
1256:
1257: private static String clipboard;
1258:
1259: public String getClipboardText() {
1260: try {
1261: Clipboard cp = Toolkit.getDefaultToolkit()
1262: .getSystemClipboard();
1263: Transferable tr = cp.getContents(this );
1264: return (String) tr.getTransferData(DataFlavor.stringFlavor);
1265: } catch (Exception e1) {
1266: return clipboard;
1267: }
1268: }
1269:
1270: public void setClipboardText(String text) {
1271: try {
1272: Clipboard cp = Toolkit.getDefaultToolkit()
1273: .getSystemClipboard();
1274: StringSelection ss = new StringSelection(text);
1275: cp.setContents(ss, ss);
1276: } catch (Exception e2) {
1277: clipboard = text;
1278: }
1279: }
1280: }
|