From: Jim Procter Date: Sun, 3 Sep 2017 17:06:24 +0000 (+0100) Subject: Merge branch 'bug/JAL-2691_nomultimermapping' into documentation/JAL-2675_release2102b1 X-Git-Tag: Release_2_10_2b1~11^2~9^2~2 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=e04bcd38992def511190a30f98cb3db3a720e0fc;hp=1bfb581512684bc3bcbb108405d5db0e795cbc6f;p=jalview.git Merge branch 'bug/JAL-2691_nomultimermapping' into documentation/JAL-2675_release2102b1 --- diff --git a/.ant-targets-build.xml b/.ant-targets-build.xml new file mode 100644 index 0000000..15432a1 --- /dev/null +++ b/.ant-targets-build.xml @@ -0,0 +1,31 @@ +build +buildPropertiesFile +buildTests +buildextclients +buildindices +castorbinding +clean +compileApplet +distclean +help +init +linkcheck +makeApplet +makedist +makefulldist +obfuscate +packageApplet +prepare +prepareTests +preparejnlp +prepubapplet_1 +pubapplet +runenv +signApplet +sourcedist +sourcedoc +sourcescrub +testclean +testng +usage +writejnlpf diff --git a/help/help.jhm b/help/help.jhm index c12436f..8e4961f 100755 --- a/help/help.jhm +++ b/help/help.jhm @@ -163,4 +163,5 @@ + diff --git a/help/helpTOC.xml b/help/helpTOC.xml index f3311a7..4636ea3 100755 --- a/help/helpTOC.xml +++ b/help/helpTOC.xml @@ -28,6 +28,7 @@ + diff --git a/help/html/features/overview.html b/help/html/features/overview.html index ac5aecb..2a656a3 100755 --- a/help/html/features/overview.html +++ b/help/html/features/overview.html @@ -27,13 +27,14 @@ View→Overview window

Select the overview window menu item to get a navigable image - of the whole alignment.

+ of the whole alignment. By default, gaps are shown as dark grey, in + the overview (since 2.10.2b1).

The red box indicates the currently viewed region of the alignment, this may be moved by clicking and dragging with the mouse.

Click anywhere else in the overview to centre the view on that position

-

+

Hiding hidden regions in the overview

@@ -41,13 +42,15 @@ Hidden sequences and columns are by default shown as dark-grey rows and columns in the overview. Hidden regions can also be omitted entirely (to make the Overview 'WYSIWIG') by Right-clicking - (or CMD-Clicking) to open the overview's popup menu.
-
The option to include/exclude hidden regions in the + (or CMD-Clicking) to open the overview's popup menu.

+ The option to include/exclude hidden regions in the overview was introduced in Jalview 2.10.2.

 

+ Previous to 2.10.2b1, gaps were white, and sequences shown as + dark grey unless coloured. diff --git a/help/html/features/preferences.html b/help/html/features/preferences.html index acd7ba6..b29b66b 100755 --- a/help/html/features/preferences.html +++ b/help/html/features/preferences.html @@ -41,6 +41,9 @@ Preferences tab allows you to configure default colourschemes for a new alignment window. +
  • The "Overview" + Preferences tab configures defaults for the overview window. +
  • The "Structure" Preferences tab allows you to configure options for obtaining and displaying structure information. @@ -181,6 +184,33 @@ by Annotation... is selected from the alignment window's colours menu.

    +

    + "Overview" + Preferences tab +

    +

    + Use legacy gap colouring (gaps are white) - when enabled, + Jalview's overview shows gaps as white, and sequences with no + colourscheme applied as grey. +

    +

    + Show Hidden regions when opening overview - default setting + for inclusion of hidden regions. +

    +

    + Gap Colour - When legacy gap colouring is not enabled, this + configures the default colour for gaps in the overview. +

    +

    + Hidden Colour - colour used to highlight regions in the + overview that are hidden in the alignment. +

    +

    + Gap Colour - The default colour scheme for a new alignment + window. If the chosen option is "User Defined" then the + last User Defined Colour loaded or saved via the User Defined + Colours panel will be loaded. +

    "Structure" Preferences tab added in Jalview 2.8.2 diff --git a/help/html/releases.html b/help/html/releases.html index c331ae1..a0efc10 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -77,11 +77,38 @@ li:before {

      +
    • + Show gaps in overview window by colouring + in grey (sequences used to be coloured grey, and gaps were + white) +
    • +
    • Overview tab in Jalview Desktop Preferences
      +
    • + Overview window redraws every hidden + column region row by row +
    • +
    • + duplicate protein sequences shown after + retrieving Ensembl crossrefs for sequences from Uniprot +
    • +
    • + Overview window throws NPE if show boxes + format setting is unticked +
    • +
    • + Groups are coloured wrongly in overview + if group has show boxes format setting unticked +
    • +
    • + Redraw problems when autoscrolling whilst + dragging current selection group to include sequences and + columns not currently displayed +
    diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html index c002eb8..169e7fe 100755 --- a/help/html/whatsNew.html +++ b/help/html/whatsNew.html @@ -30,6 +30,9 @@ This is patch release for 2.10.2. See the release notes.

    +
      +
    • Gaps are now rendered as dark grey in overview window
    • +

    What's new in Jalview 2.10.2 ?

    diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 162f10f..28fbfd5 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1311,3 +1311,12 @@ label.occupancy_descr = Number of aligned positions label.show_experimental = Enable experimental features label.show_experimental_tip = Enable any new and currently 'experimental' features (see Latest Release Notes for details) label.warning_hidden = Warning: {0} {1} is currently hidden +label.overview_settings = Overview settings +label.ov_legacy_gap = Use legacy gap colouring (gaps are white) +label.gap_colour = Gap colour: +label.ov_show_hide_default = Show hidden regions when opening overview +label.hidden_colour = Hidden colour: +label.select_gap_colour = Select gap colour +label.select_hidden_colour = Select hidden colour +label.overview = Overview +label.reset_to_defaults = Reset to defaults \ No newline at end of file diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 8385142..b5b5704 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1311,3 +1311,12 @@ label.togglehidden = Mostrar regiones ocultas label.show_experimental = Habilitar funciones experimentales label.show_experimental_tip = Habilitar funciones nuevas y experimentales (ver Latest Release Notes para más detalles) label.warning_hidden = Advertencia: {0} {1} está actualmente oculto +label.overview_settings = Ajustes para la ventana resumen +label.ov_legacy_gap = Utilizar el color heredado de huecos
    (los huecos son blancos) +label.gap_colour = Color de huecos: +label.ov_show_hide_default = Mostrar regiones ocultas al abrir el resumen +label.hidden_colour = Color de las regiones ocultas: +label.select_gap_colour = Seleccionar color de huecos +label.select_hidden_colour = Seleccionar color de las regiones ocultas +label.overview = Resumen +label.reset_to_defaults = Restablecen a los predeterminados \ No newline at end of file diff --git a/src/jalview/api/AlignmentColsCollectionI.java b/src/jalview/api/AlignmentColsCollectionI.java index 3a3f660..06b1675 100644 --- a/src/jalview/api/AlignmentColsCollectionI.java +++ b/src/jalview/api/AlignmentColsCollectionI.java @@ -30,4 +30,11 @@ public interface AlignmentColsCollectionI extends Iterable * @return true if the column at the position is hidden */ public boolean isHidden(int c); + + /** + * Answers if any column in this collection is hidden + * + * @return true if there is at least 1 hidden column + */ + public boolean hasHidden(); } diff --git a/src/jalview/api/AlignmentRowsCollectionI.java b/src/jalview/api/AlignmentRowsCollectionI.java index 24affc5..fbf6ceb 100644 --- a/src/jalview/api/AlignmentRowsCollectionI.java +++ b/src/jalview/api/AlignmentRowsCollectionI.java @@ -34,6 +34,13 @@ public interface AlignmentRowsCollectionI extends Iterable public boolean isHidden(int r); /** + * Answers if any row in this collection is hidden + * + * @return true if there is at least 1 hidden row + */ + public boolean hasHidden(); + + /** * Answers the sequence at the given position in the alignment * * @param r diff --git a/src/jalview/appletgui/OverviewCanvas.java b/src/jalview/appletgui/OverviewCanvas.java index a0466d3..9597b44 100644 --- a/src/jalview/appletgui/OverviewCanvas.java +++ b/src/jalview/appletgui/OverviewCanvas.java @@ -21,6 +21,7 @@ package jalview.appletgui; import jalview.renderer.OverviewRenderer; +import jalview.renderer.OverviewResColourFinder; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; @@ -48,10 +49,6 @@ public class OverviewCanvas extends Component private AlignViewport av; - // Can set different properties in this seqCanvas than - // main visible SeqCanvas - private SequenceRenderer sr; - private jalview.renderer.seqfeatures.FeatureRenderer fr; private Frame nullFrame; @@ -65,10 +62,6 @@ public class OverviewCanvas extends Component nullFrame = new Frame(); nullFrame.addNotify(); - sr = new SequenceRenderer(av); - sr.graphics = nullFrame.getGraphics(); - sr.renderGaps = false; - sr.forOverview = true; fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); } @@ -119,7 +112,8 @@ public class OverviewCanvas extends Component setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); - or = new OverviewRenderer(sr, fr, od); + or = new OverviewRenderer(fr, od, av.getAlignment(), + av.getResidueShading(), new OverviewResColourFinder()); miniMe = nullFrame.createImage(od.getWidth(), od.getHeight()); offscreen = nullFrame.createImage(od.getWidth(), od.getHeight()); diff --git a/src/jalview/appletgui/SequenceRenderer.java b/src/jalview/appletgui/SequenceRenderer.java index a382c60..e55f2a5 100755 --- a/src/jalview/appletgui/SequenceRenderer.java +++ b/src/jalview/appletgui/SequenceRenderer.java @@ -22,7 +22,7 @@ package jalview.appletgui; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import jalview.renderer.ResidueShaderI; +import jalview.renderer.ResidueColourFinder; import jalview.renderer.seqfeatures.FeatureColourFinder; import java.awt.Color; @@ -40,19 +40,18 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer boolean renderGaps = true; - SequenceGroup currentSequenceGroup = null; - SequenceGroup[] allGroups = null; Color resBoxColour; Graphics graphics; - boolean forOverview = false; + ResidueColourFinder resColourFinder; public SequenceRenderer(AlignViewport av) { this.av = av; + resColourFinder = new ResidueColourFinder(); } /** @@ -69,25 +68,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer this.renderGaps = renderGaps; } - protected Color getResidueBoxColour(SequenceI seq, int i) - { - allGroups = av.getAlignment().findAllGroups(seq); - - if (inCurrentSequenceGroup(i)) - { - if (currentSequenceGroup.getDisplayBoxes()) - { - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i); - } - } - else if (av.getShowBoxes()) - { - getBoxColour(av.getResidueShading(), seq, i); - } - - return resBoxColour; - } - /** * Get the residue colour at the given sequence position - as determined by * the sequence group colour (if any), else the colour scheme, possibly @@ -104,31 +84,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer { // TODO replace 8 or so code duplications with calls to this method // (refactored as needed) - Color col = getResidueBoxColour(seq, position); - - if (finder != null) - { - col = finder.findFeatureColour(col, seq, position); - } - return col; - } - - void getBoxColour(ResidueShaderI shader, SequenceI seq, int i) - { - if (shader.getColourScheme() != null) - { - resBoxColour = shader.findColour(seq.getCharAt(i), i, seq); - } - else if (forOverview - && !jalview.util.Comparison.isGap(seq.getCharAt(i))) - { - resBoxColour = Color.lightGray; - } - else - { - resBoxColour = Color.white; - } - + return resColourFinder.getResidueColour(av.getShowBoxes(), + av.getResidueShading(), + allGroups, seq, position, finder); } public Color findSequenceColour(SequenceI seq, int i) @@ -165,23 +123,28 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight(); + Color resBoxColour = Color.white; Color tempColour = null; while (i <= end) { resBoxColour = Color.white; if (i < length) { - if (inCurrentSequenceGroup(i)) + SequenceGroup currentSequenceGroup = resColourFinder + .getCurrentSequenceGroup(allGroups, i); + if (currentSequenceGroup != null) { if (currentSequenceGroup.getDisplayBoxes()) { - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, + resBoxColour = resColourFinder.getBoxColour( + currentSequenceGroup.getGroupColourScheme(), seq, i); } } else if (av.getShowBoxes()) { - getBoxColour(av.getResidueShading(), seq, i); + resBoxColour = resColourFinder + .getBoxColour(av.getResidueShading(), seq, i); } } @@ -245,7 +208,9 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer continue; } - if (inCurrentSequenceGroup(i)) + SequenceGroup currentSequenceGroup = resColourFinder + .getCurrentSequenceGroup(allGroups, i); + if (currentSequenceGroup != null) { if (!currentSequenceGroup.getDisplayText()) { @@ -254,7 +219,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer if (currentSequenceGroup.getColourText()) { - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i); + resBoxColour = resColourFinder.getBoxColour( + currentSequenceGroup.getGroupColourScheme(), seq, i); graphics.setColor(resBoxColour.darker()); } if (currentSequenceGroup.getShowNonconserved()) @@ -271,7 +237,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer if (av.getColourText()) { - getBoxColour(av.getResidueShading(), seq, i); + resBoxColour = resColourFinder + .getBoxColour(av.getResidueShading(), seq, i); if (av.getShowBoxes()) { graphics.setColor(resBoxColour.darker()); @@ -358,28 +325,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer return sequenceChar; } - boolean inCurrentSequenceGroup(int res) - { - if (allGroups == null) - { - return false; - } - - for (int i = 0; i < allGroups.length; i++) - { - if (allGroups[i].getStartRes() <= res - && allGroups[i].getEndRes() >= res) - { - currentSequenceGroup = allGroups[i]; - return true; - } - } - - return false; - } - - public void drawHighlightedText(SequenceI seq, int start, int end, int x1, - int y1) + public void drawHighlightedText(SequenceI seq, int start, int end, + int x1, int y1) { int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight(); int pady = avCharHeight / 5; diff --git a/src/jalview/datamodel/AllColsCollection.java b/src/jalview/datamodel/AllColsCollection.java index 4ac4f83..e216c46 100644 --- a/src/jalview/datamodel/AllColsCollection.java +++ b/src/jalview/datamodel/AllColsCollection.java @@ -50,4 +50,10 @@ public class AllColsCollection implements AlignmentColsCollectionI { return !hidden.isVisible(c); } + + @Override + public boolean hasHidden() + { + return hidden.hasHiddenColumns(); + } } diff --git a/src/jalview/datamodel/AllRowsCollection.java b/src/jalview/datamodel/AllRowsCollection.java index 9a33094..10c47f0 100644 --- a/src/jalview/datamodel/AllRowsCollection.java +++ b/src/jalview/datamodel/AllRowsCollection.java @@ -59,4 +59,10 @@ public class AllRowsCollection implements AlignmentRowsCollectionI { return alignment.getSequenceAtAbsoluteIndex(seq); } + + @Override + public boolean hasHidden() + { + return (hidden.getSize() > 0); + } } diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index 6964b53..e2f15e1 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -27,6 +27,8 @@ import jalview.renderer.ResidueShaderI; import jalview.schemes.ColourSchemeI; import java.awt.Color; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -39,6 +41,26 @@ import java.util.Map; */ public class SequenceGroup implements AnnotatedCollectionI { + // TODO ideally this event notification functionality should be separated into + // a + // subclass of ViewportProperties similarly to ViewportRanges. Done here as + // quick fix for JAL-2665 + public static final String SEQ_GROUP_CHANGED = "Sequence group changed"; + + protected PropertyChangeSupport changeSupport = new PropertyChangeSupport( + this); + + public void addPropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.removePropertyChangeListener(listener); + } + // end of event notification functionality initialisation + String groupName; String description; @@ -496,6 +518,8 @@ public class SequenceGroup implements AnnotatedCollectionI if (s != null && !sequences.contains(s)) { sequences.add(s); + changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, + sequences.size() - 1, sequences.size()); } if (recalc) @@ -693,6 +717,8 @@ public class SequenceGroup implements AnnotatedCollectionI synchronized (sequences) { sequences.remove(s); + changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, + sequences.size() + 1, sequences.size()); if (recalc) { @@ -729,7 +755,9 @@ public class SequenceGroup implements AnnotatedCollectionI */ public void setStartRes(int i) { + int before = startRes; startRes = i; + changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, startRes); } /** @@ -739,7 +767,9 @@ public class SequenceGroup implements AnnotatedCollectionI */ public void setEndRes(int i) { + int before = endRes; endRes = i; + changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, endRes); } /** @@ -1349,7 +1379,10 @@ public class SequenceGroup implements AnnotatedCollectionI { synchronized (sequences) { + int before = sequences.size(); sequences.clear(); + changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, + sequences.size()); } } diff --git a/src/jalview/datamodel/VisibleColsCollection.java b/src/jalview/datamodel/VisibleColsCollection.java index bc32fac..e9437a7 100644 --- a/src/jalview/datamodel/VisibleColsCollection.java +++ b/src/jalview/datamodel/VisibleColsCollection.java @@ -51,4 +51,10 @@ public class VisibleColsCollection implements AlignmentColsCollectionI return false; } + @Override + public boolean hasHidden() + { + return false; + } + } diff --git a/src/jalview/datamodel/VisibleRowsCollection.java b/src/jalview/datamodel/VisibleRowsCollection.java index ee0557f..fd7cf47 100644 --- a/src/jalview/datamodel/VisibleRowsCollection.java +++ b/src/jalview/datamodel/VisibleRowsCollection.java @@ -56,4 +56,10 @@ public class VisibleRowsCollection implements AlignmentRowsCollectionI { return alignment.getSequenceAtAbsoluteIndex(seq); } + + @Override + public boolean hasHidden() + { + return false; + } } diff --git a/src/jalview/datamodel/xdb/embl/EmblEntry.java b/src/jalview/datamodel/xdb/embl/EmblEntry.java index fe3f6ef..2de100b 100644 --- a/src/jalview/datamodel/xdb/embl/EmblEntry.java +++ b/src/jalview/datamodel/xdb/embl/EmblEntry.java @@ -279,7 +279,7 @@ public class EmblEntry String translation = null; String proteinName = ""; String proteinId = null; - Map vals = new Hashtable(); + Map vals = new Hashtable<>(); /* * codon_start 1/2/3 in EMBL corresponds to phase 0/1/2 in CDS diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 922e481..fe216c0 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -1103,8 +1103,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)) @@ -1231,7 +1231,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) diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java index 27f9c3f..ed1497a 100644 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@ -21,7 +21,9 @@ package jalview.gui; import jalview.api.AlignViewportI; +import jalview.bin.Cache; import jalview.renderer.OverviewRenderer; +import jalview.renderer.OverviewResColourFinder; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; @@ -57,6 +59,8 @@ public class OverviewCanvas extends JComponent private AlignViewportI av; + private OverviewResColourFinder cf; + public OverviewCanvas(OverviewDimensions overviewDims, AlignViewportI alignvp) { @@ -65,8 +69,14 @@ public class OverviewCanvas extends JComponent sr = new SequenceRenderer(av); sr.renderGaps = false; - sr.forOverview = true; fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); + + boolean useLegacy = Cache.getDefault(Preferences.USE_LEGACY_GAP, false); + Color gapCol = Cache.getDefaultColour(Preferences.GAP_COLOUR, + Preferences.OVERVIEW_DEFAULT_GAP); + Color hiddenCol = Cache.getDefaultColour(Preferences.HIDDEN_COLOUR, + Preferences.OVERVIEW_DEFAULT_HIDDEN); + cf = new OverviewResColourFinder(useLegacy, gapCol, hiddenCol); } /** @@ -126,7 +136,8 @@ public class OverviewCanvas extends JComponent setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); - or = new OverviewRenderer(sr, fr, od); + or = new OverviewRenderer(fr, od, av.getAlignment(), + av.getResidueShading(), cf); miniMe = or.draw(od.getRows(av.getAlignment()), od.getColumns(av.getAlignment())); diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 28de801..3c94f26 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import jalview.bin.Cache; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.OverviewDimensions; @@ -79,9 +80,20 @@ public class OverviewPanel extends JPanel this.av = alPanel.av; this.ap = alPanel; - od = new OverviewDimensionsShowHidden(av.getRanges(), + showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START, + true); + if (showHidden) + { + od = new OverviewDimensionsShowHidden(av.getRanges(), (av.isShowAnnotation() && av.getAlignmentConservationAnnotation() != null)); + } + else + { + od = new OverviewDimensionsHideHidden(av.getRanges(), + (av.isShowAnnotation() + && av.getAlignmentConservationAnnotation() != null)); + } setSize(od.getWidth(), od.getHeight()); @@ -158,7 +170,6 @@ public class OverviewPanel extends JPanel } } else - // if (!av.getWrapAlignment()) { if (!od.isPositionInBox(evt.getX(), evt.getY())) { diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java index c3c9239..7ceb04e 100755 --- a/src/jalview/gui/Preferences.java +++ b/src/jalview/gui/Preferences.java @@ -109,10 +109,25 @@ public class Preferences extends GPreferences public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY"; + public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START"; + + public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP"; + + public static final String GAP_COLOUR = "GAP_COLOUR"; + + public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR"; + private static final int MIN_FONT_SIZE = 1; private static final int MAX_FONT_SIZE = 30; + public static final Color OVERVIEW_DEFAULT_GAP = Color.lightGray; + + public static final Color OVERVIEW_DEFAULT_LEGACY_GAP = Color.white; + + public static final Color OVERVIEW_DEFAULT_HIDDEN = Color.darkGray + .darker(); + /** * Holds name and link separated with | character. Sequence ID must be * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$ @@ -155,7 +170,7 @@ public class Preferences extends GPreferences * .properties file as '|' separated strings */ - groupURLLinks = new ArrayList(); + groupURLLinks = new ArrayList<>(); } JInternalFrame frame; @@ -309,6 +324,20 @@ public class Preferences extends GPreferences Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red)); /* + * Set overview panel defaults + */ + gapColour.setBackground( + Cache.getDefaultColour(GAP_COLOUR, OVERVIEW_DEFAULT_GAP)); + hiddenColour.setBackground( + Cache.getDefaultColour(HIDDEN_COLOUR, + OVERVIEW_DEFAULT_HIDDEN)); + useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false)); + gapLabel.setEnabled(!useLegacyGap.isSelected()); + gapColour.setEnabled(!useLegacyGap.isSelected()); + showHiddenAtStart + .setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, true)); + + /* * Set Structure tab defaults. */ final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, false); @@ -631,6 +660,16 @@ public class Preferences extends GPreferences maxColour.getBackground()); /* + * Save Overview settings + */ + Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground()); + Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground()); + Cache.applicationProperties.setProperty(USE_LEGACY_GAP, + Boolean.toString(useLegacyGap.isSelected())); + Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START, + Boolean.toString(showHiddenAtStart.isSelected())); + + /* * Save Structure settings */ Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN, @@ -1035,6 +1074,60 @@ public class Preferences extends GPreferences } @Override + public void gapColour_actionPerformed(JPanel gap) + { + if (!useLegacyGap.isSelected()) + { + Color col = JColorChooser.showDialog(this, + MessageManager.getString("label.select_gap_colour"), + gapColour.getBackground()); + if (col != null) + { + gap.setBackground(col); + } + gap.repaint(); + } + } + + @Override + public void hiddenColour_actionPerformed(JPanel hidden) + { + Color col = JColorChooser.showDialog(this, + MessageManager.getString("label.select_hidden_colour"), + hiddenColour.getBackground()); + if (col != null) + { + hidden.setBackground(col); + } + hidden.repaint(); + } + + @Override + protected void useLegacyGaps_actionPerformed(ActionEvent e) + { + boolean enabled = useLegacyGap.isSelected(); + if (enabled) + { + gapColour.setBackground(OVERVIEW_DEFAULT_LEGACY_GAP); + } + else + { + gapColour.setBackground(OVERVIEW_DEFAULT_GAP); + } + gapColour.setEnabled(!enabled); + gapLabel.setEnabled(!enabled); + } + + @Override + protected void resetOvDefaults_actionPerformed(ActionEvent e) + { + useLegacyGap.setSelected(false); + useLegacyGaps_actionPerformed(null); + showHiddenAtStart.setSelected(true); + hiddenColour.setBackground(OVERVIEW_DEFAULT_HIDDEN); + } + + @Override protected void userIdWidth_actionPerformed() { try diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index a052ae3..3d8b8aa 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -30,6 +30,7 @@ import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; +import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@ -54,16 +55,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { final FeatureRenderer fr; - final SequenceRenderer sr; + final SequenceRenderer seqRdr; BufferedImage img; Graphics2D gg; - int imgWidth; - - int imgHeight; - AlignViewport av; boolean fastPaint = false; @@ -76,6 +73,14 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int cursorY = 0; + int charHeight = 0; + + int charWidth = 0; + + boolean fastpainting = false; + + AnnotationPanel annotations; + /** * Creates a new SeqCanvas object. * @@ -87,7 +92,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI this.av = ap.av; updateViewport(); fr = new FeatureRenderer(ap); - sr = new SequenceRenderer(av); + seqRdr = new SequenceRenderer(av); setLayout(new BorderLayout()); PaintRefresher.Register(this, av.getSequenceSetId()); setBackground(Color.white); @@ -97,7 +102,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public SequenceRenderer getSequenceRenderer() { - return sr; + return seqRdr; } public FeatureRenderer getFeatureRenderer() @@ -105,8 +110,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return fr; } - int charHeight = 0, charWidth = 0; - private void updateViewport() { charHeight = av.getCharHeight(); @@ -266,7 +269,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } - boolean fastpainting = false; /** * need to make this thread safe move alignment rendering in response to @@ -295,8 +297,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int transX = 0; int transY = 0; - gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth, - imgHeight, -horizontal * charWidth, -vertical * charHeight); + gg.copyArea(horizontal * charWidth, vertical * charHeight, + img.getWidth(), img.getHeight(), -horizontal * charWidth, + -vertical * charHeight); if (horizontal > 0) // scrollbar pulled right, image to the left { @@ -317,7 +320,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } else { - transY = imgHeight - ((vertical + 1) * charHeight); + transY = img.getHeight() - ((vertical + 1) * charHeight); } } else if (vertical < 0) @@ -351,73 +354,201 @@ public class SeqCanvas extends JComponent implements ViewportListenerI @Override public void paintComponent(Graphics g) { - updateViewport(); - BufferedImage lcimg = img; // take reference since other threads may null - // img and call later. super.paintComponent(g); - if (lcimg != null && (fastPaint + updateViewport(); + + ViewportRanges ranges = av.getRanges(); + + int width = getWidth(); + int height = getHeight(); + + width -= (width % charWidth); + height -= (height % charHeight); + + // selectImage is the selection group outline image + BufferedImage selectImage = drawSelectionGroup( + ranges.getStartRes(), ranges.getEndRes(), + ranges.getStartSeq(), ranges.getEndSeq()); + + if ((img != null) && (fastPaint || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g.getClipBounds().height))) { + BufferedImage lcimg = buildLocalImage(selectImage); g.drawImage(lcimg, 0, 0, this); fastPaint = false; - return; } + else if ((width > 0) && (height > 0)) + { + // img is a cached version of the last view we drew, if any + // if we have no img or the size has changed, make a new one + if (img == null || width != img.getWidth() + || height != img.getHeight()) + { + img = setupImage(); + if (img == null) + { + return; + } + gg = (Graphics2D) img.getGraphics(); + gg.setFont(av.getFont()); + } - // this draws the whole of the alignment - imgWidth = getWidth(); - imgHeight = getHeight(); + if (av.antiAlias) + { + gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } - imgWidth -= (imgWidth % charWidth); - imgHeight -= (imgHeight % charHeight); + gg.setColor(Color.white); + gg.fillRect(0, 0, img.getWidth(), img.getHeight()); - if ((imgWidth < 1) || (imgHeight < 1)) - { - return; + if (av.getWrapAlignment()) + { + drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes()); + } + else + { + drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(), + ranges.getStartSeq(), ranges.getEndSeq(), 0); + } + + // lcimg is a local *copy* of img which we'll draw selectImage on top of + BufferedImage lcimg = buildLocalImage(selectImage); + g.drawImage(lcimg, 0, 0, this); } + } + + /** + * Draw an alignment panel for printing + * + * @param g1 + * Graphics object to draw with + * @param startRes + * start residue of print area + * @param endRes + * end residue of print area + * @param startSeq + * start sequence of print area + * @param endSeq + * end sequence of print area + */ + public void drawPanelForPrinting(Graphics g1, int startRes, int endRes, + int startSeq, int endSeq) + { + BufferedImage selectImage = drawSelectionGroup(startRes, endRes, + startSeq, endSeq); + drawPanel(g1, startRes, endRes, startSeq, endSeq, 0); + ((Graphics2D) g1).setComposite( + AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + g1.drawImage(selectImage, 0, 0, this); + } - if (lcimg == null || imgWidth != lcimg.getWidth() - || imgHeight != lcimg.getHeight()) + /** + * Draw a wrapped alignment panel for printing + * + * @param g + * Graphics object to draw with + * @param canvasWidth + * width of drawing area + * @param canvasHeight + * height of drawing area + * @param startRes + * start residue of print area + */ + public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth, + int canvasHeight, int startRes) + { + SequenceGroup group = av.getSelectionGroup(); + + drawWrappedPanel(g, canvasWidth, canvasHeight, startRes); + + if (group != null) { + BufferedImage selectImage = null; try { - lcimg = img = new BufferedImage(imgWidth, imgHeight, - BufferedImage.TYPE_INT_RGB); - gg = (Graphics2D) img.getGraphics(); - gg.setFont(av.getFont()); + selectImage = new BufferedImage(canvasWidth, canvasHeight, + BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works } catch (OutOfMemoryError er) { System.gc(); - System.err.println("SeqCanvas OutOfMemory Redraw Error.\n" + er); - new OOMWarning("Creating alignment image for display", er); - - return; + System.err.println("Print image OutOfMemory Error.\n" + er); + new OOMWarning("Creating wrapped alignment image for printing", er); + } + if (selectImage != null) + { + Graphics2D g2 = selectImage.createGraphics(); + setupSelectionGroup(g2, selectImage); + drawWrappedSelection(g2, group, canvasWidth, canvasHeight, + startRes); + + g2.setComposite( + AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + g.drawImage(selectImage, 0, 0, this); + g2.dispose(); } } + } - if (av.antiAlias) + /* + * Make a local image by combining the cached image img + * with any selection + */ + private BufferedImage buildLocalImage(BufferedImage selectImage) + { + // clone the cached image + BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), + img.getType()); + Graphics2D g2d = lcimg.createGraphics(); + g2d.drawImage(img, 0, 0, null); + + // overlay selection group on lcimg + if (selectImage != null) { - gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setComposite( + AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + g2d.drawImage(selectImage, 0, 0, this); } + g2d.dispose(); - gg.setColor(Color.white); - gg.fillRect(0, 0, imgWidth, imgHeight); + return lcimg; + } - ViewportRanges ranges = av.getRanges(); - if (av.getWrapAlignment()) + /* + * Set up a buffered image of the correct height and size for the sequence canvas + */ + private BufferedImage setupImage() + { + BufferedImage lcimg = null; + + int width = getWidth(); + int height = getHeight(); + + width -= (width % charWidth); + height -= (height % charHeight); + + if ((width < 1) || (height < 1)) { - drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes()); + return null; } - else + + try { - drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq(), 0); - } + lcimg = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works + } catch (OutOfMemoryError er) + { + System.gc(); + System.err.println( + "Group image OutOfMemory Redraw Error.\n" + er); + new OOMWarning("Creating alignment image for display", er); - g.drawImage(lcimg, 0, 0, this); + return null; + } + return lcimg; } /** @@ -486,7 +617,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * @param startRes * DOCUMENT ME! */ - public void drawWrappedPanel(Graphics g, int canvasWidth, + private void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight, int startRes) { updateViewport(); @@ -625,7 +756,61 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } } - AnnotationPanel annotations; + /* + * Draw a selection group over a wrapped alignment + */ + private void drawWrappedSelection(Graphics2D g, SequenceGroup group, + int canvasWidth, + int canvasHeight, int startRes) + { + // height gap above each panel + int hgap = charHeight; + if (av.getScaleAboveWrapped()) + { + hgap += charHeight; + } + + int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth; + int cHeight = av.getAlignment().getHeight() * charHeight; + + int startx = startRes; + int endx; + int ypos = hgap; // vertical offset + int maxwidth = av.getAlignment().getWidth(); + + if (av.hasHiddenColumns()) + { + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth); + } + + // chop the wrapped alignment extent up into panel-sized blocks and treat + // each block as if it were a block from an unwrapped alignment + while ((ypos <= canvasHeight) && (startx < maxwidth)) + { + // set end value to be start + width, or maxwidth, whichever is smaller + endx = startx + cWidth - 1; + + if (endx > maxwidth) + { + endx = maxwidth; + } + + g.translate(LABEL_WEST, 0); + + drawUnwrappedSelection(g, group, startx, endx, 0, + av.getAlignment().getHeight() - 1, + ypos); + + g.translate(-LABEL_WEST, 0); + + // update vertical offset + ypos += cHeight + getAnnotationHeight() + hgap; + + // update horizontal offset + startx += cWidth; + } + } int getAnnotationHeight() { @@ -642,24 +827,24 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return annotations.adjustPanelHeight(); } - /** - * DOCUMENT ME! + /* + * Draw an alignment panel for printing * * @param g1 - * DOCUMENT ME! + * Graphics object to draw with * @param startRes - * DOCUMENT ME! + * start residue of print area * @param endRes - * DOCUMENT ME! + * end residue of print area * @param startSeq - * DOCUMENT ME! + * start sequence of print area * @param endSeq - * DOCUMENT ME! + * end sequence of print area * @param offset - * DOCUMENT ME! + * vertical offset */ - public void drawPanel(Graphics g1, int startRes, int endRes, int startSeq, - int endSeq, int offset) + private void drawPanel(Graphics g1, int startRes, int endRes, + int startSeq, int endSeq, int offset) { updateViewport(); if (!av.hasHiddenColumns()) @@ -723,13 +908,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } - // int startRes, int endRes, int startSeq, int endSeq, int x, int y, - // int x1, int x2, int y1, int y2, int startx, int starty, private void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset) { g.setFont(av.getFont()); - sr.prepare(g, av.isRenderGaps()); + seqRdr.prepare(g, av.isRenderGaps()); SequenceI nextSeq; @@ -744,7 +927,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // empty continue; } - sr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq), + seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq), startRes, endRes, offset + ((i - startSeq) * charHeight)); if (av.isShowSequenceFeatures()) @@ -763,10 +946,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { for (int r = 0; r < visibleResults.length; r += 2) { - sr.drawHighlightedText(nextSeq, visibleResults[r], - visibleResults[r + 1], - (visibleResults[r] - startRes) * charWidth, - offset + ((i - startSeq) * charHeight)); + seqRdr.drawHighlightedText(nextSeq, visibleResults[r], + visibleResults[r + 1], (visibleResults[r] - startRes) + * charWidth, offset + + ((i - startSeq) * charHeight)); } } } @@ -774,7 +957,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.cursorMode && cursorY == i && cursorX >= startRes && cursorX <= endRes) { - sr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth, + seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth, offset + ((i - startSeq) * charHeight)); } } @@ -795,15 +978,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI // /////////////////////////////////// // Now outline any areas if necessary // /////////////////////////////////// - SequenceGroup group = av.getSelectionGroup(); - int sx = -1; - int sy = -1; - int ex = -1; + SequenceGroup group = null; int groupIndex = -1; - int visWidth = (endRes - startRes + 1) * charWidth; - if ((group == null) && (av.getAlignment().getGroups().size() > 0)) + if (av.getAlignment().getGroups().size() > 0) { group = av.getAlignment().getGroups().get(0); groupIndex = 0; @@ -811,168 +990,309 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (group != null) { + g.setStroke(new BasicStroke()); + g.setColor(group.getOutlineColour()); + do { - int oldY = -1; - int i = 0; - boolean inGroup = false; - int top = -1; - int bottom = -1; + drawPartialGroupOutline(g, group, startRes, endRes, startSeq, + endSeq, offset); + + groupIndex++; + + g.setStroke(new BasicStroke()); + + if (groupIndex >= av.getAlignment().getGroups().size()) + { + break; + } + + group = av.getAlignment().getGroups().get(groupIndex); + + } while (groupIndex < av.getAlignment().getGroups().size()); + + } + + } + + + /* + * Draw the selection group as a separate image and overlay + */ + private BufferedImage drawSelectionGroup(int startRes, int endRes, + int startSeq, int endSeq) + { + // get a new image of the correct size + BufferedImage selectionImage = setupImage(); + + if (selectionImage == null) + { + return null; + } + + SequenceGroup group = av.getSelectionGroup(); + if (group == null) + { + // nothing to draw + return null; + } + + // set up drawing colour + Graphics2D g = (Graphics2D) selectionImage.getGraphics(); + + setupSelectionGroup(g, selectionImage); + + if (!av.getWrapAlignment()) + { + drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq, + 0); + } + else + { + drawWrappedSelection(g, group, getWidth(), getHeight(), + av.getRanges().getStartRes()); + } + + g.dispose(); + return selectionImage; + } + + /* + * Set up graphics for selection group + */ + private void setupSelectionGroup(Graphics2D g, + BufferedImage selectionImage) + { + // set background to transparent + g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); + g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight()); + + // set up foreground to draw red dashed line + g.setComposite(AlphaComposite.Src); + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, 3f, new float[] + { 5f, 3f }, 0f)); + g.setColor(Color.RED); + } + + /* + * Draw a selection group over an unwrapped alignment + * @param g graphics object to draw with + * @param group selection group + * @param startRes start residue of area to draw + * @param endRes end residue of area to draw + * @param startSeq start sequence of area to draw + * @param endSeq end sequence of area to draw + * @param offset vertical offset (used when called from wrapped alignment code) + */ + private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, + int startRes, int endRes, int startSeq, int endSeq, int offset) + { + if (!av.hasHiddenColumns()) + { + drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, + offset); + } + else + { + // package into blocks of visible columns + int screenY = 0; + int blockStart = startRes; + int blockEnd = endRes; + + for (int[] region : av.getAlignment().getHiddenColumns() + .getHiddenColumnsCopy()) + { + int hideStart = region[0]; + int hideEnd = region[1]; - for (i = startSeq; i <= endSeq; i++) + if (hideStart <= blockStart) { - sx = (group.getStartRes() - startRes) * charWidth; - sy = offset + ((i - startSeq) * charHeight); - ex = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) - - 1; + blockStart += (hideEnd - hideStart) + 1; + continue; + } - if (sx + ex < 0 || sx > visWidth) - { - continue; - } + blockEnd = hideStart - 1; - if ((sx <= (endRes - startRes) * charWidth) - && group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i))) - { - if ((bottom == -1) && !group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i + 1))) - { - bottom = sy + charHeight; - } - - if (!inGroup) - { - if (((top == -1) && (i == 0)) || !group.getSequences(null) - .contains(av.getAlignment().getSequenceAt(i - 1))) - { - top = sy; - } - - oldY = sy; - inGroup = true; - - if (group == av.getSelectionGroup()) - { - g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_ROUND, 3f, new float[] - { 5f, 3f }, 0f)); - g.setColor(Color.RED); - } - else - { - g.setStroke(new BasicStroke()); - g.setColor(group.getOutlineColour()); - } - } - } - else + g.translate(screenY * charWidth, 0); + drawPartialGroupOutline(g, group, + blockStart, blockEnd, startSeq, endSeq, offset); + + g.translate(-screenY * charWidth, 0); + screenY += blockEnd - blockStart + 1; + blockStart = hideEnd + 1; + + if (screenY > (endRes - startRes)) + { + // already rendered last block + break; + } + } + + if (screenY <= (endRes - startRes)) + { + // remaining visible region to render + blockEnd = blockStart + (endRes - startRes) - screenY; + g.translate(screenY * charWidth, 0); + drawPartialGroupOutline(g, group, + blockStart, blockEnd, startSeq, endSeq, offset); + + g.translate(-screenY * charWidth, 0); + } + } + } + + /* + * Draw the selection group as a separate image and overlay + */ + private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group, + int startRes, int endRes, int startSeq, int endSeq, + int verticalOffset) + { + int visWidth = (endRes - startRes + 1) * charWidth; + + int oldY = -1; + int i = 0; + boolean inGroup = false; + int top = -1; + int bottom = -1; + + int sx = -1; + int sy = -1; + int xwidth = -1; + + for (i = startSeq; i <= endSeq; i++) + { + // position of start residue of group relative to startRes, in pixels + sx = (group.getStartRes() - startRes) * charWidth; + + // width of group in pixels + xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth) + - 1; + + sy = verticalOffset + (i - startSeq) * charHeight; + + if (sx + xwidth < 0 || sx > visWidth) + { + continue; + } + + if ((sx <= (endRes - startRes) * charWidth) + && group.getSequences(null) + .contains(av.getAlignment().getSequenceAt(i))) + { + if ((bottom == -1) && !group.getSequences(null) + .contains(av.getAlignment().getSequenceAt(i + 1))) + { + bottom = sy + charHeight; + } + + if (!inGroup) + { + if (((top == -1) && (i == 0)) || !group.getSequences(null) + .contains(av.getAlignment().getSequenceAt(i - 1))) { - if (inGroup) - { - if (sx >= 0 && sx < visWidth) - { - g.drawLine(sx, oldY, sx, sy); - } - - if (sx + ex < visWidth) - { - g.drawLine(sx + ex, oldY, sx + ex, sy); - } - - if (sx < 0) - { - ex += sx; - sx = 0; - } - - if (sx + ex > visWidth) - { - ex = visWidth; - } - - else if (sx + ex >= (endRes - startRes + 1) * charWidth) - { - ex = (endRes - startRes + 1) * charWidth; - } - - if (top != -1) - { - g.drawLine(sx, top, sx + ex, top); - top = -1; - } - - if (bottom != -1) - { - g.drawLine(sx, bottom, sx + ex, bottom); - bottom = -1; - } - - inGroup = false; - } + top = sy; } - } + oldY = sy; + inGroup = true; + } + } + else + { if (inGroup) { - sy = offset + ((i - startSeq) * charHeight); + // if start position is visible, draw vertical line to left of + // group if (sx >= 0 && sx < visWidth) { g.drawLine(sx, oldY, sx, sy); } - if (sx + ex < visWidth) + // if end position is visible, draw vertical line to right of + // group + if (sx + xwidth < visWidth) { - g.drawLine(sx + ex, oldY, sx + ex, sy); + g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); } if (sx < 0) { - ex += sx; + xwidth += sx; sx = 0; } - if (sx + ex > visWidth) + // don't let width extend beyond current block, or group extent + // fixes JAL-2672 + if (sx + xwidth >= (endRes - startRes + 1) * charWidth) { - ex = visWidth; + xwidth = (endRes - startRes + 1) * charWidth - sx; } - else if (sx + ex >= (endRes - startRes + 1) * charWidth) - { - ex = (endRes - startRes + 1) * charWidth; - } - + + // draw horizontal line at top of group if (top != -1) { - g.drawLine(sx, top, sx + ex, top); + g.drawLine(sx, top, sx + xwidth, top); top = -1; } + // draw horizontal line at bottom of group if (bottom != -1) { - g.drawLine(sx, bottom - 1, sx + ex, bottom - 1); + g.drawLine(sx, bottom, sx + xwidth, bottom); bottom = -1; } inGroup = false; } + } + } - groupIndex++; + if (inGroup) + { + sy = verticalOffset + ((i - startSeq) * charHeight); + if (sx >= 0 && sx < visWidth) + { + g.drawLine(sx, oldY, sx, sy); + } - g.setStroke(new BasicStroke()); + if (sx + xwidth < visWidth) + { + g.drawLine(sx + xwidth, oldY, sx + xwidth, sy); + } - if (groupIndex >= av.getAlignment().getGroups().size()) - { - break; - } + if (sx < 0) + { + xwidth += sx; + sx = 0; + } - group = av.getAlignment().getGroups().get(groupIndex); + if (sx + xwidth > visWidth) + { + xwidth = visWidth; + } + else if (sx + xwidth >= (endRes - startRes + 1) * charWidth) + { + xwidth = (endRes - startRes + 1) * charWidth; + } - } while (groupIndex < av.getAlignment().getGroups().size()); + if (top != -1) + { + g.drawLine(sx, top, sx + xwidth, top); + top = -1; + } - } + if (bottom != -1) + { + g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1); + bottom = -1; + } + inGroup = false; + } } - + /** * DOCUMENT ME! * @@ -993,7 +1313,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { String eventName = evt.getPropertyName(); - if (av.getWrapAlignment()) + if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED)) + { + fastPaint = true; + repaint(); + } + else if (av.getWrapAlignment()) { if (eventName.equals(ViewportRanges.STARTRES)) { diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 056a602..328dfdc 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -1710,43 +1710,53 @@ public class SeqPanel extends JPanel if (stretchGroup == null) { - // Only if left mouse button do we want to change group sizes + createStretchGroup(res, sequence); + } - // define a new group here - SequenceGroup sg = new SequenceGroup(); - sg.setStartRes(res); - sg.setEndRes(res); - sg.addSequence(sequence, false); - av.setSelectionGroup(sg); - stretchGroup = sg; + if (stretchGroup != null) + { + stretchGroup.addPropertyChangeListener(seqCanvas); + } - if (av.getConservationSelected()) - { - SliderPanel.setConservationSlider(ap, av.getResidueShading(), - ap.getViewName()); - } + seqCanvas.repaint(); + } - if (av.getAbovePIDThreshold()) - { - SliderPanel.setPIDSliderSource(ap, av.getResidueShading(), - ap.getViewName()); - } - // TODO: stretchGroup will always be not null. Is this a merge error ? - if ((stretchGroup != null) && (stretchGroup.getEndRes() == res)) - { - // Edit end res position of selected group - changeEndRes = true; - } - else if ((stretchGroup != null) - && (stretchGroup.getStartRes() == res)) - { - // Edit end res position of selected group - changeStartRes = true; - } - stretchGroup.getWidth(); + private void createStretchGroup(int res, SequenceI sequence) + { + // Only if left mouse button do we want to change group sizes + // define a new group here + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(res); + sg.setEndRes(res); + sg.addSequence(sequence, false); + av.setSelectionGroup(sg); + stretchGroup = sg; + + if (av.getConservationSelected()) + { + SliderPanel.setConservationSlider(ap, av.getResidueShading(), + ap.getViewName()); } - seqCanvas.repaint(); + if (av.getAbovePIDThreshold()) + { + SliderPanel.setPIDSliderSource(ap, av.getResidueShading(), + ap.getViewName()); + } + // TODO: stretchGroup will always be not null. Is this a merge error ? + // or is there a threading issue here? + if ((stretchGroup != null) && (stretchGroup.getEndRes() == res)) + { + // Edit end res position of selected group + changeEndRes = true; + } + else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res)) + { + // Edit end res position of selected group + changeStartRes = true; + } + stretchGroup.getWidth(); + } /** @@ -1795,6 +1805,9 @@ public class SeqPanel extends JPanel { return; } + + stretchGroup.removePropertyChangeListener(seqCanvas); + // always do this - annotation has own state // but defer colourscheme update until hidden sequences are passed in boolean vischange = stretchGroup.recalcConservation(true); @@ -1940,8 +1953,6 @@ public class SeqPanel extends JPanel { scrollThread.setEvent(evt); } - - seqCanvas.repaint(); } void scrollCanvas(MouseEvent evt) diff --git a/src/jalview/gui/SequenceRenderer.java b/src/jalview/gui/SequenceRenderer.java index 4498f88..5be7f55 100755 --- a/src/jalview/gui/SequenceRenderer.java +++ b/src/jalview/gui/SequenceRenderer.java @@ -23,9 +23,8 @@ package jalview.gui; import jalview.api.AlignViewportI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import jalview.renderer.ResidueShaderI; +import jalview.renderer.ResidueColourFinder; import jalview.renderer.seqfeatures.FeatureColourFinder; -import jalview.util.Comparison; import java.awt.Color; import java.awt.FontMetrics; @@ -41,17 +40,15 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer boolean renderGaps = true; - SequenceGroup currentSequenceGroup = null; - SequenceGroup[] allGroups = null; - Color resBoxColour; + // Color resBoxColour; Graphics graphics; boolean monospacedFont; - boolean forOverview = false; + ResidueColourFinder resColourFinder; /** * Creates a new SequenceRenderer object @@ -61,6 +58,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer public SequenceRenderer(AlignViewportI viewport) { this.av = viewport; + resColourFinder = new ResidueColourFinder(); } /** @@ -83,26 +81,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer this.renderGaps = renderGaps; } - protected Color getResidueBoxColour(SequenceI seq, int i) - { - // rate limiting step when rendering overview for lots of groups - allGroups = av.getAlignment().findAllGroups(seq); - - if (inCurrentSequenceGroup(i)) - { - if (currentSequenceGroup.getDisplayBoxes()) - { - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, i); - } - } - else if (av.getShowBoxes()) - { - getBoxColour(av.getResidueShading(), seq, i); - } - - return resBoxColour; - } - /** * Get the residue colour at the given sequence position - as determined by * the sequence group colour (if any), else the colour scheme, possibly @@ -117,39 +95,10 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer public Color getResidueColour(final SequenceI seq, int position, FeatureColourFinder finder) { - Color col = getResidueBoxColour(seq, position); - - if (finder != null) - { - col = finder.findFeatureColour(col, seq, position); - } - return col; - } - - /** - * DOCUMENT ME! - * - * @param shader - * DOCUMENT ME! - * @param seq - * DOCUMENT ME! - * @param i - * DOCUMENT ME! - */ - void getBoxColour(ResidueShaderI shader, SequenceI seq, int i) - { - if (shader.getColourScheme() != null) - { - resBoxColour = shader.findColour(seq.getCharAt(i), i, seq); - } - else if (forOverview && !Comparison.isGap(seq.getCharAt(i))) - { - resBoxColour = Color.lightGray; - } - else - { - resBoxColour = Color.white; - } + return resColourFinder.getResidueColour(av.getShowBoxes(), + av.getResidueShading(), + allGroups, seq, position, + finder); } /** @@ -208,6 +157,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer public synchronized void drawBoxes(SequenceI seq, int start, int end, int y1) { + Color resBoxColour = Color.white; + if (seq == null) { return; // fix for racecondition @@ -227,17 +178,22 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer if (i < length) { - if (inCurrentSequenceGroup(i)) + SequenceGroup currentSequenceGroup = resColourFinder + .getCurrentSequenceGroup( + allGroups, i); + if (currentSequenceGroup != null) { if (currentSequenceGroup.getDisplayBoxes()) { - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, + resBoxColour = resColourFinder.getBoxColour( + currentSequenceGroup.getGroupColourScheme(), seq, i); } } else if (av.getShowBoxes()) { - getBoxColour(av.getResidueShading(), seq, i); + resBoxColour = resColourFinder + .getBoxColour(av.getResidueShading(), seq, i); } } @@ -317,10 +273,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer boolean srep = av.isDisplayReferenceSeq(); boolean getboxColour = false; boolean isarep = av.getAlignment().getSeqrep() == seq; - boolean isgrep = currentSequenceGroup != null - ? currentSequenceGroup.getSeqrep() == seq - : false; - char sr_c; + Color resBoxColour = Color.white; + for (int i = start; i <= end; i++) { @@ -333,7 +287,10 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer continue; } - if (inCurrentSequenceGroup(i)) + SequenceGroup currentSequenceGroup = resColourFinder + .getCurrentSequenceGroup( + allGroups, i); + if (currentSequenceGroup != null) { if (!currentSequenceGroup.getDisplayText()) { @@ -344,7 +301,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer || currentSequenceGroup.getColourText()) { getboxColour = true; - getBoxColour(currentSequenceGroup.getGroupColourScheme(), seq, + resBoxColour = resColourFinder.getBoxColour( + currentSequenceGroup.getGroupColourScheme(), seq, i); if (currentSequenceGroup.getColourText()) @@ -366,6 +324,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer { graphics.setColor(currentSequenceGroup.textColour); } + boolean isgrep = currentSequenceGroup != null + ? currentSequenceGroup.getSeqrep() == seq : false; if (!isarep && !isgrep && currentSequenceGroup.getShowNonconserved()) // todo // optimize @@ -386,7 +346,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer if (av.getColourText()) { getboxColour = true; - getBoxColour(av.getResidueShading(), seq, i); + resBoxColour = resColourFinder + .getBoxColour(av.getResidueShading(), seq, i); if (av.getShowBoxes()) { @@ -402,7 +363,8 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer { if (!getboxColour) { - getBoxColour(av.getResidueShading(), seq, i); + resBoxColour = resColourFinder + .getBoxColour(av.getResidueShading(), seq, i); } if (resBoxColour.getRed() + resBoxColour.getBlue() @@ -473,35 +435,6 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer /** * DOCUMENT ME! * - * @param res - * DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - boolean inCurrentSequenceGroup(int res) - { - if (allGroups == null) - { - return false; - } - - for (int i = 0; i < allGroups.length; i++) - { - if ((allGroups[i].getStartRes() <= res) - && (allGroups[i].getEndRes() >= res)) - { - currentSequenceGroup = allGroups[i]; - - return true; - } - } - - return false; - } - - /** - * DOCUMENT ME! - * * @param seq * DOCUMENT ME! * @param start diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java index 633d2b8..1ca0802 100755 --- a/src/jalview/jbgui/GPreferences.java +++ b/src/jalview/jbgui/GPreferences.java @@ -95,11 +95,11 @@ public class GPreferences extends JPanel protected JCheckBox rightAlign = new JCheckBox(); - protected JComboBox fontSizeCB = new JComboBox(); + protected JComboBox fontSizeCB = new JComboBox<>(); - protected JComboBox fontStyleCB = new JComboBox(); + protected JComboBox fontStyleCB = new JComboBox<>(); - protected JComboBox fontNameCB = new JComboBox(); + protected JComboBox fontNameCB = new JComboBox<>(); protected JCheckBox showOccupancy = new JCheckBox(); @@ -111,15 +111,15 @@ public class GPreferences extends JPanel protected JCheckBox scaleProteinToCdna = new JCheckBox(); - protected JComboBox gapSymbolCB = new JComboBox(); + protected JComboBox gapSymbolCB = new JComboBox<>(); protected JCheckBox wrap = new JCheckBox(); - protected JComboBox sortby = new JComboBox(); + protected JComboBox sortby = new JComboBox<>(); - protected JComboBox sortAnnBy = new JComboBox(); + protected JComboBox sortAnnBy = new JComboBox<>(); - protected JComboBox sortAutocalc = new JComboBox(); + protected JComboBox sortAutocalc = new JComboBox<>(); protected JCheckBox startupCheckbox = new JCheckBox(); @@ -159,7 +159,7 @@ public class GPreferences extends JPanel protected JCheckBox addTempFactor = new JCheckBox(); - protected JComboBox structViewer = new JComboBox(); + protected JComboBox structViewer = new JComboBox<>(); protected JTextField chimeraPath = new JTextField(); @@ -176,9 +176,22 @@ public class GPreferences extends JPanel protected JPanel maxColour = new JPanel(); - protected JComboBox protColour = new JComboBox(); + protected JComboBox protColour = new JComboBox<>(); - protected JComboBox nucColour = new JComboBox(); + protected JComboBox nucColour = new JComboBox<>(); + + /* + * Overview tab components + */ + protected JPanel gapColour = new JPanel(); + + protected JPanel hiddenColour = new JPanel(); + + protected JCheckBox useLegacyGap; + + protected JCheckBox showHiddenAtStart; + + protected JLabel gapLabel; /* * Connections tab components @@ -216,7 +229,7 @@ public class GPreferences extends JPanel /* * Output tab components */ - protected JComboBox epsRendering = new JComboBox(); + protected JComboBox epsRendering = new JComboBox<>(); protected JLabel userIdWidthlabel = new JLabel(); @@ -294,6 +307,9 @@ public class GPreferences extends JPanel tabbedPane.add(initColoursTab(), MessageManager.getString("label.colours")); + tabbedPane.add(initOverviewTab(), + MessageManager.getString("label.overview")); + tabbedPane.add(initStructureTab(), MessageManager.getString("label.structure")); @@ -963,6 +979,151 @@ public class GPreferences extends JPanel } /** + * Initialises the Overview tabbed panel. + * + * @return + */ + private JPanel initOverviewTab() + { + JPanel overviewPanel = new JPanel(); + overviewPanel.setBorder(new TitledBorder( + MessageManager.getString("label.overview_settings"))); + + gapColour.setFont(LABEL_FONT); + // fixing the border colours stops apparent colour bleed from the panel + gapColour.setBorder( + BorderFactory.createEtchedBorder(Color.white, Color.lightGray)); + gapColour.setPreferredSize(new Dimension(40, 20)); + gapColour.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent e) + { + gapColour_actionPerformed(gapColour); + } + }); + + hiddenColour.setFont(LABEL_FONT); + // fixing the border colours stops apparent colour bleed from the panel + hiddenColour.setBorder( + BorderFactory.createEtchedBorder(Color.white, Color.lightGray)); + hiddenColour.setPreferredSize(new Dimension(40, 20)); + hiddenColour.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent e) + { + hiddenColour_actionPerformed(hiddenColour); + } + }); + + useLegacyGap = new JCheckBox( + MessageManager.getString("label.ov_legacy_gap")); + useLegacyGap.setFont(LABEL_FONT); + useLegacyGap.setHorizontalAlignment(SwingConstants.LEFT); + useLegacyGap.setVerticalTextPosition(SwingConstants.TOP); + gapLabel = new JLabel( + MessageManager.getString("label.gap_colour")); + gapLabel.setFont(LABEL_FONT); + gapLabel.setHorizontalAlignment(SwingConstants.LEFT); + gapLabel.setVerticalTextPosition(SwingConstants.TOP); + showHiddenAtStart = new JCheckBox( + MessageManager.getString("label.ov_show_hide_default")); + showHiddenAtStart.setFont(LABEL_FONT); + showHiddenAtStart.setHorizontalAlignment(SwingConstants.LEFT); + showHiddenAtStart.setVerticalTextPosition(SwingConstants.TOP); + JLabel hiddenLabel = new JLabel( + MessageManager.getString("label.hidden_colour")); + hiddenLabel.setFont(LABEL_FONT); + hiddenLabel.setHorizontalAlignment(SwingConstants.LEFT); + hiddenLabel.setVerticalTextPosition(SwingConstants.TOP); + + useLegacyGap.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + useLegacyGaps_actionPerformed(e); + } + }); + + overviewPanel.setLayout(new GridBagLayout()); + GridBagConstraints c1 = new GridBagConstraints(); + + c1.fill = GridBagConstraints.HORIZONTAL; + c1.gridx = 0; + c1.gridy = 0; + c1.weightx = 1; + c1.ipady = 20; + c1.anchor = GridBagConstraints.FIRST_LINE_START; + overviewPanel.add(useLegacyGap, c1); + + GridBagConstraints c2 = new GridBagConstraints(); + c2.fill = GridBagConstraints.HORIZONTAL; + c2.gridx = 1; + c2.gridy = 0; + c2.insets = new Insets(0, 15, 0, 10); + overviewPanel.add(gapLabel, c2); + + GridBagConstraints c3 = new GridBagConstraints(); + c3.fill = GridBagConstraints.HORIZONTAL; + c3.gridx = 2; + c3.gridy = 0; + c3.insets = new Insets(0, 0, 0, 15); + overviewPanel.add(gapColour, c3); + + GridBagConstraints c4 = new GridBagConstraints(); + c4.fill = GridBagConstraints.HORIZONTAL; + c4.gridx = 0; + c4.gridy = 1; + c4.weightx = 1; + overviewPanel.add(showHiddenAtStart, c4); + + GridBagConstraints c5 = new GridBagConstraints(); + c5.fill = GridBagConstraints.HORIZONTAL; + c5.gridx = 1; + c5.gridy = 1; + c5.insets = new Insets(0, 15, 0, 10); + overviewPanel.add(hiddenLabel, c5); + + GridBagConstraints c6 = new GridBagConstraints(); + c6.fill = GridBagConstraints.HORIZONTAL; + c6.gridx = 2; + c6.gridy = 1; + c6.insets = new Insets(0, 0, 0, 15); + overviewPanel.add(hiddenColour, c6); + + JButton resetButton = new JButton( + MessageManager.getString("label.reset_to_defaults")); + + resetButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + resetOvDefaults_actionPerformed(e); + } + }); + + GridBagConstraints c7 = new GridBagConstraints(); + c7.fill = GridBagConstraints.NONE; + c7.gridx = 0; + c7.gridy = 2; + c7.insets = new Insets(10, 0, 0, 0); + c7.anchor = GridBagConstraints.WEST; + overviewPanel.add(resetButton, c7); + + // Add padding so the panel doesn't look ridiculous + JPanel spacePanel = new JPanel(); + overviewPanel.add(spacePanel, + new GridBagConstraints(0, 3, 1, 1, 1.0, 1.0, + GridBagConstraints.WEST, GridBagConstraints.BOTH, + new Insets(0, 0, 0, 5), 0, 0)); + + return overviewPanel; + } + + /** * Initialises the Structure tabbed panel. * * @return @@ -1494,12 +1655,28 @@ public class GPreferences extends JPanel { } + protected void gapColour_actionPerformed(JPanel panel) + { + } + + protected void hiddenColour_actionPerformed(JPanel panel) + { + } + protected void showunconserved_actionPerformed(ActionEvent e) { // TODO Auto-generated method stub } + protected void useLegacyGaps_actionPerformed(ActionEvent e) + { + } + + protected void resetOvDefaults_actionPerformed(ActionEvent e) + { + } + /** * DOCUMENT ME! * diff --git a/src/jalview/renderer/OverviewRenderer.java b/src/jalview/renderer/OverviewRenderer.java index 0f8cda6..992a1b2 100644 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@ -23,21 +23,28 @@ package jalview.renderer; import jalview.api.AlignmentColsCollectionI; import jalview.api.AlignmentRowsCollectionI; import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.renderer.seqfeatures.FeatureRenderer; import jalview.viewmodel.OverviewDimensions; +import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; public class OverviewRenderer { - private FeatureColourFinder finder; + // transparency of hidden cols/seqs overlay + private final float TRANSPARENCY = 0.5f; + + private final Color HIDDEN_COLOUR = Color.DARK_GRAY.darker(); - private jalview.api.SequenceRenderer sr; + private FeatureColourFinder finder; // image to render on private BufferedImage miniMe; @@ -51,11 +58,22 @@ public class OverviewRenderer // flag to indicate whether to halt drawing private volatile boolean redraw = false; - public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer, - FeatureRenderer fr, OverviewDimensions od) + // reference to alignment, needed to get sequence groups + private AlignmentI al; + + private ResidueShaderI shader; + + private OverviewResColourFinder resColFinder; + + public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od, + AlignmentI alignment, + ResidueShaderI resshader, OverviewResColourFinder colFinder) { - sr = seqRenderer; finder = new FeatureColourFinder(fr); + resColFinder = colFinder; + + al = alignment; + shader = resshader; pixelsPerCol = od.getPixelsPerCol(); pixelsPerSeq = od.getPixelsPerSeq(); @@ -85,15 +103,17 @@ public class OverviewRenderer { break; } - + // get details of this alignment row - boolean hidden = rows.isHidden(alignmentRow); SequenceI seq = rows.getSequence(alignmentRow); + // rate limiting step when rendering overview for lots of groups + SequenceGroup[] allGroups = al.findAllGroups(seq); + // calculate where this row extends to in pixels int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1, miniMe.getHeight() - 1); - + int colIndex = 0; int pixelCol = 0; for (int alignmentCol : cols) @@ -102,21 +122,19 @@ public class OverviewRenderer { break; } - + // calculate where this column extends to in pixels int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1, miniMe.getWidth() - 1); - + // don't do expensive colour determination if we're not going to use it // NB this is important to avoid performance issues in the overview // panel if (pixelCol <= endCol) { - // determine the colour based on the sequence and column position - rgbcolor = getColumnColourFromSequence(seq, - hidden || cols.isHidden(alignmentCol), alignmentCol, - finder); - + rgbcolor = getColumnColourFromSequence(allGroups, seq, + alignmentCol, finder); + // fill in the appropriate number of pixels for (int row = pixelRow; row <= endRow; ++row) { @@ -125,7 +143,7 @@ public class OverviewRenderer miniMe.setRGB(col, row, rgbcolor); } } - + pixelCol = endCol + 1; } colIndex++; @@ -133,28 +151,146 @@ public class OverviewRenderer pixelRow = endRow + 1; seqIndex++; } + + overlayHiddenRegions(rows, cols); return miniMe; } - /* + /** * Find the colour of a sequence at a specified column position + * + * @param seq + * sequence to get colour for + * @param lastcol + * column position to get colour for + * @param fcfinder + * FeatureColourFinder to use + * @return colour of sequence at this position, as RGB */ - private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq, - boolean isHidden, int lastcol, FeatureColourFinder fcfinder) + private int getColumnColourFromSequence(SequenceGroup[] allGroups, + jalview.datamodel.SequenceI seq, + int lastcol, FeatureColourFinder fcfinder) { Color color = Color.white; if ((seq != null) && (seq.getLength() > lastcol)) { - color = sr.getResidueColour(seq, lastcol, fcfinder); + color = resColFinder.getResidueColour(true, shader, allGroups, seq, + lastcol, + fcfinder); } - if (isHidden) + return color.getRGB(); + } + + /** + * Overlay the hidden regions on the overview image + * + * @param rows + * collection of rows the overview is built over + * @param cols + * collection of columns the overview is built over + */ + private void overlayHiddenRegions(AlignmentRowsCollectionI rows, + AlignmentColsCollectionI cols) + { + if (cols.hasHidden() || rows.hasHidden()) { - color = color.darker().darker(); + BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(), + miniMe.getHeight()); + + Graphics2D g = (Graphics2D) miniMe.getGraphics(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, + TRANSPARENCY)); + g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null); } + } - return color.getRGB(); + /** + * Build a masking image of hidden columns and rows to be applied on top of + * the main overview image. + * + * @param rows + * collection of rows the overview is built over + * @param cols + * collection of columns the overview is built over + * @param width + * width of overview in pixels + * @param height + * height of overview in pixels + * @return BufferedImage containing mask of hidden regions + */ + private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows, + AlignmentColsCollectionI cols, int width, int height) + { + // new masking image + BufferedImage hiddenImage = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); + + int colIndex = 0; + int pixelCol = 0; + + Color hidden = resColFinder.getHiddenColour(); + + Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics(); + + // set background to transparent + g2d.setComposite(AlphaComposite.Clear); + g2d.fillRect(0, 0, width, height); + + // set next colour to opaque + g2d.setComposite(AlphaComposite.Src); + + for (int alignmentCol : cols) + { + if (redraw) + { + break; + } + + // calculate where this column extends to in pixels + int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1, + hiddenImage.getWidth() - 1); + + if (pixelCol <= endCol) + { + // determine the colour based on the sequence and column position + if (cols.isHidden(alignmentCol)) + { + g2d.setColor(hidden); + g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height); + } + + pixelCol = endCol + 1; + } + colIndex++; + + } + + int seqIndex = 0; + int pixelRow = 0; + for (int alignmentRow : rows) + { + if (redraw) + { + break; + } + + // calculate where this row extends to in pixels + int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1, + miniMe.getHeight() - 1); + + // get details of this alignment row + if (rows.isHidden(alignmentRow)) + { + g2d.setColor(hidden); + g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1); + } + pixelRow = endRow + 1; + seqIndex++; + } + + return hiddenImage; } /** @@ -222,6 +358,13 @@ public class OverviewRenderer } } + /** + * Allows redraw flag to be set + * + * @param b + * value to set redraw to: true = redraw is occurring, false = no + * redraw + */ public void setRedraw(boolean b) { synchronized (this) diff --git a/src/jalview/renderer/OverviewResColourFinder.java b/src/jalview/renderer/OverviewResColourFinder.java new file mode 100644 index 0000000..b606ba3 --- /dev/null +++ b/src/jalview/renderer/OverviewResColourFinder.java @@ -0,0 +1,138 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.renderer; + +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.gui.Preferences; +import jalview.util.Comparison; + +import java.awt.Color; + +public class OverviewResColourFinder extends ResidueColourFinder +{ + final Color GAP_COLOUR; // default colour to use at gaps + + final Color RESIDUE_COLOUR; // default colour to use at residues + + final Color HIDDEN_COLOUR; // colour for hidden regions + + boolean useLegacy = false; + + /** + * Constructor without colour settings (used by applet) + */ + public OverviewResColourFinder() + { + this(false, Preferences.OVERVIEW_DEFAULT_GAP, + Preferences.OVERVIEW_DEFAULT_HIDDEN); + } + + /** + * Constructor with colour settings + * + * @param useLegacyColouring + * whether to use legacy gap colouring (white gaps, grey residues) + * @param gapCol + * gap colour if not legacy + * @param hiddenCol + * hidden region colour (transparency applied by rendering code) + */ + public OverviewResColourFinder(boolean useLegacyColouring, Color gapCol, + Color hiddenCol) + { + if (useLegacyColouring) + { + GAP_COLOUR = Color.white; + RESIDUE_COLOUR = Color.lightGray; + HIDDEN_COLOUR = hiddenCol; + } + else + { + GAP_COLOUR = gapCol; + RESIDUE_COLOUR = Color.white; + HIDDEN_COLOUR = hiddenCol; + } + } + + @Override + public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i) + { + Color resBoxColour = RESIDUE_COLOUR; + char currentChar = seq.getCharAt(i); + + // In the overview window, gaps are coloured grey, unless the colour scheme + // specifies a gap colour, in which case gaps honour the colour scheme + // settings + if (shader.getColourScheme() != null) + { + if (Comparison.isGap(currentChar) + && (!shader.getColourScheme().hasGapColour())) + { + resBoxColour = GAP_COLOUR; + } + else + { + resBoxColour = shader.findColour(currentChar, i, seq); + } + } + else if (Comparison.isGap(currentChar)) + { + resBoxColour = GAP_COLOUR; + } + + return resBoxColour; + } + + /** + * {@inheritDoc} In the overview, the showBoxes setting is ignored, as the + * overview displays the colours regardless. + */ + @Override + protected Color getResidueBoxColour(boolean showBoxes, + ResidueShaderI shader, + SequenceGroup[] allGroups, SequenceI seq, int i) + { + ResidueShaderI currentShader; + SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups, + i); + if (currentSequenceGroup != null) + { + currentShader = currentSequenceGroup.getGroupColourScheme(); + } + else + { + currentShader = shader; + } + + return getBoxColour(currentShader, seq, i); + } + + /** + * Supply hidden colour + * + * @return colour of hidden regions + */ + protected Color getHiddenColour() + { + return HIDDEN_COLOUR; + } +} diff --git a/src/jalview/renderer/ResidueColourFinder.java b/src/jalview/renderer/ResidueColourFinder.java new file mode 100644 index 0000000..2da7233 --- /dev/null +++ b/src/jalview/renderer/ResidueColourFinder.java @@ -0,0 +1,156 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.renderer; + +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; + +import java.awt.Color; + +public class ResidueColourFinder +{ + public ResidueColourFinder() + { + } + + /** + * Get the colour of a residue in a sequence + * + * @param showBoxes + * true if the viewport's Show Boxes setting is true + * @param shader + * the viewport's colour scheme + * @param allGroups + * all the groups which seq participates in + * @param seq + * the sequence containing the residue + * @param position + * the position of the residue in the sequence + * @param finder + * FeatureColourFinder for the viewport + * @return colour of the residue + */ + public Color getResidueColour(boolean showBoxes, ResidueShaderI shader, + SequenceGroup[] allGroups, + final SequenceI seq, int position, FeatureColourFinder finder) + { + Color col = getResidueBoxColour(showBoxes, shader, allGroups, seq, + position); + + // if there's a FeatureColourFinder we might override the residue colour + // here with feature colouring + if (finder != null) + { + col = finder.findFeatureColour(col, seq, position); + } + return col; + } + + /** + * Get the residue colour without accounting for any features + * + * @param showBoxes + * true if the viewport's Show Boxes setting is true + * @param shader + * the viewport's colour scheme + * @param allGroups + * all the groups which seq participates in + * @param seq + * the sequence containing the residue + * @param i + * the position of the residue in the sequence + * @return + */ + protected Color getResidueBoxColour(boolean showBoxes, + ResidueShaderI shader, + SequenceGroup[] allGroups, + SequenceI seq, int i) + { + SequenceGroup currentSequenceGroup = getCurrentSequenceGroup(allGroups, + i); + if (currentSequenceGroup != null) + { + if (currentSequenceGroup.getDisplayBoxes()) + { + return getBoxColour(currentSequenceGroup.getGroupColourScheme(), + seq, i); + } + } + else if (showBoxes) + { + return getBoxColour(shader, seq, i); + } + + return Color.white; + } + + /** + * Search all the groups for a sequence to find the one which a given res + * falls into + * + * @param allGroups + * all the groups a sequence participates in + * @param res + * the residue to search for + * @return a sequence group for res, or null if no sequence group applies + */ + public SequenceGroup getCurrentSequenceGroup(SequenceGroup[] allGroups, + int res) + { + if (allGroups == null) + { + return null; + } + + for (int i = 0; i < allGroups.length; i++) + { + if ((allGroups[i].getStartRes() <= res) + && (allGroups[i].getEndRes() >= res)) + { + return (allGroups[i]); + } + } + + return null; + } + + /** + * DOCUMENT ME! + * + * @param shader + * the viewport's colour scheme + * @param seq + * the sequence containing the residue + * @param i + * the position of the residue in the sequence + */ + public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i) + { + Color resBoxColour = Color.white; + if (shader.getColourScheme() != null) + { + resBoxColour = shader.findColour(seq.getCharAt(i), i, seq); + } + return resBoxColour; + } + +} diff --git a/src/jalview/schemes/ColourSchemeI.java b/src/jalview/schemes/ColourSchemeI.java index f16ca21..d70b4e2 100755 --- a/src/jalview/schemes/ColourSchemeI.java +++ b/src/jalview/schemes/ColourSchemeI.java @@ -98,4 +98,11 @@ public interface ColourSchemeI * @return */ boolean isSimple(); + + /** + * Answers true if the colour scheme has a colour specified for gaps. + * + * @return + */ + boolean hasGapColour(); } diff --git a/src/jalview/schemes/ResidueColourScheme.java b/src/jalview/schemes/ResidueColourScheme.java index 34a5daa..2f7a5e0 100755 --- a/src/jalview/schemes/ResidueColourScheme.java +++ b/src/jalview/schemes/ResidueColourScheme.java @@ -208,4 +208,14 @@ public abstract class ResidueColourScheme implements ColourSchemeI { return true; } + + /** + * Default method returns false. Override this to return true in colour + * schemes that have a colour associated with gap residues. + */ + @Override + public boolean hasGapColour() + { + return false; + } } diff --git a/src/jalview/schemes/UserColourScheme.java b/src/jalview/schemes/UserColourScheme.java index b86250a..bf62e45 100755 --- a/src/jalview/schemes/UserColourScheme.java +++ b/src/jalview/schemes/UserColourScheme.java @@ -287,7 +287,7 @@ public class UserColourScheme extends ResidueColourScheme /* * step 1: build a map from colours to the symbol(s) that have the colour */ - Map> colours = new HashMap>(); + Map> colours = new HashMap<>(); for (char symbol = 'A'; symbol <= 'Z'; symbol++) { @@ -320,7 +320,7 @@ public class UserColourScheme extends ResidueColourScheme /* * step 2: make a list of { A,G,R=12f9d6 } residues/colour specs */ - List residueColours = new ArrayList(); + List residueColours = new ArrayList<>(); for (Entry> cols : colours.entrySet()) { boolean first = true; @@ -350,4 +350,10 @@ public class UserColourScheme extends ResidueColourScheme Collections.sort(residueColours); return StringUtils.listToDelimitedString(residueColours, ";"); } + + @Override + public boolean hasGapColour() + { + return (findColour(' ') != null); + } } diff --git a/src/jalview/ws/dbsources/EmblXmlSource.java b/src/jalview/ws/dbsources/EmblXmlSource.java index 21b226b..ca90d60 100644 --- a/src/jalview/ws/dbsources/EmblXmlSource.java +++ b/src/jalview/ws/dbsources/EmblXmlSource.java @@ -91,7 +91,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy File reply) throws Exception { EmblFile efile = null; - List seqs = new ArrayList(); + List seqs = new ArrayList<>(); if (reply != null && reply.exists()) { @@ -107,7 +107,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy * EmbFile reads something like (e.g.) this ungrammatical phrase * Entry: display type is either not supported or entry is not found. */ - List peptides = new ArrayList(); + List peptides = new ArrayList<>(); if (efile != null && efile.getEntries() != null) { for (EmblEntry entry : efile.getEntries()) diff --git a/src/jalview/ws/seqfetcher/ASequenceFetcher.java b/src/jalview/ws/seqfetcher/ASequenceFetcher.java index 977f9da..9284f82 100644 --- a/src/jalview/ws/seqfetcher/ASequenceFetcher.java +++ b/src/jalview/ws/seqfetcher/ASequenceFetcher.java @@ -126,15 +126,16 @@ public class ASequenceFetcher */ public SequenceI[] getSequences(List refs, boolean dna) { - Vector rseqs = new Vector(); - Hashtable> queries = new Hashtable>(); + Vector rseqs = new Vector<>(); + Hashtable> queries = new Hashtable<>(); for (DBRefEntry ref : refs) { - if (!queries.containsKey(ref.getSource())) + String canonical = DBRefUtils.getCanonicalName(ref.getSource()); + if (!queries.containsKey(canonical)) { - queries.put(ref.getSource(), new ArrayList()); + queries.put(canonical, new ArrayList()); } - List qset = queries.get(ref.getSource()); + List qset = queries.get(canonical); if (!qset.contains(ref.getAccessionId())) { qset.add(ref.getAccessionId()); @@ -154,14 +155,14 @@ public class ASequenceFetcher continue; } - Stack queriesLeft = new Stack(); + Stack queriesLeft = new Stack<>(); queriesLeft.addAll(query); List proxies = getSourceProxy(db); for (DbSourceProxy fetcher : proxies) { - List queriesMade = new ArrayList(); - HashSet queriesFound = new HashSet(); + List queriesMade = new ArrayList<>(); + HashSet queriesFound = new HashSet<>(); try { if (fetcher.isDnaCoding() != dna) @@ -306,13 +307,13 @@ public class ASequenceFetcher Map dblist = fetchableDbs.get(db); if (dblist == null) { - return new ArrayList(); + return new ArrayList<>(); } /* * sort so that primary sources precede secondary */ - List dbs = new ArrayList(dblist.values()); + List dbs = new ArrayList<>(dblist.values()); Collections.sort(dbs, proxyComparator); return dbs; } @@ -357,14 +358,14 @@ public class ASequenceFetcher { if (fetchableDbs == null) { - fetchableDbs = new Hashtable>(); + fetchableDbs = new Hashtable<>(); } Map slist = fetchableDbs .get(proxy.getDbSource()); if (slist == null) { fetchableDbs.put(proxy.getDbSource(), - slist = new Hashtable()); + slist = new Hashtable<>()); } slist.put(proxy.getDbName(), proxy); } @@ -391,7 +392,7 @@ public class ASequenceFetcher return null; } String[] sources = null; - Vector src = new Vector(); + Vector src = new Vector<>(); Enumeration dbs = fetchableDbs.keys(); while (dbs.hasMoreElements()) { @@ -413,7 +414,7 @@ public class ASequenceFetcher public DbSourceProxy[] getDbSourceProxyInstances(Class class1) { - List prlist = new ArrayList(); + List prlist = new ArrayList<>(); for (String fetchable : getSupportedDb()) { for (DbSourceProxy pr : getSourceProxy(fetchable)) diff --git a/test/jalview/gui/SequenceRendererTest.java b/test/jalview/gui/SequenceRendererTest.java deleted file mode 100644 index c80b830..0000000 --- a/test/jalview/gui/SequenceRendererTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) - * Copyright (C) $$Year-Rel$$ The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.gui; - -import static org.testng.AssertJUnit.assertEquals; - -import jalview.datamodel.Alignment; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.Sequence; -import jalview.datamodel.SequenceI; -import jalview.schemes.ZappoColourScheme; - -import java.awt.Color; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -public class SequenceRendererTest -{ - - @BeforeClass(alwaysRun = true) - public void setUpJvOptionPane() - { - JvOptionPane.setInteractiveMode(false); - JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); - } - - @Test(groups = { "Functional" }) - public void testGetResidueBoxColour_zappo() - { - SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE... - AlignmentI al = new Alignment(new SequenceI[] { seq }); - final AlignViewport av = new AlignViewport(al); - SequenceRenderer sr = new SequenceRenderer(av); - av.setGlobalColourScheme(new ZappoColourScheme()); - - // @see ResidueProperties.zappo - assertEquals(Color.pink, sr.getResidueBoxColour(seq, 0)); // M - assertEquals(Color.green, sr.getResidueBoxColour(seq, 2)); // T - assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G - assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F - } - - @Test(groups = { "Functional" }) - public void testGetResidueBoxColour_none() - { - SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF"); - AlignmentI al = new Alignment(new SequenceI[] { seq }); - final AlignViewport av = new AlignViewport(al); - SequenceRenderer sr = new SequenceRenderer(av); - - assertEquals(Color.white, sr.getResidueBoxColour(seq, 0)); - assertEquals(Color.white, sr.getResidueBoxColour(seq, 2)); - - // set for overview - sr.forOverview = true; - assertEquals(Color.lightGray, sr.getResidueBoxColour(seq, 0)); - assertEquals(Color.white, sr.getResidueBoxColour(seq, 2)); - } - - // TODO more tests for getResidueBoxColour covering groups, feature rendering, - // gaps, overview... - -} diff --git a/test/jalview/io/CrossRef2xmlTests.java b/test/jalview/io/CrossRef2xmlTests.java index ec5855f..0715857 100644 --- a/test/jalview/io/CrossRef2xmlTests.java +++ b/test/jalview/io/CrossRef2xmlTests.java @@ -31,10 +31,12 @@ import jalview.gui.CrossRefAction; import jalview.gui.Desktop; import jalview.gui.Jalview2XML; import jalview.gui.JvOptionPane; +import jalview.util.DBRefUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -65,10 +67,13 @@ public class CrossRef2xmlTests extends Jalview2xmlBase public void testRetrieveAndShowCrossref() throws Exception { - List failedDBRetr = new ArrayList(); - List failedXrefMenuItems = new ArrayList(); - List failedProjectRecoveries = new ArrayList(); - + List failedDBRetr = new ArrayList<>(); + List failedXrefMenuItems = new ArrayList<>(); + List failedProjectRecoveries = new ArrayList<>(); + // only search for ensembl or Uniprot crossrefs + List limit=Arrays.asList(new String[] { + DBRefUtils.getCanonicalName("ENSEMBL"), + DBRefUtils.getCanonicalName("Uniprot")}); // for every set of db queries // retrieve db query // verify presence of expected xrefs @@ -85,12 +90,11 @@ public class CrossRef2xmlTests extends Jalview2xmlBase // . codonframes // // - HashMap dbtoviewBit = new HashMap(); - List keyseq = new ArrayList(); - HashMap savedProjects = new HashMap(); + HashMap dbtoviewBit = new HashMap<>(); + List keyseq = new ArrayList<>(); + HashMap savedProjects = new HashMap<>(); - for (String[] did : new String[][] { { "ENSEMBL", "ENSG00000157764" }, - { "UNIPROT", "P01731" } }) + for (String[] did : new String[][] { { "UNIPROT", "P00338" } }) { // pass counters - 0 - first pass, 1 means retrieve project rather than // perform action @@ -163,7 +167,8 @@ public class CrossRef2xmlTests extends Jalview2xmlBase ptypes = (seqs == null || seqs.length == 0) ? null : new CrossRef( seqs, dataset).findXrefSourcesForSequences(dna); - + filterDbRefs(ptypes, limit); + // start of pass2: retrieve each cross-ref for fetched or restored // project. do // first cross ref and recover crossref loop @@ -176,7 +181,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase // build next key so we an retrieve all views String nextxref = first + " -> " + db + "{" + firstcr_ap + "}"; // perform crossref action, or retrieve stored project - List cra_views = new ArrayList(); + List cra_views = new ArrayList<>(); CrossRefAction cra = null; if (pass2 == 0) @@ -237,7 +242,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase } } - HashMap> xrptypes = new HashMap>(); + HashMap> xrptypes = new HashMap<>(); // first save/verify views. for (AlignmentViewPanel avp : cra_views) { @@ -274,7 +279,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase nextxref = first + " -> " + db + "{" + firstcr_ap++ + "}"; for (String xrefdb : xrptypes.get(nextxref)) { - List cra_views2 = new ArrayList(); + List cra_views2 = new ArrayList<>(); int q = 0; String nextnextxref = nextxref + " -> " + xrefdb + "{" + q + "}"; @@ -437,6 +442,25 @@ public class CrossRef2xmlTests extends Jalview2xmlBase } } + private void filterDbRefs(List ptypes, List limit) + { + if (limit != null) + { + int p = 0; + while (ptypes.size() > p) + { + if (!limit.contains(ptypes.get(p))) + { + ptypes.remove(p); + } + else + { + p++; + } + } + } + } + /** * wrapper to trap known defect for AH002001 testcase * @@ -480,7 +504,7 @@ public class CrossRef2xmlTests extends Jalview2xmlBase private void assertType(boolean expectProtein, AlignmentViewPanel alignmentViewPanel, String message) { - List nonType = new ArrayList(); + List nonType = new ArrayList<>(); for (SequenceI sq : alignmentViewPanel.getAlignViewport() .getAlignment().getSequences()) { diff --git a/test/jalview/renderer/OverviewResColourFinderTest.java b/test/jalview/renderer/OverviewResColourFinderTest.java new file mode 100644 index 0000000..2016087 --- /dev/null +++ b/test/jalview/renderer/OverviewResColourFinderTest.java @@ -0,0 +1,296 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.renderer; + +import static org.testng.AssertJUnit.assertEquals; + +import jalview.bin.Cache; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; +import jalview.gui.JvOptionPane; +import jalview.gui.Preferences; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.UserColourScheme; +import jalview.schemes.ZappoColourScheme; + +import java.awt.Color; +import java.util.ArrayList; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class OverviewResColourFinderTest +{ + + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + Cache.loadProperties("test/jalview/io/testProps.jvprops"); + } + + @Test(groups = { "Functional" }) + public void testGetResidueBoxColour_none() + { + SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF"); + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new OverviewResColourFinder(); + + // gaps are grey, residues white + assertEquals(Color.white, rcf.getResidueColour(true, + av.getResidueShading(), + null, seq, 0, null)); + assertEquals(Color.lightGray, rcf + .getResidueColour(true, av.getResidueShading(), null, seq, 2, + null)); + + // unaffected by showBoxes setting + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 0, null)); + assertEquals(Color.lightGray, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 2, null)); + } + + @Test(groups = { "Functional" }) + public void testGetResidueBoxColour_zappo() + { + SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a + // gap + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new OverviewResColourFinder(); + av.setGlobalColourScheme(new ZappoColourScheme()); + + // @see ResidueProperties.zappo + assertEquals(Color.pink, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 0, null)); // M + assertEquals(Color.green, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 2, null)); // T + assertEquals(Color.magenta, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 5, null)); // G + assertEquals(Color.orange, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 12, null)); // F + + // gap colour not specified so gaps are lightGray + assertEquals(Color.lightGray, rcf + .getResidueColour(true, av.getResidueShading(), null, seq, 3, + null)); + + // unaffected by showBoxes setting + assertEquals(Color.pink, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 0, null)); // M + assertEquals(Color.green, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 2, null)); // T + assertEquals(Color.magenta, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 5, null)); // G + assertEquals(Color.orange, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 12, null)); // F + + // gap colour not specified so gaps are lightGray + assertEquals(Color.lightGray, rcf + .getResidueColour(false, av.getResidueShading(), null, seq, 3, + null)); + + } + + @Test(groups = { "Functional" }) + public void testGetResidueBoxColour_userdef() + { + SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a + // gap + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new OverviewResColourFinder(); + + Color[] newColours = new Color[24]; + for (int i = 0; i < 24; i++) + { + newColours[i] = null; + } + + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour not specified so gaps are lightGray + assertEquals(Color.lightGray, rcf + .getResidueColour(true, av.getResidueShading(), null, seq, 3, + null)); + + newColours[23] = Color.pink; + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour specified as pink + assertEquals(Color.pink, rcf.getResidueColour(true, + av.getResidueShading(), + null, seq, 3, null)); + + // unaffected by showBoxes setting + // gap colour not specified so gaps are lightGray + newColours[23] = null; + assertEquals(Color.lightGray, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 3, null)); + + newColours[23] = Color.pink; + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour specified as pink + assertEquals(Color.pink, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 3, null)); + } + + @Test + public void testGetResidueBoxColour_group() + { + SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF"); + AlignmentI al = new Alignment(new SequenceI[] { seq }); + + ColourSchemeI cs = new ZappoColourScheme(); + ArrayList seqlist = new ArrayList<>(); + seqlist.add(seq); + SequenceGroup sg = new SequenceGroup(seqlist, "testgroup", cs, true, + true, true, 5, 9); + al.addGroup(sg); + SequenceGroup[] groups = new SequenceGroup[1]; + groups[0] = sg; + + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new OverviewResColourFinder(); + + // G in group specified as magenta in Zappo + assertEquals(Color.magenta, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 7, null)); + + // Residue outside group coloured white + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 0, null)); + + // Gap outside group coloured lightgray + assertEquals(Color.lightGray, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 2, null)); + + // use legacy colouring + rcf = new OverviewResColourFinder(true, Color.blue, Color.red); + + // G in group specified as magenta in Zappo + assertEquals(Color.magenta, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 7, null)); + + // Residue outside group coloured lightgray + assertEquals(Color.lightGray, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 0, null)); + + // Gap outside group coloured white + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 2, null)); + + // use new colouring + rcf = new OverviewResColourFinder(false, Color.blue, Color.red); + + // G in group specified as magenta in Zappo + assertEquals(Color.magenta, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 7, null)); + + // Residue outside group coloured white + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 0, null)); + + // Gap outside group coloured blue + assertEquals(Color.blue, rcf.getResidueColour(false, + av.getResidueShading(), groups, seq, 2, null)); + } + + @Test + public void testGetBoxColour() + { + SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a + // gap + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + + // non-legacy colouring + ResidueColourFinder rcf = new OverviewResColourFinder(); + ResidueShaderI shader = new ResidueShader(); + + // residues white + Color c = rcf.getBoxColour(shader, seq, 0); + assertEquals(Color.white, c); + + // gaps gap colour + c = rcf.getBoxColour(shader, seq, 3); + assertEquals(Preferences.OVERVIEW_DEFAULT_GAP, c); + + // legacy colouring set explicitly via constructor + rcf = new OverviewResColourFinder(true, Color.blue, Color.red); + shader = new ResidueShader(); + + // residues light gray + c = rcf.getBoxColour(shader, seq, 0); + assertEquals(Color.lightGray, c); + + // gaps white + c = rcf.getBoxColour(shader, seq, 3); + assertEquals(Color.white, c); + + // legacy colouring off + rcf = new OverviewResColourFinder(); + shader = new ResidueShader(); + + // residues white + c = rcf.getBoxColour(shader, seq, 0); + assertEquals(Color.white, c); + + // gaps gap colour + c = rcf.getBoxColour(shader, seq, 3); + assertEquals(Preferences.OVERVIEW_DEFAULT_GAP, c); + + // non legacy colouring with colour scheme + rcf = new OverviewResColourFinder(false, Color.blue, Color.red); + shader = new ResidueShader(new ZappoColourScheme()); + + // M residue pink + c = rcf.getBoxColour(shader, seq, 0); + assertEquals(Color.pink, c); + + // gaps blue + c = rcf.getBoxColour(shader, seq, 3); + assertEquals(Color.blue, c); + + // legacy colouring with colour scheme + rcf = new OverviewResColourFinder(true, Color.blue, Color.red); + + // M residue pink + c = rcf.getBoxColour(shader, seq, 0); + assertEquals(Color.pink, c); + + // gaps white + c = rcf.getBoxColour(shader, seq, 3); + assertEquals(Color.white, c); + } +} diff --git a/test/jalview/renderer/ResidueColourFinderTest.java b/test/jalview/renderer/ResidueColourFinderTest.java new file mode 100644 index 0000000..81fb2c0 --- /dev/null +++ b/test/jalview/renderer/ResidueColourFinderTest.java @@ -0,0 +1,151 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.renderer; + +import static org.testng.AssertJUnit.assertEquals; + +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignViewport; +import jalview.gui.JvOptionPane; +import jalview.schemes.UserColourScheme; +import jalview.schemes.ZappoColourScheme; + +import java.awt.Color; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ResidueColourFinderTest +{ + + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + @Test(groups = { "Functional" }) + public void testGetResidueColour_zappo() + { + SequenceI seq = new Sequence("name", "MATVLGSPRAPAFF"); // FER1_MAIZE... + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new ResidueColourFinder(); + av.setGlobalColourScheme(new ZappoColourScheme()); + + // @see ResidueProperties.zappo + assertEquals(Color.pink, + rcf.getResidueColour(true, av.getResidueShading(), null, seq, 0, + null)); // M + assertEquals(Color.green, + rcf.getResidueColour(true, av.getResidueShading(), null, seq, 2, + null)); // T + assertEquals(Color.magenta, + rcf.getResidueColour(true, av.getResidueShading(), null, seq, 5, + null)); // G + assertEquals(Color.orange, + rcf.getResidueColour(true, av.getResidueShading(), null, seq, + 12, + null)); // F + + // everything is white if showBoxes is false + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 0, null)); // M + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 2, null)); // T + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 5, null)); // G + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 12, null)); // F + } + + @Test(groups = { "Functional" }) + public void testGetResidueColour_none() + { + SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF"); + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new ResidueColourFinder(); + + assertEquals(Color.white, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 0, null)); + assertEquals(Color.white, + rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 2, null)); + + // no change if showBoxes is false + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 0, null)); + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 2, null)); + } + + @Test(groups = { "Functional" }) + public void testGetResidueColour_userdef() + { + SequenceI seq = new Sequence("name", "MAT--GSPRAPAFF"); // FER1_MAIZE... + a + // gap + AlignmentI al = new Alignment(new SequenceI[] { seq }); + final AlignViewport av = new AlignViewport(al); + ResidueColourFinder rcf = new ResidueColourFinder(); + + Color[] newColours = new Color[24]; + for (int i = 0; i < 24; i++) + { + newColours[i] = null; + } + + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour not specified so gap colour is null + // this is consistent with previous behaviour, but may not be correct? + assertEquals(null, rcf.getResidueColour(true, av.getResidueShading(), + null, seq, 3, null)); + + newColours[23] = Color.pink; + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour specified as pink + assertEquals(Color.pink, rcf.getResidueColour(true, + av.getResidueShading(), + null, seq, 3, null)); + + // everything is white if showBoxes is false + newColours[23] = null; + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), + null, seq, 3, null)); + + newColours[23] = Color.pink; + av.setGlobalColourScheme(new UserColourScheme(newColours)); + + // gap colour specified as pink + assertEquals(Color.white, rcf.getResidueColour(false, + av.getResidueShading(), null, seq, 3, null)); + } + + // TODO more tests for getResidueColour covering groups, feature rendering... +}