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