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