added copy consensus sequence to consensus annotation popup menu
[jalview.git] / src / jalview / gui / AnnotationLabels.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import jalview.datamodel.*;
22 import jalview.io.FormatAdapter;
23
24 import java.awt.*;
25 import java.awt.datatransfer.StringSelection;
26 import java.awt.event.*;
27 import java.awt.image.*;
28 import java.util.Vector;
29
30 import javax.swing.*;
31
32
33 /**
34  * DOCUMENT ME!
35  *
36  * @author $author$
37  * @version $Revision$
38  */
39 public class AnnotationLabels extends JPanel implements MouseListener,
40     MouseMotionListener, ActionListener
41 {
42     static String ADDNEW = "Add New Row";
43     static String HIDE = "Hide This Row";
44     static String DELETE = "Delete This Row";
45     static String SHOWALL = "Show All Hidden Rows";
46     static String OUTPUT_TEXT = "Export Annotation";
47     static String COPYCONS_SEQ = "Copy Consensus Sequence";
48     boolean resizePanel = false;
49     Image image;
50     AlignmentPanel ap;
51     AlignViewport av;
52     boolean resizing = false;
53     MouseEvent dragEvent;
54     int oldY;
55     int selectedRow;
56     int scrollOffset = 0;
57     Font font = new Font("Arial", Font.PLAIN, 11);
58
59
60     /**
61      * Creates a new AnnotationLabels object.
62      *
63      * @param ap DOCUMENT ME!
64      */
65     public AnnotationLabels(AlignmentPanel ap)
66     {
67         this.ap = ap;
68         av = ap.av;
69
70         java.net.URL url = getClass().getResource("/images/idwidth.gif");
71         Image temp = null;
72
73         if (url != null)
74         {
75             temp = java.awt.Toolkit.getDefaultToolkit().createImage(url);
76         }
77
78         try
79         {
80             MediaTracker mt = new MediaTracker(this);
81             mt.addImage(temp, 0);
82             mt.waitForID(0);
83         }
84         catch (Exception ex)
85         {
86         }
87
88         BufferedImage bi = new BufferedImage(temp.getHeight(this),
89                 temp.getWidth(this), BufferedImage.TYPE_INT_RGB);
90         Graphics2D g = (Graphics2D) bi.getGraphics();
91         g.rotate(Math.toRadians(90));
92         g.drawImage(temp, 0, -bi.getWidth(this), this);
93         image = (Image) bi;
94
95         addMouseListener(this);
96         addMouseMotionListener(this);
97     }
98
99     public AnnotationLabels(AlignViewport av)
100     {
101       this.av = av;
102     }
103
104     /**
105      * DOCUMENT ME!
106      *
107      * @param y DOCUMENT ME!
108      */
109     public void setScrollOffset(int y)
110     {
111         scrollOffset = y;
112         repaint();
113     }
114
115     void getSelectedRow(int y)
116     {
117       int height = 0;
118       AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
119
120       if(aa!=null)
121       {
122         for (int i = 0; i < aa.length; i++)
123         {
124           if (!aa[i].visible)
125           {
126             continue;
127           }
128
129           height += aa[i].height;
130
131           if (y < height)
132           {
133             selectedRow = i;
134
135             break;
136           }
137         }
138       }
139     }
140
141     /**
142      * DOCUMENT ME!
143      *
144      * @param evt DOCUMENT ME!
145      */
146     public void actionPerformed(ActionEvent evt)
147     {
148         int dif = 0;
149         AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
150
151         if (evt.getActionCommand().equals(ADDNEW))
152         {
153             String label = JOptionPane.showInputDialog(this,
154                     "Label for annotation");
155
156             if (label == null)
157             {
158                 label = "";
159             }
160
161             AlignmentAnnotation newAnnotation = new AlignmentAnnotation(label,
162                     "New description",
163                     new Annotation[ap.av.alignment.getWidth()]);
164
165             ap.av.alignment.addAnnotation(newAnnotation);
166             ap.av.alignment.setAnnotationIndex(newAnnotation, 0);
167             if (aa != null)
168               dif = aa[aa.length - 1].height;
169         }
170         else if (evt.getActionCommand().equals(HIDE))
171         {
172             aa[selectedRow].visible = false;
173
174             if (aa[selectedRow].label.equals("Conservation"))
175             {
176                 ap.av.showConservation = false;
177             }
178
179             if (aa[selectedRow].label.equals("Quality"))
180             {
181                 ap.av.showQuality = false;
182             }
183
184             if (aa[selectedRow].label.equals("Consensus"))
185             {
186                 ap.av.showIdentity = false;
187             }
188
189             dif = aa[selectedRow].height * -1;
190         }
191         else if (evt.getActionCommand().equals(DELETE))
192         {
193             ap.av.alignment.deleteAnnotation(aa[selectedRow]);
194             dif = aa[selectedRow].height * -1;
195         }
196         else if (evt.getActionCommand().equals(SHOWALL))
197         {
198             for (int i = 0; i < aa.length; i++)
199             {
200                 if (!aa[i].visible)
201                 {
202                     dif += aa[i].height;
203                     aa[i].visible = true;
204                 }
205             }
206         }
207         else if (evt.getActionCommand().equals(OUTPUT_TEXT))
208         {
209           new AnnotationExporter().exportAnnotations(
210               ap,
211               new AlignmentAnnotation[]
212               {aa[selectedRow]}
213               );
214         }
215         else if (evt.getActionCommand().equals(COPYCONS_SEQ))
216         {
217           SequenceI cons=av.getConsensusSeq();
218           if (cons!=null)
219             copy_annotseqtoclipboard(cons);
220           
221         }
222
223         ap.annotationPanel.adjustPanelHeight();
224         ap.annotationScroller.validate();
225         ap.repaint();
226     }
227
228     /**
229      * DOCUMENT ME!
230      *
231      * @param evt DOCUMENT ME!
232      */
233     public void mousePressed(MouseEvent evt)
234     {
235         getSelectedRow(evt.getY() - scrollOffset);
236         oldY = evt.getY();
237     }
238
239     /**
240      * DOCUMENT ME!
241      *
242      * @param evt DOCUMENT ME!
243      */
244     public void mouseReleased(MouseEvent evt)
245     {
246         int start = selectedRow;
247         getSelectedRow(evt.getY() - scrollOffset);
248         int end = selectedRow;
249
250         if(start!=end)
251         {
252           //Swap these annotations
253           AlignmentAnnotation startAA = ap.av.alignment.getAlignmentAnnotation()[start];
254           AlignmentAnnotation endAA = ap.av.alignment.getAlignmentAnnotation()[end];
255
256
257           ap.av.alignment.getAlignmentAnnotation()[end] = startAA;
258           ap.av.alignment.getAlignmentAnnotation()[start] = endAA;
259         }
260
261         resizePanel = false;
262         dragEvent = null;
263         repaint();
264         ap.annotationPanel.repaint();
265     }
266
267     /**
268      * DOCUMENT ME!
269      *
270      * @param evt DOCUMENT ME!
271      */
272     public void mouseEntered(MouseEvent evt)
273     {
274       if(evt.getY()<10)
275       {
276         resizePanel = true;
277         repaint();
278       }
279     }
280
281     /**
282      * DOCUMENT ME!
283      *
284      * @param evt DOCUMENT ME!
285      */
286     public void mouseExited(MouseEvent evt)
287     {
288       if(dragEvent == null)
289       {
290         resizePanel = false;
291         repaint();
292       }
293     }
294
295     /**
296      * DOCUMENT ME!
297      *
298      * @param evt DOCUMENT ME!
299      */
300     public void mouseDragged(MouseEvent evt)
301     {
302       dragEvent = evt;
303
304       if (resizePanel)
305       {
306         Dimension d = ap.annotationScroller.getPreferredSize();
307         int dif = evt.getY() - oldY;
308
309         dif /= ap.av.charHeight;
310         dif *= ap.av.charHeight;
311
312         if ( (d.height - dif) > 20)
313         {
314           ap.annotationScroller.setPreferredSize(new Dimension(d.width,
315               d.height - dif));
316           d = ap.annotationSpaceFillerHolder.getPreferredSize();
317           ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
318               d.width, d.height - dif));
319           ap.repaint();
320         }
321
322         ap.addNotify();
323       }
324       else
325         repaint();
326     }
327
328     /**
329      * DOCUMENT ME!
330      *
331      * @param evt DOCUMENT ME!
332      */
333     public void mouseMoved(MouseEvent evt)
334     {
335       resizePanel = evt.getY()<10;
336
337       repaint();
338     }
339
340     /**
341      * DOCUMENT ME!
342      *
343      * @param evt DOCUMENT ME!
344      */
345     public void mouseClicked(MouseEvent evt)
346     {
347        if(!SwingUtilities.isRightMouseButton(evt))
348          return;
349
350         AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
351
352         if ((aa == null) || (aa.length == 0))
353         {
354             JPopupMenu pop = new JPopupMenu("Annotations");
355             JMenuItem item = new JMenuItem(ADDNEW);
356             item.addActionListener(this);
357             pop.add(item);
358             pop.show(this, evt.getX(), evt.getY());
359
360             return;
361         }
362
363
364         JPopupMenu pop = new JPopupMenu("Annotations");
365         JMenuItem item = new JMenuItem(ADDNEW);
366         item.addActionListener(this);
367         pop.add(item);
368         item = new JMenuItem(HIDE);
369         item.addActionListener(this);
370         pop.add(item);
371         item = new JMenuItem(DELETE);
372         item.addActionListener(this);
373         pop.add(item);
374         item = new JMenuItem(SHOWALL);
375         item.addActionListener(this);
376         pop.add(item);
377         item = new JMenuItem(OUTPUT_TEXT);
378         item.addActionListener(this);
379         pop.add(item);
380         // annotation object should be typed
381         if (aa[selectedRow].label.equals("Consensus"))
382         {
383           pop.addSeparator();
384           final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
385               "Ignore Gaps In Consensus",
386               ap.av.getIgnoreGapsConsensus());
387           cbmi.addActionListener(new ActionListener()
388               {public void actionPerformed(ActionEvent e)
389                {
390                  ap.av.setIgnoreGapsConsensus(cbmi.getState());
391                  ap.repaint();
392                }
393               });
394           pop.add(cbmi);
395           final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
396           consclipbrd.addActionListener(this);
397           pop.add(consclipbrd);
398         }
399
400         pop.show(this, evt.getX(), evt.getY());
401     }
402     /**
403      * do a single sequence copy to jalview and the system clipboard
404      *
405      * @param sq sequence to be copied to clipboard
406      */
407     protected void copy_annotseqtoclipboard(SequenceI sq)
408     {
409       SequenceI [] seqs = new SequenceI[] { sq };
410       String[] omitHidden = null;
411       SequenceI [] dseqs=new SequenceI[] { sq.getDatasetSequence()};
412       if (dseqs[0]==null) {
413         dseqs[0] = new Sequence(sq);
414         dseqs[0].setSequence(jalview.analysis.AlignSeq.extractGaps(jalview.util.Comparison.GapChars, sq.getSequence()));
415         sq.setDatasetSequence(dseqs[0]);
416       }
417       Alignment ds=new Alignment(dseqs);
418       if (av.hasHiddenColumns)
419       {
420         omitHidden = av.getColumnSelection().getVisibleSequenceStrings(0, sq.getLength(), seqs);
421       }
422
423       String output = new FormatAdapter().formatSequences(
424           "Fasta",
425           seqs,
426           omitHidden);
427
428
429       Toolkit.getDefaultToolkit().getSystemClipboard()
430           .setContents(new StringSelection(output), Desktop.instance);
431
432       Vector hiddenColumns = null;
433       if(av.hasHiddenColumns)
434       {
435         hiddenColumns =new Vector();
436         for(int i=0; i<av.getColumnSelection().getHiddenColumns().size(); i++)
437         {
438           int[] region = (int[])
439               av.getColumnSelection().getHiddenColumns().elementAt(i);
440
441           hiddenColumns.addElement(new int[]{region[0],
442                             region[1]});
443         }
444       }
445       
446       Desktop.jalviewClipboard = new Object[]{ seqs,
447           ds, // what is the dataset of a consensus sequence ? need to flag sequence as special.
448           hiddenColumns};
449     }
450     /**
451      * DOCUMENT ME!
452      *
453      * @param g1 DOCUMENT ME!
454      */
455     public void paintComponent(Graphics g)
456     {
457
458         int width = getWidth();
459         if(width==0)
460          width = ap.calculateIdWidth().width + 4;
461
462        Graphics2D g2 = (Graphics2D) g;
463        if(av.antiAlias)
464         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
465             RenderingHints.VALUE_ANTIALIAS_ON);
466
467        drawComponent(g2, width);
468
469     }
470
471     /**
472      * DOCUMENT ME!
473      *
474      * @param g DOCUMENT ME!
475      */
476     public void drawComponent(Graphics g, int width)
477     {
478         if(av.getFont().getSize()<10)
479           g.setFont(font);
480         else
481           g.setFont(av.getFont());
482
483         FontMetrics fm = g.getFontMetrics(g.getFont());
484         g.setColor(Color.white);
485         g.fillRect(0, 0, getWidth(), getHeight());
486
487         g.translate(0, scrollOffset);
488         g.setColor(Color.black);
489
490         AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
491         int fontHeight = g.getFont().getSize();
492         int y = fontHeight;
493         int x = 0;
494         int graphExtras = 0;
495
496
497
498         if (aa != null)
499         {
500             for (int i = 0; i < aa.length; i++)
501             {
502                 if (!aa[i].visible)
503                 {
504                     continue;
505                 }
506
507                 x = width - fm.stringWidth(aa[i].label) - 3;
508
509                 if (aa[i].graph>0 && aa[i].graphHeight>0)
510                 {
511                     graphExtras = y;
512
513                     y += (aa[i].height / 3);
514
515                     if(aa[i].graphGroup<0)
516                         graphExtras = y + fontHeight;
517                 }
518
519                 if(aa[i].graphGroup>-1)
520                 {
521                   int groupSize = 0;
522                   for (int gg = 0; gg < aa.length; gg++)
523                   {
524                     if (aa[gg].graphGroup == aa[i].graphGroup)
525                       groupSize++;
526                   }
527
528                   if(groupSize * (fontHeight+8) < aa[i].height)
529                     graphExtras += (aa[i].height -( groupSize * (fontHeight+8)) )/2;
530
531                  for(int gg=0; gg<aa.length; gg++)
532                  {
533                    if(aa[gg].graphGroup==aa[i].graphGroup)
534                    {
535                      x = width - fm.stringWidth(aa[gg].label) - 3;
536                      g.drawString(aa[gg].label, x, graphExtras );
537                      if(aa[gg].annotations[0]!=null)
538                        g.setColor(aa[gg].annotations[0].colour);
539
540                      g.drawLine( x, graphExtras+3,
541                                  x+fm.stringWidth(aa[gg].label),
542                                  graphExtras+3);
543
544                      g.setColor(Color.black);
545                      graphExtras += fontHeight+8;
546                    }
547                  }
548                 }
549                 else
550                   g.drawString(aa[i].label, x, y);
551
552                 if (aa[i].graph>0)
553                 {
554                 /*  if (aa[i].graphLines != null)
555                   {
556                     for (int gl = 0; gl < aa[i].graphLines.size(); gl++)
557                     {
558                        x = width - fm.stringWidth(aa[i].getGraphLine(gl).label) - 3;
559                       g.drawString(aa[i].getGraphLine(gl).label, x, graphExtras);
560                       g.setColor(aa[i].getGraphLine(gl).colour);
561                       Graphics2D g2 = (Graphics2D) g;
562                       g2.setStroke(new BasicStroke(1,
563                           BasicStroke.CAP_SQUARE,
564                           BasicStroke.JOIN_ROUND, 3f,
565                           new float[]
566                           {5f, 3f}, 0f));
567
568                       graphExtras += 3;
569
570                       g.drawLine(x, graphExtras,
571                                  x+fm.stringWidth(aa[i].label),
572                                  graphExtras);
573                       g2.setStroke(new BasicStroke());
574                     }
575                   }*/
576                     y += ((2 * aa[i].height) / 3);
577                 }
578                 else
579                 {
580                     y += aa[i].height;
581                 }
582             }
583         }
584
585         if (resizePanel)
586         {
587           g.drawImage(image, 2, 0 - scrollOffset, this);
588         }
589         else if (dragEvent != null && aa!=null)
590         {
591           g.setColor(Color.lightGray);
592           g.drawString(aa[selectedRow].label, dragEvent.getX(), dragEvent.getY() - scrollOffset);
593         }
594
595
596         if ((aa == null) || (aa.length < 1))
597         {
598             g.drawString("Right click", 2, 8);
599             g.drawString("to add annotation", 2, 18);
600         }
601     }
602 }