package cnp.ew.lightweight;

import java.awt.*;
import java.util.*;

import cnp.ew.util.*;
import cnp.ew.displayer.*;
import cnp.ew.layout.*;
import cnp.ew.image.*;

public abstract class CpAbstractLc extends CpDefaultObservable implements CpLightweightComponent, CpObserver, CpEvent
{

    public static final int BORDER_NONE=0;
    public static final int BORDER_LINE=1;
    public static final int BORDER_INWARD3D=2;
    public static final int BORDER_OUTWARD3D=3;
    public static final int BORDER_LABELED_ETCHED=4;
    public static final int BORDER_LIGHTINWARD3D=5;
    public static final int BORDER_LIGHTOUTWARD3D=6;
    public static final int BORDER_LABELED_RAISED=7;
    public static final int BORDER_DROPSHADOW=8;
    public static final int BORDER_OUTWARDREVERSE3D=9;

    protected Rectangle boundingBox;  // temp made this public cause polyline touches it directly.  Protected?
    CpLightweightComponent parent;
    CpLcPanel component;
    Font font;
    Color foreground;
    Color background;
    int layer;          // a more flexible way to do hidden
    boolean disabled;

    boolean drawOffscreen;
    boolean keepCachedImage; // if image is null, create and repaint.  flushCache just sets cachedImage = null;
    Image cachedImage;
    Dimension cachedClipSize;

    public CpBorderDisplayable borderDisplayer = CpBlankBorderDisplayer.singleton;
    Insets borderMargin;

    Vector children;  // Also convenient...
    CpLayoutManager layoutManager;
    double scale = 1.0;  // Hmmmm....

    String flyingTipText=null;
    String helpText = null;

    boolean isTabStop;
    boolean isGroupStart;


    /*************************************** CONSTRUCTORS ********************************/

    public CpAbstractLc()
    {
        this(0, 0, 0, 0);
    }

    public CpAbstractLc(int x, int y, int w, int h)
    {
        boundingBox = new Rectangle(x, y, w, h);
        layer = 1;
        disabled = false;
        drawOffscreen = false;
        keepCachedImage = false;
        isTabStop = false;
        isGroupStart = true;
    }

    /**************************************** PAINTING ********************************/


    public void paintIn(Component c, Image image, Rectangle globalClip, Point offset)
    {
        if (isHidden()) {
            return;
        }

        Point loc = new Point(boundingBox.x - offset.x, boundingBox.y - offset.y);
   //     System.out.println("[paintin offscreen: " + drawOffscreen + " " + this.getClass().getName() + " bounds = " + new Rectangle(loc.x, loc.y, size().width, size().height));


        if (!globalClip.intersects(new Rectangle(loc.x, loc.y, size().width, size().height))) {
      //      System.out.println("----------->> Skipping: " + this.getClass().getName());
            return;
        }

        Rectangle clip = globalClip.intersection(new Rectangle(loc.x, loc.y, size().width, size().height));

        if (clip.width == 0 || clip.height == 0) {
            return;
        }

        Rectangle localClip = new Rectangle(clip.x - loc.x, clip.y - loc.y, clip.width, clip.height);

        Graphics g;

        if (image == null) {
            g = c.getGraphics();
        } else {
            g = image.getGraphics();
        }


        try {

            g.clipRect(clip.x, clip.y, clip.width, clip.height);
            g.setFont(getFont());
            g.setColor(getForeground());

            if (!drawOffscreen) {
                g.translate(loc.x, loc.y);

                // Weird special case speed up, see if a kid would totally obscure our painting...
                CpLightweightComponent obscured = null;
                if (hasChildren()) {
                    Vector children = getChildren();
                    for (int i = children.size() - 1; i >= 0; i--) {
                        CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                        // Getting messy
                        Rectangle childBounds = child.boundsGlobal();
                        childBounds = new Rectangle(childBounds.x - offset.x, childBounds.y - offset.y, childBounds.width, childBounds.height);
                        if (!child.isHidden() && !child.isTransparent() && childBounds.union(globalClip).equals(childBounds)) {
                          //  System.out.println("Skip " + this.getClass().getName() + " DRAW " + child.getClass().getName());
                            obscured = child;
                            break;
                        }
                        // Ted added 10/9
                        if (!child.isHidden() && childBounds.intersects(globalClip)) {
                            break;
                        }
                    }
                }
                if (obscured != null) {
                    obscured.paintIn(c, image, clip, offset);
                } else {
                //long time = System.currentTimeMillis();
                    paint(g, localClip);
                    paintBorder(g, localClip);
                //time = System.currentTimeMillis() - time;
                //System.out.println("time to drawImage: " + time);

                    // Draw children
                    if (hasChildren()) {
                        Vector children = getChildren();
                        for (int i = 0; i < children.size(); i++) {
                            CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                            //System.out.println("[Painting child " + child.getClass().getName() + " " + child.bounds());
                            child.paintIn(c, image, clip, offset);
                            //System.out.println("]Painting child " + child.getClass().getName() + " " + child.bounds());
                        }
                    }
                }
                paintAfterChildren(g, localClip);
            } else {

 // SHOULD JUST NULL CACHED IMAGE IN LAYOUT, rather than checking size change here...
                //if ((cachedImage == null) || ((cachedImage.getWidth(c) != size().width) | (cachedImage.getHeight(c) != size().height))) {
                 if (cachedImage == null) { // || (cachedClipSize.width != localClip.width) || (cachedClipSize.height != localClip.height)) {
                    cachedImage = getComponent().createImage(size().width, size().height);
                    Graphics ig=null;
                    try {
                        ig =cachedImage.getGraphics();
                        cachedClipSize = new Dimension(localClip.width, localClip.height);
                        Rectangle tempClip;
                        if (!keepCachedImage) {
                            tempClip = localClip;
                            ig.clipRect(localClip.x, localClip.y, localClip.width, localClip.height);
                        } else {
                            tempClip = new Rectangle(0, 0, size().width, size().height);
                        }

                        paint(ig, tempClip);
                        paintBorder(ig, tempClip);
                    } finally {
                        ig.dispose();
                    }
                } else if (!keepCachedImage) {
                    // System.out.println("Painting image");
                    Graphics ig=null;
                    try {
                        ig =cachedImage.getGraphics();
                        ig.clipRect(localClip.x, localClip.y, localClip.width, localClip.height);
                //long time = System.currentTimeMillis();
                        paint(ig, localClip);
                        paintBorder(ig, localClip);
                //time = System.currentTimeMillis() - time;
                //System.out.println("time to drawImage: " + time);
                    } finally {
                        ig.dispose();
                    }
                }
                // Draw children
                if (hasChildren()) {
                    Vector children = getChildren();
                    for (int i = 0; i < children.size(); i++) {
                        CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                        //    System.out.println("Painting image child, clip = " + localClip + " offset = " + boundingBox.x + ", " + boundingBox.y);
                        child.paintIn(c, cachedImage, localClip, new Point(boundingBox.x, boundingBox.y));
                    }
                }
              //  paintAfterChildren(g, localClip);
               // g.setColor(Color.yellow);
                //g.fillRect(0, 0, size().width, size().height);
              //  try { Thread.sleep(500); } catch (Exception e) { }
                g.drawImage(cachedImage, loc.x, loc.y, c);
            }
        } finally {
            g.dispose();
        }
        //System.out.println("]paintin " + this.getClass().getName() + " bounds = " + new Rectangle(loc.x, loc.y, size().width, size().height));

    }

    /**
     * Paint the border for the lc.  Optimization: if it's not within the cliprect,
     * don't paint it. This goes hand in hand with repaintClientRect().
     */
    void paintBorder(Graphics g, Rectangle clip)
    {
        Rectangle clientRect = getClientRect();
        if (clip.x < clientRect.x ||
            clip.y < clientRect.y ||
            clip.x + clip.width > clientRect.x + clientRect.width ||
            clip.y + clip.height > clientRect.y + clientRect.height) {
            borderDisplayer.paintIn(this, g, 0, 0, size().width, size().height);
        }
    }

    public void paint(Graphics g, Rectangle clip)
    {
    }

    public void paintAfterChildren(Graphics g, Rectangle clip)
    {
    }

    public void damage(int x, int y, int w, int h)
    {
        damageGlobal(new Rectangle(x + boundingBox.x, y + boundingBox.y, w, h));
    }

    public void damage(Rectangle r)
    {
        damageGlobal(new Rectangle(r.x + boundingBox.x, r.y + boundingBox.y, r.width, r.height));
    }

    public void clearDamagedRect()
    {
        if (getComponent() != null) {
            getComponent().clearDamagedRect();
        }
    }

    public void damage()
    {
        Dimension size = size();
        damage(new Rectangle(0, 0, size.width, size.height));
    }

    // Convenience
    public void repaint()
    {
        damage();
        repairDamage();
    }

    public void repaintClientArea()
    {
        damage(getClientRect());
        repairDamage();
    }

    public void enableDamageRepair(boolean enable)
    {
   //     System.out.println("enab dam repair = " + enable + " by " + getClass().getName());
        if (getComponent() != null) {
            getComponent().enableDamageRepair(enable);
        } else {
     //       System.out.println("Tried to ENABLE " + enable + " but COMPONENT iS NULL");
        }
    }

    public boolean isDamageRepairEnabled()
    {
        if (getComponent() != null) {
            return getComponent().isDamageRepairEnabled();
        }
        return true;
    }

    public void drawXORRect(Rectangle r)
    {
        drawXORRect(r.x, r.y, r.width, r.height);
    }

    public void drawXORRect(int x, int y, int w, int h)
    {
        Point glob = locationGlobal();
        Graphics g = getComponent().getGraphics();
        g.setColor(Color.white);
        g.setXORMode(Color.black);
        g.drawRect(glob.x + x, glob.y + y, w, h);
        g.setPaintMode();
        g.dispose();
    }


    /**************************************** FONTS AND COLORS *******************************/

    // Ted added repaint() in setFont and color 10/9.

    public void setFont(Font newFont)
    {
        font = newFont;
        repaint();
    }

    public void setForeground(Color newForeground)
    {
        foreground = newForeground;
        repaint();
    }

    public void setBackground(Color newBackground)
    {
        background = newBackground;
        repaint();
    }

    public Color getForeground()
    {
        if (foreground == null) {
            if (getParent() == null) {
                if (getComponent() == null) {
                    return Color.black;
                }
                return getComponent().getForeground();
            }
            return getParent().getForeground();
        }
        return foreground;
    }

    public Color getBackground()
    {
        if (background == null) {
            if (getParent() == null) {
                if (getComponent() == null) {
                    return Color.white;
                }
                return getComponent().getBackground();
            }
            return getParent().getBackground();
        }
        return background;
    }

    public Font getFont()
    {
        if (font == null) {
            if (getParent() == null) {
                if (getComponent() == null) {
                    // KEN ADDED
                    return CpToolkit.getDefaultFont();
                }
                Font returnFont;
                returnFont = getComponent().getFont();
                if (returnFont == null) {
                    return CpToolkit.getDefaultFont();
                } else {
                    return returnFont;
                }
            }
            return getParent().getFont();
        }
        return font;
    }

    public FontMetrics getFontMetrics()
    {
        return Toolkit.getDefaultToolkit().getFontMetrics(getFont());
    }

    public FontMetrics getFontMetrics(Font aFont)
    {
        return Toolkit.getDefaultToolkit().getFontMetrics(aFont);
    }


    /******************************* BOUNDS AND HIT DETECTION ********************************/

    public void setLayout(CpLayoutManager newLayout)
    {
        layoutManager = newLayout;
    }

    public CpLayoutManager getLayout()
    {
        return layoutManager;
    }



    // KEN ADDED 9/6 TBD: Change the name?
    public Rectangle getClientRect()
    {
        Insets insets = insets();
        Rectangle bounds = bounds();

        return new Rectangle(insets.left, insets.top, bounds.width - insets.left - insets.right, bounds.height - insets.top - insets.bottom);
    }

    public Rectangle bounds()
    {
        // changed by ken 9/5 - parent could be null
        CpLightweightComponent parent;
        Point loc;
        if ((parent = getParent()) == null) {
            loc = new Point(0, 0);
        } else {
            loc = parent.locationGlobal();
        }
        return new Rectangle(boundingBox.x - loc.x, boundingBox.y - loc.y, boundingBox.width, boundingBox.height);
    }

    public Rectangle boundsGlobal()
    {
        return boundingBox;
    }

    public boolean intersectsGlobal(Rectangle r)
	{
	    return boundsGlobal().intersects(r);
	}

	public boolean insideGlobal(int x, int y)
	{
	    return boundsGlobal().inside(x, y);
	}

	public boolean inside(int x, int y)
	{
	    return insideGlobal(x + boundingBox.x, y + boundingBox.y);
	}


    public void moveBy(int x, int y)
    {
        move(x + location().x, y + location().y);
    }


    public void move(Point loc)
    {
        move(loc.x, loc.y);
    }

    public void move(int x, int y)
    {

        int deltaX = x - location().x;
        int deltaY = y - location().y;

        if (deltaX == 0 && deltaY == 0) {
            return;
        }

        //System.out.println("Thread: " + Thread.currentThread().hashCode());
        //System.out.println("DISABLING IN ABS MOVE");
        enableDamageRepair(false);
        damage();

        Rectangle oldBounds = boundingBox;
//        System.out.println("Move to: " + x + "," + y + " delta = " + deltaX + "," + deltaY + " bounds = " + boundsGlobal());
        boundingBox = new Rectangle(boundingBox.x + deltaX, boundingBox.y + deltaY, boundingBox.width, boundingBox.height);
//        System.out.println("  Now: " + boundsGlobal());

        if (hasChildren()) {
            Vector children = getChildren();
            for (int i = 0; i < children.size(); i++) {
                CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                child.move(child.location().x + deltaX, child.location().y + deltaY);
            }
        }
        // SHOULDN'T THIS BE REMOVED?
        //layout();
        if (!oldBounds.intersects(boundingBox)) {
            //System.out.println("** ENSABLING IN ABS MOVE");
            enableDamageRepair(true);
            repairDamage();
            damage();
        } else {
            damage();
            //System.out.println("** ENABLING IN ABS MOVE");
            enableDamageRepair(true);
        }
        repairDamage();
    }

    public void resize(Dimension dim)
    {
        resize(dim.width, dim.height);
    }

    public boolean isVisible()
    {
        boolean result;
        if (isHidden()) {
            result = false;
        } else if (getComponent() == null) {
            result =  false;
        } else if (getParent() == null) {
            result =  getComponent().isShowing();
        } else {
            result =  getParent().isVisible();
        }
        return result;
    }

    public void resize(int w, int h)
    {
        //System.out.println("-----disableing before resize");
        enableDamageRepair(false);
        damage();
        boundingBox = new Rectangle(boundingBox.x, boundingBox.y, w, h);
        damage();
        layout();
        enableDamageRepair(true);
        repairDamage();
    }

    public void reshape(Rectangle rect)
    {
        reshape(rect.x, rect.y, rect.width, rect.height);
    }

    public void reshape(int x, int y, int width, int height)
    {
        // This should check whether size or pos has changed, and then call moveGlobal and/or resize
        //System.out.println("-----disableing before reshape");
        enableDamageRepair(false);
        if (location().x != x || location().y != y) {
            move(x, y);
        }
        if (size().width != width || size().height != height) {
            resize(width, height);
        }
        enableDamageRepair(true);
        repairDamage();
    }

	public Point location()
	{
        Point loc = new Point(0, 0);
        if (getParent() != null) {
            loc = getParent().locationGlobal();
        }
        return new Point(boundingBox.x - loc.x, boundingBox.y - loc.y);
    }

	public Point locationGlobal()
	{
	    return new Point(boundingBox.x, boundingBox.y);
	}

    public Dimension size()
    {
        return new Dimension(boundingBox.width, boundingBox.height);
    }

    public Dimension preferredSize()
    {
        if (hasChildren() && layoutManager != null) {
            return layoutManager.preferredLayoutSize(this);
        }

        // Should this even be here?
        return new Dimension(100, 100);
    }

    public Dimension minimumSize()
    {
        if (hasChildren() && layoutManager != null) {
            return layoutManager.minimumLayoutSize(this);
        }
        return new Dimension(1, 1); // Is this right?
    }

    public Dimension maximumSize()
    {
        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    public CpLightweightComponent locateGlobal(int x, int y)
    {
        // Ken added !isHidden below 9/9
        if (insideGlobal(x, y)  && !isHidden()) {
            if (hasChildren()) {
                Vector children = getChildren();
                // Ted modified order 10/27
                for (int i = children.size() - 1; i >= 0; i--) {
                    //(int i = 0; i < children.size(); i++) {
                    CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                    CpLightweightComponent lcHit = child.locateGlobal(x, y);
                    if (lcHit != null) {
                        return lcHit;
                    }
                }
            }
            return this;
        }
        return null;
    }

    public Insets insets()
    {
        Insets borderInsets = borderDisplayer.insets(this);
        if (borderMargin != null) {
            borderInsets.left += borderMargin.left;
            borderInsets.right += borderMargin.right;
            borderInsets.top += borderMargin.top;
            borderInsets.bottom += borderMargin.bottom;
        }
        return borderInsets;
    }

    public Dimension borderedPreferredSize(Dimension innerPreferredSize)
    {
        Dimension preferredSize = borderDisplayer.preferredSize(this);

        preferredSize.width += innerPreferredSize.width;
        preferredSize.height += innerPreferredSize.height;

        if (borderMargin != null) {
            preferredSize.width += borderMargin.left + borderMargin.right;
            preferredSize.height += borderMargin.top + borderMargin.bottom;
        }
        return preferredSize;
    }

    public void setFlyingTipText(String tipText)
    {
        flyingTipText = tipText;
    }

    public String getFlyingTipText()
    {
        return flyingTipText;
    }

    public void setHelpText(String newHelpText)
    {
        helpText = newHelpText;
    }

    public String getHelpText(Point p)
    {
        return getHelpText();
    }

    public String getHelpText()
    {
        if (helpText != null) {
            return helpText;
        } else {
            if (getParent() != null) {
                return getParent().getHelpText();
            } else {
                return null;
            }
        }
    }


    /******************************** PASS ALONG TO COMPONENT ********************************/

        // These could indicate that maybe we should store the component

    public void setComponent(CpLcPanel newComponent)
    {
        component = newComponent;
    }

    public CpLcPanel getComponent()
    {
        // lazy evaluate
        if (component == null) {
            if (getParent() == null) {
                return null;
            }
            component = getParent().getComponent();
        }
        return component;
    }

    public Image createImage(int width, int height)
    {
        return getComponent().createImage(width, height);
    }

    public void damageGlobal(Rectangle r)
    {
        if (!isVisible()) {
            return;
        }

        Rectangle newRect = r.intersection(boundsGlobal());

        if(getParent() != null) {
            getParent().damageGlobal(newRect);
        } else if (getComponent() != null) {
            getComponent().damageGlobal(newRect);
        }
    }

    public void repairDamage()
    {
        if (getComponent() != null) {
            getComponent().repairDamage();
        }
    }

    /************************* CHILDREN *****************************/


    public boolean hasChildren()
	{
	    return !getChildren().isEmpty();
	}

	public Vector getChildren()
	{
	    if (children == null) {
	        children = new Vector();
	    }
	    return children;
	}

    public Vector getSiblings()
    {
        Vector siblings = new Vector();
        Vector children = getParent().getChildren();
        for (int i = 0; i < children.size(); i++) {
            CpLightweightComponent sib = (CpLightweightComponent)children.elementAt(i);
            if (this != sib) {
                siblings.addElement(sib);
            }
        }
        return siblings;
    }

	public void add(Vector v)
    {

	    CpLightweightComponent child;
	    for (int i = 0; i < v.size(); i++) {
            child = (CpLightweightComponent)v.elementAt(i);
            // Globalize the coords
            child.move(child.location().x + boundingBox.x, child.location().y + boundingBox.y);
            child.setParent(this);
    	    getChildren().addElement(child);
    	 //   child.layout();
    	    damage(child.bounds());
    	}
        repairDamage();
	}

	public void remove(Vector v)
	{
		CpLightweightComponent child;

	    for (int i = 0; i < v.size(); i++) {
            child = (CpLightweightComponent)v.elementAt(i);
    	    damage(child.bounds());
    	    getChildren().removeElement(child);
    	    child.setComponent(null);
    	    child.setParent(null);
    	    // unglobalize the coords
    	    child.move(child.location().x - boundingBox.x, child.location().y - boundingBox.y);
    	    if (layoutManager != null) {
    	        layoutManager.removeLayoutLc(child);
    	    }
    	}
        repairDamage();
	}

	public void add(CpLightweightComponent child)
	{
	    Vector v = new Vector();
	    v.addElement(child);
	    add(v);
	}

	public void remove(CpLightweightComponent child)
	{
	    Vector v = new Vector();
	    v.addElement(child);
	    remove(v);
	}

	public void remove()
	{
	    getParent().remove(this);
	}

    public void setParent(CpLightweightComponent newParent)
    {
        parent = newParent;
    }

	public CpLightweightComponent getParent()
	{
	    return parent;
	}

	public void sendToBack()
	{
	    if (getParent() != null) {
	        getParent().sendToBack(this);
	    }
	}

	public void bringToFront()
	{
	    if (getParent() != null) {
	        getParent().bringToFront(this);
	    }
	}

	public void sendToBack(CpLightweightComponent child)
	{
	    getChildren().removeElement(child);
	    getChildren().insertElementAt(child, 0);
	}

	public void bringToFront(CpLightweightComponent child)
	{
	    getChildren().removeElement(child);
	    getChildren().addElement(child);
	}


    public void layout()
    {
        cachedImage = null;
        if (hasChildren() && layoutManager != null) {
            //System.out.println("-----disableing before layout");
            enableDamageRepair(false);
            layoutManager.layoutLc(this);
            enableDamageRepair(true);
            // Added by ken 9/5
            // removed by ken & ted 10/10
            // repairDamage();
        }
    }

	/************************** INPUT EVENTS **********************************/

    public boolean keyDown(Event e, int key)
    {
        return false;
    }

    public boolean parentKeyDown(Event e, int key)
    {
        return false;
    }

    public boolean keyUp(Event e, int key)
    {
        return false;
    }

    public boolean parentKeyUp(Event e, int key)
    {
        return false;
    }

    public boolean parentMouseDown(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseDown(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseDrag(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseMove(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseUp(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseExit(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public boolean mouseEnter(Event evt, int cellX, int cellY)
    {
        return false;
    }

    public Point mouseLocation()
    {
        Point location = getComponent().mouseLocation();
        return new Point(location.x - boundingBox.x, location.y - boundingBox.y);
    }

    public boolean gotFocus()
    {
        return false;
    }

    public boolean lostFocus()
    {
        return false;
    }

    public boolean hasFocus()
    {
        return getComponent().hasFocus(this);
    }

    /*********************************************  MISC ********************************************/

    public boolean isHidden()
    {
        return layer == 0;  // This can be replaced by some Vector of booleans in the root lc, to allow hiding/showing layers
    }

    public void hide(boolean hide)
    {
        if (hide) {
            layer = 0;
        } else {
            layer = 1;
        }
        repaint();
    }

    public void requestFocus()
    {
        getComponent().lcRequestFocus(this);
    }

    public boolean isDisabled()
    {
        return disabled;
    }

    public void setIsDisabled(boolean disable)
    {
        disabled = disable;
    }

	public void scale(double newScale)
	{
	    // SHould scale kids
	    double factor = newScale / scale;
	    scale = newScale;
	    Rectangle bounds = bounds();
            //System.out.println("-----disableing before scale");
	    enableDamageRepair(false);
	    reshape((int)(bounds.x * factor), (int)(bounds.y * factor), (int)(bounds.width * factor), (int)(bounds.height * factor));
	    setFont(new Font(getFont().getName(), getFont().getStyle(), (int)(getFont().getSize() * factor)));
	    enableDamageRepair(true);
    	repaint();
    }

    public void setScale(double newScale)
    {
        // Simply sets the scale var.  Use scale() to actually scale something
        scale = newScale;
    }

    public double getScale()
    {
        return scale;
    }

	public Object getCopy()
	{
	    // FIX THIS
	    return this;
	}

	public void setDrawOffscreen(boolean newDrawOffscreen)
	{
	    drawOffscreen = newDrawOffscreen;
	}

	public void setKeepCachedImage(boolean newKeepCachedImage)
	{
	    keepCachedImage = newKeepCachedImage;
	    drawOffscreen = true;
	}

	public void flushCachedImage()
	{
	    cachedImage = null;
	}

    public void update(CpObservable o, int facet, Object arg)
    {

    }

    public void setBorderStyle(int borderStyle)
    {
        switch (borderStyle) {
        case BORDER_NONE:
            setBorderDisplayer(CpBlankBorderDisplayer.singleton);
            break;
        case BORDER_LINE:
            setBorderDisplayer(CpFlatBorderDisplayer.singleton);
            break;
        case BORDER_LIGHTINWARD3D:
            setBorderDisplayer(new Cp3DBorderDisplayer(false, true));
            break;
        case BORDER_LIGHTOUTWARD3D:
            setBorderDisplayer(new Cp3DBorderDisplayer(true, true));
            break;
        case BORDER_INWARD3D:
            setBorderDisplayer(new Cp3DBorderDisplayer(false, false));
            break;
        case BORDER_OUTWARD3D:
            setBorderDisplayer(new Cp3DBorderDisplayer(true, false));
            break;
        case BORDER_OUTWARDREVERSE3D:
            setBorderDisplayer(new Cp3DBorderDisplayer(true, false, true));
            break;
        case BORDER_LABELED_ETCHED:
            setBorderDisplayer(new CpEtched3DBorderDisplayer(false));
            break;
        case BORDER_LABELED_RAISED:
            setBorderDisplayer(new CpEtched3DBorderDisplayer(true));
            break;
        case BORDER_DROPSHADOW:
            setBorderDisplayer(new CpDropShadowBorderDisplayer());
            break;
        }
        repaint();
    }


    public void setBorderDisplayer(CpBorderDisplayable newDisplayer)
    {
        borderDisplayer = newDisplayer;
    }

    public CpBorderDisplayable getBorderDisplayer()
    {
        return borderDisplayer;
    }

    public void setBorderMargin(int newMargin)
    {
        borderMargin = new Insets(newMargin, newMargin, newMargin, newMargin);
    }

    public void setBorderMargin(int marginX, int marginY)
    {
        borderMargin = new Insets(marginY, marginX, marginY, marginX);
    }

    public void setBorderMargin(Insets newMargin)
    {
        borderMargin = newMargin;
    }

    public Insets getBorderMargin()
    {
        return borderMargin;
    }

    public boolean isTransparent()
    {
        return false;
    }

    // Added by Ken 9/10
    public CpLightweightComponent getRootLc()
    {
        CpLightweightComponent rootLc = this;

        while (rootLc.getParent() != null) {
            rootLc = rootLc.getParent();
        }

        return rootLc;
    }

    public void popUpLcAt(CpLightweightComponent lc, Rectangle r)
    {
        Point locationGlobal = locationGlobal();
        r.x += locationGlobal.x;
        r.y += locationGlobal.y;

        getComponent().popUpLcAt(lc, r);
    }

    public void popDownPoppedUpLc()
    {
        getComponent().popDownPoppedUpLc();
    }

    public void setCursor(int cursorId)
    {
        getComponent().setCursor(cursorId);
    }

    public Graphics getGraphics()
    {
        return getComponent().getGraphics();
    }

    public Frame getFrame()
    {
        return getComponent().getFrame();
    }

    public boolean wantsTab()
    {
        if (getParent() != null) {
            return getParent().wantsTab();
        } else {
            return false;
        }
    }

    public boolean wantsCr()
    {
        if (getParent() != null) {
            return getParent().wantsCr();
        } else {
            return false;
        }
    }

    public boolean wantsEscape()
    {
        if (getParent() != null) {
            return getParent().wantsCr();
        } else {
            return false;
        }
    }

    public boolean wantsLeftAndRightArrows()
    {
        if (getParent() != null) {
            return getParent().wantsLeftAndRightArrows();
        } else {
            return false;
        }
    }


    public void shiftFocusForward()
    {
        if (getParent() != null) {
            Vector siblings = getParent().getChildren();
            int index = siblings.indexOf(this);

            for (int i = index + 1; i < siblings.size(); i++) {
                CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);
                if (sibling.wantsTabFocus()) {
                    sibling.giveFirstChildFocus();
                    return;
                }
            }
            getParent().shiftFocusForward();
            return;
        } else {
            giveFirstChildFocus();
        }
    }


    public void shiftFocusForwardWithinGroup()
    {
        if (getParent() != null) {
            Vector siblings = getParent().getChildren();
            int index = siblings.indexOf(this);
            for (int i = index + 1; i < siblings.size(); i++) {
                CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);

                if (sibling.isGroupStart()) {
                    break;
                }

                if (sibling.usesFocus()) {
                    sibling.requestFocus();
                    return;
                }
            }
            // didn't find it before the next group starts; scan back to find the
            // start of the group we're in, and scan forward.
            for (int i = index; i >= 0; i--) {
                CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);
                if (sibling.isGroupStart() || i == 0) {
                    for (int j=i; j < index; j++) {
                        sibling = (CpLightweightComponent)siblings.elementAt(j);
                        if (sibling.usesFocus()) {
                            sibling.requestFocus();
                            return;
                        }
                    }
                    break;
                }
            }

        } else {
            giveFirstChildFocus();
        }
    }

    public void shiftFocusBackward()
    {
        if (getParent() != null) {
            Vector siblings = getParent().getChildren();
            int index = siblings.indexOf(this);

            for (int i = index - 1; i >= 0; i--) {
                CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);
                if (sibling.wantsTabFocus()) {
                    sibling.giveLastChildFocus();
                    return;
                }
            }
            getParent().shiftFocusBackward();
            return;
        } else {
            giveLastChildFocus();
        }
    }

    public void shiftFocusBackwardWithinGroup()
    {
        if (getParent() != null) {
            Vector siblings = getParent().getChildren();
            int index = siblings.indexOf(this);

            if (!isGroupStart()) {
                for (int i = index - 1; i >= 0; i--) {
                    CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);

                    if (sibling.usesFocus()) {
                        sibling.requestFocus();
                        return;
                    }

                    if (sibling.isGroupStart()) {
                        break;
                    }
                }
            }
            // didn't find it before the next group starts; scan forward to find the
            // end of the group we're in, and scan backward.
            for (int i = index; i < siblings.size(); i++) {
                CpLightweightComponent sibling = (CpLightweightComponent)siblings.elementAt(i);
                if (sibling.isGroupStart() || i == siblings.size() - 1) {
                    for (int j = i - 1; j >= index; j--) {
                        sibling = (CpLightweightComponent)siblings.elementAt(j);
                        if (sibling.usesFocus()) {
                            sibling.requestFocus();
                            return;
                        }
                    }
                    break;
                }
            }

        } else {
            giveFirstChildFocus();
        }
    }

    public void giveFirstChildFocus()
    {
        CpLightweightComponent c = firstChildWantingFocus();
        if (c != null) {
            c.requestFocus();
        }
    }

    public CpLightweightComponent firstChildWantingFocus()
    {
        if (basicWantsTabFocus() || !hasChildren()) {
            return this;
        }

        for (int i = 0; i < children.size(); i++) {
            CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
            if (child.wantsTabFocus()) {
                return child.firstChildWantingFocus();
            }
        }
        return null;
    }

    public void giveLastChildFocus()
    {
        CpLightweightComponent c = lastChildWantingFocus();
        if (c != null) {
            c.requestFocus();
        }
    }


    public CpLightweightComponent lastChildWantingFocus()
    {
        if (basicWantsTabFocus() || !hasChildren()) {
            return this;
        }

        for (int i = children.size() - 1; i >= 0; i--) {
            CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
            if (child.wantsTabFocus()) {
                return child.lastChildWantingFocus();
            }
        }
        return null;
    }

    public boolean usesFocus()
    {
        return false;
    }

    boolean basicWantsTabFocus()
    {
        return usesFocus() && !isDisabled() && isTabStop();
    }

    public boolean wantsTabFocus()
    {
        if (isDisabled()) {
            return false;
        }

        if (basicWantsTabFocus()) {
            return true;
        }

        if (hasChildren()) {
            for (int i=0; i < children.size(); i++) {
                CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                if (child.wantsTabFocus()) {
                    return true;
                }
            }
        }
        return false;
    }


    public boolean isGroupStart()
    {
        return isGroupStart;
    }

    public void setIsGroupStart(boolean isGroupStart)
    {
        this.isGroupStart = isGroupStart;
    }

    public boolean isTabStop()
    {
        return isTabStop;
    }

    public void setIsTabStop(boolean isTabStop)
    {
        this.isTabStop = isTabStop;
    }

    public boolean pushDefaultButton()
    {
        if (hasChildren()) {
            for (int i=0; i < children.size(); i++) {
                CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                if (!child.isDisabled() && child.pushDefaultButton()) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean pushCancelButton()
    {
        if (hasChildren()) {
            for (int i=0; i < children.size(); i++) {
                CpLightweightComponent child = (CpLightweightComponent)children.elementAt(i);
                if (!child.isDisabled() && child.pushCancelButton()) {
                    return true;
                }
            }
        }
        return false;
    }
}

