JAL-1807 update
[jalviewjs.git] / unused / appletgui / AnnotationPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Annotation;
25 import jalview.renderer.AnnotationRenderer;
26 import jalview.renderer.AwtRenderPanelI;
27 import jalview.util.MessageManager;
28 import jalview.util.Platform;
29
30 import java.awt.Color;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.Graphics;
35 import java.awt.Image;
36 import java.awt.MenuItem;
37 import java.awt.Panel;
38 import java.awt.PopupMenu;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.awt.event.AdjustmentEvent;
42 import java.awt.event.AdjustmentListener;
43 import java.awt.event.InputEvent;
44 import java.awt.event.MouseEvent;
45 import java.awt.event.MouseListener;
46 import java.awt.event.MouseMotionListener;
47
48 public class AnnotationPanel extends Panel implements AwtRenderPanelI,
49         AdjustmentListener, ActionListener, MouseListener,
50         MouseMotionListener
51 {
52   AlignViewport av;
53
54   AlignmentPanel ap;
55
56   int activeRow = -1;
57
58   final String HELIX = "Helix";
59
60   final String SHEET = "Sheet";
61
62   /**
63    * For RNA secondary structure "stems" aka helices
64    */
65   final String STEM = "RNA Helix";
66
67   final String LABEL = "Label";
68
69   final String REMOVE = "Remove Annotation";
70
71   final String COLOUR = "Colour";
72
73   final Color HELIX_COLOUR = Color.red.darker();
74
75   final Color SHEET_COLOUR = Color.green.darker().darker();
76
77   Image image;
78
79   Graphics gg;
80
81   FontMetrics fm;
82
83   int imgWidth = 0;
84
85   boolean fastPaint = false;
86
87   // Used For mouse Dragging and resizing graphs
88   int graphStretch = -1;
89
90   int graphStretchY = -1;
91
92   boolean mouseDragging = false;
93
94   public static int GRAPH_HEIGHT = 40;
95
96   boolean MAC = false;
97
98   public final AnnotationRenderer renderer;
99
100   public AnnotationPanel(AlignmentPanel ap)
101   {
102     MAC = Platform.isAMac();
103     this.ap = ap;
104     av = ap.av;
105     setLayout(null);
106     int height = adjustPanelHeight();
107     ap.apvscroll.setValues(0, getSize().height, 0, height);
108
109     addMouseMotionListener(this);
110
111     addMouseListener(this);
112
113     // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );
114     renderer = new AnnotationRenderer();
115   }
116
117   public AnnotationPanel(AlignViewport av)
118   {
119     this.av = av;
120     renderer = new AnnotationRenderer();
121   }
122
123   @Override
124   public void adjustmentValueChanged(AdjustmentEvent evt)
125   {
126   }
127
128   /**
129    * DOCUMENT ME!
130    * 
131    * @param evt
132    *          DOCUMENT ME!
133    */
134   @Override
135   public void actionPerformed(ActionEvent evt)
136   {
137     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
138     if (aa == null)
139     {
140       return;
141     }
142     Annotation[] anot = aa[activeRow].annotations;
143
144     if (anot.length < av.getColumnSelection().getMax())
145     {
146       Annotation[] temp = new Annotation[av.getColumnSelection().getMax() + 2];
147       System.arraycopy(anot, 0, temp, 0, anot.length);
148       anot = temp;
149       aa[activeRow].annotations = anot;
150     }
151
152     String label = "";
153     if (av.getColumnSelection() != null
154             && av.getColumnSelection().size() > 0
155             && anot[av.getColumnSelection().getMin()] != null)
156     {
157       label = anot[av.getColumnSelection().getMin()].displayCharacter;
158     }
159
160     if (evt.getActionCommand().equals(REMOVE))
161     {
162       for (int i = 0; i < av.getColumnSelection().size(); i++)
163       {
164         anot[av.getColumnSelection().columnAt(i)] = null;
165       }
166     }
167     else if (evt.getActionCommand().equals(LABEL))
168     {
169       label = enterLabel(label, "Enter Label");
170
171       if (label == null)
172       {
173         return;
174       }
175
176       if ((label.length() > 0) && !aa[activeRow].hasText)
177       {
178         aa[activeRow].hasText = true;
179       }
180
181       for (int i = 0; i < av.getColumnSelection().size(); i++)
182       {
183         int index = av.getColumnSelection().columnAt(i);
184
185         if (!av.getColumnSelection().isVisible(index))
186         {
187           continue;
188         }
189
190         if (anot[index] == null)
191         {
192           anot[index] = new Annotation(label, "", ' ', 0);
193         }
194
195         anot[index].displayCharacter = label;
196       }
197     }
198     else if (evt.getActionCommand().equals(COLOUR))
199     {
200       UserDefinedColours udc = new UserDefinedColours(this, Color.black,
201               ap.alignFrame);
202
203       Color col = udc.getColor();
204
205       for (int i = 0; i < av.getColumnSelection().size(); i++)
206       {
207         int index = av.getColumnSelection().columnAt(i);
208
209         if (!av.getColumnSelection().isVisible(index))
210         {
211           continue;
212         }
213
214         if (anot[index] == null)
215         {
216           anot[index] = new Annotation("", "", ' ', 0);
217         }
218
219         anot[index].colour = col;
220       }
221     }
222     else
223     // HELIX OR SHEET
224     {
225       char type = 0;
226       String symbol = "\u03B1";
227
228       if (evt.getActionCommand().equals(HELIX))
229       {
230         type = 'H';
231       }
232       else if (evt.getActionCommand().equals(SHEET))
233       {
234         type = 'E';
235         symbol = "\u03B2";
236       }
237
238       // Added by LML to color stems
239       else if (evt.getActionCommand().equals(STEM))
240       {
241         type = 'S';
242         symbol = "\u03C3";
243       }
244
245       if (!aa[activeRow].hasIcons)
246       {
247         aa[activeRow].hasIcons = true;
248       }
249
250       label = enterLabel(symbol, "Enter Label");
251
252       if (label == null)
253       {
254         return;
255       }
256
257       if ((label.length() > 0) && !aa[activeRow].hasText)
258       {
259         aa[activeRow].hasText = true;
260         if (evt.getActionCommand().equals(STEM))
261         {
262           aa[activeRow].showAllColLabels = true;
263         }
264       }
265
266       for (int i = 0; i < av.getColumnSelection().size(); i++)
267       {
268         int index = av.getColumnSelection().columnAt(i);
269
270         if (!av.getColumnSelection().isVisible(index))
271         {
272           continue;
273         }
274
275         if (anot[index] == null)
276         {
277           anot[index] = new Annotation(label, "", type, 0);
278         }
279
280         anot[index].secondaryStructure = type != 'S' ? type : label
281                 .length() == 0 ? ' ' : label.charAt(0);
282         anot[index].displayCharacter = label;
283       }
284     }
285
286     av.getAlignment().validateAnnotation(aa[activeRow]);
287
288     ap.alignmentChanged();
289     adjustPanelHeight();
290     repaint();
291
292     return;
293   }
294
295   String enterLabel(String text, String label)
296   {
297     EditNameDialog dialog = new EditNameDialog(text, null, label, null,
298             ap.alignFrame, "Enter Label", 400, 200, true);
299
300     if (dialog.accept)
301     {
302       return dialog.getName();
303     }
304     else
305     {
306       return null;
307     }
308   }
309
310   @Override
311   public void mousePressed(MouseEvent evt)
312   {
313     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
314     if (aa == null)
315     {
316       return;
317     }
318
319     int height = -scrollOffset;
320     activeRow = -1;
321
322     for (int i = 0; i < aa.length; i++)
323     {
324       if (aa[i].visible)
325       {
326         height += aa[i].height;
327       }
328
329       if (evt.getY() < height)
330       {
331         if (aa[i].editable)
332         {
333           activeRow = i;
334         }
335         else if (aa[i].graph > 0)
336         {
337           // Stretch Graph
338           graphStretch = i;
339           graphStretchY = evt.getY();
340         }
341
342         break;
343       }
344     }
345
346     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
347             && activeRow != -1)
348     {
349       if (av.getColumnSelection() == null)
350       {
351         return;
352       }
353
354       PopupMenu pop = new PopupMenu(
355               MessageManager.getString("label.structure_type"));
356       MenuItem item;
357       /*
358        * Just display the needed structure options
359        */
360       if (av.getAlignment().isNucleotide() == true)
361       {
362         item = new MenuItem(STEM);
363         item.addActionListener(this);
364         pop.add(item);
365       }
366       else
367       {
368         item = new MenuItem(HELIX);
369         item.addActionListener(this);
370         pop.add(item);
371         item = new MenuItem(SHEET);
372         item.addActionListener(this);
373         pop.add(item);
374       }
375       item = new MenuItem(LABEL);
376       item.addActionListener(this);
377       pop.add(item);
378       item = new MenuItem(COLOUR);
379       item.addActionListener(this);
380       pop.add(item);
381       item = new MenuItem(REMOVE);
382       item.addActionListener(this);
383       pop.add(item);
384       ap.alignFrame.add(pop);
385       pop.show(this, evt.getX(), evt.getY());
386
387       return;
388     }
389
390     ap.scalePanel.mousePressed(evt);
391   }
392
393   @Override
394   public void mouseReleased(MouseEvent evt)
395   {
396     graphStretch = -1;
397     graphStretchY = -1;
398     mouseDragging = false;
399     if (needValidating)
400     {
401       ap.validate();
402       needValidating = false;
403     }
404     ap.scalePanel.mouseReleased(evt);
405   }
406
407   @Override
408   public void mouseClicked(MouseEvent evt)
409   {
410   }
411
412   boolean needValidating = false;
413
414   @Override
415   public void mouseDragged(MouseEvent evt)
416   {
417     if (graphStretch > -1)
418     {
419       av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
420               - evt.getY();
421       if (av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight < 0)
422       {
423         av.getAlignment().getAlignmentAnnotation()[graphStretch].graphHeight = 0;
424       }
425       graphStretchY = evt.getY();
426       av.calcPanelHeight();
427       needValidating = true;
428       ap.paintAlignment(true);
429     }
430     else
431     {
432       ap.scalePanel.mouseDragged(evt);
433     }
434   }
435
436   @Override
437   public void mouseMoved(MouseEvent evt)
438   {
439     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
440     if (aa == null)
441     {
442       return;
443     }
444
445     int row = -1;
446     int height = -scrollOffset;
447     for (int i = 0; i < aa.length; i++)
448     {
449
450       if (aa[i].visible)
451       {
452         height += aa[i].height;
453       }
454
455       if (evt.getY() < height)
456       {
457         row = i;
458         break;
459       }
460     }
461
462     int res = evt.getX() / av.getCharWidth() + av.getStartRes();
463
464     if (av.hasHiddenColumns())
465     {
466       res = av.getColumnSelection().adjustForHiddenColumns(res);
467     }
468
469     if (row > -1 && res < aa[row].annotations.length
470             && aa[row].annotations[res] != null)
471     {
472       StringBuffer text = new StringBuffer("Sequence position " + (res + 1));
473       if (aa[row].annotations[res].description != null)
474       {
475         text.append("  " + aa[row].annotations[res].description);
476       }
477       ap.alignFrame.statusBar.setText(text.toString());
478     }
479   }
480
481   @Override
482   public void mouseEntered(MouseEvent evt)
483   {
484     ap.scalePanel.mouseEntered(evt);
485   }
486
487   @Override
488   public void mouseExited(MouseEvent evt)
489   {
490     ap.scalePanel.mouseExited(evt);
491   }
492
493   public int adjustPanelHeight()
494   {
495     return adjustPanelHeight(true);
496   }
497
498   public int adjustPanelHeight(boolean repaint)
499   {
500     int height = av.calcPanelHeight();
501     this.setSize(new Dimension(getSize().width, height));
502     if (repaint)
503     {
504       repaint();
505     }
506     return height;
507   }
508
509   /**
510    * calculate the height for visible annotation, revalidating bounds where
511    * necessary ABSTRACT GUI METHOD
512    * 
513    * @return total height of annotation
514    */
515
516   public void addEditableColumn(int i)
517   {
518     if (activeRow == -1)
519     {
520       AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
521       if (aa == null)
522       {
523         return;
524       }
525
526       for (int j = 0; j < aa.length; j++)
527       {
528         if (aa[j].editable)
529         {
530           activeRow = j;
531           break;
532         }
533       }
534     }
535   }
536
537   @Override
538   public void update(Graphics g)
539   {
540     paint(g);
541   }
542
543   @Override
544   public void paint(Graphics g)
545   {
546     Dimension d = getSize();
547     imgWidth = d.width;
548     // (av.endRes - av.startRes + 1) * av.charWidth;
549     if (imgWidth < 1 || d.height < 1)
550     {
551       return;
552     }
553     if (image == null || imgWidth != image.getWidth(this)
554             || d.height != image.getHeight(this))
555     {
556       image = createImage(imgWidth, d.height);
557       gg = image.getGraphics();
558       gg.setFont(av.getFont());
559       fm = gg.getFontMetrics();
560       fastPaint = false;
561     }
562
563     if (fastPaint)
564     {
565       g.drawImage(image, 0, 0, this);
566       fastPaint = false;
567       return;
568     }
569
570     gg.setColor(Color.white);
571     gg.fillRect(0, 0, getSize().width, getSize().height);
572     drawComponent(gg, av.startRes, av.endRes + 1);
573
574     g.drawImage(image, 0, 0, this);
575   }
576
577   public void fastPaint(int horizontal)
578   {
579     if (horizontal == 0
580             || av.getAlignment().getAlignmentAnnotation() == null
581             || av.getAlignment().getAlignmentAnnotation().length < 1)
582     {
583       repaint();
584       return;
585     }
586
587     gg.copyArea(0, 0, imgWidth, getSize().height,
588             -horizontal * av.getCharWidth(), 0);
589     int sr = av.startRes, er = av.endRes + 1, transX = 0;
590
591     if (horizontal > 0) // scrollbar pulled right, image to the left
592     {
593       transX = (er - sr - horizontal) * av.getCharWidth();
594       sr = er - horizontal;
595     }
596     else if (horizontal < 0)
597     {
598       er = sr - horizontal;
599     }
600
601     gg.translate(transX, 0);
602
603     drawComponent(gg, sr, er);
604
605     gg.translate(-transX, 0);
606
607     fastPaint = true;
608     repaint();
609   }
610
611   /**
612    * DOCUMENT ME!
613    * 
614    * @param g
615    *          DOCUMENT ME!
616    * @param startRes
617    *          DOCUMENT ME!
618    * @param endRes
619    *          DOCUMENT ME!
620    */
621   public void drawComponent(Graphics g, int startRes, int endRes)
622   {
623     Font ofont = av.getFont();
624     g.setFont(ofont);
625
626     g.setColor(Color.white);
627     g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(),
628             getSize().height);
629
630     if (fm == null)
631     {
632       fm = g.getFontMetrics();
633     }
634
635     if ((av.getAlignment().getAlignmentAnnotation() == null)
636             || (av.getAlignment().getAlignmentAnnotation().length < 1))
637     {
638       g.setColor(Color.white);
639       g.fillRect(0, 0, getSize().width, getSize().height);
640       g.setColor(Color.black);
641       if (av.validCharWidth)
642       {
643         g.drawString(MessageManager
644                 .getString("label.alignment_has_no_annotations"), 20, 15);
645       }
646
647       return;
648     }
649     g.translate(0, -scrollOffset);
650     renderer.drawComponent(this, av, g, activeRow, startRes, endRes);
651     g.translate(0, +scrollOffset);
652   }
653
654   int scrollOffset = 0;
655
656   public void setScrollOffset(int value, boolean repaint)
657   {
658     scrollOffset = value;
659     if (repaint)
660     {
661       repaint();
662     }
663   }
664
665   @Override
666   public FontMetrics getFontMetrics()
667   {
668     return fm;
669   }
670
671   @Override
672   public Image getFadedImage()
673   {
674     return image;
675   }
676
677   @Override
678   public int getFadedImageWidth()
679   {
680     return imgWidth;
681   }
682
683   private int[] bounds = new int[2];
684
685   @Override
686   public int[] getVisibleVRange()
687   {
688     if (ap != null && ap.alabels != null)
689     {
690       int sOffset = -ap.alabels.scrollOffset;
691       int visHeight = sOffset + ap.annotationPanelHolder.getHeight();
692       bounds[0] = sOffset;
693       bounds[1] = visHeight;
694       return bounds;
695     }
696     else
697     {
698       return null;
699     }
700   }
701 }