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