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