    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jcommon/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * --------------------
    028     * RectangleInsets.java
    029     * --------------------
    030     * (C) Copyright 2004, 2005, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: RectangleInsets.java,v 1.13 2005/11/14 10:55:19 mungady Exp $
    036     *
    037     * Changes:
    038     * --------
    039     * 11-Feb-2004 : Version 1 (DG);
    040     * 14-Jun-2004 : Implemented Serializable (DG);
    041     * 02-Feb-2005 : Added new methods and renamed some existing methods (DG);
    042     * 22-Feb-2005 : Added a new constructor for convenience (DG);
    043     * 19-Apr-2005 : Changed order of parameters in constructors to match
    044     *               java.awt.Insets (DG);
    045     *
    046     */
    047
    048    package org.jfree.ui;
    049
    050    import java.awt.geom.Rectangle2D;
    051    import java.io.Serializable;
    052
    053    import org.jfree.util.UnitType;
    054
    055    /**
    056     * Represents the insets for a rectangle, specified in absolute or relative
    057     * terms. This class is immutable.
    058     *
    059     * @author David Gilbert
    060     */
    061    public class RectangleInsets implements Serializable {
    062
    063        /** For serialization. */
    064        private static final long serialVersionUID = 1902273207559319996L;
    065
    066        /**
    067         * A useful constant representing zero insets.
    068         */
    069        public static final RectangleInsets ZERO_INSETS = new RectangleInsets(
    070            UnitType.ABSOLUTE, 0.0, 0.0, 0.0, 0.0);
    071
    072        /** Absolute or relative units. */
    073        private UnitType unitType;
    074
    075        /** The top insets. */
    076        private double top;
    077
    078        /** The left insets. */
    079        private double left;
    080
    081        /** The bottom insets. */
    082        private double bottom;
    083
    084        /** The right insets. */
    085        private double right;
    086
    087        /**
    088         * Creates a new instance with the specified insets (as 'absolute' units).
    089         *
    090         * @param top  the top insets.
    091         * @param left  the left insets.
    092         * @param bottom  the bottom insets.
    093         * @param right  the right insets.
    094         */
    095        public RectangleInsets(final double top, final double left,
    096                               final double bottom, final double right) {
    097            this(UnitType.ABSOLUTE, top, left, bottom, right);
    098        }
    099
    100        /**
    101         * Creates a new instance.
    102         *
    103         * @param unitType  absolute or relative units (<code>null</code> not
    104         *                  permitted).
    105         * @param top  the top insets.
    106         * @param left  the left insets.
    107         * @param bottom  the bottom insets.
    108         * @param right  the right insets.
    109         */
    110        public RectangleInsets(final UnitType unitType,
    111                               final double top, final double left,
    112                               final double bottom, final double right) {
    113            if (unitType == null) {
    114                throw new IllegalArgumentException("Null 'unitType' argument.");
    115            }
    116            this.unitType = unitType;
    117            this.top = top;
    118            this.bottom = bottom;
    119            this.left = left;
    120            this.right = right;
    121        }
    122
    123        /**
    124         * Returns the unit type (absolute or relative).  This specifies whether
    125         * the insets are measured as Java2D units or percentages.
    126         *
    127         * @return The unit type (never <code>null</code>).
    128         */
    129        public UnitType getUnitType() {
    130            return this.unitType;
    131        }
    132
    133        /**
    134         * Returns the top insets.
    135         *
    136         * @return The top insets.
    137         */
    138        public double getTop() {
    139            return this.top;
    140        }
    141
    142        /**
    143         * Returns the bottom insets.
    144         *
    145         * @return The bottom insets.
    146         */
    147        public double getBottom() {
    148            return this.bottom;
    149        }
    150
    151        /**
    152         * Returns the left insets.
    153         *
    154         * @return The left insets.
    155         */
    156        public double getLeft() {
    157            return this.left;
    158        }
    159
    160        /**
    161         * Returns the right insets.
    162         *
    163         * @return The right insets.
    164         */
    165        public double getRight() {
    166            return this.right;
    167        }
    168
    169        /**
    170         * Tests this instance for equality with an arbitrary object.
    171         *
    172         * @param obj  the object (<code>null</code> permitted).
    173         *
    174         * @return A boolean.
    175         */
    176        public boolean equals(final Object obj) {
    177            if (obj == this) {
    178                return true;
    179            }
    180            if (!(obj instanceof RectangleInsets)) {
    181                    return false;
    182            }
    183            final RectangleInsets that = (RectangleInsets) obj;
    184            if (that.unitType != this.unitType) {
    185                return false;
    186            }
    187            if (this.left != that.left) {
    188                return false;
    189            }
    190            if (this.right != that.right) {
    191                return false;
    192            }
    193            if (this.top != that.top) {
    194                return false;
    195            }
    196            if (this.bottom != that.bottom) {
    197                return false;
    198            }
    199            return true;
    200        }
    201
    202        /**
    203         * Returns a hash code for the object.
    204         *
    205         * @return A hash code.
    206         */
    207        public int hashCode() {
    208            int result;
    209            long temp;
    210            result = (this.unitType != null ? this.unitType.hashCode() : 0);
    211            temp = this.top != +0.0d ? Double.doubleToLongBits(this.top) : 0l;
    212            result = 29 * result + (int) (temp ^ (temp >>> 32));
    213            temp = this.bottom != +0.0d ? Double.doubleToLongBits(this.bottom) : 0l;
    214            result = 29 * result + (int) (temp ^ (temp >>> 32));
    215            temp = this.left != +0.0d ? Double.doubleToLongBits(this.left) : 0l;
    216            result = 29 * result + (int) (temp ^ (temp >>> 32));
    217            temp = this.right != +0.0d ? Double.doubleToLongBits(this.right) : 0l;
    218            result = 29 * result + (int) (temp ^ (temp >>> 32));
    219            return result;
    220        }
    221
    222        /**
    223         * Returns a textual representation of this instance, useful for debugging
    224         * purposes.
    225         *
    226         * @return A string representing this instance.
    227         */
    228        public String toString() {
    229            return "RectangleInsets[t=" + this.top + ",l=" + this.left
    230                    + ",b=" + this.bottom + ",r=" + this.right + "]";
    231        }
    232
    233        /**
    234         * Creates an adjusted rectangle using the supplied rectangle, the insets
    235         * specified by this instance, and the horizontal and vertical
    236         * adjustment types.
    237         *
    238         * @param base  the base rectangle (<code>null</code> not permitted).
    239         * @param horizontal  the horizontal adjustment type (<code>null</code> not
    240         *                    permitted).
    241         * @param vertical  the vertical adjustment type (<code>null</code> not
    242         *                  permitted).
    243         *
    244         * @return The inset rectangle.
    245         */
    246        public Rectangle2D createAdjustedRectangle(final Rectangle2D base,
    247                                              final LengthAdjustmentType horizontal,
    248                                                      final LengthAdjustmentType vertical) {
    249            if (base == null) {
    250                throw new IllegalArgumentException("Null 'base' argument.");
    251            }
    252            double x = base.getX();
    253            double y = base.getY();
    254            double w = base.getWidth();
    255            double h = base.getHeight();
    256            if (horizontal == LengthAdjustmentType.EXPAND) {
    257                final double leftOutset = calculateLeftOutset(w);
    258                x = x - leftOutset;
    259                w = w + leftOutset + calculateRightOutset(w);
    260            }
    261            else if (horizontal == LengthAdjustmentType.CONTRACT) {
    262                final double leftMargin = calculateLeftInset(w);
    263                x = x + leftMargin;
    264                w = w - leftMargin - calculateRightInset(w);
    265            }
    266            if (vertical == LengthAdjustmentType.EXPAND) {
    267                final double topMargin = calculateTopOutset(h);
    268                y = y - topMargin;
    269                h = h + topMargin + calculateBottomOutset(h);
    270            }
    271            else if (vertical == LengthAdjustmentType.CONTRACT) {
    272                final double topMargin = calculateTopInset(h);
    273                y = y + topMargin;
    274                h = h - topMargin - calculateBottomInset(h);
    275            }
    276            return new Rectangle2D.Double(x, y, w, h);
    277        }
    278
    279        /**
    280         * Creates an 'inset' rectangle.
    281         *
    282         * @param base  the base rectangle (<code>null</code> not permitted).
    283         *
    284         * @return The inset rectangle.
    285         */
    286        public Rectangle2D createInsetRectangle(final Rectangle2D base) {
    287            return createInsetRectangle(base, true, true);
    288        }
    289
    290        /**
    291         * Creates an 'inset' rectangle.
    292         *
    293         * @param base  the base rectangle (<code>null</code> not permitted).
    294         * @param horizontal  apply horizontal insets?
    295         * @param vertical  apply vertical insets?
    296         *
    297         * @return The inset rectangle.
    298         */
    299        public Rectangle2D createInsetRectangle(final Rectangle2D base,
    300                                                final boolean horizontal,
    301                                                final boolean vertical) {
    302            if (base == null) {
    303                throw new IllegalArgumentException("Null 'base' argument.");
    304            }
    305            double topMargin = 0.0;
    306            double bottomMargin = 0.0;
    307            if (vertical) {
    308                topMargin = calculateTopInset(base.getHeight());
    309                bottomMargin = calculateBottomInset(base.getHeight());
    310            }
    311            double leftMargin = 0.0;
    312            double rightMargin = 0.0;
    313            if (horizontal) {
    314                leftMargin = calculateLeftInset(base.getWidth());
    315                rightMargin = calculateRightInset(base.getWidth());
    316            }
    317            return new Rectangle2D.Double(
    318                base.getX() + leftMargin,
    319                base.getY() + topMargin,
    320                base.getWidth() - leftMargin - rightMargin,
    321                base.getHeight() - topMargin - bottomMargin
    322            );
    323        }
    324
    325        /**
    326         * Creates an outset rectangle.
    327         *
    328         * @param base  the base rectangle (<code>null</code> not permitted).
    329         *
    330         * @return An outset rectangle.
    331         */
    332        public Rectangle2D createOutsetRectangle(final Rectangle2D base) {
    333            return createOutsetRectangle(base, true, true);
    334        }
    335
    336        /**
    337         * Creates an outset rectangle.
    338         *
    339         * @param base  the base rectangle (<code>null</code> not permitted).
    340         * @param horizontal  apply horizontal insets?
    341         * @param vertical  apply vertical insets?
    342         *
    343         * @return An outset rectangle.
    344         */
    345        public Rectangle2D createOutsetRectangle(final Rectangle2D base,
    346                                                 final boolean horizontal,
    347                                                 final boolean vertical) {
    348            if (base == null) {
    349                throw new IllegalArgumentException("Null 'base' argument.");
    350            }
    351            double topMargin = 0.0;
    352            double bottomMargin = 0.0;
    353            if (vertical) {
    354                topMargin = calculateTopOutset(base.getHeight());
    355                bottomMargin = calculateBottomOutset(base.getHeight());
    356            }
    357            double leftMargin = 0.0;
    358            double rightMargin = 0.0;
    359            if (horizontal) {
    360                leftMargin = calculateLeftOutset(base.getWidth());
    361                rightMargin = calculateRightOutset(base.getWidth());
    362            }
    363            return new Rectangle2D.Double(
    364                base.getX() - leftMargin,
    365                base.getY() - topMargin,
    366                base.getWidth() + leftMargin + rightMargin,
    367                base.getHeight() + topMargin + bottomMargin
    368            );
    369        }
    370
    371        /**
    372         * Returns the top margin.
    373         *
    374         * @param height  the height of the base rectangle.
    375         *
    376         * @return The top margin (in Java2D units).
    377         */
    378        public double calculateTopInset(final double height) {
    379            double result = this.top;
    380            if (this.unitType == UnitType.RELATIVE) {
    381                result = (this.top * height);
    382            }
    383            return result;
    384        }
    385
    386        /**
    387         * Returns the top margin.
    388         *
    389         * @param height  the height of the base rectangle.
    390         *
    391         * @return The top margin (in Java2D units).
    392         */
    393        public double calculateTopOutset(final double height) {
    394            double result = this.top;
    395            if (this.unitType == UnitType.RELATIVE) {
    396                result = (height / (1 - this.top - this.bottom)) * this.top;
    397            }
    398            return result;
    399        }
    400
    401        /**
    402         * Returns the bottom margin.
    403         *
    404         * @param height  the height of the base rectangle.
    405         *
    406         * @return The bottom margin (in Java2D units).
    407         */
    408        public double calculateBottomInset(final double height) {
    409            double result = this.bottom;
    410            if (this.unitType == UnitType.RELATIVE) {
    411                result = (this.bottom * height);
    412            }
    413            return result;
    414        }
    415
    416        /**
    417         * Returns the bottom margin.
    418         *
    419         * @param height  the height of the base rectangle.
    420         *
    421         * @return The bottom margin (in Java2D units).
    422         */
    423        public double calculateBottomOutset(final double height) {
    424            double result = this.bottom;
    425            if (this.unitType == UnitType.RELATIVE) {
    426                result = (height / (1 - this.top - this.bottom)) * this.bottom;
    427            }
    428            return result;
    429        }
    430
    431        /**
    432         * Returns the left margin.
    433         *
    434         * @param width  the width of the base rectangle.
    435         *
    436         * @return The left margin (in Java2D units).
    437         */
    438        public double calculateLeftInset(final double width) {
    439            double result = this.left;
    440            if (this.unitType == UnitType.RELATIVE) {
    441                result = (this.left * width);
    442            }
    443            return result;
    444        }
    445
    446        /**
    447         * Returns the left margin.
    448         *
    449         * @param width  the width of the base rectangle.
    450         *
    451         * @return The left margin (in Java2D units).
    452         */
    453        public double calculateLeftOutset(final double width) {
    454            double result = this.left;
    455            if (this.unitType == UnitType.RELATIVE) {
    456                result = (width / (1 - this.left - this.right)) * this.left;
    457            }
    458            return result;
    459        }
    460
    461        /**
    462         * Returns the right margin.
    463         *
    464         * @param width  the width of the base rectangle.
    465         *
    466         * @return The right margin (in Java2D units).
    467         */
    468        public double calculateRightInset(final double width) {
    469            double result = this.right;
    470            if (this.unitType == UnitType.RELATIVE) {
    471                result = (this.right * width);
    472            }
    473            return result;
    474        }
    475
    476        /**
    477         * Returns the right margin.
    478         *
    479         * @param width  the width of the base rectangle.
    480         *
    481         * @return The right margin (in Java2D units).
    482         */
    483        public double calculateRightOutset(final double width) {
    484            double result = this.right;
    485            if (this.unitType == UnitType.RELATIVE) {
    486                result = (width / (1 - this.left - this.right)) * this.right;
    487            }
    488            return result;
    489        }
    490
    491        /**
    492         * Trims the given width to allow for the insets.
    493         *
    494         * @param width  the width.
    495         *
    496         * @return The trimmed width.
    497         */
    498        public double trimWidth(final double width) {
    499            return width - calculateLeftInset(width) - calculateRightInset(width);
    500        }
    501
    502        /**
    503         * Extends the given width to allow for the insets.
    504         *
    505         * @param width  the width.
    506         *
    507         * @return The extended width.
    508         */
    509        public double extendWidth(final double width) {
    510            return width + calculateLeftOutset(width) + calculateRightOutset(width);
    511        }
    512
    513        /**
    514         * Trims the given height to allow for the insets.
    515         *
    516         * @param height  the height.
    517         *
    518         * @return The trimmed height.
    519         */
    520        public double trimHeight(final double height) {
    521            return height
    522                   - calculateTopInset(height) - calculateBottomInset(height);
    523        }
    524
    525        /**
    526         * Extends the given height to allow for the insets.
    527         *
    528         * @param height  the height.
    529         *
    530         * @return The extended height.
    531         */
    532        public double extendHeight(final double height) {
    533            return height
    534                   + calculateTopOutset(height) + calculateBottomOutset(height);
    535        }
    536
    537        /**
    538         * Shrinks the given rectangle by the amount of these insets.
    539         *
    540         * @param area  the area (<code>null</code> not permitted).
    541         */
    542        public void trim(final Rectangle2D area) {
    543            final double w = area.getWidth();
    544            final double h = area.getHeight();
    545            final double l = calculateLeftInset(w);
    546            final double r = calculateRightInset(w);
    547            final double t = calculateTopInset(h);
    548            final double b = calculateBottomInset(h);
    549            area.setRect(area.getX() + l, area.getY() + t, w - l - r, h - t - b);
    550        }
    551
    552    }