X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FAlignmentPanel.java;h=58aa645eb156091058a019476feadd7fb32a8e85;hb=f9f0b83445a2da848d21614d94a909888484e25e;hp=4e5158dfacb60c3a5232e0abb8706518f5685f95;hpb=9ce64cb890f97849c5d30cd994cc682465fd7db1;p=jalview.git diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 4e5158d..58aa645 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -24,15 +24,21 @@ import jalview.analysis.AnnotationSorter; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.bin.Cache; +import jalview.bin.Jalview; import jalview.datamodel.AlignmentI; +import jalview.datamodel.HiddenColumns; import jalview.datamodel.SearchResultsI; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.gui.ImageExporter.ImageWriterI; +import jalview.io.HTMLOutput; import jalview.jbgui.GAlignmentPanel; import jalview.math.AlignmentDimension; import jalview.schemes.ResidueProperties; import jalview.structure.StructureSelectionManager; +import jalview.util.Comparison; +import jalview.util.ImageMaker; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.ViewportListenerI; @@ -45,9 +51,10 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; -import java.awt.Insets; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; @@ -67,24 +74,18 @@ import javax.swing.SwingUtilities; * @version $Revision: 1.161 $ */ public class AlignmentPanel extends GAlignmentPanel implements - AdjustmentListener, Printable, AlignmentViewPanel, - ViewportListenerI + AdjustmentListener, Printable, AlignmentViewPanel, ViewportListenerI { public AlignViewport av; - ViewportRanges vpRanges; - OverviewPanel overviewPanel; private SeqPanel seqPanel; private IdPanel idPanel; - private boolean headless; - IdwidthAdjuster idwidthAdjuster; - /** DOCUMENT ME!! */ public AlignFrame alignFrame; private ScalePanel scalePanel; @@ -93,21 +94,20 @@ public class AlignmentPanel extends GAlignmentPanel implements private AnnotationLabels alabels; - // this value is set false when selection area being dragged - boolean fastPaint = true; - private int hextent = 0; private int vextent = 0; /* * Flag set while scrolling to follow complementary cDNA/protein scroll. When - * true, suppresses invoking the same method recursively. + * false, suppresses invoking the same method recursively. */ - private boolean dontScrollComplement; + private boolean scrollComplementaryPanel = true; private PropertyChangeListener propertyChangeListener; + private CalculationChooser calculationDialog; + /** * Creates a new AlignmentPanel object. * @@ -118,7 +118,6 @@ public class AlignmentPanel extends GAlignmentPanel implements { alignFrame = af; this.av = av; - vpRanges = av.getRanges(); setSeqPanel(new SeqPanel(av, this)); setIdPanel(new IdPanel(av, this)); @@ -142,6 +141,35 @@ public class AlignmentPanel extends GAlignmentPanel implements hscroll.addAdjustmentListener(this); vscroll.addAdjustmentListener(this); + addComponentListener(new ComponentAdapter() + { + @Override + public void componentResized(ComponentEvent evt) + { + // reset the viewport ranges when the alignment panel is resized + // in particular, this initialises the end residue value when Jalview + // is initialised + ViewportRanges ranges = av.getRanges(); + if (av.getWrapAlignment()) + { + int widthInRes = getSeqPanel().seqCanvas.getWrappedCanvasWidth( + getSeqPanel().seqCanvas.getWidth()); + ranges.setViewportWidth(widthInRes); + } + else + { + int widthInRes = getSeqPanel().seqCanvas.getWidth() + / av.getCharWidth(); + int heightInSeq = getSeqPanel().seqCanvas.getHeight() + / av.getCharHeight(); + + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); + } + } + + }); + final AlignmentPanel ap = this; propertyChangeListener = new PropertyChangeListener() { @@ -173,9 +201,15 @@ public class AlignmentPanel extends GAlignmentPanel implements { av.alignmentChanged(this); + if (getCalculationDialog() != null) + { + getCalculationDialog().validateCalcTypes(); + } + alignFrame.updateEditMenuBar(); - paintAlignment(true); + // no idea if we need to update structure + paintAlignment(true, true); } @@ -188,11 +222,12 @@ public class AlignmentPanel extends GAlignmentPanel implements // to prevent drawing old image FontMetrics fm = getFontMetrics(av.getFont()); - scalePanelHolder.setPreferredSize(new Dimension(10, av.getCharHeight() - + fm.getDescent())); - idSpaceFillerPanel1.setPreferredSize(new Dimension(10, av - .getCharHeight() + fm.getDescent())); - + scalePanelHolder.setPreferredSize( + new Dimension(10, av.getCharHeight() + fm.getDescent())); + idSpaceFillerPanel1.setPreferredSize( + new Dimension(10, av.getCharHeight() + fm.getDescent())); + idwidthAdjuster.invalidate(); + scalePanelHolder.invalidate(); getIdPanel().getIdCanvas().gg = null; getSeqPanel().seqCanvas.img = null; getAnnotationPanel().adjustPanelHeight(); @@ -203,15 +238,6 @@ public class AlignmentPanel extends GAlignmentPanel implements getIdPanel().getIdCanvas().setPreferredSize(d); hscrollFillerPanel.setPreferredSize(d); - if (overviewPanel != null) - { - overviewPanel.setBoxPosition(); - } - if (this.alignFrame.getSplitViewContainer() != null) - { - ((SplitFrame) this.alignFrame.getSplitViewContainer()).adjustLayout(); - } - repaint(); } @@ -255,8 +281,8 @@ public class AlignmentPanel extends GAlignmentPanel implements { Container c = new Container(); - FontMetrics fm = c.getFontMetrics(new Font(av.font.getName(), - Font.ITALIC, av.font.getSize())); + FontMetrics fm = c.getFontMetrics( + new Font(av.font.getName(), Font.ITALIC, av.font.getSize())); AlignmentI al = av.getAlignment(); int i = 0; @@ -297,8 +323,8 @@ public class AlignmentPanel extends GAlignmentPanel implements } } - return new Dimension(maxwidth < 0 ? idWidth : Math.min(maxwidth, - idWidth), 12); + return new Dimension( + maxwidth < 0 ? idWidth : Math.min(maxwidth, idWidth), 12); } /** @@ -307,19 +333,11 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public void highlightSearchResults(SearchResultsI results) { - scrollToPosition(results); - getSeqPanel().seqCanvas.highlightSearchResults(results); - } + boolean scrolled = scrollToPosition(results, 0, true, false); - /** - * Scroll the view to show the position of the highlighted region in results - * (if any) and redraw the overview - * - * @param results - */ - public boolean scrollToPosition(SearchResultsI results) - { - return scrollToPosition(results, 0, true, false); + boolean fastPaint = !(scrolled && av.getWrapAlignment()); + + getSeqPanel().seqCanvas.highlightSearchResults(results, fastPaint); } /** @@ -337,8 +355,10 @@ public class AlignmentPanel extends GAlignmentPanel implements } /** - * Scroll the view to show the position of the highlighted region in results - * (if any) + * Scrolls the view (if necessary) to show the position of the first + * highlighted region in results (if any). Answers true if the view was + * scrolled, or false if no matched region was found, or it is already + * visible. * * @param results * @param verticalOffset @@ -348,138 +368,118 @@ public class AlignmentPanel extends GAlignmentPanel implements * - when set, the overview will be recalculated (takes longer) * @param centre * if true, try to centre the search results horizontally in the view - * @return false if results were not found + * @return */ - public boolean scrollToPosition(SearchResultsI results, + protected boolean scrollToPosition(SearchResultsI results, int verticalOffset, boolean redrawOverview, boolean centre) { int startv, endv, starts, ends; - // TODO: properly locate search results in view when large numbers of hidden - // columns exist before highlighted region - // do we need to scroll the panel? - // TODO: tons of nullpointerexceptions raised here. - if (results != null && results.getSize() > 0 && av != null - && av.getAlignment() != null) - { - int seqIndex = av.getAlignment().findIndex(results); - if (seqIndex == -1) - { - return false; - } - SequenceI seq = av.getAlignment().getSequenceAt(seqIndex); + ViewportRanges ranges = av.getRanges(); - int[] r = results.getResults(seq, 0, av.getAlignment().getWidth()); - if (r == null) - { - return false; - } - int start = r[0]; - int end = r[1]; - // DEBUG - // System.err.println(this.av.viewName + " Seq : " + seqIndex - // + " Scroll to " + start + "," + end); + if (results == null || results.isEmpty() || av == null + || av.getAlignment() == null) + { + return false; + } + int seqIndex = av.getAlignment().findIndex(results); + if (seqIndex == -1) + { + return false; + } + SequenceI seq = av.getAlignment().getSequenceAt(seqIndex); - /* - * To centre results, scroll to positions half the visible width - * left/right of the start/end positions - */ - if (centre) - { - int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1; - start = Math.max(start - offset, 0); - end = end + offset - 1; - } - if (start < 0) - { - return false; - } - if (end == seq.getEnd()) - { - return false; - } - if (av.hasHiddenColumns()) + int[] r = results.getResults(seq, 0, av.getAlignment().getWidth()); + if (r == null) + { + return false; + } + int start = r[0]; + int end = r[1]; + + /* + * To centre results, scroll to positions half the visible width + * left/right of the start/end positions + */ + if (centre) + { + int offset = (ranges.getEndRes() - ranges.getStartRes() + 1) / 2 - 1; + start = Math.max(start - offset, 0); + end = end + offset - 1; + } + if (start < 0) + { + return false; + } + if (end == seq.getEnd()) + { + return false; + } + + if (av.hasHiddenColumns()) + { + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + start = hidden.absoluteToVisibleColumn(start); + end = hidden.absoluteToVisibleColumn(end); + if (start == end) { - start = av.getColumnSelection().findColumnPosition(start); - end = av.getColumnSelection().findColumnPosition(end); - if (start == end) + if (!hidden.isVisible(r[0])) { - if (!av.getColumnSelection().isVisible(r[0])) - { - // don't scroll - position isn't visible - return false; - } + // don't scroll - position isn't visible + return false; } } + } - /* - * allow for offset of target sequence (actually scroll to one above it) - */ - seqIndex = Math.max(0, seqIndex - verticalOffset); + /* + * allow for offset of target sequence (actually scroll to one above it) + */ + seqIndex = Math.max(0, seqIndex - verticalOffset); + boolean scrollNeeded = true; - // System.out.println("start=" + start + ", end=" + end + ", startv=" - // + av.getStartRes() + ", endv=" + av.getEndRes() + ", starts=" - // + av.getStartSeq() + ", ends=" + av.getEndSeq()); - if (!av.getWrapAlignment()) + if (!av.getWrapAlignment()) + { + if ((startv = ranges.getStartRes()) >= start) { - if ((startv = vpRanges.getStartRes()) >= start) - { - /* - * Scroll left to make start of search results visible - */ - // setScrollValues(start - 1, seqIndex); // plus one residue - setScrollValues(start, seqIndex); - } - else if ((endv = vpRanges.getEndRes()) <= end) - { - /* - * Scroll right to make end of search results visible - */ - // setScrollValues(startv + 1 + end - endv, seqIndex); // plus one - setScrollValues(startv + end - endv, seqIndex); - } - else if ((starts = vpRanges.getStartSeq()) > seqIndex) - { - /* - * Scroll up to make start of search results visible - */ - setScrollValues(vpRanges.getStartRes(), seqIndex); - } - else if ((ends = vpRanges.getEndSeq()) <= seqIndex) - { - /* - * Scroll down to make end of search results visible - */ - setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends - + 1); - } /* - * Else results are already visible - no need to scroll + * Scroll left to make start of search results visible */ + setScrollValues(start, seqIndex); } - else + else if ((endv = ranges.getEndRes()) <= end) { - scrollToWrappedVisible(start); + /* + * Scroll right to make end of search results visible + */ + setScrollValues(startv + end - endv, seqIndex); + } + else if ((starts = ranges.getStartSeq()) > seqIndex) + { + /* + * Scroll up to make start of search results visible + */ + setScrollValues(ranges.getStartRes(), seqIndex); } + else if ((ends = ranges.getEndSeq()) <= seqIndex) + { + /* + * Scroll down to make end of search results visible + */ + setScrollValues(ranges.getStartRes(), starts + seqIndex - ends + + 1); + } + /* + * Else results are already visible - no need to scroll + */ + scrollNeeded = false; } - if (redrawOverview && overviewPanel != null) + else { - overviewPanel.setBoxPosition(); + scrollNeeded = ranges.scrollToWrappedVisible(start); } - paintAlignment(redrawOverview); - return true; - } - void scrollToWrappedVisible(int res) - { - int cwidth = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - if (res < vpRanges.getStartRes() - || res >= (vpRanges.getStartRes() + cwidth)) - { - vscroll.setValue((res / cwidth)); - // vpRanges.setStartRes(vscroll.getValue() * cwidth); - } + paintAlignment(redrawOverview, false); + return scrollNeeded; } /** @@ -534,7 +534,9 @@ public class AlignmentPanel extends GAlignmentPanel implements } validateAnnotationDimensions(true); addNotify(); - paintAlignment(true); + // TODO: many places call this method and also paintAlignment with various + // different settings. this means multiple redraws are triggered... + paintAlignment(true, false); } /** @@ -553,13 +555,10 @@ public class AlignmentPanel extends GAlignmentPanel implements /* * Estimate available height in the AlignFrame for alignment + * annotations. Deduct an estimate for title bar, menu bar, scale panel, - * hscroll, status bar (as these are not laid out we can't inspect their - * actual heights). Insets gives frame borders. + * hscroll, status bar, insets. */ - int stuff = Platform.isAMac() ? 80 : 100; - Insets insets = alignFrame.getInsets(); - int availableHeight = alignFrame.getHeight() - stuff - insets.top - - insets.bottom; + int stuff = Platform.isAMac() ? 120 : 140; + int availableHeight = alignFrame.getHeight() - stuff; /* * If not enough vertical space, maximize annotation height while keeping @@ -567,8 +566,8 @@ public class AlignmentPanel extends GAlignmentPanel implements */ if (annotationHeight + alignmentHeight > availableHeight) { - annotationHeight = Math.min(annotationHeight, availableHeight - 2 - * rowHeight); + annotationHeight = Math.min(annotationHeight, + availableHeight - 2 * rowHeight); } } else @@ -578,8 +577,8 @@ public class AlignmentPanel extends GAlignmentPanel implements } hscroll.addNotify(); - annotationScroller.setPreferredSize(new Dimension(annotationScroller - .getWidth(), annotationHeight)); + annotationScroller.setPreferredSize( + new Dimension(annotationScroller.getWidth(), annotationHeight)); Dimension e = idPanel.getSize(); alabels.setSize(new Dimension(e.width, annotationHeight)); @@ -601,7 +600,8 @@ public class AlignmentPanel extends GAlignmentPanel implements fontChanged(); setAnnotationVisible(av.isShowAnnotation()); boolean wrap = av.getWrapAlignment(); - vpRanges.setStartSeq(0); + ViewportRanges ranges = av.getRanges(); + ranges.setStartSeq(0); scalePanelHolder.setVisible(!wrap); hscroll.setVisible(!wrap); idwidthAdjuster.setVisible(!wrap); @@ -617,74 +617,29 @@ public class AlignmentPanel extends GAlignmentPanel implements annotationSpaceFillerHolder.setVisible(true); } - idSpaceFillerPanel1.setVisible(!wrap); - - repaint(); - } - - // return value is true if the scroll is valid - public boolean scrollUp(boolean up) - { - if (up) - { - if (vscroll.getValue() < 1) - { - return false; - } - - fastPaint = false; - vscroll.setValue(vscroll.getValue() - 1); - } - else - { - if ((vextent + vscroll.getValue()) >= av.getAlignment().getHeight()) + int canvasWidth = getSeqPanel().seqCanvas.getWidth(); + if (canvasWidth > 0) + { // may not yet be laid out + if (wrap) { - return false; + int widthInRes = getSeqPanel().seqCanvas + .getWrappedCanvasWidth(canvasWidth); + ranges.setViewportWidth(widthInRes); } - - fastPaint = false; - vscroll.setValue(vscroll.getValue() + 1); - } - - fastPaint = true; - - return true; - } - - /** - * DOCUMENT ME! - * - * @param right - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public boolean scrollRight(boolean right) - { - if (!right) - { - if (hscroll.getValue() < 1) + else { - return false; - } + int widthInRes = (canvasWidth / av.getCharWidth()); + int heightInSeq = (getSeqPanel().seqCanvas.getHeight() + / av.getCharHeight()); - fastPaint = false; - hscroll.setValue(hscroll.getValue() - 1); - } - else - { - if ((hextent + hscroll.getValue()) >= av.getAlignment().getWidth()) - { - return false; + ranges.setViewportWidth(widthInRes); + ranges.setViewportHeight(heightInSeq); } - - fastPaint = false; - hscroll.setValue(hscroll.getValue() + 1); } - fastPaint = true; + idSpaceFillerPanel1.setVisible(!wrap); - return true; + repaint(); } /** @@ -696,211 +651,195 @@ public class AlignmentPanel extends GAlignmentPanel implements * visible row to scroll to * */ - public void setScrollValues(int x, int y) + public void setScrollValues(int xpos, int ypos) { + int x = xpos; + int y = ypos; + if (av == null || av.getAlignment() == null) { return; } - int width = av.getAlignment().getWidth(); - int height = av.getAlignment().getHeight(); - if (av.hasHiddenColumns()) + if (av.getWrapAlignment()) { - // reset the width to exclude hidden columns - width = av.getColumnSelection().findColumnPosition(width); + setScrollingForWrappedPanel(x); } - - hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); - vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); - - if (hextent > width) + else { - hextent = width; - } + int width = av.getAlignment().getWidth(); + int height = av.getAlignment().getHeight(); - if (vextent > height) - { - vextent = height; - } + if (av.hasHiddenColumns()) + { + // reset the width to exclude hidden columns + width = av.getAlignment().getHiddenColumns() + .absoluteToVisibleColumn(width); + } - if ((hextent + x) > width) - { - x = width - hextent; - } + hextent = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + vextent = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); - if ((vextent + y) > height) - { - y = height - vextent; - } + if (hextent > width) + { + hextent = width; + } - if (y < 0) - { - y = 0; - } + if (vextent > height) + { + vextent = height; + } - if (x < 0) - { - x = 0; - } + if ((hextent + x) > width) + { + x = width - hextent; + } - // update endRes after x has (possibly) been adjusted - // vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av - // .getCharWidth())) - 1); + if ((vextent + y) > height) + { + y = height - vextent; + } - /* - * each scroll adjustment triggers adjustmentValueChanged, which resets the - * 'do not scroll complement' flag; ensure it is the same for both - * operations - */ - // boolean flag = isDontScrollComplement(); - hscroll.setValues(x, hextent, 0, width); - // setDontScrollComplement(flag); - vscroll.setValues(y, vextent, 0, height); + if (y < 0) + { + y = 0; + } + if (x < 0) + { + x = 0; + } + + // update the scroll values + hscroll.setValues(x, hextent, 0, width); + vscroll.setValues(y, vextent, 0, height); + } } /** - * DOCUMENT ME! + * Respond to adjustment event when horizontal or vertical scrollbar is + * changed * * @param evt - * DOCUMENT ME! + * adjustment event encoding whether hscroll or vscroll changed */ @Override public void adjustmentValueChanged(AdjustmentEvent evt) { - int oldX = vpRanges.getStartRes(); - int oldY = vpRanges.getStartSeq(); + if (av.getWrapAlignment()) + { + adjustScrollingWrapped(evt); + return; + } - // check for horizontal scroll with wrapped alignment - // as this can happen via triggered events (and we don't want to hscroll - // when wrapped) - if (evt.getSource() == hscroll && !av.getWrapAlignment()) + ViewportRanges ranges = av.getRanges(); + + if (evt.getSource() == hscroll) { + int oldX = ranges.getStartRes(); + int oldwidth = ranges.getViewportWidth(); int x = hscroll.getValue(); - if (x == oldX) + int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((x == oldX) && (width == oldwidth)) { return; } - // vpRanges.setStartRes(x); - // vpRanges.setEndRes((x + (getSeqPanel().seqCanvas.getWidth() / av - // .getCharWidth())) - 1); - vpRanges.setStartEndRes( - x, - (x + (getSeqPanel().seqCanvas.getWidth() / av - .getCharWidth())) - 1); + ranges.setViewportStartAndWidth(x, width); } - - if (evt.getSource() == vscroll) + else if (evt.getSource() == vscroll) { - int offy = vscroll.getValue(); - - if (av.getWrapAlignment()) - { - if (offy == oldX) - { - return; - } - if (offy > -1) - { - int rowSize = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - vpRanges.setStartEndRes(offy * rowSize, (offy + 1) * rowSize); - // vpRanges.setStartRes(offy * rowSize); - // vpRanges.setEndRes((offy + 1) * rowSize); + int oldY = ranges.getStartSeq(); + int oldheight = ranges.getViewportHeight(); + int y = vscroll.getValue(); + int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); - } - else - { - // This is only called if file loaded is a jar file that - // was wrapped when saved and user has wrap alignment true - // as preference setting - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - setScrollValues(vpRanges.getStartRes(), - vpRanges.getStartSeq()); - } - }); - } - } - else + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((y == oldY) && (height == oldheight)) { - if (offy == oldY) - { - return; - } - // vpRanges.setStartSeq(offy); - // vpRanges.setEndSeq(offy - // + (getSeqPanel().seqCanvas.getHeight() / av.getCharHeight()) - // - 1); - vpRanges.setStartEndSeq( - offy, - offy - + (getSeqPanel().seqCanvas.getHeight() / av - .getCharHeight()) - 1); + return; } + ranges.setViewportStartAndHeight(y, height); } + repaint(); + } - /*if (overviewPanel != null) + /** + * Responds to a scroll change by setting the start position of the viewport. + * Does + * + * @param evt + */ + protected void adjustScrollingWrapped(AdjustmentEvent evt) + { + if (evt.getSource() == hscroll) { - overviewPanel.setBoxPosition(); - }*/ + return; // no horizontal scroll when wrapped + } + final ViewportRanges ranges = av.getRanges(); - // int scrollX = vpRanges.getStartRes() - oldX; - // int scrollY = vpRanges.getStartSeq() - oldY; + if (evt.getSource() == vscroll) + { + int newY = vscroll.getValue(); - if (av.getWrapAlignment() || !fastPaint) + /* + * if we're scrolling to the position we're already at, stop + * this prevents infinite recursion of events when the scroll/viewport + * ranges values are the same + */ + int oldX = ranges.getStartRes(); + int oldY = ranges.getWrappedScrollPosition(oldX); + if (oldY == newY) + { + return; + } + if (newY > -1) + { + /* + * limit page up/down to one width's worth of positions + */ + int rowSize = ranges.getViewportWidth(); + int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; + ranges.setViewportStartAndWidth(Math.max(0, newX), rowSize); + } + } + else { - repaint(); + // This is only called if file loaded is a jar file that + // was wrapped when saved and user has wrap alignment true + // as preference setting + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + // When updating scrolling to use ViewportChange events, this code + // could not be validated and it is not clear if it is now being + // called. Log warning here in case it is called and unforeseen + // problems occur + Cache.log.warn( + "Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); + + // scroll to start of panel + ranges.setStartRes(0); + ranges.setStartSeq(0); + } + }); } - /* else - { - // Make sure we're not trying to draw a panel - // larger than the visible window - if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes()) - { - scrollX = vpRanges.getEndRes() - vpRanges.getStartRes(); - } - else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes()) - { - scrollX = vpRanges.getStartRes() - vpRanges.getEndRes(); - } - - if (scrollX != 0 || scrollY != 0) - { - getIdPanel().getIdCanvas().fastPaint(scrollY); - getSeqPanel().seqCanvas.fastPaint(scrollX, scrollY); - getScalePanel().repaint(); - - if (av.isShowAnnotation() && scrollX != 0) - { - getAnnotationPanel().fastPaint(scrollX); - } - } - } - /* - * If there is one, scroll the (Protein/cDNA) complementary alignment to - * match, unless we are ourselves doing that. - */ - /* if (isDontScrollComplement()) - { - setDontScrollComplement(false); - } - else - { - av.scrollComplementaryAlignment(); - }*/ + repaint(); } - /** - * Repaint the alignment including the annotations and overview panels (if - * shown). + /* (non-Javadoc) + * @see jalview.api.AlignmentViewPanel#paintAlignment(boolean) */ @Override - public void paintAlignment(boolean updateOverview) + public void paintAlignment(boolean updateOverview, + boolean updateStructures) { final AnnotationSorter sorter = new AnnotationSorter(getAlignment(), av.isShowAutocalculatedAbove()); @@ -908,10 +847,12 @@ public class AlignmentPanel extends GAlignmentPanel implements av.getSortAnnotationsBy()); repaint(); - if (updateOverview) + if (updateStructures) { - // TODO: determine if this paintAlignment changed structure colours av.getStructureSelectionManager().sequenceColoursChanged(this); + } + if (updateOverview) + { if (overviewPanel != null) { @@ -929,45 +870,41 @@ public class AlignmentPanel extends GAlignmentPanel implements @Override public void paintComponent(Graphics g) { - invalidate(); + invalidate(); // needed so that the id width adjuster works correctly Dimension d = getIdPanel().getIdCanvas().getPreferredSize(); idPanelHolder.setPreferredSize(d); hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12)); - validate(); + + validate(); // needed so that the id width adjuster works correctly /* - * set scroll bar positions; first suppress this being 'followed' in any - * complementary split pane + * set scroll bar positions - tried to remove but necessary for split panel to resize correctly + * though I still think this call should be elsewhere. */ - setDontScrollComplement(true); - - if (av.getWrapAlignment()) - { - int maxwidth = av.getAlignment().getWidth(); + ViewportRanges ranges = av.getRanges(); + setScrollValues(ranges.getStartRes(), ranges.getStartSeq()); + } - if (av.hasHiddenColumns()) - { - maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1; - } + /** + * Set vertical scroll bar position, and number of increments, for wrapped + * panel + * + * @param topLeftColumn + * the column position at top left (0..) + */ + private void setScrollingForWrappedPanel(int topLeftColumn) + { + ViewportRanges ranges = av.getRanges(); + int scrollPosition = ranges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = ranges.getWrappedMaxScroll(topLeftColumn); - int canvasWidth = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - if (canvasWidth > 0) - { - int max = maxwidth - / getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas - .getWidth()) + 1; - vscroll.setMaximum(max); - vscroll.setUnitIncrement(1); - vscroll.setVisibleAmount(1); - } - } - else - { - setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq()); - } + /* + * a scrollbar's value can be set to at most (maximum-extent) + * so we add extent (1) to the maxScroll value + */ + vscroll.setUnitIncrement(1); + vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1); } /** @@ -1047,7 +984,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * Get the horizontal offset to where we draw the sequences. * This is idWidth if using a single Graphics context, else zero. */ - final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0 : idWidth; + final int alignmentGraphicsOffset = idGraphics != alignmentGraphics ? 0 + : idWidth; FontMetrics fm = getFontMetrics(av.getFont()); int charHeight = av.getCharHeight(); @@ -1084,8 +1022,7 @@ public class AlignmentPanel extends GAlignmentPanel implements endSeq = alignmentHeight; } - int pagesHigh = ((alignmentHeight / totalSeq) + 1) - * pageHeight; + int pagesHigh = ((alignmentHeight / totalSeq) + 1) * pageHeight; if (av.isShowAnnotation()) { @@ -1098,8 +1035,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { return Printable.NO_SUCH_PAGE; } - final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight - + 3; + final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight + 3; /* * draw the Scale at horizontal offset, then reset to top left (0, 0) @@ -1148,9 +1084,7 @@ public class AlignmentPanel extends GAlignmentPanel implements if (av.isRightAlignIds()) { fm = idGraphics.getFontMetrics(); - xPos = idWidth - - fm.stringWidth(displayId) - - 4; + xPos = idWidth - fm.stringWidth(displayId) - 4; } idGraphics.drawString(displayId, xPos, @@ -1165,8 +1099,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * single graphics context), then reset to (0, scale height) */ alignmentGraphics.translate(alignmentGraphicsOffset, scaleHeight); - getSeqPanel().seqCanvas.drawPanel(alignmentGraphics, startRes, endRes, - startSeq, endSeq, 0); + getSeqPanel().seqCanvas.drawPanelForPrinting(alignmentGraphics, startRes, + endRes, startSeq, endSeq); alignmentGraphics.translate(-alignmentGraphicsOffset, 0); if (av.isShowAnnotation() && (endSeq == alignmentHeight)) @@ -1186,7 +1120,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * draw the annotations starting at * (idOffset, alignmentHeight) from (0, scaleHeight) */ - alignmentGraphics.translate(alignmentGraphicsOffset, alignmentDrawnHeight); + alignmentGraphics.translate(alignmentGraphicsOffset, + alignmentDrawnHeight); getAnnotationPanel().renderer.drawComponent(getAnnotationPanel(), av, alignmentGraphics, -1, startRes, endRes + 1); } @@ -1236,11 +1171,12 @@ public class AlignmentPanel extends GAlignmentPanel implements int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1; + maxwidth = av.getAlignment().getHiddenColumns() + .absoluteToVisibleColumn(maxwidth) - 1; } - int resWidth = getSeqPanel().seqCanvas.getWrappedCanvasWidth(pwidth - - idWidth); + int resWidth = getSeqPanel().seqCanvas + .getWrappedCanvasWidth(pwidth - idWidth); int totalHeight = cHeight * (maxwidth / resWidth + 1); @@ -1277,16 +1213,13 @@ public class AlignmentPanel extends GAlignmentPanel implements } if (labels != null) { - pg.translate(-3, - ypos + (av.getAlignment().getHeight() * av.getCharHeight())); + pg.translate(-3, ypos + + (av.getAlignment().getHeight() * av.getCharHeight())); pg.setFont(av.getFont()); labels.drawComponent(pg, idWidth); - pg.translate( - +3, - -ypos - - (av.getAlignment().getHeight() * av - .getCharHeight())); + pg.translate(+3, -ypos + - (av.getAlignment().getHeight() * av.getCharHeight())); } ypos += cHeight; @@ -1294,7 +1227,7 @@ public class AlignmentPanel extends GAlignmentPanel implements pg.translate(idWidth, 0); - getSeqPanel().seqCanvas.drawWrappedPanel(pg, pwidth - idWidth, + getSeqPanel().seqCanvas.drawWrappedPanelForPrinting(pg, pwidth - idWidth, totalHeight, 0); if ((pi * pheight) < totalHeight) @@ -1336,8 +1269,8 @@ public class AlignmentPanel extends GAlignmentPanel implements return calculateIdWidth(-1).width + 4; } Integer idwidth = null; - if (onscreen - || (idwidth = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null) + if (onscreen || (idwidth = Cache + .getIntegerProperty("FIGURE_FIXEDIDWIDTH")) == null) { int w = getIdPanel().getWidth(); return (w > 0 ? w : calculateIdWidth().width + 4); @@ -1345,92 +1278,59 @@ public class AlignmentPanel extends GAlignmentPanel implements return idwidth.intValue() + 4; } - void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file) + /** + * Builds an image of the alignment of the specified type (EPS/PNG/SVG) and + * writes it to the specified file + * + * @param type + * @param file + */ + void makeAlignmentImage(ImageMaker.TYPE type, File file) { - int boarderBottomOffset = 5; - long pSessionId = System.currentTimeMillis(); - headless = (System.getProperty("java.awt.headless") != null && System - .getProperty("java.awt.headless").equals("true")); - if (alignFrame != null && !headless) - { - if (file != null) - { - alignFrame.setProgressBar(MessageManager.formatMessage( - "status.saving_file", new Object[] { type.getLabel() }), - pSessionId); - } - } - try + final int borderBottomOffset = 5; + + AlignmentDimension aDimension = getAlignmentDimension(); + // todo use a lambda function in place of callback here? + ImageWriterI writer = new ImageWriterI() { - AlignmentDimension aDimension = getAlignmentDimension(); - try + @Override + public void exportImage(Graphics graphics) throws Exception { - jalview.util.ImageMaker im; - final String imageAction, imageTitle; - if (type == jalview.util.ImageMaker.TYPE.PNG) - { - imageAction = "Create PNG image from alignment"; - imageTitle = null; - } - else if (type == jalview.util.ImageMaker.TYPE.EPS) - { - imageAction = "Create EPS file from alignment"; - imageTitle = alignFrame.getTitle(); - } - else - { - imageAction = "Create SVG file from alignment"; - imageTitle = alignFrame.getTitle(); - } - - im = new jalview.util.ImageMaker(this, type, imageAction, - aDimension.getWidth(), aDimension.getHeight() - + boarderBottomOffset, file, imageTitle, - alignFrame, pSessionId, headless); - Graphics graphics = im.getGraphics(); if (av.getWrapAlignment()) { - if (graphics != null) - { - printWrappedAlignment(aDimension.getWidth(), - aDimension.getHeight() + boarderBottomOffset, 0, - graphics); - im.writeImage(); - } + printWrappedAlignment(aDimension.getWidth(), + aDimension.getHeight() + borderBottomOffset, 0, graphics); } else { - if (graphics != null) - { - printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), - 0, graphics, graphics); - im.writeImage(); - } + printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0, + graphics, graphics); } - - } catch (OutOfMemoryError err) - { - // Be noisy here. - System.out.println("########################\n" + "OUT OF MEMORY " - + file + "\n" + "########################"); - new OOMWarning("Creating Image for " + file, err); - // System.out.println("Create IMAGE: " + err); - } catch (Exception ex) - { - ex.printStackTrace(); } - } finally - { + }; - } + String fileTitle = alignFrame.getTitle(); + ImageExporter exporter = new ImageExporter(writer, alignFrame, type, + fileTitle); + int imageWidth = aDimension.getWidth(); + int imageHeight = aDimension.getHeight() + borderBottomOffset; + String of = MessageManager.getString("label.alignment"); + exporter.doExport(file, this, imageWidth, imageHeight, of); } + /** + * Calculates and returns a suitable width and height (in pixels) for an + * exported image + * + * @return + */ public AlignmentDimension getAlignmentDimension() { int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - maxwidth = av.getColumnSelection().findColumnPosition(maxwidth); + maxwidth = av.getAlignment().getHiddenColumns() + .absoluteToVisibleColumn(maxwidth); } int height = ((av.getAlignment().getHeight() + 1) * av.getCharHeight()) @@ -1440,16 +1340,15 @@ public class AlignmentPanel extends GAlignmentPanel implements if (av.getWrapAlignment()) { height = getWrappedHeight(); - if (headless) + if (Jalview.isHeadlessMode()) { // need to obtain default alignment width and then add in any // additional allowance for id margin // this duplicates the calculation in getWrappedHeight but adjusts for // offscreen idWith width = alignFrame.getWidth() - vscroll.getPreferredSize().width - - alignFrame.getInsets().left - - alignFrame.getInsets().right - getVisibleIdWidth() - + getVisibleIdWidth(false); + - alignFrame.getInsets().left - alignFrame.getInsets().right + - getVisibleIdWidth() + getVisibleIdWidth(false); } else { @@ -1465,27 +1364,6 @@ public class AlignmentPanel extends GAlignmentPanel implements } - /** - * DOCUMENT ME! - */ - public void makeEPS(File epsFile) - { - makeAlignmentImage(jalview.util.ImageMaker.TYPE.EPS, epsFile); - } - - /** - * DOCUMENT ME! - */ - public void makePNG(File pngFile) - { - makeAlignmentImage(jalview.util.ImageMaker.TYPE.PNG, pngFile); - } - - public void makeSVG(File svgFile) - { - makeAlignmentImage(jalview.util.ImageMaker.TYPE.SVG, svgFile); - } - public void makePNGImageMap(File imgMapFile, String imageName) { // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS @@ -1500,33 +1378,32 @@ public class AlignmentPanel extends GAlignmentPanel implements { try { - int s, sSize = av.getAlignment().getHeight(), res, alwidth = av - .getAlignment().getWidth(), g, gSize, f, fSize, sy; + int sSize = av.getAlignment().getHeight(); + int alwidth = av.getAlignment().getWidth(); PrintWriter out = new PrintWriter(new FileWriter(imgMapFile)); - out.println(jalview.io.HTMLOutput.getImageMapHTML()); + out.println(HTMLOutput.getImageMapHTML()); out.println("" + ""); - for (s = 0; s < sSize; s++) + for (int s = 0; s < sSize; s++) { - sy = s * av.getCharHeight() + scaleHeight; + int sy = s * av.getCharHeight() + scaleHeight; SequenceI seq = av.getAlignment().getSequenceAt(s); - SequenceFeature[] features = seq.getSequenceFeatures(); SequenceGroup[] groups = av.getAlignment().findAllGroups(seq); - for (res = 0; res < alwidth; res++) + for (int column = 0; column < alwidth; column++) { - StringBuilder text = new StringBuilder(); + StringBuilder text = new StringBuilder(512); String triplet = null; if (av.getAlignment().isNucleotide()) { triplet = ResidueProperties.nucleotideName.get(seq - .getCharAt(res) + ""); + .getCharAt(column) + ""); } else { - triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(res) + triplet = ResidueProperties.aa2Triplet.get(seq.getCharAt(column) + ""); } @@ -1535,84 +1412,73 @@ public class AlignmentPanel extends GAlignmentPanel implements continue; } - int alIndex = seq.findPosition(res); - gSize = groups.length; - for (g = 0; g < gSize; g++) + int seqPos = seq.findPosition(column); + int gSize = groups.length; + for (int g = 0; g < gSize; g++) { if (text.length() < 1) { text.append(" res) + if (groups[g].getStartRes() < column + && groups[g].getEndRes() > column) { text.append("
").append(groups[g].getName()) .append(""); } } - if (features != null) + if (text.length() < 1) { - if (text.length() < 1) - { - text.append(" features = seq.findFeatures(column, column); + for (SequenceFeature sf : features) { - - if ((features[f].getBegin() <= seq.findPosition(res)) - && (features[f].getEnd() >= seq.findPosition(res))) + if (sf.isContactFeature()) { - if (features[f].isContactFeature()) - { - if (features[f].getBegin() == seq.findPosition(res) - || features[f].getEnd() == seq - .findPosition(res)) - { - text.append("
").append(features[f].getType()) - .append(" ").append(features[f].getBegin()) - .append(":").append(features[f].getEnd()); - } - } - else + text.append("
").append(sf.getType()).append(" ") + .append(sf.getBegin()).append(":") + .append(sf.getEnd()); + } + else + { + text.append("
"); + text.append(sf.getType()); + String description = sf.getDescription(); + if (description != null + && !sf.getType().equals(description)) { - text.append("
"); - text.append(features[f].getType()); - if (features[f].getDescription() != null - && !features[f].getType().equals( - features[f].getDescription())) - { - text.append(" ").append(features[f].getDescription()); - } - - if (features[f].getValue("status") != null) - { - text.append(" (").append(features[f].getValue("status")) - .append(")"); - } + description = description.replace("\"", """); + text.append(" ").append(description); } } - + String status = sf.getStatus(); + if (status != null && !"".equals(status)) + { + text.append(" (").append(status).append(")"); + } + } + if (text.length() > 1) + { + text.append("')\"; onMouseOut=\"toolTip()\"; href=\"#\">"); + out.println(text.toString()); } - } - if (text.length() > 1) - { - text.append("')\"; onMouseOut=\"toolTip()\"; href=\"#\">"); - out.println(text.toString()); } } } @@ -1660,7 +1526,8 @@ public class AlignmentPanel extends GAlignmentPanel implements int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { - maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1; + maxwidth = av.getAlignment().getHiddenColumns() + .absoluteToVisibleColumn(maxwidth) - 1; } int height = ((maxwidth / chunkWidth) + 1) * cHeight; @@ -1678,19 +1545,22 @@ public class AlignmentPanel extends GAlignmentPanel implements PaintRefresher.RemoveComponent(getIdPanel().getIdCanvas()); PaintRefresher.RemoveComponent(this); + closeChildFrames(); + /* * try to ensure references are nulled */ if (annotationPanel != null) { annotationPanel.dispose(); + annotationPanel = null; } if (av != null) { av.removePropertyChangeListener(propertyChangeListener); - jalview.structure.StructureSelectionManager ssm = av - .getStructureSelectionManager(); + propertyChangeListener = null; + StructureSelectionManager ssm = av.getStructureSelectionManager(); ssm.removeStructureViewerListener(getSeqPanel(), null); ssm.removeSelectionListener(getSeqPanel()); ssm.removeCommandListener(av); @@ -1709,6 +1579,23 @@ public class AlignmentPanel extends GAlignmentPanel implements } /** + * Close any open dialogs that would be orphaned when this one is closed + */ + protected void closeChildFrames() + { + if (overviewPanel != null) + { + overviewPanel.dispose(); + overviewPanel = null; + } + if (calculationDialog != null) + { + calculationDialog.closeFrame(); + calculationDialog = null; + } + } + + /** * hides or shows dynamic annotation rows based on groups and av state flags */ public void updateAnnotation() @@ -1867,37 +1754,8 @@ public class AlignmentPanel extends GAlignmentPanel implements * @param verticalOffset * the number of visible sequences to show above the mapped region */ - public void scrollToCentre(SearchResultsI sr, int verticalOffset) + protected void scrollToCentre(SearchResultsI sr, int verticalOffset) { - /* - * To avoid jumpy vertical scrolling (if some sequences are gapped or not - * mapped), we can make the scroll-to location a sequence above the one - * actually mapped. - */ - SequenceI mappedTo = sr.getResults().get(0).getSequence(); - List seqs = av.getAlignment().getSequences(); - - /* - * This is like AlignmentI.findIndex(seq) but here we are matching the - * dataset sequence not the aligned sequence - */ - boolean matched = false; - for (SequenceI seq : seqs) - { - if (mappedTo == seq.getDatasetSequence()) - { - matched = true; - break; - } - } - if (!matched) - { - return; // failsafe, shouldn't happen - } - - /* - * Scroll to position but centring the target residue. - */ scrollToPosition(sr, verticalOffset, true, true); } @@ -1906,14 +1764,19 @@ public class AlignmentPanel extends GAlignmentPanel implements * * @param b */ - protected void setDontScrollComplement(boolean b) + protected void setToScrollComplementPanel(boolean b) { - this.dontScrollComplement = b; + this.scrollComplementaryPanel = b; } - protected boolean isDontScrollComplement() + /** + * Get whether to scroll complement panel + * + * @return true if cDNA/protein complement panels should be scrolled + */ + protected boolean isSetToScrollComplementPanel() { - return this.dontScrollComplement; + return this.scrollComplementaryPanel; } /** @@ -1929,7 +1792,7 @@ public class AlignmentPanel extends GAlignmentPanel implements if (adjustHeight) { // sort, repaint, update overview - paintAlignment(true); + paintAlignment(true, false); } else { @@ -1939,11 +1802,45 @@ public class AlignmentPanel extends GAlignmentPanel implements } @Override + /** + * Property change event fired when a change is made to the viewport ranges + * object associated with this alignment panel's viewport + */ public void propertyChange(PropertyChangeEvent evt) { - int x = vpRanges.getStartRes(); - int y = vpRanges.getStartSeq(); + // update this panel's scroll values based on the new viewport ranges values + ViewportRanges ranges = av.getRanges(); + int x = ranges.getStartRes(); + int y = ranges.getStartSeq(); setScrollValues(x, y); - av.scrollComplementaryAlignment(); + + // now update any complementary alignment (its viewport ranges object + // is different so does not get automatically updated) + if (isSetToScrollComplementPanel()) + { + setToScrollComplementPanel(false); + av.scrollComplementaryAlignment(); + setToScrollComplementPanel(true); + } + } + + /** + * Set the reference to the PCA/Tree chooser dialog for this panel. This + * reference should be nulled when the dialog is closed. + * + * @param calculationChooser + */ + public void setCalculationDialog(CalculationChooser calculationChooser) + { + calculationDialog = calculationChooser; + } + + /** + * Returns the reference to the PCA/Tree chooser dialog for this panel (null + * if none is open) + */ + public CalculationChooser getCalculationDialog() + { + return calculationDialog; } }