Can edit annotation name/description
[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 EDITNAME="Edit Label/Description";
44     static String HIDE = "Hide This Row";
45     static String DELETE = "Delete This Row";
46     static String SHOWALL = "Show All Hidden Rows";
47     static String OUTPUT_TEXT = "Export Annotation";
48     static String COPYCONS_SEQ = "Copy Consensus Sequence";
49     boolean resizePanel = false;
50     Image image;
51     AlignmentPanel ap;
52     AlignViewport av;
53     boolean resizing = false;
54     MouseEvent dragEvent;
55     int oldY;
56     int selectedRow;
57     int scrollOffset = 0;
58     Font font = new Font("Arial", Font.PLAIN, 11);
59
60
61     /**
62      * Creates a new AnnotationLabels object.
63      *
64      * @param ap DOCUMENT ME!
65      */
66     public AnnotationLabels(AlignmentPanel ap)
67     {
68         this.ap = ap;
69         av = ap.av;
70         ToolTipManager.sharedInstance().registerComponent(this);
71
72         java.net.URL url = getClass().getResource("/images/idwidth.gif");
73         Image temp = null;
74
75         if (url != null)
76         {
77             temp = java.awt.Toolkit.getDefaultToolkit().createImage(url);
78         }
79
80         try
81         {
82             MediaTracker mt = new MediaTracker(this);
83             mt.addImage(temp, 0);
84             mt.waitForID(0);
85         }
86         catch (Exception ex)
87         {
88         }
89
90         BufferedImage bi = new BufferedImage(temp.getHeight(this),
91                 temp.getWidth(this), BufferedImage.TYPE_INT_RGB);
92         Graphics2D g = (Graphics2D) bi.getGraphics();
93         g.rotate(Math.toRadians(90));
94         g.drawImage(temp, 0, -bi.getWidth(this), this);
95         image = (Image) bi;
96
97         addMouseListener(this);
98         addMouseMotionListener(this);
99     }
100
101     public AnnotationLabels(AlignViewport av)
102     {
103       this.av = av;
104     }
105
106     /**
107      * DOCUMENT ME!
108      *
109      * @param y DOCUMENT ME!
110      */
111     public void setScrollOffset(int y)
112     {
113         scrollOffset = y;
114         repaint();
115     }
116
117     void getSelectedRow(int y)
118     {
119       int height = 0;
120       AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
121
122       if(aa!=null)
123       {
124         for (int i = 0; i < aa.length; i++)
125         {
126           if (!aa[i].visible)
127           {
128             continue;
129           }
130
131           height += aa[i].height;
132
133           if (y < height)
134           {
135             selectedRow = i;
136
137             break;
138           }
139         }
140       }
141     }
142
143     /**
144      * DOCUMENT ME!
145      *
146      * @param evt DOCUMENT ME!
147      */
148     public void actionPerformed(ActionEvent evt)
149     {
150         int dif = 0;
151         AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
152
153         if (evt.getActionCommand().equals(ADDNEW))
154         {
155             AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null,
156                     null,
157                     new Annotation[ap.av.alignment.getWidth()]);
158
159             if(!editLabelDescription(newAnnotation))
160               return;
161
162             ap.av.alignment.addAnnotation(newAnnotation);
163             ap.av.alignment.setAnnotationIndex(newAnnotation, 0);
164             if (aa != null)
165               dif = aa[aa.length - 1].height;
166         }
167         else if(evt.getActionCommand().equals(EDITNAME))
168         {
169           editLabelDescription(aa[selectedRow]);
170           repaint();
171         }
172         else if (evt.getActionCommand().equals(HIDE))
173         {
174             aa[selectedRow].visible = false;
175
176             if (aa[selectedRow].label.equals("Quality"))
177             {
178                 ap.av.quality = null;
179             }
180
181             dif = aa[selectedRow].height * -1;
182         }
183         else if (evt.getActionCommand().equals(DELETE))
184         {
185             ap.av.alignment.deleteAnnotation(aa[selectedRow]);
186             dif = aa[selectedRow].height * -1;
187         }
188         else if (evt.getActionCommand().equals(SHOWALL))
189         {
190             for (int i = 0; i < aa.length; i++)
191             {
192                 if (!aa[i].visible)
193                 {
194                     dif += aa[i].height;
195                     aa[i].visible = true;
196                 }
197             }
198         }
199         else if (evt.getActionCommand().equals(OUTPUT_TEXT))
200         {
201           new AnnotationExporter().exportAnnotations(
202               ap,
203               new AlignmentAnnotation[]
204               {aa[selectedRow]},
205               null
206               );
207         }
208         else if (evt.getActionCommand().equals(COPYCONS_SEQ))
209         {
210           SequenceI cons=av.getConsensusSeq();
211           if (cons!=null)
212             copy_annotseqtoclipboard(cons);
213
214         }
215
216         ap.annotationPanel.adjustPanelHeight();
217         ap.annotationScroller.validate();
218         ap.repaint();
219     }
220
221
222     /**
223      * DOCUMENT ME!
224      *
225      * @param e DOCUMENT ME!
226      */
227     boolean editLabelDescription(AlignmentAnnotation annotation)
228     {
229       JLabel idlabel = new JLabel(  "      Annotation Label ");
230       JLabel desclabel = new JLabel("Annotation Description ");
231       idlabel.setFont(new Font("Courier", Font.PLAIN, 12));
232       desclabel.setFont(new Font("Courier", Font.PLAIN, 12));
233       JTextField id = new JTextField(annotation.label, 40);
234       JTextField description = new JTextField(annotation.description, 40);
235       JPanel panel = new JPanel(new BorderLayout());
236       JPanel panel2 = new JPanel(new BorderLayout());
237       panel2.add(idlabel, BorderLayout.WEST);
238       panel2.add(id, BorderLayout.CENTER);
239       panel.add(panel2, BorderLayout.NORTH);
240       panel2 = new JPanel(new BorderLayout());
241       panel2.add(desclabel, BorderLayout.WEST);
242       panel2.add(description, BorderLayout.CENTER);
243       panel.add(panel2, BorderLayout.SOUTH);
244
245
246        int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
247           panel, "Edit Annotation Name/Description",
248           JOptionPane.OK_CANCEL_OPTION );
249
250
251         if (reply != JOptionPane.OK_OPTION )
252         {
253             return false;
254         }
255
256
257         annotation.label = id.getText().trim();
258
259         String text = description.getText().trim();
260         if(text.length()==0)
261           text = null;
262         annotation.description = text;
263
264         return true;
265     }
266
267     /**
268      * DOCUMENT ME!
269      *
270      * @param evt DOCUMENT ME!
271      */
272     public void mousePressed(MouseEvent evt)
273     {
274         getSelectedRow(evt.getY() - scrollOffset);
275         oldY = evt.getY();
276     }
277
278     /**
279      * DOCUMENT ME!
280      *
281      * @param evt DOCUMENT ME!
282      */
283     public void mouseReleased(MouseEvent evt)
284     {
285         int start = selectedRow;
286         getSelectedRow(evt.getY() - scrollOffset);
287         int end = selectedRow;
288
289         if(start!=end)
290         {
291           //Swap these annotations
292           AlignmentAnnotation startAA = ap.av.alignment.getAlignmentAnnotation()[start];
293           AlignmentAnnotation endAA = ap.av.alignment.getAlignmentAnnotation()[end];
294
295
296           ap.av.alignment.getAlignmentAnnotation()[end] = startAA;
297           ap.av.alignment.getAlignmentAnnotation()[start] = endAA;
298         }
299
300         resizePanel = false;
301         dragEvent = null;
302         repaint();
303         ap.annotationPanel.repaint();
304     }
305
306     /**
307      * DOCUMENT ME!
308      *
309      * @param evt DOCUMENT ME!
310      */
311     public void mouseEntered(MouseEvent evt)
312     {
313       if(evt.getY()<10)
314       {
315         resizePanel = true;
316         repaint();
317       }
318     }
319
320     /**
321      * DOCUMENT ME!
322      *
323      * @param evt DOCUMENT ME!
324      */
325     public void mouseExited(MouseEvent evt)
326     {
327       if(dragEvent == null)
328       {
329         resizePanel = false;
330         repaint();
331       }
332     }
333
334     /**
335      * DOCUMENT ME!
336      *
337      * @param evt DOCUMENT ME!
338      */
339     public void mouseDragged(MouseEvent evt)
340     {
341       dragEvent = evt;
342
343       if (resizePanel)
344       {
345         Dimension d = ap.annotationScroller.getPreferredSize();
346         int dif = evt.getY() - oldY;
347
348         dif /= ap.av.charHeight;
349         dif *= ap.av.charHeight;
350
351         if ( (d.height - dif) > 20)
352         {
353           ap.annotationScroller.setPreferredSize(new Dimension(d.width,
354               d.height - dif));
355           d = ap.annotationSpaceFillerHolder.getPreferredSize();
356           ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
357               d.width, d.height - dif));
358           ap.repaint();
359         }
360
361         ap.addNotify();
362       }
363       else
364         repaint();
365     }
366
367     /**
368      * DOCUMENT ME!
369      *
370      * @param evt DOCUMENT ME!
371      */
372     public void mouseMoved(MouseEvent evt)
373     {
374       resizePanel = evt.getY()<10;
375
376       getSelectedRow(evt.getY() - scrollOffset);
377
378       if(selectedRow>-1)
379       {
380         String desc = ap.av.alignment.
381             getAlignmentAnnotation()[selectedRow].description;
382
383         if (desc != null && !desc.equals("New Description"))
384           setToolTipText(ap.av.alignment.
385                          getAlignmentAnnotation()[selectedRow].description);
386
387       }
388       repaint();
389     }
390
391     /**
392      * DOCUMENT ME!
393      *
394      * @param evt DOCUMENT ME!
395      */
396     public void mouseClicked(MouseEvent evt)
397     {
398        if(!SwingUtilities.isRightMouseButton(evt))
399          return;
400
401         AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
402
403         if ((aa == null) || (aa.length == 0))
404         {
405             JPopupMenu pop = new JPopupMenu("Annotations");
406             JMenuItem item = new JMenuItem(ADDNEW);
407             item.addActionListener(this);
408             pop.add(item);
409             pop.show(this, evt.getX(), evt.getY());
410
411             return;
412         }
413
414
415         JPopupMenu pop = new JPopupMenu("Annotations");
416         JMenuItem item = new JMenuItem(ADDNEW);
417         item.addActionListener(this);
418         pop.add(item);
419         item = new JMenuItem(EDITNAME);
420         item.addActionListener(this);
421         pop.add(item);
422         item = new JMenuItem(HIDE);
423         item.addActionListener(this);
424         pop.add(item);
425         item = new JMenuItem(DELETE);
426         item.addActionListener(this);
427         pop.add(item);
428         item = new JMenuItem(SHOWALL);
429         item.addActionListener(this);
430         pop.add(item);
431         item = new JMenuItem(OUTPUT_TEXT);
432         item.addActionListener(this);
433         pop.add(item);
434         // annotation object should be typed
435         if (aa[selectedRow]==ap.av.consensus)
436         {
437           pop.addSeparator();
438           final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
439               "Ignore Gaps In Consensus",
440               ap.av.getIgnoreGapsConsensus());
441           cbmi.addActionListener(new ActionListener()
442               {public void actionPerformed(ActionEvent e)
443                {
444                  ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
445                }
446               });
447           pop.add(cbmi);
448           final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
449           consclipbrd.addActionListener(this);
450           pop.add(consclipbrd);
451         }
452
453         pop.show(this, evt.getX(), evt.getY());
454     }
455     /**
456      * do a single sequence copy to jalview and the system clipboard
457      *
458      * @param sq sequence to be copied to clipboard
459      */
460     protected void copy_annotseqtoclipboard(SequenceI sq)
461     {
462       SequenceI [] seqs = new SequenceI[] { sq };
463       String[] omitHidden = null;
464       SequenceI [] dseqs=new SequenceI[] { sq.getDatasetSequence()};
465       if (dseqs[0]==null) {
466         dseqs[0] = new Sequence(sq);
467         dseqs[0].setSequence(
468             jalview.analysis.AlignSeq.extractGaps(
469                 jalview.util.Comparison.GapChars,
470                 sq.getSequenceAsString()));
471
472         sq.setDatasetSequence(dseqs[0]);
473       }
474       Alignment ds=new Alignment(dseqs);
475       if (av.hasHiddenColumns)
476       {
477         omitHidden = av.getColumnSelection().getVisibleSequenceStrings(0, sq.getLength(), seqs);
478       }
479
480       String output = new FormatAdapter().formatSequences(
481           "Fasta",
482           seqs,
483           omitHidden);
484
485
486       Toolkit.getDefaultToolkit().getSystemClipboard()
487           .setContents(new StringSelection(output), Desktop.instance);
488
489       Vector hiddenColumns = null;
490       if(av.hasHiddenColumns)
491       {
492         hiddenColumns =new Vector();
493         for(int i=0; i<av.getColumnSelection().getHiddenColumns().size(); i++)
494         {
495           int[] region = (int[])
496               av.getColumnSelection().getHiddenColumns().elementAt(i);
497
498           hiddenColumns.addElement(new int[]{region[0],
499                             region[1]});
500         }
501       }
502
503       Desktop.jalviewClipboard = new Object[]{ seqs,
504           ds, // what is the dataset of a consensus sequence ? need to flag sequence as special.
505           hiddenColumns};
506     }
507     /**
508      * DOCUMENT ME!
509      *
510      * @param g1 DOCUMENT ME!
511      */
512     public void paintComponent(Graphics g)
513     {
514
515         int width = getWidth();
516         if(width==0)
517          width = ap.calculateIdWidth().width + 4;
518
519        Graphics2D g2 = (Graphics2D) g;
520        if(av.antiAlias)
521         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
522             RenderingHints.VALUE_ANTIALIAS_ON);
523
524        drawComponent(g2, width);
525
526     }
527
528     /**
529      * DOCUMENT ME!
530      *
531      * @param g DOCUMENT ME!
532      */
533     public void drawComponent(Graphics g, int width)
534     {
535         if(av.getFont().getSize()<10)
536           g.setFont(font);
537         else
538           g.setFont(av.getFont());
539
540         FontMetrics fm = g.getFontMetrics(g.getFont());
541         g.setColor(Color.white);
542         g.fillRect(0, 0, getWidth(), getHeight());
543
544         g.translate(0, scrollOffset);
545         g.setColor(Color.black);
546
547         AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
548         int fontHeight = g.getFont().getSize();
549         int y = fontHeight;
550         int x = 0;
551         int graphExtras = 0;
552
553
554
555         if (aa != null)
556         {
557             for (int i = 0; i < aa.length; i++)
558             {
559                 if (!aa[i].visible)
560                 {
561                     continue;
562                 }
563
564                 x = width - fm.stringWidth(aa[i].label) - 3;
565
566                 if (aa[i].graph>0 && aa[i].graphHeight>0)
567                 {
568                     graphExtras = y;
569
570                     y += (aa[i].height / 3);
571
572                     if(aa[i].graphGroup<0)
573                         graphExtras = y + fontHeight;
574                 }
575
576                 if(aa[i].graphGroup>-1)
577                 {
578                   int groupSize = 0;
579                   for (int gg = 0; gg < aa.length; gg++)
580                   {
581                     if (aa[gg].graphGroup == aa[i].graphGroup)
582                       groupSize++;
583                   }
584
585                   if(groupSize * (fontHeight+8) < aa[i].height)
586                     graphExtras += (aa[i].height -( groupSize * (fontHeight+8)) )/2;
587
588                  for(int gg=0; gg<aa.length; gg++)
589                  {
590                    if(aa[gg].graphGroup==aa[i].graphGroup)
591                    {
592                      x = width - fm.stringWidth(aa[gg].label) - 3;
593                      g.drawString(aa[gg].label, x, graphExtras );
594                      if(aa[gg].annotations[0]!=null)
595                        g.setColor(aa[gg].annotations[0].colour);
596
597                      g.drawLine( x, graphExtras+3,
598                                  x+fm.stringWidth(aa[gg].label),
599                                  graphExtras+3);
600
601                      g.setColor(Color.black);
602                      graphExtras += fontHeight+8;
603                    }
604                  }
605                 }
606                 else
607                   g.drawString(aa[i].label, x, y);
608
609                 if (aa[i].graph>0)
610                 {
611                 /*  if (aa[i].graphLines != null)
612                   {
613                     for (int gl = 0; gl < aa[i].graphLines.size(); gl++)
614                     {
615                        x = width - fm.stringWidth(aa[i].getGraphLine(gl).label) - 3;
616                       g.drawString(aa[i].getGraphLine(gl).label, x, graphExtras);
617                       g.setColor(aa[i].getGraphLine(gl).colour);
618                       Graphics2D g2 = (Graphics2D) g;
619                       g2.setStroke(new BasicStroke(1,
620                           BasicStroke.CAP_SQUARE,
621                           BasicStroke.JOIN_ROUND, 3f,
622                           new float[]
623                           {5f, 3f}, 0f));
624
625                       graphExtras += 3;
626
627                       g.drawLine(x, graphExtras,
628                                  x+fm.stringWidth(aa[i].label),
629                                  graphExtras);
630                       g2.setStroke(new BasicStroke());
631                     }
632                   }*/
633                     y += ((2 * aa[i].height) / 3);
634                 }
635                 else
636                 {
637                     y += aa[i].height;
638                 }
639             }
640         }
641
642         if (resizePanel)
643         {
644           g.drawImage(image, 2, 0 - scrollOffset, this);
645         }
646         else if (dragEvent != null && aa!=null)
647         {
648           g.setColor(Color.lightGray);
649           g.drawString(aa[selectedRow].label, dragEvent.getX(), dragEvent.getY() - scrollOffset);
650         }
651
652
653         if ((aa == null) || (aa.length < 1))
654         {
655             g.drawString("Right click", 2, 8);
656             g.drawString("to add annotation", 2, 18);
657         }
658     }
659 }