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 049package org.jfree.ui; 050 051import java.awt.geom.Rectangle2D; 052import java.io.Serializable; 053 054import 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 */ 062public 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}