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