<tocitem text="Jalview Documentation" target="home" expand="true">
<tocitem text="What's new" target="new" expand="true">
<tocitem text="Latest Release Notes" target="release"/>
-
</tocitem>
<tocitem text="Editing Alignments" target="edit" />
<li><p>
<strong>Sort by Pairwise Identity</strong>
</p>
- <p>Places pairs of sequences together that align with the
- greatest fraction of conserved residues.</p>
+ <p>Sorts sequences in the selection or alignment according to percent identity with respect to the first sequence in the view.</p>
<p></li>
<li><p>
<strong>Sort by Tree Order</strong>
<li><strong>Viewing Cached Structures</strong><br />If
previously downloaded structures are available for your
sequences, the structure chooser will automatically offer them
- via the <strong>Cached PDB Entries</strong> view. If you wish
+ via the <strong>Cached Structures</strong> view. If you wish
to download new structures, select one of the PDBe selection
criteria from the drop-down menu.</li>
</ul></li>
<tr>
<td width="60" nowrap>
<div align="center">
- <strong><a name="Jalview.2.10.3">2.10.3</a><br />
- <em>14/11/2017</em></strong>
+ <strong><a name="Jalview.2.10.3">2.10.3</a><br /> <em>17/11/2017</em></strong>
</div>
</td>
<td><div align="left">
<!-- JAL-2773 -->Structure views don't get updated unless
their colours have changed
</li>
- <li><!-- JAL-2495 -->All linked sequences are highlighted for a structure mousover (Jmol) or selection (Chimera)</li>
- <li><!-- JAL-2790 -->'Cancel' button in progress bar for JABAWS AACon, RNAAliFold and Disorder prediction jobs
+ <li>
+ <!-- JAL-2495 -->All linked sequences are highlighted for
+ a structure mousover (Jmol) or selection (Chimera)
+ </li>
+ <li>
+ <!-- JAL-2790 -->'Cancel' button in progress bar for
+ JABAWS AACon, RNAAliFold and Disorder prediction jobs
+ </li>
+ <li>
+ <!-- JAL-2617 -->Stop codons are excluded in CDS/Protein
+ view from Ensembl locus cross-references
+ </li>
+ <li>
+ <!-- JAL-2685 -->Start/End limits are shown in Pairwise
+ Alignment report
+ </li>
+ <li>
+ <!-- JAL-2810 -->Sequence fetcher's Free text 'autosearch'
+ feature can be disabled
+ </li>
+ <li>
+ <!-- JAL-2810 -->Retrieve IDs tab added for UniProt and
+ PDB easier retrieval of sequences for lists of IDs
+ </li>
+ <li>
+ <!-- JAL-2758 -->Short names for sequences retrieved from
+ Uniprot
</li>
-
- <li><!-- JAL-2617 -->Stop codons are excluded in CDS/Protein view from Ensembl locus cross-references</li>
- <li><!-- JAL-2685 -->Start/End limits are shown in Pairwise Alignment report</li>
- <li><!-- JAL-2810 -->Sequence fetcher's Free text 'autosearch' feature can be disabled</li>
- <li><!-- JAL-2810 -->Retrieve IDs tab added for UniProt and PDB easier retrieval of sequences for lists of IDs</li>
-
</ul>
<em>Scripting</em>
<ul>
- <li>Groovy interpreter updated to 2.4.12</li>
- <li>Example groovy script for generating a matrix of percent identity scores for current alignment.</li>
+ <li>Groovy interpreter updated to 2.4.12</li>
+ <li>Example groovy script for generating a matrix of
+ percent identity scores for current alignment.</li>
</ul>
<em>Testing and Deployment</em>
- <ul><li><!-- JAL-2727 -->Test to catch memory leaks in Jalview UI</li></ul>
- </div>
- </td>
+ <ul>
+ <li>
+ <!-- JAL-2727 -->Test to catch memory leaks in Jalview UI
+ </li>
+ </ul>
+ </div></td>
<td><div align="left">
<em>General</em>
<ul>
- <li><!-- JAL-2643 -->Pressing tab after updating the colour threshold text field doesn't trigger an update to the alignment view</li>
- <li><!-- JAL-2682 -->Race condition when parsing sequence ID strings in parallel</li>
- <li><!-- JAL-2608 -->Overview windows are also closed when alignment window is closed</li>
- <li><!-- JAL-2548 -->Export of features doesn't always respect group visibility</li>
+ <li>
+ <!-- JAL-2643 -->Pressing tab after updating the colour
+ threshold text field doesn't trigger an update to the
+ alignment view
+ </li>
+ <li>
+ <!-- JAL-2682 -->Race condition when parsing sequence ID
+ strings in parallel
+ </li>
+ <li>
+ <!-- JAL-2608 -->Overview windows are also closed when
+ alignment window is closed
+ </li>
+ <li>
+ <!-- JAL-2548 -->Export of features doesn't always respect
+ group visibility
+ </li>
+ <li>
+ <!-- JAL-2831 -->Jumping from column 1 to column 100,000
+ takes a long time in Cursor mode
+ </li>
</ul>
<em>Desktop</em>
<ul>
- <li><!-- JAL-2777 -->Structures with whitespace chainCode cannot be viewed in Chimera</li>
- <li><!-- JAL-2728 -->Protein annotation panel too high in CDS/Protein view
- </li>
- <li><!-- JAL-2757 -->Can't edit the query after the server error warning icon is shown in Uniprot and PDB Free Text Search Dialogs
- </li>
- <li><!-- JAL-2253 -->Slow EnsemblGenome ID lookup</li>
- <li><!-- JAL-2529 -->Revised Ensembl REST API CDNA query</li>
- <li><!-- JAL-2739 -->Hidden column marker in last column not rendered when switching back from Wrapped to normal view</li>
- <li><!-- JAL-2768 -->Annotation display corrupted when scrolling right in unwapped alignment view</li>
- <li><!-- JAL-2542 -->Existing features on subsequence incorrectly relocated when full sequence retrieved from database</li>
- <li><!-- JAL-2733 -->Last reported memory still shown when Desktop->Show Memory is unticked (OSX only)</li>
- <li><!-- JAL-2658 -->Amend Features dialog doesn't allow features of same type and group to be selected for amending</li>
- <li><!-- JAL-2524 -->Jalview becomes sluggish in wide alignments when hidden columns are present</li>
- <li><!-- JAL-2392 -->Jalview freezes when loading and displaying several structures</li>
- <li><!-- JAL-2732 -->Black outlines left after resizing or moving a window</li>
- <li><!-- JAL-1900,JAL-1625 -->Unable to minimise windows within the Jalview desktop on OSX</li>
- <li><!-- JAL-2667 -->Mouse wheel doesn't scroll vertically when in wrapped alignment mode</li>
- <li><!-- JAL-2636 -->Scale mark not shown when close to right hand end of alignment</li>
- <li><!-- JAL-2684 -->Pairwise alignment only aligns selected regions of each selected sequence</li>
- <li><!-- JAL-2973 -->Alignment ruler height set incorrectly after canceling the Alignment Window's Font dialog</li>
- <li><!-- JAL-2036 -->Show cross-references not enabled after restoring project until a new view is created</li>
- <li><!-- JAL-2756 -->Warning popup about use of SEQUENCE_ID in URL links appears when only default EMBL-EBI link is configured (since 2.10.2b2)</li>
- <li><!-- JAL-2775 -->Overview redraws whole window when box position is adjusted</li>
- <li><!-- JAL-2225 -->Structure viewer doesn't map all chains in a multi-chain structure when viewing alignment involving more than one chain (since 2.10)</li>
- </ul>
- <strong><em>Applet</em></strong><br/>
- <ul>
- <li><!-- JAL-2687 -->Concurrent modification exception when closing alignment panel</li>
+ <li>
+ <!-- JAL-2777 -->Structures with whitespace chainCode
+ cannot be viewed in Chimera
+ </li>
+ <li>
+ <!-- JAL-2728 -->Protein annotation panel too high in
+ CDS/Protein view
+ </li>
+ <li>
+ <!-- JAL-2757 -->Can't edit the query after the server
+ error warning icon is shown in Uniprot and PDB Free Text
+ Search Dialogs
+ </li>
+ <li>
+ <!-- JAL-2253 -->Slow EnsemblGenome ID lookup
+ </li>
+ <li>
+ <!-- JAL-2529 -->Revised Ensembl REST API CDNA query
+ </li>
+ <li>
+ <!-- JAL-2739 -->Hidden column marker in last column not
+ rendered when switching back from Wrapped to normal view
+ </li>
+ <li>
+ <!-- JAL-2768 -->Annotation display corrupted when
+ scrolling right in unwapped alignment view
+ </li>
+ <li>
+ <!-- JAL-2542 -->Existing features on subsequence
+ incorrectly relocated when full sequence retrieved from
+ database
+ </li>
+ <li>
+ <!-- JAL-2733 -->Last reported memory still shown when
+ Desktop->Show Memory is unticked (OSX only)
+ </li>
+ <li>
+ <!-- JAL-2658 -->Amend Features dialog doesn't allow
+ features of same type and group to be selected for
+ amending
+ </li>
+ <li>
+ <!-- JAL-2524 -->Jalview becomes sluggish in wide
+ alignments when hidden columns are present
+ </li>
+ <li>
+ <!-- JAL-2392 -->Jalview freezes when loading and
+ displaying several structures
+ </li>
+ <li>
+ <!-- JAL-2732 -->Black outlines left after resizing or
+ moving a window
+ </li>
+ <li>
+ <!-- JAL-1900,JAL-1625 -->Unable to minimise windows
+ within the Jalview desktop on OSX
+ </li>
+ <li>
+ <!-- JAL-2667 -->Mouse wheel doesn't scroll vertically
+ when in wrapped alignment mode
+ </li>
+ <li>
+ <!-- JAL-2636 -->Scale mark not shown when close to right
+ hand end of alignment
+ </li>
+ <li>
+ <!-- JAL-2684 -->Pairwise alignment of selected regions of
+ each selected sequence do not have correct start/end
+ positions
+ </li>
+ <li>
+ <!-- JAL-2793 -->Alignment ruler height set incorrectly
+ after canceling the Alignment Window's Font dialog
+ </li>
+ <li>
+ <!-- JAL-2036 -->Show cross-references not enabled after
+ restoring project until a new view is created
+ </li>
+ <li>
+ <!-- JAL-2756 -->Warning popup about use of SEQUENCE_ID in
+ URL links appears when only default EMBL-EBI link is
+ configured (since 2.10.2b2)
+ </li>
+ <li>
+ <!-- JAL-2775 -->Overview redraws whole window when box
+ position is adjusted
+ </li>
+ <li>
+ <!-- JAL-2225 -->Structure viewer doesn't map all chains
+ in a multi-chain structure when viewing alignment
+ involving more than one chain (since 2.10)
+ </li>
+ <li>
+ <!-- JAL-2811 -->Double residue highlights in cursor mode
+ if new selection moves alignment window
+ </li>
+ <li>
+ <!-- JAL-2837,JAL-2840 -->Alignment vanishes when using
+ arrow key in cursor mode to pass hidden column marker
+ </li>
+ <li>
+ <!-- JAL-2679 -->Ensembl Genomes example ID changed to one
+ that produces correctly annotated transcripts and products
+ </li>
+ <li>
+ <!-- JAL-2776 -->Toggling a feature group after first time
+ doesn't update associated structure view
+ </li>
</ul>
- <strong><em>BioJSON</em></strong><br/>
+ <em>Applet</em><br />
<ul>
- <li>
- <!-- JAL-2546 -->BioJSON export does not preserve non-positional features
- </li>
+ <li>
+ <!-- JAL-2687 -->Concurrent modification exception when
+ closing alignment panel
+ </li>
</ul>
- <strong>Known Java 9 Issues</strong>
+ <em>BioJSON</em><br />
<ul>
- <li><!-- JAL-2902 -->Groovy Console very slow to open and is
- not responsive when entering characters (Webstart, Java 9.01,
- OSX 10.10)
+ <li>
+ <!-- JAL-2546 -->BioJSON export does not preserve
+ non-positional features
</li>
</ul>
- <strong>New Known Issues</strong>
+ <em>New Known Issues</em>
<ul>
- <li><!-- JAL- --></li>
+ <li>
+ <!-- JAL-2541 -->Delete/Cut selection doesn't relocate
+ sequence features correctly (for many previous versions of
+ Jalview)
+ </li>
+ <li>
+ <!-- JAL-2841 -->Cursor mode unexpectedly scrolls when
+ using cursor in wrapped panel other than top
+ </li>
+ <li>
+ <!-- JAL-2791 -->Select columns containing feature ignores
+ graduated colour threshold
+ </li>
+ <li>
+ <!-- JAL-2822,JAL-2823 -->Edit sequence operation doesn't
+ always preserve numbering and sequence features
+ </li>
</ul>
- </div>
- </td>
+ <em>Known Java 9 Issues</em>
+ <ul>
+ <li>
+ <!-- JAL-2902 -->Groovy Console very slow to open and is
+ not responsive when entering characters (Webstart, Java
+ 9.01, OSX 10.10)
+ </li>
+ </ul>
+ </div></td>
</tr>
<tr>
<td width="60" nowrap>
<strong>What's new in Jalview 2.10.3 ?</strong>
</p>
<p>
- Version 2.10.3 was released in November 2017. The full list of
- bug fixes and new features can be found in the <a
- href="releases.html#Jalview.2.10.3"> 2.10.3 Release Notes</a>, but
- the highlights are below.
+ Version 2.10.3 was released in November 2017. The major focus was to
+ improve Jalview's sequence features datamodel and the scalability of
+ the alignment rendering system. The full list of bug fixes and new
+ features can be found in the <a href="releases.html#Jalview.2.10.3">2.10.3
+ Release Notes</a>. Key improvements include:
</p>
<ul>
- <li>Faster import and more responsive UI when working with wide alignments and handling hundreds and thousands of sequence features</li>
- <li>
- <li>Improved usability with <a href="features/pdbsequencefetcher.html">PDB</a> and
- <a href="features/uniprotsequencefetcher.html">UniProt</a> Free Text Search
- dialog, and new tab for retrieval of sequences for lists of IDs.</li>
+ <li>Faster and more responsive UI when importing and working
+ with wide alignments and handling hundreds and thousands of
+ sequence features</li>
+ <li>Improved usability with <a
+ href="features/pdbsequencefetcher.html">PDB</a> and <a
+ href="features/uniprotsequencefetcher.html">UniProt</a> Free Text
+ Search dialog, and new tab for retrieval of sequences for lists of
+ IDs.
+ </li>
+ <li>Short names assigned to sequences retrieved from UniProt</li>
+ <li>Groovy console upgraded to 2.4.12 (improved support for Java 9)</li>
</ul>
<p>
<strong><a name="experimental">Experimental Features</a></strong>
</p>
<p>
- This release of Jalview introduces an <em>Experimental Features</em>
- option in the Jalview Desktop's <em>Tools</em> menu that allows you
- to try out features that are still in development. To access the
- experimental features below - first enable the <strong>Tools→Enable
- Experimental Features</strong> option, and then restart Jalview.
+ Remember, please enable the <em>Experimental Features</em> option in
+ the Jalview Desktop's <em>Tools</em> menu, and then restart Jalview
+ if you want to try out features below:
</p>
<ul>
<li><em>Annotation transfer between Chimera and Jalview</em><br />Two
the Chimera viewer's Chimera menu</a> allow positional annotation to
be exchanged between Chimera and Jalview.</li>
</ul>
-
+
</body>
</html>
label.about = About...
label.show_sequence_limits = Show Sequence Limits
action.feature_settings = Feature Settings...
-label.feature_settings = Feature Settings
label.all_columns = All Columns
label.all_sequences = All Sequences
label.selected_columns = Selected Columns
label.group_colour = Group Colour
label.sequence = Sequence
label.view_pdb_structure = View PDB Structure
-label.min_value = Min value:
-label.max_value = Max value:
-label.no_value = No value:
+label.min_value = Min value
+label.max_value = Max value
+label.no_value = No value
label.new_feature = New Feature
label.match_case = Match Case
label.view_alignment_editor = View in alignment editor
label.threshold_feature_below_threshold = Below Threshold
label.adjust_threshold = Adjust threshold
label.toggle_absolute_relative_display_threshold = Toggle between absolute and relative display threshold.
-label.colour_by_label_tip = Display features of the same type with a different label using a different colour. (e.g. domain features)
label.select_colour_minimum_value = Select Colour for Minimum Value
label.select_colour_maximum_value = Select Colour for Maximum Value
label.open_url_param = Open URL {0}
label.service_called_is_not_seq_search_service = The Service called \n{0}\nis not a \nSequence Search Service\!
label.seq_search_service_is_unknown = The Sequence Search Service named {0} is unknown
label.feature_type = Feature Type
-label.display = Display
+label.show = Show
label.service_url = Service URL
label.copied_sequences = Copied sequences
label.cut_sequences = Cut Sequences
label.matchCondition_gt = >
label.matchCondition_ge = >=
label.numeric_required = The value should be numeric
-label.no_attributes = No attributes known
-label.no_numeric_attributes = No numeric attributes known
+label.filter = Filter
label.filters = Filters
-label.match_condition = Match condition
label.join_conditions = Join conditions with
-label.feature_to_filter = Feature to filter
-label.colour_by_value = Colour by value
-label.colour_by_text = Colour by text
label.score = Score
-label.attribute = Attribute
label.colour_by_label = Colour by label
label.variable_colour = Variable colour
-label.select_new_colour = Select new colour
-label.no_feature_attributes = No feature attributes found
+label.select_colour = Select colour
option.enable_disable_autosearch = When ticked, search is performed automatically.
option.autosearch = Autosearch
label.retrieve_ids = Retrieve IDs
+label.display_settings_for = Display settings for {0} features
+label.simple = Simple
+label.simple_colour = Simple Colour
+label.colour_by_text = Colour by text
+label.graduated_colour = Graduated Colour
+label.by_text_of = By text of
+label.by_range_of = By range of
+label.filters_tooltip = Click to set or amend filters
+label.or = Or
+label.and = And
\ No newline at end of file
label.documentation = Documentación
label.about = Acerca de...
label.show_sequence_limits = Mostrar los lÃmites de la secuencia
-label.feature_settings = Ajustar funciones...
label.all_columns = Todas las columnas
label.all_sequences = Todas las secuencias
label.selected_columns = Columnas seleccionadas
label.autocalculated_annotation = Anotación autocalculada
label.min_colour = Color mÃnimo
label.max_colour = Color máximo
+label.no_colour = Sin color
label.use_original_colours = Usar colores originales
label.threshold_minmax = El umbral es mÃn/máx
label.represent_group_with = Representar al grupo con
label.group_colour = Color del grupo
label.sequence = Secuencia
label.view_pdb_structure = Ver estructura PDB
-label.min = MÃn:
-label.max = Máx:
+label.max_value = Valor máximo
+label.min_value = Valor mÃnimo
+label.no_value = Sin valor
label.colour_by_label = Color por etiquetas
label.new_feature = Nueva función
label.match_case = Hacer corresponder mayúsculas y minúsculas
label.view_full_application = Ver en la aplicación completa
label.load_associated_tree = Cargar árbol asociado ...
label.load_features_annotations = Cargar caracterÃsticas/anotaciones ...
+label.load_vcf = Cargar variantes SNP desde fichero VCF texto o tab-indexado
+label.load_vcf_file = Cargar fichero VCF
+label.searching_vcf = Cargando variantes VCF...
+label.added_vcf= {0} variantes VCF añadidas a {1} secuencia(s)
label.export_features = Exportar caracterÃsticas...
label.export_annotations = Exportar anotaciones ...
label.to_upper_case = Pasar a mayúsculas
label.threshold_feature_below_threshold = Por debajo del umbral
label.adjust_threshold = Ajustar umbral
label.toggle_absolute_relative_display_threshold = Cambiar entre mostrar el umbral absoluto y el relativo.
-label.display_features_same_type_different_label_using_different_colour = Mostrar las caracterÃsticas del mismo tipo con una etiqueta diferente y empleando un color distinto (p.e. caracterÃsticas del dominio)
label.select_colour_minimum_value = Seleccionar el color para el valor mÃnimo
label.select_colour_maximum_value = Seleccionar el color para el valor máximo
label.open_url_param = Abrir URL {0}
label.service_called_is_not_seq_search_service = El Servicio llamando \n{0}\nno es un \nServicio de B\u00FAsqueda de Secuencias\!
label.seq_search_service_is_unknown = El Servicio de Búsqueda de Sencuencias llamado {0} es desconocido
label.feature_type = Tipo de caracterÃstisca
-label.display = Representación
+label.show = Mostrar
label.service_url = URL del servicio
label.copied_sequences = Secuencias copiadas
label.cut_sequences = Cortar secuencias
label.overview = Resumen
label.reset_to_defaults = Restablecen a los predeterminados
label.oview_calc = Recalculando resumen
+label.feature_details = Detalles de caracterÃstica
+label.matchCondition_contains = Contiene
+label.matchCondition_notcontains = No contiene
+label.matchCondition_matches = Es igual a
+label.matchCondition_notmatches = No es igual a
+label.matchCondition_eq = =
+label.matchCondition_ne = not =
+label.matchCondition_lt = <
+label.matchCondition_le = <=
+label.matchCondition_gt = >
+label.matchCondition_ge = >=
+label.numeric_required = Valor numérico requerido
+label.filter = Filtro
+label.filters = Filtros
+label.join_conditions = Combinar condiciones con
+label.score = Puntuación
+label.colour_by_label = Colorear por texto
+label.variable_colour = Color variable
+label.select_colour = Seleccionar color
+option.enable_disable_autosearch = Marque para buscar automáticamente
+option.autosearch = Búsqueda automática
+label.retrieve_ids = Recuperar IDs
+label.display_settings_for = Visualización de caracterÃsticas {0}
+label.simple = Simple
+label.simple_colour = Color simple
+label.colour_by_text = Colorear por texto
+label.graduated_colour = Color graduado
+label.by_text_of = Por texto de
+label.by_range_of = Por rango de
+label.filters_tooltip = Haga clic para configurar o modificar los filtros
+label.or = O
+label.and = Y
\ No newline at end of file
void setAboveThreshold(boolean b);
/**
- * Answers true if the threshold is the minimum value (when
- * isAboveThreshold()) or maximum value (when isBelowThreshold()) of the
- * colour range; only applicable when isGraduatedColour and either
- * isAboveThreshold() or isBelowThreshold() answers true
- *
- * @return
- */
- boolean isThresholdMinMax();
-
- void setThresholdMinMax(boolean b);
-
- /**
* Returns the threshold value (if any), else zero
*
* @return
{
fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
}
+ else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0]);
+ }
+ else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ repaint();
+ }
}
}
*/
private static final int SCALE_FACTOR_1K = 1000;
+ private static final String COLON = ":";
+
private JVDialog frame;
private Frame owner;
private void jbInit() throws Exception
{
- Label minLabel = new Label(MessageManager.getString("label.min_value"));
- Label maxLabel = new Label(MessageManager.getString("label.max_value"));
+ Label minLabel = new Label(
+ MessageManager.getString("label.min_value") + COLON);
+ Label maxLabel = new Label(
+ MessageManager.getString("label.max_value") + COLON);
minLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
maxLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
// minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
*/
package jalview.appletgui;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
+
import jalview.api.FeatureColourI;
import jalview.api.FeatureSettingsControllerI;
import jalview.datamodel.AlignmentI;
Component[] comps = featurePanel.getComponents();
int cSize = comps.length;
- Object[][] tmp = new Object[cSize][3];
+ /*
+ * temporary! leave column[2] empty - used for Filter in
+ * gui.FeatureSettings
+ */
+ int columnCount = 4;
+ Object[][] tmp = new Object[cSize][columnCount];
int tmpSize = 0;
for (int i = 0; i < cSize; i++)
{
MyCheckbox check = (MyCheckbox) comps[i];
- tmp[tmpSize][0] = check.type;
- tmp[tmpSize][1] = fr.getFeatureStyle(check.type);
- tmp[tmpSize][2] = new Boolean(check.getState());
+ tmp[tmpSize][TYPE_COLUMN /* 0 */] = check.type;
+ tmp[tmpSize][COLOUR_COLUMN /* 1 */] = fr.getFeatureStyle(check.type);
+ tmp[tmpSize][SHOW_COLUMN /* 3 */] = new Boolean(check.getState());
tmpSize++;
}
- Object[][] data = new Object[tmpSize][3];
+ Object[][] data = new Object[tmpSize][columnCount];
System.arraycopy(tmp, 0, data, 0, tmpSize);
fr.setFeaturePriority(data);
{
fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
}
+ else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(((int[]) evt.getNewValue())[1]
+ - ((int[]) evt.getOldValue())[1]);
+ }
+ else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ repaint();
+ }
}
}
// 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))
+ if (evt.getPropertyName().equals(ViewportRanges.STARTRES)
+ || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)
+ || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
{
// scroll event, repaint panel
repaint();
{
String eventName = evt.getPropertyName();
+ if (eventName.equals(SequenceGroup.SEQ_GROUP_CHANGED))
+ {
+ fastPaint = true;
+ repaint();
+ return;
+ }
+ else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ fastPaint = false;
+ repaint();
+ return;
+ }
+
if (!av.getWrapAlignment())
{
int scrollX = 0;
- if (eventName.equals(ViewportRanges.STARTRES))
+ if (eventName.equals(ViewportRanges.STARTRES)
+ || eventName.equals(ViewportRanges.STARTRESANDSEQ))
{
// Make sure we're not trying to draw a panel
// larger than the visible window
+ if (eventName.equals(ViewportRanges.STARTRES))
+ {
+ scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+ }
+ else
+ {
+ scrollX = ((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0];
+ }
ViewportRanges vpRanges = av.getRanges();
- scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
int range = vpRanges.getEndRes() - vpRanges.getStartRes();
if (scrollX > range)
{
// scroll
fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
}
+ else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(scrollX, 0);
+ }
}
}
import jalview.util.MappingUtils;
import jalview.util.MessageManager;
import jalview.viewmodel.AlignmentViewport;
-import jalview.viewmodel.ViewportRanges;
import java.awt.BorderLayout;
import java.awt.Font;
void setCursorRow()
{
seqCanvas.cursorY = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorRowAndColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
seqCanvas.cursorY = getKeyboardNo2() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
}
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void moveCursor(int dx, int dy)
seqCanvas.cursorX = original;
}
}
- scrollToVisible();
+ scrollToVisible(false);
}
- void scrollToVisible()
+ /**
+ * Scroll to make the cursor visible in the viewport.
+ *
+ * @param jump
+ * just jump to the location rather than scrolling
+ */
+ void scrollToVisible(boolean jump)
{
if (seqCanvas.cursorX < 0)
{
}
endEditing();
- if (av.getWrapAlignment())
+
+ boolean repaintNeeded = true;
+ if (jump)
{
- av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
+ // only need to repaint if the viewport did not move, as otherwise it will
+ // get a repaint
+ repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
+ seqCanvas.cursorY);
}
else
{
- ViewportRanges ranges = av.getRanges();
- HiddenColumns hidden = av.getAlignment().getHiddenColumns();
- while (seqCanvas.cursorY < ranges.getStartSeq())
+ if (av.getWrapAlignment())
{
- ranges.scrollUp(true);
+ av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
}
- while (seqCanvas.cursorY > ranges.getEndSeq())
- {
- ranges.scrollUp(false);
- }
- while (seqCanvas.cursorX < hidden
- .adjustForHiddenColumns(ranges.getStartRes()))
- {
-
- if (!ranges.scrollRight(false))
- {
- break;
- }
- }
- while (seqCanvas.cursorX > hidden
- .adjustForHiddenColumns(ranges.getEndRes()))
+ else
{
- if (!ranges.scrollRight(true))
- {
- break;
- }
+ av.getRanges().scrollToVisible(seqCanvas.cursorX,
+ seqCanvas.cursorY);
}
}
setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
seqCanvas.cursorX, seqCanvas.cursorY);
- seqCanvas.repaint();
+ if (repaintNeeded)
+ {
+ seqCanvas.repaint();
+ }
}
void setSelectionAreaAtCursor(boolean topLeft)
import jalview.util.MessageManager;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
@Override
public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
{
- List<AlignmentAnnotation> aa = new ArrayList<>();
AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation();
if (alignmentAnnotation != null)
{
- for (AlignmentAnnotation a : alignmentAnnotation)
- {
- if (a.getCalcId() == calcId || (a.getCalcId() != null
- && calcId != null && a.getCalcId().equals(calcId)))
- {
- aa.add(a);
- }
- }
+ return AlignmentAnnotation.findAnnotation(
+ Arrays.asList(getAlignmentAnnotation()), calcId);
}
- return aa;
+ return Arrays.asList(new AlignmentAnnotation[] {});
}
@Override
public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
String calcId, String label)
{
- ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
- for (AlignmentAnnotation ann : getAlignmentAnnotation())
- {
- if ((calcId == null || (ann.getCalcId() != null
- && ann.getCalcId().equals(calcId)))
- && (seq == null || (ann.sequenceRef != null
- && ann.sequenceRef == seq))
- && (label == null
- || (ann.label != null && ann.label.equals(label))))
- {
- aa.add(ann);
- }
- }
- return aa;
+ return AlignmentAnnotation.findAnnotations(
+ Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
}
@Override
import jalview.analysis.SecStrConsensus.SimpleBP;
import jalview.analysis.WUSSParseException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
{
return graphMin < graphMax;
}
+
+ public static Iterable<AlignmentAnnotation> findAnnotations(
+ Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
+ String label)
+ {
+
+ ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
+ for (AlignmentAnnotation ann : list)
+ {
+ if ((calcId == null || (ann.getCalcId() != null
+ && ann.getCalcId().equals(calcId)))
+ && (seq == null || (ann.sequenceRef != null
+ && ann.sequenceRef == seq))
+ && (label == null
+ || (ann.label != null && ann.label.equals(label))))
+ {
+ aa.add(ann);
+ }
+ }
+ return aa;
+ }
+
+ /**
+ * Answer true if any annotation matches the calcId passed in (if not null).
+ *
+ * @param list
+ * annotation to search
+ * @param calcId
+ * @return
+ */
+ public static boolean hasAnnotation(List<AlignmentAnnotation> list,
+ String calcId)
+ {
+
+ if (calcId != null && !"".equals(calcId))
+ {
+ for (AlignmentAnnotation a : list)
+ {
+ if (a.getCalcId() == calcId)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static Iterable<AlignmentAnnotation> findAnnotation(
+ List<AlignmentAnnotation> list, String calcId)
+ {
+
+ List<AlignmentAnnotation> aa = new ArrayList<>();
+ if (calcId == null)
+ {
+ return aa;
+ }
+ for (AlignmentAnnotation a : list)
+ {
+
+ if (a.getCalcId() == calcId || (a.getCalcId() != null
+ && calcId != null && a.getCalcId().equals(calcId)))
+ {
+ aa.add(a);
+ }
+ }
+ return aa;
+ }
}
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel;
public interface ContiguousI
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel;
/**
}
@Override
- public void deleteChars(int i, int j)
+ public void deleteChars(final int i, final int j)
{
int newstart = start, newend = end;
if (i >= sequence.length || i < 0)
boolean createNewDs = false;
// TODO: take a (second look) at the dataset creation validation method for
// the very large sequence case
- int eindex = -1, sindex = -1;
- boolean ecalc = false, scalc = false;
+ int startIndex = findIndex(start) - 1;
+ int endIndex = findIndex(end) - 1;
+ int startDeleteColumn = -1; // for dataset sequence deletions
+ int deleteCount = 0;
+
for (int s = i; s < j; s++)
{
- if (jalview.schemes.ResidueProperties.aaIndex[sequence[s]] != 23)
+ if (Comparison.isGap(sequence[s]))
+ {
+ continue;
+ }
+ deleteCount++;
+ if (startDeleteColumn == -1)
{
- if (createNewDs)
+ startDeleteColumn = findPosition(s) - start;
+ }
+ if (createNewDs)
+ {
+ newend--;
+ }
+ else
+ {
+ if (startIndex == s)
{
- newend--;
+ /*
+ * deleting characters from start of sequence; new start is the
+ * sequence position of the next column (position to the right
+ * if the column position is gapped)
+ */
+ newstart = findPosition(j);
+ break;
}
else
{
- if (!scalc)
- {
- sindex = findIndex(start) - 1;
- scalc = true;
- }
- if (sindex == s)
+ if (endIndex < j)
{
- // delete characters including start of sequence
- newstart = findPosition(j);
- break; // don't need to search for any more residue characters.
+ /*
+ * deleting characters at end of sequence; new end is the sequence
+ * position of the column before the deletion; subtract 1 if this is
+ * gapped since findPosition returns the next sequence position
+ */
+ newend = findPosition(i - 1);
+ if (Comparison.isGap(sequence[i - 1]))
+ {
+ newend--;
+ }
+ break;
}
else
{
- // delete characters after start.
- if (!ecalc)
- {
- eindex = findIndex(end) - 1;
- ecalc = true;
- }
- if (eindex < j)
- {
- // delete characters at end of sequence
- newend = findPosition(i - 1);
- break; // don't need to search for any more residue characters.
- }
- else
- {
- createNewDs = true;
- newend--; // decrease end position by one for the deleted residue
- // and search further
- }
+ createNewDs = true;
+ newend--;
}
}
}
}
- // deletion occured in the middle of the sequence
+
if (createNewDs && this.datasetSequence != null)
{
- // construct a new sequence
+ /*
+ * if deletion occured in the middle of the sequence,
+ * construct a new dataset sequence and delete the residues
+ * that were deleted from the aligned sequence
+ */
Sequence ds = new Sequence(datasetSequence);
+ ds.deleteChars(startDeleteColumn, startDeleteColumn + deleteCount);
+ datasetSequence = ds;
// TODO: remove any non-inheritable properties ?
// TODO: create a sequence mapping (since there is a relation here ?)
- ds.deleteChars(i, j);
- datasetSequence = ds;
}
start = newstart;
end = newend;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel;
/**
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Override
public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
{
- List<AlignmentAnnotation> aa = new ArrayList<>();
- if (calcId == null)
- {
- return aa;
- }
- for (AlignmentAnnotation a : getAlignmentAnnotation())
- {
- if (calcId.equals(a.getCalcId()))
- {
- aa.add(a);
- }
- }
- return aa;
+ return AlignmentAnnotation.findAnnotation(
+ Arrays.asList(getAlignmentAnnotation()), calcId);
}
@Override
public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
String calcId, String label)
{
- ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
- for (AlignmentAnnotation ann : getAlignmentAnnotation())
- {
- if ((calcId == null || (ann.getCalcId() != null
- && ann.getCalcId().equals(calcId)))
- && (seq == null || (ann.sequenceRef != null
- && ann.sequenceRef == seq))
- && (label == null
- || (ann.label != null && ann.label.equals(label))))
- {
- aa.add(ann);
- }
- }
- return aa;
+ return AlignmentAnnotation.findAnnotations(
+ Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
}
/**
*/
public boolean hasAnnotation(String calcId)
{
- if (calcId != null && !"".equals(calcId))
- {
- for (AlignmentAnnotation a : getAlignmentAnnotation())
- {
- if (a.getCalcId() == calcId)
- {
- return true;
- }
- }
- }
- return false;
+ return AlignmentAnnotation
+ .hasAnnotation(Arrays.asList(getAlignmentAnnotation()), calcId);
}
/**
public int findIndex(int pos);
/**
- * Returns the sequence position for an alignment position.
+ * Returns the sequence position for an alignment (column) position. If at a
+ * gap, returns the position of the next residue to the right. If beyond the
+ * end of the sequence, returns 1 more than the last residue position.
*
* @param i
* column index in alignment (from 0..<length)
*
- * @return TODO: JAL-2562 - residue number for residue (left of and) nearest
- * ith column
+ * @return
*/
public int findPosition(int i);
*/
public class FeatureAttributes
{
+ public enum Datatype
+ {
+ Character, Number, Mixed
+ }
+
private static FeatureAttributes instance = new FeatureAttributes();
/*
*/
boolean hasValue = false;
+ Datatype type;
+
/**
* Note one instance of this attribute, recording unique, non-null names,
* and the min/max of any numerical values
try
{
float f = Float.valueOf(value);
- min = Float.min(min, f);
- max = Float.max(max, f);
+ min = hasValue ? Float.min(min, f) : f;
+ max = hasValue ? Float.max(max, f) : f;
hasValue = true;
+ type = (type == null || type == Datatype.Number) ? Datatype.Number
+ : Datatype.Mixed;
} catch (NumberFormatException e)
{
- // ok, wasn't a number, ignore for min-max purposes
+ // not a number, ignore for min-max purposes
+ type = (type == null || type == Datatype.Character)
+ ? Datatype.Character
+ : Datatype.Mixed;
}
}
}
return null;
}
+ public Datatype getType()
+ {
+ return type;
+ }
+
/**
* Adds the given description to the list of known descriptions (without
* duplication)
}
attData.addDescription(description);
}
+
+ /**
+ * Answers the datatype of the feature, which is one of Character, Number or
+ * Mixed (or null if not known), as discovered from values recorded.
+ *
+ * @param featureType
+ * @param attName
+ * @return
+ */
+ public Datatype getDatatype(String featureType, String... attName)
+ {
+ Map<String[], AttributeData> atts = attributes.get(featureType);
+ if (atts != null)
+ {
+ AttributeData attData = atts.get(attName);
+ if (attData != null)
+ {
+ return attData.getType();
+ }
+ }
+ return null;
+ }
}
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.ContiguousI;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.features;
import jalview.datamodel.SequenceFeature;
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel.xdb.uniprot;
/**
{
fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
}
+ else if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0]);
+ }
+ else if (evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ repaint();
+ }
}
}
JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
treePanel.setOpaque(false);
- JvSwingUtils.createItalicTitledBorder(treePanel,
+ JvSwingUtils.createTitledBorder(treePanel,
MessageManager.getString("label.tree"), true);
// then copy the inset dimensions for the border-less PCA panel
+++ /dev/null
-/*
- * 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 <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.gui;
-
-import jalview.api.FeatureColourI;
-import jalview.datamodel.GraphLine;
-import jalview.datamodel.features.FeatureAttributes;
-import jalview.schemes.FeatureColour;
-import jalview.util.MessageManager;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JCheckBox;
-import javax.swing.JColorChooser;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JSlider;
-import javax.swing.JTextField;
-import javax.swing.border.LineBorder;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-public class FeatureColourChooser extends JalviewDialog
-{
- private static final String COLON = ":";
-
- private static final int MAX_TOOLTIP_LENGTH = 50;
-
- private static int NO_COLOUR_OPTION = 0;
-
- private static int MIN_COLOUR_OPTION = 1;
-
- private static int MAX_COLOUR_OPTION = 2;
-
- private FeatureRenderer fr;
-
- private FeatureColourI cs;
-
- private FeatureColourI oldcs;
-
- private AlignmentPanel ap;
-
- private boolean adjusting = false;
-
- private float min;
-
- private float max;
-
- private float scaleFactor;
-
- private String type = null;
-
- private JPanel minColour = new JPanel();
-
- private JPanel maxColour = new JPanel();
-
- private Color noColour;
-
- private JComboBox<String> threshold = new JComboBox<>();
-
- private JSlider slider = new JSlider();
-
- private JTextField thresholdValue = new JTextField(20);
-
- private JCheckBox thresholdIsMin = new JCheckBox();
-
- private GraphLine threshline;
-
- private Color oldmaxColour;
-
- private Color oldminColour;
-
- private Color oldNoColour;
-
- private ActionListener colourEditor = null;
-
- /*
- * radio buttons to select what to colour by
- * label, attribute text, score, attribute value
- */
- private JRadioButton byDescription = new JRadioButton();
-
- private JRadioButton byAttributeText = new JRadioButton();
-
- private JRadioButton byScore = new JRadioButton();
-
- private JRadioButton byAttributeValue = new JRadioButton();
-
- private ActionListener changeColourAction;
-
- private ActionListener changeMinMaxAction;
-
- /*
- * choice of option for 'colour for no value'
- */
- private JComboBox<String> noValueCombo;
-
- /*
- * choice of attribute (if any) for 'colour by text'
- */
- private JComboBox<String> textAttributeCombo;
-
- /*
- * choice of attribute (if any) for 'colour by value'
- */
- private JComboBox<String> valueAttributeCombo;
-
- /**
- * Constructor
- *
- * @param frender
- * @param theType
- */
- public FeatureColourChooser(FeatureRenderer frender, String theType)
- {
- this(frender, false, theType);
- }
-
- /**
- * Constructor, with option to make a blocking dialog (has to complete in the
- * AWT event queue thread). Currently this option is always set to false.
- *
- * @param frender
- * @param blocking
- * @param theType
- */
- FeatureColourChooser(FeatureRenderer frender, boolean blocking,
- String theType)
- {
- this.fr = frender;
- this.type = theType;
- ap = fr.ap;
- String title = MessageManager.formatMessage("label.variable_color_for",
- new String[] { theType });
- initDialogFrame(this, true, blocking, title, 470, 300);
-
- slider.addChangeListener(new ChangeListener()
- {
- @Override
- public void stateChanged(ChangeEvent evt)
- {
- if (!adjusting)
- {
- thresholdValue.setText((slider.getValue() / scaleFactor) + "");
- sliderValueChanged();
- }
- }
- });
- slider.addMouseListener(new MouseAdapter()
- {
- @Override
- public void mouseReleased(MouseEvent evt)
- {
- /*
- * only update Overview and/or structure colouring
- * when threshold slider drag ends (mouse up)
- */
- if (ap != null)
- {
- ap.paintAlignment(true, true);
- }
- }
- });
-
- // todo move all threshold setup inside a method
- float mm[] = fr.getMinMax().get(theType)[0];
- min = mm[0];
- max = mm[1];
-
- /*
- * ensure scale factor allows a scaled range with
- * 10 integer divisions ('ticks'); if we have got here,
- * we should expect that max != min
- */
- scaleFactor = (max == min) ? 1f : 100f / (max - min);
-
- oldcs = fr.getFeatureColours().get(theType);
- if (!oldcs.isSimpleColour())
- {
- if (oldcs.isAutoScaled())
- {
- // update the scale
- cs = new FeatureColour((FeatureColour) oldcs, min, max);
- }
- else
- {
- cs = new FeatureColour((FeatureColour) oldcs);
- }
- }
- else
- {
- /*
- * promote original simple color to a graduated color
- * - by score if there is a score range, else by label
- */
- Color bl = oldcs.getColour();
- if (bl == null)
- {
- bl = Color.BLACK;
- }
- // original colour becomes the maximum colour
- cs = new FeatureColour(Color.white, bl, mm[0], mm[1]);
- cs.setColourByLabel(mm[0] == mm[1]);
- }
- minColour.setBackground(oldminColour = cs.getMinColour());
- maxColour.setBackground(oldmaxColour = cs.getMaxColour());
- noColour = cs.getNoColour();
-
- adjusting = true;
-
- try
- {
- jbInit();
- } catch (Exception ex)
- {
- ex.printStackTrace();
- return;
- }
-
- /*
- * set the initial state of options on screen
- */
- if (cs.isColourByLabel())
- {
- if (cs.isColourByAttribute())
- {
- byAttributeText.setSelected(true);
- textAttributeCombo.setEnabled(true);
- String[] attributeName = cs.getAttributeName();
- textAttributeCombo
- .setSelectedItem(String.join(COLON, attributeName));
- }
- else
- {
- byDescription.setSelected(true);
- textAttributeCombo.setEnabled(false);
- }
- }
- else
- {
- if (cs.isColourByAttribute())
- {
- byAttributeValue.setSelected(true);
- String[] attributeName = cs.getAttributeName();
- valueAttributeCombo
- .setSelectedItem(String.join(COLON, attributeName));
- valueAttributeCombo.setEnabled(true);
- updateMinMax();
- }
- else
- {
- byScore.setSelected(true);
- valueAttributeCombo.setEnabled(false);
- }
- }
-
- if (noColour == null)
- {
- noValueCombo.setSelectedIndex(NO_COLOUR_OPTION);
- }
- else if (noColour.equals(oldminColour))
- {
- noValueCombo.setSelectedIndex(MIN_COLOUR_OPTION);
- }
- else if (noColour.equals(oldmaxColour))
- {
- noValueCombo.setSelectedIndex(MAX_COLOUR_OPTION);
- }
-
- threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
- threshline.value = cs.getThreshold();
-
- if (cs.hasThreshold())
- {
- // initialise threshold slider and selector
- threshold.setSelectedIndex(cs.isAboveThreshold() ? 1 : 2);
- slider.setEnabled(true);
- slider.setValue((int) (cs.getThreshold() * scaleFactor));
- thresholdValue.setEnabled(true);
- }
-
- adjusting = false;
-
- changeColour(false);
- waitForInput();
- }
-
- /**
- * Configures the initial layout
- */
- private void jbInit()
- {
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
- this.setBackground(Color.white);
-
- changeColourAction = new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- changeColour(true);
- }
- };
-
- changeMinMaxAction = new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- updateMinMax();
- changeColour(true);
- }
- };
-
- /*
- * this panel
- * detailsPanel
- * colourByTextPanel
- * colourByScorePanel
- * okCancelPanel
- */
- JPanel detailsPanel = new JPanel();
- detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS));
-
- JPanel colourByTextPanel = initColourByTextPanel();
- detailsPanel.add(colourByTextPanel);
-
- JPanel colourByValuePanel = initColourByValuePanel();
- detailsPanel.add(colourByValuePanel);
-
- /*
- * 4 radio buttons select between colour by description, by
- * attribute text, by score, or by attribute value
- */
- ButtonGroup bg = new ButtonGroup();
- bg.add(byDescription);
- bg.add(byAttributeText);
- bg.add(byScore);
- bg.add(byAttributeValue);
-
- JPanel okCancelPanel = initOkCancelPanel();
-
- this.add(detailsPanel);
- this.add(okCancelPanel);
- }
-
- /**
- * Updates the min-max range for a change in choice of Colour by Score, or
- * Colour by Attribute (value)
- */
- protected void updateMinMax()
- {
- float[] minMax = null;
- if (byScore.isSelected())
- {
- minMax = fr.getMinMax().get(type)[0];
- }
- else if (byAttributeValue.isSelected())
- {
- String attName = (String) valueAttributeCombo.getSelectedItem();
- String[] attNames = attName.split(COLON);
- minMax = FeatureAttributes.getInstance().getMinMax(type, attNames);
- }
- if (minMax != null)
- {
- min = minMax[0];
- max = minMax[1];
- scaleFactor = (max == min) ? 1f : 100f / (max - min);
- slider.setValue((int) (min * scaleFactor));
- }
- }
-
- /**
- * Lay out fields for graduated colour by value
- *
- * @return
- */
- protected JPanel initColourByValuePanel()
- {
- JPanel byValuePanel = new JPanel();
- byValuePanel.setLayout(new BoxLayout(byValuePanel, BoxLayout.Y_AXIS));
- JvSwingUtils.createItalicTitledBorder(byValuePanel,
- MessageManager.getString("label.colour_by_value"), true);
- byValuePanel.setBackground(Color.white);
-
- /*
- * first row - choose colour by score or by attribute, choose attribute
- */
- JPanel byWhatPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- byWhatPanel.setBackground(Color.white);
- byValuePanel.add(byWhatPanel);
-
- byScore.setText(MessageManager.getString("label.score"));
- byWhatPanel.add(byScore);
- byScore.addActionListener(changeMinMaxAction);
-
- byAttributeValue.setText(MessageManager.getString("label.attribute"));
- byAttributeValue.addActionListener(changeMinMaxAction);
- byWhatPanel.add(byAttributeValue);
-
- List<String[]> attNames = FeatureAttributes.getInstance()
- .getAttributes(type);
- valueAttributeCombo = populateAttributesDropdown(type, attNames, true);
-
- /*
- * if no numeric atttibutes found, disable colour by attribute value
- */
- if (valueAttributeCombo.getItemCount() == 0)
- {
- byAttributeValue.setEnabled(false);
- }
-
- byWhatPanel.add(valueAttributeCombo);
-
- /*
- * second row - min/max/no colours
- */
- JPanel colourRangePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- colourRangePanel.setBackground(Color.white);
- byValuePanel.add(colourRangePanel);
-
- minColour.setFont(JvSwingUtils.getLabelFont());
- minColour.setBorder(BorderFactory.createLineBorder(Color.black));
- minColour.setPreferredSize(new Dimension(40, 20));
- minColour.setToolTipText(MessageManager.getString("label.min_colour"));
- minColour.addMouseListener(new MouseAdapter()
- {
- @Override
- public void mousePressed(MouseEvent e)
- {
- if (minColour.isEnabled())
- {
- minColour_actionPerformed();
- }
- }
- });
-
- maxColour.setFont(JvSwingUtils.getLabelFont());
- maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
- maxColour.setPreferredSize(new Dimension(40, 20));
- maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
- maxColour.addMouseListener(new MouseAdapter()
- {
- @Override
- public void mousePressed(MouseEvent e)
- {
- if (maxColour.isEnabled())
- {
- maxColour_actionPerformed();
- }
- }
- });
- maxColour.setBorder(new LineBorder(Color.black));
-
- noValueCombo = new JComboBox<>();
- noValueCombo.addItem(MessageManager.getString("label.no_colour"));
- noValueCombo.addItem(MessageManager.getString("label.min_colour"));
- noValueCombo.addItem(MessageManager.getString("label.max_colour"));
- noValueCombo.addItemListener(new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent e)
- {
- setNoValueColour();
- }
- });
-
- JLabel minText = new JLabel(MessageManager.getString("label.min_value"));
- minText.setFont(JvSwingUtils.getLabelFont());
- JLabel maxText = new JLabel(MessageManager.getString("label.max_value"));
- maxText.setFont(JvSwingUtils.getLabelFont());
- JLabel noText = new JLabel(MessageManager.getString("label.no_value"));
- noText.setFont(JvSwingUtils.getLabelFont());
-
- colourRangePanel.add(minText);
- colourRangePanel.add(minColour);
- colourRangePanel.add(maxText);
- colourRangePanel.add(maxColour);
- colourRangePanel.add(noText);
- colourRangePanel.add(noValueCombo);
-
- /*
- * third row - threshold options and value
- */
- JPanel thresholdPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- thresholdPanel.setBackground(Color.white);
- byValuePanel.add(thresholdPanel);
-
- threshold.addActionListener(changeColourAction);
- threshold.setToolTipText(MessageManager
- .getString("label.threshold_feature_display_by_score"));
- threshold.addItem(MessageManager
- .getString("label.threshold_feature_no_threshold")); // index 0
- threshold.addItem(MessageManager
- .getString("label.threshold_feature_above_threshold")); // index 1
- threshold.addItem(MessageManager
- .getString("label.threshold_feature_below_threshold")); // index 2
-
- thresholdValue.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- thresholdValue_actionPerformed();
- }
- });
- thresholdValue.addFocusListener(new FocusAdapter()
- {
- @Override
- public void focusLost(FocusEvent e)
- {
- thresholdValue_actionPerformed();
- }
- });
- slider.setPaintLabels(false);
- slider.setPaintTicks(true);
- slider.setBackground(Color.white);
- slider.setEnabled(false);
- slider.setOpaque(false);
- slider.setPreferredSize(new Dimension(100, 32));
- slider.setToolTipText(MessageManager
- .getString("label.adjust_threshold"));
- thresholdValue.setEnabled(false);
- thresholdValue.setColumns(7);
-
- thresholdPanel.add(threshold);
- thresholdPanel.add(slider);
- thresholdPanel.add(thresholdValue);
-
- /*
- * 4th row - threshold is min / max
- */
- JPanel isMinMaxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- isMinMaxPanel.setBackground(Color.white);
- byValuePanel.add(isMinMaxPanel);
- thresholdIsMin.setBackground(Color.white);
- thresholdIsMin.setText(MessageManager
- .getString("label.threshold_minmax"));
- thresholdIsMin.setToolTipText(MessageManager
- .getString("label.toggle_absolute_relative_display_threshold"));
- thresholdIsMin.addActionListener(changeColourAction);
- isMinMaxPanel.add(thresholdIsMin);
-
- return byValuePanel;
- }
-
- /**
- * Action on user choice of no / min / max colour to use when there is no
- * value to colour by
- */
- protected void setNoValueColour()
- {
- int i = noValueCombo.getSelectedIndex();
- if (i == NO_COLOUR_OPTION)
- {
- noColour = null;
- }
- else if (i == MIN_COLOUR_OPTION)
- {
- noColour = minColour.getBackground();
- }
- else if (i == MAX_COLOUR_OPTION)
- {
- noColour = maxColour.getBackground();
- }
- changeColour(true);
- }
-
- /**
- * Lay out OK and Cancel buttons
- *
- * @return
- */
- protected JPanel initOkCancelPanel()
- {
- JPanel okCancelPanel = new JPanel();
- okCancelPanel.setBackground(Color.white);
- okCancelPanel.add(ok);
- okCancelPanel.add(cancel);
- return okCancelPanel;
- }
-
- /**
- * Lay out Colour by Label and attribute choice elements
- *
- * @return
- */
- protected JPanel initColourByTextPanel()
- {
- JPanel byTextPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- byTextPanel.setBackground(Color.white);
- JvSwingUtils.createItalicTitledBorder(byTextPanel,
- MessageManager.getString("label.colour_by_text"), true);
-
- byDescription.setText(MessageManager.getString("label.label"));
- byDescription.setToolTipText(MessageManager
- .getString("label.colour_by_label_tip"));
- byDescription.addActionListener(changeColourAction);
- byTextPanel.add(byDescription);
-
- byAttributeText.setText(MessageManager.getString("label.attribute"));
- byAttributeText.addActionListener(changeColourAction);
- byTextPanel.add(byAttributeText);
-
- List<String[]> attNames = FeatureAttributes.getInstance()
- .getAttributes(type);
- textAttributeCombo = populateAttributesDropdown(type, attNames, false);
- byTextPanel.add(textAttributeCombo);
-
- /*
- * disable colour by attribute if no attributes
- */
- if (attNames.isEmpty())
- {
- byAttributeText.setEnabled(false);
- }
-
- return byTextPanel;
- }
-
- /**
- * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
- * set the selected colour (if the user does not cancel out of the dialog)
- */
- protected void minColour_actionPerformed()
- {
- Color col = JColorChooser.showDialog(this,
- MessageManager.getString("label.select_colour_minimum_value"),
- minColour.getBackground());
- if (col != null)
- {
- minColour.setBackground(col);
- minColour.setForeground(col);
- }
- minColour.repaint();
- changeColour(true);
- }
-
- /**
- * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
- * set the selected colour (if the user does not cancel out of the dialog)
- */
- protected void maxColour_actionPerformed()
- {
- Color col = JColorChooser.showDialog(this,
- MessageManager.getString("label.select_colour_maximum_value"),
- maxColour.getBackground());
- if (col != null)
- {
- maxColour.setBackground(col);
- maxColour.setForeground(col);
- }
- maxColour.repaint();
- changeColour(true);
- }
-
- /**
- * Constructs and sets the selected colour options as the colour for the
- * feature type, and repaints the alignment, and optionally the Overview
- * and/or structure viewer if open
- *
- * @param updateStructsAndOverview
- */
- void changeColour(boolean updateStructsAndOverview)
- {
- // Check if combobox is still adjusting
- if (adjusting)
- {
- return;
- }
-
- boolean aboveThreshold = false;
- boolean belowThreshold = false;
- if (threshold.getSelectedIndex() == 1)
- {
- aboveThreshold = true;
- }
- else if (threshold.getSelectedIndex() == 2)
- {
- belowThreshold = true;
- }
- boolean hasThreshold = aboveThreshold || belowThreshold;
-
- slider.setEnabled(true);
- thresholdValue.setEnabled(true);
-
- /*
- * make the feature colour
- */
- FeatureColourI acg;
- if (cs.isColourByLabel())
- {
- acg = new FeatureColour(oldminColour, oldmaxColour, min, max);
- }
- else
- {
- acg = new FeatureColour(oldminColour = minColour.getBackground(),
- oldmaxColour = maxColour.getBackground(),
- oldNoColour = noColour, min, max);
- }
- String attribute = null;
- textAttributeCombo.setEnabled(false);
- valueAttributeCombo.setEnabled(false);
- if (byAttributeText.isSelected())
- {
- attribute = (String) textAttributeCombo.getSelectedItem();
- textAttributeCombo.setEnabled(true);
- acg.setAttributeName(attribute.split(COLON));
- }
- else if (byAttributeValue.isSelected())
- {
- attribute = (String) valueAttributeCombo.getSelectedItem();
- valueAttributeCombo.setEnabled(true);
- acg.setAttributeName(attribute.split(COLON));
- }
- else
- {
- acg.setAttributeName((String) null);
- }
-
- if (!hasThreshold)
- {
- slider.setEnabled(false);
- thresholdValue.setEnabled(false);
- thresholdValue.setText("");
- thresholdIsMin.setEnabled(false);
- }
- else if (threshline == null)
- {
- /*
- * todo not yet implemented: visual indication of feature threshold
- */
- threshline = new GraphLine((max - min) / 2f, "Threshold",
- Color.black);
- }
-
- if (hasThreshold)
- {
- adjusting = true;
- acg.setThreshold(threshline.value);
-
- float range = (max - min) * scaleFactor;
-
- slider.setMinimum((int) (min * scaleFactor));
- slider.setMaximum((int) (max * scaleFactor));
- // slider.setValue((int) (threshline.value * scaleFactor));
- slider.setValue(Math.round(threshline.value * scaleFactor));
- thresholdValue.setText(threshline.value + "");
- slider.setMajorTickSpacing((int) (range / 10f));
- slider.setEnabled(true);
- thresholdValue.setEnabled(true);
- thresholdIsMin.setEnabled(!byDescription.isSelected());
- adjusting = false;
- }
-
- acg.setAboveThreshold(aboveThreshold);
- acg.setBelowThreshold(belowThreshold);
- if (thresholdIsMin.isSelected() && hasThreshold)
- {
- acg.setAutoScaled(false);
- if (aboveThreshold)
- {
- acg = new FeatureColour((FeatureColour) acg, threshline.value, max);
- }
- else
- {
- acg = new FeatureColour((FeatureColour) acg, min, threshline.value);
- }
- }
- else
- {
- acg.setAutoScaled(true);
- }
- acg.setColourByLabel(byDescription.isSelected()
- || byAttributeText.isSelected());
-
- if (acg.isColourByLabel())
- {
- maxColour.setEnabled(false);
- minColour.setEnabled(false);
- noValueCombo.setEnabled(false);
- maxColour.setBackground(this.getBackground());
- maxColour.setForeground(this.getBackground());
- minColour.setBackground(this.getBackground());
- minColour.setForeground(this.getBackground());
- }
- else
- {
- maxColour.setEnabled(true);
- minColour.setEnabled(true);
- noValueCombo.setEnabled(true);
- maxColour.setBackground(oldmaxColour);
- maxColour.setForeground(oldmaxColour);
- minColour.setBackground(oldminColour);
- minColour.setForeground(oldminColour);
- noColour = oldNoColour;
- }
-
- /*
- * save the colour, and repaint stuff
- */
- fr.setColour(type, acg);
- cs = acg;
- ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
- }
-
- @Override
- protected void raiseClosed()
- {
- if (this.colourEditor != null)
- {
- colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
- }
- }
-
- @Override
- public void okPressed()
- {
- changeColour(false);
- }
-
- @Override
- public void cancelPressed()
- {
- reset();
- }
-
- /**
- * Action when the user cancels the dialog. All previous settings should be
- * restored and rendered on the alignment, and any linked Overview window or
- * structure.
- */
- void reset()
- {
- fr.setColour(type, oldcs);
- ap.paintAlignment(true, true);
- cs = null;
- }
-
- /**
- * Action on text entry of a threshold value
- */
- protected void thresholdValue_actionPerformed()
- {
- try
- {
- float f = Float.parseFloat(thresholdValue.getText());
- slider.setValue((int) (f * scaleFactor));
- threshline.value = f;
-
- /*
- * force repaint of any Overview window or structure
- */
- ap.paintAlignment(true, true);
- } catch (NumberFormatException ex)
- {
- }
- }
-
- /**
- * Action on change of threshold slider value. This may be done interactively
- * (by moving the slider), or programmatically (to update the slider after
- * manual input of a threshold value).
- */
- protected void sliderValueChanged()
- {
- /*
- * squash rounding errors by forcing min/max of slider to
- * actual min/max of feature score range
- */
- int value = slider.getValue();
- threshline.value = value == slider.getMaximum() ? max
- : (value == slider.getMinimum() ? min : value / scaleFactor);
- cs.setThreshold(threshline.value);
-
- /*
- * repaint alignment, but not Overview or structure,
- * to avoid overload while dragging the slider
- */
- changeColour(false);
- }
-
- void addActionListener(ActionListener graduatedColorEditor)
- {
- if (colourEditor != null)
- {
- System.err.println(
- "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
- }
- colourEditor = graduatedColorEditor;
- }
-
- /**
- * Answers the last colour setting selected by user - either oldcs (which may
- * be a java.awt.Color) or the new GraduatedColor
- *
- * @return
- */
- FeatureColourI getLastColour()
- {
- if (cs == null)
- {
- return oldcs;
- }
- return cs;
- }
-
- /**
- * A helper method to build the drop-down choice of attributes for a feature.
- * Where metadata is available with a description for an attribute, that is
- * added as a tooltip. The list may optionally be restricted to attributes for
- * which we hold a range of numerical values (so suitable candidates for a
- * graduated colour scheme).
- * <p>
- * Attribute names may be 'simple' e.g. "AC" or 'compound' e.g. {"CSQ",
- * "Allele"}. Compound names are rendered for display as (e.g.) CSQ:Allele.
- *
- * @param featureType
- * @param attNames
- * @param withNumericRange
- */
- protected JComboBox<String> populateAttributesDropdown(
- String featureType, List<String[]> attNames,
- boolean withNumericRange)
- {
- List<String> validAtts = new ArrayList<>();
- List<String> tooltips = new ArrayList<>();
-
- FeatureAttributes fa = FeatureAttributes.getInstance();
- for (String[] attName : attNames)
- {
- if (withNumericRange)
- {
- float[] minMax = fa.getMinMax(featureType, attName);
- if (minMax == null)
- {
- continue;
- }
- }
- validAtts.add(String.join(COLON, attName));
- String desc = fa.getDescription(featureType, attName);
- if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
- {
- desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
- }
- tooltips.add(desc == null ? "" : desc);
- }
-
- JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
- validAtts, tooltips);
-
- attCombo.addItemListener(new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent e)
- {
- changeMinMaxAction.actionPerformed(null);
- }
- });
-
- if (validAtts.isEmpty())
- {
- attCombo.setToolTipText(MessageManager
- .getString(withNumericRange ? "label.no_numeric_attributes"
- : "label.no_attributes"));
- }
-
- return attCombo;
- }
-
-}
final JSpinner end = new JSpinner();
start.setPreferredSize(new Dimension(80, 20));
end.setPreferredSize(new Dimension(80, 20));
- final FeatureRenderer me = this;
final JLabel colour = new JLabel();
colour.setOpaque(true);
// colour.setBorder(BorderFactory.createEtchedBorder());
colour.setMaximumSize(new Dimension(30, 16));
colour.addMouseListener(new MouseAdapter()
{
- FeatureColourChooser fcc = null;
-
+ /*
+ * open colour chooser on click in colour panel
+ */
@Override
public void mousePressed(MouseEvent evt)
{
}
else
{
- if (fcc == null)
+ /*
+ * variable colour dialog - on OK, refetch the updated
+ * feature colour and update this display
+ */
+ final String ft = features.get(featureIndex).getType();
+ final String type = ft == null ? lastFeatureAdded : ft;
+ FeatureTypeSettings fcc = new FeatureTypeSettings(
+ FeatureRenderer.this, type);
+ fcc.setRequestFocusEnabled(true);
+ fcc.requestFocus();
+ fcc.addActionListener(new ActionListener()
{
- final String ft = features.get(featureIndex).getType();
- final String type = ft == null ? lastFeatureAdded : ft;
- fcc = new FeatureColourChooser(me, type);
- fcc.setRequestFocusEnabled(true);
- fcc.requestFocus();
-
- fcc.addActionListener(new ActionListener()
+ @Override
+ public void actionPerformed(ActionEvent e)
{
-
- @Override
- public void actionPerformed(ActionEvent e)
- {
- fcol = fcc.getLastColour();
- fcc = null;
- setColour(type, fcol);
- updateColourButton(mainPanel, colour, fcol);
- }
- });
-
- }
+ fcol = FeatureRenderer.this.getFeatureStyle(ft);
+ setColour(type, fcol);
+ updateColourButton(mainPanel, colour, fcol);
+ }
+ });
}
}
});
*/
package jalview.gui;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.COLOUR_COLUMN;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.FILTER_COLUMN;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.SHOW_COLUMN;
+import static jalview.viewmodel.seqfeatures.FeatureRendererModel.TYPE_COLUMN;
+
import jalview.api.FeatureColourI;
import jalview.api.FeatureSettingsControllerI;
import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureAttributes;
import jalview.gui.Help.HelpId;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.util.QuickSort;
-import jalview.util.ReverseListIterator;
-import jalview.util.matcher.Condition;
-import jalview.util.matcher.KeyedMatcher;
-import jalview.util.matcher.KeyedMatcherI;
import jalview.util.matcher.KeyedMatcherSet;
import jalview.util.matcher.KeyedMatcherSetI;
import jalview.viewmodel.AlignmentViewport;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
-import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
-import java.awt.LayoutManager;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import javax.help.HelpSetException;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JColorChooser;
-import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
-import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
-import javax.swing.JTabbedPane;
import javax.swing.JTable;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
public class FeatureSettings extends JPanel
implements FeatureSettingsControllerI
{
+ private static final int COLUMN_COUNT = 4;
+
private static final String COLON = ":";
private static final int MIN_WIDTH = 400;
JPanel groupPanel;
JSlider transparency = new JSlider();
-
+
/*
* when true, constructor is still executing - so ignore UI events
*/
*/
Map<String, float[]> typeWidth = null;
- /*
- * fields of the feature filters tab
- */
- private JPanel filtersPane;
-
- private JPanel chooseFiltersPanel;
-
- private JComboBox<String> filteredFeatureChoice;
-
- private JRadioButton andFilters;
-
- private JRadioButton orFilters;
-
- /*
- * filters for the currently selected feature type
- */
- private List<KeyedMatcherI> filters;
-
- private JTextArea filtersAsText;
-
- // set white normally, black to debug layout
- private Color debugBorderColour = Color.white;
-
/**
* Constructor
*
@Override
public String getToolTipText(MouseEvent e)
{
- if (table.columnAtPoint(e.getPoint()) == 0)
+ String tip = null;
+ int column = table.columnAtPoint(e.getPoint());
+ switch (column)
{
- /*
- * Tooltip for feature name only
- */
- return JvSwingUtils.wrapTooltip(true, MessageManager
+ case TYPE_COLUMN:
+ tip = JvSwingUtils.wrapTooltip(true, MessageManager
.getString("label.feature_settings_click_drag"));
+ break;
+ case FILTER_COLUMN:
+ int row = table.rowAtPoint(e.getPoint());
+ KeyedMatcherSet o = (KeyedMatcherSet) table.getValueAt(row,
+ column);
+ tip = o.isEmpty()
+ ? MessageManager.getString("label.filters_tooltip")
+ : o.toString();
+ break;
+ default:
+ break;
}
- return null;
+ return tip;
}
};
table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
table.setFont(new Font("Verdana", Font.PLAIN, 12));
- table.setDefaultRenderer(Color.class, new ColorRenderer());
-
- table.setDefaultEditor(Color.class, new ColorEditor(this));
+ // table.setDefaultRenderer(Color.class, new ColorRenderer());
+ // table.setDefaultEditor(Color.class, new ColorEditor(this));
+ //
table.setDefaultEditor(FeatureColour.class, new ColorEditor(this));
table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
+
+ table.setDefaultEditor(KeyedMatcherSet.class, new FilterEditor(this));
+ table.setDefaultRenderer(KeyedMatcherSet.class, new FilterRenderer());
+
+ TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
+ new ColorRenderer(), new ColorEditor(this));
+ table.addColumn(colourColumn);
+
+ TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
+ new FilterRenderer(), new FilterEditor(this));
+ table.addColumn(filterColumn);
+
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.addMouseListener(new MouseAdapter()
public void mousePressed(MouseEvent evt)
{
selectedRow = table.rowAtPoint(evt.getPoint());
+ String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
if (evt.isPopupTrigger())
{
- popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
- table.getValueAt(selectedRow, 1), fr.getMinMax(),
- evt.getX(), evt.getY());
+ Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
+ popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
+ evt.getY());
}
else if (evt.getClickCount() == 2)
{
boolean toggleSelection = Platform.isControlDown(evt);
boolean extendSelection = evt.isShiftDown();
fr.ap.alignFrame.avc.markColumnsContainingFeatures(
- invertSelection, extendSelection, toggleSelection,
- (String) table.getValueAt(selectedRow, 0));
+ invertSelection, extendSelection, toggleSelection, type);
}
}
selectedRow = table.rowAtPoint(evt.getPoint());
if (evt.isPopupTrigger())
{
- popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
- table.getValueAt(selectedRow, 1), fr.getMinMax(),
- evt.getX(), evt.getY());
+ String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
+ Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
+ popupSort(selectedRow, type, colour, fr.getMinMax(), evt.getX(),
+ evt.getY());
}
}
});
if (!fs.resettingTable && !fs.handlingUpdate)
{
fs.handlingUpdate = true;
- fs.resetTable(null); // new groups may be added with new seuqence
- // feature types only
+ fs.resetTable(null);
+ // new groups may be added with new sequence feature types only
fs.handlingUpdate = false;
}
}
inConstruction = false;
}
- protected void popupSort(final int selectedRow, final String type,
+ protected void popupSort(final int rowSelected, final String type,
final Object typeCol, final Map<String, float[][]> minmax, int x,
int y)
{
{
if (featureColour.isSimpleColour())
{
- FeatureColourChooser fc = new FeatureColourChooser(me.fr, type);
+ FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
fc.addActionListener(this);
}
else
{
// bring up simple color chooser
colorChooser = new JColorChooser();
+ String title = MessageManager
+ .getString("label.select_colour");
JDialog dialog = JColorChooser.createDialog(me,
- "Select new Colour", true, // modal
+ title, true, // modal
colorChooser, this, // OK button handler
null); // no CANCEL button handler
colorChooser.setColor(featureColour.getMaxColour());
}
else
{
- if (e.getSource() instanceof FeatureColourChooser)
+ if (e.getSource() instanceof FeatureTypeSettings)
{
- FeatureColourChooser fc = (FeatureColourChooser) e.getSource();
- table.setValueAt(fc.getLastColour(), selectedRow, 1);
+ /*
+ * update after OK in feature colour dialog; the updated
+ * colour will have already been set in the FeatureRenderer
+ */
+ FeatureColourI fci = fr.getFeatureColours().get(type);
+ table.setValueAt(fci, rowSelected, 1);
table.validate();
}
else
{
// probably the color chooser!
table.setValueAt(new FeatureColour(colorChooser.getColor()),
- selectedRow, 1);
+ rowSelected, 1);
table.validate();
me.updateFeatureRenderer(
- ((FeatureTableModel) table.getModel()).getData(), false);
+ ((FeatureTableModel) table.getModel()).getData(),
+ false);
}
}
}
}
}
- populateFilterableFeatures();
-
resetTable(null);
validate();
}
}
- Object[][] data = new Object[displayableTypes.size()][3];
+ int columnCount = COLUMN_COUNT;
+ Object[][] data = new Object[displayableTypes.size()][columnCount];
int dataIndex = 0;
if (fr.hasRenderOrder())
continue;
}
- data[dataIndex][0] = type;
- data[dataIndex][1] = fr.getFeatureStyle(type);
- data[dataIndex][2] = new Boolean(
+ data[dataIndex][TYPE_COLUMN] = type;
+ data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
+ KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+ data[dataIndex][FILTER_COLUMN] = featureFilter == null
+ ? new KeyedMatcherSet()
+ : featureFilter;
+ data[dataIndex][SHOW_COLUMN] = new Boolean(
af.getViewport().getFeaturesDisplayed().isVisible(type));
dataIndex++;
displayableTypes.remove(type);
while (!displayableTypes.isEmpty())
{
String type = displayableTypes.iterator().next();
- data[dataIndex][0] = type;
+ data[dataIndex][TYPE_COLUMN] = type;
- data[dataIndex][1] = fr.getFeatureStyle(type);
- if (data[dataIndex][1] == null)
+ data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
+ if (data[dataIndex][COLOUR_COLUMN] == null)
{
// "Colour has been updated in another view!!"
fr.clearRenderOrder();
return;
}
-
- data[dataIndex][2] = new Boolean(true);
+ KeyedMatcherSetI featureFilter = fr.getFeatureFilter(type);
+ data[dataIndex][FILTER_COLUMN] = featureFilter == null
+ ? new KeyedMatcherSet()
+ : featureFilter;
+ data[dataIndex][SHOW_COLUMN] = new Boolean(true);
dataIndex++;
displayableTypes.remove(type);
}
if (originalData == null)
{
- originalData = new Object[data.length][3];
+ int size = data[0].length;
+ originalData = new Object[data.length][size];
for (int i = 0; i < data.length; i++)
{
- System.arraycopy(data[i], 0, originalData[i], 0, 3);
+ System.arraycopy(data[i], 0, originalData[i], 0, size);
}
}
else
}
/**
- * Updates 'originalData' (used for restore on Cancel) if we detect that
- * changes have been made outwith this dialog
+ * Updates 'originalData' (used for restore on Cancel) if we detect that changes
+ * have been made outwith this dialog
* <ul>
* <li>a new feature type added (and made visible)</li>
* <li>a feature colour changed (in the Amend Features dialog)</li>
.getData();
for (Object[] row : foundData)
{
- String type = (String) row[0];
+ String type = (String) row[TYPE_COLUMN];
boolean found = false;
for (Object[] current : currentData)
{
- if (type.equals(current[0]))
+ if (type.equals(current[TYPE_COLUMN]))
{
found = true;
/*
* currently dependent on object equality here;
* really need an equals method on FeatureColour
*/
- if (!row[1].equals(current[1]))
+ if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
{
/*
* feature colour has changed externally - update originalData
*/
for (Object[] original : originalData)
{
- if (type.equals(original[0]))
+ if (type.equals(original[TYPE_COLUMN]))
{
- original[1] = row[1];
+ original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
break;
}
}
/*
* new feature detected - add to original data (on top)
*/
- Object[][] newData = new Object[originalData.length + 1][3];
+ int size = currentData[0].length;
+ Object[][] newData = new Object[originalData.length + 1][size];
for (int i = 0; i < originalData.length; i++)
{
- System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3);
+ System.arraycopy(originalData[i], 0, newData[i + 1], 0, size);
}
newData[0] = row;
originalData = newData;
/**
* Remove from the groups panel any checkboxes for groups that are not in the
- * foundGroups set. This enables removing a group from the display when the
- * last feature in that group is deleted.
+ * foundGroups set. This enables removing a group from the display when the last
+ * feature in that group is deleted.
*
* @param foundGroups
*/
{
for (int i = 0; i < table.getRowCount(); i++)
{
- Boolean value = (Boolean) table.getValueAt(i, 2);
+ Boolean value = (Boolean) table.getValueAt(i, SHOW_COLUMN);
- table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
+ table.setValueAt(new Boolean(!value.booleanValue()), i, SHOW_COLUMN);
}
}
float[] width = new float[data.length];
float[] awidth;
float max = 0;
- int num = 0;
+
for (int i = 0; i < data.length; i++)
{
- awidth = typeWidth.get(data[i][0]);
+ awidth = typeWidth.get(data[i][TYPE_COLUMN]);
if (awidth[0] > 0)
{
width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
// weight - but have to make per
// sequence, too (awidth[2])
// if (width[i]==1) // hack to distinguish single width sequences.
- num++;
}
else
{
// awidth = (float[]) typeWidth.get(data[i][0]);
if (width[i] == 0)
{
- width[i] = fr.getOrder(data[i][0].toString());
+ width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
if (width[i] < 0)
{
- width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
+ width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
+ i / data.length);
}
}
else
{
width[i] /= max; // normalize
- fr.setOrder(data[i][0].toString(), width[i]); // store for later
+ fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for later
}
if (i > 0)
{
}
/**
- * Update the priority order of features; only repaint if this changed the
- * order of visible features
+ * Update the priority order of features; only repaint if this changed the order
+ * of visible features
*
* @param data
* @param visibleNew
JPanel settingsPane = new JPanel();
settingsPane.setLayout(new BorderLayout());
- filtersPane = new JPanel();
-
dasSettingsPane.setLayout(new BorderLayout());
JPanel bigPanel = new JPanel();
if (!inConstruction)
{
fr.setTransparency((100 - transparency.getValue()) / 100f);
- af.alignPanel.paintAlignment(true,true);
+ af.alignPanel.paintAlignment(true, true);
}
}
});
}
});
- JTabbedPane tabbedPane = new JTabbedPane();
- this.add(tabbedPane, BorderLayout.CENTER);
- tabbedPane.addTab(MessageManager.getString("label.feature_settings"),
- settingsPane);
- tabbedPane.addTab(MessageManager.getString("label.filters"),
- filtersPane);
- // tabbedPane.addTab(MessageManager.getString("label.das_settings"),
- // dasSettingsPane);
-
JPanel transPanel = new JPanel(new GridLayout(1, 2));
bigPanel.add(transPanel, BorderLayout.SOUTH);
dasButtonPanel.add(saveDAS);
settingsPane.add(bigPanel, BorderLayout.CENTER);
settingsPane.add(buttonPanel, BorderLayout.SOUTH);
-
- initFiltersTab();
- }
-
- /**
- * Populates initial layout of the feature attribute filters panel
- */
- protected void initFiltersTab()
- {
- filters = new ArrayList<>();
-
- /*
- * choose feature type
- */
- JPanel chooseTypePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- chooseTypePanel.setBackground(Color.white);
- JvSwingUtils.createItalicTitledBorder(chooseTypePanel,
- MessageManager.getString("label.feature_type"), true);
- filteredFeatureChoice = new JComboBox<>();
- filteredFeatureChoice.addItemListener(new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent e)
- {
- refreshFiltersDisplay();
- }
- });
- chooseTypePanel.add(new JLabel(MessageManager
- .getString("label.feature_to_filter")));
- chooseTypePanel.add(filteredFeatureChoice);
- populateFilterableFeatures();
-
- /*
- * the panel with the filters for the selected feature type
- */
- JPanel filtersPanel = new JPanel();
- filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
- filtersPanel.setBackground(Color.white);
- JvSwingUtils.createItalicTitledBorder(filtersPanel,
- MessageManager.getString("label.filters"), true);
-
- /*
- * add AND or OR radio buttons
- */
- JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
- andOrPanel.setBackground(Color.white);
- andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
- andFilters = new JRadioButton("And");
- orFilters = new JRadioButton("Or");
- ActionListener actionListener = new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- filtersChanged();
- }
- };
- andFilters.addActionListener(actionListener);
- orFilters.addActionListener(actionListener);
- ButtonGroup andOr = new ButtonGroup();
- andOr.add(andFilters);
- andOr.add(orFilters);
- andFilters.setSelected(true);
- andOrPanel.add(new JLabel(MessageManager
- .getString("label.join_conditions")));
- andOrPanel.add(andFilters);
- andOrPanel.add(orFilters);
- filtersPanel.add(andOrPanel);
-
- /*
- * panel with filters - populated by refreshFiltersDisplay
- */
- chooseFiltersPanel = new JPanel();
- LayoutManager box = new BoxLayout(chooseFiltersPanel,
- BoxLayout.Y_AXIS);
- chooseFiltersPanel.setLayout(box);
- filtersPanel.add(chooseFiltersPanel);
-
- /*
- * a read-only text view of the current filters
- */
- JPanel showFiltersPanel = new JPanel(new BorderLayout(5, 5));
- showFiltersPanel.setBackground(Color.white);
- JvSwingUtils.createItalicTitledBorder(showFiltersPanel,
- MessageManager.getString("label.match_condition"), true);
- filtersAsText = new JTextArea();
- filtersAsText.setLineWrap(true);
- filtersAsText.setWrapStyleWord(true);
- showFiltersPanel.add(filtersAsText);
-
- filtersPane.setLayout(new BorderLayout());
- filtersPane.add(chooseTypePanel, BorderLayout.NORTH);
- filtersPane.add(filtersPanel, BorderLayout.CENTER);
- filtersPane.add(showFiltersPanel, BorderLayout.SOUTH);
-
- /*
- * update display for initial feature type selection
- */
- refreshFiltersDisplay();
- }
-
- /**
- * Adds entries to the 'choose feature to filter' drop-down choice. Only
- * feature types which have known attributes (so can be filtered) are
- * included, so recall this method to update the list (check for newly added
- * attributes).
- */
- protected void populateFilterableFeatures()
- {
- /*
- * suppress action handler while updating the list
- */
- ItemListener listener = filteredFeatureChoice.getItemListeners()[0];
- filteredFeatureChoice.removeItemListener(listener);
-
- filteredFeatureChoice.removeAllItems();
- ReverseListIterator<String> types = new ReverseListIterator<>(
- fr.getRenderOrder());
-
- boolean found = false;
- while (types.hasNext())
- {
- String type = types.next();
- if (FeatureAttributes.getInstance().hasAttributes(type))
- {
- filteredFeatureChoice.addItem(type);
- found = true;
- }
- }
- if (!found)
- {
- filteredFeatureChoice.addItem(MessageManager
- .getString("label.no_feature_attributes"));
- filteredFeatureChoice.setEnabled(false);
- }
-
- filteredFeatureChoice.addItemListener(listener);
- }
-
- /**
- * Refreshes the display to show any filters currently configured for the
- * selected feature type (editable, with 'remove' option), plus one extra row
- * for adding a condition. This should be called on change of selected feature
- * type, or after a filter has been removed, added or amended.
- */
- protected void refreshFiltersDisplay()
- {
- /*
- * clear the panel and list of filter conditions
- */
- chooseFiltersPanel.removeAll();
- filters.clear();
-
- /*
- * look up attributes known for feature type
- */
- String selectedType = (String) filteredFeatureChoice.getSelectedItem();
- List<String[]> attNames = FeatureAttributes.getInstance()
- .getAttributes(selectedType);
-
- /*
- * if this feature type has filters set, load them first
- */
- KeyedMatcherSetI featureFilters = fr.getFeatureFilter(selectedType);
- filtersAsText.setText("");
- if (featureFilters != null)
- {
- filtersAsText.setText(featureFilters.toString());
- if (!featureFilters.isAnded())
- {
- orFilters.setSelected(true);
- }
- featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
- }
-
- /*
- * and an empty filter for the user to populate (add)
- */
- KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
- (String) null);
- filters.add(noFilter);
-
- /*
- * render the conditions in rows, each in its own JPanel
- */
- int filterIndex = 0;
- for (KeyedMatcherI filter : filters)
- {
- String[] attName = filter.getKey();
- Condition condition = filter.getMatcher()
- .getCondition();
- String pattern = filter.getMatcher().getPattern();
- JPanel row = addFilter(attName, attNames, condition, pattern, filterIndex);
- row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
- chooseFiltersPanel.add(row);
- filterIndex++;
- }
- // chooseFiltersPanel.add(Box.createVerticalGlue());
-
- filtersPane.validate();
- filtersPane.repaint();
- }
-
- /**
- * A helper method that constructs a panel with one filter condition:
- * <ul>
- * <li>a drop-down list of attribute names to choose from</li>
- * <li>a drop-down list of conditions to choose from</li>
- * <li>a text field for input of a match pattern</li>
- * <li>optionally, a 'remove' button</li>
- * </ul>
- * If attribute, condition or pattern are not null, they are set as defaults for
- * the input fields. The 'remove' button is added unless the pattern is null or
- * empty (incomplete filter condition).
- *
- * @param attName
- * @param attNames
- * @param cond
- * @param pattern
- * @param filterIndex
- * @return
- */
- protected JPanel addFilter(String[] attName, List<String[]> attNames,
- Condition cond, String pattern, int filterIndex)
- {
- JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
- filterRow.setBackground(Color.white);
-
- /*
- * drop-down choice of attribute, with description as a tooltip
- * if we can obtain it
- */
- String featureType = (String) filteredFeatureChoice.getSelectedItem();
- final JComboBox<String> attCombo = populateAttributesDropdown(
- featureType, attNames);
- JComboBox<Condition> condCombo = new JComboBox<>();
- JTextField patternField = new JTextField(8);
-
- /*
- * action handlers that validate and (if valid) apply changes
- */
- ActionListener actionListener = new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- if (attCombo.getSelectedItem() != null)
- {
- if (validateFilter(patternField, condCombo))
- {
- updateFilter(attCombo, condCombo, patternField, filterIndex);
- filtersChanged();
- }
- }
- }
- };
- ItemListener itemListener = new ItemListener()
- {
- @Override
- public void itemStateChanged(ItemEvent e)
- {
- actionListener.actionPerformed(null);
- }
- };
-
- if (attName == null) // the 'add a condition' row
- {
- attCombo.setSelectedItem(null);
- }
- else
- {
- attCombo.setSelectedItem(String.join(COLON, attName));
- }
- attCombo.addItemListener(itemListener);
-
- filterRow.add(attCombo);
-
- /*
- * drop-down choice of test condition
- */
- for (Condition c : Condition.values())
- {
- condCombo.addItem(c);
- }
- if (cond != null)
- {
- condCombo.setSelectedItem(cond);
- }
- condCombo.addItemListener(itemListener);
- filterRow.add(condCombo);
-
- /*
- * pattern to match against
- */
- patternField.setText(pattern);
- patternField.addActionListener(actionListener);
- patternField.addFocusListener(new FocusAdapter()
- {
- @Override
- public void focusLost(FocusEvent e)
- {
- actionListener.actionPerformed(null);
- }
- });
- filterRow.add(patternField);
-
- /*
- * add remove button if filter is populated (non-empty pattern)
- */
- if (pattern != null && pattern.trim().length() > 0)
- {
- // todo: gif for button drawing '-' or 'x'
- JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
- removeCondition.setToolTipText(MessageManager
- .getString("label.delete_row"));
- removeCondition.addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- filters.remove(filterIndex);
- filtersChanged();
- }
- });
- filterRow.add(removeCondition);
- }
-
- return filterRow;
- }
-
- /**
- * A helper method to build the drop-down choice of attributes for a feature.
- * Where metadata is available with a description for an attribute, that is
- * added as a tooltip.
- *
- * @param featureType
- * @param attNames
- */
- protected JComboBox<String> populateAttributesDropdown(
- String featureType, List<String[]> attNames)
- {
- List<String> displayNames = new ArrayList<>();
- List<String> tooltips = new ArrayList<>();
- FeatureAttributes fa = FeatureAttributes.getInstance();
- for (String[] attName : attNames)
- {
- String desc = fa.getDescription(featureType, attName);
- if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
- {
- desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
- }
- displayNames.add(String.join(COLON, attName));
- tooltips.add(desc == null ? "" : desc);
- }
-
- JComboBox<String> attCombo = JvSwingUtils.buildComboWithTooltips(
- displayNames, tooltips);
- if (attNames.isEmpty())
- {
- attCombo.setToolTipText(MessageManager
- .getString("label.no_attributes"));
- }
- return attCombo;
- }
-
- /**
- * Action on any change to feature filtering, namely
- * <ul>
- * <li>change of selected attribute</li>
- * <li>change of selected condition</li>
- * <li>change of match pattern</li>
- * <li>removal of a condition</li>
- * </ul>
- * The action should be to
- * <ul>
- * <li>parse and validate the filters</li>
- * <li>if valid, update the filter text box</li>
- * <li>and apply the filters to the viewport</li>
- * </ul>
- */
- protected void filtersChanged()
- {
- /*
- * update the filter conditions for the feature type
- */
- String featureType = (String) filteredFeatureChoice.getSelectedItem();
- boolean anded = andFilters.isSelected();
- KeyedMatcherSetI combined = new KeyedMatcherSet();
-
- for (KeyedMatcherI filter : filters)
- {
- String pattern = filter.getMatcher().getPattern();
- if (pattern.trim().length() > 0)
- {
- if (anded)
- {
- combined.and(filter);
- }
- else
- {
- combined.or(filter);
- }
- }
- }
-
- /*
- * save the filter conditions in the FeatureRenderer
- * (note this might now be an empty filter with no conditions)
- */
- fr.setFeatureFilter(featureType, combined);
-
- filtersAsText.setText(combined.toString());
-
- refreshFiltersDisplay();
-
- af.alignPanel.paintAlignment(true, true);
- }
-
- /**
- * Constructs a filter condition from the given input fields, and replaces the
- * condition at filterIndex with the new one
- *
- * @param attCombo
- * @param condCombo
- * @param valueField
- * @param filterIndex
- */
- protected void updateFilter(JComboBox<String> attCombo,
- JComboBox<Condition> condCombo, JTextField valueField,
- int filterIndex)
- {
- String attName = (String) attCombo.getSelectedItem();
- Condition cond = (Condition) condCombo.getSelectedItem();
- String pattern = valueField.getText();
- KeyedMatcherI km = new KeyedMatcher(cond, pattern,
- attName.split(COLON));
-
- filters.set(filterIndex, km);
+ this.add(settingsPane);
}
public void fetchDAS_actionPerformed(ActionEvent e)
JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE);
}
- /**
- * Answers true unless a numeric condition has been selected with a
- * non-numeric value. Sets the value field to RED with a tooltip if in error.
- * <p>
- * If the pattern entered is empty, this method returns false, but does not
- * mark the field as invalid. This supports selecting an attribute for a new
- * condition before a match pattern has been entered.
- *
- * @param value
- * @param condCombo
- */
- protected boolean validateFilter(JTextField value,
- JComboBox<Condition> condCombo)
- {
- if (value == null || condCombo == null)
- {
- return true; // fields not populated
- }
-
- Condition cond = (Condition) condCombo.getSelectedItem();
- value.setBackground(Color.white);
- value.setToolTipText("");
- String v1 = value.getText().trim();
- if (v1.length() == 0)
- {
- return false;
- }
-
- if (cond.isNumeric())
- {
- try
- {
- Float.valueOf(v1);
- } catch (NumberFormatException e)
- {
- value.setBackground(Color.red);
- value.setToolTipText(MessageManager
- .getString("label.numeric_required"));
- return false;
- }
- }
-
- return true;
- }
-
// ///////////////////////////////////////////////////////////////////////
// http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
// ///////////////////////////////////////////////////////////////////////
class FeatureTableModel extends AbstractTableModel
{
- FeatureTableModel(Object[][] data)
- {
- this.data = data;
- }
private String[] columnNames = {
MessageManager.getString("label.feature_type"),
MessageManager.getString("action.colour"),
- MessageManager.getString("label.display") };
+ MessageManager.getString("label.filter"),
+ MessageManager.getString("label.show") };
private Object[][] data;
+ FeatureTableModel(Object[][] data)
+ {
+ this.data = data;
+ }
+
public Object[][] getData()
{
return data;
return data[row][col];
}
+ /**
+ * Answers the class of the object in column c of the first row of the table
+ */
@Override
- public Class getColumnClass(int c)
+ public Class<?> getColumnClass(int c)
{
- return getValueAt(0, c).getClass();
+ Object v = getValueAt(0, c);
+ return v == null ? null : v.getClass();
}
@Override
}
}
+ class FilterRenderer extends JLabel implements TableCellRenderer
+ {
+ javax.swing.border.Border unselectedBorder = null;
+
+ javax.swing.border.Border selectedBorder = null;
+
+ public FilterRenderer()
+ {
+ setOpaque(true); // MUST do this for background to show up.
+ setHorizontalTextPosition(SwingConstants.CENTER);
+ setVerticalTextPosition(SwingConstants.CENTER);
+ }
+
+ @Override
+ public Component getTableCellRendererComponent(JTable tbl,
+ Object filter, boolean isSelected, boolean hasFocus, int row,
+ int column)
+ {
+ KeyedMatcherSetI theFilter = (KeyedMatcherSetI) filter;
+ setOpaque(true);
+ String asText = theFilter.toString();
+ setBackground(tbl.getBackground());
+ this.setText(asText);
+ this.setIcon(null);
+
+ if (isSelected)
+ {
+ if (selectedBorder == null)
+ {
+ selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
+ tbl.getSelectionBackground());
+ }
+ setBorder(selectedBorder);
+ }
+ else
+ {
+ if (unselectedBorder == null)
+ {
+ unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
+ tbl.getBackground());
+ }
+ setBorder(unselectedBorder);
+ }
+
+ return this;
+ }
+ }
+
/**
* update comp using rendering settings from gcol
*
}
else
{
- comp.setToolTipText(tt.append(" ").append(comp.getToolTipText())
- .toString());
+ comp.setToolTipText(
+ tt.append(" ").append(comp.getToolTipText()).toString());
}
}
}
+
+ class ColorEditor extends AbstractCellEditor
+ implements TableCellEditor, ActionListener
+ {
+ FeatureSettings me;
+
+ FeatureColourI currentColor;
+
+ FeatureTypeSettings chooser;
+
+ String type;
+
+ JButton button;
+
+ JColorChooser colorChooser;
+
+ JDialog dialog;
+
+ protected static final String EDIT = "edit";
+
+ int rowSelected = 0;
+
+ public ColorEditor(FeatureSettings me)
+ {
+ this.me = me;
+ // Set up the editor (from the table's point of view),
+ // which is a button.
+ // This button brings up the color chooser dialog,
+ // which is the editor from the user's point of view.
+ button = new JButton();
+ button.setActionCommand(EDIT);
+ button.addActionListener(this);
+ button.setBorderPainted(false);
+ // Set up the dialog that the button brings up.
+ colorChooser = new JColorChooser();
+ dialog = JColorChooser.createDialog(button,
+ MessageManager.getString("label.select_colour"), true, // modal
+ colorChooser, this, // OK button handler
+ null); // no CANCEL button handler
+ }
+
+ /**
+ * Handles events from the editor button and from the dialog's OK button.
+ */
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ // todo test e.getSource() instead here
+ if (EDIT.equals(e.getActionCommand()))
+ {
+ // The user has clicked the cell, so
+ // bring up the dialog.
+ if (currentColor.isSimpleColour())
+ {
+ // bring up simple color chooser
+ button.setBackground(currentColor.getColour());
+ colorChooser.setColor(currentColor.getColour());
+ dialog.setVisible(true);
+ }
+ else
+ {
+ // bring up graduated chooser.
+ chooser = new FeatureTypeSettings(me.fr, type);
+ chooser.setRequestFocusEnabled(true);
+ chooser.requestFocus();
+ chooser.addActionListener(this);
+ chooser.showTab(true);
+ }
+ // Make the renderer reappear.
+ fireEditingStopped();
+
+ }
+ else
+ {
+ if (currentColor.isSimpleColour())
+ {
+ /*
+ * read off colour picked in colour chooser after OK pressed
+ */
+ currentColor = new FeatureColour(colorChooser.getColor());
+ me.table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
+ }
+ else
+ {
+ /*
+ * after OK in variable colour dialog, any changes to colour
+ * (or filters!) are already set in FeatureRenderer, so just
+ * update table data without triggering updateFeatureRenderer
+ */
+ currentColor = fr.getFeatureColours().get(type);
+ KeyedMatcherSetI currentFilter = me.fr.getFeatureFilter(type);
+ if (currentFilter == null)
+ {
+ currentFilter = new KeyedMatcherSet();
+ }
+ Object[] data = ((FeatureTableModel) table.getModel())
+ .getData()[rowSelected];
+ data[COLOUR_COLUMN] = currentColor;
+ data[FILTER_COLUMN] = currentFilter;
+ }
+ fireEditingStopped();
+ me.table.validate();
+ }
+ }
+
+ // Implement the one CellEditor method that AbstractCellEditor doesn't.
+ @Override
+ public Object getCellEditorValue()
+ {
+ return currentColor;
+ }
+
+ // Implement the one method defined by TableCellEditor.
+ @Override
+ public Component getTableCellEditorComponent(JTable theTable, Object value,
+ boolean isSelected, int row, int column)
+ {
+ currentColor = (FeatureColourI) value;
+ this.rowSelected = row;
+ type = me.table.getValueAt(row, TYPE_COLUMN).toString();
+ button.setOpaque(true);
+ button.setBackground(me.getBackground());
+ if (!currentColor.isSimpleColour())
+ {
+ JLabel btn = new JLabel();
+ btn.setSize(button.getSize());
+ FeatureSettings.renderGraduatedColor(btn, currentColor);
+ button.setBackground(btn.getBackground());
+ button.setIcon(btn.getIcon());
+ button.setText(btn.getText());
+ }
+ else
+ {
+ button.setText("");
+ button.setIcon(null);
+ button.setBackground(currentColor.getColour());
+ }
+ return button;
+ }
+ }
+
+ /**
+ * The cell editor for the Filter column. It displays the text of any filters
+ * for the feature type in that row (in full as a tooltip, possible abbreviated
+ * as display text). On click in the cell, opens the Feature Display Settings
+ * dialog at the Filters tab.
+ */
+ class FilterEditor extends AbstractCellEditor
+ implements TableCellEditor, ActionListener
+ {
+ FeatureSettings me;
+
+ KeyedMatcherSetI currentFilter;
+
+ Point lastLocation;
+
+ String type;
+
+ JButton button;
+
+ protected static final String EDIT = "edit";
+
+ int rowSelected = 0;
+
+ public FilterEditor(FeatureSettings me)
+ {
+ this.me = me;
+ button = new JButton();
+ button.setActionCommand(EDIT);
+ button.addActionListener(this);
+ button.setBorderPainted(false);
+ }
+
+ /**
+ * Handles events from the editor button
+ */
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (button == e.getSource())
+ {
+ FeatureTypeSettings chooser = new FeatureTypeSettings(me.fr, type);
+ chooser.addActionListener(this);
+ chooser.setRequestFocusEnabled(true);
+ chooser.requestFocus();
+ if (lastLocation != null)
+ {
+ // todo open at its last position on screen
+ chooser.setBounds(lastLocation.x, lastLocation.y,
+ chooser.getWidth(), chooser.getHeight());
+ chooser.validate();
+ }
+ chooser.showTab(false);
+ fireEditingStopped();
+ }
+ else if (e.getSource() instanceof Component)
+ {
+
+ /*
+ * after OK in variable colour dialog, any changes to filter
+ * (or colours!) are already set in FeatureRenderer, so just
+ * update table data without triggering updateFeatureRenderer
+ */
+ FeatureColourI currentColor = fr.getFeatureColours().get(type);
+ currentFilter = me.fr.getFeatureFilter(type);
+ if (currentFilter == null)
+ {
+ currentFilter = new KeyedMatcherSet();
+ }
+ Object[] data = ((FeatureTableModel) table.getModel())
+ .getData()[rowSelected];
+ data[COLOUR_COLUMN] = currentColor;
+ data[FILTER_COLUMN] = currentFilter;
+ fireEditingStopped();
+ me.table.validate();
+ }
+ }
+
+ @Override
+ public Object getCellEditorValue()
+ {
+ return currentFilter;
+ }
+
+ @Override
+ public Component getTableCellEditorComponent(JTable theTable, Object value,
+ boolean isSelected, int row, int column)
+ {
+ currentFilter = (KeyedMatcherSetI) value;
+ this.rowSelected = row;
+ type = me.table.getValueAt(row, TYPE_COLUMN).toString();
+ button.setOpaque(true);
+ button.setBackground(me.getBackground());
+ button.setText(currentFilter.toString());
+ button.setToolTipText(currentFilter.toString());
+ button.setIcon(null);
+ return button;
+ }
+ }
}
class FeatureIcon implements Icon
}
}
}
-
-class ColorEditor extends AbstractCellEditor
- implements TableCellEditor, ActionListener
-{
- FeatureSettings me;
-
- FeatureColourI currentColor;
-
- FeatureColourChooser chooser;
-
- String type;
-
- JButton button;
-
- JColorChooser colorChooser;
-
- JDialog dialog;
-
- protected static final String EDIT = "edit";
-
- int selectedRow = 0;
-
- public ColorEditor(FeatureSettings me)
- {
- this.me = me;
- // Set up the editor (from the table's point of view),
- // which is a button.
- // This button brings up the color chooser dialog,
- // which is the editor from the user's point of view.
- button = new JButton();
- button.setActionCommand(EDIT);
- button.addActionListener(this);
- button.setBorderPainted(false);
- // Set up the dialog that the button brings up.
- colorChooser = new JColorChooser();
- dialog = JColorChooser.createDialog(button,
- MessageManager.getString("label.select_new_colour"), true, // modal
- colorChooser, this, // OK button handler
- null); // no CANCEL button handler
- }
-
- /**
- * Handles events from the editor button and from the dialog's OK button.
- */
- @Override
- public void actionPerformed(ActionEvent e)
- {
-
- if (EDIT.equals(e.getActionCommand()))
- {
- // The user has clicked the cell, so
- // bring up the dialog.
- if (currentColor.isSimpleColour())
- {
- // bring up simple color chooser
- button.setBackground(currentColor.getColour());
- colorChooser.setColor(currentColor.getColour());
- dialog.setVisible(true);
- }
- else
- {
- // bring up graduated chooser.
- chooser = new FeatureColourChooser(me.fr, type);
- chooser.setRequestFocusEnabled(true);
- chooser.requestFocus();
- chooser.addActionListener(this);
- }
- // Make the renderer reappear.
- fireEditingStopped();
-
- }
- else
- { // User pressed dialog's "OK" button.
- if (currentColor.isSimpleColour())
- {
- currentColor = new FeatureColour(colorChooser.getColor());
- }
- else
- {
- currentColor = chooser.getLastColour();
- }
- me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
- fireEditingStopped();
- me.table.validate();
- }
- }
-
- // Implement the one CellEditor method that AbstractCellEditor doesn't.
- @Override
- public Object getCellEditorValue()
- {
- return currentColor;
- }
-
- // Implement the one method defined by TableCellEditor.
- @Override
- public Component getTableCellEditorComponent(JTable table, Object value,
- boolean isSelected, int row, int column)
- {
- currentColor = (FeatureColourI) value;
- this.selectedRow = row;
- type = me.table.getValueAt(row, 0).toString();
- button.setOpaque(true);
- button.setBackground(me.getBackground());
- if (!currentColor.isSimpleColour())
- {
- JLabel btn = new JLabel();
- btn.setSize(button.getSize());
- FeatureSettings.renderGraduatedColor(btn, currentColor);
- button.setBackground(btn.getBackground());
- button.setIcon(btn.getIcon());
- button.setText(btn.getText());
- }
- else
- {
- button.setText("");
- button.setIcon(null);
- button.setBackground(currentColor.getColour());
- }
- return button;
- }
-}
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui;
+
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureColourI;
+import jalview.datamodel.GraphLine;
+import jalview.datamodel.features.FeatureAttributes;
+import jalview.datamodel.features.FeatureAttributes.Datatype;
+import jalview.schemes.FeatureColour;
+import jalview.util.ColorUtils;
+import jalview.util.MessageManager;
+import jalview.util.matcher.Condition;
+import jalview.util.matcher.KeyedMatcher;
+import jalview.util.matcher.KeyedMatcherI;
+import jalview.util.matcher.KeyedMatcherSet;
+import jalview.util.matcher.KeyedMatcherSetI;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.LayoutManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JSlider;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.border.LineBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.basic.BasicArrowButton;
+
+/**
+ * A dialog where the user can configure colour scheme, and any filters, for one
+ * feature type
+ * <p>
+ * (Was FeatureColourChooser prior to Jalview 1.11, renamed with the addition of
+ * filter options)
+ */
+public class FeatureTypeSettings extends JalviewDialog
+{
+ private static final int RADIO_WIDTH = 130;
+
+ private static final String COLON = ":";
+
+ private static final int MAX_TOOLTIP_LENGTH = 50;
+
+ private static final int NO_COLOUR_OPTION = 0;
+
+ private static final int MIN_COLOUR_OPTION = 1;
+
+ private static final int MAX_COLOUR_OPTION = 2;
+
+ private static final int ABOVE_THRESHOLD_OPTION = 1;
+
+ private static final int BELOW_THRESHOLD_OPTION = 2;
+
+ /*
+ * FeatureRenderer holds colour scheme and filters for feature types
+ */
+ private final FeatureRenderer fr; // todo refactor to allow interface type here
+
+ /*
+ * the view panel to update when settings change
+ */
+ private final AlignmentViewPanel ap;
+
+ private final String featureType;
+
+ /*
+ * the colour and filters to reset to on Cancel
+ */
+ private final FeatureColourI originalColour;
+
+ private final KeyedMatcherSetI originalFilter;
+
+ /*
+ * set flag to true when setting values programmatically,
+ * to avoid invocation of action handlers
+ */
+ private boolean adjusting = false;
+
+ private float min;
+
+ private float max;
+
+ private float scaleFactor;
+
+ /*
+ * radio button group, to select what to colour by:
+ * simple colour, by category (text), or graduated
+ */
+ private JRadioButton simpleColour = new JRadioButton();
+
+ private JRadioButton byCategory = new JRadioButton();
+
+ private JRadioButton graduatedColour = new JRadioButton();
+
+ private JPanel singleColour = new JPanel();
+
+ private JPanel minColour = new JPanel();
+
+ private JPanel maxColour = new JPanel();
+
+ private JComboBox<String> threshold = new JComboBox<>();
+
+ private JSlider slider = new JSlider();
+
+ private JTextField thresholdValue = new JTextField(20);
+
+ private JCheckBox thresholdIsMin = new JCheckBox();
+
+ private GraphLine threshline;
+
+ private ActionListener featureSettings = null;
+
+ private ActionListener changeColourAction;
+
+ /*
+ * choice of option for 'colour for no value'
+ */
+ private JComboBox<String> noValueCombo;
+
+ /*
+ * choice of what to colour by text (Label or attribute)
+ */
+ private JComboBox<String> colourByTextCombo;
+
+ /*
+ * choice of what to colour by range (Score or attribute)
+ */
+ private JComboBox<String> colourByRangeCombo;
+
+ private JRadioButton andFilters;
+
+ private JRadioButton orFilters;
+
+ /*
+ * filters for the currently selected feature type
+ */
+ private List<KeyedMatcherI> filters;
+
+ // set white normally, black to debug layout
+ private Color debugBorderColour = Color.white;
+
+ private JPanel chooseFiltersPanel;
+
+ private JTabbedPane tabbedPane;
+
+ /**
+ * Constructor
+ *
+ * @param frender
+ * @param theType
+ */
+ public FeatureTypeSettings(FeatureRenderer frender, String theType)
+ {
+ this(frender, false, theType);
+ }
+
+ /**
+ * Constructor, with option to make a blocking dialog (has to complete in the
+ * AWT event queue thread). Currently this option is always set to false.
+ *
+ * @param frender
+ * @param blocking
+ * @param theType
+ */
+ FeatureTypeSettings(FeatureRenderer frender, boolean blocking,
+ String theType)
+ {
+ this.fr = frender;
+ this.featureType = theType;
+ ap = fr.ap;
+ originalFilter = fr.getFeatureFilter(theType);
+ originalColour = fr.getFeatureColours().get(theType);
+
+ adjusting = true;
+
+ try
+ {
+ initialise();
+ } catch (Exception ex)
+ {
+ ex.printStackTrace();
+ return;
+ }
+
+ updateColoursTab();
+
+ updateFiltersTab();
+
+ adjusting = false;
+
+ colourChanged(false);
+
+ String title = MessageManager
+ .formatMessage("label.display_settings_for", new String[]
+ { theType });
+ initDialogFrame(this, true, blocking, title, 600, 360);
+
+ waitForInput();
+ }
+
+ /**
+ * Configures the widgets on the Colours tab according to the current feature
+ * colour scheme
+ */
+ private void updateColoursTab()
+ {
+ FeatureColourI fc = fr.getFeatureColours().get(featureType);
+
+ /*
+ * suppress action handling while updating values programmatically
+ */
+ adjusting = true;
+ try
+ {
+ /*
+ * single colour
+ */
+ if (fc.isSimpleColour())
+ {
+ simpleColour.setSelected(true);
+ singleColour.setBackground(fc.getColour());
+ singleColour.setForeground(fc.getColour());
+ }
+
+ /*
+ * colour by text (Label or attribute text)
+ */
+ if (fc.isColourByLabel())
+ {
+ byCategory.setSelected(true);
+ colourByTextCombo.setEnabled(colourByTextCombo.getItemCount() > 1);
+ if (fc.isColourByAttribute())
+ {
+ String[] attributeName = fc.getAttributeName();
+ colourByTextCombo
+ .setSelectedItem(toAttributeDisplayName(attributeName));
+ }
+ else
+ {
+ colourByTextCombo
+ .setSelectedItem(MessageManager.getString("label.label"));
+ }
+ }
+ else
+ {
+ colourByTextCombo.setEnabled(false);
+ }
+
+ if (!fc.isGraduatedColour())
+ {
+ colourByRangeCombo.setEnabled(false);
+ minColour.setEnabled(false);
+ maxColour.setEnabled(false);
+ noValueCombo.setEnabled(false);
+ threshold.setEnabled(false);
+ slider.setEnabled(false);
+ thresholdValue.setEnabled(false);
+ thresholdIsMin.setEnabled(false);
+ return;
+ }
+
+ /*
+ * Graduated colour, by score or attribute value range
+ */
+ graduatedColour.setSelected(true);
+ colourByRangeCombo.setEnabled(colourByRangeCombo.getItemCount() > 1);
+ minColour.setEnabled(true);
+ maxColour.setEnabled(true);
+ noValueCombo.setEnabled(true);
+ threshold.setEnabled(true);
+ minColour.setBackground(fc.getMinColour());
+ maxColour.setBackground(fc.getMaxColour());
+
+ if (fc.isColourByAttribute())
+ {
+ String[] attributeName = fc.getAttributeName();
+ colourByRangeCombo
+ .setSelectedItem(toAttributeDisplayName(attributeName));
+ }
+ else
+ {
+ colourByRangeCombo
+ .setSelectedItem(MessageManager.getString("label.score"));
+ }
+ Color noColour = fc.getNoColour();
+ if (noColour == null)
+ {
+ noValueCombo.setSelectedIndex(NO_COLOUR_OPTION);
+ }
+ else if (noColour.equals(fc.getMinColour()))
+ {
+ noValueCombo.setSelectedIndex(MIN_COLOUR_OPTION);
+ }
+ else if (noColour.equals(fc.getMaxColour()))
+ {
+ noValueCombo.setSelectedIndex(MAX_COLOUR_OPTION);
+ }
+
+ /*
+ * update min-max scaling if there is a range to work with,
+ * else disable the widgets (this shouldn't happen if only
+ * valid options are offered in the combo box)
+ */
+ scaleFactor = (max == min) ? 1f : 100f / (max - min);
+ float range = (max - min) * scaleFactor;
+ slider.setMinimum((int) (min * scaleFactor));
+ slider.setMaximum((int) (max * scaleFactor));
+ slider.setMajorTickSpacing((int) (range / 10f));
+
+ threshline = new GraphLine((max - min) / 2f, "Threshold",
+ Color.black);
+ threshline.value = fc.getThreshold();
+
+ if (fc.hasThreshold())
+ {
+ threshold.setSelectedIndex(
+ fc.isAboveThreshold() ? ABOVE_THRESHOLD_OPTION
+ : BELOW_THRESHOLD_OPTION);
+ slider.setEnabled(true);
+ slider.setValue((int) (fc.getThreshold() * scaleFactor));
+ thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+ thresholdValue.setEnabled(true);
+ thresholdIsMin.setEnabled(true);
+ }
+ else
+ {
+ slider.setEnabled(false);
+ thresholdValue.setEnabled(false);
+ thresholdIsMin.setEnabled(false);
+ }
+ thresholdIsMin.setSelected(!fc.isAutoScaled());
+ } finally
+ {
+ adjusting = false;
+ }
+ }
+
+ /**
+ * Configures the initial layout
+ */
+ private void initialise()
+ {
+ this.setLayout(new BorderLayout());
+ tabbedPane = new JTabbedPane();
+ this.add(tabbedPane, BorderLayout.CENTER);
+
+ /*
+ * an ActionListener that applies colour changes
+ */
+ changeColourAction = new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ colourChanged(true);
+ }
+ };
+
+ /*
+ * first tab: colour options
+ */
+ JPanel coloursPanel = initialiseColoursPanel();
+ tabbedPane.addTab(MessageManager.getString("action.colour"),
+ coloursPanel);
+
+ /*
+ * second tab: filter options
+ */
+ JPanel filtersPanel = initialiseFiltersPanel();
+ tabbedPane.addTab(MessageManager.getString("label.filters"),
+ filtersPanel);
+
+ JPanel okCancelPanel = initialiseOkCancelPanel();
+
+ this.add(okCancelPanel, BorderLayout.SOUTH);
+ }
+
+ /**
+ * Updates the min-max range if Colour By selected item is Score, or an
+ * attribute, with a min-max range
+ */
+ protected void updateMinMax()
+ {
+ if (!graduatedColour.isSelected())
+ {
+ return;
+ }
+
+ float[] minMax = null;
+ String colourBy = (String) colourByRangeCombo.getSelectedItem();
+ if (MessageManager.getString("label.score").equals(colourBy))
+ {
+ minMax = fr.getMinMax().get(featureType)[0];
+ }
+ else
+ {
+ // colour by attribute range
+ String[] attNames = fromAttributeDisplayName(colourBy);
+ minMax = FeatureAttributes.getInstance().getMinMax(featureType,
+ attNames);
+ }
+
+ if (minMax != null)
+ {
+ min = minMax[0];
+ max = minMax[1];
+ }
+ }
+
+ /**
+ * Lay out fields for graduated colour (by score or attribute value)
+ *
+ * @return
+ */
+ private JPanel initialiseGraduatedColourPanel()
+ {
+ JPanel graduatedColourPanel = new JPanel();
+ graduatedColourPanel.setLayout(
+ new BoxLayout(graduatedColourPanel, BoxLayout.Y_AXIS));
+ JvSwingUtils.createTitledBorder(graduatedColourPanel,
+ MessageManager.getString("label.graduated_colour"), true);
+ graduatedColourPanel.setBackground(Color.white);
+
+ /*
+ * first row: graduated colour radio button, score/attribute drop-down
+ */
+ JPanel graduatedChoicePanel = new JPanel(
+ new FlowLayout(FlowLayout.LEFT));
+ graduatedChoicePanel.setBackground(Color.white);
+ graduatedColour = new JRadioButton(
+ MessageManager.getString("label.by_range_of") + COLON);
+ graduatedColour.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ graduatedColour.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (graduatedColour.isSelected())
+ {
+ colourChanged(true);
+ }
+ }
+ });
+ graduatedChoicePanel.add(graduatedColour);
+
+ List<String[]> attNames = FeatureAttributes.getInstance()
+ .getAttributes(featureType);
+ colourByRangeCombo = populateAttributesDropdown(attNames, true, false);
+ colourByRangeCombo.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ colourChanged(true);
+ }
+ });
+
+ /*
+ * disable graduated colour option if no range found
+ */
+ graduatedColour.setEnabled(colourByRangeCombo.getItemCount() > 0);
+
+ graduatedChoicePanel.add(colourByRangeCombo);
+ graduatedColourPanel.add(graduatedChoicePanel);
+
+ /*
+ * second row - min/max/no colours
+ */
+ JPanel colourRangePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ colourRangePanel.setBackground(Color.white);
+ graduatedColourPanel.add(colourRangePanel);
+
+ minColour.setFont(JvSwingUtils.getLabelFont());
+ minColour.setBorder(BorderFactory.createLineBorder(Color.black));
+ minColour.setPreferredSize(new Dimension(40, 20));
+ minColour.setToolTipText(MessageManager.getString("label.min_colour"));
+ minColour.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ if (minColour.isEnabled())
+ {
+ showColourChooser(minColour, "label.select_colour_minimum_value");
+ }
+ }
+ });
+
+ maxColour.setFont(JvSwingUtils.getLabelFont());
+ maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
+ maxColour.setPreferredSize(new Dimension(40, 20));
+ maxColour.setToolTipText(MessageManager.getString("label.max_colour"));
+ maxColour.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ if (maxColour.isEnabled())
+ {
+ showColourChooser(maxColour, "label.select_colour_maximum_value");
+ }
+ }
+ });
+ maxColour.setBorder(new LineBorder(Color.black));
+
+ /*
+ * default max colour to current colour (if a plain colour),
+ * or to Black if colour by label; make min colour a pale
+ * version of max colour
+ */
+ FeatureColourI fc = fr.getFeatureColours().get(featureType);
+ Color bg = fc.isSimpleColour() ? fc.getColour() : Color.BLACK;
+ maxColour.setBackground(bg);
+ minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
+
+ noValueCombo = new JComboBox<>();
+ noValueCombo.addItem(MessageManager.getString("label.no_colour"));
+ noValueCombo.addItem(MessageManager.getString("label.min_colour"));
+ noValueCombo.addItem(MessageManager.getString("label.max_colour"));
+ noValueCombo.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ colourChanged(true);
+ }
+ });
+
+ JLabel minText = new JLabel(
+ MessageManager.getString("label.min_value") + COLON);
+ minText.setFont(JvSwingUtils.getLabelFont());
+ JLabel maxText = new JLabel(
+ MessageManager.getString("label.max_value") + COLON);
+ maxText.setFont(JvSwingUtils.getLabelFont());
+ JLabel noText = new JLabel(
+ MessageManager.getString("label.no_value") + COLON);
+ noText.setFont(JvSwingUtils.getLabelFont());
+
+ colourRangePanel.add(minText);
+ colourRangePanel.add(minColour);
+ colourRangePanel.add(maxText);
+ colourRangePanel.add(maxColour);
+ colourRangePanel.add(noText);
+ colourRangePanel.add(noValueCombo);
+
+ /*
+ * third row - threshold options and value
+ */
+ JPanel thresholdPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ thresholdPanel.setBackground(Color.white);
+ graduatedColourPanel.add(thresholdPanel);
+
+ threshold.addActionListener(changeColourAction);
+ threshold.setToolTipText(MessageManager
+ .getString("label.threshold_feature_display_by_score"));
+ threshold.addItem(MessageManager
+ .getString("label.threshold_feature_no_threshold")); // index 0
+ threshold.addItem(MessageManager
+ .getString("label.threshold_feature_above_threshold")); // index 1
+ threshold.addItem(MessageManager
+ .getString("label.threshold_feature_below_threshold")); // index 2
+
+ thresholdValue.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ thresholdValue_actionPerformed();
+ }
+ });
+ thresholdValue.addFocusListener(new FocusAdapter()
+ {
+ @Override
+ public void focusLost(FocusEvent e)
+ {
+ thresholdValue_actionPerformed();
+ }
+ });
+ slider.setPaintLabels(false);
+ slider.setPaintTicks(true);
+ slider.setBackground(Color.white);
+ slider.setEnabled(false);
+ slider.setOpaque(false);
+ slider.setPreferredSize(new Dimension(100, 32));
+ slider.setToolTipText(
+ MessageManager.getString("label.adjust_threshold"));
+
+ slider.addChangeListener(new ChangeListener()
+ {
+ @Override
+ public void stateChanged(ChangeEvent evt)
+ {
+ if (!adjusting)
+ {
+ thresholdValue
+ .setText(String.valueOf(slider.getValue() / scaleFactor));
+ sliderValueChanged();
+ }
+ }
+ });
+ slider.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mouseReleased(MouseEvent evt)
+ {
+ /*
+ * only update Overview and/or structure colouring
+ * when threshold slider drag ends (mouse up)
+ */
+ if (ap != null)
+ {
+ ap.paintAlignment(true, true);
+ }
+ }
+ });
+
+ thresholdValue.setEnabled(false);
+ thresholdValue.setColumns(7);
+
+ thresholdPanel.add(threshold);
+ thresholdPanel.add(slider);
+ thresholdPanel.add(thresholdValue);
+
+ thresholdIsMin.setBackground(Color.white);
+ thresholdIsMin
+ .setText(MessageManager.getString("label.threshold_minmax"));
+ thresholdIsMin.setToolTipText(MessageManager
+ .getString("label.toggle_absolute_relative_display_threshold"));
+ thresholdIsMin.addActionListener(changeColourAction);
+ thresholdPanel.add(thresholdIsMin);
+
+ return graduatedColourPanel;
+ }
+
+ /**
+ * Lay out OK and Cancel buttons
+ *
+ * @return
+ */
+ private JPanel initialiseOkCancelPanel()
+ {
+ JPanel okCancelPanel = new JPanel();
+ // okCancelPanel.setBackground(Color.white);
+ okCancelPanel.add(ok);
+ okCancelPanel.add(cancel);
+ return okCancelPanel;
+ }
+
+ /**
+ * Lay out Colour options panel, containing
+ * <ul>
+ * <li>plain colour, with colour picker</li>
+ * <li>colour by text, with choice of Label or other attribute</li>
+ * <li>colour by range, of score or other attribute, when available</li>
+ * </ul>
+ *
+ * @return
+ */
+ private JPanel initialiseColoursPanel()
+ {
+ JPanel colourByPanel = new JPanel();
+ colourByPanel.setLayout(new BoxLayout(colourByPanel, BoxLayout.Y_AXIS));
+
+ /*
+ * simple colour radio button and colour picker
+ */
+ JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ simpleColourPanel.setBackground(Color.white);
+ JvSwingUtils.createTitledBorder(simpleColourPanel,
+ MessageManager.getString("label.simple"), true);
+ colourByPanel.add(simpleColourPanel);
+
+ simpleColour = new JRadioButton(
+ MessageManager.getString("label.simple_colour"));
+ simpleColour.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ simpleColour.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (simpleColour.isSelected() && !adjusting)
+ {
+ showColourChooser(singleColour, "label.select_colour");
+ }
+ }
+
+ });
+
+ singleColour.setFont(JvSwingUtils.getLabelFont());
+ singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
+ singleColour.setPreferredSize(new Dimension(40, 20));
+ singleColour.addMouseListener(new MouseAdapter()
+ {
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ if (simpleColour.isSelected())
+ {
+ showColourChooser(singleColour, "label.select_colour");
+ }
+ }
+ });
+ simpleColourPanel.add(simpleColour); // radio button
+ simpleColourPanel.add(singleColour); // colour picker button
+
+ /*
+ * colour by text (category) radio button and drop-down choice list
+ */
+ JPanel byTextPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ byTextPanel.setBackground(Color.white);
+ JvSwingUtils.createTitledBorder(byTextPanel,
+ MessageManager.getString("label.colour_by_text"), true);
+ colourByPanel.add(byTextPanel);
+ byCategory = new JRadioButton(
+ MessageManager.getString("label.by_text_of") + COLON);
+ byCategory.setPreferredSize(new Dimension(RADIO_WIDTH, 20));
+ byCategory.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (byCategory.isSelected())
+ {
+ colourChanged(true);
+ }
+ }
+ });
+ byTextPanel.add(byCategory);
+
+ List<String[]> attNames = FeatureAttributes.getInstance()
+ .getAttributes(featureType);
+ colourByTextCombo = populateAttributesDropdown(attNames, false, true);
+ colourByTextCombo.addItemListener(new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ colourChanged(true);
+ }
+ });
+ byTextPanel.add(colourByTextCombo);
+
+ /*
+ * graduated colour panel
+ */
+ JPanel graduatedColourPanel = initialiseGraduatedColourPanel();
+ colourByPanel.add(graduatedColourPanel);
+
+ /*
+ * 3 radio buttons select between simple colour,
+ * by category (text), or graduated
+ */
+ ButtonGroup bg = new ButtonGroup();
+ bg.add(simpleColour);
+ bg.add(byCategory);
+ bg.add(graduatedColour);
+
+ return colourByPanel;
+ }
+
+ private void showColourChooser(JPanel colourPanel, String key)
+ {
+ Color col = JColorChooser.showDialog(this,
+ MessageManager.getString(key), colourPanel.getBackground());
+ if (col != null)
+ {
+ colourPanel.setBackground(col);
+ colourPanel.setForeground(col);
+ }
+ colourPanel.repaint();
+ colourChanged(true);
+ }
+
+ /**
+ * Constructs and sets the selected colour options as the colour for the feature
+ * type, and repaints the alignment, and optionally the Overview and/or
+ * structure viewer if open
+ *
+ * @param updateStructsAndOverview
+ */
+ void colourChanged(boolean updateStructsAndOverview)
+ {
+ if (adjusting)
+ {
+ /*
+ * ignore action handlers while setting values programmatically
+ */
+ return;
+ }
+
+ /*
+ * ensure min-max range is for the latest choice of
+ * 'graduated colour by'
+ */
+ updateMinMax();
+
+ FeatureColourI acg = makeColourFromInputs();
+
+ /*
+ * save the colour, and repaint stuff
+ */
+ fr.setColour(featureType, acg);
+ ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
+
+ updateColoursTab();
+ }
+
+ /**
+ * Converts the input values into an instance of FeatureColour
+ *
+ * @return
+ */
+ private FeatureColourI makeColourFromInputs()
+ {
+ /*
+ * easiest case - a single colour
+ */
+ if (simpleColour.isSelected())
+ {
+ return new FeatureColour(singleColour.getBackground());
+ }
+
+ /*
+ * next easiest case - colour by Label, or attribute text
+ */
+ if (byCategory.isSelected())
+ {
+ Color c = this.getBackground();
+ FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
+ fc.setColourByLabel(true);
+ String byWhat = (String) colourByTextCombo.getSelectedItem();
+ if (!MessageManager.getString("label.label").equals(byWhat))
+ {
+ fc.setAttributeName(fromAttributeDisplayName(byWhat));
+ }
+ return fc;
+ }
+
+ /*
+ * remaining case - graduated colour by score, or attribute value
+ */
+ Color noColour = null;
+ if (noValueCombo.getSelectedIndex() == MIN_COLOUR_OPTION)
+ {
+ noColour = minColour.getBackground();
+ }
+ else if (noValueCombo.getSelectedIndex() == MAX_COLOUR_OPTION)
+ {
+ noColour = maxColour.getBackground();
+ }
+
+ float thresh = 0f;
+ try
+ {
+ thresh = Float.valueOf(thresholdValue.getText());
+ } catch (NumberFormatException e)
+ {
+ // invalid inputs are already handled on entry
+ }
+
+ /*
+ * min-max range is to (or from) threshold value if
+ * 'threshold is min/max' is selected
+ */
+ float minValue = min;
+ float maxValue = max;
+ final int thresholdOption = threshold.getSelectedIndex();
+ if (thresholdIsMin.isSelected()
+ && thresholdOption == ABOVE_THRESHOLD_OPTION)
+ {
+ minValue = thresh;
+ }
+ if (thresholdIsMin.isSelected()
+ && thresholdOption == BELOW_THRESHOLD_OPTION)
+ {
+ maxValue = thresh;
+ }
+
+ /*
+ * make the graduated colour
+ */
+ FeatureColourI fc = new FeatureColour(minColour.getBackground(),
+ maxColour.getBackground(), noColour, minValue, maxValue);
+
+ /*
+ * set attribute to colour by if selected
+ */
+ String byWhat = (String) colourByRangeCombo.getSelectedItem();
+ if (!MessageManager.getString("label.score").equals(byWhat))
+ {
+ fc.setAttributeName(fromAttributeDisplayName(byWhat));
+ }
+
+ /*
+ * set threshold options and 'autoscaled' which is
+ * false if 'threshold is min/max' is selected
+ * else true (colour range is on actual range of values)
+ */
+ fc.setThreshold(thresh);
+ fc.setAutoScaled(!thresholdIsMin.isSelected());
+ fc.setAboveThreshold(thresholdOption == ABOVE_THRESHOLD_OPTION);
+ fc.setBelowThreshold(thresholdOption == BELOW_THRESHOLD_OPTION);
+
+ if (threshline == null)
+ {
+ /*
+ * todo not yet implemented: visual indication of feature threshold
+ */
+ threshline = new GraphLine((max - min) / 2f, "Threshold",
+ Color.black);
+ }
+
+ return fc;
+ }
+
+ /**
+ * A helper method that converts a 'compound' attribute name from its display
+ * form, e.g. CSQ:PolyPhen to array form, e.g. { "CSQ", "PolyPhen" }
+ *
+ * @param attribute
+ * @return
+ */
+ private String[] fromAttributeDisplayName(String attribute)
+ {
+ return attribute == null ? null : attribute.split(COLON);
+ }
+
+ /**
+ * A helper method that converts a 'compound' attribute name to its display
+ * form, e.g. CSQ:PolyPhen from its array form, e.g. { "CSQ", "PolyPhen" }
+ *
+ * @param attName
+ * @return
+ */
+ private String toAttributeDisplayName(String[] attName)
+ {
+ return attName == null ? "" : String.join(COLON, attName);
+ }
+
+ @Override
+ protected void raiseClosed()
+ {
+ if (this.featureSettings != null)
+ {
+ featureSettings.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
+ }
+ }
+
+ /**
+ * Action on OK is just to dismiss the dialog - any changes have already been
+ * applied
+ */
+ @Override
+ public void okPressed()
+ {
+ }
+
+ /**
+ * Action on Cancel is to restore colour scheme and filters as they were when
+ * the dialog was opened
+ */
+ @Override
+ public void cancelPressed()
+ {
+ fr.setColour(featureType, originalColour);
+ fr.setFeatureFilter(featureType, originalFilter);
+ ap.paintAlignment(true, true);
+ }
+
+ /**
+ * Action on text entry of a threshold value
+ */
+ protected void thresholdValue_actionPerformed()
+ {
+ try
+ {
+ adjusting = true;
+ float f = Float.parseFloat(thresholdValue.getText());
+ slider.setValue((int) (f * scaleFactor));
+ threshline.value = f;
+ thresholdValue.setBackground(Color.white); // ok
+
+ /*
+ * force repaint of any Overview window or structure
+ */
+ ap.paintAlignment(true, true);
+ } catch (NumberFormatException ex)
+ {
+ thresholdValue.setBackground(Color.red); // not ok
+ } finally
+ {
+ adjusting = false;
+ }
+ }
+
+ /**
+ * Action on change of threshold slider value. This may be done interactively
+ * (by moving the slider), or programmatically (to update the slider after
+ * manual input of a threshold value).
+ */
+ protected void sliderValueChanged()
+ {
+ threshline.value = getRoundedSliderValue();
+
+ /*
+ * repaint alignment, but not Overview or structure,
+ * to avoid overload while dragging the slider
+ */
+ colourChanged(false);
+ }
+
+ /**
+ * Converts the slider value to its absolute value by dividing by the
+ * scaleFactor. Rounding errors are squashed by forcing min/max of slider range
+ * to the actual min/max of feature score range
+ *
+ * @return
+ */
+ private float getRoundedSliderValue()
+ {
+ int value = slider.getValue();
+ float f = value == slider.getMaximum() ? max
+ : (value == slider.getMinimum() ? min : value / scaleFactor);
+ return f;
+ }
+
+ void addActionListener(ActionListener listener)
+ {
+ if (featureSettings != null)
+ {
+ System.err.println(
+ "IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
+ }
+ featureSettings = listener;
+ }
+
+ /**
+ * A helper method to build the drop-down choice of attributes for a feature. If
+ * 'withRange' is true, then Score, and any attributes with a min-max range, are
+ * added. If 'withText' is true, Label and any known attributes are added. This
+ * allows 'categorical numerical' attributes e.g. codon position to be coloured
+ * by text.
+ * <p>
+ * Where metadata is available with a description for an attribute, that is
+ * added as a tooltip.
+ * <p>
+ * Attribute names may be 'simple' e.g. "AC" or 'compound' e.g. {"CSQ",
+ * "Allele"}. Compound names are rendered for display as (e.g.) CSQ:Allele.
+ * <p>
+ * This method does not add any ActionListener to the JComboBox.
+ *
+ * @param attNames
+ * @param withRange
+ * @param withText
+ */
+ protected JComboBox<String> populateAttributesDropdown(
+ List<String[]> attNames, boolean withRange, boolean withText)
+ {
+ List<String> displayAtts = new ArrayList<>();
+ List<String> tooltips = new ArrayList<>();
+
+ if (withText)
+ {
+ displayAtts.add(MessageManager.getString("label.label"));
+ tooltips.add(MessageManager.getString("label.description"));
+ }
+ if (withRange)
+ {
+ float[][] minMax = fr.getMinMax().get(featureType);
+ if (minMax != null && minMax[0][0] != minMax[0][1])
+ {
+ displayAtts.add(MessageManager.getString("label.score"));
+ tooltips.add(MessageManager.getString("label.score"));
+ }
+ }
+
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ for (String[] attName : attNames)
+ {
+ float[] minMax = fa.getMinMax(featureType, attName);
+ boolean hasRange = minMax != null && minMax[0] != minMax[1];
+ if (!withText && !hasRange)
+ {
+ continue;
+ }
+ displayAtts.add(toAttributeDisplayName(attName));
+ String desc = fa.getDescription(featureType, attName);
+ if (desc != null && desc.length() > MAX_TOOLTIP_LENGTH)
+ {
+ desc = desc.substring(0, MAX_TOOLTIP_LENGTH) + "...";
+ }
+ tooltips.add(desc == null ? "" : desc);
+ }
+
+ JComboBox<String> attCombo = JvSwingUtils
+ .buildComboWithTooltips(displayAtts, tooltips);
+
+ return attCombo;
+ }
+
+ /**
+ * Populates initial layout of the feature attribute filters panel
+ */
+ private JPanel initialiseFiltersPanel()
+ {
+ filters = new ArrayList<>();
+
+ JPanel filtersPanel = new JPanel();
+ filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
+ filtersPanel.setBackground(Color.white);
+ JvSwingUtils.createTitledBorder(filtersPanel,
+ MessageManager.getString("label.filters"), true);
+
+ JPanel andOrPanel = initialiseAndOrPanel();
+ filtersPanel.add(andOrPanel);
+
+ /*
+ * panel with filters - populated by refreshFiltersDisplay
+ */
+ chooseFiltersPanel = new JPanel();
+ LayoutManager box = new BoxLayout(chooseFiltersPanel, BoxLayout.Y_AXIS);
+ chooseFiltersPanel.setLayout(box);
+ filtersPanel.add(chooseFiltersPanel);
+
+ return filtersPanel;
+ }
+
+ /**
+ * Lays out the panel with radio buttons to AND or OR filter conditions
+ *
+ * @return
+ */
+ private JPanel initialiseAndOrPanel()
+ {
+ JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ andOrPanel.setBackground(Color.white);
+ andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
+ andFilters = new JRadioButton(MessageManager.getString("label.and"));
+ orFilters = new JRadioButton(MessageManager.getString("label.or"));
+ ActionListener actionListener = new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ filtersChanged();
+ }
+ };
+ andFilters.addActionListener(actionListener);
+ orFilters.addActionListener(actionListener);
+ ButtonGroup andOr = new ButtonGroup();
+ andOr.add(andFilters);
+ andOr.add(orFilters);
+ andFilters.setSelected(true);
+ andOrPanel.add(
+ new JLabel(MessageManager.getString("label.join_conditions")));
+ andOrPanel.add(andFilters);
+ andOrPanel.add(orFilters);
+ return andOrPanel;
+ }
+
+ /**
+ * Refreshes the display to show any filters currently configured for the
+ * selected feature type (editable, with 'remove' option), plus one extra row
+ * for adding a condition. This should be called after a filter has been
+ * removed, added or amended.
+ */
+ private void updateFiltersTab()
+ {
+ /*
+ * clear the panel and list of filter conditions
+ */
+ chooseFiltersPanel.removeAll();
+ filters.clear();
+
+ /*
+ * look up attributes known for feature type
+ */
+ List<String[]> attNames = FeatureAttributes.getInstance()
+ .getAttributes(featureType);
+
+ /*
+ * if this feature type has filters set, load them first
+ */
+ KeyedMatcherSetI featureFilters = fr.getFeatureFilter(featureType);
+ if (featureFilters != null)
+ {
+ if (!featureFilters.isAnded())
+ {
+ orFilters.setSelected(true);
+ }
+ featureFilters.getMatchers().forEach(matcher -> filters.add(matcher));
+ }
+
+ /*
+ * and an empty filter for the user to populate (add)
+ */
+ KeyedMatcherI noFilter = new KeyedMatcher(Condition.values()[0], "",
+ (String) null);
+ filters.add(noFilter);
+
+ /*
+ * render the conditions in rows, each in its own JPanel
+ */
+ int filterIndex = 0;
+ for (KeyedMatcherI filter : filters)
+ {
+ String[] attName = filter.getKey();
+ Condition condition = filter.getMatcher().getCondition();
+ String pattern = filter.getMatcher().getPattern();
+ JPanel row = addFilter(attName, attNames, condition, pattern,
+ filterIndex);
+ row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
+ chooseFiltersPanel.add(row);
+ filterIndex++;
+ }
+
+ this.validate();
+ this.repaint();
+ }
+
+ /**
+ * A helper method that constructs a row (panel) with one filter condition:
+ * <ul>
+ * <li>a drop-down list of attribute names to choose from</li>
+ * <li>a drop-down list of conditions to choose from</li>
+ * <li>a text field for input of a match pattern</li>
+ * <li>optionally, a 'remove' button</li>
+ * </ul>
+ * If attribute, condition or pattern are not null, they are set as defaults for
+ * the input fields. The 'remove' button is added unless the pattern is null or
+ * empty (incomplete filter condition).
+ *
+ * @param attName
+ * @param attNames
+ * @param cond
+ * @param pattern
+ * @param filterIndex
+ * @return
+ */
+ protected JPanel addFilter(String[] attName, List<String[]> attNames,
+ Condition cond, String pattern, int filterIndex)
+ {
+ JPanel filterRow = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ filterRow.setBackground(Color.white);
+
+ /*
+ * drop-down choice of attribute, with description as a tooltip
+ * if we can obtain it
+ */
+ final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
+ true, true);
+ JComboBox<Condition> condCombo = new JComboBox<>();
+ JTextField patternField = new JTextField(8);
+
+ /*
+ * action handlers that validate and (if valid) apply changes
+ */
+ ActionListener actionListener = new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ if (attCombo.getSelectedItem() != null)
+ {
+ if (validateFilter(patternField, condCombo))
+ {
+ updateFilter(attCombo, condCombo, patternField, filterIndex);
+ filtersChanged();
+ }
+ }
+ }
+ };
+ ItemListener itemListener = new ItemListener()
+ {
+ @Override
+ public void itemStateChanged(ItemEvent e)
+ {
+ actionListener.actionPerformed(null);
+ }
+ };
+
+ if (attName == null) // the 'add a condition' row
+ {
+ attCombo.setSelectedIndex(0);
+ }
+ else
+ {
+ attCombo.setSelectedItem(toAttributeDisplayName(attName));
+ }
+ attCombo.addItemListener(itemListener);
+
+ filterRow.add(attCombo);
+
+ /*
+ * drop-down choice of test condition
+ */
+ populateConditions((String) attCombo.getSelectedItem(), cond,
+ condCombo);
+ condCombo.addItemListener(itemListener);
+ filterRow.add(condCombo);
+
+ /*
+ * pattern to match against
+ */
+ patternField.setText(pattern);
+ patternField.addActionListener(actionListener);
+ patternField.addFocusListener(new FocusAdapter()
+ {
+ @Override
+ public void focusLost(FocusEvent e)
+ {
+ actionListener.actionPerformed(null);
+ }
+ });
+ filterRow.add(patternField);
+
+ /*
+ * add remove button if filter is populated (non-empty pattern)
+ */
+ if (pattern != null && pattern.trim().length() > 0)
+ {
+ // todo: gif for button drawing '-' or 'x'
+ JButton removeCondition = new BasicArrowButton(SwingConstants.WEST);
+ removeCondition
+ .setToolTipText(MessageManager.getString("label.delete_row"));
+ removeCondition.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ filters.remove(filterIndex);
+ filtersChanged();
+ }
+ });
+ filterRow.add(removeCondition);
+ }
+
+ return filterRow;
+ }
+
+ /**
+ * Populates the drop-down list of comparison conditions for the given attribute
+ * name. The conditions added depend on the datatype of the attribute values.
+ * The supplied condition is set as the selected item in the list, provided it
+ * is in the list.
+ *
+ * @param attName
+ * @param cond
+ * @param condCombo
+ */
+ private void populateConditions(String attName, Condition cond,
+ JComboBox<Condition> condCombo)
+ {
+ Datatype type = FeatureAttributes.getInstance().getDatatype(featureType,
+ attName);
+ if (MessageManager.getString("label.label").equals(attName))
+ {
+ type = Datatype.Character;
+ }
+ else if (MessageManager.getString("label.score").equals(attName))
+ {
+ type = Datatype.Number;
+ }
+
+ for (Condition c : Condition.values())
+ {
+ if ((c.isNumeric() && type != Datatype.Character)
+ || (!c.isNumeric() && type != Datatype.Number))
+ {
+ condCombo.addItem(c);
+ }
+ }
+
+ /*
+ * set the selected condition (does nothing if not in the list)
+ */
+ if (cond != null)
+ {
+ condCombo.setSelectedItem(cond);
+ }
+ }
+
+ /**
+ * Answers true unless a numeric condition has been selected with a non-numeric
+ * value. Sets the value field to RED with a tooltip if in error.
+ * <p>
+ * If the pattern entered is empty, this method returns false, but does not mark
+ * the field as invalid. This supports selecting an attribute for a new
+ * condition before a match pattern has been entered.
+ *
+ * @param value
+ * @param condCombo
+ */
+ protected boolean validateFilter(JTextField value,
+ JComboBox<Condition> condCombo)
+ {
+ if (value == null || condCombo == null)
+ {
+ return true; // fields not populated
+ }
+
+ Condition cond = (Condition) condCombo.getSelectedItem();
+ value.setBackground(Color.white);
+ value.setToolTipText("");
+ String v1 = value.getText().trim();
+ if (v1.length() == 0)
+ {
+ return false;
+ }
+
+ if (cond.isNumeric())
+ {
+ try
+ {
+ Float.valueOf(v1);
+ } catch (NumberFormatException e)
+ {
+ value.setBackground(Color.red);
+ value.setToolTipText(
+ MessageManager.getString("label.numeric_required"));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Constructs a filter condition from the given input fields, and replaces the
+ * condition at filterIndex with the new one
+ *
+ * @param attCombo
+ * @param condCombo
+ * @param valueField
+ * @param filterIndex
+ */
+ protected void updateFilter(JComboBox<String> attCombo,
+ JComboBox<Condition> condCombo, JTextField valueField,
+ int filterIndex)
+ {
+ String attName = (String) attCombo.getSelectedItem();
+ Condition cond = (Condition) condCombo.getSelectedItem();
+ String pattern = valueField.getText();
+ KeyedMatcherI km = new KeyedMatcher(cond, pattern,
+ fromAttributeDisplayName(attName));
+
+ filters.set(filterIndex, km);
+ }
+
+ /**
+ * Makes the dialog visible, at the Feature Colour tab or at the Filters tab
+ *
+ * @param coloursTab
+ */
+ public void showTab(boolean coloursTab)
+ {
+ setVisible(true);
+ tabbedPane.setSelectedIndex(coloursTab ? 0 : 1);
+ }
+
+ /**
+ * Action on any change to feature filtering, namely
+ * <ul>
+ * <li>change of selected attribute</li>
+ * <li>change of selected condition</li>
+ * <li>change of match pattern</li>
+ * <li>removal of a condition</li>
+ * </ul>
+ * The inputs are parsed into a combined filter and this is set for the feature
+ * type, and the alignment redrawn.
+ */
+ protected void filtersChanged()
+ {
+ /*
+ * update the filter conditions for the feature type
+ */
+ boolean anded = andFilters.isSelected();
+ KeyedMatcherSetI combined = new KeyedMatcherSet();
+
+ for (KeyedMatcherI filter : filters)
+ {
+ String pattern = filter.getMatcher().getPattern();
+ if (pattern.trim().length() > 0)
+ {
+ if (anded)
+ {
+ combined.and(filter);
+ }
+ else
+ {
+ combined.or(filter);
+ }
+ }
+ }
+
+ /*
+ * save the filter conditions in the FeatureRenderer
+ * (note this might now be an empty filter with no conditions)
+ */
+ fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
+ ap.paintAlignment(true, true);
+
+ updateFiltersTab();
+ }
+}
{
fastPaint((int) evt.getNewValue() - (int) evt.getOldValue());
}
+ else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ fastPaint(((int[]) evt.getNewValue())[1]
+ - ((int[]) evt.getOldValue())[1]);
+ }
+ else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ repaint();
+ }
}
}
{
try
{
- frame.dispose();
raiseClosed();
+ frame.dispose();
} catch (Exception ex)
{
}
/**
* Adds a titled border to the component in the default font and position (top
- * left)
+ * left), optionally witht italic text
*
* @param comp
* @param title
+ * @param italic
*/
- public static void createItalicTitledBorder(JComponent comp,
+ public static TitledBorder createTitledBorder(JComponent comp,
String title, boolean italic)
{
Font font = comp.getFont();
title, TitledBorder.LEADING, TitledBorder.DEFAULT_POSITION,
font);
comp.setBorder(titledBorder);
+
+ return titledBorder;
}
}
// 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))
+ if (evt.getPropertyName().equals(ViewportRanges.STARTRES)
+ || evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ)
+ || evt.getPropertyName().equals(ViewportRanges.MOVE_VIEWPORT))
{
// scroll event, repaint panel
repaint();
int transX = 0;
int transY = 0;
- gg.copyArea(horizontal * charWidth, vertical * charHeight,
- img.getWidth(), img.getHeight(), -horizontal * charWidth,
- -vertical * charHeight);
+ gg.copyArea(horizontal * charWidth, vertical * charHeight,
+ img.getWidth(), img.getHeight(), -horizontal * charWidth,
+ -vertical * charHeight);
- if (horizontal > 0) // scrollbar pulled right, image to the left
- {
- transX = (endRes - startRes - horizontal) * charWidth;
- startRes = endRes - horizontal;
- }
- else if (horizontal < 0)
- {
- endRes = startRes - horizontal;
- }
- else if (vertical > 0) // scroll down
- {
- startSeq = endSeq - vertical;
-
- if (startSeq < ranges.getStartSeq())
- { // ie scrolling too fast, more than a page at a time
- startSeq = ranges.getStartSeq();
+ if (horizontal > 0) // scrollbar pulled right, image to the left
+ {
+ transX = (endRes - startRes - horizontal) * charWidth;
+ startRes = endRes - horizontal;
}
- else
+ else if (horizontal < 0)
{
- transY = img.getHeight() - ((vertical + 1) * charHeight);
+ endRes = startRes - horizontal;
}
- }
- else if (vertical < 0)
- {
- endSeq = startSeq - vertical;
- if (endSeq > ranges.getEndSeq())
+ if (vertical > 0) // scroll down
+ {
+ startSeq = endSeq - vertical;
+
+ if (startSeq < ranges.getStartSeq())
+ { // ie scrolling too fast, more than a page at a time
+ startSeq = ranges.getStartSeq();
+ }
+ else
+ {
+ transY = img.getHeight() - ((vertical + 1) * charHeight);
+ }
+ }
+ else if (vertical < 0)
{
- endSeq = ranges.getEndSeq();
+ endSeq = startSeq - vertical;
+
+ if (endSeq > ranges.getEndSeq())
+ {
+ endSeq = ranges.getEndSeq();
+ }
}
- }
- gg.translate(transX, transY);
- drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
- gg.translate(-transX, -transY);
+ gg.translate(transX, transY);
+ drawPanel(gg, startRes, endRes, startSeq, endSeq, 0);
+ gg.translate(-transX, -transY);
- repaint();
+ repaint();
} finally
{
fastpainting = false;
// lcimg is a local *copy* of img which we'll draw selectImage on top of
BufferedImage lcimg = buildLocalImage(selectImage);
g.drawImage(lcimg, 0, 0, this);
+
+ }
+
+ if (av.cursorMode)
+ {
+ drawCursor(g, ranges.getStartRes(), ranges.getEndRes(),
+ ranges.getStartSeq(), ranges.getEndSeq());
}
}
AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
g2d.drawImage(selectImage, 0, 0, this);
}
+
g2d.dispose();
return lcimg;
* white fill the region to be drawn (so incremental fast paint doesn't
* scribble over an existing image)
*/
- gg.setColor(Color.white);
- gg.fillRect(0, ypos, (endx - startColumn + 1) * charWidth,
+ g.setColor(Color.white);
+ g.fillRect(0, ypos, (endx - startColumn + 1) * charWidth,
wrappedRepeatHeightPx);
drawPanel(g, startColumn, endx, 0, av.getAlignment().getHeight() - 1,
int canvasWidth,
int canvasHeight, int startRes)
{
- int charHeight = av.getCharHeight();
- int charWidth = av.getCharWidth();
-
+ int charHeight = av.getCharHeight();
+ int charWidth = av.getCharWidth();
+
// height gap above each panel
int hgap = charHeight;
if (av.getScaleAboveWrapped())
}
}
}
-
- if (av.cursorMode && cursorY == i && cursorX >= startRes
- && cursorX <= endRes)
- {
- seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
- offset + ((i - startSeq) * charHeight));
- }
}
if (av.getSelectionGroup() != null
return selectionImage;
}
+ /**
+ * Draw the cursor as a separate image and overlay
+ *
+ * @param startRes
+ * start residue of area to draw cursor in
+ * @param endRes
+ * end residue of area to draw cursor in
+ * @param startSeq
+ * start sequence of area to draw cursor in
+ * @param endSeq
+ * end sequence of are to draw cursor in
+ * @return a transparent image of the same size as the sequence canvas, with
+ * the cursor drawn on it, if any
+ */
+ private void drawCursor(Graphics g, int startRes, int endRes,
+ int startSeq,
+ int endSeq)
+ {
+ // convert the cursorY into a position on the visible alignment
+ int cursor_ypos = cursorY;
+
+ // don't do work unless we have to
+ if (cursor_ypos >= startSeq && cursor_ypos <= endSeq)
+ {
+ int yoffset = 0;
+ int xoffset = 0;
+ int startx = startRes;
+ int endx = endRes;
+
+ // convert the cursorX into a position on the visible alignment
+ int cursor_xpos = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(cursorX);
+
+ if (av.getAlignment().getHiddenColumns().isVisible(cursorX))
+ {
+
+ if (av.getWrapAlignment())
+ {
+ // work out the correct offsets for the cursor
+ int charHeight = av.getCharHeight();
+ int charWidth = av.getCharWidth();
+ int canvasWidth = getWidth();
+ int canvasHeight = getHeight();
+
+ // height gap above each panel
+ int hgap = charHeight;
+ if (av.getScaleAboveWrapped())
+ {
+ hgap += charHeight;
+ }
+
+ int cWidth = (canvasWidth - labelWidthEast - labelWidthWest)
+ / charWidth;
+ int cHeight = av.getAlignment().getHeight() * charHeight;
+
+ endx = startx + cWidth - 1;
+ int ypos = hgap; // vertical offset
+
+ // iterate down the wrapped panels
+ while ((ypos <= canvasHeight) && (endx < cursor_xpos))
+ {
+ // update vertical offset
+ ypos += cHeight + getAnnotationHeight() + hgap;
+
+ // update horizontal offset
+ startx += cWidth;
+ endx = startx + cWidth - 1;
+ }
+ yoffset = ypos;
+ xoffset = labelWidthWest;
+ }
+
+ // now check if cursor is within range for x values
+ if (cursor_xpos >= startx && cursor_xpos <= endx)
+ {
+ // get the character the cursor is drawn at
+ SequenceI seq = av.getAlignment().getSequenceAt(cursorY);
+ char s = seq.getCharAt(cursorX);
+
+ seqRdr.drawCursor(g, s,
+ xoffset + (cursor_xpos - startx) * av.getCharWidth(),
+ yoffset + (cursor_ypos - startSeq) * av.getCharHeight());
+ }
+ }
+ }
+ }
+
+
/*
* Set up graphics for selection group
*/
private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group,
int startRes, int endRes, int startSeq, int endSeq, int offset)
{
- int charWidth = av.getCharWidth();
-
+ int charWidth = av.getCharWidth();
+
if (!av.hasHiddenColumns())
{
drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq,
int startRes, int endRes, int startSeq, int endSeq,
int verticalOffset)
{
- int charHeight = av.getCharHeight();
- int charWidth = av.getCharWidth();
-
+ int charHeight = av.getCharHeight();
+ int charWidth = av.getCharWidth();
int visWidth = (endRes - startRes + 1) * charWidth;
int oldY = -1;
boolean inGroup = false;
int top = -1;
int bottom = -1;
-
- int sx = -1;
int sy = -1;
- int xwidth = -1;
- for (i = startSeq; i <= endSeq; i++)
- {
- // position of start residue of group relative to startRes, in pixels
- sx = (group.getStartRes() - startRes) * charWidth;
+ List<SequenceI> seqs = group.getSequences(null);
- // width of group in pixels
- xwidth = (((group.getEndRes() + 1) - group.getStartRes()) * charWidth)
- - 1;
+ // position of start residue of group relative to startRes, in pixels
+ int sx = (group.getStartRes() - startRes) * charWidth;
- sy = verticalOffset + (i - startSeq) * charHeight;
+ // width of group in pixels
+ int xwidth = (((group.getEndRes() + 1) - group.getStartRes())
+ * charWidth) - 1;
- if (sx + xwidth < 0 || sx > visWidth)
- {
- continue;
- }
-
- if ((sx <= (endRes - startRes) * charWidth)
- && group.getSequences(null)
- .contains(av.getAlignment().getSequenceAt(i)))
+ if (!(sx + xwidth < 0 || sx > visWidth))
+ {
+ for (i = startSeq; i <= endSeq; i++)
{
- if ((bottom == -1) && !group.getSequences(null)
- .contains(av.getAlignment().getSequenceAt(i + 1)))
- {
- bottom = sy + charHeight;
- }
-
- if (!inGroup)
- {
- if (((top == -1) && (i == 0)) || !group.getSequences(null)
- .contains(av.getAlignment().getSequenceAt(i - 1)))
- {
- top = sy;
- }
+ sy = verticalOffset + (i - startSeq) * charHeight;
- oldY = sy;
- inGroup = true;
- }
- }
- else
- {
- if (inGroup)
+ if ((sx <= (endRes - startRes) * charWidth)
+ && seqs.contains(av.getAlignment().getSequenceAt(i)))
{
- // if start position is visible, draw vertical line to left of
- // group
- if (sx >= 0 && sx < visWidth)
+ if ((bottom == -1)
+ && !seqs.contains(av.getAlignment().getSequenceAt(i + 1)))
{
- g.drawLine(sx, oldY, sx, sy);
+ bottom = sy + charHeight;
}
- // if end position is visible, draw vertical line to right of
- // group
- if (sx + xwidth < visWidth)
- {
- g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
- }
-
- if (sx < 0)
- {
- xwidth += sx;
- sx = 0;
- }
-
- // don't let width extend beyond current block, or group extent
- // fixes JAL-2672
- if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
- {
- xwidth = (endRes - startRes + 1) * charWidth - sx;
- }
-
- // draw horizontal line at top of group
- if (top != -1)
+ if (!inGroup)
{
- g.drawLine(sx, top, sx + xwidth, top);
- top = -1;
- }
+ if (((top == -1) && (i == 0)) || !seqs
+ .contains(av.getAlignment().getSequenceAt(i - 1)))
+ {
+ top = sy;
+ }
- // draw horizontal line at bottom of group
- if (bottom != -1)
- {
- g.drawLine(sx, bottom, sx + xwidth, bottom);
- bottom = -1;
+ oldY = sy;
+ inGroup = true;
}
+ }
+ else if (inGroup)
+ {
+ drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
+ drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
+ // reset top and bottom
+ top = -1;
+ bottom = -1;
inGroup = false;
}
}
- }
-
- if (inGroup)
- {
- sy = verticalOffset + ((i - startSeq) * charHeight);
- if (sx >= 0 && sx < visWidth)
+ if (inGroup)
{
- g.drawLine(sx, oldY, sx, sy);
+ sy = verticalOffset + ((i - startSeq) * charHeight);
+ drawVerticals(g, sx, xwidth, visWidth, oldY, sy);
+ drawHorizontals(g, sx, xwidth, visWidth, top, bottom);
}
+ }
+ }
- if (sx + xwidth < visWidth)
- {
- g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
- }
+ /**
+ * Draw horizontal selection group boundaries at top and bottom positions
+ *
+ * @param g
+ * graphics object to draw on
+ * @param sx
+ * start x position
+ * @param xwidth
+ * width of gap
+ * @param visWidth
+ * visWidth maximum available width
+ * @param top
+ * position to draw top of group at
+ * @param bottom
+ * position to draw bottom of group at
+ */
+ private void drawHorizontals(Graphics2D g, int sx, int xwidth,
+ int visWidth, int top, int bottom)
+ {
+ int width = xwidth;
+ int startx = sx;
+ if (startx < 0)
+ {
+ width += startx;
+ startx = 0;
+ }
- if (sx < 0)
- {
- xwidth += sx;
- sx = 0;
- }
+ // don't let width extend beyond current block, or group extent
+ // fixes JAL-2672
+ if (startx + width >= visWidth)
+ {
+ width = visWidth - startx;
+ }
- if (sx + xwidth > visWidth)
- {
- xwidth = visWidth;
- }
- else if (sx + xwidth >= (endRes - startRes + 1) * charWidth)
- {
- xwidth = (endRes - startRes + 1) * charWidth;
- }
+ if (top != -1)
+ {
+ g.drawLine(startx, top, startx + width, top);
+ }
- if (top != -1)
- {
- g.drawLine(sx, top, sx + xwidth, top);
- top = -1;
- }
+ if (bottom != -1)
+ {
+ g.drawLine(startx, bottom - 1, startx + width, bottom - 1);
+ }
+ }
- if (bottom != -1)
- {
- g.drawLine(sx, bottom - 1, sx + xwidth, bottom - 1);
- bottom = -1;
- }
+ /**
+ * Draw vertical lines at sx and sx+xwidth providing they lie within
+ * [0,visWidth)
+ *
+ * @param g
+ * graphics object to draw on
+ * @param sx
+ * start x position
+ * @param xwidth
+ * width of gap
+ * @param visWidth
+ * visWidth maximum available width
+ * @param oldY
+ * top y value
+ * @param sy
+ * bottom y value
+ */
+ private void drawVerticals(Graphics2D g, int sx, int xwidth, int visWidth,
+ int oldY, int sy)
+ {
+ // if start position is visible, draw vertical line to left of
+ // group
+ if (sx >= 0 && sx < visWidth)
+ {
+ g.drawLine(sx, oldY, sx, sy);
+ }
- inGroup = false;
+ // if end position is visible, draw vertical line to right of
+ // group
+ if (sx + xwidth < visWidth)
+ {
+ g.drawLine(sx + xwidth, oldY, sx + xwidth, sy);
}
}
*/
protected boolean drawMappedPositions(SearchResultsI results)
{
- if (results == null)
+ if ((results == null) || (gg == null)) // JAL-2784 check gg is not null
{
return false;
}
repaint();
return;
}
+ else if (eventName.equals(ViewportRanges.MOVE_VIEWPORT))
+ {
+ fastPaint = false;
+ repaint();
+ return;
+ }
int scrollX = 0;
- if (eventName.equals(ViewportRanges.STARTRES))
+ if (eventName.equals(ViewportRanges.STARTRES)
+ || eventName.equals(ViewportRanges.STARTRESANDSEQ))
{
// Make sure we're not trying to draw a panel
// larger than the visible window
+ if (eventName.equals(ViewportRanges.STARTRES))
+ {
+ scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
+ }
+ else
+ {
+ scrollX = ((int[]) evt.getNewValue())[0]
+ - ((int[]) evt.getOldValue())[0];
+ }
ViewportRanges vpRanges = av.getRanges();
- scrollX = (int) evt.getNewValue() - (int) evt.getOldValue();
- int range = vpRanges.getViewportWidth();
+
+ int range = vpRanges.getEndRes() - vpRanges.getStartRes();
if (scrollX > range)
{
scrollX = range;
{
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.
- // scroll - startres and endres both change
- if (eventName.equals(ViewportRanges.STARTRES))
- {
- if (av.getWrapAlignment())
+ // 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))
{
- fastPaintWrapped(scrollX);
+ if (av.getWrapAlignment())
+ {
+ fastPaintWrapped(scrollX);
+ }
+ else
+ {
+ fastPaint(scrollX, 0);
+ }
}
- else
+ else if (eventName.equals(ViewportRanges.STARTSEQ))
{
- fastPaint(scrollX, 0);
+ // scroll
+ fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+ }
+ else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ if (av.getWrapAlignment())
+ {
+ fastPaintWrapped(scrollX);
+ }
+ else
+ {
+ fastPaint(scrollX, 0);
+ }
+ // bizarrely, we only need to scroll on the x value here as fastpaint
+ // copies the full height of the image anyway. Passing in the y value
+ // causes nasty repaint artefacts, which only disappear on a full
+ // repaint.
}
- }
- else if (eventName.equals(ViewportRanges.STARTSEQ))
- {
- // scroll
- fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
}
}
{
ViewportRanges ranges = av.getRanges();
- if (Math.abs(scrollX) > ranges.getViewportWidth())
+ // if (Math.abs(scrollX) > ranges.getViewportWidth())
+ // JAL-2836, 2836 temporarily removed wrapped fastpaint for release 2.10.3
+ if (true)
{
/*
* shift of more than one view width is
*/
protected boolean drawMappedPositionsWrapped(SearchResultsI results)
{
- if (results == null)
+ if ((results == null) || (gg == null)) // JAL-2784 check gg is not null
{
return false;
}
void setCursorRow()
{
seqCanvas.cursorY = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void setCursorRowAndColumn()
{
seqCanvas.cursorX = getKeyboardNo1() - 1;
seqCanvas.cursorY = getKeyboardNo2() - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
}
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
- scrollToVisible();
+ scrollToVisible(true);
}
void moveCursor(int dx, int dy)
}
}
- scrollToVisible();
+ scrollToVisible(false);
}
- void scrollToVisible()
+ /**
+ * Scroll to make the cursor visible in the viewport.
+ *
+ * @param jump
+ * just jump to the location rather than scrolling
+ */
+ void scrollToVisible(boolean jump)
{
if (seqCanvas.cursorX < 0)
{
}
endEditing();
- if (av.getWrapAlignment())
+
+ boolean repaintNeeded = true;
+ if (jump)
{
- av.getRanges().scrollToWrappedVisible(seqCanvas.cursorX);
+ // only need to repaint if the viewport did not move, as otherwise it will
+ // get a repaint
+ repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
+ seqCanvas.cursorY);
}
else
{
- av.getRanges().scrollToVisible(seqCanvas.cursorX, seqCanvas.cursorY);
+ if (av.getWrapAlignment())
+ {
+ // scrollToWrappedVisible expects x-value to have hidden cols subtracted
+ int x = av.getAlignment().getHiddenColumns()
+ .findColumnPosition(seqCanvas.cursorX);
+ av.getRanges().scrollToWrappedVisible(x);
+ }
+ else
+ {
+ av.getRanges().scrollToVisible(seqCanvas.cursorX,
+ seqCanvas.cursorY);
+ }
}
- setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
+
+ if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
+ {
+ setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
seqCanvas.cursorX, seqCanvas.cursorY);
+ }
- seqCanvas.repaint();
+ if (repaintNeeded)
+ {
+ seqCanvas.repaint();
+ }
}
+
void setSelectionAreaAtCursor(boolean topLeft)
{
SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
}
}
- public void drawCursor(SequenceI seq, int res, int x1, int y1)
+ /**
+ * Draw a sequence canvas cursor
+ *
+ * @param g
+ * graphics context to draw on
+ * @param s
+ * character to draw at cursor
+ * @param x1
+ * x position of cursor in graphics context
+ * @param y1
+ * y position of cursor in graphics context
+ */
+ public void drawCursor(Graphics g, char s, int x1, int y1)
{
int pady = av.getCharHeight() / 5;
int charOffset = 0;
- graphics.setColor(Color.black);
- graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
+ g.setColor(Color.black);
+ g.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight());
if (av.isValidCharWidth())
{
- graphics.setColor(Color.white);
-
- char s = seq.getCharAt(res);
-
+ g.setColor(Color.white);
charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2;
- graphics.drawString(String.valueOf(s), charOffset + x1,
+ g.drawString(String.valueOf(s), charOffset + x1,
(y1 + av.getCharHeight()) - pady);
}
if (cachedPDBExists)
{
- FilterOption cachedOption = new FilterOption("Cached PDB Entries",
+ FilterOption cachedOption = new FilterOption("Cached Structures",
"-", VIEWS_LOCAL_PDB, false);
cmb_filterOption.addItem(cachedOption);
cmb_filterOption.setSelectedItem(cachedOption);
*/
public class FeatureColour implements FeatureColourI
{
- static final Color DEFAULT_NO_COLOUR = Color.LIGHT_GRAY;
+ static final Color DEFAULT_NO_COLOUR = null;
private static final String BAR = "|";
private boolean aboveThreshold;
- private boolean thresholdIsMinOrMax;
-
private boolean isHighToLow;
private boolean autoScaled;
updateBounds(min, max);
}
+ /**
+ * Constructor for a graduated colour
+ *
+ * @param low
+ * @param high
+ * @param noValueColour
+ * @param min
+ * @param max
+ */
public FeatureColour(Color low, Color high, Color noValueColour,
float min, float max)
{
}
@Override
- public boolean isThresholdMinMax()
- {
- return thresholdIsMinOrMax;
- }
-
- @Override
- public void setThresholdMinMax(boolean b)
- {
- thresholdIsMinOrMax = b;
- }
-
- @Override
public float getThreshold()
{
return threshold;
/*
* graduated colour case, optionally with threshold
* may be based on feature score on an attribute value
- * Float.NaN is assigned minimum visible score colour
- * no such attribute is assigned the 'no value' colour
+ * Float.NaN, or no value, is assigned the 'no value' colour
*/
float scr = feature.getScore();
if (attributeName != null)
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.util;
import java.util.Comparator;
}
}
- // update viewport
- ranges.setStartRes(xAsRes);
- ranges.setStartSeq(yAsSeq);
+ ranges.setStartResAndSeq(xAsRes, yAsSeq);
}
@Override
}
// update viewport
- ranges.setStartRes(visXAsRes);
- ranges.setStartSeq(visYAsSeq);
+ ranges.setStartResAndSeq(visXAsRes, visYAsSeq);
}
/**
import jalview.datamodel.HiddenColumns;
/**
- * Slightly less embryonic class which: Supplies and updates viewport properties
- * relating to position such as: start and end residues and sequences; ideally
- * will serve hidden columns/rows too. Intention also to support calculations
- * for positioning, scrolling etc. such as finding the middle of the viewport,
- * checking for scrolls off screen
+ * Supplies and updates viewport properties relating to position such as: start
+ * and end residues and sequences; ideally will serve hidden columns/rows too.
+ * Intention also to support calculations for positioning, scrolling etc. such
+ * as finding the middle of the viewport, checking for scrolls off screen
*/
public class ViewportRanges extends ViewportProperties
{
public static final String ENDSEQ = "endseq";
+ public static final String STARTRESANDSEQ = "startresandseq";
+
+ public static final String MOVE_VIEWPORT = "move_viewport";
+
private boolean wrappedMode = false;
// start residue of viewport
*/
public void setStartEndRes(int start, int end)
{
+ int[] oldvalues = updateStartEndRes(start, end);
+ int oldstartres = oldvalues[0];
+ int oldendres = oldvalues[1];
+
+ 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);
+ }
+ }
+
+ /**
+ * Update start and end residue values, adjusting for width constraints if
+ * necessary
+ *
+ * @param start
+ * start residue
+ * @param end
+ * end residue
+ * @return array containing old start and end residue values
+ */
+ private int[] updateStartEndRes(int start, int end)
+ {
int oldstartres = this.startRes;
/*
{
endRes = end;
}
-
- 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);
- }
+ return new int[] { oldstartres, oldendres };
}
/**
*/
public void setStartEndSeq(int start, int end)
{
+ int[] oldvalues = updateStartEndSeq(start, end);
+ int oldstartseq = oldvalues[0];
+ int oldendseq = oldvalues[1];
+
+ 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);
+ }
+ }
+
+ /**
+ * Update start and end sequence values, adjusting for height constraints if
+ * necessary
+ *
+ * @param start
+ * start sequence
+ * @param end
+ * end sequence
+ * @return array containing old start and end sequence values
+ */
+ private int[] updateStartEndSeq(int start, int end)
+ {
int oldstartseq = this.startSeq;
int visibleHeight = getVisibleAlignmentHeight();
if (start > visibleHeight - 1)
{
endSeq = end;
}
-
- 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);
- }
+ return new int[] { oldstartseq, oldendseq };
}
/**
}
/**
+ * Set start residue and start sequence together (fires single event). The
+ * event supplies a pair of old values and a pair of new values: [old start
+ * residue, old start sequence] and [new start residue, new start sequence]
+ *
+ * @param res
+ * the start residue
+ * @param seq
+ * the start sequence
+ */
+ public void setStartResAndSeq(int res, int seq)
+ {
+ int width = getViewportWidth();
+ int[] oldresvalues = updateStartEndRes(res, res + width - 1);
+
+ int startseq = seq;
+ int height = getViewportHeight();
+ if (startseq + height - 1 > getVisibleAlignmentHeight() - 1)
+ {
+ startseq = getVisibleAlignmentHeight() - height;
+ }
+ int[] oldseqvalues = updateStartEndSeq(startseq, startseq + height - 1);
+
+ int[] old = new int[] { oldresvalues[0], oldseqvalues[0] };
+ int[] newresseq = new int[] { startRes, startSeq };
+ changeSupport.firePropertyChange(STARTRESANDSEQ, old, newresseq);
+ }
+
+ /**
* Get start residue of viewport
*/
public int getStartRes()
* the startRes changed, else false.
*
* @param res
- * residue position to scroll to
+ * residue position to scroll to NB visible position not absolute
+ * alignment position
* @return
*/
public boolean scrollToWrappedVisible(int res)
{
- int oldStartRes = startRes;
- int width = getViewportWidth();
-
- if (res >= oldStartRes && res < oldStartRes + width)
+ int newStartRes = calcWrappedStartResidue(res);
+ if (newStartRes == startRes)
{
return false;
}
+ setStartRes(newStartRes);
+
+ return true;
+ }
+
+ /**
+ * Calculate wrapped start residue from visible start residue
+ *
+ * @param res
+ * visible start residue
+ * @return left column of panel res will be located in
+ */
+ private int calcWrappedStartResidue(int res)
+ {
+ int oldStartRes = startRes;
+ int width = getViewportWidth();
boolean up = res < oldStartRes;
int widthsToScroll = Math.abs((res - oldStartRes) / width);
{
newStartRes = 0;
}
-
- setStartRes(newStartRes);
-
- return true;
+ return newStartRes;
}
/**
* Scroll so that (x,y) is visible. Fires a property change event.
*
* @param x
- * x position in alignment
+ * x position in alignment (absolute position)
* @param y
- * y position in alignment
+ * y position in alignment (absolute position)
*/
public void scrollToVisible(int x, int y)
{
{
scrollUp(false);
}
-
+
HiddenColumns hidden = al.getHiddenColumns();
while (x < hidden.adjustForHiddenColumns(startRes))
{
}
/**
+ * Set the viewport location so that a position is visible
+ *
+ * @param x
+ * column to be visible: absolute position in alignment
+ * @param y
+ * row to be visible: absolute position in alignment
+ */
+ public boolean setViewportLocation(int x, int y)
+ {
+ boolean changedLocation = false;
+
+ // convert the x,y location to visible coordinates
+ int visX = al.getHiddenColumns().findColumnPosition(x);
+ int visY = al.getHiddenSequences().findIndexWithoutHiddenSeqs(y);
+
+ // if (vis_x,vis_y) is already visible don't do anything
+ if (startRes > visX || visX > endRes
+ || startSeq > visY && visY > endSeq)
+ {
+ int[] old = new int[] { startRes, startSeq };
+ int[] newresseq;
+ if (wrappedMode)
+ {
+ int newstartres = calcWrappedStartResidue(visX);
+ setStartRes(newstartres);
+ newresseq = new int[] { startRes, startSeq };
+ }
+ else
+ {
+ // set the viewport x location to contain vis_x
+ int newstartres = visX;
+ int width = getViewportWidth();
+ if (newstartres + width - 1 > getVisibleAlignmentWidth() - 1)
+ {
+ newstartres = getVisibleAlignmentWidth() - width;
+ }
+ updateStartEndRes(newstartres, newstartres + width - 1);
+
+ // set the viewport y location to contain vis_y
+ int newstartseq = visY;
+ int height = getViewportHeight();
+ if (newstartseq + height - 1 > getVisibleAlignmentHeight() - 1)
+ {
+ newstartseq = getVisibleAlignmentHeight() - height;
+ }
+ updateStartEndSeq(newstartseq, newstartseq + height - 1);
+
+ newresseq = new int[] { startRes, startSeq };
+ }
+ changedLocation = true;
+ changeSupport.firePropertyChange(MOVE_VIEWPORT, old, newresseq);
+ }
+ return changedLocation;
+ }
+
+ /**
* Adjust sequence position for page up. Fires a property change event.
*/
public void pageUp()
public abstract class FeatureRendererModel
implements jalview.api.FeatureRenderer
{
+ /*
+ * column indices of fields in Feature Settings table
+ * todo: transfer valuers as data beans instead of Object[][]
+ */
+ public static final int TYPE_COLUMN = 0;
+
+ public static final int COLOUR_COLUMN = 1;
+
+ public static final int FILTER_COLUMN = 2;
+
+ public static final int SHOW_COLUMN = 3;
/*
* global transparency for feature
List<SequenceFeature> features = sequence.findFeatures(column, column,
visibleTypes);
+ /*
+ * include features unless their feature group is not displayed, or
+ * they are hidden (have no colour) based on a filter or colour threshold
+ */
for (SequenceFeature sf : features)
{
- if (!featureGroupNotShown(sf))
+ if (!featureGroupNotShown(sf) && getColour(sf) != null)
{
result.add(sf);
}
{
for (int i = 0; i < data.length; i++)
{
- String type = data[i][0].toString();
- setColour(type, (FeatureColourI) data[i][1]);
- if (((Boolean) data[i][2]).booleanValue())
+ String type = data[i][TYPE_COLUMN].toString();
+ setColour(type, (FeatureColourI) data[i][COLOUR_COLUMN]);
+ if (((Boolean) data[i][SHOW_COLUMN]).booleanValue())
{
av_featuresdisplayed.setVisible(type);
}
for (SequenceFeature sf : features)
{
- if (!featureGroupNotShown(sf))
+ if (!featureGroupNotShown(sf) && getColour(sf) != null)
{
result.add(sf);
}
protected boolean featureMatchesFilters(SequenceFeature sf)
{
KeyedMatcherSetI filter = featureFilters.get(sf.getType());
- return filter == null ? true : filter.matches(key -> sf
- .getValueAsString(key));
+ // TODO temporary fudge for Score and Label
+ return filter == null ? true
+ : filter.matches(
+ key -> "Label".equals(key[0]) ? sf.getDescription()
+ : ("Score".equals(key[0])
+ ? String.valueOf(sf.getScore())
+ : sf.getValueAsString(key)));
}
}
/**
*
* @param entry
- * UniportEntry
+ * UniprotEntry
* @return The accession id(s) and name(s) delimited by '|'.
*/
public static String getUniprotEntryId(UniprotEntry entry)
{
StringBuilder name = new StringBuilder(32);
- // name.append("UniProt/Swiss-Prot");
- // use 'canonicalised' name for optimal id matching
- name.append(DBRefSource.UNIPROT);
- for (String accessionId : entry.getAccession())
- {
- name.append(BAR_DELIMITER);
- name.append(accessionId);
- }
for (String n : entry.getName())
{
- name.append(BAR_DELIMITER);
+ if (name.length() > 0)
+ {
+ name.append(BAR_DELIMITER);
+ }
name.append(n);
}
return name.toString();
sq.checkValidRange();
assertEquals(22, sq.getEnd());
}
+
+ @Test(groups = { "Functional" })
+ public void testDeleteChars_withGaps()
+ {
+ /*
+ * delete gaps only
+ */
+ SequenceI sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString());
+ sq.deleteChars(1, 2); // delete first gap
+ assertEquals("AB-C", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(10, sq.getEnd());
+ assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString());
+
+ /*
+ * delete gaps and residues at start (no new dataset sequence)
+ */
+ sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ sq.deleteChars(0, 3); // delete A-B
+ assertEquals("-C", sq.getSequenceAsString());
+ assertEquals(10, sq.getStart());
+ assertEquals(10, sq.getEnd());
+ assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString());
+
+ /*
+ * delete gaps and residues at end (no new dataset sequence)
+ */
+ sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ sq.deleteChars(2, 5); // delete B-C
+ assertEquals("A-", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(8, sq.getEnd());
+ assertEquals("ABC", sq.getDatasetSequence().getSequenceAsString());
+
+ /*
+ * delete gaps and residues internally (new dataset sequence)
+ * first delete from gap to residue
+ */
+ sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ sq.deleteChars(1, 3); // delete -B
+ assertEquals("A-C", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(9, sq.getEnd());
+ assertEquals("AC", sq.getDatasetSequence().getSequenceAsString());
+ assertEquals(8, sq.getDatasetSequence().getStart());
+ assertEquals(9, sq.getDatasetSequence().getEnd());
+
+ /*
+ * internal delete from gap to gap
+ */
+ sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ sq.deleteChars(1, 4); // delete -B-
+ assertEquals("AC", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(9, sq.getEnd());
+ assertEquals("AC", sq.getDatasetSequence().getSequenceAsString());
+ assertEquals(8, sq.getDatasetSequence().getStart());
+ assertEquals(9, sq.getDatasetSequence().getEnd());
+
+ /*
+ * internal delete from residue to residue
+ */
+ sq = new Sequence("test/8-10", "A-B-C");
+ sq.createDatasetSequence();
+ sq.deleteChars(2, 3); // delete B
+ assertEquals("A--C", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(9, sq.getEnd());
+ assertEquals("AC", sq.getDatasetSequence().getSequenceAsString());
+ assertEquals(8, sq.getDatasetSequence().getStart());
+ assertEquals(9, sq.getDatasetSequence().getEnd());
+ }
}
package jalview.datamodel.features;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
-import java.util.Comparator;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.features.FeatureAttributes.Datatype;
-import junit.extensions.PA;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class FeatureAttributesTest
{
/**
+ * clear down attributes map after tests
+ */
+ @AfterMethod
+ public void tearDown()
+ {
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ ((Map<?, ?>) PA.getValue(fa, "attributes")).clear();
+ }
+
+ /**
* Test the method that keeps attribute names in non-case-sensitive order,
* including handling of 'compound' names
*/
assertTrue(comp.compare(new String[] { "CSQ", "ac" }, new String[] {
"csq", "AF" }) < 0);
}
+
+ @Test
+ public void testGetMinMax()
+ {
+ SequenceFeature sf = new SequenceFeature("Pfam", "desc", 10, 20,
+ "group");
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ assertNull(fa.getMinMax("Pfam", "kd"));
+ sf.setValue("domain", "xyz");
+ assertNull(fa.getMinMax("Pfam", "kd"));
+ sf.setValue("kd", "some text");
+ assertNull(fa.getMinMax("Pfam", "kd"));
+ sf.setValue("kd", "1.3");
+ assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { 1.3f, 1.3f });
+ sf.setValue("kd", "-2.6");
+ assertEquals(fa.getMinMax("Pfam", "kd"), new float[] { -2.6f, 1.3f });
+ Map<String, String> csq = new HashMap<>();
+ csq.put("AF", "-3");
+ sf.setValue("CSQ", csq);
+ assertEquals(fa.getMinMax("Pfam", "CSQ", "AF"),
+ new float[]
+ { -3f, -3f });
+ csq.put("AF", "4");
+ sf.setValue("CSQ", csq);
+ assertEquals(fa.getMinMax("Pfam", "CSQ", "AF"),
+ new float[]
+ { -3f, 4f });
+ }
+
+ /**
+ * Test the method that returns an attribute description, provided it is
+ * recorded and unique
+ */
+ @Test
+ public void testGetDescription()
+ {
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ // with no description returns null
+ assertNull(fa.getDescription("Pfam", "kd"));
+ // with a unique description, returns that value
+ fa.addDescription("Pfam", "desc1", "kd");
+ assertEquals(fa.getDescription("Pfam", "kd"), "desc1");
+ // with ambiguous description, returns null
+ fa.addDescription("Pfam", "desc2", "kd");
+ assertNull(fa.getDescription("Pfam", "kd"));
+ }
+
+ @Test
+ public void testDatatype()
+ {
+ FeatureAttributes fa = FeatureAttributes.getInstance();
+ assertNull(fa.getDatatype("Pfam", "kd"));
+ SequenceFeature sf = new SequenceFeature("Pfam", "desc", 10, 20,
+ "group");
+ sf.setValue("kd", "-1");
+ sf.setValue("domain", "Metal");
+ sf.setValue("phase", "1");
+ sf.setValue("phase", "reverse");
+ assertEquals(fa.getDatatype("Pfam", "kd"), Datatype.Number);
+ assertEquals(fa.getDatatype("Pfam", "domain"), Datatype.Character);
+ assertEquals(fa.getDatatype("Pfam", "phase"), Datatype.Mixed);
+ }
}
package jalview.io.vcf;
import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
+import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.DBRefEntry;
import jalview.datamodel.Mapping;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Map;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class VCFLoaderTest
// insertion G/GA is transferred to nucleotide but not to peptide
"17\t45051613\t.\tG\tGA,C\t1666.64\tRF\tAC=15;AF=3.0e-03,2.0e-03" };
+ @BeforeClass
+ public void setUp()
+ {
+ /*
+ * configure to capture all available VCF and VEP (CSQ) fields
+ */
+ Cache.loadProperties("test/jalview/io/testProps.jvprops");
+ Cache.setProperty("VCF_FIELDS", ".*");
+ Cache.setProperty("VEP_FIELDS", ".*");
+ }
+
@Test(groups = "Functional")
public void testDoLoad() throws IOException
{
assertEquals(sf.getScore(), 0.1f, DELTA);
assertEquals(sf.getValue("alleles"), "C,A");
// gene features include Consequence for all transcripts
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ Map map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(1);
assertEquals(sf.getBegin(), 5);
assertEquals(sf.getEnd(), 5);
assertEquals(sf.getScore(), 0.2f, DELTA);
assertEquals(sf.getValue("alleles"), "C,T");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(2);
assertEquals(sf.getBegin(), 9);
assertEquals(sf.getEnd(), 11); // deletion over 3 positions
assertEquals(sf.getScore(), 0.3f, DELTA);
assertEquals(sf.getValue("alleles"), "CGG,C");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(3);
assertEquals(sf.getBegin(), 13);
assertEquals(sf.getEnd(), 13);
assertEquals(sf.getScore(), 0.5f, DELTA);
assertEquals(sf.getValue("alleles"), "C,T");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(4);
assertEquals(sf.getBegin(), 13);
assertEquals(sf.getEnd(), 13);
assertEquals(sf.getScore(), 0.4f, DELTA);
assertEquals(sf.getValue("alleles"), "C,G");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(5);
assertEquals(sf.getBegin(), 17);
assertEquals(sf.getEnd(), 17);
assertEquals(sf.getScore(), 0.7f, DELTA);
assertEquals(sf.getValue("alleles"), "A,G");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
sf = geneFeatures.get(6);
assertEquals(sf.getBegin(), 17);
assertEquals(sf.getEnd(), 17); // insertion
assertEquals(sf.getScore(), 0.6f, DELTA);
assertEquals(sf.getValue("alleles"), "A,AC");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 2);
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
/*
* verify variant feature(s) added to transcript3
assertEquals(sf.getScore(), 0.2f, DELTA);
assertEquals(sf.getValue("alleles"), "C,T");
// transcript features only have Consequence for that transcripts
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript3"));
+ map = (Map) sf.getValue("CSQ");
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
sf = transcriptFeatures.get(1);
assertEquals(sf.getBegin(), 11);
assertEquals(sf.getEnd(), 11);
assertEquals(sf.getScore(), 0.7f, DELTA);
assertEquals(sf.getValue("alleles"), "A,G");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript3"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
sf = transcriptFeatures.get(2);
assertEquals(sf.getBegin(), 11);
assertEquals(sf.getEnd(), 11);
assertEquals(sf.getScore(), 0.6f, DELTA);
assertEquals(sf.getValue("alleles"), "A,AC");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript3"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
/*
* verify variants computed on protein product for transcript3
assertEquals(sf.getEnd(), 7);
assertEquals(sf.getScore(), 0.5f, DELTA);
assertEquals(sf.getValue("alleles"), "C,T");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript4"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
sf = transcriptFeatures.get(1);
assertEquals(sf.getBegin(), 7);
assertEquals(sf.getEnd(), 7);
assertEquals(sf.getScore(), 0.4f, DELTA);
assertEquals(sf.getValue("alleles"), "C,G");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript4"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
sf = transcriptFeatures.get(2);
assertEquals(sf.getBegin(), 11);
assertEquals(sf.getEnd(), 11);
assertEquals(sf.getScore(), 0.7f, DELTA);
assertEquals(sf.getValue("alleles"), "A,G");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript4"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
sf = transcriptFeatures.get(3);
assertEquals(sf.getBegin(), 11);
assertEquals(sf.getEnd(), 11);
assertEquals(sf.getScore(), 0.6f, DELTA);
assertEquals(sf.getValue("alleles"), "A,AC");
- assertEquals(((String) sf.getValue("CSQ")).split(",").length, 1);
- assertTrue(sf.getValue("CSQ").toString().contains("transcript4"));
+ assertEquals(map.size(), 9);
+ assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
}
}
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import jalview.api.AlignViewportI;
import jalview.io.DataSourceType;
import jalview.io.FileLoader;
import jalview.schemes.FeatureColour;
+import jalview.util.matcher.Condition;
+import jalview.util.matcher.KeyedMatcher;
+import jalview.util.matcher.KeyedMatcherSet;
+import jalview.util.matcher.KeyedMatcherSetI;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
features = fr.findFeaturesAtColumn(seq, 5);
assertEquals(features.size(), 1);
assertTrue(features.contains(sf8));
+
+ /*
+ * give "Type3" features a graduated colour scheme
+ * - first with no threshold
+ */
+ FeatureColourI gc = new FeatureColour(Color.yellow, Color.red, null, 0f,
+ 10f);
+ fr.getFeatureColours().put("Type3", gc);
+ features = fr.findFeaturesAtColumn(seq, 8);
+ assertTrue(features.contains(sf4));
+ // now with threshold > 2f - feature score of 1f is excluded
+ gc.setAboveThreshold(true);
+ gc.setThreshold(2f);
+ features = fr.findFeaturesAtColumn(seq, 8);
+ assertFalse(features.contains(sf4));
+
+ /*
+ * make "Type3" graduated colour by attribute "AF"
+ * - first with no attribute held - feature should be excluded
+ */
+ gc.setAttributeName("AF");
+ features = fr.findFeaturesAtColumn(seq, 8);
+ assertFalse(features.contains(sf4));
+ // now with the attribute above threshold - should be included
+ sf4.setValue("AF", "2.4");
+ features = fr.findFeaturesAtColumn(seq, 8);
+ assertTrue(features.contains(sf4));
+ // now with the attribute below threshold - should be excluded
+ sf4.setValue("AF", "1.4");
+ features = fr.findFeaturesAtColumn(seq, 8);
+ assertFalse(features.contains(sf4));
}
@Test(groups = "Functional")
assertFalse(features.contains(sf2) && features.contains(sf3));
assertTrue(features.contains(sf5));
}
+
+ @Test(groups = "Functional")
+ public void testGetColour()
+ {
+ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(">s1\nABCD\n",
+ DataSourceType.PASTE);
+ AlignViewportI av = af.getViewport();
+ FeatureRenderer fr = new FeatureRenderer(av);
+
+ /*
+ * simple colour, feature type and group displayed
+ */
+ FeatureColourI fc = new FeatureColour(Color.red);
+ fr.getFeatureColours().put("Cath", fc);
+ SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
+ "group1");
+ assertEquals(fr.getColour(sf1), Color.red);
+
+ /*
+ * hide feature type, then unhide
+ */
+ Object[][] data = new Object[1][];
+ data[0] = new Object[] { "Cath", fc, false };
+ fr.setFeaturePriority(data);
+ assertNull(fr.getColour(sf1));
+ data[0] = new Object[] { "Cath", fc, true };
+ fr.setFeaturePriority(data);
+ assertEquals(fr.getColour(sf1), Color.red);
+
+ /*
+ * hide feature group, then unhide
+ */
+ fr.setGroupVisibility("group1", false);
+ assertNull(fr.getColour(sf1));
+ fr.setGroupVisibility("group1", true);
+ assertEquals(fr.getColour(sf1), Color.red);
+
+ /*
+ * graduated colour by score, no threshold, no score
+ *
+ */
+ FeatureColourI gc = new FeatureColour(Color.yellow, Color.red,
+ Color.green, 1f, 11f);
+ fr.getFeatureColours().put("Cath", gc);
+ assertEquals(fr.getColour(sf1), Color.green);
+
+ /*
+ * graduated colour by score, no threshold, with score value
+ */
+ SequenceFeature sf2 = new SequenceFeature("Cath", "", 6, 8, 6f,
+ "group1");
+ // score 6 is half way from yellow(255, 255, 0) to red(255, 0, 0)
+ Color expected = new Color(255, 128, 0);
+ assertEquals(fr.getColour(sf2), expected);
+
+ /*
+ * above threshold, score is above threshold - no change
+ */
+ gc.setAboveThreshold(true);
+ gc.setThreshold(5f);
+ assertEquals(fr.getColour(sf2), expected);
+
+ /*
+ * threshold is min-max; now score 6 is 1/6 of the way from 5 to 11
+ * or from yellow(255, 255, 0) to red(255, 0, 0)
+ */
+ gc = new FeatureColour(Color.yellow, Color.red, Color.green, 5f, 11f);
+ fr.getFeatureColours().put("Cath", gc);
+ gc.setAutoScaled(false); // this does little other than save a checkbox setting!
+ assertEquals(fr.getColour(sf2), new Color(255, 213, 0));
+
+ /*
+ * feature score is below threshold - no colour
+ */
+ gc.setAboveThreshold(true);
+ gc.setThreshold(7f);
+ assertNull(fr.getColour(sf2));
+
+ /*
+ * feature score is above threshold - no colour
+ */
+ gc.setBelowThreshold(true);
+ gc.setThreshold(3f);
+ assertNull(fr.getColour(sf2));
+
+ /*
+ * colour by feature attribute value
+ * first with no value held
+ */
+ gc = new FeatureColour(Color.yellow, Color.red, Color.green, 1f, 11f);
+ fr.getFeatureColours().put("Cath", gc);
+ gc.setAttributeName("AF");
+ assertEquals(fr.getColour(sf2), Color.green);
+
+ // with non-numeric attribute value
+ sf2.setValue("AF", "Five");
+ assertEquals(fr.getColour(sf2), Color.green);
+
+ // with numeric attribute value
+ sf2.setValue("AF", "6");
+ assertEquals(fr.getColour(sf2), expected);
+
+ // with numeric value outwith threshold
+ gc.setAboveThreshold(true);
+ gc.setThreshold(10f);
+ assertNull(fr.getColour(sf2));
+
+ // with filter on AF < 4
+ gc.setAboveThreshold(false);
+ assertEquals(fr.getColour(sf2), expected);
+ KeyedMatcherSetI filter = new KeyedMatcherSet();
+ filter.and(new KeyedMatcher(Condition.LT, 4f, "AF"));
+ fr.setFeatureFilter("Cath", filter);
+ assertNull(fr.getColour(sf2));
+
+ // with filter on 'Consequence contains missense'
+ filter = new KeyedMatcherSet();
+ filter.and(new KeyedMatcher(Condition.Contains, "missense",
+ "Consequence"));
+ fr.setFeatureFilter("Cath", filter);
+ // if feature has no Consequence attribute, no colour
+ assertNull(fr.getColour(sf2));
+ // if attribute does not match filter, no colour
+ sf2.setValue("Consequence", "Synonymous");
+ assertNull(fr.getColour(sf2));
+ // attribute matches filter
+ sf2.setValue("Consequence", "Missense variant");
+ assertEquals(fr.getColour(sf2), expected);
+
+ // with filter on CSQ.Feature contains "ENST01234"
+ filter = new KeyedMatcherSet();
+ filter.and(new KeyedMatcher(Condition.Matches, "ENST01234", "CSQ",
+ "Feature"));
+ fr.setFeatureFilter("Cath", filter);
+ // if feature has no CSQ data, no colour
+ assertNull(fr.getColour(sf2));
+ // if CSQ data does not include Feature, no colour
+ Map<String, String> csqData = new HashMap<>();
+ csqData.put("BIOTYPE", "Transcript");
+ sf2.setValue("CSQ", csqData);
+ assertNull(fr.getColour(sf2));
+ // if attribute does not match filter, no colour
+ csqData.put("Feature", "ENST9876");
+ assertNull(fr.getColour(sf2));
+ // attribute matches filter
+ csqData.put("Feature", "ENST01234");
+ assertEquals(fr.getColour(sf2), expected);
+ }
}
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
+import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import jalview.datamodel.SequenceFeature;
import jalview.gui.JvOptionPane;
import java.awt.Color;
-import junit.extensions.PA;
-
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class FeatureColourTest
{
assertTrue(fc1.isColourByLabel());
assertFalse(fc1.isGraduatedColour());
assertTrue(fc1.isColourByAttribute());
- assertEquals("AF", fc1.getAttributeName());
+ assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
/*
* colour by attribute (value)
assertTrue(fc1.isGraduatedColour());
assertFalse(fc1.isColourByLabel());
assertTrue(fc1.isColourByAttribute());
- assertEquals("AF", fc1.getAttributeName());
+ assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
assertTrue(fc1.isAboveThreshold());
assertEquals(12f, fc1.getThreshold());
assertEquals(Color.gray, fc1.getMinColour());
assertTrue(fc1.isGraduatedColour());
assertFalse(fc1.isColourByLabel());
assertTrue(fc1.isColourByAttribute());
- assertEquals("AF", fc1.getAttributeName());
+ assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
assertEquals(13f, fc1.getMin());
assertEquals(36f, fc1.getMax());
assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
assertFalse(fc1.isGraduatedColour());
assertTrue(fc1.isColourByLabel());
assertTrue(fc1.isColourByAttribute());
- assertEquals("AC", fc1.getAttributeName());
+ assertArrayEquals(new String[] { "AC" }, fc1.getAttributeName());
assertEquals(13f, fc1.getMin());
assertEquals(36f, fc1.getMax());
}
vr.setEndSeq(al.getHeight());
assertEquals(vr.getEndSeq(), al.getHeight() - 1);
- // vr.setEndRes(al.getHeight() - 1);
vr.setEndSeq(al.getHeight() - 1);
assertEquals(vr.getEndSeq(), al.getHeight() - 1);
}
}
@Test(groups = { "Functional" })
+ public void testSetStartResAndSeq()
+ {
+ ViewportRanges vr = new ViewportRanges(al);
+ vr.setViewportHeight(10);
+ vr.setStartResAndSeq(3, 6);
+ assertEquals(vr.getStartRes(), 3);
+ assertEquals(vr.getStartSeq(), 6);
+ assertEquals(vr.getEndRes(), 3 + vr.getViewportWidth() - 1);
+ assertEquals(vr.getEndSeq(), 6 + vr.getViewportHeight() - 1);
+
+ vr.setStartResAndSeq(10, 25);
+ assertEquals(vr.getStartRes(), 10);
+ assertEquals(vr.getStartSeq(), 19);
+ assertEquals(vr.getEndRes(), 10 + vr.getViewportWidth() - 1);
+ assertEquals(vr.getEndSeq(), 19 + vr.getViewportHeight() - 1);
+ }
+
+ @Test(groups = { "Functional" })
public void testSetViewportHeight()
{
ViewportRanges vr = new ViewportRanges(al);
assertEquals(vr.getEndRes(), 52);
}
- // leave until JAL-2388 is merged and we can do without viewport
- /*@Test(groups = { "Functional" })
+ @Test(groups = { "Functional" })
public void testScrollToVisible()
{
ViewportRanges vr = new ViewportRanges(al);
vr.setViewportStartAndWidth(12,5);
vr.setViewportStartAndHeight(10,6);
- vr.scrollToVisible(13,14)
+ vr.scrollToVisible(13, 14);
// no change
assertEquals(vr.getStartRes(), 12);
assertEquals(vr.getStartSeq(), 6);
// test for hidden columns too
- }*/
+ al.getHiddenColumns().hideColumns(1, 3);
+ vr.scrollToVisible(13, 3);
+ assertEquals(vr.getStartRes(), 6);
+ assertEquals(vr.getStartSeq(), 3);
+
+ vr.scrollToVisible(2, 9);
+ assertEquals(vr.getStartRes(), 0);
+ assertEquals(vr.getStartSeq(), 4);
+ }
@Test(groups = { "Functional" })
public void testEventFiring()
// one event fired when startRes is called with new value
vr.setStartRes(4);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
l.reset();
// no event fired for same value
l.reset();
vr.setStartSeq(4);
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.setStartSeq(4);
l.reset();
vr.setEndSeq(10);
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.setEndSeq(10);
l.reset();
vr.setStartEndRes(2, 15);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
l.reset();
vr.setStartEndRes(2, 15);
// check new value fired by event is corrected startres
vr.setStartEndRes(-1, 5);
- assertTrue(l.verify(1, Arrays.asList("startres"), Arrays.asList(0)));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES),
+ Arrays.asList(0)));
l.reset();
// check new value fired by event is corrected endres
vr.setStartEndRes(0, -1);
- assertTrue(l.verify(1, Arrays.asList("endres"), Arrays.asList(0)));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES),
+ Arrays.asList(0)));
l.reset();
vr.setStartEndSeq(2, 15);
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.setStartEndSeq(2, 15);
// check new value fired by event is corrected startseq
vr.setStartEndSeq(-1, 5);
- assertTrue(l.verify(1, Arrays.asList("startseq"), Arrays.asList(0)));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ),
+ Arrays.asList(0)));
l.reset();
// check new value fired by event is corrected endseq
vr.setStartEndSeq(0, -1);
- assertTrue(l.verify(1, Arrays.asList("endseq"), Arrays.asList(0)));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ),
+ Arrays.asList(0)));
l.reset();
// reset for later tests
// test viewport height and width setting triggers event
vr.setViewportHeight(10);
- assertTrue(l.verify(1, Arrays.asList("endseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ)));
l.reset();
vr.setViewportWidth(18);
- assertTrue(l.verify(1, Arrays.asList("endres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDRES)));
l.reset();
// already has seq start set to 2, so triggers endseq
vr.setViewportStartAndHeight(2, 16);
- assertTrue(l.verify(1, Arrays.asList("endseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.ENDSEQ)));
l.reset();
vr.setViewportStartAndWidth(1, 14);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
l.reset();
// test page up/down triggers event
vr.pageUp();
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.pageDown();
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
// test scrolling triggers event
vr.scrollUp(true);
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.scrollUp(false);
- assertTrue(l.verify(1, Arrays.asList("startseq")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTSEQ)));
l.reset();
vr.scrollRight(true);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
l.reset();
vr.scrollRight(false);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
l.reset();
vr.scrollToVisible(10, 10);
assertTrue(l.verify(4,
- Arrays.asList("startseq", "startseq", "startseq", "startseq")));
+ Arrays.asList(ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ,
+ ViewportRanges.STARTSEQ, ViewportRanges.STARTSEQ)));
l.reset();
/*
l.reset();
vr.scrollToWrappedVisible(25);
- assertTrue(l.verify(1, Arrays.asList("startres")));
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRES)));
+ l.reset();
+
+ // test setStartResAndSeq triggers one event
+ vr.setStartResAndSeq(5, 7);
+ assertTrue(l.verify(1, Arrays.asList(ViewportRanges.STARTRESANDSEQ),
+ Arrays.asList(5, 7)));
+
+ l.reset();
}
@Test(groups = { "Functional" })
assertEquals(vr.getStartSeq(), 1);
assertEquals(vr.getStartRes(), 43);
}
+
+ @Test(groups = { "Functional" })
+ public void testSetViewportLocation()
+ {
+ AlignmentI al2 = gen.generate(60, 80, 1, 0, 0);
+
+ ViewportRanges vr = new ViewportRanges(al2);
+
+ // start with viewport on 5-14
+ vr.setViewportStartAndWidth(5, 10);
+ assertEquals(vr.getStartRes(), 5);
+ assertEquals(vr.getEndRes(), 14);
+
+ vr.setViewportStartAndHeight(3, 13);
+ assertEquals(vr.getStartSeq(), 3);
+ assertEquals(vr.getEndSeq(), 15);
+
+ // set location to (8,5) - no change
+ vr.setViewportLocation(8, 5);
+ assertEquals(vr.getStartRes(), 5);
+ assertEquals(vr.getEndRes(), 14);
+ assertEquals(vr.getStartSeq(), 3);
+ assertEquals(vr.getEndSeq(), 15);
+
+ // set location to (40,50) - change to top left (40,50)
+ vr.setViewportLocation(40, 50);
+ assertEquals(vr.getStartRes(), 40);
+ assertEquals(vr.getEndRes(), 49);
+ assertEquals(vr.getStartSeq(), 50);
+ assertEquals(vr.getEndSeq(), 62);
+
+ // set location past end of alignment - resets to leftmost pos
+ vr.setViewportLocation(63, 85);
+ assertEquals(vr.getStartRes(), 50);
+ assertEquals(vr.getEndRes(), 59);
+ assertEquals(vr.getStartSeq(), 67);
+ assertEquals(vr.getEndSeq(), 79);
+
+ // hide some columns
+ al2.getHiddenColumns().hideColumns(20, 50);
+ vr.setViewportLocation(55, 4);
+ assertEquals(vr.getStartRes(), 19);
+ assertEquals(vr.getEndRes(), 28);
+ assertEquals(vr.getStartSeq(), 4);
+ assertEquals(vr.getEndSeq(), 16);
+
+ // hide some sequences
+ al2.getHiddenSequences().hideSequence(al2.getSequenceAt(3));
+ al2.getHiddenSequences().hideSequence(al2.getSequenceAt(4));
+ vr.setViewportLocation(17, 5);
+ assertEquals(vr.getStartRes(), 17);
+ assertEquals(vr.getEndRes(), 26);
+ assertEquals(vr.getStartSeq(), 3);
+ assertEquals(vr.getEndSeq(), 15);
+
+ // set wrapped mode
+ vr.setWrappedMode(true);
+ vr.setViewportLocation(1, 8);
+ assertEquals(vr.getStartRes(), 0);
+ assertEquals(vr.getEndRes(), 9);
+ assertEquals(vr.getStartSeq(), 3);
+ assertEquals(vr.getEndSeq(), 15);
+
+ // try further down the alignment
+ vr.setViewportLocation(57, 5);
+ assertEquals(vr.getStartRes(), 20);
+ assertEquals(vr.getEndRes(), 29);
+ assertEquals(vr.getStartSeq(), 3);
+ assertEquals(vr.getEndSeq(), 15);
+ }
}
// mock listener for property change events
{
firecount++;
events.add(evt.getPropertyName());
- newvalues.add((Integer) evt.getNewValue());
+ if (evt.getPropertyName().equals(ViewportRanges.STARTRESANDSEQ))
+ {
+ newvalues.add(((int[]) evt.getNewValue())[0]);
+ newvalues.add(((int[]) evt.getNewValue())[1]);
+ }
+ else
+ {
+ newvalues.add((Integer) evt.getNewValue());
+ }
}
public boolean verify(int count, List<String> eventslist,
new StringReader(UNIPROT_XML)).get(0);
/*
- * name formatted as source | accession ids | names
- * source database converted to Jalview canonical name
+ * name formatted with Uniprot Entry name
*/
- String expectedName = "UNIPROT|A9CKP4|A9CKP5|A9CKP4_AGRT5|A9CKP4_AGRT6";
- assertEquals(expectedName, Uniprot.getUniprotEntryId(entry));
+ String expectedName = "A9CKP4_AGRT5|A9CKP4_AGRT6";
+ assertEquals(expectedName,
+ Uniprot.getUniprotEntryId(entry));
}
/**
<string><![CDATA[664]]></string>
</property>
<property name="sourceName">
- <string><![CDATA[groovy-all-2.4.6-indy.jar]]></string>
+ <string><![CDATA[groovy-all-2.4.12-indy.jar]]></string>
</property>
<property name="overrideUnixPermissions">
<boolean>false</boolean>
<boolean>true</boolean>
</property>
<property name="destinationName">
- <string><![CDATA[groovy-all-2.4.6-indy.jar]]></string>
+ <string><![CDATA[groovy-all-2.4.12-indy.jar]]></string>
</property>
<property name="fileSize">
<long>6149494</long>
</method>
<method name="put">
<string><![CDATA[com.zerog.ia.installer.options.valid.vm.list]]></string>
- <string><![CDATA[1.8+]]></string>
+ <string><![CDATA[1.8*]]></string>
</method>
<method name="put">
<string><![CDATA[com.zerog.ia.project.build.last.date]]></string>
</method>
<method name="put">
<string><![CDATA[com.zerog.ia.installer.options.platform.macosx.vm.version]]></string>
- <string><![CDATA[1.8+]]></string>
+ <string><![CDATA[1.8*]]></string>
</method>
<method name="put">
<string><![CDATA[com.zerog.ia.build.platform.java.novm]]></string>
import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
+import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
private int javaCount;
- private HashSet<String> invalidKeys;
+ private Set<String> invalidKeys;
+
+ private Set<String> dynamicKeys;
/**
* Runs the scan given the path to the root of Java source directories
private void doMain(String srcPath) throws IOException
{
System.out.println("Scanning " + srcPath
- + " for calls to MessageManager");
+ + " for calls to MessageManager\n");
sourcePath = srcPath;
loadMessages();
File dir = new File(srcPath);
System.out.println(srcPath + " not found");
return;
}
- invalidKeys = new HashSet<String>();
+
+ invalidKeys = new HashSet<>();
+ dynamicKeys = new HashSet<>();
+
if (dir.isDirectory())
{
scanDirectory(dir);
private void reportResults()
{
System.out.println("\nScanned " + javaCount + " source files");
- System.out.println("Message.properties has " + messages.size()
+ System.out.println(
+ "Messages.properties has " + messages.size()
+ " keys");
- System.out.println("Found " + invalidKeys.size()
- + " possibly invalid parameter calls");
+ if (!invalidKeys.isEmpty())
+ {
+ System.out.println("Found " + invalidKeys.size()
+ + " possibly invalid parameter call"
+ + (invalidKeys.size() > 1 ? "s" : ""));
+ }
- System.out.println(messageKeys.size()
- + " keys not found, either unused or constructed dynamically");
+ System.out.println("Keys not found, assumed constructed dynamically:");
+ int dynamicCount = 0;
for (String key : messageKeys)
{
- System.out.println(" " + key);
+ if (isDynamic(key))
+ {
+ System.out.println(" " + key);
+ dynamicCount++;
+ }
+ }
+
+ if (dynamicCount < messageKeys.size())
+ {
+ System.out.println((messageKeys.size() - dynamicCount)
+ + " keys not found, possibly unused");
+ for (String key : messageKeys)
+ {
+ if (!isDynamic(key))
+ {
+ System.out.println(" " + key);
+ }
+ }
+ }
+ System.out
+ .println("(Run i18nAnt.xml to compare other message bundles)");
+ }
+
+ /**
+ * Answers true if the key starts with one of the recorded dynamic key stubs,
+ * else false
+ *
+ * @param key
+ * @return
+ */
+ private boolean isDynamic(String key)
+ {
+ for (String dynamic : dynamicKeys)
+ {
+ if (key.startsWith(dynamic))
+ {
+ return true;
+ }
}
+ return false;
}
/**
continue;
}
+ String messageKey = getMessageKey(method, methodArgs);
+
if (METHOD3 == method)
{
System.out.println(String.format("Dynamic key at %s line %s %s",
path.substring(sourcePath.length()), lineNos, line));
+ String key = messageKey.substring(1, messageKey.length() - 1);
+ dynamicKeys.add(key);
continue;
}
- String messageKey = getMessageKey(method, methodArgs);
if (messageKey == null)
{
System.out.println(String.format("Trouble parsing %s line %s %s",
messages.load(reader);
reader.close();
- messageKeys = new TreeSet<String>();
+ messageKeys = new TreeSet<>();
for (Object key : messages.keySet())
{
messageKeys.add((String) key);