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 */
021package org.biojava.bio.gui.sequence;
022
023import java.awt.Color;
024import java.awt.Font;
025import java.awt.Graphics;
026import java.util.ArrayList;
027import java.util.Iterator;
028import java.util.List;
029
030import javax.swing.BoxLayout;
031import javax.swing.JPanel;
032
033import org.biojava.bio.gui.sequence.tracklayout.SimpleTrackLayout;
034import org.biojava.bio.gui.sequence.tracklayout.TrackLayout;
035import org.biojava.bio.seq.Sequence;
036import org.biojava.bio.symbol.RangeLocation;
037import org.biojava.utils.ChangeVetoException;
038
039/**
040 * Handles multiple SequencePanels and Ranges so that a Sequence can be wrapped
041 * over more than one line on screen. This is particularly useful for viewing
042 * Protein sequences that would be viewed at a single residue resolution.
043 * 
044 * The interface is very similar to that of the SequencePanels that it wraps
045 * 
046 * @author Mark Southern
047 * @see SequencePanel
048 * @since 1.5
049 */
050public class SequencePanelWrapper extends JPanel {
051
052        /**
053         * Generated Serial Version UID
054         */
055        private static final long serialVersionUID = 8749249181471157230L;
056        protected SequencePanel[] seqPanels = new SequencePanel[0];
057        private RangeLocation range;
058        private Sequence sequence;
059        private SequenceRenderer renderer;
060        private double scale = 14.0;
061        private java.awt.RenderingHints hints;
062        private int direction = SequencePanel.HORIZONTAL;
063        private TrackLayout trackLayout = new SimpleTrackLayout();
064        private List<SequenceViewerListener> viewerListeners = new ArrayList<SequenceViewerListener>();
065        private List<SequenceViewerMotionListener> motionListeners = new ArrayList<SequenceViewerMotionListener>();
066
067        /**
068         * Creates a new instance of WrappedSequencePanel
069         */
070        public SequencePanelWrapper() {
071                initComponents();
072        }
073
074        protected void initComponents() {
075                setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
076                setBackground(Color.WHITE);
077        }
078
079        /*
080         * Sets the track (line) layout strategy. Layouts include a simple wrapping
081         * at a given number of residues and user-defined layouts that can contain
082         * arbitrary length rows.
083         */
084        public void setTrackLayout(TrackLayout tl) {
085                this.trackLayout = tl;
086                trackLayout.setSequence(getSequence());
087                trackLayout.setRange(getRange());
088
089                if (isActive()) {
090                        refreshSequencePanels();
091                }
092        }
093
094        public TrackLayout getTrackLayout() {
095                return this.trackLayout;
096        }
097
098        protected boolean isActive() {
099                return (sequence != null) && (renderer != null) && (range != null);
100        }
101
102        public void setScale(double scale) {
103                this.scale = scale;
104
105                for (int i = 0; i < seqPanels.length; i++) {
106                        seqPanels[i].setScale(scale);
107                }
108        }
109
110        public double getScale() {
111                return scale;
112        }
113
114        public void setDirection(int direction) {
115                this.direction = direction;
116
117                for (int i = 0; i < seqPanels.length; i++) {
118                        seqPanels[i].setDirection(direction);
119                }
120
121                if (isActive()) {
122                        refreshSequencePanels();
123                }
124        }
125
126        public int getDirection() {
127                return direction;
128        }
129
130        /*
131         * a convenience method. The wrap is passed through to the TrackLayout
132         * implementation.
133         */
134        public synchronized void setWrap(int w) {
135                trackLayout.setWrap(w);
136
137                if (!isActive()) {
138                        return;
139                }
140
141                refreshSequencePanels();
142        }
143
144        public int getWrap() {
145                return trackLayout.getWrap();
146        }
147
148        public void setRenderingHints(java.awt.RenderingHints hints) {
149                this.hints = hints;
150
151                for (int i = 0; i < seqPanels.length; i++) {
152                        seqPanels[i].setRenderingHints(hints);
153                }
154        }
155
156        public java.awt.RenderingHints getRenderingHints() {
157                return hints;
158        }
159
160        public void setRenderer(SequenceRenderer renderer) {
161                this.renderer = renderer;
162
163                for (int i = 0; i < seqPanels.length; i++) {
164                        try {
165                                seqPanels[i].setRenderer(renderer);
166                        } catch (ChangeVetoException e) {
167                                // should never get here
168                                e.printStackTrace();
169                        }
170                }
171        }
172
173        public SequenceRenderer getRenderer() {
174                return renderer;
175        }
176
177        public void setSequence(Sequence seq) {
178                if (seq == null) {
179                        removeSeqPanels();
180
181                        return;
182                }
183
184                trackLayout.setSequence(sequence = seq);
185                trackLayout.setRange(range = new RangeLocation(1, seq.length()));
186                refreshSequencePanels();
187        }
188
189        public Sequence getSequence() {
190                return sequence;
191        }
192
193        public void setRange(RangeLocation loc) {
194                trackLayout.setRange(range = loc);
195                refreshSequencePanels();
196        }
197
198        public RangeLocation getRange() {
199                return range;
200        }
201
202        private void removeSeqPanels() {
203                for (int i = 0; i < seqPanels.length; i++) {
204                        remove(seqPanels[i]);
205                }
206
207                setSize(0, 0); // make sure view is refreshed properly
208                setLayout(null);
209                revalidate();
210        }
211
212        public void resizeAndValidate() {
213                for (int i = 0; i < seqPanels.length; i++) {
214                        seqPanels[i].resizeAndValidate();
215                }
216        }
217
218        protected void refreshSequencePanels() {
219                removeSeqPanels();
220
221                RangeLocation[] ranges = trackLayout.getRanges();
222                seqPanels = new SequencePanel[ranges.length];
223
224                if (getDirection() == SequencePanel.HORIZONTAL) {
225                        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
226                } else {
227                        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
228                }
229
230                for (int i = 0; i < ranges.length; i++) {
231                        // logger.debug("Setting sequence panel " + i);
232                        seqPanels[i] = new SequencePanel();
233                        seqPanels[i].setFont(getFont());
234                        seqPanels[i].setAlignmentX(LEFT_ALIGNMENT);
235                        seqPanels[i].setAlignmentY(TOP_ALIGNMENT);
236                        seqPanels[i].setSequence(getSequence());
237
238                        // seqPanels[i].setRange( ranges[i] );
239                        // bug in biojava-1.4pre1 - add +1 to max range
240                        seqPanels[i].setRange(new RangeLocation(ranges[i].getMin(),
241                                        ranges[i].getMax() + 1));
242                        seqPanels[i].setDirection(getDirection());
243                        seqPanels[i].setScale(getScale());
244
245                        for (Iterator<SequenceViewerListener> it = viewerListeners
246                                        .iterator(); it.hasNext();) {
247                                seqPanels[i].addSequenceViewerListener(it.next());
248                        }
249
250                        for (Iterator<SequenceViewerMotionListener> jt = motionListeners
251                                        .iterator(); jt.hasNext();) {
252                                seqPanels[i].addSequenceViewerMotionListener(jt.next());
253                        }
254
255                        add(seqPanels[i]);
256                }
257
258                setRenderer(getRenderer());
259        }
260
261        public void addSequenceViewerListener(SequenceViewerListener l) {
262                viewerListeners.add(l);
263
264                for (int i = 0; i < seqPanels.length; i++) {
265                        seqPanels[i].addSequenceViewerListener(l);
266                }
267        }
268
269        public void removeSequenceViewerListener(SequenceViewerListener l) {
270                viewerListeners.remove(l);
271
272                for (int i = 0; i < seqPanels.length; i++) {
273                        seqPanels[i].removeSequenceViewerListener(l);
274                }
275        }
276
277        public void addSequenceViewerMotionListener(SequenceViewerMotionListener l) {
278                motionListeners.add(l);
279
280                for (int i = 0; i < seqPanels.length; i++) {
281                        seqPanels[i].addSequenceViewerMotionListener(l);
282                }
283        }
284
285        public void removeSequenceViewerMotionListener(
286                        SequenceViewerMotionListener l) {
287                motionListeners.remove(l);
288
289                for (int i = 0; i < seqPanels.length; i++) {
290                        seqPanels[i].removeSequenceViewerMotionListener(l);
291                }
292        }
293
294        public void setFont(Font f) {
295                try {
296                        super.setFont(f);
297
298                        for (int i = 0; i < seqPanels.length; i++) {
299                                seqPanels[i].setFont(f);
300                                seqPanels[i].resizeAndValidate();
301                        }
302                } catch (NullPointerException e) {
303                }
304        }
305
306        public void paint(Graphics g) {
307                // some wierd sequence graphics exception is going on in biojava
308                try {
309                        super.paint(g);
310                } catch (Exception e) {
311                        e.printStackTrace();
312                        System.out.println("Caught sequence graphics exception in paint() "
313                                        + e.toString());
314                        repaint();
315                }
316        }
317
318        public void paintComponent(Graphics g) {
319                // some wierd sequence graphics exception is going on in biojava-1.3
320                try {
321                        super.paintComponent(g);
322                } catch (Exception e) {
323                        e.printStackTrace();
324                        System.out
325                                        .println("Caught sequence graphics exception in paintComponent() "
326                                                        + e.toString());
327                        repaint();
328                }
329        }
330} // class