Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, 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-2007, 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.4 2007/03/20 08:31:20 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: * ------------- JFREECHART 1.0.x --------------------------------------------- 46: * 16-Mar-2007 : Changed border from BlockBorder to BlockFrame, updated 47: * equals(), and implemented Cloneable (DG); 48: * 49: */ 50: 51: package org.jfree.chart.block; 52: 53: import java.awt.Graphics2D; 54: import java.awt.geom.Rectangle2D; 55: import java.io.IOException; 56: import java.io.ObjectInputStream; 57: import java.io.ObjectOutputStream; 58: import java.io.Serializable; 59: 60: import org.jfree.data.Range; 61: import org.jfree.io.SerialUtilities; 62: import org.jfree.ui.RectangleInsets; 63: import org.jfree.ui.Size2D; 64: import org.jfree.util.ObjectUtilities; 65: import org.jfree.util.PublicCloneable; 66: import org.jfree.util.ShapeUtilities; 67: 68: /** 69: * A convenience class for creating new classes that implement 70: * the {@link Block} interface. 71: */ 72: public class AbstractBlock implements Cloneable, Serializable { 73: 74: /** For serialization. */ 75: private static final long serialVersionUID = 7689852412141274563L; 76: 77: /** The id for the block. */ 78: private String id; 79: 80: /** The margin around the outside of the block. */ 81: private RectangleInsets margin; 82: 83: /** The frame (or border) for the block. */ 84: private BlockFrame frame; 85: 86: /** The padding between the block content and the border. */ 87: private RectangleInsets padding; 88: 89: /** 90: * The natural width of the block (may be overridden if there are 91: * constraints in sizing). 92: */ 93: private double width; 94: 95: /** 96: * The natural height of the block (may be overridden if there are 97: * constraints in sizing). 98: */ 99: private double height; 100: 101: /** 102: * The current bounds for the block (position of the block in Java2D space). 103: */ 104: private transient Rectangle2D bounds; 105: 106: /** 107: * Creates a new block. 108: */ 109: protected AbstractBlock() { 110: this.id = null; 111: this.width = 0.0; 112: this.height = 0.0; 113: this.bounds = new Rectangle2D.Float(); 114: this.margin = RectangleInsets.ZERO_INSETS; 115: this.frame = BlockBorder.NONE; 116: this.padding = RectangleInsets.ZERO_INSETS; 117: } 118: 119: /** 120: * Returns the id. 121: * 122: * @return The id (possibly <code>null</code>). 123: * 124: * @see #setID(String) 125: */ 126: public String getID() { 127: return this.id; 128: } 129: 130: /** 131: * Sets the id for the block. 132: * 133: * @param id the id (<code>null</code> permitted). 134: * 135: * @see #getID() 136: */ 137: public void setID(String id) { 138: this.id = id; 139: } 140: 141: /** 142: * Returns the natural width of the block, if this is known in advance. 143: * The actual width of the block may be overridden if layout constraints 144: * make this necessary. 145: * 146: * @return The width. 147: * 148: * @see #setWidth(double) 149: */ 150: public double getWidth() { 151: return this.width; 152: } 153: 154: /** 155: * Sets the natural width of the block, if this is known in advance. 156: * 157: * @param width the width (in Java2D units) 158: * 159: * @see #getWidth() 160: */ 161: public void setWidth(double width) { 162: this.width = width; 163: } 164: 165: /** 166: * Returns the natural height of the block, if this is known in advance. 167: * The actual height of the block may be overridden if layout constraints 168: * make this necessary. 169: * 170: * @return The height. 171: * 172: * @see #setHeight(double) 173: */ 174: public double getHeight() { 175: return this.height; 176: } 177: 178: /** 179: * Sets the natural width of the block, if this is known in advance. 180: * 181: * @param height the width (in Java2D units) 182: * 183: * @see #getHeight() 184: */ 185: public void setHeight(double height) { 186: this.height = height; 187: } 188: 189: /** 190: * Returns the margin. 191: * 192: * @return The margin (never <code>null</code>). 193: * 194: * @see #getMargin() 195: */ 196: public RectangleInsets getMargin() { 197: return this.margin; 198: } 199: 200: /** 201: * Sets the margin (use {@link RectangleInsets#ZERO_INSETS} for no 202: * padding). 203: * 204: * @param margin the margin (<code>null</code> not permitted). 205: * 206: * @see #getMargin() 207: */ 208: public void setMargin(RectangleInsets margin) { 209: if (margin == null) { 210: throw new IllegalArgumentException("Null 'margin' argument."); 211: } 212: this.margin = margin; 213: } 214: 215: /** 216: * Sets the margin. 217: * 218: * @param top the top margin. 219: * @param left the left margin. 220: * @param bottom the bottom margin. 221: * @param right the right margin. 222: * 223: * @see #getMargin() 224: */ 225: public void setMargin(double top, double left, double bottom, 226: double right) { 227: setMargin(new RectangleInsets(top, left, bottom, right)); 228: } 229: 230: /** 231: * Returns the border. 232: * 233: * @return The border (never <code>null</code>). 234: * 235: * @deprecated Use getBlockFrame() instead. 236: */ 237: public BlockBorder getBorder() { 238: if (this.frame instanceof BlockBorder) { 239: return (BlockBorder) this.frame; 240: } 241: else { 242: return null; 243: } 244: } 245: 246: /** 247: * Sets the border for the block (use {@link BlockBorder#NONE} for 248: * no border). 249: * 250: * @param border the border (<code>null</code> not permitted). 251: * 252: * @see #getBorder() 253: * 254: * @deprecated Use setBorderFrame() instead. 255: */ 256: public void setBorder(BlockBorder border) { 257: setFrame(border); 258: } 259: 260: /** 261: * Sets a black border with the specified line widths. 262: * 263: * @param top the top border line width. 264: * @param left the left border line width. 265: * @param bottom the bottom border line width. 266: * @param right the right border line width. 267: */ 268: public void setBorder(double top, double left, double bottom, 269: double right) { 270: setFrame(new BlockBorder(top, left, bottom, right)); 271: } 272: 273: /** 274: * Returns the current frame (border). 275: * 276: * @return The frame. 277: * 278: * @since 1.0.5 279: */ 280: public BlockFrame getFrame() { 281: return this.frame; 282: } 283: 284: /** 285: * Sets the frame (or border). 286: * 287: * @param frame the frame (<code>null</code> not permitted). 288: * 289: * @since 1.0.5 290: */ 291: public void setFrame(BlockFrame frame) { 292: if (frame == null) { 293: throw new IllegalArgumentException("Null 'frame' argument."); 294: } 295: this.frame = frame; 296: } 297: 298: /** 299: * Returns the padding. 300: * 301: * @return The padding (never <code>null</code>). 302: * 303: * @see #setPadding(RectangleInsets) 304: */ 305: public RectangleInsets getPadding() { 306: return this.padding; 307: } 308: 309: /** 310: * Sets the padding (use {@link RectangleInsets#ZERO_INSETS} for no 311: * padding). 312: * 313: * @param padding the padding (<code>null</code> not permitted). 314: * 315: * @see #getPadding() 316: */ 317: public void setPadding(RectangleInsets padding) { 318: if (padding == null) { 319: throw new IllegalArgumentException("Null 'padding' argument."); 320: } 321: this.padding = padding; 322: } 323: 324: /** 325: * Sets the padding. 326: * 327: * @param top the top padding. 328: * @param left the left padding. 329: * @param bottom the bottom padding. 330: * @param right the right padding. 331: */ 332: public void setPadding(double top, double left, double bottom, 333: double right) { 334: setPadding(new RectangleInsets(top, left, bottom, right)); 335: } 336: 337: /** 338: * Returns the x-offset for the content within the block. 339: * 340: * @return The x-offset. 341: * 342: * @see #getContentYOffset() 343: */ 344: public double getContentXOffset() { 345: return this.margin.getLeft() + this.frame.getInsets().getLeft() 346: + this.padding.getLeft(); 347: } 348: 349: /** 350: * Returns the y-offset for the content within the block. 351: * 352: * @return The y-offset. 353: * 354: * @see #getContentXOffset() 355: */ 356: public double getContentYOffset() { 357: return this.margin.getTop() + this.frame.getInsets().getTop() 358: + this.padding.getTop(); 359: } 360: 361: /** 362: * Arranges the contents of the block, with no constraints, and returns 363: * the block size. 364: * 365: * @param g2 the graphics device. 366: * 367: * @return The block size (in Java2D units, never <code>null</code>). 368: */ 369: public Size2D arrange(Graphics2D g2) { 370: return arrange(g2, RectangleConstraint.NONE); 371: } 372: 373: /** 374: * Arranges the contents of the block, within the given constraints, and 375: * returns the block size. 376: * 377: * @param g2 the graphics device. 378: * @param constraint the constraint (<code>null</code> not permitted). 379: * 380: * @return The block size (in Java2D units, never <code>null</code>). 381: */ 382: public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 383: Size2D base = new Size2D(getWidth(), getHeight()); 384: return constraint.calculateConstrainedSize(base); 385: } 386: 387: /** 388: * Returns the current bounds of the block. 389: * 390: * @return The bounds. 391: * 392: * @see #setBounds(Rectangle2D) 393: */ 394: public Rectangle2D getBounds() { 395: return this.bounds; 396: } 397: 398: /** 399: * Sets the bounds of the block. 400: * 401: * @param bounds the bounds (<code>null</code> not permitted). 402: * 403: * @see #getBounds() 404: */ 405: public void setBounds(Rectangle2D bounds) { 406: if (bounds == null) { 407: throw new IllegalArgumentException("Null 'bounds' argument."); 408: } 409: this.bounds = bounds; 410: } 411: 412: /** 413: * Calculate the width available for content after subtracting 414: * the margin, border and padding space from the specified fixed 415: * width. 416: * 417: * @param fixedWidth the fixed width. 418: * 419: * @return The available space. 420: * 421: * @see #trimToContentHeight(double) 422: */ 423: protected double trimToContentWidth(double fixedWidth) { 424: double result = this.margin.trimWidth(fixedWidth); 425: result = this.frame.getInsets().trimWidth(result); 426: result = this.padding.trimWidth(result); 427: return Math.max(result, 0.0); 428: } 429: 430: /** 431: * Calculate the height available for content after subtracting 432: * the margin, border and padding space from the specified fixed 433: * height. 434: * 435: * @param fixedHeight the fixed height. 436: * 437: * @return The available space. 438: * 439: * @see #trimToContentWidth(double) 440: */ 441: protected double trimToContentHeight(double fixedHeight) { 442: double result = this.margin.trimHeight(fixedHeight); 443: result = this.frame.getInsets().trimHeight(result); 444: result = this.padding.trimHeight(result); 445: return Math.max(result, 0.0); 446: } 447: 448: /** 449: * Returns a constraint for the content of this block that will result in 450: * the bounds of the block matching the specified constraint. 451: * 452: * @param c the outer constraint (<code>null</code> not permitted). 453: * 454: * @return The content constraint. 455: */ 456: protected RectangleConstraint toContentConstraint(RectangleConstraint c) { 457: if (c == null) { 458: throw new IllegalArgumentException("Null 'c' argument."); 459: } 460: if (c.equals(RectangleConstraint.NONE)) { 461: return c; 462: } 463: double w = c.getWidth(); 464: Range wr = c.getWidthRange(); 465: double h = c.getHeight(); 466: Range hr = c.getHeightRange(); 467: double ww = trimToContentWidth(w); 468: double hh = trimToContentHeight(h); 469: Range wwr = trimToContentWidth(wr); 470: Range hhr = trimToContentHeight(hr); 471: return new RectangleConstraint( 472: ww, wwr, c.getWidthConstraintType(), 473: hh, hhr, c.getHeightConstraintType() 474: ); 475: } 476: 477: private Range trimToContentWidth(Range r) { 478: if (r == null) { 479: return null; 480: } 481: double lowerBound = 0.0; 482: double upperBound = Double.POSITIVE_INFINITY; 483: if (r.getLowerBound() > 0.0) { 484: lowerBound = trimToContentWidth(r.getLowerBound()); 485: } 486: if (r.getUpperBound() < Double.POSITIVE_INFINITY) { 487: upperBound = trimToContentWidth(r.getUpperBound()); 488: } 489: return new Range(lowerBound, upperBound); 490: } 491: 492: private Range trimToContentHeight(Range r) { 493: if (r == null) { 494: return null; 495: } 496: double lowerBound = 0.0; 497: double upperBound = Double.POSITIVE_INFINITY; 498: if (r.getLowerBound() > 0.0) { 499: lowerBound = trimToContentHeight(r.getLowerBound()); 500: } 501: if (r.getUpperBound() < Double.POSITIVE_INFINITY) { 502: upperBound = trimToContentHeight(r.getUpperBound()); 503: } 504: return new Range(lowerBound, upperBound); 505: } 506: 507: /** 508: * Adds the margin, border and padding to the specified content width. 509: * 510: * @param contentWidth the content width. 511: * 512: * @return The adjusted width. 513: */ 514: protected double calculateTotalWidth(double contentWidth) { 515: double result = contentWidth; 516: result = this.padding.extendWidth(result); 517: result = this.frame.getInsets().extendWidth(result); 518: result = this.margin.extendWidth(result); 519: return result; 520: } 521: 522: /** 523: * Adds the margin, border and padding to the specified content height. 524: * 525: * @param contentHeight the content height. 526: * 527: * @return The adjusted height. 528: */ 529: protected double calculateTotalHeight(double contentHeight) { 530: double result = contentHeight; 531: result = this.padding.extendHeight(result); 532: result = this.frame.getInsets().extendHeight(result); 533: result = this.margin.extendHeight(result); 534: return result; 535: } 536: 537: /** 538: * Reduces the specified area by the amount of space consumed 539: * by the margin. 540: * 541: * @param area the area (<code>null</code> not permitted). 542: * 543: * @return The trimmed area. 544: */ 545: protected Rectangle2D trimMargin(Rectangle2D area) { 546: // defer argument checking... 547: this.margin.trim(area); 548: return area; 549: } 550: 551: /** 552: * Reduces the specified area by the amount of space consumed 553: * by the border. 554: * 555: * @param area the area (<code>null</code> not permitted). 556: * 557: * @return The trimmed area. 558: */ 559: protected Rectangle2D trimBorder(Rectangle2D area) { 560: // defer argument checking... 561: this.frame.getInsets().trim(area); 562: return area; 563: } 564: 565: /** 566: * Reduces the specified area by the amount of space consumed 567: * by the padding. 568: * 569: * @param area the area (<code>null</code> not permitted). 570: * 571: * @return The trimmed area. 572: */ 573: protected Rectangle2D trimPadding(Rectangle2D area) { 574: // defer argument checking... 575: this.padding.trim(area); 576: return area; 577: } 578: 579: /** 580: * Draws the border around the perimeter of the specified area. 581: * 582: * @param g2 the graphics device. 583: * @param area the area. 584: */ 585: protected void drawBorder(Graphics2D g2, Rectangle2D area) { 586: this.frame.draw(g2, area); 587: } 588: 589: /** 590: * Tests this block for equality with an arbitrary object. 591: * 592: * @param obj the object (<code>null</code> permitted). 593: * 594: * @return A boolean. 595: */ 596: public boolean equals(Object obj) { 597: if (obj == this) { 598: return true; 599: } 600: if (!(obj instanceof AbstractBlock)) { 601: return false; 602: } 603: AbstractBlock that = (AbstractBlock) obj; 604: if (!ObjectUtilities.equal(this.id, that.id)) { 605: return false; 606: } 607: if (!this.frame.equals(that.frame)) { 608: return false; 609: } 610: if (!this.bounds.equals(that.bounds)) { 611: return false; 612: } 613: if (!this.margin.equals(that.margin)) { 614: return false; 615: } 616: if (!this.padding.equals(that.padding)) { 617: return false; 618: } 619: if (this.height != that.height) { 620: return false; 621: } 622: if (this.width != that.width) { 623: return false; 624: } 625: return true; 626: } 627: 628: /** 629: * Returns a clone of this block. 630: * 631: * @return A clone. 632: * 633: * @throws CloneNotSupportedException if there is a problem creating the 634: * clone. 635: */ 636: public Object clone() throws CloneNotSupportedException { 637: AbstractBlock clone = (AbstractBlock) super.clone(); 638: clone.bounds = (Rectangle2D) ShapeUtilities.clone(this.bounds); 639: if (this.frame instanceof PublicCloneable) { 640: PublicCloneable pc = (PublicCloneable) this.frame; 641: clone.frame = (BlockFrame) pc.clone(); 642: } 643: return clone; 644: } 645: 646: /** 647: * Provides serialization support. 648: * 649: * @param stream the output stream. 650: * 651: * @throws IOException if there is an I/O error. 652: */ 653: private void writeObject(ObjectOutputStream stream) throws IOException { 654: stream.defaultWriteObject(); 655: SerialUtilities.writeShape(this.bounds, stream); 656: } 657: 658: /** 659: * Provides serialization support. 660: * 661: * @param stream the input stream. 662: * 663: * @throws IOException if there is an I/O error. 664: * @throws ClassNotFoundException if there is a classpath problem. 665: */ 666: private void readObject(ObjectInputStream stream) 667: throws IOException, ClassNotFoundException { 668: stream.defaultReadObject(); 669: this.bounds = (Rectangle2D) SerialUtilities.readShape(stream); 670: } 671: 672: }