From 163ed5b997bbda48e4cdd950e87a8fe01baae7fb Mon Sep 17 00:00:00 2001 From: gmungoc Date: Mon, 24 Jul 2017 14:43:38 +0200 Subject: [PATCH] spike branch updated from latest features/JAL-2446 --- .../features/AnnotationColumnSelectionWithSM.png | Bin 0 -> 37455 bytes help/html/features/columnFilterByAnnotation.html | 6 +- help/html/releases.html | 4 +- resources/lang/Messages.properties | 2 - resources/lang/Messages_es.properties | 8 +- src/jalview/api/FeatureColourI.java | 15 +- src/jalview/appletgui/APopupMenu.java | 14 +- src/jalview/appletgui/AlignFrame.java | 39 +- src/jalview/appletgui/AlignViewport.java | 38 +- src/jalview/appletgui/AlignmentPanel.java | 75 +- src/jalview/appletgui/AnnotationColumnChooser.java | 14 +- src/jalview/appletgui/AnnotationLabels.java | 10 +- src/jalview/appletgui/AnnotationPanel.java | 13 +- src/jalview/appletgui/FeatureColourChooser.java | 10 +- src/jalview/appletgui/FeatureRenderer.java | 11 +- src/jalview/appletgui/FeatureSettings.java | 4 +- src/jalview/appletgui/IdCanvas.java | 221 ++-- src/jalview/appletgui/OverviewCanvas.java | 8 +- src/jalview/appletgui/OverviewPanel.java | 25 + src/jalview/appletgui/ScalePanel.java | 57 +- src/jalview/appletgui/SeqCanvas.java | 51 +- src/jalview/appletgui/SeqPanel.java | 22 +- src/jalview/appletgui/SliderPanel.java | 62 +- src/jalview/datamodel/Alignment.java | 36 - src/jalview/datamodel/AlignmentI.java | 9 - src/jalview/datamodel/BinarySequence.java | 3 +- src/jalview/datamodel/CigarArray.java | 2 +- src/jalview/datamodel/ColumnSelection.java | 4 +- src/jalview/datamodel/HiddenColumns.java | 1346 +++++++++++++------- src/jalview/datamodel/HiddenSequences.java | 6 +- src/jalview/datamodel/SequenceFeature.java | 22 +- src/jalview/datamodel/SequenceGroup.java | 2 +- src/jalview/datamodel/VisibleColsIterator.java | 2 +- src/jalview/gui/AlignFrame.java | 75 +- src/jalview/gui/AlignViewport.java | 39 - src/jalview/gui/AlignmentPanel.java | 441 +++---- src/jalview/gui/AnnotationColumnChooser.java | 14 +- src/jalview/gui/AnnotationLabels.java | 19 +- src/jalview/gui/AnnotationPanel.java | 19 +- src/jalview/gui/FeatureColourChooser.java | 3 +- src/jalview/gui/FeatureRenderer.java | 7 +- src/jalview/gui/IdCanvas.java | 241 ++-- src/jalview/gui/IdPanel.java | 4 +- src/jalview/gui/Jalview2XML.java | 109 +- src/jalview/gui/OverviewCanvas.java | 8 +- src/jalview/gui/OverviewPanel.java | 34 +- src/jalview/gui/PopupMenu.java | 15 +- src/jalview/gui/ScalePanel.java | 67 +- src/jalview/gui/SeqCanvas.java | 351 +++-- src/jalview/gui/SeqPanel.java | 35 +- src/jalview/gui/SliderPanel.java | 31 +- src/jalview/io/AnnotationFile.java | 29 +- src/jalview/io/JSONFile.java | 26 +- src/jalview/renderer/OverviewRenderer.java | 29 +- src/jalview/renderer/ResidueShader.java | 14 + .../renderer/seqfeatures/FeatureRenderer.java | 44 +- src/jalview/schemes/FeatureColour.java | 59 +- src/jalview/schemes/ResidueColourScheme.java | 3 +- src/jalview/schemes/ScoreColourScheme.java | 33 +- src/jalview/util/MappingUtils.java | 11 +- src/jalview/viewmodel/AlignmentViewport.java | 61 +- src/jalview/viewmodel/OverviewDimensions.java | 3 +- .../viewmodel/OverviewDimensionsHideHidden.java | 5 + .../viewmodel/OverviewDimensionsShowHidden.java | 8 +- src/jalview/viewmodel/ViewportRanges.java | 209 ++- .../seqfeatures/FeatureRendererModel.java | 16 +- test/jalview/datamodel/AlignmentTest.java | 30 - test/jalview/datamodel/ColumnSelectionTest.java | 10 +- test/jalview/datamodel/HiddenColumnsTest.java | 202 ++- test/jalview/datamodel/HiddenSequencesTest.java | 18 +- test/jalview/datamodel/SequenceFeatureTest.java | 33 +- test/jalview/datamodel/SequenceGroupTest.java | 11 + test/jalview/gui/AlignFrameTest.java | 8 +- test/jalview/gui/AlignViewportTest.java | 60 + test/jalview/io/JSONFileTest.java | 16 +- .../seqfeatures/FeatureColourFinderTest.java | 82 ++ .../renderer/seqfeatures/FeatureRendererTest.java | 16 +- test/jalview/schemes/BuriedColourSchemeTest.java | 33 + test/jalview/schemes/FeatureColourTest.java | 80 +- test/jalview/schemes/HelixColourSchemeTest.java | 33 + .../schemes/HydrophobicColourSchemeTest.java | 35 + test/jalview/schemes/StrandColourSchemeTest.java | 35 + test/jalview/schemes/TurnColourSchemeTest.java | 35 + test/jalview/schemes/UserColourSchemeTest.java | 29 + test/jalview/util/MappingUtilsTest.java | 20 +- test/jalview/viewmodel/ViewportRangesTest.java | 284 ++++- utils/InstallAnywhere/Jalview.iap_xml | 2 +- 87 files changed, 3424 insertions(+), 1851 deletions(-) create mode 100644 help/html/features/AnnotationColumnSelectionWithSM.png create mode 100644 test/jalview/schemes/BuriedColourSchemeTest.java create mode 100644 test/jalview/schemes/HelixColourSchemeTest.java create mode 100644 test/jalview/schemes/HydrophobicColourSchemeTest.java create mode 100644 test/jalview/schemes/StrandColourSchemeTest.java create mode 100644 test/jalview/schemes/TurnColourSchemeTest.java diff --git a/help/html/features/AnnotationColumnSelectionWithSM.png b/help/html/features/AnnotationColumnSelectionWithSM.png new file mode 100644 index 0000000000000000000000000000000000000000..c86c5b8609005aec0ab41a2688a2249b68398c34 GIT binary patch literal 37455 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 K7u+)N{XYN>k(;0Z literal 0 HcmV?d00001 diff --git a/help/html/features/columnFilterByAnnotation.html b/help/html/features/columnFilterByAnnotation.html index b260cee..2651a10 100644 --- a/help/html/features/columnFilterByAnnotation.html +++ b/help/html/features/columnFilterByAnnotation.html @@ -37,7 +37,7 @@

- +
@@ -76,7 +76,9 @@
  • Select whether to filter the alignment above or below the threshold.
  • Change the threshold value with the slider, or enter it - in the text box.
  • + in the text box. +
  • The As Percentage checkbox allows thresholds to + be set as a percentage rather than absolute value.
  • Actions
      diff --git a/help/html/releases.html b/help/html/releases.html index 97d6789..e39a4c1 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -127,7 +127,7 @@ li:before {
      • Fixed incorrect value in BLOSUM 62 score - matrix - C->R should be '3'
        Old matrix restored with + matrix - C->R should be '-3'
        Old matrix restored with this one-line groovy script:
        jalview.analysis.scoremodels.ScoreModels.instance.BLOSUM62.@matrix[4][1]=3
      • @@ -136,7 +136,7 @@ li:before { earlier versions of Jalview, gaps matching gaps were penalised, and gaps matching non-gaps penalised even more. In the PCA calculation, gaps were actually treated as - non-gaps - so different costs were applied, which mean't + non-gaps - so different costs were applied, which meant Jalview's PCAs were different to those produced by SeqSpace.
        Jalview now treats gaps in the same way as SeqSpace (ie it scores them as 0). To restore pre-2.10.2 diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index f1ac099..c9d2e63 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -745,7 +745,6 @@ label.delete_sbrs_definition = Delete SBRS Definition label.your_sequences_have_been_verified = Your sequences have been verified against known sequence databases.\n(Use Calculate | Show flanking regions to show enclosing sequence.)\nTo preserve data changes, save your alignment.\n\n label.sequences_updated = Sequences updated label.dbref_search_completed = DBRef search completed -label.show_all_chains = Show all chains label.fetch_all_param = Fetch all {0} label.paste_new_window = Paste To New Window label.settings_for_param = Settings for {0} @@ -1282,7 +1281,6 @@ label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ is no longer used for DB access label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Connections' tab of the Preferences window: label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'. label.do_not_display_again = Do not display this message again -exception.url_cannot_have_miriam_id = {0} is a MIRIAM id and cannot be used as a custom url name exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line label.filter = Filter text: action.customfilter = Custom only diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 7808480..64f06c8 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -682,7 +682,6 @@ label.delete_sbrs_definition = Borrar una definici label.your_sequences_have_been_verified = Sus secuencias has sido verificadas en una base de datos de secuencias conocidas.\n(Usar Calcular | Mostrar flancos para ver ampliación.)\nPara mantener los datos actualizados, guarde su alineamiento.\n\n label.sequences_updated = Secuencias actualizadas label.dbref_search_completed = Búsqueda de DBRef terminada -label.show_all_chains = Mostrar todas las cadenas label.fetch_all_param = Recuperar todas {0} label.paste_new_window = Pegar en una nueva ventana label.settings_for_param = Configuración para {0} @@ -1282,7 +1281,6 @@ label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ no se utiliza m label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pestaña 'Conexiones' de la ventana de Preferencias: label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'. label.do_not_display_again = No mostrar este mensaje de nuevo -exception.url_cannot_have_miriam_id = {0} es una id MIRIAM y no puede ser usada como nombre url personalizado exception.url_cannot_have_duplicate_id = {0} no puede ser usada como etiqueta en más de un enlace label.filter = Filtrar texto: action.customfilter = Sólo personalizado @@ -1300,11 +1298,15 @@ warn.name_cannot_be_duplicate = Los nombres URL definidos por el usuario deben s label.invalid_name = Nombre inválido ! label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas label.urllinks = Enlaces +label.default_cache_size = Tamaño del caché por defecto +action.clear_cached_items = Borrar elementos en caché label.quality_descr = Calidad de alineamiento basándose en puntuación Blosum62 label.conservation_descr = Conservación del alineamiento total menos de {0}% huecos label.consensus_descr = % Identidad label.complement_consensus_descr = % Identidad para cDNA label.strucconsensus_descr = % Identidad para pares de bases label.occupancy_descr = Número de posiciones alineadas -label.togglehidden = Show hidden regions +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 diff --git a/src/jalview/api/FeatureColourI.java b/src/jalview/api/FeatureColourI.java index 01eb7fa..0ded079 100644 --- a/src/jalview/api/FeatureColourI.java +++ b/src/jalview/api/FeatureColourI.java @@ -146,7 +146,9 @@ public interface FeatureColourI boolean hasThreshold(); /** - * Returns the computed colour for the given sequence feature + * Returns the computed colour for the given sequence feature. Answers null if + * the score of this feature instance is outside the range to render (if any), + * i.e. lies below or above a configured threshold. * * @param feature * @return @@ -154,17 +156,6 @@ public interface FeatureColourI Color getColor(SequenceFeature feature); /** - * Answers true if the feature has a simple colour, or is coloured by label, - * or has a graduated colour and the score of this feature instance is within - * the range to render (if any), i.e. does not lie below or above any - * threshold set. - * - * @param feature - * @return - */ - boolean isColored(SequenceFeature feature); - - /** * Update the min-max range for a graduated colour scheme * * @param min diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index d5b7132..80b2d73 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -836,12 +836,16 @@ public class APopupMenu extends java.awt.PopupMenu implements } } - if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs, - features, true, ap)) + if (!seqs.isEmpty()) { - ap.alignFrame.sequenceFeatures.setState(true); - ap.av.setShowSequenceFeatures(true); - ap.highlightSearchResults(null); + if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs, + features, true, ap)) + { + ap.alignFrame.sequenceFeatures.setState(true); + ap.av.setShowSequenceFeatures(true); + ap.av.setSearchResults(null); // clear highlighting + ap.repaint(); // draw new/amended features + } } } else diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 24f882e..b30c5ae 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -599,25 +599,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } case KeyEvent.VK_PAGE_UP: - if (viewport.getWrapAlignment()) - { - ranges.scrollUp(true); - } - else - { - ranges.pageUp(); - } + ranges.pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - if (viewport.getWrapAlignment()) - { - ranges.scrollUp(false); - } - else - { - ranges.pageDown(); - } + ranges.pageDown(); break; case KeyEvent.VK_Z: @@ -1811,7 +1797,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, synchronized void slideSequences(boolean right, int size) { - List sg = new Vector(); + List sg = new Vector<>(); if (viewport.cursorMode) { sg.add(viewport.getAlignment().getSequenceAt( @@ -1913,7 +1899,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, static StringBuffer copiedSequences; - static Vector copiedHiddenColumns; + static Vector copiedHiddenColumns; protected void copy_actionPerformed() { @@ -1924,7 +1910,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, SequenceGroup sg = viewport.getSelectionGroup(); copiedSequences = new StringBuffer(); - Map orderedSeqs = new HashMap(); + Map orderedSeqs = new HashMap<>(); for (int i = 0; i < sg.getSize(); i++) { SequenceI seq = sg.getSequenceAt(i); @@ -1937,13 +1923,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, if (viewport.hasHiddenColumns() && viewport.getSelectionGroup() != null) { - copiedHiddenColumns = new Vector(); + copiedHiddenColumns = new Vector<>(viewport.getAlignment() + .getHiddenColumns().getHiddenColumnsCopy()); int hiddenOffset = viewport.getSelectionGroup().getStartRes(); - for (int[] region : viewport.getAlignment().getHiddenColumns() - .getHiddenRegions()) + for (int[] region : copiedHiddenColumns) { - copiedHiddenColumns.addElement(new int[] { - region[0] - hiddenOffset, region[1] - hiddenOffset }); + region[0] = region[0] - hiddenOffset; + region[1] = region[1] - hiddenOffset; } } else @@ -2058,7 +2044,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { for (int i = 0; i < copiedHiddenColumns.size(); i++) { - int[] region = (int[]) copiedHiddenColumns.elementAt(i); + int[] region = copiedHiddenColumns.elementAt(i); af.viewport.hideColumns(region[0], region[1]); } } @@ -2644,7 +2630,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } Frame frame = new Frame(); - OverviewPanel overview = new OverviewPanel(alignPanel); + final OverviewPanel overview = new OverviewPanel(alignPanel); frame.add(overview); // +50 must allow for applet frame window jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage( @@ -2659,6 +2645,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, @Override public void windowClosing(WindowEvent e) { + overview.dispose(); if (ap != null) { ap.setOverviewPanel(null); diff --git a/src/jalview/appletgui/AlignViewport.java b/src/jalview/appletgui/AlignViewport.java index 73cd9e9..1d2c4bc 100644 --- a/src/jalview/appletgui/AlignViewport.java +++ b/src/jalview/appletgui/AlignViewport.java @@ -26,6 +26,7 @@ import jalview.api.FeatureSettingsModelI; import jalview.bin.JalviewLite; import jalview.commands.CommandI; import jalview.datamodel.AlignmentI; +import jalview.datamodel.Annotation; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; import jalview.datamodel.SearchResults; @@ -229,43 +230,6 @@ public class AlignViewport extends AlignmentViewport implements } - /** - * get the consensus sequence as displayed under the PID consensus annotation - * row. - * - * @return consensus sequence as a new sequence object - */ - public SequenceI getConsensusSeq() - { - if (consensus == null) - { - updateConsensus(null); - } - if (consensus == null) - { - return null; - } - StringBuilder seqs = new StringBuilder(consensus.annotations.length); - for (int i = 0; i < consensus.annotations.length; i++) - { - if (consensus.annotations[i] != null) - { - if (consensus.annotations[i].description.charAt(0) == '[') - { - seqs.append(consensus.annotations[i].description.charAt(1)); - } - else - { - seqs.append(consensus.annotations[i].displayCharacter); - } - } - } - SequenceI sq = new Sequence("Consensus", seqs.toString()); - sq.setDescription("Percentage Identity Consensus " - + ((ignoreGapsInConsensusCalculation) ? " without gaps" : "")); - return sq; - } - java.awt.Frame nullFrame; protected FeatureSettings featureSettings = null; diff --git a/src/jalview/appletgui/AlignmentPanel.java b/src/jalview/appletgui/AlignmentPanel.java index e402b9b..4147177 100644 --- a/src/jalview/appletgui/AlignmentPanel.java +++ b/src/jalview/appletgui/AlignmentPanel.java @@ -802,42 +802,45 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, sendViewPosition(); } - private void adjustVertical(int offy) + private void adjustVertical(int newY) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); - if (av.getWrapAlignment()) { - int rowSize = seqPanel.seqCanvas - .getWrappedCanvasWidth(seqPanel.seqCanvas.getWidth()); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((offy * rowSize == oldX) && (oldwidth == rowSize)) + /* + * if we're scrolling to the position we're already at, stop + * this prevents infinite recursion of events when the scroll/viewport + * ranges values are the same + */ + int oldX = vpRanges.getStartRes(); + int oldY = vpRanges.getWrappedScrollPosition(oldX); + if (oldY == newY) { return; } - else if (offy > -1) + if (newY > -1) { - vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize); + /* + * limit page up/down to one width's worth of positions + */ + int rowSize = vpRanges.getViewportWidth(); + int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; + vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } else { int height = seqPanel.seqCanvas.getHeight() / av.getCharHeight(); + int oldY = vpRanges.getStartSeq(); + int oldheight = vpRanges.getViewportHeight(); // if we're scrolling to the position we're already at, stop // this prevents infinite recursion of events when the scroll/viewport // ranges values are the same - if ((offy == oldY) && (height == oldheight)) + if ((newY == oldY) && (height == oldheight)) { return; } - vpRanges.setViewportStartAndHeight(offy, height); + vpRanges.setViewportStartAndHeight(newY, height); } if (av.getWrapAlignment() || !fastPaint) { @@ -982,35 +985,23 @@ public class AlignmentPanel extends Panel implements AdjustmentListener, } - /* + /** * Set vertical scroll bar parameters for wrapped panel - * @param res - * the residue to scroll to + * + * @param topLeftColumn + * the column position at top left (0..) */ - private void setScrollingForWrappedPanel(int res) + private void setScrollingForWrappedPanel(int topLeftColumn) { - // get the width of the alignment in residues - int maxwidth = av.getAlignment().getWidth(); - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); - // get the width of the canvas in residues - int canvasWidth = seqPanel.seqCanvas - .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width); - if (canvasWidth > 0) - { - // position we want to scroll to is number of canvasWidth's to get there - int current = res / canvasWidth; - - // max scroll position: add one because extent is 1 and scrollbar value - // can only be set to at most max - extent - int max = maxwidth / canvasWidth + 1; - vscroll.setUnitIncrement(1); - vscroll.setValues(current, 1, 0, max); - } + /* + * a scrollbar's value can be set to at most (maximum-extent) + * so we add extent (1) to the maxScroll value + */ + vscroll.setUnitIncrement(1); + vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1); } protected Panel sequenceHolderPanel = new Panel(); diff --git a/src/jalview/appletgui/AnnotationColumnChooser.java b/src/jalview/appletgui/AnnotationColumnChooser.java index 22978c3..3a7188e 100644 --- a/src/jalview/appletgui/AnnotationColumnChooser.java +++ b/src/jalview/appletgui/AnnotationColumnChooser.java @@ -46,7 +46,7 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.TextEvent; import java.awt.event.TextListener; -import java.util.Iterator; +import java.util.ArrayList; import java.util.Vector; //import javax.swing.JPanel; @@ -142,7 +142,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements } setOldHiddenColumns(av.getAlignment().getHiddenColumns()); adjusting = true; - Vector list = new Vector(); + Vector list = new Vector<>(); int index = 1; for (int i = 0; i < anns.length; i++) { @@ -298,16 +298,16 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements HiddenColumns oldHidden = av .getAnnotationColumnSelectionState() .getOldHiddenColumns(); - if (oldHidden != null && oldHidden.getHiddenRegions() != null - && !oldHidden.getHiddenRegions().isEmpty()) + if (oldHidden != null) { - for (Iterator itr = oldHidden.getHiddenRegions() - .iterator(); itr.hasNext();) + ArrayList regions = oldHidden.getHiddenColumnsCopy(); + for (int[] positions : regions) { - int positions[] = itr.next(); av.hideColumns(positions[0], positions[1]); } } + // TODO not clear why we need to hide all the columns (above) if we are + // going to copy the hidden columns over wholesale anyway av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); diff --git a/src/jalview/appletgui/AnnotationLabels.java b/src/jalview/appletgui/AnnotationLabels.java index 307301d..3f7e523 100755 --- a/src/jalview/appletgui/AnnotationLabels.java +++ b/src/jalview/appletgui/AnnotationLabels.java @@ -838,13 +838,9 @@ public class AnnotationLabels extends Panel implements ActionListener, + sq.getSequenceAsString() + "\n"); if (av.hasHiddenColumns()) { - jalview.appletgui.AlignFrame.copiedHiddenColumns = new Vector(); - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenRegions()) - { - jalview.appletgui.AlignFrame.copiedHiddenColumns - .addElement(new int[] { region[0], region[1] }); - } + jalview.appletgui.AlignFrame.copiedHiddenColumns = new Vector<>( + av.getAlignment().getHiddenColumns() + .getHiddenColumnsCopy()); } } diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index c658811..39b718d 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -30,6 +30,7 @@ import jalview.util.Comparison; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.ViewportListenerI; +import jalview.viewmodel.ViewportRanges; import java.awt.Color; import java.awt.Dimension; @@ -118,13 +119,15 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, // ap.annotationScroller.getVAdjustable().addAdjustmentListener( this ); renderer = new AnnotationRenderer(); + + av.getRanges().addPropertyChangeListener(this); } public AnnotationPanel(AlignViewport av) { this.av = av; renderer = new AnnotationRenderer(); - av.getRanges().addPropertyChangeListener(this); + } @Override @@ -758,8 +761,12 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, public void propertyChange(PropertyChangeEvent evt) { // Respond to viewport range changes (e.g. alignment panel was scrolled) - if (evt.getPropertyName().equals("startres") - || evt.getPropertyName().equals("endres")) + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (evt.getPropertyName().equals(ViewportRanges.STARTRES)) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/appletgui/FeatureColourChooser.java b/src/jalview/appletgui/FeatureColourChooser.java index 0e85017..4075e8b 100644 --- a/src/jalview/appletgui/FeatureColourChooser.java +++ b/src/jalview/appletgui/FeatureColourChooser.java @@ -95,9 +95,11 @@ public class FeatureColourChooser extends Panel implements ActionListener, float mm[] = fr.getMinMax().get(type)[0]; min = mm[0]; max = mm[1]; + threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); oldcs = fr.getFeatureColours().get(type); if (oldcs.isGraduatedColour()) { + threshline.value = oldcs.getThreshold(); cs = new FeatureColour((FeatureColour) oldcs, min, max); } else @@ -384,14 +386,6 @@ public class FeatureColourChooser extends Panel implements ActionListener, thresholdValue.setText(""); } - else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD - && threshline == null) - { - // todo visual indication of feature threshold - threshline = new jalview.datamodel.GraphLine((max - min) / 2f, - "Threshold", Color.black); - } - if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD) { adjusting = true; diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index 435e78d..8721ff4 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -413,7 +413,8 @@ public class FeatureRenderer extends if (!colourPanel.isGcol) { // update colour - otherwise its already done. - setColour(sf.type, new FeatureColour(colourPanel.getBackground())); + setColour(enteredType, + new FeatureColour(colourPanel.getBackground())); } int newBegin = sf.begin; int newEnd = sf.end; @@ -431,14 +432,14 @@ public class FeatureRenderer extends * (to ensure integrity of SequenceFeatures data store) */ sequences.get(0).deleteFeature(sf); - SequenceFeature newSf = new SequenceFeature(sf, newBegin, newEnd, - enteredGroup, sf.getScore()); + SequenceFeature newSf = new SequenceFeature(sf, enteredType, + newBegin, newEnd, enteredGroup, sf.getScore()); newSf.setDescription(enteredDesc); ffile.parseDescriptionHTML(newSf, false); // amend features dialog only updates one sequence at a time sequences.get(0).addSequenceFeature(newSf); - boolean typeOrGroupChanged = (!featureType.equals(sf.type) || !featureGroup - .equals(sf.featureGroup)); + boolean typeOrGroupChanged = (!featureType.equals(newSf.getType()) || !featureGroup + .equals(newSf.getFeatureGroup())); ffile.parseDescriptionHTML(sf, false); if (typeOrGroupChanged) diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index b0bb372..46498ad 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -386,11 +386,13 @@ public class FeatureSettings extends Panel implements ItemListener, Set visibleGroups = new HashSet(); for (String group : groups) { - if (group == null || fr.checkGroupVisibility(group, true)) + // if (group == null || fr.checkGroupVisibility(group, true)) + if (group == null || checkGroupState(group)) { visibleGroups.add(group); } } + foundGroups.addAll(groups); /* * get distinct feature types for visible groups diff --git a/src/jalview/appletgui/IdCanvas.java b/src/jalview/appletgui/IdCanvas.java index 74bbcf5..48c0c40 100755 --- a/src/jalview/appletgui/IdCanvas.java +++ b/src/jalview/appletgui/IdCanvas.java @@ -101,7 +101,7 @@ public class IdCanvas extends Panel implements ViewportListenerI public void fastPaint(int vertical) { - if (gg == null) + if (gg == null || av.getWrapAlignment()) { repaint(); return; @@ -199,130 +199,148 @@ public class IdCanvas extends Panel implements ViewportListenerI void drawIds(int starty, int endy) { - // hardwired italic IDs in applet currently - Font italic = new Font(av.getFont().getName(), Font.ITALIC, av - .getFont().getSize()); - // temp variable for speed avcharHeight = av.getCharHeight(); - gg.setFont(italic); - Color currentColor = Color.white; Color currentTextColor = Color.black; final boolean doHiddenCheck = av.isDisplayReferenceSeq() - || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows() - && av.getShowHiddenMarkers(); + || av.hasHiddenRows(); + boolean hiddenRows = av.hasHiddenRows() && av.getShowHiddenMarkers(); if (av.getWrapAlignment()) { - int maxwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getHeight(); + drawIdsWrapped(starty, doHiddenCheck, hiddenRows); + return; + } - if (av.hasHiddenColumns()) + // Now draw the id strings + SequenceI seq; + for (int i = starty; i <= endy; i++) + { + seq = av.getAlignment().getSequenceAt(i); + if (seq == null) { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + continue; + } + // hardwired italic IDs in applet currently + Font italic = new Font(av.getFont().getName(), Font.ITALIC, av + .getFont().getSize()); + gg.setFont(italic); + // boolean isrep=false; + if (doHiddenCheck) + { + // isrep = + setHiddenFont(seq); } - int annotationHeight = 0; - AnnotationLabels labels = null; - - if (av.isShowAnnotation()) + // Selected sequence colours + if ((searchResults != null) && searchResults.contains(seq)) { - AnnotationPanel ap = new AnnotationPanel(av); - annotationHeight = ap.adjustPanelHeight(); - labels = new AnnotationLabels(av); + currentColor = Color.black; + currentTextColor = Color.white; } - int hgap = avcharHeight; - if (av.getScaleAboveWrapped()) + else if ((av.getSelectionGroup() != null) + && av.getSelectionGroup().getSequences(null).contains(seq)) + { + currentColor = Color.lightGray; + currentTextColor = Color.black; + } + else { - hgap += avcharHeight; + currentColor = av.getSequenceColour(seq); + currentTextColor = Color.black; } - int cHeight = alheight * avcharHeight + hgap + annotationHeight; + gg.setColor(currentColor); + // TODO: isrep could be used to highlight the representative in a + // different way + gg.fillRect(0, (i - starty) * avcharHeight, getSize().width, + avcharHeight); + gg.setColor(currentTextColor); - int rowSize = av.getRanges().getEndRes() - - av.getRanges().getStartRes(); - // Draw the rest of the panels - for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getSize().height) - && (row < maxwidth); ypos += cHeight, row += rowSize) + gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0, + (((i - starty) * avcharHeight) + avcharHeight) + - (avcharHeight / 5)); + + if (hiddenRows) { - for (int i = starty; i < alheight; i++) - { + drawMarker(i, starty, 0); + } + } + } - SequenceI s = av.getAlignment().getSequenceAt(i); - gg.setFont(italic); - if (doHiddenCheck) - { - setHiddenFont(s); - } - drawIdString(gg, hiddenRows, s, i, 0, ypos); - } + /** + * Draws sequence ids in wrapped mode + * + * @param starty + * @param doHiddenCheck + * @param hiddenRows + */ + protected void drawIdsWrapped(int starty, final boolean doHiddenCheck, + boolean hiddenRows) + { + int maxwidth = av.getAlignment().getWidth(); + int alheight = av.getAlignment().getHeight(); - if (labels != null) - { - gg.translate(0, ypos + (alheight * avcharHeight)); - labels.drawComponent(gg, getSize().width); - gg.translate(0, -ypos - (alheight * avcharHeight)); - } + if (av.hasHiddenColumns()) + { + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth) - 1; + } - } + int annotationHeight = 0; + AnnotationLabels labels = null; + + if (av.isShowAnnotation()) + { + AnnotationPanel ap = new AnnotationPanel(av); + annotationHeight = ap.adjustPanelHeight(); + labels = new AnnotationLabels(av); } - else + int hgap = avcharHeight; + if (av.getScaleAboveWrapped()) { - // Now draw the id strings - SequenceI seq; - for (int i = starty; i <= endy; i++) - { + hgap += avcharHeight; + } - seq = av.getAlignment().getSequenceAt(i); - if (seq == null) - { - continue; - } - gg.setFont(italic); - // boolean isrep=false; - if (doHiddenCheck) - { - // isrep = - setHiddenFont(seq); - } + int cHeight = alheight * avcharHeight + hgap + annotationHeight; - // Selected sequence colours - if ((searchResults != null) && searchResults.contains(seq)) - { - currentColor = Color.black; - currentTextColor = Color.white; - } - else if ((av.getSelectionGroup() != null) - && av.getSelectionGroup().getSequences(null).contains(seq)) - { - currentColor = Color.lightGray; - currentTextColor = Color.black; - } - else - { - currentColor = av.getSequenceColour(seq); - currentTextColor = Color.black; - } + int rowSize = av.getRanges().getViewportWidth(); - gg.setColor(currentColor); - // TODO: isrep could be used to highlight the representative in a - // different way - gg.fillRect(0, (i - starty) * avcharHeight, getSize().width, - avcharHeight); - gg.setColor(currentTextColor); + // hardwired italic IDs in applet currently + Font italic = new Font(av.getFont().getName(), Font.ITALIC, av + .getFont().getSize()); + gg.setFont(italic); - gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0, - (((i - starty) * avcharHeight) + avcharHeight) - - (avcharHeight / 5)); + /* + * draw repeating sequence ids until out of sequence data or + * out of visible space, whichever comes first + */ + int ypos = hgap; + int row = av.getRanges().getStartRes(); + while ((ypos <= getHeight()) && (row < maxwidth)) + { + for (int i = starty; i < alheight; i++) + { - if (hiddenRows) + SequenceI s = av.getAlignment().getSequenceAt(i); + // gg.setFont(italic); + if (doHiddenCheck) { - drawMarker(i, starty, 0); + setHiddenFont(s); } + drawIdString(gg, hiddenRows, s, i, 0, ypos); } + + if (labels != null) + { + gg.translate(0, ypos + (alheight * avcharHeight)); + labels.drawComponent(gg, getSize().width); + gg.translate(0, -ypos - (alheight * avcharHeight)); + } + ypos += cHeight; + row += rowSize; } } @@ -398,12 +416,25 @@ public class IdCanvas extends Panel implements ViewportListenerI return false; } + /** + * Respond to viewport range changes (e.g. alignment panel was scrolled). Both + * scrolling and resizing change viewport ranges. Scrolling changes both start + * and end points, but resize only changes end values. Here we only want to + * fastpaint on a scroll, with resize using a normal paint, so scroll events + * are identified as changes to the horizontal or vertical start value. + *

        + * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll + * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is + * provided, but it generates a change of "startres" which does require an + * update here. + */ @Override public void propertyChange(PropertyChangeEvent evt) { - // Respond to viewport range changes (e.g. alignment panel was scrolled) - if (evt.getPropertyName().equals("startseq") - || evt.getPropertyName().equals("endseq")) + String propertyName = evt.getPropertyName(); + if (propertyName.equals(ViewportRanges.STARTSEQ) + || (av.getWrapAlignment() && propertyName + .equals(ViewportRanges.STARTRES))) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/appletgui/OverviewCanvas.java b/src/jalview/appletgui/OverviewCanvas.java index 23e82df..a0466d3 100644 --- a/src/jalview/appletgui/OverviewCanvas.java +++ b/src/jalview/appletgui/OverviewCanvas.java @@ -40,6 +40,8 @@ public class OverviewCanvas extends Component private OverviewDimensions od; + private OverviewRenderer or = null; + private Image miniMe; private Image offscreen; @@ -92,6 +94,10 @@ public class OverviewCanvas extends Component if (updaterunning) { restart = true; + if (or != null) + { + or.setRedraw(true); + } } else { @@ -113,7 +119,7 @@ public class OverviewCanvas extends Component setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); - OverviewRenderer or = new OverviewRenderer(sr, fr, od); + or = new OverviewRenderer(sr, fr, od); miniMe = nullFrame.createImage(od.getWidth(), od.getHeight()); offscreen = nullFrame.createImage(od.getWidth(), od.getHeight()); diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index b3c4a37..ccdfee1 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -160,6 +160,14 @@ public class OverviewPanel extends Panel implements Runnable, */ public void updateOverviewImage() { + if (oviewCanvas == null) + { + /* + * panel has been disposed + */ + return; + } + if ((getSize().width > 0) && (getSize().height > 0)) { od.setWidth(getSize().width); @@ -257,4 +265,21 @@ public class OverviewPanel extends Panel implements Runnable, oviewCanvas.resetOviewDims(od); updateOverviewImage(); } + + /** + * Removes this object as a property change listener, and nulls references + */ + protected void dispose() + { + try + { + av.getRanges().removePropertyChangeListener(this); + } finally + { + av = null; + oviewCanvas = null; + ap = null; + od = null; + } + } } diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index ec3e246..5e0a2fd 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -27,6 +27,7 @@ import jalview.renderer.ScaleRenderer; import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.MessageManager; import jalview.viewmodel.ViewportListenerI; +import jalview.viewmodel.ViewportRanges; import java.awt.Color; import java.awt.FontMetrics; @@ -139,7 +140,7 @@ public class ScalePanel extends Panel implements MouseMotionListener, sg.setStartRes(min); sg.setEndRes(max); } - ap.paintAlignment(true); + ap.paintAlignment(false); av.sendSelection(); } @@ -165,10 +166,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, av.showColumn(reveal[0]); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -185,10 +182,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, av.showAllHiddenColumns(); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -215,10 +208,6 @@ public class ScalePanel extends Panel implements MouseMotionListener, } ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -334,18 +323,8 @@ public class ScalePanel extends Panel implements MouseMotionListener, int res = (evt.getX() / av.getCharWidth()) + av.getRanges().getStartRes(); - res = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(res); - - reveal = null; - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenRegions()) - { - if (res + 1 == region[0] || res - 1 == region[1]) - { - reveal = region; - break; - } - } + reveal = av.getAlignment().getHiddenColumns() + .getRegionWithEdgeAtRes(res); repaint(); } @@ -359,9 +338,15 @@ public class ScalePanel extends Panel implements MouseMotionListener, @Override public void paint(Graphics g) { - drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(), - getSize().width, - getSize().height); + /* + * shouldn't get called in wrapped mode as the scale above is + * drawn instead by SeqCanvas.drawNorthScale + */ + if (!av.getWrapAlignment()) + { + drawScale(g, av.getRanges().getStartRes(), + av.getRanges().getEndRes(), getSize().width, getSize().height); + } } // scalewidth will normally be screenwidth, @@ -450,10 +435,11 @@ public class ScalePanel extends Panel implements MouseMotionListener, if (av.getShowHiddenMarkers()) { int widthx = 1 + endx - startx; - for (int i = 0; i < hidden.getHiddenRegions().size(); i++) + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) { - res = hidden.findHiddenRegionPosition(i) - startx; + res = pos - startx; if (res < 0 || res > widthx) { @@ -473,7 +459,16 @@ public class ScalePanel extends Panel implements MouseMotionListener, public void propertyChange(PropertyChangeEvent evt) { // Respond to viewport change events (e.g. alignment panel was scrolled) - repaint(); + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (evt.getPropertyName().equals(ViewportRanges.STARTRES)) + { + // scroll event, repaint panel + repaint(); + } } } diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index 538f1d2..fe7abbb 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -37,6 +37,7 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.Panel; import java.beans.PropertyChangeEvent; +import java.util.List; public class SeqCanvas extends Panel implements ViewportListenerI { @@ -419,6 +420,9 @@ public class SeqCanvas extends Panel implements ViewportListenerI FontMetrics fm = getFontMetrics(av.getFont()); + LABEL_EAST = 0; + LABEL_WEST = 0; + if (av.getScaleRightWrapped()) { LABEL_EAST = fm.stringWidth(getMask()); @@ -440,17 +444,17 @@ public class SeqCanvas extends Panel implements ViewportListenerI av.setWrappedWidth(cWidth); - av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1); + av.getRanges().setViewportStartAndWidth(startRes, cWidth); int endx; int ypos = hgap; - int maxwidth = av.getAlignment().getWidth() - 1; + int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + .findColumnPosition(maxwidth); } while ((ypos <= canvasHeight) && (startRes < maxwidth)) @@ -487,11 +491,10 @@ public class SeqCanvas extends Panel implements ViewportListenerI HiddenColumns hidden = av.getAlignment().getHiddenColumns(); g.setColor(Color.blue); int res; - for (int i = 0; i < hidden.getHiddenRegions() - .size(); i++) + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) { - res = hidden.findHiddenRegionPosition(i) - - startRes; + res = pos - startRes; if (res < 0 || res > endx - startRes) { @@ -570,7 +573,7 @@ public class SeqCanvas extends Panel implements ViewportListenerI if (av.hasHiddenColumns()) { HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - for (int[] region : hidden.getHiddenRegions()) + for (int[] region : hidden.getHiddenColumnsCopy()) { int hideStart = region[0]; int hideEnd = region[1]; @@ -882,32 +885,44 @@ public class SeqCanvas extends Panel implements ViewportListenerI @Override public void propertyChange(PropertyChangeEvent evt) { + String eventName = evt.getPropertyName(); + if (!av.getWrapAlignment()) { - if (evt.getPropertyName().equals("startres") - || evt.getPropertyName().equals("endres")) + int scrollX = 0; + if (eventName.equals(ViewportRanges.STARTRES)) { // Make sure we're not trying to draw a panel // larger than the visible window ViewportRanges vpRanges = av.getRanges(); - int scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes()) + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + int range = vpRanges.getEndRes() - vpRanges.getStartRes(); + if (scrollX > range) { - scrollX = vpRanges.getEndRes() - vpRanges.getStartRes(); + scrollX = range; } - else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes()) + else if (scrollX < -range) { - scrollX = vpRanges.getStartRes() - vpRanges.getEndRes(); + scrollX = -range; } + } + + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (eventName.equals(ViewportRanges.STARTRES)) + { + // scroll - startres and endres both change fastPaint(scrollX, 0); } - else if (evt.getPropertyName().equals("startseq") - || evt.getPropertyName().equals("endseq")) + else if (eventName.equals(ViewportRanges.STARTSEQ)) { + // scroll fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } } - } } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 9e31efe..42e4d8e 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -577,7 +577,8 @@ public class SeqPanel extends Panel implements MouseMotionListener, seqCanvas.highlightSearchResults(highlight); seqCanvas.getFeatureRenderer().amendFeatures( Collections.singletonList(sequence), features, false, ap); - seqCanvas.highlightSearchResults(null); + av.setSearchResults(null); // clear highlighting + seqCanvas.repaint(); // draw new/amended features } } } @@ -615,6 +616,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, int res = 0; int x = evt.getX(); + int startRes = av.getRanges().getStartRes(); if (av.getWrapAlignment()) { @@ -629,7 +631,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, int y = evt.getY(); y -= hgap; - x -= seqCanvas.LABEL_WEST; + x = Math.max(0, x - seqCanvas.LABEL_WEST); int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width); if (cwidth < 1) @@ -638,14 +640,15 @@ public class SeqPanel extends Panel implements MouseMotionListener, } wrappedBlock = y / cHeight; - wrappedBlock += av.getRanges().getStartRes() / cwidth; - - res = wrappedBlock * cwidth + x / av.getCharWidth(); - + wrappedBlock += startRes / cwidth; + int startOffset = startRes % cwidth; // in case start is scrolled right + // from 0 + res = wrappedBlock * cwidth + + Math.min(cwidth - 1, startOffset + x / av.getCharWidth()); } else { - res = (x / av.getCharWidth()) + av.getRanges().getStartRes(); + res = (x / av.getCharWidth()) + startRes; } if (av.hasHiddenColumns()) @@ -1175,7 +1178,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, // Find the next gap before the end // of the visible region boundary boolean blank = false; - for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--) + for (; fixedRight > lastres; fixedRight--) { blank = true; @@ -1871,8 +1874,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } if (copycolsel && av.hasHiddenColumns() - && (av.getColumnSelection() == null || av.getAlignment() - .getHiddenColumns().getHiddenRegions() == null)) + && (av.getColumnSelection() == null)) { System.err.println("Bad things"); } diff --git a/src/jalview/appletgui/SliderPanel.java b/src/jalview/appletgui/SliderPanel.java index 9154aa0..47a0669 100644 --- a/src/jalview/appletgui/SliderPanel.java +++ b/src/jalview/appletgui/SliderPanel.java @@ -20,6 +20,7 @@ */ package jalview.appletgui; +import jalview.analysis.Conservation; import jalview.datamodel.SequenceGroup; import jalview.renderer.ResidueShaderI; import jalview.util.MessageManager; @@ -44,7 +45,7 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.util.Iterator; +import java.util.List; public class SliderPanel extends Panel implements ActionListener, AdjustmentListener, MouseListener @@ -82,7 +83,8 @@ public class SliderPanel extends Panel implements ActionListener, conservationSlider.setTitle(MessageManager.formatMessage( "label.conservation_colour_increment", new String[] { source == null ? BACKGROUND : source })); - if (ap.av.getAlignment().getGroups() != null) + List groups = ap.av.getAlignment().getGroups(); + if (groups != null && !groups.isEmpty()) { sp.setAllGroupsCheckEnabled(true); } @@ -248,45 +250,53 @@ public class SliderPanel extends Panel implements ActionListener, { return; } - - ResidueShaderI toChange = cs; - Iterator allGroups = null; - - if (allGroupsCheck.getState()) + if (forConservation) { - allGroups = ap.av.getAlignment().getGroups().listIterator(); + cs.setConservationApplied(true); + cs.setConservationInc(i); + } + else + { + cs.setThreshold(i, ap.av.isIgnoreGapsConsensus()); } - while (toChange != null) + if (allGroupsCheck.getState()) { - if (forConservation) + for (SequenceGroup group : ap.av.getAlignment().getGroups()) { - toChange.setConservationInc(i); - } - else - { - toChange.setThreshold(i, ap.av.isIgnoreGapsConsensus()); - } - if (allGroups != null && allGroups.hasNext()) - { - while ((toChange = allGroups.next().cs) == null - && allGroups.hasNext()) + ResidueShaderI groupColourScheme = group.getGroupColourScheme(); + if (forConservation) { - ; + if (!groupColourScheme.conservationApplied()) + { + /* + * first time the colour scheme has had Conservation shading applied + * - compute conservation + */ + Conservation c = new Conservation("Group", + group.getSequences(null), group.getStartRes(), + group.getEndRes()); + c.calculate(); + c.verdict(false, ap.av.getConsPercGaps()); + group.cs.setConservation(c); + + } + groupColourScheme.setConservationApplied(true); + groupColourScheme.setConservationInc(i); + } + else + { + groupColourScheme.setThreshold(i, ap.av.isIgnoreGapsConsensus()); } - } - else - { - toChange = null; } } ap.seqPanel.seqCanvas.repaint(); - } public void setAllGroupsCheckEnabled(boolean b) { + allGroupsCheck.setState(ap.av.getColourAppliesToAllGroups()); allGroupsCheck.setEnabled(b); } diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 5553840..26e8db1 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -1909,42 +1909,6 @@ public class Alignment implements AlignmentI } @Override - public int[] getVisibleStartAndEndIndex(List hiddenCols) - { - int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 }; - int startPos = alignmentStartEnd[0]; - int endPos = alignmentStartEnd[1]; - - int[] lowestRange = new int[] { -1, -1 }; - int[] higestRange = new int[] { -1, -1 }; - - for (int[] hiddenCol : hiddenCols) - { - lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange; - higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange; - } - - if (lowestRange[0] == -1 && lowestRange[1] == -1) - { - startPos = alignmentStartEnd[0]; - } - else - { - startPos = lowestRange[1] + 1; - } - - if (higestRange[0] == -1 && higestRange[1] == -1) - { - endPos = alignmentStartEnd[1]; - } - else - { - endPos = higestRange[0] - 1; - } - return new int[] { startPos, endPos }; - } - - @Override public void setHiddenColumns(HiddenColumns cols) { hiddenCols = cols; diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index 2e61f9d..1b5207f 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -580,15 +580,6 @@ public interface AlignmentI extends AnnotatedCollectionI */ AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo); - /** - * Calculate the visible start and end index of an alignment. The result is - * returned an int array where: int[0] = startIndex, and int[1] = endIndex. - * - * @param hiddenCols - * @return - */ - public int[] getVisibleStartAndEndIndex(List hiddenCols); - public void setHiddenColumns(HiddenColumns cols); } diff --git a/src/jalview/datamodel/BinarySequence.java b/src/jalview/datamodel/BinarySequence.java index 5789b2b..477f4a7 100755 --- a/src/jalview/datamodel/BinarySequence.java +++ b/src/jalview/datamodel/BinarySequence.java @@ -69,6 +69,7 @@ public class BinarySequence extends Sequence { int nores = (isNa) ? ResidueProperties.maxNucleotideIndex : ResidueProperties.maxProteinIndex; + dbinary = new double[getLength() * nores]; return nores; @@ -131,7 +132,7 @@ public class BinarySequence extends Sequence { int nores = initMatrixGetNoRes(); - for (int i = 0, iSize = getSequence().length; i < iSize; i++) + for (int i = 0, iSize = getLength(); i < iSize; i++) { int aanum = nores - 1; diff --git a/src/jalview/datamodel/CigarArray.java b/src/jalview/datamodel/CigarArray.java index 837a10b..febf6b4 100644 --- a/src/jalview/datamodel/CigarArray.java +++ b/src/jalview/datamodel/CigarArray.java @@ -90,7 +90,7 @@ public class CigarArray extends CigarBase { this(constructSeqCigarArray(alignment, selectionGroup)); constructFromAlignment(alignment, - hidden != null ? hidden.getHiddenRegions() + hidden != null ? hidden.getHiddenColumnsCopy() : null, selectionGroup); } diff --git a/src/jalview/datamodel/ColumnSelection.java b/src/jalview/datamodel/ColumnSelection.java index eb2d174..4cdd7af 100644 --- a/src/jalview/datamodel/ColumnSelection.java +++ b/src/jalview/datamodel/ColumnSelection.java @@ -482,7 +482,7 @@ public class ColumnSelection */ public void invertColumnSelection(int first, int width, AlignmentI al) { - boolean hasHidden = al.getHiddenColumns().hasHidden(); + boolean hasHidden = al.getHiddenColumns().hasHiddenColumns(); for (int i = first; i < width; i++) { if (contains(i)) @@ -511,7 +511,7 @@ public class ColumnSelection selection = new IntList(); if (colsel.selection != null && colsel.selection.size() > 0) { - if (hiddenColumns.hasHidden()) + if (hiddenColumns.hasHiddenColumns()) { // only select visible columns in this columns selection for (Integer col : colsel.getSelected()) diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index f0d99e5..169b0a4 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -1,3 +1,23 @@ +/* + * 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.datamodel; import jalview.util.Comparison; @@ -8,85 +28,159 @@ import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Vector; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class HiddenColumns { + private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock(); + /* * list of hidden column [start, end] ranges; the list is maintained in * ascending start column order */ - private Vector hiddenColumns; + private ArrayList hiddenColumns; /** - * This Method is used to return all the HiddenColumn regions - * - * @return empty list or List of hidden column intervals + * Constructor */ - public List getHiddenRegions() + public HiddenColumns() { - return hiddenColumns == null ? Collections. emptyList() - : hiddenColumns; } /** - * Find the number of hidden columns + * Copy constructor * - * @return number of hidden columns + * @param copy */ - public int getSize() + public HiddenColumns(HiddenColumns copy) { - int size = 0; - if (hasHidden()) + try { - for (int[] range : hiddenColumns) + LOCK.writeLock().lock(); + if (copy != null) { - size += range[1] - range[0] + 1; + if (copy.hiddenColumns != null) + { + hiddenColumns = copy.copyHiddenRegionsToArrayList(); + } } + } finally + { + LOCK.writeLock().unlock(); } - return size; } /** - * Answers if there are any hidden columns + * This method is used to return all the HiddenColumn regions and is intended + * to remain private. External callers which need a copy of the regions can + * call getHiddenColumnsCopyAsList. * - * @return true if there are hidden columns + * @return empty list or List of hidden column intervals */ - public boolean hasHidden() + private List getHiddenRegions() { - return (hiddenColumns != null) && (!hiddenColumns.isEmpty()); + return hiddenColumns == null ? Collections. emptyList() + : hiddenColumns; } - @Override - public boolean equals(Object obj) + /** + * Output regions data as a string. String is in the format: + * reg0[0]reg0[1]reg1[0]reg1[1] ... regn[1] + * + * @param delimiter + * string to delimit regions + * @param betweenstring + * to put between start and end region values + * @return regions formatted according to delimiter and between strings + */ + public String regionsToString(String delimiter, String between) { - if (!(obj instanceof HiddenColumns)) + try + { + LOCK.readLock().lock(); + StringBuilder regionBuilder = new StringBuilder(); + if (hiddenColumns != null) + { + for (int[] range : hiddenColumns) + { + regionBuilder.append(delimiter).append(range[0]).append(between) + .append(range[1]); + } + + regionBuilder.deleteCharAt(0); + } + return regionBuilder.toString(); + } finally { - return false; + LOCK.readLock().unlock(); } - HiddenColumns that = (HiddenColumns) obj; + } - /* - * check hidden columns are either both null, or match - */ - if (this.hiddenColumns == null) + /** + * Find the number of hidden columns + * + * @return number of hidden columns + */ + public int getSize() + { + try { - return (that.hiddenColumns == null); + LOCK.readLock().lock(); + int size = 0; + if (hasHiddenColumns()) + { + for (int[] range : hiddenColumns) + { + size += range[1] - range[0] + 1; + } + } + return size; } - if (that.hiddenColumns == null - || that.hiddenColumns.size() != this.hiddenColumns.size()) + finally { - return false; + LOCK.readLock().unlock(); } - int i = 0; - for (int[] thisRange : hiddenColumns) + } + + @Override + public boolean equals(Object obj) + { + try { - int[] thatRange = that.hiddenColumns.get(i++); - if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1]) + LOCK.readLock().lock(); + + if (!(obj instanceof HiddenColumns)) + { + return false; + } + HiddenColumns that = (HiddenColumns) obj; + + /* + * check hidden columns are either both null, or match + */ + if (this.hiddenColumns == null) + { + return (that.hiddenColumns == null); + } + if (that.hiddenColumns == null + || that.hiddenColumns.size() != this.hiddenColumns.size()) { return false; } + int i = 0; + for (int[] thisRange : hiddenColumns) + { + int[] thatRange = that.hiddenColumns.get(i++); + if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1]) + { + return false; + } + } + return true; + } finally + { + LOCK.readLock().unlock(); } - return true; } /** @@ -98,19 +192,26 @@ public class HiddenColumns */ public int adjustForHiddenColumns(int column) { - int result = column; - if (hiddenColumns != null) + try { - for (int i = 0; i < hiddenColumns.size(); i++) + LOCK.readLock().lock(); + int result = column; + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(i); - if (result >= region[0]) + for (int i = 0; i < hiddenColumns.size(); i++) { - result += region[1] - region[0] + 1; + int[] region = hiddenColumns.get(i); + if (result >= region[0]) + { + result += region[1] - region[0] + 1; + } } } + return result; + } finally + { + LOCK.readLock().unlock(); } - return result; } /** @@ -124,42 +225,52 @@ public class HiddenColumns */ public int findColumnPosition(int hiddenColumn) { - int result = hiddenColumn; - if (hiddenColumns != null) + try { - int index = 0; - int[] region; - do + LOCK.readLock().lock(); + int result = hiddenColumn; + if (hiddenColumns != null) { - region = hiddenColumns.elementAt(index++); - if (hiddenColumn > region[1]) + int index = 0; + int[] region; + do { - result -= region[1] + 1 - region[0]; - } - } while ((hiddenColumn > region[1]) && (index < hiddenColumns.size())); + region = hiddenColumns.get(index++); + if (hiddenColumn > region[1]) + { + result -= region[1] + 1 - region[0]; + } + } while ((hiddenColumn > region[1]) + && (index < hiddenColumns.size())); - if (hiddenColumn >= region[0] && hiddenColumn <= region[1]) - { - // Here the hidden column is within a region, so - // we want to return the position of region[0]-1, adjusted for any - // earlier hidden columns. - // Calculate the difference between the actual hidden col position - // and region[0]-1, and then subtract from result to convert result from - // the adjusted hiddenColumn value to the adjusted region[0]-1 value - - // However, if the region begins at 0 we cannot return region[0]-1 - // just return 0 - if (region[0] == 0) - { - return 0; - } - else + if (hiddenColumn >= region[0] && hiddenColumn <= region[1]) { - return result - (hiddenColumn - region[0] + 1); + // Here the hidden column is within a region, so + // we want to return the position of region[0]-1, adjusted for any + // earlier hidden columns. + // Calculate the difference between the actual hidden col position + // and region[0]-1, and then subtract from result to convert result + // from + // the adjusted hiddenColumn value to the adjusted region[0]-1 value + + // However, if the region begins at 0 we cannot return region[0]-1 + // just return 0 + if (region[0] == 0) + { + return 0; + } + else + { + return result - (hiddenColumn - region[0] + 1); + } } } + return result; // return the shifted position after removing hidden + // columns. + } finally + { + LOCK.readLock().unlock(); } - return result; // return the shifted position after removing hidden columns. } /** @@ -175,6 +286,10 @@ public class HiddenColumns */ public int subtractVisibleColumns(int visibleDistance, int startColumn) { + try + { + + LOCK.readLock().lock(); int distance = visibleDistance; // in case startColumn is in a hidden region, move it to the left @@ -216,40 +331,62 @@ public class HiddenColumns return nextstart - distance; } return start - distance; + } finally + { + LOCK.readLock().unlock(); + } } /** - * Use this method to determine where the next hiddenRegion starts + * Use this method to determine the set of hiddenRegion start positions * - * @param hiddenRegion - * index of hidden region (counts from 0) - * @return column number in visible view + * @return list of column number in visible view where hidden regions start */ - public int findHiddenRegionPosition(int hiddenRegion) + public List findHiddenRegionPositions() { - int result = 0; - if (hiddenColumns != null) + try { - int index = 0; - int gaps = 0; - do + LOCK.readLock().lock(); + List positions = null; + + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(index); - if (hiddenRegion == 0) + positions = new ArrayList<>(hiddenColumns.size()); + + positions.add(hiddenColumns.get(0)[0]); + for (int i = 1; i < hiddenColumns.size(); ++i) { - return region[0]; - } - gaps += region[1] + 1 - region[0]; - result = region[1] + 1; - index++; - } while (index <= hiddenRegion); + int result = 0; + if (hiddenColumns != null) + { + int index = 0; + int gaps = 0; + do + { + int[] region = hiddenColumns.get(index); + gaps += region[1] + 1 - region[0]; + result = region[1] + 1; + index++; + } while (index <= i); - result -= gaps; - } + result -= gaps; + } + positions.add(result); + } + } + else + { + positions = new ArrayList<>(); + } - return result; + return positions; + } + finally + { + LOCK.readLock().unlock(); + } } /** @@ -261,22 +398,29 @@ public class HiddenColumns */ public int getHiddenBoundaryRight(int alPos) { - if (hiddenColumns != null) + try { - int index = 0; - do + LOCK.readLock().lock(); + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(index); - if (alPos < region[0]) + int index = 0; + do { - return region[0]; - } + int[] region = hiddenColumns.get(index); + if (alPos < region[0]) + { + return region[0]; + } - index++; - } while (index < hiddenColumns.size()); - } + index++; + } while (index < hiddenColumns.size()); + } - return alPos; + return alPos; + } finally + { + LOCK.readLock().unlock(); + } } @@ -289,12 +433,16 @@ public class HiddenColumns */ public int getHiddenBoundaryLeft(int alPos) { + try + { + LOCK.readLock().lock(); + if (hiddenColumns != null) { int index = hiddenColumns.size() - 1; do { - int[] region = hiddenColumns.elementAt(index); + int[] region = hiddenColumns.get(index); if (alPos > region[1]) { return region[1]; @@ -305,7 +453,10 @@ public class HiddenColumns } return alPos; - + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -318,12 +469,16 @@ public class HiddenColumns */ private int getHiddenIndexLeft(int pos) { + try + { + + LOCK.readLock().lock(); if (hiddenColumns != null) { int index = hiddenColumns.size() - 1; do { - int[] region = hiddenColumns.elementAt(index); + int[] region = hiddenColumns.get(index); if (pos > region[1]) { return index; @@ -334,6 +489,10 @@ public class HiddenColumns } return -1; + } finally + { + LOCK.readLock().unlock(); + } } @@ -345,76 +504,101 @@ public class HiddenColumns */ public void hideColumns(int start, int end) { - if (hiddenColumns == null) - { - hiddenColumns = new Vector(); - } - - /* - * traverse existing hidden ranges and insert / amend / append as - * appropriate - */ - for (int i = 0; i < hiddenColumns.size(); i++) + boolean wasAlreadyLocked = false; + try { - int[] region = hiddenColumns.elementAt(i); - - if (end < region[0] - 1) + // check if the write lock was already locked by this thread, + // as this method can be called internally in loops within HiddenColumns + if (!LOCK.isWriteLockedByCurrentThread()) { - /* - * insert discontiguous preceding range - */ - hiddenColumns.insertElementAt(new int[] { start, end }, i); - return; + LOCK.writeLock().lock(); + } + else + { + wasAlreadyLocked = true; } - if (end <= region[1]) + if (hiddenColumns == null) { - /* - * new range overlaps existing, or is contiguous preceding it - adjust - * start column - */ - region[0] = Math.min(region[0], start); - return; + hiddenColumns = new ArrayList<>(); } - if (start <= region[1] + 1) + /* + * traverse existing hidden ranges and insert / amend / append as + * appropriate + */ + for (int i = 0; i < hiddenColumns.size(); i++) { - /* - * new range overlaps existing, or is contiguous following it - adjust - * start and end columns - */ - region[0] = Math.min(region[0], start); - region[1] = Math.max(region[1], end); - - /* - * also update or remove any subsequent ranges - * that are overlapped - */ - while (i < hiddenColumns.size() - 1) + int[] region = hiddenColumns.get(i); + + if (end < region[0] - 1) + { + /* + * insert discontiguous preceding range + */ + hiddenColumns.add(i, new int[] { start, end }); + return; + } + + if (end <= region[1]) + { + /* + * new range overlaps existing, or is contiguous preceding it - adjust + * start column + */ + region[0] = Math.min(region[0], start); + return; + } + + if (start <= region[1] + 1) { - int[] nextRegion = hiddenColumns.get(i + 1); - if (nextRegion[0] > end + 1) + /* + * new range overlaps existing, or is contiguous following it - adjust + * start and end columns + */ + region[0] = Math.min(region[0], start); + region[1] = Math.max(region[1], end); + + /* + * also update or remove any subsequent ranges + * that are overlapped + */ + while (i < hiddenColumns.size() - 1) { - /* - * gap to next hidden range - no more to update - */ - break; + int[] nextRegion = hiddenColumns.get(i + 1); + if (nextRegion[0] > end + 1) + { + /* + * gap to next hidden range - no more to update + */ + break; + } + region[1] = Math.max(nextRegion[1], end); + hiddenColumns.remove(i + 1); } - region[1] = Math.max(nextRegion[1], end); - hiddenColumns.remove(i + 1); + return; } - return; - } } /* * remaining case is that the new range follows everything else */ - hiddenColumns.addElement(new int[] { start, end }); + hiddenColumns.add(new int[] { start, end }); + } finally + { + if (!wasAlreadyLocked) + { + LOCK.writeLock().unlock(); + } + } } public boolean isVisible(int column) { + try + { + LOCK.readLock().lock(); + if (hiddenColumns != null) { for (int[] region : hiddenColumns) @@ -427,39 +611,54 @@ public class HiddenColumns } return true; + } finally + { + LOCK.readLock().unlock(); + } } - /** - * ColumnSelection - */ - public HiddenColumns() + private ArrayList copyHiddenRegionsToArrayList() { - } + int size = 0; + if (hiddenColumns != null) + { + size = hiddenColumns.size(); + } + ArrayList copy = new ArrayList<>(size); + + for (int i = 0, j = size; i < j; i++) + { + int[] rh; + int[] cp; + rh = hiddenColumns.get(i); + if (rh != null) + { + cp = new int[rh.length]; + System.arraycopy(rh, 0, cp, 0, rh.length); + copy.add(cp); + } + } + return copy; + } + /** - * Copy constructor + * Returns a copy of the vector of hidden regions, as an ArrayList. Before + * using this method please consider if you really need access to the hidden + * regions - a new (or existing!) method on HiddenColumns might be more + * appropriate. * - * @param copy + * @return hidden regions as an ArrayList of [start,end] pairs */ - public HiddenColumns(HiddenColumns copy) + public ArrayList getHiddenColumnsCopy() { - if (copy != null) + try { - if (copy.hiddenColumns != null) - { - hiddenColumns = new Vector(copy.hiddenColumns.size()); - for (int i = 0, j = copy.hiddenColumns.size(); i < j; i++) - { - int[] rh, cp; - rh = copy.hiddenColumns.elementAt(i); - if (rh != null) - { - cp = new int[rh.length]; - System.arraycopy(rh, 0, cp, 0, rh.length); - hiddenColumns.addElement(cp); - } - } - } + LOCK.readLock().lock(); + return copyHiddenRegionsToArrayList(); + } finally + { + LOCK.readLock().unlock(); } } @@ -474,42 +673,49 @@ public class HiddenColumns public List compensateForEdit(int start, int change, ColumnSelection sel) { - List deletedHiddenColumns = null; - - if (hiddenColumns != null) + try { - deletedHiddenColumns = new ArrayList(); - int hSize = hiddenColumns.size(); - for (int i = 0; i < hSize; i++) + LOCK.writeLock().lock(); + List deletedHiddenColumns = null; + + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(i); - if (region[0] > start && start + change > region[1]) + deletedHiddenColumns = new ArrayList<>(); + int hSize = hiddenColumns.size(); + for (int i = 0; i < hSize; i++) { - deletedHiddenColumns.add(region); + int[] region = hiddenColumns.get(i); + if (region[0] > start && start + change > region[1]) + { + deletedHiddenColumns.add(region); - hiddenColumns.removeElementAt(i); - i--; - hSize--; - continue; - } + hiddenColumns.remove(i); + i--; + hSize--; + continue; + } - if (region[0] > start) - { - region[0] -= change; - region[1] -= change; - } + if (region[0] > start) + { + region[0] -= change; + region[1] -= change; + } + + if (region[0] < 0) + { + region[0] = 0; + } - if (region[0] < 0) - { - region[0] = 0; } + this.revealHiddenColumns(0, sel); } - this.revealHiddenColumns(0, sel); + return deletedHiddenColumns; + } finally + { + LOCK.writeLock().unlock(); } - - return deletedHiddenColumns; } /** @@ -523,34 +729,42 @@ public class HiddenColumns */ public void compensateForDelEdits(int start, int change) { - if (hiddenColumns != null) + try { - for (int i = 0; i < hiddenColumns.size(); i++) + LOCK.writeLock().lock(); + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(i); - if (region[0] >= start) - { - region[0] -= change; - } - if (region[1] >= start) - { - region[1] -= change; - } - if (region[1] < region[0]) + for (int i = 0; i < hiddenColumns.size(); i++) { - hiddenColumns.removeElementAt(i--); - } + int[] region = hiddenColumns.get(i); + if (region[0] >= start) + { + region[0] -= change; + } + if (region[1] >= start) + { + region[1] -= change; + } + if (region[1] < region[0]) + { + hiddenColumns.remove(i--); + } - if (region[0] < 0) - { - region[0] = 0; - } - if (region[1] < 0) - { - region[1] = 0; + if (region[0] < 0) + { + region[0] = 0; + } + if (region[1] < 0) + { + region[1] = 0; + } } } } + finally + { + LOCK.writeLock().unlock(); + } } /** @@ -565,111 +779,130 @@ public class HiddenColumns */ public int[] getVisibleContigs(int start, int end) { - if (hiddenColumns != null && hiddenColumns.size() > 0) + try { - List visiblecontigs = new ArrayList(); - List regions = getHiddenRegions(); + LOCK.readLock().lock(); + if (hiddenColumns != null && hiddenColumns.size() > 0) + { + List visiblecontigs = new ArrayList<>(); + List regions = getHiddenRegions(); - int vstart = start; - int[] region; - int hideStart, hideEnd; + int vstart = start; + int[] region; + int hideStart; + int hideEnd; - for (int j = 0; vstart < end && j < regions.size(); j++) - { - region = regions.get(j); - hideStart = region[0]; - hideEnd = region[1]; + for (int j = 0; vstart < end && j < regions.size(); j++) + { + region = regions.get(j); + hideStart = region[0]; + hideEnd = region[1]; + + if (hideEnd < vstart) + { + continue; + } + if (hideStart > vstart) + { + visiblecontigs.add(new int[] { vstart, hideStart - 1 }); + } + vstart = hideEnd + 1; + } - if (hideEnd < vstart) + if (vstart < end) { - continue; + visiblecontigs.add(new int[] { vstart, end - 1 }); } - if (hideStart > vstart) + int[] vcontigs = new int[visiblecontigs.size() * 2]; + for (int i = 0, j = visiblecontigs.size(); i < j; i++) { - visiblecontigs.add(new int[] { vstart, hideStart - 1 }); + int[] vc = visiblecontigs.get(i); + visiblecontigs.set(i, null); + vcontigs[i * 2] = vc[0]; + vcontigs[i * 2 + 1] = vc[1]; } - vstart = hideEnd + 1; - } - - if (vstart < end) - { - visiblecontigs.add(new int[] { vstart, end - 1 }); + visiblecontigs.clear(); + return vcontigs; } - int[] vcontigs = new int[visiblecontigs.size() * 2]; - for (int i = 0, j = visiblecontigs.size(); i < j; i++) + else { - int[] vc = visiblecontigs.get(i); - visiblecontigs.set(i, null); - vcontigs[i * 2] = vc[0]; - vcontigs[i * 2 + 1] = vc[1]; + return new int[] { start, end - 1 }; } - visiblecontigs.clear(); - return vcontigs; } - else + finally { - return new int[] { start, end - 1 }; + LOCK.readLock().unlock(); } } public String[] getVisibleSequenceStrings(int start, int end, SequenceI[] seqs) { - int i, iSize = seqs.length; - String selections[] = new String[iSize]; - if (hiddenColumns != null && hiddenColumns.size() > 0) + try { - for (i = 0; i < iSize; i++) + LOCK.readLock().lock(); + int iSize = seqs.length; + String[] selections = new String[iSize]; + if (hiddenColumns != null && hiddenColumns.size() > 0) { - StringBuffer visibleSeq = new StringBuffer(); - List regions = getHiddenRegions(); - - int blockStart = start, blockEnd = end; - int[] region; - int hideStart, hideEnd; - - for (int j = 0; j < regions.size(); j++) + for (int i = 0; i < iSize; i++) { - region = regions.get(j); - hideStart = region[0]; - hideEnd = region[1]; + StringBuffer visibleSeq = new StringBuffer(); + List regions = getHiddenRegions(); - if (hideStart < start) + int blockStart = start; + int blockEnd = end; + int[] region; + int hideStart; + int hideEnd; + + for (int j = 0; j < regions.size(); j++) { - continue; - } + region = regions.get(j); + hideStart = region[0]; + hideEnd = region[1]; - blockStart = Math.min(blockStart, hideEnd + 1); - blockEnd = Math.min(blockEnd, hideStart); + if (hideStart < start) + { + continue; + } - if (blockStart > blockEnd) - { - break; + blockStart = Math.min(blockStart, hideEnd + 1); + blockEnd = Math.min(blockEnd, hideStart); + + if (blockStart > blockEnd) + { + break; + } + + visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd)); + + blockStart = hideEnd + 1; + blockEnd = end; } - visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd)); + if (end > blockStart) + { + visibleSeq.append(seqs[i].getSequence(blockStart, end)); + } - blockStart = hideEnd + 1; - blockEnd = end; + selections[i] = visibleSeq.toString(); } - - if (end > blockStart) + } + else + { + for (int i = 0; i < iSize; i++) { - visibleSeq.append(seqs[i].getSequence(blockStart, end)); + selections[i] = seqs[i].getSequenceAsString(start, end); } - - selections[i] = visibleSeq.toString(); } + + return selections; } - else + finally { - for (i = 0; i < iSize; i++) - { - selections[i] = seqs[i].getSequenceAsString(start, end); - } + LOCK.readLock().unlock(); } - - return selections; } /** @@ -684,70 +917,86 @@ public class HiddenColumns */ public int[] locateVisibleBoundsOfSequence(SequenceI seq) { - int fpos = seq.getStart(), lpos = seq.getEnd(); - int start = 0; - - if (hiddenColumns == null || hiddenColumns.size() == 0) + try { - int ifpos = seq.findIndex(fpos) - 1, ilpos = seq.findIndex(lpos) - 1; - return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos }; - } + LOCK.readLock().lock(); + int fpos = seq.getStart(); + int lpos = seq.getEnd(); + int start = 0; - // Simply walk along the sequence whilst watching for hidden column - // boundaries - List regions = getHiddenRegions(); - int spos = fpos, lastvispos = -1, rcount = 0, hideStart = seq - .getLength(), hideEnd = -1; - int visPrev = 0, visNext = 0, firstP = -1, lastP = -1; - boolean foundStart = false; - for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd() - && p < pLen; p++) - { - if (!Comparison.isGap(seq.getCharAt(p))) + if (hiddenColumns == null || hiddenColumns.size() == 0) { - // keep track of first/last column - // containing sequence data regardless of visibility - if (firstP == -1) - { - firstP = p; - } - lastP = p; - // update hidden region start/end - while (hideEnd < p && rcount < regions.size()) - { - int[] region = regions.get(rcount++); - visPrev = visNext; - visNext += region[0] - visPrev; - hideStart = region[0]; - hideEnd = region[1]; - } - if (hideEnd < p) - { - hideStart = seq.getLength(); - } - // update visible boundary for sequence - if (p < hideStart) + int ifpos = seq.findIndex(fpos) - 1; + int ilpos = seq.findIndex(lpos) - 1; + return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos }; + } + + // Simply walk along the sequence whilst watching for hidden column + // boundaries + List regions = getHiddenRegions(); + int spos = fpos; + int lastvispos = -1; + int rcount = 0; + int hideStart = seq.getLength(); + int hideEnd = -1; + int visPrev = 0; + int visNext = 0; + int firstP = -1; + int lastP = -1; + boolean foundStart = false; + for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd() + && p < pLen; p++) + { + if (!Comparison.isGap(seq.getCharAt(p))) { - if (!foundStart) + // keep track of first/last column + // containing sequence data regardless of visibility + if (firstP == -1) + { + firstP = p; + } + lastP = p; + // update hidden region start/end + while (hideEnd < p && rcount < regions.size()) + { + int[] region = regions.get(rcount++); + visPrev = visNext; + visNext += region[0] - visPrev; + hideStart = region[0]; + hideEnd = region[1]; + } + if (hideEnd < p) { - fpos = spos; - start = p; - foundStart = true; + hideStart = seq.getLength(); } - lastvispos = p; - lpos = spos; + // update visible boundary for sequence + if (p < hideStart) + { + if (!foundStart) + { + fpos = spos; + start = p; + foundStart = true; + } + lastvispos = p; + lpos = spos; + } + // look for next sequence position + spos++; } - // look for next sequence position - spos++; } + if (foundStart) + { + return new int[] { findColumnPosition(start), + findColumnPosition(lastvispos), fpos, lpos, firstP, lastP }; + } + // otherwise, sequence was completely hidden + return new int[] { visPrev, visNext, 0, 0, firstP, lastP }; } - if (foundStart) + finally { - return new int[] { findColumnPosition(start), - findColumnPosition(lastvispos), fpos, lpos, firstP, lastP }; + LOCK.readLock().unlock(); } - // otherwise, sequence was completely hidden - return new int[] { visPrev, visNext, 0, 0, firstP, lastP }; } /** @@ -775,88 +1024,100 @@ public class HiddenColumns public void makeVisibleAnnotation(int start, int end, AlignmentAnnotation alignmentAnnotation) { - if (alignmentAnnotation.annotations == null) + try { - return; - } - if (start == end && end == -1) - { - start = 0; - end = alignmentAnnotation.annotations.length; - } - if (hiddenColumns != null && hiddenColumns.size() > 0) - { - // then mangle the alignmentAnnotation annotation array - Vector annels = new Vector(); - Annotation[] els = null; - List regions = getHiddenRegions(); - int blockStart = start, blockEnd = end; - int[] region; - int hideStart, hideEnd, w = 0; - - for (int j = 0; j < regions.size(); j++) + LOCK.readLock().lock(); + if (alignmentAnnotation.annotations == null) { - region = regions.get(j); - hideStart = region[0]; - hideEnd = region[1]; + return; + } + if (start == end && end == -1) + { + start = 0; + end = alignmentAnnotation.annotations.length; + } + if (hiddenColumns != null && hiddenColumns.size() > 0) + { + // then mangle the alignmentAnnotation annotation array + Vector annels = new Vector<>(); + Annotation[] els = null; + List regions = getHiddenRegions(); + int blockStart = start; + int blockEnd = end; + int[] region; + int hideStart; + int hideEnd; + int w = 0; - if (hideStart < start) + for (int j = 0; j < regions.size(); j++) { - continue; - } + region = regions.get(j); + hideStart = region[0]; + hideEnd = region[1]; - blockStart = Math.min(blockStart, hideEnd + 1); - blockEnd = Math.min(blockEnd, hideStart); + if (hideStart < start) + { + continue; + } - if (blockStart > blockEnd) - { - break; - } + blockStart = Math.min(blockStart, hideEnd + 1); + blockEnd = Math.min(blockEnd, hideStart); - annels.addElement(els = new Annotation[blockEnd - blockStart]); - System.arraycopy(alignmentAnnotation.annotations, blockStart, els, - 0, els.length); - w += els.length; - blockStart = hideEnd + 1; - blockEnd = end; - } + if (blockStart > blockEnd) + { + break; + } - if (end > blockStart) - { - annels.addElement(els = new Annotation[end - blockStart + 1]); - if ((els.length + blockStart) <= alignmentAnnotation.annotations.length) + annels.addElement(els = new Annotation[blockEnd - blockStart]); + System.arraycopy(alignmentAnnotation.annotations, blockStart, els, + 0, els.length); + w += els.length; + blockStart = hideEnd + 1; + blockEnd = end; + } + + if (end > blockStart) { - // copy just the visible segment of the annotation row - System.arraycopy(alignmentAnnotation.annotations, blockStart, - els, 0, els.length); + annels.addElement(els = new Annotation[end - blockStart + 1]); + if ((els.length + + blockStart) <= alignmentAnnotation.annotations.length) + { + // copy just the visible segment of the annotation row + System.arraycopy(alignmentAnnotation.annotations, blockStart, + els, 0, els.length); + } + else + { + // copy to the end of the annotation row + System.arraycopy(alignmentAnnotation.annotations, blockStart, + els, 0, + (alignmentAnnotation.annotations.length - blockStart)); + } + w += els.length; } - else + if (w == 0) { - // copy to the end of the annotation row - System.arraycopy(alignmentAnnotation.annotations, blockStart, - els, 0, - (alignmentAnnotation.annotations.length - blockStart)); + return; } - w += els.length; - } - if (w == 0) - { - return; - } - alignmentAnnotation.annotations = new Annotation[w]; - w = 0; + alignmentAnnotation.annotations = new Annotation[w]; + w = 0; - for (Annotation[] chnk : annels) + for (Annotation[] chnk : annels) + { + System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w, + chnk.length); + w += chnk.length; + } + } + else { - System.arraycopy(chnk, 0, alignmentAnnotation.annotations, w, - chnk.length); - w += chnk.length; + alignmentAnnotation.restrict(start, end); } } - else + finally { - alignmentAnnotation.restrict(start, end); + LOCK.readLock().unlock(); } } @@ -866,7 +1127,14 @@ public class HiddenColumns */ public boolean hasHiddenColumns() { - return hiddenColumns != null && hiddenColumns.size() > 0; + try + { + LOCK.readLock().lock(); + return hiddenColumns != null && hiddenColumns.size() > 0; + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -875,7 +1143,14 @@ public class HiddenColumns */ public boolean hasManyHiddenColumns() { - return hiddenColumns != null && hiddenColumns.size() > 1; + try + { + LOCK.readLock().lock(); + return hiddenColumns != null && hiddenColumns.size() > 1; + } finally + { + LOCK.readLock().unlock(); + } } /** @@ -886,10 +1161,17 @@ public class HiddenColumns */ public void hideInsertionsFor(SequenceI sr) { - List inserts = sr.getInsertions(); - for (int[] r : inserts) + try { - hideColumns(r[0], r[1]); + LOCK.writeLock().lock(); + List inserts = sr.getInsertions(); + for (int[] r : inserts) + { + hideColumns(r[0], r[1]); + } + } finally + { + LOCK.writeLock().unlock(); } } @@ -898,19 +1180,27 @@ public class HiddenColumns */ public void revealAllHiddenColumns(ColumnSelection sel) { - if (hiddenColumns != null) + try { - for (int i = 0; i < hiddenColumns.size(); i++) + LOCK.writeLock().lock(); + if (hiddenColumns != null) { - int[] region = hiddenColumns.elementAt(i); - for (int j = region[0]; j < region[1] + 1; j++) + for (int i = 0; i < hiddenColumns.size(); i++) { - sel.addElement(j); + int[] region = hiddenColumns.get(i); + for (int j = region[0]; j < region[1] + 1; j++) + { + sel.addElement(j); + } } } - } - hiddenColumns = null; + hiddenColumns = null; + } + finally + { + LOCK.writeLock().unlock(); + } } /** @@ -921,23 +1211,31 @@ public class HiddenColumns */ public void revealHiddenColumns(int start, ColumnSelection sel) { - for (int i = 0; i < hiddenColumns.size(); i++) + try { - int[] region = hiddenColumns.elementAt(i); - if (start == region[0]) + LOCK.writeLock().lock(); + for (int i = 0; i < hiddenColumns.size(); i++) { - for (int j = region[0]; j < region[1] + 1; j++) + int[] region = hiddenColumns.get(i); + if (start == region[0]) { - sel.addElement(j); - } + for (int j = region[0]; j < region[1] + 1; j++) + { + sel.addElement(j); + } - hiddenColumns.removeElement(region); - break; + hiddenColumns.remove(region); + break; + } + } + if (hiddenColumns.size() == 0) + { + hiddenColumns = null; } } - if (hiddenColumns.size() == 0) + finally { - hiddenColumns = null; + LOCK.writeLock().unlock(); } } @@ -949,13 +1247,16 @@ public class HiddenColumns * @param intervals * @return */ - private boolean pruneIntervalVector(final List shifts, - Vector intervals) + private boolean pruneIntervalList(final List shifts, + ArrayList intervals) { boolean pruned = false; - int i = 0, j = intervals.size() - 1, s = 0, t = shifts.size() - 1; - int hr[] = intervals.elementAt(i); - int sr[] = shifts.get(s); + int i = 0; + int j = intervals.size() - 1; + int s = 0; + int t = shifts.size() - 1; + int[] hr = intervals.get(i); + int[] sr = shifts.get(s); while (i <= j && s <= t) { boolean trailinghn = hr[1] >= sr[0]; @@ -963,7 +1264,7 @@ public class HiddenColumns { if (i < j) { - hr = intervals.elementAt(++i); + hr = intervals.get(++i); } else { @@ -991,12 +1292,12 @@ public class HiddenColumns { if (trailinghc) { // deleted hidden region. - intervals.removeElementAt(i); + intervals.remove(i); pruned = true; j--; if (i <= j) { - hr = intervals.elementAt(i); + hr = intervals.get(i); } continue; } @@ -1044,15 +1345,23 @@ public class HiddenColumns */ public void pruneDeletions(List shifts) { - // delete any intervals intersecting. - if (hiddenColumns != null) + try { - pruneIntervalVector(shifts, hiddenColumns); - if (hiddenColumns != null && hiddenColumns.size() == 0) + LOCK.writeLock().lock(); + // delete any intervals intersecting. + if (hiddenColumns != null) { - hiddenColumns = null; + pruneIntervalList(shifts, hiddenColumns); + if (hiddenColumns != null && hiddenColumns.size() == 0) + { + hiddenColumns = null; + } } } + finally + { + LOCK.writeLock().unlock(); + } } /** @@ -1098,8 +1407,7 @@ public class HiddenColumns // recover mapping between sequence's non-gap positions and positions // mapping to view. pruneDeletions(ShiftList.parseMap(origseq.gapMap())); - int[] viscontigs = al.getHiddenColumns().getVisibleContigs(0, - profileseq.getLength()); + int[] viscontigs = getVisibleContigs(0, profileseq.getLength()); int spos = 0; int offset = 0; @@ -1251,16 +1559,24 @@ public class HiddenColumns @Override public int hashCode() { - int hashCode = 1; - if (hiddenColumns != null) + try { - for (int[] hidden : hiddenColumns) + LOCK.readLock().lock(); + int hashCode = 1; + if (hiddenColumns != null) { - hashCode = 31 * hashCode + hidden[0]; - hashCode = 31 * hashCode + hidden[1]; + for (int[] hidden : hiddenColumns) + { + hashCode = 31 * hashCode + hidden[0]; + hashCode = 31 * hashCode + hidden[1]; + } } + return hashCode; + } + finally + { + LOCK.readLock().unlock(); } - return hashCode; } /** @@ -1271,11 +1587,19 @@ public class HiddenColumns */ public void hideMarkedBits(BitSet inserts) { - for (int firstSet = inserts.nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts - .nextSetBit(lastSet)) + try { - lastSet = inserts.nextClearBit(firstSet); - hideColumns(firstSet, lastSet - 1); + LOCK.writeLock().lock(); + for (int firstSet = inserts + .nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts + .nextSetBit(lastSet)) + { + lastSet = inserts.nextClearBit(firstSet); + hideColumns(firstSet, lastSet - 1); + } + } finally + { + LOCK.writeLock().unlock(); } } @@ -1286,13 +1610,109 @@ public class HiddenColumns */ public void markHiddenRegions(BitSet inserts) { - if (hiddenColumns == null) + try + { + LOCK.readLock().lock(); + if (hiddenColumns == null) + { + return; + } + for (int[] range : hiddenColumns) + { + inserts.set(range[0], range[1] + 1); + } + } + finally { - return; + LOCK.readLock().unlock(); } - for (int[] range : hiddenColumns) + } + + /** + * Calculate the visible start and end index of an alignment. + * + * @param width + * full alignment width + * @return integer array where: int[0] = startIndex, and int[1] = endIndex + */ + public int[] getVisibleStartAndEndIndex(int width) + { + try + { + LOCK.readLock().lock(); + int[] alignmentStartEnd = new int[] { 0, width - 1 }; + int startPos = alignmentStartEnd[0]; + int endPos = alignmentStartEnd[1]; + + int[] lowestRange = new int[] { -1, -1 }; + int[] higestRange = new int[] { -1, -1 }; + + if (hiddenColumns == null) + { + return new int[] { startPos, endPos }; + } + + for (int[] hiddenCol : hiddenColumns) + { + lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange; + higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange; + } + + if (lowestRange[0] == -1 && lowestRange[1] == -1) + { + startPos = alignmentStartEnd[0]; + } + else + { + startPos = lowestRange[1] + 1; + } + + if (higestRange[0] == -1 && higestRange[1] == -1) + { + endPos = alignmentStartEnd[1]; + } + else + { + endPos = higestRange[0] - 1; + } + return new int[] { startPos, endPos }; + } finally + { + LOCK.readLock().unlock(); + } + + } + + /** + * Finds the hidden region (if any) which starts or ends at res + * + * @param res + * visible residue position, unadjusted for hidden columns + * @return region as [start,end] or null if no matching region is found + */ + public int[] getRegionWithEdgeAtRes(int res) + { + try + { + LOCK.readLock().lock(); + int adjres = adjustForHiddenColumns(res); + + int[] reveal = null; + if (hiddenColumns != null) + { + for (int[] region : hiddenColumns) + { + if (adjres + 1 == region[0] || adjres - 1 == region[1]) + { + reveal = region; + break; + } + } + } + return reveal; + } finally { - inserts.set(range[0], range[1] + 1); + LOCK.readLock().unlock(); } } diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java index a98b10e..98e9694 100755 --- a/src/jalview/datamodel/HiddenSequences.java +++ b/src/jalview/datamodel/HiddenSequences.java @@ -157,9 +157,10 @@ public class HiddenSequences int absAlignmentIndex = alignment.findIndex(sequence); int alignmentIndex = adjustForHiddenSeqs(absAlignmentIndex); - if (hiddenSequences[alignmentIndex] != null) + if (alignmentIndex < 0 || hiddenSequences[alignmentIndex] != null) { System.out.println("ERROR!!!!!!!!!!!"); + return; } hiddenSequences[alignmentIndex] = sequence; @@ -253,7 +254,8 @@ public class HiddenSequences } /** - * Convert absolute alignment index to visible alignment index + * Convert absolute alignment index to visible alignment index (or -1 if + * before the first visible sequence) * * @param alignmentIndex * @return diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index 9a6cf2b..ba7412c 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -143,15 +143,16 @@ public class SequenceFeature implements FeatureLocationI * A copy constructor that allows the value of final fields to be 'modified' * * @param sf + * @param newType * @param newBegin * @param newEnd * @param newGroup * @param newScore */ - public SequenceFeature(SequenceFeature sf, int newBegin, int newEnd, - String newGroup, float newScore) + public SequenceFeature(SequenceFeature sf, String newType, int newBegin, + int newEnd, String newGroup, float newScore) { - this(sf.getType(), sf.getDescription(), newBegin, newEnd, newScore, + this(newType, sf.getDescription(), newBegin, newEnd, newScore, newGroup); if (sf.otherDetails != null) @@ -173,6 +174,21 @@ public class SequenceFeature implements FeatureLocationI } /** + * A copy constructor that allows the value of final fields to be 'modified' + * + * @param sf + * @param newBegin + * @param newEnd + * @param newGroup + * @param newScore + */ + public SequenceFeature(SequenceFeature sf, int newBegin, int newEnd, + String newGroup, float newScore) + { + this(sf, sf.getType(), newBegin, newEnd, newGroup, newScore); + } + + /** * Two features are considered equal if they have the same type, group, * description, start, end, phase, strand, and (if present) 'Name', ID' and * 'Parent' attributes. diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index 463b909..46c802f 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -188,7 +188,7 @@ public class SequenceGroup implements AnnotatedCollectionI colourText = seqsel.colourText; startRes = seqsel.startRes; endRes = seqsel.endRes; - cs = new ResidueShader(seqsel.getColourScheme()); + cs = new ResidueShader((ResidueShader) seqsel.cs); if (seqsel.description != null) { description = new String(seqsel.description); diff --git a/src/jalview/datamodel/VisibleColsIterator.java b/src/jalview/datamodel/VisibleColsIterator.java index 70de1e3..a82de93 100644 --- a/src/jalview/datamodel/VisibleColsIterator.java +++ b/src/jalview/datamodel/VisibleColsIterator.java @@ -48,7 +48,7 @@ public class VisibleColsIterator implements Iterator last = lastcol; current = firstcol; next = firstcol; - hidden = hiddenCols.getHiddenRegions(); + hidden = hiddenCols.getHiddenColumnsCopy(); lasthiddenregion = -1; if (hidden != null) diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index a9a970f..e5d550f 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -167,7 +167,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public AlignViewControllerI avc; - List alignPanels = new ArrayList(); + List alignPanels = new ArrayList<>(); /** * Last format used to load or save alignments in this window @@ -396,8 +396,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, addKeyListener(); - final List selviews = new ArrayList(); - final List origview = new ArrayList(); + final List selviews = new ArrayList<>(); + final List origview = new ArrayList<>(); final String menuLabel = MessageManager .getString("label.copy_format_from"); ViewSelectionMenu vsel = new ViewSelectionMenu(menuLabel, @@ -410,7 +410,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, origview.clear(); origview.add(alignPanel); // make an array of all alignment panels except for this one - List aps = new ArrayList( + List aps = new ArrayList<>( Arrays.asList(Desktop.getAlignmentPanels(null))); aps.remove(AlignFrame.this.alignPanel); return aps.toArray(new AlignmentPanel[aps.size()]); @@ -689,24 +689,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, break; } case KeyEvent.VK_PAGE_UP: - if (viewport.getWrapAlignment()) - { - vpRanges.scrollUp(true); - } - else - { - vpRanges.pageUp(); - } + vpRanges.pageUp(); break; case KeyEvent.VK_PAGE_DOWN: - if (viewport.getWrapAlignment()) - { - vpRanges.scrollUp(false); - } - else - { - vpRanges.pageDown(); - } + vpRanges.pageDown(); break; } } @@ -1320,10 +1306,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { alignmentToExport = viewport.getAlignment(); } - alignmentStartEnd = alignmentToExport - .getVisibleStartAndEndIndex(viewport.getAlignment() - .getHiddenColumns() - .getHiddenRegions()); + alignmentStartEnd = viewport.getAlignment().getHiddenColumns() + .getVisibleStartAndEndIndex(alignmentToExport.getWidth()); AlignmentExportData ed = new AlignmentExportData(alignmentToExport, omitHidden, alignmentStartEnd, settings); return ed; @@ -1732,7 +1716,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, synchronized void slideSequences(boolean right, int size) { - List sg = new ArrayList(); + List sg = new ArrayList<>(); if (viewport.cursorMode) { sg.add(viewport.getAlignment().getSequenceAt( @@ -1751,7 +1735,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return; } - List invertGroup = new ArrayList(); + List invertGroup = new ArrayList<>(); for (SequenceI seq : viewport.getAlignment().getSequences()) { @@ -1884,11 +1868,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, ArrayList hiddenColumns = null; if (viewport.hasHiddenColumns()) { - hiddenColumns = new ArrayList(); - int hiddenOffset = viewport.getSelectionGroup().getStartRes(), hiddenCutoff = viewport - .getSelectionGroup().getEndRes(); - for (int[] region : viewport.getAlignment().getHiddenColumns() - .getHiddenRegions()) + hiddenColumns = new ArrayList<>(); + int hiddenOffset = viewport.getSelectionGroup().getStartRes(); + int hiddenCutoff = viewport.getSelectionGroup().getEndRes(); + ArrayList hiddenRegions = viewport.getAlignment() + .getHiddenColumns().getHiddenColumnsCopy(); + for (int[] region : hiddenRegions) { if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff) { @@ -1994,7 +1979,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } int alwidth = 0; - ArrayList newGraphGroups = new ArrayList(); + ArrayList newGraphGroups = new ArrayList<>(); int fgroup = -1; if (newAlignment) @@ -2457,7 +2442,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.setSelectionGroup(null); viewport.getColumnSelection().clear(); viewport.setSelectionGroup(null); - alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null); alignPanel.getIdPanel().getIdCanvas().searchResults = null; // JAL-2034 - should delegate to // alignPanel to decide if overview needs @@ -2826,7 +2810,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, */ protected List getExistingViewNames(List comps) { - List existingNames = new ArrayList(); + List existingNames = new ArrayList<>(); for (Component comp : comps) { if (comp instanceof AlignmentPanel) @@ -3215,10 +3199,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { viewport.setShowSequenceFeatures(showSeqFeatures.isSelected()); alignPanel.paintAlignment(true); - if (alignPanel.getOverviewPanel() != null) - { - alignPanel.getOverviewPanel().updateOverviewImage(); - } } /** @@ -3274,7 +3254,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } JInternalFrame frame = new JInternalFrame(); - OverviewPanel overview = new OverviewPanel(alignPanel); + final OverviewPanel overview = new OverviewPanel(alignPanel); frame.setContentPane(overview); Desktop.addInternalFrame(frame, MessageManager.formatMessage( "label.overview_params", new Object[] { this.getTitle() }), @@ -3287,6 +3267,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, public void internalFrameClosed( javax.swing.event.InternalFrameEvent evt) { + overview.dispose(); alignPanel.setOverviewPanel(null); }; }); @@ -3760,7 +3741,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, List comps = PaintRefresher.components.get(viewport .getSequenceSetId()); - List treePanels = new ArrayList(); + List treePanels = new ArrayList<>(); for (Component comp : comps) { if (comp instanceof TreePanel) @@ -4020,7 +4001,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void run() { - final List legacyItems = new ArrayList(); + final List legacyItems = new ArrayList<>(); try { // System.err.println("Building ws menu again " @@ -4035,7 +4016,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // TODO: group services by location as well as function and/or // introduce // object broker mechanism. - final Vector wsmenu = new Vector(); + final Vector wsmenu = new Vector<>(); final IProgressIndicator af = me; /* @@ -4403,8 +4384,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // Java's Transferable for native dnd evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); Transferable t = evt.getTransferable(); - List files = new ArrayList(); - List protocols = new ArrayList(); + List files = new ArrayList<>(); + List protocols = new ArrayList<>(); try { @@ -4424,8 +4405,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, /** * Object[] { String,SequenceI} */ - ArrayList filesmatched = new ArrayList(); - ArrayList filesnotmatched = new ArrayList(); + ArrayList filesmatched = new ArrayList<>(); + ArrayList filesnotmatched = new ArrayList<>(); for (int i = 0; i < files.size(); i++) { String file = files.get(i).toString(); @@ -5416,7 +5397,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { return; } - List cdnaSeqs = new ArrayList(); + List cdnaSeqs = new ArrayList<>(); for (SequenceI aaSeq : alignment.getSequences()) { for (AlignedCodonFrame acf : mappings) diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 86e1144..835371f 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -39,7 +39,6 @@ import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SearchResults; import jalview.datamodel.SearchResultsI; -import jalview.datamodel.Sequence; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.ResidueShader; @@ -311,44 +310,6 @@ public class AlignViewport extends AlignmentViewport implements } } - /** - * get the consensus sequence as displayed under the PID consensus annotation - * row. - * - * @return consensus sequence as a new sequence object - */ - public SequenceI getConsensusSeq() - { - if (consensus == null) - { - updateConsensus(null); - } - if (consensus == null) - { - return null; - } - StringBuffer seqs = new StringBuffer(); - for (int i = 0; i < consensus.annotations.length; i++) - { - if (consensus.annotations[i] != null) - { - if (consensus.annotations[i].description.charAt(0) == '[') - { - seqs.append(consensus.annotations[i].description.charAt(1)); - } - else - { - seqs.append(consensus.annotations[i].displayCharacter); - } - } - } - - SequenceI sq = new Sequence("Consensus", seqs.toString()); - sq.setDescription("Percentage Identity Consensus " - + ((ignoreGapsInConsensusCalculation) ? " without gaps" : "")); - return sq; - } - boolean validCharWidth; /** diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 72b1cc9..fc687b4 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -343,19 +343,11 @@ public class AlignmentPanel extends GAlignmentPanel implements */ public void highlightSearchResults(SearchResultsI results) { - scrollToPosition(results); - getSeqPanel().seqCanvas.highlightSearchResults(results); - } + boolean scrolled = scrollToPosition(results, 0, true, false); - /** - * Scroll the view to show the position of the highlighted region in results - * (if any) and redraw the overview - * - * @param results - */ - public boolean scrollToPosition(SearchResultsI results) - { - return scrollToPosition(results, 0, true, false); + boolean noFastPaint = scrolled && av.getWrapAlignment(); + + getSeqPanel().seqCanvas.highlightSearchResults(results, noFastPaint); } /** @@ -373,8 +365,10 @@ public class AlignmentPanel extends GAlignmentPanel implements } /** - * Scroll the view to show the position of the highlighted region in results - * (if any) + * Scrolls the view (if necessary) to show the position of the first + * highlighted region in results (if any). Answers true if the view was + * scrolled, or false if no matched region was found, or it is already + * visible. * * @param results * @param verticalOffset @@ -384,116 +378,117 @@ public class AlignmentPanel extends GAlignmentPanel implements * - when set, the overview will be recalculated (takes longer) * @param centre * if true, try to centre the search results horizontally in the view - * @return false if results were not found + * @return */ - public boolean scrollToPosition(SearchResultsI results, + protected boolean scrollToPosition(SearchResultsI results, int verticalOffset, boolean redrawOverview, boolean centre) { int startv, endv, starts, ends; - // TODO: properly locate search results in view when large numbers of hidden - // columns exist before highlighted region - // do we need to scroll the panel? - // TODO: tons of nullpointerexceptions raised here. - if (results != null && results.getSize() > 0 && av != null - && av.getAlignment() != null) - { - int seqIndex = av.getAlignment().findIndex(results); - if (seqIndex == -1) - { - return false; - } - SequenceI seq = av.getAlignment().getSequenceAt(seqIndex); - int[] r = results.getResults(seq, 0, av.getAlignment().getWidth()); - if (r == null) - { - return false; - } - int start = r[0]; - int end = r[1]; + if (results == null || results.isEmpty() || av == null + || av.getAlignment() == null) + { + return false; + } + int seqIndex = av.getAlignment().findIndex(results); + if (seqIndex == -1) + { + return false; + } + SequenceI seq = av.getAlignment().getSequenceAt(seqIndex); - /* - * To centre results, scroll to positions half the visible width - * left/right of the start/end positions - */ - if (centre) - { - int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - - 1; - start = Math.max(start - offset, 0); - end = end + offset - 1; - } - if (start < 0) - { - return false; - } - if (end == seq.getEnd()) - { - return false; - } - if (av.hasHiddenColumns()) + int[] r = results.getResults(seq, 0, av.getAlignment().getWidth()); + if (r == null) + { + return false; + } + int start = r[0]; + int end = r[1]; + + /* + * To centre results, scroll to positions half the visible width + * left/right of the start/end positions + */ + if (centre) + { + int offset = (vpRanges.getEndRes() - vpRanges.getStartRes() + 1) / 2 - 1; + start = Math.max(start - offset, 0); + end = end + offset - 1; + } + if (start < 0) + { + return false; + } + if (end == seq.getEnd()) + { + return false; + } + + if (av.hasHiddenColumns()) + { + HiddenColumns hidden = av.getAlignment().getHiddenColumns(); + start = hidden.findColumnPosition(start); + end = hidden.findColumnPosition(end); + if (start == end) { - HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - start = hidden.findColumnPosition(start); - end = hidden.findColumnPosition(end); - if (start == end) + if (!hidden.isVisible(r[0])) { - if (!hidden.isVisible(r[0])) - { - // don't scroll - position isn't visible - return false; - } + // don't scroll - position isn't visible + return false; } } + } - /* - * allow for offset of target sequence (actually scroll to one above it) - */ - seqIndex = Math.max(0, seqIndex - verticalOffset); + /* + * allow for offset of target sequence (actually scroll to one above it) + */ + seqIndex = Math.max(0, seqIndex - verticalOffset); + boolean scrollNeeded = true; - if (!av.getWrapAlignment()) + if (!av.getWrapAlignment()) + { + if ((startv = vpRanges.getStartRes()) >= start) { - if ((startv = vpRanges.getStartRes()) >= start) - { - /* - * Scroll left to make start of search results visible - */ - setScrollValues(start, seqIndex); - } - else if ((endv = vpRanges.getEndRes()) <= end) - { - /* - * Scroll right to make end of search results visible - */ - setScrollValues(startv + end - endv, seqIndex); - } - else if ((starts = vpRanges.getStartSeq()) > seqIndex) - { - /* - * Scroll up to make start of search results visible - */ - setScrollValues(vpRanges.getStartRes(), seqIndex); - } - else if ((ends = vpRanges.getEndSeq()) <= seqIndex) - { - /* - * Scroll down to make end of search results visible - */ - setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends - + 1); - } /* - * Else results are already visible - no need to scroll + * Scroll left to make start of search results visible */ + setScrollValues(start, seqIndex); } - else + else if ((endv = vpRanges.getEndRes()) <= end) + { + /* + * Scroll right to make end of search results visible + */ + setScrollValues(startv + end - endv, seqIndex); + } + else if ((starts = vpRanges.getStartSeq()) > seqIndex) + { + /* + * Scroll up to make start of search results visible + */ + setScrollValues(vpRanges.getStartRes(), seqIndex); + } + else if ((ends = vpRanges.getEndSeq()) <= seqIndex) { - vpRanges.scrollToWrappedVisible(start); + /* + * Scroll down to make end of search results visible + */ + setScrollValues(vpRanges.getStartRes(), starts + seqIndex - ends + + 1); } + /* + * Else results are already visible - no need to scroll + */ + scrollNeeded = false; + } + else + { + scrollNeeded = vpRanges.scrollToWrappedVisible(start); } paintAlignment(redrawOverview); - return true; + + return scrollNeeded; } /** @@ -631,21 +626,24 @@ public class AlignmentPanel extends GAlignmentPanel implements annotationSpaceFillerHolder.setVisible(true); } - if (wrap) - { - int widthInRes = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - vpRanges.setViewportWidth(widthInRes); - } - else - { - int widthInRes = (getSeqPanel().seqCanvas.getWidth() / av - .getCharWidth()) - 1; - int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av - .getCharHeight()) - 1; + int canvasWidth = getSeqPanel().seqCanvas.getWidth(); + if (canvasWidth > 0) + { // may not yet be laid out + if (wrap) + { + int widthInRes = getSeqPanel().seqCanvas + .getWrappedCanvasWidth(canvasWidth); + vpRanges.setViewportWidth(widthInRes); + } + else + { + int widthInRes = (canvasWidth / av.getCharWidth()) - 1; + int heightInSeq = (getSeqPanel().seqCanvas.getHeight() / av + .getCharHeight()) - 1; - vpRanges.setViewportWidth(widthInRes); - vpRanges.setViewportHeight(heightInSeq); + vpRanges.setViewportWidth(widthInRes); + vpRanges.setViewportHeight(heightInSeq); + } } idSpaceFillerPanel1.setVisible(!wrap); @@ -737,97 +735,111 @@ public class AlignmentPanel extends GAlignmentPanel implements @Override public void adjustmentValueChanged(AdjustmentEvent evt) { - int oldX = vpRanges.getStartRes(); - int oldwidth = vpRanges.getViewportWidth(); - int oldY = vpRanges.getStartSeq(); - int oldheight = vpRanges.getViewportHeight(); - if (av.getWrapAlignment()) { - if (evt.getSource() == hscroll) - { - return; // no horizontal scroll when wrapped - } - else if (evt.getSource() == vscroll) + adjustScrollingWrapped(evt); + return; + } + + if (evt.getSource() == hscroll) + { + int oldX = vpRanges.getStartRes(); + int oldwidth = vpRanges.getViewportWidth(); + int x = hscroll.getValue(); + int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((x == oldX) && (width == oldwidth)) { - int offy = vscroll.getValue(); - int rowSize = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((offy * rowSize == oldX) && (oldwidth == rowSize)) - { - return; - } - else if (offy > -1) - { - vpRanges.setViewportStartAndWidth(offy * rowSize, rowSize); - } + return; } - else + vpRanges.setViewportStartAndWidth(x, width); + } + else if (evt.getSource() == vscroll) + { + int oldY = vpRanges.getStartSeq(); + int oldheight = vpRanges.getViewportHeight(); + int y = vscroll.getValue(); + int height = getSeqPanel().seqCanvas.getHeight() / av.getCharHeight(); + + // if we're scrolling to the position we're already at, stop + // this prevents infinite recursion of events when the scroll/viewport + // ranges values are the same + if ((y == oldY) && (height == oldheight)) { - // This is only called if file loaded is a jar file that - // was wrapped when saved and user has wrap alignment true - // as preference setting - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - // When updating scrolling to use ViewportChange events, this code - // could not be validated and it is not clear if it is now being - // called. Log warning here in case it is called and unforeseen - // problems occur - Cache.log - .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); - - // scroll to start of panel - vpRanges.setStartRes(0); - vpRanges.setStartSeq(0); - } - }); + return; } + vpRanges.setViewportStartAndHeight(y, height); + } + if (!fastPaint) + { repaint(); } - else + } + + /** + * Responds to a scroll change by setting the start position of the viewport. + * Does + * + * @param evt + */ + protected void adjustScrollingWrapped(AdjustmentEvent evt) + { + if (evt.getSource() == hscroll) { - // horizontal scroll - if (evt.getSource() == hscroll) - { - int x = hscroll.getValue(); - int width = getSeqPanel().seqCanvas.getWidth() / av.getCharWidth(); + return; // no horizontal scroll when wrapped + } + if (evt.getSource() == vscroll) + { + int newY = vscroll.getValue(); - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((x == oldX) && (width == oldwidth)) - { - return; - } - vpRanges.setViewportStartAndWidth(x, width); - } - else if (evt.getSource() == vscroll) + /* + * if we're scrolling to the position we're already at, stop + * this prevents infinite recursion of events when the scroll/viewport + * ranges values are the same + */ + int oldX = vpRanges.getStartRes(); + int oldY = vpRanges.getWrappedScrollPosition(oldX); + if (oldY == newY) { - int y = vscroll.getValue(); - int height = getSeqPanel().seqCanvas.getHeight() - / av.getCharHeight(); - - // if we're scrolling to the position we're already at, stop - // this prevents infinite recursion of events when the scroll/viewport - // ranges values are the same - if ((y == oldY) && (height == oldheight)) - { - return; - } - vpRanges.setViewportStartAndHeight(y, height); + return; } - if (!fastPaint) + if (newY > -1) { - repaint(); + /* + * limit page up/down to one width's worth of positions + */ + int rowSize = vpRanges.getViewportWidth(); + int newX = newY > oldY ? oldX + rowSize : oldX - rowSize; + vpRanges.setViewportStartAndWidth(Math.max(0, newX), rowSize); } } + else + { + // This is only called if file loaded is a jar file that + // was wrapped when saved and user has wrap alignment true + // as preference setting + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + // When updating scrolling to use ViewportChange events, this code + // could not be validated and it is not clear if it is now being + // called. Log warning here in case it is called and unforeseen + // problems occur + Cache.log + .warn("Unexpected path through code: Wrapped jar file opened with wrap alignment set in preferences"); + + // scroll to start of panel + vpRanges.setStartRes(0); + vpRanges.setStartSeq(0); + } + }); + } + repaint(); } /** @@ -877,35 +889,24 @@ public class AlignmentPanel extends GAlignmentPanel implements setScrollValues(vpRanges.getStartRes(), vpRanges.getStartSeq()); } - /* - * Set vertical scroll bar parameters for wrapped panel - * @param res - * the residue to scroll to + /** + * Set vertical scroll bar position, and number of increments, for wrapped + * panel + * + * @param topLeftColumn + * the column position at top left (0..) */ - private void setScrollingForWrappedPanel(int res) + private void setScrollingForWrappedPanel(int topLeftColumn) { - // get the width of the alignment in residues - int maxwidth = av.getAlignment().getWidth(); - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + int scrollPosition = vpRanges.getWrappedScrollPosition(topLeftColumn); + int maxScroll = vpRanges.getWrappedMaxScroll(topLeftColumn); - // get the width of the canvas in residues - int canvasWidth = getSeqPanel().seqCanvas - .getWrappedCanvasWidth(getSeqPanel().seqCanvas.getWidth()); - if (canvasWidth > 0) - { - // position we want to scroll to is number of canvasWidth's to get there - int current = res / canvasWidth; - - // max scroll position: add one because extent is 1 and scrollbar value - // can only be set to at most max - extent - int max = maxwidth / canvasWidth + 1; - vscroll.setUnitIncrement(1); - vscroll.setValues(current, 1, 0, max); - } + /* + * a scrollbar's value can be set to at most (maximum-extent) + * so we add extent (1) to the maxScroll value + */ + vscroll.setUnitIncrement(1); + vscroll.setValues(scrollPosition, 1, 0, maxScroll + 1); } /** @@ -1809,7 +1810,7 @@ public class AlignmentPanel extends GAlignmentPanel implements * @param verticalOffset * the number of visible sequences to show above the mapped region */ - public void scrollToCentre(SearchResultsI sr, int verticalOffset) + protected void scrollToCentre(SearchResultsI sr, int verticalOffset) { /* * To avoid jumpy vertical scrolling (if some sequences are gapped or not diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index f81455e..6fc5fad 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -36,7 +36,7 @@ import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; -import java.util.Iterator; +import java.util.ArrayList; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; @@ -244,16 +244,16 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements HiddenColumns oldHidden = av .getAnnotationColumnSelectionState() .getOldHiddenColumns(); - if (oldHidden != null && oldHidden.getHiddenRegions() != null - && !oldHidden.getHiddenRegions().isEmpty()) + if (oldHidden != null) { - for (Iterator itr = oldHidden.getHiddenRegions() - .iterator(); itr.hasNext();) + ArrayList regions = oldHidden.getHiddenColumnsCopy(); + for (int[] positions : regions) { - int positions[] = itr.next(); av.hideColumns(positions[0], positions[1]); } } + // TODO not clear why we need to hide all the columns (above) if we are + // going to copy the hidden columns over wholesale anyway av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); @@ -728,7 +728,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements private static final String FILTER_BY_ANN_CACHE_KEY = "CACHE.SELECT_FILTER_BY_ANNOT"; - public JvCacheableInputBox searchBox = new JvCacheableInputBox( + public JvCacheableInputBox searchBox = new JvCacheableInputBox<>( FILTER_BY_ANN_CACHE_KEY); public SearchPanel(AnnotationColumnChooser aColChooser) diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 8ca1a4e..e16867a 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -52,7 +52,6 @@ import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.regex.Pattern; import javax.swing.JCheckBoxMenuItem; @@ -953,13 +952,12 @@ public class AnnotationLabels extends JPanel implements MouseListener, } int[] alignmentStartEnd = new int[] { 0, ds.getWidth() - 1 }; - List hiddenCols = av.getAlignment().getHiddenColumns() - .getHiddenRegions(); - if (hiddenCols != null) + if (av.hasHiddenColumns()) { - alignmentStartEnd = av.getAlignment().getVisibleStartAndEndIndex( - hiddenCols); + alignmentStartEnd = av.getAlignment().getHiddenColumns() + .getVisibleStartAndEndIndex(av.getAlignment().getWidth()); } + String output = new FormatAdapter().formatSequences(FileFormat.Fasta, seqs, omitHidden, alignmentStartEnd); @@ -967,14 +965,11 @@ public class AnnotationLabels extends JPanel implements MouseListener, .setContents(new StringSelection(output), Desktop.instance); ArrayList hiddenColumns = null; + if (av.hasHiddenColumns()) { - hiddenColumns = new ArrayList(); - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenRegions()) - { - hiddenColumns.add(new int[] { region[0], region[1] }); - } + hiddenColumns = av.getAlignment().getHiddenColumns() + .getHiddenColumnsCopy(); } Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index 452f002..61099c3 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -31,6 +31,7 @@ import jalview.schemes.ResidueProperties; import jalview.util.Comparison; import jalview.util.MessageManager; import jalview.viewmodel.ViewportListenerI; +import jalview.viewmodel.ViewportRanges; import java.awt.AlphaComposite; import java.awt.Color; @@ -451,7 +452,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, * the selection list (read-only view) is in selection order, not * column order; make a copy so we can sort it */ - List selected = new ArrayList(viscols.getSelected()); + List selected = new ArrayList<>(viscols.getSelected()); Collections.sort(selected); for (int index : selected) { @@ -981,13 +982,15 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, repaint(); return; } + + int sr = av.getRanges().getStartRes(); + int er = av.getRanges().getEndRes() + 1; + int transX = 0; + long stime = System.currentTimeMillis(); gg.copyArea(0, 0, imgWidth, getHeight(), -horizontal * av.getCharWidth(), 0); long mtime = System.currentTimeMillis(); - int sr = av.getRanges().getStartRes(); - int er = av.getRanges().getEndRes() + 1; - int transX = 0; if (horizontal > 0) // scrollbar pulled right, image to the left { @@ -1168,8 +1171,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, public void propertyChange(PropertyChangeEvent evt) { // Respond to viewport range changes (e.g. alignment panel was scrolled) - if (evt.getPropertyName().equals("startres") - || evt.getPropertyName().equals("endres")) + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (evt.getPropertyName().equals(ViewportRanges.STARTRES)) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index 5594e1a..a1c1bff 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -178,9 +178,10 @@ public class FeatureColourChooser extends JalviewDialog // initialise threshold slider and selector threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2); slider.setEnabled(true); + slider.setValue((int) (cs.getThreshold() * scaleFactor)); thresholdValue.setEnabled(true); threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); - + threshline.value = cs.getThreshold(); } adjusting = false; diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index ac56590..6f6bc02 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -271,7 +271,8 @@ public class FeatureRenderer extends highlight.addResult(sequences.get(0), sf.getBegin(), sf.getEnd()); - alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(highlight); + alignPanel.getSeqPanel().seqCanvas.highlightSearchResults( + highlight, false); } FeatureColourI col = getFeatureStyle(name.getText()); @@ -448,8 +449,8 @@ public class FeatureRenderer extends * (to ensure integrity of SequenceFeatures data store) */ sequences.get(0).deleteFeature(sf); - SequenceFeature newSf = new SequenceFeature(sf, newBegin, newEnd, - enteredGroup, sf.getScore()); + SequenceFeature newSf = new SequenceFeature(sf, enteredType, + newBegin, newEnd, enteredGroup, sf.getScore()); sf.setDescription(enteredDescription); ffile.parseDescriptionHTML(newSf, false); // amend features dialog only updates one sequence at a time diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index 5ce36cb..052c527 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -155,7 +155,10 @@ public class IdCanvas extends JPanel implements ViewportListenerI */ public void fastPaint(int vertical) { - if (gg == null) + /* + * for now, not attempting fast paint of wrapped ids... + */ + if (gg == null || av.getWrapAlignment()) { repaint(); @@ -283,143 +286,158 @@ public class IdCanvas extends JPanel implements ViewportListenerI Color currentColor = Color.white; Color currentTextColor = Color.black; - final boolean doHiddenCheck = av.isDisplayReferenceSeq() - || av.hasHiddenRows(), hiddenRows = av.hasHiddenRows(); + boolean hasHiddenRows = av.hasHiddenRows(); if (av.getWrapAlignment()) { - int maxwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getHeight(); + drawIdsWrapped(starty, hasHiddenRows); + return; + } - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; - } + // No need to hang on to labels if we're not wrapped + labels = null; - int annotationHeight = 0; + // Now draw the id strings + int panelWidth = getWidth(); + int xPos = 0; + + SequenceI sequence; + // Now draw the id strings + for (int i = starty; i <= endy; i++) + { + sequence = av.getAlignment().getSequenceAt(i); - if (av.isShowAnnotation()) + if (sequence == null) { - if (ap == null) - { - ap = new AnnotationPanel(av); - } + continue; + } - annotationHeight = ap.adjustPanelHeight(); - if (labels == null) - { - labels = new AnnotationLabels(av); - } + if (hasHiddenRows || av.isDisplayReferenceSeq()) + { + setHiddenFont(sequence); } - int hgap = av.getCharHeight(); - if (av.getScaleAboveWrapped()) + // Selected sequence colours + if ((searchResults != null) && searchResults.contains(sequence)) { - hgap += av.getCharHeight(); + currentColor = Color.black; + currentTextColor = Color.white; } + else if ((av.getSelectionGroup() != null) + && av.getSelectionGroup().getSequences(null) + .contains(sequence)) + { + currentColor = Color.lightGray; + currentTextColor = Color.black; + } + else + { + currentColor = av.getSequenceColour(sequence); + currentTextColor = Color.black; + } + + gg.setColor(currentColor); + + gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(), + av.getCharHeight()); - int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight; + gg.setColor(currentTextColor); - int rowSize = av.getRanges().getEndRes() - - av.getRanges().getStartRes(); + String string = sequence.getDisplayId(av.getShowJVSuffix()); - // Draw the rest of the panels - for (int ypos = hgap, row = av.getRanges().getStartRes(); (ypos <= getHeight()) - && (row < maxwidth); ypos += cHeight, row += rowSize) + if (av.isRightAlignIds()) { - for (int i = starty; i < alheight; i++) - { - SequenceI s = av.getAlignment().getSequenceAt(i); - if (doHiddenCheck) - { - setHiddenFont(s); - } - else - { - gg.setFont(getIdfont()); - } - - drawIdString(gg, hiddenRows, s, i, 0, ypos); - } + xPos = panelWidth - fm.stringWidth(string) - 4; + } - if (labels != null && av.isShowAnnotation()) - { - gg.translate(0, ypos + (alheight * av.getCharHeight())); - labels.drawComponent(gg, getWidth()); - gg.translate(0, -ypos - (alheight * av.getCharHeight())); - } + gg.drawString(string, xPos, + (((i - starty) * av.getCharHeight()) + av.getCharHeight()) + - (av.getCharHeight() / 5)); + + if (hasHiddenRows) + { + drawMarker(i, starty, 0); } } - else - { - // No need to hang on to labels if we're not wrapped - labels = null; + } - // Now draw the id strings - int panelWidth = getWidth(); - int xPos = 0; + /** + * Draws sequence ids in wrapped mode + * + * @param starty + * @param hasHiddenRows + */ + protected void drawIdsWrapped(int starty, boolean hasHiddenRows) + { + int maxwidth = av.getAlignment().getWidth(); + int alheight = av.getAlignment().getHeight(); - SequenceI sequence; - // Now draw the id strings - for (int i = starty; i <= endy; i++) - { - sequence = av.getAlignment().getSequenceAt(i); + if (av.hasHiddenColumns()) + { + maxwidth = av.getAlignment().getHiddenColumns() + .findColumnPosition(maxwidth) - 1; + } - if (sequence == null) - { - continue; - } + int annotationHeight = 0; - if (doHiddenCheck) - { - setHiddenFont(sequence); - } + if (av.isShowAnnotation()) + { + if (ap == null) + { + ap = new AnnotationPanel(av); + } - // Selected sequence colours - if ((searchResults != null) && searchResults.contains(sequence)) - { - currentColor = Color.black; - currentTextColor = Color.white; - } - else if ((av.getSelectionGroup() != null) - && av.getSelectionGroup().getSequences(null) - .contains(sequence)) - { - currentColor = Color.lightGray; - currentTextColor = Color.black; - } - else - { - currentColor = av.getSequenceColour(sequence); - currentTextColor = Color.black; - } + annotationHeight = ap.adjustPanelHeight(); + if (labels == null) + { + labels = new AnnotationLabels(av); + } + } - gg.setColor(currentColor); + int hgap = av.getCharHeight(); + if (av.getScaleAboveWrapped()) + { + hgap += av.getCharHeight(); + } - gg.fillRect(0, (i - starty) * av.getCharHeight(), getWidth(), - av.getCharHeight()); + int cHeight = alheight * av.getCharHeight() + hgap + annotationHeight; - gg.setColor(currentTextColor); + ViewportRanges ranges = av.getRanges(); - String string = sequence.getDisplayId(av.getShowJVSuffix()); + int rowSize = ranges.getViewportWidth(); - if (av.isRightAlignIds()) + /* + * draw repeating sequence ids until out of sequence data or + * out of visible space, whichever comes first + */ + int ypos = hgap; + int row = ranges.getStartRes(); + while ((ypos <= getHeight()) && (row < maxwidth)) + { + for (int i = starty; i < alheight; i++) + { + SequenceI s = av.getAlignment().getSequenceAt(i); + if (hasHiddenRows || av.isDisplayReferenceSeq()) { - xPos = panelWidth - fm.stringWidth(string) - 4; + setHiddenFont(s); } - - gg.drawString(string, xPos, - (((i - starty) * av.getCharHeight()) + av.getCharHeight()) - - (av.getCharHeight() / 5)); - - if (hiddenRows) + else { - drawMarker(i, starty, 0); + gg.setFont(getIdfont()); } + drawIdString(gg, hasHiddenRows, s, i, 0, ypos); + } + + if (labels != null && av.isShowAnnotation()) + { + gg.translate(0, ypos + (alheight * av.getCharHeight())); + labels.drawComponent(gg, getWidth()); + gg.translate(0, -ypos - (alheight * av.getCharHeight())); } + ypos += cHeight; + row += rowSize; } } @@ -520,12 +538,25 @@ public class IdCanvas extends JPanel implements ViewportListenerI this.idfont = idfont; } + /** + * Respond to viewport range changes (e.g. alignment panel was scrolled). Both + * scrolling and resizing change viewport ranges. Scrolling changes both start + * and end points, but resize only changes end values. Here we only want to + * fastpaint on a scroll, with resize using a normal paint, so scroll events + * are identified as changes to the horizontal or vertical start value. + *

        + * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll + * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is + * provided, but it generates a change of "startres" which does require an + * update here. + */ @Override public void propertyChange(PropertyChangeEvent evt) { - // Respond to viewport range changes (e.g. alignment panel was scrolled) - if (evt.getPropertyName().equals("startseq") - || evt.getPropertyName().equals("endseq")) + String propertyName = evt.getPropertyName(); + if (propertyName.equals(ViewportRanges.STARTSEQ) + || (av.getWrapAlignment() && propertyName + .equals(ViewportRanges.STARTRES))) { fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); } diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index 29bb522..099d76a 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -138,7 +138,7 @@ public class IdPanel extends JPanel implements MouseListener, } lastid = seq; - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false); } /** @@ -313,7 +313,7 @@ public class IdPanel extends JPanel implements MouseListener, av.isSelectionGroupChanged(true); - alignPanel.paintAlignment(true); + alignPanel.paintAlignment(false); } /** diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index 358cca6..8168ce1 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -181,13 +181,13 @@ public class Jalview2XML * Map of reconstructed AlignFrame objects that appear to have come from * SplitFrame objects (have a dna/protein complement view). */ - private Map splitFrameCandidates = new HashMap(); + private Map splitFrameCandidates = new HashMap<>(); /* * Map from displayed rna structure models to their saved session state jar * entry names */ - private Map rnaSessions = new HashMap(); + private Map rnaSessions = new HashMap<>(); /** * create/return unique hash string for sq @@ -248,19 +248,19 @@ public class Jalview2XML { if (seqsToIds == null) { - seqsToIds = new IdentityHashMap(); + seqsToIds = new IdentityHashMap<>(); } if (seqRefIds == null) { - seqRefIds = new HashMap(); + seqRefIds = new HashMap<>(); } if (incompleteSeqs == null) { - incompleteSeqs = new HashMap(); + incompleteSeqs = new HashMap<>(); } if (frefedSequence == null) { - frefedSequence = new ArrayList(); + frefedSequence = new ArrayList<>(); } } @@ -459,9 +459,9 @@ public class Jalview2XML * This maintains a map of viewports, the key being the seqSetId. Important to * set historyItem and redoList for multiple views */ - Map viewportsAdded = new HashMap(); + Map viewportsAdded = new HashMap<>(); - Map annotationIds = new HashMap(); + Map annotationIds = new HashMap<>(); String uniqueSetSuffix = ""; @@ -537,7 +537,7 @@ public class Jalview2XML */ private void saveAllFrames(List frames, JarOutputStream jout) { - Hashtable dsses = new Hashtable(); + Hashtable dsses = new Hashtable<>(); /* * ensure cached data is clear before starting @@ -552,8 +552,8 @@ public class Jalview2XML // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS // ////////////////////////////////////////////////// - List shortNames = new ArrayList(); - List viewIds = new ArrayList(); + List shortNames = new ArrayList<>(); + List viewIds = new ArrayList<>(); // REVERSE ORDER for (int i = frames.size() - 1; i > -1; i--) @@ -663,7 +663,7 @@ public class Jalview2XML { FileOutputStream fos = new FileOutputStream(jarFile); JarOutputStream jout = new JarOutputStream(fos); - List frames = new ArrayList(); + List frames = new ArrayList<>(); // resolve splitframes if (af.getViewport().getCodingComplement() != null) @@ -749,12 +749,12 @@ public class Jalview2XML { if (viewIds == null) { - viewIds = new ArrayList(); + viewIds = new ArrayList<>(); } initSeqRefs(); - List userColours = new ArrayList(); + List userColours = new ArrayList<>(); AlignViewport av = ap.av; ViewportRanges vpRanges = av.getRanges(); @@ -808,9 +808,9 @@ public class Jalview2XML } JSeq jseq; - Set calcIdSet = new HashSet(); + Set calcIdSet = new HashSet<>(); // record the set of vamsas sequence XML POJO we create. - HashMap vamsasSetIds = new HashMap(); + HashMap vamsasSetIds = new HashMap<>(); // SAVE SEQUENCES for (final SequenceI jds : rjal.getSequences()) { @@ -982,7 +982,7 @@ public class Jalview2XML pdb.setFile(matchedFile); // entry.getFile()); if (pdbfiles == null) { - pdbfiles = new ArrayList(); + pdbfiles = new ArrayList<>(); } if (!pdbfiles.contains(pdbId)) @@ -1131,7 +1131,7 @@ public class Jalview2XML /** * store forward refs from an annotationRow to any groups */ - IdentityHashMap groupRefs = new IdentityHashMap(); + IdentityHashMap groupRefs = new IdentityHashMap<>(); if (storeDS) { for (SequenceI sq : jal.getSequences()) @@ -1345,7 +1345,7 @@ public class Jalview2XML .getFeatureRenderer().getRenderOrder() .toArray(new String[0]); - Vector settingsAdded = new Vector(); + Vector settingsAdded = new Vector<>(); if (renderOrder != null) { for (String featureType : renderOrder) @@ -1388,7 +1388,7 @@ public class Jalview2XML // is groups actually supposed to be a map here ? Iterator en = ap.getSeqPanel().seqCanvas .getFeatureRenderer().getFeatureGroups().iterator(); - Vector groupsAdded = new Vector(); + Vector groupsAdded = new Vector<>(); while (en.hasNext()) { String grp = en.next(); @@ -1411,17 +1411,16 @@ public class Jalview2XML { jalview.datamodel.HiddenColumns hidden = av.getAlignment() .getHiddenColumns(); - if (hidden == null || hidden.getHiddenRegions() == null) + if (hidden == null) { warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this."); } else { - for (int c = 0; c < hidden.getHiddenRegions() - .size(); c++) + ArrayList hiddenRegions = hidden + .getHiddenColumnsCopy(); + for (int[] region : hiddenRegions) { - int[] region = hidden.getHiddenRegions() - .get(c); HiddenColumns hc = new HiddenColumns(); hc.setStart(region[0]); hc.setEnd(region[1]); @@ -2286,7 +2285,7 @@ public class Jalview2XML try { // create list to store references for any new Jmol viewers created - newStructureViewers = new Vector(); + newStructureViewers = new Vector<>(); // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING // Workaround is to make sure caller implements the JarInputStreamProvider // interface @@ -2379,8 +2378,8 @@ public class Jalview2XML initSeqRefs(); } AlignFrame af = null, _af = null; - IdentityHashMap importedDatasets = new IdentityHashMap(); - Map gatherToThisFrame = new HashMap(); + IdentityHashMap importedDatasets = new IdentityHashMap<>(); + Map gatherToThisFrame = new HashMap<>(); final String file = jprovider.getFilename(); try { @@ -2521,9 +2520,9 @@ public class Jalview2XML */ protected void restoreSplitFrames() { - List gatherTo = new ArrayList(); - List addedToSplitFrames = new ArrayList(); - Map dna = new HashMap(); + List gatherTo = new ArrayList<>(); + List addedToSplitFrames = new ArrayList<>(); + Map dna = new HashMap<>(); /* * Identify the DNA alignments @@ -2665,7 +2664,7 @@ public class Jalview2XML errorMessage = null; } - Map alreadyLoadedPDB = new HashMap(); + Map alreadyLoadedPDB = new HashMap<>(); /** * when set, local views will be updated from view stored in JalviewXML @@ -2836,7 +2835,7 @@ public class Jalview2XML List hiddenSeqs = null; - List tmpseqs = new ArrayList(); + List tmpseqs = new ArrayList<>(); boolean multipleView = false; SequenceI referenceseqForView = null; @@ -2904,7 +2903,7 @@ public class Jalview2XML { if (hiddenSeqs == null) { - hiddenSeqs = new ArrayList(); + hiddenSeqs = new ArrayList<>(); } hiddenSeqs.add(tmpSeq); @@ -3111,12 +3110,12 @@ public class Jalview2XML // //////////////////////////////// // LOAD ANNOTATIONS - List autoAlan = new ArrayList(); + List autoAlan = new ArrayList<>(); /* * store any annotations which forward reference a group's ID */ - Map> groupAnnotRefs = new Hashtable>(); + Map> groupAnnotRefs = new Hashtable<>(); if (vamsasSet.getAnnotationCount() > 0) { @@ -3271,7 +3270,7 @@ public class Jalview2XML .get(an[i].getGroupRef()); if (aal == null) { - aal = new ArrayList(); + aal = new ArrayList<>(); groupAnnotRefs.put(an[i].getGroupRef(), aal); } aal.add(jaa); @@ -3361,7 +3360,7 @@ public class Jalview2XML } int pidThreshold = jGroup.getPidThreshold(); - Vector seqs = new Vector(); + Vector seqs = new Vector<>(); for (int s = 0; s < jGroup.getSeqCount(); s++) { @@ -3754,7 +3753,7 @@ public class Jalview2XML * Run through all PDB ids on the alignment, and collect mappings between * distinct view ids and all sequences referring to that view. */ - Map structureViewers = new LinkedHashMap(); + Map structureViewers = new LinkedHashMap<>(); for (int i = 0; i < jseqs.length; i++) { @@ -3952,8 +3951,8 @@ public class Jalview2XML Set> fileData = data.getFileData() .entrySet(); - List pdbs = new ArrayList(); - List allseqs = new ArrayList(); + List pdbs = new ArrayList<>(); + List allseqs = new ArrayList<>(); for (Entry pdb : fileData) { String filePath = pdb.getValue().getFilePath(); @@ -4009,9 +4008,9 @@ public class Jalview2XML getViewerJarEntryName(svattrib.getViewId())); } - List pdbfilenames = new ArrayList(); - List seqmaps = new ArrayList(); - List pdbids = new ArrayList(); + List pdbfilenames = new ArrayList<>(); + List seqmaps = new ArrayList<>(); + List pdbids = new ArrayList<>(); StringBuilder newFileLoc = new StringBuilder(64); int cp = 0, ncp, ecp; Map oldFiles = svattrib.getFileData(); @@ -4572,8 +4571,8 @@ public class Jalview2XML af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed()); String[] renderOrder = new String[jms.getFeatureSettings() .getSettingCount()]; - Map featureColours = new Hashtable(); - Map featureOrder = new Hashtable(); + Map featureColours = new Hashtable<>(); + Map featureOrder = new Hashtable<>(); for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++) { @@ -4632,7 +4631,7 @@ public class Jalview2XML fdi.setVisible(setting.getType()); } } - Map fgtable = new Hashtable(); + Map fgtable = new Hashtable<>(); for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++) { Group grp = jms.getFeatureSettings().getGroup(gs); @@ -4825,7 +4824,7 @@ public class Jalview2XML String[] magicNames = new String[] { "Consensus", "Quality", "Conservation" }; JvAnnotRow nullAnnot = new JvAnnotRow(-1, null); - Hashtable visan = new Hashtable(); + Hashtable visan = new Hashtable<>(); for (String nm : magicNames) { visan.put(nm, nullAnnot); @@ -4837,11 +4836,11 @@ public class Jalview2XML + auan.template.getCalcId()), auan); } int hSize = al.getAlignmentAnnotation().length; - List reorder = new ArrayList(); + List reorder = new ArrayList<>(); // work through any autoCalculated annotation already on the view // removing it if it should be placed in a different location on the // annotation panel. - List remains = new ArrayList(visan.keySet()); + List remains = new ArrayList<>(visan.keySet()); for (int h = 0; h < hSize; h++) { jalview.datamodel.AlignmentAnnotation jalan = al @@ -5169,7 +5168,7 @@ public class Jalview2XML { if (datasetIds == null) { - datasetIds = new Hashtable(); + datasetIds = new Hashtable<>(); return null; } if (datasetIds.containsKey(datasetId)) @@ -5183,7 +5182,7 @@ public class Jalview2XML { if (datasetIds == null) { - datasetIds = new Hashtable(); + datasetIds = new Hashtable<>(); } datasetIds.put(datasetId, dataset); } @@ -5206,7 +5205,7 @@ public class Jalview2XML // make a new datasetId and record it if (dataset2Ids == null) { - dataset2Ids = new IdentityHashMap(); + dataset2Ids = new IdentityHashMap<>(); } else { @@ -5485,11 +5484,11 @@ public class Jalview2XML // register sequence object so the XML parser can recover it. if (seqRefIds == null) { - seqRefIds = new HashMap(); + seqRefIds = new HashMap<>(); } if (seqsToIds == null) { - seqsToIds = new IdentityHashMap(); + seqsToIds = new IdentityHashMap<>(); } seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj); seqsToIds.put((SequenceI) jvobj, id); diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java index 6f9fbbf..27f9c3f 100644 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@ -53,6 +53,8 @@ public class OverviewCanvas extends JComponent private OverviewDimensions od; + private OverviewRenderer or = null; + private AlignViewportI av; public OverviewCanvas(OverviewDimensions overviewDims, @@ -89,6 +91,10 @@ public class OverviewCanvas extends JComponent if (updaterunning) { restart = true; + if (or != null) + { + or.setRedraw(true); + } } else { @@ -120,7 +126,7 @@ public class OverviewCanvas extends JComponent setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); - OverviewRenderer or = new OverviewRenderer(sr, fr, od); + or = new OverviewRenderer(sr, fr, od); 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 3fa674e..7a4456e 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -107,13 +107,11 @@ public class OverviewPanel extends JPanel implements Runnable, @Override public void mouseDragged(MouseEvent evt) { - if (!SwingUtilities.isRightMouseButton(evt) - && !av.getWrapAlignment()) + if (!SwingUtilities.isRightMouseButton(evt)) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av.getAlignment() .getHiddenColumns()); - } } }); @@ -130,7 +128,8 @@ public class OverviewPanel extends JPanel implements Runnable, showPopupMenu(evt); } } - else if (!av.getWrapAlignment()) + else + // if (!av.getWrapAlignment()) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av.getAlignment() @@ -148,7 +147,6 @@ public class OverviewPanel extends JPanel implements Runnable, } }); - updateOverviewImage(); } @@ -206,6 +204,14 @@ public class OverviewPanel extends JPanel implements Runnable, */ public void updateOverviewImage() { + if (oviewCanvas == null) + { + /* + * panel has been disposed + */ + return; + } + if ((getWidth() > 0) && (getHeight() > 0)) { od.setWidth(getWidth()); @@ -222,7 +228,6 @@ public class OverviewPanel extends JPanel implements Runnable, Thread thread = new Thread(this); thread.start(); repaint(); - } @Override @@ -252,4 +257,21 @@ public class OverviewPanel extends JPanel implements Runnable, { setBoxPosition(); } + + /** + * Removes this object as a property change listener, and nulls references + */ + protected void dispose() + { + try + { + av.getRanges().removePropertyChangeListener(this); + } finally + { + av = null; + oviewCanvas = null; + ap = null; + od = null; + } + } } diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index a8e2f7e..c78021c 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1944,11 +1944,18 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener } } - if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures(seqs, - features, true, ap)) + /* + * an entirely gapped region will generate empty lists of sequence / features + */ + if (!seqs.isEmpty()) { - ap.alignFrame.setShowSeqFeatures(true); - ap.highlightSearchResults(null); + if (ap.getSeqPanel().seqCanvas.getFeatureRenderer().amendFeatures( + seqs, features, true, ap)) + { + ap.alignFrame.setShowSeqFeatures(true); + ap.av.setSearchResults(null); // clear highlighting + ap.repaint(); // draw new/amended features + } } } diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index cb19539..2302ebe 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -29,6 +29,7 @@ import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.ViewportListenerI; +import jalview.viewmodel.ViewportRanges; import java.awt.Color; import java.awt.FontMetrics; @@ -168,10 +169,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, av.showColumn(reveal[0]); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -188,10 +185,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, av.showAllHiddenColumns(); reveal = null; ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -217,10 +210,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener, } ap.paintAlignment(true); - if (ap.overviewPanel != null) - { - ap.overviewPanel.updateOverviewImage(); - } av.sendSelection(); } }); @@ -401,24 +390,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener, int res = (evt.getX() / av.getCharWidth()) + av.getRanges().getStartRes(); + reveal = av.getAlignment().getHiddenColumns() + .getRegionWithEdgeAtRes(res); + res = av.getAlignment().getHiddenColumns().adjustForHiddenColumns(res); - if (av.getAlignment().getHiddenColumns().getHiddenRegions() != null) - { - for (int[] region : av.getAlignment().getHiddenColumns() - .getHiddenRegions()) - { - if (res + 1 == region[0] || res - 1 == region[1]) - { - reveal = region; - ToolTipManager.sharedInstance().registerComponent(this); - this.setToolTipText(MessageManager - .getString("label.reveal_hidden_columns")); - repaint(); - return; - } - } - } + ToolTipManager.sharedInstance().registerComponent(this); + this.setToolTipText( + MessageManager.getString("label.reveal_hidden_columns")); + repaint(); } /** @@ -430,8 +410,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener, @Override public void paintComponent(Graphics g) { - drawScale(g, av.getRanges().getStartRes(), av.getRanges().getEndRes(), - getWidth(), getHeight()); + /* + * shouldn't get called in wrapped mode as the scale above is + * drawn instead by SeqCanvas.drawNorthScale + */ + if (!av.getWrapAlignment()) + { + drawScale(g, av.getRanges().getStartRes(), + av.getRanges().getEndRes(), getWidth(), getHeight()); + } } // scalewidth will normally be screenwidth, @@ -499,13 +486,12 @@ public class ScalePanel extends JPanel implements MouseMotionListener, gg.setColor(Color.blue); int res; - if (av.getShowHiddenMarkers() && hidden.getHiddenRegions() != null) + if (av.getShowHiddenMarkers()) { - for (int i = 0; i < hidden.getHiddenRegions() - .size(); i++) + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) { - res = hidden.findHiddenRegionPosition(i) - - startx; + res = pos - startx; if (res < 0 || res > widthx) { @@ -556,7 +542,16 @@ public class ScalePanel extends JPanel implements MouseMotionListener, public void propertyChange(PropertyChangeEvent evt) { // Respond to viewport change events (e.g. alignment panel was scrolled) - repaint(); + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (evt.getPropertyName().equals(ViewportRanges.STARTRES)) + { + // scroll event, repaint panel + repaint(); + } } } diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 8707e9a..0e31246 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -52,6 +52,8 @@ import javax.swing.JComponent; */ public class SeqCanvas extends JComponent implements ViewportListenerI { + private static String ZEROS = "0000000000"; + final FeatureRenderer fr; final SequenceRenderer sr; @@ -68,9 +70,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI boolean fastPaint = false; - int LABEL_WEST; + int labelWidthWest; - int LABEL_EAST; + int labelWidthEast; int cursorX = 0; @@ -206,7 +208,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (value != -1) { - int x = LABEL_WEST - fm.stringWidth(String.valueOf(value)) + int x = labelWidthWest - fm.stringWidth(String.valueOf(value)) - charWidth / 2; g.drawString(value + "", x, (ypos + (i * charHeight)) - (charHeight / 5)); @@ -286,8 +288,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI fastpainting = true; fastPaint = true; updateViewport(); - gg.copyArea(horizontal * charWidth, vertical * charHeight, imgWidth, - imgHeight, -horizontal * charWidth, -vertical * charHeight); ViewportRanges ranges = av.getRanges(); int startRes = ranges.getStartRes(); @@ -297,6 +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); + if (horizontal > 0) // scrollbar pulled right, image to the left { transX = (endRes - startRes - horizontal) * charWidth; @@ -411,57 +414,63 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } /** - * DOCUMENT ME! + * Returns the visible width of the canvas in residues, after allowing for + * East or West scales (if shown) * - * @param cwidth - * DOCUMENT ME! + * @param canvasWidth + * the width in pixels (possibly including scales) * - * @return DOCUMENT ME! + * @return */ - public int getWrappedCanvasWidth(int cwidth) + public int getWrappedCanvasWidth(int canvasWidth) { FontMetrics fm = getFontMetrics(av.getFont()); - LABEL_EAST = 0; - LABEL_WEST = 0; + labelWidthEast = 0; + labelWidthWest = 0; if (av.getScaleRightWrapped()) { - LABEL_EAST = fm.stringWidth(getMask()); + labelWidthEast = getLabelWidth(fm); } if (av.getScaleLeftWrapped()) { - LABEL_WEST = fm.stringWidth(getMask()); + labelWidthWest = labelWidthEast > 0 ? labelWidthEast + : getLabelWidth(fm); } - return (cwidth - LABEL_EAST - LABEL_WEST) / charWidth; + return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; } /** - * Generates a string of zeroes. + * Returns a pixel width suitable for showing the largest sequence coordinate + * (end position) in the alignment. Returns 2 plus the number of decimal + * digits to be shown (3 for 1-10, 4 for 11-99 etc). * - * @return String + * @param fm + * @return */ - String getMask() + protected int getLabelWidth(FontMetrics fm) { - String mask = "00"; + /* + * find the biggest sequence end position we need to show + * (note this is not necessarily the sequence length) + */ int maxWidth = 0; - int tmp; - for (int i = 0; i < av.getAlignment().getHeight(); i++) + AlignmentI alignment = av.getAlignment(); + for (int i = 0; i < alignment.getHeight(); i++) { - tmp = av.getAlignment().getSequenceAt(i).getEnd(); - if (tmp > maxWidth) - { - maxWidth = tmp; - } + maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } + int length = 2; for (int i = maxWidth; i > 0; i /= 10) { - mask += "0"; + length++; } - return mask; + + return fm.stringWidth(ZEROS.substring(0, length)); } /** @@ -482,17 +491,15 @@ public class SeqCanvas extends JComponent implements ViewportListenerI updateViewport(); AlignmentI al = av.getAlignment(); - FontMetrics fm = getFontMetrics(av.getFont()); - - if (av.getScaleRightWrapped()) + int labelWidth = 0; + if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) { - LABEL_EAST = fm.stringWidth(getMask()); + FontMetrics fm = getFontMetrics(av.getFont()); + labelWidth = getLabelWidth(fm); } - if (av.getScaleLeftWrapped()) - { - LABEL_WEST = fm.stringWidth(getMask()); - } + labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; + labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; int hgap = charHeight; if (av.getScaleAboveWrapped()) @@ -500,23 +507,25 @@ public class SeqCanvas extends JComponent implements ViewportListenerI hgap += charHeight; } - int cWidth = (canvasWidth - LABEL_EAST - LABEL_WEST) / charWidth; + int cWidth = (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; int cHeight = av.getAlignment().getHeight() * charHeight; av.setWrappedWidth(cWidth); - av.getRanges().setEndRes(av.getRanges().getStartRes() + cWidth - 1); + av.getRanges().setViewportStartAndWidth(startRes, cWidth); int endx; int ypos = hgap; - int maxwidth = av.getAlignment().getWidth() - 1; + int maxwidth = av.getAlignment().getWidth(); if (av.hasHiddenColumns()) { maxwidth = av.getAlignment().getHiddenColumns() - .findColumnPosition(maxwidth) - 1; + .findColumnPosition(maxwidth); } + int annotationHeight = getAnnotationHeight(); + while ((ypos <= canvasHeight) && (startRes < maxwidth)) { endx = startRes + cWidth - 1; @@ -536,12 +545,12 @@ public class SeqCanvas extends JComponent implements ViewportListenerI if (av.getScaleRightWrapped()) { - g.translate(canvasWidth - LABEL_EAST, 0); + g.translate(canvasWidth - labelWidthEast, 0); drawEastScale(g, startRes, endx, ypos); - g.translate(-(canvasWidth - LABEL_EAST), 0); + g.translate(-(canvasWidth - labelWidthEast), 0); } - g.translate(LABEL_WEST, 0); + g.translate(labelWidthWest, 0); if (av.getScaleAboveWrapped()) { @@ -553,9 +562,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.setColor(Color.blue); int res; HiddenColumns hidden = av.getAlignment().getHiddenColumns(); - for (int i = 0; i < hidden.getHiddenRegions().size(); i++) + List positions = hidden.findHiddenRegionPositions(); + for (int pos : positions) { - res = hidden.findHiddenRegionPosition(i) - startRes; + res = pos - startRes; if (res < 0 || res > endx - startRes) { @@ -601,9 +611,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI g.translate(0, -cHeight - ypos - 3); } g.setClip(clip); - g.translate(-LABEL_WEST, 0); + g.translate(-labelWidthWest, 0); - ypos += cHeight + getAnnotationHeight() + hgap; + ypos += cHeight + annotationHeight + hgap; startRes += cWidth; } @@ -654,15 +664,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } else { - List regions = av.getAlignment().getHiddenColumns() - .getHiddenRegions(); - int screenY = 0; final int screenYMax = endRes - startRes; int blockStart = startRes; int blockEnd = endRes; - for (int[] region : regions) + for (int[] region : av.getAlignment().getHiddenColumns() + .getHiddenColumnsCopy()) { int hideStart = region[0]; int hideEnd = region[1]; @@ -989,30 +997,36 @@ public class SeqCanvas extends JComponent implements ViewportListenerI /** * Highlights search results in the visible region by rendering as white text - * on a black background. Any previous highlighting is removed. + * on a black background. Any previous highlighting is removed. Answers true + * if any highlight was left on the visible alignment (so status bar should be + * set to match), else false. + *

        + * Currently fastPaint is not implemented for wrapped alignments. If a wrapped + * alignment had to be scrolled to show the highlighted region, then it should + * be fully redrawn, otherwise a fast paint can be performed. This argument + * could be removed if fast paint of scrolled wrapped alignment is coded in + * future (JAL-2609). * * @param results + * @param noFastPaint + * @return */ - public void highlightSearchResults(SearchResultsI results) + public boolean highlightSearchResults(SearchResultsI results, + boolean noFastPaint) { - updateViewport(); - - /* - * for now, don't attempt fastpaint if wrapped format - */ - if (av.getWrapAlignment()) + if (fastpainting) { - img = null; - av.setSearchResults(results); - repaint(); - return; + return false; } - - fastpainting = true; - fastPaint = true; + boolean wrapped = av.getWrapAlignment(); try { + fastPaint = !noFastPaint; + fastpainting = fastPaint; + + updateViewport(); + /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1020,12 +1034,34 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ SearchResultsI previous = av.getSearchResults(); av.setSearchResults(results); - boolean redrawn = drawMappedPositions(previous); - redrawn |= drawMappedPositions(results); + boolean redrawn = false; + boolean drawn = false; + if (wrapped) + { + redrawn = drawMappedPositionsWrapped(previous); + drawn = drawMappedPositionsWrapped(results); + redrawn |= drawn; + } + else + { + redrawn = drawMappedPositions(previous); + drawn = drawMappedPositions(results); + redrawn |= drawn; + } + + /* + * if highlights were either removed or added, repaint + */ if (redrawn) { repaint(); } + + /* + * return true only if highlights were added + */ + return drawn; + } finally { fastpainting = false; @@ -1128,30 +1164,189 @@ public class SeqCanvas extends JComponent implements ViewportListenerI @Override public void propertyChange(PropertyChangeEvent evt) { - if (!av.getWrapAlignment()) + String eventName = evt.getPropertyName(); + + if (av.getWrapAlignment()) { - if (evt.getPropertyName().equals("startres") - || evt.getPropertyName().equals("endres")) + if (eventName.equals(ViewportRanges.STARTRES)) + { + repaint(); + } + } + else + { + int scrollX = 0; + if (eventName.equals(ViewportRanges.STARTRES)) { // Make sure we're not trying to draw a panel // larger than the visible window ViewportRanges vpRanges = av.getRanges(); - int scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); - if (scrollX > vpRanges.getEndRes() - vpRanges.getStartRes()) + scrollX = (int) evt.getNewValue() - (int) evt.getOldValue(); + int range = vpRanges.getEndRes() - vpRanges.getStartRes(); + if (scrollX > range) { - scrollX = vpRanges.getEndRes() - vpRanges.getStartRes(); + scrollX = range; } - else if (scrollX < vpRanges.getStartRes() - vpRanges.getEndRes()) + else if (scrollX < -range) { - scrollX = vpRanges.getStartRes() - vpRanges.getEndRes(); + scrollX = -range; } + } + + // Both scrolling and resizing change viewport ranges: scrolling changes + // both start and end points, but resize only changes end values. + // Here we only want to fastpaint on a scroll, with resize using a normal + // paint, so scroll events are identified as changes to the horizontal or + // vertical start value. + if (eventName.equals(ViewportRanges.STARTRES)) + { + // scroll - startres and endres both change fastPaint(scrollX, 0); } - else if (evt.getPropertyName().equals("startseq") - || evt.getPropertyName().equals("endseq")) + else if (eventName.equals(ViewportRanges.STARTSEQ)) { + // scroll fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue()); } } } + + /** + * Redraws any positions in the search results in the visible region of a + * wrapped alignment. Any highlights are drawn depending on the search results + * set on the Viewport, not the results argument. This allows + * this method to be called either to clear highlights (passing the previous + * search results), or to draw new highlights. + * + * @param results + * @return + */ + protected boolean drawMappedPositionsWrapped(SearchResultsI results) + { + if (results == null) + { + return false; + } + + boolean matchFound = false; + + int wrappedWidth = av.getWrappedWidth(); + int wrappedHeight = getRepeatHeightWrapped(); + + ViewportRanges ranges = av.getRanges(); + int canvasHeight = getHeight(); + int repeats = canvasHeight / wrappedHeight; + if (canvasHeight / wrappedHeight > 0) + { + repeats++; + } + + int firstVisibleColumn = ranges.getStartRes(); + int lastVisibleColumn = ranges.getStartRes() + repeats + * ranges.getViewportWidth() - 1; + + AlignmentI alignment = av.getAlignment(); + if (av.hasHiddenColumns()) + { + firstVisibleColumn = alignment.getHiddenColumns() + .adjustForHiddenColumns(firstVisibleColumn); + lastVisibleColumn = alignment.getHiddenColumns() + .adjustForHiddenColumns(lastVisibleColumn); + } + + int gapHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); + + for (int seqNo = ranges.getStartSeq(); seqNo <= ranges + .getEndSeq(); seqNo++) + { + SequenceI seq = alignment.getSequenceAt(seqNo); + + int[] visibleResults = results.getResults(seq, firstVisibleColumn, + lastVisibleColumn); + if (visibleResults != null) + { + for (int i = 0; i < visibleResults.length - 1; i += 2) + { + int firstMatchedColumn = visibleResults[i]; + int lastMatchedColumn = visibleResults[i + 1]; + if (firstMatchedColumn <= lastVisibleColumn + && lastMatchedColumn >= firstVisibleColumn) + { + /* + * found a search results match in the visible region + */ + firstMatchedColumn = Math.max(firstMatchedColumn, + firstVisibleColumn); + lastMatchedColumn = Math.min(lastMatchedColumn, + lastVisibleColumn); + + /* + * draw each mapped position separately (as contiguous positions may + * wrap across lines) + */ + for (int mappedPos = firstMatchedColumn; mappedPos <= lastMatchedColumn; mappedPos++) + { + int displayColumn = mappedPos; + if (av.hasHiddenColumns()) + { + displayColumn = alignment.getHiddenColumns() + .findColumnPosition(displayColumn); + } + + /* + * transX: offset from left edge of canvas to residue position + */ + int transX = labelWidthWest + + ((displayColumn - ranges.getStartRes()) % wrappedWidth) + * av.getCharWidth(); + + /* + * transY: offset from top edge of canvas to residue position + */ + int transY = gapHeight; + transY += (displayColumn - ranges.getStartRes()) + / wrappedWidth * wrappedHeight; + transY += (seqNo - ranges.getStartSeq()) * av.getCharHeight(); + + /* + * yOffset is from graphics origin to start of visible region + */ + int yOffset = 0;// (displayColumn / wrappedWidth) * wrappedHeight; + if (transY < getHeight()) + { + matchFound = true; + gg.translate(transX, transY); + drawPanel(gg, displayColumn, displayColumn, seqNo, seqNo, + yOffset); + gg.translate(-transX, -transY); + } + } + } + } + } + } + + return matchFound; + } + + /** + * Answers the height in pixels of a repeating section of the wrapped + * alignment, including space above, scale above if shown, sequences, and + * annotation panel if shown + * + * @return + */ + protected int getRepeatHeightWrapped() + { + // gap (and maybe scale) above + int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); + + // add sequences + repeatHeight += av.getRanges().getViewportHeight() * charHeight; + + // add annotations panel height if shown + repeatHeight += getAnnotationHeight(); + + return repeatHeight; + } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 5634ead..2b1b0e5 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -201,6 +201,7 @@ public class SeqPanel extends JPanel implements MouseListener, int res = 0; int x = evt.getX(); + int startRes = av.getRanges().getStartRes(); if (av.getWrapAlignment()) { @@ -215,7 +216,7 @@ public class SeqPanel extends JPanel implements MouseListener, int y = evt.getY(); y -= hgap; - x -= seqCanvas.LABEL_WEST; + x = Math.max(0, x - seqCanvas.labelWidthWest); int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth()); if (cwidth < 1) @@ -224,10 +225,11 @@ public class SeqPanel extends JPanel implements MouseListener, } wrappedBlock = y / cHeight; - wrappedBlock += av.getRanges().getStartRes() / cwidth; - - res = wrappedBlock * cwidth + x / av.getCharWidth(); - + wrappedBlock += startRes / cwidth; + // allow for wrapped view scrolled right (possible from Overview) + int startOffset = startRes % cwidth; + res = wrappedBlock * cwidth + + Math.min(cwidth - 1, startOffset + x / av.getCharWidth()); } else { @@ -237,7 +239,7 @@ public class SeqPanel extends JPanel implements MouseListener, // right-hand gutter x = seqCanvas.getX() + seqCanvas.getWidth(); } - res = (x / av.getCharWidth()) + av.getRanges().getStartRes(); + res = (x / av.getCharWidth()) + startRes; if (res > av.getRanges().getEndRes()) { // moused off right @@ -681,6 +683,8 @@ public class SeqPanel extends JPanel implements MouseListener, } lastSearchResults = results; + boolean wasScrolled = false; + if (av.isFollowHighlight()) { // don't allow highlight of protein/cDNA to also scroll a complementary @@ -688,14 +692,19 @@ public class SeqPanel extends JPanel implements MouseListener, // over residue to change abruptly, causing highlighted residue in panel 2 // to change, causing a scroll in panel 1 etc) ap.setToScrollComplementPanel(false); - if (ap.scrollToPosition(results, false)) + wasScrolled = ap.scrollToPosition(results, false); + if (wasScrolled) { seqCanvas.revalidate(); } ap.setToScrollComplementPanel(true); } - setStatusMessage(results); - seqCanvas.highlightSearchResults(results); + + boolean noFastPaint = wasScrolled && av.getWrapAlignment(); + if (seqCanvas.highlightSearchResults(results, noFastPaint)) + { + setStatusMessage(results); + } } @Override @@ -1592,7 +1601,7 @@ public class SeqPanel extends JPanel implements MouseListener, SearchResultsI highlight = new SearchResults(); highlight.addResult(sequence, features.get(0).getBegin(), features .get(0).getEnd()); - seqCanvas.highlightSearchResults(highlight); + seqCanvas.highlightSearchResults(highlight, false); /* * open the Amend Features dialog; clear highlighting afterwards, @@ -1601,7 +1610,8 @@ public class SeqPanel extends JPanel implements MouseListener, List seqs = Collections.singletonList(sequence); seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, false, ap); - seqCanvas.highlightSearchResults(null); + av.setSearchResults(null); // clear highlighting + seqCanvas.repaint(); // draw new/amended features } } } @@ -2147,8 +2157,7 @@ public class SeqPanel extends JPanel implements MouseListener, if (copycolsel && av.hasHiddenColumns() - && (av.getAlignment().getHiddenColumns() == null || av - .getAlignment().getHiddenColumns().getHiddenRegions() == null)) + && (av.getAlignment().getHiddenColumns() == null)) { System.err.println("Bad things"); } diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java index 0c4e03e..ec53e93 100755 --- a/src/jalview/gui/SliderPanel.java +++ b/src/jalview/gui/SliderPanel.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import jalview.analysis.Conservation; import jalview.datamodel.SequenceGroup; import jalview.jbgui.GSliderPanel; import jalview.renderer.ResidueShaderI; @@ -28,6 +29,7 @@ import jalview.util.MessageManager; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; +import java.util.List; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; @@ -170,7 +172,8 @@ public class SliderPanel extends GSliderPanel "label.conservation_colour_increment", new String[] { source == null ? BACKGROUND : source })); - if (ap.av.getAlignment().getGroups() != null) + List groups = ap.av.getAlignment().getGroups(); + if (groups != null && !groups.isEmpty()) { sliderPanel.setAllGroupsCheckEnabled(true); } @@ -331,13 +334,14 @@ public class SliderPanel extends GSliderPanel { ap.av.setThreshold(percent); } - updateColourScheme(percent, cs); + updateColourScheme(percent, cs, null); if (allGroupsCheck.isSelected()) { - for (SequenceGroup sg : ap.av.getAlignment().getGroups()) + List groups = ap.av.getAlignment().getGroups(); + for (SequenceGroup sg : groups) { - updateColourScheme(percent, sg.getGroupColourScheme()); + updateColourScheme(percent, sg.getGroupColourScheme(), sg); } } @@ -350,8 +354,10 @@ public class SliderPanel extends GSliderPanel * * @param percent * @param scheme + * @param sg */ - protected void updateColourScheme(int percent, ResidueShaderI scheme) + protected void updateColourScheme(int percent, ResidueShaderI scheme, + SequenceGroup sg) { if (scheme == null) { @@ -359,6 +365,20 @@ public class SliderPanel extends GSliderPanel } if (forConservation) { + if (!scheme.conservationApplied()) + { + /* + * first time the colour scheme has had Conservation shading applied + * - compute conservation + */ + Conservation c = new Conservation("Group", sg.getSequences(null), + sg.getStartRes(), sg.getEndRes()); + c.calculate(); + c.verdict(false, ap.av.getConsPercGaps()); + sg.cs.setConservation(c); + + } + scheme.setConservationApplied(true); scheme.setConservationInc(percent); } else @@ -376,6 +396,7 @@ public class SliderPanel extends GSliderPanel public void setAllGroupsCheckEnabled(boolean b) { allGroupsCheck.setEnabled(b); + allGroupsCheck.setSelected(ap.av.getColourAppliesToAllGroups()); } /** diff --git a/src/jalview/io/AnnotationFile.java b/src/jalview/io/AnnotationFile.java index c3e71da..b669ee5 100755 --- a/src/jalview/io/AnnotationFile.java +++ b/src/jalview/io/AnnotationFile.java @@ -171,22 +171,9 @@ public class AnnotationFile if (cs != null && cs.hasHiddenColumns()) { text.append("VIEW_HIDECOLS\t"); - List hc = cs.getHiddenRegions(); - boolean comma = false; - for (int[] r : hc) - { - if (!comma) - { - comma = true; - } - else - { - text.append(","); - } - text.append(r[0]); - text.append("-"); - text.append(r[1]); - } + + String regions = cs.regionsToString(",", "-"); + text.append(regions); text.append("\n"); } // TODO: allow efficient recovery of annotation data shown in several @@ -202,8 +189,8 @@ public class AnnotationFile StringBuffer colours = new StringBuffer(); StringBuffer graphLine = new StringBuffer(); StringBuffer rowprops = new StringBuffer(); - Hashtable graphGroup = new Hashtable(); - Hashtable graphGroup_refs = new Hashtable(); + Hashtable graphGroup = new Hashtable<>(); + Hashtable graphGroup_refs = new Hashtable<>(); BitSet graphGroupSeen = new BitSet(); java.awt.Color color; @@ -748,8 +735,8 @@ public class AnnotationFile BufferedReader in) throws Exception { nlinesread = 0; - ArrayList combineAnnotation_calls = new ArrayList(); - ArrayList deferredAnnotation_calls = new ArrayList(); + ArrayList combineAnnotation_calls = new ArrayList<>(); + ArrayList deferredAnnotation_calls = new ArrayList<>(); boolean modified = false; String groupRef = null; Hashtable groupRefRows = new Hashtable(); @@ -1112,7 +1099,7 @@ public class AnnotationFile modified = true; } // Resolve the groupRefs - Hashtable groupRefLookup = new Hashtable(); + Hashtable groupRefLookup = new Hashtable<>(); Enumeration en = groupRefRows.keys(); while (en.hasMoreElements()) diff --git a/src/jalview/io/JSONFile.java b/src/jalview/io/JSONFile.java index 14574d0..36fe35a 100644 --- a/src/jalview/io/JSONFile.java +++ b/src/jalview/io/JSONFile.java @@ -279,18 +279,8 @@ public class JSONFile extends AlignFile implements ComplexAlignFile // hidden column business if (getViewport().hasHiddenColumns()) { - List hiddenCols = getViewport().getAlignment() - .getHiddenColumns() - .getHiddenRegions(); - StringBuilder hiddenColsBuilder = new StringBuilder(); - for (int[] range : hiddenCols) - { - hiddenColsBuilder.append(";").append(range[0]).append("-") - .append(range[1]); - } - - hiddenColsBuilder.deleteCharAt(0); - hiddenSections[0] = hiddenColsBuilder.toString(); + hiddenSections[0] = getViewport().getAlignment().getHiddenColumns() + .regionsToString(";", "-"); } // hidden rows/seqs business @@ -323,7 +313,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile SequenceI[] sqs) { displayedFeatures = (fr == null) ? null : fr.getFeaturesDisplayed(); - List sequenceFeaturesPojo = new ArrayList(); + List sequenceFeaturesPojo = new ArrayList<>(); if (sqs == null) { return sequenceFeaturesPojo; @@ -371,7 +361,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile public static List annotationToJsonPojo( Vector annotations) { - List jsonAnnotations = new ArrayList(); + List jsonAnnotations = new ArrayList<>(); if (annotations == null) { return jsonAnnotations; @@ -468,8 +458,8 @@ public class JSONFile extends AlignFile implements ComplexAlignFile parseHiddenCols(jvSettingsJsonObj); } - hiddenSequences = new ArrayList(); - seqMap = new Hashtable(); + hiddenSequences = new ArrayList<>(); + seqMap = new Hashtable<>(); for (Iterator sequenceIter = seqJsonArray.iterator(); sequenceIter .hasNext();) { @@ -512,7 +502,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile int endRes = Integer.valueOf(seqGrpObj.get("endRes").toString()); JSONArray sequenceRefs = (JSONArray) seqGrpObj.get("sequenceRefs"); - ArrayList grpSeqs = new ArrayList(); + ArrayList grpSeqs = new ArrayList<>(); if (sequenceRefs.size() > 0) { Iterator seqHashIter = sequenceRefs.iterator(); @@ -646,7 +636,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile public void parseHiddenSeqRefsAsList(JSONObject jvSettingsJson) { - hiddenSeqRefs = new ArrayList(); + hiddenSeqRefs = new ArrayList<>(); String hiddenSeqs = (String) jvSettingsJson.get("hiddenSeqs"); if (hiddenSeqs != null && !hiddenSeqs.isEmpty()) { diff --git a/src/jalview/renderer/OverviewRenderer.java b/src/jalview/renderer/OverviewRenderer.java index 9291ca6..46490cd 100644 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@ -48,12 +48,14 @@ public class OverviewRenderer // raw number of pixels to allocate to each row private float pixelsPerSeq; + // flag to indicate whether to halt drawing + private volatile boolean redraw = false; + public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer, FeatureRenderer fr, OverviewDimensions od) - // FeatureColourFinder colfinder, OverviewDimensions od) { sr = seqRenderer; - finder = new FeatureColourFinder(fr); // colfinder; + finder = new FeatureColourFinder(fr); pixelsPerCol = od.getPixelsPerCol(); pixelsPerSeq = od.getPixelsPerSeq(); @@ -76,8 +78,14 @@ public class OverviewRenderer int rgbcolor = Color.white.getRGB(); int seqIndex = 0; int pixelRow = 0; + for (int alignmentRow : rows) { + if (redraw) + { + break; + } + // get details of this alignment row boolean hidden = rows.isHidden(alignmentRow); SequenceI seq = rows.getSequence(alignmentRow); @@ -90,6 +98,11 @@ public class OverviewRenderer int pixelCol = 0; 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, @@ -171,6 +184,10 @@ public class OverviewRenderer int pixelCol = 0; for (int alignmentCol : cols) { + if (redraw) + { + break; + } if (alignmentCol >= annotations.length) { break; // no more annotations to draw here @@ -205,4 +222,12 @@ public class OverviewRenderer } } } + + public void setRedraw(boolean b) + { + synchronized (this) + { + redraw = b; + } + } } diff --git a/src/jalview/renderer/ResidueShader.java b/src/jalview/renderer/ResidueShader.java index b6f7fe6..8ecb2ad 100644 --- a/src/jalview/renderer/ResidueShader.java +++ b/src/jalview/renderer/ResidueShader.java @@ -96,6 +96,20 @@ public class ResidueShader implements ResidueShaderI } /** + * Copy constructor + */ + public ResidueShader(ResidueShader rs) + { + this.colourScheme = rs.colourScheme; + this.consensus = rs.consensus; + this.conservation = rs.conservation; + this.conservationColouring = rs.conservationColouring; + this.conservationIncrement = rs.conservationIncrement; + this.ignoreGaps = rs.ignoreGaps; + this.pidThreshold = rs.pidThreshold; + } + + /** * @see jalview.renderer.ResidueShaderI#setConsensus(jalview.datamodel.ProfilesI) */ @Override diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index 541288e..8feee1f 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -309,14 +309,39 @@ public class FeatureRenderer extends FeatureRendererModel for (SequenceFeature sf : overlaps) { Color featureColour = fc.getColor(sf); - int visibleStart = Math.max(sf.getBegin(), - visiblePositions.getBegin()); + if (featureColour == null) + { + // score feature outwith threshold for colouring + continue; + } + + /* + * if feature starts/ends outside the visible range, + * restrict to visible positions (or if a contact feature, + * to a single position) + */ + int visibleStart = sf.getBegin(); + if (visibleStart < visiblePositions.getBegin()) + { + visibleStart = sf.isContactFeature() ? sf.getEnd() + : visiblePositions.getBegin(); + } + int visibleEnd = sf.getEnd(); + if (visibleEnd > visiblePositions.getEnd()) + { + visibleEnd = sf.isContactFeature() ? sf.getBegin() + : visiblePositions.getEnd(); + } + int featureStartCol = seq.findIndex(visibleStart); - int visibleEnd = Math.min(sf.getEnd(), visiblePositions.getEnd()); int featureEndCol = sf.begin == sf.end ? featureStartCol : seq .findIndex(visibleEnd); - if (sf.isContactFeature()) + // Color featureColour = getColour(sequenceFeature); + + boolean isContactFeature = sf.isContactFeature(); + + if (isContactFeature) { boolean drawn = renderFeature(g, seq, featureStartCol - 1, featureStartCol - 1, featureColour, start, end, y1, @@ -329,7 +354,7 @@ public class FeatureRenderer extends FeatureRendererModel drawnColour = featureColour; } } - else if (showFeature(sf)) + else { /* * showing feature score by height of colour @@ -391,7 +416,8 @@ public class FeatureRenderer extends FeatureRendererModel * Returns the sequence feature colour rendered at the given column position, * or null if none found. The feature of highest render order (i.e. on top) is * found, subject to both feature type and feature group being visible, and - * its colour returned. + * its colour returned. This method is suitable when no feature transparency + * applied (only the topmost visible feature colour is rendered). *

        * Note this method does not check for a gap in the column so would return the * colour for features enclosing a gapped column. Check for gap before calling @@ -427,7 +453,11 @@ public class FeatureRenderer extends FeatureRendererModel { if (!featureGroupNotShown(sequenceFeature)) { - return getColour(sequenceFeature); + Color col = getColour(sequenceFeature); + if (col != null) + { + return col; + } } } } diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java index dbe4901..b748d9e 100644 --- a/src/jalview/schemes/FeatureColour.java +++ b/src/jalview/schemes/FeatureColour.java @@ -551,16 +551,27 @@ public class FeatureColour implements FeatureColourI return getColour(); } - // todo should we check for above/below threshold here? - if (range == 0.0) - { - return getMaxColour(); - } + /* + * graduated colour case, optionally with threshold + * Float.NaN is assigned minimum visible score colour + */ float scr = feature.getScore(); if (Float.isNaN(scr)) { return getMinColour(); } + if (isAboveThreshold() && scr <= threshold) + { + return null; + } + if (isBelowThreshold() && scr >= threshold) + { + return null; + } + if (range == 0.0) + { + return getMaxColour(); + } float scl = (scr - base) / range; if (isHighToLow) { @@ -602,44 +613,6 @@ public class FeatureColour implements FeatureColourI return (isHighToLow) ? (base + range) : base; } - /** - * Answers true if the feature has a simple colour, or is coloured by label, - * or has a graduated colour and the score of this feature instance is within - * the range to render (if any), i.e. does not lie below or above any - * threshold set. - * - * @param feature - * @return - */ - @Override - public boolean isColored(SequenceFeature feature) - { - if (isColourByLabel() || !isGraduatedColour()) - { - return true; - } - - float val = feature.getScore(); - if (Float.isNaN(val)) - { - return true; - } - if (Float.isNaN(this.threshold)) - { - return true; - } - - if (isAboveThreshold() && val <= threshold) - { - return false; - } - if (isBelowThreshold() && val >= threshold) - { - return false; - } - return true; - } - @Override public boolean isSimpleColour() { diff --git a/src/jalview/schemes/ResidueColourScheme.java b/src/jalview/schemes/ResidueColourScheme.java index 03fc129..b47b82e 100755 --- a/src/jalview/schemes/ResidueColourScheme.java +++ b/src/jalview/schemes/ResidueColourScheme.java @@ -24,7 +24,6 @@ import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import jalview.util.Comparison; import java.awt.Color; import java.util.Map; @@ -91,7 +90,7 @@ public abstract class ResidueColourScheme implements ColourSchemeI { Color colour = Color.white; - if (!Comparison.isGap(c) && colors != null && symbolIndex != null + if (colors != null && symbolIndex != null && c < symbolIndex.length && symbolIndex[c] < colors.length) { diff --git a/src/jalview/schemes/ScoreColourScheme.java b/src/jalview/schemes/ScoreColourScheme.java index aa20121..e1b60ca 100755 --- a/src/jalview/schemes/ScoreColourScheme.java +++ b/src/jalview/schemes/ScoreColourScheme.java @@ -23,6 +23,7 @@ package jalview.schemes; import jalview.datamodel.AnnotatedCollectionI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; +import jalview.util.Comparison; import java.awt.Color; import java.util.Map; @@ -35,13 +36,10 @@ import java.util.Map; */ public class ScoreColourScheme extends ResidueColourScheme { - /** DOCUMENT ME!! */ public double min; - /** DOCUMENT ME!! */ public double max; - /** DOCUMENT ME!! */ public double[] scores; /** @@ -65,25 +63,38 @@ public class ScoreColourScheme extends ResidueColourScheme // Make colours in constructor // Why wasn't this done earlier? - int i, iSize = scores.length; + int iSize = scores.length; colors = new Color[scores.length]; - for (i = 0; i < iSize; i++) + for (int i = 0; i < iSize; i++) { - float red = (float) (scores[i] - (float) min) / (float) (max - min); + /* + * scale score between min and max to the range 0.0 - 1.0 + */ + float score = (float) (scores[i] - (float) min) / (float) (max - min); - if (red > 1.0f) + if (score > 1.0f) { - red = 1.0f; + score = 1.0f; } - if (red < 0.0f) + if (score < 0.0f) { - red = 0.0f; + score = 0.0f; } - colors[i] = makeColour(red); + colors[i] = makeColour(score); } } + @Override + public Color findColour(char c, int j, SequenceI seq) + { + if (Comparison.isGap(c)) + { + return Color.white; + } + return super.findColour(c); + } + /** * DOCUMENT ME! * diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index 926ccc7..b2ec120 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -108,7 +108,7 @@ public final class MappingUtils * Cache a copy of the target sequences so we can mimic successive edits on * them. This lets us compute mappings for all edits in the set. */ - Map targetCopies = new HashMap(); + Map targetCopies = new HashMap<>(); for (SequenceI seq : mapTo.getSequences()) { SequenceI ds = seq.getDatasetSequence(); @@ -433,7 +433,7 @@ public final class MappingUtils boolean undo, AlignmentI mapTo, List mappings) { SequenceI[] sortOrder = command.getSequenceOrder(undo); - List mappedOrder = new ArrayList(); + List mappedOrder = new ArrayList<>(); int j = 0; /* @@ -518,7 +518,6 @@ public final class MappingUtils AlignViewportI protein = targetIsNucleotide ? mapFrom : mapTo; List codonFrames = protein.getAlignment() .getCodonFrames(); - // ColumnSelection mappedColumns = new ColumnSelection(); if (colsel == null) { @@ -540,7 +539,7 @@ public final class MappingUtils toSequences, fromGapChar); } - for (int[] hidden : hiddencols.getHiddenRegions()) + for (int[] hidden : hiddencols.getHiddenColumnsCopy()) { mapHiddenColumns(hidden, codonFrames, newHidden, fromSequences, toSequences, fromGapChar); @@ -696,7 +695,7 @@ public final class MappingUtils public static List findCodonsFor(SequenceI seq, int col, List mappings) { - List result = new ArrayList(); + List result = new ArrayList<>(); int dsPos = seq.findPosition(col); for (AlignedCodonFrame mapping : mappings) { @@ -775,7 +774,7 @@ public final class MappingUtils SequenceI sequence, List mappings, List filterList) { - List result = new ArrayList(); + List result = new ArrayList<>(); if (sequence == null || mappings == null) { return result; diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 60cee46..5a7a27f 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -405,6 +405,7 @@ public abstract class AlignmentViewport implements AlignViewportI, public void setWrapAlignment(boolean state) { viewStyle.setWrapAlignment(state); + ranges.setWrappedMode(state); } /** @@ -1159,7 +1160,7 @@ public abstract class AlignmentViewport implements AlignViewportI, @Override public boolean hasHiddenColumns() { - return colSel != null + return alignment.getHiddenColumns() != null && alignment.getHiddenColumns().hasHiddenColumns(); } @@ -1390,6 +1391,9 @@ public abstract class AlignmentViewport implements AlignViewportI, // common hide/show seq stuff public void showAllHiddenSeqs() { + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + if (alignment.getHiddenSequences().getSize() > 0) { if (selectionGroup == null) @@ -1407,6 +1411,8 @@ public abstract class AlignmentViewport implements AlignViewportI, hiddenRepSequences = null; + ranges.setStartEndSeq(startSeq, endSeq + tmp.size()); + firePropertyChange("alignment", null, alignment.getSequences()); // used to set hasHiddenRows/hiddenRepSequences here, after the property // changed event @@ -1416,6 +1422,9 @@ public abstract class AlignmentViewport implements AlignViewportI, public void showSequence(int index) { + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + List tmp = alignment.getHiddenSequences().showSequence( index, hiddenRepSequences); if (tmp.size() > 0) @@ -1431,6 +1440,9 @@ public abstract class AlignmentViewport implements AlignViewportI, selectionGroup.addSequence(seq, false); setSequenceAnnotationsVisible(seq, true); } + + ranges.setStartEndSeq(startSeq, endSeq + tmp.size()); + firePropertyChange("alignment", null, alignment.getSequences()); sendSelection(); } @@ -1452,6 +1464,11 @@ public abstract class AlignmentViewport implements AlignViewportI, public void hideSequence(SequenceI[] seq) { + /* + * cache offset to first visible sequence + */ + int startSeq = ranges.getStartSeq(); + if (seq != null) { for (int i = 0; i < seq.length; i++) @@ -1459,6 +1476,7 @@ public abstract class AlignmentViewport implements AlignViewportI, alignment.getHiddenSequences().hideSequence(seq[i]); setSequenceAnnotationsVisible(seq[i], false); } + ranges.setStartSeq(startSeq); firePropertyChange("alignment", null, alignment.getSequences()); } } @@ -2870,4 +2888,45 @@ public abstract class AlignmentViewport implements AlignViewportI, { return searchResults; } + + /** + * get the consensus sequence as displayed under the PID consensus annotation + * row. + * + * @return consensus sequence as a new sequence object + */ + public SequenceI getConsensusSeq() + { + if (consensus == null) + { + updateConsensus(null); + } + if (consensus == null) + { + return null; + } + StringBuffer seqs = new StringBuffer(); + for (int i = 0; i < consensus.annotations.length; i++) + { + Annotation annotation = consensus.annotations[i]; + if (annotation != null) + { + String description = annotation.description; + if (description != null && description.startsWith("[")) + { + // consensus is a tie - just pick the first one + seqs.append(description.charAt(1)); + } + else + { + seqs.append(annotation.displayCharacter); + } + } + } + + SequenceI sq = new Sequence("Consensus", seqs.toString()); + sq.setDescription("Percentage Identity Consensus " + + ((ignoreGapsInConsensusCalculation) ? " without gaps" : "")); + return sq; + } } diff --git a/src/jalview/viewmodel/OverviewDimensions.java b/src/jalview/viewmodel/OverviewDimensions.java index d2912d8..7ac07ac 100644 --- a/src/jalview/viewmodel/OverviewDimensions.java +++ b/src/jalview/viewmodel/OverviewDimensions.java @@ -231,7 +231,8 @@ public abstract class OverviewDimensions resetAlignmentDims(); // boxX, boxY is the x,y location equivalent to startRes, startSeq - boxX = Math.round((float) startRes * width / alwidth); + int xPos = Math.min(startRes, alwidth - vpwidth + 1); + boxX = Math.round((float) xPos * width / alwidth); boxY = Math.round((float) startSeq * sequencesHeight / alheight); // boxWidth is the width in residues translated to pixels diff --git a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java index 4bf7694..4d64f1c 100644 --- a/src/jalview/viewmodel/OverviewDimensionsHideHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsHideHidden.java @@ -39,6 +39,11 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions y = 0; } + if (ranges.isWrappedMode()) + { + y = 0; // sorry, no vertical scroll when wrapped + } + // // Convert x value to residue position // diff --git a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java index 4b396a6..62e8000 100644 --- a/src/jalview/viewmodel/OverviewDimensionsShowHidden.java +++ b/src/jalview/viewmodel/OverviewDimensionsShowHidden.java @@ -81,6 +81,11 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions y = 0; } + if (ranges.isWrappedMode()) + { + y = 0; // sorry, no vertical scroll when wrapped + } + // // Convert x value to residue position // @@ -135,10 +140,12 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions // so convert back after getting visible region position yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs .findIndexWithoutHiddenSeqs(yAsSeq)); + yAsSeq = Math.max(yAsSeq, 0); // -1 if before first visible sequence // check in case we went off the edge of the alignment int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight); int visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq); + visYAsSeq = Math.max(visYAsSeq, 0); // -1 if before first visible sequence if (visYAsSeq + vpheight - 1 > visAlignHeight) { // went past the end of the alignment, adjust backwards @@ -156,7 +163,6 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions // update viewport ranges.setStartRes(visXAsRes); ranges.setStartSeq(visYAsSeq); - } /** diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 4eb8c95..49d0f65 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -32,6 +32,16 @@ import jalview.datamodel.HiddenColumns; */ public class ViewportRanges extends ViewportProperties { + public static final String STARTRES = "startres"; + + public static final String ENDRES = "endres"; + + public static final String STARTSEQ = "startseq"; + + public static final String ENDSEQ = "endseq"; + + private boolean wrappedMode = false; + // start residue of viewport private int startRes; @@ -121,9 +131,14 @@ public class ViewportRanges extends ViewportProperties public void setStartEndRes(int start, int end) { int oldstartres = this.startRes; - if (start > getVisibleAlignmentWidth() - 1) + + /* + * if not wrapped, don't leave white space at the right margin + */ + int lastColumn = getVisibleAlignmentWidth() - 1; + if (!wrappedMode && (start > lastColumn)) { - startRes = Math.max(getVisibleAlignmentWidth() - 1, 0); + startRes = Math.max(lastColumn, 0); } else if (start < 0) { @@ -139,39 +154,22 @@ public class ViewportRanges extends ViewportProperties { endRes = 0; } - else if (end > getVisibleAlignmentWidth() - 1) + else if (!wrappedMode && (end > lastColumn)) { - endRes = Math.max(getVisibleAlignmentWidth() - 1, 0); + endRes = Math.max(lastColumn, 0); } else { endRes = end; } - changeSupport.firePropertyChange("startres", oldstartres, startRes); + changeSupport.firePropertyChange(STARTRES, oldstartres, startRes); if (oldstartres == startRes) { // event won't be fired if start positions are same // fire an event for the end positions in case they changed - changeSupport.firePropertyChange("endres", oldendres, endRes); - } - } - - /** - * Set last residue visible in the viewport. Fires a property change event. - * - * @param res - * residue position - */ - public void setEndRes(int res) - { - int startres = res; - int width = getViewportWidth(); - if (startres + width - 1 > getVisibleAlignmentWidth() - 1) - { - startres = getVisibleAlignmentWidth() - width; + changeSupport.firePropertyChange(ENDRES, oldendres, endRes); } - setStartEndRes(startres - width + 1, startres); } /** @@ -206,9 +204,10 @@ public class ViewportRanges extends ViewportProperties public void setStartEndSeq(int start, int end) { int oldstartseq = this.startSeq; - if (start > getVisibleAlignmentHeight() - 1) + int visibleHeight = getVisibleAlignmentHeight(); + if (start > visibleHeight - 1) { - startSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); + startSeq = Math.max(visibleHeight - 1, 0); } else if (start < 0) { @@ -220,9 +219,9 @@ public class ViewportRanges extends ViewportProperties } int oldendseq = this.endSeq; - if (end >= getVisibleAlignmentHeight()) + if (end >= visibleHeight) { - endSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); + endSeq = Math.max(visibleHeight - 1, 0); } else if (end < 0) { @@ -233,12 +232,12 @@ public class ViewportRanges extends ViewportProperties endSeq = end; } - changeSupport.firePropertyChange("startseq", oldstartseq, startSeq); + changeSupport.firePropertyChange(STARTSEQ, oldstartseq, startSeq); if (oldstartseq == startSeq) { // event won't be fired if start positions are the same // fire in case the end positions changed - changeSupport.firePropertyChange("endseq", oldendseq, endSeq); + changeSupport.firePropertyChange(ENDSEQ, oldendseq, endSeq); } } @@ -330,12 +329,18 @@ public class ViewportRanges extends ViewportProperties { vpstart = 0; } - else if ((w <= getVisibleAlignmentWidth()) - && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1)) - // viewport width is less than the full alignment and we are running off the - // RHS edge + + /* + * if not wrapped, don't leave white space at the right margin + */ + if (!wrappedMode) { - vpstart = getVisibleAlignmentWidth() - w; + if ((w <= getVisibleAlignmentWidth()) + && (vpstart + w - 1 > getVisibleAlignmentWidth() - 1)) + { + vpstart = getVisibleAlignmentWidth() - w; + } + } setStartEndRes(vpstart, vpstart + w - 1); } @@ -451,18 +456,42 @@ public class ViewportRanges extends ViewportProperties } /** - * Scroll a wrapped alignment so that the specified residue is visible. Fires - * a property change event. + * Scroll a wrapped alignment so that the specified residue is in the first + * repeat of the wrapped view. Fires a property change event. Answers true if + * the startRes changed, else false. * * @param res * residue position to scroll to + * @return */ - public void scrollToWrappedVisible(int res) + public boolean scrollToWrappedVisible(int res) { - // get the start residue of the wrapped row which res is in - // and set that as our start residue + int oldStartRes = startRes; int width = getViewportWidth(); - setStartRes((res / width) * width); + + if (res >= oldStartRes && res < oldStartRes + width) + { + return false; + } + + boolean up = res < oldStartRes; + int widthsToScroll = Math.abs((res - oldStartRes) / width); + if (up) + { + widthsToScroll++; + } + + int residuesToScroll = width * widthsToScroll; + int newStartRes = up ? oldStartRes - residuesToScroll : oldStartRes + + residuesToScroll; + if (newStartRes < 0) + { + newStartRes = 0; + } + + setStartRes(newStartRes); + + return true; } /** @@ -506,7 +535,15 @@ public class ViewportRanges extends ViewportProperties */ public void pageUp() { - setViewportStartAndHeight(2 * startSeq - endSeq, getViewportHeight()); + if (wrappedMode) + { + setStartRes(Math.max(0, getStartRes() - getViewportWidth())); + } + else + { + setViewportStartAndHeight(startSeq - (endSeq - startSeq), + getViewportHeight()); + } } /** @@ -514,6 +551,94 @@ public class ViewportRanges extends ViewportProperties */ public void pageDown() { - setViewportStartAndHeight(endSeq, getViewportHeight()); + if (wrappedMode) + { + /* + * if height is more than width (i.e. not all sequences fit on screen), + * increase page down to height + */ + int newStart = getStartRes() + + Math.max(getViewportHeight(), getViewportWidth()); + + /* + * don't page down beyond end of alignment, or if not all + * sequences fit in the visible height + */ + if (newStart < getVisibleAlignmentWidth()) + { + setStartRes(newStart); + } + } + else + { + setViewportStartAndHeight(endSeq, getViewportHeight()); + } + } + + public void setWrappedMode(boolean wrapped) + { + wrappedMode = wrapped; + } + + public boolean isWrappedMode() + { + return wrappedMode; + } + + /** + * Answers the vertical scroll position (0..) to set, given the visible column + * that is at top left. + * + *

        +   * Example:
        +   *    viewport width 40 columns (0-39, 40-79, 80-119...)
        +   *    column 0 returns scroll position 0
        +   *    columns 1-40 return scroll position 1
        +   *    columns 41-80 return scroll position 2
        +   *    etc
        +   * 
        + * + * @param topLeftColumn + * (0..) + * @return + */ + public int getWrappedScrollPosition(final int topLeftColumn) + { + int w = getViewportWidth(); + + /* + * visible whole widths + */ + int scroll = topLeftColumn / w; + + /* + * add 1 for a part width if there is one + */ + scroll += topLeftColumn % w > 0 ? 1 : 0; + + return scroll; + } + + /** + * Answers the maximum wrapped vertical scroll value, given the column + * position (0..) to show at top left of the visible region. + * + * @param topLeftColumn + * @return + */ + public int getWrappedMaxScroll(int topLeftColumn) + { + int scrollPosition = getWrappedScrollPosition(topLeftColumn); + + /* + * how many more widths could be drawn after this one? + */ + int columnsRemaining = getVisibleAlignmentWidth() - topLeftColumn; + int width = getViewportWidth(); + int widthsRemaining = columnsRemaining / width + + (columnsRemaining % width > 0 ? 1 : 0) - 1; + int maxScroll = scrollPosition + widthsRemaining; + + return maxScroll; } } diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index de1ee5e..1d09dca 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -559,7 +559,8 @@ public abstract class FeatureRendererModel implements * Returns the configured colour for a particular feature instance. This * includes calculation of 'colour by label', or of a graduated score colour, * if applicable. It does not take into account feature visibility or colour - * transparency. + * transparency. Returns null for a score feature whose score value lies + * outside any colour threshold. * * @param feature * @return @@ -571,19 +572,6 @@ public abstract class FeatureRendererModel implements } /** - * Answers true unless the feature has a graduated colour scheme and the - * feature value lies outside the current threshold for display - * - * @param sequenceFeature - * @return - */ - protected boolean showFeature(SequenceFeature sequenceFeature) - { - FeatureColourI fc = getFeatureStyle(sequenceFeature.type); - return fc.isColored(sequenceFeature); - } - - /** * Answers true if the feature type is currently selected to be displayed, * else false * diff --git a/test/jalview/datamodel/AlignmentTest.java b/test/jalview/datamodel/AlignmentTest.java index 2adefc9..4b5d096 100644 --- a/test/jalview/datamodel/AlignmentTest.java +++ b/test/jalview/datamodel/AlignmentTest.java @@ -37,7 +37,6 @@ import jalview.io.FormatAdapter; import jalview.util.MapList; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -1107,35 +1106,6 @@ public class AlignmentTest "addSequence broke dataset reference integrity"); } - @Test(groups = "Functional") - public void getVisibleStartAndEndIndexTest() - { - Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - AlignmentI align = new Alignment(new SequenceI[] { seq }); - ArrayList hiddenCols = new ArrayList(); - - int[] startEnd = align.getVisibleStartAndEndIndex(hiddenCols); - assertEquals(0, startEnd[0]); - assertEquals(25, startEnd[1]); - - hiddenCols.add(new int[] { 0, 0 }); - startEnd = align.getVisibleStartAndEndIndex(hiddenCols); - assertEquals(1, startEnd[0]); - assertEquals(25, startEnd[1]); - - hiddenCols.add(new int[] { 6, 9 }); - hiddenCols.add(new int[] { 11, 12 }); - startEnd = align.getVisibleStartAndEndIndex(hiddenCols); - assertEquals(1, startEnd[0]); - assertEquals(25, startEnd[1]); - - hiddenCols.add(new int[] { 24, 25 }); - startEnd = align.getVisibleStartAndEndIndex(hiddenCols); - System.out.println(startEnd[0] + " : " + startEnd[1]); - assertEquals(1, startEnd[0]); - assertEquals(23, startEnd[1]); - } - /** * Tests that dbrefs with mappings to sequence get updated if the sequence * acquires a dataset sequence diff --git a/test/jalview/datamodel/ColumnSelectionTest.java b/test/jalview/datamodel/ColumnSelectionTest.java index 7237e63..e99e952 100644 --- a/test/jalview/datamodel/ColumnSelectionTest.java +++ b/test/jalview/datamodel/ColumnSelectionTest.java @@ -132,7 +132,7 @@ public class ColumnSelectionTest // hide column 5 (and adjacent): cs.hideSelectedColumns(5, al.getHiddenColumns()); // 4,5,6 now hidden: - List hidden = al.getHiddenColumns().getHiddenRegions(); + List hidden = al.getHiddenColumns().getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[4, 6]", Arrays.toString(hidden.get(0))); // none now selected: @@ -145,7 +145,7 @@ public class ColumnSelectionTest cs.addElement(5); cs.addElement(6); cs.hideSelectedColumns(4, al.getHiddenColumns()); - hidden = al.getHiddenColumns().getHiddenRegions(); + hidden = al.getHiddenColumns().getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[4, 6]", Arrays.toString(hidden.get(0))); assertTrue(cs.getSelected().isEmpty()); @@ -157,7 +157,7 @@ public class ColumnSelectionTest cs.addElement(5); cs.addElement(6); cs.hideSelectedColumns(6, al.getHiddenColumns()); - hidden = al.getHiddenColumns().getHiddenRegions(); + hidden = al.getHiddenColumns().getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[4, 6]", Arrays.toString(hidden.get(0))); assertTrue(cs.getSelected().isEmpty()); @@ -168,7 +168,7 @@ public class ColumnSelectionTest cs.addElement(4); cs.addElement(6); cs.hideSelectedColumns(5, al.getHiddenColumns()); - hidden = al.getHiddenColumns().getHiddenRegions(); + hidden = al.getHiddenColumns().getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[4, 6]", Arrays.toString(hidden.get(0))); assertTrue(cs.getSelected().isEmpty()); @@ -196,7 +196,7 @@ public class ColumnSelectionTest cs.hideSelectedColumns(al); assertTrue(cs.getSelected().isEmpty()); - List hidden = cols.getHiddenRegions(); + List hidden = cols.getHiddenColumnsCopy(); assertEquals(4, hidden.size()); assertEquals("[2, 4]", Arrays.toString(hidden.get(0))); assertEquals("[7, 9]", Arrays.toString(hidden.get(1))); diff --git a/test/jalview/datamodel/HiddenColumnsTest.java b/test/jalview/datamodel/HiddenColumnsTest.java index 10808d6..7c88d71 100644 --- a/test/jalview/datamodel/HiddenColumnsTest.java +++ b/test/jalview/datamodel/HiddenColumnsTest.java @@ -20,9 +20,9 @@ */ package jalview.datamodel; +import static org.testng.Assert.assertNull; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; import jalview.analysis.AlignmentGenerator; @@ -232,14 +232,17 @@ public class HiddenColumnsTest HiddenColumns cs = new HiddenColumns(); cs.hideColumns(10, 11); cs.hideColumns(5, 7); - assertEquals("[5, 7]", Arrays.toString(cs.getHiddenRegions().get(0))); + assertEquals("[5, 7]", + Arrays.toString(cs.getHiddenColumnsCopy().get(0))); HiddenColumns cs2 = new HiddenColumns(cs); assertTrue(cs2.hasHiddenColumns()); - assertEquals(2, cs2.getHiddenRegions().size()); + assertEquals(2, cs2.getHiddenColumnsCopy().size()); // hidden columns are held in column order - assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenRegions().get(0))); - assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenRegions().get(1))); + assertEquals("[5, 7]", + Arrays.toString(cs2.getHiddenColumnsCopy().get(0))); + assertEquals("[10, 11]", + Arrays.toString(cs2.getHiddenColumnsCopy().get(1))); } /** @@ -336,66 +339,78 @@ public class HiddenColumnsTest ColumnSelection colsel = new ColumnSelection(); HiddenColumns cs = al.getHiddenColumns(); colsel.hideSelectedColumns(5, al.getHiddenColumns()); - List hidden = cs.getHiddenRegions(); + List hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[5, 5]", Arrays.toString(hidden.get(0))); colsel.hideSelectedColumns(3, al.getHiddenColumns()); + hidden = cs.getHiddenColumnsCopy(); assertEquals(2, hidden.size()); // two hidden ranges, in order: - assertSame(hidden, cs.getHiddenRegions()); + assertEquals(hidden.size(), cs.getHiddenColumnsCopy().size()); assertEquals("[3, 3]", Arrays.toString(hidden.get(0))); assertEquals("[5, 5]", Arrays.toString(hidden.get(1))); // hiding column 4 expands [3, 3] to [3, 4] // and merges to [5, 5] to make [3, 5] colsel.hideSelectedColumns(4, al.getHiddenColumns()); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[3, 5]", Arrays.toString(hidden.get(0))); // clear hidden columns (note they are added to selected) cs.revealAllHiddenColumns(colsel); // it is now actually null but getter returns an empty list - assertTrue(cs.getHiddenRegions().isEmpty()); + assertTrue(cs.getHiddenColumnsCopy().isEmpty()); cs.hideColumns(3, 6); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); int[] firstHiddenRange = hidden.get(0); assertEquals("[3, 6]", Arrays.toString(firstHiddenRange)); // adding a subrange of already hidden should do nothing cs.hideColumns(4, 5); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); - assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); + assertEquals("[3, 6]", + Arrays.toString(cs.getHiddenColumnsCopy().get(0))); cs.hideColumns(3, 5); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); - assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); + assertEquals("[3, 6]", + Arrays.toString(cs.getHiddenColumnsCopy().get(0))); cs.hideColumns(4, 6); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); - assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); + assertEquals("[3, 6]", + Arrays.toString(cs.getHiddenColumnsCopy().get(0))); cs.hideColumns(3, 6); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); - assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); + assertEquals("[3, 6]", + Arrays.toString(cs.getHiddenColumnsCopy().get(0))); cs.revealAllHiddenColumns(colsel); cs.hideColumns(2, 4); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[2, 4]", Arrays.toString(hidden.get(0))); // extend contiguous with 2 positions overlap cs.hideColumns(3, 5); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[2, 5]", Arrays.toString(hidden.get(0))); // extend contiguous with 1 position overlap cs.hideColumns(5, 6); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[2, 6]", Arrays.toString(hidden.get(0))); // extend contiguous with overlap both ends: cs.hideColumns(1, 7); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[1, 7]", Arrays.toString(hidden.get(0))); } @@ -413,7 +428,7 @@ public class HiddenColumnsTest colsel.addElement(10); cs.revealHiddenColumns(5, colsel); // hidden columns list now null but getter returns empty list: - assertTrue(cs.getHiddenRegions().isEmpty()); + assertTrue(cs.getHiddenColumnsCopy().isEmpty()); // revealed columns are marked as selected (added to selection): assertEquals("[10, 5, 6, 7, 8]", colsel.getSelected().toString()); @@ -421,9 +436,9 @@ public class HiddenColumnsTest colsel = new ColumnSelection(); cs = new HiddenColumns(); cs.hideColumns(5, 8); - List hidden = cs.getHiddenRegions(); + List hidden = cs.getHiddenColumnsCopy(); cs.revealHiddenColumns(6, colsel); - assertSame(hidden, cs.getHiddenRegions()); + assertEquals(hidden.size(), cs.getHiddenColumnsCopy().size()); assertTrue(colsel.getSelected().isEmpty()); } @@ -442,7 +457,7 @@ public class HiddenColumnsTest * revealing hidden columns adds them (in order) to the (unordered) * selection list */ - assertTrue(cs.getHiddenRegions().isEmpty()); + assertTrue(cs.getHiddenColumnsCopy().isEmpty()); assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", colsel.getSelected() .toString()); } @@ -478,13 +493,13 @@ public class HiddenColumnsTest HiddenColumns cs = new HiddenColumns(); cs.hideColumns(49, 59); cs.hideColumns(69, 79); - List hidden = cs.getHiddenRegions(); + List hidden = cs.getHiddenColumnsCopy(); assertEquals(2, hidden.size()); assertEquals("[49, 59]", Arrays.toString(hidden.get(0))); assertEquals("[69, 79]", Arrays.toString(hidden.get(1))); cs.hideColumns(48, 80); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[48, 80]", Arrays.toString(hidden.get(0))); @@ -497,7 +512,7 @@ public class HiddenColumnsTest cs.hideColumns(50, 60); // hiding 21-49 should merge to one range cs.hideColumns(21, 49); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[10, 60]", Arrays.toString(hidden.get(0))); @@ -513,7 +528,7 @@ public class HiddenColumnsTest cs.hideColumns(60, 70); cs.hideColumns(15, 45); - hidden = cs.getHiddenRegions(); + hidden = cs.getHiddenColumnsCopy(); assertEquals(2, hidden.size()); assertEquals("[10, 50]", Arrays.toString(hidden.get(0))); assertEquals("[60, 70]", Arrays.toString(hidden.get(1))); @@ -530,23 +545,23 @@ public class HiddenColumnsTest one.set(1); cs = new HiddenColumns(); cs.hideMarkedBits(one); - assertEquals(1, cs.getHiddenRegions().size()); + assertEquals(1, cs.getHiddenColumnsCopy().size()); one.set(2); cs = new HiddenColumns(); cs.hideMarkedBits(one); - assertEquals(1, cs.getHiddenRegions().size()); + assertEquals(1, cs.getHiddenColumnsCopy().size()); one.set(3); cs = new HiddenColumns(); cs.hideMarkedBits(one); - assertEquals(1, cs.getHiddenRegions().size()); + assertEquals(1, cs.getHiddenColumnsCopy().size()); // split one.clear(2); cs = new HiddenColumns(); cs.hideMarkedBits(one); - assertEquals(2, cs.getHiddenRegions().size()); + assertEquals(2, cs.getHiddenColumnsCopy().size()); assertEquals(0, cs.adjustForHiddenColumns(0)); assertEquals(2, cs.adjustForHiddenColumns(1)); @@ -557,7 +572,7 @@ public class HiddenColumnsTest cs = new HiddenColumns(); cs.hideMarkedBits(one); - assertEquals(1, cs.getHiddenRegions().size()); + assertEquals(1, cs.getHiddenColumnsCopy().size()); assertEquals(0, cs.adjustForHiddenColumns(0)); assertEquals(1, cs.adjustForHiddenColumns(1)); @@ -585,4 +600,135 @@ public class HiddenColumnsTest assertEquals(toMark, fromMark); } } + + @Test(groups = { "Functional" }) + public void testFindHiddenRegionPositions() + { + HiddenColumns hc = new HiddenColumns(); + + List positions = hc.findHiddenRegionPositions(); + assertTrue(positions.isEmpty()); + + hc.hideColumns(3, 7); + hc.hideColumns(10, 10); + hc.hideColumns(14, 15); + + positions = hc.findHiddenRegionPositions(); + assertEquals(3, positions.size()); + assertEquals(3, positions.get(0).intValue()); + assertEquals(5, positions.get(1).intValue()); + assertEquals(8, positions.get(2).intValue()); + } + + @Test(groups = { "Functional" }) + public void testRegionsToString() + { + HiddenColumns hc = new HiddenColumns(); + + String result = hc.regionsToString(",", "--"); + assertEquals("", result); + + hc.hideColumns(3, 7); + hc.hideColumns(10, 10); + hc.hideColumns(14, 15); + + result = hc.regionsToString(",", "--"); + assertEquals("3--7,10--10,14--15", result); + } + + @Test(groups = "Functional") + public void getVisibleStartAndEndIndexTest() + { + Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + AlignmentI align = new Alignment(new SequenceI[] { seq }); + HiddenColumns hc = new HiddenColumns(); + + int[] startEnd = hc.getVisibleStartAndEndIndex(align.getWidth()); + assertEquals(0, startEnd[0]); + assertEquals(25, startEnd[1]); + + hc.hideColumns(0, 0); + startEnd = hc.getVisibleStartAndEndIndex(align.getWidth()); + assertEquals(1, startEnd[0]); + assertEquals(25, startEnd[1]); + + hc.hideColumns(6, 9); + hc.hideColumns(11, 12); + startEnd = hc.getVisibleStartAndEndIndex(align.getWidth()); + assertEquals(1, startEnd[0]); + assertEquals(25, startEnd[1]); + + hc.hideColumns(24, 25); + startEnd = hc.getVisibleStartAndEndIndex(align.getWidth()); + System.out.println(startEnd[0] + " : " + startEnd[1]); + assertEquals(1, startEnd[0]); + assertEquals(23, startEnd[1]); + } + + @Test(groups = "Functional") + public void testGetRegionWithEdgeAtRes() + { + HiddenColumns hc = new HiddenColumns(); + + int[] result = hc.getRegionWithEdgeAtRes(5); + assertNull(result); + + hc.hideColumns(3, 7); + hc.hideColumns(10, 10); + hc.hideColumns(14, 15); + + result = hc.getRegionWithEdgeAtRes(3); + assertEquals(3, result[0]); + assertEquals(7, result[1]); + + result = hc.getRegionWithEdgeAtRes(5); + assertEquals(10, result[0]); + assertEquals(10, result[1]); + + result = hc.getRegionWithEdgeAtRes(6); + assertNull(result); + } + + @Test(groups = "Functional") + public void testPropagateInsertions() + { + // create an alignment with no gaps - this will be the profile seq and other + // JPRED seqs + AlignmentGenerator gen = new AlignmentGenerator(false); + AlignmentI al = gen.generate(20, 10, 1234, 0, 0); + + // get the profileseq + SequenceI profileseq = al.getSequenceAt(0); + SequenceI gappedseq = new Sequence(profileseq); + gappedseq.insertCharAt(5, al.getGapCharacter()); + gappedseq.insertCharAt(6, al.getGapCharacter()); + gappedseq.insertCharAt(7, al.getGapCharacter()); + gappedseq.insertCharAt(8, al.getGapCharacter()); + + // create an alignment view with the gapped sequence + SequenceI[] seqs = new SequenceI[1]; + seqs[0] = gappedseq; + AlignmentI newal = new Alignment(seqs); + HiddenColumns hidden = new HiddenColumns(); + hidden.hideColumns(15, 17); + + AlignmentView view = new AlignmentView(newal, hidden, null, true, false, + false); + + // confirm that original contigs are as expected + int[] oldcontigs = hidden.getVisibleContigs(0, 20); + int[] testcontigs = { 0, 14, 18, 19 }; + assertTrue(Arrays.equals(oldcontigs, testcontigs)); + + // propagate insertions + HiddenColumns result = HiddenColumns.propagateInsertions(profileseq, al, + view); + + // confirm that the contigs have changed to account for the gaps + int[] newcontigs = result.getVisibleContigs(0, 20); + testcontigs[1] = 10; + testcontigs[2] = 14; + assertTrue(Arrays.equals(newcontigs, testcontigs)); + + } } diff --git a/test/jalview/datamodel/HiddenSequencesTest.java b/test/jalview/datamodel/HiddenSequencesTest.java index 7795988..11b993d 100644 --- a/test/jalview/datamodel/HiddenSequencesTest.java +++ b/test/jalview/datamodel/HiddenSequencesTest.java @@ -181,7 +181,8 @@ public class HiddenSequencesTest { AlignmentI al = new Alignment(seqs); HiddenSequences hs = al.getHiddenSequences(); - for (int i = 0; i < SEQ_COUNT; i++) + int height = al.getHeight(); + for (int i = 0; i < height; i++) { assertEquals(i, hs.findIndexWithoutHiddenSeqs(i)); } @@ -194,7 +195,7 @@ public class HiddenSequencesTest /* * alignment is now seq0/2/3/4/7/8/9 */ - assertEquals(SEQ_COUNT - 3, al.getHeight()); + assertEquals(height - 3, al.getHeight()); assertEquals(0, hs.findIndexWithoutHiddenSeqs(0)); assertEquals(0, hs.findIndexWithoutHiddenSeqs(1)); assertEquals(1, hs.findIndexWithoutHiddenSeqs(2)); @@ -205,6 +206,19 @@ public class HiddenSequencesTest assertEquals(4, hs.findIndexWithoutHiddenSeqs(7)); assertEquals(5, hs.findIndexWithoutHiddenSeqs(8)); assertEquals(6, hs.findIndexWithoutHiddenSeqs(9)); + + /* + * hide first two sequences + */ + hs.showAll(null); + hs.hideSequence(seqs[0]); + hs.hideSequence(seqs[1]); + assertEquals(-1, hs.findIndexWithoutHiddenSeqs(0)); + assertEquals(-1, hs.findIndexWithoutHiddenSeqs(1)); + for (int i = 2; i < height; i++) + { + assertEquals(i - 2, hs.findIndexWithoutHiddenSeqs(i)); + } } /** diff --git a/test/jalview/datamodel/SequenceFeatureTest.java b/test/jalview/datamodel/SequenceFeatureTest.java index d105cc5..fbeb365 100644 --- a/test/jalview/datamodel/SequenceFeatureTest.java +++ b/test/jalview/datamodel/SequenceFeatureTest.java @@ -42,7 +42,7 @@ public class SequenceFeatureTest } @Test(groups = { "Functional" }) - public void testCopyConstructor() + public void testCopyConstructors() { SequenceFeature sf1 = new SequenceFeature("type", "desc", 22, 33, 12.5f, "group"); @@ -56,10 +56,41 @@ public class SequenceFeatureTest assertEquals("desc", sf2.getDescription()); assertEquals(22, sf2.getBegin()); assertEquals(33, sf2.getEnd()); + assertEquals(12.5f, sf2.getScore()); assertEquals("+", sf2.getValue("STRAND")); assertEquals("Testing", sf2.getValue("Note")); // shallow clone of otherDetails map - contains the same object values! assertSame(count, sf2.getValue("Count")); + + /* + * copy constructor modifying begin/end/group/score + */ + SequenceFeature sf3 = new SequenceFeature(sf1, 11, 14, "group2", 17.4f); + assertEquals("type", sf3.getType()); + assertEquals("desc", sf3.getDescription()); + assertEquals(11, sf3.getBegin()); + assertEquals(14, sf3.getEnd()); + assertEquals(17.4f, sf3.getScore()); + assertEquals("+", sf3.getValue("STRAND")); + assertEquals("Testing", sf3.getValue("Note")); + // shallow clone of otherDetails map - contains the same object values! + assertSame(count, sf3.getValue("Count")); + + /* + * copy constructor modifying type/begin/end/group/score + */ + SequenceFeature sf4 = new SequenceFeature(sf1, "Disulfide bond", 12, + 15, "group3", -9.1f); + assertEquals("Disulfide bond", sf4.getType()); + assertTrue(sf4.isContactFeature()); + assertEquals("desc", sf4.getDescription()); + assertEquals(12, sf4.getBegin()); + assertEquals(15, sf4.getEnd()); + assertEquals(-9.1f, sf4.getScore()); + assertEquals("+", sf4.getValue("STRAND")); + assertEquals("Testing", sf4.getValue("Note")); + // shallow clone of otherDetails map - contains the same object values! + assertSame(count, sf4.getValue("Count")); } /** diff --git a/test/jalview/datamodel/SequenceGroupTest.java b/test/jalview/datamodel/SequenceGroupTest.java index f6d4028..b0af5c8 100644 --- a/test/jalview/datamodel/SequenceGroupTest.java +++ b/test/jalview/datamodel/SequenceGroupTest.java @@ -9,10 +9,12 @@ import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import jalview.analysis.Conservation; import jalview.schemes.NucleotideColourScheme; import jalview.schemes.PIDColourScheme; import java.awt.Color; +import java.util.Collections; import junit.extensions.PA; @@ -232,6 +234,11 @@ public class SequenceGroupTest sg.setName("g1"); sg.setDescription("desc"); sg.setColourScheme(new PIDColourScheme()); + Conservation cons = new Conservation("Cons", 2, + Collections. emptyList(), 3, 12); + PA.setValue(cons, "consSequence", new Sequence("s", "abc")); + sg.getGroupColourScheme().setConservation(cons); + sg.getGroupColourScheme().setConsensus(new Profiles(null)); sg.setDisplayBoxes(false); sg.setDisplayText(false); sg.setColourText(true); @@ -255,6 +262,10 @@ public class SequenceGroupTest assertEquals(sg2.getDescription(), sg.getDescription()); assertNotSame(sg2.getGroupColourScheme(), sg.getGroupColourScheme()); assertSame(sg2.getColourScheme(), sg.getColourScheme()); + assertSame(PA.getValue(sg2.getGroupColourScheme(), "consensus"), + PA.getValue(sg.getGroupColourScheme(), "consensus")); + assertSame(PA.getValue(sg2.getGroupColourScheme(), "conservation"), + PA.getValue(sg.getGroupColourScheme(), "conservation")); assertEquals(sg2.getDisplayBoxes(), sg.getDisplayBoxes()); assertEquals(sg2.getDisplayText(), sg.getDisplayText()); assertEquals(sg2.getColourText(), sg.getColourText()); diff --git a/test/jalview/gui/AlignFrameTest.java b/test/jalview/gui/AlignFrameTest.java index fed5992..67098ae 100644 --- a/test/jalview/gui/AlignFrameTest.java +++ b/test/jalview/gui/AlignFrameTest.java @@ -86,12 +86,12 @@ public class AlignFrameTest assertFalse(alignFrame.hideFeatureColumns("exon", true)); assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenRegions() + .getHiddenColumnsCopy() .isEmpty()); assertFalse(alignFrame.hideFeatureColumns("exon", false)); assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenRegions() + .getHiddenColumnsCopy() .isEmpty()); /* @@ -101,7 +101,7 @@ public class AlignFrameTest assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); List hidden = alignFrame.getViewport().getAlignment() .getHiddenColumns() - .getHiddenRegions(); + .getHiddenColumnsCopy(); assertTrue(hidden.isEmpty()); /* @@ -111,7 +111,7 @@ public class AlignFrameTest */ assertTrue(alignFrame.hideFeatureColumns("Turn", true)); hidden = alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenRegions(); + .getHiddenColumnsCopy(); assertEquals(hidden.size(), 2); assertEquals(hidden.get(0)[0], 1); assertEquals(hidden.get(0)[1], 3); diff --git a/test/jalview/gui/AlignViewportTest.java b/test/jalview/gui/AlignViewportTest.java index 4660842..b2286e0 100644 --- a/test/jalview/gui/AlignViewportTest.java +++ b/test/jalview/gui/AlignViewportTest.java @@ -46,6 +46,7 @@ import jalview.schemes.ColourSchemeI; import jalview.schemes.PIDColourScheme; import jalview.structure.StructureSelectionManager; import jalview.util.MapList; +import jalview.viewmodel.ViewportRanges; import java.util.ArrayList; import java.util.List; @@ -450,6 +451,65 @@ public class AlignViewportTest } ; Assert.assertEquals(c, 1, "Expected to find one occupancy row."); + } + @Test(groups = { "Functional" }) + public void testGetConsensusSeq() + { + /* + * A-C + * A-C + * A-D + * --D + * consensus expected to be A-C + */ + String fasta = ">s1\nA-C\n>s2\nA-C\n>s3\nA-D\n>s4\n--D\n"; + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta, + DataSourceType.PASTE); + AlignViewport testme = af.getViewport(); + SequenceI cons = testme.getConsensusSeq(); + assertEquals("A-C", cons.getSequenceAsString()); + } + + @Test(groups = { "Functional" }) + public void testHideRevealSequences() + { + ViewportRanges ranges = testee.getRanges(); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); + + /* + * hide first sequence + */ + testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) }); + assertEquals(2, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(1, ranges.getEndSeq()); + + /* + * reveal hidden sequences above the first + */ + testee.showSequence(0); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); + + /* + * hide first and third sequences + */ + testee.hideSequence(new SequenceI[] { al.getSequenceAt(0), + al.getSequenceAt(2) }); + assertEquals(1, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(0, ranges.getEndSeq()); + + /* + * reveal all hidden sequences + */ + testee.showAllHiddenSeqs(); + assertEquals(3, al.getHeight()); + assertEquals(0, ranges.getStartSeq()); + assertEquals(2, ranges.getEndSeq()); } } diff --git a/test/jalview/io/JSONFileTest.java b/test/jalview/io/JSONFileTest.java index 17982be..158c901 100644 --- a/test/jalview/io/JSONFileTest.java +++ b/test/jalview/io/JSONFileTest.java @@ -75,11 +75,11 @@ public class JSONFileTest private Alignment alignment; - private HashMap expectedSeqs = new HashMap(); + private HashMap expectedSeqs = new HashMap<>(); - private HashMap expectedAnnots = new HashMap(); + private HashMap expectedAnnots = new HashMap<>(); - private HashMap expectedGrps = new HashMap(); + private HashMap expectedGrps = new HashMap<>(); private HiddenColumns expectedColSel = new HiddenColumns(); @@ -141,7 +141,7 @@ public class JSONFileTest } // create and add a sequence group - List grpSeqs = new ArrayList(); + List grpSeqs = new ArrayList<>(); grpSeqs.add(seqs[1]); grpSeqs.add(seqs[2]); grpSeqs.add(seqs[3]); @@ -205,7 +205,7 @@ public class JSONFileTest TEST_SEQ_HEIGHT = expectedSeqs.size(); TEST_GRP_HEIGHT = expectedGrps.size(); TEST_ANOT_HEIGHT = expectedAnnots.size(); - TEST_CS_HEIGHT = expectedColSel.getHiddenRegions().size(); + TEST_CS_HEIGHT = expectedColSel.getHiddenColumnsCopy().size(); exportSettings = new AlignExportSettingI() { @@ -325,11 +325,11 @@ public class JSONFileTest { HiddenColumns cs = testJsonFile.getHiddenColumns(); Assert.assertNotNull(cs); - Assert.assertNotNull(cs.getHiddenRegions()); - List hiddenCols = cs.getHiddenRegions(); + Assert.assertNotNull(cs.getHiddenColumnsCopy()); + List hiddenCols = cs.getHiddenColumnsCopy(); Assert.assertEquals(hiddenCols.size(), TEST_CS_HEIGHT); Assert.assertEquals(hiddenCols.get(0), expectedColSel - .getHiddenRegions().get(0), + .getHiddenColumnsCopy().get(0), "Mismatched hidden columns!"); } diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java index 28d608f..7fd7abc 100644 --- a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java @@ -448,4 +448,86 @@ public class FeatureColourFinderTest FeatureColourFinder finder2 = new FeatureColourFinder(null); assertTrue(finder2.noFeaturesDisplayed()); } + + @Test(groups = "Functional") + public void testFindFeatureColour_graduatedWithThreshold() + { + String kdFeature = "kd"; + String metalFeature = "Metal"; + seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 2, + 2, 0f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 4, + 4, 5f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4, + 5f, "MetalGroup")); + seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 7, + 7, 10f, "KdGroup")); + + /* + * kd feature has graduated colour from 0 to 10 + * above threshold value of 5 + */ + Color min = new Color(100, 50, 150); + Color max = new Color(200, 0, 100); + FeatureColourI fc = new FeatureColour(min, max, 0, 10); + fc.setAboveThreshold(true); + fc.setThreshold(5f); + fr.setColour(kdFeature, fc); + FeatureColour green = new FeatureColour(Color.green); + fr.setColour(metalFeature, green); + fr.featuresAdded(); + + /* + * render order is kd above Metal + */ + Object[][] data = new Object[2][]; + data[0] = new Object[] { kdFeature, fc, true }; + data[1] = new Object[] { metalFeature, green, true }; + fr.setFeaturePriority(data); + + av.setShowSequenceFeatures(true); + + /* + * position 2, column 1, score 0 - below threshold - default colour + */ + Color c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, Color.blue); + + /* + * position 4, column 3, score 5 - at threshold + * should return Green (colour of Metal feature) + */ + c = finder.findFeatureColour(Color.blue, seq, 3); + assertEquals(c, Color.green); + + /* + * position 7, column 9, score 10 - maximum colour in range + */ + c = finder.findFeatureColour(Color.blue, seq, 9); + assertEquals(c, max); + + /* + * now colour below threshold of 5 + */ + fc.setBelowThreshold(true); + + /* + * position 2, column 1, score 0 - min colour + */ + c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, min); + + /* + * position 4, column 3, score 5 - at threshold + * should return Green (colour of Metal feature) + */ + c = finder.findFeatureColour(Color.blue, seq, 3); + assertEquals(c, Color.green); + + /* + * position 7, column 9, score 10 - above threshold - default colour + */ + c = finder.findFeatureColour(Color.blue, seq, 9); + assertEquals(c, Color.blue); + } } diff --git a/test/jalview/renderer/seqfeatures/FeatureRendererTest.java b/test/jalview/renderer/seqfeatures/FeatureRendererTest.java index 3e27aba..dc86605 100644 --- a/test/jalview/renderer/seqfeatures/FeatureRendererTest.java +++ b/test/jalview/renderer/seqfeatures/FeatureRendererTest.java @@ -267,15 +267,20 @@ public class FeatureRendererTest fr.filterFeaturesForDisplay(features, null); // empty list, does nothing SequenceI seq = av.getAlignment().getSequenceAt(0); - SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, "group1"); + SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN, + "group1"); seq.addSequenceFeature(sf1); - SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, "group2"); + SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, 2f, + "group2"); seq.addSequenceFeature(sf2); - SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, "group3"); + SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, 3f, + "group3"); seq.addSequenceFeature(sf3); - SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, "group4"); + SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, 4f, + "group4"); seq.addSequenceFeature(sf4); - SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, "group4"); + SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, 5f, + "group4"); seq.addSequenceFeature(sf5); fr.findAllFeatures(true); @@ -329,6 +334,7 @@ public class FeatureRendererTest /* * no filtering of co-located features with graduated colour scheme + * filterFeaturesForDisplay does _not_ check colour threshold * sf2 is removed as its group is hidden */ features = seq.getSequenceFeatures(); diff --git a/test/jalview/schemes/BuriedColourSchemeTest.java b/test/jalview/schemes/BuriedColourSchemeTest.java new file mode 100644 index 0000000..cdb0e80 --- /dev/null +++ b/test/jalview/schemes/BuriedColourSchemeTest.java @@ -0,0 +1,33 @@ +package jalview.schemes; + +import static org.testng.Assert.assertEquals; + +import java.awt.Color; + +import org.testng.annotations.Test; + +public class BuriedColourSchemeTest +{ + /** + * Turn colours are based on the scores in ResidueProperties.buried A = 1.7, R + * = 0.1, N = 0.4, D = 0.4... min = 0.05 max = 4.6 + *

        + * scores are scaled to c 0-1 between min and max and colour is (0, 1-c, c) + */ + @Test(groups = "Functional") + public void testFindColour() + { + ScoreColourScheme scheme = new BuriedColourScheme(); + + float min = 0.05f; + float max = 4.6f; + float a = (1.7f - min) / (max - min); + assertEquals(scheme.findColour('A', 0, null), new Color(0, 1 - a, a)); + + float d = (0.4f - min) / (max - min); + assertEquals(scheme.findColour('D', 0, null), new Color(0, 1 - d, d)); + + assertEquals(scheme.findColour('-', 0, null), Color.WHITE); + } + +} diff --git a/test/jalview/schemes/FeatureColourTest.java b/test/jalview/schemes/FeatureColourTest.java index 1beca80..7a72c15 100644 --- a/test/jalview/schemes/FeatureColourTest.java +++ b/test/jalview/schemes/FeatureColourTest.java @@ -22,6 +22,7 @@ package jalview.schemes; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; @@ -84,62 +85,6 @@ public class FeatureColourTest } @Test(groups = { "Functional" }) - public void testIsColored_simpleColour() - { - FeatureColour fc = new FeatureColour(Color.RED); - assertTrue(fc - .isColored(new SequenceFeature("Cath", "", 1, 2, 0f, null))); - } - - @Test(groups = { "Functional" }) - public void testIsColored_colourByLabel() - { - FeatureColour fc = new FeatureColour(); - fc.setColourByLabel(true); - assertTrue(fc - .isColored(new SequenceFeature("Cath", "", 1, 2, 0f, null))); - } - - @Test(groups = { "Functional" }) - public void testIsColored_aboveThreshold() - { - // graduated colour range from score 20 to 100 - FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 20f, - 100f); - - // score 0 is adjusted to bottom of range - SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 0f, - null); - assertTrue(fc.isColored(sf)); - assertEquals(Color.WHITE, fc.getColor(sf)); - - // score 120 is adjusted to top of range - sf = new SequenceFeature(sf, sf.getBegin(), sf.getEnd(), - sf.getFeatureGroup(), 120f); - assertEquals(Color.BLACK, fc.getColor(sf)); - - // value below threshold is still rendered - // setting threshold has no effect yet... - fc.setThreshold(60f); - sf = new SequenceFeature(sf, sf.getBegin(), sf.getEnd(), - sf.getFeatureGroup(), 36f); - assertTrue(fc.isColored(sf)); - assertEquals(new Color(204, 204, 204), fc.getColor(sf)); - - // now apply threshold: - fc.setAboveThreshold(true); - assertFalse(fc.isColored(sf)); - // colour is still returned though ?!? - assertEquals(new Color(204, 204, 204), fc.getColor(sf)); - - sf = new SequenceFeature(sf, sf.getBegin(), sf.getEnd(), - sf.getFeatureGroup(), 84f); - // above threshold now - assertTrue(fc.isColored(sf)); - assertEquals(new Color(51, 51, 51), fc.getColor(sf)); - } - - @Test(groups = { "Functional" }) public void testGetColor_simpleColour() { FeatureColour fc = new FeatureColour(Color.RED); @@ -176,20 +121,35 @@ public class FeatureColourTest } @Test(groups = { "Functional" }) - public void testGetColor_belowThreshold() + public void testGetColor_aboveBelowThreshold() { // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0) FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f, 150f); SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f, null); + + /* + * feature with score of Float.NaN is always assigned minimum colour + */ + SequenceFeature sf2 = new SequenceFeature("type", "desc", 0, 20, + Float.NaN, null); + fc.setThreshold(100f); // ignore for now - assertTrue(fc.isColored(sf)); assertEquals(new Color(204, 204, 204), fc.getColor(sf)); + assertEquals(Color.white, fc.getColor(sf2)); fc.setAboveThreshold(true); // feature lies below threshold - assertFalse(fc.isColored(sf)); - assertEquals(new Color(204, 204, 204), fc.getColor(sf)); + assertNull(fc.getColor(sf)); + assertEquals(Color.white, fc.getColor(sf2)); + + fc.setBelowThreshold(true); + fc.setThreshold(70f); + assertNull(fc.getColor(sf)); // feature score == threshold - hidden + assertEquals(Color.white, fc.getColor(sf2)); + fc.setThreshold(69f); + assertNull(fc.getColor(sf)); // feature score > threshold - hidden + assertEquals(Color.white, fc.getColor(sf2)); } /** diff --git a/test/jalview/schemes/HelixColourSchemeTest.java b/test/jalview/schemes/HelixColourSchemeTest.java new file mode 100644 index 0000000..ed46419 --- /dev/null +++ b/test/jalview/schemes/HelixColourSchemeTest.java @@ -0,0 +1,33 @@ +package jalview.schemes; + +import static org.testng.Assert.assertEquals; + +import java.awt.Color; + +import org.testng.annotations.Test; + +public class HelixColourSchemeTest +{ + /** + * Turn colours are based on the scores in ResidueProperties.helix A = 1.42, R + * = 0.98, N = 0.67, D = 1.01... min = 0.57 max = 1.51 + *

        + * scores are scaled to c 0-1 between min and max and colour is (c, 1-c, c) + */ + @Test(groups = "Functional") + public void testFindColour() + { + ScoreColourScheme scheme = new HelixColourScheme(); + + float min = 0.57f; + float max = 1.51f; + float a = (1.42f - min) / (max - min); + assertEquals(scheme.findColour('A', 0, null), new Color(a, 1 - a, a)); + + float d = (1.01f - min) / (max - min); + assertEquals(scheme.findColour('D', 0, null), new Color(d, 1 - d, d)); + + assertEquals(scheme.findColour('-', 0, null), Color.WHITE); + } + +} diff --git a/test/jalview/schemes/HydrophobicColourSchemeTest.java b/test/jalview/schemes/HydrophobicColourSchemeTest.java new file mode 100644 index 0000000..f3b93a9 --- /dev/null +++ b/test/jalview/schemes/HydrophobicColourSchemeTest.java @@ -0,0 +1,35 @@ +package jalview.schemes; + +import static org.testng.Assert.assertEquals; + +import java.awt.Color; + +import org.testng.annotations.Test; + +public class HydrophobicColourSchemeTest +{ + /** + * Turn colours are based on the scores in ResidueProperties.hyd A = 1.8, R = + * -4.5, N = -3.5, D = -3.5... min = -3.9 max = 4.5 + *

        + * scores are scaled to c 0-1 between min and max and colour is (c, 0, 1-c) + */ + @Test(groups = "Functional") + public void testFindColour() + { + ScoreColourScheme scheme = new HydrophobicColourScheme(); + + float min = -3.9f; + float max = 4.5f; + float a = (1.8f - min) / (max - min); + assertEquals(scheme.findColour('A', 0, null), + new Color(a, 0, 1 - a)); + + float d = (-3.5f - min) / (max - min); + assertEquals(scheme.findColour('D', 0, null), + new Color(d, 0, 1 - d)); + + assertEquals(scheme.findColour('-', 0, null), Color.WHITE); + } + +} diff --git a/test/jalview/schemes/StrandColourSchemeTest.java b/test/jalview/schemes/StrandColourSchemeTest.java new file mode 100644 index 0000000..4a8d0e9 --- /dev/null +++ b/test/jalview/schemes/StrandColourSchemeTest.java @@ -0,0 +1,35 @@ +package jalview.schemes; + +import static org.testng.Assert.assertEquals; + +import java.awt.Color; + +import org.testng.annotations.Test; + +public class StrandColourSchemeTest +{ + /** + * Turn colours are based on the scores in ResidueProperties.strand A = 0.83, + * R = 0.93, N = 0.89, D = 0.54... min = 0.37 max = 1.7 + *

        + * scores are scaled to c 0-1 between min and max and colour is (c, c, 1-c) + */ + @Test(groups = "Functional") + public void testFindColour() + { + ScoreColourScheme scheme = new StrandColourScheme(); + + float min = 0.37f; + float max = 1.7f; + float a = (0.83f - min) / (max - min); + assertEquals(scheme.findColour('A', 0, null), + new Color(a, a, 1 - a)); + + float d = (0.54f - min) / (max - min); + assertEquals(scheme.findColour('D', 0, null), + new Color(d, d, 1 - d)); + + assertEquals(scheme.findColour('-', 0, null), Color.WHITE); + } + +} diff --git a/test/jalview/schemes/TurnColourSchemeTest.java b/test/jalview/schemes/TurnColourSchemeTest.java new file mode 100644 index 0000000..5e6baf0 --- /dev/null +++ b/test/jalview/schemes/TurnColourSchemeTest.java @@ -0,0 +1,35 @@ +package jalview.schemes; + +import static org.testng.Assert.assertEquals; + +import java.awt.Color; + +import org.testng.annotations.Test; + +public class TurnColourSchemeTest +{ + /** + * Turn colours are based on the scores in ResidueProperties.turn A = 0.66, R + * = 0.95, N = 1.56, D = 1.46... min = 0.47 max = 1.56 + *

        + * scores are scaled to c 0-1 between min and max and colour is (c, 1-c, 1-c) + */ + @Test(groups = "Functional") + public void testFindColour() + { + ScoreColourScheme scheme = new TurnColourScheme(); + + float min = 0.47f; + float max = 1.56f; + float a = (0.66f - min) / (max - min); + assertEquals(scheme.findColour('A', 0, null), + new Color(a, 1 - a, 1 - a)); + + float d = (1.46f - min) / (max - min); + assertEquals(scheme.findColour('D', 0, null), + new Color(d, 1 - d, 1 - d)); + + assertEquals(scheme.findColour('-', 0, null), Color.WHITE); + } + +} diff --git a/test/jalview/schemes/UserColourSchemeTest.java b/test/jalview/schemes/UserColourSchemeTest.java index 497014e..2a482ee 100644 --- a/test/jalview/schemes/UserColourSchemeTest.java +++ b/test/jalview/schemes/UserColourSchemeTest.java @@ -54,6 +54,10 @@ public class UserColourSchemeTest assertEquals(c1, cs.findColour('h')); Color c2 = new Color(10, 20, 30); assertEquals(c2, cs.findColour('c')); + assertEquals(Color.WHITE, cs.findColour('G')); + assertEquals(Color.WHITE, cs.findColour('-')); + assertEquals(Color.WHITE, cs.findColour('.')); + assertEquals(Color.WHITE, cs.findColour(' ')); cs = new UserColourScheme("white"); cs.parseAppletParameter("D,E=red; K,R,H=0022FF; c=10 , 20,30;t=orange;lowercase=blue;s=pink"); @@ -80,4 +84,29 @@ public class UserColourSchemeTest String param = cs.toAppletParameter(); assertEquals("D,E=ff0000;H,K,R=0022ff;c=0a141e", param); } + + /** + * Test for user colour scheme constructed with a colour per residue, + * including gap. Note this can currently be done from the User Defined + * Colours dialog, but not by parsing a colours parameter, as + * parseAppletParameter only recognises amino acid codes. + */ + @Test(groups = "Functional") + public void testConstructor_coloursArray() + { + Color g = Color.green; + Color y = Color.yellow; + Color b = Color.blue; + Color r = Color.red; + // colours for ARNDCQEGHILKMFPSTWYVBZ and gap + Color[] colours = new Color[] { g, y, b, r, g, y, r, b, g, y, r, b, g, + y, r, b, g, y, r, b, g, y, r, g }; + UserColourScheme cs = new UserColourScheme(colours); + + assertEquals(g, cs.findColour('A')); + assertEquals(b, cs.findColour('n')); + assertEquals(g, cs.findColour('-')); + assertEquals(g, cs.findColour('.')); + assertEquals(g, cs.findColour(' ')); + } } diff --git a/test/jalview/util/MappingUtilsTest.java b/test/jalview/util/MappingUtilsTest.java index 19c8438..d0ec3e8 100644 --- a/test/jalview/util/MappingUtilsTest.java +++ b/test/jalview/util/MappingUtilsTest.java @@ -689,7 +689,7 @@ public class MappingUtilsTest AlignedCodonFrame acf3 = new AlignedCodonFrame(); acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map); - List mappings = new ArrayList(); + List mappings = new ArrayList<>(); mappings.add(acf1); mappings.add(acf2); mappings.add(acf3); @@ -764,7 +764,7 @@ public class MappingUtilsTest AlignedCodonFrame acf4 = new AlignedCodonFrame(); acf4.addMap(seq3.getDatasetSequence(), seq4.getDatasetSequence(), map); - List mappings = new ArrayList(); + List mappings = new ArrayList<>(); mappings.add(acf1); mappings.add(acf2); mappings.add(acf3); @@ -821,7 +821,7 @@ public class MappingUtilsTest AlignedCodonFrame acf = new AlignedCodonFrame(); MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3, 1); acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map); - List mappings = new ArrayList(); + List mappings = new ArrayList<>(); mappings.add(acf); AlignmentI prot = new Alignment(new SequenceI[] { protein }); @@ -913,7 +913,7 @@ public class MappingUtilsTest MappingUtils.mapColumnSelection(proteinSelection, hiddenCols, proteinView, dnaView, dnaSelection, dnaHidden); assertEquals("[]", dnaSelection.getSelected().toString()); - List hidden = dnaHidden.getHiddenRegions(); + List hidden = dnaHidden.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[0, 4]", Arrays.toString(hidden.get(0))); @@ -930,7 +930,7 @@ public class MappingUtilsTest proteinSelection.hideSelectedColumns(1, hiddenCols); MappingUtils.mapColumnSelection(proteinSelection, hiddenCols, proteinView, dnaView, dnaSelection, dnaHidden); - hidden = dnaHidden.getHiddenRegions(); + hidden = dnaHidden.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[0, 3]", Arrays.toString(hidden.get(0))); @@ -944,7 +944,7 @@ public class MappingUtilsTest proteinSelection.hideSelectedColumns(2, hiddenCols); MappingUtils.mapColumnSelection(proteinSelection, hiddenCols, proteinView, dnaView, dnaSelection, dnaHidden); - assertTrue(dnaHidden.getHiddenRegions().isEmpty()); + assertTrue(dnaHidden.getHiddenColumnsCopy().isEmpty()); /* * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns @@ -959,7 +959,7 @@ public class MappingUtilsTest MappingUtils.mapColumnSelection(proteinSelection, hiddenCols, proteinView, dnaView, dnaSelection, dnaHidden); assertEquals("[0, 1, 2, 3]", dnaSelection.getSelected().toString()); - hidden = dnaHidden.getHiddenRegions(); + hidden = dnaHidden.getHiddenColumnsCopy(); assertEquals(1, hidden.size()); assertEquals("[5, 10]", Arrays.toString(hidden.get(0))); @@ -974,7 +974,7 @@ public class MappingUtilsTest proteinSelection.hideSelectedColumns(3, hiddenCols); MappingUtils.mapColumnSelection(proteinSelection, hiddenCols, proteinView, dnaView, dnaSelection, dnaHidden); - hidden = dnaHidden.getHiddenRegions(); + hidden = dnaHidden.getHiddenColumnsCopy(); assertEquals(2, hidden.size()); assertEquals("[0, 3]", Arrays.toString(hidden.get(0))); assertEquals("[5, 10]", Arrays.toString(hidden.get(1))); @@ -988,7 +988,7 @@ public class MappingUtilsTest /* * [start, end] ranges */ - List ranges = new ArrayList(); + List ranges = new ArrayList<>(); assertEquals(0, MappingUtils.getLength(ranges)); ranges.add(new int[] { 1, 1 }); assertEquals(1, MappingUtils.getLength(ranges)); @@ -1011,7 +1011,7 @@ public class MappingUtilsTest public void testContains() { assertFalse(MappingUtils.contains(null, 1)); - List ranges = new ArrayList(); + List ranges = new ArrayList<>(); assertFalse(MappingUtils.contains(ranges, 1)); ranges.add(new int[] { 1, 4 }); diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index 636f8dd..74cd8f9 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -1,6 +1,7 @@ package jalview.viewmodel; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import jalview.analysis.AlignmentGenerator; @@ -14,6 +15,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -25,6 +27,14 @@ public class ViewportRangesTest { AlignmentI smallAl = gen.generate(7, 2, 2, 5, 5); + @BeforeClass(alwaysRun = true) + public void setUp() + { + gen = new AlignmentGenerator(false); + al = gen.generate(20, 30, 1, 5, 5); + smallAl = gen.generate(7, 2, 2, 5, 5); + } + @BeforeMethod(alwaysRun = true) public void cleanUp() { @@ -65,17 +75,6 @@ public class ViewportRangesTest { } @Test(groups = { "Functional" }) - public void testSetEndRes() - { - ViewportRanges vr = new ViewportRanges(al); - vr.setEndRes(-1); - assertEquals(vr.getEndRes(), 0); - - vr.setEndRes(al.getWidth() - 1); - assertEquals(vr.getEndRes(), al.getWidth() - 1); - } - - @Test(groups = { "Functional" }) public void testSetEndSeq() { ViewportRanges vr = new ViewportRanges(al); @@ -85,7 +84,8 @@ public class ViewportRangesTest { vr.setEndSeq(al.getHeight()); assertEquals(vr.getEndSeq(), al.getHeight() - 1); - vr.setEndRes(al.getHeight() - 1); + // vr.setEndRes(al.getHeight() - 1); + vr.setEndSeq(al.getHeight() - 1); assertEquals(vr.getEndSeq(), al.getHeight() - 1); } @@ -347,17 +347,41 @@ public class ViewportRangesTest { @Test(groups = { "Functional" }) public void testScrollToWrappedVisible() { - ViewportRanges vr = new ViewportRanges(al); + AlignmentI al2 = gen.generate(60, 30, 1, 5, 5); + + ViewportRanges vr = new ViewportRanges(al2); + + // start with viewport on 5-14 vr.setViewportStartAndWidth(5, 10); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 14); + + // scroll to 12 - no change + assertFalse(vr.scrollToWrappedVisible(12)); + assertEquals(vr.getStartRes(), 5); - vr.scrollToWrappedVisible(0); + // scroll to 2 - back to 0-9 + assertTrue(vr.scrollToWrappedVisible(2)); assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 9); - vr.scrollToWrappedVisible(10); - assertEquals(vr.getStartRes(), 10); + // scroll to 9 - no change + assertFalse(vr.scrollToWrappedVisible(9)); + assertEquals(vr.getStartRes(), 0); - vr.scrollToWrappedVisible(15); + // scroll to 12 - moves to 10-19 + assertTrue(vr.scrollToWrappedVisible(12)); assertEquals(vr.getStartRes(), 10); + assertEquals(vr.getEndRes(), 19); + + vr.setStartRes(13); + assertEquals(vr.getStartRes(), 13); + assertEquals(vr.getEndRes(), 22); + + // scroll to 45 - jumps to 43-52 + assertTrue(vr.scrollToWrappedVisible(45)); + assertEquals(vr.getStartRes(), 43); + assertEquals(vr.getEndRes(), 52); } // leave until JAL-2388 is merged and we can do without viewport @@ -401,15 +425,6 @@ public class ViewportRangesTest { assertTrue(l.verify(0, emptylist)); l.reset(); - vr.setEndRes(10); - assertTrue(l.verify(1, Arrays.asList("startres"))); - l.reset(); - - // no event fired for same value - vr.setEndRes(10); - assertTrue(l.verify(0, emptylist)); - l.reset(); - vr.setStartSeq(4); assertTrue(l.verify(1, Arrays.asList("startseq"))); l.reset(); @@ -523,6 +538,223 @@ public class ViewportRangesTest { assertTrue(l.verify(1, Arrays.asList("startres"))); l.reset(); } + + @Test(groups = { "Functional" }) + public void testGetWrappedScrollPosition() + { + AlignmentI al2 = gen.generate(157, 15, 1, 5, 5); + ViewportRanges vr = new ViewportRanges(al2); + vr.setStartEndRes(0, 39); + int width = vr.getViewportWidth(); // 40 + + /* + * scroll is 0 at column 0 (only) + */ + assertEquals(vr.getWrappedScrollPosition(0), 0); + + /* + * scroll is 1 at columns 1-40 + */ + int i = 1; + int j = width; + for (; i <= j; i++) + { + assertEquals(1, vr.getWrappedScrollPosition(i)); + } + + /* + * scroll is 2 at columns 41-80, etc + */ + j += width; + for (; i <= j; i++) + { + assertEquals(2, vr.getWrappedScrollPosition(i), "For " + i); + } + } + + @Test(groups = { "Functional" }) + public void testPageUpDownWrapped() + { + /* + * 15 sequences, 110 residues wide (+gaps) + */ + AlignmentI al2 = gen.generate(110, 15, 1, 5, 5); + + ViewportRanges vr = new ViewportRanges(al2); + vr.setWrappedMode(true); + + // first row + vr.setViewportStartAndWidth(0, 40); + int width = vr.getViewportWidth(); + assertEquals(width, 40); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // second row + vr.pageDown(); + assertEquals(vr.getStartRes(), 40); + assertEquals(vr.getEndRes(), 79); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // third and last row + // note endRes is nominal (>width) to preserve viewport width + vr.pageDown(); + assertEquals(vr.getStartRes(), 80); + assertEquals(vr.getEndRes(), 119); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // another pageDown should do nothing + vr.pageDown(); + assertEquals(vr.getStartRes(), 80); + assertEquals(vr.getEndRes(), 119); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // back to second row + vr.pageUp(); + assertEquals(vr.getStartRes(), 40); + assertEquals(vr.getEndRes(), 79); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // back to first row + vr.pageUp(); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + // another pageUp should do nothing + vr.pageUp(); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + assertEquals(vr.getStartSeq(), 0); + assertEquals(vr.getEndSeq(), 14); + + /* + * simulate scroll right a few positions + */ + vr.setStartRes(5); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 5 + width - 1); // 44 + + vr.pageDown(); // 5-44 shifts to 45-84 + assertEquals(vr.getStartRes(), 45); + assertEquals(vr.getEndRes(), 84); + + vr.pageDown(); // 45-84 shifts to 85-124 + assertEquals(vr.getStartRes(), 85); + assertEquals(vr.getEndRes(), 124); + + vr.pageDown(); // no change - at end already + assertEquals(vr.getStartRes(), 85); + assertEquals(vr.getEndRes(), 124); + + vr.pageUp(); // back we go + assertEquals(vr.getStartRes(), 45); + assertEquals(vr.getEndRes(), 84); + + vr.pageUp(); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 44); + + vr.pageUp(); // back to the start + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 39); + } + + @Test(groups = { "Functional" }) + public void testSetStartEndResWrapped() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setWrappedMode(true); + vr.setStartEndRes(-1, -1); + assertEquals(vr.getStartRes(), 0); + assertEquals(vr.getEndRes(), 0); + + vr.setStartEndRes(5, 19); + assertEquals(vr.getStartRes(), 5); + assertEquals(vr.getEndRes(), 19); + + // bounds are not constrained to alignment width + // when in wrapped mode + vr.setStartEndRes(88, 888); + assertEquals(vr.getStartRes(), 88); + assertEquals(vr.getEndRes(), 888); + + ViewportRanges vrsmall = new ViewportRanges(smallAl); + vrsmall.setWrappedMode(true); + vrsmall.setStartEndRes(88, 888); + assertEquals(vrsmall.getStartRes(), 88); + assertEquals(vrsmall.getEndRes(), 888); + + // make visible alignment width = 0 + smallAl.getHiddenColumns().hideColumns(0, 6); + vrsmall.setStartEndRes(0, 4); + assertEquals(vrsmall.getStartRes(), 0); + assertEquals(vrsmall.getEndRes(), 4); + } + + @Test(groups = { "Functional" }) + public void testSetViewportStartAndWidthWrapped() + { + ViewportRanges vr = new ViewportRanges(al); + vr.setWrappedMode(true); + vr.setViewportStartAndWidth(2, 6); + assertEquals(vr.getViewportWidth(), 6); + assertEquals(vr.getStartRes(), 2); + + // reset -ve values of start to 0 + vr.setViewportStartAndWidth(-1, 7); + assertEquals(vr.getViewportWidth(), 7); + assertEquals(vr.getStartRes(), 0); + + // out of bounds values are not forced to within bounds + vr.setViewportStartAndWidth(35, 5); + assertEquals(vr.getViewportWidth(), 5); + assertEquals(vr.getStartRes(), 35); + + // small alignment doesn't get bounds reset + ViewportRanges vrsmall = new ViewportRanges(smallAl); + vrsmall.setViewportStartAndWidth(0, 63); + assertEquals(vrsmall.getViewportWidth(), 7); + assertEquals(vrsmall.getStartRes(), 0); + } + + @Test(groups = { "Functional" }) + public void testGetWrappedMaxScroll() + { + // generate an ungapped alignment of width 140 + int alignmentWidth = 140; + AlignmentI al2 = gen.generate(alignmentWidth, 15, 1, 0, 5); + ViewportRanges vr = new ViewportRanges(al2); + vr.setStartEndRes(0, 39); + int width = vr.getViewportWidth(); // 40 + int partWidth = alignmentWidth % width; // 20 + + /* + * there are 3 * 40 remainder 20 residues + * number of widths depends on offset (scroll right) + * 4 widths (maxScroll = 3) if offset by 0 or more than 19 columns + * 5 widths (maxScroll = 4) if 1 <= offset <= 19 + */ + for (int col = 0; col < alignmentWidth; col++) + { + int offset = col % width; + if (offset > 0 && offset < partWidth) + { + assertEquals(vr.getWrappedMaxScroll(col), 4, "col " + col); + } + else + { + assertEquals(vr.getWrappedMaxScroll(col), 3, "col " + col); + } + } + } } // mock listener for property change events diff --git a/utils/InstallAnywhere/Jalview.iap_xml b/utils/InstallAnywhere/Jalview.iap_xml index 3cfc2bb..557700a 100755 --- a/utils/InstallAnywhere/Jalview.iap_xml +++ b/utils/InstallAnywhere/Jalview.iap_xml @@ -1453,7 +1453,7 @@ and any path to a file to save to the file]]> - + false -- 1.7.10.2