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