001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.gui.sequence;
023
024import java.awt.Graphics2D;
025import java.awt.event.MouseEvent;
026import java.awt.geom.AffineTransform;
027import java.util.List;
028
029import org.biojava.utils.AssertionFailure;
030import org.biojava.utils.ChangeEvent;
031import org.biojava.utils.ChangeSupport;
032import org.biojava.utils.ChangeType;
033import org.biojava.utils.ChangeVetoException;
034
035/**
036 * A renderer that adds padding before and after a delegate renderer.
037 *
038 * @author Matthew Pocock
039 * @since 1.2
040 */ 
041public class PaddingRenderer
042extends SequenceRendererWrapper {
043  /**
044   * Event type for when the size of the padding changes.
045   */
046  public static ChangeType PADDING = new ChangeType(
047    "The padding has changed",
048    "org.biojava.bio.gui.sequence.PaddingRenderer",
049    "PADDING",
050    SequenceRenderContext.LAYOUT
051  );
052
053  private double padding;
054
055  protected boolean hasListeners() {
056    return super.hasListeners();
057  }
058
059  protected ChangeSupport getChangeSupport(ChangeType ct) {
060    return super.getChangeSupport(ct);
061  }
062  
063  /**
064   * Build a new PaddingRenderer with zero padding.
065   * <p>
066   * This will cause a rendering effect equivalent to missing out the padding
067   * renderer all together.
068   */
069  public PaddingRenderer() {
070    padding = 0.0;
071  }
072  
073  /**
074   * Build a new PaddingRenderer that wraps <code>renderer</code> and has
075   * padding depth <code>padding</code>.
076   *
077   * @param renderer  the SequenceRenderer that will actually do the rendering
078   * @param padding  the number of pixels to leave both before and after
079   *        rendering the child renderer
080   */
081  public PaddingRenderer(
082    SequenceRenderer renderer,
083    double padding
084  ) {
085    super(renderer);
086    try {
087      setPadding(padding);
088    } catch (ChangeVetoException cve) {
089      throw new AssertionFailure("Assertion Failure: Should have no listeners", cve);
090    }
091  }
092  
093  /**
094   * Set the padding.
095   * <p>
096   * The padding will be added to the area before and after that required to
097   * render the delegate renderer.
098   *
099   * @param padding  the new padding size
100   * @throws ChangeVetoException if padding is negative or if any listener
101   *         objected to the change
102   */
103  public void setPadding(double padding)
104  throws ChangeVetoException {
105    if(padding < 0.0) {
106      ChangeEvent ce = new ChangeEvent(
107        this, PADDING, new Double(this.padding), new Double(padding)
108      );
109      throw new ChangeVetoException(
110        ce,
111        "Can't set padding to a negative value"
112      );
113    }
114    if(hasListeners()) {
115      ChangeSupport cs = getChangeSupport(PADDING);
116      synchronized(cs) {
117        ChangeEvent ce = new ChangeEvent(
118          this, PADDING, new Double(this.padding), new Double(padding)
119        );
120        cs.firePreChangeEvent(ce);
121        this.padding = padding;
122        cs.firePostChangeEvent(ce);
123      }
124    } else {
125      this.padding = padding;
126    }
127  }
128  
129  /**
130   * Retrieve the current padding.
131   *
132   * @return the current padding
133   */
134  public double getPadding() {
135    return this.padding;
136  }
137  
138  public double getDepth(SequenceRenderContext src) {
139    return super.getDepth(src) + padding * 2.0;
140  }    
141  
142  public double getMinimumLeader(SequenceRenderContext src) {
143    return super.getMinimumLeader(src);
144  }
145  
146  public double getMinimumTrailer(SequenceRenderContext src) {
147    return super.getMinimumTrailer(src);
148  }
149  
150  public void paint(
151    Graphics2D g,
152    SequenceRenderContext src
153  ) {
154    AffineTransform old = g.getTransform();
155    if(src.getDirection() == SequenceRenderContext.HORIZONTAL) {
156      g.translate(0.0, getPadding());
157    } else {
158      g.translate(getPadding(), 0.0);
159    }
160    super.paint(g, src);
161    g.setTransform(old);
162  }
163  
164  public SequenceViewerEvent processMouseEvent(
165    SequenceRenderContext src,
166    MouseEvent me,
167    List path
168  ) {
169    int padding = (int) getPadding();
170    if(src.getDirection() == SequenceRenderContext.HORIZONTAL) {
171      me.translatePoint(0, -padding);
172    } else {
173      me.translatePoint(-padding, 0);
174    }
175    SequenceViewerEvent sve = super.processMouseEvent(
176      src,
177      me,
178      path
179    );
180    if(src.getDirection() == SequenceRenderContext.HORIZONTAL) {
181      me.translatePoint(0, padding);
182    } else {
183      me.translatePoint(padding, 0);
184    }
185    return sve;
186  }
187
188    public String toString() {
189        return "PaddingRenderer(" + getRenderer().toString() + ")";
190    }
191}