Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.gui

File AlignmentPanel.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
30% of files have more coverage

Code metrics

240
623
84
1
2,207
1,339
228
0.37
7.42
84
2.71

Classes

Class Line # Actions
AlignmentPanel 83 623 228
0.6705385467.1%
 

Contributing tests

This file is covered by 251 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.Container;
26    import java.awt.Dimension;
27    import java.awt.Font;
28    import java.awt.FontMetrics;
29    import java.awt.Graphics;
30    import java.awt.Graphics2D;
31    import java.awt.event.AdjustmentEvent;
32    import java.awt.event.AdjustmentListener;
33    import java.awt.event.ComponentAdapter;
34    import java.awt.event.ComponentEvent;
35    import java.awt.print.PageFormat;
36    import java.awt.print.Printable;
37    import java.awt.print.PrinterException;
38    import java.beans.PropertyChangeEvent;
39    import java.beans.PropertyChangeListener;
40    import java.io.File;
41    import java.io.FileWriter;
42    import java.io.PrintWriter;
43    import java.util.List;
44   
45    import javax.swing.BoundedRangeModel;
46    import javax.swing.SwingUtilities;
47   
48    import jalview.analysis.AnnotationSorter;
49    import jalview.api.AlignViewportI;
50    import jalview.api.AlignmentViewPanel;
51    import jalview.bin.Cache;
52    import jalview.bin.Console;
53    import jalview.bin.Jalview;
54    import jalview.datamodel.AlignmentAnnotation;
55    import jalview.datamodel.AlignmentI;
56    import jalview.datamodel.HiddenColumns;
57    import jalview.datamodel.SearchResultsI;
58    import jalview.datamodel.SequenceFeature;
59    import jalview.datamodel.SequenceGroup;
60    import jalview.datamodel.SequenceI;
61    import jalview.gui.ImageExporter.ImageWriterI;
62    import jalview.io.HTMLOutput;
63    import jalview.io.exceptions.ImageOutputException;
64    import jalview.jbgui.GAlignmentPanel;
65    import jalview.math.AlignmentDimension;
66    import jalview.schemes.ResidueProperties;
67    import jalview.structure.StructureSelectionManager;
68    import jalview.util.Comparison;
69    import jalview.util.ImageMaker;
70    import jalview.util.MessageManager;
71    import jalview.util.imagemaker.BitmapImageSizing;
72    import jalview.viewmodel.AlignmentViewport;
73    import jalview.viewmodel.ViewportListenerI;
74    import jalview.viewmodel.ViewportRanges;
75   
76    /**
77    * DOCUMENT ME!
78    *
79    * @author $author$
80    * @version $Revision: 1.161 $
81    */
82    @SuppressWarnings("serial")
 
83    public class AlignmentPanel extends GAlignmentPanel implements
84    AdjustmentListener, Printable, AlignmentViewPanel, ViewportListenerI
85    {
86    /*
87    * spare space in pixels between sequence id and alignment panel
88    */
89    private static final int ID_WIDTH_PADDING = 4;
90   
91    public AlignViewport av;
92   
93    OverviewPanel overviewPanel;
94   
95    private SeqPanel seqPanel;
96   
97    private IdPanel idPanel;
98   
99    IdwidthAdjuster idwidthAdjuster;
100   
101    public AlignFrame alignFrame;
102   
103    private ScalePanel scalePanel;
104   
105    private AnnotationPanel annotationPanel;
106   
107    private AnnotationLabels alabels;
108   
109    private int hextent = 0;
110   
111    private int vextent = 0;
112   
113    /*
114    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
115    * false, suppresses invoking the same method recursively.
116    */
117    private boolean scrollComplementaryPanel = true;
118   
119    private PropertyChangeListener propertyChangeListener;
120   
121    private CalculationChooser calculationDialog;
122   
123    /**
124    * Creates a new AlignmentPanel object.
125    *
126    * @param af
127    * @param av
128    */
 
129  513 toggle public AlignmentPanel(AlignFrame af, final AlignViewport av)
130    {
131  513 setName("AligmentPanel");
132    // setBackground(Color.white); // BH 2019
133  513 alignFrame = af;
134  513 this.av = av;
135  513 av.setAlignPanel(this);
136  513 setSeqPanel(new SeqPanel(av, this));
137  513 setIdPanel(new IdPanel(av, this));
138   
139  513 setScalePanel(new ScalePanel(av, this));
140   
141  513 idPanelHolder.add(getIdPanel(), BorderLayout.CENTER);
142  513 idwidthAdjuster = new IdwidthAdjuster(this);
143  513 idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
144   
145  513 setAnnotationPanel(new AnnotationPanel(this));
146  513 setAlabels(new AnnotationLabels(this));
147   
148  513 annotationScroller.setViewportView(getAnnotationPanel());
149  513 annotationSpaceFillerHolder.add(getAlabels(), BorderLayout.CENTER);
150   
151  513 scalePanelHolder.add(getScalePanel(), BorderLayout.CENTER);
152  513 seqPanelHolder.add(getSeqPanel(), BorderLayout.CENTER);
153   
154  513 setScrollValues(0, 0);
155   
156  513 hscroll.addAdjustmentListener(this);
157  513 vscroll.addAdjustmentListener(this);
158   
159  513 addComponentListener(new ComponentAdapter()
160    {
 
161  831 toggle @Override
162    public void componentResized(ComponentEvent evt)
163    {
164    // reset the viewport ranges when the alignment panel is resized
165    // in particular, this initialises the end residue value when Jalview
166    // is initialised
167  831 ViewportRanges ranges = av.getRanges();
168  831 if (av.getWrapAlignment())
169    {
170  21 int widthInRes = getSeqPanel().seqCanvas.getWrappedCanvasWidth(
171    getSeqPanel().seqCanvas.getWidth());
172  21 ranges.setViewportWidth(widthInRes);
173    }
174    else
175    {
176  810 int widthInRes = getSeqPanel().seqCanvas.getWidth()
177    / av.getCharWidth();
178  810 int heightInSeq = getSeqPanel().seqCanvas.getHeight()
179    / av.getCharHeight();
180   
181  810 ranges.setViewportWidth(widthInRes);
182  810 ranges.setViewportHeight(heightInSeq);
183    }
184  830 repaint();
185    }
186   
187    });
188   
189  513 final AlignmentPanel ap = this;
190  513 propertyChangeListener = new PropertyChangeListener()
191    {
 
192  355 toggle @Override
193    public void propertyChange(PropertyChangeEvent evt)
194    {
195  355 switch (evt.getPropertyName()) {
196  290 case AlignmentViewport.PROPERTY_SEQUENCE:
197  290 updateScrollBarsFromRanges();
198  290 if (annotationPanel != null)
199  290 annotationPanel.paintImmediately(0, 0, getWidth(), getHeight());
200  290 break;
201  65 case AlignmentViewport.PROPERTY_ALIGNMENT:
202  65 updateScrollBarsFromRanges();
203  65 PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
204  65 alignmentChanged();
205  65 break;
206    }
207    }
208    };
209  513 av.addPropertyChangeListener(propertyChangeListener);
210   
211  513 av.getRanges().addPropertyChangeListener(this);
212  513 fontChanged();
213  513 adjustAnnotationHeight();
214  513 updateLayout();
215    }
216   
 
217  856 toggle @Override
218    public AlignViewportI getAlignViewport()
219    {
220  856 return av;
221    }
222   
 
223  920 toggle public void alignmentChanged()
224    {
225  920 av.alignmentChanged(this);
226   
227  920 if (getCalculationDialog() != null)
228    {
229  0 getCalculationDialog().validateCalcTypes();
230    }
231   
232  920 alignFrame.updateEditMenuBar();
233   
234    // no idea if we need to update structure
235  920 paintAlignment(true, true);
236   
237    }
238   
239    /**
240    * DOCUMENT ME!
241    */
 
242  2239 toggle public void fontChanged()
243    {
244    // set idCanvas bufferedImage to null
245    // to prevent drawing old image
246  2239 FontMetrics fm = getFontMetrics(av.getFont());
247   
248    // update the flag controlling whether the grid is too small to render the
249    // font
250  2239 av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
251   
252  2239 scalePanelHolder.setPreferredSize(
253    new Dimension(10, av.getCharHeight() + fm.getDescent()));
254  2239 idSpaceFillerPanel1.setPreferredSize(
255    new Dimension(10, av.getCharHeight() + fm.getDescent()));
256  2239 idwidthAdjuster.invalidate();
257  2239 scalePanelHolder.invalidate();
258    // BH 2018 getIdPanel().getIdCanvas().gg = null;
259  2239 getSeqPanel().seqCanvas.img = null;
260  2239 getAnnotationPanel().adjustPanelHeight();
261   
262  2239 Dimension d = calculateIdWidth();
263  2239 getIdPanel().getIdCanvas().setPreferredSize(d);
264  2239 hscrollFillerPanel.setPreferredSize(d);
265   
266  2239 repaint();
267    }
268   
269    /**
270    * Calculates the width of the alignment labels based on the displayed names
271    * and any bounds on label width set in preferences.
272    *
273    * The calculated width is set as a property of the viewport and the layout is
274    * updated.
275    *
276    * @return Dimension giving the maximum width of the alignment label panel
277    * that should be used.
278    */
 
279  2242 toggle public Dimension calculateIdWidth()
280    {
281  2242 int oldWidth = av.getIdWidth();
282   
283    // calculate sensible default width when no preference is available
284  2242 Dimension d = null;
285  2242 if (av.getIdWidth() < 0)
286    {
287  484 d = calculateDefaultAlignmentIdWidth();
288  484 av.setIdWidth(d.width);
289    }
290    else
291    {
292  1758 d = new Dimension();
293  1758 d.width = av.getIdWidth();
294  1758 d.height = 0;
295    }
296   
297    /*
298    * fudge: if desired width has changed, update layout
299    * (see also paintComponent - updates layout on a repaint)
300    */
301  2242 if (d.width != oldWidth)
302    {
303  484 idPanelHolder.setPreferredSize(d);
304  484 validate();
305    }
306  2242 return d;
307    }
308   
 
309  2991 toggle public Dimension calculateDefaultAlignmentIdWidth()
310    {
311  2991 return calculateIdWidth(-1, false, false);
312    }
313   
314    /**
315    * pre 2.11.3 Id width calculation - used when importing old projects only
316    *
317    * @return
318    */
 
319  43 toggle public int getLegacyIdWidth()
320    {
321  43 int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
322  43 int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3);
323  43 int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth);
324  43 Dimension w = calculateIdWidthOrLegacy(true, maxwidth, false, false);
325  43 return w.width;
326    }
327   
328    /**
329    * Calculate the width of the alignment labels based on the displayed names
330    * and any bounds on label width set in preferences. Also includes annotations
331    * not actually visible.
332    *
333    * FIXME JAL-244 JAL-4091 - doesn't include sequence associated annotation
334    * label decorators and only called during tests
335    *
336    * @param maxwidth
337    * -1 or maximum width allowed for IdWidth
338    * @return Dimension giving the maximum width of the alignment label panel
339    * that should be used.
340    */
 
341  0 toggle public Dimension calculateIdWidth(int maxwidth)
342    {
343  0 return calculateIdWidth(maxwidth, true, false);
344    }
345   
346    /**
347    * Calculate the width of the alignment labels based on the displayed names
348    * and any bounds on label width set in preferences.
349    *
350    * @param maxwidth
351    * -1 or maximum width allowed for IdWidth
352    * @param includeAnnotations
353    * - when true includes width of any additional marks in annotation
354    * id panel
355    * @param visibleOnly
356    * - when true, ignore label widths for hidden annotation rows
357    * @return Dimension giving the maximum width of the alignment label panel
358    * that should be used.
359    */
 
360  3067 toggle public Dimension calculateIdWidth(int maxwidth,
361    boolean includeAnnotations, boolean visibleOnly)
362    {
363  3067 return calculateIdWidthOrLegacy(false, maxwidth, includeAnnotations,
364    visibleOnly);
365    }
366   
367    /**
368    * legacy mode or post 2.11.3 ID width calculation
369    *
370    * @param legacy
371    * - uses annotation labels, not rendered label width (excludes
372    * additional decorators)
373    * @param maxwidth
374    * @param includeAnnotations
375    * @param visibleOnly
376    * @return
377    */
 
378  3110 toggle private Dimension calculateIdWidthOrLegacy(boolean legacy, int maxwidth,
379    boolean includeAnnotations, boolean visibleOnly)
380    {
381  3110 Container c = this; // TODO: JAL-4107 from 2.12 - in 2.11.5 was new Container();
382   
383  3110 FontMetrics fm = c.getFontMetrics(
384    new Font(av.font.getName(), Font.ITALIC, av.font.getSize()));
385   
386  3110 AlignmentI al = av.getAlignment();
387  3110 int i = 0;
388  3110 int idWidth = 0;
389   
390  3110 boolean withSuffix = av.getShowJVSuffix();
391  34056 while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
392    {
393  30946 SequenceI s = al.getSequenceAt(i);
394  30946 String id = s.getDisplayId(withSuffix);
395  30946 int stringWidth = fm.stringWidth(id);
396  30946 idWidth = Math.max(idWidth, stringWidth);
397  30946 i++;
398    }
399   
400    // Also check annotation label widths
401  3110 if (includeAnnotations && al.getAlignmentAnnotation() != null)
402    {
403  76 if (legacy)
404    {
405  0 fm = c.getFontMetrics(getAlabels().getFont());
406    }
407   
408  76 if (!legacy || Jalview.isHeadlessMode())
409    {
410  76 AnnotationLabels aal = getAlabels();
411  76 int stringWidth = aal.drawLabels(null, false, idWidth, false, false,
412    fm, !visibleOnly);
413  76 idWidth = Math.max(idWidth, stringWidth);
414    }
415    else
416    {
417  0 for (i = 0; i < al.getAlignmentAnnotation().length; i++)
418    {
419  0 AlignmentAnnotation aa = al.getAlignmentAnnotation()[i];
420  0 if (visibleOnly && !aa.isForDisplay())
421    {
422  0 continue;
423    }
424  0 String label = aa.label;
425  0 int stringWidth = fm.stringWidth(label);
426  0 idWidth = Math.max(idWidth, stringWidth);
427    }
428    }
429    }
430   
431  3110 int w = maxwidth < 0 ? idWidth : Math.min(maxwidth, idWidth);
432  3110 w += ID_WIDTH_PADDING;
433   
434  3110 return new Dimension(w, 12);
435    }
436   
437    /**
438    * Highlight the given results on the alignment
439    *
440    */
 
441  1 toggle public void highlightSearchResults(SearchResultsI results)
442    {
443  1 boolean scrolled = scrollToPosition(results, 0, false);
444   
445  1 boolean fastPaint = !(scrolled && av.getWrapAlignment());
446   
447  1 getSeqPanel().seqCanvas.highlightSearchResults(results, fastPaint);
448    }
449   
450    /**
451    * Scroll the view to show the position of the highlighted region in results
452    * (if any)
453    *
454    * @param searchResults
455    * @return
456    */
 
457  9 toggle public boolean scrollToPosition(SearchResultsI searchResults)
458    {
459  9 return scrollToPosition(searchResults, 0, false);
460    }
461   
462    /**
463    * Scrolls the view (if necessary) to show the position of the first
464    * highlighted region in results (if any). Answers true if the view was
465    * scrolled, or false if no matched region was found, or it is already
466    * visible.
467    *
468    * @param results
469    * @param verticalOffset
470    * if greater than zero, allows scrolling to a position below the
471    * first displayed sequence
472    * @param centre
473    * if true, try to centre the search results horizontally in the view
474    * @return
475    */
 
476  12 toggle protected boolean scrollToPosition(SearchResultsI results,
477    int verticalOffset, boolean centre)
478    {
479  12 int startv, endv, starts, ends;
480  12 ViewportRanges ranges = av.getRanges();
481   
482  12 if (results == null || results.isEmpty() || av == null
483    || av.getAlignment() == null)
484    {
485  0 return false;
486    }
487  12 int seqIndex = av.getAlignment().findIndex(results);
488  12 if (seqIndex == -1)
489    {
490  7 return false;
491    }
492  5 SequenceI seq = av.getAlignment().getSequenceAt(seqIndex);
493   
494  5 int[] r = results.getResults(seq, 0, av.getAlignment().getWidth());
495  5 if (r == null)
496    {
497  0 return false;
498    }
499  5 int start = r[0];
500  5 int end = r[1];
501   
502    /*
503    * To centre results, scroll to positions half the visible width
504    * left/right of the start/end positions
505    */
506  5 if (centre)
507    {
508  2 int offset = (ranges.getEndRes() - ranges.getStartRes() + 1) / 2 - 1;
509  2 start = Math.max(start - offset, 0);
510  2 end = end + offset - 1;
511    }
512  5 if (start < 0)
513    {
514  0 return false;
515    }
516  5 if (end == seq.getEnd())
517    {
518  0 return false;
519    }
520   
521  5 if (av.hasHiddenColumns())
522    {
523  0 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
524  0 start = hidden.absoluteToVisibleColumn(start);
525  0 end = hidden.absoluteToVisibleColumn(end);
526  0 if (start == end)
527    {
528  0 if (!hidden.isVisible(r[0]))
529    {
530    // don't scroll - position isn't visible
531  0 return false;
532    }
533    }
534    }
535   
536    /*
537    * allow for offset of target sequence (actually scroll to one above it)
538    */
539  5 seqIndex = Math.max(0, seqIndex - verticalOffset);
540  5 boolean scrollNeeded = true;
541   
542  5 if (!av.getWrapAlignment())
543    {
544  ? if ((startv = ranges.getStartRes()) >= start)
545    {
546    /*
547    * Scroll left to make start of search results visible
548    */
549  1 setScrollValues(start, seqIndex);
550    }
551  ? else if ((endv = ranges.getEndRes()) <= end)
552    {
553    /*
554    * Scroll right to make end of search results visible
555    */
556  1 setScrollValues(startv + end - endv, seqIndex);
557    }
558  ? else if ((starts = ranges.getStartSeq()) > seqIndex)
559    {
560    /*
561    * Scroll up to make start of search results visible
562    */
563  0 setScrollValues(ranges.getStartRes(), seqIndex);
564    }
565  ? else if ((ends = ranges.getEndSeq()) <= seqIndex)
566    {
567    /*
568    * Scroll down to make end of search results visible
569    */
570  1 setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
571    }
572    /*
573    * Else results are already visible - no need to scroll
574    */
575  4 scrollNeeded = false;
576    }
577    else
578    {
579  1 scrollNeeded = ranges.scrollToWrappedVisible(start);
580    }
581   
582  5 paintAlignment(false, false);
583   
584  5 return scrollNeeded;
585    }
586   
587    /**
588    * DOCUMENT ME!
589    *
590    * @return DOCUMENT ME!
591    */
 
592  58 toggle public OverviewPanel getOverviewPanel()
593    {
594  58 return overviewPanel;
595    }
596   
597    /**
598    * DOCUMENT ME!
599    *
600    * @param op
601    * DOCUMENT ME!
602    */
 
603  86 toggle public void setOverviewPanel(OverviewPanel op)
604    {
605  86 overviewPanel = op;
606    }
607   
608    /**
609    *
610    * @param b
611    * Hide or show annotation panel
612    *
613    */
 
614  666 toggle public void setAnnotationVisible(boolean b)
615    {
616  666 if (!av.getWrapAlignment())
617    {
618  631 annotationSpaceFillerHolder.setVisible(b);
619  631 annotationScroller.setVisible(b);
620    }
621  666 repaint();
622    }
623   
624    /**
625    * automatically adjust annotation panel height for new annotation whilst
626    * ensuring the alignment is still visible.
627    */
 
628  4148 toggle @Override
629    public void adjustAnnotationHeight()
630    {
631    // TODO: display vertical annotation scrollbar if necessary
632    // this is called after loading new annotation onto alignment
633  4148 if (alignFrame.getHeight() == 0)
634    {
635  15 jalview.bin.Console.error(
636    "adjustAnnotationHeight called with zero height alignment window");
637    }
638  4148 validateAnnotationDimensions(true);
639  4144 addNotify();
640    // TODO: many places call this method and also paintAlignment with various
641    // different settings. this means multiple redraws are triggered...
642  4144 paintAlignment(true, av.needToUpdateStructureViews());
643    }
644   
645    /**
646    * calculate the annotation dimensions and refresh slider values accordingly.
647    * need to do repaints/notifys afterwards.
648    */
 
649  4469 toggle protected void validateAnnotationDimensions(boolean adjustPanelHeight)
650    {
651    // BH 2018.04.18 comment: addNotify() is not appropriate here. We
652    // are not changing ancestors, and keyboard action listeners do
653    // not need to be reset, and most importantly, we can't be sure we are actually
654    // connected to resources.
655   
656    // addNotify() is a very expensive operation,
657    // requiring a full re-layout of all parents and children.
658   
659    // Note in JComponent:
660   
661    // This method is called by the toolkit internally and should
662    // not be called directly by programs.
663   
664    // I note that addNotify() is called in several areas of Jalview.
665   
666  4469 AnnotationPanel ap = getAnnotationPanel();
667  4469 int annotationHeight = ap.adjustPanelHeight();
668  4467 annotationHeight = ap.adjustForAlignFrame(adjustPanelHeight,
669    annotationHeight);
670   
671    // BH no!!
672  4464 hscroll.addNotify();
673  4465 Dimension e = idPanel.getSize();
674  4465 int idWidth = e.width;
675  4465 boolean manuallyAdjusted = this.getIdPanel().getIdCanvas()
676    .isManuallyAdjusted();
677  4465 annotationScroller.setPreferredSize(new Dimension(
678  4465 manuallyAdjusted ? idWidth : annotationScroller.getWidth(),
679    annotationHeight));
680   
681  4465 alabels.setPreferredSize(new Dimension(idWidth, annotationHeight));
682   
683  4465 annotationSpaceFillerHolder.setPreferredSize(new Dimension(
684  4465 manuallyAdjusted ? idWidth
685    : annotationSpaceFillerHolder.getWidth(),
686    annotationHeight));
687  4465 annotationScroller.validate();
688  4465 annotationScroller.addNotify();
689  4465 ap.validate();
690    }
691   
692    /**
693    * update alignment layout for viewport settings
694    */
 
695  666 toggle public void updateLayout()
696    {
697  666 fontChanged();
698  666 setAnnotationVisible(av.isShowAnnotation());
699  666 boolean wrap = av.getWrapAlignment();
700  666 ViewportRanges ranges = av.getRanges();
701  666 ranges.setStartSeq(0);
702    // scalePanelHolder.setVisible(!wrap);
703  666 hscroll.setVisible(!wrap);
704    // Allow idPanel width adjustment in wrap mode
705  666 idwidthAdjuster.setVisible(true);
706   
707  666 if (wrap)
708    {
709  35 annotationScroller.setVisible(false);
710  35 annotationSpaceFillerHolder.setVisible(false);
711    }
712  631 else if (av.isShowAnnotation())
713    {
714  621 annotationScroller.setVisible(true);
715  621 annotationSpaceFillerHolder.setVisible(true);
716    }
717  666 fontChanged();
718    // FIXME JAL-4107 Commented out in 2.11.5
719    // idSpaceFillerPanel1.setVisible(!wrap);
720   
721    /*
722    * defer dimension calculations if panel not yet added to a Window
723    * BH 2020.06.09
724    */
725  666 if (getTopLevelAncestor() == null)
726    {
727  636 repaint();
728  636 return;
729    }
730   
731  30 if (!wrap && av.isShowAnnotation())
732    {
733  21 validateAnnotationDimensions(false);
734    }
735   
736  30 int canvasWidth = getSeqPanel().seqCanvas.getWidth();
737  30 if (canvasWidth > 0)
738    { // may not yet be laid out
739  30 if (wrap)
740    {
741  9 int widthInRes = getSeqPanel().seqCanvas
742    .getWrappedCanvasWidth(canvasWidth);
743  9 ranges.setViewportWidth(widthInRes);
744    }
745    else
746    {
747  21 int widthInRes = (canvasWidth / av.getCharWidth());
748  21 int heightInSeq = (getSeqPanel().seqCanvas.getHeight()
749    / av.getCharHeight());
750   
751  21 ranges.setViewportWidth(widthInRes);
752  21 ranges.setViewportHeight(heightInSeq);
753    }
754    }
755   
756    // idSpaceFillerPanel1.setVisible(!wrap);
757    // debug messages from 2.12:
758    // System.out.println("ap dim = " + getSize());
759    // these values will go negative if getSize() returns (0,0):
760    // System.out.println("seqpan dim = " + getSeqPanel().getSize());
761    // System.out.println("seqcan dim = " + getSeqPanel().seqCanvas.getSize());
762   
763  30 repaint();
764    }
765   
766    /**
767    * Adjust row/column scrollers to show a visible position in the alignment.
768    *
769    * @param x
770    * visible column to scroll to
771    * @param y
772    * visible row to scroll to
773    *
774    */
 
775  1538 toggle public void setScrollValues(int x, int y)
776    {
777   
778  1538 if (av == null || av.getAlignment() == null)
779    {
780  0 return;
781    }
782   
783  1538 if (av.getWrapAlignment())
784    {
785  78 setScrollingForWrappedPanel(x);
786    }
787    else
788    {
789  1460 int width = av.getAlignment().getVisibleWidth();
790  1460 int height = av.getAlignment().getHeight();
791   
792    // TODO JAL-4107 DELETE COMMENTED 2.11.5 logic if not needed !
793    // 2.12:
794  1460 hextent = Math.min(getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(), width);
795  1460 vextent = Math.min(getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(), height);
796   
797  1460 x = Math.max(0, Math.min(x, width - hextent));
798  1460 y = Math.max(0, Math.min(y, height - vextent));
799   
800    // 2.11.5
801    // hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
802    // vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
803   
804    // if (hextent > width)
805    // {
806    // hextent = width;
807    // }
808   
809    // if (vextent > height)
810    // {
811    // vextent = height;
812    // }
813   
814    // if ((hextent + x) > width)
815    // {
816    // x = width - hextent;
817    // }
818   
819    // if ((vextent + y) > height)
820    // {
821    // y = height - vextent;
822    // }
823   
824    // if (y < 0)
825    // {
826    // y = 0;
827    // }
828   
829    // if (x < 0)
830    // {
831    // x = 0;
832    // }
833   
834   
835  1460 updateRanges(x, y);
836  1460 updateScrollBars(x, y, width, height);
837    }
838    }
839   
840    /**
841    * Answers true if the panel has no horizontal scrollbar, or the scrollbar is
842    * at its rightmost position, else false.
843    *
844    * @return
845    */
 
846  0 toggle boolean isScrolledFullyRight()
847    {
848  0 if (hscroll == null)
849    {
850  0 return true;
851    }
852  0 BoundedRangeModel model = hscroll.getModel();
853  0 return (model.getExtent() + model.getValue() >= model.getMaximum());
854    }
855   
 
856  1460 toggle private void updateScrollBars(int x, int y, int width, int height)
857    {
858  1460 hscroll.setValues(x, hextent, 0, width);
859  1460 vscroll.setValues(y, vextent, 0, height);
860    }
861    /**
862    * Respond to adjustment event when horizontal or vertical scrollbar is
863    * changed
864    *
865    * @param evt
866    * adjustment event encoding whether hscroll or vscroll changed
867    */
 
868  731 toggle @Override
869    public void adjustmentValueChanged(AdjustmentEvent evt)
870    {
871  730 if (av.getWrapAlignment())
872    {
873  41 adjustScrollingWrapped(evt);
874  41 return;
875    }
876   
877   
878  689 if (evt.getSource() == hscroll)
879    {
880  296 if (!updateRanges(hscroll.getValue(), Integer.MIN_VALUE))
881  271 return;
882    }
883  393 else if (evt.getSource() == vscroll)
884    {
885  393 if (!updateRanges(Integer.MIN_VALUE, vscroll.getValue()))
886  341 return;
887    }
888  77 repaint();
889    }
890   
 
891  2149 toggle private boolean updateRanges(int x, int y)
892    {
893  2149 ViewportRanges ranges = av.getRanges();
894  2149 boolean isChanged = false;
895  2149 if (x != Integer.MIN_VALUE)
896    {
897  1756 int oldX = ranges.getStartRes();
898  1756 int oldwidth = ranges.getViewportWidth();
899  1756 int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
900   
901    // if we're scrolling to the position we're already at, stop
902    // this prevents infinite recursion of events when the scroll/viewport
903    // ranges values are the same
904  1756 if (width > 0 && (x != oldX || width != oldwidth))
905    {
906  140 ranges.setViewportStartAndWidth(x, width);
907  140 isChanged = true;
908    }
909    }
910  2149 if (y != Integer.MIN_VALUE)
911    {
912  1853 int oldY = ranges.getStartSeq();
913  1853 int oldheight = ranges.getViewportHeight();
914  1853 int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
915   
916    // if we're scrolling to the position we're already at, stop
917    // this prevents infinite recursion of events when the scroll/viewport
918    // ranges values are the same
919  1853 if (height > 0 && (y != oldY || height != oldheight))
920    {
921  342 ranges.setViewportStartAndHeight(y, height);
922  342 isChanged = true;
923    }
924    }
925  2149 return isChanged;
926    }
927   
928    /**
929    * Responds to a scroll change by setting the start position of the viewport.
930    * Does
931    *
932    * @param evt
933    */
 
934  41 toggle protected void adjustScrollingWrapped(AdjustmentEvent evt)
935    {
936  41 if (evt.getSource() == hscroll)
937    {
938  0 return; // no horizontal scroll when wrapped
939    }
940  41 final ViewportRanges ranges = av.getRanges();
941   
942  41 if (evt.getSource() == vscroll)
943    {
944  41 int newY = vscroll.getValue();
945   
946    /*
947    * if we're scrolling to the position we're already at, stop
948    * this prevents infinite recursion of events when the scroll/viewport
949    * ranges values are the same
950    */
951  41 int oldX = ranges.getStartRes();
952  41 int oldY = ranges.getWrappedScrollPosition(oldX);
953  41 if (oldY == newY)
954    {
955  41 return;
956    }
957  0 if (newY > -1)
958    {
959    /*
960    * limit page up/down to one width's worth of positions
961    */
962  0 int rowSize = ranges.getViewportWidth();
963  0 int newX = newY > oldY ? oldX + rowSize : oldX - rowSize;
964  0 ranges.setViewportStartAndWidth(Math.max(0, newX), rowSize);
965    }
966    }
967    else
968    {
969    // This is only called if file loaded is a jar file that
970    // was wrapped when saved and user has wrap alignment true
971    // as preference setting
972  0 SwingUtilities.invokeLater(new Runnable()
973    {
 
974  0 toggle @Override
975    public void run()
976    {
977    // When updating scrolling to use ViewportChange events, this code
978    // could not be validated and it is not clear if it is now being
979    // called. Log warning here in case it is called and unforeseen
980    // problems occur
981  0 Console.warn(
982    "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences");
983   
984    // scroll to start of panel
985  0 ranges.setStartRes(0);
986  0 ranges.setStartSeq(0);
987    }
988    });
989    }
990  0 repaint();
991    }
992   
993    /* (non-Javadoc)
994    * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean)
995    */
 
996  7210 toggle @Override
997    public void paintAlignment(boolean updateOverview,
998    boolean updateStructures)
999    {
1000  7210 final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
1001    av.isShowAutocalculatedAbove());
1002  7209 sorter.sort(getAlignment().getAlignmentAnnotation(),
1003    av.getSortAnnotationsBy());
1004   
1005  7209 if (updateStructures)
1006    {
1007  2886 av.getStructureSelectionManager().sequenceColoursChanged(this);
1008    }
1009  7208 if (updateOverview)
1010    {
1011   
1012  7172 alignFrame.repaint();
1013  7172 if (overviewPanel != null)
1014    {
1015  578 overviewPanel.updateOverviewImage();
1016    }
1017    } else {
1018  36 invalidate(); // needed so that the id width adjuster works correctly
1019  36 repaint();
1020    }
1021    }
1022   
 
1023  3187 toggle @Override
1024    public void paintComponent(Graphics g)
1025    {
1026    // BH OUCH!
1027  3187 invalidate(); // needed so that the id width adjuster works correctly
1028  3187 Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
1029  3187 int idWidth = d.width;
1030   
1031    // check wrapped alignment as at least 1 residue width
1032  3187 if (av.getWrapAlignment())
1033    {
1034  193 SeqCanvas sc = this.getSeqPanel().seqCanvas;
1035  193 if (sc != null && sc.getWidth() < sc.getMinimumWrappedCanvasWidth())
1036    {
1037    // need to make some adjustments
1038  0 idWidth -= (sc.getMinimumWrappedCanvasWidth() - sc.getWidth());
1039  0 av.setIdWidth(idWidth);
1040  0 av.getAlignPanel().getIdPanel().getIdCanvas()
1041    .setManuallyAdjusted(true);
1042   
1043  0 validateAnnotationDimensions(false);
1044    }
1045    }
1046   
1047  3187 idPanelHolder.setPreferredSize(new Dimension(idWidth, d.height));
1048  3187 hscrollFillerPanel.setPreferredSize(new Dimension(idWidth, 12));
1049   
1050  3187 validate(); // needed so that the id width adjuster works correctly
1051   
1052    /*
1053    * set scroll bar positions - tried to remove but necessary for split panel to resize correctly
1054    * though I still think this call should be elsewhere.
1055    */
1056  3187 ViewportRanges ranges = av.getRanges();
1057    // FIXME JAL-4107 this was not commented out in 2.11.5.0 - is there an issue in 2.12 with CDS/Protein linked horiz scrolling ?
1058    // setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
1059  3187 super.paintComponent(g);
1060    }
1061   
1062    /**
1063    * Set vertical scroll bar position, and number of increments, for wrapped
1064    * panel
1065    *
1066    * @param topLeftColumn
1067    * the column position at top left (0..)
1068    */
 
1069  78 toggle private void setScrollingForWrappedPanel(int topLeftColumn)
1070    {
1071  78 ViewportRanges ranges = av.getRanges();
1072  78 int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn);
1073  78 int maxScroll = ranges.getWrappedMaxScroll(topLeftColumn);
1074   
1075    /*
1076    * a scrollbar's value can be set to at most (maximum-extent)
1077    * so we add extent (1) to the maxScroll value
1078    */
1079  78 vscroll.setUnitIncrement(1);
1080  78 vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1);
1081    }
1082   
1083    /**
1084    * DOCUMENT ME!
1085    *
1086    * @param pg
1087    * DOCUMENT ME!
1088    * @param pf
1089    * DOCUMENT ME!
1090    * @param pi
1091    * DOCUMENT ME!
1092    *
1093    * @return DOCUMENT ME!
1094    *
1095    * @throws PrinterException
1096    * DOCUMENT ME!
1097    */
 
1098  0 toggle @Override
1099    public int print(Graphics pg, PageFormat pf, int pi)
1100    throws PrinterException
1101    {
1102  0 pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
1103   
1104  0 int pwidth = (int) pf.getImageableWidth();
1105  0 int pheight = (int) pf.getImageableHeight();
1106   
1107  0 if (av.getWrapAlignment())
1108    {
1109  0 return printWrappedAlignment(pwidth, pheight, pi, pg);
1110    }
1111    else
1112    {
1113  0 return printUnwrapped(pwidth, pheight, pi, pg, pg);
1114    }
1115    }
1116   
1117    /**
1118    * Draws the alignment image, including sequence ids, sequences, and
1119    * annotation labels and annotations if shown, on either one or two Graphics
1120    * contexts.
1121    *
1122    * @param pageWidth
1123    * in pixels
1124    * @param pageHeight
1125    * in pixels
1126    * @param pageIndex
1127    * (0, 1, ...)
1128    * @param idGraphics
1129    * the graphics context for sequence ids and annotation labels
1130    * @param alignmentGraphics
1131    * the graphics context for sequences and annotations (may or may not
1132    * be the same context as idGraphics)
1133    * @return
1134    * @throws PrinterException
1135    */
 
1136  31 toggle public int printUnwrapped(int pageWidth, int pageHeight, int pageIndex,
1137    Graphics idGraphics, Graphics alignmentGraphics)
1138    throws PrinterException
1139    {
1140  31 final int idWidth, idWidthForGui;
1141    // otherwise calculate it
1142  31 idWidth = getVisibleIdWidth(false);
1143    // if (getIdPanel()!=null && getIdPanel().getWidth()>0)
1144    // {
1145    // // use the current IdPanel's width, if its set and non-zero
1146    // idWidthForGui = getIdPanel().getWidth();
1147    // } else {
1148    // idWidthForGui=0;
1149    // }
1150    /*
1151    * Get the horizontal offset to where we draw the sequences.
1152    * This is idWidth if using a single Graphics context, else zero.
1153    */
1154  31 final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0
1155    : idWidth;
1156   
1157  31 FontMetrics fm = getFontMetrics(av.getFont());
1158  31 final int charHeight = av.getCharHeight();
1159  31 final int scaleHeight = charHeight + fm.getDescent();
1160   
1161  31 idGraphics.setColor(Color.white);
1162  31 idGraphics.fillRect(0, 0, pageWidth, pageHeight);
1163  31 idGraphics.setFont(av.getFont());
1164   
1165    /*
1166    * How many sequences and residues can we fit on a printable page?
1167    */
1168  31 final int totalRes = (pageWidth - idWidth) / av.getCharWidth();
1169   
1170  31 final int totalSeq = (pageHeight - scaleHeight) / charHeight - 1;
1171   
1172  31 final int alignmentWidth = av.getAlignment().getVisibleWidth();
1173  31 int pagesWide = (alignmentWidth / totalRes) + 1;
1174   
1175  31 final int startRes = (pageIndex % pagesWide) * totalRes;
1176  31 final int endRes = Math.min(startRes + totalRes - 1,
1177    alignmentWidth - 1);
1178   
1179  31 final int startSeq = (pageIndex / pagesWide) * totalSeq;
1180  31 final int alignmentHeight = av.getAlignment().getHeight();
1181  31 final int endSeq = Math.min(startSeq + totalSeq, alignmentHeight);
1182   
1183  31 int pagesHigh = ((alignmentHeight / totalSeq) + 1) * pageHeight;
1184   
1185  31 if (av.isShowAnnotation())
1186    {
1187  31 pagesHigh += getAnnotationPanel().adjustPanelHeight() + 3;
1188    }
1189   
1190  31 pagesHigh /= pageHeight;
1191   
1192  31 if (pageIndex >= (pagesWide * pagesHigh))
1193    {
1194  0 return Printable.NO_SUCH_PAGE;
1195    }
1196  31 final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight + 3;
1197   
1198  31 alignmentGraphics.setColor(Color.white);
1199  31 alignmentGraphics.fillRect(0, 0, pageWidth, pageHeight + scaleHeight);
1200   
1201    /*
1202    * draw the Scale at horizontal offset, then reset to top left (0, 0)
1203    */
1204  31 alignmentGraphics.translate(alignmentGraphicsOffset, 0);
1205  31 getScalePanel().drawScale(alignmentGraphics, startRes, endRes,
1206    pageWidth - idWidth, scaleHeight);
1207  31 alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
1208   
1209    /*
1210    * Draw the sequence ids, offset for scale height,
1211    * then reset to top left (0, 0)
1212    */
1213  31 idGraphics.translate(0, scaleHeight);
1214  31 IdCanvas idCanvas = getIdPanel().getIdCanvas();
1215  31 List<SequenceI> selection = av.getSelectionGroup() == null ? null
1216    : av.getSelectionGroup().getSequences(null);
1217   
1218  31 idCanvas.drawIds((Graphics2D) idGraphics, av, startSeq, endSeq - 1,
1219    selection, false, idWidth);
1220   
1221  31 idGraphics.setFont(av.getFont());
1222  31 idGraphics.translate(0, -scaleHeight);
1223   
1224    /*
1225    * draw the sequences, offset for scale height, and id width (if using a
1226    * single graphics context), then reset to (0, scale height)
1227    */
1228  31 alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight);
1229  31 getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics,
1230    startRes, endRes, startSeq, endSeq - 1);
1231  31 alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
1232   
1233  31 if (av.isShowAnnotation() && (endSeq == alignmentHeight))
1234    {
1235    /*
1236    * draw annotation labels; drawComponent() translates by
1237    * getScrollOffset(), so compensate for that first;
1238    * then reset to (0, scale height)
1239    */
1240  31 int offset = getAlabels().getScrollOffset();
1241  31 idGraphics.translate(0, -offset);
1242  31 idGraphics.translate(0, alignmentDrawnHeight);
1243  31 getAlabels().drawComponentNotGUI(idGraphics, idWidth);
1244  31 idGraphics.translate(0, -alignmentDrawnHeight);
1245   
1246    /*
1247    * draw the annotations starting at
1248    * (idOffset, alignmentHeight) from (0, scaleHeight)
1249    */
1250  31 alignmentGraphics.translate(alignmentGraphicsOffset,
1251    alignmentDrawnHeight);
1252  31 updateLayout();
1253  31 getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
1254    alignmentGraphics, -1, startRes, endRes + 1);
1255    }
1256   
1257  31 return Printable.PAGE_EXISTS;
1258    }
1259   
1260    /**
1261    * Prints one page of an alignment in wrapped mode. Returns
1262    * Printable.PAGE_EXISTS (0) if a page was drawn, or Printable.NO_SUCH_PAGE if
1263    * no page could be drawn (page number out of range).
1264    *
1265    * @param pageWidth
1266    * @param pageHeight
1267    * @param pageNumber
1268    * (0, 1, ...)
1269    * @param g
1270    *
1271    * @return
1272    *
1273    * @throws PrinterException
1274    */
 
1275  14 toggle public int printWrappedAlignment(int pageWidth, int pageHeight,
1276    int pageNumber, Graphics g) throws PrinterException
1277    {
1278  14 getSeqPanel().seqCanvas.calculateWrappedGeometry();
1279  14 int annotationHeight = 0;
1280  14 if (av.isShowAnnotation())
1281    {
1282  0 annotationHeight = getAnnotationPanel().adjustPanelHeight();
1283    }
1284   
1285  14 int hgap = av.getCharHeight();
1286  14 if (av.getScaleAboveWrapped())
1287    {
1288  0 hgap += av.getCharHeight();
1289    }
1290   
1291  14 int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1292    + annotationHeight;
1293   
1294  14 int idWidth = getVisibleIdWidth(false);
1295   
1296  14 int maxwidth = av.getAlignment().getVisibleWidth();
1297   
1298  14 int resWidth = getSeqPanel().seqCanvas
1299    .getWrappedCanvasWidth(pageWidth - idWidth);
1300  14 av.getRanges().setViewportStartAndWidth(0, resWidth);
1301   
1302    // Fix JAL-4603: use ceiling division ((maxwidth + resWidth - 1) / resWidth)
1303    // to round up
1304  14 int totalHeight = cHeight * ((maxwidth + resWidth - 1) / resWidth + 1);
1305   
1306  14 g.setColor(Color.white);
1307  14 g.fillRect(0, 0, pageWidth, pageHeight);
1308  14 g.setFont(av.getFont());
1309  14 g.setColor(Color.black);
1310   
1311    /*
1312    * method: print the whole wrapped alignment, but with a clip region that
1313    * is restricted to the requested page; this supports selective print of
1314    * single pages or ranges, (at the cost of repeated processing in the
1315    * 'normal' case, when all pages are printed)
1316    */
1317  14 g.translate(0, -pageNumber * pageHeight);
1318   
1319  14 g.setClip(0, pageNumber * pageHeight, pageWidth, pageHeight);
1320   
1321    /*
1322    * draw sequence ids and annotation labels (if shown)
1323    */
1324  14 IdCanvas idCanvas = getIdPanel().getIdCanvas();
1325  14 idCanvas.drawIdsWrappedNoGUI((Graphics2D) g, av, 0, totalHeight);
1326   
1327  14 g.translate(idWidth, 0);
1328   
1329  14 getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(g,
1330    pageWidth - idWidth, totalHeight, 0);
1331   
1332  14 if ((pageNumber * pageHeight) < totalHeight)
1333    {
1334  9 return Printable.PAGE_EXISTS;
1335    }
1336    else
1337    {
1338  5 return Printable.NO_SUCH_PAGE;
1339    }
1340    }
1341   
1342    /**
1343    * get current sequence ID panel width, or nominal value if panel were to be
1344    * displayed using default settings
1345    *
1346    * @return
1347    */
 
1348  0 toggle public int getVisibleIdWidth()
1349    {
1350  0 return getVisibleIdWidth(true);
1351    }
1352   
1353    /**
1354    * get current sequence ID panel width, or nominal value if panel were to be
1355    * displayed using default settings
1356    *
1357    * @param onscreen
1358    * indicate if the Id width for onscreen or offscreen display should
1359    * be returned
1360    * @return
1361    */
 
1362  76 toggle protected int getVisibleIdWidth(boolean onscreen)
1363    {
1364    // see if rendering offscreen - check preferences and calc width accordingly
1365  76 if (!onscreen && Cache.getDefault("FIGURE_AUTOIDWIDTH", false))
1366    {
1367  0 return calculateIdWidth(-1, true, true).width;
1368    }
1369  76 Integer idwidth = onscreen ? null
1370    : Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
1371  76 if (idwidth != null)
1372    {
1373  0 return idwidth.intValue() + ID_WIDTH_PADDING;
1374    }
1375   
1376    // int w = getIdPanel().getWidth();
1377    // w = calculateIdWidth(-1, true, true).width;
1378  76 int w = calculateIdWidth(-1, true, true).width;
1379  76 return (w > 0 ? w : calculateIdWidth().width);
1380    }
1381   
 
1382  12 toggle void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer)
1383    throws ImageOutputException
1384    {
1385  12 makeAlignmentImage(type, file, renderer,
1386    BitmapImageSizing.defaultBitmapImageSizing());
1387    }
1388   
1389    /**
1390    * Builds an image of the alignment of the specified type (EPS/PNG/SVG) and
1391    * writes it to the specified file
1392    *
1393    * @param type
1394    * @param file
1395    * @param textrenderer
1396    * @param bitmapscale
1397    */
 
1398  29 toggle void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer,
1399    BitmapImageSizing userBis) throws ImageOutputException
1400    {
1401  29 final int borderBottomOffset = 5;
1402   
1403  29 AlignmentDimension aDimension = getAlignmentDimension();
1404   
1405    // todo use a lambda function in place of callback here?
1406  29 ImageWriterI writer = new ImageWriterI()
1407    {
 
1408  29 toggle @Override
1409    public void exportImage(Graphics graphics) throws Exception
1410    {
1411  29 if (av.getWrapAlignment())
1412    {
1413  0 printWrappedAlignment(aDimension.getWidth(),
1414    aDimension.getHeight() + borderBottomOffset, 0, graphics);
1415    }
1416    else
1417    {
1418  29 printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
1419    graphics, graphics);
1420    }
1421    }
1422    };
1423   
1424  29 String fileTitle = alignFrame.getTitle();
1425  29 ImageExporter exporter = new ImageExporter(writer, alignFrame, type,
1426    fileTitle);
1427  29 int imageWidth = aDimension.getWidth();
1428  29 int imageHeight = aDimension.getHeight() + borderBottomOffset;
1429  29 String of = MessageManager.getString("label.alignment");
1430  29 exporter.doExport(file, this, imageWidth, imageHeight, of, renderer,
1431    userBis);
1432    }
1433   
1434    /**
1435    * Calculates and returns a suitable width and height (in pixels) for an
1436    * exported image
1437    *
1438    * @return
1439    */
 
1440  31 toggle public AlignmentDimension getAlignmentDimension()
1441    {
1442  31 int maxwidth = av.getAlignment().getVisibleWidth();
1443   
1444  31 int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
1445    + getScalePanel().getHeight();
1446  31 int width = getVisibleIdWidth(false) + (maxwidth * av.getCharWidth());
1447   
1448  31 if (av.getWrapAlignment())
1449    {
1450  0 height = getWrappedHeight();
1451  0 if (Jalview.isHeadlessMode())
1452    {
1453    // need to obtain default alignment width and then add in any
1454    // additional allowance for id margin
1455    // this duplicates the calculation in getWrappedHeight but adjusts for
1456    // offscreen idWidth
1457  0 width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1458    - alignFrame.getInsets().left - alignFrame.getInsets().right
1459    - getVisibleIdWidth() + getVisibleIdWidth(false);
1460    }
1461    else
1462    {
1463  0 width = getSeqPanel().getWidth() + getVisibleIdWidth(false);
1464    }
1465   
1466    }
1467  31 else if (av.isShowAnnotation())
1468    {
1469  31 height += getAnnotationPanel().adjustPanelHeight() + 3;
1470    }
1471  31 return new AlignmentDimension(width, height);
1472   
1473    }
1474   
 
1475  0 toggle public void makePNGImageMap(File imgMapFile, String imageName)
1476    throws ImageOutputException
1477    {
1478    // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
1479    // ////////////////////////////////////////////
1480  0 int idWidth = getVisibleIdWidth(false);
1481  0 FontMetrics fm = getFontMetrics(av.getFont());
1482  0 int scaleHeight = av.getCharHeight() + fm.getDescent();
1483   
1484    // Gen image map
1485    // ////////////////////////////////
1486  0 if (imgMapFile != null)
1487    {
1488  0 try
1489    {
1490  0 int sSize = av.getAlignment().getHeight();
1491  0 int alwidth = av.getAlignment().getWidth();
1492  0 PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1493  0 out.println(HTMLOutput.getImageMapHTML());
1494  0 out.println("<img src=\"" + imageName
1495    + "\" border=\"0\" usemap=\"#Map\" >"
1496    + "<map name=\"Map\">");
1497   
1498  0 for (int s = 0; s < sSize; s++)
1499    {
1500  0 int sy = s * av.getCharHeight() + scaleHeight;
1501   
1502  0 SequenceI seq = av.getAlignment().getSequenceAt(s);
1503  0 SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
1504  0 for (int column = 0; column < alwidth; column++)
1505    {
1506  0 StringBuilder text = new StringBuilder(512);
1507  0 String triplet = null;
1508  0 if (av.getAlignment().isNucleotide())
1509    {
1510  0 triplet = ResidueProperties.nucleotideName
1511    .get(seq.getCharAt(column) + "");
1512    }
1513    else
1514    {
1515  0 triplet = ResidueProperties.aa2Triplet
1516    .get(seq.getCharAt(column) + "");
1517    }
1518   
1519  0 if (triplet == null)
1520    {
1521  0 continue;
1522    }
1523   
1524  0 int seqPos = seq.findPosition(column);
1525  0 int gSize = groups.length;
1526  0 for (int g = 0; g < gSize; g++)
1527    {
1528  0 if (text.length() < 1)
1529    {
1530  0 text.append("<area shape=\"rect\" coords=\"")
1531    .append((idWidth + column * av.getCharWidth()))
1532    .append(",").append(sy).append(",")
1533    .append((idWidth
1534    + (column + 1) * av.getCharWidth()))
1535    .append(",").append((av.getCharHeight() + sy))
1536    .append("\"").append(" onMouseOver=\"toolTip('")
1537    .append(seqPos).append(" ").append(triplet);
1538    }
1539   
1540  0 if (groups[g].getStartRes() < column
1541    && groups[g].getEndRes() > column)
1542    {
1543  0 text.append("<br><em>").append(groups[g].getName())
1544    .append("</em>");
1545    }
1546    }
1547   
1548  0 if (text.length() < 1)
1549    {
1550  0 text.append("<area shape=\"rect\" coords=\"")
1551    .append((idWidth + column * av.getCharWidth()))
1552    .append(",").append(sy).append(",")
1553    .append((idWidth + (column + 1) * av.getCharWidth()))
1554    .append(",").append((av.getCharHeight() + sy))
1555    .append("\"").append(" onMouseOver=\"toolTip('")
1556    .append(seqPos).append(" ").append(triplet);
1557    }
1558  0 if (!Comparison.isGap(seq.getCharAt(column)))
1559    {
1560  0 List<SequenceFeature> features = seq.findFeatures(column,
1561    column);
1562  0 for (SequenceFeature sf : features)
1563    {
1564  0 if (sf.isContactFeature())
1565    {
1566  0 text.append("<br>").append(sf.getType()).append(" ")
1567    .append(sf.getBegin()).append(":")
1568    .append(sf.getEnd());
1569    }
1570    else
1571    {
1572  0 text.append("<br>");
1573  0 text.append(sf.getType());
1574  0 String description = sf.getDescription();
1575  0 if (description != null
1576    && !sf.getType().equals(description))
1577    {
1578  0 description = description.replace("\"", "&quot;");
1579  0 text.append(" ").append(description);
1580    }
1581    }
1582  0 String status = sf.getStatus();
1583  0 if (status != null && !"".equals(status))
1584    {
1585  0 text.append(" (").append(status).append(")");
1586    }
1587    }
1588  0 if (text.length() > 1)
1589    {
1590  0 text.append("')\"; onMouseOut=\"toolTip()\"; href=\"#\">");
1591  0 out.println(text.toString());
1592    }
1593    }
1594    }
1595    }
1596  0 out.println("</map></body></html>");
1597  0 out.close();
1598   
1599    } catch (Exception ex)
1600    {
1601  0 throw new ImageOutputException(
1602    "couldn't write ImageMap due to unexpected error", ex);
1603    }
1604    } // /////////END OF IMAGE MAP
1605   
1606    }
1607   
1608    /**
1609    * Answers the height of the entire alignment in pixels, assuming it is in
1610    * wrapped mode
1611    *
1612    * @return
1613    */
 
1614  0 toggle int getWrappedHeight()
1615    {
1616  0 int seqPanelWidth = getSeqPanel().seqCanvas.getWidth();
1617   
1618  0 if (Jalview.isHeadlessMode())
1619    {
1620  0 seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1621    - vscroll.getPreferredSize().width
1622    - alignFrame.getInsets().left - alignFrame.getInsets().right;
1623    }
1624   
1625  0 int chunkWidth = getSeqPanel().seqCanvas
1626    .getWrappedCanvasWidth(seqPanelWidth);
1627   
1628  0 int hgap = av.getCharHeight();
1629  0 if (av.getScaleAboveWrapped())
1630    {
1631  0 hgap += av.getCharHeight();
1632    }
1633   
1634  0 int annotationHeight = 0;
1635  0 if (av.isShowAnnotation())
1636    {
1637  0 hgap += SeqCanvas.SEQS_ANNOTATION_GAP;
1638  0 annotationHeight = getAnnotationPanel().adjustPanelHeight();
1639    }
1640   
1641  0 int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1642    + annotationHeight;
1643   
1644  0 int maxwidth = av.getAlignment().getWidth();
1645  0 if (av.hasHiddenColumns())
1646    {
1647  0 maxwidth = av.getAlignment().getHiddenColumns()
1648    .absoluteToVisibleColumn(maxwidth) - 1;
1649    }
1650   
1651  0 int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1652   
1653  0 return height;
1654    }
1655   
1656    /**
1657    * close the panel - deregisters all listeners and nulls any references to
1658    * alignment data.
1659    */
 
1660  317 toggle public void closePanel()
1661    {
1662  317 PaintRefresher.RemoveComponent(getSeqPanel().seqCanvas);
1663  317 PaintRefresher.RemoveComponent(getIdPanel().getIdCanvas());
1664  317 PaintRefresher.RemoveComponent(this);
1665   
1666  317 closeChildFrames();
1667   
1668    /*
1669    * try to ensure references are nulled
1670    */
1671  317 if (annotationPanel != null)
1672    {
1673  317 annotationPanel.dispose();
1674  317 annotationPanel = null;
1675    }
1676   
1677  317 if (av != null)
1678    {
1679  317 av.removePropertyChangeListener(propertyChangeListener);
1680  317 propertyChangeListener = null;
1681  317 StructureSelectionManager ssm = av.getStructureSelectionManager();
1682  317 ssm.removeStructureViewerListener(getSeqPanel(), null);
1683  312 ssm.removeSelectionListener(getSeqPanel());
1684  312 ssm.removeCommandListener(av);
1685  312 ssm.removeStructureViewerListener(getSeqPanel(), null);
1686  312 ssm.removeSelectionListener(getSeqPanel());
1687  312 av.dispose();
1688  312 av = null;
1689    }
1690    else
1691    {
1692  0 if (Console.isDebugEnabled())
1693    {
1694  0 Console.warn("Closing alignment panel which is already closed.");
1695    }
1696    }
1697    }
1698   
1699    /**
1700    * Close any open dialogs that would be orphaned when this one is closed
1701    */
 
1702  317 toggle protected void closeChildFrames()
1703    {
1704  317 if (overviewPanel != null)
1705    {
1706  1 overviewPanel.dispose();
1707  1 overviewPanel = null;
1708    }
1709  317 if (calculationDialog != null)
1710    {
1711  0 calculationDialog.closeFrame();
1712  0 calculationDialog = null;
1713    }
1714    }
1715   
1716    /**
1717    * hides or shows dynamic annotation rows based on groups and av state flags
1718    */
 
1719  344 toggle public void updateAnnotation()
1720    {
1721  344 updateAnnotation(false, false);
1722    }
1723   
 
1724  0 toggle public void updateAnnotation(boolean applyGlobalSettings)
1725    {
1726  0 updateAnnotation(applyGlobalSettings, false);
1727    }
1728   
 
1729  440 toggle public void updateAnnotation(boolean applyGlobalSettings,
1730    boolean preserveNewGroupSettings)
1731    {
1732  440 av.updateSecondaryStructureConsensus(this);
1733  440 av.updateGroupAnnotationSettings(applyGlobalSettings,
1734    preserveNewGroupSettings);
1735  440 adjustAnnotationHeight();
1736    }
1737   
 
1738  18367 toggle @Override
1739    public AlignmentI getAlignment()
1740    {
1741  18367 return av == null ? null : av.getAlignment();
1742    }
1743   
 
1744  4620 toggle @Override
1745    public String getViewName()
1746    {
1747  4620 return av.getViewName();
1748    }
1749   
1750    /**
1751    * Make/Unmake this alignment panel the current input focus
1752    *
1753    * @param b
1754    */
 
1755  0 toggle public void setSelected(boolean b)
1756    {
1757  0 try
1758    {
1759  0 if (alignFrame.getSplitViewContainer() != null)
1760    {
1761    /*
1762    * bring enclosing SplitFrame to front first if there is one
1763    */
1764  0 ((SplitFrame) alignFrame.getSplitViewContainer()).setSelected(b);
1765    }
1766  0 alignFrame.setSelected(b);
1767    } catch (Exception ex)
1768    {
1769    }
1770   
1771  0 if (b)
1772    {
1773  0 setAlignFrameView();
1774    }
1775    }
1776   
 
1777  0 toggle public void setAlignFrameView()
1778    {
1779  0 alignFrame.setDisplayedView(this);
1780    }
1781   
 
1782  114 toggle @Override
1783    public StructureSelectionManager getStructureSelectionManager()
1784    {
1785  114 return av.getStructureSelectionManager();
1786    }
1787   
 
1788  0 toggle @Override
1789    public void raiseOOMWarning(String string, OutOfMemoryError error)
1790    {
1791  0 new OOMWarning(string, error, this);
1792    }
1793   
 
1794  20 toggle @Override
1795    public jalview.api.FeatureRenderer cloneFeatureRenderer()
1796    {
1797   
1798  20 return new FeatureRenderer(this);
1799    }
1800   
 
1801  344 toggle @Override
1802    public jalview.api.FeatureRenderer getFeatureRenderer()
1803    {
1804  344 return seqPanel.seqCanvas.getFeatureRenderer();
1805    }
1806   
 
1807  0 toggle public void updateFeatureRenderer(
1808    jalview.renderer.seqfeatures.FeatureRenderer fr)
1809    {
1810  0 fr.transferSettings(getSeqPanel().seqCanvas.getFeatureRenderer());
1811    }
1812   
 
1813  0 toggle public void updateFeatureRendererFrom(jalview.api.FeatureRenderer fr)
1814    {
1815  0 if (getSeqPanel().seqCanvas.getFeatureRenderer() != null)
1816    {
1817  0 getSeqPanel().seqCanvas.getFeatureRenderer().transferSettings(fr);
1818    }
1819    }
1820   
 
1821  584 toggle public ScalePanel getScalePanel()
1822    {
1823  584 return scalePanel;
1824    }
1825   
 
1826  513 toggle public void setScalePanel(ScalePanel scalePanel)
1827    {
1828  513 this.scalePanel = scalePanel;
1829    }
1830   
 
1831  14511 toggle public SeqPanel getSeqPanel()
1832    {
1833  14511 return seqPanel;
1834    }
1835   
 
1836  513 toggle public void setSeqPanel(SeqPanel seqPanel)
1837    {
1838  513 this.seqPanel = seqPanel;
1839    }
1840   
 
1841  18467 toggle public AnnotationPanel getAnnotationPanel()
1842    {
1843  18467 return annotationPanel;
1844    }
1845   
 
1846  513 toggle public void setAnnotationPanel(AnnotationPanel annotationPanel)
1847    {
1848  513 this.annotationPanel = annotationPanel;
1849    }
1850   
 
1851  18247 toggle public AnnotationLabels getAlabels()
1852    {
1853  18247 return alabels;
1854    }
1855   
 
1856  513 toggle public void setAlabels(AnnotationLabels alabels)
1857    {
1858  513 this.alabels = alabels;
1859    }
1860   
 
1861  15060 toggle public IdPanel getIdPanel()
1862    {
1863  15060 return idPanel;
1864    }
1865   
 
1866  513 toggle public void setIdPanel(IdPanel idPanel)
1867    {
1868  513 this.idPanel = idPanel;
1869    }
1870   
1871    /**
1872    * Follow a scrolling change in the (cDNA/Protein) complementary alignment.
1873    * The aim is to keep the two alignments 'lined up' on their centre columns.
1874    *
1875    * @param sr
1876    * holds mapped region(s) of this alignment that we are scrolling
1877    * 'to'; may be modified for sequence offset by this method
1878    * @param verticalOffset
1879    * the number of visible sequences to show above the mapped region
1880    */
 
1881  2 toggle protected void scrollToCentre(SearchResultsI sr, int verticalOffset)
1882    {
1883  2 scrollToPosition(sr, verticalOffset, true);
1884    }
1885   
1886    /**
1887    * Set a flag to say do not scroll any (cDNA/protein) complement.
1888    *
1889    * @param b
1890    */
 
1891  1328 toggle protected void setToScrollComplementPanel(boolean b)
1892    {
1893  1328 this.scrollComplementaryPanel = b;
1894    }
1895   
1896    /**
1897    * Get whether to scroll complement panel
1898    *
1899    * @return true if cDNA/protein complement panels should be scrolled
1900    */
 
1901  658 toggle protected boolean isSetToScrollComplementPanel()
1902    {
1903  658 return this.scrollComplementaryPanel;
1904    }
1905   
1906    /**
1907    * Redraw sensibly.
1908    *
1909    * @adjustHeight if true, try to recalculate panel height for visible
1910    * annotations
1911    */
 
1912  0 toggle protected void refresh(boolean adjustHeight)
1913    {
1914  0 validateAnnotationDimensions(adjustHeight);
1915  0 addNotify();
1916  0 if (adjustHeight)
1917    {
1918    // sort, repaint, update overview
1919  0 paintAlignment(true, false);
1920    }
1921    else
1922    {
1923    // lightweight repaint
1924  0 repaint();
1925    }
1926    }
1927   
 
1928  659 toggle @Override
1929    /**
1930    * Property change event fired when a change is made to the viewport ranges
1931    * object associated with this alignment panel's viewport
1932    */
1933    public void propertyChange(PropertyChangeEvent evt)
1934    {
1935    // update this panel's scroll values based on the new viewport ranges values
1936  659 updateScrollBarsFromRanges();
1937   
1938    // now update any complementary alignment (its viewport ranges object
1939    // is different so does not get automatically updated)
1940  658 if (isSetToScrollComplementPanel())
1941    {
1942  654 setToScrollComplementPanel(false);
1943  654 av.scrollComplementaryAlignment();
1944  654 setToScrollComplementPanel(true);
1945    }
1946    }
1947   
 
1948  1014 toggle void updateScrollBarsFromRanges()
1949    {
1950  1014 ViewportRanges ranges = av.getRanges();
1951  1014 setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
1952    }
1953    /**
1954    * Set the reference to the PCA/Tree chooser dialog for this panel. This
1955    * reference should be nulled when the dialog is closed.
1956    *
1957    * @param calculationChooser
1958    */
 
1959  0 toggle public void setCalculationDialog(CalculationChooser calculationChooser)
1960    {
1961  0 calculationDialog = calculationChooser;
1962    }
1963   
1964    /**
1965    * Returns the reference to the PCA/Tree chooser dialog for this panel (null
1966    * if none is open)
1967    */
 
1968  1113 toggle public CalculationChooser getCalculationDialog()
1969    {
1970  1113 return calculationDialog;
1971    }
1972   
1973    /**
1974    * Constructs and sets the title for the Overview window (if there is one),
1975    * including the align frame's title, and view name (if applicable). Returns
1976    * the title, or null if this panel has no Overview window open.
1977    *
1978    * @param alignFrame
1979    * @return
1980    */
 
1981  496 toggle public String setOverviewTitle(AlignFrame alignFrame)
1982    {
1983  496 if (this.overviewPanel == null)
1984    {
1985  394 return null;
1986    }
1987  102 String overviewTitle = MessageManager
1988    .formatMessage("label.overview_params", new Object[]
1989    { alignFrame.getTitle() });
1990  102 String viewName = getViewName();
1991  102 if (viewName != null)
1992    {
1993  65 overviewTitle += (" " + viewName);
1994    }
1995  102 overviewPanel.setTitle(overviewTitle);
1996  102 return overviewTitle;
1997    }
1998   
1999    /**
2000    * If this alignment panel has an Overview panel open, closes it
2001    */
 
2002  43 toggle public void closeOverviewPanel()
2003    {
2004  43 if (overviewPanel != null)
2005    {
2006  12 overviewPanel.close();
2007  12 overviewPanel = null;
2008    }
2009    }
2010    /**
2011    * From appletgui, for JalviewJS JavaScript interface
2012    *
2013    * preliminary - untested
2014    *
2015    * @param ostart
2016    * @param end
2017    * @param seqIndex
2018    * @param scrollToNearest
2019    * @param redrawOverview
2020    * @return
2021    */
 
2022  0 toggle public boolean scrollTo(int ostart, int end, int seqIndex,
2023    boolean scrollToNearest, boolean redrawOverview)
2024    {
2025  0 int startv, endv, starts, ends;// , width;
2026   
2027  0 int start = -1;
2028  0 if (av.hasHiddenColumns())
2029    {
2030  0 AlignmentI al = av.getAlignment();
2031  0 start = al.getHiddenColumns().absoluteToVisibleColumn(ostart);
2032  0 end = al.getHiddenColumns().absoluteToVisibleColumn(end);
2033  0 if (start == end)
2034    {
2035  0 if (!scrollToNearest && !al.getHiddenColumns().isVisible(ostart))
2036    {
2037    // don't scroll - position isn't visible
2038  0 return false;
2039    }
2040    }
2041    }
2042    else
2043    {
2044  0 start = ostart;
2045    }
2046   
2047  0 ViewportRanges ranges = av.getRanges();
2048  0 if (!av.getWrapAlignment())
2049    {
2050    /*
2051    * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =
2052    * av.getStartRes()) >= start) { spos=start-1; // seqIn //
2053    * setScrollValues(start - 1, seqIndex); } else if ((endv =
2054    * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end -
2055    * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts =
2056    * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),
2057    * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {
2058    * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }
2059    */
2060   
2061    // below is scrolling logic up to Jalview 2.8.2
2062    // if ((av.getStartRes() > end)
2063    // || (av.getEndRes() < start)
2064    // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
2065    // {
2066    // if (start > av.getAlignment().getWidth() - hextent)
2067    // {
2068    // start = av.getAlignment().getWidth() - hextent;
2069    // if (start < 0)
2070    // {
2071    // start = 0;
2072    // }
2073    //
2074    // }
2075    // if (seqIndex > av.getAlignment().getHeight() - vextent)
2076    // {
2077    // seqIndex = av.getAlignment().getHeight() - vextent;
2078    // if (seqIndex < 0)
2079    // {
2080    // seqIndex = 0;
2081    // }
2082    // }
2083    // setScrollValues(start, seqIndex);
2084    // }
2085    // logic copied from jalview.gui.AlignmentPanel:
2086  0 if ((startv = ranges.getStartRes()) >= start)
2087    {
2088    /*
2089    * Scroll left to make start of search results visible
2090    */
2091  0 setScrollValues(start - 1, seqIndex);
2092    }
2093  0 else if ((endv = ranges.getEndRes()) <= end)
2094    {
2095    /*
2096    * Scroll right to make end of search results visible
2097    */
2098  0 setScrollValues(startv + 1 + end - endv, seqIndex);
2099    }
2100  0 else if ((starts = ranges.getStartSeq()) > seqIndex)
2101    {
2102    /*
2103    * Scroll up to make start of search results visible
2104    */
2105  0 setScrollValues(ranges.getStartRes(), seqIndex);
2106    }
2107  0 else if ((ends = ranges.getEndSeq()) <= seqIndex)
2108    {
2109    /*
2110    * Scroll down to make end of search results visible
2111    */
2112  0 setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + 1);
2113    }
2114    /*
2115    * Else results are already visible - no need to scroll
2116    */
2117    }
2118    else
2119    {
2120  0 ranges.scrollToWrappedVisible(start);
2121    }
2122   
2123  0 paintAlignment(redrawOverview, false);
2124  0 return true;
2125    }
2126   
2127    private boolean holdRepaint = false;
2128   
2129    /**
2130    * Called by IdCanvas and SeqPanel to defer painting until after JVP loading.
2131    *
2132    * @return true if holding
2133    */
 
2134  6038 toggle public boolean getHoldRepaint()
2135    {
2136  6038 return holdRepaint;
2137    }
2138   
2139    /**
2140    * Called by Jalview2xml while loading
2141    *
2142    * @param tf
2143    */
 
2144  192 toggle public void setHoldRepaint(boolean tf)
2145    {
2146  192 if (holdRepaint == tf)
2147    {
2148  0 return;
2149    }
2150  192 holdRepaint = tf;
2151  192 if (!tf)
2152    {
2153  96 repaint();
2154    }
2155    }
2156   
 
2157  8258 toggle @Override
2158    public void repaint()
2159    {
2160  8258 if (holdRepaint)
2161    {
2162    // System.out.println("AP repaint holding");
2163    // Platform.stackTrace();
2164  922 return;
2165    }
2166  7336 super.repaint();
2167    }
2168   
 
2169  6 toggle public void selectAllSequences()
2170    {
2171  6 selectSequences(av.getAlignment().getSequences());
2172    }
2173   
 
2174  2 toggle public void deselectAllSequences()
2175    {
2176  2 if (av.cursorMode)
2177    {
2178  0 getSeqPanel().keyboardNo1 = null;
2179  0 getSeqPanel().keyboardNo2 = null;
2180    }
2181  2 av.setSelectionGroup(null);
2182  2 av.getColumnSelection().clear();
2183  2 av.setSearchResults(null);
2184  2 getIdPanel().getIdCanvas().searchResults = null;
2185  2 av.sendSelection();
2186    // JAL-2034 - should delegate to
2187    // alignPanel to decide if overview needs
2188    // updating.
2189  2 paintAlignment(false, false);
2190  2 PaintRefresher.Refresh(this, av.getSequenceSetId());
2191    }
2192   
 
2193  6 toggle public void selectSequences(List<SequenceI> seqs)
2194    {
2195  6 SequenceGroup sg = new SequenceGroup(seqs);
2196  6 sg.setEndRes(av.getAlignment().getWidth() - 1);
2197  6 av.setSelectionGroup(sg);
2198  6 av.isSelectionGroupChanged(true);
2199  6 av.sendSelection();
2200    // JAL-2034 - should delegate to
2201    // alignPanel to decide if overview needs
2202    // updating.
2203  6 paintAlignment(false, false);
2204  6 PaintRefresher.Refresh(this, av.getSequenceSetId());
2205    }
2206   
2207    }