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