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 }