Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ------------------ 28: * AbstractBlock.java 29: * ------------------ 30: * (C) Copyright 2004, 2005, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: AbstractBlock.java,v 1.12.2.2 2006/08/04 11:37:50 mungady Exp $ 36: * 37: * Changes: 38: * -------- 39: * 22-Oct-2004 : Version 1 (DG); 40: * 02-Feb-2005 : Added accessor methods for margin (DG); 41: * 04-Feb-2005 : Added equals() method and implemented Serializable (DG); 42: * 03-May-2005 : Added null argument checks (DG); 43: * 06-May-2005 : Added convenience methods for setting margin, border and 44: * padding (DG); 45: * 46: */ 47: 48: package org.jfree.chart.block; 49: 50: import java.awt.Graphics2D; 51: import java.awt.geom.Rectangle2D; 52: import java.io.IOException; 53: import java.io.ObjectInputStream; 54: import java.io.ObjectOutputStream; 55: import java.io.Serializable; 56: 57: import org.jfree.data.Range; 58: import org.jfree.io.SerialUtilities; 59: import org.jfree.ui.RectangleInsets; 60: import org.jfree.ui.Size2D; 61: 62: /** 63: * A convenience class for creating new classes that implement 64: * the {@link Block} interface. 65: */ 66: public class AbstractBlock implements Serializable { 67: 68: /** For serialization. */ 69: private static final long serialVersionUID = 7689852412141274563L; 70: 71: /** The id for the block. */ 72: private String id; 73: 74: /** The margin around the outside of the block. */ 75: private RectangleInsets margin; 76: 77: /** The border for the block. */ 78: private BlockBorder border; 79: 80: /** The padding between the block content and the border. */ 81: private RectangleInsets padding; 82: 83: /** 84: * The natural width of the block (may be overridden if there are 85: * constraints in sizing). 86: */ 87: private double width; 88: 89: /** 90: * The natural height of the block (may be overridden if there are 91: * constraints in sizing). 92: */ 93: private double height; 94: 95: /** 96: * The current bounds for the block (position of the block in Java2D space). 97: */ 98: private transient Rectangle2D bounds; 99: 100: /** 101: * Creates a new block. 102: */ 103: protected AbstractBlock() { 104: this.id = null; 105: this.width = 0.0; 106: this.height = 0.0; 107: this.bounds = new Rectangle2D.Float(); 108: this.margin = RectangleInsets.ZERO_INSETS; 109: this.border = BlockBorder.NONE; 110: this.padding = RectangleInsets.ZERO_INSETS; 111: } 112: 113: /** 114: * Returns the id. 115: * 116: * @return The id (possibly <code>null</code>). 117: */ 118: public String getID() { 119: return this.id; 120: } 121: 122: /** 123: * Sets the id for the block. 124: * 125: * @param id the id (<code>null</code> permitted). 126: */ 127: public void setID(String id) { 128: this.id = id; 129: } 130: 131: /** 132: * Returns the natural width of the block, if this is known in advance. 133: * The actual width of the block may be overridden if layout constraints 134: * make this necessary. 135: * 136: * @return The width. 137: */ 138: public double getWidth() { 139: return this.width; 140: } 141: 142: /** 143: * Sets the natural width of the block, if this is known in advance. 144: * 145: * @param width the width (in Java2D units) 146: */ 147: public void setWidth(double width) { 148: this.width = width; 149: } 150: 151: /** 152: * Returns the natural height of the block, if this is known in advance. 153: * The actual height of the block may be overridden if layout constraints 154: * make this necessary. 155: * 156: * @return The height. 157: */ 158: public double getHeight() { 159: return this.height; 160: } 161: 162: /** 163: * Sets the natural width of the block, if this is known in advance. 164: * 165: * @param height the width (in Java2D units) 166: */ 167: public void setHeight(double height) { 168: this.height = height; 169: } 170: 171: /** 172: * Returns the margin. 173: * 174: * @return The margin (never <code>null</code>). 175: */ 176: public RectangleInsets getMargin() { 177: return this.margin; 178: } 179: 180: /** 181: * Sets the margin (use {@link RectangleInsets#ZERO_INSETS} for no 182: * padding). 183: * 184: * @param margin the margin (<code>null</code> not permitted). 185: */ 186: public void setMargin(RectangleInsets margin) { 187: if (margin == null) { 188: throw new IllegalArgumentException("Null 'margin' argument."); 189: } 190: this.margin = margin; 191: } 192: 193: /** 194: * Sets the margin. 195: * 196: * @param top the top margin. 197: * @param left the left margin. 198: * @param bottom the bottom margin. 199: * @param right the right margin. 200: */ 201: public void setMargin(double top, double left, double bottom, 202: double right) { 203: setMargin(new RectangleInsets(top, left, bottom, right)); 204: } 205: 206: /** 207: * Returns the border. 208: * 209: * @return The border (never <code>null</code>). 210: */ 211: public BlockBorder getBorder() { 212: return this.border; 213: } 214: 215: /** 216: * Sets the border for the block (use {@link BlockBorder#NONE} for 217: * no border). 218: * 219: * @param border the border (<code>null</code> not permitted). 220: */ 221: public void setBorder(BlockBorder border) { 222: if (border == null) { 223: throw new IllegalArgumentException("Null 'border' argument."); 224: } 225: this.border = border; 226: } 227: 228: /** 229: * Sets a black border with the specified line widths. 230: * 231: * @param top the top border line width. 232: * @param left the left border line width. 233: * @param bottom the bottom border line width. 234: * @param right the right border line width. 235: */ 236: public void setBorder(double top, double left, double bottom, 237: double right) { 238: setBorder(new BlockBorder(top, left, bottom, right)); 239: } 240: 241: /** 242: * Returns the padding. 243: * 244: * @return The padding (never <code>null</code>). 245: */ 246: public RectangleInsets getPadding() { 247: return this.padding; 248: } 249: 250: /** 251: * Sets the padding (use {@link RectangleInsets#ZERO_INSETS} for no 252: * padding). 253: * 254: * @param padding the padding (<code>null</code> not permitted). 255: */ 256: public void setPadding(RectangleInsets padding) { 257: if (padding == null) { 258: throw new IllegalArgumentException("Null 'padding' argument."); 259: } 260: this.padding = padding; 261: } 262: 263: /** 264: * Returns the x-offset for the content within the block. 265: * 266: * @return The x-offset. 267: */ 268: public double getContentXOffset() { 269: return this.margin.getLeft() + this.border.getInsets().getLeft() 270: + this.padding.getLeft(); 271: } 272: 273: /** 274: * Returns the y-offset for the content within the block. 275: * 276: * @return The y-offset. 277: */ 278: public double getContentYOffset() { 279: return this.margin.getTop() + this.border.getInsets().getTop() 280: + this.padding.getTop(); 281: } 282: 283: /** 284: * Sets the padding. 285: * 286: * @param top the top padding. 287: * @param left the left padding. 288: * @param bottom the bottom padding. 289: * @param right the right padding. 290: */ 291: public void setPadding(double top, double left, double bottom, 292: double right) { 293: setPadding(new RectangleInsets(top, left, bottom, right)); 294: } 295: 296: /** 297: * Arranges the contents of the block, with no constraints, and returns 298: * the block size. 299: * 300: * @param g2 the graphics device. 301: * 302: * @return The block size (in Java2D units, never <code>null</code>). 303: */ 304: public Size2D arrange(Graphics2D g2) { 305: return arrange(g2, RectangleConstraint.NONE); 306: } 307: 308: /** 309: * Arranges the contents of the block, within the given constraints, and 310: * returns the block size. 311: * 312: * @param g2 the graphics device. 313: * @param constraint the constraint (<code>null</code> not permitted). 314: * 315: * @return The block size (in Java2D units, never <code>null</code>). 316: */ 317: public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 318: Size2D base = new Size2D(getWidth(), getHeight()); 319: return constraint.calculateConstrainedSize(base); 320: } 321: 322: /** 323: * Returns the current bounds of the block. 324: * 325: * @return The bounds. 326: */ 327: public Rectangle2D getBounds() { 328: return this.bounds; 329: } 330: 331: /** 332: * Sets the bounds of the block. 333: * 334: * @param bounds the bounds (<code>null</code> not permitted). 335: */ 336: public void setBounds(Rectangle2D bounds) { 337: if (bounds == null) { 338: throw new IllegalArgumentException("Null 'bounds' argument."); 339: } 340: this.bounds = bounds; 341: } 342: 343: /** 344: * Calculate the width available for content after subtracting 345: * the margin, border and padding space from the specified fixed 346: * width. 347: * 348: * @param fixedWidth the fixed width. 349: * 350: * @return The available space. 351: */ 352: protected double trimToContentWidth(double fixedWidth) { 353: double result = this.margin.trimWidth(fixedWidth); 354: result = this.border.getInsets().trimWidth(result); 355: result = this.padding.trimWidth(result); 356: return Math.max(result, 0.0); 357: } 358: 359: /** 360: * Calculate the height available for content after subtracting 361: * the margin, border and padding space from the specified fixed 362: * height. 363: * 364: * @param fixedHeight the fixed height. 365: * 366: * @return The available space. 367: */ 368: protected double trimToContentHeight(double fixedHeight) { 369: double result = this.margin.trimHeight(fixedHeight); 370: result = this.border.getInsets().trimHeight(result); 371: result = this.padding.trimHeight(result); 372: return Math.max(result, 0.0); 373: } 374: 375: /** 376: * Returns a constraint for the content of this block that will result in 377: * the bounds of the block matching the specified constraint. 378: * 379: * @param c the outer constraint (<code>null</code> not permitted). 380: * 381: * @return The content constraint. 382: */ 383: protected RectangleConstraint toContentConstraint(RectangleConstraint c) { 384: if (c == null) { 385: throw new IllegalArgumentException("Null 'c' argument."); 386: } 387: if (c.equals(RectangleConstraint.NONE)) { 388: return c; 389: } 390: double w = c.getWidth(); 391: Range wr = c.getWidthRange(); 392: double h = c.getHeight(); 393: Range hr = c.getHeightRange(); 394: double ww = trimToContentWidth(w); 395: double hh = trimToContentHeight(h); 396: Range wwr = trimToContentWidth(wr); 397: Range hhr = trimToContentHeight(hr); 398: return new RectangleConstraint( 399: ww, wwr, c.getWidthConstraintType(), 400: hh, hhr, c.getHeightConstraintType() 401: ); 402: } 403: 404: private Range trimToContentWidth(Range r) { 405: if (r == null) { 406: return null; 407: } 408: double lowerBound = 0.0; 409: double upperBound = Double.POSITIVE_INFINITY; 410: if (r.getLowerBound() > 0.0) { 411: lowerBound = trimToContentWidth(r.getLowerBound()); 412: } 413: if (r.getUpperBound() < Double.POSITIVE_INFINITY) { 414: upperBound = trimToContentWidth(r.getUpperBound()); 415: } 416: return new Range(lowerBound, upperBound); 417: } 418: 419: private Range trimToContentHeight(Range r) { 420: if (r == null) { 421: return null; 422: } 423: double lowerBound = 0.0; 424: double upperBound = Double.POSITIVE_INFINITY; 425: if (r.getLowerBound() > 0.0) { 426: lowerBound = trimToContentHeight(r.getLowerBound()); 427: } 428: if (r.getUpperBound() < Double.POSITIVE_INFINITY) { 429: upperBound = trimToContentHeight(r.getUpperBound()); 430: } 431: return new Range(lowerBound, upperBound); 432: } 433: 434: /** 435: * Adds the margin, border and padding to the specified content width. 436: * 437: * @param contentWidth the content width. 438: * 439: * @return The adjusted width. 440: */ 441: protected double calculateTotalWidth(double contentWidth) { 442: double result = contentWidth; 443: result = this.padding.extendWidth(result); 444: result = this.border.getInsets().extendWidth(result); 445: result = this.margin.extendWidth(result); 446: return result; 447: } 448: 449: /** 450: * Adds the margin, border and padding to the specified content height. 451: * 452: * @param contentHeight the content height. 453: * 454: * @return The adjusted height. 455: */ 456: protected double calculateTotalHeight(double contentHeight) { 457: double result = contentHeight; 458: result = this.padding.extendHeight(result); 459: result = this.border.getInsets().extendHeight(result); 460: result = this.margin.extendHeight(result); 461: return result; 462: } 463: 464: /** 465: * Reduces the specified area by the amount of space consumed 466: * by the margin. 467: * 468: * @param area the area (<code>null</code> not permitted). 469: * 470: * @return The trimmed area. 471: */ 472: protected Rectangle2D trimMargin(Rectangle2D area) { 473: // defer argument checking... 474: this.margin.trim(area); 475: return area; 476: } 477: 478: /** 479: * Reduces the specified area by the amount of space consumed 480: * by the border. 481: * 482: * @param area the area (<code>null</code> not permitted). 483: * 484: * @return The trimmed area. 485: */ 486: protected Rectangle2D trimBorder(Rectangle2D area) { 487: // defer argument checking... 488: this.border.getInsets().trim(area); 489: return area; 490: } 491: 492: /** 493: * Reduces the specified area by the amount of space consumed 494: * by the padding. 495: * 496: * @param area the area (<code>null</code> not permitted). 497: * 498: * @return The trimmed area. 499: */ 500: protected Rectangle2D trimPadding(Rectangle2D area) { 501: // defer argument checking... 502: this.padding.trim(area); 503: return area; 504: } 505: 506: /** 507: * Draws the border around the perimeter of the specified area. 508: * 509: * @param g2 the graphics device. 510: * @param area the area. 511: */ 512: protected void drawBorder(Graphics2D g2, Rectangle2D area) { 513: this.border.draw(g2, area); 514: } 515: 516: /** 517: * Tests this block for equality with an arbitrary object. 518: * 519: * @param obj the object (<code>null</code> permitted). 520: * 521: * @return A boolean. 522: */ 523: public boolean equals(Object obj) { 524: if (obj == this) { 525: return true; 526: } 527: if (!(obj instanceof AbstractBlock)) { 528: return false; 529: } 530: AbstractBlock that = (AbstractBlock) obj; 531: if (!this.border.equals(that.border)) { 532: return false; 533: } 534: if (!this.bounds.equals(that.bounds)) { 535: return false; 536: } 537: if (!this.margin.equals(that.margin)) { 538: return false; 539: } 540: if (!this.padding.equals(that.padding)) { 541: return false; 542: } 543: if (this.height != that.height) { 544: return false; 545: } 546: if (this.width != that.width) { 547: return false; 548: } 549: return true; 550: } 551: 552: /** 553: * Provides serialization support. 554: * 555: * @param stream the output stream. 556: * 557: * @throws IOException if there is an I/O error. 558: */ 559: private void writeObject(ObjectOutputStream stream) throws IOException { 560: stream.defaultWriteObject(); 561: SerialUtilities.writeShape(this.bounds, stream); 562: } 563: 564: /** 565: * Provides serialization support. 566: * 567: * @param stream the input stream. 568: * 569: * @throws IOException if there is an I/O error. 570: * @throws ClassNotFoundException if there is a classpath problem. 571: */ 572: private void readObject(ObjectInputStream stream) 573: throws IOException, ClassNotFoundException { 574: stream.defaultReadObject(); 575: this.bounds = (Rectangle2D) SerialUtilities.readShape(stream); 576: } 577: 578: }