Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 17:03:17 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 243 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  2611 toggle public void drawIdString(Graphics2D g, boolean hiddenRows, SequenceI s,
103    int i, int starty, int ypos)
104    {
105  2611 int xPos = 0;
106  2611 int panelWidth = getWidth();
107  2611 int charHeight = av.getCharHeight();
108   
109  2611 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  2611 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  2611 g.setColor(av.getSequenceColour(s));
127  2611 g.fillRect(0, ((i - starty) * charHeight) + ypos, getWidth(),
128    charHeight);
129  2611 g.setColor(Color.black);
130    }
131   
132  2611 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  2611 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  2611 if (hiddenRows && av.getShowHiddenMarkers())
144    {
145  8 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  31 repaint();
165   
166  31 return;
167    }
168   
169  9 ViewportRanges ranges = av.getRanges();
170   
171  9 Graphics2D gg = image.createGraphics();
172  9 gg.copyArea(0, 0, getWidth(), imgHeight, 0,
173    -vertical * av.getCharHeight());
174   
175  9 int ss = ranges.getStartSeq();
176  9 int es = ranges.getEndSeq();
177  9 int transY = 0;
178   
179  9 if (vertical > 0) // scroll down
180    {
181  5 ss = es - vertical;
182   
183  5 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  2 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  9 gg.translate(0, transY);
203   
204  9 drawIds(gg, av, ss, es, searchResults, true, getWidth());
205   
206  9 gg.translate(0, -transY);
207   
208  9 gg.dispose();
209   
210  9 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  9 av.getAlignPanel().repaint();
216    }
217   
218    /**
219    * DOCUMENT ME!
220    *
221    * @param g
222    * DOCUMENT ME!
223    */
 
224  3061 toggle @Override
225    public void paintComponent(Graphics g)
226    {
227  3061 if (av.getAlignPanel().getHoldRepaint())
228    {
229  246 return;
230    }
231  2815 g.setColor(Color.white);
232  2815 g.fillRect(0, 0, getWidth(), getHeight());
233   
234  2815 if (allowFastPaint && fastPaint)
235    {
236  0 fastPaint = false;
237  0 g.drawImage(image, 0, 0, this);
238   
239  0 return;
240    }
241   
242  2815 int oldHeight = imgHeight;
243   
244  2815 imgHeight = getHeight();
245  2815 imgHeight -= (imgHeight % av.getCharHeight());
246   
247  2815 if (imgHeight < 1)
248    {
249  0 return;
250    }
251   
252  2815 if (oldHeight != imgHeight || image.getWidth(this) != getWidth())
253    {
254  563 image = new BufferedImage(getWidth(), imgHeight,
255    BufferedImage.TYPE_INT_RGB);
256    }
257   
258  2815 Graphics2D gg = image.createGraphics();
259   
260    // Fill in the background
261  2815 gg.setColor(Color.white);
262  2815 gg.fillRect(0, 0, getWidth(), imgHeight);
263   
264  2815 drawIds(gg, av, av.getRanges().getStartSeq(),
265    av.getRanges().getEndSeq(), searchResults, true, getWidth());
266   
267  2815 gg.dispose();
268   
269  2815 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  2855 toggle void drawIds(Graphics2D g, AlignViewport alignViewport,
291    final int startSeq, final int endSeq, List<SequenceI> selection,
292    boolean forGUI, int panelWidth)
293    {
294  2855 Font font = alignViewport.getFont();
295  2855 if (alignViewport.isSeqNameItalics())
296    {
297  2855 setIdfont(new Font(font.getName(), Font.ITALIC,
298    font.getSize()));
299    }
300    else
301    {
302  0 setIdfont(font);
303    }
304   
305  2855 g.setFont(getIdfont());
306  2855 FontMetrics fm = g.getFontMetrics();
307   
308  2855 if (alignViewport.antiAlias)
309    {
310  822 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
311    RenderingHints.VALUE_ANTIALIAS_ON);
312    }
313   
314  2855 Color currentColor = Color.white;
315  2855 Color currentTextColor = Color.black;
316   
317  2855 boolean hasHiddenRows = alignViewport.hasHiddenRows();
318   
319  2855 if (alignViewport.getWrapAlignment())
320    {
321  237 drawIdsWrapped(g, alignViewport, startSeq, getHeight(),
322  237 isManuallyAdjusted() ? panelWidth : -1, forGUI);
323  237 return;
324    }
325   
326    // Now draw the id strings
327  2618 int xPos = 0;
328   
329    // Now draw the id strings
330  17613 for (int i = startSeq; i <= endSeq; i++)
331    {
332  14995 SequenceI sequence = alignViewport.getAlignment().getSequenceAt(i);
333   
334  14995 if (sequence == null)
335    {
336  0 continue;
337    }
338   
339  14995 if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
340    {
341  271 g.setFont(getHiddenFont(sequence, alignViewport));
342  271 fm = g.getFontMetrics();
343    }
344   
345    // Selected sequence colours
346  14995 if (selection != null && selection.contains(sequence))
347    {
348  0 currentColor = Color.black;
349  0 currentTextColor = Color.white;
350    }
351  14995 else if ((alignViewport.getSelectionGroup() != null) && alignViewport
352    .getSelectionGroup().getSequences(null).contains(sequence))
353    {
354  53 currentColor = Color.lightGray;
355  53 currentTextColor = Color.black;
356    }
357    else
358    {
359  14942 currentColor = alignViewport.getSequenceColour(sequence);
360  14942 currentTextColor = Color.black;
361    }
362   
363  14995 g.setColor(currentColor);
364   
365  14995 int charHeight = alignViewport.getCharHeight();
366  14995 g.fillRect(0, (i - startSeq) * charHeight,
367    getWidth(), charHeight);
368   
369  14995 g.setColor(currentTextColor);
370   
371  14995 String string = sequence
372    .getDisplayId(alignViewport.getShowJVSuffix());
373   
374  14995 if (alignViewport.isRightAlignIds())
375    {
376  0 xPos = panelWidth - fm.stringWidth(string) - 4;
377    }
378   
379  14995 g.drawString(string, xPos, (((i - startSeq) * charHeight) + charHeight)
380    - (charHeight / 5));
381   
382  14995 if (hasHiddenRows && av.getShowHiddenMarkers())
383    {
384  271 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  251 toggle public void drawIdsWrapped(Graphics2D g, AlignViewport alignViewport,
418    int startSeq, int pageHeight, int idWidth, boolean forGUI)
419    {
420  251 int alignmentWidth = alignViewport.getAlignment().getWidth();
421  251 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  251 SeqCanvas seqCanvas = alignViewport.getAlignPanel()
434    .getSeqPanel().seqCanvas;
435    // ...better: let's call it now
436  251 seqCanvas.calculateWrappedGeometry();
437   
438  251 final int charHeight = alignViewport.getCharHeight();
439   
440  251 AnnotationLabels labels = null;
441  251 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  19 if (ap == null)
449    {
450  5 ap = new AnnotationPanel(alignViewport);
451    }
452  19 labels = new AnnotationLabels(alignViewport);
453    }
454   
455  251 ViewportRanges ranges = alignViewport.getRanges();
456   
457  251 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  251 boolean hasHiddenRows = alignViewport.hasHiddenRows();
464  251 int ypos = seqCanvas.wrappedSpaceAboveAlignment;
465  251 int rowStartRes = ranges.getStartRes();
466  2542 while ((ypos <= pageHeight) && (rowStartRes < alignmentWidth))
467    {
468  4902 for (int i = startSeq; i < alheight; i++)
469    {
470  2611 SequenceI s = alignViewport.getAlignment().getSequenceAt(i);
471  2611 if (hasHiddenRows || alignViewport.isDisplayReferenceSeq())
472    {
473  8 g.setFont(getHiddenFont(s, alignViewport));
474    }
475    else
476    {
477  2603 g.setFont(getIdfont());
478    }
479  2611 drawIdString(g, hasHiddenRows, s, i, 0, ypos);
480    }
481   
482  2291 if (labels != null && alignViewport.isShowAnnotation())
483    {
484  19 int getWidth = getWidth();
485  19 int thisIdWidth = getWidth;
486  19 g.translate(0, ypos + (alheight * charHeight));
487  19 if (!isManuallyAdjusted())
488    {
489  15 int getAnnotationsIdWidth = labels.drawLabels(g, false, -1, false,
490    forGUI, null, false);
491  15 thisIdWidth = idWidth < 0 ? getAnnotationsIdWidth : idWidth;
492  15 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  19 labels.drawComponent(g, false, thisIdWidth, forGUI);
501  19 g.translate(0, -ypos - (alheight * charHeight));
502    }
503   
504  2291 ypos += seqCanvas.wrappedRepeatHeightPx;
505  2291 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  279 toggle void drawMarker(Graphics2D g, AlignViewport alignViewport, int seqIndex, int starty, int yoffset)
520    {
521  279 SequenceI[] hseqs = alignViewport.getAlignment()
522    .getHiddenSequences().hiddenSequences;
523    // Use this method here instead of calling hiddenSeq adjust
524    // 3 times.
525  279 int hSize = hseqs.length;
526   
527  279 int hiddenIndex = seqIndex;
528  279 int lastIndex = seqIndex - 1;
529  279 int nextIndex = seqIndex + 1;
530   
531  4690 for (int j = 0; j < hSize; j++)
532    {
533  4411 if (hseqs[j] != null)
534    {
535  503 if (j - 1 < hiddenIndex)
536    {
537  55 hiddenIndex++;
538    }
539  503 if (j - 1 < lastIndex)
540    {
541  42 lastIndex++;
542    }
543  503 if (j - 1 < nextIndex)
544    {
545  131 nextIndex++;
546    }
547    }
548    }
549   
550    /*
551    * are we below or above the hidden sequences?
552    */
553  279 boolean below = (hiddenIndex > lastIndex + 1);
554  279 boolean above = (nextIndex > hiddenIndex + 1);
555   
556  279 g.setColor(Color.blue);
557  279 int charHeight = av.getCharHeight();
558   
559    /*
560    * vertices of the triangle, below or above hidden seqs
561    */
562  279 int[] xPoints = new int[]
563    { getWidth() - charHeight,
564    getWidth() - charHeight, getWidth() };
565  279 int yShift = seqIndex - starty;
566   
567  279 if (below)
568    {
569  13 int[] yPoints = new int[] { yShift * charHeight + yoffset,
570    yShift * charHeight + yoffset + charHeight / 4,
571    yShift * charHeight + yoffset };
572  13 g.fillPolygon(xPoints, yPoints, 3);
573    }
574  279 if (above)
575    {
576  24 yShift++;
577  24 int[] yPoints = new int[] { yShift * charHeight + yoffset,
578    yShift * charHeight + yoffset - charHeight / 4,
579    yShift * charHeight + yoffset };
580  24 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  279 toggle private Font getHiddenFont(SequenceI seq, AlignViewport alignViewport)
593    {
594  279 if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
595    {
596  14 return new Font(av.getFont().getName(), Font.BOLD,
597    av.getFont().getSize());
598    }
599  265 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  5723 toggle public Font getIdfont()
615    {
616  5723 return idfont;
617    }
618   
 
619  2855 toggle public void setIdfont(Font idfont)
620    {
621  2855 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  655 toggle @Override
637    public void propertyChange(PropertyChangeEvent evt)
638    {
639  655 String propertyName = evt.getPropertyName();
640  655 switch (propertyName)
641    {
642  39 case ViewportRanges.STARTSEQ:
643  39 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
644  39 break;
645  38 case ViewportRanges.STARTRES:
646  38 if (av.getWrapAlignment())
647    {
648  1 fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
649    }
650  38 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  578 default:
659    }
660    }
661   
662    private boolean manuallyAdjusted = false;
663   
 
664  7767 toggle public boolean isManuallyAdjusted()
665    {
666  7767 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    }