JAL-1807 update
[jalviewjs.git] / unused / appletgui / AnnotationLabels.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.analysis.AlignmentUtils;
24 import jalview.bin.JalviewLite;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.Annotation;
27 import jalview.datamodel.SequenceGroup;
28 import jalview.datamodel.SequenceI;
29 import jalview.util.MessageManager;
30 import jalview.util.ParseHtmlBodyAndLinks;
31
32 import java.awt.Color;
33 import java.awt.Dimension;
34 import java.awt.FlowLayout;
35 import java.awt.FontMetrics;
36 import java.awt.Graphics;
37 import java.awt.Image;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.event.InputEvent;
41 import java.awt.event.ItemEvent;
42 import java.awt.event.ItemListener;
43 import java.awt.event.MouseEvent;
44 import java.awt.event.MouseListener;
45 import java.awt.event.MouseMotionListener;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.Vector;
49
50 import javax.swing.JCheckBox;
51 import javax.swing.JCheckBoxMenuItem;
52 import javax.swing.JFrame;
53 import javax.swing.JMenuItem;
54 import javax.swing.JPanel;
55 import javax.swing.JPopupMenu;
56
57 public class AnnotationLabels extends JPanel implements ActionListener,
58         MouseListener, MouseMotionListener
59 {
60   Image image;
61
62   boolean active = false;
63
64   AlignmentPanel ap;
65
66   AlignViewport av;
67
68   boolean resizing = false;
69
70   int oldY, mouseX;
71
72   static String ADDNEW = "Add New Row";
73
74   static String EDITNAME = "Edit Label/Description";
75
76   static String HIDE = "Hide This Row";
77
78   static String SHOWALL = "Show All Hidden Rows";
79
80   static String OUTPUT_TEXT = "Show Values In Textbox";
81
82   static String COPYCONS_SEQ = "Copy Consensus Sequence";
83
84   int scrollOffset = 0;
85
86   int selectedRow = -1;
87
88   Tooltip tooltip;
89
90   private boolean hasHiddenRows;
91
92   public AnnotationLabels(AlignmentPanel ap)
93   {
94     this.ap = ap;
95     this.av = ap.av;
96     setLayout(null);
97
98     /**
99      * this retrieves the adjustable height glyph from resources. we don't use
100      * it at the moment. java.net.URL url =
101      * getClass().getResource("/images/idwidth.gif"); Image temp = null;
102      * 
103      * if (url != null) { temp =
104      * java.awt.Toolkit.getDefaultToolkit().createImage(url); }
105      * 
106      * try { MediaTracker mt = new MediaTracker(this); mt.addImage(temp, 0);
107      * mt.waitForID(0); } catch (Exception ex) { }
108      * 
109      * BufferedImage bi = new BufferedImage(temp.getHeight(this),
110      * temp.getWidth(this), BufferedImage.TYPE_INT_RGB); Graphics2D g =
111      * (Graphics2D) bi.getGraphics(); g.rotate(Math.toRadians(90));
112      * g.drawImage(temp, 0, -bi.getWidth(this), this); image = (Image) bi;
113      */
114     addMouseListener(this);
115     addMouseMotionListener(this);
116   }
117
118   public AnnotationLabels(AlignViewport av)
119   {
120     this.av = av;
121   }
122
123   public void setScrollOffset(int y, boolean repaint)
124   {
125     scrollOffset = y;
126     if (repaint)
127     {
128       repaint();
129     }
130   }
131
132   /**
133    * 
134    * @param y
135    * @return -2 if no rows are visible at all, -1 if no visible rows were
136    *         selected
137    */
138   int getSelectedRow(int y)
139   {
140     int row = -2;
141     AlignmentAnnotation[] aa = ap.av.getAlignment()
142             .getAlignmentAnnotation();
143
144     if (aa == null)
145     {
146       return row;
147     }
148     int height = 0;
149     for (int i = 0; i < aa.length; i++)
150     {
151       row = -1;
152       if (!aa[i].visible)
153       {
154         continue;
155       }
156       height += aa[i].height;
157       if (y < height)
158       {
159         row = i;
160         break;
161       }
162     }
163
164     return row;
165   }
166
167   public void actionPerformed(ActionEvent evt)
168   {
169     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
170
171     if (evt.getActionCommand().equals(ADDNEW))
172     {
173       AlignmentAnnotation newAnnotation = new AlignmentAnnotation("", null,
174               new Annotation[ap.av.getAlignment().getWidth()]);
175
176       if (!editLabelDescription(newAnnotation))
177       {
178         return;
179       }
180
181       ap.av.getAlignment().addAnnotation(newAnnotation);
182       ap.av.getAlignment().setAnnotationIndex(newAnnotation, 0);
183     }
184     else if (evt.getActionCommand().equals(EDITNAME))
185     {
186       editLabelDescription(aa[selectedRow]);
187     }
188     else if (evt.getActionCommand().equals(HIDE))
189     {
190       aa[selectedRow].visible = false;
191     }
192     else if (evt.getActionCommand().equals(SHOWALL))
193     {
194       for (int i = 0; i < aa.length; i++)
195       {
196         aa[i].visible = (aa[i].annotations == null) ? false : true;
197       }
198     }
199     else if (evt.getActionCommand().equals(OUTPUT_TEXT))
200     {
201       CutAndPasteTransfer cap = new CutAndPasteTransfer(false,
202               ap.alignFrame);
203       JFrame frame = new JFrame();
204       frame.add(cap);
205       JalviewLite.addFrame(frame, ap.alignFrame.getTitle()
206               + " - " + aa[selectedRow].label, 500, 100);
207       cap.setText(aa[selectedRow].toString());
208     }
209     else if (evt.getActionCommand().equals(COPYCONS_SEQ))
210     {
211       SequenceI cons = av.getConsensusSeq();
212       if (cons != null)
213       {
214         copy_annotseqtoclipboard(cons);
215       }
216
217     }
218     refresh();
219   }
220
221   /**
222    * Adjust size and repaint
223    */
224   protected void refresh()
225   {
226     ap.annotationPanel.adjustPanelHeight();
227     setSize(getSize().width, ap.annotationPanel.getSize().height);
228     ap.validate();
229     ap.paintAlignment(true);
230   }
231
232   boolean editLabelDescription(AlignmentAnnotation annotation)
233   {
234     JCheckBox padGaps = new JCheckBox("Fill Empty Gaps With \""
235             + ap.av.getGapCharacter() + "\"", annotation.padGaps);
236
237     EditNameDialog dialog = new EditNameDialog(annotation.label,
238             annotation.description, "      Annotation Label",
239             "Annotation Description", ap.alignFrame,
240             "Edit Annotation Name / Description", 500, 180, false);
241
242     JPanel empty = new JPanel(new FlowLayout());
243     empty.add(padGaps);
244     dialog.add(empty);
245     dialog.pack();
246
247     dialog.setVisible(true);
248
249     if (dialog.accept)
250     {
251       annotation.label = dialog.getName();
252       annotation.description = dialog.getDescription();
253       annotation.setPadGaps(padGaps.isSelected(), av.getGapCharacter());
254       repaint();
255       return true;
256     }
257     else
258     {
259       return false;
260     }
261
262   }
263
264   boolean resizePanel = false;
265
266   public void mouseMoved(MouseEvent evt)
267   {
268     resizePanel = evt.getY() < 10 && evt.getX() < 14;
269     int row = getSelectedRow(evt.getY() + scrollOffset);
270
271     if (row > -1)
272     {
273       ParseHtmlBodyAndLinks phb = new ParseHtmlBodyAndLinks(
274               av.getAlignment().getAlignmentAnnotation()[row]
275                       .getDescription(true),
276               true, "\n");
277       if (tooltip == null)
278       {
279         tooltip = new Tooltip(phb.getNonHtmlContent(), this);
280       }
281       else
282       {
283         tooltip.setTip(phb.getNonHtmlContent());
284       }
285     }
286     else if (tooltip != null)
287     {
288       tooltip.setTip("");
289     }
290   }
291
292   /**
293    * curent drag position
294    */
295   MouseEvent dragEvent = null;
296
297   /**
298    * flag to indicate drag events should be ignored
299    */
300   private boolean dragCancelled = false;
301
302   /**
303    * clear any drag events in progress
304    */
305   public void cancelDrag()
306   {
307     dragEvent = null;
308     dragCancelled = true;
309   }
310
311   public void mouseDragged(MouseEvent evt)
312   {
313     if (dragCancelled)
314     {
315       return;
316     }
317     ;
318     dragEvent = evt;
319
320     if (resizePanel)
321     {
322       Dimension d = ap.annotationPanelHolder.getSize(), e = ap.annotationSpaceFillerHolder
323               .getSize(), f = ap.seqPanelHolder.getSize();
324       int dif = evt.getY() - oldY;
325
326       dif /= ap.av.getCharHeight();
327       dif *= ap.av.getCharHeight();
328
329       if ((d.height - dif) > 20 && (f.height + dif) > 20)
330       {
331         ap.annotationPanel.setSize(d.width, d.height - dif);
332         setSize(new Dimension(e.width, d.height - dif));
333         ap.annotationSpaceFillerHolder.setSize(new Dimension(e.width,
334                 d.height - dif));
335         ap.annotationPanelHolder.setSize(new Dimension(d.width, d.height
336                 - dif));
337         ap.apvscroll.setValues(ap.apvscroll.getValue(), d.height - dif, 0,
338                 av.calcPanelHeight());
339         f.height += dif;
340         ap.seqPanelHolder.setPreferredSize(f);
341         ap.setScrollValues(av.getStartRes(), av.getStartSeq());
342         ap.validate();
343         // ap.paintAlignment(true);
344         ap.addNotify();
345       }
346
347     }
348     else
349     {
350       int diff;
351       if ((diff = 6 - evt.getY()) > 0)
352       {
353         // nudge scroll up
354         ap.apvscroll.setValue(ap.apvscroll.getValue() - diff);
355         ap.adjustmentValueChanged(null);
356
357       }
358       else if ((0 < (diff = 6
359               - ap.annotationSpaceFillerHolder.getSize().height
360               + evt.getY())))
361       {
362         // nudge scroll down
363         ap.apvscroll.setValue(ap.apvscroll.getValue() + diff);
364         ap.adjustmentValueChanged(null);
365       }
366       repaint();
367     }
368   }
369
370   public void mouseClicked(MouseEvent evt)
371   {
372   }
373
374   public void mouseReleased(MouseEvent evt)
375   {
376     if (!resizePanel && !dragCancelled)
377     {
378       int start = selectedRow;
379
380       int end = getSelectedRow(evt.getY() + scrollOffset);
381
382       if (start > -1 && start != end)
383       {
384         // Swap these annotations
385         AlignmentAnnotation startAA = ap.av.getAlignment()
386                 .getAlignmentAnnotation()[start];
387         if (end == -1)
388         {
389           end = ap.av.getAlignment().getAlignmentAnnotation().length - 1;
390         }
391         AlignmentAnnotation endAA = ap.av.getAlignment()
392                 .getAlignmentAnnotation()[end];
393
394         ap.av.getAlignment().getAlignmentAnnotation()[end] = startAA;
395         ap.av.getAlignment().getAlignmentAnnotation()[start] = endAA;
396       }
397     }
398     resizePanel = false;
399     dragEvent = null;
400     dragCancelled = false;
401     repaint();
402     ap.annotationPanel.repaint();
403   }
404
405   public void mouseEntered(MouseEvent evt)
406   {
407     if (evt.getY() < 10 && evt.getX() < 14)
408     {
409       resizePanel = true;
410       repaint();
411     }
412   }
413
414   public void mouseExited(MouseEvent evt)
415   {
416     dragCancelled = false;
417
418     if (dragEvent == null)
419     {
420       resizePanel = false;
421     }
422     else
423     {
424       if (!resizePanel)
425       {
426         dragEvent = null;
427       }
428     }
429     repaint();
430   }
431
432   public void mousePressed(MouseEvent evt)
433   {
434     oldY = evt.getY();
435     if (resizePanel)
436     {
437       return;
438     }
439     dragCancelled = false;
440     // todo: move below to mouseClicked ?
441     selectedRow = getSelectedRow(evt.getY() + scrollOffset);
442
443     AlignmentAnnotation[] aa = ap.av.getAlignment()
444             .getAlignmentAnnotation();
445
446     // DETECT RIGHT MOUSE BUTTON IN AWT
447     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
448     {
449
450       JPopupMenu popup = new JPopupMenu(
451               MessageManager.getString("label.annotations"));
452
453       JMenuItem item = new JMenuItem(ADDNEW);
454       item.addActionListener(this);
455       popup.add(item);
456       if (selectedRow < 0)
457       {
458         // this never happens at moment: - see comment on JAL-563
459         if (hasHiddenRows)
460         {
461           item = new JMenuItem(SHOWALL);
462           item.addActionListener(this);
463           popup.add(item);
464         }
465         this.add(popup);
466         popup.show(this, evt.getX(), evt.getY());
467         return;
468       }
469       // add the rest if there are actually rows to show
470       item = new JMenuItem(EDITNAME);
471       item.addActionListener(this);
472       popup.add(item);
473       item = new JMenuItem(HIDE);
474       item.addActionListener(this);
475       popup.add(item);
476
477       /*
478        * Hide all <label>:
479        */
480       if (selectedRow < aa.length)
481       {
482         if (aa[selectedRow].sequenceRef != null)
483         {
484           final String label = aa[selectedRow].label;
485           JMenuItem hideType = new JMenuItem(
486                   MessageManager.getString("label.hide_all") + " " + label);
487           hideType.addActionListener(new ActionListener()
488           {
489             @Override
490             public void actionPerformed(ActionEvent e)
491             {
492               AlignmentUtils.showOrHideSequenceAnnotations(
493                       ap.av.getAlignment(), Collections.singleton(label),
494                       null, false, false);
495               refresh();
496             }
497           });
498           popup.add(hideType);
499         }
500       }
501
502       if (hasHiddenRows)
503       {
504         item = new JMenuItem(SHOWALL);
505         item.addActionListener(this);
506         popup.add(item);
507       }
508       this.add(popup);
509       item = new JMenuItem(OUTPUT_TEXT);
510       item.addActionListener(this);
511       popup.add(item);
512       if (selectedRow < aa.length)
513       {
514         if (aa[selectedRow].autoCalculated)
515         {
516           if (aa[selectedRow].label.indexOf("Consensus") > -1)
517           {
518             popup.addSeparator();
519             final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
520                     MessageManager.getString("label.ignore_gaps_consensus"),
521                     (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
522                             .getIgnoreGapsConsensus() : ap.av
523                             .isIgnoreGapsConsensus());
524             final AlignmentAnnotation aaa = aa[selectedRow];
525             cbmi.addItemListener(new ItemListener()
526             {
527               public void itemStateChanged(ItemEvent e)
528               {
529                 if (aaa.groupRef != null)
530                 {
531                   // TODO: pass on reference to ap so the view can be updated.
532                   aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
533                 }
534                 else
535                 {
536                   ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
537                 }
538                 ap.paintAlignment(true);
539               }
540             });
541             popup.add(cbmi);
542             if (aaa.groupRef != null)
543             {
544               final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
545                       MessageManager.getString("label.show_group_histogram"),
546                       aa[selectedRow].groupRef.isShowConsensusHistogram());
547               chist.addItemListener(new ItemListener()
548               {
549                 public void itemStateChanged(ItemEvent e)
550                 {
551                   // TODO: pass on reference
552                   // to ap
553                   // so the
554                   // view
555                   // can be
556                   // updated.
557                   aaa.groupRef.setShowConsensusHistogram(chist.getState());
558                   ap.repaint();
559                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
560                 }
561               });
562               popup.add(chist);
563               final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
564                       MessageManager.getString("label.show_group_logo"),
565                       aa[selectedRow].groupRef.isShowSequenceLogo());
566               cprofl.addItemListener(new ItemListener()
567               {
568                 public void itemStateChanged(ItemEvent e)
569                 {
570                   // TODO: pass on reference
571                   // to ap
572                   // so the
573                   // view
574                   // can be
575                   // updated.
576                   aaa.groupRef.setshowSequenceLogo(cprofl.getState());
577                   ap.repaint();
578                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
579                 }
580               });
581
582               popup.add(cprofl);
583               final JCheckBoxMenuItem cprofn = new JCheckBoxMenuItem(
584                       MessageManager.getString("label.normalise_group_logo"),
585                       aa[selectedRow].groupRef.isNormaliseSequenceLogo());
586               cprofn.addItemListener(new ItemListener()
587               {
588                 public void itemStateChanged(ItemEvent e)
589                 {
590                   // TODO: pass on reference
591                   // to ap
592                   // so the
593                   // view
594                   // can be
595                   // updated.
596                   aaa.groupRef.setshowSequenceLogo(true);
597                   aaa.groupRef.setNormaliseSequenceLogo(cprofn.getState());
598                   ap.repaint();
599                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
600                 }
601               });
602               popup.add(cprofn);
603             }
604             else
605             {
606               final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
607                       MessageManager.getString("label.show_histogram"), av.isShowConsensusHistogram());
608               chist.addItemListener(new ItemListener()
609               {
610                 public void itemStateChanged(ItemEvent e)
611                 {
612                   // TODO: pass on reference
613                   // to ap
614                   // so the
615                   // view
616                   // can be
617                   // updated.
618                   av.setShowConsensusHistogram(chist.getState());
619                   ap.alignFrame.showConsensusHistogram.setState(chist
620                           .getState()); // TODO: implement
621                                         // ap.updateGUI()/alignFrame.updateGUI
622                                         // for applet
623                   ap.repaint();
624                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
625                 }
626               });
627               popup.add(chist);
628               final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
629                       MessageManager.getString("label.show_logo"), av.isShowSequenceLogo());
630               cprof.addItemListener(new ItemListener()
631               {
632                 public void itemStateChanged(ItemEvent e)
633                 {
634                   // TODO: pass on reference
635                   // to ap
636                   // so the
637                   // view
638                   // can be
639                   // updated.
640                   av.setShowSequenceLogo(cprof.getState());
641                   ap.alignFrame.showSequenceLogo.setState(cprof.getState()); // TODO:
642                                                                              // implement
643                                                                              // ap.updateGUI()/alignFrame.updateGUI
644                                                                              // for
645                                                                              // applet
646                   ap.repaint();
647                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
648                 }
649               });
650               popup.add(cprof);
651               final JCheckBoxMenuItem cprofn = new JCheckBoxMenuItem(
652                       MessageManager.getString("label.normalise_logo"), av.isNormaliseSequenceLogo());
653               cprofn.addItemListener(new ItemListener()
654               {
655                 public void itemStateChanged(ItemEvent e)
656                 {
657                   // TODO: pass on reference
658                   // to ap
659                   // so the
660                   // view
661                   // can be
662                   // updated.
663                   av.setShowSequenceLogo(true);
664                   ap.alignFrame.normSequenceLogo.setState(cprofn.getState()); // TODO:
665                                                                               // implement
666                                                                               // ap.updateGUI()/alignFrame.updateGUI
667                                                                               // for
668                                                                               // applet
669                   av.setNormaliseSequenceLogo(cprofn.getState());
670                   ap.repaint();
671                   // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
672                 }
673               });
674               popup.add(cprofn);
675             }
676
677             item = new JMenuItem(COPYCONS_SEQ);
678             item.addActionListener(this);
679             popup.add(item);
680           }
681         }
682       }
683       popup.show(this, evt.getX(), evt.getY());
684     }
685     else
686     {
687       // selection action.
688       if (selectedRow > -1 && selectedRow < aa.length)
689       {
690         if (aa[selectedRow].groupRef != null)
691         {
692           if (evt.getClickCount() >= 2)
693           {
694             // todo: make the ap scroll to the selection - not necessary, first
695             // click highlights/scrolls, second selects
696             ap.seqPanel.ap.idPanel.highlightSearchResults(null);
697             ap.av.setSelectionGroup(// new SequenceGroup(
698             aa[selectedRow].groupRef); // );
699             ap.av.sendSelection();
700             ap.paintAlignment(false);
701             PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
702           }
703           else
704           {
705             ap.seqPanel.ap.idPanel
706                     .highlightSearchResults(aa[selectedRow].groupRef
707                             .getSequences(null));
708           }
709           return;
710         }
711         else if (aa[selectedRow].sequenceRef != null)
712         {
713           if (evt.getClickCount() == 1)
714           {
715             ap.seqPanel.ap.idPanel.highlightSearchResults(Arrays
716                     .asList(new SequenceI[]
717                     { aa[selectedRow].sequenceRef }));
718           }
719           else if (evt.getClickCount() >= 2)
720           {
721             ap.seqPanel.ap.idPanel.highlightSearchResults(null);
722             SequenceGroup sg = ap.av.getSelectionGroup();
723             if (sg!=null)
724             {
725               // we make a copy rather than edit the current selection if no modifiers pressed
726               // see Enhancement JAL-1557
727               if (!(evt.isControlDown() || evt.isShiftDown()))
728               {
729                 sg = new SequenceGroup(sg);
730                 sg.clear();
731                 sg.addSequence(aa[selectedRow].sequenceRef, false);
732               } else {
733                 if (evt.isControlDown())
734                 {
735                   sg.addOrRemove(aa[selectedRow].sequenceRef, true);
736                 } else {
737                   // notionally, we should also add intermediate sequences from last added sequence ?
738                   sg.addSequence(aa[selectedRow].sequenceRef, true);
739                 }
740               }
741             } else {
742               sg = new SequenceGroup();
743               sg.setStartRes(0);
744               sg.setEndRes(ap.av.getAlignment().getWidth()-1);
745               sg.addSequence(aa[selectedRow].sequenceRef, false);
746             }
747             ap.av.setSelectionGroup(sg);
748             ap.paintAlignment(false);
749             PaintRefresher.Refresh(ap, ap.av.getSequenceSetId());
750             ap.av.sendSelection();
751           }
752
753         }
754       }
755
756     }
757   }
758
759   /**
760    * DOCUMENT ME!
761    * 
762    * @param e
763    *          DOCUMENT ME!
764    */
765   protected void copy_annotseqtoclipboard(SequenceI sq)
766   {
767     if (sq == null || sq.getLength() < 1)
768     {
769       return;
770     }
771     AlignFrame.copiedSequences = new StringBuffer();
772     AlignFrame.copiedSequences.append(sq.getName() + "\t"
773             + sq.getStart() + "\t" + sq.getEnd() + "\t"
774             + sq.getSequenceAsString() + "\n");
775     if (av.hasHiddenColumns())
776     {
777       AlignFrame.copiedHiddenColumns = new Vector();
778       for (int[] region : av.getColumnSelection().getHiddenColumns())
779       {
780         AlignFrame.copiedHiddenColumns
781                 .addElement(new int[]
782                 { region[0], region[1] });
783       }
784     }
785   }
786
787   public void update(Graphics g)
788   {
789     paint(g);
790   }
791
792   public void paint(Graphics g)
793   {
794     int w = getSize().width;
795     int h = getSize().height;
796     if (image == null || w != image.getWidth(this)
797             || h != image.getHeight(this))
798     {
799       image = createImage(w, ap.annotationPanel.getSize().height);
800     }
801
802     drawComponent(image.getGraphics(), w);
803     g.drawImage(image, 0, 0, this);
804   }
805
806   public void drawComponent(Graphics g, int width)
807   {
808     g.setFont(av.getFont());
809     FontMetrics fm = g.getFontMetrics(av.getFont());
810     g.setColor(Color.white);
811     g.fillRect(0, 0, getSize().width, getSize().height);
812
813     g.translate(0, -scrollOffset);
814     g.setColor(Color.black);
815
816     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
817     int y = 0, fy = g.getFont().getSize();
818     int x = 0, offset;
819
820     if (aa != null)
821     {
822       hasHiddenRows = false;
823       for (int i = 0; i < aa.length; i++)
824       {
825         if (!aa[i].visible)
826         {
827           hasHiddenRows = true;
828           continue;
829         }
830
831         x = width - fm.stringWidth(aa[i].label) - 3;
832
833         y += aa[i].height;
834         offset = -(aa[i].height - fy) / 2;
835
836         g.drawString(aa[i].label, x, y + offset);
837       }
838     }
839     g.translate(0, +scrollOffset);
840     if (resizePanel)
841     {
842       g.setColor(Color.red);
843       g.setPaintMode();
844       g.drawLine(2, 8, 5, 2);
845       g.drawLine(5, 2, 8, 8);
846     }
847     else if (!dragCancelled && dragEvent != null && aa != null)
848     {
849       g.setColor(Color.lightGray);
850       g.drawString(aa[selectedRow].label, dragEvent.getX(),
851               dragEvent.getY());
852     }
853
854     if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1)))
855     {
856       g.setColor(Color.black);
857       g.drawString(MessageManager.getString("label.right_click"), 2, 8);
858       g.drawString(MessageManager.getString("label.to_add_annotation"), 2,
859               18);
860     }
861   }
862 }