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