One class for edit 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       EditNameDialog dialog = new EditNameDialog(annotation.label,
230                                                  annotation.description,
231                                                  "       Annotation Name ",
232                                                  "Annotation Description ",
233                                                  "Edit Annotation Name/Description");
234
235       if (!dialog.accept)
236       {
237         return false;
238       }
239
240         annotation.label = dialog.getName();
241
242         String text = dialog.getDescription();
243         if(text.length()==0)
244           text = null;
245         annotation.description = text;
246
247         return true;
248     }
249
250     /**
251      * DOCUMENT ME!
252      *
253      * @param evt DOCUMENT ME!
254      */
255     public void mousePressed(MouseEvent evt)
256     {
257         getSelectedRow(evt.getY() - scrollOffset);
258         oldY = evt.getY();
259     }
260
261     /**
262      * DOCUMENT ME!
263      *
264      * @param evt DOCUMENT ME!
265      */
266     public void mouseReleased(MouseEvent evt)
267     {
268         int start = selectedRow;
269         getSelectedRow(evt.getY() - scrollOffset);
270         int end = selectedRow;
271
272         if(start!=end)
273         {
274           //Swap these annotations
275           AlignmentAnnotation startAA = ap.av.alignment.getAlignmentAnnotation()[start];
276           AlignmentAnnotation endAA = ap.av.alignment.getAlignmentAnnotation()[end];
277
278
279           ap.av.alignment.getAlignmentAnnotation()[end] = startAA;
280           ap.av.alignment.getAlignmentAnnotation()[start] = endAA;
281         }
282
283         resizePanel = false;
284         dragEvent = null;
285         repaint();
286         ap.annotationPanel.repaint();
287     }
288
289     /**
290      * DOCUMENT ME!
291      *
292      * @param evt DOCUMENT ME!
293      */
294     public void mouseEntered(MouseEvent evt)
295     {
296       if(evt.getY()<10)
297       {
298         resizePanel = true;
299         repaint();
300       }
301     }
302
303     /**
304      * DOCUMENT ME!
305      *
306      * @param evt DOCUMENT ME!
307      */
308     public void mouseExited(MouseEvent evt)
309     {
310       if(dragEvent == null)
311       {
312         resizePanel = false;
313         repaint();
314       }
315     }
316
317     /**
318      * DOCUMENT ME!
319      *
320      * @param evt DOCUMENT ME!
321      */
322     public void mouseDragged(MouseEvent evt)
323     {
324       dragEvent = evt;
325
326       if (resizePanel)
327       {
328         Dimension d = ap.annotationScroller.getPreferredSize();
329         int dif = evt.getY() - oldY;
330
331         dif /= ap.av.charHeight;
332         dif *= ap.av.charHeight;
333
334         if ( (d.height - dif) > 20)
335         {
336           ap.annotationScroller.setPreferredSize(new Dimension(d.width,
337               d.height - dif));
338           d = ap.annotationSpaceFillerHolder.getPreferredSize();
339           ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
340               d.width, d.height - dif));
341           ap.repaint();
342         }
343
344         ap.addNotify();
345       }
346       else
347         repaint();
348     }
349
350     /**
351      * DOCUMENT ME!
352      *
353      * @param evt DOCUMENT ME!
354      */
355     public void mouseMoved(MouseEvent evt)
356     {
357       resizePanel = evt.getY()<10;
358
359       getSelectedRow(evt.getY() - scrollOffset);
360
361       if(selectedRow>-1)
362       {
363         String desc = ap.av.alignment.
364             getAlignmentAnnotation()[selectedRow].description;
365
366         if (desc != null && !desc.equals("New Description"))
367           setToolTipText(ap.av.alignment.
368                          getAlignmentAnnotation()[selectedRow].description);
369
370       }
371       repaint();
372     }
373
374     /**
375      * DOCUMENT ME!
376      *
377      * @param evt DOCUMENT ME!
378      */
379     public void mouseClicked(MouseEvent evt)
380     {
381        if(!SwingUtilities.isRightMouseButton(evt))
382          return;
383
384         AlignmentAnnotation[] aa = ap.av.alignment.getAlignmentAnnotation();
385
386         if ((aa == null) || (aa.length == 0))
387         {
388             JPopupMenu pop = new JPopupMenu("Annotations");
389             JMenuItem item = new JMenuItem(ADDNEW);
390             item.addActionListener(this);
391             pop.add(item);
392             pop.show(this, evt.getX(), evt.getY());
393
394             return;
395         }
396
397
398         JPopupMenu pop = new JPopupMenu("Annotations");
399         JMenuItem item = new JMenuItem(ADDNEW);
400         item.addActionListener(this);
401         pop.add(item);
402         item = new JMenuItem(EDITNAME);
403         item.addActionListener(this);
404         pop.add(item);
405         item = new JMenuItem(HIDE);
406         item.addActionListener(this);
407         pop.add(item);
408         item = new JMenuItem(DELETE);
409         item.addActionListener(this);
410         pop.add(item);
411         item = new JMenuItem(SHOWALL);
412         item.addActionListener(this);
413         pop.add(item);
414         item = new JMenuItem(OUTPUT_TEXT);
415         item.addActionListener(this);
416         pop.add(item);
417         // annotation object should be typed
418         if (aa[selectedRow]==ap.av.consensus)
419         {
420           pop.addSeparator();
421           final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
422               "Ignore Gaps In Consensus",
423               ap.av.getIgnoreGapsConsensus());
424           cbmi.addActionListener(new ActionListener()
425               {public void actionPerformed(ActionEvent e)
426                {
427                  ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
428                }
429               });
430           pop.add(cbmi);
431           final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
432           consclipbrd.addActionListener(this);
433           pop.add(consclipbrd);
434         }
435
436         pop.show(this, evt.getX(), evt.getY());
437     }
438     /**
439      * do a single sequence copy to jalview and the system clipboard
440      *
441      * @param sq sequence to be copied to clipboard
442      */
443     protected void copy_annotseqtoclipboard(SequenceI sq)
444     {
445       SequenceI [] seqs = new SequenceI[] { sq };
446       String[] omitHidden = null;
447       SequenceI [] dseqs=new SequenceI[] { sq.getDatasetSequence()};
448       if (dseqs[0]==null) {
449         dseqs[0] = new Sequence(sq);
450         dseqs[0].setSequence(
451             jalview.analysis.AlignSeq.extractGaps(
452                 jalview.util.Comparison.GapChars,
453                 sq.getSequenceAsString()));
454
455         sq.setDatasetSequence(dseqs[0]);
456       }
457       Alignment ds=new Alignment(dseqs);
458       if (av.hasHiddenColumns)
459       {
460         omitHidden = av.getColumnSelection().getVisibleSequenceStrings(0, sq.getLength(), seqs);
461       }
462
463       String output = new FormatAdapter().formatSequences(
464           "Fasta",
465           seqs,
466           omitHidden);
467
468
469       Toolkit.getDefaultToolkit().getSystemClipboard()
470           .setContents(new StringSelection(output), Desktop.instance);
471
472       Vector hiddenColumns = null;
473       if(av.hasHiddenColumns)
474       {
475         hiddenColumns =new Vector();
476         for(int i=0; i<av.getColumnSelection().getHiddenColumns().size(); i++)
477         {
478           int[] region = (int[])
479               av.getColumnSelection().getHiddenColumns().elementAt(i);
480
481           hiddenColumns.addElement(new int[]{region[0],
482                             region[1]});
483         }
484       }
485
486       Desktop.jalviewClipboard = new Object[]{ seqs,
487           ds, // what is the dataset of a consensus sequence ? need to flag sequence as special.
488           hiddenColumns};
489     }
490     /**
491      * DOCUMENT ME!
492      *
493      * @param g1 DOCUMENT ME!
494      */
495     public void paintComponent(Graphics g)
496     {
497
498         int width = getWidth();
499         if(width==0)
500          width = ap.calculateIdWidth().width + 4;
501
502        Graphics2D g2 = (Graphics2D) g;
503        if(av.antiAlias)
504         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
505             RenderingHints.VALUE_ANTIALIAS_ON);
506
507        drawComponent(g2, width);
508
509     }
510
511     /**
512      * DOCUMENT ME!
513      *
514      * @param g DOCUMENT ME!
515      */
516     public void drawComponent(Graphics g, int width)
517     {
518         if(av.getFont().getSize()<10)
519           g.setFont(font);
520         else
521           g.setFont(av.getFont());
522
523         FontMetrics fm = g.getFontMetrics(g.getFont());
524         g.setColor(Color.white);
525         g.fillRect(0, 0, getWidth(), getHeight());
526
527         g.translate(0, scrollOffset);
528         g.setColor(Color.black);
529
530         AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
531         int fontHeight = g.getFont().getSize();
532         int y = fontHeight;
533         int x = 0;
534         int graphExtras = 0;
535
536
537
538         if (aa != null)
539         {
540             for (int i = 0; i < aa.length; i++)
541             {
542                 if (!aa[i].visible)
543                 {
544                     continue;
545                 }
546
547                 x = width - fm.stringWidth(aa[i].label) - 3;
548
549                 if (aa[i].graph>0 && aa[i].graphHeight>0)
550                 {
551                     graphExtras = y;
552
553                     y += (aa[i].height / 3);
554
555                     if(aa[i].graphGroup<0)
556                         graphExtras = y + fontHeight;
557                 }
558
559                 if(aa[i].graphGroup>-1)
560                 {
561                   int groupSize = 0;
562                   for (int gg = 0; gg < aa.length; gg++)
563                   {
564                     if (aa[gg].graphGroup == aa[i].graphGroup)
565                       groupSize++;
566                   }
567
568                   if(groupSize * (fontHeight+8) < aa[i].height)
569                     graphExtras += (aa[i].height -( groupSize * (fontHeight+8)) )/2;
570
571                  for(int gg=0; gg<aa.length; gg++)
572                  {
573                    if(aa[gg].graphGroup==aa[i].graphGroup)
574                    {
575                      x = width - fm.stringWidth(aa[gg].label) - 3;
576                      g.drawString(aa[gg].label, x, graphExtras );
577                      if(aa[gg].annotations[0]!=null)
578                        g.setColor(aa[gg].annotations[0].colour);
579
580                      g.drawLine( x, graphExtras+3,
581                                  x+fm.stringWidth(aa[gg].label),
582                                  graphExtras+3);
583
584                      g.setColor(Color.black);
585                      graphExtras += fontHeight+8;
586                    }
587                  }
588                 }
589                 else
590                   g.drawString(aa[i].label, x, y);
591
592                 if (aa[i].graph>0)
593                 {
594                 /*  if (aa[i].graphLines != null)
595                   {
596                     for (int gl = 0; gl < aa[i].graphLines.size(); gl++)
597                     {
598                        x = width - fm.stringWidth(aa[i].getGraphLine(gl).label) - 3;
599                       g.drawString(aa[i].getGraphLine(gl).label, x, graphExtras);
600                       g.setColor(aa[i].getGraphLine(gl).colour);
601                       Graphics2D g2 = (Graphics2D) g;
602                       g2.setStroke(new BasicStroke(1,
603                           BasicStroke.CAP_SQUARE,
604                           BasicStroke.JOIN_ROUND, 3f,
605                           new float[]
606                           {5f, 3f}, 0f));
607
608                       graphExtras += 3;
609
610                       g.drawLine(x, graphExtras,
611                                  x+fm.stringWidth(aa[i].label),
612                                  graphExtras);
613                       g2.setStroke(new BasicStroke());
614                     }
615                   }*/
616                     y += ((2 * aa[i].height) / 3);
617                 }
618                 else
619                 {
620                     y += aa[i].height;
621                 }
622             }
623         }
624
625         if (resizePanel)
626         {
627           g.drawImage(image, 2, 0 - scrollOffset, this);
628         }
629         else if (dragEvent != null && aa!=null)
630         {
631           g.setColor(Color.lightGray);
632           g.drawString(aa[selectedRow].label, dragEvent.getX(), dragEvent.getY() - scrollOffset);
633         }
634
635
636         if ((aa == null) || (aa.length < 1))
637         {
638             g.drawString("Right click", 2, 8);
639             g.drawString("to add annotation", 2, 18);
640         }
641     }
642 }