Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.gui

File IdCanvas.java

 

Coverage histogram

../../img/srcFileCovDistChart9.png
13% of files have more coverage

Code metrics

84
185
16
1
647
395
73
0.39
11.56
16
4.56

Classes

Class Line # Actions
IdCanvas 47 185 73
0.8631578786.3%
 

Contributing tests

This file is covered by 202 tests. .

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.gui;
22   
23    import java.awt.BorderLayout;
24    import java.awt.Color;
25    import java.awt.Dimension;
26    import java.awt.Font;
27    import java.awt.FontMetrics;
28    import java.awt.Graphics;
29    import java.awt.Graphics2D;
30    import java.awt.RenderingHints;
31    import java.awt.image.BufferedImage;
32    import java.beans.PropertyChangeEvent;
33    import java.util.List;
34   
35    import javax.swing.JPanel;
36   
37    import jalview.datamodel.SequenceI;
38    import jalview.viewmodel.ViewportListenerI;
39    import jalview.viewmodel.ViewportRanges;
40   
41    /**
42    * DOCUMENT ME!
43    *
44    * @author $author$
45    * @version $Revision$
46    */
 
47    public class IdCanvas extends JPanel implements ViewportListenerI
48    {
49    protected AlignViewport av;
50   
51    protected boolean showScores = true;
52   
53    protected int maxIdLength = -1;
54   
55    protected String maxIdStr = null;
56   
57    BufferedImage image;
58   
59    // Graphics2D gg;
60   
61    int imgHeight = 0;
62   
63    boolean fastPaint = false;
64   
65    List<SequenceI> searchResults;
66   
67    AnnotationPanel ap;
68   
69    private Font idfont;
70   
71    /**
72    * Creates a new IdCanvas object.
73    *
74    * @param av
75    * DOCUMENT ME!
76    */
 
77  478 toggle public IdCanvas(AlignViewport av)
78    {
79  478 setLayout(new BorderLayout());
80  478 this.av = av;
81  478 PaintRefresher.Register(this, av.getSequenceSetId());
82  478 av.getRanges().addPropertyChangeListener(this);
83    }
84   
85    /**
86    * DOCUMENT ME!
87    *
88    * @param g
89    * DOCUMENT ME!
90    * @param hiddenRows
91    * true - check and display hidden row marker if need be
92    * @param s
93    * DOCUMENT ME!
94    * @param i
95    * DOCUMENT ME!
96    * @param starty
97    * DOCUMENT ME!
98    * @param ypos
99    * DOCUMENT ME!
100    */
 
101  6788 toggle public void drawIdString(Graphics2D g, boolean hiddenRows, SequenceI s,
102    int i, int starty, int ypos)
103    {
104  6788 int xPos = 0;
105  6788 int panelWidth = getWidth();
106  6788 int charHeight = av.getCharHeight();
107   
108  6788 if ((searchResults != null) && searchResults.contains(s))
109    {
110  0 g.setColor(Color.black);
111  0 g.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(),
112    charHeight);
113  0 g.setColor(Color.white);
114    }
115  6788 else if ((av.getSelectionGroup() != null)
116    && av.getSelectionGroup().getSequences(null).contains(s))
117    {
118  0 g.setColor(Color.lightGray);
119  0 g.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(),
120    charHeight);
121  0 g.setColor(Color.white);
122    }
123    else
124    {
125  6788 g.setColor(av.getSequenceColour(s));
126  6788 g.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(),
127    charHeight);
128  6788 g.setColor(Color.black);
129    }
130   
131  6788 if (av.isRightAlignIds())
132    {
133  0 FontMetrics fm = g.getFontMetrics();
134  0 xPos = panelWidth
135    - fm.stringWidth(s.getDisplayId(av.getShowJVSuffix())) - 4;
136    }
137   
138  6788 g.drawString(s.getDisplayId(av.getShowJVSuffix()), xPos,
139    (((i - starty + 1) * charHeight) + ypos) - (charHeight / 5));
140   
141  6788 if (hiddenRows && av.getShowHiddenMarkers())
142    {
143  612 drawMarker(g, av, i, starty, ypos);
144    }
145   
146    }
147   
148    /**
149    * DOCUMENT ME!
150    *
151    * @param vertical
152    * DOCUMENT ME!
153    */
 
154  43 toggle public void fastPaint(int vertical)
155    {
156   
157    /*
158    * for now, not attempting fast paint of wrapped ids...
159    */
160  43 if (image == null || av.getWrapAlignment())
161    {
162  21 repaint();
163   
164  21 return;
165    }
166   
167  22 ViewportRanges ranges = av.getRanges();
168   
169  22 Graphics2D gg = image.createGraphics();
170  22 gg.copyArea(0, 0, getWidth(), imgHeight, 0,
171    -vertical * av.getCharHeight());
172   
173  22 int ss = ranges.getStartSeq();
174  22 int es = ranges.getEndSeq();
175  22 int transY = 0;
176   
177  22 if (vertical > 0) // scroll down
178    {
179  11 ss = es - vertical;
180   
181  11 if (ss < ranges.getStartSeq())
182    { // ie scrolling too fast, more than a page at a time
183  4 ss = ranges.getStartSeq();
184    }
185    else
186    {
187  7 transY = imgHeight - ((vertical + 1) * av.getCharHeight());
188    }
189    }
190  11 else if (vertical < 0) // scroll up
191    {
192  11 es = ss - vertical;
193   
194  11 if (es > ranges.getEndSeq())
195    {
196  0 es = ranges.getEndSeq();
197    }
198    }
199   
200  22 gg.translate(0, transY);
201   
202  22 drawIds(gg, av, ss, es, searchResults, true, getWidth());
203   
204  22 gg.translate(0, -transY);
205   
206  22 gg.dispose();
207   
208  22 fastPaint = true;
209   
210    // Call repaint on alignment panel so that repaints from other alignment
211    // panel components can be aggregated. Otherwise performance of the overview
212    // window and others may be adversely affected.
213  22 av.getAlignPanel().repaint();
214    }
215   
216    /**
217    * DOCUMENT ME!
218    *
219    * @param g
220    * DOCUMENT ME!
221    */
 
222  2997 toggle @Override
223    public void paintComponent(Graphics g)
224    {
225  2997 g.setColor(Color.white);
226  2997 g.fillRect(0, 0, getWidth(), getHeight());
227   
228  2997 if (fastPaint)
229    {
230  6 fastPaint = false;
231  6 g.drawImage(image, 0, 0, this);
232   
233  6 return;
234    }
235   
236  2991 int oldHeight = imgHeight;
237   
238  2991 imgHeight = getHeight();
239  2991 imgHeight -= (imgHeight % av.getCharHeight());
240   
241  2991 if (imgHeight < 1)
242    {
243  0 return;
244    }
245   
246  2991 if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
247    {
248  384 image = new BufferedImage(getWidth(), imgHeight,
249    BufferedImage.TYPE_INT_RGB);
250    }
251   
252  2991 Graphics2D gg = image.createGraphics();
253   
254    // Fill in the background
255  2991 gg.setColor(Color.white);
256  2991 gg.fillRect(0, 0, getWidth(), imgHeight);
257   
258  2991 drawIds(gg, av, av.getRanges().getStartSeq(),
259    av.getRanges().getEndSeq(), searchResults, true, getWidth());
260   
261  2991 gg.dispose();
262   
263  2991 g.drawImage(image, 0, 0, this);
264    }
265   
266    /**
267    * Draws sequence ids from sequence index startSeq to endSeq (inclusive), with
268    * the font and other display settings configured on the viewport. Ids of
269    * sequences included in the selection are coloured grey, otherwise the
270    * current id colour for the sequence id is used.
271    *
272    * @param g
273    * @param alignViewport
274    * @param startSeq
275    * @param endSeq
276    * @param selection
277    * @param forGUI
278    * when false rendering for print
279    * @param panelWidth
280    * width used to calculate righthand margin - usually
281    * idCanvas.getWidth()
282    *
283    */
 
284  3051 toggle void drawIds(Graphics2D g, AlignViewport alignViewport,
285    final int startSeq, final int endSeq, List<SequenceI> selection,
286    boolean forGUI, int panelWidth)
287    {
288  3051 Font font = alignViewport.getFont();
289  3051 if (alignViewport.isSeqNameItalics())
290    {
291  3051 setIdfont(new Font(font.getName(), Font.ITALIC, font.getSize()));
292    }
293    else
294    {
295  0 setIdfont(font);
296    }
297   
298  3051 g.setFont(getIdfont());
299  3051 FontMetrics fm = g.getFontMetrics();
300   
301  3051 if (alignViewport.antiAlias)
302    {
303  828 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
304    RenderingHints.VALUE_ANTIALIAS_ON);
305    }
306   
307  3051 Color currentColor = Color.white;
308  3051 Color currentTextColor = Color.black;
309   
310  3051 boolean hasHiddenRows = alignViewport.hasHiddenRows();
311   
312  3051 if (alignViewport.getWrapAlignment())
313    {
314  692 drawIdsWrapped(g, alignViewport, startSeq, getHeight(),
315  692 isManuallyAdjusted() ? panelWidth : -1, forGUI);
316  692 return;
317    }
318   
319    // Now draw the id strings
320  2359 int xPos = 0;
321   
322    // Now draw the id strings
323  13644 for (int i = startSeq; i <= endSeq; i++)
324    {
325  11285 SequenceI sequence = alignViewport.getAlignment().getSequenceAt(i);
326   
327  11285 if (sequence == null)
328    {
329  0 continue;
330    }
331   
332  11285 if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
333    {
334  4204 g.setFont(getHiddenFont(sequence, alignViewport));
335  4204 fm = g.getFontMetrics();
336    }
337   
338    // Selected sequence colours
339  11285 if (selection != null && selection.contains(sequence))
340    {
341  0 currentColor = Color.black;
342  0 currentTextColor = Color.white;
343    }
344  11285 else if ((alignViewport.getSelectionGroup() != null) && alignViewport
345    .getSelectionGroup().getSequences(null).contains(sequence))
346    {
347  23 currentColor = Color.lightGray;
348  23 currentTextColor = Color.black;
349    }
350    else
351    {
352  11262 currentColor = alignViewport.getSequenceColour(sequence);
353  11262 currentTextColor = Color.black;
354    }
355   
356  11285 g.setColor(currentColor);
357   
358  11285 int charHeight = alignViewport.getCharHeight();
359  11285 g.fillRect(0, (i - startSeq) * charHeight, getWidth(), charHeight);
360   
361  11285 g.setColor(currentTextColor);
362   
363  11285 String string = sequence
364    .getDisplayId(alignViewport.getShowJVSuffix());
365   
366  11285 if (alignViewport.isRightAlignIds())
367    {
368  0 xPos = panelWidth - fm.stringWidth(string) - 4;
369    }
370   
371  11285 g.drawString(string, xPos,
372    (((i - startSeq) * charHeight) + charHeight)
373    - (charHeight / 5));
374   
375  11285 if (hasHiddenRows && av.getShowHiddenMarkers())
376    {
377  4204 drawMarker(g, alignViewport, i, startSeq, 0);
378    }
379    }
380    }
381   
382    /**
383    * Draws sequence ids, and annotation labels if annotations are shown, in
384    * wrapped mode
385    *
386    * @param g
387    * @param alignViewport
388    * @param startSeq
389    */
 
390  0 toggle void drawIdsWrapped(Graphics2D g, AlignViewport alignViewport,
391    int startSeq, int pageHeight)
392    {
393  0 drawIdsWrapped(g, alignViewport, startSeq, pageHeight, -1, true);
394    }
395   
396    /**
397    * render sequence IDs and annotation labels when wrapped - without GUI junk
398    *
399    * @param g
400    * @param av2
401    * @param startSeq
402    * @param totalHeight
403    */
 
404  0 toggle public void drawIdsWrappedNoGUI(Graphics2D g, AlignViewport av2,
405    int startSeq, int totalHeight)
406    {
407  0 drawIdsWrapped(g, av2, startSeq, totalHeight, -1, false);
408    }
409   
 
410  692 toggle public void drawIdsWrapped(Graphics2D g, AlignViewport alignViewport,
411    int startSeq, int pageHeight, int idWidth, boolean forGUI)
412    {
413  692 int alignmentWidth = alignViewport.getAlignment().getWidth();
414  692 final int alheight = alignViewport.getAlignment().getHeight();
415   
416    /*
417    * assumption: SeqCanvas.calculateWrappedGeometry has been called
418    */
419  692 SeqCanvas seqCanvas = alignViewport.getAlignPanel()
420    .getSeqPanel().seqCanvas;
421   
422  692 final int charHeight = alignViewport.getCharHeight();
423   
424  692 AnnotationLabels labels = null;
425  692 if (alignViewport.isShowAnnotation())
426    {
427    // in wrapped mode, no alignPanel reference is available
428    // FIXME: make the renderer not create a new object in wrapped mode
429    // everytime!
430  692 labels = new AnnotationLabels(alignViewport);
431    }
432   
433  692 ViewportRanges ranges = alignViewport.getRanges();
434   
435  692 int rowSize = ranges.getViewportWidth();
436   
437    /*
438    * draw repeating sequence ids until out of sequence data or
439    * out of visible space, whichever comes first
440    */
441  692 boolean hasHiddenRows = alignViewport.hasHiddenRows();
442  692 int ypos = seqCanvas.wrappedSpaceAboveAlignment;
443  692 int rowStartRes = ranges.getStartRes();
444  1384 while ((ypos <= pageHeight) && (rowStartRes < alignmentWidth))
445    {
446  7480 for (int i = startSeq; i < alheight; i++)
447    {
448  6788 SequenceI s = alignViewport.getAlignment().getSequenceAt(i);
449  6788 if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
450    {
451  612 g.setFont(getHiddenFont(s, alignViewport));
452    }
453    else
454    {
455  6176 g.setFont(getIdfont());
456    }
457  6788 drawIdString(g, hasHiddenRows, s, i, 0, ypos);
458    }
459   
460  692 if (labels != null && alignViewport.isShowAnnotation())
461    {
462  692 int getWidth = getWidth();
463  692 int thisIdWidth = getWidth;
464  692 g.translate(0, ypos + (alheight * charHeight));
465  692 if (!isManuallyAdjusted())
466    {
467  386 int getAnnotationsIdWidth = labels.drawLabels(g, false, -1, false,
468    forGUI, null, false);
469  386 thisIdWidth = idWidth < 0 ? getAnnotationsIdWidth : idWidth;
470  386 if (thisIdWidth > getWidth)
471    {
472  383 this.setPreferredSize(
473    new Dimension(thisIdWidth, this.getHeight()));
474  383 this.repaint();
475  383 alignViewport.setIdWidth(thisIdWidth);
476    }
477    }
478  692 labels.drawComponent(g, false, thisIdWidth, forGUI);
479  692 g.translate(0, -ypos - (alheight * charHeight));
480    }
481   
482  692 ypos += seqCanvas.wrappedRepeatHeightPx;
483  692 rowStartRes += rowSize;
484    }
485    }
486   
487    /**
488    * Draws a marker (a blue right-pointing triangle) between sequences to
489    * indicate hidden sequences.
490    *
491    * @param g
492    * @param alignViewport
493    * @param seqIndex
494    * @param starty
495    * @param yoffset
496    */
 
497  4816 toggle void drawMarker(Graphics2D g, AlignViewport alignViewport, int seqIndex,
498    int starty, int yoffset)
499    {
500  4816 SequenceI[] hseqs = alignViewport.getAlignment()
501    .getHiddenSequences().hiddenSequences;
502    // Use this method here instead of calling hiddenSeq adjust
503    // 3 times.
504  4816 int hSize = hseqs.length;
505   
506  4816 int hiddenIndex = seqIndex;
507  4816 int lastIndex = seqIndex - 1;
508  4816 int nextIndex = seqIndex + 1;
509   
510  81872 for (int j = 0; j < hSize; j++)
511    {
512  77056 if (hseqs[j] != null)
513    {
514  16207 if (j - 1 < hiddenIndex)
515    {
516  182 hiddenIndex++;
517    }
518  16207 if (j - 1 < lastIndex)
519    {
520  6 lastIndex++;
521    }
522  16207 if (j - 1 < nextIndex)
523    {
524  4824 nextIndex++;
525    }
526    }
527    }
528   
529    /*
530    * are we below or above the hidden sequences?
531    */
532  4816 boolean below = (hiddenIndex > lastIndex + 1);
533  4816 boolean above = (nextIndex > hiddenIndex + 1);
534   
535  4816 g.setColor(Color.blue);
536  4816 int charHeight = av.getCharHeight();
537   
538    /*
539    * vertices of the triangle, below or above hidden seqs
540    */
541  4816 int[] xPoints = new int[] { getWidth() - charHeight,
542    getWidth() - charHeight, getWidth() };
543  4816 int yShift = seqIndex - starty;
544   
545  4816 if (below)
546    {
547  176 int[] yPoints = new int[] { yShift * charHeight + yoffset,
548    yShift * charHeight + yoffset + charHeight / 4,
549    yShift * charHeight + yoffset };
550  176 g.fillPolygon(xPoints, yPoints, 3);
551    }
552  4816 if (above)
553    {
554  652 yShift++;
555  652 int[] yPoints = new int[] { yShift * charHeight + yoffset,
556    yShift * charHeight + yoffset - charHeight / 4,
557    yShift * charHeight + yoffset };
558  652 g.fillPolygon(xPoints, yPoints, 3);
559    }
560    }
561   
562    /**
563    * Answers the standard sequence id font, or a bold font if the sequence is
564    * set as reference or a hidden group representative
565    *
566    * @param seq
567    * @param alignViewport
568    * @return
569    */
 
570  4816 toggle private Font getHiddenFont(SequenceI seq, AlignViewport alignViewport)
571    {
572  4816 if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
573    {
574  128 return new Font(av.getFont().getName(), Font.BOLD,
575    av.getFont().getSize());
576    }
577  4688 return getIdfont();
578    }
579   
580    /**
581    * DOCUMENT ME!
582    *
583    * @param list
584    * DOCUMENT ME!
585    */
 
586  0 toggle public void setHighlighted(List<SequenceI> list)
587    {
588  0 searchResults = list;
589  0 repaint();
590    }
591   
 
592  13915 toggle public Font getIdfont()
593    {
594  13915 return idfont;
595    }
596   
 
597  3051 toggle public void setIdfont(Font idfont)
598    {
599  3051 this.idfont = idfont;
600    }
601   
602    /**
603    * Respond to viewport range changes (e.g. alignment panel was scrolled). Both
604    * scrolling and resizing change viewport ranges. Scrolling changes both start
605    * and end points, but resize only changes end values. Here we only want to
606    * fastpaint on a scroll, with resize using a normal paint, so scroll events
607    * are identified as changes to the horizontal or vertical start value.
608    * <p>
609    * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll
610    * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is
611    * provided, but it generates a change of "startres" which does require an
612    * update here.
613    */
 
614  635 toggle @Override
615    public void propertyChange(PropertyChangeEvent evt)
616    {
617  635 String propertyName = evt.getPropertyName();
618  635 if (propertyName.equals(ViewportRanges.STARTSEQ)
619    || (av.getWrapAlignment()
620    && propertyName.equals(ViewportRanges.STARTRES)))
621    {
622  43 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
623    }
624  592 else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
625    {
626  0 fastPaint(((int[]) evt.getNewValue())[1]
627    - ((int[]) evt.getOldValue())[1]);
628    }
629  592 else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
630    {
631  0 repaint();
632    }
633    }
634   
635    private boolean manuallyAdjusted = false;
636   
 
637  5283 toggle public boolean isManuallyAdjusted()
638    {
639  5283 return manuallyAdjusted;
640    }
641   
 
642  88 toggle public void setManuallyAdjusted(boolean b)
643    {
644  88 manuallyAdjusted = b;
645    }
646   
647    }