Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.gui

File IdCanvas.java

 

Coverage histogram

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

Code metrics

84
200
17
1
682
415
77
0.38
11.76
17
4.53

Classes

Class Line # Actions
IdCanvas 47 200 77
0.8637873586.4%
 

Contributing tests

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