apply jalview code style
[jalview.git] / src / jalview / gui / AlignmentPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.beans.*;
21 import java.io.*;
22 import java.util.Hashtable;
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,
183             Math.min(afwidth - 200, (int) 2 * afwidth / 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       // System.err.println("Seq : "+seqIndex+" Scroll to "+start+","+end); //
287       // DEBUG
288       if (start < 0)
289       {
290         return false;
291       }
292       if (end == seq.getEnd())
293       {
294         return false;
295       }
296       if (!av.wrapAlignment)
297       {
298         if ((startv = av.getStartRes()) >= start)
299         {
300           setScrollValues(start - 1, seqIndex);
301         }
302         else if ((endv = av.getEndRes()) <= end)
303         {
304           setScrollValues(startv + 1 + end - endv, seqIndex);
305         }
306         else if ((starts = av.getStartSeq()) > seqIndex)
307         {
308           setScrollValues(av.getStartRes(), seqIndex);
309         }
310         else if ((ends = av.getEndSeq()) <= seqIndex)
311         {
312           setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
313         }
314       }
315       else
316       {
317         scrollToWrappedVisible(start);
318       }
319     }
320     if (!redrawOverview && overviewPanel != null)
321     {
322       overviewPanel.setBoxPosition();
323     }
324     paintAlignment(!redrawOverview);
325     return true;
326   }
327
328   void scrollToWrappedVisible(int res)
329   {
330     int cwidth = seqPanel.seqCanvas
331             .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth());
332     if (res < av.getStartRes() || res >= (av.getStartRes() + cwidth))
333     {
334       vscroll.setValue((res / cwidth));
335       av.startRes = vscroll.getValue() * cwidth;
336     }
337
338   }
339
340   /**
341    * DOCUMENT ME!
342    * 
343    * @return DOCUMENT ME!
344    */
345   public OverviewPanel getOverviewPanel()
346   {
347     return overviewPanel;
348   }
349
350   /**
351    * DOCUMENT ME!
352    * 
353    * @param op
354    *          DOCUMENT ME!
355    */
356   public void setOverviewPanel(OverviewPanel op)
357   {
358     overviewPanel = op;
359   }
360
361   /**
362    * DOCUMENT ME!
363    * 
364    * @param b
365    *          DOCUMENT ME!
366    */
367   public void setAnnotationVisible(boolean b)
368   {
369     if (!av.wrapAlignment)
370     {
371       annotationSpaceFillerHolder.setVisible(b);
372       annotationScroller.setVisible(b);
373     }
374     repaint();
375   }
376
377   public void adjustAnnotationHeight()
378   {
379     // TODO: display vertical annotation scrollbar if necessary
380     // this is called after loading new annotation onto alignment
381     if (alignFrame.getHeight() == 0)
382     {
383       System.out.println("NEEDS FIXING");
384     }
385
386     int height = annotationPanel.adjustPanelHeight();
387
388     if (hscroll.isVisible())
389     {
390       height += hscroll.getPreferredSize().height;
391     }
392     if (height > alignFrame.getHeight() / 2)
393     {
394       height = alignFrame.getHeight() / 2;
395     }
396
397     hscroll.addNotify();
398
399     annotationScroller.setPreferredSize(new Dimension(annotationScroller
400             .getWidth(), height));
401
402     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
403             annotationSpaceFillerHolder.getWidth(), height));
404     annotationScroller.validate();// repaint();
405     addNotify();
406     repaint();
407   }
408
409   /**
410    * DOCUMENT ME!
411    * 
412    * @param wrap
413    *          DOCUMENT ME!
414    */
415   public void setWrapAlignment(boolean wrap)
416   {
417     av.startSeq = 0;
418     scalePanelHolder.setVisible(!wrap);
419     hscroll.setVisible(!wrap);
420     idwidthAdjuster.setVisible(!wrap);
421
422     if (wrap)
423     {
424       annotationScroller.setVisible(false);
425       annotationSpaceFillerHolder.setVisible(false);
426     }
427     else if (av.showAnnotation)
428     {
429       annotationScroller.setVisible(true);
430       annotationSpaceFillerHolder.setVisible(true);
431     }
432
433     idSpaceFillerPanel1.setVisible(!wrap);
434
435     repaint();
436   }
437
438   // return value is true if the scroll is valid
439   public boolean scrollUp(boolean up)
440   {
441     if (up)
442     {
443       if (vscroll.getValue() < 1)
444       {
445         return false;
446       }
447
448       fastPaint = false;
449       vscroll.setValue(vscroll.getValue() - 1);
450     }
451     else
452     {
453       if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight())
454       {
455         return false;
456       }
457
458       fastPaint = false;
459       vscroll.setValue(vscroll.getValue() + 1);
460     }
461
462     fastPaint = true;
463
464     return true;
465   }
466
467   /**
468    * DOCUMENT ME!
469    * 
470    * @param right
471    *          DOCUMENT ME!
472    * 
473    * @return DOCUMENT ME!
474    */
475   public boolean scrollRight(boolean right)
476   {
477     if (!right)
478     {
479       if (hscroll.getValue() < 1)
480       {
481         return false;
482       }
483
484       fastPaint = false;
485       hscroll.setValue(hscroll.getValue() - 1);
486     }
487     else
488     {
489       if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth())
490       {
491         return false;
492       }
493
494       fastPaint = false;
495       hscroll.setValue(hscroll.getValue() + 1);
496     }
497
498     fastPaint = true;
499
500     return true;
501   }
502
503   /**
504    * DOCUMENT ME!
505    * 
506    * @param x
507    *          DOCUMENT ME!
508    * @param y
509    *          DOCUMENT ME!
510    */
511   public void setScrollValues(int x, int y)
512   {
513     // System.err.println("Scroll to "+x+","+y);
514     if (av == null || av.alignment == null)
515     {
516       return;
517     }
518     int width = av.alignment.getWidth();
519     int height = av.alignment.getHeight();
520
521     if (av.hasHiddenColumns)
522     {
523       width = av.getColumnSelection().findColumnPosition(width);
524     }
525
526     av.setEndRes((x + (seqPanel.seqCanvas.getWidth() / av.charWidth)) - 1);
527
528     hextent = seqPanel.seqCanvas.getWidth() / av.charWidth;
529     vextent = seqPanel.seqCanvas.getHeight() / av.charHeight;
530
531     if (hextent > width)
532     {
533       hextent = width;
534     }
535
536     if (vextent > height)
537     {
538       vextent = height;
539     }
540
541     if ((hextent + x) > width)
542     {
543       x = width - hextent;
544     }
545
546     if ((vextent + y) > height)
547     {
548       y = height - vextent;
549     }
550
551     if (y < 0)
552     {
553       y = 0;
554     }
555
556     if (x < 0)
557     {
558       x = 0;
559     }
560
561     hscroll.setValues(x, hextent, 0, width);
562     vscroll.setValues(y, vextent, 0, height);
563   }
564
565   /**
566    * DOCUMENT ME!
567    * 
568    * @param evt
569    *          DOCUMENT ME!
570    */
571   public void adjustmentValueChanged(AdjustmentEvent evt)
572   {
573
574     int oldX = av.getStartRes();
575     int oldY = av.getStartSeq();
576
577     if (evt.getSource() == hscroll)
578     {
579       int x = hscroll.getValue();
580       av.setStartRes(x);
581       av.setEndRes((x + (seqPanel.seqCanvas.getWidth() / av.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,
862               av.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(
876               seq.getDisplayId(av.getShowJVSuffix()),
877               xPos,
878               (((i - startSeq) * av.charHeight) + av.getCharHeight())
879                       - (av.getCharHeight() / 5));
880     }
881
882     pg.setFont(av.getFont());
883
884     // draw main sequence panel
885     pg.translate(idWidth, 0);
886     seqPanel.seqCanvas.drawPanel(pg, startRes, endRes, startSeq, endSeq, 0);
887
888     if (av.showAnnotation && (endSeq == av.alignment.getHeight()))
889     {
890       pg.translate(-idWidth - 3, (endSeq - startSeq) * av.charHeight + 3);
891       alabels.drawComponent((Graphics2D) pg, idWidth);
892       pg.translate(idWidth + 3, 0);
893       annotationPanel.drawComponent((Graphics2D) pg, startRes, endRes + 1);
894     }
895
896     return Printable.PAGE_EXISTS;
897   }
898
899   /**
900    * DOCUMENT ME!
901    * 
902    * @param pg
903    *          DOCUMENT ME!
904    * @param pwidth
905    *          DOCUMENT ME!
906    * @param pheight
907    *          DOCUMENT ME!
908    * @param pi
909    *          DOCUMENT ME!
910    * 
911    * @return DOCUMENT ME!
912    * 
913    * @throws PrinterException
914    *           DOCUMENT ME!
915    */
916   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
917           int pi) throws PrinterException
918   {
919
920     int annotationHeight = 0;
921     AnnotationLabels labels = null;
922     if (av.showAnnotation)
923     {
924       annotationHeight = annotationPanel.adjustPanelHeight();
925       labels = new AnnotationLabels(av);
926     }
927
928     int hgap = av.charHeight;
929     if (av.scaleAboveWrapped)
930     {
931       hgap += av.charHeight;
932     }
933
934     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
935             + annotationHeight;
936
937     int idWidth = getVisibleIdWidth();
938
939     int maxwidth = av.alignment.getWidth();
940     if (av.hasHiddenColumns)
941     {
942       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
943     }
944
945     int resWidth = seqPanel.seqCanvas.getWrappedCanvasWidth(pwidth
946             - idWidth);
947
948     int totalHeight = cHeight * (maxwidth / resWidth + 1);
949
950     pg.setColor(Color.white);
951     pg.fillRect(0, 0, pwidth, pheight);
952     pg.setFont(av.getFont());
953
954     // //////////////
955     // Draw the ids
956     pg.setColor(Color.black);
957
958     pg.translate(0, -pi * pheight);
959
960     pg.setClip(0, pi * pheight, pwidth, pheight);
961
962     int ypos = hgap;
963
964     do
965     {
966       for (int i = 0; i < av.alignment.getHeight(); i++)
967       {
968         pg.setFont(idPanel.idCanvas.idfont);
969         SequenceI s = av.alignment.getSequenceAt(i);
970         String string = s.getDisplayId(av.getShowJVSuffix());
971         int xPos = 0;
972         if (av.rightAlignIds)
973         {
974           FontMetrics fm = pg.getFontMetrics();
975           xPos = idWidth - fm.stringWidth(string) - 4;
976         }
977         pg.drawString(string, xPos,
978                 ((i * av.charHeight) + ypos + av.charHeight)
979                         - (av.charHeight / 5));
980       }
981       if (labels != null)
982       {
983         pg.translate(-3, ypos
984                 + (av.getAlignment().getHeight() * av.charHeight));
985
986         pg.setFont(av.getFont());
987         labels.drawComponent(pg, idWidth);
988         pg.translate(+3, -ypos
989                 - (av.getAlignment().getHeight() * av.charHeight));
990       }
991
992       ypos += cHeight;
993     } while (ypos < totalHeight);
994
995     pg.translate(idWidth, 0);
996
997     seqPanel.seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, totalHeight,
998             0);
999
1000     if ((pi * pheight) < totalHeight)
1001     {
1002       return Printable.PAGE_EXISTS;
1003
1004     }
1005     else
1006     {
1007       return Printable.NO_SUCH_PAGE;
1008     }
1009   }
1010
1011   int getVisibleIdWidth()
1012   {
1013     return idPanel.getWidth() > 0 ? idPanel.getWidth()
1014             : calculateIdWidth().width + 4;
1015   }
1016
1017   void makeAlignmentImage(int type, File file)
1018   {
1019     int maxwidth = av.alignment.getWidth();
1020     if (av.hasHiddenColumns)
1021     {
1022       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth);
1023     }
1024
1025     int height = ((av.alignment.getHeight() + 1) * av.charHeight)
1026             + scalePanel.getHeight();
1027     int width = getVisibleIdWidth() + (maxwidth * av.charWidth);
1028
1029     if (av.getWrapAlignment())
1030     {
1031       height = getWrappedHeight();
1032       if (System.getProperty("java.awt.headless") != null
1033               && System.getProperty("java.awt.headless").equals("true"))
1034       {
1035         width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1036                 - alignFrame.getInsets().left
1037                 - alignFrame.getInsets().right;
1038       }
1039       else
1040       {
1041         width = seqPanel.getWidth() + getVisibleIdWidth();
1042       }
1043
1044     }
1045     else if (av.getShowAnnotation())
1046     {
1047       height += annotationPanel.adjustPanelHeight() + 3;
1048     }
1049
1050     try
1051     {
1052
1053       jalview.util.ImageMaker im;
1054       if (type == jalview.util.ImageMaker.PNG)
1055       {
1056         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
1057                 "Create PNG image from alignment", width, height, file,
1058                 null);
1059       }
1060       else
1061       {
1062         im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
1063                 "Create EPS file from alignment", width, height, file,
1064                 alignFrame.getTitle());
1065       }
1066
1067       if (av.getWrapAlignment())
1068       {
1069         if (im.getGraphics() != null)
1070         {
1071           printWrappedAlignment(im.getGraphics(), width, height, 0);
1072           im.writeImage();
1073         }
1074       }
1075       else
1076       {
1077         if (im.getGraphics() != null)
1078         {
1079           printUnwrapped(im.getGraphics(), width, height, 0);
1080           im.writeImage();
1081         }
1082       }
1083     } catch (OutOfMemoryError err)
1084     {
1085       // Be noisy here.
1086       System.out.println("########################\n" + "OUT OF MEMORY "
1087               + file + "\n" + "########################");
1088       new OOMWarning("Creating Image for " + file, err);
1089       // System.out.println("Create IMAGE: " + err);
1090     } catch (Exception ex)
1091     {
1092       ex.printStackTrace();
1093     }
1094   }
1095
1096   /**
1097    * DOCUMENT ME!
1098    */
1099   public void makeEPS(File epsFile)
1100   {
1101     makeAlignmentImage(jalview.util.ImageMaker.EPS, epsFile);
1102   }
1103
1104   /**
1105    * DOCUMENT ME!
1106    */
1107   public void makePNG(File pngFile)
1108   {
1109     makeAlignmentImage(jalview.util.ImageMaker.PNG, pngFile);
1110   }
1111
1112   public void makePNGImageMap(File imgMapFile, String imageName)
1113   {
1114     // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
1115     // ////////////////////////////////////////////
1116     int idWidth = getVisibleIdWidth();
1117     FontMetrics fm = getFontMetrics(av.getFont());
1118     int scaleHeight = av.charHeight + fm.getDescent();
1119
1120     // Gen image map
1121     // ////////////////////////////////
1122     if (imgMapFile != null)
1123     {
1124       try
1125       {
1126         int s, sSize = av.alignment.getHeight(), res, alwidth = av.alignment
1127                 .getWidth(), g, gSize, f, fSize, sy;
1128         StringBuffer text = new StringBuffer();
1129         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1130         out.println(jalview.io.HTMLOutput.getImageMapHTML());
1131         out.println("<img src=\"" + imageName
1132                 + "\" border=\"0\" usemap=\"#Map\" >"
1133                 + "<map name=\"Map\">");
1134
1135         for (s = 0; s < sSize; s++)
1136         {
1137           sy = s * av.charHeight + scaleHeight;
1138
1139           SequenceI seq = av.alignment.getSequenceAt(s);
1140           SequenceFeature[] features = seq.getDatasetSequence()
1141                   .getSequenceFeatures();
1142           SequenceGroup[] groups = av.alignment.findAllGroups(seq);
1143           for (res = 0; res < alwidth; res++)
1144           {
1145             text = new StringBuffer();
1146             Object obj = null;
1147             if (av.alignment.isNucleotide())
1148             {
1149               obj = ResidueProperties.nucleotideName.get(seq.getCharAt(res)
1150                       + "");
1151             }
1152             else
1153             {
1154               obj = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
1155                       + "");
1156             }
1157
1158             if (obj == null)
1159             {
1160               continue;
1161             }
1162
1163             String triplet = obj.toString();
1164             int alIndex = seq.findPosition(res);
1165             gSize = groups.length;
1166             for (g = 0; g < gSize; g++)
1167             {
1168               if (text.length() < 1)
1169               {
1170                 text.append("<area shape=\"rect\" coords=\""
1171                         + (idWidth + res * av.charWidth) + "," + sy + ","
1172                         + (idWidth + (res + 1) * av.charWidth) + ","
1173                         + (av.charHeight + sy) + "\""
1174                         + " onMouseOver=\"toolTip('" + alIndex + " "
1175                         + triplet);
1176               }
1177
1178               if (groups[g].getStartRes() < res
1179                       && groups[g].getEndRes() > res)
1180               {
1181                 text.append("<br><em>" + groups[g].getName() + "</em>");
1182               }
1183             }
1184
1185             if (features != null)
1186             {
1187               if (text.length() < 1)
1188               {
1189                 text.append("<area shape=\"rect\" coords=\""
1190                         + (idWidth + res * av.charWidth) + "," + sy + ","
1191                         + (idWidth + (res + 1) * av.charWidth) + ","
1192                         + (av.charHeight + sy) + "\""
1193                         + " onMouseOver=\"toolTip('" + alIndex + " "
1194                         + triplet);
1195               }
1196               fSize = features.length;
1197               for (f = 0; f < fSize; f++)
1198               {
1199
1200                 if ((features[f].getBegin() <= seq.findPosition(res))
1201                         && (features[f].getEnd() >= seq.findPosition(res)))
1202                 {
1203                   if (features[f].getType().equals("disulfide bond"))
1204                   {
1205                     if (features[f].getBegin() == seq.findPosition(res)
1206                             || features[f].getEnd() == seq
1207                                     .findPosition(res))
1208                     {
1209                       text.append("<br>disulfide bond "
1210                               + features[f].getBegin() + ":"
1211                               + features[f].getEnd());
1212                     }
1213                   }
1214                   else
1215                   {
1216                     text.append("<br>");
1217                     text.append(features[f].getType());
1218                     if (features[f].getDescription() != null
1219                             && !features[f].getType().equals(
1220                                     features[f].getDescription()))
1221                     {
1222                       text.append(" " + features[f].getDescription());
1223                     }
1224
1225                     if (features[f].getValue("status") != null)
1226                     {
1227                       text.append(" (" + features[f].getValue("status")
1228                               + ")");
1229                     }
1230                   }
1231                 }
1232
1233               }
1234             }
1235             if (text.length() > 1)
1236             {
1237               text.append("')\"; onMouseOut=\"toolTip()\";  href=\"#\">");
1238               out.println(text.toString());
1239             }
1240           }
1241         }
1242         out.println("</map></body></html>");
1243         out.close();
1244
1245       } catch (Exception ex)
1246       {
1247         ex.printStackTrace();
1248       }
1249     } // /////////END OF IMAGE MAP
1250
1251   }
1252
1253   int getWrappedHeight()
1254   {
1255     int seqPanelWidth = seqPanel.seqCanvas.getWidth();
1256
1257     if (System.getProperty("java.awt.headless") != null
1258             && System.getProperty("java.awt.headless").equals("true"))
1259     {
1260       seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1261               - vscroll.getPreferredSize().width
1262               - alignFrame.getInsets().left - alignFrame.getInsets().right;
1263     }
1264
1265     int chunkWidth = seqPanel.seqCanvas
1266             .getWrappedCanvasWidth(seqPanelWidth);
1267
1268     int hgap = av.charHeight;
1269     if (av.scaleAboveWrapped)
1270     {
1271       hgap += av.charHeight;
1272     }
1273
1274     int annotationHeight = 0;
1275     if (av.showAnnotation)
1276     {
1277       annotationHeight = annotationPanel.adjustPanelHeight();
1278     }
1279
1280     int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
1281             + annotationHeight;
1282
1283     int maxwidth = av.alignment.getWidth();
1284     if (av.hasHiddenColumns)
1285     {
1286       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
1287     }
1288
1289     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1290
1291     return height;
1292   }
1293
1294   /**
1295    * close the panel - deregisters all listeners and nulls any references to
1296    * alignment data.
1297    */
1298   public void closePanel()
1299   {
1300     jalview.structure.StructureSelectionManager ssm = jalview.structure.StructureSelectionManager
1301             .getStructureSelectionManager();
1302     ssm.removeStructureViewerListener(seqPanel, null);
1303     ssm.removeSelectionListener(seqPanel);
1304     PaintRefresher.RemoveComponent(seqPanel.seqCanvas);
1305     PaintRefresher.RemoveComponent(idPanel.idCanvas);
1306     PaintRefresher.RemoveComponent(this);
1307     if (av != null)
1308     {
1309       av.alignment = null;
1310       av = null;
1311     }
1312     else
1313     {
1314       if (Cache.log.isDebugEnabled())
1315       {
1316         Cache.log.warn("Closing alignment panel which is already closed.");
1317       }
1318     }
1319   }
1320
1321   /**
1322    * hides or shows dynamic annotation rows based on groups and av state flags
1323    */
1324   public void updateAnnotation()
1325   {
1326     updateAnnotation(false);
1327   }
1328
1329   public void updateAnnotation(boolean applyGlobalSettings)
1330   {
1331     // TODO: this should be merged with other annotation update stuff - that
1332     // sits on AlignViewport
1333     boolean updateCalcs = false;
1334     boolean conv = av.isShowGroupConservation();
1335     boolean cons = av.isShowGroupConsensus();
1336     boolean showprf = av.isShowSequenceLogo();
1337     boolean showConsHist = av.isShowConsensusHistogram();
1338
1339     boolean sortg = true;
1340
1341     // remove old automatic annotation
1342     // add any new annotation
1343
1344     Vector gr = av.alignment.getGroups(); // OrderedBy(av.alignment.getSequencesArray());
1345     // intersect alignment annotation with alignment groups
1346
1347     AlignmentAnnotation[] aan = av.alignment.getAlignmentAnnotation();
1348     Hashtable oldrfs = new Hashtable();
1349     if (aan != null)
1350     {
1351       for (int an = 0; an < aan.length; an++)
1352       {
1353         if (aan[an].autoCalculated && aan[an].groupRef != null)
1354         {
1355           oldrfs.put(aan[an].groupRef, aan[an].groupRef);
1356           av.alignment.deleteAnnotation(aan[an]);
1357           aan[an] = null;
1358         }
1359       }
1360     }
1361     SequenceGroup sg;
1362     if (gr != null)
1363     {
1364       for (int g = 0; g < gr.size(); g++)
1365       {
1366         updateCalcs = false;
1367         sg = (SequenceGroup) gr.elementAt(g);
1368         if (applyGlobalSettings || !oldrfs.containsKey(sg))
1369         {
1370           // set defaults for this group's conservation/consensus
1371           sg.setshowSequenceLogo(showprf);
1372           sg.setShowConsensusHistogram(showConsHist);
1373         }
1374         if (conv)
1375         {
1376           updateCalcs = true;
1377           av.alignment.addAnnotation(sg.getConservationRow(), 0);
1378         }
1379         if (cons)
1380         {
1381           updateCalcs = true;
1382           av.alignment.addAnnotation(sg.getConsensus(), 0);
1383         }
1384         // refresh the annotation rows
1385         if (updateCalcs)
1386         {
1387           sg.recalcConservation();
1388         }
1389       }
1390     }
1391     oldrfs.clear();
1392     adjustAnnotationHeight();
1393   }
1394 }