0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.api.visual.widget;
0042:
0043: import org.netbeans.api.visual.action.WidgetAction;
0044: import org.netbeans.api.visual.border.Border;
0045: import org.netbeans.api.visual.border.BorderFactory;
0046: import org.netbeans.api.visual.layout.Layout;
0047: import org.netbeans.api.visual.layout.LayoutFactory;
0048: import org.netbeans.api.visual.model.ObjectState;
0049: import org.netbeans.modules.visual.util.GeomUtil;
0050: import org.netbeans.modules.visual.widget.WidgetAccessibleContext;
0051: import org.openide.util.Lookup;
0052:
0053: import javax.accessibility.AccessibleContext;
0054: import javax.accessibility.Accessible;
0055: import java.awt.*;
0056: import java.awt.geom.AffineTransform;
0057: import java.util.*;
0058: import java.util.List;
0059:
0060: /**
0061: * A scene is a tree of small building blocks called widgets and represented by this class.
0062: * <p>
0063: * Each widget has a origin location specified relatively to the location its parent widget
0064: * and placement is specified be its boundary.
0065: * <p>
0066: * The widget is also responsible for rendering the region. The widget is an abstract implementation
0067: * and does not have render anything except borders and background. There are various built-in widget
0068: * each for a specific visualization. The widget also holds general properties like foreground, opacity, ...
0069: * that could be reused by the high-level widgets.
0070: * <p>
0071: * The widget has a layout assigned. The layout takes care about resolving the placement of children widgets.
0072: * For that it can use various properties like preferredLocation, preferredBounds, ...
0073: * When the widget is resolved (placed) than the read only location and bounds properties contains
0074: * resolved location and boundary of a widget.
0075: * <p>
0076: * Each widget has a chain of actions. Actions defined defines a behaviour of the widget. E.g. MoveAction
0077: * makes the widget moveable. Also there is possible to create/assign other chains that will be activated
0078: * based on the active tool of a scene.
0079: * <p>
0080: * The widget have its state specified by ObjectState class. When the widget state is change,
0081: * notifyStateChanged is called to notify about it. The state is automatically updated by high-level scenes
0082: * and actions. Yherefore you can define your own look and feel directly in the that method.
0083: *
0084: * Since version 2.6 Widget class implements Accessible interface.
0085: *
0086: * @author David Kaspar
0087: */
0088: // TODO - Should Widget be an abstract class?
0089: public class Widget implements Accessible {
0090:
0091: static final String MESSAGE_NULL_BOUNDS = "Scene.validate was not called after last change. Widget is not validated. See first Q/A at http://graph.netbeans.org/faq.html page.";
0092:
0093: private static final HashMap<String, WidgetAction.Chain> EMPTY_HASH_MAP = new HashMap<String, WidgetAction.Chain>(
0094: 0);
0095:
0096: private Scene scene;
0097: private Widget parentWidget;
0098:
0099: private List<Widget> children;
0100: private List<Widget> childrenUm;
0101:
0102: private HashMap<Widget, Object> constraints;
0103:
0104: private WidgetAction.Chain actionsChain;
0105: private HashMap<String, WidgetAction.Chain> toolsActions = EMPTY_HASH_MAP;
0106:
0107: private ArrayList<Widget.Dependency> dependencies;
0108:
0109: private boolean visible = true;
0110:
0111: private boolean opaque;
0112: private Paint background;
0113: private Color foreground;
0114: private Font font;
0115: private Border border;
0116: private Layout layout;
0117: private Point preferredLocation;
0118: private Dimension minimumSize;
0119: private Dimension maximumSize;
0120: private Dimension preferredSize;
0121: private Rectangle preferredBounds;
0122: private boolean checkClipping;
0123: private boolean enabled;
0124:
0125: private ObjectState state = ObjectState.createNormal();
0126:
0127: private Cursor cursor;
0128: private String toolTipText;
0129: private AccessibleContext accessibleContext;
0130:
0131: private Point location;
0132: private Rectangle bounds;
0133: private Rectangle calculatedPreferredBounds;
0134:
0135: private boolean requiresFullValidation;
0136: private boolean requiresPartValidation;
0137:
0138: private boolean requiresFullJustification;
0139: private boolean requiresPartJustification;
0140:
0141: /**
0142: * Creates a new widget which will be used in a specified scene.
0143: * @param scene the scene where the widget is going to be used
0144: */
0145: public Widget(Scene scene) {
0146: if (scene == null)
0147: scene = (Scene) this ;
0148: this .scene = scene;
0149: children = new ArrayList<Widget>();
0150: childrenUm = Collections.unmodifiableList(children);
0151:
0152: actionsChain = new WidgetAction.Chain();
0153:
0154: opaque = false;
0155: font = null;
0156: background = Color.WHITE;
0157: foreground = Color.BLACK;
0158: border = BorderFactory.createEmptyBorder();
0159: layout = LayoutFactory.createAbsoluteLayout();
0160: preferredLocation = null;
0161: preferredBounds = null;
0162: checkClipping = false;
0163: enabled = true;
0164:
0165: location = new Point();
0166: bounds = null;
0167: calculatedPreferredBounds = null;
0168: requiresFullValidation = true;
0169: requiresPartValidation = true;
0170: }
0171:
0172: /**
0173: * Returns a scene where the widget is assigned
0174: * @return the scene
0175: */
0176: public final Scene getScene() {
0177: return scene;
0178: }
0179:
0180: /**
0181: * Returns a Graphics2D instance with is assigned to the scene.
0182: * Usually used in the calculatedClientArea and paintWidget method.
0183: * @return the Graphics2D instance; null if the scene view is not created or visible yet
0184: */
0185: protected Graphics2D getGraphics() {
0186: return scene.getGraphics();
0187: }
0188:
0189: /**
0190: * Returns a parent widget.
0191: * @return the parent widget
0192: */
0193: public final Widget getParentWidget() {
0194: return parentWidget;
0195: }
0196:
0197: /**
0198: * Returns a list of children widgets.
0199: * @return the list of children widgets
0200: */
0201: public final List<Widget> getChildren() {
0202: return childrenUm;
0203: }
0204:
0205: /**
0206: * Adds a child widget as the last one.
0207: * @param child the child widget to be added
0208: */
0209: public final void addChild(Widget child) {
0210: addChild(child, null);
0211: }
0212:
0213: /**
0214: * Adds a child widget as the last one.
0215: * @param child the child widget to be added
0216: * @param constraint the constraint assigned to the child widget
0217: */
0218: public final void addChild(Widget child, Object constraint) {
0219: assert child.parentWidget == null;
0220: Widget widget = this ;
0221: while (widget != null) {
0222: assert widget != child;
0223: widget = widget.parentWidget;
0224: }
0225: children.add(child);
0226: child.parentWidget = this ;
0227: setChildConstraint(child, constraint);
0228: child.revalidate();
0229: revalidate();
0230: scene.dispatchNotifyAdded(child);
0231: }
0232:
0233: /**
0234: * Adds a child at a specified index
0235: * @param index the index (the child is added before the one that is not the index place)
0236: * @param child the child widget
0237: */
0238: public final void addChild(int index, Widget child) {
0239: addChild(index, child, null);
0240: }
0241:
0242: /**
0243: * Adds a child at a specified index
0244: * @param index the index (the child is added before the one that is not the index place)
0245: * @param child the child widget
0246: * @param constraint the constraint assigned to the child widget
0247: */
0248: public final void addChild(int index, Widget child,
0249: Object constraint) {
0250: assert child.parentWidget == null;
0251: children.add(index, child);
0252: child.parentWidget = this ;
0253: setChildConstraint(child, constraint);
0254: child.revalidate();
0255: revalidate();
0256: if (accessibleContext != null
0257: && accessibleContext instanceof WidgetAccessibleContext)
0258: ((WidgetAccessibleContext) accessibleContext)
0259: .notifyChildAdded(this , child);
0260: scene.dispatchNotifyAdded(child);
0261: }
0262:
0263: /**
0264: * Removes a child widget.
0265: * @param child the child widget
0266: */
0267: public final void removeChild(Widget child) {
0268: assert child.parentWidget == this ;
0269: setChildConstraint(child, null);
0270: child.parentWidget = null;
0271: children.remove(child);
0272: child.revalidate();
0273: revalidate();
0274: if (accessibleContext != null
0275: && accessibleContext instanceof WidgetAccessibleContext)
0276: ((WidgetAccessibleContext) accessibleContext)
0277: .notifyChildRemoved(this , child);
0278: scene.dispatchNotifyRemoved(child);
0279: }
0280:
0281: /**
0282: * Removes the widget from its parent.
0283: */
0284: public final void removeFromParent() {
0285: if (parentWidget != null)
0286: parentWidget.removeChild(this );
0287: }
0288:
0289: /**
0290: * Removes all children widgets.
0291: */
0292: public final void removeChildren() {
0293: while (!children.isEmpty())
0294: removeChild(children.get(0));
0295: }
0296:
0297: /**
0298: * Adds all children in a specified list.
0299: * @param children the list of children widgets
0300: */
0301: public final void addChildren(List<? extends Widget> children) {
0302: for (Widget child : children)
0303: addChild(child);
0304: }
0305:
0306: /**
0307: * Removes all children widget that are in a specified list.
0308: * @param widgets the list of children widgets to be removed
0309: */
0310: public final void removeChildren(List<Widget> widgets) {
0311: for (Widget widget : widgets)
0312: removeChild(widget);
0313: }
0314:
0315: void dispatchNotifyAddedCore() {
0316: notifyAdded();
0317: for (Widget widget : children)
0318: widget.dispatchNotifyAddedCore();
0319: }
0320:
0321: void dispatchNotifyRemovedCore() {
0322: notifyRemoved();
0323: for (Widget widget : children)
0324: widget.dispatchNotifyRemovedCore();
0325: }
0326:
0327: /**
0328: * This method is called to notify that the view is shown.
0329: * Note: You must not modify a tree of widgets from within this method.
0330: * It means: do not call addChild, removeChild and similar methods.
0331: */
0332: protected void notifyAdded() {
0333: }
0334:
0335: /**
0336: * This method is called to notify that the view is hidden.
0337: * Note: You must not modify a tree of widgets from within this method.
0338: * It means: do not call addChild, removeChild and similar methods.
0339: */
0340: protected void notifyRemoved() {
0341: }
0342:
0343: /**
0344: * Brings the widget to the front. Means: the widget becomes the last child in the list of children of the parent widget.
0345: */
0346: public final void bringToFront() {
0347: if (parentWidget == null)
0348: return;
0349: List<Widget> children = parentWidget.children;
0350: int i = children.indexOf(this );
0351: if (i < 0)
0352: return;
0353: children.remove(i);
0354: children.add(children.size(), this );
0355: revalidate();
0356: parentWidget.revalidate();
0357: }
0358:
0359: /**
0360: * Brings the widget to the back. Means: the widget becomes the first child in the list of children of the parent widget.
0361: */
0362: public final void bringToBack() {
0363: if (parentWidget == null)
0364: return;
0365: List<Widget> children = parentWidget.children;
0366: int i = children.indexOf(this );
0367: if (i <= 0)
0368: return;
0369: children.remove(i);
0370: children.add(0, this );
0371: revalidate();
0372: parentWidget.revalidate();
0373: }
0374:
0375: /**
0376: * Returns constraint assigned to a specified child widget.
0377: * @param child the child widget
0378: * @return the constraint
0379: */
0380: public final Object getChildConstraint(Widget child) {
0381: return constraints != null ? constraints.get(child) : null;
0382: }
0383:
0384: /**
0385: * Assigns a constraint to a child widget.
0386: * @param child the child widget
0387: * @param constraint the constraint
0388: */
0389: public final void setChildConstraint(Widget child, Object constraint) {
0390: assert children.contains(child);
0391: if (constraint == null) {
0392: if (constraints != null)
0393: constraints.remove(child);
0394: return;
0395: }
0396: if (constraints == null)
0397: constraints = new HashMap<Widget, Object>();
0398: constraints.put(child, constraint);
0399: }
0400:
0401: /**
0402: * Returns whether the widget is visible.
0403: * @return true if the widget is visible
0404: */
0405: public final boolean isVisible() {
0406: return visible;
0407: }
0408:
0409: /**
0410: * Sets whether the widget is visible.
0411: * @param visible if true, then the widget is visible
0412: */
0413: public final void setVisible(boolean visible) {
0414: this .visible = visible;
0415: revalidate();
0416: }
0417:
0418: /**
0419: * Returns whether the widget is enabled.
0420: * If the widget is disabled then any event is processed by assigned actions.
0421: * @return true if the widget is enabled.
0422: */
0423: public final boolean isEnabled() {
0424: return enabled;
0425: }
0426:
0427: /**
0428: * Sets whether the widget is enabled.
0429: * If the widget is disabled then any event is processed by assigned actions.
0430: * @param enabled if true, then the widget is enabled
0431: */
0432: public final void setEnabled(boolean enabled) {
0433: this .enabled = enabled;
0434: }
0435:
0436: /**
0437: * Returns a default action chain.
0438: * @return the default action chain.
0439: */
0440: public final WidgetAction.Chain getActions() {
0441: return actionsChain;
0442: }
0443:
0444: /**
0445: * Returns already created action chain for a specified tool.
0446: * @param tool the tool
0447: * @return the action chain; null, if no chain for the tool exists
0448: */
0449: public final WidgetAction.Chain getActions(String tool) {
0450: return toolsActions.get(tool);
0451: }
0452:
0453: /**
0454: * Creates and returns an action chain for a specified tool.
0455: * @param tool the tool
0456: * @return the action chain
0457: */
0458: public final WidgetAction.Chain createActions(String tool) {
0459: if (tool == null)
0460: return actionsChain;
0461: if (toolsActions == EMPTY_HASH_MAP) {
0462: toolsActions = new HashMap<String, WidgetAction.Chain>();
0463: toolsActions.put(null, actionsChain);
0464: }
0465: WidgetAction.Chain chain = toolsActions.get(tool);
0466: if (chain == null) {
0467: chain = new WidgetAction.Chain();
0468: toolsActions.put(tool, chain);
0469: }
0470: return chain;
0471: }
0472:
0473: /**
0474: * Returns a lookup of the widget.
0475: * @return the lookup
0476: */
0477: public Lookup getLookup() {
0478: return Lookup.EMPTY;
0479: }
0480:
0481: /**
0482: * Adds a dependency listener which is notified when the widget placement or boundary is going to be changed or similar thing happens to its parent widget.
0483: * @param dependency the dependency listener
0484: */
0485: public final void addDependency(Widget.Dependency dependency) {
0486: if (dependencies == null)
0487: dependencies = new ArrayList<Widget.Dependency>();
0488: dependencies.add(dependency);
0489: }
0490:
0491: /**
0492: * Removes a dependency listener.
0493: * @param dependency the dependency listener
0494: */
0495: public final void removeDependency(Widget.Dependency dependency) {
0496: if (dependencies == null)
0497: return;
0498: dependencies.remove(dependency);
0499: }
0500:
0501: /**
0502: * Returns a collection of registered dependencies.
0503: * @return the unmodifiable collection of dependencies
0504: * @since 2.6
0505: */
0506: public final Collection<Dependency> getDependencies() {
0507: return dependencies != null ? Collections
0508: .unmodifiableCollection(dependencies) : Collections
0509: .<Dependency> emptyList();
0510: }
0511:
0512: /**
0513: * Returns whether the widget is opaque.
0514: * @return true, if the widget is opaque
0515: */
0516: public final boolean isOpaque() {
0517: return opaque;
0518: }
0519:
0520: /**
0521: * Sets the widget opacity.
0522: * @param opaque if true, then the widget is opaque
0523: */
0524: public final void setOpaque(boolean opaque) {
0525: this .opaque = opaque;
0526: repaint();
0527: }
0528:
0529: /**
0530: * Returns the widget background paint.
0531: * @return the background paint
0532: */
0533: public final Paint getBackground() {
0534: return background != null ? background : parentWidget
0535: .getBackground();
0536: }
0537:
0538: /**
0539: * Sets the widget background paint.
0540: * @param background the background paint
0541: */
0542: public final void setBackground(Paint background) {
0543: this .background = background;
0544: repaint();
0545: }
0546:
0547: /**
0548: * Returns the widget foreground color.
0549: * @return the foreground color
0550: */
0551: public final Color getForeground() {
0552: return foreground != null ? foreground : parentWidget
0553: .getForeground();
0554: }
0555:
0556: /**
0557: * Sets the widget foreground color.
0558: * @param foreground the foreground color
0559: */
0560: public final void setForeground(Color foreground) {
0561: this .foreground = foreground;
0562: repaint();
0563: }
0564:
0565: /**
0566: * Returns the font assigned to the widget. If not set yet, then it returns the font of its parent widget.
0567: * @return the font
0568: */
0569: public final Font getFont() {
0570: return font != null ? font : parentWidget.getFont();
0571: }
0572:
0573: /**
0574: * Sets the widget font.
0575: * @param font the font; if null, then widget unassignes its font.
0576: */
0577: public final void setFont(Font font) {
0578: this .font = font;
0579: revalidate();
0580: }
0581:
0582: /**
0583: * Returns the border of the widget.
0584: * @return the border
0585: */
0586: public final Border getBorder() {
0587: return border;
0588: }
0589:
0590: /**
0591: * Sets the border of the widget.
0592: * @param border the border
0593: */
0594: public final void setBorder(Border border) {
0595: assert border != null;
0596: boolean repaintOnly = this .border.getInsets().equals(
0597: border.getInsets());
0598: this .border = border;
0599: revalidate(repaintOnly);
0600: }
0601:
0602: /**
0603: * Sets the Swing layout as the border of the widget.
0604: * @param swingBorder the Swing border
0605: */
0606: public final void setBorder(javax.swing.border.Border swingBorder) {
0607: assert swingBorder != null;
0608: setBorder(BorderFactory.createSwingBorder(scene, swingBorder));
0609: }
0610:
0611: /**
0612: * Returns the layout of the widget.
0613: * @return the layout
0614: */
0615: public final Layout getLayout() {
0616: return layout;
0617: }
0618:
0619: /**
0620: * Sets the layout of the widget.
0621: * @param layout the layout
0622: */
0623: public final void setLayout(Layout layout) {
0624: this .layout = layout;
0625: revalidate();
0626: }
0627:
0628: /**
0629: * Returns a minimum size of the widget.
0630: * @return the minimum size; if null, then no minumum size are set.
0631: */
0632: public final Dimension getMinimumSize() {
0633: return minimumSize != null ? new Dimension(minimumSize) : null;
0634: }
0635:
0636: /**
0637: * Sets a minumum size of the widget
0638: * @param minimumSize the minimum size; if null, then minimum size are unset.
0639: */
0640: public final void setMinimumSize(Dimension minimumSize) {
0641: if (GeomUtil.equals(this .minimumSize, minimumSize))
0642: return;
0643: this .minimumSize = minimumSize;
0644: revalidate();
0645: }
0646:
0647: /**
0648: * Returns a maximum size of the widget.
0649: * @return the maximum size; if null, then no maximum size are set.
0650: */
0651: public final Dimension getMaximumSize() {
0652: return maximumSize != null ? new Dimension(maximumSize) : null;
0653: }
0654:
0655: /**
0656: * Sets a maximum size of the widget
0657: * @param maximumSize the maximum size; if null, then maximum size are unset.
0658: */
0659: public final void setMaximumSize(Dimension maximumSize) {
0660: if (GeomUtil.equals(this .maximumSize, maximumSize))
0661: return;
0662: this .maximumSize = maximumSize;
0663: revalidate();
0664: }
0665:
0666: /**
0667: * Returns a preferred size of the widget.
0668: * @return the preferred size; if null, then no preferred size are set.
0669: */
0670: public final Dimension getPreferredSize() {
0671: return preferredSize != null ? new Dimension(preferredSize)
0672: : null;
0673: }
0674:
0675: /**
0676: * Sets a preferred size of the widget
0677: * @param preferredSize the preferred size; if null, then preferred size are unset.
0678: */
0679: public final void setPreferredSize(Dimension preferredSize) {
0680: if (GeomUtil.equals(this .preferredSize, preferredSize))
0681: return;
0682: this .preferredSize = preferredSize;
0683: revalidate();
0684: }
0685:
0686: /**
0687: * Returns a preferred location of the widget.
0688: * @return the preferred location; if null, then no preferred location is set
0689: */
0690: public final Point getPreferredLocation() {
0691: return preferredLocation != null ? new Point(preferredLocation)
0692: : null;
0693: }
0694:
0695: /**
0696: * Sets a preferred location of the widget.
0697: * @param preferredLocation the preferred location; if null, then the preferred location is unset
0698: */
0699: public final void setPreferredLocation(Point preferredLocation) {
0700: if (GeomUtil.equals(this .preferredLocation, preferredLocation))
0701: return;
0702: this .preferredLocation = preferredLocation;
0703: revalidate();
0704: }
0705:
0706: /**
0707: * Returns whether a preferred bounds are set.
0708: * @return true, if preferred bounds are set
0709: */
0710: public final boolean isPreferredBoundsSet() {
0711: return preferredBounds != null;
0712: }
0713:
0714: /**
0715: * Returns a preferred bounds relatively to the location of the widget. If no preferred bounds are set, then it returns a preferred bounds
0716: * that are calculated from the calculateClientArea method of this widget and location and bounds of the children widgets.
0717: * This calculated bounds are processed by the minimum and maximum bounds too.
0718: * <p>
0719: * This method can be called after child widgets are layed out which is assured in method calls of the <code>Layout</code> interface implementation.
0720: * If preferred bounds are set (check it using <code>isPreferredBoundsSet</code> method), you can call this method at any time.
0721: * @return the preferred bounds
0722: */
0723: public final Rectangle getPreferredBounds() {
0724: Rectangle rect;
0725: if (isPreferredBoundsSet())
0726: rect = new Rectangle(preferredBounds);
0727: else {
0728: if (calculatedPreferredBounds == null)
0729: calculatedPreferredBounds = calculatePreferredBounds();
0730: rect = new Rectangle(calculatedPreferredBounds);
0731: if (preferredSize != null) {
0732: rect.width = preferredSize.width;
0733: rect.height = preferredSize.height;
0734: }
0735: }
0736: if (minimumSize != null) {
0737: if (rect.width < minimumSize.width)
0738: rect.width = minimumSize.width;
0739: if (rect.height < minimumSize.height)
0740: rect.height = minimumSize.height;
0741: }
0742: if (maximumSize != null) {
0743: if (rect.width > maximumSize.width)
0744: rect.width = maximumSize.width;
0745: if (rect.height > maximumSize.height)
0746: rect.height = maximumSize.height;
0747: }
0748: return rect;
0749: }
0750:
0751: private Rectangle calculatePreferredBounds() {
0752: Insets insets = border.getInsets();
0753: Rectangle clientArea = calculateClientArea();
0754: for (Widget child : children) {
0755: if (!child.isVisible())
0756: continue;
0757: Point location = child.getLocation();
0758: Rectangle bounds = child.getBounds();
0759: bounds.translate(location.x, location.y);
0760: clientArea.add(bounds);
0761: }
0762: clientArea.x -= insets.left;
0763: clientArea.y -= insets.top;
0764: clientArea.width += insets.left + insets.right;
0765: clientArea.height += insets.top + insets.bottom;
0766: return clientArea;
0767: }
0768:
0769: /**
0770: * Called to calculate the client area required by the widget without the children widgets.
0771: * @return the calculated client area
0772: */
0773: protected Rectangle calculateClientArea() {
0774: return new Rectangle();
0775: }
0776:
0777: /**
0778: * Sets a preferred bounds that are specified relatively to the location of the widget.
0779: * @param preferredBounds the preferred bounds; if null, then the preferred bounds are unset
0780: */
0781: public final void setPreferredBounds(Rectangle preferredBounds) {
0782: if (GeomUtil.equals(this .preferredBounds, preferredBounds))
0783: return;
0784: this .preferredBounds = preferredBounds;
0785: revalidate();
0786: }
0787:
0788: /**
0789: * Returns whether clipping is used in the widget.
0790: * @return true, if the check clipping is used
0791: */
0792: public final boolean isCheckClipping() {
0793: return checkClipping;
0794: }
0795:
0796: /**
0797: * Sets a clipping for the widget.
0798: * @param checkClipping if true, then the clipping is used
0799: */
0800: public final void setCheckClipping(boolean checkClipping) {
0801: this .checkClipping = checkClipping;
0802: repaint();
0803: }
0804:
0805: /**
0806: * Returns a mouse cursor for a specified local location in the widget.
0807: * @param localLocation the local location
0808: * @return the mouse cursor; default implementation return value of cursor property.
0809: * @since 2.3
0810: */
0811: protected Cursor getCursorAt(Point localLocation) {
0812: return getCursor();
0813: }
0814:
0815: /**
0816: * Returns a mouse cursor for the widget.
0817: * @return the mouse cursor
0818: */
0819: public final Cursor getCursor() {
0820: return cursor;
0821: }
0822:
0823: /**
0824: * Sets a cursor for the widget.
0825: * @param cursor the mouse cursor; if null, the cursor is unset
0826: */
0827: public final void setCursor(Cursor cursor) {
0828: this .cursor = cursor;
0829: }
0830:
0831: /**
0832: * Returns a tool-tip text of the widget.
0833: * @return the tool-tip text
0834: */
0835: public final String getToolTipText() {
0836: return toolTipText;
0837: }
0838:
0839: /**
0840: * Sets a tool-tip of the widget.
0841: * @param toolTipText the tool tip text
0842: */
0843: public final void setToolTipText(String toolTipText) {
0844: this .toolTipText = toolTipText;
0845: }
0846:
0847: /**
0848: * Returns an accessible context of the widget.
0849: * @return the accessible context
0850: */
0851: public final AccessibleContext getAccessibleContext() {
0852: if (accessibleContext == null)
0853: accessibleContext = new WidgetAccessibleContext(this );
0854: return accessibleContext;
0855: }
0856:
0857: /**
0858: * Sets a accessible context of the widget.
0859: * @param accessibleContext the accessible context
0860: */
0861: public final void setAccessibleContext(
0862: AccessibleContext accessibleContext) {
0863: this .accessibleContext = accessibleContext;
0864: }
0865:
0866: /**
0867: * Returns a state of the widget.
0868: * @return the widget state
0869: */
0870: public final ObjectState getState() {
0871: return state;
0872: }
0873:
0874: /**
0875: * Sets a state of the widget.
0876: * @param state the widget state
0877: */
0878: public final void setState(ObjectState state) {
0879: ObjectState previousState = this .state;
0880: this .state = state;
0881: notifyStateChanged(previousState, state);
0882: }
0883:
0884: /**
0885: * Called to notify about the change of the widget state.
0886: * @param previousState the previous state
0887: * @param state the new state
0888: */
0889: protected void notifyStateChanged(ObjectState previousState,
0890: ObjectState state) {
0891: }
0892:
0893: /**
0894: * Converts a location in the local coordination system to the scene coordination system.
0895: * @param localLocation the local location
0896: * @return the scene location
0897: */
0898: public final Point convertLocalToScene(Point localLocation) {
0899: Point sceneLocation = new Point(localLocation);
0900: Widget widget = this ;
0901: while (widget != null) {
0902: if (widget == scene)
0903: break;
0904: Point location = widget.getLocation();
0905: sceneLocation.x += location.x;
0906: sceneLocation.y += location.y;
0907: widget = widget.getParentWidget();
0908: }
0909: return sceneLocation;
0910: }
0911:
0912: /**
0913: * Converts a rectangle in the local coordination system to the scene coordination system.
0914: * @param localRectangle the local rectangle
0915: * @return the scene rectangle
0916: */
0917: public final Rectangle convertLocalToScene(Rectangle localRectangle) {
0918: Rectangle sceneRectangle = new Rectangle(localRectangle);
0919: Widget widget = this ;
0920: while (widget != null) {
0921: if (widget == scene)
0922: break;
0923: Point location = widget.getLocation();
0924: sceneRectangle.x += location.x;
0925: sceneRectangle.y += location.y;
0926: widget = widget.getParentWidget();
0927: }
0928: return sceneRectangle;
0929: }
0930:
0931: /**
0932: * Converts a location in the scene coordination system to the local coordination system.
0933: * @param sceneLocation the scene location
0934: * @return the local location
0935: */
0936: public final Point convertSceneToLocal(Point sceneLocation) {
0937: Point localLocation = new Point(sceneLocation);
0938: Widget widget = this ;
0939: while (widget != null) {
0940: if (widget == scene)
0941: break;
0942: Point location = widget.getLocation();
0943: localLocation.x -= location.x;
0944: localLocation.y -= location.y;
0945: widget = widget.getParentWidget();
0946: }
0947: return localLocation;
0948: }
0949:
0950: /**
0951: * Converts a rectangle in the scene coordination system to the local coordination system.
0952: * @param sceneRectangle the scene rectangle
0953: * @return the local rectangle
0954: */
0955: public final Rectangle convertSceneToLocal(Rectangle sceneRectangle) {
0956: Rectangle localRectangle = new Rectangle(sceneRectangle);
0957: Widget widget = this ;
0958: while (widget != null) {
0959: if (widget == scene)
0960: break;
0961: Point location = widget.getLocation();
0962: localRectangle.x -= location.x;
0963: localRectangle.y -= location.y;
0964: widget = widget.getParentWidget();
0965: }
0966: return localRectangle;
0967: }
0968:
0969: /**
0970: * Returns the resolved location of the widget. The location is specified relatively to the location of the parent widget.
0971: * <p>
0972: * The location is resolved/set by calling <code>resolveBounds</code> method which should be called from <code>Layout</code> interface implementation only.
0973: * Therefore the corrent value is available only after the scene is validated (<code>SceneListener.sceneValidated</code> method).
0974: * Before validation a previous/obsolete or <code>[0,0]</code> value could be returned.
0975: * See <strong>Layout</strong> section in documentation.
0976: * @return the location in the local coordination system of the parent widget
0977: */
0978: public final Point getLocation() {
0979: return location != null ? new Point(location) : null;
0980: }
0981:
0982: /**
0983: * Returns the resolved bounds of the widget. The bounds are specified relatively to the location of the widget.
0984: * <p>
0985: * The location is resolved/set by calling <code>resolveBounds</code> method which should be called from <code>Layout</code> interface implementation only.
0986: * Therefore the corrent value is available only after the scene is validated (<code>SceneListener.sceneValidated</code> method).
0987: * Before validation a previous/obsolete or <code>null</code> value could be returned.
0988: * See <strong>Layout</strong> section in documentation.
0989: * @return the bounds in local coordination system
0990: */
0991: public final Rectangle getBounds() {
0992: return bounds != null ? new Rectangle(bounds) : null;
0993: }
0994:
0995: /**
0996: * Sets resolved location and bounds of the widget
0997: * This method is usually called from implementations of <code>Layout</code> interface.
0998: * @param location the resolved location; if null then [0,0] point is used instead
0999: * @param bounds the resolved bounds; if null then the preferred bounds are used instead
1000: */
1001: public final void resolveBounds(Point location, Rectangle bounds) {
1002: this .location = location != null ? location : new Point();
1003: this .bounds = bounds != null ? new Rectangle(bounds)
1004: : new Rectangle(getPreferredBounds());
1005: }
1006:
1007: /**
1008: * Returns a client area of the widget.
1009: * @return the client area
1010: */
1011: public final Rectangle getClientArea() {
1012: Rectangle bounds = getBounds();
1013: if (bounds == null)
1014: return null;
1015: Insets insets = getBorder().getInsets();
1016: return new Rectangle(bounds.x + insets.left, bounds.y
1017: + insets.top,
1018: bounds.width - insets.left - insets.right,
1019: bounds.height - insets.top - insets.bottom);
1020: }
1021:
1022: /**
1023: * Called to whether a particular location in local coordination system is controlled (otionally also painted) by the widget.
1024: * @param localLocation the local location
1025: * @return true, if the location belong to the widget
1026: */
1027: public boolean isHitAt(Point localLocation) {
1028: return visible && getBounds().contains(localLocation);
1029: }
1030:
1031: /**
1032: * Schedules the widget for repainting.
1033: */
1034: // NOTE - has to be called before a change is set into the widget when the change immediatelly affects calculation of the local/scene location/boundary (means any property used in convertLocalToScene) because repaint/revalidate needs to calculate old scene boundaries
1035: public final void repaint() {
1036: scene.revalidateWidget(this );
1037: }
1038:
1039: /**
1040: * Returns true if the widget is validated (is not scheduled to revalidation).
1041: * @return true, if is validated
1042: */
1043: public boolean isValidated() {
1044: return !requiresPartValidation;
1045: }
1046:
1047: /**
1048: * Schedules the widget to repaint or revalidation.
1049: * @param repaintOnly if true, then the widget is scheduled for repainting only;
1050: * if false, then widget is scheduled for revalidation (the Scene.validate method has to be called after all changes to invoke validation)
1051: */
1052: // NOTE - has to be called before a change is set into the widget when the change affects the local/scene location/boundary because repaint/revalidate needs to calculate old scene boundaries
1053: public final void revalidate(boolean repaintOnly) {
1054: if (repaintOnly)
1055: repaint();
1056: else
1057: revalidate();
1058: }
1059:
1060: /**
1061: * Schedules the widget for revalidation.
1062: * The Scene.validate method has to be called after all changes to invoke validation. In some cases it is invoked automatically.
1063: */
1064: // NOTE - has to be called before a change is set into the widget when the change affects the local/scene location/boundary because repaint/revalidate needs to calculate old scene boundaries
1065: public final void revalidate() {
1066: requiresFullValidation = true;
1067: revalidateUptoRoot();
1068: }
1069:
1070: /**
1071: * Returns whether whole area of the widget has to be repainted after the validation of the widget.
1072: * Used be LayerWidget for performance optiomalization.
1073: * @return true, if requires; false, if does not require
1074: */
1075: protected boolean isRepaintRequiredForRevalidating() {
1076: return true;
1077: }
1078:
1079: private void revalidateUptoRoot() {
1080: if (requiresPartValidation)
1081: return;
1082: if (isRepaintRequiredForRevalidating())
1083: repaint();
1084: calculatedPreferredBounds = null;
1085: requiresPartValidation = true;
1086: if (parentWidget != null)
1087: parentWidget.revalidateUptoRoot();
1088: if (dependencies != null)
1089: for (Dependency dependency : dependencies)
1090: dependency.revalidateDependency();
1091: }
1092:
1093: void layout(boolean fullValidation) {
1094: boolean childFullValidation = fullValidation
1095: || requiresFullValidation;
1096: for (Widget widget : children)
1097: widget.layout(childFullValidation);
1098:
1099: if (fullValidation)
1100: if (dependencies != null)
1101: for (Dependency dependency : dependencies)
1102: dependency.revalidateDependency();
1103:
1104: if (childFullValidation || requiresPartValidation) {
1105: layout.layout(this );
1106: if (layout.requiresJustification(this )) {
1107: rejustify();
1108: }
1109: }
1110:
1111: requiresFullValidation = false;
1112: requiresPartValidation = false;
1113: }
1114:
1115: private void rejustify() {
1116: requiresFullJustification = true;
1117: rejustifyUptoRoot();
1118: }
1119:
1120: private void rejustifyUptoRoot() {
1121: if (requiresPartJustification)
1122: return;
1123: requiresPartJustification = true;
1124: if (parentWidget != null)
1125: parentWidget.rejustifyUptoRoot();
1126: }
1127:
1128: final void justify() {
1129: if (requiresFullJustification) {
1130: layout.justify(this );
1131: if (layout.requiresJustification(this ))
1132: for (Widget child : children)
1133: child.rejustify();
1134: }
1135:
1136: for (Widget widget : children)
1137: if (widget.requiresPartJustification)
1138: widget.justify();
1139:
1140: requiresFullJustification = false;
1141: requiresPartJustification = false;
1142: }
1143:
1144: /**
1145: * Paints the widget with its children widget into the Graphics2D instance acquired from Scene.getGraphics method.
1146: */
1147: public final void paint() {
1148: if (!visible)
1149: return;
1150:
1151: assert bounds != null : MESSAGE_NULL_BOUNDS; // NOI18N
1152: Graphics2D gr = scene.getGraphics();
1153:
1154: AffineTransform previousTransform = gr.getTransform();
1155: gr.translate(location.x, location.y);
1156:
1157: Shape tempClip = null;
1158: if (checkClipping) {
1159: tempClip = gr.getClip();
1160: gr.clip(bounds);
1161: }
1162:
1163: if (!checkClipping || bounds.intersects(gr.getClipBounds())) {
1164: if (opaque)
1165: paintBackground();
1166:
1167: paintBorder();
1168:
1169: if (checkClipping) {
1170: Insets insets = border.getInsets();
1171: gr.clipRect(bounds.x + insets.left, bounds.y
1172: + insets.top, bounds.width - insets.left
1173: - insets.right, bounds.height - insets.top
1174: - insets.bottom);
1175: }
1176:
1177: paintWidget();
1178: paintChildren();
1179: }
1180:
1181: if (checkClipping)
1182: gr.setClip(tempClip);
1183:
1184: gr.setTransform(previousTransform);
1185: }
1186:
1187: /**
1188: * Called to paint the widget background itself only using the Graphics2D instance acquired from Scene.getGraphics method.
1189: */
1190: protected void paintBackground() {
1191: Graphics2D gr = scene.getGraphics();
1192: Insets insets = border.getInsets();
1193:
1194: gr.setPaint(background);
1195: if (border.isOpaque())
1196: gr
1197: .fillRect(bounds.x, bounds.y, bounds.width,
1198: bounds.height);
1199: else
1200: gr.fillRect(bounds.x + insets.left, bounds.y + insets.top,
1201: bounds.width - insets.left - insets.right,
1202: bounds.height - insets.top - insets.bottom);
1203: }
1204:
1205: /**
1206: * Called to paint the widget border itself only using the Graphics2D instance acquired from Scene.getGraphics method.
1207: * @since 2.1
1208: */
1209: protected void paintBorder() {
1210: border.paint(getGraphics(), new Rectangle(bounds));
1211: }
1212:
1213: /**
1214: * Called to paint the widget itself only using the Graphics2D instance acquired from Scene.getGraphics method.
1215: */
1216: protected void paintWidget() {
1217: }
1218:
1219: /**
1220: * Called to paint the children widgets only using the Graphics2D instance acquired from Scene.getGraphics method.
1221: */
1222: protected void paintChildren() {
1223: if (checkClipping) {
1224: Rectangle clipBounds = scene.getGraphics().getClipBounds();
1225: for (Widget child : children) {
1226: Point location = child.getLocation();
1227: Rectangle bounds = child.getBounds();
1228: bounds.translate(location.x, location.y);
1229: if (bounds.intersects(clipBounds))
1230: child.paint();
1231: }
1232: } else
1233: for (Widget child : children)
1234: child.paint();
1235: }
1236:
1237: /**
1238: * Returns the object hash code.
1239: * @return the object hash code
1240: */
1241: public final int hashCode() {
1242: return super .hashCode();
1243: }
1244:
1245: /**
1246: * Returns whether a specified object is the same as the widget.
1247: * @param object the object
1248: * @return true if the object reference is the same as the widget
1249: */
1250: public final boolean equals(Object object) {
1251: return this == object;
1252: }
1253:
1254: /**
1255: * The dependency listener which is used for notifying dependent widgets, anchor, ...
1256: * that the widget (or one of its parent widget) location or bounds are going to or were changed.
1257: */
1258: public interface Dependency {
1259:
1260: /**
1261: * Called when the widget (or one of its parent widget) location or bounds are going to or were changed.
1262: */
1263: public void revalidateDependency();
1264:
1265: }
1266:
1267: }
|