RelativeLayout.java Source code

Java tutorial

Introduction

Here is the source code for RelativeLayout.java

Source

/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Java, the Duke mascot, and all variants of Sun's Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * <p>
 * RelativeLayout, a Relative Layout Manager for Java J2SE. Mainly for porting
 * tired old code that uses x,y locations. You really can't just assign x,y
 * locations to components in Java Java J2SE - it breaks badly when the user
 * resizes (and you can <em>not</em> mandate that the user can't resize you --
 * see any book on UI design for <em>that</em> little discussion -- and can
 * also look bad due to resolution independance. Symantec Cafe 1.x, for example,
 * used to spit out unfortunate (and unmaintainable) code like this:
 * 
 * <pre>
 * setLayout(null);
 * setSize(331, 241);
 * label1 = new Label(&quot;Info Applet&quot;, Label.CENTER);
 * add(label1);
 * label1.setBounds(91, 19, 107, 15);
 * </pre>
 * 
 * </p>
 * <p>
 * <em>Bleaarrgghh!!!</em> To make it work properly at all resolutions and
 * survive user-initiated resize actions, change it to
 * 
 * <pre>
 * 
 *    setLayout(new RelativeLayout(331,241,false);
 *    label1=new Label(&quot;Info Applet&quot;, Label.CENTER);
 *    add(&quot;91,19&quot;, label1);
 *  
 * </pre>
 * 
 * Note that it's actually <EM>less</EM> work to get it right. Symantec,
 * Microsoft, and others, please take note!
 * </p>
 * 
 * @author Ian Darwin, http://www.darwinsys.com/
 */

public class RelativeLayout implements LayoutManager {
    /** requested absolute width of canvas */
    protected int reqWid;

    /** requested absolute height of canvas */
    protected int reqHgt;

    /** actual size width when laid out */
    protected int curWid;

    /** actual size height when laid out */
    protected int curHgt;

    /** to track Components added by named add form. */
    protected Vector curComps = new Vector();

    /**
     * Constructs an RelativeLayout, given original hard-coded size of panel.
     */
    public RelativeLayout(int wid, int ht) {
        this.reqWid = wid;
        this.reqHgt = ht;
    }

    /**
     * Called by AWT when the user uses the form add(name, Component). Adds the
     * specified component with the specified name to the layout.
     * 
     * @param name
     *            String with location for component c <EM>Note</EM>: the
     *            "name" <EM>must</EM> contain x, y location, ie., <BR>
     *            add("" + 320 + "," + 100, new Button("Quit")); <BR>
     *            or <BR>
     *            add("320,100", new Button("Quit"). <BR>
     *            This adds the Button at x=320, y=100 when the Panel is at its
     *            original full size.
     * @param c
     *            Component to be added.
     */
    public void addLayoutComponent(String name, Component c) {
        int x, y;
        StringTokenizer st = new StringTokenizer(name, ",");
        x = Integer.parseInt(st.nextToken());
        y = Integer.parseInt(st.nextToken());
        // System.out.println("Adding: Name " + name +"; obj " + c
        //    + "; x " + x + "; y " + y);
        Tracker t = new Tracker(x, y, c);
        curComps.addElement(t);
    }

    /**
     * Called by AWT to lay out the components in the target Container at its
     * current size.
     * 
     * @param target
     *            Container whose components are to be laid out.
     */
    public void layoutContainer(Container target) {
        Dimension targSize = target.getSize();
        Insets ins = target.getInsets();
        // System.out.println("layoutContainer: size " + targSize);
        curWid = targSize.width;
        curHgt = targSize.height;
        float widRatio = (float) curWid / (float) reqWid;
        float hgtRatio = (float) curHgt / (float) reqHgt;
        for (int i = 0; i < curComps.size(); i++) {
            int px, py, pw, ph;
            Tracker t = (Tracker) curComps.elementAt(i);
            Component tc = t.getComponent();
            Dimension d = tc.getPreferredSize();
            px = ins.right + (int) (t.getRequestedLoc().x * widRatio);
            py = ins.top + (int) (t.getRequestedLoc().y * hgtRatio);
            pw = d.width;
            ph = d.height;
            // System.out.println("layoutContainer["+i+"]: move " +
            // tc + " to " + px + ", " + py);
            tc.setBounds(px, py, pw, ph);
        }
    }

    /**
     * Called from AWT to calculate the minimum size dimensions for the target
     * panel given the components in it. But we use our own list of named
     * insertions, not the list of Components that the container keeps.
     * 
     * @param target
     *            Container to calculate for
     */
    public Dimension minimumLayoutSize(Container target) {
        int minw = 0, minh = 0;
        for (int i = 0; i < curComps.size(); i++) {
            Tracker t = (Tracker) curComps.elementAt(i);
            Component tc = t.getComponent();
            Dimension d = tc.getMinimumSize();
            Point rl = t.getRequestedLoc();
            minw = Math.max(minw, rl.x + d.width);
            minh = Math.max(minh, rl.y + d.height);
            // System.out.println("minLay, minw = " + minw
            // + "; minh = " + minh);
        }
        return new Dimension(minw, minw);
    }

    /**
     * Called by AWT to compute the preferred size for the target panel given
     * our list of the components that it contains.
     * 
     * @param target
     *            Container to calculate for
     */
    public Dimension preferredLayoutSize(Container target) {
        int prefw = 0, prefh = 0;
        for (int i = 0; i < curComps.size(); i++) {
            Tracker t = (Tracker) curComps.elementAt(i);
            Component tc = t.getComponent();
            Dimension d = tc.getMinimumSize();
            Point rl = t.getRequestedLoc();
            prefw = Math.max(prefw, rl.x + d.width);
            prefh = Math.max(prefh, rl.y + d.height);
            // System.out.println("prefLay, prefw = " +
            // prefw + "; prefh = " + prefh);
        }
        return new Dimension(prefw, prefh);
    }

    /**
     * Called by AWT to remove a given component from the layout.
     * 
     * @param c
     *            Component to be removed
     */
    public void removeLayoutComponent(Component c) {
        curComps.removeElement(c);
    }

    /**
     * Tracker is a class used only by RelativeLayout, to track the original
     * "requested" (hard-coded) x,y locations of each Component.
     */
    class Tracker {
        int absx, absy;

        Component theComp;

        /** Construct a tracker item given its location and Component. */
        Tracker(int x, int y, Component c) {
            this.absx = x;
            this.absy = y;
            this.theComp = c;
        }

        /** Extract the location as a Point. */
        public Point getRequestedLoc() {
            return new Point(absx, absy);
        }

        /** Extract the Component from this Tracker. */
        public Component getComponent() {
            return theComp;
        }
    }
}