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