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