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 }