reversed order of addition of group consensus and conservation
[jalview.git] / src / jalview / gui / AlignmentPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.beans.*;
22 import java.io.*;
23 import java.util.Vector;
24
25 import java.awt.*;
26 import java.awt.event.*;
27 import java.awt.print.*;
28 import javax.swing.*;
29
30 import jalview.bin.Cache;
31 import jalview.datamodel.*;
32 import jalview.jbgui.*;
33 import jalview.schemes.*;
34 import jalview.structure.SelectionSource;
35
36 /**
37  * DOCUMENT ME!
38  * 
39  * @author $author$
40  * @version $Revision$
41  */
42 public class AlignmentPanel extends GAlignmentPanel implements
43         AdjustmentListener, Printable
44 {
45   public AlignViewport av;
46
47   OverviewPanel overviewPanel;
48
49   SeqPanel seqPanel;
50
51   IdPanel idPanel;
52
53   IdwidthAdjuster idwidthAdjuster;
54
55   /** DOCUMENT ME!! */
56   public AlignFrame alignFrame;
57
58   ScalePanel scalePanel;
59
60   AnnotationPanel annotationPanel;
61
62   AnnotationLabels alabels;
63
64   // this value is set false when selection area being dragged
65   boolean fastPaint = true;
66
67   int hextent = 0;
68
69   int vextent = 0;
70
71   /**
72    * Creates a new AlignmentPanel object.
73    * 
74    * @param af
75    *          DOCUMENT ME!
76    * @param av
77    *          DOCUMENT ME!
78    */
79   public AlignmentPanel(AlignFrame af, final AlignViewport av)
80   {
81     alignFrame = af;
82     this.av = av;
83     seqPanel = new SeqPanel(av, this);
84     idPanel = new IdPanel(av, this);
85
86     scalePanel = new ScalePanel(av, this);
87
88     idPanelHolder.add(idPanel, BorderLayout.CENTER);
89     idwidthAdjuster = new IdwidthAdjuster(this);
90     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
91
92     annotationPanel = new AnnotationPanel(this);
93     alabels = new AnnotationLabels(this);
94
95     annotationScroller.setViewportView(annotationPanel);
96     annotationSpaceFillerHolder.add(alabels, BorderLayout.CENTER);
97
98     scalePanelHolder.add(scalePanel, BorderLayout.CENTER);
99     seqPanelHolder.add(seqPanel, BorderLayout.CENTER);
100
101     setScrollValues(0, 0);
102
103     setAnnotationVisible(av.getShowAnnotation());
104
105     hscroll.addAdjustmentListener(this);
106     vscroll.addAdjustmentListener(this);
107
108     final AlignmentPanel ap = this;
109     av.addPropertyChangeListener(new PropertyChangeListener()
110     {
111       public void propertyChange(PropertyChangeEvent evt)
112       {
113         if (evt.getPropertyName().equals("alignment"))
114         {
115           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
116           alignmentChanged();
117         }
118       }
119     });
120     fontChanged();
121     adjustAnnotationHeight();
122
123   }
124
125   public void alignmentChanged()
126   {
127     av.alignmentChanged(this);
128
129     alignFrame.updateEditMenuBar();
130
131     paintAlignment(true);
132
133   }
134
135   /**
136    * DOCUMENT ME!
137    */
138   public void fontChanged()
139   {
140     // set idCanvas bufferedImage to null
141     // to prevent drawing old image
142     FontMetrics fm = getFontMetrics(av.getFont());
143
144     scalePanelHolder.setPreferredSize(new Dimension(10, av.charHeight
145             + fm.getDescent()));
146     idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av.charHeight
147             + fm.getDescent()));
148
149     idPanel.idCanvas.gg = null;
150     seqPanel.seqCanvas.img = null;
151     annotationPanel.adjustPanelHeight();
152
153     Dimension d = calculateIdWidth();
154     d.setSize(d.width + 4, d.height);
155     idPanel.idCanvas.setPreferredSize(d);
156     hscrollFillerPanel.setPreferredSize(d);
157
158     if (overviewPanel != null)
159     {
160       overviewPanel.setBoxPosition();
161     }
162
163     repaint();
164   }
165
166   /**
167    * Calculate the width of the alignment labels based on the displayed names
168    * and any bounds on label width set in preferences.
169    * 
170    * @return Dimension giving the maximum width of the alignment label panel
171    *         that should be used.
172    */
173   public Dimension calculateIdWidth()
174   {
175     Container c = new Container();
176
177     FontMetrics fm = c.getFontMetrics(new Font(av.font.getName(),
178             Font.ITALIC, av.font.getSize()));
179
180     AlignmentI al = av.getAlignment();
181     int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
182     int maxwidth = Math.max(20, Math.min(afwidth - 200, (int) 2 * afwidth
183             / 3));
184     int i = 0;
185     int idWidth = 0;
186     String id;
187
188     while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
189     {
190       SequenceI s = al.getSequenceAt(i);
191
192       id = s.getDisplayId(av.getShowJVSuffix());
193
194       if (fm.stringWidth(id) > idWidth)
195       {
196         idWidth = fm.stringWidth(id);
197       }
198
199       i++;
200     }
201
202     // Also check annotation label widths
203     i = 0;
204
205     if (al.getAlignmentAnnotation() != null)
206     {
207       fm = c.getFontMetrics(alabels.getFont());
208
209       while (i < al.getAlignmentAnnotation().length)
210       {
211         String label = al.getAlignmentAnnotation()[i].label;
212
213         if (fm.stringWidth(label) > idWidth)
214         {
215           idWidth = fm.stringWidth(label);
216         }
217
218         i++;
219       }
220     }
221
222     return new Dimension(Math.min(maxwidth, idWidth), 12);
223   }
224
225   /**
226    * DOCUMENT ME!
227    * 
228    * @param results
229    *          DOCUMENT ME!
230    */
231   public void highlightSearchResults(SearchResults results)
232   {
233     scrollToPosition(results);
234     seqPanel.seqCanvas.highlightSearchResults(results);
235   }
236
237   /**
238    * scroll the view to show the position of the highlighted region in results
239    * (if any) and redraw the overview
240    * 
241    * @param results
242    */
243   public boolean scrollToPosition(SearchResults results)
244   {
245     return scrollToPosition(results, true);
246   }
247
248   /**
249    * scroll the view to show the position of the highlighted region in results
250    * (if any)
251    * 
252    * @param results
253    * @param redrawOverview
254    *          - when set, the overview will be recalculated (takes longer)
255    * @return false if results were not found
256    */
257   public boolean scrollToPosition(SearchResults results,
258           boolean redrawOverview)
259   {
260     int startv, endv, starts, ends, width;
261     // TODO: properly locate search results in view when large numbers of hidden
262     // columns exist before highlighted region
263     // do we need to scroll the panel?
264     // TODO: tons of nullpointereexceptions raised here.
265     if (results != null && results.getSize() > 0 && av != null
266             && av.alignment != null)
267     {
268       int seqIndex = av.alignment.findIndex(results);
269       if (seqIndex == -1)
270       {
271         return false;
272       }
273       SequenceI seq = av.alignment.getSequenceAt(seqIndex);
274
275       int[] r = results.getResults(seq, 0, av.alignment.getWidth()); // results.getResults(seq,
276       // seq.getStart(),
277       // seq.getEnd());
278       // TODO: VAMSAS: fix hidden column issue where scroll to left from C
279       // terminus is not visible
280       if (r == null)
281       {
282         return false;
283       }
284       int start = r[0];
285       int end = r[1];
286       if (start < 0)
287       {
288         return false;
289       }
290       if (end == seq.getEnd())
291       {
292         return false;
293       }
294       if (!av.wrapAlignment)
295       {
296         if ((startv = av.getStartRes()) >= start)
297         {
298           setScrollValues(start - 1, seqIndex);
299         }
300         else if ((endv = av.getEndRes()) <= end)
301         {
302           setScrollValues(startv + 1 + end - endv, seqIndex);
303         }
304         else if ((starts = av.getStartSeq()) > seqIndex)
305         {
306           setScrollValues(av.getStartRes(), seqIndex);
307         }
308         else if ((ends = av.getEndSeq()) <= seqIndex)
309         {
310           setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
311         }
312       }
313       else
314       {
315         scrollToWrappedVisible(start);
316       }
317     }
318     if (!redrawOverview && overviewPanel != null)
319     {
320       overviewPanel.setBoxPosition();
321     }
322     paintAlignment(!redrawOverview);
323     return true;
324   }
325
326   void scrollToWrappedVisible(int res)
327   {
328     int cwidth = seqPanel.seqCanvas
329             .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
330     if (res < av.getStartRes() || res >= (av.getStartRes() + cwidth))
331     {
332       vscroll.setValue((res / cwidth));
333       av.startRes = vscroll.getValue() * cwidth;
334     }
335
336   }
337
338   /**
339    * DOCUMENT ME!
340    * 
341    * @return DOCUMENT ME!
342    */
343   public OverviewPanel getOverviewPanel()
344   {
345     return overviewPanel;
346   }
347
348   /**
349    * DOCUMENT ME!
350    * 
351    * @param op
352    *          DOCUMENT ME!
353    */
354   public void setOverviewPanel(OverviewPanel op)
355   {
356     overviewPanel = op;
357   }
358
359   /**
360    * DOCUMENT ME!
361    * 
362    * @param b
363    *          DOCUMENT ME!
364    */
365   public void setAnnotationVisible(boolean b)
366   {
367     if (!av.wrapAlignment)
368     {
369       annotationSpaceFillerHolder.setVisible(b);
370       annotationScroller.setVisible(b);
371     }
372     repaint();
373   }
374
375   public void adjustAnnotationHeight()
376   {
377     // TODO: display vertical annotation scrollbar if necessary
378     // this is called after loading new annotation onto alignment
379     if (alignFrame.getHeight() == 0)
380     {
381       System.out.println("NEEDS FIXING");
382     }
383
384     int height = annotationPanel.adjustPanelHeight();
385
386     if (hscroll.isVisible())
387     {
388       height += hscroll.getPreferredSize().height;
389     }
390     if (height > alignFrame.getHeight() / 2)
391     {
392       height = alignFrame.getHeight() / 2;
393     }
394
395     hscroll.addNotify();
396
397     annotationScroller.setPreferredSize(new Dimension(annotationScroller
398             .getWidth(), height));
399
400     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
401             annotationSpaceFillerHolder.getWidth(), height));
402     annotationScroller.validate();// repaint();
403     repaint();
404   }
405
406   /**
407    * DOCUMENT ME!
408    * 
409    * @param wrap
410    *          DOCUMENT ME!
411    */
412   public void setWrapAlignment(boolean wrap)
413   {
414     av.startSeq = 0;
415     scalePanelHolder.setVisible(!wrap);
416     hscroll.setVisible(!wrap);
417     idwidthAdjuster.setVisible(!wrap);
418
419     if (wrap)
420     {
421       annotationScroller.setVisible(false);
422       annotationSpaceFillerHolder.setVisible(false);
423     }
424     else if (av.showAnnotation)
425     {
426       annotationScroller.setVisible(true);
427       annotationSpaceFillerHolder.setVisible(true);
428     }
429
430     idSpaceFillerPanel1.setVisible(!wrap);
431
432     repaint();
433   }
434
435   // return value is true if the scroll is valid
436   public boolean scrollUp(boolean up)
437   {
438     if (up)
439     {
440       if (vscroll.getValue() < 1)
441       {
442         return false;
443       }
444
445       fastPaint = false;
446       vscroll.setValue(vscroll.getValue() - 1);
447     }
448     else
449     {
450       if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight())
451       {
452         return false;
453       }
454
455       fastPaint = false;
456       vscroll.setValue(vscroll.getValue() + 1);
457     }
458
459     fastPaint = true;
460
461     return true;
462   }
463
464   /**
465    * DOCUMENT ME!
466    * 
467    * @param right
468    *          DOCUMENT ME!
469    * 
470    * @return DOCUMENT ME!
471    */
472   public boolean scrollRight(boolean right)
473   {
474     if (!right)
475     {
476       if (hscroll.getValue() < 1)
477       {
478         return false;
479       }
480
481       fastPaint = false;
482       hscroll.setValue(hscroll.getValue() - 1);
483     }
484     else
485     {
486       if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth())
487       {
488         return false;
489       }
490
491       fastPaint = false;
492       hscroll.setValue(hscroll.getValue() + 1);
493     }
494
495     fastPaint = true;
496
497     return true;
498   }
499
500   /**
501    * DOCUMENT ME!
502    * 
503    * @param x
504    *          DOCUMENT ME!
505    * @param y
506    *          DOCUMENT ME!
507    */
508   public void setScrollValues(int x, int y)
509   {
510     // System.err.println("Scroll to "+x+","+y);
511     if (av == null || av.alignment == null)
512     {
513       return;
514     }
515     int width = av.alignment.getWidth();
516     int height = av.alignment.getHeight();
517
518     if (av.hasHiddenColumns)
519     {
520       width = av.getColumnSelection().findColumnPosition(width);
521     }
522
523     av.setEndRes((x + (seqPanel.seqCanvas.getWidth() / av.charWidth)) - 1);
524
525     hextent = seqPanel.seqCanvas.getWidth() / av.charWidth;
526     vextent = seqPanel.seqCanvas.getHeight() / av.charHeight;
527
528     if (hextent > width)
529     {
530       hextent = width;
531     }
532
533     if (vextent > height)
534     {
535       vextent = height;
536     }
537
538     if ((hextent + x) > width)
539     {
540       x = width - hextent;
541     }
542
543     if ((vextent + y) > height)
544     {
545       y = height - vextent;
546     }
547
548     if (y < 0)
549     {
550       y = 0;
551     }
552
553     if (x < 0)
554     {
555       x = 0;
556     }
557
558     hscroll.setValues(x, hextent, 0, width);
559     vscroll.setValues(y, vextent, 0, height);
560   }
561
562   /**
563    * DOCUMENT ME!
564    * 
565    * @param evt
566    *          DOCUMENT ME!
567    */
568   public void adjustmentValueChanged(AdjustmentEvent evt)
569   {
570
571     int oldX = av.getStartRes();
572     int oldY = av.getStartSeq();
573
574     if (evt.getSource() == hscroll)
575     {
576       int x = hscroll.getValue();
577       av.setStartRes(x);
578       av
579               .setEndRes((x + (seqPanel.seqCanvas.getWidth() / av
580                       .getCharWidth())) - 1);
581     }
582
583     if (evt.getSource() == vscroll)
584     {
585       int offy = vscroll.getValue();
586
587       if (av.getWrapAlignment())
588       {
589         if (offy > -1)
590         {
591           int rowSize = seqPanel.seqCanvas
592                   .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
593           av.setStartRes(offy * rowSize);
594           av.setEndRes((offy + 1) * rowSize);
595         }
596         else
597         {
598           // This is only called if file loaded is a jar file that
599           // was wrapped when saved and user has wrap alignment true
600           // as preference setting
601           SwingUtilities.invokeLater(new Runnable()
602           {
603             public void run()
604             {
605               setScrollValues(av.getStartRes(), av.getStartSeq());
606             }
607           });
608         }
609       }
610       else
611       {
612         av.setStartSeq(offy);
613         av.setEndSeq(offy
614                 + (seqPanel.seqCanvas.getHeight() / av.getCharHeight()));
615       }
616     }
617
618     if (overviewPanel != null)
619     {
620       overviewPanel.setBoxPosition();
621     }
622
623     int scrollX = av.startRes - oldX;
624     int scrollY = av.startSeq - oldY;
625
626     if (av.getWrapAlignment() || !fastPaint)
627     {
628       repaint();
629     }
630     else
631     {
632       // Make sure we're not trying to draw a panel
633       // larger than the visible window
634       if (scrollX > av.endRes - av.startRes)
635       {
636         scrollX = av.endRes - av.startRes;
637       }
638       else if (scrollX < av.startRes - av.endRes)
639       {
640         scrollX = av.startRes - av.endRes;
641       }
642
643       if (scrollX != 0 || scrollY != 0)
644       {
645         idPanel.idCanvas.fastPaint(scrollY);
646         seqPanel.seqCanvas.fastPaint(scrollX, scrollY);
647         scalePanel.repaint();
648
649         if (av.getShowAnnotation())
650         {
651           annotationPanel.fastPaint(scrollX);
652         }
653       }
654     }
655   }
656
657   public void paintAlignment(boolean updateOverview)
658   {
659     repaint();
660
661     if (updateOverview)
662     {
663       jalview.structure.StructureSelectionManager
664               .getStructureSelectionManager().sequenceColoursChanged(this);
665
666       if (overviewPanel != null)
667       {
668         overviewPanel.updateOverviewImage();
669       }
670     }
671   }
672
673   /**
674    * DOCUMENT ME!
675    * 
676    * @param g
677    *          DOCUMENT ME!
678    */
679   public void paintComponent(Graphics g)
680   {
681     invalidate();
682
683     Dimension d = idPanel.idCanvas.getPreferredSize();
684     idPanelHolder.setPreferredSize(d);
685     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
686     validate();
687
688     if (av.getWrapAlignment())
689     {
690       int maxwidth = av.alignment.getWidth();
691
692       if (av.hasHiddenColumns)
693       {
694         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
695       }
696
697       int canvasWidth = seqPanel.seqCanvas
698               .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
699       if (canvasWidth > 0)
700       {
701         int max = maxwidth
702                 / seqPanel.seqCanvas
703                         .getWrappedCanvasWidth(seqPanel.seqCanvas
704                                 .getWidth()) + 1;
705         vscroll.setMaximum(max);
706         vscroll.setUnitIncrement(1);
707         vscroll.setVisibleAmount(1);
708       }
709     }
710     else
711     {
712       setScrollValues(av.getStartRes(), av.getStartSeq());
713     }
714   }
715
716   /**
717    * DOCUMENT ME!
718    * 
719    * @param pg
720    *          DOCUMENT ME!
721    * @param pf
722    *          DOCUMENT ME!
723    * @param pi
724    *          DOCUMENT ME!
725    * 
726    * @return DOCUMENT ME!
727    * 
728    * @throws PrinterException
729    *           DOCUMENT ME!
730    */
731   public int print(Graphics pg, PageFormat pf, int pi)
732           throws PrinterException
733   {
734     pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
735
736     int pwidth = (int) pf.getImageableWidth();
737     int pheight = (int) pf.getImageableHeight();
738
739     if (av.getWrapAlignment())
740     {
741       return printWrappedAlignment(pg, pwidth, pheight, pi);
742     }
743     else
744     {
745       return printUnwrapped(pg, pwidth, pheight, pi);
746     }
747   }
748
749   /**
750    * DOCUMENT ME!
751    * 
752    * @param pg
753    *          DOCUMENT ME!
754    * @param pwidth
755    *          DOCUMENT ME!
756    * @param pheight
757    *          DOCUMENT ME!
758    * @param pi
759    *          DOCUMENT ME!
760    * 
761    * @return DOCUMENT ME!
762    * 
763    * @throws PrinterException
764    *           DOCUMENT ME!
765    */
766   public int printUnwrapped(Graphics pg, int pwidth, int pheight, int pi)
767           throws PrinterException
768   {
769     int idWidth = getVisibleIdWidth();
770     FontMetrics fm = getFontMetrics(av.getFont());
771     int scaleHeight = av.charHeight + fm.getDescent();
772
773     pg.setColor(Color.white);
774     pg.fillRect(0, 0, pwidth, pheight);
775     pg.setFont(av.getFont());
776
777     // //////////////////////////////////
778     // / How many sequences and residues can we fit on a printable page?
779     int totalRes = (pwidth - idWidth) / av.getCharWidth();
780
781     int totalSeq = (int) ((pheight - scaleHeight) / av.getCharHeight()) - 1;
782
783     int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
784
785     // ///////////////////////////
786     // / Only print these sequences and residues on this page
787     int startRes;
788
789     // ///////////////////////////
790     // / Only print these sequences and residues on this page
791     int endRes;
792
793     // ///////////////////////////
794     // / Only print these sequences and residues on this page
795     int startSeq;
796
797     // ///////////////////////////
798     // / Only print these sequences and residues on this page
799     int endSeq;
800     startRes = (pi % pagesWide) * totalRes;
801     endRes = (startRes + totalRes) - 1;
802
803     if (endRes > (av.getAlignment().getWidth() - 1))
804     {
805       endRes = av.getAlignment().getWidth() - 1;
806     }
807
808     startSeq = (pi / pagesWide) * totalSeq;
809     endSeq = startSeq + totalSeq;
810
811     if (endSeq > av.getAlignment().getHeight())
812     {
813       endSeq = av.getAlignment().getHeight();
814     }
815
816     int pagesHigh = ((av.alignment.getHeight() / totalSeq) + 1) * pheight;
817
818     if (av.showAnnotation)
819     {
820       pagesHigh += annotationPanel.adjustPanelHeight() + 3;
821     }
822
823     pagesHigh /= pheight;
824
825     if (pi >= (pagesWide * pagesHigh))
826     {
827       return Printable.NO_SUCH_PAGE;
828     }
829
830     // draw Scale
831     pg.translate(idWidth, 0);
832     scalePanel.drawScale(pg, startRes, endRes, pwidth - idWidth,
833             scaleHeight);
834     pg.translate(-idWidth, scaleHeight);
835
836     // //////////////
837     // Draw the ids
838     Color currentColor = null;
839     Color currentTextColor = null;
840
841     pg.setFont(idPanel.idCanvas.idfont);
842
843     SequenceI seq;
844     for (int i = startSeq; i < endSeq; i++)
845     {
846       seq = av.getAlignment().getSequenceAt(i);
847       if ((av.getSelectionGroup() != null)
848               && av.getSelectionGroup().getSequences(null).contains(seq))
849       {
850         currentColor = Color.gray;
851         currentTextColor = Color.black;
852       }
853       else
854       {
855         currentColor = av.getSequenceColour(seq);
856         currentTextColor = Color.black;
857       }
858
859       pg.setColor(currentColor);
860       pg.fillRect(0, (i - startSeq) * av.charHeight, idWidth, av
861               .getCharHeight());
862
863       pg.setColor(currentTextColor);
864
865       int xPos = 0;
866       if (av.rightAlignIds)
867       {
868         fm = pg.getFontMetrics();
869         xPos = idWidth
870                 - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix()))
871                 - 4;
872       }
873
874       pg.drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos,
875               (((i - startSeq) * av.charHeight) + av.getCharHeight())
876                       - (av.getCharHeight() / 5));
877     }
878
879     pg.setFont(av.getFont());
880
881     // draw main sequence panel
882     pg.translate(idWidth, 0);
883     seqPanel.seqCanvas.drawPanel(pg, startRes, endRes, startSeq, endSeq, 0);
884
885     if (av.showAnnotation && (endSeq == av.alignment.getHeight()))
886     {
887       pg.translate(-idWidth - 3, (endSeq - startSeq) * av.charHeight + 3);
888       alabels.drawComponent((Graphics2D) pg, idWidth);
889       pg.translate(idWidth + 3, 0);
890       annotationPanel.drawComponent((Graphics2D) pg, startRes, endRes + 1);
891     }
892
893     return Printable.PAGE_EXISTS;
894   }
895
896   /**
897    * DOCUMENT ME!
898    * 
899    * @param pg
900    *          DOCUMENT ME!
901    * @param pwidth
902    *          DOCUMENT ME!
903    * @param pheight
904    *          DOCUMENT ME!
905    * @param pi
906    *          DOCUMENT ME!
907    * 
908    * @return DOCUMENT ME!
909    * 
910    * @throws PrinterException
911    *           DOCUMENT ME!
912    */
913   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
914           int pi) throws PrinterException
915   {
916
917     int annotationHeight = 0;
918     AnnotationLabels labels = null;
919     if (av.showAnnotation)
920     {
921       annotationHeight = annotationPanel.adjustPanelHeight();
922       labels = new AnnotationLabels(av);
923     }
924
925     int hgap = av.charHeight;
926     if (av.scaleAboveWrapped)
927     {
928       hgap += av.charHeight;
929     }
930
931     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
932             + annotationHeight;
933
934     int idWidth = getVisibleIdWidth();
935
936     int maxwidth = av.alignment.getWidth();
937     if (av.hasHiddenColumns)
938     {
939       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
940     }
941
942     int resWidth = seqPanel.seqCanvas.getWrappedCanvasWidth(pwidth
943             - idWidth);
944
945     int totalHeight = cHeight * (maxwidth / resWidth + 1);
946
947     pg.setColor(Color.white);
948     pg.fillRect(0, 0, pwidth, pheight);
949     pg.setFont(av.getFont());
950
951     // //////////////
952     // Draw the ids
953     pg.setColor(Color.black);
954
955     pg.translate(0, -pi * pheight);
956
957     pg.setClip(0, pi * pheight, pwidth, pheight);
958
959     int ypos = hgap;
960
961     do
962     {
963       for (int i = 0; i < av.alignment.getHeight(); i++)
964       {
965         pg.setFont(idPanel.idCanvas.idfont);
966         SequenceI s = av.alignment.getSequenceAt(i);
967         String string = s.getDisplayId(av.getShowJVSuffix());
968         int xPos = 0;
969         if (av.rightAlignIds)
970         {
971           FontMetrics fm = pg.getFontMetrics();
972           xPos = idWidth - fm.stringWidth(string) - 4;
973         }
974         pg.drawString(string, xPos,
975                 ((i * av.charHeight) + ypos + av.charHeight)
976                         - (av.charHeight / 5));
977       }
978       if (labels != null)
979       {
980         pg.translate(-3, ypos
981                 + (av.getAlignment().getHeight() * av.charHeight));
982
983         pg.setFont(av.getFont());
984         labels.drawComponent(pg, idWidth);
985         pg.translate(+3, -ypos
986                 - (av.getAlignment().getHeight() * av.charHeight));
987       }
988
989       ypos += cHeight;
990     } while (ypos < totalHeight);
991
992     pg.translate(idWidth, 0);
993
994     seqPanel.seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, totalHeight,
995             0);
996
997     if ((pi * pheight) < totalHeight)
998     {
999       return Printable.PAGE_EXISTS;
1000
1001     }
1002     else
1003     {
1004       return Printable.NO_SUCH_PAGE;
1005     }
1006   }
1007
1008   int getVisibleIdWidth()
1009   {
1010     return idPanel.getWidth() > 0 ? idPanel.getWidth()
1011             : calculateIdWidth().width + 4;
1012   }
1013
1014   void makeAlignmentImage(int type, File file)
1015   {
1016     int maxwidth = av.alignment.getWidth();
1017     if (av.hasHiddenColumns)
1018     {
1019       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth);
1020     }
1021
1022     int height = ((av.alignment.getHeight() + 1) * av.charHeight)
1023             + scalePanel.getHeight();
1024     int width = getVisibleIdWidth() + (maxwidth * av.charWidth);
1025
1026     if (av.getWrapAlignment())
1027     {
1028       height = getWrappedHeight();
1029       if (System.getProperty("java.awt.headless") != null
1030               && System.getProperty("java.awt.headless").equals("true"))
1031       {
1032         width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1033                 - alignFrame.getInsets().left
1034                 - alignFrame.getInsets().right;
1035       }
1036       else
1037       {
1038         width = seqPanel.getWidth() + getVisibleIdWidth();
1039       }
1040
1041     }
1042     else if (av.getShowAnnotation())
1043     {
1044       height += annotationPanel.adjustPanelHeight() + 3;
1045     }
1046
1047     try
1048     {
1049
1050       jalview.util.ImageMaker im;
1051       if (type == jalview.util.ImageMaker.PNG)
1052       {
1053         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
1054                 "Create PNG image from alignment", width, height, file,
1055                 null);
1056       }
1057       else
1058       {
1059         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
1060                 "Create EPS file from alignment", width, height, file,
1061                 alignFrame.getTitle());
1062       }
1063
1064       if (av.getWrapAlignment())
1065       {
1066         if (im.getGraphics() != null)
1067         {
1068           printWrappedAlignment(im.getGraphics(), width, height, 0);
1069           im.writeImage();
1070         }
1071       }
1072       else
1073       {
1074         if (im.getGraphics() != null)
1075         {
1076           printUnwrapped(im.getGraphics(), width, height, 0);
1077           im.writeImage();
1078         }
1079       }
1080     } catch (OutOfMemoryError err)
1081     {
1082       // Be noisy here.
1083       System.out.println("########################\n" + "OUT OF MEMORY "
1084               + file + "\n" + "########################");
1085       new OOMWarning("Creating Image for " + file, err);
1086       // System.out.println("Create IMAGE: " + err);
1087     } catch (Exception ex)
1088     {
1089       ex.printStackTrace();
1090     }
1091   }
1092
1093   /**
1094    * DOCUMENT ME!
1095    */
1096   public void makeEPS(File epsFile)
1097   {
1098     makeAlignmentImage(jalview.util.ImageMaker.EPS, epsFile);
1099   }
1100
1101   /**
1102    * DOCUMENT ME!
1103    */
1104   public void makePNG(File pngFile)
1105   {
1106     makeAlignmentImage(jalview.util.ImageMaker.PNG, pngFile);
1107   }
1108
1109   public void makePNGImageMap(File imgMapFile, String imageName)
1110   {
1111     // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
1112     // ////////////////////////////////////////////
1113     int idWidth = getVisibleIdWidth();
1114     FontMetrics fm = getFontMetrics(av.getFont());
1115     int scaleHeight = av.charHeight + fm.getDescent();
1116
1117     // Gen image map
1118     // ////////////////////////////////
1119     if (imgMapFile != null)
1120     {
1121       try
1122       {
1123         int s, sSize = av.alignment.getHeight(), res, alwidth = av.alignment
1124                 .getWidth(), g, gSize, f, fSize, sy;
1125         StringBuffer text = new StringBuffer();
1126         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1127         out.println(jalview.io.HTMLOutput.getImageMapHTML());
1128         out.println("<img src=\"" + imageName
1129                 + "\" border=\"0\" usemap=\"#Map\" >"
1130                 + "<map name=\"Map\">");
1131
1132         for (s = 0; s < sSize; s++)
1133         {
1134           sy = s * av.charHeight + scaleHeight;
1135
1136           SequenceI seq = av.alignment.getSequenceAt(s);
1137           SequenceFeature[] features = seq.getDatasetSequence()
1138                   .getSequenceFeatures();
1139           SequenceGroup[] groups = av.alignment.findAllGroups(seq);
1140           for (res = 0; res < alwidth; res++)
1141           {
1142             text = new StringBuffer();
1143             Object obj = null;
1144             if (av.alignment.isNucleotide())
1145             {
1146               obj = ResidueProperties.nucleotideName.get(seq.getCharAt(res)
1147                       + "");
1148             }
1149             else
1150             {
1151               obj = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
1152                       + "");
1153             }
1154
1155             if (obj == null)
1156             {
1157               continue;
1158             }
1159
1160             String triplet = obj.toString();
1161             int alIndex = seq.findPosition(res);
1162             gSize = groups.length;
1163             for (g = 0; g < gSize; g++)
1164             {
1165               if (text.length() < 1)
1166               {
1167                 text.append("<area shape=\"rect\" coords=\""
1168                         + (idWidth + res * av.charWidth) + "," + sy + ","
1169                         + (idWidth + (res + 1) * av.charWidth) + ","
1170                         + (av.charHeight + sy) + "\""
1171                         + " onMouseOver=\"toolTip('" + alIndex + " "
1172                         + triplet);
1173               }
1174
1175               if (groups[g].getStartRes() < res
1176                       && groups[g].getEndRes() > res)
1177               {
1178                 text.append("<br><em>" + groups[g].getName() + "</em>");
1179               }
1180             }
1181
1182             if (features != null)
1183             {
1184               if (text.length() < 1)
1185               {
1186                 text.append("<area shape=\"rect\" coords=\""
1187                         + (idWidth + res * av.charWidth) + "," + sy + ","
1188                         + (idWidth + (res + 1) * av.charWidth) + ","
1189                         + (av.charHeight + sy) + "\""
1190                         + " onMouseOver=\"toolTip('" + alIndex + " "
1191                         + triplet);
1192               }
1193               fSize = features.length;
1194               for (f = 0; f < fSize; f++)
1195               {
1196
1197                 if ((features[f].getBegin() <= seq.findPosition(res))
1198                         && (features[f].getEnd() >= seq.findPosition(res)))
1199                 {
1200                   if (features[f].getType().equals("disulfide bond"))
1201                   {
1202                     if (features[f].getBegin() == seq.findPosition(res)
1203                             || features[f].getEnd() == seq
1204                                     .findPosition(res))
1205                     {
1206                       text.append("<br>disulfide bond "
1207                               + features[f].getBegin() + ":"
1208                               + features[f].getEnd());
1209                     }
1210                   }
1211                   else
1212                   {
1213                     text.append("<br>");
1214                     text.append(features[f].getType());
1215                     if (features[f].getDescription() != null
1216                             && !features[f].getType().equals(
1217                                     features[f].getDescription()))
1218                     {
1219                       text.append(" " + features[f].getDescription());
1220                     }
1221
1222                     if (features[f].getValue("status") != null)
1223                     {
1224                       text.append(" (" + features[f].getValue("status")
1225                               + ")");
1226                     }
1227                   }
1228                 }
1229
1230               }
1231             }
1232             if (text.length() > 1)
1233             {
1234               text.append("')\"; onMouseOut=\"toolTip()\";  href=\"#\">");
1235               out.println(text.toString());
1236             }
1237           }
1238         }
1239         out.println("</map></body></html>");
1240         out.close();
1241
1242       } catch (Exception ex)
1243       {
1244         ex.printStackTrace();
1245       }
1246     } // /////////END OF IMAGE MAP
1247
1248   }
1249
1250   int getWrappedHeight()
1251   {
1252     int seqPanelWidth = seqPanel.seqCanvas.getWidth();
1253
1254     if (System.getProperty("java.awt.headless") != null
1255             && System.getProperty("java.awt.headless").equals("true"))
1256     {
1257       seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1258               - vscroll.getPreferredSize().width
1259               - alignFrame.getInsets().left - alignFrame.getInsets().right;
1260     }
1261
1262     int chunkWidth = seqPanel.seqCanvas
1263             .getWrappedCanvasWidth(seqPanelWidth);
1264
1265     int hgap = av.charHeight;
1266     if (av.scaleAboveWrapped)
1267     {
1268       hgap += av.charHeight;
1269     }
1270
1271     int annotationHeight = 0;
1272     if (av.showAnnotation)
1273     {
1274       annotationHeight = annotationPanel.adjustPanelHeight();
1275     }
1276
1277     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
1278             + annotationHeight;
1279
1280     int maxwidth = av.alignment.getWidth();
1281     if (av.hasHiddenColumns)
1282     {
1283       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
1284     }
1285
1286     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1287
1288     return height;
1289   }
1290
1291   /**
1292    * close the panel - deregisters all listeners and nulls any references to
1293    * alignment data.
1294    */
1295   public void closePanel()
1296   {
1297     jalview.structure.StructureSelectionManager ssm = jalview.structure.StructureSelectionManager
1298             .getStructureSelectionManager();
1299     ssm.removeStructureViewerListener(seqPanel, null);
1300     ssm.removeSelectionListener(seqPanel);
1301     PaintRefresher.RemoveComponent(seqPanel.seqCanvas);
1302     PaintRefresher.RemoveComponent(idPanel.idCanvas);
1303     PaintRefresher.RemoveComponent(this);
1304     if (av != null)
1305     {
1306       av.alignment = null;
1307       av = null;
1308     }
1309     else
1310     {
1311       if (Cache.log.isDebugEnabled())
1312       {
1313         Cache.log.warn("Closing alignment panel which is already closed.");
1314       }
1315     }
1316   }
1317
1318   /**
1319    * hides or shows dynamic annotation rows based on groups and av state flags
1320    */
1321   public void updateAnnotation()
1322   {
1323     boolean updateCalcs = false;
1324     boolean conv = av.isShowGroupConservation();
1325     boolean cons = av.isShowGroupConsensus();
1326     boolean sortg = true;
1327
1328     // remove old automatic annotation
1329     // add any new annotation
1330
1331     Vector gr = av.alignment.getGroups(); // OrderedBy(av.alignment.getSequencesArray());
1332     // intersect alignment annotation with alignment groups
1333
1334     AlignmentAnnotation[] aan = av.alignment.getAlignmentAnnotation();
1335     for (int an = 0; an < aan.length; an++)
1336     {
1337       if (aan[an].autoCalculated && aan[an].groupRef != null)
1338       {
1339         av.alignment.deleteAnnotation(aan[an]);
1340         aan[an] = null;
1341       }
1342     }
1343     SequenceGroup sg;
1344     for (int g = 0; g < gr.size(); g++)
1345     {
1346       updateCalcs = false;
1347       sg = (SequenceGroup) gr.elementAt(g);
1348
1349       if (conv)
1350       {
1351         updateCalcs = true;
1352         av.alignment.addAnnotation(sg.getConservationRow(),0);
1353       }
1354       if (cons)
1355       {
1356         updateCalcs = true;
1357         av.alignment.addAnnotation(sg.getConsensus(),0);
1358       }
1359       // refresh the annotation rows
1360       if (updateCalcs)
1361       {
1362         sg.recalcConservation();
1363       }
1364     }
1365     adjustAnnotationHeight();
1366   }
1367 }