Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.gui

File AlignmentPanel.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
40% of files have more coverage

Code metrics

206
520
65
1
1,760
1,124
184
0.35
8
65
2.83

Classes

Class Line # Actions
AlignmentPanel 77 520 184 394
0.501896350.2%
 

Contributing tests

This file is covered by 112 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 jalview.analysis.AnnotationSorter;
24    import jalview.api.AlignViewportI;
25    import jalview.api.AlignmentViewPanel;
26    import jalview.bin.Cache;
27    import jalview.bin.Jalview;
28    import jalview.datamodel.AlignmentI;
29    import jalview.datamodel.HiddenColumns;
30    import jalview.datamodel.SearchResultsI;
31    import jalview.datamodel.SequenceFeature;
32    import jalview.datamodel.SequenceGroup;
33    import jalview.datamodel.SequenceI;
34    import jalview.gui.ImageExporter.ImageWriterI;
35    import jalview.io.HTMLOutput;
36    import jalview.jbgui.GAlignmentPanel;
37    import jalview.math.AlignmentDimension;
38    import jalview.schemes.ResidueProperties;
39    import jalview.structure.StructureSelectionManager;
40    import jalview.util.Comparison;
41    import jalview.util.ImageMaker;
42    import jalview.util.MessageManager;
43    import jalview.util.Platform;
44    import jalview.viewmodel.ViewportListenerI;
45    import jalview.viewmodel.ViewportRanges;
46   
47    import java.awt.BorderLayout;
48    import java.awt.Color;
49    import java.awt.Container;
50    import java.awt.Dimension;
51    import java.awt.Font;
52    import java.awt.FontMetrics;
53    import java.awt.Graphics;
54    import java.awt.Graphics2D;
55    import java.awt.event.AdjustmentEvent;
56    import java.awt.event.AdjustmentListener;
57    import java.awt.event.ComponentAdapter;
58    import java.awt.event.ComponentEvent;
59    import java.awt.print.PageFormat;
60    import java.awt.print.Printable;
61    import java.awt.print.PrinterException;
62    import java.beans.PropertyChangeEvent;
63    import java.beans.PropertyChangeListener;
64    import java.io.File;
65    import java.io.FileWriter;
66    import java.io.PrintWriter;
67    import java.util.List;
68   
69    import javax.swing.SwingUtilities;
70   
71    /**
72    * DOCUMENT ME!
73    *
74    * @author $author$
75    * @version $Revision: 1.161 $
76    */
 
77    public class AlignmentPanel extends GAlignmentPanel implements
78    AdjustmentListener, Printable, AlignmentViewPanel, ViewportListenerI
79    {
80    public AlignViewport av;
81   
82    OverviewPanel overviewPanel;
83   
84    private SeqPanel seqPanel;
85   
86    private IdPanel idPanel;
87   
88    IdwidthAdjuster idwidthAdjuster;
89   
90    public AlignFrame alignFrame;
91   
92    private ScalePanel scalePanel;
93   
94    private AnnotationPanel annotationPanel;
95   
96    private AnnotationLabels alabels;
97   
98    private int hextent = 0;
99   
100    private int vextent = 0;
101   
102    /*
103    * Flag set while scrolling to follow complementary cDNA/protein scroll. When
104    * false, suppresses invoking the same method recursively.
105    */
106    private boolean scrollComplementaryPanel = true;
107   
108    private PropertyChangeListener propertyChangeListener;
109   
110    private CalculationChooser calculationDialog;
111   
112    /**
113    * Creates a new AlignmentPanel object.
114    *
115    * @param af
116    * @param av
117    */
 
118  217 toggle public AlignmentPanel(AlignFrame af, final AlignViewport av)
119    {
120  217 alignFrame = af;
121  217 this.av = av;
122  217 setSeqPanel(new SeqPanel(av, this));
123  217 setIdPanel(new IdPanel(av, this));
124   
125  217 setScalePanel(new ScalePanel(av, this));
126   
127  217 idPanelHolder.add(getIdPanel(), BorderLayout.CENTER);
128  217 idwidthAdjuster = new IdwidthAdjuster(this);
129  217 idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
130   
131  217 setAnnotationPanel(new AnnotationPanel(this));
132  217 setAlabels(new AnnotationLabels(this));
133   
134  217 annotationScroller.setViewportView(getAnnotationPanel());
135  217 annotationSpaceFillerHolder.add(getAlabels(), BorderLayout.CENTER);
136   
137  217 scalePanelHolder.add(getScalePanel(), BorderLayout.CENTER);
138  217 seqPanelHolder.add(getSeqPanel(), BorderLayout.CENTER);
139   
140  217 setScrollValues(0, 0);
141   
142  217 hscroll.addAdjustmentListener(this);
143  217 vscroll.addAdjustmentListener(this);
144   
145  217 addComponentListener(new ComponentAdapter()
146    {
 
147  180 toggle @Override
148    public void componentResized(ComponentEvent evt)
149    {
150    // reset the viewport ranges when the alignment panel is resized
151    // in particular, this initialises the end residue value when Jalview
152    // is initialised
153  180 ViewportRanges ranges = av.getRanges();
154  180 if (av.getWrapAlignment())
155    {
156  25 int widthInRes = getSeqPanel().seqCanvas.getWrappedCanvasWidth(
157    getSeqPanel().seqCanvas.getWidth());
158  25 ranges.setViewportWidth(widthInRes);
159    }
160    else
161    {
162  155 int widthInRes = getSeqPanel().seqCanvas.getWidth()
163    / av.getCharWidth();
164  155 int heightInSeq = getSeqPanel().seqCanvas.getHeight()
165    / av.getCharHeight();
166   
167  155 ranges.setViewportWidth(widthInRes);
168  155 ranges.setViewportHeight(heightInSeq);
169    }
170    }
171   
172    });
173   
174  217 final AlignmentPanel ap = this;
175  217 propertyChangeListener = new PropertyChangeListener()
176    {
 
177  67 toggle @Override
178    public void propertyChange(PropertyChangeEvent evt)
179    {
180  67 if (evt.getPropertyName().equals("alignment"))
181    {
182  67 PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
183  67 alignmentChanged();
184    }
185    }
186    };
187  217 av.addPropertyChangeListener(propertyChangeListener);
188   
189  217 av.getRanges().addPropertyChangeListener(this);
190  217 fontChanged();
191  217 adjustAnnotationHeight();
192  217 updateLayout();
193    }
194   
 
195  48 toggle @Override
196    public AlignViewportI getAlignViewport()
197    {
198  48 return av;
199    }
200   
 
201  267 toggle public void alignmentChanged()
202    {
203  267 av.alignmentChanged(this);
204   
205  267 if (getCalculationDialog() != null)
206    {
207  0 getCalculationDialog().validateCalcTypes();
208    }
209   
210  267 alignFrame.updateEditMenuBar();
211   
212    // no idea if we need to update structure
213  267 paintAlignment(true, true);
214   
215    }
216   
217    /**
218    * DOCUMENT ME!
219    */
 
220  509 toggle public void fontChanged()
221    {
222    // set idCanvas bufferedImage to null
223    // to prevent drawing old image
224  509 FontMetrics fm = getFontMetrics(av.getFont());
225   
226  509 scalePanelHolder.setPreferredSize(
227    new Dimension(10, av.getCharHeight() + fm.getDescent()));
228  509 idSpaceFillerPanel1.setPreferredSize(
229    new Dimension(10, av.getCharHeight() + fm.getDescent()));
230  509 idwidthAdjuster.invalidate();
231  509 scalePanelHolder.invalidate();
232  509 getIdPanel().getIdCanvas().gg = null;
233  509 getSeqPanel().seqCanvas.img = null;
234  509 getAnnotationPanel().adjustPanelHeight();
235   
236  509 Dimension d = calculateIdWidth();
237   
238  509 d.setSize(d.width + 4, d.height);
239  509 getIdPanel().getIdCanvas().setPreferredSize(d);
240  509 hscrollFillerPanel.setPreferredSize(d);
241   
242  509 repaint();
243    }
244   
245    /**
246    * Calculate the width of the alignment labels based on the displayed names
247    * and any bounds on label width set in preferences.
248    *
249    * @return Dimension giving the maximum width of the alignment label panel
250    * that should be used.
251    */
 
252  509 toggle public Dimension calculateIdWidth()
253    {
254    // calculate sensible default width when no preference is available
255  509 Dimension r = null;
256  509 if (av.getIdWidth() < 0)
257    {
258  188 int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300);
259  188 int maxwidth = Math.max(20, Math.min(afwidth - 200, 2 * afwidth / 3));
260  188 r = calculateIdWidth(maxwidth);
261  188 av.setIdWidth(r.width);
262    }
263    else
264    {
265  321 r = new Dimension();
266  321 r.width = av.getIdWidth();
267  321 r.height = 0;
268    }
269  509 return r;
270    }
271   
272    /**
273    * Calculate the width of the alignment labels based on the displayed names
274    * and any bounds on label width set in preferences.
275    *
276    * @param maxwidth
277    * -1 or maximum width allowed for IdWidth
278    * @return Dimension giving the maximum width of the alignment label panel
279    * that should be used.
280    */
 
281  188 toggle public Dimension calculateIdWidth(int maxwidth)
282    {
283  188 Container c = new Container();
284   
285  188 FontMetrics fm = c.getFontMetrics(
286    new Font(av.font.getName(), Font.ITALIC, av.font.getSize()));
287   
288  188 AlignmentI al = av.getAlignment();
289  188 int i = 0;
290  188 int idWidth = 0;
291  188 String id;
292   
293  2135 while ((i < al.getHeight()) && (al.getSequenceAt(i) != null))
294    {
295  1947 SequenceI s = al.getSequenceAt(i);
296   
297  1947 id = s.getDisplayId(av.getShowJVSuffix());
298   
299  1947 if (fm.stringWidth(id) > idWidth)
300    {
301  558 idWidth = fm.stringWidth(id);
302    }
303   
304  1947 i++;
305    }
306   
307    // Also check annotation label widths
308  188 i = 0;
309   
310  188 if (al.getAlignmentAnnotation() != null)
311    {
312  188 fm = c.getFontMetrics(getAlabels().getFont());
313   
314  1273 while (i < al.getAlignmentAnnotation().length)
315    {
316  1085 String label = al.getAlignmentAnnotation()[i].label;
317   
318  1085 if (fm.stringWidth(label) > idWidth)
319    {
320  136 idWidth = fm.stringWidth(label);
321    }
322   
323  1085 i++;
324    }
325    }
326   
327  188 return new Dimension(
328  188 maxwidth < 0 ? idWidth : Math.min(maxwidth, idWidth), 12);
329    }
330   
331    /**
332    * Highlight the given results on the alignment.
333    *
334    */
 
335  0 toggle public void highlightSearchResults(SearchResultsI results)
336    {
337  0 boolean scrolled = scrollToPosition(results, 0, true, false);
338   
339  0 boolean fastPaint = !(scrolled && av.getWrapAlignment());
340   
341  0 getSeqPanel().seqCanvas.highlightSearchResults(results, fastPaint);
342    }
343   
344    /**
345    * Scroll the view to show the position of the highlighted region in results
346    * (if any)
347    *
348    * @param searchResults
349    * @param redrawOverview
350    * @return
351    */
 
352  3 toggle public boolean scrollToPosition(SearchResultsI searchResults,
353    boolean redrawOverview)
354    {
355  3 return scrollToPosition(searchResults, 0, redrawOverview, false);
356    }
357   
358    /**
359    * Scrolls the view (if necessary) to show the position of the first
360    * highlighted region in results (if any). Answers true if the view was
361    * scrolled, or false if no matched region was found, or it is already
362    * visible.
363    *
364    * @param results
365    * @param verticalOffset
366    * if greater than zero, allows scrolling to a position below the
367    * first displayed sequence
368    * @param redrawOverview
369    * - when set, the overview will be recalculated (takes longer)
370    * @param centre
371    * if true, try to centre the search results horizontally in the view
372    * @return
373    */
 
374  3 toggle protected boolean scrollToPosition(SearchResultsI results,
375    int verticalOffset, boolean redrawOverview, boolean centre)
376    {
377  3 int startv, endv, starts, ends;
378  3 ViewportRanges ranges = av.getRanges();
379   
380  3 if (results == null || results.isEmpty() || av == null
381    || av.getAlignment() == null)
382    {
383  0 return false;
384    }
385  3 int seqIndex = av.getAlignment().findIndex(results);
386  3 if (seqIndex == -1)
387    {
388  2 return false;
389    }
390  1 SequenceI seq = av.getAlignment().getSequenceAt(seqIndex);
391   
392  1 int[] r = results.getResults(seq, 0, av.getAlignment().getWidth());
393  1 if (r == null)
394    {
395  0 return false;
396    }
397  1 int start = r[0];
398  1 int end = r[1];
399   
400    /*
401    * To centre results, scroll to positions half the visible width
402    * left/right of the start/end positions
403    */
404  1 if (centre)
405    {
406  0 int offset = (ranges.getEndRes() - ranges.getStartRes() + 1) / 2 - 1;
407  0 start = Math.max(start - offset, 0);
408  0 end = end + offset - 1;
409    }
410  1 if (start < 0)
411    {
412  0 return false;
413    }
414  1 if (end == seq.getEnd())
415    {
416  0 return false;
417    }
418   
419  1 if (av.hasHiddenColumns())
420    {
421  0 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
422  0 start = hidden.absoluteToVisibleColumn(start);
423  0 end = hidden.absoluteToVisibleColumn(end);
424  0 if (start == end)
425    {
426  0 if (!hidden.isVisible(r[0]))
427    {
428    // don't scroll - position isn't visible
429  0 return false;
430    }
431    }
432    }
433   
434    /*
435    * allow for offset of target sequence (actually scroll to one above it)
436    */
437  1 seqIndex = Math.max(0, seqIndex - verticalOffset);
438  1 boolean scrollNeeded = true;
439   
440  1 if (!av.getWrapAlignment())
441    {
442  ? if ((startv = ranges.getStartRes()) >= start)
443    {
444    /*
445    * Scroll left to make start of search results visible
446    */
447  0 setScrollValues(start, seqIndex);
448    }
449  ? else if ((endv = ranges.getEndRes()) <= end)
450    {
451    /*
452    * Scroll right to make end of search results visible
453    */
454  1 setScrollValues(startv + end - endv, seqIndex);
455    }
456  0 else if ((starts = ranges.getStartSeq()) > seqIndex)
457    {
458    /*
459    * Scroll up to make start of search results visible
460    */
461  0 setScrollValues(ranges.getStartRes(), seqIndex);
462    }
463  0 else if ((ends = ranges.getEndSeq()) <= seqIndex)
464    {
465    /*
466    * Scroll down to make end of search results visible
467    */
468  0 setScrollValues(ranges.getStartRes(), starts + seqIndex - ends
469    + 1);
470    }
471    /*
472    * Else results are already visible - no need to scroll
473    */
474  1 scrollNeeded = false;
475    }
476    else
477    {
478  0 scrollNeeded = ranges.scrollToWrappedVisible(start);
479    }
480   
481  1 paintAlignment(redrawOverview, false);
482   
483  1 return scrollNeeded;
484    }
485   
486    /**
487    * DOCUMENT ME!
488    *
489    * @return DOCUMENT ME!
490    */
 
491  0 toggle public OverviewPanel getOverviewPanel()
492    {
493  0 return overviewPanel;
494    }
495   
496    /**
497    * DOCUMENT ME!
498    *
499    * @param op
500    * DOCUMENT ME!
501    */
 
502  0 toggle public void setOverviewPanel(OverviewPanel op)
503    {
504  0 overviewPanel = op;
505    }
506   
507    /**
508    *
509    * @param b
510    * Hide or show annotation panel
511    *
512    */
 
513  292 toggle public void setAnnotationVisible(boolean b)
514    {
515  292 if (!av.getWrapAlignment())
516    {
517  278 annotationSpaceFillerHolder.setVisible(b);
518  278 annotationScroller.setVisible(b);
519    }
520  292 repaint();
521    }
522   
523    /**
524    * automatically adjust annotation panel height for new annotation whilst
525    * ensuring the alignment is still visible.
526    */
 
527  411 toggle @Override
528    public void adjustAnnotationHeight()
529    {
530    // TODO: display vertical annotation scrollbar if necessary
531    // this is called after loading new annotation onto alignment
532  411 if (alignFrame.getHeight() == 0)
533    {
534  0 System.out.println("NEEDS FIXING");
535    }
536  411 validateAnnotationDimensions(true);
537  411 addNotify();
538    // TODO: many places call this method and also paintAlignment with various
539    // different settings. this means multiple redraws are triggered...
540  411 paintAlignment(true, false);
541    }
542   
543    /**
544    * calculate the annotation dimensions and refresh slider values accordingly.
545    * need to do repaints/notifys afterwards.
546    */
 
547  411 toggle protected void validateAnnotationDimensions(boolean adjustPanelHeight)
548    {
549  411 int annotationHeight = getAnnotationPanel().adjustPanelHeight();
550   
551  411 if (adjustPanelHeight)
552    {
553  411 int rowHeight = av.getCharHeight();
554  411 int alignmentHeight = rowHeight * av.getAlignment().getHeight();
555   
556    /*
557    * Estimate available height in the AlignFrame for alignment +
558    * annotations. Deduct an estimate for title bar, menu bar, scale panel,
559    * hscroll, status bar, insets.
560    */
561  411 int stuff = Platform.isAMac() ? 120 : 140;
562  411 int availableHeight = alignFrame.getHeight() - stuff;
563   
564    /*
565    * If not enough vertical space, maximize annotation height while keeping
566    * at least two rows of alignment visible
567    */
568  411 if (annotationHeight + alignmentHeight > availableHeight)
569    {
570  269 annotationHeight = Math.min(annotationHeight,
571    availableHeight - 2 * rowHeight);
572    }
573    }
574    else
575    {
576    // maintain same window layout whilst updating sliders
577  0 annotationHeight = annotationScroller.getSize().height;
578    }
579  411 hscroll.addNotify();
580   
581  411 annotationScroller.setPreferredSize(
582    new Dimension(annotationScroller.getWidth(), annotationHeight));
583   
584  411 Dimension e = idPanel.getSize();
585  411 alabels.setSize(new Dimension(e.width, annotationHeight));
586   
587  411 annotationSpaceFillerHolder.setPreferredSize(new Dimension(
588    annotationSpaceFillerHolder.getWidth(), annotationHeight));
589  411 annotationScroller.validate();
590  411 annotationScroller.addNotify();
591    }
592   
593    /**
594    * update alignment layout for viewport settings
595    *
596    * @param wrap
597    * DOCUMENT ME!
598    */
 
599  292 toggle public void updateLayout()
600    {
601  292 fontChanged();
602  292 setAnnotationVisible(av.isShowAnnotation());
603  292 boolean wrap = av.getWrapAlignment();
604  292 ViewportRanges ranges = av.getRanges();
605  292 ranges.setStartSeq(0);
606  292 scalePanelHolder.setVisible(!wrap);
607  292 hscroll.setVisible(!wrap);
608  292 idwidthAdjuster.setVisible(!wrap);
609   
610  292 if (wrap)
611    {
612  14 annotationScroller.setVisible(false);
613  14 annotationSpaceFillerHolder.setVisible(false);
614    }
615  278 else if (av.isShowAnnotation())
616    {
617  278 annotationScroller.setVisible(true);
618  278 annotationSpaceFillerHolder.setVisible(true);
619    }
620   
621  292 int canvasWidth = getSeqPanel().seqCanvas.getWidth();
622  292 if (canvasWidth > 0)
623    { // may not yet be laid out
624  2 if (wrap)
625    {
626  2 int widthInRes = getSeqPanel().seqCanvas
627    .getWrappedCanvasWidth(canvasWidth);
628  2 ranges.setViewportWidth(widthInRes);
629    }
630    else
631    {
632  0 int widthInRes = (canvasWidth / av.getCharWidth());
633  0 int heightInSeq = (getSeqPanel().seqCanvas.getHeight()
634    / av.getCharHeight());
635   
636  0 ranges.setViewportWidth(widthInRes);
637  0 ranges.setViewportHeight(heightInSeq);
638    }
639    }
640   
641  292 idSpaceFillerPanel1.setVisible(!wrap);
642   
643  292 repaint();
644    }
645   
646    /**
647    * Adjust row/column scrollers to show a visible position in the alignment.
648    *
649    * @param x
650    * visible column to scroll to
651    * @param y
652    * visible row to scroll to
653    *
654    */
 
655  1586 toggle public void setScrollValues(int xpos, int ypos)
656    {
657  1586 int x = xpos;
658  1586 int y = ypos;
659   
660  1586 if (av == null || av.getAlignment() == null)
661    {
662  0 return;
663    }
664   
665  1586 if (av.getWrapAlignment())
666    {
667  178 setScrollingForWrappedPanel(x);
668    }
669    else
670    {
671  1408 int width = av.getAlignment().getWidth();
672  1408 int height = av.getAlignment().getHeight();
673   
674  1408 if (av.hasHiddenColumns())
675    {
676    // reset the width to exclude hidden columns
677  190 width = av.getAlignment().getHiddenColumns()
678    .absoluteToVisibleColumn(width);
679    }
680   
681  1408 hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
682  1408 vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
683   
684  1408 if (hextent > width)
685    {
686  24 hextent = width;
687    }
688   
689  1408 if (vextent > height)
690    {
691  56 vextent = height;
692    }
693   
694  1408 if ((hextent + x) > width)
695    {
696  2 x = width - hextent;
697    }
698   
699  1408 if ((vextent + y) > height)
700    {
701  8 y = height - vextent;
702    }
703   
704  1408 if (y < 0)
705    {
706  0 y = 0;
707    }
708   
709  1408 if (x < 0)
710    {
711  1 x = 0;
712    }
713   
714    // update the scroll values
715  1408 hscroll.setValues(x, hextent, 0, width);
716  1408 vscroll.setValues(y, vextent, 0, height);
717    }
718    }
719   
720    /**
721    * Respond to adjustment event when horizontal or vertical scrollbar is
722    * changed
723    *
724    * @param evt
725    * adjustment event encoding whether hscroll or vscroll changed
726    */
 
727  381 toggle @Override
728    public void adjustmentValueChanged(AdjustmentEvent evt)
729    {
730  381 if (av.getWrapAlignment())
731    {
732  26 adjustScrollingWrapped(evt);
733  26 return;
734    }
735   
736  355 ViewportRanges ranges = av.getRanges();
737   
738  355 if (evt.getSource() == hscroll)
739    {
740  141 int oldX = ranges.getStartRes();
741  141 int oldwidth = ranges.getViewportWidth();
742  141 int x = hscroll.getValue();
743  141 int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth();
744   
745    // if we're scrolling to the position we're already at, stop
746    // this prevents infinite recursion of events when the scroll/viewport
747    // ranges values are the same
748  141 if ((x == oldX) && (width == oldwidth))
749    {
750  32 return;
751    }
752  109 ranges.setViewportStartAndWidth(x, width);
753    }
754  214 else if (evt.getSource() == vscroll)
755    {
756  214 int oldY = ranges.getStartSeq();
757  214 int oldheight = ranges.getViewportHeight();
758  214 int y = vscroll.getValue();
759  214 int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight();
760   
761    // if we're scrolling to the position we're already at, stop
762    // this prevents infinite recursion of events when the scroll/viewport
763    // ranges values are the same
764  214 if ((y == oldY) && (height == oldheight))
765    {
766  44 return;
767    }
768  170 ranges.setViewportStartAndHeight(y, height);
769    }
770  279 repaint();
771    }
772   
773    /**
774    * Responds to a scroll change by setting the start position of the viewport.
775    * Does
776    *
777    * @param evt
778    */
 
779  26 toggle protected void adjustScrollingWrapped(AdjustmentEvent evt)
780    {
781  26 if (evt.getSource() == hscroll)
782    {
783  0 return; // no horizontal scroll when wrapped
784    }
785  26 final ViewportRanges ranges = av.getRanges();
786   
787  26 if (evt.getSource() == vscroll)
788    {
789  26 int newY = vscroll.getValue();
790   
791    /*
792    * if we're scrolling to the position we're already at, stop
793    * this prevents infinite recursion of events when the scroll/viewport
794    * ranges values are the same
795    */
796  26 int oldX = ranges.getStartRes();
797  26 int oldY = ranges.getWrappedScrollPosition(oldX);
798  26 if (oldY == newY)
799    {
800  26 return;
801    }
802  0 if (newY > -1)
803    {
804    /*
805    * limit page up/down to one width's worth of positions
806    */
807  0 int rowSize = ranges.getViewportWidth();
808  0 int newX = newY > oldY ? oldX + rowSize : oldX - rowSize;
809  0 ranges.setViewportStartAndWidth(Math.max(0, newX), rowSize);
810    }
811    }
812    else
813    {
814    // This is only called if file loaded is a jar file that
815    // was wrapped when saved and user has wrap alignment true
816    // as preference setting
817  0 SwingUtilities.invokeLater(new Runnable()
818    {
 
819  0 toggle @Override
820    public void run()
821    {
822    // When updating scrolling to use ViewportChange events, this code
823    // could not be validated and it is not clear if it is now being
824    // called. Log warning here in case it is called and unforeseen
825    // problems occur
826  0 Cache.log.warn(
827    "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences");
828   
829    // scroll to start of panel
830  0 ranges.setStartRes(0);
831  0 ranges.setStartSeq(0);
832    }
833    });
834    }
835  0 repaint();
836    }
837   
838    /* (non-Javadoc)
839    * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean)
840    */
 
841  1786 toggle @Override
842    public void paintAlignment(boolean updateOverview,
843    boolean updateStructures)
844    {
845  1786 final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
846    av.isShowAutocalculatedAbove());
847  1786 sorter.sort(getAlignment().getAlignmentAnnotation(),
848    av.getSortAnnotationsBy());
849  1786 repaint();
850   
851  1786 if (updateStructures)
852    {
853  1328 av.getStructureSelectionManager().sequenceColoursChanged(this);
854    }
855  1786 if (updateOverview)
856    {
857   
858  1772 if (overviewPanel != null)
859    {
860  0 overviewPanel.updateOverviewImage();
861    }
862    }
863    }
864   
865    /**
866    * DOCUMENT ME!
867    *
868    * @param g
869    * DOCUMENT ME!
870    */
 
871  908 toggle @Override
872    public void paintComponent(Graphics g)
873    {
874  908 invalidate(); // needed so that the id width adjuster works correctly
875   
876  908 Dimension d = getIdPanel().getIdCanvas().getPreferredSize();
877  908 idPanelHolder.setPreferredSize(d);
878  908 hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));
879   
880  908 validate(); // needed so that the id width adjuster works correctly
881   
882    /*
883    * set scroll bar positions - tried to remove but necessary for split panel to resize correctly
884    * though I still think this call should be elsewhere.
885    */
886  908 ViewportRanges ranges = av.getRanges();
887  908 setScrollValues(ranges.getStartRes(), ranges.getStartSeq());
888    }
889   
890    /**
891    * Set vertical scroll bar position, and number of increments, for wrapped
892    * panel
893    *
894    * @param topLeftColumn
895    * the column position at top left (0..)
896    */
 
897  178 toggle private void setScrollingForWrappedPanel(int topLeftColumn)
898    {
899  178 ViewportRanges ranges = av.getRanges();
900  178 int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn);
901  178 int maxScroll = ranges.getWrappedMaxScroll(topLeftColumn);
902   
903    /*
904    * a scrollbar's value can be set to at most (maximum-extent)
905    * so we add extent (1) to the maxScroll value
906    */
907  178 vscroll.setUnitIncrement(1);
908  178 vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1);
909    }
910   
911    /**
912    * DOCUMENT ME!
913    *
914    * @param pg
915    * DOCUMENT ME!
916    * @param pf
917    * DOCUMENT ME!
918    * @param pi
919    * DOCUMENT ME!
920    *
921    * @return DOCUMENT ME!
922    *
923    * @throws PrinterException
924    * DOCUMENT ME!
925    */
 
926  0 toggle @Override
927    public int print(Graphics pg, PageFormat pf, int pi)
928    throws PrinterException
929    {
930  0 pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
931   
932  0 int pwidth = (int) pf.getImageableWidth();
933  0 int pheight = (int) pf.getImageableHeight();
934   
935  0 if (av.getWrapAlignment())
936    {
937  0 return printWrappedAlignment(pwidth, pheight, pi, pg);
938    }
939    else
940    {
941  0 return printUnwrapped(pwidth, pheight, pi, pg, pg);
942    }
943    }
944   
945    /**
946    * Draws the alignment image, including sequence ids, sequences, and
947    * annotation labels and annotations if shown, on either one or two Graphics
948    * contexts.
949    *
950    * @param pageWidth
951    * in pixels
952    * @param pageHeight
953    * in pixels
954    * @param pageIndex
955    * (0, 1, ...)
956    * @param idGraphics
957    * the graphics context for sequence ids and annotation labels
958    * @param alignmentGraphics
959    * the graphics context for sequences and annotations (may or may not
960    * be the same context as idGraphics)
961    * @return
962    * @throws PrinterException
963    */
 
964  0 toggle public int printUnwrapped(int pageWidth, int pageHeight, int pageIndex,
965    Graphics idGraphics, Graphics alignmentGraphics)
966    throws PrinterException
967    {
968  0 final int idWidth = getVisibleIdWidth(false);
969   
970    /*
971    * Get the horizontal offset to where we draw the sequences.
972    * This is idWidth if using a single Graphics context, else zero.
973    */
974  0 final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0
975    : idWidth;
976   
977  0 FontMetrics fm = getFontMetrics(av.getFont());
978  0 final int charHeight = av.getCharHeight();
979  0 final int scaleHeight = charHeight + fm.getDescent();
980   
981  0 idGraphics.setColor(Color.white);
982  0 idGraphics.fillRect(0, 0, pageWidth, pageHeight);
983  0 idGraphics.setFont(av.getFont());
984   
985    /*
986    * How many sequences and residues can we fit on a printable page?
987    */
988  0 final int totalRes = (pageWidth - idWidth) / av.getCharWidth();
989   
990  0 final int totalSeq = (pageHeight - scaleHeight) / charHeight - 1;
991   
992  0 final int alignmentWidth = av.getAlignment().getWidth();
993  0 final int pagesWide = (alignmentWidth / totalRes) + 1;
994   
995  0 final int startRes = (pageIndex % pagesWide) * totalRes;
996  0 final int endRes = Math.min(startRes + totalRes - 1,
997    alignmentWidth - 1);
998   
999  0 final int startSeq = (pageIndex / pagesWide) * totalSeq;
1000  0 final int alignmentHeight = av.getAlignment().getHeight();
1001  0 final int endSeq = Math.min(startSeq + totalSeq, alignmentHeight);
1002   
1003  0 int pagesHigh = ((alignmentHeight / totalSeq) + 1) * pageHeight;
1004   
1005  0 if (av.isShowAnnotation())
1006    {
1007  0 pagesHigh += getAnnotationPanel().adjustPanelHeight() + 3;
1008    }
1009   
1010  0 pagesHigh /= pageHeight;
1011   
1012  0 if (pageIndex >= (pagesWide * pagesHigh))
1013    {
1014  0 return Printable.NO_SUCH_PAGE;
1015    }
1016  0 final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight + 3;
1017   
1018    /*
1019    * draw the Scale at horizontal offset, then reset to top left (0, 0)
1020    */
1021  0 alignmentGraphics.translate(alignmentGraphicsOffset, 0);
1022  0 getScalePanel().drawScale(alignmentGraphics, startRes, endRes,
1023    pageWidth - idWidth, scaleHeight);
1024  0 alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
1025   
1026    /*
1027    * Draw the sequence ids, offset for scale height,
1028    * then reset to top left (0, 0)
1029    */
1030  0 idGraphics.translate(0, scaleHeight);
1031  0 IdCanvas idCanvas = getIdPanel().getIdCanvas();
1032  0 List<SequenceI> selection = av.getSelectionGroup() == null ? null
1033    : av.getSelectionGroup().getSequences(null);
1034  0 idCanvas.drawIds((Graphics2D) idGraphics, av, startSeq, endSeq - 1,
1035    selection);
1036   
1037  0 idGraphics.setFont(av.getFont());
1038  0 idGraphics.translate(0, -scaleHeight);
1039   
1040    /*
1041    * draw the sequences, offset for scale height, and id width (if using a
1042    * single graphics context), then reset to (0, scale height)
1043    */
1044  0 alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight);
1045  0 getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics, startRes,
1046    endRes, startSeq, endSeq - 1);
1047  0 alignmentGraphics.translate(-alignmentGraphicsOffset, 0);
1048   
1049  0 if (av.isShowAnnotation() && (endSeq == alignmentHeight))
1050    {
1051    /*
1052    * draw annotation labels; drawComponent() translates by
1053    * getScrollOffset(), so compensate for that first;
1054    * then reset to (0, scale height)
1055    */
1056  0 int offset = getAlabels().getScrollOffset();
1057  0 idGraphics.translate(0, -offset);
1058  0 idGraphics.translate(0, alignmentDrawnHeight);
1059  0 getAlabels().drawComponent(idGraphics, idWidth);
1060  0 idGraphics.translate(0, -alignmentDrawnHeight);
1061   
1062    /*
1063    * draw the annotations starting at
1064    * (idOffset, alignmentHeight) from (0, scaleHeight)
1065    */
1066  0 alignmentGraphics.translate(alignmentGraphicsOffset,
1067    alignmentDrawnHeight);
1068  0 getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av,
1069    alignmentGraphics, -1, startRes, endRes + 1);
1070    }
1071   
1072  0 return Printable.PAGE_EXISTS;
1073    }
1074   
1075    /**
1076    * Prints one page of an alignment in wrapped mode. Returns
1077    * Printable.PAGE_EXISTS (0) if a page was drawn, or Printable.NO_SUCH_PAGE if
1078    * no page could be drawn (page number out of range).
1079    *
1080    * @param pageWidth
1081    * @param pageHeight
1082    * @param pageNumber
1083    * (0, 1, ...)
1084    * @param g
1085    *
1086    * @return
1087    *
1088    * @throws PrinterException
1089    */
 
1090  0 toggle public int printWrappedAlignment(int pageWidth, int pageHeight, int pageNumber,
1091    Graphics g) throws PrinterException
1092    {
1093  0 int annotationHeight = 0;
1094  0 if (av.isShowAnnotation())
1095    {
1096  0 annotationHeight = getAnnotationPanel().adjustPanelHeight();
1097    }
1098   
1099  0 int hgap = av.getCharHeight();
1100  0 if (av.getScaleAboveWrapped())
1101    {
1102  0 hgap += av.getCharHeight();
1103    }
1104   
1105  0 int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1106    + annotationHeight;
1107   
1108  0 int idWidth = getVisibleIdWidth(false);
1109   
1110  0 int maxwidth = av.getAlignment().getWidth();
1111  0 if (av.hasHiddenColumns())
1112    {
1113  0 maxwidth = av.getAlignment().getHiddenColumns()
1114    .absoluteToVisibleColumn(maxwidth) - 1;
1115    }
1116   
1117  0 int resWidth = getSeqPanel().seqCanvas
1118    .getWrappedCanvasWidth(pageWidth - idWidth);
1119   
1120  0 int totalHeight = cHeight * (maxwidth / resWidth + 1);
1121   
1122  0 g.setColor(Color.white);
1123  0 g.fillRect(0, 0, pageWidth, pageHeight);
1124  0 g.setFont(av.getFont());
1125  0 g.setColor(Color.black);
1126   
1127    /*
1128    * method: print the whole wrapped alignment, but with a clip region that
1129    * is restricted to the requested page; this supports selective print of
1130    * single pages or ranges, (at the cost of some repeated processing in
1131    * the 'normal' case, when all pages are printed)
1132    */
1133  0 g.translate(0, -pageNumber * pageHeight);
1134   
1135  0 g.setClip(0, pageNumber * pageHeight, pageWidth, pageHeight);
1136   
1137    /*
1138    * draw sequence ids and annotation labels (if shown)
1139    */
1140  0 IdCanvas idCanvas = getIdPanel().getIdCanvas();
1141  0 idCanvas.drawIdsWrapped((Graphics2D) g, av, 0, totalHeight);
1142   
1143  0 g.translate(idWidth, 0);
1144   
1145  0 getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(g, pageWidth - idWidth,
1146    totalHeight, 0);
1147   
1148  0 if ((pageNumber * pageHeight) < totalHeight)
1149    {
1150  0 return Printable.PAGE_EXISTS;
1151    }
1152    else
1153    {
1154  0 return Printable.NO_SUCH_PAGE;
1155    }
1156    }
1157   
1158    /**
1159    * get current sequence ID panel width, or nominal value if panel were to be
1160    * displayed using default settings
1161    *
1162    * @return
1163    */
 
1164  0 toggle public int getVisibleIdWidth()
1165    {
1166  0 return getVisibleIdWidth(true);
1167    }
1168   
1169    /**
1170    * get current sequence ID panel width, or nominal value if panel were to be
1171    * displayed using default settings
1172    *
1173    * @param onscreen
1174    * indicate if the Id width for onscreen or offscreen display should
1175    * be returned
1176    * @return
1177    */
 
1178  0 toggle public int getVisibleIdWidth(boolean onscreen)
1179    {
1180    // see if rendering offscreen - check preferences and calc width accordingly
1181  0 if (!onscreen && Cache.getDefault("FIGURE_AUTOIDWIDTH", false))
1182    {
1183  0 return calculateIdWidth(-1).width + 4;
1184    }
1185  0 Integer idwidth = null;
1186  0 if (onscreen || (idwidth = Cache
1187    .getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null)
1188    {
1189  0 int w = getIdPanel().getWidth();
1190  0 return (w > 0 ? w : calculateIdWidth().width + 4);
1191    }
1192  0 return idwidth.intValue() + 4;
1193    }
1194   
1195    /**
1196    * Builds an image of the alignment of the specified type (EPS/PNG/SVG) and
1197    * writes it to the specified file
1198    *
1199    * @param type
1200    * @param file
1201    */
 
1202  0 toggle void makeAlignmentImage(ImageMaker.TYPE type, File file)
1203    {
1204  0 final int borderBottomOffset = 5;
1205   
1206  0 AlignmentDimension aDimension = getAlignmentDimension();
1207    // todo use a lambda function in place of callback here?
1208  0 ImageWriterI writer = new ImageWriterI()
1209    {
 
1210  0 toggle @Override
1211    public void exportImage(Graphics graphics) throws Exception
1212    {
1213  0 if (av.getWrapAlignment())
1214    {
1215  0 printWrappedAlignment(aDimension.getWidth(),
1216    aDimension.getHeight() + borderBottomOffset, 0, graphics);
1217    }
1218    else
1219    {
1220  0 printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0,
1221    graphics, graphics);
1222    }
1223    }
1224    };
1225   
1226  0 String fileTitle = alignFrame.getTitle();
1227  0 ImageExporter exporter = new ImageExporter(writer, alignFrame, type,
1228    fileTitle);
1229  0 int imageWidth = aDimension.getWidth();
1230  0 int imageHeight = aDimension.getHeight() + borderBottomOffset;
1231  0 String of = MessageManager.getString("label.alignment");
1232  0 exporter.doExport(file, this, imageWidth, imageHeight, of);
1233    }
1234   
1235    /**
1236    * Calculates and returns a suitable width and height (in pixels) for an
1237    * exported image
1238    *
1239    * @return
1240    */
 
1241  0 toggle public AlignmentDimension getAlignmentDimension()
1242    {
1243  0 int maxwidth = av.getAlignment().getWidth();
1244  0 if (av.hasHiddenColumns())
1245    {
1246  0 maxwidth = av.getAlignment().getHiddenColumns()
1247    .absoluteToVisibleColumn(maxwidth);
1248    }
1249   
1250  0 int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight())
1251    + getScalePanel().getHeight();
1252  0 int width = getVisibleIdWidth(false) + (maxwidth * av.getCharWidth());
1253   
1254  0 if (av.getWrapAlignment())
1255    {
1256  0 height = getWrappedHeight();
1257  0 if (Jalview.isHeadlessMode())
1258    {
1259    // need to obtain default alignment width and then add in any
1260    // additional allowance for id margin
1261    // this duplicates the calculation in getWrappedHeight but adjusts for
1262    // offscreen idWith
1263  0 width = alignFrame.getWidth() - vscroll.getPreferredSize().width
1264    - alignFrame.getInsets().left - alignFrame.getInsets().right
1265    - getVisibleIdWidth() + getVisibleIdWidth(false);
1266    }
1267    else
1268    {
1269  0 width = getSeqPanel().getWidth() + getVisibleIdWidth(false);
1270    }
1271   
1272    }
1273  0 else if (av.isShowAnnotation())
1274    {
1275  0 height += getAnnotationPanel().adjustPanelHeight() + 3;
1276    }
1277  0 return new AlignmentDimension(width, height);
1278   
1279    }
1280   
 
1281  0 toggle public void makePNGImageMap(File imgMapFile, String imageName)
1282    {
1283    // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS
1284    // ////////////////////////////////////////////
1285  0 int idWidth = getVisibleIdWidth(false);
1286  0 FontMetrics fm = getFontMetrics(av.getFont());
1287  0 int scaleHeight = av.getCharHeight() + fm.getDescent();
1288   
1289    // Gen image map
1290    // ////////////////////////////////
1291  0 if (imgMapFile != null)
1292    {
1293  0 try
1294    {
1295  0 int sSize = av.getAlignment().getHeight();
1296  0 int alwidth = av.getAlignment().getWidth();
1297  0 PrintWriter out = new PrintWriter(new FileWriter(imgMapFile));
1298  0 out.println(HTMLOutput.getImageMapHTML());
1299  0 out.println("<img src=\"" + imageName
1300    + "\" border=\"0\" usemap=\"#Map\" >"
1301    + "<map name=\"Map\">");
1302   
1303  0 for (int s = 0; s < sSize; s++)
1304    {
1305  0 int sy = s * av.getCharHeight() + scaleHeight;
1306   
1307  0 SequenceI seq = av.getAlignment().getSequenceAt(s);
1308  0 SequenceGroup[] groups = av.getAlignment().findAllGroups(seq);
1309  0 for (int column = 0; column < alwidth; column++)
1310    {
1311  0 StringBuilder text = new StringBuilder(512);
1312  0 String triplet = null;
1313  0 if (av.getAlignment().isNucleotide())
1314    {
1315  0 triplet = ResidueProperties.nucleotideName.get(seq
1316    .getCharAt(column) + "");
1317    }
1318    else
1319    {
1320  0 triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(column)
1321    + "");
1322    }
1323   
1324  0 if (triplet == null)
1325    {
1326  0 continue;
1327    }
1328   
1329  0 int seqPos = seq.findPosition(column);
1330  0 int gSize = groups.length;
1331  0 for (int g = 0; g < gSize; g++)
1332    {
1333  0 if (text.length() < 1)
1334    {
1335  0 text.append("<area shape=\"rect\" coords=\"")
1336    .append((idWidth + column * av.getCharWidth()))
1337    .append(",").append(sy).append(",")
1338    .append((idWidth + (column + 1) * av.getCharWidth()))
1339    .append(",").append((av.getCharHeight() + sy))
1340    .append("\"").append(" onMouseOver=\"toolTip('")
1341    .append(seqPos).append(" ").append(triplet);
1342    }
1343   
1344  0 if (groups[g].getStartRes() < column
1345    && groups[g].getEndRes() > column)
1346    {
1347  0 text.append("<br><em>").append(groups[g].getName())
1348    .append("</em>");
1349    }
1350    }
1351   
1352  0 if (text.length() < 1)
1353    {
1354  0 text.append("<area shape=\"rect\" coords=\"")
1355    .append((idWidth + column * av.getCharWidth()))
1356    .append(",").append(sy).append(",")
1357    .append((idWidth + (column + 1) * av.getCharWidth()))
1358    .append(",").append((av.getCharHeight() + sy))
1359    .append("\"").append(" onMouseOver=\"toolTip('")
1360    .append(seqPos).append(" ").append(triplet);
1361    }
1362  0 if (!Comparison.isGap(seq.getCharAt(column)))
1363    {
1364  0 List<SequenceFeature> features = seq.findFeatures(column, column);
1365  0 for (SequenceFeature sf : features)
1366    {
1367  0 if (sf.isContactFeature())
1368    {
1369  0 text.append("<br>").append(sf.getType()).append(" ")
1370    .append(sf.getBegin()).append(":")
1371    .append(sf.getEnd());
1372    }
1373    else
1374    {
1375  0 text.append("<br>");
1376  0 text.append(sf.getType());
1377  0 String description = sf.getDescription();
1378  0 if (description != null
1379    && !sf.getType().equals(description))
1380    {
1381  0 description = description.replace("\"", "&quot;");
1382  0 text.append(" ").append(description);
1383    }
1384    }
1385  0 String status = sf.getStatus();
1386  0 if (status != null && !"".equals(status))
1387    {
1388  0 text.append(" (").append(status).append(")");
1389    }
1390    }
1391  0 if (text.length() > 1)
1392    {
1393  0 text.append("')\"; onMouseOut=\"toolTip()\"; href=\"#\">");
1394  0 out.println(text.toString());
1395    }
1396    }
1397    }
1398    }
1399  0 out.println("</map></body></html>");
1400  0 out.close();
1401   
1402    } catch (Exception ex)
1403    {
1404  0 ex.printStackTrace();
1405    }
1406    } // /////////END OF IMAGE MAP
1407   
1408    }
1409   
 
1410  0 toggle int getWrappedHeight()
1411    {
1412  0 int seqPanelWidth = getSeqPanel().seqCanvas.getWidth();
1413   
1414  0 if (System.getProperty("java.awt.headless") != null
1415    && System.getProperty("java.awt.headless").equals("true"))
1416    {
1417  0 seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth()
1418    - vscroll.getPreferredSize().width
1419    - alignFrame.getInsets().left - alignFrame.getInsets().right;
1420    }
1421   
1422  0 int chunkWidth = getSeqPanel().seqCanvas
1423    .getWrappedCanvasWidth(seqPanelWidth);
1424   
1425  0 int hgap = av.getCharHeight();
1426  0 if (av.getScaleAboveWrapped())
1427    {
1428  0 hgap += av.getCharHeight();
1429    }
1430   
1431  0 int annotationHeight = 0;
1432  0 if (av.isShowAnnotation())
1433    {
1434  0 annotationHeight = getAnnotationPanel().adjustPanelHeight();
1435    }
1436   
1437  0 int cHeight = av.getAlignment().getHeight() * av.getCharHeight() + hgap
1438    + annotationHeight;
1439   
1440  0 int maxwidth = av.getAlignment().getWidth();
1441  0 if (av.hasHiddenColumns())
1442    {
1443  0 maxwidth = av.getAlignment().getHiddenColumns()
1444    .absoluteToVisibleColumn(maxwidth) - 1;
1445    }
1446   
1447  0 int height = ((maxwidth / chunkWidth) + 1) * cHeight;
1448   
1449  0 return height;
1450    }
1451   
1452    /**
1453    * close the panel - deregisters all listeners and nulls any references to
1454    * alignment data.
1455    */
 
1456  91 toggle public void closePanel()
1457    {
1458  91 PaintRefresher.RemoveComponent(getSeqPanel().seqCanvas);
1459  91 PaintRefresher.RemoveComponent(getIdPanel().getIdCanvas());
1460  91 PaintRefresher.RemoveComponent(this);
1461   
1462  91 closeChildFrames();
1463   
1464    /*
1465    * try to ensure references are nulled
1466    */
1467  91 if (annotationPanel != null)
1468    {
1469  91 annotationPanel.dispose();
1470  91 annotationPanel = null;
1471    }
1472   
1473  91 if (av != null)
1474    {
1475  91 av.removePropertyChangeListener(propertyChangeListener);
1476  91 propertyChangeListener = null;
1477  91 StructureSelectionManager ssm = av.getStructureSelectionManager();
1478  91 ssm.removeStructureViewerListener(getSeqPanel(), null);
1479  91 ssm.removeSelectionListener(getSeqPanel());
1480  91 ssm.removeCommandListener(av);
1481  91 ssm.removeStructureViewerListener(getSeqPanel(), null);
1482  91 ssm.removeSelectionListener(getSeqPanel());
1483  91 av.dispose();
1484  91 av = null;
1485    }
1486    else
1487    {
1488  0 if (Cache.log.isDebugEnabled())
1489    {
1490  0 Cache.log.warn("Closing alignment panel which is already closed.");
1491    }
1492    }
1493    }
1494   
1495    /**
1496    * Close any open dialogs that would be orphaned when this one is closed
1497    */
 
1498  91 toggle protected void closeChildFrames()
1499    {
1500  91 if (overviewPanel != null)
1501    {
1502  0 overviewPanel.dispose();
1503  0 overviewPanel = null;
1504    }
1505  91 if (calculationDialog != null)
1506    {
1507  0 calculationDialog.closeFrame();
1508  0 calculationDialog = null;
1509    }
1510    }
1511   
1512    /**
1513    * hides or shows dynamic annotation rows based on groups and av state flags
1514    */
 
1515  45 toggle public void updateAnnotation()
1516    {
1517  45 updateAnnotation(false, false);
1518    }
1519   
 
1520  0 toggle public void updateAnnotation(boolean applyGlobalSettings)
1521    {
1522  0 updateAnnotation(applyGlobalSettings, false);
1523    }
1524   
 
1525  117 toggle public void updateAnnotation(boolean applyGlobalSettings,
1526    boolean preserveNewGroupSettings)
1527    {
1528  117 av.updateGroupAnnotationSettings(applyGlobalSettings,
1529    preserveNewGroupSettings);
1530  117 adjustAnnotationHeight();
1531    }
1532   
 
1533  3872 toggle @Override
1534    public AlignmentI getAlignment()
1535    {
1536  3872 return av == null ? null : av.getAlignment();
1537    }
1538   
 
1539  52 toggle @Override
1540    public String getViewName()
1541    {
1542  52 return av.viewName;
1543    }
1544   
1545    /**
1546    * Make/Unmake this alignment panel the current input focus
1547    *
1548    * @param b
1549    */
 
1550  0 toggle public void setSelected(boolean b)
1551    {
1552  0 try
1553    {
1554  0 if (alignFrame.getSplitViewContainer() != null)
1555    {
1556    /*
1557    * bring enclosing SplitFrame to front first if there is one
1558    */
1559  0 ((SplitFrame) alignFrame.getSplitViewContainer()).setSelected(b);
1560    }
1561  0 alignFrame.setSelected(b);
1562    } catch (Exception ex)
1563    {
1564    }
1565   
1566  0 if (b)
1567    {
1568  0 alignFrame.setDisplayedView(this);
1569    }
1570    }
1571   
 
1572  19 toggle @Override
1573    public StructureSelectionManager getStructureSelectionManager()
1574    {
1575  19 return av.getStructureSelectionManager();
1576    }
1577   
 
1578  0 toggle @Override
1579    public void raiseOOMWarning(String string, OutOfMemoryError error)
1580    {
1581  0 new OOMWarning(string, error, this);
1582    }
1583   
 
1584  8 toggle @Override
1585    public jalview.api.FeatureRenderer cloneFeatureRenderer()
1586    {
1587   
1588  8 return new FeatureRenderer(this);
1589    }
1590   
 
1591  32 toggle @Override
1592    public jalview.api.FeatureRenderer getFeatureRenderer()
1593    {
1594  32 return seqPanel.seqCanvas.getFeatureRenderer();
1595    }
1596   
 
1597  0 toggle public void updateFeatureRenderer(
1598    jalview.renderer.seqfeatures.FeatureRenderer fr)
1599    {
1600  0 fr.transferSettings(getSeqPanel().seqCanvas.getFeatureRenderer());
1601    }
1602   
 
1603  0 toggle public void updateFeatureRendererFrom(jalview.api.FeatureRenderer fr)
1604    {
1605  0 if (getSeqPanel().seqCanvas.getFeatureRenderer() != null)
1606    {
1607  0 getSeqPanel().seqCanvas.getFeatureRenderer().transferSettings(fr);
1608    }
1609    }
1610   
 
1611  217 toggle public ScalePanel getScalePanel()
1612    {
1613  217 return scalePanel;
1614    }
1615   
 
1616  217 toggle public void setScalePanel(ScalePanel scalePanel)
1617    {
1618  217 this.scalePanel = scalePanel;
1619    }
1620   
 
1621  5418 toggle public SeqPanel getSeqPanel()
1622    {
1623  5418 return seqPanel;
1624    }
1625   
 
1626  217 toggle public void setSeqPanel(SeqPanel seqPanel)
1627    {
1628  217 this.seqPanel = seqPanel;
1629    }
1630   
 
1631  2773 toggle public AnnotationPanel getAnnotationPanel()
1632    {
1633  2773 return annotationPanel;
1634    }
1635   
 
1636  217 toggle public void setAnnotationPanel(AnnotationPanel annotationPanel)
1637    {
1638  217 this.annotationPanel = annotationPanel;
1639    }
1640   
 
1641  3285 toggle public AnnotationLabels getAlabels()
1642    {
1643  3285 return alabels;
1644    }
1645   
 
1646  217 toggle public void setAlabels(AnnotationLabels alabels)
1647    {
1648  217 this.alabels = alabels;
1649    }
1650   
 
1651  2234 toggle public IdPanel getIdPanel()
1652    {
1653  2234 return idPanel;
1654    }
1655   
 
1656  217 toggle public void setIdPanel(IdPanel idPanel)
1657    {
1658  217 this.idPanel = idPanel;
1659    }
1660   
1661    /**
1662    * Follow a scrolling change in the (cDNA/Protein) complementary alignment.
1663    * The aim is to keep the two alignments 'lined up' on their centre columns.
1664    *
1665    * @param sr
1666    * holds mapped region(s) of this alignment that we are scrolling
1667    * 'to'; may be modified for sequence offset by this method
1668    * @param verticalOffset
1669    * the number of visible sequences to show above the mapped region
1670    */
 
1671  0 toggle protected void scrollToCentre(SearchResultsI sr, int verticalOffset)
1672    {
1673  0 scrollToPosition(sr, verticalOffset, true, true);
1674    }
1675   
1676    /**
1677    * Set a flag to say do not scroll any (cDNA/protein) complement.
1678    *
1679    * @param b
1680    */
 
1681  908 toggle protected void setToScrollComplementPanel(boolean b)
1682    {
1683  908 this.scrollComplementaryPanel = b;
1684    }
1685   
1686    /**
1687    * Get whether to scroll complement panel
1688    *
1689    * @return true if cDNA/protein complement panels should be scrolled
1690    */
 
1691  452 toggle protected boolean isSetToScrollComplementPanel()
1692    {
1693  452 return this.scrollComplementaryPanel;
1694    }
1695   
1696    /**
1697    * Redraw sensibly.
1698    *
1699    * @adjustHeight if true, try to recalculate panel height for visible
1700    * annotations
1701    */
 
1702  0 toggle protected void refresh(boolean adjustHeight)
1703    {
1704  0 validateAnnotationDimensions(adjustHeight);
1705  0 addNotify();
1706  0 if (adjustHeight)
1707    {
1708    // sort, repaint, update overview
1709  0 paintAlignment(true, false);
1710    }
1711    else
1712    {
1713    // lightweight repaint
1714  0 repaint();
1715    }
1716    }
1717   
 
1718  452 toggle @Override
1719    /**
1720    * Property change event fired when a change is made to the viewport ranges
1721    * object associated with this alignment panel's viewport
1722    */
1723    public void propertyChange(PropertyChangeEvent evt)
1724    {
1725    // update this panel's scroll values based on the new viewport ranges values
1726  452 ViewportRanges ranges = av.getRanges();
1727  452 int x = ranges.getStartRes();
1728  452 int y = ranges.getStartSeq();
1729  452 setScrollValues(x, y);
1730   
1731    // now update any complementary alignment (its viewport ranges object
1732    // is different so does not get automatically updated)
1733  452 if (isSetToScrollComplementPanel())
1734    {
1735  451 setToScrollComplementPanel(false);
1736  451 av.scrollComplementaryAlignment();
1737  451 setToScrollComplementPanel(true);
1738    }
1739    }
1740   
1741    /**
1742    * Set the reference to the PCA/Tree chooser dialog for this panel. This
1743    * reference should be nulled when the dialog is closed.
1744    *
1745    * @param calculationChooser
1746    */
 
1747  0 toggle public void setCalculationDialog(CalculationChooser calculationChooser)
1748    {
1749  0 calculationDialog = calculationChooser;
1750    }
1751   
1752    /**
1753    * Returns the reference to the PCA/Tree chooser dialog for this panel (null
1754    * if none is open)
1755    */
 
1756  345 toggle public CalculationChooser getCalculationDialog()
1757    {
1758  345 return calculationDialog;
1759    }
1760    }