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