Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.gui

File AlignmentPanel.java

 

Coverage histogram

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

Code metrics

194
516
65
1
1,740
1,102
177
0.34
7.94
65
2.72

Classes

Class Line # Actions
AlignmentPanel 77 516 177
0.6374193463.7%
 

Contributing tests

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