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