001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------- 028 * CompositeTitle.java 029 * ------------------- 030 * (C) Copyright 2005-present, by David Gilbert and Contributors. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): Eric Penfold (patch 2006826); 034 * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); 035 * 036 */ 037 038package org.jfree.chart.title; 039 040import java.awt.Graphics2D; 041import java.awt.Paint; 042import java.awt.geom.Rectangle2D; 043import java.io.IOException; 044import java.io.ObjectInputStream; 045import java.io.ObjectOutputStream; 046import java.io.Serializable; 047import java.util.Objects; 048import org.jfree.chart.HashUtils; 049 050import org.jfree.chart.block.BlockContainer; 051import org.jfree.chart.block.BorderArrangement; 052import org.jfree.chart.block.RectangleConstraint; 053import org.jfree.chart.event.TitleChangeEvent; 054import org.jfree.chart.ui.Size2D; 055import org.jfree.chart.util.PaintUtils; 056import org.jfree.chart.util.Args; 057import org.jfree.chart.util.SerialUtils; 058 059/** 060 * A title that contains multiple titles within a {@link BlockContainer}. 061 */ 062public class CompositeTitle extends Title implements Cloneable, Serializable { 063 064 /** For serialization. */ 065 private static final long serialVersionUID = -6770854036232562290L; 066 067 /** 068 * The background paint. 069 */ 070 private transient Paint backgroundPaint; 071 072 /** A container for the individual titles. */ 073 private BlockContainer container; 074 075 /** 076 * Creates a new composite title with a default border arrangement. 077 */ 078 public CompositeTitle() { 079 this(new BlockContainer(new BorderArrangement())); 080 } 081 082 /** 083 * Creates a new title using the specified container. 084 * 085 * @param container the container ({@code null} not permitted). 086 */ 087 public CompositeTitle(BlockContainer container) { 088 Args.nullNotPermitted(container, "container"); 089 this.container = container; 090 this.backgroundPaint = null; 091 } 092 093 /** 094 * Returns the background paint. 095 * 096 * @return The paint (possibly {@code null}). 097 */ 098 public Paint getBackgroundPaint() { 099 return this.backgroundPaint; 100 } 101 102 /** 103 * Sets the background paint and sends a {@link TitleChangeEvent} to all 104 * registered listeners. If you set this attribute to {@code null}, 105 * no background is painted (which makes the title background transparent). 106 * 107 * @param paint the background paint ({@code null} permitted). 108 */ 109 public void setBackgroundPaint(Paint paint) { 110 this.backgroundPaint = paint; 111 notifyListeners(new TitleChangeEvent(this)); 112 } 113 114 /** 115 * Returns the container holding the titles. 116 * 117 * @return The title container (never {@code null}). 118 */ 119 public BlockContainer getContainer() { 120 return this.container; 121 } 122 123 /** 124 * Sets the title container. 125 * 126 * @param container the container ({@code null} not permitted). 127 */ 128 public void setTitleContainer(BlockContainer container) { 129 Args.nullNotPermitted(container, "container"); 130 this.container = container; 131 } 132 133 /** 134 * Arranges the contents of the block, within the given constraints, and 135 * returns the block size. 136 * 137 * @param g2 the graphics device. 138 * @param constraint the constraint ({@code null} not permitted). 139 * 140 * @return The block size (in Java2D units, never {@code null}). 141 */ 142 @Override 143 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 144 RectangleConstraint contentConstraint = toContentConstraint(constraint); 145 Size2D contentSize = this.container.arrange(g2, contentConstraint); 146 return new Size2D(calculateTotalWidth(contentSize.getWidth()), 147 calculateTotalHeight(contentSize.getHeight())); 148 } 149 150 /** 151 * Draws the title on a Java 2D graphics device (such as the screen or a 152 * printer). 153 * 154 * @param g2 the graphics device. 155 * @param area the area allocated for the title. 156 */ 157 @Override 158 public void draw(Graphics2D g2, Rectangle2D area) { 159 draw(g2, area, null); 160 } 161 162 /** 163 * Draws the block within the specified area. 164 * 165 * @param g2 the graphics device. 166 * @param area the area. 167 * @param params ignored ({@code null} permitted). 168 * 169 * @return Always {@code null}. 170 */ 171 @Override 172 public Object draw(Graphics2D g2, Rectangle2D area, Object params) { 173 area = trimMargin(area); 174 drawBorder(g2, area); 175 area = trimBorder(area); 176 if (this.backgroundPaint != null) { 177 g2.setPaint(this.backgroundPaint); 178 g2.fill(area); 179 } 180 area = trimPadding(area); 181 return this.container.draw(g2, area, params); 182 } 183 184 /** 185 * Tests this title for equality with an arbitrary object. 186 * 187 * @param obj the object ({@code null} permitted). 188 * 189 * @return A boolean. 190 */ 191 @Override 192 public boolean equals(Object obj) { 193 if (obj == this) { 194 return true; 195 } 196 if (!(obj instanceof CompositeTitle)) { 197 return false; 198 } 199 CompositeTitle that = (CompositeTitle) obj; 200 if (!Objects.equals(this.container, that.container)) { 201 return false; 202 } 203 if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { 204 return false; 205 } 206 if (!that.canEqual(this)) { 207 return false; 208 } 209 return super.equals(obj); 210 } 211 212 /** 213 * Ensures symmetry between super/subclass implementations of equals. For 214 * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. 215 * 216 * @param other Object 217 * 218 * @return true ONLY if the parameter is THIS class type 219 */ 220 @Override 221 public boolean canEqual(Object other) { 222 // fix the "equals not symmetric" problem 223 return (other instanceof CompositeTitle); 224 } 225 226 @Override 227 public int hashCode() { 228 int hash = super.hashCode(); // equals calls superclass, hashCode must also 229 hash = 53 * hash + Objects.hashCode(this.container); 230 hash = 53 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); 231 return hash; 232 } 233 234 /** 235 * Provides serialization support. 236 * 237 * @param stream the output stream. 238 * 239 * @throws IOException if there is an I/O error. 240 */ 241 private void writeObject(ObjectOutputStream stream) throws IOException { 242 stream.defaultWriteObject(); 243 SerialUtils.writePaint(this.backgroundPaint, stream); 244 } 245 246 /** 247 * Provides serialization support. 248 * 249 * @param stream the input stream. 250 * 251 * @throws IOException if there is an I/O error. 252 * @throws ClassNotFoundException if there is a classpath problem. 253 */ 254 private void readObject(ObjectInputStream stream) 255 throws IOException, ClassNotFoundException { 256 stream.defaultReadObject(); 257 this.backgroundPaint = SerialUtils.readPaint(stream); 258 } 259 260}