Output groups in annotation file
[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               null
205               );
206         }
207         else if (evt.getActionCommand().equals(COPYCONS_SEQ))
208         {
209           SequenceI cons=av.getConsensusSeq();
210           if (cons!=null)
211             copy_annotseqtoclipboard(cons);
212
213         }
214
215         ap.annotationPanel.adjustPanelHeight();
216         ap.annotationScroller.validate();
217         ap.repaint();
218     }
219
220     /**
221      * DOCUMENT ME!
222      *
223      * @param evt DOCUMENT ME!
224      */
225     public void mousePressed(MouseEvent evt)
226     {
227         getSelectedRow(evt.getY() - scrollOffset);
228         oldY = evt.getY();
229     }
230
231     /**
232      * DOCUMENT ME!
233      *
234      * @param evt DOCUMENT ME!
235      */
236     public void mouseReleased(MouseEvent evt)
237     {
238         int start = selectedRow;
239         getSelectedRow(evt.getY() - scrollOffset);
240         int end = selectedRow;
241
242         if(start!=end)
243         {
244           //Swap these annotations
245           AlignmentAnnotation startAA = ap.av.alignment.getAlignmentAnnotation()[start];
246           AlignmentAnnotation endAA = ap.av.alignment.getAlignmentAnnotation()[end];
247
248
249           ap.av.alignment.getAlignmentAnnotation()[end] = startAA;
250           ap.av.alignment.getAlignmentAnnotation()[start] = endAA;
251         }
252
253         resizePanel = false;
254         dragEvent = null;
255         repaint();
256         ap.annotationPanel.repaint();
257     }
258
259     /**
260      * DOCUMENT ME!
261      *
262      * @param evt DOCUMENT ME!
263      */
264     public void mouseEntered(MouseEvent evt)
265     {
266       if(evt.getY()<10)
267       {
268         resizePanel = true;
269         repaint();
270       }
271     }
272
273     /**
274      * DOCUMENT ME!
275      *
276      * @param evt DOCUMENT ME!
277      */
278     public void mouseExited(MouseEvent evt)
279     {
280       if(dragEvent == null)
281       {
282         resizePanel = false;
283         repaint();
284       }
285     }
286
287     /**
288      * DOCUMENT ME!
289      *
290      * @param evt DOCUMENT ME!
291      */
292     public void mouseDragged(MouseEvent evt)
293     {
294       dragEvent = evt;
295
296       if (resizePanel)
297       {
298         Dimension d = ap.annotationScroller.getPreferredSize();
299         int dif = evt.getY() - oldY;
300
301         dif /= ap.av.charHeight;
302         dif *= ap.av.charHeight;
303
304         if ( (d.height - dif) > 20)
305         {
306           ap.annotationScroller.setPreferredSize(new Dimension(d.width,
307               d.height - dif));
308           d = ap.annotationSpaceFillerHolder.getPreferredSize();
309           ap.annotationSpaceFillerHolder.setPreferredSize(new Dimension(
310               d.width, d.height - dif));
311           ap.repaint();
312         }
313
314         ap.addNotify();
315       }
316       else
317         repaint();
318     }
319
320     /**
321      * DOCUMENT ME!
322      *
323      * @param evt DOCUMENT ME!
324      */
325     public void mouseMoved(MouseEvent evt)
326     {
327       resizePanel = evt.getY()<10;
328
329       getSelectedRow(evt.getY() - scrollOffset);
330
331       if(selectedRow>-1)
332       {
333         setToolTipText(ap.av.alignment.
334             getAlignmentAnnotation()[selectedRow].description);
335
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(), ap);
391                }
392               });
393           pop.add(cbmi);
394           final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
395           consclipbrd.addActionListener(this);
396           pop.add(consclipbrd);
397         }
398
399         pop.show(this, evt.getX(), evt.getY());
400     }
401     /**
402      * do a single sequence copy to jalview and the system clipboard
403      *
404      * @param sq sequence to be copied to clipboard
405      */
406     protected void copy_annotseqtoclipboard(SequenceI sq)
407     {
408       SequenceI [] seqs = new SequenceI[] { sq };
409       String[] omitHidden = null;
410       SequenceI [] dseqs=new SequenceI[] { sq.getDatasetSequence()};
411       if (dseqs[0]==null) {
412         dseqs[0] = new Sequence(sq);
413         dseqs[0].setSequence(
414             jalview.analysis.AlignSeq.extractGaps(
415                 jalview.util.Comparison.GapChars,
416                 sq.getSequenceAsString()));
417
418         sq.setDatasetSequence(dseqs[0]);
419       }
420       Alignment ds=new Alignment(dseqs);
421       if (av.hasHiddenColumns)
422       {
423         omitHidden = av.getColumnSelection().getVisibleSequenceStrings(0, sq.getLength(), seqs);
424       }
425
426       String output = new FormatAdapter().formatSequences(
427           "Fasta",
428           seqs,
429           omitHidden);
430
431
432       Toolkit.getDefaultToolkit().getSystemClipboard()
433           .setContents(new StringSelection(output), Desktop.instance);
434
435       Vector hiddenColumns = null;
436       if(av.hasHiddenColumns)
437       {
438         hiddenColumns =new Vector();
439         for(int i=0; i<av.getColumnSelection().getHiddenColumns().size(); i++)
440         {
441           int[] region = (int[])
442               av.getColumnSelection().getHiddenColumns().elementAt(i);
443
444           hiddenColumns.addElement(new int[]{region[0],
445                             region[1]});
446         }
447       }
448
449       Desktop.jalviewClipboard = new Object[]{ seqs,
450           ds, // what is the dataset of a consensus sequence ? need to flag sequence as special.
451           hiddenColumns};
452     }
453     /**
454      * DOCUMENT ME!
455      *
456      * @param g1 DOCUMENT ME!
457      */
458     public void paintComponent(Graphics g)
459     {
460
461         int width = getWidth();
462         if(width==0)
463          width = ap.calculateIdWidth().width + 4;
464
465        Graphics2D g2 = (Graphics2D) g;
466        if(av.antiAlias)
467         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
468             RenderingHints.VALUE_ANTIALIAS_ON);
469
470        drawComponent(g2, width);
471
472     }
473
474     /**
475      * DOCUMENT ME!
476      *
477      * @param g DOCUMENT ME!
478      */
479     public void drawComponent(Graphics g, int width)
480     {
481         if(av.getFont().getSize()<10)
482           g.setFont(font);
483         else
484           g.setFont(av.getFont());
485
486         FontMetrics fm = g.getFontMetrics(g.getFont());
487         g.setColor(Color.white);
488         g.fillRect(0, 0, getWidth(), getHeight());
489
490         g.translate(0, scrollOffset);
491         g.setColor(Color.black);
492
493         AlignmentAnnotation[] aa = av.alignment.getAlignmentAnnotation();
494         int fontHeight = g.getFont().getSize();
495         int y = fontHeight;
496         int x = 0;
497         int graphExtras = 0;
498
499
500
501         if (aa != null)
502         {
503             for (int i = 0; i < aa.length; i++)
504             {
505                 if (!aa[i].visible)
506                 {
507                     continue;
508                 }
509
510                 x = width - fm.stringWidth(aa[i].label) - 3;
511
512                 if (aa[i].graph>0 && aa[i].graphHeight>0)
513                 {
514                     graphExtras = y;
515
516                     y += (aa[i].height / 3);
517
518                     if(aa[i].graphGroup<0)
519                         graphExtras = y + fontHeight;
520                 }
521
522                 if(aa[i].graphGroup>-1)
523                 {
524                   int groupSize = 0;
525                   for (int gg = 0; gg < aa.length; gg++)
526                   {
527                     if (aa[gg].graphGroup == aa[i].graphGroup)
528                       groupSize++;
529                   }
530
531                   if(groupSize * (fontHeight+8) < aa[i].height)
532                     graphExtras += (aa[i].height -( groupSize * (fontHeight+8)) )/2;
533
534                  for(int gg=0; gg<aa.length; gg++)
535                  {
536                    if(aa[gg].graphGroup==aa[i].graphGroup)
537                    {
538                      x = width - fm.stringWidth(aa[gg].label) - 3;
539                      g.drawString(aa[gg].label, x, graphExtras );
540                      if(aa[gg].annotations[0]!=null)
541                        g.setColor(aa[gg].annotations[0].colour);
542
543                      g.drawLine( x, graphExtras+3,
544                                  x+fm.stringWidth(aa[gg].label),
545                                  graphExtras+3);
546
547                      g.setColor(Color.black);
548                      graphExtras += fontHeight+8;
549                    }
550                  }
551                 }
552                 else
553                   g.drawString(aa[i].label, x, y);
554
555                 if (aa[i].graph>0)
556                 {
557                 /*  if (aa[i].graphLines != null)
558                   {
559                     for (int gl = 0; gl < aa[i].graphLines.size(); gl++)
560                     {
561                        x = width - fm.stringWidth(aa[i].getGraphLine(gl).label) - 3;
562                       g.drawString(aa[i].getGraphLine(gl).label, x, graphExtras);
563                       g.setColor(aa[i].getGraphLine(gl).colour);
564                       Graphics2D g2 = (Graphics2D) g;
565                       g2.setStroke(new BasicStroke(1,
566                           BasicStroke.CAP_SQUARE,
567                           BasicStroke.JOIN_ROUND, 3f,
568                           new float[]
569                           {5f, 3f}, 0f));
570
571                       graphExtras += 3;
572
573                       g.drawLine(x, graphExtras,
574                                  x+fm.stringWidth(aa[i].label),
575                                  graphExtras);
576                       g2.setStroke(new BasicStroke());
577                     }
578                   }*/
579                     y += ((2 * aa[i].height) / 3);
580                 }
581                 else
582                 {
583                     y += aa[i].height;
584                 }
585             }
586         }
587
588         if (resizePanel)
589         {
590           g.drawImage(image, 2, 0 - scrollOffset, this);
591         }
592         else if (dragEvent != null && aa!=null)
593         {
594           g.setColor(Color.lightGray);
595           g.drawString(aa[selectedRow].label, dragEvent.getX(), dragEvent.getY() - scrollOffset);
596         }
597
598
599         if ((aa == null) || (aa.length < 1))
600         {
601             g.drawString("Right click", 2, 8);
602             g.drawString("to add annotation", 2, 18);
603         }
604     }
605 }