// ContainerView.java
// By Ned Etcode
// Copyright 1995, 1996 Netscape Communications Corp.  All rights reserved.

package netscape.application;


// ALERT!!!s
// Should we archive the title textField?

import netscape.util.*;

/** View subclass that fills itself with a Color or an Image, and can draw a
  * Border around its perimeter.  The Image can be centered, scaled, or
  * tiled.
  */


public class ContainerView extends View implements Component {
    private String      title = "";
    private Font        titleFont;
    private Image       image;
    private TextField   titleField;
    private Color       backgroundColor = Color.lightGray;
    private Color       titleColor = Color.black;
    private Border      border = BezelBorder.groovedBezel();
    private int         imageDisplayStyle = Image.CENTERED;
    private boolean     transparent = false;

    static Vector    _fieldDescription = null;

    final static String TITLE_KEY = "title";
    final static String TITLE_FONT_KEY = "titleFont";
    final static String BACKGROUND_COLOR_KEY = "backgroundColor";
    final static String TITLE_COLOR_KEY = "titleColor";
    final static String BORDER_KEY = "border";
    final static String IMAGE_KEY = "image";
    final static String IMAGE_DISPLAY_STYLE_KEY = "imageDisplayStyle";
    final static String TRANSPARENT_KEY = "transparent";

    final static String         displayNameArray[] = { "Centered", "Tiled",
                                                       "Scaled" };
    final static int            displayValueArray[] = { Image.CENTERED,
                                                        Image.TILED,
                                                        Image.SCALED };

/* constructors */

    /** Constructs a ContainerView with origin (0, 0) and zero width and
      * height.
      */
    public ContainerView() {
        this(0, 0, 0, 0);
    }

    /** Constructs a ContainerView with bounds <B>rect</B>.
      */
    public ContainerView(Rect rect) {
        this(rect.x, rect.y, rect.width, rect.height);
    }

    /** Constructs a ContainerView with
      * bounds (<B>x</B>, <B>y</B>, <B>width</B>, <B>height</B>).
      */
    public ContainerView(int x, int y, int width, int height) {
        super(x, y, width, height);


        titleField = new TextField(0, 0, 10, 18);
        titleField.setBorder(null);
        titleField.setTransparent(true);
        titleField.setEditable(false);
        titleField.setVertResizeInstruction(View.BOTTOM_MARGIN_CAN_CHANGE);
        titleField.setHorizResizeInstruction(View.WIDTH_CAN_CHANGE);
        titleField.setJustification(Graphics.CENTERED);

        titleFont = Font.fontNamed("Helvetica", Font.BOLD, 12);
        layoutParts();
    }

    void layoutParts() {
        titleField.removeFromSuperview();

        titleField.setStringValue(title);
        titleField.setFont(titleFont);
        titleField.sizeToMinSize();

        if (!titleField.isEmpty()) {
//          titleField.moveTo((bounds.width - titleField.bounds.width) / 2, 0);
            titleField.moveTo(0, 0);
            titleField.sizeTo(width(), titleField.height());
            addSubview(titleField);
        }
    }

    /** Returns the ContainterView's minimum size, which is governed by the
      * space needed to fully display its title.  Absolute minimum size is
      * (2, 2).
      */
    public Size minSize() {
        Size    setMinSize, minSize;

        setMinSize = super.minSize();

        if (titleField._superview == null) {
            minSize = new Size(2, 2);
        } else {
            minSize = titleField.minSize();
            minSize.sizeBy(6, 2);
        }

        if (minSize.width < setMinSize.width) {
            minSize.width = setMinSize.width;
        }
        if (minSize.height < setMinSize.height) {
            minSize.height = setMinSize.height;
        }

        return minSize;
    }

    /** Returns the area not used by the ContainerView to draw its bounds
      * and title.
      */
    public Rect interiorRect() {
        Rect    tmpRect;

        if (border != null)
            tmpRect = border.interiorRect(0, 0, width(), height());
        else
            tmpRect = new Rect(0, 0, width(), height());

        if (titleField._superview != null) {
            tmpRect.y += titleField.bounds.height;
            tmpRect.height -= titleField.bounds.height;
        }

        return tmpRect;
    }

    /** Sets the ContainerView's title string and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see View#disableDrawing
      */
    public void setTitle(String aString) {
        if (aString != null) {
            title = aString;
        } else {
            title = "";
        }
        layoutParts();
        draw();
    }

    /** Returns the ContainerView's title.
      * @see #setTitle
      */
    public String title() {
        return title;
    }

    /** Sets the ContainerView's title string Color and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see #setTitle
      * @see View#disableDrawing
      */
    public void setTitleColor(Color aColor) {
        if (aColor == null || aColor.equals(titleColor)) {
            return;
        }

        titleColor = aColor;
        titleField.setTextColor(aColor);
        draw();
    }

    /** Returns the ContainerView's title string Color.
      * @see #setTitleColor
      */
    public Color titleColor() {
        return titleColor;
    }

    /** Sets the ContainerView's title string Font and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see #setTitle
      * @see View#disableDrawing
      */
    public void setTitleFont(Font aFont) {
        if (aFont == null) {
            titleFont = Font.fontNamed("Helvetica", Font.BOLD, 12);
        } else {
            titleFont = aFont;
        }
        layoutParts();
        draw();
    }

    /** Returns the ContainerView's title string Font.
      * @see #setTitleFont
      */
    public Font titleFont() {
        return titleFont;
    }

    /** Sets the ContainerView's background Color and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see View#disableDrawing
      */
    public void setBackgroundColor(Color aColor) {
        backgroundColor = aColor;
        draw();
    }

    /** Returns the ContainerView's background Color.
      * @see #setBackgroundColor
      */
    public Color backgroundColor() {
        return backgroundColor;
    }

    /** Sets the ContainerView's Border and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see View#disableDrawing
      */
    public void setBorder(Border newBorder) {
        if (newBorder == null) {
            newBorder = EmptyBorder.emptyBorder();
        }

        border = newBorder;
        layoutParts();
        draw();
    }

    /** Returns the ContainerView's Border.
      * @see #setBorder
      */
    public Border border() {
        return border;
    }

    /** Sets the ContainerView's Image and then calls its
      * <B>draw()</B> method to redraw it.  If you do not want to immediately
      * redraw the ContainerView, you should first call its
      * <B>disableDrawing()</B> method.
      * @see #setImageDisplayStyle
      * @see View#disableDrawing
      */
    public void setImage(Image anImage) {
        image = anImage;
        draw();
    }

    /** Returns the ContainerView's Image.
      * @see #setImage
      */
    public Image image() {
        return image;
    }

    /** Sets the style the ContainerView uses to display its Image
      * (Image.CENTERED, Image.TILED, or Image.SCALED).
      * @see #setImage
      */
    public void setImageDisplayStyle(int aStyle) {
        if (aStyle != Image.CENTERED && aStyle != Image.TILED &&
            aStyle != Image.SCALED) {
            throw new InconsistencyException("Unknown image display style: " +
                aStyle);
        }

        imageDisplayStyle = aStyle;
        draw();
    }

    /** Returns the style the ContainerView uses to display its Image.
      * @see #setImageDisplayStyle
      */
    public int imageDisplayStyle() {
        return imageDisplayStyle;
    }

    /** Sets the ContainerView to be transparent or opaque.
      */
    public void setTransparent(boolean flag) {
        transparent = flag;
    }

    /** Overridden to return <b>true</b> if the ContainerView is transparent.
      * @see #setTransparent
      */
    public boolean isTransparent() {
        return transparent;
    }

    /** Draws the ContainerView's background, using its Image and
      * background Color.  If the ContainerView is transparent, or its
      * background Color is <b>null</b>, it only draws the Image.
      * You never call this method directly, but
      * should override it to produce custom background drawing.
     */
    public void drawViewBackground(Graphics g) {
        Rect    tmpRect;

        /* draw background color only if color has been set and image doesn't
         * completely fill our bounds
         */
        if (!isTransparent() && (image == null ||
            imageDisplayStyle == Image.CENTERED && backgroundColor != null)) {

            if (image == null) {
                tmpRect = Rect.newRect();
            } else {
                tmpRect = Rect.newRect(0, 0, image.width(), image.height());
            }
            if (!tmpRect.contains(bounds)) {
                tmpRect.setBounds(0, 0, width(), height());
                if (titleField.isInViewHierarchy()) {
                    tmpRect.moveBy(0, titleField.bounds.height / 2);
                    tmpRect.sizeBy(0, -titleField.bounds.height / 2);
                }
                g.setColor(backgroundColor);
                g.fillRect(tmpRect);
            }
            Rect.returnRect(tmpRect);
        }

        if (image != null) {
            image.drawWithStyle(g, 0, 0, width(), height(), imageDisplayStyle);
        }
    }

    /** Draws the ContainerView's Border, including its title.  You never call
      * this method directly, but should override it to produce custom
      * border drawing.
      * @see #setBorder
      * @see #setTitle
      */
    public void drawViewBorder(Graphics g) {
        Rect    borderRect, titleRect, clipRect;
        Size    titleSize;

        if (border == null)
            return;

        borderRect = Rect.newRect(0, 0, width(), height());

        if (titleField.isInViewHierarchy()) {
            borderRect.moveBy(0, titleField.bounds.height / 2);
            borderRect.sizeBy(0, -titleField.bounds.height / 2);
        }

        /* avoid drawing the border where the title will show up.  If the
         * title isn't to be drawn, then things are nice and easy.
         */
        if (!titleField.isInViewHierarchy()) {
            border.drawInRect(g, borderRect);
            Rect.returnRect(borderRect);
            return;
        }

        titleSize = titleField.minSize();
        titleRect = Rect.newRect(titleField.bounds);
        titleRect.x = titleRect.midX() - titleSize.width / 2 - 4;
        titleRect.width = titleSize.width + 8;

        clipRect = Rect.newRect(borderRect);

        /* draw the left part of the border */
        clipRect.width = titleRect.x - borderRect.x;

        g.pushState();
        g.setClipRect(clipRect);
        border.drawInRect(g, borderRect);
        g.popState();

        /* draw the right part */
        clipRect.x = titleRect.maxX();
        clipRect.width = borderRect.maxX() - titleRect.maxX();
        g.pushState();
        g.setClipRect(clipRect);
        border.drawInRect(g, borderRect);
        g.popState();

        /* draw the bottom part */
        clipRect.x = titleRect.x;
        clipRect.y = titleRect.maxY();
        clipRect.width = titleRect.width;
        clipRect.height = borderRect.maxY() - titleRect.maxY();
        g.pushState();
        g.setClipRect(clipRect);
        border.drawInRect(g, borderRect);
        g.popState();

        Rect.returnRect(clipRect);
        Rect.returnRect(titleRect);
        Rect.returnRect(borderRect);
    }

    /** Draws the ContainerView's background.  Calls
      * <b>drawViewBackground()</b>.
      * @see #drawViewBackground
      */
    public void drawView(Graphics g) {
        drawViewBackground(g);
    }

    /** Draws the ContainerView's border, after drawing its subviews.  Calls
      * <b>drawViewBorder()</b>.
      * @see #drawViewBorder
      */
    public void drawSubviews(Graphics g) {
        super.drawSubviews(g);
        drawViewBorder(g);
    }


/* archiving */



    /** Describes the ContainerView class' information.
      * @see Codable#describeClassInfo
      */
    public void describeClassInfo(ClassInfo info) {
        super.describeClassInfo(info);

        info.addClass("netscape.application.ContainerView", 1);
        info.addPublicField(TITLE_KEY, STRING_TYPE);
        info.addPublicField(TITLE_FONT_KEY, OBJECT_TYPE);
        info.addPublicField(BACKGROUND_COLOR_KEY, OBJECT_TYPE);
        info.addPublicField(TITLE_COLOR_KEY, OBJECT_TYPE);
        info.addPublicField(IMAGE_KEY, OBJECT_TYPE);
        info.addPublicField(IMAGE_DISPLAY_STYLE_KEY, INT_TYPE);
        info.addPublicField(BORDER_KEY, OBJECT_TYPE);
        info.addPublicField(TRANSPARENT_KEY, BOOLEAN_TYPE);
    }

    /** Encodes the ContainerView instance.
      * @see Codable#encode
      */
    public void encode(Encoder encoder) throws CodingException {
        boolean         addField = false;

        if (titleField.superview() != null) {
            titleField.removeFromSuperview();
            addField = true;
        }

        super.encode(encoder);

        encoder.encodeString(TITLE_KEY, title);
        encoder.encodeObject(TITLE_FONT_KEY, titleFont);
        encoder.encodeObject(BACKGROUND_COLOR_KEY, backgroundColor);
        encoder.encodeObject(TITLE_COLOR_KEY, titleColor);
        encoder.encodeObject(IMAGE_KEY, image);
        encoder.encodeInt(IMAGE_DISPLAY_STYLE_KEY, imageDisplayStyle);

        if (border instanceof EmptyBorder) {
            encoder.encodeObject(BORDER_KEY, null);
        } else {
            encoder.encodeObject(BORDER_KEY, border);
        }

        encoder.encodeBoolean(TRANSPARENT_KEY, transparent);

        if (addField) {
            addSubview(titleField);
        }
    }

    /** Decodes the ContainerView instance.
      * @see Codable#decode
      */
    public void decode(Decoder decoder) throws CodingException {
        super.decode(decoder);

        title = decoder.decodeString(TITLE_KEY);
        setTitleFont((Font)decoder.decodeObject(TITLE_FONT_KEY));
        backgroundColor = (Color)decoder.decodeObject(BACKGROUND_COLOR_KEY);
        setTitleColor((Color)decoder.decodeObject(TITLE_COLOR_KEY));
        image = (Image)decoder.decodeObject(IMAGE_KEY);
        imageDisplayStyle = decoder.decodeInt(IMAGE_DISPLAY_STYLE_KEY);
        setBorder((Border)decoder.decodeObject(BORDER_KEY));
        transparent = decoder.decodeBoolean(TRANSPARENT_KEY);
    }

    /** Finishes the ContainerView instance decoding.
      * @see Codable#finishDecoding
      */
    public void finishDecoding() throws CodingException {
        String  unarchTitle;

        super.finishDecoding();

        unarchTitle = title;
        title = null;
        setTitle(unarchTitle);
    }



/* components */

    /** @private */
    public void describeBuilderInfo(BuilderInfo info) {
        info.addFieldInfo(TITLE_FONT_KEY, BuilderInfo.FONT);
        info.addFieldInfo(BACKGROUND_COLOR_KEY, BuilderInfo.COLOR);
        info.addFieldInfo(TITLE_COLOR_KEY, BuilderInfo.COLOR,
                          BuilderInfo.ALTERNATE_KEY);

        info.addFieldInfo(IMAGE_KEY, BuilderInfo.IMAGE);

        info.addFieldEnumeration(IMAGE_DISPLAY_STYLE_KEY, displayNameArray,
                                 displayValueArray, BuilderInfo.POPUP);

        info.addProperty(BuilderInfo.canContainComponents, "True");
    }

    /** @private */
    public void builderEncode(Encoder encoder) throws CodingException {
        encoder.encodeString(TITLE_KEY, title);
        encoder.encodeObject(TITLE_FONT_KEY, titleFont);
        encoder.encodeObject(BACKGROUND_COLOR_KEY, backgroundColor);
        encoder.encodeObject(TITLE_COLOR_KEY, titleColor);
        encoder.encodeObject(IMAGE_KEY, image);
        encoder.encodeInt(IMAGE_DISPLAY_STYLE_KEY, imageDisplayStyle);

        if (border instanceof EmptyBorder) {
            encoder.encodeObject(BORDER_KEY, null);
        } else {
            encoder.encodeObject(BORDER_KEY, border);
        }
        encoder.encodeBoolean(TRANSPARENT_KEY, transparent);
    }

    /** @private */
    public void builderDecode(Decoder decoder, String changedField) throws
                                                            CodingException {
        setTitle(decoder.decodeString(TITLE_KEY));
        setTitleFont((Font)decoder.decodeObject(TITLE_FONT_KEY));
        backgroundColor = (Color)decoder.decodeObject(BACKGROUND_COLOR_KEY);
        setTitleColor((Color)decoder.decodeObject(TITLE_COLOR_KEY));
        image = (Image)decoder.decodeObject(IMAGE_KEY);
        imageDisplayStyle = decoder.decodeInt(IMAGE_DISPLAY_STYLE_KEY);

        if (changedField.equals(BORDER_KEY)) {
            setBorder((Border)decoder.decodeObject(BORDER_KEY));
        } else if (changedField.equals(TRANSPARENT_KEY)) {
            setTransparent(decoder.decodeBoolean(TRANSPARENT_KEY));
        }
    }
}
