Merge develop to Release_2_8_3_Branch
[jalview.git] / src / jalview / gui / AlignmentPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.analysis.AnnotationSorter;
24 import jalview.api.AlignViewportI;
25 import jalview.api.AlignmentViewPanel;
26 import jalview.bin.Cache;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.SearchResults;
29 import jalview.datamodel.SequenceFeature;
30 import jalview.datamodel.SequenceGroup;
31 import jalview.datamodel.SequenceI;
32 import jalview.jbgui.GAlignmentPanel;
33 import jalview.math.AlignmentDimension;
34 import jalview.schemes.ResidueProperties;
35 import jalview.structure.StructureSelectionManager;
36 import jalview.util.MessageManager;
37
38 import java.awt.BorderLayout;
39 import java.awt.Color;
40 import java.awt.Container;
41 import java.awt.Dimension;
42 import java.awt.Font;
43 import java.awt.FontMetrics;
44 import java.awt.Graphics;
45 import java.awt.event.AdjustmentEvent;
46 import java.awt.event.AdjustmentListener;
47 import java.awt.print.PageFormat;
48 import java.awt.print.Printable;
49 import java.awt.print.PrinterException;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
52 import java.io.File;
53 import java.io.FileWriter;
54 import java.io.PrintWriter;
55
56 import javax.swing.SwingUtilities;
57
58 /**
59  * DOCUMENT ME!
60  * 
61  * @author $author$
62  * @version $Revision: 1.161 $
63  */
64 public class AlignmentPanel extends GAlignmentPanel implements
65         AdjustmentListener, Printable, AlignmentViewPanel
66 {
67   public AlignViewport av;
68
69   OverviewPanel overviewPanel;
70
71   private SeqPanel seqPanel;
72
73   private IdPanel idPanel;
74
75   private boolean headless;
76   IdwidthAdjuster idwidthAdjuster;
77
78   /** DOCUMENT ME!! */
79   public AlignFrame alignFrame;
80
81   private ScalePanel scalePanel;
82
83   private AnnotationPanel annotationPanel;
84
85   private AnnotationLabels alabels;
86
87   // this value is set false when selection area being dragged
88   boolean fastPaint = true;
89
90   int hextent = 0;
91
92   int vextent = 0;
93
94   /**
95    * Creates a new AlignmentPanel object.
96    * 
97    * @param af
98    * @param av
99    */
100   public AlignmentPanel(AlignFrame af, final AlignViewport av)
101   {
102     alignFrame = af;
103     this.av = av;
104     setSeqPanel(new SeqPanel(av, this));
105     setIdPanel(new IdPanel(av, this));
106
107     setScalePanel(new ScalePanel(av, this));
108
109     idPanelHolder.add(getIdPanel(), BorderLayout.CENTER);
110     idwidthAdjuster = new IdwidthAdjuster(this);
111     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
112
113     setAnnotationPanel(new AnnotationPanel(this));
114     setAlabels(new AnnotationLabels(this));
115
116     annotationScroller.setViewportView(getAnnotationPanel());
117     annotationSpaceFillerHolder.add(getAlabels(), BorderLayout.CENTER);
118
119     scalePanelHolder.add(getScalePanel(), BorderLayout.CENTER);
120     seqPanelHolder.add(getSeqPanel(), BorderLayout.CENTER);
121
122     setScrollValues(0, 0);
123
124     hscroll.addAdjustmentListener(this);
125     vscroll.addAdjustmentListener(this);
126
127     final AlignmentPanel ap = this;
128     av.addPropertyChangeListener(new PropertyChangeListener()
129     {
130       public void propertyChange(PropertyChangeEvent evt)
131       {
132         if (evt.getPropertyName().equals("alignment"))
133         {
134           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
135           alignmentChanged();
136         }
137       }
138     });
139     fontChanged();
140     adjustAnnotationHeight();
141     updateLayout();
142   }
143
144   @Override
145   public AlignViewportI getAlignViewport()
146   {
147     return av;
148   }
149   public void alignmentChanged()
150   {
151     av.alignmentChanged(this);
152
153     alignFrame.updateEditMenuBar();
154
155     paintAlignment(true);
156
157   }
158
159   /**
160    * DOCUMENT ME!
161    */
162   public void fontChanged()
163   {
164     // set idCanvas bufferedImage to null
165     // to prevent drawing old image
166     FontMetrics fm = getFontMetrics(av.getFont());
167
168     scalePanelHolder.setPreferredSize(new Dimension(10, av.getCharHeight()
169             + fm.getDescent()));
170     idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av
171             .getCharHeight()
172             + fm.getDescent()));
173
174     getIdPanel().getIdCanvas().gg = null;
175     getSeqPanel().seqCanvas.img = null;
176     getAnnotationPanel().adjustPanelHeight();
177
178     Dimension d = calculateIdWidth();
179
180     d.setSize(d.width + 4, d.height);
181     getIdPanel().getIdCanvas().setPreferredSize(d);
182     hscrollFillerPanel.setPreferredSize(d);
183
184     if (overviewPanel != null)
185     {
186       overviewPanel.setBoxPosition();
187     }
188     if (this.alignFrame.getSplitViewContainer() != null)
189     {
190       ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout();
191     }
192
193     repaint();
194   }
195
196   /**
197    * Calculate the width of the alignment labels based on the displayed names
198    * and any bounds on label width set in preferences.
199    * 
200    * @return Dimension giving the maximum width of the alignment label panel
201    *         that should be used.
202    */
203   public Dimension calculateIdWidth()
204   {
205     // calculate sensible default width when no preference is available
206     Dimension r = null;
207     if (av.getIdWidth() < 0)
208     {
209       int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
210       int maxwidth = Math.max(20, Math.min(afwidth - 200, 2 * afwidth / 3));
211       r = calculateIdWidth(maxwidth);
212       av.setIdWidth(r.width);
213     }
214     else
215     {
216       r = new Dimension();
217       r.width = av.getIdWidth();
218       r.height = 0;
219     }
220     return r;
221   }
222
223   /**
224    * Calculate the width of the alignment labels based on the displayed names
225    * and any bounds on label width set in preferences.
226    * 
227    * @param maxwidth
228    *          -1 or maximum width allowed for IdWidth
229    * @return Dimension giving the maximum width of the alignment label panel
230    *         that should be used.
231    */
232   public Dimension calculateIdWidth(int maxwidth)
233   {
234     Container c = new Container();
235
236     FontMetrics fm = c.getFontMetrics(new Font(av.font.getName(),
237             Font.ITALIC, av.font.getSize()));
238
239     AlignmentI al = av.getAlignment();
240     int i = 0;
241     int idWidth = 0;
242     String id;
243
244     while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
245     {
246       SequenceI s = al.getSequenceAt(i);
247
248       id = s.getDisplayId(av.getShowJVSuffix());
249
250       if (fm.stringWidth(id) > idWidth)
251       {
252         idWidth = fm.stringWidth(id);
253       }
254
255       i++;
256     }
257
258     // Also check annotation label widths
259     i = 0;
260
261     if (al.getAlignmentAnnotation() != null)
262     {
263       fm = c.getFontMetrics(getAlabels().getFont());
264
265       while (i < al.getAlignmentAnnotation().length)
266       {
267         String label = al.getAlignmentAnnotation()[i].label;
268
269         if (fm.stringWidth(label) > idWidth)
270         {
271           idWidth = fm.stringWidth(label);
272         }
273
274         i++;
275       }
276     }
277
278     return new Dimension(maxwidth < 0 ? idWidth : Math.min(maxwidth,
279             idWidth), 12);
280   }
281
282   /**
283    * Highlight the given results on the alignment.
284    * 
285    */
286   public void highlightSearchResults(SearchResults results)
287   {
288     scrollToPosition(results);
289     getSeqPanel().seqCanvas.highlightSearchResults(results);
290   }
291
292   /**
293    * scroll the view to show the position of the highlighted region in results
294    * (if any) and redraw the overview
295    * 
296    * @param results
297    */
298   public boolean scrollToPosition(SearchResults results)
299   {
300     return scrollToPosition(results, true);
301   }
302
303   /**
304    * scroll the view to show the position of the highlighted region in results
305    * (if any)
306    * 
307    * @param results
308    * @param redrawOverview
309    *          - when set, the overview will be recalculated (takes longer)
310    * @return false if results were not found
311    */
312   public boolean scrollToPosition(SearchResults results,
313           boolean redrawOverview)
314   {
315     int startv, endv, starts, ends, width;
316     // TODO: properly locate search results in view when large numbers of hidden
317     // columns exist before highlighted region
318     // do we need to scroll the panel?
319     // TODO: tons of nullpointereexceptions raised here.
320     if (results != null && results.getSize() > 0 && av != null
321             && av.getAlignment() != null)
322     {
323       int seqIndex = av.getAlignment().findIndex(results);
324       if (seqIndex == -1)
325       {
326         return false;
327       }
328       SequenceI seq = av.getAlignment().getSequenceAt(seqIndex);
329
330       int[] r = results.getResults(seq, 0, av.getAlignment().getWidth());
331       if (r == null)
332       {
333         return false;
334       }
335       int start = r[0];
336       int end = r[1];
337       // System.err.println("Seq : "+seqIndex+" Scroll to "+start+","+end); //
338       // DEBUG
339       if (start < 0)
340       {
341         return false;
342       }
343       if (end == seq.getEnd())
344       {
345         return false;
346       }
347       if (av.hasHiddenColumns())
348       {
349         start = av.getColumnSelection().findColumnPosition(start);
350         end = av.getColumnSelection().findColumnPosition(end);
351         if (start == end)
352         {
353           if (!av.getColumnSelection().isVisible(r[0]))
354           {
355             // don't scroll - position isn't visible
356             return false;
357           }
358         }
359       }
360       if (!av.getWrapAlignment())
361       {
362         if ((startv = av.getStartRes()) >= start)
363         {
364           setScrollValues(start - 1, seqIndex);
365         }
366         else if ((endv = av.getEndRes()) <= end)
367         {
368           setScrollValues(startv + 1 + end - endv, seqIndex);
369         }
370         else if ((starts = av.getStartSeq()) > seqIndex)
371         {
372           setScrollValues(av.getStartRes(), seqIndex);
373         }
374         else if ((ends = av.getEndSeq()) <= seqIndex)
375         {
376           setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);
377         }
378       }
379       else
380       {
381         scrollToWrappedVisible(start);
382       }
383     }
384     if (redrawOverview && overviewPanel != null)
385     {
386       overviewPanel.setBoxPosition();
387     }
388     paintAlignment(redrawOverview);
389     return true;
390   }
391
392   void scrollToWrappedVisible(int res)
393   {
394     int cwidth = getSeqPanel().seqCanvas
395             .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
396     if (res < av.getStartRes() || res >= (av.getStartRes() + cwidth))
397     {
398       vscroll.setValue((res / cwidth));
399       av.startRes = vscroll.getValue() * cwidth;
400     }
401
402   }
403
404   /**
405    * DOCUMENT ME!
406    * 
407    * @return DOCUMENT ME!
408    */
409   public OverviewPanel getOverviewPanel()
410   {
411     return overviewPanel;
412   }
413
414   /**
415    * DOCUMENT ME!
416    * 
417    * @param op
418    *          DOCUMENT ME!
419    */
420   public void setOverviewPanel(OverviewPanel op)
421   {
422     overviewPanel = op;
423   }
424
425   /**
426    * 
427    * @param b
428    *          Hide or show annotation panel
429    * 
430    */
431   public void setAnnotationVisible(boolean b)
432   {
433     if (!av.getWrapAlignment())
434     {
435       annotationSpaceFillerHolder.setVisible(b);
436       annotationScroller.setVisible(b);
437     }
438     repaint();
439   }
440
441   /**
442    * automatically adjust annotation panel height for new annotation whilst
443    * ensuring the alignment is still visible.
444    */
445   public void adjustAnnotationHeight()
446   {
447     // TODO: display vertical annotation scrollbar if necessary
448     // this is called after loading new annotation onto alignment
449     if (alignFrame.getHeight() == 0)
450     {
451       System.out.println("NEEDS FIXING");
452     }
453     validateAnnotationDimensions(true);
454     addNotify();
455     paintAlignment(true);
456   }
457
458   /**
459    * calculate the annotation dimensions and refresh slider values accordingly.
460    * need to do repaints/notifys afterwards.
461    */
462   protected void validateAnnotationDimensions(boolean adjustPanelHeight)
463   {
464     int height = getAnnotationPanel().adjustPanelHeight();
465
466     int theight = av.getCharHeight()
467             * (av.getAlignment().getHeight() + (!av.hasHiddenRows() ? 0
468                     : av.getAlignment().getHiddenSequences().getSize()));
469     float sscaling = (float) (theight / (1.0 * theight + height));
470     float ascaling = (float) (height * 1.0 / alignFrame.getHeight());
471     int rheight = alignFrame.getHeight() - height - av.getCharHeight();
472     if (adjustPanelHeight)
473     {
474       // NOTE: this logic is different in the applet. Need a better algorithm to
475       // define behaviour
476       // try and set height according to alignment
477       if (ascaling > 0 && sscaling < 0.5)
478       {
479         // if the alignment is too big then
480         // default is 0.5 split
481         height = alignFrame.getHeight() / 2;
482       }
483       else
484       {
485         // if space for more than one sequence row left when annotation is fully
486         // displayed then set height to annotation height
487         // otherwise, leave at least two lines of sequence shown.
488         height = (rheight > av.getCharHeight()) ? height
489                 : (-av.getCharHeight() * 3 + (int) (alignFrame.getHeight() * (1 - sscaling)));
490       }
491     }
492     else
493     {
494       // maintain same window layout whilst updating sliders
495       height = annotationScroller.getSize().height;
496     }
497     hscroll.addNotify();
498
499     annotationScroller.setPreferredSize(new Dimension(annotationScroller
500             .getWidth(), height));
501
502     annotationSpaceFillerHolder.setPreferredSize(new Dimension(
503             annotationSpaceFillerHolder.getWidth(), height));
504     annotationScroller.validate();// repaint();
505     annotationScroller.addNotify();
506   }
507
508   /**
509    * update alignment layout for viewport settings
510    * 
511    * @param wrap
512    *          DOCUMENT ME!
513    */
514   public void updateLayout()
515   {
516     fontChanged();
517     setAnnotationVisible(av.isShowAnnotation());
518     boolean wrap = av.getWrapAlignment();
519     av.startSeq = 0;
520     scalePanelHolder.setVisible(!wrap);
521     hscroll.setVisible(!wrap);
522     idwidthAdjuster.setVisible(!wrap);
523
524     if (wrap)
525     {
526       annotationScroller.setVisible(false);
527       annotationSpaceFillerHolder.setVisible(false);
528     }
529     else if (av.isShowAnnotation())
530     {
531       annotationScroller.setVisible(true);
532       annotationSpaceFillerHolder.setVisible(true);
533     }
534
535     idSpaceFillerPanel1.setVisible(!wrap);
536
537     repaint();
538   }
539
540   // return value is true if the scroll is valid
541   public boolean scrollUp(boolean up)
542   {
543     if (up)
544     {
545       if (vscroll.getValue() < 1)
546       {
547         return false;
548       }
549
550       fastPaint = false;
551       vscroll.setValue(vscroll.getValue() - 1);
552     }
553     else
554     {
555       if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight())
556       {
557         return false;
558       }
559
560       fastPaint = false;
561       vscroll.setValue(vscroll.getValue() + 1);
562     }
563
564     fastPaint = true;
565
566     return true;
567   }
568
569   /**
570    * DOCUMENT ME!
571    * 
572    * @param right
573    *          DOCUMENT ME!
574    * 
575    * @return DOCUMENT ME!
576    */
577   public boolean scrollRight(boolean right)
578   {
579     if (!right)
580     {
581       if (hscroll.getValue() < 1)
582       {
583         return false;
584       }
585
586       fastPaint = false;
587       hscroll.setValue(hscroll.getValue() - 1);
588     }
589     else
590     {
591       if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth())
592       {
593         return false;
594       }
595
596       fastPaint = false;
597       hscroll.setValue(hscroll.getValue() + 1);
598     }
599
600     fastPaint = true;
601
602     return true;
603   }
604
605   /**
606    * Adjust row/column scrollers to show a visible position in the alignment.
607    * 
608    * @param x
609    *          visible column to scroll to DOCUMENT ME!
610    * @param y
611    *          visible row to scroll to
612    * 
613    */
614   public void setScrollValues(int x, int y)
615   {
616     // System.err.println("Scroll to "+x+","+y);
617     if (av == null || av.getAlignment() == null)
618     {
619       return;
620     }
621     int width = av.getAlignment().getWidth();
622     int height = av.getAlignment().getHeight();
623
624     if (av.hasHiddenColumns())
625     {
626       width = av.getColumnSelection().findColumnPosition(width);
627     }
628
629     av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av
630             .getCharWidth())) - 1);
631
632     hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
633     vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
634
635     if (hextent > width)
636     {
637       hextent = width;
638     }
639
640     if (vextent > height)
641     {
642       vextent = height;
643     }
644
645     if ((hextent + x) > width)
646     {
647       x = width - hextent;
648     }
649
650     if ((vextent + y) > height)
651     {
652       y = height - vextent;
653     }
654
655     if (y < 0)
656     {
657       y = 0;
658     }
659
660     if (x < 0)
661     {
662       x = 0;
663     }
664
665     hscroll.setValues(x, hextent, 0, width);
666     vscroll.setValues(y, vextent, 0, height);
667   }
668
669   /**
670    * DOCUMENT ME!
671    * 
672    * @param evt
673    *          DOCUMENT ME!
674    */
675   public void adjustmentValueChanged(AdjustmentEvent evt)
676   {
677     int oldX = av.getStartRes();
678     int oldY = av.getStartSeq();
679
680     if (evt.getSource() == hscroll)
681     {
682       int x = hscroll.getValue();
683       av.setStartRes(x);
684       av.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av.getCharWidth())) - 1);
685     }
686
687     if (evt.getSource() == vscroll)
688     {
689       int offy = vscroll.getValue();
690
691       if (av.getWrapAlignment())
692       {
693         if (offy > -1)
694         {
695           int rowSize = getSeqPanel().seqCanvas
696                   .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
697           av.setStartRes(offy * rowSize);
698           av.setEndRes((offy + 1) * rowSize);
699         }
700         else
701         {
702           // This is only called if file loaded is a jar file that
703           // was wrapped when saved and user has wrap alignment true
704           // as preference setting
705           SwingUtilities.invokeLater(new Runnable()
706           {
707             public void run()
708             {
709               setScrollValues(av.getStartRes(), av.getStartSeq());
710             }
711           });
712         }
713       }
714       else
715       {
716         av.setStartSeq(offy);
717         av.setEndSeq(offy
718                 + (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight()));
719       }
720     }
721
722     if (overviewPanel != null)
723     {
724       overviewPanel.setBoxPosition();
725     }
726
727     int scrollX = av.startRes - oldX;
728     int scrollY = av.startSeq - oldY;
729
730     if (av.getWrapAlignment() || !fastPaint)
731     {
732       repaint();
733     }
734     else
735     {
736       // Make sure we're not trying to draw a panel
737       // larger than the visible window
738       if (scrollX > av.endRes - av.startRes)
739       {
740         scrollX = av.endRes - av.startRes;
741       }
742       else if (scrollX < av.startRes - av.endRes)
743       {
744         scrollX = av.startRes - av.endRes;
745       }
746
747       if (scrollX != 0 || scrollY != 0)
748       {
749         getIdPanel().getIdCanvas().fastPaint(scrollY);
750         getSeqPanel().seqCanvas.fastPaint(scrollX, scrollY);
751         getScalePanel().repaint();
752
753         if (av.isShowAnnotation() && scrollX != 0)
754         {
755           getAnnotationPanel().fastPaint(scrollX);
756         }
757       }
758     }
759   }
760
761   /**
762    * Repaint the alignment including the annotations and overview panels (if
763    * shown).
764    */
765   public void paintAlignment(boolean updateOverview)
766   {
767     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
768             av.isShowAutocalculatedAbove());
769     sorter.sort(getAlignment()
770             .getAlignmentAnnotation(),
771             av.getSortAnnotationsBy());
772     repaint();
773
774     if (updateOverview)
775     {
776       av.getStructureSelectionManager().sequenceColoursChanged(this);
777
778       if (overviewPanel != null)
779       {
780         overviewPanel.updateOverviewImage();
781       }
782     }
783   }
784
785   /**
786    * DOCUMENT ME!
787    * 
788    * @param g
789    *          DOCUMENT ME!
790    */
791   public void paintComponent(Graphics g)
792   {
793     invalidate();
794
795     Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
796     idPanelHolder.setPreferredSize(d);
797     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
798     validate();
799
800     if (av.getWrapAlignment())
801     {
802       int maxwidth = av.getAlignment().getWidth();
803
804       if (av.hasHiddenColumns())
805       {
806         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
807       }
808
809       int canvasWidth = getSeqPanel().seqCanvas
810               .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth());
811       if (canvasWidth > 0)
812       {
813         int max = maxwidth
814                 / getSeqPanel().seqCanvas
815                         .getWrappedCanvasWidth(getSeqPanel().seqCanvas
816                                 .getWidth()) + 1;
817         vscroll.setMaximum(max);
818         vscroll.setUnitIncrement(1);
819         vscroll.setVisibleAmount(1);
820       }
821     }
822     else
823     {
824       setScrollValues(av.getStartRes(), av.getStartSeq());
825     }
826   }
827
828   /**
829    * DOCUMENT ME!
830    * 
831    * @param pg
832    *          DOCUMENT ME!
833    * @param pf
834    *          DOCUMENT ME!
835    * @param pi
836    *          DOCUMENT ME!
837    * 
838    * @return DOCUMENT ME!
839    * 
840    * @throws PrinterException
841    *           DOCUMENT ME!
842    */
843   public int print(Graphics pg, PageFormat pf, int pi)
844           throws PrinterException
845   {
846     pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
847
848     int pwidth = (int) pf.getImageableWidth();
849     int pheight = (int) pf.getImageableHeight();
850
851     if (av.getWrapAlignment())
852     {
853       return printWrappedAlignment(pg, pwidth, pheight, pi);
854     }
855     else
856     {
857       return printUnwrapped(pg, pwidth, pheight, pi);
858     }
859   }
860
861   /**
862    * DOCUMENT ME!
863    * 
864    * @param pg
865    *          DOCUMENT ME!
866    * @param pwidth
867    *          DOCUMENT ME!
868    * @param pheight
869    *          DOCUMENT ME!
870    * @param pi
871    *          DOCUMENT ME!
872    * 
873    * @return DOCUMENT ME!
874    * 
875    * @throws PrinterException
876    *           DOCUMENT ME!
877    */
878   public int printUnwrapped(Graphics pg, int pwidth, int pheight, int pi)
879           throws PrinterException
880   {
881     int idWidth = getVisibleIdWidth(false);
882     FontMetrics fm = getFontMetrics(av.getFont());
883     int scaleHeight = av.getCharHeight() + fm.getDescent();
884
885     pg.setColor(Color.white);
886     pg.fillRect(0, 0, pwidth, pheight);
887     pg.setFont(av.getFont());
888
889     // //////////////////////////////////
890     // / How many sequences and residues can we fit on a printable page?
891     int totalRes = (pwidth - idWidth) / av.getCharWidth();
892
893     int totalSeq = (pheight - scaleHeight) / av.getCharHeight() - 1;
894
895     int pagesWide = (av.getAlignment().getWidth() / totalRes) + 1;
896
897     // ///////////////////////////
898     // / Only print these sequences and residues on this page
899     int startRes;
900
901     // ///////////////////////////
902     // / Only print these sequences and residues on this page
903     int endRes;
904
905     // ///////////////////////////
906     // / Only print these sequences and residues on this page
907     int startSeq;
908
909     // ///////////////////////////
910     // / Only print these sequences and residues on this page
911     int endSeq;
912     startRes = (pi % pagesWide) * totalRes;
913     endRes = (startRes + totalRes) - 1;
914
915     if (endRes > (av.getAlignment().getWidth() - 1))
916     {
917       endRes = av.getAlignment().getWidth() - 1;
918     }
919
920     startSeq = (pi / pagesWide) * totalSeq;
921     endSeq = startSeq + totalSeq;
922
923     if (endSeq > av.getAlignment().getHeight())
924     {
925       endSeq = av.getAlignment().getHeight();
926     }
927
928     int pagesHigh = ((av.getAlignment().getHeight() / totalSeq) + 1)
929             * pheight;
930
931     if (av.isShowAnnotation())
932     {
933       pagesHigh += getAnnotationPanel().adjustPanelHeight() + 3;
934     }
935
936     pagesHigh /= pheight;
937
938     if (pi >= (pagesWide * pagesHigh))
939     {
940       return Printable.NO_SUCH_PAGE;
941     }
942
943     // draw Scale
944     pg.translate(idWidth, 0);
945     getScalePanel().drawScale(pg, startRes, endRes, pwidth - idWidth,
946             scaleHeight);
947     pg.translate(-idWidth, scaleHeight);
948
949     // //////////////
950     // Draw the ids
951     Color currentColor = null;
952     Color currentTextColor = null;
953
954     pg.setFont(getIdPanel().getIdCanvas().getIdfont());
955
956     SequenceI seq;
957     for (int i = startSeq; i < endSeq; i++)
958     {
959       seq = av.getAlignment().getSequenceAt(i);
960       if ((av.getSelectionGroup() != null)
961               && av.getSelectionGroup().getSequences(null).contains(seq))
962       {
963         currentColor = Color.gray;
964         currentTextColor = Color.black;
965       }
966       else
967       {
968         currentColor = av.getSequenceColour(seq);
969         currentTextColor = Color.black;
970       }
971
972       pg.setColor(currentColor);
973       pg.fillRect(0, (i - startSeq) * av.getCharHeight(), idWidth,
974               av.getCharHeight());
975
976       pg.setColor(currentTextColor);
977
978       int xPos = 0;
979       if (av.isRightAlignIds())
980       {
981         fm = pg.getFontMetrics();
982         xPos = idWidth
983                 - fm.stringWidth(seq.getDisplayId(av.getShowJVSuffix()))
984                 - 4;
985       }
986
987       pg.drawString(
988               seq.getDisplayId(av.getShowJVSuffix()),
989               xPos,
990               (((i - startSeq) * av.getCharHeight()) + av.getCharHeight())
991                       - (av.getCharHeight() / 5));
992     }
993
994     pg.setFont(av.getFont());
995
996     // draw main sequence panel
997     pg.translate(idWidth, 0);
998     getSeqPanel().seqCanvas.drawPanel(pg, startRes, endRes, startSeq, endSeq, 0);
999
1000     if (av.isShowAnnotation() && (endSeq == av.getAlignment().getHeight()))
1001     {
1002       // draw annotation - need to offset for current scroll position
1003       int offset = -getAlabels().getScrollOffset();
1004       pg.translate(0, offset);
1005       pg.translate(-idWidth - 3, (endSeq - startSeq) * av.getCharHeight()
1006               + 3);
1007       getAlabels().drawComponent(pg, idWidth);
1008       pg.translate(idWidth + 3, 0);
1009       getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
1010               pg, -1, startRes, endRes + 1);
1011       pg.translate(0, -offset);
1012     }
1013
1014     return Printable.PAGE_EXISTS;
1015   }
1016
1017   /**
1018    * DOCUMENT ME!
1019    * 
1020    * @param pg
1021    *          DOCUMENT ME!
1022    * @param pwidth
1023    *          DOCUMENT ME!
1024    * @param pheight
1025    *          DOCUMENT ME!
1026    * @param pi
1027    *          DOCUMENT ME!
1028    * 
1029    * @return DOCUMENT ME!
1030    * 
1031    * @throws PrinterException
1032    *           DOCUMENT ME!
1033    */
1034   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight,
1035           int pi) throws PrinterException
1036   {
1037     int annotationHeight = 0;
1038     AnnotationLabels labels = null;
1039     if (av.isShowAnnotation())
1040     {
1041       annotationHeight = getAnnotationPanel().adjustPanelHeight();
1042       labels = new AnnotationLabels(av);
1043     }
1044
1045     int hgap = av.getCharHeight();
1046     if (av.getScaleAboveWrapped())
1047     {
1048       hgap += av.getCharHeight();
1049     }
1050
1051     int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1052             + annotationHeight;
1053
1054     int idWidth = getVisibleIdWidth(false);
1055
1056     int maxwidth = av.getAlignment().getWidth();
1057     if (av.hasHiddenColumns())
1058     {
1059       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
1060     }
1061
1062     int resWidth = getSeqPanel().seqCanvas.getWrappedCanvasWidth(pwidth
1063             - idWidth);
1064
1065     int totalHeight = cHeight * (maxwidth / resWidth + 1);
1066
1067     pg.setColor(Color.white);
1068     pg.fillRect(0, 0, pwidth, pheight);
1069     pg.setFont(av.getFont());
1070
1071     // //////////////
1072     // Draw the ids
1073     pg.setColor(Color.black);
1074
1075     pg.translate(0, -pi * pheight);
1076
1077     pg.setClip(0, pi * pheight, pwidth, pheight);
1078
1079     int ypos = hgap;
1080
1081     do
1082     {
1083       for (int i = 0; i < av.getAlignment().getHeight(); i++)
1084       {
1085         pg.setFont(getIdPanel().getIdCanvas().getIdfont());
1086         SequenceI s = av.getAlignment().getSequenceAt(i);
1087         String string = s.getDisplayId(av.getShowJVSuffix());
1088         int xPos = 0;
1089         if (av.isRightAlignIds())
1090         {
1091           FontMetrics fm = pg.getFontMetrics();
1092           xPos = idWidth - fm.stringWidth(string) - 4;
1093         }
1094         pg.drawString(string, xPos,
1095                 ((i * av.getCharHeight()) + ypos + av.getCharHeight())
1096                         - (av.getCharHeight() / 5));
1097       }
1098       if (labels != null)
1099       {
1100         pg.translate(-3, ypos
1101  + (av.getAlignment().getHeight() * av.getCharHeight()));
1102
1103         pg.setFont(av.getFont());
1104         labels.drawComponent(pg, idWidth);
1105         pg.translate(+3, -ypos
1106                         - (av.getAlignment().getHeight() * av
1107                                 .getCharHeight()));
1108       }
1109
1110       ypos += cHeight;
1111     } while (ypos < totalHeight);
1112
1113     pg.translate(idWidth, 0);
1114
1115     getSeqPanel().seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, totalHeight,
1116             0);
1117
1118     if ((pi * pheight) < totalHeight)
1119     {
1120       return Printable.PAGE_EXISTS;
1121
1122     }
1123     else
1124     {
1125       return Printable.NO_SUCH_PAGE;
1126     }
1127   }
1128
1129   /**
1130    * get current sequence ID panel width, or nominal value if panel were to be
1131    * displayed using default settings
1132    * 
1133    * @return
1134    */
1135   public int getVisibleIdWidth()
1136   {
1137     return getVisibleIdWidth(true);
1138   }
1139
1140   /**
1141    * get current sequence ID panel width, or nominal value if panel were to be
1142    * displayed using default settings
1143    * 
1144    * @param onscreen
1145    *          indicate if the Id width for onscreen or offscreen display should
1146    *          be returned
1147    * @return
1148    */
1149   public int getVisibleIdWidth(boolean onscreen)
1150   {
1151     // see if rendering offscreen - check preferences and calc width accordingly
1152     if (!onscreen && Cache.getDefault("FIGURE_AUTOIDWIDTH", false))
1153     {
1154       return calculateIdWidth(-1).width + 4;
1155     }
1156     Integer idwidth = null;
1157     if (onscreen
1158             || (idwidth = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null)
1159     {
1160       return (getIdPanel().getWidth() > 0 ? getIdPanel().getWidth()
1161               : calculateIdWidth().width + 4);
1162     }
1163     return idwidth.intValue() + 4;
1164   }
1165
1166   void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file)
1167   {
1168     long progress = System.currentTimeMillis();
1169     headless = (System.getProperty("java.awt.headless") != null && System
1170             .getProperty("java.awt.headless").equals("true"));
1171     if (alignFrame != null && !headless)
1172     {
1173       alignFrame.setProgressBar(MessageManager.formatMessage(
1174               "status.saving_file", new Object[]
1175               { type.getLabel() }), progress);
1176     }
1177     try
1178     {
1179       AlignmentDimension aDimension = getAlignmentDimension();
1180       try
1181       {
1182         jalview.util.ImageMaker im;
1183         final String imageAction, imageTitle;
1184         if (type == jalview.util.ImageMaker.TYPE.PNG)
1185         {
1186           imageAction = "Create PNG image from alignment";
1187           imageTitle = null;
1188         }
1189         else if (type == jalview.util.ImageMaker.TYPE.EPS)
1190         {
1191           imageAction = "Create EPS file from alignment";
1192           imageTitle = alignFrame.getTitle();
1193         }
1194         else
1195         {
1196           imageAction = "Create SVG file from alignment";
1197           imageTitle = alignFrame.getTitle();
1198         }
1199
1200         im = new jalview.util.ImageMaker(this, type, imageAction,
1201                 aDimension.getWidth(), aDimension.getHeight(), file,
1202                 imageTitle);
1203         if (av.getWrapAlignment())
1204         {
1205           if (im.getGraphics() != null)
1206           {
1207             printWrappedAlignment(im.getGraphics(), aDimension.getWidth(),
1208                     aDimension.getHeight(), 0);
1209             im.writeImage();
1210           }
1211         }
1212         else
1213         {
1214           if (im.getGraphics() != null)
1215           {
1216             printUnwrapped(im.getGraphics(), aDimension.getWidth(),
1217                     aDimension.getHeight(), 0);
1218             im.writeImage();
1219           }
1220         }
1221       } catch (OutOfMemoryError err)
1222       {
1223         // Be noisy here.
1224         System.out.println("########################\n" + "OUT OF MEMORY "
1225                 + file + "\n" + "########################");
1226         new OOMWarning("Creating Image for " + file, err);
1227         // System.out.println("Create IMAGE: " + err);
1228       } catch (Exception ex)
1229       {
1230         ex.printStackTrace();
1231       }
1232     } finally
1233     {
1234       if (alignFrame != null && !headless)
1235       {
1236         alignFrame.setProgressBar(MessageManager.getString("status.export_complete"), progress);
1237       }
1238     }
1239   }
1240
1241   public AlignmentDimension getAlignmentDimension()
1242   {
1243     int maxwidth = av.getAlignment().getWidth();
1244     if (av.hasHiddenColumns())
1245     {
1246       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth);
1247     }
1248
1249     int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
1250             + getScalePanel().getHeight();
1251     int width = getVisibleIdWidth(false) + (maxwidth * av.getCharWidth());
1252
1253     if (av.getWrapAlignment())
1254     {
1255       height = getWrappedHeight();
1256       if (headless)
1257       {
1258         // need to obtain default alignment width and then add in any
1259         // additional allowance for id margin
1260         // this duplicates the calculation in getWrappedHeight but adjusts for
1261         // offscreen idWith
1262         width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1263                 - alignFrame.getInsets().left
1264                 - alignFrame.getInsets().right - getVisibleIdWidth()
1265                 + getVisibleIdWidth(false);
1266       }
1267       else
1268       {
1269         width = getSeqPanel().getWidth() + getVisibleIdWidth(false);
1270       }
1271
1272     }
1273     else if (av.isShowAnnotation())
1274     {
1275       height += getAnnotationPanel().adjustPanelHeight() + 3;
1276     }
1277     return new AlignmentDimension(width, height);
1278
1279   }
1280
1281   /**
1282    * DOCUMENT ME!
1283    */
1284   public void makeEPS(File epsFile)
1285   {
1286     makeAlignmentImage(jalview.util.ImageMaker.TYPE.EPS, epsFile);
1287   }
1288
1289   /**
1290    * DOCUMENT ME!
1291    */
1292   public void makePNG(File pngFile)
1293   {
1294     makeAlignmentImage(jalview.util.ImageMaker.TYPE.PNG, pngFile);
1295   }
1296
1297   public void makeSVG(File svgFile)
1298   {
1299     makeAlignmentImage(jalview.util.ImageMaker.TYPE.SVG, svgFile);
1300   }
1301   public void makePNGImageMap(File imgMapFile, String imageName)
1302   {
1303     // /////ONLY WORKS WITH NONE WRAPPED ALIGNMENTS
1304     // ////////////////////////////////////////////
1305     int idWidth = getVisibleIdWidth(false);
1306     FontMetrics fm = getFontMetrics(av.getFont());
1307     int scaleHeight = av.getCharHeight() + fm.getDescent();
1308
1309     // Gen image map
1310     // ////////////////////////////////
1311     if (imgMapFile != null)
1312     {
1313       try
1314       {
1315         int s, sSize = av.getAlignment().getHeight(), res, alwidth = av
1316                 .getAlignment().getWidth(), g, gSize, f, fSize, sy;
1317         StringBuffer text = new StringBuffer();
1318         PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1319         out.println(jalview.io.HTMLOutput.getImageMapHTML());
1320         out.println("<img src=\"" + imageName
1321                 + "\" border=\"0\" usemap=\"#Map\" >"
1322                 + "<map name=\"Map\">");
1323
1324         for (s = 0; s < sSize; s++)
1325         {
1326           sy = s * av.getCharHeight() + scaleHeight;
1327
1328           SequenceI seq = av.getAlignment().getSequenceAt(s);
1329           SequenceFeature[] features = seq.getSequenceFeatures();
1330           SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
1331           for (res = 0; res < alwidth; res++)
1332           {
1333             text = new StringBuffer();
1334             String triplet = null;
1335             if (av.getAlignment().isNucleotide())
1336             {
1337               triplet = ResidueProperties.nucleotideName.get(seq
1338                       .getCharAt(res)
1339                       + "");
1340             }
1341             else
1342             {
1343               triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(res)
1344                       + "");
1345             }
1346
1347             if (triplet == null)
1348             {
1349               continue;
1350             }
1351
1352             int alIndex = seq.findPosition(res);
1353             gSize = groups.length;
1354             for (g = 0; g < gSize; g++)
1355             {
1356               if (text.length() < 1)
1357               {
1358                 text.append("<area shape=\"rect\" coords=\""
1359                         + (idWidth + res * av.getCharWidth()) + "," + sy
1360                         + "," + (idWidth + (res + 1) * av.getCharWidth())
1361                         + ","
1362                         + (av.getCharHeight() + sy) + "\""
1363                         + " onMouseOver=\"toolTip('" + alIndex + " "
1364                         + triplet);
1365               }
1366
1367               if (groups[g].getStartRes() < res
1368                       && groups[g].getEndRes() > res)
1369               {
1370                 text.append("<br><em>" + groups[g].getName() + "</em>");
1371               }
1372             }
1373
1374             if (features != null)
1375             {
1376               if (text.length() < 1)
1377               {
1378                 text.append("<area shape=\"rect\" coords=\""
1379                         + (idWidth + res * av.getCharWidth()) + "," + sy
1380                         + "," + (idWidth + (res + 1) * av.getCharWidth())
1381                         + ","
1382                         + (av.getCharHeight() + sy) + "\""
1383                         + " onMouseOver=\"toolTip('" + alIndex + " "
1384                         + triplet);
1385               }
1386               fSize = features.length;
1387               for (f = 0; f < fSize; f++)
1388               {
1389
1390                 if ((features[f].getBegin() <= seq.findPosition(res))
1391                         && (features[f].getEnd() >= seq.findPosition(res)))
1392                 {
1393                   if (features[f].getType().equals("disulfide bond"))
1394                   {
1395                     if (features[f].getBegin() == seq.findPosition(res)
1396                             || features[f].getEnd() == seq
1397                                     .findPosition(res))
1398                     {
1399                       text.append("<br>disulfide bond "
1400                               + features[f].getBegin() + ":"
1401                               + features[f].getEnd());
1402                     }
1403                   }
1404                   else
1405                   {
1406                     text.append("<br>");
1407                     text.append(features[f].getType());
1408                     if (features[f].getDescription() != null
1409                             && !features[f].getType().equals(
1410                                     features[f].getDescription()))
1411                     {
1412                       text.append(" " + features[f].getDescription());
1413                     }
1414
1415                     if (features[f].getValue("status") != null)
1416                     {
1417                       text.append(" (" + features[f].getValue("status")
1418                               + ")");
1419                     }
1420                   }
1421                 }
1422
1423               }
1424             }
1425             if (text.length() > 1)
1426             {
1427               text.append("')\"; onMouseOut=\"toolTip()\";  href=\"#\">");
1428               out.println(text.toString());
1429             }
1430           }
1431         }
1432         out.println("</map></body></html>");
1433         out.close();
1434
1435       } catch (Exception ex)
1436       {
1437         ex.printStackTrace();
1438       }
1439     } // /////////END OF IMAGE MAP
1440
1441   }
1442
1443   int getWrappedHeight()
1444   {
1445     int seqPanelWidth = getSeqPanel().seqCanvas.getWidth();
1446
1447     if (System.getProperty("java.awt.headless") != null
1448             && System.getProperty("java.awt.headless").equals("true"))
1449     {
1450       seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1451               - vscroll.getPreferredSize().width
1452               - alignFrame.getInsets().left - alignFrame.getInsets().right;
1453     }
1454
1455     int chunkWidth = getSeqPanel().seqCanvas
1456             .getWrappedCanvasWidth(seqPanelWidth);
1457
1458     int hgap = av.getCharHeight();
1459     if (av.getScaleAboveWrapped())
1460     {
1461       hgap += av.getCharHeight();
1462     }
1463
1464     int annotationHeight = 0;
1465     if (av.isShowAnnotation())
1466     {
1467       annotationHeight = getAnnotationPanel().adjustPanelHeight();
1468     }
1469
1470     int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1471             + annotationHeight;
1472
1473     int maxwidth = av.getAlignment().getWidth();
1474     if (av.hasHiddenColumns())
1475     {
1476       maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
1477     }
1478
1479     int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1480
1481     return height;
1482   }
1483
1484   /**
1485    * close the panel - deregisters all listeners and nulls any references to
1486    * alignment data.
1487    */
1488   public void closePanel()
1489   {
1490     PaintRefresher.RemoveComponent(getSeqPanel().seqCanvas);
1491     PaintRefresher.RemoveComponent(getIdPanel().getIdCanvas());
1492     PaintRefresher.RemoveComponent(this);
1493     if (av != null)
1494     {
1495       jalview.structure.StructureSelectionManager ssm = av
1496               .getStructureSelectionManager();
1497       ssm.removeStructureViewerListener(getSeqPanel(), null);
1498       ssm.removeSelectionListener(getSeqPanel());
1499       ssm.removeCommandListener(av);
1500       ssm.removeStructureViewerListener(getSeqPanel(), null);
1501       ssm.removeSelectionListener(getSeqPanel());
1502       av.setAlignment(null);
1503       av = null;
1504     }
1505     else
1506     {
1507       if (Cache.log.isDebugEnabled())
1508       {
1509         Cache.log.warn("Closing alignment panel which is already closed.");
1510       }
1511     }
1512   }
1513
1514   /**
1515    * hides or shows dynamic annotation rows based on groups and av state flags
1516    */
1517   public void updateAnnotation()
1518   {
1519     updateAnnotation(false, false);
1520   }
1521
1522   public void updateAnnotation(boolean applyGlobalSettings)
1523   {
1524     updateAnnotation(applyGlobalSettings, false);
1525   }
1526
1527   public void updateAnnotation(boolean applyGlobalSettings,
1528           boolean preserveNewGroupSettings)
1529   {
1530     av.updateGroupAnnotationSettings(applyGlobalSettings,
1531             preserveNewGroupSettings);
1532     adjustAnnotationHeight();
1533   }
1534
1535   @Override
1536   public AlignmentI getAlignment()
1537   {
1538     return av.getAlignment();
1539   }
1540
1541
1542   @Override
1543   public String getViewName()
1544   {
1545     return av.viewName;
1546   }
1547
1548   /**
1549    * Make/Unmake this alignment panel the current input focus
1550    * 
1551    * @param b
1552    */
1553   public void setSelected(boolean b)
1554   {
1555     try
1556     {
1557       alignFrame.setSelected(b);
1558     } catch (Exception ex)
1559     {
1560     }
1561     ;
1562
1563     if (b)
1564     {
1565       alignFrame.setDisplayedView(this);
1566     }
1567   }
1568
1569   @Override
1570   public StructureSelectionManager getStructureSelectionManager()
1571   {
1572     return av.getStructureSelectionManager();
1573   }
1574
1575   @Override
1576   public void raiseOOMWarning(String string, OutOfMemoryError error)
1577   {
1578     new OOMWarning(string, error, this);
1579   }
1580
1581   @Override
1582   public jalview.api.FeatureRenderer cloneFeatureRenderer()
1583   {
1584
1585     return new FeatureRenderer(this);
1586   }
1587   @Override 
1588   public jalview.api.FeatureRenderer getFeatureRenderer()
1589   {
1590     return seqPanel.seqCanvas.getFeatureRenderer();
1591   }
1592   public void updateFeatureRenderer(jalview.renderer.seqfeatures.FeatureRenderer fr)
1593   {
1594     fr.transferSettings(getSeqPanel().seqCanvas.getFeatureRenderer());
1595   }
1596
1597   public void updateFeatureRendererFrom(jalview.api.FeatureRenderer fr)
1598   {
1599     if (getSeqPanel().seqCanvas.getFeatureRenderer() != null)
1600     {
1601       getSeqPanel().seqCanvas.getFeatureRenderer().transferSettings(fr);
1602     }
1603   }
1604
1605   public ScalePanel getScalePanel()
1606   {
1607     return scalePanel;
1608   }
1609
1610   public void setScalePanel(ScalePanel scalePanel)
1611   {
1612     this.scalePanel = scalePanel;
1613   }
1614
1615   public SeqPanel getSeqPanel()
1616   {
1617     return seqPanel;
1618   }
1619
1620   public void setSeqPanel(SeqPanel seqPanel)
1621   {
1622     this.seqPanel = seqPanel;
1623   }
1624
1625   public AnnotationPanel getAnnotationPanel()
1626   {
1627     return annotationPanel;
1628   }
1629
1630   public void setAnnotationPanel(AnnotationPanel annotationPanel)
1631   {
1632     this.annotationPanel = annotationPanel;
1633   }
1634
1635   public AnnotationLabels getAlabels()
1636   {
1637     return alabels;
1638   }
1639
1640   public void setAlabels(AnnotationLabels alabels)
1641   {
1642     this.alabels = alabels;
1643   }
1644
1645   public IdPanel getIdPanel()
1646   {
1647     return idPanel;
1648   }
1649
1650   public void setIdPanel(IdPanel idPanel)
1651   {
1652     this.idPanel = idPanel;
1653   }
1654 }