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