001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.sdm.spa.actors.piw.viz;
031
032import java.awt.AlphaComposite;
033import java.awt.Color;
034import java.awt.Dimension;
035import java.awt.Font;
036import java.awt.Graphics2D;
037import java.awt.Rectangle;
038import java.awt.event.ActionEvent;
039import java.awt.event.ActionListener;
040import java.awt.geom.AffineTransform;
041
042import javax.swing.SwingConstants;
043
044import diva.canvas.CompositeFigure;
045import diva.canvas.Figure;
046import diva.canvas.FigureLayer;
047import diva.canvas.GraphicsPane;
048import diva.canvas.JCanvas;
049import diva.canvas.event.LayerEvent;
050import diva.canvas.event.MouseFilter;
051import diva.canvas.interactor.DragInteractor;
052import diva.canvas.interactor.Interactor;
053import diva.canvas.toolbox.BasicRectangle;
054import diva.canvas.toolbox.LabelFigure;
055
056public class OverviewPanel extends JCanvas {
057
058        private static final long serialVersionUID = 3257288041205608504L;
059        private final ActionListener _listenerViewPortMove;
060        private final ActionListener _listenerSequenceMove;
061        private Figure _viewPort;
062        private int _sizeViewPort;
063
064        private final SequenceCollection _sequenceCollection;
065        private final SiteCollection _siteCollection;
066
067        public OverviewPanel(SequenceCollection sequenceCollection,
068                        SiteCollection siteCollection, ActionListener listenerViewPortMove,
069                        ActionListener listenerSequenceMove) {
070
071                this._listenerViewPortMove = listenerViewPortMove;
072                this._listenerSequenceMove = listenerSequenceMove;
073                this._sizeViewPort = 100;
074
075                this._sequenceCollection = sequenceCollection;
076                this._siteCollection = siteCollection;
077        }
078
079        public void setPreferredSize() {
080                int width = _sequenceCollection.getMaximumSequenceLength() + 200;
081                int height = (_sequenceCollection.size() + 2) * 50;
082
083                AffineTransform current = this.getCanvasPane().getTransformContext()
084                                .getTransform();
085                width *= current.getScaleX();
086                height *= current.getScaleY();
087
088                this.setPreferredSize(new Dimension(width, height));
089        }
090
091        private void setViewPortSize(int height, int maxDrag) {
092                int size = 100;
093                int start = 150;
094
095                if (null != _viewPort) {
096                        size = (int) _viewPort.getShape().getBounds().getWidth();
097                        start = (int) _viewPort.getShape().getBounds().getX();
098                }
099
100                setViewPortSize(size, start, height, maxDrag);
101        }
102
103        private void setViewPortSize(int size, int start, int height, int maxDrag) {
104                FigureLayer layer = ((GraphicsPane) this.getCanvasPane())
105                                .getForegroundLayer();
106
107                // Create anew.
108                BasicRectangle newViewPort = new BasicRectangle(start, 25,
109                                _sizeViewPort, height, Color.MAGENTA) {
110                        public void paint(Graphics2D g) {
111                                AlphaComposite c = AlphaComposite.getInstance(
112                                                AlphaComposite.SRC_OVER, 0.1f);
113                                g.setComposite(c);
114                                g.setPaint(new Color(150, 50, 150));
115                                g.fill(this.getShape());
116                                g.setComposite(AlphaComposite.SrcOver);
117                        }
118                };
119                newViewPort.setInteractor(new DraggerSnap(_listenerViewPortMove, 10,
120                                150, maxDrag));
121
122                // If _viewPort exists, then remove it.
123                if (null != _viewPort) {
124                        // Need to make sure the new viewport is inserted
125                        // at the same zlayer as the previous.
126                        int index = layer.getFigures().indexOf(_viewPort);
127                        if (-1 == index) {
128                                layer.add(newViewPort);
129                        } else {
130                                layer.add(index, newViewPort);
131                                layer.remove(_viewPort);
132                        }
133                } else {
134                        layer.add(newViewPort);
135                }
136
137                _viewPort = newViewPort;
138        }
139
140        public void setViewPortSize(int size) {
141
142                // If the input is non-positive, then set the size to 1.
143                if (0 >= size) {
144                        this._sizeViewPort = 1;
145                } else {
146                        this._sizeViewPort = size;
147                }
148
149                int start = 150;
150                int height = 100;
151                int maxDrag = 500;
152
153                if (null != _viewPort) {
154                        start = (int) _viewPort.getShape().getBounds().getX();
155                        height = (int) _viewPort.getShape().getBounds().getHeight();
156                        maxDrag = ((DraggerSnap) _viewPort.getInteractor())._maximum;
157                }
158
159                setViewPortSize(this._sizeViewPort, start, height, maxDrag);
160        }
161
162        private void drawStartEndMarkers(Sequence sequence,
163                        CompositeFigure compositeFigure, int y) {
164                LabelFigure start = new LabelFigure("1", "SansSerif", Font.PLAIN, 10);
165                LabelFigure end = new LabelFigure(new Integer(sequence.lengthNoGaps())
166                                .toString(), "SansSerif", Font.PLAIN, 10);
167                start.translateTo(0, y);
168                end.translateTo(sequence.alignedSequence.length() - 1, y);
169                compositeFigure.add(start);
170                compositeFigure.add(end);
171        }
172
173        private void drawSequence(String strSequence,
174                        CompositeFigure compositeFigure, int y, int height, Color color,
175                        boolean isConsensus) {
176
177                String loopSequence = strSequence;
178                int loopSpot = 0;
179                while (loopSequence.length() > 0) {
180                        int length = loopSequence.length();
181                        if (loopSequence.charAt(0) == '-') {
182                                loopSequence = loopSequence.replaceFirst("-+", "");
183                                length -= loopSequence.length();
184                        } else {
185                                loopSequence = loopSequence.replaceFirst("[^-]+", "");
186                                length -= loopSequence.length();
187
188                                if (isConsensus
189                                                && length < _sequenceCollection
190                                                                .getMinimumConsensusLength()) {
191                                        continue;
192                                }
193
194                                compositeFigure.add(new BasicRectangle(loopSpot, y, length,
195                                                height, color, 0));
196                        }
197                        loopSpot += length;
198                }
199        }
200
201        public void drawSequences() {
202                FigureLayer layer = ((GraphicsPane) this.getCanvasPane())
203                                .getForegroundLayer();
204
205                // Clear whatever's been drawn before.
206                layer.clear();
207
208                // Draw consensus sequence.
209                CompositeFigure compositeFigure = new CompositeFigure();
210                drawSequence(_sequenceCollection.getConsensus(), compositeFigure, 0,
211                                _sequenceCollection.size() * 50, Color.LIGHT_GRAY, true);
212                compositeFigure.translate(150, 25);
213                layer.add(compositeFigure);
214
215                // Set the preferred size and draw the viewport.
216                this.setPreferredSize();
217                this.setViewPortSize(_sequenceCollection.size() * 50,
218                                _sequenceCollection.getConsensus().length() + 150);
219
220                for (int i = 0; i < _sequenceCollection.size(); i++) {
221                        Sequence seq = _sequenceCollection.getSequence(i);
222
223                        Interactor boundedDragger = new DraggerSnapSequence(
224                                        _listenerSequenceMove, 1, 148, _sequenceCollection
225                                                        .getConsensus().length() + 150 - 1);
226                        boundedDragger.setMouseFilter(MouseFilter.defaultFilter);
227
228                        // Add the label for accession number.
229                        LabelFigure labelFigure = new LabelFigure(
230                                        seq.accessionNumberOriginal, "SansSerif", Font.PLAIN, 14);
231                        labelFigure.setFillPaint(Color.BLUE);
232                        labelFigure.translateTo(20, (i + 1) * 50);
233                        labelFigure.setAnchor(SwingConstants.SOUTH_WEST);
234                        layer.add(labelFigure);
235
236                        // Add the label for gene ID.
237                        labelFigure = new LabelFigure(seq.geneID, "SansSerif", Font.ITALIC,
238                                        14);
239                        labelFigure.translateTo(20, (i + 1) * 50);
240                        labelFigure.setAnchor(SwingConstants.NORTH_WEST);
241                        layer.add(labelFigure);
242
243                        // Draw the sequence.
244                        compositeFigure = new CompositeFigure();
245                        drawSequence(seq.alignedSequence, compositeFigure, 0, 1,
246                                        Color.DARK_GRAY, false);
247                        drawStartEndMarkers(seq, compositeFigure, 15);
248
249                        // Draw the transcription factor binding sites.
250                        for (int j = 0; j < seq.arrTFBSs.length; j++) {
251                                final TranscriptionFactorBindingSite tfbs = seq.arrTFBSs[j];
252                                final SiteCollection.Site site = _siteCollection
253                                                .getSite(tfbs.name);
254                                // If not visible, then don't paint.
255                                if (!site.selected) {
256                                        continue;
257                                }
258
259                                // Get the y.
260                                int y = 4;
261                                if (tfbs.location < 0) {
262                                        y = -4;
263                                }
264
265                                compositeFigure.add(new BasicRectangle(Math.abs(tfbs.location),
266                                                y, 10, 4) {
267                                        public void paint(Graphics2D g) {
268                                                AlphaComposite c = AlphaComposite.getInstance(
269                                                                AlphaComposite.SRC_OVER, 0.3f);
270                                                g.setComposite(c);
271                                                g.setPaint(site.color);
272                                                g.fill(this.getShape());
273                                                g.setComposite(AlphaComposite.SrcOver);
274                                        }
275                                });
276                        }
277
278                        compositeFigure.translate(150 + seq.offset, (i + 1) * 50);
279                        compositeFigure.setInteractor(boundedDragger);
280                        layer.add(compositeFigure);
281                }
282        }
283
284        class DraggerSnap extends DragInteractor {
285                private double _remainderX = 0;
286                protected final ActionListener _listener;
287                public final double _step;
288                public final int _minimum;
289                public final int _maximum;
290
291                protected DraggerSnap(ActionListener listener, double step,
292                                int minimum, int maximum) {
293                        this._listener = listener;
294                        this._step = step;
295                        this._minimum = minimum;
296                        this._maximum = maximum;
297                }
298
299                public void translate(LayerEvent e, double x, double y) {
300                        _remainderX += x;
301
302                        double translateX = ((int) ((_remainderX + _step / 2) / _step))
303                                        * _step;
304                        _remainderX -= translateX;
305
306                        double figureX = e.getFigureSource().getShape().getBounds().getX();
307
308                        // If this puts us negative, then stop.
309                        if (figureX + translateX < _minimum) {
310                                translateX = -(((int) (figureX - _minimum) / _step) * _step);
311                        }
312
313                        // If this puts us past max, then to up to it.
314                        if (figureX + translateX > _maximum) {
315                                translateX = (((int) (_maximum - figureX)) / _step) * _step;
316                        }
317
318                        super.translate(e, translateX, 0);
319                }
320
321                public void mouseReleased(LayerEvent e) {
322                        if (null != _listener) {
323                                _listener.actionPerformed(new ActionEvent(this, (int) e
324                                                .getFigureSource().getShape().getBounds().getX()
325                                                - _minimum, "ViewPort move"));
326                        }
327                }
328        }
329
330        class DraggerSnapSequence extends DraggerSnap {
331
332                protected DraggerSnapSequence(ActionListener listener, double step,
333                                int minimum, int maximum) {
334
335                        super(listener, step, minimum, maximum);
336                }
337
338                public void mouseReleased(LayerEvent e) {
339                        if (null != _listener) {
340                                Rectangle r = e.getFigureSource().getShape().getBounds();
341                                _listener
342                                                .actionPerformed(new ActionEvent(this, (int) r.getX()
343                                                                - _minimum, "Sequence move",
344                                                                ((int) r.getY() - 20) / 50));
345                        }
346                }
347        }
348}