Clover icon

Coverage Report

  1. Project Clover database Wed Nov 13 2024 16:12:26 GMT
  2. Package jalview.appletgui

File AnnotationPanel.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
59% of files have more coverage

Code metrics

142
266
25
1
791
608
113
0.42
10.64
25
4.52

Classes

Class Line # Actions
AnnotationPanel 54 266 113
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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.datamodel.SequenceI;
26    import jalview.renderer.AnnotationRenderer;
27    import jalview.renderer.AwtRenderPanelI;
28    import jalview.schemes.ResidueProperties;
29    import jalview.util.Comparison;
30    import jalview.util.MessageManager;
31    import jalview.util.Platform;
32    import jalview.viewmodel.ViewportListenerI;
33    import jalview.viewmodel.ViewportRanges;
34   
35    import java.awt.Color;
36    import java.awt.Dimension;
37    import java.awt.Font;
38    import java.awt.FontMetrics;
39    import java.awt.Graphics;
40    import java.awt.Image;
41    import java.awt.MenuItem;
42    import java.awt.Panel;
43    import java.awt.PopupMenu;
44    import java.awt.event.ActionEvent;
45    import java.awt.event.ActionListener;
46    import java.awt.event.AdjustmentEvent;
47    import java.awt.event.AdjustmentListener;
48    import java.awt.event.InputEvent;
49    import java.awt.event.MouseEvent;
50    import java.awt.event.MouseListener;
51    import java.awt.event.MouseMotionListener;
52    import java.beans.PropertyChangeEvent;
53   
 
54    public class AnnotationPanel extends Panel
55    implements AwtRenderPanelI, AdjustmentListener, ActionListener,
56    MouseListener, MouseMotionListener, ViewportListenerI
57    {
58    AlignViewport av;
59   
60    AlignmentPanel ap;
61   
62    int activeRow = -1;
63   
64    final String HELIX = "Helix";
65   
66    final String SHEET = "Sheet";
67   
68    /**
69    * For RNA secondary structure "stems" aka helices
70    */
71    final String STEM = "RNA Helix";
72   
73    final String LABEL = "Label";
74   
75    final String REMOVE = "Remove Annotation";
76   
77    final String COLOUR = "Colour";
78   
79    final Color HELIX_COLOUR = Color.red.darker();
80   
81    final Color SHEET_COLOUR = Color.green.darker().darker();
82   
83    Image image;
84   
85    Graphics gg;
86   
87    FontMetrics fm;
88   
89    int imgWidth = 0;
90   
91    boolean fastPaint = false;
92   
93    // Used For mouse Dragging and resizing graphs
94    int graphStretch = -1;
95   
96    int graphStretchY = -1;
97   
98    boolean mouseDragging = false;
99   
100    public static int GRAPH_HEIGHT = 40;
101   
102    // boolean MAC = false;
103   
104    public final AnnotationRenderer renderer;
105   
 
106  0 toggle public AnnotationPanel(AlignmentPanel ap)
107    {
108  0 new jalview.util.Platform();
109    // MAC = Platform.isAMac();
110  0 this.ap = ap;
111  0 av = ap.av;
112  0 setLayout(null);
113  0 int height = adjustPanelHeight();
114  0 ap.apvscroll.setValues(0, getSize().height, 0, height);
115   
116  0 addMouseMotionListener(this);
117   
118  0 addMouseListener(this);
119   
120    // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this );
121  0 renderer = new AnnotationRenderer();
122   
123  0 av.getRanges().addPropertyChangeListener(this);
124    }
125   
 
126  0 toggle public AnnotationPanel(AlignViewport av)
127    {
128  0 this.av = av;
129  0 renderer = new AnnotationRenderer();
130   
131    }
132   
 
133  0 toggle @Override
134    public void adjustmentValueChanged(AdjustmentEvent evt)
135    {
136    }
137   
138    /**
139    * DOCUMENT ME!
140    *
141    * @param evt
142    * DOCUMENT ME!
143    */
 
144  0 toggle @Override
145    public void actionPerformed(ActionEvent evt)
146    {
147  0 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
148  0 if (aa == null)
149    {
150  0 return;
151    }
152  0 Annotation[] anot = aa[activeRow].annotations;
153   
154  0 if (anot.length < av.getColumnSelection().getMax())
155    {
156  0 Annotation[] temp = new Annotation[av.getColumnSelection().getMax()
157    + 2];
158  0 System.arraycopy(anot, 0, temp, 0, anot.length);
159  0 anot = temp;
160  0 aa[activeRow].annotations = anot;
161    }
162   
163  0 String label = "";
164  0 if (av.getColumnSelection() != null
165    && !av.getColumnSelection().isEmpty()
166    && anot[av.getColumnSelection().getMin()] != null)
167    {
168  0 label = anot[av.getColumnSelection().getMin()].displayCharacter;
169    }
170   
171  0 if (evt.getActionCommand().equals(REMOVE))
172    {
173  0 for (int index : av.getColumnSelection().getSelected())
174    {
175  0 if (av.getAlignment().getHiddenColumns().isVisible(index))
176    {
177  0 anot[index] = null;
178    }
179    }
180    }
181  0 else if (evt.getActionCommand().equals(LABEL))
182    {
183  0 label = enterLabel(label, "Enter Label");
184   
185  0 if (label == null)
186    {
187  0 return;
188    }
189   
190  0 if ((label.length() > 0) && !aa[activeRow].hasText)
191    {
192  0 aa[activeRow].hasText = true;
193    }
194   
195  0 for (int index : av.getColumnSelection().getSelected())
196    {
197    // TODO: JAL-2001 - provide a fast method to list visible selected
198    // columns
199  0 if (!av.getAlignment().getHiddenColumns().isVisible(index))
200    {
201  0 continue;
202    }
203   
204  0 if (anot[index] == null)
205    {
206  0 anot[index] = new Annotation(label, "", ' ', 0);
207    }
208   
209  0 anot[index].displayCharacter = label;
210    }
211    }
212  0 else if (evt.getActionCommand().equals(COLOUR))
213    {
214  0 UserDefinedColours udc = new UserDefinedColours(this, Color.black,
215    ap.alignFrame);
216   
217  0 Color col = udc.getColor();
218   
219  0 for (int index : av.getColumnSelection().getSelected())
220    {
221  0 if (!av.getAlignment().getHiddenColumns().isVisible(index))
222    {
223  0 continue;
224    }
225   
226  0 if (anot[index] == null)
227    {
228  0 anot[index] = new Annotation("", "", ' ', 0);
229    }
230   
231  0 anot[index].colour = col;
232    }
233    }
234    else
235    // HELIX OR SHEET
236    {
237  0 char type = 0;
238  0 String symbol = "\u03B1";
239   
240  0 if (evt.getActionCommand().equals(HELIX))
241    {
242  0 type = 'H';
243    }
244  0 else if (evt.getActionCommand().equals(SHEET))
245    {
246  0 type = 'E';
247  0 symbol = "\u03B2";
248    }
249   
250    // Added by LML to color stems
251  0 else if (evt.getActionCommand().equals(STEM))
252    {
253  0 type = 'S';
254  0 int column = av.getColumnSelection().getSelectedRanges().get(0)[0];
255  0 symbol = aa[activeRow].getDefaultRnaHelixSymbol(column);
256    }
257   
258  0 if (!aa[activeRow].hasIcons)
259    {
260  0 aa[activeRow].hasIcons = true;
261    }
262   
263  0 label = enterLabel(symbol, "Enter Label");
264   
265  0 if (label == null)
266    {
267  0 return;
268    }
269   
270  0 if ((label.length() > 0) && !aa[activeRow].hasText)
271    {
272  0 aa[activeRow].hasText = true;
273  0 if (evt.getActionCommand().equals(STEM))
274    {
275  0 aa[activeRow].showAllColLabels = true;
276    }
277    }
278   
279  0 for (int index : av.getColumnSelection().getSelected())
280    {
281  0 if (!av.getAlignment().getHiddenColumns().isVisible(index))
282    {
283  0 continue;
284    }
285   
286  0 if (anot[index] == null)
287    {
288  0 anot[index] = new Annotation(label, "", type, 0);
289    }
290   
291  0 anot[index].secondaryStructure = type != 'S' ? type
292  0 : label.length() == 0 ? ' ' : label.charAt(0);
293  0 anot[index].displayCharacter = label;
294    }
295    }
296   
297  0 av.getAlignment().validateAnnotation(aa[activeRow]);
298   
299  0 ap.alignmentChanged();
300  0 adjustPanelHeight();
301  0 repaint();
302   
303  0 return;
304    }
305   
 
306  0 toggle String enterLabel(String text, String label)
307    {
308  0 EditNameDialog dialog = new EditNameDialog(text, null, label, null,
309    ap.alignFrame, "Enter Label", 400, 200, true);
310   
311  0 if (dialog.accept)
312    {
313  0 return dialog.getName();
314    }
315    else
316    {
317  0 return null;
318    }
319    }
320   
 
321  0 toggle @Override
322    public void mousePressed(MouseEvent evt)
323    {
324  0 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
325  0 if (aa == null)
326    {
327  0 return;
328    }
329   
330  0 int height = -scrollOffset;
331  0 activeRow = -1;
332   
333  0 for (int i = 0; i < aa.length; i++)
334    {
335  0 if (aa[i].visible)
336    {
337  0 height += aa[i].height;
338    }
339   
340  0 if (evt.getY() < height)
341    {
342  0 if (aa[i].editable)
343    {
344  0 activeRow = i;
345    }
346  0 else if (aa[i].graph != AlignmentAnnotation.NO_GRAPH)
347    {
348    // Stretch Graph
349  0 graphStretch = i;
350  0 graphStretchY = evt.getY();
351    }
352   
353  0 break;
354    }
355    }
356   
357  0 if ((evt.getModifiersEx()
358    & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK
359    && activeRow != -1)
360    {
361  0 if (av.getColumnSelection() == null
362    || av.getColumnSelection().isEmpty())
363    {
364  0 return;
365    }
366   
367  0 PopupMenu pop = new PopupMenu(
368    MessageManager.getString("label.structure_type"));
369  0 MenuItem item;
370   
371  0 if (av.getAlignment().isNucleotide())
372    {
373  0 item = new MenuItem(STEM);
374  0 item.addActionListener(this);
375  0 pop.add(item);
376    }
377    else
378    {
379  0 item = new MenuItem(HELIX);
380  0 item.addActionListener(this);
381  0 pop.add(item);
382  0 item = new MenuItem(SHEET);
383  0 item.addActionListener(this);
384  0 pop.add(item);
385    }
386  0 item = new MenuItem(LABEL);
387  0 item.addActionListener(this);
388  0 pop.add(item);
389  0 item = new MenuItem(COLOUR);
390  0 item.addActionListener(this);
391  0 pop.add(item);
392  0 item = new MenuItem(REMOVE);
393  0 item.addActionListener(this);
394  0 pop.add(item);
395  0 ap.alignFrame.add(pop);
396  0 pop.show(this, evt.getX(), evt.getY());
397   
398  0 return;
399    }
400   
401  0 ap.scalePanel.mousePressed(evt);
402    }
403   
 
404  0 toggle @Override
405    public void mouseReleased(MouseEvent evt)
406    {
407  0 graphStretch = -1;
408  0 graphStretchY = -1;
409  0 mouseDragging = false;
410  0 if (needValidating)
411    {
412  0 ap.validate();
413  0 needValidating = false;
414    }
415  0 ap.scalePanel.mouseReleased(evt);
416    }
417   
 
418  0 toggle @Override
419    public void mouseClicked(MouseEvent evt)
420    {
421    }
422   
423    boolean needValidating = false;
424   
 
425  0 toggle @Override
426    public void mouseDragged(MouseEvent evt)
427    {
428  0 if (graphStretch > -1)
429    {
430  0 av.getAlignment()
431    .getAlignmentAnnotation()[graphStretch].graphHeight += graphStretchY
432    - evt.getY();
433  0 if (av.getAlignment()
434    .getAlignmentAnnotation()[graphStretch].graphHeight < 0)
435    {
436  0 av.getAlignment()
437    .getAlignmentAnnotation()[graphStretch].graphHeight = 0;
438    }
439  0 graphStretchY = evt.getY();
440  0 av.calcPanelHeight();
441  0 needValidating = true;
442    // TODO: only update overview visible geometry
443  0 ap.paintAlignment(true, false);
444    }
445    else
446    {
447  0 ap.scalePanel.mouseDragged(evt);
448    }
449    }
450   
 
451  0 toggle @Override
452    public void mouseMoved(MouseEvent evt)
453    {
454  0 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
455  0 if (aa == null)
456    {
457  0 return;
458    }
459   
460  0 int row = -1;
461  0 int height = -scrollOffset;
462  0 for (int i = 0; i < aa.length; i++)
463    {
464   
465  0 if (aa[i].visible)
466    {
467  0 height += aa[i].height;
468    }
469   
470  0 if (evt.getY() < height)
471    {
472  0 row = i;
473  0 break;
474    }
475    }
476   
477  0 int column = evt.getX() / av.getCharWidth()
478    + av.getRanges().getStartRes();
479   
480  0 if (av.hasHiddenColumns())
481    {
482  0 column = av.getAlignment().getHiddenColumns()
483    .visibleToAbsoluteColumn(column);
484    }
485   
486  0 if (row > -1 && column < aa[row].annotations.length
487    && aa[row].annotations[column] != null)
488    {
489  0 StringBuilder text = new StringBuilder();
490  0 text.append(MessageManager.getString("label.column")).append(" ")
491    .append(column + 1);
492  0 String description = aa[row].annotations[column].description;
493  0 if (description != null && description.length() > 0)
494    {
495  0 text.append(" ").append(description);
496    }
497   
498    /*
499    * if the annotation is sequence-specific, show the sequence number
500    * in the alignment, and (if not a gap) the residue and position
501    */
502  0 SequenceI seqref = aa[row].sequenceRef;
503  0 if (seqref != null)
504    {
505  0 int seqIndex = av.getAlignment().findIndex(seqref);
506  0 if (seqIndex != -1)
507    {
508  0 text.append(", ")
509    .append(MessageManager.getString("label.sequence"))
510    .append(" ").append(seqIndex + 1);
511  0 char residue = seqref.getCharAt(column);
512  0 if (!Comparison.isGap(residue))
513    {
514  0 text.append(" ");
515  0 String name;
516  0 if (av.getAlignment().isNucleotide())
517    {
518  0 name = ResidueProperties.nucleotideName
519    .get(String.valueOf(residue));
520  0 text.append(" Nucleotide: ")
521  0 .append(name != null ? name : residue);
522    }
523    else
524    {
525  0 name = 'X' == residue ? "X"
526  0 : ('*' == residue ? "STOP"
527    : ResidueProperties.aa2Triplet
528    .get(String.valueOf(residue)));
529  0 text.append(" Residue: ")
530  0 .append(name != null ? name : residue);
531    }
532  0 int residuePos = seqref.findPosition(column);
533  0 text.append(" (").append(residuePos).append(")");
534    // int residuePos = seqref.findPosition(column);
535    // text.append(residue).append(" (")
536    // .append(residuePos).append(")");
537    }
538    }
539    }
540   
541  0 ap.alignFrame.statusBar.setText(text.toString());
542    }
543    }
544   
 
545  0 toggle @Override
546    public void mouseEntered(MouseEvent evt)
547    {
548  0 ap.scalePanel.mouseEntered(evt);
549    }
550   
 
551  0 toggle @Override
552    public void mouseExited(MouseEvent evt)
553    {
554  0 ap.scalePanel.mouseExited(evt);
555    }
556   
 
557  0 toggle public int adjustPanelHeight()
558    {
559  0 return adjustPanelHeight(true);
560    }
561   
 
562  0 toggle public int adjustPanelHeight(boolean repaint)
563    {
564  0 int height = av.calcPanelHeight();
565  0 this.setSize(new Dimension(getSize().width, height));
566  0 if (repaint)
567    {
568  0 repaint();
569    }
570  0 return height;
571    }
572   
573    /**
574    * calculate the height for visible annotation, revalidating bounds where
575    * necessary ABSTRACT GUI METHOD
576    *
577    * @return total height of annotation
578    */
579   
 
580  0 toggle public void addEditableColumn(int i)
581    {
582  0 if (activeRow == -1)
583    {
584  0 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
585  0 if (aa == null)
586    {
587  0 return;
588    }
589   
590  0 for (int j = 0; j < aa.length; j++)
591    {
592  0 if (aa[j].editable)
593    {
594  0 activeRow = j;
595  0 break;
596    }
597    }
598    }
599    }
600   
 
601  0 toggle @Override
602    public void update(Graphics g)
603    {
604  0 paint(g);
605    }
606   
 
607  0 toggle @Override
608    public void paint(Graphics g)
609    {
610  0 Dimension d = getSize();
611  0 imgWidth = d.width;
612    // (av.endRes - av.startRes + 1) * av.charWidth;
613  0 if (imgWidth < 1 || d.height < 1)
614    {
615  0 return;
616    }
617  0 if (image == null || imgWidth != image.getWidth(this)
618    || d.height != image.getHeight(this))
619    {
620  0 image = createImage(imgWidth, d.height);
621  0 gg = image.getGraphics();
622  0 gg.setFont(av.getFont());
623  0 fm = gg.getFontMetrics();
624  0 fastPaint = false;
625    }
626   
627  0 if (fastPaint)
628    {
629  0 g.drawImage(image, 0, 0, this);
630  0 fastPaint = false;
631  0 return;
632    }
633   
634  0 gg.setColor(Color.white);
635  0 gg.fillRect(0, 0, getSize().width, getSize().height);
636  0 drawComponent(gg, av.getRanges().getStartRes(),
637    av.getRanges().getEndRes() + 1);
638   
639  0 g.drawImage(image, 0, 0, this);
640    }
641   
 
642  0 toggle public void fastPaint(int horizontal)
643    {
644  0 if (horizontal == 0 || gg == null
645    || av.getAlignment().getAlignmentAnnotation() == null
646    || av.getAlignment().getAlignmentAnnotation().length < 1)
647    {
648  0 repaint();
649  0 return;
650    }
651   
652  0 gg.copyArea(0, 0, imgWidth, getSize().height,
653    -horizontal * av.getCharWidth(), 0);
654  0 int sr = av.getRanges().getStartRes(),
655    er = av.getRanges().getEndRes() + 1, transX = 0;
656   
657  0 if (horizontal > 0) // scrollbar pulled right, image to the left
658    {
659  0 transX = (er - sr - horizontal) * av.getCharWidth();
660  0 sr = er - horizontal;
661    }
662  0 else if (horizontal < 0)
663    {
664  0 er = sr - horizontal;
665    }
666   
667  0 gg.translate(transX, 0);
668   
669  0 drawComponent(gg, sr, er);
670   
671  0 gg.translate(-transX, 0);
672   
673  0 fastPaint = true;
674  0 repaint();
675    }
676   
677    /**
678    * DOCUMENT ME!
679    *
680    * @param g
681    * DOCUMENT ME!
682    * @param startRes
683    * DOCUMENT ME!
684    * @param endRes
685    * DOCUMENT ME!
686    */
 
687  0 toggle public void drawComponent(Graphics g, int startRes, int endRes)
688    {
689  0 Font ofont = av.getFont();
690  0 g.setFont(ofont);
691   
692  0 g.setColor(Color.white);
693  0 g.fillRect(0, 0, (endRes - startRes) * av.getCharWidth(),
694    getSize().height);
695   
696  0 if (fm == null)
697    {
698  0 fm = g.getFontMetrics();
699    }
700   
701  0 if ((av.getAlignment().getAlignmentAnnotation() == null)
702    || (av.getAlignment().getAlignmentAnnotation().length < 1))
703    {
704  0 g.setColor(Color.white);
705  0 g.fillRect(0, 0, getSize().width, getSize().height);
706  0 g.setColor(Color.black);
707  0 if (av.validCharWidth)
708    {
709  0 g.drawString(MessageManager
710    .getString("label.alignment_has_no_annotations"), 20, 15);
711    }
712   
713  0 return;
714    }
715  0 g.translate(0, -scrollOffset);
716  0 renderer.drawComponent(this, av, g, activeRow, startRes, endRes);
717  0 g.translate(0, +scrollOffset);
718    }
719   
720    int scrollOffset = 0;
721   
 
722  0 toggle public void setScrollOffset(int value, boolean repaint)
723    {
724  0 scrollOffset = value;
725  0 if (repaint)
726    {
727  0 repaint();
728    }
729    }
730   
 
731  0 toggle @Override
732    public FontMetrics getFontMetrics()
733    {
734  0 return fm;
735    }
736   
 
737  0 toggle @Override
738    public Image getFadedImage()
739    {
740  0 return image;
741    }
742   
 
743  0 toggle @Override
744    public int getFadedImageWidth()
745    {
746  0 return imgWidth;
747    }
748   
749    private int[] bounds = new int[2];
750   
 
751  0 toggle @Override
752    public int[] getVisibleVRange()
753    {
754  0 if (ap != null && ap.alabels != null)
755    {
756  0 int sOffset = -ap.alabels.scrollOffset;
757  0 int visHeight = sOffset + ap.annotationPanelHolder.getHeight();
758  0 bounds[0] = sOffset;
759  0 bounds[1] = visHeight;
760  0 return bounds;
761    }
762    else
763    {
764  0 return null;
765    }
766    }
767   
 
768  0 toggle @Override
769    public void propertyChange(PropertyChangeEvent evt)
770    {
771    // Respond to viewport range changes (e.g. alignment panel was scrolled)
772    // Both scrolling and resizing change viewport ranges: scrolling changes
773    // both start and end points, but resize only changes end values.
774    // Here we only want to fastpaint on a scroll, with resize using a normal
775    // paint, so scroll events are identified as changes to the horizontal or
776    // vertical start value.
777  0 if (evt.getPropertyName().equals(ViewportRanges.STARTRES))
778    {
779  0 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
780    }
781  0 else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
782    {
783  0 fastPaint(((int[]) evt.getNewValue())[0]
784    - ((int[]) evt.getOldValue())[0]);
785    }
786  0 else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
787    {
788  0 repaint();
789    }
790    }
791    }