ensure that all bits of annotation panel (slider, annotation labels) get updated
[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     addNotify();
404     repaint();
405   }
406
407   /**
408    * DOCUMENT ME!
409    * 
410    * @param wrap
411    *          DOCUMENT ME!
412    */
413   public void setWrapAlignment(boolean wrap)
414   {
415     av.startSeq = 0;
416     scalePanelHolder.setVisible(!wrap);
417     hscroll.setVisible(!wrap);
418     idwidthAdjuster.setVisible(!wrap);
419
420     if (wrap)
421     {
422       annotationScroller.setVisible(false);
423       annotationSpaceFillerHolder.setVisible(false);
424     }
425     else if (av.showAnnotation)
426     {
427       annotationScroller.setVisible(true);
428       annotationSpaceFillerHolder.setVisible(true);
429     }
430
431     idSpaceFillerPanel1.setVisible(!wrap);
432
433     repaint();
434   }
435
436   // return value is true if the scroll is valid
437   public boolean scrollUp(boolean up)
438   {
439     if (up)
440     {
441       if (vscroll.getValue() < 1)
442       {
443         return false;
444       }
445
446       fastPaint = false;
447       vscroll.setValue(vscroll.getValue() - 1);
448     }
449     else
450     {
451       if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight())
452       {
453         return false;
454       }
455
456       fastPaint = false;
457       vscroll.setValue(vscroll.getValue() + 1);
458     }
459
460     fastPaint = true;
461
462     return true;
463   }
464
465   /**
466    * DOCUMENT ME!
467    * 
468    * @param right
469    *          DOCUMENT ME!
470    * 
471    * @return DOCUMENT ME!
472    */
473   public boolean scrollRight(boolean right)
474   {
475     if (!right)
476     {
477       if (hscroll.getValue() < 1)
478       {
479         return false;
480       }
481
482       fastPaint = false;
483       hscroll.setValue(hscroll.getValue() - 1);
484     }
485     else
486     {
487       if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth())
488       {
489         return false;
490       }
491
492       fastPaint = false;
493       hscroll.setValue(hscroll.getValue() + 1);
494     }
495
496     fastPaint = true;
497
498     return true;
499   }
500
501   /**
502    * DOCUMENT ME!
503    * 
504    * @param x
505    *          DOCUMENT ME!
506    * @param y
507    *          DOCUMENT ME!
508    */
509   public void setScrollValues(int x, int y)
510   {
511     // System.err.println("Scroll to "+x+","+y);
512     if (av == null || av.alignment == null)
513     {
514       return;
515     }
516     int width = av.alignment.getWidth();
517     int height = av.alignment.getHeight();
518
519     if (av.hasHiddenColumns)
520     {
521       width = av.getColumnSelection().findColumnPosition(width);
522     }
523
524     av.setEndRes((x + (seqPanel.seqCanvas.getWidth() / av.charWidth)) - 1);
525
526     hextent = seqPanel.seqCanvas.getWidth() / av.charWidth;
527     vextent = seqPanel.seqCanvas.getHeight() / av.charHeight;
528
529     if (hextent > width)
530     {
531       hextent = width;
532     }
533
534     if (vextent > height)
535     {
536       vextent = height;
537     }
538
539     if ((hextent + x) > width)
540     {
541       x = width - hextent;
542     }
543
544     if ((vextent + y) > height)
545     {
546       y = height - vextent;
547     }
548
549     if (y < 0)
550     {
551       y = 0;
552     }
553
554     if (x < 0)
555     {
556       x = 0;
557     }
558
559     hscroll.setValues(x, hextent, 0, width);
560     vscroll.setValues(y, vextent, 0, height);
561   }
562
563   /**
564    * DOCUMENT ME!
565    * 
566    * @param evt
567    *          DOCUMENT ME!
568    */
569   public void adjustmentValueChanged(AdjustmentEvent evt)
570   {
571
572     int oldX = av.getStartRes();
573     int oldY = av.getStartSeq();
574
575     if (evt.getSource() == hscroll)
576     {
577       int x = hscroll.getValue();
578       av.setStartRes(x);
579       av
580               .setEndRes((x + (seqPanel.seqCanvas.getWidth() / av
581                       .getCharWidth())) - 1);
582     }
583
584     if (evt.getSource() == vscroll)
585     {
586       int offy = vscroll.getValue();
587
588       if (av.getWrapAlignment())
589       {
590         if (offy > -1)
591         {
592           int rowSize = seqPanel.seqCanvas
593                   .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
594           av.setStartRes(offy * rowSize);
595           av.setEndRes((offy + 1) * rowSize);
596         }
597         else
598         {
599           // This is only called if file loaded is a jar file that
600           // was wrapped when saved and user has wrap alignment true
601           // as preference setting
602           SwingUtilities.invokeLater(new Runnable()
603           {
604             public void run()
605             {
606               setScrollValues(av.getStartRes(), av.getStartSeq());
607             }
608           });
609         }
610       }
611       else
612       {
613         av.setStartSeq(offy);
614         av.setEndSeq(offy
615                 + (seqPanel.seqCanvas.getHeight() / av.getCharHeight()));
616       }
617     }
618
619     if (overviewPanel != null)
620     {
621       overviewPanel.setBoxPosition();
622     }
623
624     int scrollX = av.startRes - oldX;
625     int scrollY = av.startSeq - oldY;
626
627     if (av.getWrapAlignment() || !fastPaint)
628     {
629       repaint();
630     }
631     else
632     {
633       // Make sure we're not trying to draw a panel
634       // larger than the visible window
635       if (scrollX > av.endRes - av.startRes)
636       {
637         scrollX = av.endRes - av.startRes;
638       }
639       else if (scrollX < av.startRes - av.endRes)
640       {
641         scrollX = av.startRes - av.endRes;
642       }
643
644       if (scrollX != 0 || scrollY != 0)
645       {
646         idPanel.idCanvas.fastPaint(scrollY);
647         seqPanel.seqCanvas.fastPaint(scrollX, scrollY);
648         scalePanel.repaint();
649
650         if (av.getShowAnnotation())
651         {
652           annotationPanel.fastPaint(scrollX);
653         }
654       }
655     }
656   }
657
658   public void paintAlignment(boolean updateOverview)
659   {
660     repaint();
661
662     if (updateOverview)
663     {
664       jalview.structure.StructureSelectionManager
665               .getStructureSelectionManager().sequenceColoursChanged(this);
666
667       if (overviewPanel != null)
668       {
669         overviewPanel.updateOverviewImage();
670       }
671     }
672   }
673
674   /**
675    * DOCUMENT ME!
676    * 
677    * @param g
678    *          DOCUMENT ME!
679    */
680   public void paintComponent(Graphics g)
681   {
682     invalidate();
683
684     Dimension d = idPanel.idCanvas.getPreferredSize();
685     idPanelHolder.setPreferredSize(d);
686     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
687     validate();
688
689     if (av.getWrapAlignment())
690     {
691       int maxwidth = av.alignment.getWidth();
692
693       if (av.hasHiddenColumns)
694       {
695         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
696       }
697
698       int canvasWidth = seqPanel.seqCanvas
699               .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
700       if (canvasWidth > 0)
701       {
702         int max = maxwidth
703                 / seqPanel.seqCanvas
704                         .getWrappedCanvasWidth(seqPanel.seqCanvas
705                                 .getWidth()) + 1;
706         vscroll.setMaximum(max);
707         vscroll.setUnitIncrement(1);
708         vscroll.setVisibleAmount(1);
709       }
710     }
711     else
712     {
713       setScrollValues(av.getStartRes(), av.getStartSeq());
714     }
715   }
716
717   /**
718    * DOCUMENT ME!
719    * 
720    * @param pg
721    *          DOCUMENT ME!
722    * @param pf
723    *          DOCUMENT ME!
724    * @param pi
725    *          DOCUMENT ME!
726    * 
727    * @return DOCUMENT ME!
728    * 
729    * @throws PrinterException
730    *           DOCUMENT ME!
731    */
732   public int print(Graphics pg, PageFormat pf, int pi)
733           throws PrinterException
734   {
735     pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
736
737     int pwidth = (int) pf.getImageableWidth();
738     int pheight = (int) pf.getImageableHeight();
739
740     if (av.getWrapAlignment())
741     {
742       return printWrappedAlignment(pg, pwidth, pheight, pi);
743     }
744     else
745     {
746       return printUnwrapped(pg, pwidth, pheight, pi);
747     }
748   }
749
750   /**
751    * DOCUMENT ME!
752    * 
753    * @param pg
754    *          DOCUMENT ME!
755    * @param pwidth
756    *          DOCUMENT ME!
757    * @param pheight
758    *          DOCUMENT ME!
759    * @param pi
760    *          DOCUMENT ME!
761    * 
762    * @return DOCUMENT ME!
763    * 
764    * @throws PrinterException
765    *           DOCUMENT ME!
766    */
767   public int printUnwrapped(Graphics pg, int pwidth, int pheight, int pi)
768           throws PrinterException
769   {
770     int idWidth = getVisibleIdWidth();
771     FontMetrics fm = getFontMetrics(av.getFont());
772     int scaleHeight = av.charHeight + fm.getDescent();
773
774     pg.setColor(Color.white);
775     pg.fillRect(0, 0, pwidth, pheight);
776     pg.setFont(av.getFont());
777
778     // //////////////////////////////////
779     // / How many sequences and residues can we fit on a printable page?
780     int totalRes = (pwidth - idWidth) / av.getCharWidth();
781
782     int totalSeq = (int) ((pheight - scaleHeight) / av.getCharHeight()) - 1;
783
784     int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
785
786     // ///////////////////////////
787     // / Only print these sequences and residues on this page
788     int startRes;
789
790     // ///////////////////////////
791     // / Only print these sequences and residues on this page
792     int endRes;
793
794     // ///////////////////////////
795     // / Only print these sequences and residues on this page
796     int startSeq;
797
798     // ///////////////////////////
799     // / Only print these sequences and residues on this page
800     int endSeq;
801     startRes = (pi % pagesWide) * totalRes;
802     endRes = (startRes + totalRes) - 1;
803
804     if (endRes > (av.getAlignment().getWidth() - 1))
805     {
806       endRes = av.getAlignment().getWidth() - 1;
807     }
808
809     startSeq = (pi / pagesWide) * totalSeq;
810     endSeq = startSeq + totalSeq;
811
812     if (endSeq > av.getAlignment().getHeight())
813     {
814       endSeq = av.getAlignment().getHeight();
815     }
816
817     int pagesHigh = ((av.alignment.getHeight() / totalSeq) + 1) * pheight;
818
819     if (av.showAnnotation)
820     {
821       pagesHigh += annotationPanel.adjustPanelHeight() + 3;
822     }
823
824     pagesHigh /= pheight;
825
826     if (pi >= (pagesWide * pagesHigh))
827     {
828       return Printable.NO_SUCH_PAGE;
829     }
830
831     // draw Scale
832     pg.translate(idWidth, 0);
833     scalePanel.drawScale(pg, startRes, endRes, pwidth - idWidth,
834             scaleHeight);
835     pg.translate(-idWidth, scaleHeight);
836
837     // //////////////
838     // Draw the ids
839     Color currentColor = null;
840     Color currentTextColor = null;
841
842     pg.setFont(idPanel.idCanvas.idfont);
843
844     SequenceI seq;
845     for (int i = startSeq; i < endSeq; i++)
846     {
847       seq = av.getAlignment().getSequenceAt(i);
848       if ((av.getSelectionGroup() != null)
849               && av.getSelectionGroup().getSequences(null).contains(seq))
850       {
851         currentColor = Color.gray;
852         currentTextColor = Color.black;
853       }
854       else
855       {
856         currentColor = av.getSequenceColour(seq);
857         currentTextColor = Color.black;
858       }
859
860       pg.setColor(currentColor);
861       pg.fillRect(0, (i - startSeq) * av.charHeight, idWidth, av
862               .getCharHeight());
863
864       pg.setColor(currentTextColor);
865
866       int xPos = 0;
867       if (av.rightAlignIds)
868       {
869         fm = pg.getFontMetrics();
870         xPos = idWidth
871                 - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix()))
872                 - 4;
873       }
874
875       pg.drawString(seq.getDisplayId(av.getShowJVSuffix()), xPos,
876               (((i - startSeq) * av.charHeight) + av.getCharHeight())
877                       - (av.getCharHeight() / 5));
878     }
879
880     pg.setFont(av.getFont());
881
882     // draw main sequence panel
883     pg.translate(idWidth, 0);
884     seqPanel.seqCanvas.drawPanel(pg, startRes, endRes, startSeq, endSeq, 0);
885
886     if (av.showAnnotation && (endSeq == av.alignment.getHeight()))
887     {
888       pg.translate(-idWidth - 3, (endSeq - startSeq) * av.charHeight + 3);
889       alabels.drawComponent((Graphics2D) pg, idWidth);
890       pg.translate(idWidth + 3, 0);
891       annotationPanel.drawComponent((Graphics2D) pg, startRes, endRes + 1);
892     }
893
894     return Printable.PAGE_EXISTS;
895   }
896
897   /**
898    * DOCUMENT ME!
899    * 
900    * @param pg
901    *          DOCUMENT ME!
902    * @param pwidth
903    *          DOCUMENT ME!
904    * @param pheight
905    *          DOCUMENT ME!
906    * @param pi
907    *          DOCUMENT ME!
908    * 
909    * @return DOCUMENT ME!
910    * 
911    * @throws PrinterException
912    *           DOCUMENT ME!
913    */
914   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
915           int pi) throws PrinterException
916   {
917
918     int annotationHeight = 0;
919     AnnotationLabels labels = null;
920     if (av.showAnnotation)
921     {
922       annotationHeight = annotationPanel.adjustPanelHeight();
923       labels = new AnnotationLabels(av);
924     }
925
926     int hgap = av.charHeight;
927     if (av.scaleAboveWrapped)
928     {
929       hgap += av.charHeight;
930     }
931
932     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
933             + annotationHeight;
934
935     int idWidth = getVisibleIdWidth();
936
937     int maxwidth = av.alignment.getWidth();
938     if (av.hasHiddenColumns)
939     {
940       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
941     }
942
943     int resWidth = seqPanel.seqCanvas.getWrappedCanvasWidth(pwidth
944             - idWidth);
945
946     int totalHeight = cHeight * (maxwidth / resWidth + 1);
947
948     pg.setColor(Color.white);
949     pg.fillRect(0, 0, pwidth, pheight);
950     pg.setFont(av.getFont());
951
952     // //////////////
953     // Draw the ids
954     pg.setColor(Color.black);
955
956     pg.translate(0, -pi * pheight);
957
958     pg.setClip(0, pi * pheight, pwidth, pheight);
959
960     int ypos = hgap;
961
962     do
963     {
964       for (int i = 0; i < av.alignment.getHeight(); i++)
965       {
966         pg.setFont(idPanel.idCanvas.idfont);
967         SequenceI s = av.alignment.getSequenceAt(i);
968         String string = s.getDisplayId(av.getShowJVSuffix());
969         int xPos = 0;
970         if (av.rightAlignIds)
971         {
972           FontMetrics fm = pg.getFontMetrics();
973           xPos = idWidth - fm.stringWidth(string) - 4;
974         }
975         pg.drawString(string, xPos,
976                 ((i * av.charHeight) + ypos + av.charHeight)
977                         - (av.charHeight / 5));
978       }
979       if (labels != null)
980       {
981         pg.translate(-3, ypos
982                 + (av.getAlignment().getHeight() * av.charHeight));
983
984         pg.setFont(av.getFont());
985         labels.drawComponent(pg, idWidth);
986         pg.translate(+3, -ypos
987                 - (av.getAlignment().getHeight() * av.charHeight));
988       }
989
990       ypos += cHeight;
991     } while (ypos < totalHeight);
992
993     pg.translate(idWidth, 0);
994
995     seqPanel.seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, totalHeight,
996             0);
997
998     if ((pi * pheight) < totalHeight)
999     {
1000       return Printable.PAGE_EXISTS;
1001
1002     }
1003     else
1004     {
1005       return Printable.NO_SUCH_PAGE;
1006     }
1007   }
1008
1009   int getVisibleIdWidth()
1010   {
1011     return idPanel.getWidth() > 0 ? idPanel.getWidth()
1012             : calculateIdWidth().width + 4;
1013   }
1014
1015   void makeAlignmentImage(int type, File file)
1016   {
1017     int maxwidth = av.alignment.getWidth();
1018     if (av.hasHiddenColumns)
1019     {
1020       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth);
1021     }
1022
1023     int height = ((av.alignment.getHeight() + 1) * av.charHeight)
1024             + scalePanel.getHeight();
1025     int width = getVisibleIdWidth() + (maxwidth * av.charWidth);
1026
1027     if (av.getWrapAlignment())
1028     {
1029       height = getWrappedHeight();
1030       if (System.getProperty("java.awt.headless") != null
1031               && System.getProperty("java.awt.headless").equals("true"))
1032       {
1033         width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1034                 - alignFrame.getInsets().left
1035                 - alignFrame.getInsets().right;
1036       }
1037       else
1038       {
1039         width = seqPanel.getWidth() + getVisibleIdWidth();
1040       }
1041
1042     }
1043     else if (av.getShowAnnotation())
1044     {
1045       height += annotationPanel.adjustPanelHeight() + 3;
1046     }
1047
1048     try
1049     {
1050
1051       jalview.util.ImageMaker im;
1052       if (type == jalview.util.ImageMaker.PNG)
1053       {
1054         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
1055                 "Create PNG image from alignment", width, height, file,
1056                 null);
1057       }
1058       else
1059       {
1060         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
1061                 "Create EPS file from alignment", width, height, file,
1062                 alignFrame.getTitle());
1063       }
1064
1065       if (av.getWrapAlignment())
1066       {
1067         if (im.getGraphics() != null)
1068         {
1069           printWrappedAlignment(im.getGraphics(), width, height, 0);
1070           im.writeImage();
1071         }
1072       }
1073       else
1074       {
1075         if (im.getGraphics() != null)
1076         {
1077           printUnwrapped(im.getGraphics(), width, height, 0);
1078           im.writeImage();
1079         }
1080       }
1081     } catch (OutOfMemoryError err)
1082     {
1083       // Be noisy here.
1084       System.out.println("########################\n" + "OUT OF MEMORY "
1085               + file + "\n" + "########################");
1086       new OOMWarning("Creating Image for " + file, err);
1087       // System.out.println("Create IMAGE: " + err);
1088     } catch (Exception ex)
1089     {
1090       ex.printStackTrace();
1091     }
1092   }
1093
1094   /**
1095    * DOCUMENT ME!
1096    */
1097   public void makeEPS(File epsFile)
1098   {
1099     makeAlignmentImage(jalview.util.ImageMaker.EPS, epsFile);
1100   }
1101
1102   /**
1103    * DOCUMENT ME!
1104    */
1105   public void makePNG(File pngFile)
1106   {
1107     makeAlignmentImage(jalview.util.ImageMaker.PNG, pngFile);
1108   }
1109
1110   public void makePNGImageMap(File imgMapFile, String imageName)
1111   {
1112     // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
1113     // ////////////////////////////////////////////
1114     int idWidth = getVisibleIdWidth();
1115     FontMetrics fm = getFontMetrics(av.getFont());
1116     int scaleHeight = av.charHeight + fm.getDescent();
1117
1118     // Gen image map
1119     // ////////////////////////////////
1120     if (imgMapFile != null)
1121     {
1122       try
1123       {
1124         int s, sSize = av.alignment.getHeight(), res, alwidth = av.alignment
1125                 .getWidth(), g, gSize, f, fSize, sy;
1126         StringBuffer text = new StringBuffer();
1127         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1128         out.println(jalview.io.HTMLOutput.getImageMapHTML());
1129         out.println("<img src=\"" + imageName
1130                 + "\" border=\"0\" usemap=\"#Map\" >"
1131                 + "<map name=\"Map\">");
1132
1133         for (s = 0; s < sSize; s++)
1134         {
1135           sy = s * av.charHeight + scaleHeight;
1136
1137           SequenceI seq = av.alignment.getSequenceAt(s);
1138           SequenceFeature[] features = seq.getDatasetSequence()
1139                   .getSequenceFeatures();
1140           SequenceGroup[] groups = av.alignment.findAllGroups(seq);
1141           for (res = 0; res < alwidth; res++)
1142           {
1143             text = new StringBuffer();
1144             Object obj = null;
1145             if (av.alignment.isNucleotide())
1146             {
1147               obj = ResidueProperties.nucleotideName.get(seq.getCharAt(res)
1148                       + "");
1149             }
1150             else
1151             {
1152               obj = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
1153                       + "");
1154             }
1155
1156             if (obj == null)
1157             {
1158               continue;
1159             }
1160
1161             String triplet = obj.toString();
1162             int alIndex = seq.findPosition(res);
1163             gSize = groups.length;
1164             for (g = 0; g < gSize; g++)
1165             {
1166               if (text.length() < 1)
1167               {
1168                 text.append("<area shape=\"rect\" coords=\""
1169                         + (idWidth + res * av.charWidth) + "," + sy + ","
1170                         + (idWidth + (res + 1) * av.charWidth) + ","
1171                         + (av.charHeight + sy) + "\""
1172                         + " onMouseOver=\"toolTip('" + alIndex + " "
1173                         + triplet);
1174               }
1175
1176               if (groups[g].getStartRes() < res
1177                       && groups[g].getEndRes() > res)
1178               {
1179                 text.append("<br><em>" + groups[g].getName() + "</em>");
1180               }
1181             }
1182
1183             if (features != null)
1184             {
1185               if (text.length() < 1)
1186               {
1187                 text.append("<area shape=\"rect\" coords=\""
1188                         + (idWidth + res * av.charWidth) + "," + sy + ","
1189                         + (idWidth + (res + 1) * av.charWidth) + ","
1190                         + (av.charHeight + sy) + "\""
1191                         + " onMouseOver=\"toolTip('" + alIndex + " "
1192                         + triplet);
1193               }
1194               fSize = features.length;
1195               for (f = 0; f < fSize; f++)
1196               {
1197
1198                 if ((features[f].getBegin() <= seq.findPosition(res))
1199                         && (features[f].getEnd() >= seq.findPosition(res)))
1200                 {
1201                   if (features[f].getType().equals("disulfide bond"))
1202                   {
1203                     if (features[f].getBegin() == seq.findPosition(res)
1204                             || features[f].getEnd() == seq
1205                                     .findPosition(res))
1206                     {
1207                       text.append("<br>disulfide bond "
1208                               + features[f].getBegin() + ":"
1209                               + features[f].getEnd());
1210                     }
1211                   }
1212                   else
1213                   {
1214                     text.append("<br>");
1215                     text.append(features[f].getType());
1216                     if (features[f].getDescription() != null
1217                             && !features[f].getType().equals(
1218                                     features[f].getDescription()))
1219                     {
1220                       text.append(" " + features[f].getDescription());
1221                     }
1222
1223                     if (features[f].getValue("status") != null)
1224                     {
1225                       text.append(" (" + features[f].getValue("status")
1226                               + ")");
1227                     }
1228                   }
1229                 }
1230
1231               }
1232             }
1233             if (text.length() > 1)
1234             {
1235               text.append("')\"; onMouseOut=\"toolTip()\";  href=\"#\">");
1236               out.println(text.toString());
1237             }
1238           }
1239         }
1240         out.println("</map></body></html>");
1241         out.close();
1242
1243       } catch (Exception ex)
1244       {
1245         ex.printStackTrace();
1246       }
1247     } // /////////END OF IMAGE MAP
1248
1249   }
1250
1251   int getWrappedHeight()
1252   {
1253     int seqPanelWidth = seqPanel.seqCanvas.getWidth();
1254
1255     if (System.getProperty("java.awt.headless") != null
1256             && System.getProperty("java.awt.headless").equals("true"))
1257     {
1258       seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1259               - vscroll.getPreferredSize().width
1260               - alignFrame.getInsets().left - alignFrame.getInsets().right;
1261     }
1262
1263     int chunkWidth = seqPanel.seqCanvas
1264             .getWrappedCanvasWidth(seqPanelWidth);
1265
1266     int hgap = av.charHeight;
1267     if (av.scaleAboveWrapped)
1268     {
1269       hgap += av.charHeight;
1270     }
1271
1272     int annotationHeight = 0;
1273     if (av.showAnnotation)
1274     {
1275       annotationHeight = annotationPanel.adjustPanelHeight();
1276     }
1277
1278     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
1279             + annotationHeight;
1280
1281     int maxwidth = av.alignment.getWidth();
1282     if (av.hasHiddenColumns)
1283     {
1284       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
1285     }
1286
1287     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1288
1289     return height;
1290   }
1291
1292   /**
1293    * close the panel - deregisters all listeners and nulls any references to
1294    * alignment data.
1295    */
1296   public void closePanel()
1297   {
1298     jalview.structure.StructureSelectionManager ssm = jalview.structure.StructureSelectionManager
1299             .getStructureSelectionManager();
1300     ssm.removeStructureViewerListener(seqPanel, null);
1301     ssm.removeSelectionListener(seqPanel);
1302     PaintRefresher.RemoveComponent(seqPanel.seqCanvas);
1303     PaintRefresher.RemoveComponent(idPanel.idCanvas);
1304     PaintRefresher.RemoveComponent(this);
1305     if (av != null)
1306     {
1307       av.alignment = null;
1308       av = null;
1309     }
1310     else
1311     {
1312       if (Cache.log.isDebugEnabled())
1313       {
1314         Cache.log.warn("Closing alignment panel which is already closed.");
1315       }
1316     }
1317   }
1318
1319   /**
1320    * hides or shows dynamic annotation rows based on groups and av state flags
1321    */
1322   public void updateAnnotation()
1323   {
1324     boolean updateCalcs = false;
1325     boolean conv = av.isShowGroupConservation();
1326     boolean cons = av.isShowGroupConsensus();
1327     boolean sortg = true;
1328
1329     // remove old automatic annotation
1330     // add any new annotation
1331
1332     Vector gr = av.alignment.getGroups(); // OrderedBy(av.alignment.getSequencesArray());
1333     // intersect alignment annotation with alignment groups
1334
1335     AlignmentAnnotation[] aan = av.alignment.getAlignmentAnnotation();
1336     for (int an = 0; an < aan.length; an++)
1337     {
1338       if (aan[an].autoCalculated && aan[an].groupRef != null)
1339       {
1340         av.alignment.deleteAnnotation(aan[an]);
1341         aan[an] = null;
1342       }
1343     }
1344     SequenceGroup sg;
1345     for (int g = 0; g < gr.size(); g++)
1346     {
1347       updateCalcs = false;
1348       sg = (SequenceGroup) gr.elementAt(g);
1349
1350       if (conv)
1351       {
1352         updateCalcs = true;
1353         av.alignment.addAnnotation(sg.getConservationRow(),0);
1354       }
1355       if (cons)
1356       {
1357         updateCalcs = true;
1358         av.alignment.addAnnotation(sg.getConsensus(),0);
1359       }
1360       // refresh the annotation rows
1361       if (updateCalcs)
1362       {
1363         sg.recalcConservation();
1364       }
1365     }
1366     adjustAnnotationHeight();
1367   }
1368 }