label.autoadd_temp = Add Temperature Factor annotation to alignment
label.structure_viewer = Default structure viewer
label.double_click_to_browse = Double-click to browse for file
-label.chimera_path = Path to Chimera program
-label.chimera_path_tip = Jalview will first try any path entered here, else standard installation locations.<br>Double-click to browse for file.
-label.invalid_chimera_path = Chimera path not found or not executable
-label.chimera_missing = Chimera structure viewer not found.<br/>Please enter the path to Chimera (if installed),<br/>or download and install UCSF Chimera.
-label.chimera_failed = Error opening Chimera - is it installed?\nCheck path in Preferences, Structure
+label.viewer_path = Path to {0} program
+label.viewer_path_tip = Jalview will first try any path entered here, else standard installation locations.<br>Double-click to browse for file.
+label.invalid_viewer_path = Path not found or not executable
+label.viewer_missing = Structure viewer not found.<br/>Please enter the path to the executable (if installed),<br/>or download and install the program.
+label.open_viewer_failed = Error opening {0} - is it installed?\nCheck path in Preferences, Structure
label.min_colour = Minimum Colour
label.max_colour = Maximum Colour
label.no_colour = No Colour
label.delete_gap = Delete 1 gap
label.delete_gaps = Delete {0} gaps
label.sequence_details = Sequence Details
-label.jmol_help = Jmol Help
-label.chimera_help = Chimera Help
+label.viewer_help = {0} Help
label.close_viewer = Close Viewer
-label.confirm_close_chimera = This will close Jalview''s connection to {0}.<br>Do you want to close the Chimera window as well?
+label.confirm_close_viewer = This will close Jalview''s connection to {0}.<br>Do you want to close the {1} window as well?
label.all = All
label.sort_by = Sort alignment by
label.sort_by_score = Sort by Score
label.web_services = Web Services
label.right_click_to_edit_currently_selected_parameter = Right click to edit currently selected parameter.
label.let_jmol_manage_structure_colours = Let Jmol manage structure colours
-label.let_chimera_manage_structure_colours = Let Chimera manage structure colours
label.fetch_chimera_attributes = Fetch Chimera attributes
label.fetch_chimera_attributes_tip = Copy Chimera attribute to Jalview feature
label.marks_leaves_tree_not_associated_with_sequence = Marks leaves of tree not associated with a sequence
label.link_name = Link Name
label.pdb_file = PDB file
label.colour_with_jmol = Colour with Jmol
-label.colour_with_chimera = Colour with Chimera
+label.let_viewer_manage_structure_colours = Let viewer manage structure colours
+label.colour_with_viewer = Colour in structure viewer
label.superpose_structures = Superpose Structures
error.superposition_failed = Superposition failed: {0}
label.insufficient_residues = Not enough aligned residues ({0}) to perform superposition
-label.jmol = Jmol
-label.chimera = Chimera
-label.create_chimera_attributes = Write Jalview features
-label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features
+label.create_viewer_attributes = Write Jalview features
+label.create_viewer_attributes_tip = Set structure residue attributes for Jalview features
label.attributes_set = {0} attribute values set on Chimera
label.sort_alignment_by_tree = Sort Alignment By Tree
label.mark_unlinked_leaves = Mark Unlinked Leaves
status.loading_cached_pdb_entries = Loading Cached PDB Entries
status.searching_for_pdb_structures = Searching for PDB Structures
status.opening_file_for = opening file for
-status.colouring_chimera = Colouring Chimera
+status.colouring_structures = Colouring structures
label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
label.font_too_small = Font size is too small
label.error_loading_file_params = Error loading file {0}
label.delete_gap = Borrar 1 hueco
label.delete_gaps = Borrar {0} huecos
label.sequence_details = Detalles de la secuencia
-label.jmol_help = Ayuda de Jmol
+label.viewer_help = Ayuda sobre {0}
# Todos/Todas is gender-sensitive, but currently only used for feminine (cadena / anotación)!
label.all = Todas
label.sort_by = Ordenar por
label.link_name = Nombre del enalce
label.pdb_file = Fichero PDB
label.colour_with_jmol = Colorear con Jmol
-label.jmol = Jmol
label.sort_alignment_by_tree = Ordenar alineamiento por árbol
label.mark_unlinked_leaves = Marcar las hojas como no enlazadas
label.associate_leaves_with = Asociar hojas con
action.annotations=Anotaciones
label.nuc_alignment_colour=Color del Alineamiento Nucleotídico
label.copy_format_from=Copiar formato de
-label.chimera=Chimera
label.create_chimera_attributes = Escribir características de Jalview
label.create_chimera_attributes_tip = Establecer atributos en Chimera para características visibles
label.attributes_set = {0} valores de atributos establecidos en Chimera
action.export_annotations=Exportar Anotaciones
action.set_as_reference=Marcar como Referencia
action.unmark_as_reference=Desmarcar como Referencia
-label.chimera_failed=Error al abrir Chimera - está instalado?\nCompruebe ruta en Preferencias, Estructura
+label.open_viewer_failed=Error al abrir {0} - está instalado?\nCompruebe ruta en Preferencias, Estructura
label.find=Buscar
label.select_pdb_file=Seleccionar Fichero PDB
label.structures_filter=Filtro de Estructuras
error.invalid_regex=Expresión regular inválida
label.autoadd_temp=Añadir anotación factor de temperatura al alineamiento
label.double_click_to_browse = Haga doble clic para buscar fichero
-label.chimera_path_tip=Jalview intentará primero las rutas introducidas aquí, Y si no las rutas usuales de instalación
label.structure_chooser=Selector de Estructuras
label.structure_chooser_manual_association=Selector de Estructuras - asociación manual
label.threshold_filter=Filtro de Umbral
label.hide_insertions=Ocultar Inserciones
info.change_threshold_mode_to_enable=Cambiar Modo de Umbral para Habilitar
label.separate_multiple_query_values=Introducir uno o mas {0}s separados por punto y coma ";"
-label.let_chimera_manage_structure_colours=Deja que Chimera maneje colores de estructuras
label.fetch_chimera_attributes = Buscar atributos desde Chimera
label.fetch_chimera_attributes_tip = Copiar atributo de Chimera a característica de Jalview
label.view_rna_structure=Estructura 2D VARNA
-label.colour_with_chimera=Colorear con Chimera
+label.colour_with_viewer = Colorear con visualizador de estructuras
+label.let_viewer_manage_structure_colours = Deja que el visualizador maneje los colores de estructuras
label.superpose_structures = Superponer estructuras
error.superposition_failed = Superposición fallido: {0}
label.insufficient_residues = Residuos alineados ({0}) insuficentes para superponer
label.mark_as_representative=Marcar como representativa
label.include_description=Incluir Descripción
label.for=para
-label.invalid_chimera_path=Ruta de Chimera no encontrada o no ejecutable
info.search_in_annotation_label=Buscar en etiqueta de {0}
info.search_in_annotation_description=Buscar en descripción de {0}
label.select_many_views=Seleccionar múltiples vistas
warn.oneseq_msainput_selection=La selección actual sólo contiene una única secuencia. ¿Quieres enviar todas las secuencias para la alineación en su lugar?
label.use_rnaview=Usar RNAView para estructura secondaria
label.search_all=Introducir uno o más valores de búsqueda separados por punto y coma ";" (Nota: buscará en toda la base de datos PDB)
-label.confirm_close_chimera=Cerrará la conexión de Jalview a {0}.<br>¿Quieres cerrar la ventana Chimera también?
+label.confirm_close_viewer=Cerrará la conexión de Jalview a {0}.<br>¿Quieres cerrar la ventana {1} también?
tooltip.rnalifold_calculations=Se calcularán predicciones de estructura secondaria de RNA para el alineaminento, y se actualizarán si se efectuan cambios
tooltip.rnalifold_settings=Modificar la configuración de la predicción RNAAlifold. Úselo para ocultar o mostrar resultados del cálculo de RNA, o cambiar parámetros de el plegado de RNA.
label.show_selected_annotations=Mostrar anotaciones seleccionadas
-status.colouring_chimera=Coloreando Chimera
+status.colouring_structures=Coloreando estructuras
label.configure_displayed_columns=Configurar Columnas Mostradas
label.aacon_calculations=cálculos AACon
label.pdb_web-service_error=Error de servicio web PDB
exception.unable_to_detect_internet_connection=Jalview no puede detectar una conexión a Internet
-label.chimera_path=Ruta de acceso a Chimera
+label.viewer_path=Ruta de acceso a {0}
+label.viewer_path_tip=Jalview intentará primero las rutas introducidas aquí, Y si no las rutas usuales de instalación
+label.invalid_viewer_path=Ruta no encontrada o no ejecutable
+label.viewer_missing=Visualizador de estructura no encontrado.<br/>Por favor, introduzca la ruta de la ejecutable,<br/>o descargar e instalar el programa.
warn.delete_all=<html>Borrar todas las secuencias cerrará la ventana del alineamiento.<br>Confirmar o Cancelar.
label.select_all=Seleccionar Todos
label.alpha_helix=Hélice Alfa
-label.chimera_help=Ayuda para Chimera
label.find_tip=Buscar alineamiento, selección o IDs de secuencia para una subsecuencia (sin huecos)
label.structure_viewer=Visualizador por defecto
label.embbed_biojson=Incrustar BioJSON al exportar HTML
tooltip.aacon_calculations=Actualizar cálculos AACon automáticamente.
info.select_filter_option=Escoger Opción de Filtro / Entrada Manual
info.invalid_msa_input_mininfo=Necesita por lo menos dos secuencias con al menos 3 residuos cada una, sin regiones ocultas entre ellas.
-label.chimera_missing=Visualizador de estructura Chimera no encontrado.<br/>Por favor, introduzca la ruta de Chimera,<br/>o descargar e instalar la UCSF Chimera.
exception.fts_server_unreachable=Jalview no puede conectar con el servidor {0}. \nPor favor asegúrese de que está conectado a Internet y vuelva a intentarlo.
exception.outofmemory_loading_mmcif_file=Sin memoria al cargar el fichero mmCIF
label.hide_columns_not_containing=Ocultar las columnas que no contengan
*/
package ext.edu.ucsf.rbvi.strucviz2;
-import jalview.ws.HttpClientUtils;
-
import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
import ext.edu.ucsf.rbvi.strucviz2.port.ListenerThreads;
+import jalview.ws.HttpClientUtils;
/**
* This object maintains the Chimera communication information.
for (ChimeraModel chimeraModel : modelList)
{
// get model color
- Color modelColor = getModelColor(chimeraModel);
+ Color modelColor = isChimeraX() ? null : getModelColor(chimeraModel);
if (modelColor != null)
{
chimeraModel.setModelColor(modelColor);
// chimeraSend("repr stick "+newModel.toSpec());
// Create the information we need for the navigator
- if (type != ModelType.SMILES)
+ if (type != ModelType.SMILES && !isChimeraX())
{
addResidues(chimeraModel);
}
public void stopListening()
{
- sendChimeraCommand("listen stop models ; listen stop selection ", false);
+ // TODO send this command when viewer connection is closed in Jalview
+ String command = isChimeraX
+ ? "info notify stop models jalview; info notify stop selection jalview"
+ : "listen stop models ; listen stop selection ";
+ sendChimeraCommand(command, false);
}
/**
*/
public void startListening(String uri)
{
- sendChimeraCommand("listen start models url " + uri
- + ";listen start select prefix SelectionChanged url " + uri,
- false);
+ /*
+ * listen for model changes
+ */
+ String command = isChimeraX
+ ? ("info notify start models prefix ModelChanged jalview url "
+ + uri)
+ : ("listen start models url " + uri);
+ sendChimeraCommand(command, false);
+
+ /*
+ * listen for selection changes
+ */
+ command = isChimeraX
+ ? ("info notify start selection jalview prefix SelectionChanged url "
+ + uri)
+ : ("listen start select prefix SelectionChanged url " + uri);
+ sendChimeraCommand(command, false);
}
/**
public List<String> getSelectedResidueSpecs()
{
List<String> selectedResidues = new ArrayList<>();
- List<String> chimeraReply = sendChimeraCommand(
- "list selection level residue", true);
+
+ // in fact 'listinfo' (undocumented) works in ChimeraX
+ String command = (isChimeraX
+ ? "info"
+ : "list") + " selection level residue";
+ List<String> chimeraReply = sendChimeraCommand(command, true);
if (chimeraReply != null)
{
/*
- * expect 0, 1 or more lines of the format
+ * expect 0, 1 or more lines of the format either
+ * Chimera:
* residue id #0:43.A type GLY
- * where we are only interested in the atomspec #0.43.A
+ * ChimeraX:
+ * residue id /A:89 name THR index 88
+ * We are only interested in the atomspec (third token of the reply)
*/
for (String inputLine : chimeraReply)
{
String[] inputLineParts = inputLine.split("\\s+");
- if (inputLineParts.length == 5)
+ if (inputLineParts.length >= 5)
{
selectedResidues.add(inputLineParts[2]);
}
public List<ChimeraModel> getModelList()
{
List<ChimeraModel> modelList = new ArrayList<>();
- List<String> list = sendChimeraCommand("list models type molecule",
- true);
+ String command = "list models type "
+ + (isChimeraX ? "AtomicStructure" : "molecule");
+ List<String> list = sendChimeraCommand(command, true);
if (list != null)
{
for (String modelLine : list)
{
- ChimeraModel chimeraModel = new ChimeraModel(modelLine);
- modelList.add(chimeraModel);
+ try
+ {
+ ChimeraModel chimeraModel = new ChimeraModel(modelLine);
+ modelList.add(chimeraModel);
+ } catch (NullPointerException e)
+ {
+ // hack for now
+ }
}
}
return modelList;
{
// ensure symbolic links are resolved
chimeraPath = Paths.get(chimeraPath).toRealPath().toString();
+ isChimeraX = chimeraPath.toLowerCase().contains("chimerax");
File path = new File(chimeraPath);
// uncomment the next line to simulate Chimera not installed
// path = new File(chimeraPath + "x");
args.add(chimeraPath);
// shows Chimera output window but suppresses REST responses:
// args.add("--debug");
- args.add("--start");
- args.add("RESTServer");
+ if (isChimeraX())
+ {
+ args.add("--cmd");
+ args.add("remote rest start");
+ }
+ else
+ {
+ args.add("--start");
+ args.add("RESTServer");
+ }
ProcessBuilder pb = new ProcessBuilder(args);
chimera = pb.start();
error = "";
{
responses.append("\n" + response);
// expect: REST server on host 127.0.0.1 port port_number
+ // ChimeraX is the same except "REST server started on host..."
if (response.startsWith("REST server"))
{
String[] tokens = response.split(" ");
- if (tokens.length == 7 && "port".equals(tokens[5]))
+ for (int i = 0; i < tokens.length - 1; i++)
{
- port = Integer.parseInt(tokens[6]);
- break;
+ if ("port".equals(tokens[i]))
+ {
+ port = Integer.parseInt(tokens[i + 1]);
+ break;
+ }
}
}
+ if (port > 0)
+ {
+ break; // hack for hanging readLine()
+ }
response = lineReader.readLine();
}
} catch (Exception e)
public List<String> getAttrList()
{
List<String> attributes = new ArrayList<>();
- final List<String> reply = sendChimeraCommand("list resattr", true);
+ String command = (isChimeraX ? "info " : "list ") + "resattr";
+ final List<String> reply = sendChimeraCommand(command, true);
if (reply != null)
{
for (String inputLine : reply)
private volatile boolean busy = false;
+ private boolean isChimeraX;
+
/**
* Send a command to Chimera.
*
*/
public List<String> sendChimeraCommand(String command, boolean reply)
{
- // System.out.println("chimeradebug>> " + command);
+ System.out.println("chimeradebug>> " + command);
if (!isChimeraLaunched() || command == null
|| "".equals(command.trim()))
{
{
String restUrl = "http://127.0.0.1:" + this.chimeraRestPort + "/run";
List<NameValuePair> commands = new ArrayList<>(1);
+ String method = isChimeraX() ? "GET" : "POST";
+ if ("GET".equals(method))
+ {
+ command = command.replace(" ", "+").replace("#", "%23")
+ .replace("|", "%7C").replace(";", "%3B");
+ }
commands.add(new BasicNameValuePair("command", command));
List<String> reply = new ArrayList<>();
BufferedReader response = null;
try
{
- response = HttpClientUtils.doHttpUrlPost(restUrl, commands, CONNECTION_TIMEOUT_MS,
- REST_REPLY_TIMEOUT_MS);
+ response = "GET".equals(method)
+ ? HttpClientUtils.doHttpGet(restUrl, commands,
+ CONNECTION_TIMEOUT_MS, REST_REPLY_TIMEOUT_MS)
+ : HttpClientUtils.doHttpUrlPost(restUrl, commands,
+ CONNECTION_TIMEOUT_MS, REST_REPLY_TIMEOUT_MS);
String line = "";
while ((line = response.readLine()) != null)
{
{
return chimera;
}
+
+ public boolean isChimeraX()
+ {
+ return isChimeraX;
+ }
+
+ public void setChimeraX(boolean b)
+ {
+ isChimeraX = b;
+ }
}
*/
package ext.edu.ucsf.rbvi.strucviz2;
-import jalview.bin.Cache;
-import jalview.gui.Preferences;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import jalview.bin.Cache;
+import jalview.gui.Preferences;
+
/**
* This object maintains the relationship between Chimera objects and Cytoscape
* objects.
public class StructureManager
{
+ /*
+ * Version numbers to build Windows installation paths for
+ * Chimera https://www.cgl.ucsf.edu/chimera/download.html
+ * ChimeraX http://www.rbvi.ucsf.edu/chimerax/download.html#release
+ * https://www.rbvi.ucsf.edu/trac/ChimeraX/wiki/ChangeLog
+ * These are a fallback for Jalview users who don't save path in Preferences;
+ * these will need to be updated as new versions are released;
+ * deliberately not 'final' (so modifiable using Groovy).
+ *
+ * May 2020: 1.14 is Chimera latest, anticipating a few more...
+ * 0.93 is ChimeraX latest, 1.0 expected soon
+ */
+ private static String[] CHIMERA_VERSIONS = new String[] { "1.16.2",
+ "1.16.1", "1.16",
+ "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1", "1.14",
+ "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2",
+ "1.11.2", "1.11.1", "1.11" };
+
+ private static String[] CHIMERAX_VERSIONS = new String[] { "1.0", "0.93",
+ "0.92", "0.91", "0.9" };
+
static final String[] defaultStructureKeys = { "Structure", "pdb",
"pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids",
"ModelName", "ModelNumber" };
StructureSettings defaultSettings = null;
// TODO: [Optional] Change priority of Chimera paths
- public static List<String> getChimeraPaths()
+ public static List<String> getChimeraPaths(boolean isChimeraX)
{
List<String> pathList = new ArrayList<>();
// }
/*
- * Jalview addition: check if path set in user preferences.
+ * Jalview addition: check if path set in user preferences
*/
- String userPath = Cache.getDefault(Preferences.CHIMERA_PATH, null);
+ String userPath = Cache
+ .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH
+ : Preferences.CHIMERA_PATH, null);
if (userPath != null)
{
- pathList.add(0, userPath);
+ pathList.add(userPath);
}
+ /*
+ * paths are based on getChimeraPaths() in
+ * Chimera:
+ * https://github.com/RBVI/structureViz2/blob/master/src/main/java/edu/ucsf/rbvi/structureViz2/internal/model/StructureManager.java
+ * ChimeraX:
+ * https://github.com/RBVI/structureVizX/blob/master/src/main/java/edu/ucsf/rbvi/structureVizX/internal/model/StructureManager.java
+ */
+ String chimera = isChimeraX ? "ChimeraX" : "Chimera";
+ String chimeraExe = isChimeraX ? "ChimeraX" : "chimera";
+
// Add default installation paths
String os = System.getProperty("os.name");
if (os.startsWith("Linux"))
{
- pathList.add("/usr/local/chimera/bin/chimera");
- pathList.add("/usr/local/bin/chimera");
- pathList.add("/usr/bin/chimera");
- pathList.add(System.getProperty("user.home") + "/opt/bin/chimera");
+ // todo should this be /chimeraX/ for ChimeraX? not in structureVizX code
+ pathList.add("/usr/local/chimera/bin/" + chimeraExe);
+ pathList.add("/usr/local/bin/" + chimeraExe);
+ pathList.add("/usr/bin/" + chimeraExe);
+ pathList.add(System.getProperty("user.home") + "/opt/bin/" + chimeraExe);
}
else if (os.startsWith("Windows"))
{
"C:\\Program Files", "\\Program Files (x86)",
"C:\\Program Files (x86)" })
{
- for (String version : new String[] { "1.11", "1.11.1", "1.11.2",
- "1.12", "1.12.1", "1.12.2", "1.13" })
+ String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
+ : CHIMERA_VERSIONS;
+ for (String version : candidates)
{
- pathList.add(root + "\\Chimera " + version + "\\bin\\chimera");
- pathList.add(
- root + "\\Chimera " + version + "\\bin\\chimera.exe");
+ // TODO original code doesn't include version in path; which is right?
+ String path = String.format("%s\\%s %s\\bin\\%s", root, chimera,
+ version, chimeraExe);
+ pathList.add(path);
+ pathList.add(path + ".exe");
}
}
}
else if (os.startsWith("Mac"))
{
- pathList.add("/Applications/Chimera.app/Contents/MacOS/chimera");
+ pathList.add(String.format("/Applications/%s.app/Contents/MacOS/%s",
+ chimera, chimeraExe));
}
return pathList;
}
import jalview.api.AlignmentViewPanel;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
-import jalview.schemes.ColourSchemeI;
import jalview.structures.models.AAStructureBindingModel;
public interface JalviewStructureDisplayI
void closeViewer(boolean closeExternalViewer);
/**
- * apply a colourscheme to the structures in the viewer
- *
- * @param colourScheme
- */
- void setJalviewColourScheme(ColourSchemeI colourScheme);
-
- /**
*
* @return true if all background sequence/structure binding threads have
* completed for this viewer instance
*/
void raiseViewer();
+ AlignmentViewPanel getAlignmentPanel();
+
+ /**
+ * Answers true if the given alignment view is used to colour structures by
+ * sequence, false if not
+ *
+ * @param ap
+ * @return
+ */
+ boolean isUsedForColourBy(AlignmentViewPanel ap);
+
+ /**
+ * If implemented, shows a command line console in the structure viewer
+ *
+ * @param show
+ * true to show, false to hide
+ */
+ void showConsole(boolean show);
+
+ /**
+ * Remove references to the given alignment view for this structure viewer
+ *
+ * @param avp
+ */
+ void removeAlignmentPanel(AlignmentViewPanel avp);
+
+ /**
+ * Updates the progress bar if there is one. Call stopProgressBar with the
+ * returned handle to remove the message.
+ *
+ * @param msg
+ * @return handle
+ */
+ long startProgressBar(String msg);
+
+ /**
+ * Ends the progress bar with the specified handle, leaving a message (if not
+ * null) on the status bar
+ *
+ * @param msg
+ * @param handle
+ */
+ void stopProgressBar(String msg, long handle);
+
}
* without an additional javascript library to exchange messages between the
* distinct applets. See http://issues.jalview.org/browse/JAL-621
*
- * @param viewer
+ * @param jmolViewer
* JmolViewer instance
* @param sequenceIds
* - sequence Ids to search for associations
*/
package jalview.appletgui;
-import jalview.bin.JalviewLite;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.io.DataSourceType;
-import jalview.io.FileParse;
-import jalview.io.StructureFile;
-import jalview.schemes.BuriedColourScheme;
-import jalview.schemes.HelixColourScheme;
-import jalview.schemes.HydrophobicColourScheme;
-import jalview.schemes.PurinePyrimidineColourScheme;
-import jalview.schemes.StrandColourScheme;
-import jalview.schemes.TaylorColourScheme;
-import jalview.schemes.TurnColourScheme;
-import jalview.schemes.UserColourScheme;
-import jalview.schemes.ZappoColourScheme;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.MessageManager;
-
import java.awt.BorderLayout;
import java.awt.CheckboxMenuItem;
import java.awt.Color;
import java.util.List;
import java.util.Vector;
+import jalview.bin.JalviewLite;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileParse;
+import jalview.io.StructureFile;
+import jalview.schemes.BuriedColourScheme;
+import jalview.schemes.HelixColourScheme;
+import jalview.schemes.HydrophobicColourScheme;
+import jalview.schemes.PurinePyrimidineColourScheme;
+import jalview.schemes.StrandColourScheme;
+import jalview.schemes.TaylorColourScheme;
+import jalview.schemes.TurnColourScheme;
+import jalview.schemes.UserColourScheme;
+import jalview.schemes.ZappoColourScheme;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+
public class AppletJmol extends EmbmenuFrame implements
// StructureListener,
KeyListener, ActionListener, ItemListener
else if (protocol == DataSourceType.FILE
|| protocol == DataSourceType.URL)
{
- jmb.viewer.openFile(pdbentry.getFile());
+ jmb.jmolViewer.openFile(pdbentry.getFile());
}
else
{
throw new Exception(MessageManager.getString(
"exception.invalid_datasource_couldnt_obtain_reader"));
}
- jmb.viewer.openReader(pdbentry.getFile(), pdbentry.getId(),
+ jmb.jmolViewer.openReader(pdbentry.getFile(), pdbentry.getId(),
freader);
} catch (Exception e)
{
}
}
}
- jmb.centerViewer(toshow);
+ jmb.showChains(toshow);
}
void closeViewer()
{
- jmb.closeViewer();
+ jmb.closeViewer(true);
jmb = null;
this.setVisible(false);
}
else if (evt.getSource() == zappo)
{
setEnabled(zappo);
- jmb.setJalviewColourScheme(new ZappoColourScheme());
+ jmb.colourByJalviewColourScheme(new ZappoColourScheme());
}
else if (evt.getSource() == taylor)
{
setEnabled(taylor);
- jmb.setJalviewColourScheme(new TaylorColourScheme());
+ jmb.colourByJalviewColourScheme(new TaylorColourScheme());
}
else if (evt.getSource() == hydro)
{
setEnabled(hydro);
- jmb.setJalviewColourScheme(new HydrophobicColourScheme());
+ jmb.colourByJalviewColourScheme(new HydrophobicColourScheme());
}
else if (evt.getSource() == helix)
{
setEnabled(helix);
- jmb.setJalviewColourScheme(new HelixColourScheme());
+ jmb.colourByJalviewColourScheme(new HelixColourScheme());
}
else if (evt.getSource() == strand)
{
setEnabled(strand);
- jmb.setJalviewColourScheme(new StrandColourScheme());
+ jmb.colourByJalviewColourScheme(new StrandColourScheme());
}
else if (evt.getSource() == turn)
{
setEnabled(turn);
- jmb.setJalviewColourScheme(new TurnColourScheme());
+ jmb.colourByJalviewColourScheme(new TurnColourScheme());
}
else if (evt.getSource() == buried)
{
setEnabled(buried);
- jmb.setJalviewColourScheme(new BuriedColourScheme());
+ jmb.colourByJalviewColourScheme(new BuriedColourScheme());
}
else if (evt.getSource() == purinepyrimidine)
{
- jmb.setJalviewColourScheme(new PurinePyrimidineColourScheme());
+ jmb.colourByJalviewColourScheme(new PurinePyrimidineColourScheme());
}
else if (evt.getSource() == user)
{
{
currentSize = this.getSize();
- if (jmb.viewer == null)
+ if (jmb.jmolViewer == null)
{
g.setColor(Color.black);
g.fillRect(0, 0, currentSize.width, currentSize.height);
}
else
{
- jmb.viewer.renderScreenImage(g, currentSize.width,
+ jmb.jmolViewer.renderScreenImage(g, currentSize.width,
currentSize.height);
}
}
*
* }
*/
- public void setJalviewColourScheme(UserColourScheme ucs)
+ public void colourByJalviewColourScheme(UserColourScheme ucs)
{
- jmb.setJalviewColourScheme(ucs);
+ jmb.colourByJalviewColourScheme(ucs);
}
public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
}
@Override
- public jalview.api.FeatureRenderer getFeatureRenderer(
- AlignmentViewPanel alignment)
- {
- return appletJmolBinding.ap.getFeatureRenderer();
- }
-
- @Override
public jalview.api.SequenceRenderer getSequenceRenderer(
AlignmentViewPanel alignment)
{
Container consolePanel, String buttonsToShow)
{
JmolAppConsoleInterface appc = new AppletConsole();
- appc.start(viewer);
+ appc.start(jmolViewer);
return appc;
}
package jalview.appletgui;
import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
import jalview.api.SequenceRenderer;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
}
@Override
- public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
- {
- AlignmentPanel alignPanel = (AlignmentPanel) alignment;
- if (alignPanel.av.isShowSequenceFeatures())
- {
- return alignPanel.getFeatureRenderer();
- }
- else
- {
- return null;
- }
- }
-
-
- @Override
public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
{
return ((AlignmentPanel) alignment).getSequenceRenderer();
}
@Override
- public void releaseReferences(Object svl)
- {
- }
-
- @Override
public Map<String, Object> getJSpecViewProperty(String arg0)
{
return null;
}
-
}
}
else if (jmol != null)
{
- jmol.setJalviewColourScheme(ucs);
+ jmol.colourByJalviewColourScheme(ucs);
}
else if (pdbcanvas != null)
{
*/
package jalview.ext.jmol;
-import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
-import jalview.api.SequenceRenderer;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.IProgressIndicator;
-import jalview.io.DataSourceType;
-import jalview.io.StructureFile;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.AtomSpec;
-import jalview.structure.StructureMappingcommandSet;
-import jalview.structure.StructureSelectionManager;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.MessageManager;
-
-import java.awt.Color;
import java.awt.Container;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.jmol.api.JmolStatusListener;
import org.jmol.api.JmolViewer;
import org.jmol.c.CBK;
-import org.jmol.script.T;
import org.jmol.viewer.Viewer;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
+import jalview.api.SequenceRenderer;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.IProgressIndicator;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+import javajs.util.BS;
+
public abstract class JalviewJmolBinding extends AAStructureBindingModel
implements JmolStatusListener, JmolSelectionListener,
ComponentListener
{
private String lastMessage;
- boolean allChainsSelected = false;
-
/*
* when true, try to search the associated datamodel for sequences that are
* associated with any unknown structures in the Jmol view.
*/
private boolean associateNewStructs = false;
- Vector<String> atomsPicked = new Vector<>();
-
- private List<String> chainNames;
-
- Hashtable<String, String> chainFile;
-
- /*
- * the default or current model displayed if the model cannot be identified
- * from the selection message
- */
- int frameNo = 0;
-
- // protected JmolGenericPopup jmolpopup; // not used - remove?
+ private Vector<String> atomsPicked = new Vector<>();
- String lastCommand;
+ private String lastCommand;
- boolean loadedInline;
+ private boolean loadedInline;
- StringBuffer resetLastRes = new StringBuffer();
+ private StringBuffer resetLastRes = new StringBuffer();
- public Viewer viewer;
+ public Viewer jmolViewer;
public JalviewJmolBinding(StructureSelectionManager ssm,
PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
DataSourceType protocol)
{
super(ssm, pdbentry, sequenceIs, protocol);
+ setStructureCommands(new JmolCommands());
/*
* viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
* "jalviewJmol", ap.av.applet .getDocumentBase(),
{
super(ssm, seqs);
- viewer = theViewer;
- viewer.setJmolStatusListener(this);
- viewer.addSelectionListener(this);
+ jmolViewer = theViewer;
+ jmolViewer.setJmolStatusListener(this);
+ jmolViewer.addSelectionListener(this);
+ setStructureCommands(new JmolCommands());
}
/**
return getViewerTitle("Jmol", true);
}
- /**
- * prepare the view for a given set of models/chains. chainList contains
- * strings of the form 'pdbfilename:Chaincode'
- *
- * @param chainList
- * list of chains to make visible
- */
- public void centerViewer(Vector<String> chainList)
- {
- StringBuilder cmd = new StringBuilder(128);
- int mlength, p;
- for (String lbl : chainList)
- {
- mlength = 0;
- do
- {
- p = mlength;
- mlength = lbl.indexOf(":", p);
- } while (p < mlength && mlength < (lbl.length() - 2));
- // TODO: lookup each pdb id and recover proper model number for it.
- cmd.append(":" + lbl.substring(mlength + 1) + " /"
- + (1 + getModelNum(chainFile.get(lbl))) + " or ");
- }
- if (cmd.length() > 0)
- {
- cmd.setLength(cmd.length() - 4);
- }
- evalStateCommand("select *;restrict " + cmd + ";cartoon;center " + cmd);
- }
-
- public void closeViewer()
- {
- // remove listeners for all structures in viewer
- getSsm().removeStructureViewerListener(this, this.getStructureFiles());
- if (viewer != null)
- {
- viewer.dispose();
- }
- lastCommand = null;
- viewer = null;
- releaseUIResources();
- }
-
- @Override
- public void colourByChain()
- {
- colourBySequence = false;
- // TODO: colour by chain should colour each chain distinctly across all
- // visible models
- // TODO: http://issues.jalview.org/browse/JAL-628
- evalStateCommand("select *;color chain");
- }
-
- @Override
- public void colourByCharge()
- {
- colourBySequence = false;
- evalStateCommand("select *;color white;select ASP,GLU;color red;"
- + "select LYS,ARG;color blue;select CYS;color yellow");
- }
-
- /**
- * superpose the structures associated with sequences in the alignment
- * according to their corresponding positions.
- */
- public void superposeStructures(AlignmentI alignment)
- {
- superposeStructures(alignment, -1, null);
- }
-
- /**
- * superpose the structures associated with sequences in the alignment
- * according to their corresponding positions. ded)
- *
- * @param refStructure
- * - select which pdb file to use as reference (default is -1 - the
- * first structure in the alignment)
- */
- public void superposeStructures(AlignmentI alignment, int refStructure)
+ private String jmolScript(String script)
{
- superposeStructures(alignment, refStructure, null);
- }
+ Cache.log.debug(">>Jmol>> " + script);
+ String s = jmolViewer.evalStringQuiet(script); // scriptWait(script); BH
+ Cache.log.debug("<<Jmol<< " + s);
- /**
- * superpose the structures associated with sequences in the alignment
- * according to their corresponding positions. ded)
- *
- * @param refStructure
- * - select which pdb file to use as reference (default is -1 - the
- * first structure in the alignment)
- * @param hiddenCols
- * TODO
- */
- public void superposeStructures(AlignmentI alignment, int refStructure,
- HiddenColumns hiddenCols)
- {
- superposeStructures(new AlignmentI[] { alignment },
- new int[]
- { refStructure }, new HiddenColumns[] { hiddenCols });
+ return s;
}
- /**
- * {@inheritDoc}
- */
@Override
- public String superposeStructures(AlignmentI[] _alignment,
- int[] _refStructure, HiddenColumns[] _hiddenCols)
+ public List<String> executeCommand(StructureCommandI command,
+ boolean getReply)
{
- while (viewer.isScriptExecuting())
- {
- try
- {
- Thread.sleep(10);
- } catch (InterruptedException i)
- {
- }
- }
-
- /*
- * get the distinct structure files modelled
- * (a file with multiple chains may map to multiple sequences)
- */
- String[] files = getStructureFiles();
- if (!waitForFileLoad(files))
+ if (command == null)
{
return null;
}
-
- StringBuilder selectioncom = new StringBuilder(256);
- // In principle - nSeconds specifies the speed of animation for each
- // superposition - but is seems to behave weirdly, so we don't specify it.
- String nSeconds = " ";
- if (files.length > 10)
- {
- nSeconds = " 0.005 ";
- }
- else
- {
- nSeconds = " " + (2.0 / files.length) + " ";
- // if (nSeconds).substring(0,5)+" ";
- }
-
- // see JAL-1345 - should really automatically turn off the animation for
- // large numbers of structures, but Jmol doesn't seem to allow that.
- // nSeconds = " ";
- // union of all aligned positions are collected together.
- for (int a = 0; a < _alignment.length; a++)
- {
- int refStructure = _refStructure[a];
- AlignmentI alignment = _alignment[a];
- HiddenColumns hiddenCols = _hiddenCols[a];
- if (a > 0 && selectioncom.length() > 0 && !selectioncom
- .substring(selectioncom.length() - 1).equals("|"))
- {
- selectioncom.append("|");
- }
- // process this alignment
- if (refStructure >= files.length)
- {
- System.err.println(
- "Invalid reference structure value " + refStructure);
- refStructure = -1;
- }
-
- /*
- * 'matched' bit j will be set for visible alignment columns j where
- * all sequences have a residue with a mapping to the PDB structure
- */
- BitSet matched = new BitSet();
- for (int m = 0; m < alignment.getWidth(); m++)
- {
- if (hiddenCols == null || hiddenCols.isVisible(m))
- {
- matched.set(m);
- }
- }
-
- SuperposeData[] structures = new SuperposeData[files.length];
- for (int f = 0; f < files.length; f++)
- {
- structures[f] = new SuperposeData(alignment.getWidth());
- }
-
- /*
- * Calculate the superposable alignment columns ('matched'), and the
- * corresponding structure residue positions (structures.pdbResNo)
- */
- int candidateRefStructure = findSuperposableResidues(alignment,
- matched, structures);
- if (refStructure < 0)
- {
- /*
- * If no reference structure was specified, pick the first one that has
- * a mapping in the alignment
- */
- refStructure = candidateRefStructure;
- }
-
- String[] selcom = new String[files.length];
- int nmatched = matched.cardinality();
- if (nmatched < 4)
- {
- return (MessageManager.formatMessage("label.insufficient_residues",
- nmatched));
- }
-
- /*
- * generate select statements to select regions to superimpose structures
- */
- {
- // TODO extract method to construct selection statements
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- String chainCd = ":" + structures[pdbfnum].chain;
- int lpos = -1;
- boolean run = false;
- StringBuilder molsel = new StringBuilder();
- molsel.append("{");
-
- int nextColumnMatch = matched.nextSetBit(0);
- while (nextColumnMatch != -1)
- {
- int pdbResNo = structures[pdbfnum].pdbResNo[nextColumnMatch];
- if (lpos != pdbResNo - 1)
- {
- // discontinuity
- if (lpos != -1)
- {
- molsel.append(lpos);
- molsel.append(chainCd);
- molsel.append("|");
- }
- run = false;
- }
- else
- {
- // continuous run - and lpos >-1
- if (!run)
- {
- // at the beginning, so add dash
- molsel.append(lpos);
- molsel.append("-");
- }
- run = true;
- }
- lpos = pdbResNo;
- nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
- }
- /*
- * add final selection phrase
- */
- if (lpos != -1)
- {
- molsel.append(lpos);
- molsel.append(chainCd);
- molsel.append("}");
- }
- if (molsel.length() > 1)
- {
- selcom[pdbfnum] = molsel.toString();
- selectioncom.append("((");
- selectioncom.append(selcom[pdbfnum].substring(1,
- selcom[pdbfnum].length() - 1));
- selectioncom.append(" )& ");
- selectioncom.append(pdbfnum + 1);
- selectioncom.append(".1)");
- if (pdbfnum < files.length - 1)
- {
- selectioncom.append("|");
- }
- }
- else
- {
- selcom[pdbfnum] = null;
- }
- }
- }
- StringBuilder command = new StringBuilder(256);
- // command.append("set spinFps 10;\n");
-
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- if (pdbfnum == refStructure || selcom[pdbfnum] == null
- || selcom[refStructure] == null)
- {
- continue;
- }
- command.append("echo ");
- command.append("\"Superposing (");
- command.append(structures[pdbfnum].pdbId);
- command.append(") against reference (");
- command.append(structures[refStructure].pdbId);
- command.append(")\";\ncompare " + nSeconds);
- command.append("{");
- command.append(Integer.toString(1 + pdbfnum));
- command.append(".1} {");
- command.append(Integer.toString(1 + refStructure));
- // conformation=1 excludes alternate locations for CA (JAL-1757)
- command.append(
- ".1} SUBSET {(*.CA | *.P) and conformation=1} ATOMS ");
-
- // for (int s = 0; s < 2; s++)
- // {
- // command.append(selcom[(s == 0 ? pdbfnum : refStructure)]);
- // }
- command.append(selcom[pdbfnum]);
- command.append(selcom[refStructure]);
- command.append(" ROTATE TRANSLATE;\n");
- }
- if (selectioncom.length() > 0)
- {
- // TODO is performing selectioncom redundant here? is done later on
- // System.out.println("Select regions:\n" + selectioncom.toString());
- evalStateCommand("select *; cartoons off; backbone; select ("
- + selectioncom.toString() + "); cartoons; ");
- // selcom.append("; ribbons; ");
- String cmdString = command.toString();
- // System.out.println("Superimpose command(s):\n" + cmdString);
-
- evalStateCommand(cmdString);
- }
- }
- if (selectioncom.length() > 0)
- {// finally, mark all regions that were superposed.
- if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
- {
- selectioncom.setLength(selectioncom.length() - 1);
- }
- // System.out.println("Select regions:\n" + selectioncom.toString());
- evalStateCommand("select *; cartoons off; backbone; select ("
- + selectioncom.toString() + "); cartoons; ");
- // evalStateCommand("select *; backbone; select "+selcom.toString()+";
- // cartoons; center "+selcom.toString());
- }
-
- return null;
- }
-
- public void evalStateCommand(String command)
- {
+ String cmd = command.getCommand();
jmolHistory(false);
- if (lastCommand == null || !lastCommand.equals(command))
+ if (lastCommand == null || !lastCommand.equals(cmd))
{
- jmolScript(command + "\n");
+ jmolScript(cmd + "\n");
}
jmolHistory(true);
- lastCommand = command;
- }
-
- Thread colourby = null;
-
- /**
- * Sends a set of colour commands to the structure viewer
- *
- * @param colourBySequenceCommands
- */
- @Override
- protected void colourBySequence(
- final StructureMappingcommandSet[] colourBySequenceCommands)
- {
- if (colourby != null)
- {
- colourby.interrupt();
- colourby = null;
- }
- Thread colourby = new Thread(new Runnable()
- {
- @Override
- public void run()
- {
- for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
- {
- for (String cbyseq : cpdbbyseq.commands)
- {
- executeWhenReady(cbyseq);
- }
- }
- }
- });
- colourby.start();
- this.colourby = colourby;
- }
-
- /**
- * @param files
- * @param sr
- * @param viewPanel
- * @return
- */
- @Override
- protected StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
- {
- return JmolCommands.getColourBySequenceCommand(getSsm(), files,
- getSequence(), sr, viewPanel);
- }
-
- /**
- * @param command
- */
- protected void executeWhenReady(String command)
- {
- evalStateCommand(command);
+ lastCommand = cmd;
+ return null;
}
public void createImage(String file, String type, int quality)
return null;
}
- public Color getColour(int atomIndex, int pdbResNum, String chain,
- String pdbfile)
- {
- if (getModelNum(pdbfile) < 0)
- {
- return null;
- }
- // TODO: verify atomIndex is selecting correct model.
- // return new Color(viewer.getAtomArgb(atomIndex)); Jmol 12.2.4
- int colour = viewer.ms.at[atomIndex].atomPropertyInt(T.color);
- return new Color(colour);
- }
-
- /**
- * instruct the Jalview binding to update the pdbentries vector if necessary
- * prior to matching the jmol view's contents to the list of structure files
- * Jalview knows about.
- */
- public abstract void refreshPdbEntries();
-
- private int getModelNum(String modelFileName)
- {
- String[] mfn = getStructureFiles();
- if (mfn == null)
- {
- return -1;
- }
- for (int i = 0; i < mfn.length; i++)
- {
- if (mfn[i].equalsIgnoreCase(modelFileName))
- {
- return i;
- }
- }
- return -1;
- }
-
/**
* map between index of model filename returned from getPdbFile and the first
* index of models from this file in the viewer. Note - this is not trimmed -
@Override
public synchronized String[] getStructureFiles()
{
- List<String> mset = new ArrayList<>();
- if (viewer == null)
+ if (jmolViewer == null)
{
return new String[0];
}
if (modelFileNames == null)
{
- int modelCount = viewer.ms.mc;
+ int modelCount = jmolViewer.ms.mc;
String filePath = null;
+ List<String> mset = new ArrayList<>();
for (int i = 0; i < modelCount; ++i)
{
- filePath = viewer.ms.getModelFileName(i);
- if (!mset.contains(filePath))
+ /*
+ * defensive check for null as getModelFileName can return null
+ * even when model count ms.mc is > 0
+ */
+ filePath = jmolViewer.ms.getModelFileName(i);
+ if (filePath != null && !mset.contains(filePath))
{
mset.add(filePath);
}
}
- modelFileNames = mset.toArray(new String[mset.size()]);
+ if (!mset.isEmpty())
+ {
+ modelFileNames = mset.toArray(new String[mset.size()]);
+ }
}
return modelFileNames;
public void highlightAtom(int atomIndex, int pdbResNum, String chain,
String pdbfile)
{
- if (modelFileNames == null)
- {
- return;
- }
-
- // look up file model number for this pdbfile
- int mdlNum = 0;
- // may need to adjust for URLencoding here - we don't worry about that yet.
- while (mdlNum < modelFileNames.length
- && !pdbfile.equals(modelFileNames[mdlNum]))
- {
- mdlNum++;
- }
- if (mdlNum == modelFileNames.length)
+ String modelId = getModelIdForFile(pdbfile);
+ if (modelId.isEmpty())
{
return;
}
jmolHistory(false);
+ StringBuilder selection = new StringBuilder(32);
StringBuilder cmd = new StringBuilder(64);
- cmd.append("select " + pdbResNum); // +modelNum
-
- resetLastRes.append("select " + pdbResNum); // +modelNum
-
- cmd.append(":");
- resetLastRes.append(":");
+ selection.append("select ").append(String.valueOf(pdbResNum));
+ selection.append(":");
if (!chain.equals(" "))
{
- cmd.append(chain);
- resetLastRes.append(chain);
- }
- {
- cmd.append(" /" + (mdlNum + 1));
- resetLastRes.append("/" + (mdlNum + 1));
+ selection.append(chain);
}
- cmd.append(";wireframe 100;" + cmd.toString() + " and not hetero;");
+ selection.append(" /").append(modelId);
- resetLastRes.append(";wireframe 0;" + resetLastRes.toString()
- + " and not hetero; spacefill 0;");
+ cmd.append(selection).append(";wireframe 100;").append(selection)
+ .append(" and not hetero;").append("spacefill 200;select none");
- cmd.append("spacefill 200;select none");
+ resetLastRes.append(selection).append(";wireframe 0;").append(selection)
+ .append(" and not hetero; spacefill 0;");
jmolScript(cmd.toString());
jmolHistory(true);
-
}
- boolean debug = true;
+ private boolean debug = true;
private void jmolHistory(boolean enable)
{
// Then, construct pass a reader for the string to Jmol.
// ((org.jmol.Viewer.Viewer) viewer).loadModelFromFile(fullPathName,
// fileName, null, reader, false, null, null, 0);
- viewer.openStringInline(string);
+ jmolViewer.openStringInline(string);
}
protected void mouseOverStructure(int atomIndex, final String strInfo)
chainId = " ";
}
- String pdbfilename = modelFileNames[frameNo]; // default is first or current
- // model
+ String pdbfilename = modelFileNames[0]; // default is first model
if (mdlSep > -1)
{
if (chainSeparator1 == -1)
if (pdbfilename == null)
{
- pdbfilename = new File(viewer.ms.getModelFileName(mnumber))
+ pdbfilename = new File(jmolViewer.ms.getModelFileName(mnumber))
.getAbsolutePath();
}
}
sb.append(";set hoverLabel \"").append(toks.nextToken()).append(" ")
.append(toks.nextToken());
sb.append("|").append(label).append("\"");
- evalStateCommand(sb.toString());
+ executeCommand(new StructureCommand(sb.toString()), false);
}
}
}
else
{
- viewer.evalString("select " + picked + ";label off");
+ jmolViewer.evalString("select " + picked + ";label off");
atomsPicked.removeElement(picked);
}
jmolHistory(true);
fileLoadingError = null;
String[] oldmodels = modelFileNames;
modelFileNames = null;
- chainNames = new ArrayList<>();
- chainFile = new Hashtable<>();
boolean notifyLoaded = false;
String[] modelfilenames = getStructureFiles();
+ if (modelfilenames == null)
+ {
+ // Jmol is still loading files!
+ return;
+ }
// first check if we've lost any structures
if (oldmodels != null && oldmodels.length > 0)
{
// calculate essential attributes for the pdb data imported inline.
// prolly need to resolve modelnumber properly - for now just use our
// 'best guess'
- pdbfile = viewer.getData(
+ pdbfile = jmolViewer.getData(
"" + (1 + _modelFileNameMap[modelnum]) + ".0", "PDB");
}
// search pdbentries and sequences to find correct pdbentry for this
}
if (matches)
{
- // add an entry for every chain in the model
- for (int i = 0; i < pdb.getChains().size(); i++)
- {
- String chid = new String(
- pdb.getId() + ":" + pdb.getChains().elementAt(i).id);
- chainFile.put(chid, fileName);
- chainNames.add(chid);
- }
+ stashFoundChains(pdb, fileName);
notifyLoaded = true;
}
}
// this is a foreign pdb file that jalview doesn't know about - add
// it to the dataset and try to find a home - either on a matching
// sequence or as a new sequence.
- String pdbcontent = viewer.getData("/" + (modelnum + 1) + ".1",
+ String pdbcontent = jmolViewer.getData("/" + (modelnum + 1) + ".1",
"PDB");
// parse pdb file into a chain, etc.
// locate best match for pdb in associated views and add mapping to
setLoadingFromArchive(false);
}
- @Override
- public List<String> getChainNames()
- {
- return chainNames;
- }
-
protected IProgressIndicator getIProgressIndicator()
{
return null;
}
- @Override
- public void setJalviewColourScheme(ColourSchemeI cs)
- {
- colourBySequence = false;
-
- if (cs == null)
- {
- return;
- }
-
- jmolHistory(false);
- StringBuilder command = new StringBuilder(128);
- command.append("select *;color white;");
- List<String> residueSet = ResidueProperties.getResidues(isNucleotide(),
- false);
- for (String resName : residueSet)
- {
- char res = resName.length() == 3
- ? ResidueProperties.getSingleCharacterCode(resName)
- : resName.charAt(0);
- Color col = cs.findColour(res, 0, null, null, 0f);
- command.append("select " + resName + ";color[" + col.getRed() + ","
- + col.getGreen() + "," + col.getBlue() + "];");
- }
-
- evalStateCommand(command.toString());
- jmolHistory(true);
- }
-
public void showHelp()
{
showUrl("http://wiki.jmol.org"
public abstract void showUrl(String url, String target);
/**
- * called when the binding thinks the UI needs to be refreshed after a Jmol
- * state change. this could be because structures were loaded, or because an
- * error has occured.
- */
- public abstract void refreshGUI();
-
- /**
* called to show or hide the associated console window container.
*
* @param show
{
commandOptions = "";
}
- viewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
+ jmolViewer = (Viewer) JmolViewer.allocateViewer(renderPanel,
(jmolfileio ? new SmarterJmolAdapter() : null),
htmlName + ((Object) this).toString(), documentBase, codeBase,
commandOptions, this);
- viewer.setJmolStatusListener(this); // extends JmolCallbackListener
+ jmolViewer.setJmolStatusListener(this); // extends JmolCallbackListener
try
{
protected org.jmol.api.JmolAppConsoleInterface console = null;
@Override
- public void setBackgroundColour(java.awt.Color col)
- {
- jmolHistory(false);
- jmolScript("background [" + col.getRed() + "," + col.getGreen() + ","
- + col.getBlue() + "];");
- jmolHistory(true);
- }
-
- private String jmolScript(String script)
- {
-
- System.err.println(">>Jmol>> " + script);
-
- String s = viewer.scriptWait(script);
-
- System.err.println("<<Jmol<< " + s);
-
- return s;
- }
-
- @Override
public int[] resizeInnerPanel(String data)
{
// Jalview doesn't honour resize panel requests
showConsole(false);
}
+ @Override
+ protected String getModelIdForFile(String pdbFile)
+ {
+ if (modelFileNames == null)
+ {
+ return "";
+ }
+ for (int i = 0; i < modelFileNames.length; i++)
+ {
+ if (modelFileNames[i].equalsIgnoreCase(pdbFile))
+ {
+ return String.valueOf(i + 1);
+ }
+ }
+ return "";
+ }
+
+ @Override
+ protected ViewerType getViewerType()
+ {
+ return ViewerType.JMOL;
+ }
+
+ @Override
+ protected String getModelId(int pdbfnum, String file)
+ {
+ return String.valueOf(pdbfnum + 1);
+ }
+
+ /**
+ * Returns ".spt" - the Jmol session file extension
+ *
+ * @return
+ * @see https://chemapps.stolaf.edu/jmol/docs/#writemodel
+ */
+ @Override
+ public String getSessionFileExtension()
+ {
+ return ".spt";
+ }
+
+ @Override
+ public void selectionChanged(BS arg0)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
+ {
+ return new jalview.gui.SequenceRenderer(avp.getAlignViewport());
+ }
+
+ @Override
+ public String getHelpURL()
+ {
+ return "http://wiki.jmol.org"; // BH 2018
+ }
}
*/
package jalview.ext.jmol;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.api.FeatureRenderer;
import jalview.datamodel.HiddenColumns;
import jalview.datamodel.SequenceI;
import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsBase;
import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
import jalview.structure.StructureSelectionManager;
-
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.List;
+import jalview.util.Comparison;
+import jalview.util.Platform;
/**
- * Routines for generating Jmol commands for Jalview/Jmol binding another
- * cruisecontrol test.
+ * Routines for generating Jmol commands for Jalview/Jmol binding
*
* @author JimP
*
*/
-public class JmolCommands
+public class JmolCommands extends StructureCommandsBase
{
+ private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
+ "select *; cartoons off; backbone");
+
+ private static final StructureCommand FOCUS_VIEW = new StructureCommand("zoom 0");
+
+ private static final StructureCommand COLOUR_ALL_WHITE = new StructureCommand(
+ "select *;color white;");
+
+ private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
+ "select *;color white;select ASP,GLU;color red;"
+ + "select LYS,ARG;color blue;select CYS;color yellow");
+
+ private static final StructureCommandI COLOUR_BY_CHAIN = new StructureCommand(
+ "select *;color chain");
+
+ private static final String PIPE = "|";
+
+ private static final String HYPHEN = "-";
+
+ private static final String COLON = ":";
+
+ private static final String SLASH = "/";
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return
+ */
+ @Override
+ public int getModelStartNo()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns a string representation of the given colour suitable for inclusion
+ * in Jmol commands
+ *
+ * @param c
+ * @return
+ */
+ protected String getColourString(Color c)
+ {
+ return c == null ? null
+ : String.format("[%d,%d,%d]", c.getRed(), c.getGreen(),
+ c.getBlue());
+ }
+
+ @Override
+ public StructureCommandI colourByChain()
+ {
+ return COLOUR_BY_CHAIN;
+ }
+
+ @Override
+ public List<StructureCommandI> colourByCharge()
+ {
+ return Arrays.asList(COLOUR_BY_CHARGE);
+ }
+
+ @Override
+ public List<StructureCommandI> colourByResidues(Map<String, Color> colours)
+ {
+ List<StructureCommandI> cmds = super.colourByResidues(colours);
+ cmds.add(0, COLOUR_ALL_WHITE);
+ return cmds;
+ }
+
+ @Override
+ public StructureCommandI setBackgroundColour(Color col)
+ {
+ return new StructureCommand("background " + getColourString(col));
+ }
+
+ @Override
+ public StructureCommandI focusView()
+ {
+ return FOCUS_VIEW;
+ }
+
+ @Override
+ public List<StructureCommandI> showChains(List<String> toShow)
+ {
+ StringBuilder atomSpec = new StringBuilder(128);
+ boolean first = true;
+ for (String chain : toShow)
+ {
+ String[] tokens = chain.split(":");
+ if (tokens.length == 2)
+ {
+ if (!first)
+ {
+ atomSpec.append(" or ");
+ }
+ first = false;
+ atomSpec.append(":").append(tokens[1]).append(" /").append(tokens[0]);
+ }
+ }
+
+ String spec = atomSpec.toString();
+ String command = "select *;restrict " + spec + ";cartoon;center "
+ + spec;
+ return Arrays.asList(new StructureCommand(command));
+ }
+
+ /**
+ * Returns a command to superpose atoms in {@code atomSpec} to those in
+ * {@code refAtoms}, restricted to alpha carbons only (Phosphorous for rna).
+ * For example
+ *
+ * <pre>
+ * compare {2.1} {1.1} SUBSET {(*.CA | *.P) and conformation=1}
+ * ATOMS {1-87:A}{2-54:A|61-94:A} ROTATE TRANSLATE 1.0;
+ * </pre>
+ *
+ * where {@code conformation=1} excludes ALTLOC atom locations, and 1.0 is the
+ * time in seconds to animate the action. For this example, atoms in model 2
+ * are moved towards atoms in model 1.
+ * <p>
+ * The two atomspecs should each be for one model only, but may have more than
+ * one chain. The number of atoms specified should be the same for both
+ * models, though if not, Jmol may make a 'best effort' at superposition.
+ *
+ * @see https://chemapps.stolaf.edu/jmol/docs/#compare
+ */
+ @Override
+ public List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
+ AtomSpecModel atomSpec)
+ {
+ StringBuilder sb = new StringBuilder(64);
+ String refModel = refAtoms.getModels().iterator().next();
+ String model2 = atomSpec.getModels().iterator().next();
+ sb.append(String.format("compare {%s.1} {%s.1}", model2, refModel));
+ sb.append(" SUBSET {(*.CA | *.P) and conformation=1} ATOMS {");
+
+ /*
+ * command examples don't include modelspec with atoms, getAtomSpec does;
+ * it works, so leave it as it is for simplicity
+ */
+ sb.append(getAtomSpec(atomSpec, true)).append("}{");
+ sb.append(getAtomSpec(refAtoms, true)).append("}");
+ sb.append(" ROTATE TRANSLATE ");
+ sb.append(getCommandSeparator());
+
+ /*
+ * show residues used for superposition as ribbon
+ */
+ sb.append("select ").append(getAtomSpec(atomSpec, false)).append("|");
+ sb.append(getAtomSpec(refAtoms, false)).append(getCommandSeparator())
+ .append("cartoons");
+
+ return Arrays.asList(new StructureCommand(sb.toString()));
+ }
+
+ @Override
+ public StructureCommandI openCommandFile(String path)
+ {
+ /*
+ * https://chemapps.stolaf.edu/jmol/docs/#script
+ * not currently used in Jalview
+ */
+ return new StructureCommand("script " + path);
+ }
+
+ @Override
+ public StructureCommandI saveSession(String filepath)
+ {
+ /*
+ * https://chemapps.stolaf.edu/jmol/docs/#writemodel
+ */
+ return new StructureCommand("write STATE \"" + filepath + "\"");
+ }
+
+ @Override
+ protected StructureCommandI colourResidues(String atomSpec, Color colour)
+ {
+ StringBuilder sb = new StringBuilder(atomSpec.length()+20);
+ sb.append("select ").append(atomSpec).append(getCommandSeparator())
+ .append("color").append(getColourString(colour));
+ return new StructureCommand(sb.toString());
+ }
+
+ @Override
+ protected String getResidueSpec(String residue)
+ {
+ return residue;
+ }
/**
- * Jmol utility which constructs the commands to colour chains by the given
- * alignment
+ * Generates a Jmol atomspec string like
+ *
+ * <pre>
+ * 2-5:A/1.1,8:A/1.1,5-10:B/2.1
+ * </pre>
*
- * @returns Object[] { Object[] { <model being coloured>,
+ * Parameter {@code alphaOnly} is not used here - this restriction is made by
+ * a separate clause in the {@code compare} (superposition) command.
+ */
+ @Override
+ public String getAtomSpec(AtomSpecModel model, boolean alphaOnly)
+ {
+ StringBuilder sb = new StringBuilder(128);
+
+ boolean first = true;
+ for (String modelNo : model.getModels())
+ {
+ for (String chain : model.getChains(modelNo))
+ {
+ for (int[] range : model.getRanges(modelNo, chain))
+ {
+ if (!first)
+ {
+ sb.append(PIPE);
+ }
+ first = false;
+ if (range[0] == range[1])
+ {
+ sb.append(range[0]);
+ }
+ else
+ {
+ sb.append(range[0]).append(HYPHEN).append(range[1]);
+ }
+ sb.append(COLON).append(chain.trim()).append(SLASH);
+ sb.append(String.valueOf(modelNo)).append(".1");
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public List<StructureCommandI> showBackbone()
+ {
+ return Arrays.asList(SHOW_BACKBONE);
+ }
+
+ @Override
+ public StructureCommandI loadFile(String file)
+ {
+ // https://chemapps.stolaf.edu/jmol/docs/#loadfiles
+ return new StructureCommand("load FILES \"" +
+ Platform.escapeBackslashes(file) + "\"");
+ }
+
+ /**
+ * Obsolete method, only referenced from
+ * jalview.javascript.MouseOverStructureListener
*
+ * @param ssm
+ * @param files
+ * @param sequence
+ * @param sr
+ * @param viewPanel
+ * @return
*/
- public static StructureMappingcommandSet[] getColourBySequenceCommand(
- StructureSelectionManager ssm, String[] files,
- SequenceI[][] sequence, SequenceRenderer sr,
+ @Deprecated
+ public String[] colourBySequence(StructureSelectionManager ssm,
+ String[] files, SequenceI[][] sequence, SequenceRenderer sr,
AlignmentViewPanel viewPanel)
{
+ // TODO delete method
+
FeatureRenderer fr = viewPanel.getFeatureRenderer();
FeatureColourFinder finder = new FeatureColourFinder(fr);
AlignViewportI viewport = viewPanel.getAlignViewport();
HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
AlignmentI al = viewport.getAlignment();
- List<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
+ List<String> cset = new ArrayList<>();
for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
{
StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
- StringBuilder command = new StringBuilder();
- StructureMappingcommandSet smc;
- ArrayList<String> str = new ArrayList<String>();
+ StringBuilder command = new StringBuilder(128);
+ List<String> str = new ArrayList<>();
if (mapping == null || mapping.length < 1)
{
for (int r = 0; r < asp.getLength(); r++)
{
// no mapping to gaps in sequence
- if (jalview.util.Comparison.isGap(asp.getCharAt(r)))
+ if (Comparison.isGap(asp.getCharAt(r)))
{
continue;
}
col = Color.GRAY;
}
- // todo JAL-3152 handle 'no chain' case without errors
- boolean hasChain = true || mapping[m].getChain() != " ";
- String chainSpec = hasChain
+ String newSelcom = (mapping[m].getChain() != " "
? ":" + mapping[m].getChain()
- : "";
- String newSelcom = chainSpec + "/" + (pdbfnum + 1) + ".1" + ";color["
- + col.getRed() + "," + col.getGreen() + ","
- + col.getBlue() + "]";
+ : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
+ + getColourString(col);
if (command.length() > newSelcom.length() && command
.substring(command.length() - newSelcom.length())
.equals(newSelcom))
str.add(command.toString());
command.setLength(0);
}
- // Finally, add the command set ready to be returned.
- cset.add(new StructureMappingcommandSet(JmolCommands.class,
- files[pdbfnum], str.toArray(new String[str.size()])));
+ cset.addAll(str);
}
- return cset.toArray(new StructureMappingcommandSet[cset.size()]);
+ return cset.toArray(new String[cset.size()]);
}
- public static StringBuilder condenseCommand(StringBuilder command, int pos)
+ /**
+ * Helper method
+ *
+ * @param command
+ * @param pos
+ * @return
+ */
+ @Deprecated
+ private static StringBuilder condenseCommand(
+ StringBuilder command,
+ int pos)
{
// work back to last 'select'
return sb;
}
+ @Override
+ public StructureCommandI openSession(String filepath)
+ {
+ return loadFile(filepath);
+ }
}
--- /dev/null
+package jalview.ext.pymol;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsBase;
+
+/**
+ * A class that generates commands to send to PyMol over its XML-RPC interface.
+ * <p>
+ * Note that because the xml-rpc interface can only accept one command at a
+ * time, we can't concatenate commands, and must instead form and send them
+ * individually.
+ *
+ * @see https://pymolwiki.org/index.php/Category:Commands
+ * @see https://pymolwiki.org/index.php/RPC
+ */
+public class PymolCommands extends StructureCommandsBase
+{
+ private static final StructureCommand COLOUR_BY_CHAIN = new StructureCommand("spectrum", "chain");
+
+ private static final List<StructureCommandI> COLOR_BY_CHARGE = new ArrayList<>();
+
+ private static final List<StructureCommandI> SHOW_BACKBONE = new ArrayList<>();
+
+ static {
+ COLOR_BY_CHARGE.add(new StructureCommand("color", "white", "*"));
+ COLOR_BY_CHARGE
+ .add(new StructureCommand("color", "red", "resn ASP resn GLU"));
+ COLOR_BY_CHARGE.add(
+ new StructureCommand("color", "blue", "resn LYS resn ARG"));
+ COLOR_BY_CHARGE
+ .add(new StructureCommand("color", "yellow", "resn CYS"));
+ SHOW_BACKBONE.add(new StructureCommand("hide", "everything"));
+ SHOW_BACKBONE.add(new StructureCommand("show", "ribbon"));
+ }
+
+ @Override
+ public StructureCommandI colourByChain()
+ {
+ return COLOUR_BY_CHAIN;
+ }
+
+ @Override
+ public List<StructureCommandI> colourByCharge()
+ {
+ return COLOR_BY_CHARGE;
+ }
+
+ @Override
+ public StructureCommandI setBackgroundColour(Color col)
+ {
+ // https://pymolwiki.org/index.php/Bg_Color
+ return new StructureCommand("bg_color", getColourString(col));
+ }
+
+ /**
+ * Returns a colour formatted suitable for use in viewer command syntax. For
+ * example, red is {@code "0xff0000"}.
+ *
+ * @param c
+ * @return
+ */
+ protected String getColourString(Color c)
+ {
+ return String.format("0x%02x%02x%02x", c.getRed(), c.getGreen(),
+ c.getBlue());
+ }
+
+ @Override
+ public StructureCommandI focusView()
+ {
+ // TODO what?
+ return null;
+ }
+
+ @Override
+ public List<StructureCommandI> showChains(List<String> toShow)
+ {
+ // https://pymolwiki.org/index.php/Show
+ List<StructureCommandI> commands = new ArrayList<>();
+ commands.add(new StructureCommand("hide", "everything"));
+ commands.add(new StructureCommand("show", "lines"));
+ StringBuilder chains = new StringBuilder();
+ for (String chain : toShow)
+ {
+ chains.append(" chain ").append(chain);
+ }
+ commands.add(new StructureCommand("show", "cartoon", chains.toString()));
+ return commands;
+ }
+
+ @Override
+ public List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
+ AtomSpecModel atomSpec)
+ {
+ // https://pymolwiki.org/index.php/Super
+ List<StructureCommandI> commands = new ArrayList<>();
+ String refAtomsAlphaOnly = getAtomSpec(refAtoms, true);
+ String atomSpec2AlphaOnly = getAtomSpec(atomSpec, true);
+ commands.add(new StructureCommand("super", refAtomsAlphaOnly,
+ atomSpec2AlphaOnly));
+
+ /*
+ * and show superposed residues as cartoon
+ */
+ String refAtomsAll = getAtomSpec(refAtoms, false);
+ String atomSpec2All = getAtomSpec(atomSpec, false);
+ commands.add(new StructureCommand("show", "cartoon",
+ refAtomsAll + " " + atomSpec2All));
+
+ return commands;
+ }
+
+ @Override
+ public StructureCommandI openCommandFile(String path)
+ {
+ // https://pymolwiki.org/index.php/Run
+ return new StructureCommand("run", path); // should be .pml
+ }
+
+ @Override
+ public StructureCommandI saveSession(String filepath)
+ {
+ // https://pymolwiki.org/index.php/Save#EXAMPLES
+ return new StructureCommand("save", filepath); // should be .pse
+ }
+
+ /**
+ * Returns a selection string in PyMOL 'selection macro' format:
+ *
+ * <pre>
+ * modelId// chain/residues/
+ * </pre>
+ *
+ * If more than one chain, makes a selection expression for each, and they are
+ * separated by spaces.
+ *
+ * @see https://pymolwiki.org/index.php/Selection_Macros
+ */
+ @Override
+ public String getAtomSpec(AtomSpecModel model, boolean alphaOnly)
+ {
+ StringBuilder sb = new StringBuilder(64);
+ boolean first = true;
+ for (String modelId : model.getModels())
+ {
+ for (String chain : model.getChains(modelId))
+ {
+ if (!first)
+ {
+ sb.append(" ");
+ }
+ first = false;
+ List<int[]> rangeList = model.getRanges(modelId, chain);
+ chain = chain.trim();
+ sb.append(modelId).append("//").append(chain).append("/");
+ boolean firstRange = true;
+ for (int[] range : rangeList)
+ {
+ if (!firstRange)
+ {
+ sb.append("+");
+ }
+ firstRange = false;
+ sb.append(String.valueOf(range[0]));
+ if (range[0] != range[1])
+ {
+ sb.append("-").append(String.valueOf(range[1]));
+ }
+ }
+ sb.append("/");
+ if (alphaOnly)
+ {
+ sb.append("CA");
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public List<StructureCommandI> showBackbone()
+ {
+ return SHOW_BACKBONE;
+ }
+
+ @Override
+ protected StructureCommandI colourResidues(String atomSpec, Color colour)
+ {
+ // https://pymolwiki.org/index.php/Color
+ return new StructureCommand("color", getColourString(colour), atomSpec);
+ }
+
+ @Override
+ protected String getResidueSpec(String residue)
+ {
+ // https://pymolwiki.org/index.php/Selection_Algebra
+ return "resn " + residue;
+ }
+
+ @Override
+ public StructureCommandI loadFile(String file)
+ {
+ return new StructureCommand("load", file);
+ }
+
+ /**
+ * Overrides the default implementation (which generates concatenated
+ * commands) to generate one per colour (because the XML-RPC interface to
+ * PyMOL only accepts one command at a time)
+ *
+ * @param colourMap
+ * @return
+ */
+ @Override
+ public List<StructureCommandI> colourBySequence(
+ Map<Object, AtomSpecModel> colourMap)
+ {
+ List<StructureCommandI> commands = new ArrayList<>();
+ for (Object key : colourMap.keySet())
+ {
+ Color colour = (Color) key;
+ final AtomSpecModel colourData = colourMap.get(colour);
+ commands.add(getColourCommand(colourData, colour));
+ }
+
+ return commands;
+ }
+
+ /**
+ * Returns a viewer command to set the given atom property value on atoms
+ * specified by the AtomSpecModel, for example
+ *
+ * <pre>
+ * iterate 4zho//B/12-34,48-55/CA,jv_chain='primary'
+ * </pre>
+ *
+ * @param attributeName
+ * @param attributeValue
+ * @param atomSpecModel
+ * @return
+ */
+ protected StructureCommandI setAttribute(String attributeName,
+ String attributeValue,
+ AtomSpecModel atomSpecModel)
+ {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("p.").append(attributeName).append("='")
+ .append(attributeValue).append("'");
+ String atomSpec = getAtomSpec(atomSpecModel, false);
+ return new StructureCommand("iterate", atomSpec, sb.toString());
+ }
+
+ /**
+ * Traverse the map of features/values/models/chains/positions to construct a
+ * list of 'set property' commands (one per distinct feature type and value).
+ * The values are stored in the 'p' dictionary of user-defined properties of
+ * each atom.
+ * <p>
+ * The format of each command is
+ *
+ * <pre>
+ * <blockquote> iterate atomspec, p.featureName='value'
+ * e.g. iterate 4zho//A/23,28-29/CA, p.jv_Metal='Fe'
+ * </blockquote>
+ * </pre>
+ *
+ * @param featureMap
+ * @return
+ */
+ @Override
+ public List<StructureCommandI> setAttributes(
+ Map<String, Map<Object, AtomSpecModel>> featureMap)
+ {
+ List<StructureCommandI> commands = new ArrayList<>();
+ for (String featureType : featureMap.keySet())
+ {
+ String attributeName = makeAttributeName(featureType);
+
+ /*
+ * todo: clear down existing attributes for this feature?
+ */
+ // commands.add(new StructureCommand("iterate", "all",
+ // "p."+attributeName+"='None'"); //?
+
+ Map<Object, AtomSpecModel> values = featureMap.get(featureType);
+ for (Object value : values.keySet())
+ {
+ /*
+ * for each distinct value recorded for this feature type,
+ * add a command to set the attribute on the mapped residues
+ * Put values in single quotes, encoding any embedded single quotes
+ */
+ AtomSpecModel atomSpecModel = values.get(value);
+ String featureValue = value.toString();
+ featureValue = featureValue.replaceAll("\\'", "'");
+ StructureCommandI cmd = setAttribute(attributeName, featureValue,
+ atomSpecModel);
+ commands.add(cmd);
+ }
+ }
+
+ return commands;
+ }
+
+ @Override
+ public StructureCommandI openSession(String filepath)
+ {
+ // https://pymolwiki.org/index.php/Load
+ // this version of the command has no dependency on file extension
+ return new StructureCommand("load", filepath, "", "0", "pse");
+ }
+
+}
--- /dev/null
+package jalview.ext.pymol;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.HttpURLConnection;
+import java.net.SocketException;
+import java.net.URL;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+import jalview.bin.Cache;
+import jalview.gui.Preferences;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+
+public class PymolManager
+{
+ private static final int RPC_REPLY_TIMEOUT_MS = 15000;
+
+ private static final int CONNECTION_TIMEOUT_MS = 100;
+
+ private static final String POST1 = "<methodCall><methodName>";
+
+ private static final String POST2 = "</methodName><params>";
+
+ private static final String POST3 = "</params></methodCall>";
+
+ private Process pymolProcess;
+
+ private int pymolXmlRpcPort;
+
+ /**
+ * Returns a list of paths to try for the PyMOL executable. Any user
+ * preference is placed first, otherwise 'standard' paths depending on the
+ * operating system.
+ *
+ * @return
+ */
+ public static List<String> getPymolPaths()
+ {
+ return getPymolPaths(System.getProperty("os.name"));
+ }
+
+ /**
+ * Returns a list of paths to try for the PyMOL executable. Any user
+ * preference is placed first, otherwise 'standard' paths depending on the
+ * operating system.
+ *
+ * @param os
+ * operating system as reported by environment variable
+ * {@code os.name}
+ * @return
+ */
+ protected static List<String> getPymolPaths(String os)
+ {
+ List<String> pathList = new ArrayList<>();
+
+ String userPath = Cache
+ .getDefault(Preferences.PYMOL_PATH, null);
+ if (userPath != null)
+ {
+ pathList.add(userPath);
+ }
+
+ /*
+ * add default installation paths
+ */
+ String pymol = "PyMOL";
+ if (os.startsWith("Linux"))
+ {
+ pathList.add("/usr/local/pymol/bin/" + pymol);
+ pathList.add("/usr/local/bin/" + pymol);
+ pathList.add("/usr/bin/" + pymol);
+ pathList.add(System.getProperty("user.home") + "/opt/bin/" + pymol);
+ }
+ else if (os.startsWith("Windows"))
+ {
+ // todo Windows installation path(s)
+ }
+ else if (os.startsWith("Mac"))
+ {
+ pathList.add("/Applications/PyMOL.app/Contents/MacOS/" + pymol);
+ }
+ return pathList;
+ }
+
+ public boolean isPymolLaunched()
+ {
+ // TODO pull up generic methods for external viewer processes
+ boolean launched = false;
+ if (pymolProcess != null)
+ {
+ try
+ {
+ pymolProcess.exitValue();
+ // if we get here, process has ended
+ } catch (IllegalThreadStateException e)
+ {
+ // ok - not yet terminated
+ launched = true;
+ }
+ }
+ return launched;
+ }
+
+ public void exitPymol()
+ {
+ if (isPymolLaunched() && pymolProcess != null)
+ {
+ sendCommand(new StructureCommand("quit"), false);
+ }
+ pymolProcess = null;
+ // currentModelsMap.clear();
+ this.pymolXmlRpcPort = 0;
+ }
+
+ /**
+ * Sends the command to Pymol; if requested, tries to get and return any
+ * replies, else returns null
+ *
+ * @param command
+ * @param getReply
+ * @return
+ */
+ public List<String> sendCommand(StructureCommandI command,
+ boolean getReply)
+ {
+ String postBody = getPostRequest(command);
+ // System.out.println(postBody);// debug
+ String rpcUrl = "http://127.0.0.1:" + this.pymolXmlRpcPort;
+ PrintWriter out = null;
+ BufferedReader in = null;
+ List<String> result = getReply ? new ArrayList<>() : null;
+
+ try
+ {
+ URL realUrl = new URL(rpcUrl);
+ HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("content-type", "text/xml");
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ out = new PrintWriter(conn.getOutputStream());
+ out.print(postBody);
+ out.flush();
+ int rc = conn.getResponseCode();
+ if (rc != HttpURLConnection.HTTP_OK)
+ {
+ Cache.log.error(
+ String.format("Error status from %s: %d", rpcUrl, rc));
+ return result;
+ }
+
+ InputStream inputStream = conn.getInputStream();
+ if (getReply)
+ {
+ in = new BufferedReader(new InputStreamReader(inputStream));
+ String line;
+ while ((line = in.readLine()) != null)
+ {
+ result.add(line);
+ }
+ }
+ } catch (SocketException e)
+ {
+ // thrown when 'quit' command is sent to PyMol
+ Cache.log.warn(String.format("Request to %s returned %s", rpcUrl,
+ e.toString()));
+ } catch (Exception e)
+ {
+ e.printStackTrace();
+ } finally
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Builds the body of the XML-RPC format POST request to execute the command
+ *
+ * @param command
+ * @return
+ */
+ static String getPostRequest(StructureCommandI command)
+ {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append(POST1).append(command.getCommand()).append(POST2);
+ if (command.hasParameters())
+ {
+ for (String p : command.getParameters())
+ {
+ /*
+ * for now assuming all are string - <string> element is optional
+ * refactor in future if other data types needed
+ * https://www.tutorialspoint.com/xml-rpc/xml_rpc_data_model.htm
+ */
+ sb.append("<parameter><value>").append(p)
+ .append("</value></parameter>");
+ }
+ }
+ sb.append(POST3);
+ return sb.toString();
+ }
+
+ public boolean launchPymol()
+ {
+ // todo pull up much of this
+ // Do nothing if already launched
+ if (isPymolLaunched())
+ {
+ return true;
+ }
+
+ String error = "Error message: ";
+ for (String pymolPath : getPymolPaths())
+ {
+ try
+ {
+ // ensure symbolic links are resolved
+ pymolPath = Paths.get(pymolPath).toRealPath().toString();
+ File path = new File(pymolPath);
+ // uncomment the next line to simulate Pymol not installed
+ // path = new File(pymolPath + "x");
+ if (!path.canExecute())
+ {
+ error += "File '" + path + "' does not exist.\n";
+ continue;
+ }
+ List<String> args = new ArrayList<>();
+ args.add(pymolPath);
+ args.add("-R"); // https://pymolwiki.org/index.php/RPC
+ ProcessBuilder pb = new ProcessBuilder(args);
+ pymolProcess = pb.start();
+ error = "";
+ break;
+ } catch (Exception e)
+ {
+ // pPymol could not be started using this path
+ error += e.getMessage();
+ }
+ }
+ if (error.length() == 0)
+ {
+ this.pymolXmlRpcPort = getPortNumber();
+ System.out.println(
+ "PyMOL XMLRPC started on port " + pymolXmlRpcPort);
+ return (pymolXmlRpcPort > 0);
+ }
+
+ // logger.warn(error);
+ return false;
+ }
+
+ private int getPortNumber()
+ {
+ // TODO pull up most of this!
+ int port = 0;
+ InputStream readChan = pymolProcess.getInputStream();
+ BufferedReader lineReader = new BufferedReader(
+ new InputStreamReader(readChan));
+ StringBuilder responses = new StringBuilder();
+ try
+ {
+ String response = lineReader.readLine();
+ while (response != null)
+ {
+ responses.append("\n" + response);
+ // expect: xml-rpc server running on host localhost, port 9123
+ if (response.contains("xml-rpc"))
+ {
+ String[] tokens = response.split(" ");
+ for (int i = 0; i < tokens.length - 1; i++)
+ {
+ if ("port".equals(tokens[i]))
+ {
+ port = Integer.parseInt(tokens[i + 1]);
+ break;
+ }
+ }
+ }
+ if (port > 0)
+ {
+ break; // hack for hanging readLine()
+ }
+ response = lineReader.readLine();
+ }
+ } catch (Exception e)
+ {
+ System.err.println(
+ "Failed to get REST port number from " + responses + ": "
+ + e.getMessage());
+ // logger.error("Failed to get REST port number from " + responses + ": "
+ // + e.getMessage());
+ } finally
+ {
+ try
+ {
+ lineReader.close();
+ } catch (IOException e2)
+ {
+ }
+ }
+ if (port == 0)
+ {
+ System.err.println("Failed to start PyMOL with XMLRPC, response was: "
+ + responses);
+ }
+ System.err.println("PyMOL started with XMLRPC on port " + port);
+ return port;
+ }
+
+}
+++ /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.ext.rbvi.chimera;
-
-import jalview.util.IntRangeComparator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * A class to model a Chimera atomspec pattern, for example
- *
- * <pre>
- * #0:15.A,28.A,54.A,63.A,70-72.A,83-84.A,97-98.A|#1:2.A,6.A,11.A,13-14.A,70.A,82.A,96-97.A
- * </pre>
- *
- * where
- * <ul>
- * <li>#0 is a model number</li>
- * <li>15 or 70-72 is a residue number, or range of residue numbers</li>
- * <li>.A is a chain identifier</li>
- * <li>residue ranges are separated by comma</li>
- * <li>atomspecs for distinct models are separated by | (or)</li>
- * </ul>
- *
- * <pre>
- * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
- * </pre>
- */
-public class AtomSpecModel
-{
- private Map<Integer, Map<String, List<int[]>>> atomSpec;
-
- /**
- * Constructor
- */
- public AtomSpecModel()
- {
- atomSpec = new TreeMap<Integer, Map<String, List<int[]>>>();
- }
-
- /**
- * Adds one contiguous range to this atom spec
- *
- * @param model
- * @param startPos
- * @param endPos
- * @param chain
- */
- public void addRange(int model, int startPos, int endPos, String chain)
- {
- /*
- * Get/initialize map of data for the colour and model
- */
- Map<String, List<int[]>> modelData = atomSpec.get(model);
- if (modelData == null)
- {
- atomSpec.put(model, modelData = new TreeMap<String, List<int[]>>());
- }
-
- /*
- * Get/initialize map of data for colour, model and chain
- */
- List<int[]> chainData = modelData.get(chain);
- if (chainData == null)
- {
- chainData = new ArrayList<int[]>();
- modelData.put(chain, chainData);
- }
-
- /*
- * Add the start/end positions
- */
- chainData.add(new int[] { startPos, endPos });
- // TODO add intelligently, using a RangeList class
- }
-
- /**
- * Returns the range(s) formatted as a Chimera atomspec
- *
- * @return
- */
- public String getAtomSpec()
- {
- StringBuilder sb = new StringBuilder(128);
- boolean firstModel = true;
- for (Integer model : atomSpec.keySet())
- {
- if (!firstModel)
- {
- sb.append("|");
- }
- firstModel = false;
- sb.append("#").append(model).append(":");
-
- boolean firstPositionForModel = true;
- final Map<String, List<int[]>> modelData = atomSpec.get(model);
-
- for (String chain : modelData.keySet())
- {
- chain = " ".equals(chain) ? chain : chain.trim();
-
- List<int[]> rangeList = modelData.get(chain);
-
- /*
- * sort ranges into ascending start position order
- */
- Collections.sort(rangeList, IntRangeComparator.ASCENDING);
-
- int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0];
- int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1];
-
- Iterator<int[]> iterator = rangeList.iterator();
- while (iterator.hasNext())
- {
- int[] range = iterator.next();
- if (range[0] <= end + 1)
- {
- /*
- * range overlaps or is contiguous with the last one
- * - so just extend the end position, and carry on
- * (unless this is the last in the list)
- */
- end = Math.max(end, range[1]);
- }
- else
- {
- /*
- * we have a break so append the last range
- */
- appendRange(sb, start, end, chain, firstPositionForModel);
- firstPositionForModel = false;
- start = range[0];
- end = range[1];
- }
- }
-
- /*
- * and append the last range
- */
- if (!rangeList.isEmpty())
- {
- appendRange(sb, start, end, chain, firstPositionForModel);
- firstPositionForModel = false;
- }
- }
- }
- return sb.toString();
- }
-
- /**
- * @param sb
- * @param start
- * @param end
- * @param chain
- * @param firstPositionForModel
- */
- protected void appendRange(StringBuilder sb, int start, int end,
- String chain, boolean firstPositionForModel)
- {
- if (!firstPositionForModel)
- {
- sb.append(",");
- }
- if (end == start)
- {
- sb.append(start);
- }
- else
- {
- sb.append(start).append("-").append(end);
- }
-
- sb.append(".");
- if (!" ".equals(chain)) {
- sb.append(chain);
- }
- }
-}
*/
package jalview.ext.rbvi.chimera;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
-import jalview.api.SequenceRenderer;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.MappedFeatures;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.gui.Desktop;
-import jalview.renderer.seqfeatures.FeatureColourFinder;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
-import jalview.structure.StructureSelectionManager;
-import jalview.util.ColorUtils;
-import jalview.util.Comparison;
-
import java.awt.Color;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsBase;
+import jalview.util.ColorUtils;
+
/**
* Routines for generating Chimera commands for Jalview/Chimera binding
*
* @author JimP
*
*/
-public class ChimeraCommands
+public class ChimeraCommands extends StructureCommandsBase
{
+ private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
+ "~display all;~ribbon;chain @CA|P");
- public static final String NAMESPACE_PREFIX = "jv_";
+ private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
+ "color white;color red ::ASP,GLU;color blue ::LYS,ARG;color yellow ::CYS");
+
+ private static final StructureCommandI COLOUR_BY_CHAIN = new StructureCommand(
+ "rainbow chain");
+
+ // Chimera clause to exclude alternate locations in atom selection
+ private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
+
+ @Override
+ public StructureCommandI colourResidues(String atomSpec, Color colour)
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/color.html
+ String colourCode = getColourString(colour);
+ return new StructureCommand("color " + colourCode + " " + atomSpec);
+ }
/**
- * Constructs Chimera commands to colour residues as per the Jalview alignment
+ * Returns a colour formatted suitable for use in viewer command syntax
*
- * @param ssm
- * @param files
- * @param sequence
- * @param sr
- * @param fr
- * @param viewPanel
+ * @param colour
* @return
*/
- public static StructureMappingcommandSet[] getColourBySequenceCommand(
- StructureSelectionManager ssm, String[] files,
- SequenceI[][] sequence, SequenceRenderer sr,
- AlignmentViewPanel viewPanel)
+ protected String getColourString(Color colour)
{
- Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, files,
- sequence, sr, viewPanel);
-
- List<String> colourCommands = buildColourCommands(colourMap);
-
- StructureMappingcommandSet cs = new StructureMappingcommandSet(
- ChimeraCommands.class, null,
- colourCommands.toArray(new String[colourCommands.size()]));
-
- return new StructureMappingcommandSet[] { cs };
+ return ColorUtils.toTkCode(colour);
}
/**
- * Traverse the map of colours/models/chains/positions to construct a list of
- * 'color' commands (one per distinct colour used). The format of each command
- * is
+ * Traverse the map of features/values/models/chains/positions to construct a
+ * list of 'setattr' commands (one per distinct feature type and value).
+ * <p>
+ * The format of each command is
*
* <pre>
- * <blockquote>
- * color colorname #modelnumber:range.chain
- * e.g. color #00ff00 #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
+ * <blockquote> setattr r <featureName> " " #modelnumber:range.chain
+ * e.g. setattr r jv_chain <value> #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
* </blockquote>
* </pre>
*
- * @param colourMap
+ * @param featureMap
* @return
*/
- protected static List<String> buildColourCommands(
- Map<Object, AtomSpecModel> colourMap)
+ @Override
+ public List<StructureCommandI> setAttributes(
+ Map<String, Map<Object, AtomSpecModel>> featureMap)
{
- /*
- * This version concatenates all commands into a single String (semi-colon
- * delimited). If length limit issues arise, refactor to return one color
- * command per colour.
- */
- List<String> commands = new ArrayList<>();
- StringBuilder sb = new StringBuilder(256);
- boolean firstColour = true;
- for (Object key : colourMap.keySet())
+ List<StructureCommandI> commands = new ArrayList<>();
+ for (String featureType : featureMap.keySet())
{
- Color colour = (Color) key;
- String colourCode = ColorUtils.toTkCode(colour);
- if (!firstColour)
- {
- sb.append("; ");
- }
- sb.append("color ").append(colourCode).append(" ");
- firstColour = false;
- final AtomSpecModel colourData = colourMap.get(colour);
- sb.append(colourData.getAtomSpec());
- }
- commands.add(sb.toString());
- return commands;
- }
+ String attributeName = makeAttributeName(featureType);
- /**
- * Traverses a map of { modelNumber, {chain, {list of from-to ranges} } } and
- * builds a Chimera format atom spec
- *
- * @param modelAndChainRanges
- */
- protected static String getAtomSpec(
- Map<Integer, Map<String, List<int[]>>> modelAndChainRanges)
- {
- StringBuilder sb = new StringBuilder(128);
- boolean firstModelForColour = true;
- for (Integer model : modelAndChainRanges.keySet())
- {
- boolean firstPositionForModel = true;
- if (!firstModelForColour)
- {
- sb.append("|");
- }
- firstModelForColour = false;
- sb.append("#").append(model).append(":");
+ /*
+ * clear down existing attributes for this feature
+ */
+ // 'problem' - sets attribute to None on all residues - overkill?
+ // commands.add("~setattr r " + attributeName + " :*");
- final Map<String, List<int[]>> modelData = modelAndChainRanges
- .get(model);
- for (String chain : modelData.keySet())
+ Map<Object, AtomSpecModel> values = featureMap.get(featureType);
+ for (Object value : values.keySet())
{
- boolean hasChain = !"".equals(chain.trim());
- for (int[] range : modelData.get(chain))
- {
- if (!firstPositionForModel)
- {
- sb.append(",");
- }
- if (range[0] == range[1])
- {
- sb.append(range[0]);
- }
- else
- {
- sb.append(range[0]).append("-").append(range[1]);
- }
- if (hasChain)
- {
- sb.append(".").append(chain);
- }
- firstPositionForModel = false;
- }
+ /*
+ * for each distinct value recorded for this feature type,
+ * add a command to set the attribute on the mapped residues
+ * Put values in single quotes, encoding any embedded single quotes
+ */
+ AtomSpecModel atomSpecModel = values.get(value);
+ String featureValue = value.toString();
+ featureValue = featureValue.replaceAll("\\'", "'");
+ StructureCommandI cmd = setAttribute(attributeName, featureValue,
+ atomSpecModel);
+ commands.add(cmd);
}
}
- return sb.toString();
+
+ return commands;
}
/**
+ * Returns a viewer command to set the given residue attribute value on
+ * residues specified by the AtomSpecModel, for example
+ *
* <pre>
- * Build a data structure which records contiguous subsequences for each colour.
- * From this we can easily generate the Chimera command for colour by sequence.
- * Color
- * Model number
- * Chain
- * list of start/end ranges
- * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
+ * setatr res jv_chain 'primary' #1:12-34,48-55.B
* </pre>
+ *
+ * @param attributeName
+ * @param attributeValue
+ * @param atomSpecModel
+ * @return
*/
- protected static Map<Object, AtomSpecModel> buildColoursMap(
- StructureSelectionManager ssm, String[] files,
- SequenceI[][] sequence, SequenceRenderer sr,
- AlignmentViewPanel viewPanel)
+ protected StructureCommandI setAttribute(String attributeName,
+ String attributeValue,
+ AtomSpecModel atomSpecModel)
{
- FeatureRenderer fr = viewPanel.getFeatureRenderer();
- FeatureColourFinder finder = new FeatureColourFinder(fr);
- AlignViewportI viewport = viewPanel.getAlignViewport();
- HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
- AlignmentI al = viewport.getAlignment();
- Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
- Color lastColour = null;
-
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-
- if (mapping == null || mapping.length < 1)
- {
- continue;
- }
-
- int startPos = -1, lastPos = -1;
- String lastChain = "";
- for (int s = 0; s < sequence[pdbfnum].length; s++)
- {
- for (int sp, m = 0; m < mapping.length; m++)
- {
- final SequenceI seq = sequence[pdbfnum][s];
- if (mapping[m].getSequence() == seq
- && (sp = al.findIndex(seq)) > -1)
- {
- SequenceI asp = al.getSequenceAt(sp);
- for (int r = 0; r < asp.getLength(); r++)
- {
- // no mapping to gaps in sequence
- if (Comparison.isGap(asp.getCharAt(r)))
- {
- continue;
- }
- int pos = mapping[m].getPDBResNum(asp.findPosition(r));
-
- if (pos < 1 || pos == lastPos)
- {
- continue;
- }
-
- Color colour = sr.getResidueColour(seq, r, finder);
-
- /*
- * darker colour for hidden regions
- */
- if (!cs.isVisible(r))
- {
- colour = Color.GRAY;
- }
-
- final String chain = mapping[m].getChain();
-
- /*
- * Just keep incrementing the end position for this colour range
- * _unless_ colour, PDB model or chain has changed, or there is a
- * gap in the mapped residue sequence
- */
- final boolean newColour = !colour.equals(lastColour);
- final boolean nonContig = lastPos + 1 != pos;
- final boolean newChain = !chain.equals(lastChain);
- if (newColour || nonContig || newChain)
- {
- if (startPos != -1)
- {
- addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
- lastPos, lastChain);
- }
- startPos = pos;
- }
- lastColour = colour;
- lastPos = pos;
- lastChain = chain;
- }
- // final colour range
- if (lastColour != null)
- {
- addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos,
- lastPos, lastChain);
- }
- // break;
- }
- }
- }
- }
- return colourMap;
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("setattr res ").append(attributeName).append(" '")
+ .append(attributeValue).append("' ");
+ sb.append(getAtomSpec(atomSpecModel, false));
+ return new StructureCommand(sb.toString());
}
/**
- * Helper method to add one contiguous range to the AtomSpec model for the given
- * value (creating the model if necessary). As used by Jalview, {@code value} is
- * <ul>
- * <li>a colour, when building a 'colour structure by sequence' command</li>
- * <li>a feature value, when building a 'set Chimera attributes from features'
- * command</li>
- * </ul>
+ * Makes a prefixed and valid Chimera attribute name. A jv_ prefix is applied
+ * for a 'Jalview' namespace, and any non-alphanumeric character is converted
+ * to an underscore.
*
- * @param map
- * @param value
- * @param model
- * @param startPos
- * @param endPos
- * @param chain
+ * @param featureType
+ * @return
+ * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
*/
- protected static void addAtomSpecRange(Map<Object, AtomSpecModel> map,
- Object value, int model, int startPos, int endPos, String chain)
+ @Override
+ protected String makeAttributeName(String featureType)
{
+ String attName = super.makeAttributeName(featureType);
+
/*
- * Get/initialize map of data for the colour
+ * Chimera treats an attribute name ending in 'color' as colour-valued;
+ * Jalview doesn't, so prevent this by appending an underscore
*/
- AtomSpecModel atomSpec = map.get(value);
- if (atomSpec == null)
+ if (attName.toUpperCase().endsWith("COLOR"))
{
- atomSpec = new AtomSpecModel();
- map.put(value, atomSpec);
+ attName += "_";
}
- atomSpec.addRange(model, startPos, endPos, chain);
+ return attName;
}
- /**
- * Constructs and returns Chimera commands to set attributes on residues
- * corresponding to features in Jalview. Attribute names are the Jalview
- * feature type, with a "jv_" prefix.
- *
- * @param ssm
- * @param files
- * @param seqs
- * @param viewPanel
- * @return
- */
- public static StructureMappingcommandSet getSetAttributeCommandsForFeatures(
- StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
- AlignmentViewPanel viewPanel)
+ @Override
+ public StructureCommandI colourByChain()
{
- Map<String, Map<Object, AtomSpecModel>> featureMap = buildFeaturesMap(
- ssm, files, seqs, viewPanel);
-
- List<String> commands = buildSetAttributeCommands(featureMap);
-
- StructureMappingcommandSet cs = new StructureMappingcommandSet(
- ChimeraCommands.class, null,
- commands.toArray(new String[commands.size()]));
+ return COLOUR_BY_CHAIN;
+ }
- return cs;
+ @Override
+ public List<StructureCommandI> colourByCharge()
+ {
+ return Arrays.asList(COLOUR_BY_CHARGE);
}
- /**
- * <pre>
- * Helper method to build a map of
- * { featureType, { feature value, AtomSpecModel } }
- * </pre>
- *
- * @param ssm
- * @param files
- * @param seqs
- * @param viewPanel
- * @return
- */
- protected static Map<String, Map<Object, AtomSpecModel>> buildFeaturesMap(
- StructureSelectionManager ssm, String[] files, SequenceI[][] seqs,
- AlignmentViewPanel viewPanel)
+ @Override
+ public String getResidueSpec(String residue)
{
- Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
+ return "::" + residue;
+ }
- FeatureRenderer fr = viewPanel.getFeatureRenderer();
- if (fr == null)
- {
- return theMap;
- }
+ @Override
+ public StructureCommandI setBackgroundColour(Color col)
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/set.html#bgcolor
+ return new StructureCommand("set bgColor " + ColorUtils.toTkCode(col));
+ }
- AlignViewportI viewport = viewPanel.getAlignViewport();
- List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
+ @Override
+ public StructureCommandI focusView()
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/focus.html
+ return new StructureCommand("focus");
+ }
+ @Override
+ public List<StructureCommandI> showChains(List<String> toShow)
+ {
/*
- * if alignment is showing features from complement, we also transfer
- * these features to the corresponding mapped structure residues
+ * Construct a chimera command like
+ *
+ * ~display #*;~ribbon #*;ribbon :.A,:.B
*/
- boolean showLinkedFeatures = viewport.isShowComplementFeatures();
- List<String> complementFeatures = new ArrayList<>();
- FeatureRenderer complementRenderer = null;
- if (showLinkedFeatures)
+ StringBuilder cmd = new StringBuilder(64);
+ boolean first = true;
+ for (String chain : toShow)
{
- AlignViewportI comp = fr.getViewport().getCodingComplement();
- if (comp != null)
+ String[] tokens = chain.split(":");
+ if (tokens.length == 2)
{
- complementRenderer = Desktop.getAlignFrameFor(comp)
- .getFeatureRenderer();
- complementFeatures = complementRenderer.getDisplayedFeatureTypes();
+ String showChainCmd = tokens[0] + ":." + tokens[1];
+ if (!first)
+ {
+ cmd.append(",");
+ }
+ cmd.append(showChainCmd);
+ first = false;
}
}
- if (visibleFeatures.isEmpty() && complementFeatures.isEmpty())
- {
- return theMap;
- }
- AlignmentI alignment = viewPanel.getAlignment();
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+ /*
+ * could append ";focus" to this command to resize the display to fill the
+ * window, but it looks more helpful not to (easier to relate chains to the
+ * whole)
+ */
+ final String command = "~display #*; ~ribbon #*; ribbon :"
+ + cmd.toString();
+ return Arrays.asList(new StructureCommand(command));
+ }
- if (mapping == null || mapping.length < 1)
- {
- continue;
- }
+ @Override
+ public List<StructureCommandI> superposeStructures(AtomSpecModel ref,
+ AtomSpecModel spec)
+ {
+ /*
+ * Form Chimera match command to match spec to ref
+ * (the first set of atoms are moved on to the second)
+ *
+ * match #1:1-30.B,81-100.B@CA #0:21-40.A,61-90.A@CA
+ *
+ * @see https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/midas/match.html
+ */
+ StringBuilder cmd = new StringBuilder();
+ String atomSpecAlphaOnly = getAtomSpec(spec, true);
+ String refSpecAlphaOnly = getAtomSpec(ref, true);
+ cmd.append("match ").append(atomSpecAlphaOnly).append(" ").append(refSpecAlphaOnly);
- for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
- {
- for (int m = 0; m < mapping.length; m++)
- {
- final SequenceI seq = seqs[pdbfnum][seqNo];
- int sp = alignment.findIndex(seq);
- StructureMapping structureMapping = mapping[m];
- if (structureMapping.getSequence() == seq && sp > -1)
- {
- /*
- * found a sequence with a mapping to a structure;
- * now scan its features
- */
- if (!visibleFeatures.isEmpty())
- {
- scanSequenceFeatures(visibleFeatures, structureMapping, seq,
- theMap, pdbfnum);
- }
- if (showLinkedFeatures)
- {
- scanComplementFeatures(complementRenderer, structureMapping,
- seq, theMap, pdbfnum);
- }
- }
- }
- }
- }
- return theMap;
+ /*
+ * show superposed residues as ribbon
+ */
+ String atomSpec = getAtomSpec(spec, false);
+ String refSpec = getAtomSpec(ref, false);
+ cmd.append("; ribbon ");
+ cmd.append(atomSpec).append("|").append(refSpec).append("; focus");
+
+ return Arrays.asList(new StructureCommand(cmd.toString()));
+ }
+
+ @Override
+ public StructureCommandI openCommandFile(String path)
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
+ return new StructureCommand("open cmd:" + path);
+ }
+
+ @Override
+ public StructureCommandI saveSession(String filepath)
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/save.html
+ return new StructureCommand("save " + filepath);
}
/**
- * Scans visible features in mapped positions of the CDS/peptide complement, and
- * adds any found to the map of attribute values/structure positions
+ * Returns the range(s) modelled by {@code atomSpec} formatted as a Chimera
+ * atomspec string, e.g.
*
- * @param complementRenderer
- * @param structureMapping
- * @param seq
- * @param theMap
- * @param modelNumber
+ * <pre>
+ * #0:15.A,28.A,54.A,70-72.A|#1:2.A,6.A,11.A,13-14.A
+ * </pre>
+ *
+ * where
+ * <ul>
+ * <li>#0 is a model number</li>
+ * <li>15 or 70-72 is a residue number, or range of residue numbers</li>
+ * <li>.A is a chain identifier</li>
+ * <li>residue ranges are separated by comma</li>
+ * <li>atomspecs for distinct models are separated by | (or)</li>
+ * </ul>
+ *
+ * <pre>
+ *
+ * @param model
+ * @param alphaOnly
+ * @return
+ * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
*/
- protected static void scanComplementFeatures(
- FeatureRenderer complementRenderer,
- StructureMapping structureMapping, SequenceI seq,
- Map<String, Map<Object, AtomSpecModel>> theMap, int modelNumber)
+ @Override
+ public String getAtomSpec(AtomSpecModel atomSpec, boolean alphaOnly)
{
- /*
- * for each sequence residue mapped to a structure position...
- */
- for (int seqPos : structureMapping.getMapping().keySet())
+ StringBuilder sb = new StringBuilder(128);
+ boolean firstModel = true;
+ for (String model : atomSpec.getModels())
{
- /*
- * find visible complementary features at mapped position(s)
- */
- MappedFeatures mf = complementRenderer
- .findComplementFeaturesAtResidue(seq, seqPos);
- if (mf != null)
+ if (!firstModel)
{
- for (SequenceFeature sf : mf.features)
- {
- String type = sf.getType();
-
- /*
- * Don't copy features which originated from Chimera
- */
- if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
- .equals(sf.getFeatureGroup()))
- {
- continue;
- }
-
- /*
- * record feature 'value' (score/description/type) as at the
- * corresponding structure position
- */
- List<int[]> mappedRanges = structureMapping
- .getPDBResNumRanges(seqPos, seqPos);
-
- if (!mappedRanges.isEmpty())
- {
- String value = sf.getDescription();
- if (value == null || value.length() == 0)
- {
- value = type;
- }
- float score = sf.getScore();
- if (score != 0f && !Float.isNaN(score))
- {
- value = Float.toString(score);
- }
- Map<Object, AtomSpecModel> featureValues = theMap.get(type);
- if (featureValues == null)
- {
- featureValues = new HashMap<>();
- theMap.put(type, featureValues);
- }
- for (int[] range : mappedRanges)
- {
- addAtomSpecRange(featureValues, value, modelNumber, range[0],
- range[1], structureMapping.getChain());
- }
- }
- }
+ sb.append("|");
}
+ firstModel = false;
+ appendModel(sb, model, atomSpec, alphaOnly);
}
+ return sb.toString();
}
/**
- * Inspect features on the sequence; for each feature that is visible, determine
- * its mapped ranges in the structure (if any) according to the given mapping,
- * and add them to the map.
+ * A helper method to append an atomSpec string for atoms in the given model
*
- * @param visibleFeatures
- * @param mapping
- * @param seq
- * @param theMap
- * @param modelNumber
+ * @param sb
+ * @param model
+ * @param atomSpec
+ * @param alphaOnly
*/
- protected static void scanSequenceFeatures(List<String> visibleFeatures,
- StructureMapping mapping, SequenceI seq,
- Map<String, Map<Object, AtomSpecModel>> theMap, int modelNumber)
+ protected void appendModel(StringBuilder sb, String model,
+ AtomSpecModel atomSpec, boolean alphaOnly)
{
- List<SequenceFeature> sfs = seq.getFeatures().getPositionalFeatures(
- visibleFeatures.toArray(new String[visibleFeatures.size()]));
- for (SequenceFeature sf : sfs)
- {
- String type = sf.getType();
+ sb.append("#").append(model).append(":");
- /*
- * Don't copy features which originated from Chimera
- */
- if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
- .equals(sf.getFeatureGroup()))
- {
- continue;
- }
+ boolean firstPositionForModel = true;
- List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
- sf.getEnd());
+ for (String chain : atomSpec.getChains(model))
+ {
+ chain = " ".equals(chain) ? chain : chain.trim();
- if (!mappedRanges.isEmpty())
+ List<int[]> rangeList = atomSpec.getRanges(model, chain);
+ for (int[] range : rangeList)
{
- String value = sf.getDescription();
- if (value == null || value.length() == 0)
- {
- value = type;
- }
- float score = sf.getScore();
- if (score != 0f && !Float.isNaN(score))
- {
- value = Float.toString(score);
- }
- Map<Object, AtomSpecModel> featureValues = theMap.get(type);
- if (featureValues == null)
- {
- featureValues = new HashMap<>();
- theMap.put(type, featureValues);
- }
- for (int[] range : mappedRanges)
- {
- addAtomSpecRange(featureValues, value, modelNumber, range[0],
- range[1], mapping.getChain());
- }
+ appendRange(sb, range[0], range[1], chain, firstPositionForModel,
+ false);
+ firstPositionForModel = false;
}
}
- }
-
- /**
- * Traverse the map of features/values/models/chains/positions to construct a
- * list of 'setattr' commands (one per distinct feature type and value).
- * <p>
- * The format of each command is
- *
- * <pre>
- * <blockquote> setattr r <featureName> " " #modelnumber:range.chain
- * e.g. setattr r jv:chain <value> #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
- * </blockquote>
- * </pre>
- *
- * @param featureMap
- * @return
- */
- protected static List<String> buildSetAttributeCommands(
- Map<String, Map<Object, AtomSpecModel>> featureMap)
- {
- List<String> commands = new ArrayList<>();
- for (String featureType : featureMap.keySet())
+ if (alphaOnly)
{
- String attributeName = makeAttributeName(featureType);
-
/*
- * clear down existing attributes for this feature
+ * restrict to alpha carbon, no alternative locations
+ * (needed to ensuring matching atom counts for superposition)
*/
- // 'problem' - sets attribute to None on all residues - overkill?
- // commands.add("~setattr r " + attributeName + " :*");
-
- Map<Object, AtomSpecModel> values = featureMap.get(featureType);
- for (Object value : values.keySet())
- {
- /*
- * for each distinct value recorded for this feature type,
- * add a command to set the attribute on the mapped residues
- * Put values in single quotes, encoding any embedded single quotes
- */
- StringBuilder sb = new StringBuilder(128);
- String featureValue = value.toString();
- featureValue = featureValue.replaceAll("\\'", "'");
- sb.append("setattr r ").append(attributeName).append(" '")
- .append(featureValue).append("' ");
- sb.append(values.get(value).getAtomSpec());
- commands.add(sb.toString());
- }
+ // TODO @P instead if RNA - add nucleotide flag to AtomSpecModel?
+ sb.append("@CA").append(NO_ALTLOCS);
}
+ }
- return commands;
+ @Override
+ public List<StructureCommandI> showBackbone()
+ {
+ return Arrays.asList(SHOW_BACKBONE);
+ }
+
+ @Override
+ public StructureCommandI loadFile(String file)
+ {
+ return new StructureCommand("open " + file);
}
/**
- * Makes a prefixed and valid Chimera attribute name. A jv_ prefix is applied
- * for a 'Jalview' namespace, and any non-alphanumeric character is converted
- * to an underscore.
- *
- * @param featureType
- * @return
- *
- * <pre>
- * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/setattr.html
- * </pre>
+ * Overrides the default method to concatenate colour commands into one
*/
- protected static String makeAttributeName(String featureType)
+ @Override
+ public List<StructureCommandI> colourBySequence(
+ Map<Object, AtomSpecModel> colourMap)
{
- StringBuilder sb = new StringBuilder();
- if (featureType != null)
+ List<StructureCommandI> commands = new ArrayList<>();
+ StringBuilder sb = new StringBuilder(colourMap.size() * 20);
+ boolean first = true;
+ for (Object key : colourMap.keySet())
{
- for (char c : featureType.toCharArray())
+ Color colour = (Color) key;
+ final AtomSpecModel colourData = colourMap.get(colour);
+ StructureCommandI command = getColourCommand(colourData, colour);
+ if (!first)
{
- sb.append(Character.isLetterOrDigit(c) ? c : '_');
+ sb.append(getCommandSeparator());
}
+ first = false;
+ sb.append(command.getCommand());
}
- String attName = NAMESPACE_PREFIX + sb.toString();
- /*
- * Chimera treats an attribute name ending in 'color' as colour-valued;
- * Jalview doesn't, so prevent this by appending an underscore
- */
- if (attName.toUpperCase().endsWith("COLOR"))
- {
- attName += "_";
- }
+ commands.add(new StructureCommand(sb.toString()));
+ return commands;
+ }
- return attName;
+ @Override
+ public StructureCommandI openSession(String filepath)
+ {
+ // https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/filetypes.html
+ // this version of the command has no dependency on file extension
+ return new StructureCommand("open chimera:" + filepath);
}
}
{
// dumpRequest(request);
String message = request.getParameter(CHIMERA_NOTIFICATION);
- if (SELECTION_CHANGED.equals(message))
+ if (message == null)
{
- this.chimeraBinding.highlightChimeraSelection();
+ message = request.getParameter("chimerax_notification");
}
- else if (message != null && message.startsWith(MODEL_CHANGED))
+ if (message != null)
{
- processModelChanged(message.substring(MODEL_CHANGED.length()));
- }
- else
- {
- System.err.println("Unexpected chimeraNotification: " + message);
+ if (message.startsWith("SelectionChanged"))
+ {
+ this.chimeraBinding.highlightChimeraSelection();
+ }
+ else if (message.startsWith(MODEL_CHANGED))
+ {
+ System.err.println(message);
+ processModelChanged(message.substring(MODEL_CHANGED.length()));
+ }
+ else
+ {
+ System.err.println("Unexpected chimeraNotification: " + message);
+ }
}
}
--- /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.ext.rbvi.chimera;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.List;
+
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.util.ColorUtils;
+
+/**
+ * Routines for generating ChimeraX commands for Jalview/ChimeraX binding
+ */
+public class ChimeraXCommands extends ChimeraCommands
+{
+ private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
+ "~display all;~ribbon;show @CA|P atoms");
+
+ private static final StructureCommand FOCUS_VIEW = new StructureCommand(
+ "view");
+
+ private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
+ "color white;color :ASP,GLU red;color :LYS,ARG blue;color :CYS yellow");
+
+ @Override
+ public List<StructureCommandI> colourByCharge()
+ {
+ return Arrays.asList(COLOUR_BY_CHARGE);
+ }
+
+ @Override
+ public String getResidueSpec(String residue)
+ {
+ return ":" + residue;
+ }
+
+ @Override
+ public StructureCommandI setBackgroundColour(Color col)
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/set.html
+ return new StructureCommand("set bgColor " + ColorUtils.toTkCode(col));
+ }
+
+ @Override
+ public StructureCommandI colourResidues(String atomSpec, Color colour)
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/color.html
+ String colourCode = getColourString(colour);
+
+ return new StructureCommand("color " + atomSpec + " " + colourCode);
+ }
+
+ @Override
+ public StructureCommandI focusView()
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/view.html
+ return FOCUS_VIEW;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return
+ */
+ @Override
+ public int getModelStartNo()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns a viewer command to set the given residue attribute value on
+ * residues specified by the AtomSpecModel, for example
+ *
+ * <pre>
+ * setattr #0/A:3-9,14-20,39-43 res jv_strand 'strand' create true
+ * </pre>
+ *
+ * @param attributeName
+ * @param attributeValue
+ * @param atomSpecModel
+ * @return
+ */
+ @Override
+ protected StructureCommandI setAttribute(String attributeName,
+ String attributeValue, AtomSpecModel atomSpecModel)
+ {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("setattr ").append(getAtomSpec(atomSpecModel, false));
+ sb.append(" res ").append(attributeName).append(" '")
+ .append(attributeValue).append("'");
+ sb.append(" create true");
+ return new StructureCommand(sb.toString());
+ }
+
+ @Override
+ public StructureCommandI openCommandFile(String path)
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/open.html
+ return new StructureCommand("open " + path);
+ }
+
+ @Override
+ public StructureCommandI saveSession(String filepath)
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/save.html
+ // note ChimeraX will append ".cxs" to the filepath!
+ return new StructureCommand("save " + filepath + " format session");
+ }
+
+ /**
+ * Returns the range(s) formatted as a ChimeraX atomspec, for example
+ * <p>
+ * #1/A:2-20,30-40/B:10-20|#2/A:12-30
+ *
+ * @return
+ */
+ @Override
+ public String getAtomSpec(AtomSpecModel atomSpec, boolean alphaOnly)
+ {
+ StringBuilder sb = new StringBuilder(128);
+ boolean firstModel = true;
+ for (String model : atomSpec.getModels())
+ {
+ if (!firstModel)
+ {
+ sb.append("|");
+ }
+ firstModel = false;
+ appendModel(sb, model, atomSpec);
+ if (alphaOnly)
+ {
+ // TODO @P if RNA - add nucleotide flag to AtomSpecModel?
+ sb.append("@CA");
+ }
+ // todo: is there ChimeraX syntax to exclude altlocs?
+ }
+ return sb.toString();
+ }
+
+ /**
+ * A helper method to append an atomSpec string for atoms in the given model
+ *
+ * @param sb
+ * @param model
+ * @param atomSpec
+ */
+ protected void appendModel(StringBuilder sb, String model,
+ AtomSpecModel atomSpec)
+ {
+ sb.append("#").append(model);
+
+ for (String chain : atomSpec.getChains(model))
+ {
+ boolean firstPositionForChain = true;
+ sb.append("/").append(chain.trim()).append(":");
+ List<int[]> rangeList = atomSpec.getRanges(model, chain);
+ boolean first = true;
+ for (int[] range : rangeList)
+ {
+ if (!first)
+ {
+ sb.append(",");
+ }
+ first = false;
+ appendRange(sb, range[0], range[1], chain, firstPositionForChain,
+ true);
+ }
+ }
+ }
+
+ @Override
+ public List<StructureCommandI> showBackbone()
+ {
+ return Arrays.asList(SHOW_BACKBONE);
+ }
+
+ @Override
+ public List<StructureCommandI> superposeStructures(AtomSpecModel ref,
+ AtomSpecModel spec)
+ {
+ /*
+ * Form ChimeraX match command to match spec to ref
+ *
+ * match #1/A:2-94 toAtoms #2/A:1-93
+ *
+ * @see https://www.cgl.ucsf.edu/chimerax/docs/user/commands/align.html
+ */
+ StringBuilder cmd = new StringBuilder();
+ String atomSpec = getAtomSpec(spec, true);
+ String refSpec = getAtomSpec(ref, true);
+ cmd.append("align ").append(atomSpec).append(" toAtoms ")
+ .append(refSpec);
+
+ /*
+ * show superposed residues as ribbon, others as chain
+ */
+ cmd.append("; ribbon ");
+ cmd.append(getAtomSpec(spec, false)).append("|");
+ cmd.append(getAtomSpec(ref, false)).append("; view");
+
+ return Arrays.asList(new StructureCommand(cmd.toString()));
+ }
+
+ @Override
+ public StructureCommandI openSession(String filepath)
+ {
+ // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/open.html#composite
+ // this version of the command has no dependency on file extension
+ return new StructureCommand("open " + filepath + " format session");
+ }
+}
*/
package jalview.ext.rbvi.chimera;
-import jalview.api.AlignmentViewPanel;
-import jalview.api.SequenceRenderer;
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SearchResultMatchI;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.httpserver.AbstractRequestHandler;
-import jalview.io.DataSourceType;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ResidueProperties;
-import jalview.structure.AtomSpec;
-import jalview.structure.StructureMappingcommandSet;
-import jalview.structure.StructureSelectionManager;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.MessageManager;
-
-import java.awt.Color;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.BindException;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.Collections;
-import java.util.Hashtable;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.httpserver.AbstractRequestHandler;
+import jalview.io.DataSourceType;
+import jalview.structure.AtomSpec;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
public abstract class JalviewChimeraBinding extends AAStructureBindingModel
{
- public static final String CHIMERA_FEATURE_GROUP = "Chimera";
-
- // Chimera clause to exclude alternate locations in atom selection
- private static final String NO_ALTLOCS = "&~@.B-Z&~@.2-9";
-
- private static final String COLOURING_CHIMERA = MessageManager
- .getString("status.colouring_chimera");
-
- private static final boolean debug = false;
-
- private static final String PHOSPHORUS = "P";
-
- private static final String ALPHACARBON = "CA";
+ public static final String CHIMERA_SESSION_EXTENSION = ".py";
- private List<String> chainNames = new ArrayList<String>();
-
- private Hashtable<String, String> chainFile = new Hashtable<String, String>();
+ public static final String CHIMERA_FEATURE_GROUP = "Chimera";
/*
* Object through which we talk to Chimera
*/
- private ChimeraManager viewer;
+ private ChimeraManager chimeraManager;
/*
* Object which listens to Chimera notifications
private AbstractRequestHandler chimeraListener;
/*
- * set if chimera state is being restored from some source - instructs binding
- * not to apply default display style when structure set is updated for first
- * time.
- */
- private boolean loadingFromArchive = false;
-
- /*
- * flag to indicate if the Chimera viewer should ignore sequence colouring
- * events from the structure manager because the GUI is still setting up
- */
- private boolean loadingFinished = true;
-
- /*
* Map of ChimeraModel objects keyed by PDB full local file name
*/
- private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<String, List<ChimeraModel>>();
+ protected Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<>();
String lastHighlightCommand;
- /*
- * incremented every time a load notification is successfully handled -
- * lightweight mechanism for other threads to detect when they can start
- * referring to new structures.
- */
- private long loadNotifiesHandled = 0;
-
private Thread chimeraMonitor;
/**
* Open a PDB structure file in Chimera and set up mappings from Jalview.
*
- * We check if the PDB model id is already loaded in Chimera, if so don't
- * reopen it. This is the case if Chimera has opened a saved session file.
+ * We check if the PDB model id is already loaded in Chimera, if so don't reopen
+ * it. This is the case if Chimera has opened a saved session file.
*
* @param pe
* @return
String file = pe.getFile();
try
{
- List<ChimeraModel> modelsToMap = new ArrayList<ChimeraModel>();
- List<ChimeraModel> oldList = viewer.getModelList();
+ List<ChimeraModel> modelsToMap = new ArrayList<>();
+ List<ChimeraModel> oldList = chimeraManager.getModelList();
boolean alreadyOpen = false;
/*
*/
if (!alreadyOpen)
{
- viewer.openModel(file, pe.getId(), ModelType.PDB_MODEL);
- List<ChimeraModel> newList = viewer.getModelList();
- // JAL-1728 newList.removeAll(oldList) does not work
- for (ChimeraModel cm : newList)
- {
- if (cm.getModelName().equals(pe.getId()))
- {
- modelsToMap.add(cm);
- }
- }
+ chimeraManager.openModel(file, pe.getId(), ModelType.PDB_MODEL);
+ addChimeraModel(pe, modelsToMap);
}
chimeraMaps.put(file, modelsToMap);
}
/**
+ * Adds the ChimeraModel corresponding to the given PDBEntry, based on model
+ * name matching PDB id
+ *
+ * @param pe
+ * @param modelsToMap
+ */
+ protected void addChimeraModel(PDBEntry pe,
+ List<ChimeraModel> modelsToMap)
+ {
+ /*
+ * Chimera: query for actual models and find the one with
+ * matching model name - already set in viewer.openModel()
+ */
+ List<ChimeraModel> newList = chimeraManager.getModelList();
+ // JAL-1728 newList.removeAll(oldList) does not work
+ for (ChimeraModel cm : newList)
+ {
+ if (cm.getModelName().equals(pe.getId()))
+ {
+ modelsToMap.add(cm);
+ }
+ }
+ }
+
+ /**
* Constructor
*
* @param ssm
DataSourceType protocol)
{
super(ssm, pdbentry, sequenceIs, protocol);
- viewer = new ChimeraManager(new StructureManager(true));
+ chimeraManager = new ChimeraManager(new StructureManager(true));
+ chimeraManager.setChimeraX(ViewerType.CHIMERAX.equals(getViewerType()));
+ setStructureCommands(new ChimeraCommands());
+ }
+
+ @Override
+ protected ViewerType getViewerType()
+ {
+ return ViewerType.CHIMERA;
}
/**
- * Starts a thread that waits for the Chimera process to finish, so that we
- * can then close the associated resources. This avoids leaving orphaned
- * Chimera viewer panels in Jalview if the user closes Chimera.
+ * Starts a thread that waits for the Chimera process to finish, so that we can
+ * then close the associated resources. This avoids leaving orphaned Chimera
+ * viewer panels in Jalview if the user closes Chimera.
*/
protected void startChimeraProcessMonitor()
{
- final Process p = viewer.getChimeraProcess();
+ final Process p = chimeraManager.getChimeraProcess();
chimeraMonitor = new Thread(new Runnable()
{
}
/**
- * Start a dedicated HttpServer to listen for Chimera notifications, and tell
- * it to start listening
+ * Start a dedicated HttpServer to listen for Chimera notifications, and tell it
+ * to start listening
*/
public void startChimeraListener()
{
try
{
chimeraListener = new ChimeraListener(this);
- viewer.startListening(chimeraListener.getUri());
+ chimeraManager.startListening(chimeraListener.getUri());
} catch (BindException e)
{
System.err.println(
}
/**
- * Tells Chimera to display only the specified chains
- *
- * @param toshow
- */
- public void showChains(List<String> toshow)
- {
- /*
- * Construct a chimera command like
- *
- * ~display #*;~ribbon #*;ribbon :.A,:.B
- */
- StringBuilder cmd = new StringBuilder(64);
- boolean first = true;
- for (String chain : toshow)
- {
- int modelNumber = getModelNoForChain(chain);
- String showChainCmd = modelNumber == -1 ? ""
- : modelNumber + ":." + chain.split(":")[1];
- if (!first)
- {
- cmd.append(",");
- }
- cmd.append(showChainCmd);
- first = false;
- }
-
- /*
- * could append ";focus" to this command to resize the display to fill the
- * window, but it looks more helpful not to (easier to relate chains to the
- * whole)
- */
- final String command = "~display #*; ~ribbon #*; ribbon :"
- + cmd.toString();
- sendChimeraCommand(command, false);
- }
-
- /**
* Close down the Jalview viewer and listener, and (optionally) the associated
* Chimera window.
*/
+ @Override
public void closeViewer(boolean closeChimera)
{
- getSsm().removeStructureViewerListener(this, this.getStructureFiles());
+ super.closeViewer(closeChimera);
if (closeChimera)
{
- viewer.exitChimera();
+ chimeraManager.exitChimera();
}
if (this.chimeraListener != null)
{
chimeraListener.shutdown();
chimeraListener = null;
}
- viewer = null;
+ chimeraManager = null;
if (chimeraMonitor != null)
{
chimeraMonitor.interrupt();
}
- releaseUIResources();
- }
-
- @Override
- public void colourByChain()
- {
- colourBySequence = false;
- sendAsynchronousCommand("rainbow chain", COLOURING_CHIMERA);
- }
-
- /**
- * Constructs and sends a Chimera command to colour by charge
- * <ul>
- * <li>Aspartic acid and Glutamic acid (negative charge) red</li>
- * <li>Lysine and Arginine (positive charge) blue</li>
- * <li>Cysteine - yellow</li>
- * <li>all others - white</li>
- * </ul>
- */
- @Override
- public void colourByCharge()
- {
- colourBySequence = false;
- String command = "color white;color red ::ASP;color red ::GLU;color blue ::LYS;color blue ::ARG;color yellow ::CYS";
- sendAsynchronousCommand(command, COLOURING_CHIMERA);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String superposeStructures(AlignmentI[] _alignment,
- int[] _refStructure, HiddenColumns[] _hiddenCols)
- {
- StringBuilder allComs = new StringBuilder(128);
- String[] files = getStructureFiles();
-
- if (!waitForFileLoad(files))
- {
- return null;
- }
-
- refreshPdbEntries();
- StringBuilder selectioncom = new StringBuilder(256);
- for (int a = 0; a < _alignment.length; a++)
- {
- int refStructure = _refStructure[a];
- AlignmentI alignment = _alignment[a];
- HiddenColumns hiddenCols = _hiddenCols[a];
-
- if (refStructure >= files.length)
- {
- System.err.println("Ignoring invalid reference structure value "
- + refStructure);
- refStructure = -1;
- }
-
- /*
- * 'matched' bit i will be set for visible alignment columns i where
- * all sequences have a residue with a mapping to the PDB structure
- */
- BitSet matched = new BitSet();
- for (int m = 0; m < alignment.getWidth(); m++)
- {
- if (hiddenCols == null || hiddenCols.isVisible(m))
- {
- matched.set(m);
- }
- }
-
- SuperposeData[] structures = new SuperposeData[files.length];
- for (int f = 0; f < files.length; f++)
- {
- structures[f] = new SuperposeData(alignment.getWidth());
- }
-
- /*
- * Calculate the superposable alignment columns ('matched'), and the
- * corresponding structure residue positions (structures.pdbResNo)
- */
- int candidateRefStructure = findSuperposableResidues(alignment,
- matched, structures);
- if (refStructure < 0)
- {
- /*
- * If no reference structure was specified, pick the first one that has
- * a mapping in the alignment
- */
- refStructure = candidateRefStructure;
- }
-
- int nmatched = matched.cardinality();
- if (nmatched < 4)
- {
- return MessageManager.formatMessage("label.insufficient_residues",
- nmatched);
- }
-
- /*
- * Generate select statements to select regions to superimpose structures
- */
- String[] selcom = new String[files.length];
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- String chainCd = "." + structures[pdbfnum].chain;
- int lpos = -1;
- boolean run = false;
- StringBuilder molsel = new StringBuilder();
-
- int nextColumnMatch = matched.nextSetBit(0);
- while (nextColumnMatch != -1)
- {
- int pdbResNum = structures[pdbfnum].pdbResNo[nextColumnMatch];
- if (lpos != pdbResNum - 1)
- {
- /*
- * discontiguous - append last residue now
- */
- if (lpos != -1)
- {
- molsel.append(String.valueOf(lpos));
- molsel.append(chainCd);
- molsel.append(",");
- }
- run = false;
- }
- else
- {
- /*
- * extending a contiguous run
- */
- if (!run)
- {
- /*
- * start the range selection
- */
- molsel.append(String.valueOf(lpos));
- molsel.append("-");
- }
- run = true;
- }
- lpos = pdbResNum;
- nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
- }
-
- /*
- * and terminate final selection
- */
- if (lpos != -1)
- {
- molsel.append(String.valueOf(lpos));
- molsel.append(chainCd);
- }
- if (molsel.length() > 1)
- {
- selcom[pdbfnum] = molsel.toString();
- selectioncom.append("#").append(String.valueOf(pdbfnum))
- .append(":");
- selectioncom.append(selcom[pdbfnum]);
- selectioncom.append(" ");
- if (pdbfnum < files.length - 1)
- {
- selectioncom.append("| ");
- }
- }
- else
- {
- selcom[pdbfnum] = null;
- }
- }
-
- StringBuilder command = new StringBuilder(256);
- for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
- {
- if (pdbfnum == refStructure || selcom[pdbfnum] == null
- || selcom[refStructure] == null)
- {
- continue;
- }
- if (command.length() > 0)
- {
- command.append(";");
- }
-
- /*
- * Form Chimera match command, from the 'new' structure to the
- * 'reference' structure e.g. (50 residues, chain B/A, alphacarbons):
- *
- * match #1:1-30.B,81-100.B@CA #0:21-40.A,61-90.A@CA
- *
- * @see
- * https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/midas/match.html
- */
- command.append("match ").append(getModelSpec(pdbfnum)).append(":");
- command.append(selcom[pdbfnum]);
- command.append("@").append(
- structures[pdbfnum].isRna ? PHOSPHORUS : ALPHACARBON);
- // JAL-1757 exclude alternate CA locations
- command.append(NO_ALTLOCS);
- command.append(" ").append(getModelSpec(refStructure)).append(":");
- command.append(selcom[refStructure]);
- command.append("@").append(
- structures[refStructure].isRna ? PHOSPHORUS : ALPHACARBON);
- command.append(NO_ALTLOCS);
- }
- if (selectioncom.length() > 0)
- {
- if (debug)
- {
- System.out.println("Select regions:\n" + selectioncom.toString());
- System.out.println(
- "Superimpose command(s):\n" + command.toString());
- }
- allComs.append("~display all; chain @CA|P; ribbon ")
- .append(selectioncom.toString())
- .append(";" + command.toString());
- }
- }
-
- String error = null;
- if (selectioncom.length() > 0)
- {
- // TODO: visually distinguish regions that were superposed
- if (selectioncom.substring(selectioncom.length() - 1).equals("|"))
- {
- selectioncom.setLength(selectioncom.length() - 1);
- }
- if (debug)
- {
- System.out.println("Select regions:\n" + selectioncom.toString());
- }
- allComs.append("; ~display all; chain @CA|P; ribbon ")
- .append(selectioncom.toString()).append("; focus");
- List<String> chimeraReplies = sendChimeraCommand(allComs.toString(),
- true);
- for (String reply : chimeraReplies)
- {
- if (reply.toLowerCase().contains("unequal numbers of atoms"))
- {
- error = reply;
- }
- }
- }
- return error;
}
/**
{
if (pdbfnum < 0 || pdbfnum >= getPdbCount())
{
- return "";
+ return "#" + pdbfnum; // temp hack for ChimeraX
}
/*
*/
public boolean launchChimera()
{
- if (viewer.isChimeraLaunched())
+ if (chimeraManager.isChimeraLaunched())
{
return true;
}
- boolean launched = viewer
- .launchChimera(StructureManager.getChimeraPaths());
+ boolean launched = chimeraManager.launchChimera(getChimeraPaths());
if (launched)
{
startChimeraProcessMonitor();
}
/**
+ * Returns a list of candidate paths to the Chimera program executable
+ *
+ * @return
+ */
+ protected List<String> getChimeraPaths()
+ {
+ return StructureManager.getChimeraPaths(false);
+ }
+
+ /**
* Answers true if the Chimera process is still running, false if ended or not
* started.
*
* @return
*/
- public boolean isChimeraRunning()
+ @Override
+ public boolean isViewerRunning()
{
- return viewer.isChimeraLaunched();
+ return chimeraManager.isChimeraLaunched();
}
/**
* Send a command to Chimera, and optionally log and return any responses.
- * <p>
- * Does nothing, and returns null, if the command is the same as the last one
- * sent [why?].
*
* @param command
* @param getResponse
*/
- public List<String> sendChimeraCommand(final String command,
+ @Override
+ public List<String> executeCommand(final StructureCommandI command,
boolean getResponse)
{
- if (viewer == null)
+ if (chimeraManager == null || command == null)
{
// ? thread running after viewer shut down
return null;
}
List<String> reply = null;
- viewerCommandHistory(false);
- if (true /*lastCommand == null || !lastCommand.equals(command)*/)
+ // trim command or it may never find a match in the replyLog!!
+ String cmd = command.getCommand().trim();
+ List<String> lastReply = chimeraManager
+ .sendChimeraCommand(cmd, getResponse);
+ if (getResponse)
{
- // trim command or it may never find a match in the replyLog!!
- List<String> lastReply = viewer.sendChimeraCommand(command.trim(),
- getResponse);
- if (getResponse)
- {
- reply = lastReply;
- if (debug)
- {
- log("Response from command ('" + command + "') was:\n"
- + lastReply);
- }
- }
+ reply = lastReply;
+ Cache.log.debug(
+ "Response from command ('" + cmd + "') was:\n" + lastReply);
}
- viewerCommandHistory(true);
return reply;
}
- /**
- * Send a Chimera command asynchronously in a new thread. If the progress
- * message is not null, display this message while the command is executing.
- *
- * @param command
- * @param progressMsg
- */
- protected abstract void sendAsynchronousCommand(String command,
- String progressMsg);
-
- /**
- * Sends a set of colour commands to the structure viewer
- *
- * @param colourBySequenceCommands
- */
- @Override
- protected void colourBySequence(
- StructureMappingcommandSet[] colourBySequenceCommands)
- {
- for (StructureMappingcommandSet cpdbbyseq : colourBySequenceCommands)
- {
- for (String command : cpdbbyseq.commands)
- {
- sendAsynchronousCommand(command, COLOURING_CHIMERA);
- }
- }
- }
-
- /**
- * @param files
- * @param sr
- * @param viewPanel
- * @return
- */
- @Override
- protected StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
- {
- return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
- getSequence(), sr, viewPanel);
- }
-
- /**
- * @param command
- */
- protected void executeWhenReady(String command)
- {
- waitForChimera();
- sendChimeraCommand(command, false);
- waitForChimera();
- }
-
- private void waitForChimera()
- {
- while (viewer != null && viewer.isBusy())
- {
- try
- {
- Thread.sleep(15);
- } catch (InterruptedException q)
- {
- }
- }
- }
-
- // End StructureListener
- // //////////////////////////
-
- /**
- * instruct the Jalview binding to update the pdbentries vector if necessary
- * prior to matching the viewer's contents to the list of structure files
- * Jalview knows about.
- */
- public abstract void refreshPdbEntries();
-
- /**
- * map between index of model filename returned from getPdbFile and the first
- * index of models from this file in the viewer. Note - this is not trimmed -
- * use getPdbFile to get number of unique models.
- */
- private int _modelFileNameMap[];
-
- // ////////////////////////////////
- // /StructureListener
@Override
public synchronized String[] getStructureFiles()
{
- if (viewer == null)
+ if (chimeraManager == null)
{
return new String[0];
}
}
/**
- * Construct and send a command to highlight zero, one or more atoms. We do
- * this by sending an "rlabel" command to show the residue label at that
- * position.
+ * Construct and send a command to highlight zero, one or more atoms. We do this
+ * by sending an "rlabel" command to show the residue label at that position.
*/
@Override
public void highlightAtoms(List<AtomSpec> atoms)
return;
}
+ boolean forChimeraX = chimeraManager.isChimeraX();
StringBuilder cmd = new StringBuilder(128);
boolean first = true;
boolean found = false;
{
if (first)
{
- cmd.append("rlabel #").append(cms.get(0).getModelNumber())
- .append(":");
+ cmd.append(forChimeraX ? "label #" : "rlabel #");
}
else
{
cmd.append(",");
}
first = false;
- cmd.append(pdbResNum);
- if (!chain.equals(" "))
+ if (forChimeraX)
{
- cmd.append(".").append(chain);
+ cmd.append(cms.get(0).getModelNumber())
+ .append("/").append(chain).append(":").append(pdbResNum);
+ }
+ else
+ {
+ cmd.append(cms.get(0).getModelNumber())
+ .append(":").append(pdbResNum);
+ if (!chain.equals(" ") && !forChimeraX)
+ {
+ cmd.append(".").append(chain);
+ }
}
found = true;
}
*/
if (lastHighlightCommand != null)
{
- viewer.sendChimeraCommand("~" + lastHighlightCommand, false);
+ chimeraManager.sendChimeraCommand("~" + lastHighlightCommand, false);
}
if (found)
{
- viewer.sendChimeraCommand(command, false);
+ chimeraManager.sendChimeraCommand(command, false);
}
this.lastHighlightCommand = command;
}
/*
* Ask Chimera for its current selection
*/
- List<String> selection = viewer.getSelectedResidueSpecs();
+ List<String> selection = chimeraManager.getSelectedResidueSpecs();
/*
* Parse model number, residue and chain for each selected position,
protected List<AtomSpec> convertStructureResiduesToAlignment(
List<String> structureSelection)
{
- List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
+ boolean chimeraX = chimeraManager.isChimeraX();
+ List<AtomSpec> atomSpecs = new ArrayList<>();
for (String atomSpec : structureSelection)
{
try
{
- AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+ AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec, chimeraX);
String pdbfilename = getPdbFileForModel(spec.getModelNumber());
spec.setPdbFile(pdbfilename);
atomSpecs.add(spec);
System.err.println("## Chimera log: " + message);
}
- private void viewerCommandHistory(boolean enable)
- {
- // log("(Not yet implemented) History "
- // + ((debug || enable) ? "on" : "off"));
- }
-
- public long getLoadNotifiesHandled()
- {
- return loadNotifiesHandled;
- }
-
- @Override
- public void setJalviewColourScheme(ColourSchemeI cs)
- {
- colourBySequence = false;
-
- if (cs == null)
- {
- return;
- }
-
- // Chimera expects RBG values in the range 0-1
- final double normalise = 255D;
- viewerCommandHistory(false);
- StringBuilder command = new StringBuilder(128);
-
- List<String> residueSet = ResidueProperties.getResidues(isNucleotide(),
- false);
- for (String resName : residueSet)
- {
- char res = resName.length() == 3
- ? ResidueProperties.getSingleCharacterCode(resName)
- : resName.charAt(0);
- Color col = cs.findColour(res, 0, null, null, 0f);
- command.append("color " + col.getRed() / normalise + ","
- + col.getGreen() / normalise + "," + col.getBlue() / normalise
- + " ::" + resName + ";");
- }
-
- sendAsynchronousCommand(command.toString(), COLOURING_CHIMERA);
- viewerCommandHistory(true);
- }
-
- /**
- * called when the binding thinks the UI needs to be refreshed after a Chimera
- * state change. this could be because structures were loaded, or because an
- * error has occurred.
- */
- public abstract void refreshGUI();
-
- @Override
- public void setLoadingFromArchive(boolean loadingFromArchive)
- {
- this.loadingFromArchive = loadingFromArchive;
- }
-
- /**
- *
- * @return true if Chimeral is still restoring state or loading is still going
- * on (see setFinsihedLoadingFromArchive)
- */
- @Override
- public boolean isLoadingFromArchive()
- {
- return loadingFromArchive && !loadingFinished;
- }
-
- /**
- * modify flag which controls if sequence colouring events are honoured by the
- * binding. Should be true for normal operation
- *
- * @param finishedLoading
- */
- @Override
- public void setFinishedLoadingFromArchive(boolean finishedLoading)
- {
- loadingFinished = finishedLoading;
- }
-
- /**
- * Send the Chimera 'background solid <color>" command.
- *
- * @see https
- * ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/background
- * .html
- * @param col
- */
- @Override
- public void setBackgroundColour(Color col)
- {
- viewerCommandHistory(false);
- double normalise = 255D;
- final String command = "background solid " + col.getRed() / normalise
- + "," + col.getGreen() / normalise + ","
- + col.getBlue() / normalise + ";";
- viewer.sendChimeraCommand(command, false);
- viewerCommandHistory(true);
- }
-
- /**
- * Ask Chimera to save its session to the given file. Returns true if
- * successful, else false.
- *
- * @param filepath
- * @return
- */
- public boolean saveSession(String filepath)
- {
- if (isChimeraRunning())
- {
- List<String> reply = viewer.sendChimeraCommand("save " + filepath,
- true);
- if (reply.contains("Session written"))
- {
- return true;
- }
- else
- {
- Cache.log
- .error("Error saving Chimera session: " + reply.toString());
- }
- }
- return false;
- }
-
- /**
- * Ask Chimera to open a session file. Returns true if successful, else false.
- * The filename must have a .py extension for this command to work.
- *
- * @param filepath
- * @return
- */
- public boolean openSession(String filepath)
- {
- sendChimeraCommand("open " + filepath, true);
- // todo: test for failure - how?
- return true;
- }
-
- /**
- * Returns a list of chains mapped in this viewer. Note this list is not
- * currently scoped per structure.
- *
- * @return
- */
- @Override
- public List<String> getChainNames()
- {
- return chainNames;
- }
-
- /**
- * Send a 'focus' command to Chimera to recentre the visible display
- */
- public void focusView()
- {
- sendChimeraCommand("focus", false);
- }
-
/**
* Send a 'show' command for all atoms in the currently selected columns
*
/**
* Constructs and send commands to Chimera to set attributes on residues for
- * features visible in Jalview
+ * features visible in Jalview.
+ * <p>
+ * The syntax is: setattr r <attName> <attValue> <atomSpec>
+ * <p>
+ * For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
*
* @param avp
* @return
public int sendFeaturesToViewer(AlignmentViewPanel avp)
{
// TODO refactor as required to pull up to an interface
- AlignmentI alignment = avp.getAlignment();
- String[] files = getStructureFiles();
- if (files == null)
- {
- return 0;
- }
-
- StructureMappingcommandSet commandSet = ChimeraCommands
- .getSetAttributeCommandsForFeatures(getSsm(), files,
- getSequence(), avp);
- String[] commands = commandSet.commands;
- if (commands.length > 10)
+ Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
+ avp);
+ List<StructureCommandI> commands = getCommandGenerator()
+ .setAttributes(featureValues);
+ if (commands.size() > 10)
{
sendCommandsByFile(commands);
}
else
{
- for (String command : commands)
+ for (StructureCommandI command : commands)
{
sendAsynchronousCommand(command, null);
}
}
- return commands.length;
+ return commands.size();
}
/**
- * Write commands to a temporary file, and send a command to Chimera to open
- * the file as a commands script. For use when sending a large number of
- * separate commands would overload the REST interface mechanism.
+ * Write commands to a temporary file, and send a command to Chimera to open the
+ * file as a commands script. For use when sending a large number of separate
+ * commands would overload the REST interface mechanism.
*
* @param commands
*/
- protected void sendCommandsByFile(String[] commands)
+ protected void sendCommandsByFile(List<StructureCommandI> commands)
{
try
{
- File tmp = File.createTempFile("chim", ".com");
+ File tmp = File.createTempFile("chim", getCommandFileExtension());
tmp.deleteOnExit();
PrintWriter out = new PrintWriter(new FileOutputStream(tmp));
- for (String command : commands)
+ for (StructureCommandI command : commands)
{
- out.println(command);
+ out.println(command.getCommand());
}
out.flush();
out.close();
String path = tmp.getAbsolutePath();
- sendAsynchronousCommand("open cmd:" + path, null);
+ StructureCommandI command = getCommandGenerator()
+ .openCommandFile(path);
+ sendAsynchronousCommand(command, null);
} catch (IOException e)
{
System.err.println("Sending commands to Chimera via file failed with "
}
/**
+ * Returns the file extension required for a file of commands to be read by
+ * the structure viewer
+ * @return
+ */
+ protected String getCommandFileExtension()
+ {
+ return ".com";
+ }
+
+ /**
* Get Chimera residues which have the named attribute, find the mapped
* positions in the Jalview sequence(s), and set as sequence features
*
// fails for 'average.bfactor' (which is bad):
String cmd = "list residues attr '" + attName + "'";
- List<String> residues = sendChimeraCommand(cmd, true);
+ List<String> residues = executeCommand(new StructureCommand(cmd), true);
boolean featureAdded = createFeaturesForAttributes(attName, residues);
if (featureAdded)
{
boolean featureAdded = false;
String featureGroup = getViewerFeatureGroup();
+ boolean chimeraX = chimeraManager.isChimeraX();
for (String residue : residues)
{
try
{
- spec = AtomSpec.fromChimeraAtomspec(atomSpec);
+ spec = AtomSpec.fromChimeraAtomspec(atomSpec, chimeraX);
} catch (IllegalArgumentException e)
{
System.err.println("Problem parsing atomspec " + atomSpec);
return CHIMERA_FEATURE_GROUP;
}
- public Hashtable<String, String> getChainFile()
+ @Override
+ public String getModelIdForFile(String pdbFile)
{
- return chainFile;
+ List<ChimeraModel> foundModels = chimeraMaps.get(pdbFile);
+ if (foundModels != null && !foundModels.isEmpty())
+ {
+ return String.valueOf(foundModels.get(0).getModelNumber());
+ }
+ return "";
}
- public List<ChimeraModel> getChimeraModelByChain(String chain)
+ /**
+ * Answers a (possibly empty) list of attribute names in Chimera[X], excluding
+ * any which were added from Jalview
+ *
+ * @return
+ */
+ public List<String> getChimeraAttributes()
+ {
+ List<String> atts = chimeraManager.getAttrList();
+ Iterator<String> it = atts.iterator();
+ while (it.hasNext())
+ {
+ if (it.next().startsWith(ChimeraCommands.NAMESPACE_PREFIX))
+ {
+ /*
+ * attribute added from Jalview - exclude it
+ */
+ it.remove();
+ }
+ }
+ return atts;
+ }
+
+ /**
+ * Returns the file extension to use for a saved viewer session file (.py)
+ *
+ * @return
+ */
+ @Override
+ public String getSessionFileExtension()
{
- return chimeraMaps.get(chainFile.get(chain));
+ return CHIMERA_SESSION_EXTENSION;
}
- public int getModelNoForChain(String chain)
+ @Override
+ public String getHelpURL()
{
- List<ChimeraModel> foundModels = getChimeraModelByChain(chain);
- if (foundModels != null && !foundModels.isEmpty())
- {
- return foundModels.get(0).getModelNumber();
- }
- return -1;
+ return "https://www.cgl.ucsf.edu/chimera/docs/UsersGuide";
}
}
addKeyListener();
- final List<AlignmentPanel> selviews = new ArrayList<>();
+ final List<AlignmentViewPanel> selviews = new ArrayList<>();
final List<AlignmentPanel> origview = new ArrayList<>();
final String menuLabel = MessageManager
.getString("label.copy_format_from");
*/
package jalview.gui;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.ImageExporter.ImageWriterI;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.BrowserLauncher;
-import jalview.util.ImageMaker;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.dbsources.Pdb;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
-import java.awt.Rectangle;
-import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
-import java.util.Vector;
+import java.util.Map;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.gui.ImageExporter.ImageWriterI;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.structure.StructureCommand;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.BrowserLauncher;
+import jalview.util.ImageMaker;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.dbsources.Pdb;
+
public class AppJmol extends StructureViewerBase
{
// ms to wait for Jmol to load files
* @param bounds
* @param viewid
*/
- public AppJmol(String[] files, String[] ids, SequenceI[][] seqs,
- AlignmentPanel ap, boolean usetoColour, boolean useToAlign,
- boolean leaveColouringToJmol, String loadStatus, Rectangle bounds,
- String viewid)
+ public AppJmol(StructureViewerModel viewerModel, AlignmentPanel ap,
+ String sessionFile, String viewid)
{
- PDBEntry[] pdbentrys = new PDBEntry[files.length];
- for (int i = 0; i < pdbentrys.length; i++)
+ Map<File, StructureData> pdbData = viewerModel.getFileData();
+ PDBEntry[] pdbentrys = new PDBEntry[pdbData.size()];
+ SequenceI[][] seqs = new SequenceI[pdbData.size()][];
+ int i = 0;
+ for (StructureData data : pdbData.values())
{
- // PDBEntry pdbentry = new PDBEntry(files[i], ids[i]);
- PDBEntry pdbentry = new PDBEntry(ids[i], null, PDBEntry.Type.PDB,
- files[i]);
+ PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+ PDBEntry.Type.PDB, data.getFilePath());
pdbentrys[i] = pdbentry;
+ List<SequenceI> sequencesForPdb = data.getSeqList();
+ seqs[i] = sequencesForPdb
+ .toArray(new SequenceI[sequencesForPdb.size()]);
+ i++;
}
- // / TODO: check if protocol is needed to be set, and if chains are
+
+ // TODO: check if protocol is needed to be set, and if chains are
// autodiscovered.
jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
pdbentrys, seqs, null);
jmb.setLoadingFromArchive(true);
addAlignmentPanel(ap);
- if (useToAlign)
+ if (viewerModel.isAlignWithPanel())
{
useAlignmentPanelForSuperposition(ap);
}
initMenus();
- if (leaveColouringToJmol || !usetoColour)
+ boolean useToColour = viewerModel.isColourWithAlignPanel();
+ boolean leaveColouringToJmol = viewerModel.isColourByViewer();
+ if (leaveColouringToJmol || !useToColour)
{
jmb.setColourBySequence(false);
seqColour.setSelected(false);
viewerColour.setSelected(true);
}
- else if (usetoColour)
+ else if (useToColour)
{
useAlignmentPanelForColourbyseq(ap);
jmb.setColourBySequence(true);
seqColour.setSelected(true);
viewerColour.setSelected(false);
}
- this.setBounds(bounds);
+
+ this.setBounds(viewerModel.getX(), viewerModel.getY(),
+ viewerModel.getWidth(), viewerModel.getHeight());
setViewId(viewid);
- // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
- // bounds.width,bounds.height);
this.addInternalFrameListener(new InternalFrameAdapter()
{
closeViewer(false);
}
});
- initJmol(loadStatus); // pdbentry, seq, JBPCHECK!
+ StringBuilder cmd = new StringBuilder();
+ cmd.append("load FILES ").append(QUOTE)
+ .append(Platform.escapeBackslashes(sessionFile)).append(QUOTE);
+ initJmol(cmd.toString());
}
@Override
{
super.initMenus();
- viewerActionMenu.setText(MessageManager.getString("label.jmol"));
-
viewerColour
.setText(MessageManager.getString("label.colour_with_jmol"));
viewerColour.setToolTipText(MessageManager
.getString("label.let_jmol_manage_structure_colours"));
}
- IProgressIndicator progressBar = null;
-
- @Override
- protected IProgressIndicator getIProgressIndicator()
- {
- return progressBar;
- }
-
/**
* display a single PDB structure in a new Jmol view
*
public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
final AlignmentPanel ap)
{
- progressBar = ap.alignFrame;
+ setProgressIndicator(ap.alignFrame);
openNewJmol(ap, alignAddedStructures, new PDBEntry[] { pdbentry },
new SequenceI[][]
PDBEntry[] pdbentrys,
SequenceI[][] seqs)
{
- progressBar = ap.alignFrame;
+ setProgressIndicator(ap.alignFrame);
jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
pdbentrys, seqs, null);
addAlignmentPanel(ap);
useAlignmentPanelForColourbyseq(ap);
alignAddedStructures = alignAdded;
- useAlignmentPanelForSuperposition(ap);
+ if (pdbentrys.length > 1)
+ {
+ useAlignmentPanelForSuperposition(ap);
+ }
jmb.setColourBySequence(true);
setSize(400, 400); // probably should be a configurable/dynamic default here
{
command = "";
}
- jmb.evalStateCommand(command);
- jmb.evalStateCommand("set hoverDelay=0.1");
+ jmb.executeCommand(new StructureCommand(command), false);
+ jmb.executeCommand(new StructureCommand("set hoverDelay=0.1"), false);
jmb.setFinishedInit(true);
}
@Override
- void showSelectedChains()
- {
- Vector<String> toshow = new Vector<>();
- for (int i = 0; i < chainMenu.getItemCount(); i++)
- {
- if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
- {
- JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
- if (item.isSelected())
- {
- toshow.addElement(item.getText());
- }
- }
- }
- jmb.centerViewer(toshow);
- }
-
- @Override
- public void closeViewer(boolean closeExternalViewer)
- {
- // Jmol does not use an external viewer
- if (jmb != null)
- {
- jmb.closeViewer();
- }
- setAlignmentPanel(null);
- _aps.clear();
- _alignwith.clear();
- _colourwith.clear();
- // TODO: check for memory leaks where instance isn't finalised because jmb
- // holds a reference to the window
- jmb = null;
- }
-
- @Override
public void run()
{
_started = true;
cmd.append("loadingJalviewdata=true\nload APPEND ");
cmd.append(filesString);
cmd.append("\nloadingJalviewdata=null");
- final String command = cmd.toString();
+ final StructureCommand command = new StructureCommand(cmd.toString());
lastnotify = jmb.getLoadNotifiesHandled();
try
{
- jmb.evalStateCommand(command);
+ jmb.executeCommand(command, false);
} catch (OutOfMemoryError oomerror)
{
new OOMWarning("When trying to add structures to the Jmol viewer!",
try
{
Cache.log.debug("Waiting around for jmb notify.");
- Thread.sleep(waitFor);
waitTotal += waitFor;
+
+ // Thread.sleep() throws an exception in JS
+ Thread.sleep(waitFor);
} catch (Exception e)
{
}
}
// refresh the sequence colours for the new structure(s)
- for (AlignmentPanel ap : _colourwith)
+ for (AlignmentViewPanel ap : _colourwith)
{
jmb.updateColours(ap);
}
@Override
public void run()
{
- if (jmb.viewer.isScriptExecuting())
+ if (jmb.jmolViewer.isScriptExecuting())
{
SwingUtilities.invokeLater(this);
try
}
else
{
- alignStructs_withAllAlignPanels();
+ alignStructsWithAllAlignPanels();
}
}
});
AlignmentI pdbseq = null;
pdbid = jmb.getPdbEntry(pi).getId();
long hdl = pdbid.hashCode() - System.currentTimeMillis();
- if (progressBar != null)
- {
- progressBar.setProgressBar(MessageManager
- .formatMessage("status.fetching_pdb", new String[]
- { pdbid }), hdl);
- }
+ setProgressMessage(MessageManager
+ .formatMessage("status.fetching_pdb", new String[]
+ { pdbid }), hdl);
try
{
pdbseq = pdbclient.getSequenceRecords(pdbid);
errormsgs.append("'").append(pdbid).append("'");
} finally
{
- if (progressBar != null)
- {
- progressBar.setProgressBar(
- MessageManager.getString("label.state_completed"),
- hdl);
- }
+ setProgressMessage(
+ MessageManager.getString("label.state_completed"), hdl);
}
if (pdbseq != null)
{
*
* @param type
*/
+ @Override
public void makePDBImage(ImageMaker.TYPE type)
{
int width = getWidth();
@Override
public void exportImage(Graphics g) throws Exception
{
- jmb.viewer.renderScreenImage(g, width, height);
+ jmb.jmolViewer.renderScreenImage(g, width, height);
}
};
String view = MessageManager.getString("action.view").toLowerCase();
ImageExporter exporter = new ImageExporter(writer,
- jmb.getIProgressIndicator(), type, getTitle());
+ getProgressIndicator(), type, getTitle());
exporter.doExport(null, this, width, height, view);
}
@Override
- public void showHelp_actionPerformed(ActionEvent actionEvent)
+ public void showHelp_actionPerformed()
{
try
{
.openURL("http://wiki.jmol.org");//http://jmol.sourceforge.net/docs/JmolUserGuide/");
} catch (Exception ex)
{
+ System.err.println("Show Jmol help failed with: " + ex.getMessage());
}
}
+ @Override
public void showConsole(boolean showConsole)
{
-
if (showConsole)
{
if (splitPane == null)
}
}
}
- else if (jmb == null || jmb.viewer == null || !jmb.isFinishedInit())
+ else if (jmb == null || jmb.jmolViewer == null || !jmb.isFinishedInit())
{
g.setColor(Color.black);
g.fillRect(0, 0, currentSize.width, currentSize.height);
}
else
{
- jmb.viewer.renderScreenImage(g, currentSize.width,
+ jmb.jmolViewer.renderScreenImage(g, currentSize.width,
currentSize.height);
}
}
}
@Override
- public String getStateInfo()
- {
- return jmb == null ? null : jmb.viewer.getStateInfo();
- }
-
- @Override
public ViewerType getViewerType()
{
return ViewerType.JMOL;
*/
package jalview.gui;
+import java.awt.Container;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JComponent;
+
+import org.jmol.api.JmolAppConsoleInterface;
+import org.openscience.jmol.app.jmolpanel.console.AppConsole;
+
import jalview.api.AlignmentViewPanel;
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
import jalview.util.Platform;
-
-import java.awt.Container;
-import java.io.File;
-import java.util.List;
-import java.util.Map;
-
-import org.jmol.api.JmolAppConsoleInterface;
-
import javajs.util.BS;
public class AppJmolBinding extends JalviewJmolBinding
{
- protected AppJmol appJmolWindow;
-
public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm,
PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
DataSourceType protocol)
{
super(sSm, pdbentry, sequenceIs, protocol);
- appJmolWindow = appJmol;
- }
-
- @Override
- protected IProgressIndicator getIProgressIndicator()
- {
- return appJmolWindow.progressBar;
+ setViewer(appJmol);
}
@Override
@Override
public void refreshGUI()
{
+ if (getMappedStructureCount() == 0)
+ {
+ // too soon!
+ return;
+ }
// appJmolWindow.repaint();
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
- appJmolWindow.updateTitleAndMenus();
- // initiates a colourbySequence
- // via seqColour_ActionPerformed.
- appJmolWindow.revalidate();
+ JalviewStructureDisplayI theViewer = getViewer();
+ // invokes colourbySequence() via seqColour_ActionPerformed()
+ theViewer.updateTitleAndMenus();
+ ((JComponent) theViewer).revalidate();
}
});
}
@Override
- public void updateColours(Object source)
- {
- AlignmentPanel ap = (AlignmentPanel) source;
- // ignore events from panels not used to colour this view
- if (!appJmolWindow.isUsedforcolourby(ap))
- {
- return;
- }
- if (!isLoadingFromArchive())
- {
- colourBySequence(ap);
- }
- }
-
- @Override
public void notifyScriptTermination(String strStatus, int msWalltime)
{
// todo - script termination doesn't happen ?
@Override
public void selectionChanged(BS arg0)
{
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void refreshPdbEntries()
- {
- // TODO Auto-generated method stub
-
}
@Override
public void showConsole(boolean b)
{
- appJmolWindow.showConsole(b);
+ getViewer().showConsole(b);
}
@Override
protected JmolAppConsoleInterface createJmolConsole(
Container consolePanel, String buttonsToShow)
{
- viewer.setJmolCallbackListener(this);
- return null;//BH can't do this yet. new AppConsole(viewer, consolePanel, buttonsToShow);
+ jmolViewer.setJmolCallbackListener(this);
+ // BH comment: can't do this yet [for JS only, or generally?]
+ return Platform.isJS() ? null
+ : new AppConsole(jmolViewer, consolePanel, buttonsToShow);
}
@Override
protected void releaseUIResources()
{
- appJmolWindow = null;
+ setViewer(null);
closeConsole();
}
{
if (svl instanceof SeqPanel)
{
- appJmolWindow.removeAlignmentPanel(((SeqPanel) svl).ap);
+ getViewer().removeAlignmentPanel(((SeqPanel) svl).ap);
}
}
return null;
}
- @Override
- public JalviewStructureDisplayI getViewer()
- {
- return appJmolWindow;
- }
-
- @Override
- public jalview.api.FeatureRenderer getFeatureRenderer(
- AlignmentViewPanel alignment)
- {
- AlignmentPanel ap = (alignment == null)
- ? appJmolWindow.getAlignmentPanel()
- : (AlignmentPanel) alignment;
- if (ap.av.isShowSequenceFeatures())
- {
- return ap.av.getAlignPanel().getSeqPanel().seqCanvas.fr;
- }
-
- return null;
- }
-
@SuppressWarnings("unused")
public void cacheFiles(List<File> files)
{
*/
package jalview.gui;
-import jalview.api.FeatureRenderer;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.ext.rbvi.chimera.ChimeraCommands;
-import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.DataSourceType;
-import jalview.io.StructureFile;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.BrowserLauncher;
-import jalview.util.ImageMaker.TYPE;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.dbsources.Pdb;
-
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Random;
+import java.util.Map;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.ImageMaker.TYPE;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
/**
* GUI elements for handling an external chimera display
*
{
private JalviewChimeraBinding jmb;
- private IProgressIndicator progressBar = null;
-
/*
* Path to Chimera session file. This is set when an open Jalview/Chimera
* session is saved, or on restore from a Jalview project (if it holds the
*/
private String chimeraSessionFile = null;
- private Random random = new Random();
-
private int myWidth = 500;
private int myHeight = 150;
{
super.initMenus();
- viewerActionMenu.setText(MessageManager.getString("label.chimera"));
-
- viewerColour
- .setText(MessageManager.getString("label.colour_with_chimera"));
- viewerColour.setToolTipText(MessageManager
- .getString("label.let_chimera_manage_structure_colours"));
-
- helpItem.setText(MessageManager.getString("label.chimera_help"));
savemenu.setVisible(false); // not yet implemented
viewMenu.add(fitToWindow);
JMenuItem writeFeatures = new JMenuItem(
- MessageManager.getString("label.create_chimera_attributes"));
+ MessageManager.getString("label.create_viewer_attributes"));
writeFeatures.setToolTipText(MessageManager
- .getString("label.create_chimera_attributes_tip"));
+ .getString("label.create_viewer_attributes_tip"));
writeFeatures.addActionListener(new ActionListener()
{
@Override
*/
protected void buildAttributesMenu(JMenu attributesMenu)
{
- List<String> atts = jmb.sendChimeraCommand("list resattr", true);
- if (atts == null)
- {
- return;
- }
+ List<String> atts = jmb.getChimeraAttributes();
attributesMenu.removeAll();
Collections.sort(atts);
- for (String att : atts)
+ for (String attName : atts)
{
- final String attName = att.split(" ")[1];
-
- /*
- * ignore 'jv_*' attributes, as these are Jalview features that have
- * been transferred to residue attributes in Chimera!
- */
- if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX))
+ JMenuItem menuItem = new JMenuItem(attName);
+ menuItem.addActionListener(new ActionListener()
{
- JMenuItem menuItem = new JMenuItem(attName);
- menuItem.addActionListener(new ActionListener()
+ @Override
+ public void actionPerformed(ActionEvent e)
{
- @Override
- public void actionPerformed(ActionEvent e)
- {
- getChimeraAttributes(attName);
- }
- });
- attributesMenu.add(menuItem);
- }
+ getChimeraAttributes(attName);
+ }
+ });
+ attributesMenu.add(menuItem);
}
}
}
/**
- * Send a command to Chimera to create residue attributes for Jalview features
- * <p>
- * The syntax is: setattr r <attName> <attValue> <atomSpec>
- * <p>
- * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
+ * Sends command(s) to the structure viewer to create residue attributes for
+ * visible Jalview features
*/
protected void sendFeaturesToChimera()
{
+ // todo pull up?
int count = jmb.sendFeaturesToViewer(getAlignmentPanel());
statusBar.setText(
MessageManager.formatMessage("label.attributes_set", count));
*/
protected void createProgressBar()
{
- if (progressBar == null)
+ if (getProgressIndicator() == null)
{
- progressBar = new ProgressBar(statusPanel, statusBar);
+ setProgressIndicator(new ProgressBar(statusPanel, statusBar));
}
}
SequenceI[][] seqs)
{
createProgressBar();
- jmb = new JalviewChimeraBindingModel(this,
- ap.getStructureSelectionManager(), pdbentrys, seqs, null);
+ jmb = newBindingModel(ap, pdbentrys, seqs);
addAlignmentPanel(ap);
useAlignmentPanelForColourbyseq(ap);
}
+ protected JalviewChimeraBindingModel newBindingModel(AlignmentPanel ap,
+ PDBEntry[] pdbentrys, SequenceI[][] seqs)
+ {
+ return new JalviewChimeraBindingModel(this,
+ ap.getStructureSelectionManager(), pdbentrys, seqs, null);
+ }
+
/**
* Create a new viewer from saved session state data including Chimera session
* file
* @param colourBySequence
* @param newViewId
*/
- public ChimeraViewFrame(String chimeraSessionFile,
- AlignmentPanel alignPanel, PDBEntry[] pdbArray,
- SequenceI[][] seqsArray, boolean colourByChimera,
- boolean colourBySequence, String newViewId)
+ public ChimeraViewFrame(StructureViewerModel viewerData,
+ AlignmentPanel alignPanel, String sessionFile, String vid)
{
this();
- setViewId(newViewId);
- this.chimeraSessionFile = chimeraSessionFile;
+ setViewId(vid);
+ this.chimeraSessionFile = sessionFile;
+ Map<File, StructureData> pdbData = viewerData.getFileData();
+ PDBEntry[] pdbArray = new PDBEntry[pdbData.size()];
+ SequenceI[][] seqsArray = new SequenceI[pdbData.size()][];
+ int i = 0;
+ for (StructureData data : pdbData.values())
+ {
+ PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+ PDBEntry.Type.PDB, data.getFilePath());
+ pdbArray[i] = pdbentry;
+ List<SequenceI> sequencesForPdb = data.getSeqList();
+ seqsArray[i] = sequencesForPdb
+ .toArray(new SequenceI[sequencesForPdb.size()]);
+ i++;
+ }
openNewChimera(alignPanel, pdbArray, seqsArray);
- if (colourByChimera)
+ if (viewerData.isColourByViewer())
{
jmb.setColourBySequence(false);
seqColour.setSelected(false);
viewerColour.setSelected(true);
}
- else if (colourBySequence)
+ else if (viewerData.isColourWithAlignPanel())
{
jmb.setColourBySequence(true);
seqColour.setSelected(true);
if (!jmb.launchChimera())
{
JvOptionPane.showMessageDialog(Desktop.desktop,
- MessageManager.getString("label.chimera_failed"),
+ MessageManager.formatMessage("label.open_viewer_failed",
+ getViewerName()),
MessageManager.getString("label.error_loading_file"),
JvOptionPane.ERROR_MESSAGE);
this.dispose();
}
/**
- * Show only the selected chain(s) in the viewer
- */
- @Override
- void showSelectedChains()
- {
- List<String> toshow = new ArrayList<>();
- for (int i = 0; i < chainMenu.getItemCount(); i++)
- {
- if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
- {
- JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
- if (item.isSelected())
- {
- toshow.add(item.getText());
- }
- }
- }
- jmb.showChains(toshow);
- }
-
- /**
- * Close down this instance of Jalview's Chimera viewer, giving the user the
- * option to close the associated Chimera window (process). They may wish to
- * keep it open until they have had an opportunity to save any work.
- *
- * @param closeChimera
- * if true, close any linked Chimera process; if false, prompt first
- */
- @Override
- public void closeViewer(boolean closeChimera)
- {
- if (jmb != null && jmb.isChimeraRunning())
- {
- if (!closeChimera)
- {
- String prompt = MessageManager
- .formatMessage("label.confirm_close_chimera", new Object[]
- { jmb.getViewerTitle(getViewerName(), false) });
- prompt = JvSwingUtils.wrapTooltip(true, prompt);
- int confirm = JvOptionPane.showConfirmDialog(this, prompt,
- MessageManager.getString("label.close_viewer"),
- JvOptionPane.YES_NO_CANCEL_OPTION);
- /*
- * abort closure if user hits escape or Cancel
- */
- if (confirm == JvOptionPane.CANCEL_OPTION
- || confirm == JvOptionPane.CLOSED_OPTION)
- {
- return;
- }
- closeChimera = confirm == JvOptionPane.YES_OPTION;
- }
- jmb.closeViewer(closeChimera);
- }
- setAlignmentPanel(null);
- _aps.clear();
- _alignwith.clear();
- _colourwith.clear();
- // TODO: check for memory leaks where instance isn't finalised because jmb
- // holds a reference to the window
- jmb = null;
- dispose();
- }
-
- /**
* Open any newly added PDB structures in Chimera, having first fetched data
* from PDB (if not already saved).
*/
pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
jmb.getChains()[pos], pe.getFile(), protocol,
- progressBar);
- stashFoundChains(pdb, pe.getFile());
+ getProgressIndicator());
+ jmb.stashFoundChains(pdb, pe.getFile());
} catch (OutOfMemoryError oomerror)
{
}
// refresh the sequence colours for the new structure(s)
- for (AlignmentPanel ap : _colourwith)
+ for (AlignmentViewPanel ap : _colourwith)
{
jmb.updateColours(ap);
}
@Override
public void run()
{
- alignStructs_withAllAlignPanels();
+ alignStructsWithAllAlignPanels();
}
}).start();
}
worker = null;
}
- /**
- * Fetch PDB data and save to a local file. Returns the full path to the file,
- * or null if fetch fails. TODO: refactor to common with Jmol ? duplication
- *
- * @param processingEntry
- * @return
- * @throws Exception
- */
-
- private void stashFoundChains(StructureFile pdb, String file)
- {
- for (int i = 0; i < pdb.getChains().size(); i++)
- {
- String chid = new String(
- pdb.getId() + ":" + pdb.getChains().elementAt(i).id);
- jmb.getChainNames().add(chid);
- jmb.getChainFile().put(chid, file);
- }
- }
-
- private String fetchPdbFile(PDBEntry processingEntry) throws Exception
- {
- String filePath = null;
- Pdb pdbclient = new Pdb();
- AlignmentI pdbseq = null;
- String pdbid = processingEntry.getId();
- long handle = System.currentTimeMillis()
- + Thread.currentThread().hashCode();
-
- /*
- * Write 'fetching PDB' progress on AlignFrame as we are not yet visible
- */
- String msg = MessageManager.formatMessage("status.fetching_pdb",
- new Object[]
- { pdbid });
- getAlignmentPanel().alignFrame.setProgressBar(msg, handle);
- // long hdl = startProgressBar(MessageManager.formatMessage(
- // "status.fetching_pdb", new Object[]
- // { pdbid }));
- try
- {
- pdbseq = pdbclient.getSequenceRecords(pdbid);
- } catch (OutOfMemoryError oomerror)
- {
- new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
- } finally
- {
- msg = pdbid + " " + MessageManager.getString("label.state_completed");
- getAlignmentPanel().alignFrame.setProgressBar(msg, handle);
- // stopProgressBar(msg, hdl);
- }
- /*
- * If PDB data were saved and are not invalid (empty alignment), return the
- * file path.
- */
- if (pdbseq != null && pdbseq.getHeight() > 0)
- {
- // just use the file name from the first sequence's first PDBEntry
- filePath = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
- .elementAt(0).getFile()).getAbsolutePath();
- processingEntry.setFile(filePath);
- }
- return filePath;
- }
-
- /**
- * Convenience method to update the progress bar if there is one. Be sure to
- * call stopProgressBar with the returned handle to remove the message.
- *
- * @param msg
- * @param handle
- */
- public long startProgressBar(String msg)
- {
- // TODO would rather have startProgress/stopProgress as the
- // IProgressIndicator interface
- long tm = random.nextLong();
- if (progressBar != null)
- {
- progressBar.setProgressBar(msg, tm);
- }
- return tm;
- }
-
- /**
- * End the progress bar with the specified handle, leaving a message (if not
- * null) on the status bar
- *
- * @param msg
- * @param handle
- */
- public void stopProgressBar(String msg, long handle)
- {
- if (progressBar != null)
- {
- progressBar.setProgressBar(msg, handle);
- }
- }
-
@Override
public void makePDBImage(TYPE imageType)
{
}
@Override
- public void showHelp_actionPerformed(ActionEvent actionEvent)
- {
- try
- {
- BrowserLauncher
- .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
- } catch (IOException ex)
- {
- }
- }
-
- @Override
public AAStructureBindingModel getBinding()
{
return jmb;
}
- /**
- * Ask Chimera to save its session to the designated file path, or to a
- * temporary file if the path is null. Returns the file path if successful,
- * else null.
- *
- * @param filepath
- * @see getStateInfo
- */
- protected String saveSession(String filepath)
- {
- String pathUsed = filepath;
- try
- {
- if (pathUsed == null)
- {
- File tempFile = File.createTempFile("chimera", ".py");
- tempFile.deleteOnExit();
- pathUsed = tempFile.getPath();
- }
- boolean result = jmb.saveSession(pathUsed);
- if (result)
- {
- this.chimeraSessionFile = pathUsed;
- return pathUsed;
- }
- } catch (IOException e)
- {
- }
- return null;
- }
-
- /**
- * Returns a string representing the state of the Chimera session. This is
- * done by requesting Chimera to save its session to a temporary file, then
- * reading the file contents. Returns an empty string on any error.
- */
- @Override
- public String getStateInfo()
- {
- String sessionFile = saveSession(null);
- if (sessionFile == null)
- {
- return "";
- }
- InputStream is = null;
- try
- {
- File f = new File(sessionFile);
- byte[] bytes = new byte[(int) f.length()];
- is = new FileInputStream(sessionFile);
- is.read(bytes);
- return new String(bytes);
- } catch (IOException e)
- {
- return "";
- } finally
- {
- if (is != null)
- {
- try
- {
- is.close();
- } catch (IOException e)
- {
- // ignore
- }
- }
- }
- }
-
@Override
protected void fitToWindow_actionPerformed()
{
{
return "Chimera";
}
-
- /**
- * Sends commands to align structures according to associated alignment(s).
- *
- * @return
- */
- @Override
- protected String alignStructs_withAllAlignPanels()
- {
- String reply = super.alignStructs_withAllAlignPanels();
- if (reply != null)
- {
- statusBar.setText("Superposition failed: " + reply);
- }
- return reply;
- }
-
- @Override
- protected IProgressIndicator getIProgressIndicator()
- {
- return progressBar;
- }
}
--- /dev/null
+package jalview.gui;
+
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.gui.StructureViewer.ViewerType;
+
+/**
+ * A class for the gui frame through which Jalview interacts with the ChimeraX
+ * structure viewer. Mostly the same as ChimeraViewFrame with a few overrides
+ * for the differences.
+ *
+ * @author gmcarstairs
+ *
+ */
+public class ChimeraXViewFrame extends ChimeraViewFrame
+{
+
+ public ChimeraXViewFrame(PDBEntry pdb, SequenceI[] seqsForPdb,
+ String[] chains, AlignmentPanel ap)
+ {
+ super(pdb, seqsForPdb, chains, ap);
+ }
+
+ public ChimeraXViewFrame(PDBEntry[] pdbsForFile, boolean superposeAdded,
+ SequenceI[][] theSeqs, AlignmentPanel ap)
+ {
+ super(pdbsForFile, superposeAdded, theSeqs, ap);
+ }
+
+ /**
+ * Constructor given a session file to be loaded
+ *
+ * @param viewerData
+ * @param alignPanel
+ * @param sessionFile
+ * @param vid
+ */
+ public ChimeraXViewFrame(StructureViewerModel viewerData,
+ AlignmentPanel alignPanel, String sessionFile, String vid)
+ {
+ super(viewerData, alignPanel, sessionFile, vid);
+ }
+
+ @Override
+ public ViewerType getViewerType()
+ {
+ return ViewerType.CHIMERAX;
+ }
+
+ @Override
+ protected String getViewerName()
+ {
+ return "ChimeraX";
+ }
+
+ @Override
+ protected JalviewChimeraBindingModel newBindingModel(AlignmentPanel ap,
+ PDBEntry[] pdbentrys, SequenceI[][] seqs)
+ {
+ return new JalviewChimeraXBindingModel(this,
+ ap.getStructureSelectionManager(), pdbentrys, seqs, null);
+ }
+}
import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
-import jalview.viewmodel.seqfeatures.FeatureRendererModel;
+import javax.swing.JComponent;
import javax.swing.SwingUtilities;
public class JalviewChimeraBindingModel extends JalviewChimeraBinding
{
- private ChimeraViewFrame cvf;
-
public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame,
StructureSelectionManager ssm, PDBEntry[] pdbentry,
SequenceI[][] sequenceIs, DataSourceType protocol)
{
super(ssm, pdbentry, sequenceIs, protocol);
- cvf = chimeraViewFrame;
- }
-
- @Override
- public FeatureRendererModel getFeatureRenderer(AlignmentViewPanel alignment)
- {
- AlignmentPanel ap = (alignment == null) ? cvf.getAlignmentPanel()
- : (AlignmentPanel) alignment;
- if (ap.av.isShowSequenceFeatures())
- {
- return ap.getSeqPanel().seqCanvas.fr;
- }
-
- return null;
+ setViewer(chimeraViewFrame);
}
@Override
@Override
public void run()
{
- cvf.updateTitleAndMenus();
- cvf.revalidate();
+ JalviewStructureDisplayI theViewer = getViewer();
+ theViewer.updateTitleAndMenus();
+ ((JComponent) theViewer).revalidate();
}
});
}
-
- @Override
- public void updateColours(Object source)
- {
- AlignmentPanel ap = (AlignmentPanel) source;
- // ignore events from panels not used to colour this view
- if (!cvf.isUsedforcolourby(ap))
- {
- return;
- }
- if (!isLoadingFromArchive())
- {
- colourBySequence(ap);
- }
- }
-
- @Override
- public void releaseReferences(Object svl)
- {
- }
-
- @Override
- protected void releaseUIResources()
- {
- }
-
- @Override
- public void refreshPdbEntries()
- {
- }
-
- /**
- * Send an asynchronous command to Chimera, in a new thread, optionally with
- * an 'in progress' message in a progress bar somewhere
- */
- @Override
- protected void sendAsynchronousCommand(final String command,
- final String progressMsg)
- {
- final long handle = progressMsg == null ? 0
- : cvf.startProgressBar(progressMsg);
- SwingUtilities.invokeLater(new Runnable()
- {
- @Override
- public void run()
- {
- try
- {
- sendChimeraCommand(command, false);
- } finally
- {
- if (progressMsg != null)
- {
- cvf.stopProgressBar(null, handle);
- }
- }
- }
- });
- }
-
- @Override
- public JalviewStructureDisplayI getViewer()
- {
- return cvf;
- }
}
--- /dev/null
+package jalview.gui;
+
+import java.util.List;
+
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
+import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
+import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.ChimeraXCommands;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureSelectionManager;
+
+public class JalviewChimeraXBindingModel extends JalviewChimeraBindingModel
+{
+
+ public static final String CHIMERAX_SESSION_EXTENSION = ".cxs";
+
+ public JalviewChimeraXBindingModel(ChimeraViewFrame chimeraViewFrame,
+ StructureSelectionManager ssm, PDBEntry[] pdbentry,
+ SequenceI[][] sequenceIs, DataSourceType protocol)
+ {
+ super(chimeraViewFrame, ssm, pdbentry, sequenceIs, protocol);
+ setStructureCommands(new ChimeraXCommands());
+ }
+
+ @Override
+ protected List<String> getChimeraPaths()
+ {
+ return StructureManager.getChimeraPaths(true);
+ }
+
+ @Override
+ protected void addChimeraModel(PDBEntry pe,
+ List<ChimeraModel> modelsToMap)
+ {
+ /*
+ * ChimeraX hack: force chimera model name to pdbId here
+ */
+ int modelNumber = chimeraMaps.size() + 1;
+ String command = "setattr #" + modelNumber + " models name "
+ + pe.getId();
+ executeCommand(new StructureCommand(command), false);
+ modelsToMap.add(new ChimeraModel(pe.getId(), ModelType.PDB_MODEL,
+ modelNumber, 0));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return
+ */
+ @Override
+ protected String getCommandFileExtension()
+ {
+ return ".cxc";
+ }
+
+ /**
+ * Returns the file extension to use for a saved viewer session file (.cxs)
+ *
+ * @return
+ * @see https://www.cgl.ucsf.edu/chimerax/docs/user/commands/save.html#sesformat
+ */
+ @Override
+ public String getSessionFileExtension()
+ {
+ return CHIMERAX_SESSION_EXTENSION;
+ }
+
+ @Override
+ public String getHelpURL()
+ {
+ return "http://www.rbvi.ucsf.edu/chimerax/docs/user/index.html";
+ }
+
+ @Override
+ protected ViewerType getViewerType()
+ {
+ return ViewerType.CHIMERAX;
+ }
+
+ @Override
+ protected String getModelId(int pdbfnum, String file)
+ {
+ return String.valueOf(pdbfnum + 1);
+ }
+
+}
*/
package jalview.gui;
-import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
-import jalview.bin.Cache;
-import jalview.gui.Help.HelpId;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.BackupFiles;
-import jalview.io.BackupFilesPresetEntry;
-import jalview.io.FileFormatI;
-import jalview.io.JalviewFileChooser;
-import jalview.io.JalviewFileView;
-import jalview.jbgui.GPreferences;
-import jalview.jbgui.GSequenceLink;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemes;
-import jalview.schemes.ResidueColourScheme;
-import jalview.urls.UrlLinkTableModel;
-import jalview.urls.api.UrlProviderFactoryI;
-import jalview.urls.api.UrlProviderI;
-import jalview.urls.desktop.DesktopUrlProviderFactory;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.util.UrlConstants;
-import jalview.ws.sifts.SiftsSettings;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.table.TableRowSorter;
import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
+import jalview.bin.Cache;
+import jalview.ext.pymol.PymolManager;
+import jalview.gui.Help.HelpId;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.FileFormatI;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.jbgui.GPreferences;
+import jalview.jbgui.GSequenceLink;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemes;
+import jalview.schemes.ResidueColourScheme;
+import jalview.urls.UrlLinkTableModel;
+import jalview.urls.api.UrlProviderFactoryI;
+import jalview.urls.api.UrlProviderI;
+import jalview.urls.desktop.DesktopUrlProviderFactory;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.util.UrlConstants;
+import jalview.ws.sifts.SiftsSettings;
/**
* DOCUMENT ME!
public static final String CHIMERA_PATH = "CHIMERA_PATH";
+ public static final String CHIMERAX_PATH = "CHIMERAX_PATH";
+
+ public static final String PYMOL_PATH = "PYMOL_PATH";
+
public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
.setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, false));
/*
- * Set Structure tab defaults.
+ * Set Structure tab defaults
*/
final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, false);
structFromPdb.setSelected(structSelected);
addSecondaryStructure.setEnabled(structSelected);
addTempFactor.setSelected(Cache.getDefault(ADD_TEMPFACT_ANN, false));
addTempFactor.setEnabled(structSelected);
- structViewer.setSelectedItem(
- Cache.getDefault(STRUCTURE_DISPLAY, ViewerType.JMOL.name()));
- chimeraPath.setText(Cache.getDefault(CHIMERA_PATH, ""));
- chimeraPath.addActionListener(new ActionListener()
+
+ /*
+ * set choice of structure viewer, and path if saved as a preference;
+ * default to Jmol (first choice) if an unexpected value is found
+ */
+ String viewerType = Cache.getDefault(STRUCTURE_DISPLAY, ViewerType.JMOL.name());
+ structViewer.setSelectedItem(viewerType);
+ String viewerPath = "";
+ ViewerType type = null;
+ try
+ {
+ type = ViewerType.valueOf(viewerType);
+ switch (type)
+ {
+ case JMOL:
+ break;
+ case CHIMERA:
+ viewerPath = Cache.getDefault(CHIMERA_PATH, "");
+ break;
+ case CHIMERAX:
+ viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
+ break;
+ case PYMOL:
+ viewerPath = Cache.getDefault(PYMOL_PATH, "");
+ break;
+ }
+ } catch (IllegalArgumentException e)
+ {
+ Cache.log.error("Unknown structure viewer type: " + viewerType
+ + ", defaulting to Jmol");
+ type = ViewerType.JMOL;
+ }
+ structureViewerPath.setText(viewerPath);
+
+ structureViewerPath.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- validateChimeraPath();
+ if (validateViewerPath())
+ {
+ Cache.setProperty(structViewer.getSelectedItem()
+ .equals(ViewerType.CHIMERAX.name())
+ ? CHIMERAX_PATH
+ : CHIMERA_PATH, structureViewerPath.getText());
+ }
}
});
Boolean.toString(useRnaView.isSelected()));
Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
Boolean.toString(structFromPdb.isSelected()));
+ String viewer = structViewer.getSelectedItem().toString();
+ String viewerPath = structureViewerPath.getText();
Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY,
- structViewer.getSelectedItem().toString());
- Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText());
+ viewer);
+ if (viewer.equals(ViewerType.CHIMERA.name()))
+ {
+ Cache.setOrRemove(CHIMERA_PATH, viewerPath);
+ }
+ else if (viewer.equals(ViewerType.CHIMERAX.name()))
+ {
+ Cache.setOrRemove(CHIMERAX_PATH, viewerPath);
+ }
+ else if (viewer.equals(ViewerType.PYMOL.name()))
+ {
+ Cache.setOrRemove(PYMOL_PATH, viewerPath);
+ }
Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
Boolean.toString(siftsMapping.isSelected()));
SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
@Override
protected boolean validateStructure()
{
- return validateChimeraPath();
+ return validateViewerPath();
}
}
/**
- * Returns true if chimera path is to a valid executable, else show an error
- * dialog.
+ * Returns true if structure viewer path is to a valid executable, else shows
+ * an error dialog. Does nothing if the path is empty, as is the case for Jmol
+ * (built in to Jalview) or when Jalview is left to try default paths.
*/
- private boolean validateChimeraPath()
+ private boolean validateViewerPath()
{
- if (chimeraPath.getText().trim().length() > 0)
+ if (structureViewerPath.getText().trim().length() > 0)
{
- File f = new File(chimeraPath.getText());
+ File f = new File(structureViewerPath.getText());
if (!f.canExecute())
{
JvOptionPane.showInternalMessageDialog(Desktop.desktop,
- MessageManager.getString("label.invalid_chimera_path"),
- MessageManager.getString("label.invalid_name"),
+ MessageManager.getString("label.invalid_viewer_path"),
+ MessageManager.getString("label.invalid_viewer_path"),
JvOptionPane.ERROR_MESSAGE);
return false;
}
}
/**
- * If Chimera is selected, check it can be found on default or user-specified
- * path, if not show a warning/help dialog.
+ * If Chimera or ChimeraX or Pymol is selected, check it can be found on
+ * default or user-specified path, if not show a warning/help dialog
*/
@Override
protected void structureViewer_actionPerformed(String selectedItem)
{
- if (!selectedItem.equals(ViewerType.CHIMERA.name()))
+ if (selectedItem.equals(ViewerType.JMOL.name()))
{
+ structureViewerPath.setEnabled(false);
+ structureViewerPathLabel.setEnabled(false);
return;
}
boolean found = false;
+ structureViewerPath.setEnabled(true);
+ structureViewerPathLabel.setEnabled(true);
+ structureViewerPathLabel.setText(MessageManager
+ .formatMessage("label.viewer_path", selectedItem));
/*
- * Try user-specified and standard paths for Chimera executable.
+ * Try user-specified and standard paths for structure viewer executable
*/
- List<String> paths = StructureManager.getChimeraPaths();
- paths.add(0, chimeraPath.getText());
+ String viewerPath = "";
+ List<String> paths = null;
+ try
+ {
+ ViewerType viewerType = ViewerType.valueOf(selectedItem);
+ switch (viewerType)
+ {
+ case JMOL:
+ // dealt with above
+ break;
+ case CHIMERA:
+ viewerPath = Cache.getDefault(CHIMERA_PATH, "");
+ paths = StructureManager.getChimeraPaths(false);
+ break;
+ case CHIMERAX:
+ viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
+ paths = StructureManager.getChimeraPaths(true);
+ break;
+ case PYMOL:
+ viewerPath = Cache.getDefault(PYMOL_PATH, "");
+ paths = PymolManager.getPymolPaths();
+ break;
+ }
+ } catch (IllegalArgumentException e)
+ {
+ // only valid entries should be in the drop-down
+ }
+ structureViewerPath.setText(viewerPath);
+
+ paths.add(0, structureViewerPath.getText());
for (String path : paths)
{
if (new File(path.trim()).canExecute())
break;
}
}
+
if (!found)
{
String[] options = { "OK", "Help" };
int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
JvSwingUtils.wrapTooltip(true,
- MessageManager.getString("label.chimera_missing")),
+ MessageManager.getString("label.viewer_missing")),
"", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
null, options, options[0]);
if (showHelp == JvOptionPane.NO_OPTION)
--- /dev/null
+package jalview.gui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.ext.pymol.PymolCommands;
+import jalview.ext.pymol.PymolManager;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.structure.AtomSpec;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+
+public class PymolBindingModel extends AAStructureBindingModel
+{
+ /*
+ * format for labels shown on structures when mousing over sequence;
+ * see https://pymolwiki.org/index.php/Label#examples
+ * left not final so customisable e.g. with a Groovy script
+ */
+ private static String LABEL_FORMAT = "\"%s %s\" % (resn,resi)";
+
+ private PymolManager pymolManager;
+
+ private Thread pymolMonitor;
+
+ /*
+ * full paths to structure files opened in PyMOL
+ */
+ List<String> structureFiles = new ArrayList<>();
+
+ /*
+ * lookup from file path to PyMOL object name
+ */
+ Map<String, String> pymolObjects = new HashMap<>();
+
+ private String lastLabelSpec;
+
+ /**
+ * Constructor
+ *
+ * @param viewer
+ * @param ssm
+ * @param pdbentry
+ * @param sequenceIs
+ */
+ public PymolBindingModel(StructureViewerBase viewer,
+ StructureSelectionManager ssm, PDBEntry[] pdbentry,
+ SequenceI[][] sequenceIs)
+ {
+ super(ssm, pdbentry, sequenceIs, null);
+ pymolManager = new PymolManager();
+ setStructureCommands(new PymolCommands());
+ setViewer(viewer);
+ }
+
+ @Override
+ public String[] getStructureFiles()
+ {
+ return structureFiles.toArray(new String[structureFiles.size()]);
+ }
+
+ @Override
+ public void highlightAtoms(List<AtomSpec> atoms)
+ {
+ /*
+ * https://pymolwiki.org/index.php/Label#examples
+ */
+ StringBuilder sb = new StringBuilder();
+ for (AtomSpec atom : atoms)
+ {
+ // todo promote to StructureCommandsI.showLabel()
+ // todo handle CA|P correctly
+ String modelId = getModelIdForFile(atom.getPdbFile());
+ sb.append(String.format(" %s//%s/%d/CA", modelId,
+ atom.getChain(),
+ atom.getPdbResNum()));
+ }
+ String labelSpec = sb.toString();
+ if (labelSpec.equals(lastLabelSpec))
+ {
+ return;
+ }
+ StructureCommandI command = new StructureCommand("label", labelSpec, LABEL_FORMAT);
+ executeCommand(command, false);
+
+ /*
+ * and remove the label(s) previously shown
+ */
+ if (lastLabelSpec != null)
+ {
+ command = new StructureCommand("label", lastLabelSpec, "");
+ executeCommand(command, false);
+ }
+
+ lastLabelSpec = labelSpec;
+ }
+
+ @Override
+ public SequenceRenderer getSequenceRenderer(AlignmentViewPanel avp)
+ {
+ return new SequenceRenderer(avp.getAlignViewport());
+ }
+
+ @Override
+ protected List<String> executeCommand(StructureCommandI command,
+ boolean getReply)
+ {
+ // System.out.println(command.toString()); // debug
+ return pymolManager.sendCommand(command, getReply);
+ }
+
+ @Override
+ protected String getModelIdForFile(String file)
+ {
+ return pymolObjects.containsKey(file) ? pymolObjects.get(file) : "";
+ }
+
+ @Override
+ protected ViewerType getViewerType()
+ {
+ return ViewerType.PYMOL;
+ }
+
+ @Override
+ public boolean isViewerRunning()
+ {
+ return pymolManager.isPymolLaunched();
+ }
+
+ @Override
+ public void closeViewer(boolean closePymol)
+ {
+ super.closeViewer(closePymol);
+ if (closePymol)
+ {
+ pymolManager.exitPymol();
+ }
+ pymolManager = null;
+
+ if (pymolMonitor != null)
+ {
+ pymolMonitor.interrupt();
+ }
+ }
+
+ public boolean launchPymol()
+ {
+ if (pymolManager.isPymolLaunched())
+ {
+ return true;
+ }
+
+ boolean launched = pymolManager.launchPymol();
+ if (launched)
+ {
+ // start listening for PyMOL selections - how??
+ }
+ else
+ {
+ System.err.println("Failed to launch PyMOL!");
+ }
+ return launched;
+ }
+
+ public void openFile(PDBEntry pe)
+ {
+ // todo : check not already open, remap / rename, etc
+ String file = pe.getFile();
+ StructureCommandI cmd = getCommandGenerator().loadFile(file);
+
+ /*
+ * a second parameter sets the pdbid as the loaded PyMOL object name
+ */
+ String pdbId = pe.getId();
+ cmd.addParameter(pdbId);
+
+ executeCommand(cmd, false);
+
+ pymolObjects.put(file, pdbId);
+ if (!structureFiles.contains(file))
+ {
+ structureFiles.add(file);
+ }
+ if (getSsm() != null)
+ {
+ getSsm().addStructureViewerListener(this);
+ }
+
+ }
+
+ @Override
+ protected String getModelId(int pdbfnum, String file)
+ {
+ return file;
+ }
+
+ /**
+ * Returns the file extension to use for a saved viewer session file (.pse)
+ *
+ * @return
+ * @see https://pymolwiki.org/index.php/Save
+ */
+ @Override
+ public String getSessionFileExtension()
+ {
+ return ".pse";
+ }
+
+ @Override
+ public String getHelpURL()
+ {
+ return "https://pymolwiki.org/";
+ }
+
+ /**
+ * Constructs and sends commands to set atom properties for visible Jalview
+ * features on residues mapped to structure
+ *
+ * @param avp
+ * @return
+ */
+ public int sendFeaturesToViewer(AlignmentViewPanel avp)
+ {
+ // todo pull up this and JalviewChimeraBinding variant
+ Map<String, Map<Object, AtomSpecModel>> featureValues = buildFeaturesMap(
+ avp);
+ List<StructureCommandI> commands = getCommandGenerator()
+ .setAttributes(featureValues);
+ executeCommands(commands, false, null);
+ return commands.size();
+ }
+
+}
--- /dev/null
+package jalview.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JMenuItem;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.MessageManager;
+
+public class PymolViewer extends StructureViewerBase
+{
+ private static final int myWidth = 500;
+
+ private static final int myHeight = 150;
+
+ private PymolBindingModel binding;
+
+ private String pymolSessionFile;
+
+ public PymolViewer()
+ {
+ super();
+
+ /*
+ * closeViewer will decide whether or not to close this frame
+ * depending on whether user chooses to Cancel or not
+ */
+ setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE);
+ }
+
+ public PymolViewer(PDBEntry pdb, SequenceI[] seqs, Object object,
+ AlignmentPanel ap)
+ {
+ this();
+ openNewPymol(ap, new PDBEntry[] { pdb },
+ new SequenceI[][]
+ { seqs });
+ }
+
+ public PymolViewer(PDBEntry[] pe, boolean alignAdded, SequenceI[][] seqs,
+ AlignmentPanel ap)
+ {
+ this();
+ setAlignAddedStructures(alignAdded);
+ openNewPymol(ap, pe, seqs);
+ }
+
+ /**
+ * Constructor given a session file to be restored
+ *
+ * @param sessionFile
+ * @param alignPanel
+ * @param pdbArray
+ * @param seqsArray
+ * @param colourByPymol
+ * @param colourBySequence
+ * @param newViewId
+ */
+ public PymolViewer(StructureViewerModel viewerModel,
+ AlignmentPanel alignPanel, String sessionFile, String vid)
+ {
+ // TODO convert to base/factory class method
+ this();
+ setViewId(vid);
+ this.pymolSessionFile = sessionFile;
+ Map<File, StructureData> pdbData = viewerModel.getFileData();
+ PDBEntry[] pdbArray = new PDBEntry[pdbData.size()];
+ SequenceI[][] seqsArray = new SequenceI[pdbData.size()][];
+ int i = 0;
+ for (StructureData data : pdbData.values())
+ {
+ PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null,
+ PDBEntry.Type.PDB, data.getFilePath());
+ pdbArray[i] = pdbentry;
+ List<SequenceI> sequencesForPdb = data.getSeqList();
+ seqsArray[i] = sequencesForPdb
+ .toArray(new SequenceI[sequencesForPdb.size()]);
+ i++;
+ }
+
+ openNewPymol(alignPanel, pdbArray, seqsArray);
+ if (viewerModel.isColourByViewer())
+ {
+ binding.setColourBySequence(false);
+ seqColour.setSelected(false);
+ viewerColour.setSelected(true);
+ }
+ else if (viewerModel.isColourWithAlignPanel())
+ {
+ binding.setColourBySequence(true);
+ seqColour.setSelected(true);
+ viewerColour.setSelected(false);
+ }
+ }
+
+ private void openNewPymol(AlignmentPanel ap, PDBEntry[] pe,
+ SequenceI[][] seqs)
+ {
+ createProgressBar();
+ binding = new PymolBindingModel(this, ap.getStructureSelectionManager(),
+ pe, seqs);
+ addAlignmentPanel(ap);
+ useAlignmentPanelForColourbyseq(ap);
+
+ if (pe.length > 1)
+ {
+ useAlignmentPanelForSuperposition(ap);
+ }
+ binding.setColourBySequence(true);
+ setSize(myWidth, myHeight);
+ initMenus();
+ viewerActionMenu.setText("PyMOL");
+ updateTitleAndMenus();
+
+ addingStructures = false;
+ worker = new Thread(this);
+ worker.start();
+
+ this.addInternalFrameListener(new InternalFrameAdapter()
+ {
+ @Override
+ public void internalFrameClosing(
+ InternalFrameEvent internalFrameEvent)
+ {
+ closeViewer(false);
+ }
+ });
+
+ }
+
+ /**
+ * Create a helper to manage progress bar display
+ */
+ protected void createProgressBar()
+ {
+ if (getProgressIndicator() == null)
+ {
+ setProgressIndicator(new ProgressBar(statusPanel, statusBar));
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ // todo pull up much of this
+
+ StringBuilder errormsgs = new StringBuilder(128);
+ List<PDBEntry> filePDB = new ArrayList<>();
+ List<Integer> filePDBpos = new ArrayList<>();
+ String[] curfiles = binding.getStructureFiles(); // files currently in viewer
+ for (int pi = 0; pi < binding.getPdbCount(); pi++)
+ {
+ String file = null;
+ PDBEntry thePdbEntry = binding.getPdbEntry(pi);
+ if (thePdbEntry.getFile() == null)
+ {
+ /*
+ * Retrieve PDB data, save to file, attach to PDBEntry
+ */
+ file = fetchPdbFile(thePdbEntry);
+ if (file == null)
+ {
+ errormsgs.append("'" + thePdbEntry.getId() + "' ");
+ }
+ }
+ else
+ {
+ /*
+ * got file already
+ */
+ file = new File(thePdbEntry.getFile()).getAbsoluteFile()
+ .getPath();
+ // todo - skip if already loaded in PyMOL
+ }
+ if (file != null)
+ {
+ filePDB.add(thePdbEntry);
+ filePDBpos.add(Integer.valueOf(pi));
+ }
+ }
+
+ if (!filePDB.isEmpty())
+ {
+ /*
+ * at least one structure to add to viewer
+ */
+ binding.setFinishedInit(false);
+ if (!addingStructures)
+ {
+ try
+ {
+ initPymol();
+ } catch (Exception ex)
+ {
+ Cache.log.error("Couldn't open PyMOL viewer!", ex);
+ }
+ }
+ int num = -1;
+ for (PDBEntry pe : filePDB)
+ {
+ num++;
+ if (pe.getFile() != null)
+ {
+ try
+ {
+ int pos = filePDBpos.get(num).intValue();
+ long startTime = startProgressBar(getViewerName() + " "
+ + MessageManager.getString("status.opening_file_for")
+ + " " + pe.getId());
+ binding.openFile(pe);
+ binding.addSequence(pos, binding.getSequence()[pos]);
+ File fl = new File(pe.getFile());
+ DataSourceType protocol = DataSourceType.URL;
+ try
+ {
+ if (fl.exists())
+ {
+ protocol = DataSourceType.FILE;
+ }
+ } catch (Throwable e)
+ {
+ } finally
+ {
+ stopProgressBar("", startTime);
+ }
+
+ StructureFile pdb = binding.getSsm().setMapping(
+ binding.getSequence()[pos], binding.getChains()[pos],
+ pe.getFile(), protocol,
+ getProgressIndicator());
+ binding.stashFoundChains(pdb, pe.getFile());
+ } catch (Exception ex)
+ {
+ Cache.log.error(
+ "Couldn't open " + pe.getFile() + " in Chimera viewer!",
+ ex);
+ } finally
+ {
+ // Cache.log.debug("File locations are " + files);
+ }
+ }
+ }
+
+ binding.refreshGUI();
+ binding.setFinishedInit(true);
+ binding.setLoadingFromArchive(false);
+
+ /*
+ * ensure that any newly discovered features (e.g. RESNUM)
+ * are added to any open feature settings dialog
+ */
+ FeatureRenderer fr = getBinding().getFeatureRenderer(null);
+ if (fr != null)
+ {
+ fr.featuresAdded();
+ }
+
+ // refresh the sequence colours for the new structure(s)
+ for (AlignmentViewPanel ap : _colourwith)
+ {
+ binding.updateColours(ap);
+ }
+ // do superposition if asked to
+ if (alignAddedStructures)
+ {
+ new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ alignStructsWithAllAlignPanels();
+ }
+ }).start();
+ }
+ addingStructures = false;
+ }
+ _started = false;
+ worker = null;
+
+ }
+
+ /**
+ * Launch PyMOL. If we have a session file name, send PyMOL the command to
+ * open its saved session file.
+ */
+ void initPymol()
+ {
+ Desktop.addInternalFrame(this,
+ binding.getViewerTitle(getViewerName(), true),
+ getBounds().width, getBounds().height);
+
+ if (!binding.launchPymol())
+ {
+ JvOptionPane.showMessageDialog(Desktop.desktop,
+ MessageManager.formatMessage("label.open_viewer_failed",
+ getViewerName()),
+ MessageManager.getString("label.error_loading_file"),
+ JvOptionPane.ERROR_MESSAGE);
+ this.dispose();
+ return;
+ }
+
+ if (this.pymolSessionFile != null)
+ {
+ boolean opened = binding.openSession(pymolSessionFile);
+ if (!opened)
+ {
+ Cache.log.error(
+ "An error occurred opening PyMOL session file "
+ + pymolSessionFile);
+ }
+ }
+ // binding.startPymolListener();
+ }
+
+ @Override
+ public AAStructureBindingModel getBinding()
+ {
+ return binding;
+ }
+
+ @Override
+ public ViewerType getViewerType()
+ {
+ return ViewerType.PYMOL;
+ }
+
+ @Override
+ protected String getViewerName()
+ {
+ return "PyMOL";
+ }
+
+ @Override
+ protected void initMenus()
+ {
+ super.initMenus();
+
+ savemenu.setVisible(false); // not yet implemented
+ viewMenu.add(fitToWindow);
+
+ JMenuItem writeFeatures = new JMenuItem(
+ MessageManager.getString("label.create_viewer_attributes"));
+ writeFeatures.setToolTipText(MessageManager
+ .getString("label.create_viewer_attributes_tip"));
+ writeFeatures.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ sendFeaturesToPymol();
+ }
+ });
+ viewerActionMenu.add(writeFeatures);
+ }
+
+ protected void sendFeaturesToPymol()
+ {
+ int count = binding.sendFeaturesToViewer(getAlignmentPanel());
+ statusBar.setText(
+ MessageManager.formatMessage("label.attributes_set", count));
+ }
+
+}
*/
package jalview.gui;
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.bin.Cache;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.StructureViewerModel;
-import jalview.structure.StructureSelectionManager;
-
-import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.StructureViewerModel;
+import jalview.structure.StructureSelectionManager;
+
/**
* A proxy for handling structure viewers, that orchestrates adding selected
* structures, associated with sequences in Jalview, to an existing viewer, or
public enum ViewerType
{
- JMOL, CHIMERA
+ JMOL, CHIMERA, CHIMERAX, PYMOL
};
/**
sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
ap);
}
+ else if (viewerType.equals(ViewerType.CHIMERAX))
+ {
+ sview = new ChimeraXViewFrame(pdbsForFile, superposeAdded, theSeqs,
+ ap);
+ }
+ else if (viewerType.equals(ViewerType.PYMOL))
+ {
+ sview = new PymolViewer(pdbsForFile, superposeAdded, theSeqs, ap);
+ }
else
{
Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
{
sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap);
}
+ else if (viewerType.equals(ViewerType.CHIMERAX))
+ {
+ sview = new ChimeraXViewFrame(pdb, seqsForPdb, null, ap);
+ }
+ else if (viewerType.equals(ViewerType.PYMOL))
+ {
+ sview = new PymolViewer(pdb, seqsForPdb, null, ap);
+ }
else
{
Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
}
/**
- * Create a new panel controlling a structure viewer.
+ * Creates a new panel controlling a structure viewer
*
* @param type
- * @param pdbf
- * @param id
- * @param sq
* @param alignPanel
* @param viewerData
- * @param fileloc
- * @param rect
+ * @param sessionFile
* @param vid
* @return
*/
- public JalviewStructureDisplayI createView(ViewerType type, String[] pdbf,
- String[] id, SequenceI[][] sq, AlignmentPanel alignPanel,
- StructureViewerModel viewerData, String fileloc, Rectangle rect,
- String vid)
+ public static JalviewStructureDisplayI createView(ViewerType type,
+ AlignmentPanel alignPanel, StructureViewerModel viewerData,
+ String sessionFile, String vid)
{
- final boolean useinViewerSuperpos = viewerData.isAlignWithPanel();
- final boolean usetoColourbyseq = viewerData.isColourWithAlignPanel();
- final boolean viewerColouring = viewerData.isColourByViewer();
-
+ JalviewStructureDisplayI viewer = null;
switch (type)
{
case JMOL:
- sview = new AppJmol(pdbf, id, sq, alignPanel, usetoColourbyseq,
- useinViewerSuperpos, viewerColouring, fileloc, rect, vid);
+ viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid);
+ // todo or construct and then openSession(sessionFile)?
break;
case CHIMERA:
- Cache.log.error(
- "Unsupported structure viewer type " + type.toString());
+ viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile,
+ vid);
+ break;
+ case CHIMERAX:
+ viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile,
+ vid);
+ break;
+ case PYMOL:
+ viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid);
break;
default:
Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString());
}
- return sview;
+ return viewer;
}
public boolean isBusy()
*/
package jalview.gui;
-import jalview.api.AlignmentViewPanel;
-import jalview.bin.Cache;
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.SequenceI;
-import jalview.gui.JalviewColourChooser.ColourChooserListener;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.gui.ViewSelectionMenu.ViewSetProvider;
-import jalview.io.DataSourceType;
-import jalview.io.JalviewFileChooser;
-import jalview.io.JalviewFileView;
-import jalview.jbgui.GStructureViewer;
-import jalview.schemes.ColourSchemeI;
-import jalview.schemes.ColourSchemes;
-import jalview.structure.StructureMapping;
-import jalview.structures.models.AAStructureBindingModel;
-import jalview.util.MessageManager;
-
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
+import jalview.api.AlignmentViewPanel;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.gui.ViewSelectionMenu.ViewSetProvider;
+import jalview.io.DataSourceType;
+import jalview.io.JalviewFileChooser;
+import jalview.io.JalviewFileView;
+import jalview.jbgui.GStructureViewer;
+import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ColourSchemes;
+import jalview.structure.StructureMapping;
+import jalview.structures.models.AAStructureBindingModel;
+import jalview.util.BrowserLauncher;
+import jalview.util.MessageManager;
+import jalview.ws.dbsources.Pdb;
+
/**
* Base class with common functionality for JMol, Chimera or other structure
* viewers.
/**
* list of alignment panels to use for superposition
*/
- protected Vector<AlignmentPanel> _alignwith = new Vector<>();
+ protected Vector<AlignmentViewPanel> _alignwith = new Vector<>();
/**
* list of alignment panels that are used for colouring structures by aligned
* sequences
*/
- protected Vector<AlignmentPanel> _colourwith = new Vector<>();
+ protected Vector<AlignmentViewPanel> _colourwith = new Vector<>();
private String viewId = null;
*/
protected volatile boolean seqColoursApplied = false;
+ private IProgressIndicator progressBar = null;
+
+ private Random random = new Random();
+
/**
* Default constructor
*/
return _aps.contains(ap2.av.getSequenceSetId());
}
- public boolean isUsedforaligment(AlignmentPanel ap2)
+ public boolean isUsedforaligment(AlignmentViewPanel ap2)
{
return (_alignwith != null) && _alignwith.contains(ap2);
}
- public boolean isUsedforcolourby(AlignmentPanel ap2)
+ @Override
+ public boolean isUsedForColourBy(AlignmentViewPanel ap2)
{
return (_colourwith != null) && _colourwith.contains(ap2);
}
this.viewId = viewId;
}
- public abstract String getStateInfo();
-
protected void buildActionMenu()
{
if (_alignwith == null)
}
}
+ @Override
public AlignmentPanel getAlignmentPanel()
{
return ap;
*
* @param nap
*/
- public void removeAlignmentPanel(AlignmentPanel nap)
+ @Override
+ public void removeAlignmentPanel(AlignmentViewPanel nap)
{
try
{
public abstract ViewerType getViewerType();
- protected abstract IProgressIndicator getIProgressIndicator();
-
/**
* add a new structure (with associated sequences and chains) to this viewer,
* retrieving it if necessary first.
* create the mappings
*/
apanel.getStructureSelectionManager().setMapping(seq, chains,
- pdbFilename, DataSourceType.FILE, getIProgressIndicator());
+ pdbFilename, DataSourceType.FILE, getProgressIndicator());
/*
* alert the FeatureRenderer to show new (PDB RESNUM) features
}
}
- abstract void showSelectedChains();
-
/**
* Action on selecting one of Jalview's registered colour schemes
*/
ColourSchemeI cs = ColourSchemes.getInstance()
.getColourScheme(colourSchemeName, getAlignmentPanel().av, al,
null);
- getBinding().setJalviewColourScheme(cs);
+ getBinding().colourByJalviewColourScheme(cs);
}
/**
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- viewerColour_actionPerformed(actionEvent);
+ viewerColour_actionPerformed();
}
});
colourMenu.add(viewerColour);
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- background_actionPerformed(actionEvent);
+ background_actionPerformed();
}
});
colourMenu.add(backGround);
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- seqColour_actionPerformed(actionEvent);
+ seqColour_actionPerformed();
}
});
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- chainColour_actionPerformed(actionEvent);
+ chainColour_actionPerformed();
}
});
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- chargeColour_actionPerformed(actionEvent);
+ chargeColour_actionPerformed();
}
});
viewerColour = new JRadioButtonMenuItem();
- // text is set in overrides of this method
+ viewerColour
+ .setText(MessageManager.getString("label.colour_with_viewer"));
+ viewerColour.setToolTipText(MessageManager
+ .getString("label.let_viewer_manage_structure_colours"));
viewerColour.setName(ViewerColour.ByViewer.name());
viewerColour.setSelected(!binding.isColourBySequence());
}
else
{
- // update the Chimera display now.
- seqColour_actionPerformed(null);
+ // update the viewer display now.
+ seqColour_actionPerformed();
}
}
});
@Override
public void itemStateChanged(ItemEvent e)
{
- alignStructs.setEnabled(!_alignwith.isEmpty());
- alignStructs.setToolTipText(MessageManager.formatMessage(
- "label.align_structures_using_linked_alignment_views",
- _alignwith.size()));
+ if (_alignwith.isEmpty())
+ {
+ alignStructs.setEnabled(false);
+ alignStructs.setToolTipText(null);
+ }
+ else
+ {
+ alignStructs.setEnabled(true);
+ alignStructs.setToolTipText(MessageManager.formatMessage(
+ "label.align_structures_using_linked_alignment_views",
+ _alignwith.size()));
+ }
}
};
viewSelectionMenu = new ViewSelectionMenu(
}
});
- buildColourMenu();
- }
+ viewerActionMenu.setText(getViewerName());
+ helpItem.setText(MessageManager.formatMessage("label.viewer_help",
+ getViewerName()));
- @Override
- public void setJalviewColourScheme(ColourSchemeI cs)
- {
- getBinding().setJalviewColourScheme(cs);
+ buildColourMenu();
}
/**
* the operation.
*/
@Override
- protected String alignStructs_actionPerformed(ActionEvent actionEvent)
- {
- return alignStructs_withAllAlignPanels();
- }
-
- protected String alignStructs_withAllAlignPanels()
+ protected String alignStructsWithAllAlignPanels()
{
if (getAlignmentPanel() == null)
{
String reply = null;
try
{
- AlignmentI[] als = new Alignment[_alignwith.size()];
- HiddenColumns[] alc = new HiddenColumns[_alignwith.size()];
- int[] alm = new int[_alignwith.size()];
- int a = 0;
-
- for (AlignmentPanel alignPanel : _alignwith)
- {
- als[a] = alignPanel.av.getAlignment();
- alm[a] = -1;
- alc[a++] = alignPanel.av.getAlignment().getHiddenColumns();
- }
- reply = getBinding().superposeStructures(als, alm, alc);
- if (reply != null)
+ reply = getBinding().superposeStructures(_alignwith);
+ if (reply != null && !reply.isEmpty())
{
String text = MessageManager
.formatMessage("error.superposition_failed", reply);
} catch (Exception e)
{
StringBuffer sp = new StringBuffer();
- for (AlignmentPanel alignPanel : _alignwith)
+ for (AlignmentViewPanel alignPanel : _alignwith)
{
- sp.append("'" + alignPanel.alignFrame.getTitle() + "' ");
+ sp.append("'" + alignPanel.getViewName() + "' ");
}
Cache.log.info("Couldn't align structures with the " + sp.toString()
+ "associated alignment panels.", e);
* background of the structure viewer
*/
@Override
- public void background_actionPerformed(ActionEvent actionEvent)
+ public void background_actionPerformed()
{
String ttl = MessageManager.getString("label.select_background_colour");
ColourChooserListener listener = new ColourChooserListener()
}
@Override
- public void viewerColour_actionPerformed(ActionEvent actionEvent)
+ public void viewerColour_actionPerformed()
{
if (viewerColour.isSelected())
{
}
@Override
- public void chainColour_actionPerformed(ActionEvent actionEvent)
+ public void chainColour_actionPerformed()
{
chainColour.setSelected(true);
getBinding().colourByChain();
}
@Override
- public void chargeColour_actionPerformed(ActionEvent actionEvent)
+ public void chargeColour_actionPerformed()
{
chargeColour.setSelected(true);
getBinding().colourByCharge();
}
@Override
- public void seqColour_actionPerformed(ActionEvent actionEvent)
+ public void seqColour_actionPerformed()
{
AAStructureBindingModel binding = getBinding();
binding.setColourBySequence(seqColour.isSelected());
}
}
// Set the colour using the current view for the associated alignframe
- for (AlignmentPanel alignPanel : _colourwith)
+ for (AlignmentViewPanel alignPanel : _colourwith)
{
binding.colourBySequence(alignPanel);
}
}
@Override
- public void pdbFile_actionPerformed(ActionEvent actionEvent)
+ public void pdbFile_actionPerformed()
{
// TODO: JAL-3048 not needed for Jalview-JS - save PDB file
JalviewFileChooser chooser = new JalviewFileChooser(
}
@Override
- public void viewMapping_actionPerformed(ActionEvent actionEvent)
+ public void viewMapping_actionPerformed()
{
CutAndPasteTransfer cap = new CutAndPasteTransfer();
try
* enable 'Superpose with' if more than one mapped structure
*/
viewSelectionMenu.setEnabled(false);
- if (getBinding().getStructureFiles().length > 1
+ if (getBinding().getMappedStructureCount() > 1
&& getBinding().getSequence().length > 1)
{
viewSelectionMenu.setEnabled(true);
if (!binding.isLoadingFromArchive())
{
- seqColour_actionPerformed(null);
+ seqColour_actionPerformed();
}
}
toFront();
}
+ @Override
+ public long startProgressBar(String msg)
+ {
+ // TODO would rather have startProgress/stopProgress as the
+ // IProgressIndicator interface
+ long tm = random.nextLong();
+ if (progressBar != null)
+ {
+ progressBar.setProgressBar(msg, tm);
+ }
+ return tm;
+ }
+
+ @Override
+ public void stopProgressBar(String msg, long handle)
+ {
+ if (progressBar != null)
+ {
+ progressBar.setProgressBar(msg, handle);
+ }
+ }
+
+ protected IProgressIndicator getProgressIndicator()
+ {
+ return progressBar;
+ }
+
+ protected void setProgressIndicator(IProgressIndicator pi)
+ {
+ progressBar = pi;
+ }
+
+ protected void setProgressMessage(String message, long id)
+ {
+ if (progressBar != null)
+ {
+ progressBar.setProgressBar(message, id);
+ }
+ }
+
+ @Override
+ public void showConsole(boolean show)
+ {
+ // default does nothing
+ }
+
+ /**
+ * Show only the selected chain(s) in the viewer
+ */
+ protected void showSelectedChains()
+ {
+ List<String> toshow = new ArrayList<>();
+ for (int i = 0; i < chainMenu.getItemCount(); i++)
+ {
+ if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
+ {
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
+ if (item.isSelected())
+ {
+ toshow.add(item.getText());
+ }
+ }
+ }
+ getBinding().showChains(toshow);
+ }
+
+ /**
+ * Tries to fetch a PDB file and save to a temporary local file. Returns the
+ * saved file path if successful, or null if not.
+ *
+ * @param processingEntry
+ * @return
+ */
+ protected String fetchPdbFile(PDBEntry processingEntry)
+ {
+ String filePath = null;
+ Pdb pdbclient = new Pdb();
+ AlignmentI pdbseq = null;
+ String pdbid = processingEntry.getId();
+ long handle = System.currentTimeMillis()
+ + Thread.currentThread().hashCode();
+
+ /*
+ * Write 'fetching PDB' progress on AlignFrame as we are not yet visible
+ */
+ String msg = MessageManager.formatMessage("status.fetching_pdb",
+ new Object[]
+ { pdbid });
+ getAlignmentPanel().alignFrame.setProgressBar(msg, handle);
+ // long hdl = startProgressBar(MessageManager.formatMessage(
+ // "status.fetching_pdb", new Object[]
+ // { pdbid }));
+ try
+ {
+ pdbseq = pdbclient.getSequenceRecords(pdbid);
+ } catch (Exception e)
+ {
+ System.err.println(
+ "Error retrieving PDB id " + pdbid + ": " + e.getMessage());
+ } finally
+ {
+ msg = pdbid + " " + MessageManager.getString("label.state_completed");
+ getAlignmentPanel().alignFrame.setProgressBar(msg, handle);
+ // stopProgressBar(msg, hdl);
+ }
+ /*
+ * If PDB data were saved and are not invalid (empty alignment), return the
+ * file path.
+ */
+ if (pdbseq != null && pdbseq.getHeight() > 0)
+ {
+ // just use the file name from the first sequence's first PDBEntry
+ filePath = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
+ .elementAt(0).getFile()).getAbsolutePath();
+ processingEntry.setFile(filePath);
+ }
+ return filePath;
+ }
+
+ /**
+ * If supported, saves the state of the structure viewer to a temporary file
+ * and returns the file, else returns null
+ *
+ * @return
+ */
+ public File saveSession()
+ {
+ // TODO: a wait loop to ensure the file is written fully before returning?
+ return getBinding() == null ? null : getBinding().saveSession();
+ }
+
+ /**
+ * Close down this instance of Jalview's Chimera viewer, giving the user the
+ * option to close the associated Chimera window (process). They may wish to
+ * keep it open until they have had an opportunity to save any work.
+ *
+ * @param forceClose
+ * if true, close any linked Chimera process; if false, prompt first
+ */
+ @Override
+ public void closeViewer(boolean forceClose)
+ {
+ AAStructureBindingModel binding = getBinding();
+ if (binding != null && binding.isViewerRunning())
+ {
+ if (!forceClose)
+ {
+ String viewerName = getViewerName();
+ String prompt = MessageManager
+ .formatMessage("label.confirm_close_viewer", new Object[]
+ { binding.getViewerTitle(viewerName, false), viewerName });
+ prompt = JvSwingUtils.wrapTooltip(true, prompt);
+ int confirm = JvOptionPane.showConfirmDialog(this, prompt,
+ MessageManager.getString("label.close_viewer"),
+ JvOptionPane.YES_NO_CANCEL_OPTION);
+ /*
+ * abort closure if user hits escape or Cancel
+ */
+ if (confirm == JvOptionPane.CANCEL_OPTION
+ || confirm == JvOptionPane.CLOSED_OPTION)
+ {
+ return;
+ }
+ forceClose = confirm == JvOptionPane.YES_OPTION;
+ }
+ binding.closeViewer(forceClose);
+ }
+ setAlignmentPanel(null);
+ _aps.clear();
+ _alignwith.clear();
+ _colourwith.clear();
+ // TODO: check for memory leaks where instance isn't finalised because jmb
+ // holds a reference to the window
+ // jmb = null;
+ dispose();
+ }
+
+ @Override
+ public void showHelp_actionPerformed()
+ {
+ try
+ {
+ String url = getBinding().getHelpURL();
+ if (url != null)
+ {
+ BrowserLauncher.openURL(url);
+ }
+ } catch (IOException ex)
+ {
+ System.err
+ .println("Show " + getViewerName() + " failed with: "
+ + ex.getMessage());
+ }
+ }
+
}
*/
package jalview.gui;
+import jalview.api.AlignmentViewPanel;
import jalview.util.MessageManager;
import java.awt.Component;
private ViewSetProvider _allviews;
- private List<AlignmentPanel> _selectedviews;
+ private List<AlignmentViewPanel> _selectedviews;
private ItemListener _handler;
* selection/deselection state
*/
public ViewSelectionMenu(String title, final ViewSetProvider allviews,
- final List<AlignmentPanel> selectedviews,
+ final List<AlignmentViewPanel> selectedviews,
final ItemListener handler)
{
super(title);
import jalview.structure.AtomSpec;
import jalview.structure.StructureListener;
import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
import jalview.structure.StructureSelectionManager;
import jalview.util.HttpUtils;
// Form a colour command from the given alignment panel for each distinct
// structure
- ArrayList<String[]> ccomands = new ArrayList<String[]>();
- ArrayList<String> pdbfn = new ArrayList<String>();
- StructureMappingcommandSet[] colcommands = JmolCommands
- .getColourBySequenceCommand(ssm, modelSet, sequence, sr,
+ ArrayList<String[]> ccomands = new ArrayList<>();
+ ArrayList<String> pdbfn = new ArrayList<>();
+ String[] colcommands = new JmolCommands()
+ .colourBySequence(ssm, modelSet, sequence, sr,
(AlignmentViewPanel) source);
if (colcommands == null)
{
return;
}
int sz = 0;
- for (jalview.structure.StructureMappingcommandSet ccset : colcommands)
+ // for (jalview.structure.StructureMappingcommandSet ccset : colcommands)
+ for (String command : colcommands)
{
- sz += ccset.commands.length;
- ccomands.add(ccset.commands);
- pdbfn.add(ccset.mapping);
+ // sz += ccset.commands.length;
+ // ccomands.add(command); // ccset.commands);
+ // pdbfn.add(ccset.mapping);
}
String mclass, mhandle;
*/
package jalview.jbgui;
-import jalview.bin.Cache;
-import jalview.fts.core.FTSDataColumnPreferences;
-import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
-import jalview.fts.service.pdb.PDBFTSRestClient;
-import jalview.gui.Desktop;
-import jalview.gui.JalviewBooleanRadioButtons;
-import jalview.gui.JvOptionPane;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.StructureViewer.ViewerType;
-import jalview.io.BackupFilenameParts;
-import jalview.io.BackupFiles;
-import jalview.io.BackupFilesPresetEntry;
-import jalview.io.IntKeyStringValueEntry;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
+import jalview.bin.Cache;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.gui.Desktop;
+import jalview.gui.JalviewBooleanRadioButtons;
+import jalview.gui.JvOptionPane;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.BackupFilenameParts;
+import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.IntKeyStringValueEntry;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
/**
* Base class for the Preferences panel.
*
protected JComboBox<String> structViewer = new JComboBox<>();
- protected JTextField chimeraPath = new JTextField();
+ protected JLabel structureViewerPathLabel;
+
+ protected JTextField structureViewerPath = new JTextField();
protected ButtonGroup mappingMethod = new ButtonGroup();
structureTab.setBorder(new TitledBorder(
MessageManager.getString("label.structure_options")));
structureTab.setLayout(null);
- final int width = 400;
+ final int width = 420;
final int height = 22;
final int lineSpacing = 25;
int ypos = 15;
viewerLabel.setFont(LABEL_FONT);
viewerLabel.setHorizontalAlignment(SwingConstants.LEFT);
viewerLabel.setText(MessageManager.getString("label.structure_viewer"));
- viewerLabel.setBounds(new Rectangle(10, ypos, 200, height));
+ viewerLabel.setBounds(new Rectangle(10, ypos, 220, height));
structureTab.add(viewerLabel);
+ /*
+ * add all external viewers as options here - check
+ * when selected whether the program is installed
+ */
structViewer.setFont(LABEL_FONT);
- structViewer.setBounds(new Rectangle(160, ypos, 120, height));
+ structViewer.setBounds(new Rectangle(190, ypos, 120, height));
structViewer.addItem(ViewerType.JMOL.name());
structViewer.addItem(ViewerType.CHIMERA.name());
+ structViewer.addItem(ViewerType.CHIMERAX.name());
+ structViewer.addItem(ViewerType.PYMOL.name());
structViewer.addActionListener(new ActionListener()
{
@Override
structureTab.add(structViewer);
ypos += lineSpacing;
- JLabel pathLabel = new JLabel();
- pathLabel.setFont(new java.awt.Font("SansSerif", 0, 11));
- pathLabel.setHorizontalAlignment(SwingConstants.LEFT);
- pathLabel.setText(MessageManager.getString("label.chimera_path"));
- pathLabel.setBounds(new Rectangle(10, ypos, 140, height));
- structureTab.add(pathLabel);
-
- chimeraPath.setFont(LABEL_FONT);
- chimeraPath.setText("");
+ structureViewerPathLabel = new JLabel();
+ structureViewerPathLabel.setFont(LABEL_FONT);// new Font("SansSerif", 0, 11));
+ structureViewerPathLabel.setHorizontalAlignment(SwingConstants.LEFT);
+ structureViewerPathLabel.setText(MessageManager
+ .formatMessage("label.viewer_path", "Chimera(X)"));
+ structureViewerPathLabel.setBounds(new Rectangle(10, ypos, 170, height));
+ structureViewerPathLabel.setEnabled(false);
+ structureTab.add(structureViewerPathLabel);
+
+ structureViewerPath.setFont(LABEL_FONT);
+ structureViewerPath.setText("");
+ structureViewerPath.setEnabled(false);
final String tooltip = JvSwingUtils.wrapTooltip(true,
- MessageManager.getString("label.chimera_path_tip"));
- chimeraPath.setToolTipText(tooltip);
- chimeraPath.setBounds(new Rectangle(160, ypos, 300, height));
- chimeraPath.addMouseListener(new MouseAdapter()
+ MessageManager.getString("label.viewer_path_tip"));
+ structureViewerPath.setToolTipText(tooltip);
+ structureViewerPath.setBounds(new Rectangle(190, ypos, 290, height));
+ structureViewerPath.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
- if (e.getClickCount() == 2)
+ if (structureViewerPath.isEnabled() && e.getClickCount() == 2)
{
String chosen = openFileChooser();
if (chosen != null)
{
- chimeraPath.setText(chosen);
+ structureViewerPath.setText(chosen);
}
}
}
});
- structureTab.add(chimeraPath);
+ structureTab.add(structureViewerPath);
ypos += lineSpacing;
nwMapping.setFont(LABEL_FONT);
MessageManager.getString("label.mapping_method"));
mmTitledBorder.setTitleFont(LABEL_FONT);
mappingPanel.setBorder(mmTitledBorder);
- mappingPanel.setBounds(new Rectangle(10, ypos, 452, 45));
+ mappingPanel.setBounds(new Rectangle(10, ypos, 472, 45));
// GridLayout mappingLayout = new GridLayout();
mappingPanel.setLayout(new GridLayout());
mappingPanel.add(nwMapping);
ypos += lineSpacing;
FTSDataColumnPreferences docFieldPref = new FTSDataColumnPreferences(
PreferenceSource.PREFERENCES, PDBFTSRestClient.getInstance());
- docFieldPref.setBounds(new Rectangle(10, ypos, 450, 120));
+ docFieldPref.setBounds(new Rectangle(10, ypos, 470, 120));
structureTab.add(docFieldPref);
/*
*/
if (Platform.isJS())
{
- pathLabel.setVisible(false);
- chimeraPath.setVisible(false);
+ structureViewerPathLabel.setVisible(false);
+ structureViewerPath.setVisible(false);
viewerLabel.setVisible(false);
structViewer.setVisible(false);
}
*/
package jalview.jbgui;
-import jalview.api.structures.JalviewStructureDisplayI;
-import jalview.gui.ColourMenuHelper.ColourChangeListener;
-import jalview.util.ImageMaker.TYPE;
-import jalview.util.MessageManager;
-
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
+import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.gui.ColourMenuHelper.ColourChangeListener;
+import jalview.util.ImageMaker.TYPE;
+import jalview.util.MessageManager;
+
@SuppressWarnings("serial")
public abstract class GStructureViewer extends JInternalFrame
implements JalviewStructureDisplayI, ColourChangeListener
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- pdbFile_actionPerformed(actionEvent);
+ pdbFile_actionPerformed();
}
});
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- viewMapping_actionPerformed(actionEvent);
+ viewMapping_actionPerformed();
}
});
JMenu helpMenu = new JMenu();
helpMenu.setText(MessageManager.getString("action.help"));
helpItem = new JMenuItem();
- helpItem.setText(MessageManager.getString("label.jmol_help"));
helpItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- showHelp_actionPerformed(actionEvent);
+ showHelp_actionPerformed();
}
});
alignStructs = new JMenuItem();
@Override
public void actionPerformed(ActionEvent actionEvent)
{
- alignStructs_actionPerformed(actionEvent);
+ alignStructsWithAllAlignPanels();
}
});
{
}
- protected void viewerColour_actionPerformed(ActionEvent actionEvent)
+ protected void viewerColour_actionPerformed()
{
}
- protected abstract String alignStructs_actionPerformed(
- ActionEvent actionEvent);
+ protected abstract String alignStructsWithAllAlignPanels();
- public void pdbFile_actionPerformed(ActionEvent actionEvent)
+ public void pdbFile_actionPerformed()
{
}
}
- public void viewMapping_actionPerformed(ActionEvent actionEvent)
+ public void viewMapping_actionPerformed()
{
}
- public void seqColour_actionPerformed(ActionEvent actionEvent)
+ public void seqColour_actionPerformed()
{
}
- public void chainColour_actionPerformed(ActionEvent actionEvent)
+ public void chainColour_actionPerformed()
{
}
- public void chargeColour_actionPerformed(ActionEvent actionEvent)
+ public void chargeColour_actionPerformed()
{
}
- public void background_actionPerformed(ActionEvent actionEvent)
+ public void background_actionPerformed()
{
}
- public void showHelp_actionPerformed(ActionEvent actionEvent)
+ public void showHelp_actionPerformed()
{
}
import static jalview.math.RotatableMatrix.Axis.Y;
import static jalview.math.RotatableMatrix.Axis.Z;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.SwingUtilities;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.Marshaller;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamReader;
+
import jalview.analysis.Conservation;
import jalview.analysis.PCA;
import jalview.analysis.scoremodels.ScoreModels;
import jalview.gui.AlignViewport;
import jalview.gui.AlignmentPanel;
import jalview.gui.AppVarna;
-import jalview.gui.ChimeraViewFrame;
import jalview.gui.Desktop;
import jalview.gui.JvOptionPane;
import jalview.gui.OOMWarning;
import jalview.xml.binding.jalview.ThresholdType;
import jalview.xml.binding.jalview.VAMSAS;
-import java.awt.Color;
-import java.awt.Font;
-import java.awt.Rectangle;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.math.BigInteger;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Hashtable;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.Vector;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-
-import javax.swing.JInternalFrame;
-import javax.swing.SwingUtilities;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.Marshaller;
-import javax.xml.datatype.DatatypeConfigurationException;
-import javax.xml.datatype.DatatypeFactory;
-import javax.xml.datatype.XMLGregorianCalendar;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamReader;
-
/**
* Write out the current jalview desktop state as a Jalview XML stream.
*
if (frames[f] instanceof StructureViewerBase)
{
StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
- matchedFile = saveStructureState(ap, jds, pdb, entry, viewIds,
+ matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds,
matchedFile, viewFrame);
/*
* Only store each structure viewer's state once in the project
* jar. First time through only (storeDS==false)
*/
String viewId = viewFrame.getViewId();
+ String viewerType = viewFrame.getViewerType().toString();
if (!storeDS && !viewIds.contains(viewId))
{
viewIds.add(viewId);
- try
+ File viewerState = viewFrame.saveSession();
+ if (viewerState != null)
{
- String viewerState = viewFrame.getStateInfo();
- writeJarEntry(jout, getViewerJarEntryName(viewId),
- viewerState.getBytes());
- } catch (IOException e)
+ copyFileToJar(jout, viewerState.getPath(),
+ getViewerJarEntryName(viewId), viewerType);
+ }
+ else
{
- System.err.println(
- "Error saving viewer state: " + e.getMessage());
+ Cache.log.error("Failed to save viewer state for "
+ +
+ viewerType);
}
}
}
if (!pdbfiles.contains(pdbId))
{
pdbfiles.add(pdbId);
- copyFileToJar(jout, matchedFile, pdbId);
+ copyFileToJar(jout, matchedFile, pdbId, pdbId);
}
}
// using save and then load
try
{
- fileName = fileName.replace('\\', '/');
+ fileName = fileName.replace('\\', '/');
System.out.println("Writing jar entry " + fileName);
JarEntry entry = new JarEntry(fileName);
jout.putNextEntry(entry);
String varnaStateFile = varna.getStateInfo(model.rna);
jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter();
- copyFileToJar(jout, varnaStateFile, jarEntryName);
+ copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna");
rnaSessions.put(model, jarEntryName);
}
SecondaryStructure ss = new SecondaryStructure();
* @param jout
* @param infilePath
* @param jarEntryName
+ * @param msg
+ * additional identifying info to log to the console
*/
protected void copyFileToJar(JarOutputStream jout, String infilePath,
- String jarEntryName)
+ String jarEntryName, String msg)
{
- DataInputStream dis = null;
- try
+ try (InputStream is = new FileInputStream(infilePath))
{
File file = new File(infilePath);
if (file.exists() && jout != null)
{
- dis = new DataInputStream(new FileInputStream(file));
- byte[] data = new byte[(int) file.length()];
- dis.readFully(data);
- writeJarEntry(jout, jarEntryName, data);
+ System.out.println(
+ "Writing jar entry " + jarEntryName + " (" + msg + ")");
+ jout.putNextEntry(new JarEntry(jarEntryName));
+ copyAll(is, jout);
+ jout.closeEntry();
+ // dis = new DataInputStream(new FileInputStream(file));
+ // byte[] data = new byte[(int) file.length()];
+ // dis.readFully(data);
+ // writeJarEntry(jout, jarEntryName, data);
}
} catch (Exception ex)
{
ex.printStackTrace();
- } finally
- {
- if (dis != null)
- {
- try
- {
- dis.close();
- } catch (IOException e)
- {
- // ignore
- }
- }
}
}
/**
- * Write the data to a new entry of given name in the output jar file
+ * Copies input to output, in 4K buffers; handles any data (text or binary)
*
- * @param jout
- * @param jarEntryName
- * @param data
+ * @param in
+ * @param out
* @throws IOException
*/
- protected void writeJarEntry(JarOutputStream jout, String jarEntryName,
- byte[] data) throws IOException
+ protected void copyAll(InputStream in, OutputStream out)
+ throws IOException
{
- if (jout != null)
+ byte[] buffer = new byte[4096];
+ int bytesRead = 0;
+ while ((bytesRead = in.read(buffer)) != -1)
{
- jarEntryName = jarEntryName.replace('\\','/');
- System.out.println("Writing jar entry " + jarEntryName);
- jout.putNextEntry(new JarEntry(jarEntryName));
- DataOutputStream dout = new DataOutputStream(jout);
- dout.write(data, 0, data.length);
- dout.flush();
- jout.closeEntry();
+ out.write(buffer, 0, bytesRead);
}
}
* @param viewFrame
* @return
*/
- protected String saveStructureState(AlignmentPanel ap, SequenceI jds,
+ protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
Pdbids pdb, PDBEntry entry, List<String> viewIds,
String matchedFile, StructureViewerBase viewFrame)
{
final String viewId = viewFrame.getViewId();
state.setViewId(viewId);
state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
- state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap));
+ state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap));
state.setColourByJmol(viewFrame.isColouredByViewer());
state.setType(viewFrame.getViewerType().toString());
// pdb.addStructureState(state);
protected String copyJarEntry(jarInputStreamProvider jprovider,
String jarEntryName, String prefix, String suffixModel)
{
- BufferedReader in = null;
- PrintWriter out = null;
String suffix = ".tmp";
if (suffixModel == null)
{
{
suffix = "." + suffixModel.substring(sfpos + 1);
}
- try
- {
- JarInputStream jin = jprovider.getJarInputStream();
- /*
- * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
- * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
- * FileInputStream(jprovider)); }
- */
+ try (JarInputStream jin = jprovider.getJarInputStream())
+ {
JarEntry entry = null;
do
{
entry = jin.getNextJarEntry();
} while (entry != null && !entry.getName().equals(jarEntryName));
+
if (entry != null)
{
- in = new BufferedReader(new InputStreamReader(jin, UTF_8));
+ // in = new BufferedReader(new InputStreamReader(jin, UTF_8));
File outFile = File.createTempFile(prefix, suffix);
outFile.deleteOnExit();
- out = new PrintWriter(new FileOutputStream(outFile));
- String data;
-
- while ((data = in.readLine()) != null)
+ try (OutputStream os = new FileOutputStream(outFile))
{
- out.println(data);
+ copyAll(jin, os);
}
- out.flush();
String t = outFile.getAbsolutePath();
return t;
}
} catch (Exception ex)
{
ex.printStackTrace();
- } finally
- {
- if (in != null)
- {
- try
- {
- in.close();
- } catch (IOException e)
- {
- // ignore
- }
- }
- if (out != null)
- {
- out.close();
- }
}
return null;
}
if (!structureViewers.containsKey(sviewid))
{
+ String viewerType = structureState.getType();
+ if (viewerType == null) // pre Jalview 2.9
+ {
+ viewerType = ViewerType.JMOL.toString();
+ }
structureViewers.put(sviewid,
new StructureViewerModel(x, y, width, height, false,
false, true, structureState.getViewId(),
- structureState.getType()));
+ viewerType));
// Legacy pre-2.7 conversion JAL-823 :
// do not assume any view has to be linked for colour by
// sequence
return;
}
- /*
- * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry
- * "viewer_"+stateData.viewId
- */
- if (ViewerType.CHIMERA.toString().equals(stateData.getType()))
- {
- createChimeraViewer(viewerData, af, jprovider);
- }
- else
- {
- /*
- * else Jmol (if pre-2.9, stateData contains JMOL state string)
- */
- createJmolViewer(viewerData, af, jprovider);
- }
- }
-
- /**
- * Create a new Chimera viewer.
- *
- * @param data
- * @param af
- * @param jprovider
- */
- protected void createChimeraViewer(
- Entry<String, StructureViewerModel> viewerData, AlignFrame af,
- jarInputStreamProvider jprovider)
- {
- StructureViewerModel data = viewerData.getValue();
- String chimeraSessionFile = data.getStateData();
-
- /*
- * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file
- *
- * NB this is the 'saved' viewId as in the project file XML, _not_ the
- * 'uniquified' sviewid used to reconstruct the viewer here
- */
- String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
- chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
- "chimera", ".py");
-
- Set<Entry<File, StructureData>> fileData = data.getFileData()
- .entrySet();
- List<PDBEntry> pdbs = new ArrayList<>();
- List<SequenceI[]> allseqs = new ArrayList<>();
- for (Entry<File, StructureData> pdb : fileData)
- {
- String filePath = pdb.getValue().getFilePath();
- String pdbId = pdb.getValue().getPdbId();
- // pdbs.add(new PDBEntry(filePath, pdbId));
- pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath));
- final List<SequenceI> seqList = pdb.getValue().getSeqList();
- SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
- allseqs.add(seqs);
- }
-
- boolean colourByChimera = data.isColourByViewer();
- boolean colourBySequence = data.isColourWithAlignPanel();
-
- // TODO use StructureViewer as a factory here, see JAL-1761
- final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
- final SequenceI[][] seqsArray = allseqs
- .toArray(new SequenceI[allseqs.size()][]);
- String newViewId = viewerData.getKey();
-
- ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
- af.alignPanel, pdbArray, seqsArray, colourByChimera,
- colourBySequence, newViewId);
- cvf.setSize(data.getWidth(), data.getHeight());
- cvf.setLocation(data.getX(), data.getY());
- }
-
- /**
- * Create a new Jmol window. First parse the Jmol state to translate filenames
- * loaded into the view, and record the order in which files are shown in the
- * Jmol view, so we can add the sequence mappings in same order.
- *
- * @param viewerData
- * @param af
- * @param jprovider
- */
- protected void createJmolViewer(
- final Entry<String, StructureViewerModel> viewerData,
- AlignFrame af, jarInputStreamProvider jprovider)
- {
- final StructureViewerModel svattrib = viewerData.getValue();
- String state = svattrib.getStateData();
-
- /*
- * Pre-2.9: state element value is the Jmol state string
- *
- * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_"
- * + viewId
- */
- if (ViewerType.JMOL.toString().equals(svattrib.getType()))
- {
- state = readJarEntry(jprovider,
- getViewerJarEntryName(svattrib.getViewId()));
- }
-
- List<String> pdbfilenames = new ArrayList<>();
- List<SequenceI[]> seqmaps = new ArrayList<>();
- List<String> pdbids = new ArrayList<>();
- StringBuilder newFileLoc = new StringBuilder(64);
- int cp = 0, ncp, ecp;
- Map<File, StructureData> oldFiles = svattrib.getFileData();
- while ((ncp = state.indexOf("load ", cp)) > -1)
- {
- do
- {
- // look for next filename in load statement
- newFileLoc.append(state.substring(cp,
- ncp = (state.indexOf("\"", ncp + 1) + 1)));
- String oldfilenam = state.substring(ncp,
- ecp = state.indexOf("\"", ncp));
- // recover the new mapping data for this old filename
- // have to normalize filename - since Jmol and jalview do
- // filename
- // translation differently.
- StructureData filedat = oldFiles.get(new File(oldfilenam));
- if (filedat == null)
- {
- String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
- filedat = oldFiles.get(new File(reformatedOldFilename));
- }
- newFileLoc.append(Platform.escapeBackslashes(filedat.getFilePath()));
- pdbfilenames.add(filedat.getFilePath());
- pdbids.add(filedat.getPdbId());
- seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
- newFileLoc.append("\"");
- cp = ecp + 1; // advance beyond last \" and set cursor so we can
- // look for next file statement.
- } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
- }
- if (cp > 0)
- {
- // just append rest of state
- newFileLoc.append(state.substring(cp));
- }
- else
- {
- System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
- newFileLoc = new StringBuilder(state);
- newFileLoc.append("; load append ");
- for (File id : oldFiles.keySet())
- {
- // add this and any other pdb files that should be present in
- // the viewer
- StructureData filedat = oldFiles.get(id);
- newFileLoc.append(filedat.getFilePath());
- pdbfilenames.add(filedat.getFilePath());
- pdbids.add(filedat.getPdbId());
- seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
- newFileLoc.append(" \"");
- newFileLoc.append(filedat.getFilePath());
- newFileLoc.append("\"");
-
- }
- newFileLoc.append(";");
- }
-
- if (newFileLoc.length() == 0)
- {
- return;
- }
- int histbug = newFileLoc.indexOf("history = ");
- if (histbug > -1)
- {
- /*
- * change "history = [true|false];" to "history = [1|0];"
- */
- histbug += 10;
- int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
- String val = (diff == -1) ? null
- : newFileLoc.substring(histbug, diff);
- if (val != null && val.length() >= 4)
- {
- if (val.contains("e")) // eh? what can it be?
- {
- if (val.trim().equals("true"))
- {
- val = "1";
- }
- else
- {
- val = "0";
- }
- newFileLoc.replace(histbug, diff, val);
- }
- }
- }
-
- final String[] pdbf = pdbfilenames
- .toArray(new String[pdbfilenames.size()]);
- final String[] id = pdbids.toArray(new String[pdbids.size()]);
- final SequenceI[][] sq = seqmaps
- .toArray(new SequenceI[seqmaps.size()][]);
- final String fileloc = newFileLoc.toString();
- final String sviewid = viewerData.getKey();
- final AlignFrame alf = af;
- final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
- svattrib.getWidth(), svattrib.getHeight());
+ String type = stateData.getType();
try
{
- javax.swing.SwingUtilities.invokeAndWait(new Runnable()
- {
- @Override
- public void run()
- {
- JalviewStructureDisplayI sview = null;
- try
- {
- sview = new StructureViewer(
- alf.alignPanel.getStructureSelectionManager())
- .createView(StructureViewer.ViewerType.JMOL,
- pdbf, id, sq, alf.alignPanel, svattrib,
- fileloc, rect, sviewid);
- addNewStructureViewer(sview);
- } catch (OutOfMemoryError ex)
- {
- new OOMWarning("restoring structure view for PDB id " + id,
- (OutOfMemoryError) ex.getCause());
- if (sview != null && sview.isVisible())
- {
- sview.closeViewer(false);
- sview.setVisible(false);
- sview.dispose();
- }
- }
- }
- });
- } catch (InvocationTargetException ex)
- {
- warn("Unexpected error when opening Jmol view.", ex);
-
- } catch (InterruptedException e)
+ ViewerType viewerType = ViewerType.valueOf(type);
+ createStructureViewer(viewerType, viewerData, af, jprovider);
+ } catch (IllegalArgumentException | NullPointerException e)
{
- // e.printStackTrace();
+ // TODO JAL-3619 show error dialog / offer an alternative viewer
+ Cache.log.error(
+ "Invalid structure viewer type: " + type);
}
-
}
/**
addDatasetRef(vamsasSet.getDatasetId(), ds);
}
}
- Vector dseqs = null;
+ Vector<SequenceI> dseqs = null;
if (!ignoreUnrefed)
{
// recovering an alignment View
// try even harder to restore dataset
AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence());
// create a list of new dataset sequences
- dseqs = new Vector();
+ dseqs = new Vector<>();
}
for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++)
{
* vamsasSeq array ordering, to preserve ordering of dataset
*/
private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
- AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos)
+ AlignmentI ds, Vector<SequenceI> dseqs, boolean ignoreUnrefed,
+ int vseqpos)
{
// JBP TODO: Check this is called for AlCodonFrames to support recovery of
// xRef Codon Maps
}
/**
+ * Creates a new structure viewer window
+ *
+ * @param viewerType
+ * @param viewerData
+ * @param af
+ * @param jprovider
+ */
+ protected void createStructureViewer(
+ ViewerType viewerType, final Entry<String, StructureViewerModel> viewerData,
+ AlignFrame af, jarInputStreamProvider jprovider)
+ {
+ final StructureViewerModel viewerModel = viewerData.getValue();
+ String sessionFilePath = null;
+
+ if (viewerType == ViewerType.JMOL)
+ {
+ sessionFilePath = rewriteJmolSession(viewerModel, jprovider);
+ }
+ else
+ {
+ String viewerJarEntryName = getViewerJarEntryName(
+ viewerModel.getViewId());
+ sessionFilePath = copyJarEntry(jprovider,
+ viewerJarEntryName,
+ "viewerSession", ".tmp");
+ }
+ final String sessionPath = sessionFilePath;
+ final String sviewid = viewerData.getKey();
+ try
+ {
+ SwingUtilities.invokeAndWait(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ JalviewStructureDisplayI sview = null;
+ try
+ {
+ sview = StructureViewer.createView(viewerType, af.alignPanel,
+ viewerModel, sessionPath, sviewid);
+ addNewStructureViewer(sview);
+ } catch (OutOfMemoryError ex)
+ {
+ new OOMWarning("Restoring structure view for "
+ + viewerType,
+ (OutOfMemoryError) ex.getCause());
+ if (sview != null && sview.isVisible())
+ {
+ sview.closeViewer(false);
+ sview.setVisible(false);
+ sview.dispose();
+ }
+ }
+ }
+ });
+ } catch (InvocationTargetException | InterruptedException ex)
+ {
+ warn("Unexpected error when opening " + viewerType
+ + " structure viewer", ex);
+ }
+ }
+
+ /**
+ * Rewrites a Jmol session script, saves it to a temporary file, and returns
+ * the path of the file. "load file" commands are rewritten to change the
+ * original PDB file names to those created as the Jalview project is loaded.
+ *
+ * @param svattrib
+ * @param jprovider
+ * @return
+ */
+ private String rewriteJmolSession(StructureViewerModel svattrib,
+ jarInputStreamProvider jprovider)
+ {
+ String state = svattrib.getStateData(); // Jalview < 2.9
+ if (state == null || state.isEmpty()) // Jalview >= 2.9
+ {
+ String jarEntryName = getViewerJarEntryName(svattrib.getViewId());
+ state = readJarEntry(jprovider, jarEntryName);
+ }
+ // TODO or simpler? for each key in oldFiles,
+ // replace key.getPath() in state with oldFiles.get(key).getFilePath()
+ // (allowing for different path escapings)
+ StringBuilder rewritten = new StringBuilder(state.length());
+ int cp = 0, ncp, ecp;
+ Map<File, StructureData> oldFiles = svattrib.getFileData();
+ while ((ncp = state.indexOf("load ", cp)) > -1)
+ {
+ do
+ {
+ // look for next filename in load statement
+ rewritten.append(state.substring(cp,
+ ncp = (state.indexOf("\"", ncp + 1) + 1)));
+ String oldfilenam = state.substring(ncp,
+ ecp = state.indexOf("\"", ncp));
+ // recover the new mapping data for this old filename
+ // have to normalize filename - since Jmol and jalview do
+ // filename translation differently.
+ StructureData filedat = oldFiles.get(new File(oldfilenam));
+ if (filedat == null)
+ {
+ String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
+ filedat = oldFiles.get(new File(reformatedOldFilename));
+ }
+ rewritten
+ .append(Platform.escapeBackslashes(filedat.getFilePath()));
+ rewritten.append("\"");
+ cp = ecp + 1; // advance beyond last \" and set cursor so we can
+ // look for next file statement.
+ } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
+ }
+ if (cp > 0)
+ {
+ // just append rest of state
+ rewritten.append(state.substring(cp));
+ }
+ else
+ {
+ System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
+ rewritten = new StringBuilder(state);
+ rewritten.append("; load append ");
+ for (File id : oldFiles.keySet())
+ {
+ // add pdb files that should be present in the viewer
+ StructureData filedat = oldFiles.get(id);
+ rewritten.append(" \"").append(filedat.getFilePath()).append("\"");
+ }
+ rewritten.append(";");
+ }
+
+ if (rewritten.length() == 0)
+ {
+ return null;
+ }
+ final String history = "history = ";
+ int historyIndex = rewritten.indexOf(history);
+ if (historyIndex > -1)
+ {
+ /*
+ * change "history = [true|false];" to "history = [1|0];"
+ */
+ historyIndex += history.length();
+ String val = rewritten.substring(historyIndex, historyIndex + 5);
+ if (val.startsWith("true"))
+ {
+ rewritten.replace(historyIndex, historyIndex + 4, "1");
+ }
+ else if (val.startsWith("false"))
+ {
+ rewritten.replace(historyIndex, historyIndex + 5, "0");
+ }
+ }
+
+ try
+ {
+ File tmp = File.createTempFile("viewerSession", ".tmp");
+ try (OutputStream os = new FileOutputStream(tmp))
+ {
+ InputStream is = new ByteArrayInputStream(
+ rewritten.toString().getBytes());
+ copyAll(is, os);
+ return tmp.getAbsolutePath();
+ }
+ } catch (IOException e)
+ {
+ Cache.log.error("Error restoring Jmol session: " + e.toString());
+ }
+ return null;
+ }
+
+ /**
* Populates an XML model of the feature colour scheme for one feature type
*
* @param featureType
* Parses a Chimera atomspec e.g. #1:12.A to construct an AtomSpec model (with
* null pdb file name)
*
+ * <pre>
+ * Chimera format:
+ * #1.2:12-20.A model 1, submodel 2, chain A, atoms 12-20
+ * ChimeraX format:
+ * #1.2/A:12-20
+ * </pre>
+ *
* @param spec
+ * @param chimeraX
* @return
* @throw IllegalArgumentException if the spec cannot be parsed, or represents
* more than one residue
+ * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
+ * @see http://rbvi.ucsf.edu/chimerax/docs/user/commands/atomspec.html
*/
- public static AtomSpec fromChimeraAtomspec(String spec)
+ public static AtomSpec fromChimeraAtomspec(String spec, boolean chimeraX)
{
- int colonPos = spec.indexOf(":");
- if (colonPos == -1)
+ int modelSeparatorPos = spec.indexOf(chimeraX ? "/" : ":");
+ if (modelSeparatorPos == -1)
{
throw new IllegalArgumentException(spec);
}
int hashPos = spec.indexOf("#");
- if (hashPos == -1 && colonPos != 0)
+ if (hashPos == -1 && modelSeparatorPos != 0)
{
// # is missing but something precedes : - reject
throw new IllegalArgumentException(spec);
}
- String modelSubmodel = spec.substring(hashPos + 1, colonPos);
- int dotPos = modelSubmodel.indexOf(".");
+ String modelSubmodel = spec.substring(hashPos + 1, modelSeparatorPos);
int modelId = 0;
try
{
- modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel
- : modelSubmodel.substring(0, dotPos));
+ int subModelPos = modelSubmodel.indexOf(".");
+ modelId = Integer.valueOf(
+ subModelPos > 0 ? modelSubmodel.substring(0, subModelPos)
+ : modelSubmodel);
} catch (NumberFormatException e)
{
// ignore, default to model 0
}
- String residueChain = spec.substring(colonPos + 1);
- dotPos = residueChain.indexOf(".");
+ /*
+ * now process what follows the model, either
+ * Chimera: atoms.chain
+ * ChimeraX: chain:atoms
+ */
+ String atomsAndChain = spec.substring(modelSeparatorPos + 1);
+ String[] tokens = atomsAndChain.split(chimeraX ? "\\:" : "\\.");
+ String atoms = tokens.length == 1 ? atomsAndChain
+ : (chimeraX ? tokens[1] : tokens[0]);
int resNum = 0;
try
{
- resNum = Integer.parseInt(dotPos == -1 ? residueChain
- : residueChain.substring(0, dotPos));
+ resNum = Integer.parseInt(atoms);
} catch (NumberFormatException e)
{
// could be a range e.g. #1:4-7.B
throw new IllegalArgumentException(spec);
}
- String chainId = dotPos == -1 ? "" : residueChain.substring(dotPos + 1);
+ String chainId = tokens.length == 1 ? ""
+ : (chimeraX ? tokens[0] : tokens[1]);
return new AtomSpec(modelId, chainId, resNum, 0);
}
--- /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.structure;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A class to model a set of models, chains and atom range positions
+ *
+ */
+public class AtomSpecModel
+{
+ /*
+ * { modelId, {chainCode, List<from-to> ranges} }
+ */
+ private Map<String, Map<String, BitSet>> atomSpec;
+
+ /**
+ * Constructor
+ */
+ public AtomSpecModel()
+ {
+ atomSpec = new TreeMap<>();
+ }
+
+ /**
+ * Adds one contiguous range to this atom spec
+ *
+ * @param model
+ * @param startPos
+ * @param endPos
+ * @param chain
+ */
+ public void addRange(String model, int startPos, int endPos, String chain)
+ {
+ /*
+ * Get/initialize map of data for the colour and model
+ */
+ Map<String, BitSet> modelData = atomSpec.get(model);
+ if (modelData == null)
+ {
+ atomSpec.put(model, modelData = new TreeMap<>());
+ }
+
+ /*
+ * Get/initialize map of data for colour, model and chain
+ */
+ BitSet chainData = modelData.get(chain);
+ if (chainData == null)
+ {
+ chainData = new BitSet();
+ modelData.put(chain, chainData);
+ }
+
+ /*
+ * Add the start/end positions
+ */
+ chainData.set(startPos, endPos + 1);
+ }
+
+ public Iterable<String> getModels()
+ {
+ return atomSpec.keySet();
+ }
+
+ public int getModelCount()
+ {
+ return atomSpec.size();
+ }
+
+ public Iterable<String> getChains(String model)
+ {
+ return atomSpec.containsKey(model) ? atomSpec.get(model).keySet()
+ : null;
+ }
+
+ /**
+ * Returns a (possibly empty) ordered list of contiguous atom ranges for the
+ * given model and chain.
+ *
+ * @param model
+ * @param chain
+ * @return
+ */
+ public List<int[]> getRanges(String model, String chain)
+ {
+ List<int[]> ranges = new ArrayList<>();
+ if (atomSpec.containsKey(model))
+ {
+ BitSet bs = atomSpec.get(model).get(chain);
+ int start = 0;
+ if (bs != null)
+ {
+ start = bs.nextSetBit(start);
+ int end = 0;
+ while (start != -1)
+ {
+ end = bs.nextClearBit(start);
+ ranges.add(new int[] { start, end - 1 });
+ start = bs.nextSetBit(end);
+ }
+ }
+ }
+ return ranges;
+ }
+}
--- /dev/null
+package jalview.structure;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StructureCommand implements StructureCommandI
+{
+ private String command;
+
+ private List<String> parameters;
+
+ public StructureCommand(String cmd, String... params)
+ {
+ command = cmd;
+ if (params != null)
+ {
+ for (String p : params)
+ {
+ addParameter(p);
+ }
+ }
+ }
+
+ @Override
+ public void addParameter(String param)
+ {
+ if (parameters == null)
+ {
+ parameters = new ArrayList<>();
+ }
+ parameters.add(param);
+ }
+
+ @Override
+ public String getCommand()
+ {
+ return command;
+ }
+
+ @Override
+ public List<String> getParameters()
+ {
+ return parameters;
+ }
+
+ @Override
+ public boolean hasParameters()
+ {
+ return parameters != null && !parameters.isEmpty();
+ }
+
+ @Override
+ public String toString()
+ {
+ if (!hasParameters())
+ {
+ return command;
+ }
+ StringBuilder sb = new StringBuilder(32);
+ sb.append(command).append("(");
+ boolean first = true;
+ for (String p : parameters)
+ {
+ if (!first)
+ {
+ sb.append(",");
+ }
+ first = false;
+ sb.append(p);
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+}
--- /dev/null
+package jalview.structure;
+
+import java.util.List;
+
+public interface StructureCommandI
+{
+ String getCommand();
+
+ List<String> getParameters();
+
+ void addParameter(String param);
+
+ boolean hasParameters();
+}
--- /dev/null
+package jalview.structure;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * A base class holding methods useful to all classes that implement commands
+ * for structure viewers
+ *
+ * @author gmcarstairs
+ *
+ */
+public abstract class StructureCommandsBase implements StructureCommandsI
+{
+ private static final String CMD_SEPARATOR = ";";
+ public static final String NAMESPACE_PREFIX = "jv_";
+
+ /**
+ * Returns something that separates concatenated commands
+ *
+ * @return
+ */
+ protected static String getCommandSeparator()
+ {
+ return CMD_SEPARATOR;
+ }
+
+ /**
+ * Returns the lowest model number used by the structure viewer
+ *
+ * @return
+ */
+ @Override
+ public int getModelStartNo()
+ {
+ return 0;
+ }
+
+ /**
+ * Helper method to add one contiguous range to the AtomSpec model for the given
+ * value (creating the model if necessary). As used by Jalview, {@code value} is
+ * <ul>
+ * <li>a colour, when building a 'colour structure by sequence' command</li>
+ * <li>a feature value, when building a 'set Chimera attributes from features'
+ * command</li>
+ * </ul>
+ *
+ * @param map
+ * @param value
+ * @param model
+ * @param startPos
+ * @param endPos
+ * @param chain
+ */
+ public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+ Object value, String model, int startPos, int endPos,
+ String chain)
+ {
+ /*
+ * Get/initialize map of data for the colour
+ */
+ AtomSpecModel atomSpec = map.get(value);
+ if (atomSpec == null)
+ {
+ atomSpec = new AtomSpecModel();
+ map.put(value, atomSpec);
+ }
+
+ atomSpec.addRange(model, startPos, endPos, chain);
+ }
+
+ /**
+ * Makes a structure viewer attribute name for a Jalview feature type by
+ * prefixing it with "jv_", and replacing any non-alphanumeric characters with
+ * an underscore
+ *
+ * @param featureType
+ * @return
+ */
+ protected String makeAttributeName(String featureType)
+ {
+ StringBuilder sb = new StringBuilder();
+ if (featureType != null)
+ {
+ for (char c : featureType.toCharArray())
+ {
+ sb.append(Character.isLetterOrDigit(c) ? c : '_');
+ }
+ }
+ String attName = NAMESPACE_PREFIX + sb.toString();
+ return attName;
+ }
+
+ /**
+ * Traverse the map of colours/models/chains/positions to construct a list of
+ * 'color' commands (one per distinct colour used). The format of each command
+ * is specific to the structure viewer.
+ * <p>
+ * The default implementation returns a single command containing one command
+ * per colour, concatenated.
+ *
+ * @param colourMap
+ * @return
+ */
+ @Override
+ public List<StructureCommandI> colourBySequence(
+ Map<Object, AtomSpecModel> colourMap)
+ {
+ List<StructureCommandI> commands = new ArrayList<>();
+ StringBuilder sb = new StringBuilder(colourMap.size() * 20);
+ boolean first = true;
+ for (Object key : colourMap.keySet())
+ {
+ Color colour = (Color) key;
+ final AtomSpecModel colourData = colourMap.get(colour);
+ StructureCommandI command = getColourCommand(colourData, colour);
+ if (!first)
+ {
+ sb.append(getCommandSeparator());
+ }
+ first = false;
+ sb.append(command.getCommand());
+ }
+
+ commands.add(new StructureCommand(sb.toString()));
+ return commands;
+ }
+
+ /**
+ * Returns a command to colour the atoms represented by {@code atomSpecModel}
+ * with the colour specified by {@code colourCode}.
+ *
+ * @param atomSpecModel
+ * @param colour
+ * @return
+ */
+ protected StructureCommandI getColourCommand(AtomSpecModel atomSpecModel,
+ Color colour)
+ {
+ String atomSpec = getAtomSpec(atomSpecModel, false);
+ return colourResidues(atomSpec, colour);
+ }
+
+ /**
+ * Returns a command to colour the atoms described (in viewer command syntax)
+ * by {@code atomSpec} with the colour specified by {@code colourCode}
+ *
+ * @param atomSpec
+ * @param colour
+ * @return
+ */
+ protected abstract StructureCommandI colourResidues(String atomSpec,
+ Color colour);
+
+ @Override
+ public List<StructureCommandI> colourByResidues(
+ Map<String, Color> colours)
+ {
+ List<StructureCommandI> commands = new ArrayList<>();
+ for (Entry<String, Color> entry : colours.entrySet())
+ {
+ commands.add(colourResidue(entry.getKey(), entry.getValue()));
+ }
+ return commands;
+ }
+
+ private StructureCommandI colourResidue(String resName, Color col)
+ {
+ String atomSpec = getResidueSpec(resName);
+ return colourResidues(atomSpec, col);
+ }
+
+ /**
+ * Helper method to append one start-end range to an atomspec string
+ *
+ * @param sb
+ * @param start
+ * @param end
+ * @param chain
+ * @param firstPositionForModel
+ */
+ protected void appendRange(StringBuilder sb, int start, int end,
+ String chain, boolean firstPositionForModel, boolean isChimeraX)
+ {
+ if (!firstPositionForModel)
+ {
+ sb.append(",");
+ }
+ if (end == start)
+ {
+ sb.append(start);
+ }
+ else
+ {
+ sb.append(start).append("-").append(end);
+ }
+
+ if (!isChimeraX)
+ {
+ sb.append(".");
+ if (!" ".equals(chain))
+ {
+ sb.append(chain);
+ }
+ }
+ }
+
+ /**
+ * Returns the atom specifier meaning all occurrences of the given residue
+ *
+ * @param residue
+ * @return
+ */
+ protected abstract String getResidueSpec(String residue);
+
+ @Override
+ public List<StructureCommandI> setAttributes(
+ Map<String, Map<Object, AtomSpecModel>> featureValues)
+ {
+ // default does nothing, override where this is implemented
+ return null;
+ }
+}
--- /dev/null
+package jalview.structure;
+
+import java.awt.Color;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Methods that generate commands that can be sent to a molecular structure
+ * viewer program (e.g. Jmol, Chimera, ChimeraX)
+ *
+ * @author gmcarstairs
+ *
+ */
+public interface StructureCommandsI
+{
+ /**
+ * Returns the command to colour by chain
+ *
+ * @return
+ */
+ StructureCommandI colourByChain();
+
+ /**
+ * Returns the command to colour residues using a charge-based scheme:
+ * <ul>
+ * <li>Aspartic acid and Glutamic acid (negative charge) red</li>
+ * <li>Lysine and Arginine (positive charge) blue</li>
+ * <li>Cysteine - yellow</li>
+ * <li>all others - white</li>
+ * </ul>
+ *
+ * @return
+ */
+ List<StructureCommandI> colourByCharge();
+
+ /**
+ * Returns the command to colour residues with the colours provided in the
+ * map, one per three letter residue code
+ *
+ * @param colours
+ * @return
+ */
+ List<StructureCommandI> colourByResidues(Map<String, Color> colours);
+
+ /**
+ * Returns the command to set the background colour of the structure viewer
+ *
+ * @param col
+ * @return
+ */
+ StructureCommandI setBackgroundColour(Color col);
+
+ /**
+ * Returns commands to colour mapped residues of structures according to
+ * Jalview's colouring (including feature colouring if applied). Parameter is
+ * a map from Color to a model of all residues assigned that colour.
+ *
+ * @param colourMap
+ * @return
+ */
+
+ List<StructureCommandI> colourBySequence(
+ Map<Object, AtomSpecModel> colourMap);
+
+ /**
+ * Returns a command to centre the display in the structure viewer
+ *
+ * @return
+ */
+ StructureCommandI focusView();
+
+ /**
+ * Returns a command to show only the selected chains. The items in the input
+ * list should be formatted as "modelid:chainid".
+ *
+ * @param toShow
+ * @return
+ */
+ List<StructureCommandI> showChains(List<String> toShow);
+
+ /**
+ * Returns a command to superpose structures by closest positioning of
+ * residues in {@code atomSpec} to the corresponding residues in
+ * {@code refAtoms}. If wanted, this may include commands to visually
+ * highlight the residues that were used for the superposition.
+ *
+ * @param refAtoms
+ * @param atomSpec
+ * @return
+ */
+ List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
+ AtomSpecModel atomSpec);
+
+ /**
+ * Returns a command to open a file of commands at the given path
+ *
+ * @param path
+ * @return
+ */
+ StructureCommandI openCommandFile(String path);
+
+ /**
+ * Returns a command to save the current viewer session state to the given
+ * file
+ *
+ * @param filepath
+ * @return
+ */
+ StructureCommandI saveSession(String filepath);
+
+ /**
+ * Returns a representation of the atom set represented by the model, in
+ * viewer syntax format. If {@code alphaOnly} is true, this is restricted to
+ * Alpha Carbon (peptide) or Phosphorous (rna) only
+ *
+ * @param model
+ * @param alphaOnly
+ * @return
+ */
+ String getAtomSpec(AtomSpecModel model, boolean alphaOnly);
+
+ /**
+ * Returns the lowest model number used by the structure viewer (likely 0 or
+ * 1)
+ *
+ * @return
+ */
+ // TODO remove by refactoring so command generation is purely driven by
+ // AtomSpecModel objects derived in the binding classes?
+ int getModelStartNo();
+
+ /**
+ * Returns command(s) to show only the backbone of the peptide (cartoons in
+ * Jmol, chain in Chimera)
+ *
+ * @return
+ */
+ List<StructureCommandI> showBackbone();
+
+ /**
+ * Returns a command to open a file at the given path
+ *
+ * @param file
+ * @return
+ */
+ // refactor if needed to distinguish loading data or session files
+ StructureCommandI loadFile(String file);
+
+ /**
+ * Returns commands to set atom attributes or properties, given a map of
+ * Jalview features as {featureType, {featureValue, AtomSpecModel}}. The
+ * assumption is that one command can be constructed for each feature type and
+ * value combination, to apply it to one or more residues.
+ *
+ * @param featureValues
+ * @return
+ */
+ List<StructureCommandI> setAttributes(
+ Map<String, Map<Object, AtomSpecModel>> featureValues);
+
+ /**
+ * Returns command to open a saved structure viewer session file, or null if
+ * not supported
+ *
+ * @param filepath
+ * @return
+ */
+ StructureCommandI openSession(String filepath);
+}
pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
seqToStrucMapping.add(siftsMapping);
maxChain.makeExactMapping(siftsMapping, seq);
- maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
- // "IEA:SIFTS" ?
+ maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
maxChain.transferResidueAnnotation(siftsMapping, null);
ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
} catch (SiftsException e)
{
// fall back to NW alignment
- System.err.println(e.getMessage());
+ Cache.log.error(e.getMessage());
StructureMapping nwMapping = getNWMappings(seq, pdbFile,
targetChainId, maxChain, pdb, maxAlignseq);
seqToStrucMapping.add(nwMapping);
*/
package jalview.structures.models;
+import java.awt.Color;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+
+import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
+import jalview.api.FeatureRenderer;
import jalview.api.SequenceRenderer;
import jalview.api.StructureSelectionManagerProvider;
import jalview.api.structures.JalviewStructureDisplayI;
+import jalview.bin.Cache;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.MappedFeatures;
import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
+import jalview.gui.Desktop;
+import jalview.gui.StructureViewer.ViewerType;
import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.schemes.ColourSchemeI;
+import jalview.schemes.ResidueProperties;
import jalview.structure.AtomSpec;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsI;
import jalview.structure.StructureListener;
import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
import jalview.structure.StructureSelectionManager;
import jalview.util.Comparison;
import jalview.util.MessageManager;
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.List;
-
/**
*
* A base class to hold common function for protein structure model binding.
extends SequenceStructureBindingModel
implements StructureListener, StructureSelectionManagerProvider
{
+ /**
+ * Data bean class to simplify parameterisation in superposeStructures
+ */
+ public static class SuperposeData
+ {
+ public String filename;
+
+ public String pdbId;
+
+ public String chain = "";
+
+ public boolean isRna;
+
+ /*
+ * The pdb residue number (if any) mapped to columns of the alignment
+ */
+ public int[] pdbResNo; // or use SparseIntArray?
+
+ public String modelId;
+
+ /**
+ * Constructor
+ *
+ * @param width
+ * width of alignment (number of columns that may potentially
+ * participate in superposition)
+ * @param model
+ * structure viewer model number
+ */
+ public SuperposeData(int width, String model)
+ {
+ pdbResNo = new int[width];
+ modelId = model;
+ }
+ }
+
+ private static final int MIN_POS_TO_SUPERPOSE = 4;
+
+ private static final String COLOURING_STRUCTURES = MessageManager
+ .getString("status.colouring_structures");
+
+ /*
+ * the Jalview panel through which the user interacts
+ * with the structure viewer
+ */
+ private JalviewStructureDisplayI viewer;
+
+ /*
+ * helper that generates command syntax
+ */
+ private StructureCommandsI commandGenerator;
private StructureSelectionManager ssm;
/*
+ * modelled chains, formatted as "pdbid:chainCode"
+ */
+ private List<String> chainNames;
+
+ /*
+ * lookup of pdb file name by key "pdbid:chainCode"
+ */
+ private Map<String, String> chainFile;
+
+ /*
* distinct PDB entries (pdb files) associated
* with sequences
*/
private boolean finishedInit = false;
/**
- * current set of model filenames loaded in the Jmol instance
+ * current set of model filenames loaded in the viewer
*/
protected String[] modelFileNames = null;
public String fileLoadingError;
/**
- * Data bean class to simplify parameterisation in superposeStructures
- */
- protected class SuperposeData
- {
- /**
- * Constructor with alignment width argument
- *
- * @param width
- */
- public SuperposeData(int width)
- {
- pdbResNo = new int[width];
- }
-
- public String filename;
-
- public String pdbId;
-
- public String chain = "";
-
- public boolean isRna;
-
- /*
- * The pdb residue number (if any) mapped to each column of the alignment
- */
- public int[] pdbResNo;
- }
-
- /**
* Constructor
*
* @param ssm
{
this.ssm = ssm;
this.sequence = seqs;
+ chainNames = new ArrayList<>();
+ chainFile = new HashMap<>();
}
/**
PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
DataSourceType protocol)
{
- this.ssm = ssm;
- this.sequence = sequenceIs;
+ this(ssm, sequenceIs);
this.nucleotide = Comparison.isNucleotide(sequenceIs);
this.pdbEntry = pdbentry;
this.protocol = protocol;
*/
protected void releaseUIResources()
{
+ }
+ @Override
+ public void releaseReferences(Object svl)
+ {
}
public boolean isColourBySequence()
return colourBySequence;
}
+ /**
+ * Called when the binding thinks the UI needs to be refreshed after a
+ * structure viewer state change. This could be because structures were
+ * loaded, or because an error has occurred. Default does nothing, override as
+ * required.
+ */
+ public void refreshGUI()
+ {
+ }
+
+ /**
+ * Instruct the Jalview binding to update the pdbentries vector if necessary
+ * prior to matching the jmol view's contents to the list of structure files
+ * Jalview knows about. By default does nothing, override as required.
+ */
+ public void refreshPdbEntries()
+ {
+ }
+
public void setColourBySequence(boolean colourBySequence)
{
this.colourBySequence = colourBySequence;
{ Integer.valueOf(pe).toString() }));
}
final String nullChain = "TheNullChain";
- List<SequenceI> s = new ArrayList<SequenceI>();
- List<String> c = new ArrayList<String>();
+ List<SequenceI> s = new ArrayList<>();
+ List<String> c = new ArrayList<>();
if (getChains() == null)
{
setChains(new String[getPdbCount()][]);
public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
SequenceI[][] seq, String[][] chns)
{
- List<PDBEntry> v = new ArrayList<PDBEntry>();
- List<int[]> rtn = new ArrayList<int[]>();
+ List<PDBEntry> v = new ArrayList<>();
+ List<int[]> rtn = new ArrayList<>();
for (int i = 0; i < getPdbCount(); i++)
{
v.add(getPdbEntry(i));
* @return
*/
protected int findSuperposableResidues(AlignmentI alignment,
- BitSet matched, SuperposeData[] structures)
+ BitSet matched, AAStructureBindingModel.SuperposeData[] structures)
{
int refStructure = -1;
String[] files = getStructureFiles();
* for the same structure)
*/
s = seqCountForPdbFile;
- break;
+ break; // fixme break out of two loops here!
}
}
}
}
/**
- * Returns a list of chains mapped in this viewer.
+ * Returns a list of chains mapped in this viewer, formatted as
+ * "pdbid:chainCode"
*
* @return
*/
- public abstract List<String> getChainNames();
+ public List<String> getChainNames()
+ {
+ return chainNames;
+ }
/**
* Returns the Jalview panel hosting the structure viewer (if any)
*/
public JalviewStructureDisplayI getViewer()
{
- return null;
+ return viewer;
}
- public abstract void setJalviewColourScheme(ColourSchemeI cs);
+ public void setViewer(JalviewStructureDisplayI v)
+ {
+ viewer = v;
+ }
/**
* Constructs and sends a command to align structures against a reference
* structure, based on one or more sequence alignments. May optionally return
- * an error or warning message for the alignment command.
- *
- * @param alignments
- * an array of alignments to process
- * @param structureIndices
- * an array of corresponding reference structures (index into pdb
- * file array); if a negative value is passed, the first PDB file
- * mapped to an alignment sequence is used as the reference for
- * superposition
- * @param hiddenCols
- * an array of corresponding hidden columns for each alignment
+ * an error or warning message for the alignment command(s).
+ *
+ * @param alignWith
+ * an array of one or more alignment views to process
* @return
*/
- public abstract String superposeStructures(AlignmentI[] alignments,
- int[] structureIndices, HiddenColumns[] hiddenCols);
+ public String superposeStructures(List<AlignmentViewPanel> alignWith)
+ {
+ String error = "";
+ String[] files = getStructureFiles();
+
+ if (!waitForFileLoad(files))
+ {
+ return null;
+ }
+ refreshPdbEntries();
+
+ for (AlignmentViewPanel view : alignWith)
+ {
+ AlignmentI alignment = view.getAlignment();
+ HiddenColumns hiddenCols = alignment.getHiddenColumns();
+
+ /*
+ * 'matched' bit i will be set for visible alignment columns i where
+ * all sequences have a residue with a mapping to their PDB structure
+ */
+ BitSet matched = new BitSet();
+ final int width = alignment.getWidth();
+ for (int m = 0; m < width; m++)
+ {
+ if (hiddenCols == null || hiddenCols.isVisible(m))
+ {
+ matched.set(m);
+ }
+ }
+
+ AAStructureBindingModel.SuperposeData[] structures = new AAStructureBindingModel.SuperposeData[files.length];
+ for (int f = 0; f < files.length; f++)
+ {
+ structures[f] = new AAStructureBindingModel.SuperposeData(width,
+ getModelIdForFile(files[f]));
+ }
+
+ /*
+ * Calculate the superposable alignment columns ('matched'), and the
+ * corresponding structure residue positions (structures.pdbResNo)
+ */
+ int refStructure = findSuperposableResidues(alignment,
+ matched, structures);
+
+ /*
+ * require at least 4 positions to be able to execute superposition
+ */
+ int nmatched = matched.cardinality();
+ if (nmatched < MIN_POS_TO_SUPERPOSE)
+ {
+ String msg = MessageManager.formatMessage("label.insufficient_residues",
+ nmatched);
+ error += view.getViewName() + ": " + msg + "; ";
+ continue;
+ }
+
+ /*
+ * get a model of the superposable residues in the reference structure
+ */
+ AtomSpecModel refAtoms = getAtomSpec(structures[refStructure],
+ matched);
+
+ /*
+ * Show all as backbone before doing superposition(s)
+ * (residues used for matching will be shown as ribbon)
+ */
+ // todo better way to ensure synchronous than setting getReply true!!
+ executeCommands(commandGenerator.showBackbone(), true, null);
+
+ /*
+ * superpose each (other) structure to the reference in turn
+ */
+ for (int i = 0; i < structures.length; i++)
+ {
+ if (i != refStructure)
+ {
+ AtomSpecModel atomSpec = getAtomSpec(structures[i], matched);
+ List<StructureCommandI> commands = commandGenerator
+ .superposeStructures(refAtoms, atomSpec);
+ List<String> replies = executeCommands(commands, true, null);
+ for (String reply : replies)
+ {
+ // return this error (Chimera only) to the user
+ if (reply.toLowerCase().contains("unequal numbers of atoms"))
+ {
+ error += "; " + reply;
+ }
+ }
+ }
+ }
+ }
+
+ return error;
+ }
- public abstract void setBackgroundColour(Color col);
+ private AtomSpecModel getAtomSpec(AAStructureBindingModel.SuperposeData superposeData,
+ BitSet matched)
+ {
+ AtomSpecModel model = new AtomSpecModel();
+ int nextColumnMatch = matched.nextSetBit(0);
+ while (nextColumnMatch != -1)
+ {
+ int pdbResNum = superposeData.pdbResNo[nextColumnMatch];
+ model.addRange(superposeData.modelId, pdbResNum, pdbResNum,
+ superposeData.chain);
+ nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1);
+ }
- protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, AlignmentViewPanel avp);
+ return model;
+ }
/**
* returns the current sequenceRenderer that should be used to colour the
public abstract SequenceRenderer getSequenceRenderer(
AlignmentViewPanel alignment);
- protected abstract void colourBySequence(
- StructureMappingcommandSet[] colourBySequenceCommands);
+ /**
+ * Sends a command to the structure viewer to colour each chain with a
+ * distinct colour (to the extent supported by the viewer)
+ */
+ public void colourByChain()
+ {
+ colourBySequence = false;
- public abstract void colourByChain();
+ // TODO: JAL-628 colour chains distinctly across all visible models
- public abstract void colourByCharge();
+ executeCommand(commandGenerator.colourByChain(), false,
+ COLOURING_STRUCTURES);
+ }
/**
- * colour any structures associated with sequences in the given alignment
- * using the getFeatureRenderer() and getSequenceRenderer() renderers but only
- * if colourBySequence is enabled.
+ * Sends a command to the structure viewer to colour each chain with a
+ * distinct colour (to the extent supported by the viewer)
*/
- public void colourBySequence(AlignmentViewPanel alignmentv)
+ public void colourByCharge()
{
- if (!colourBySequence || !isLoadingFinished())
+ colourBySequence = false;
+
+ executeCommands(commandGenerator.colourByCharge(), false,
+ COLOURING_STRUCTURES);
+ }
+
+ /**
+ * Sends a command to the structure to apply a colour scheme (defined in
+ * Jalview but not necessarily applied to the alignment), which defines a
+ * colour per residue letter. More complex schemes (e.g. that depend on
+ * consensus) cannot be used here and are ignored.
+ *
+ * @param cs
+ */
+ public void colourByJalviewColourScheme(ColourSchemeI cs)
+ {
+ colourBySequence = false;
+
+ if (cs == null || !cs.isSimple())
{
return;
}
- if (getSsm() == null)
+
+ /*
+ * build a map of {Residue3LetterCode, Color}
+ */
+ Map<String, Color> colours = new HashMap<>();
+ List<String> residues = ResidueProperties.getResidues(isNucleotide(),
+ false);
+ for (String resName : residues)
+ {
+ char res = resName.length() == 3
+ ? ResidueProperties.getSingleCharacterCode(resName)
+ : resName.charAt(0);
+ Color colour = cs.findColour(res, 0, null, null, 0f);
+ colours.put(resName, colour);
+ }
+
+ /*
+ * pass to the command constructor, and send the command
+ */
+ List<StructureCommandI> cmd = commandGenerator
+ .colourByResidues(colours);
+ executeCommands(cmd, false, COLOURING_STRUCTURES);
+ }
+
+ public void setBackgroundColour(Color col)
+ {
+ StructureCommandI cmd = commandGenerator.setBackgroundColour(col);
+ executeCommand(cmd, false, null);
+ }
+
+ /**
+ * Sends one command to the structure viewer. If {@code getReply} is true, the
+ * command is sent synchronously, otherwise in a deferred thread.
+ * <p>
+ * If a progress message is supplied, this is displayed before command
+ * execution, and removed afterwards.
+ *
+ * @param cmd
+ * @param getReply
+ * @param msg
+ * @return
+ */
+ private List<String> executeCommand(StructureCommandI cmd,
+ boolean getReply, String msg)
+ {
+ if (getReply)
+ {
+ /*
+ * synchronous (same thread) execution so reply can be returned
+ */
+ final JalviewStructureDisplayI theViewer = getViewer();
+ final long handle = msg == null ? 0 : theViewer.startProgressBar(msg);
+ try
+ {
+ return executeCommand(cmd, getReply);
+ } finally
+ {
+ if (msg != null)
+ {
+ theViewer.stopProgressBar(null, handle);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * asynchronous (new thread) execution if no reply needed
+ */
+ final JalviewStructureDisplayI theViewer = getViewer();
+ final long handle = msg == null ? 0 : theViewer.startProgressBar(msg);
+
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ executeCommand(cmd, false);
+ } finally
+ {
+ if (msg != null)
+ {
+ theViewer.stopProgressBar(null, handle);
+ }
+ }
+ }
+ });
+ return null;
+ }
+ }
+
+ /**
+ * Execute one structure viewer command. If {@code getReply} is true, may
+ * optionally return one or more reply messages, else returns null.
+ *
+ * @param cmd
+ * @param getReply
+ */
+ protected abstract List<String> executeCommand(StructureCommandI cmd,
+ boolean getReply);
+
+ /**
+ * A helper method that converts list of commands to a vararg array
+ *
+ * @param commands
+ * @param getReply
+ * @param msg
+ */
+ protected List<String> executeCommands(
+ List<StructureCommandI> commands,
+ boolean getReply, String msg)
+ {
+ return executeCommands(getReply, msg,
+ commands.toArray(new StructureCommandI[commands.size()]));
+ }
+
+ /**
+ * Executes one or more structure viewer commands. If a progress message is
+ * provided, it is shown first, and removed after all commands have been run.
+ *
+ * @param getReply
+ * @param msg
+ * @param commands
+ * @return
+ */
+ protected List<String> executeCommands(boolean getReply, String msg,
+ StructureCommandI[] commands)
+ {
+ // todo: tidy this up
+
+ /*
+ * show progress message if specified
+ */
+ final JalviewStructureDisplayI theViewer = getViewer();
+ final long handle = msg == null ? 0 : theViewer.startProgressBar(msg);
+
+ List<String> response = getReply ? new ArrayList<>() : null;
+ try
+ {
+ for (StructureCommandI cmd : commands)
+ {
+ List<String> replies = executeCommand(cmd, getReply, null);
+ if (getReply && replies != null)
+ {
+ response.addAll(replies);
+ }
+ }
+ return response;
+ } finally
+ {
+ if (msg != null)
+ {
+ theViewer.stopProgressBar(null, handle);
+ }
+ }
+ }
+
+ /**
+ * Colours any structures associated with sequences in the given alignment as
+ * coloured in the alignment view, provided colourBySequence is enabled
+ */
+ public void colourBySequence(AlignmentViewPanel alignmentv)
+ {
+ if (!colourBySequence || !isLoadingFinished() || getSsm() == null)
{
return;
}
- String[] files = getStructureFiles();
+ Map<Object, AtomSpecModel> colourMap = buildColoursMap(ssm, sequence,
+ alignmentv);
+
+ List<StructureCommandI> colourBySequenceCommands = commandGenerator
+ .colourBySequence(colourMap);
+ executeCommands(colourBySequenceCommands, false, null);
+ }
+
+ /**
+ * Centre the display in the structure viewer
+ */
+ public void focusView()
+ {
+ executeCommand(commandGenerator.focusView(), false, null);
+ }
- SequenceRenderer sr = getSequenceRenderer(alignmentv);
+ /**
+ * Generates and executes a command to show only specified chains in the
+ * structure viewer. The list of chains to show should contain entries
+ * formatted as "pdbid:chaincode".
+ *
+ * @param toShow
+ */
+ public void showChains(List<String> toShow)
+ {
+ // todo or reformat toShow list entries as modelNo:pdbId:chainCode ?
- StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands(
- files, sr, alignmentv);
- colourBySequence(colourBySequenceCommands);
+ /*
+ * Reformat the pdbid:chainCode values as modelNo:chainCode
+ * since this is what is needed to construct the viewer command
+ * todo: find a less messy way to do this
+ */
+ List<String> showThese = new ArrayList<>();
+ for (String chainId : toShow)
+ {
+ String[] tokens = chainId.split("\\:");
+ if (tokens.length == 2)
+ {
+ String pdbFile = getFileForChain(chainId);
+ String model = getModelIdForFile(pdbFile);
+ showThese.add(model + ":" + tokens[1]);
+ }
+ }
+ executeCommands(commandGenerator.showChains(showThese), false, null);
}
+ /**
+ * Answers the structure viewer's model id given a PDB file name. Returns an
+ * empty string if model id is not found.
+ *
+ * @param chainId
+ * @return
+ */
+ protected abstract String getModelIdForFile(String chainId);
+
public boolean hasFileLoadingError()
{
return fileLoadingError != null && fileLoadingError.length() > 0;
}
- public abstract jalview.api.FeatureRenderer getFeatureRenderer(
- AlignmentViewPanel alignment);
+ /**
+ * Returns the FeatureRenderer for the given alignment view, or null if
+ * feature display is turned off in the view.
+ *
+ * @param avp
+ * @return
+ */
+ public FeatureRenderer getFeatureRenderer(AlignmentViewPanel avp)
+ {
+ AlignmentViewPanel ap = (avp == null) ? getViewer().getAlignmentPanel()
+ : avp;
+ if (ap == null)
+ {
+ return null;
+ }
+ return ap.getAlignViewport().isShowSequenceFeatures()
+ ? ap.getFeatureRenderer()
+ : null;
+ }
+
+ protected void setStructureCommands(StructureCommandsI cmd)
+ {
+ commandGenerator = cmd;
+ }
+
+ /**
+ * Records association of one chain id (formatted as "pdbid:chainCode") with
+ * the corresponding PDB file name
+ *
+ * @param chainId
+ * @param fileName
+ */
+ public void addChainFile(String chainId, String fileName)
+ {
+ chainFile.put(chainId, fileName);
+ }
+
+ /**
+ * Returns the PDB filename for the given chain id (formatted as
+ * "pdbid:chainCode"), or null if not found
+ *
+ * @param chainId
+ * @return
+ */
+ protected String getFileForChain(String chainId)
+ {
+ return chainFile.get(chainId);
+ }
+
+ @Override
+ public void updateColours(Object source)
+ {
+ AlignmentViewPanel ap = (AlignmentViewPanel) source;
+ // ignore events from panels not used to colour this view
+ if (!getViewer().isUsedForColourBy(ap))
+ {
+ return;
+ }
+ if (!isLoadingFromArchive())
+ {
+ colourBySequence(ap);
+ }
+ }
+
+ public StructureCommandsI getCommandGenerator()
+ {
+ return commandGenerator;
+ }
+
+ protected abstract ViewerType getViewerType();
+
+ /**
+ * Send a structure viewer command asynchronously in a new thread. If the
+ * progress message is not null, display this message while the command is
+ * executing.
+ *
+ * @param command
+ * @param progressMsg
+ */
+ protected void sendAsynchronousCommand(StructureCommandI command,
+ String progressMsg)
+ {
+ final JalviewStructureDisplayI theViewer = getViewer();
+ final long handle = progressMsg == null ? 0
+ : theViewer.startProgressBar(progressMsg);
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ executeCommand(command, false, null);
+ } finally
+ {
+ if (progressMsg != null)
+ {
+ theViewer.stopProgressBar(null, handle);
+ }
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Builds a data structure which records mapped structure residues for each
+ * colour. From this we can easily generate the viewer commands for colour by
+ * sequence. Constructs and returns a map of {@code Color} to
+ * {@code AtomSpecModel}, where the atomspec model holds
+ *
+ * <pre>
+ * Model ids
+ * Chains
+ * Residue positions
+ * </pre>
+ *
+ * Ordering is by order of addition (for colours), natural ordering (for
+ * models and chains)
+ *
+ * @param ssm
+ * @param sequence
+ * @param viewPanel
+ * @return
+ */
+ protected Map<Object, AtomSpecModel> buildColoursMap(
+ StructureSelectionManager ssm, SequenceI[][] sequence,
+ AlignmentViewPanel viewPanel)
+ {
+ String[] files = getStructureFiles();
+ SequenceRenderer sr = getSequenceRenderer(viewPanel);
+ FeatureRenderer fr = viewPanel.getFeatureRenderer();
+ FeatureColourFinder finder = new FeatureColourFinder(fr);
+ AlignViewportI viewport = viewPanel.getAlignViewport();
+ HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
+ AlignmentI al = viewport.getAlignment();
+ Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
+ Color lastColour = null;
+
+ for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+ {
+ final String modelId = getModelIdForFile(files[pdbfnum]);
+ StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+
+ if (mapping == null || mapping.length < 1)
+ {
+ continue;
+ }
+
+ int startPos = -1, lastPos = -1;
+ String lastChain = "";
+ for (int s = 0; s < sequence[pdbfnum].length; s++)
+ {
+ for (int sp, m = 0; m < mapping.length; m++)
+ {
+ final SequenceI seq = sequence[pdbfnum][s];
+ if (mapping[m].getSequence() == seq
+ && (sp = al.findIndex(seq)) > -1)
+ {
+ SequenceI asp = al.getSequenceAt(sp);
+ for (int r = 0; r < asp.getLength(); r++)
+ {
+ // no mapping to gaps in sequence
+ if (Comparison.isGap(asp.getCharAt(r)))
+ {
+ continue;
+ }
+ int pos = mapping[m].getPDBResNum(asp.findPosition(r));
+
+ if (pos < 1 || pos == lastPos)
+ {
+ continue;
+ }
+
+ Color colour = sr.getResidueColour(seq, r, finder);
+
+ /*
+ * darker colour for hidden regions
+ */
+ if (!cs.isVisible(r))
+ {
+ colour = Color.GRAY;
+ }
+
+ final String chain = mapping[m].getChain();
+
+ /*
+ * Just keep incrementing the end position for this colour range
+ * _unless_ colour, PDB model or chain has changed, or there is a
+ * gap in the mapped residue sequence
+ */
+ final boolean newColour = !colour.equals(lastColour);
+ final boolean nonContig = lastPos + 1 != pos;
+ final boolean newChain = !chain.equals(lastChain);
+ if (newColour || nonContig || newChain)
+ {
+ if (startPos != -1)
+ {
+ addAtomSpecRange(colourMap, lastColour, modelId,
+ startPos, lastPos, lastChain);
+ }
+ startPos = pos;
+ }
+ lastColour = colour;
+ lastPos = pos;
+ lastChain = chain;
+ }
+ // final colour range
+ if (lastColour != null)
+ {
+ addAtomSpecRange(colourMap, lastColour, modelId, startPos,
+ lastPos, lastChain);
+ }
+ // break;
+ }
+ }
+ }
+ }
+ return colourMap;
+ }
+
+ /**
+ * todo better refactoring (map lookup or similar to get viewer structure id)
+ *
+ * @param pdbfnum
+ * @param file
+ * @return
+ */
+ protected String getModelId(int pdbfnum, String file)
+ {
+ return String.valueOf(pdbfnum);
+ }
+
+ /**
+ * Saves chains, formatted as "pdbId:chainCode", and lookups from this to the
+ * full PDB file path
+ *
+ * @param pdb
+ * @param file
+ */
+ public void stashFoundChains(StructureFile pdb, String file)
+ {
+ for (int i = 0; i < pdb.getChains().size(); i++)
+ {
+ String chid = pdb.getId() + ":" + pdb.getChains().elementAt(i).id;
+ addChainFile(chid, file);
+ getChainNames().add(chid);
+ }
+ }
+
+ /**
+ * Helper method to add one contiguous range to the AtomSpec model for the given
+ * value (creating the model if necessary). As used by Jalview, {@code value} is
+ * <ul>
+ * <li>a colour, when building a 'colour structure by sequence' command</li>
+ * <li>a feature value, when building a 'set Chimera attributes from features'
+ * command</li>
+ * </ul>
+ *
+ * @param map
+ * @param value
+ * @param model
+ * @param startPos
+ * @param endPos
+ * @param chain
+ */
+ public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+ Object value,
+ String model, int startPos, int endPos, String chain)
+ {
+ /*
+ * Get/initialize map of data for the colour
+ */
+ AtomSpecModel atomSpec = map.get(value);
+ if (atomSpec == null)
+ {
+ atomSpec = new AtomSpecModel();
+ map.put(value, atomSpec);
+ }
+
+ atomSpec.addRange(model, startPos, endPos, chain);
+ }
+
+ /**
+ * Returns the file extension (including '.' separator) to use for a saved
+ * viewer session file. Default is to return null (not supported), override as
+ * required.
+ *
+ * @return
+ */
+ public String getSessionFileExtension()
+ {
+ return null;
+ }
+
+ /**
+ * If supported, saves the state of the structure viewer to a temporary file
+ * and returns the file. Returns null and logs an error on any failure.
+ *
+ * @return
+ */
+ public File saveSession()
+ {
+ String prefix = getViewerType().toString();
+ String suffix = getSessionFileExtension();
+ File f = null;
+ try
+ {
+ f = File.createTempFile(prefix, suffix);
+ saveSession(f);
+ } catch (IOException e)
+ {
+ Cache.log.error(String.format("Error saving %s session: %s",
+ prefix, e.toString()));
+ }
+
+ return f;
+ }
+
+ /**
+ * Saves the structure viewer session to the given file
+ *
+ * @param f
+ */
+ protected void saveSession(File f)
+ {
+ StructureCommandI cmd = commandGenerator
+ .saveSession(f.getPath());
+ if (cmd != null)
+ {
+ executeCommand(cmd, false);
+ }
+ }
+
+ /**
+ * Returns true if the viewer is an external structure viewer for which the
+ * process is still alive, else false (for Jmol, or an external viewer which
+ * the user has independently closed)
+ *
+ * @return
+ */
+ public boolean isViewerRunning()
+ {
+ return false;
+ }
+
+ /**
+ * Closes Jalview's structure viewer panel and releases associated resources.
+ * If it is managing an external viewer program, and {@code forceClose} is
+ * true, also shuts down that program.
+ *
+ * @param forceClose
+ */
+ public void closeViewer(boolean forceClose)
+ {
+ getSsm().removeStructureViewerListener(this, this.getStructureFiles());
+ releaseUIResources();
+
+ // add external viewer shutdown in overrides
+ // todo - or can maybe pull up to here
+ }
+
+ /**
+ * Returns the URL of a help page for the structure viewer, or null if none is
+ * known
+ *
+ * @return
+ */
+ public String getHelpURL()
+ {
+ return null;
+ }
+
+ /**
+ * <pre>
+ * Helper method to build a map of
+ * { featureType, { feature value, AtomSpecModel } }
+ * </pre>
+ *
+ * @param viewPanel
+ * @return
+ */
+ protected Map<String, Map<Object, AtomSpecModel>> buildFeaturesMap(
+ AlignmentViewPanel viewPanel)
+ {
+ Map<String, Map<Object, AtomSpecModel>> theMap = new LinkedHashMap<>();
+ String[] files = getStructureFiles();
+ if (files == null)
+ {
+ return theMap;
+ }
+
+ FeatureRenderer fr = viewPanel.getFeatureRenderer();
+ if (fr == null)
+ {
+ return theMap;
+ }
+
+ AlignViewportI viewport = viewPanel.getAlignViewport();
+ List<String> visibleFeatures = fr.getDisplayedFeatureTypes();
+
+ /*
+ * if alignment is showing features from complement, we also transfer
+ * these features to the corresponding mapped structure residues
+ */
+ boolean showLinkedFeatures = viewport.isShowComplementFeatures();
+ List<String> complementFeatures = new ArrayList<>();
+ FeatureRenderer complementRenderer = null;
+ if (showLinkedFeatures)
+ {
+ AlignViewportI comp = fr.getViewport().getCodingComplement();
+ if (comp != null)
+ {
+ complementRenderer = Desktop.getAlignFrameFor(comp)
+ .getFeatureRenderer();
+ complementFeatures = complementRenderer.getDisplayedFeatureTypes();
+ }
+ }
+ if (visibleFeatures.isEmpty() && complementFeatures.isEmpty())
+ {
+ return theMap;
+ }
+
+ AlignmentI alignment = viewPanel.getAlignment();
+ SequenceI[][] seqs = getSequence();
+
+ for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+ {
+ String modelId = getModelIdForFile(files[pdbfnum]);
+ StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+
+ if (mapping == null || mapping.length < 1)
+ {
+ continue;
+ }
+
+ for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++)
+ {
+ for (int m = 0; m < mapping.length; m++)
+ {
+ final SequenceI seq = seqs[pdbfnum][seqNo];
+ int sp = alignment.findIndex(seq);
+ StructureMapping structureMapping = mapping[m];
+ if (structureMapping.getSequence() == seq && sp > -1)
+ {
+ /*
+ * found a sequence with a mapping to a structure;
+ * now scan its features
+ */
+ if (!visibleFeatures.isEmpty())
+ {
+ scanSequenceFeatures(visibleFeatures, structureMapping, seq,
+ theMap, modelId);
+ }
+ if (showLinkedFeatures)
+ {
+ scanComplementFeatures(complementRenderer, structureMapping,
+ seq, theMap, modelId);
+ }
+ }
+ }
+ }
+ }
+ return theMap;
+ }
+
+ /**
+ * Ask the structure viewer to open a session file. Returns true if
+ * successful, else false (or not supported).
+ *
+ * @param filepath
+ * @return
+ */
+ public boolean openSession(String filepath)
+ {
+ StructureCommandI cmd = getCommandGenerator().openSession(filepath);
+ if (cmd == null)
+ {
+ return false;
+ }
+ executeCommand(cmd, true);
+ // todo: test for failure - how?
+ return true;
+ }
+
+ /**
+ * Scans visible features in mapped positions of the CDS/peptide complement, and
+ * adds any found to the map of attribute values/structure positions
+ *
+ * @param complementRenderer
+ * @param structureMapping
+ * @param seq
+ * @param theMap
+ * @param modelNumber
+ */
+ protected static void scanComplementFeatures(
+ FeatureRenderer complementRenderer,
+ StructureMapping structureMapping, SequenceI seq,
+ Map<String, Map<Object, AtomSpecModel>> theMap,
+ String modelNumber)
+ {
+ /*
+ * for each sequence residue mapped to a structure position...
+ */
+ for (int seqPos : structureMapping.getMapping().keySet())
+ {
+ /*
+ * find visible complementary features at mapped position(s)
+ */
+ MappedFeatures mf = complementRenderer
+ .findComplementFeaturesAtResidue(seq, seqPos);
+ if (mf != null)
+ {
+ for (SequenceFeature sf : mf.features)
+ {
+ String type = sf.getType();
+
+ /*
+ * Don't copy features which originated from Chimera
+ */
+ if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+ .equals(sf.getFeatureGroup()))
+ {
+ continue;
+ }
+
+ /*
+ * record feature 'value' (score/description/type) as at the
+ * corresponding structure position
+ */
+ List<int[]> mappedRanges = structureMapping
+ .getPDBResNumRanges(seqPos, seqPos);
+
+ if (!mappedRanges.isEmpty())
+ {
+ String value = sf.getDescription();
+ if (value == null || value.length() == 0)
+ {
+ value = type;
+ }
+ float score = sf.getScore();
+ if (score != 0f && !Float.isNaN(score))
+ {
+ value = Float.toString(score);
+ }
+ Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+ if (featureValues == null)
+ {
+ featureValues = new HashMap<>();
+ theMap.put(type, featureValues);
+ }
+ for (int[] range : mappedRanges)
+ {
+ addAtomSpecRange(featureValues, value, modelNumber, range[0],
+ range[1], structureMapping.getChain());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Inspect features on the sequence; for each feature that is visible,
+ * determine its mapped ranges in the structure (if any) according to the
+ * given mapping, and add them to the map.
+ *
+ * @param visibleFeatures
+ * @param mapping
+ * @param seq
+ * @param theMap
+ * @param modelId
+ */
+ protected static void scanSequenceFeatures(List<String> visibleFeatures,
+ StructureMapping mapping, SequenceI seq,
+ Map<String, Map<Object, AtomSpecModel>> theMap, String modelId)
+ {
+ List<SequenceFeature> sfs = seq.getFeatures().getPositionalFeatures(
+ visibleFeatures.toArray(new String[visibleFeatures.size()]));
+ for (SequenceFeature sf : sfs)
+ {
+ String type = sf.getType();
+
+ /*
+ * Don't copy features which originated from Chimera
+ */
+ if (JalviewChimeraBinding.CHIMERA_FEATURE_GROUP
+ .equals(sf.getFeatureGroup()))
+ {
+ continue;
+ }
+
+ List<int[]> mappedRanges = mapping.getPDBResNumRanges(sf.getBegin(),
+ sf.getEnd());
+
+ if (!mappedRanges.isEmpty())
+ {
+ String value = sf.getDescription();
+ if (value == null || value.length() == 0)
+ {
+ value = type;
+ }
+ float score = sf.getScore();
+ if (score != 0f && !Float.isNaN(score))
+ {
+ value = Float.toString(score);
+ }
+ Map<Object, AtomSpecModel> featureValues = theMap.get(type);
+ if (featureValues == null)
+ {
+ featureValues = new HashMap<>();
+ theMap.put(type, featureValues);
+ }
+ for (int[] range : mappedRanges)
+ {
+ addAtomSpecRange(featureValues, value, modelId, range[0],
+ range[1], mapping.getChain());
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the number of structure files in the structure viewer and mapped to
+ * Jalview. This may be zero if the files are still in the process of loading
+ * in the viewer.
+ *
+ * @return
+ */
+ public int getMappedStructureCount()
+ {
+ String[] files = getStructureFiles();
+ return files == null ? 0 : files.length;
+ }
}
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
return null;
}
}
+
+ /**
+ * do an HTTP GET with URL-Encoded parameters passed in the Query string
+ *
+ * @param url
+ * @param vals
+ * @return Reader containing content, if any, or null if no entity returned.
+ * @throws IOException
+ * @throws ClientProtocolException
+ * @throws Exception
+ */
+ public static BufferedReader doHttpGet(String url,
+ List<NameValuePair> vals, int connectionTimeoutMs,
+ int readTimeoutMs) throws ClientProtocolException, IOException
+ {
+ // todo use HttpClient 4.3 or later and class RequestConfig
+ HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
+ HttpVersion.HTTP_1_1);
+ if (connectionTimeoutMs > 0)
+ {
+ HttpConnectionParams.setConnectionTimeout(params,
+ connectionTimeoutMs);
+ }
+ if (readTimeoutMs > 0)
+ {
+ HttpConnectionParams.setSoTimeout(params, readTimeoutMs);
+ }
+ boolean first = true;
+ for (NameValuePair param : vals)
+ {
+ if (first)
+ {
+ url += "?";
+ }
+ else
+ {
+ url += "&";
+ }
+ url += param.getName();
+ url += "=";
+ url += param.getValue();
+ }
+ HttpClient httpclient = new DefaultHttpClient(params);
+ HttpGet httpGet = new HttpGet(url);
+ // UrlEncodedFormEntity ue = new UrlEncodedFormEntity(vals, "UTF-8");
+ // httpGet.setEntity(ue);
+ HttpResponse response = httpclient.execute(httpGet);
+ HttpEntity resEntity = response.getEntity();
+
+ if (resEntity != null)
+ {
+ BufferedReader r = new BufferedReader(
+ new InputStreamReader(resEntity.getContent()));
+ return r;
+ }
+ else
+ {
+ return null;
+ }
+ }
}
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.gui.JvOptionPane;
import jalview.gui.SequenceRenderer;
import jalview.schemes.JalviewColourScheme;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsI;
import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
import jalview.structure.StructureSelectionManager;
-import java.util.HashMap;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
public class JmolCommandsTest
{
}
@Test(groups = { "Functional" })
- public void testGetColourBySequenceCommand_noFeatures()
- {
- SequenceI seq1 = new Sequence("seq1", "MHRSQTRALK");
- SequenceI seq2 = new Sequence("seq2", "MRLEITQSGD");
- AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
- AlignFrame af = new AlignFrame(al, 800, 500);
- SequenceRenderer sr = new SequenceRenderer(af.getViewport());
- SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
- String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
- StructureSelectionManager ssm = new StructureSelectionManager();
-
- // need some mappings!
-
- StructureMappingcommandSet[] commands = JmolCommands
- .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
- }
-
- @Test(groups = { "Functional" })
public void testGetColourBySequenceCommands_hiddenColumns()
{
/*
SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
StructureSelectionManager ssm = new StructureSelectionManager();
-
+
/*
* map residues 1-10 to residues 21-30 (atoms 105-150) in structures
*/
- HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
+ HashMap<Integer, int[]> map = new HashMap<>();
for (int pos = 1; pos <= seq1.getLength(); pos++)
{
map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) });
StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
"B", map, null);
ssm.addStructureMapping(sm2);
-
- StructureMappingcommandSet[] commands = JmolCommands
- .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
+
+ String[] commands = new JmolCommands().colourBySequence(ssm, files,
+ seqs, sr, af.alignPanel);
assertEquals(commands.length, 2);
- assertEquals(commands[0].commands.length, 1);
- String chainACommand = commands[0].commands[0];
+ String chainACommand = commands[0];
// M colour is #82827d == (130, 130, 125) (see strand.html help page)
- assertTrue(chainACommand
- .contains("select 21:A/1.1;color[130,130,125]")); // first one
+ assertTrue(
+ chainACommand.contains("select 21:A/1.1;color[130,130,125]")); // first
+ // one
// H colour is #60609f == (96, 96, 159)
assertTrue(chainACommand.contains(";select 22:A/1.1;color[96,96,159]"));
// hidden columns are Gray (128, 128, 128)
assertTrue(chainACommand
.contains(";select 23-25:A/1.1;color[128,128,128]"));
// S and G are both coloured #4949b6 == (73, 73, 182)
- assertTrue(chainACommand
- .contains(";select 26-30:A/1.1;color[73,73,182]"));
+ assertTrue(
+ chainACommand.contains(";select 26-30:A/1.1;color[73,73,182]"));
- String chainBCommand = commands[1].commands[0];
+ String chainBCommand = commands[1];
// M colour is #82827d == (130, 130, 125)
- assertTrue(chainBCommand
- .contains("select 21:B/2.1;color[130,130,125]"));
+ assertTrue(
+ chainBCommand.contains("select 21:B/2.1;color[130,130,125]"));
// V colour is #ffff00 == (255, 255, 0)
- assertTrue(chainBCommand
-.contains(";select 22:B/2.1;color[255,255,0]"));
+ assertTrue(chainBCommand.contains(";select 22:B/2.1;color[255,255,0]"));
// hidden columns are Gray (128, 128, 128)
assertTrue(chainBCommand
.contains(";select 23-25:B/2.1;color[128,128,128]"));
// S and G are both coloured #4949b6 == (73, 73, 182)
- assertTrue(chainBCommand
- .contains(";select 26-30:B/2.1;color[73,73,182]"));
+ assertTrue(
+ chainBCommand.contains(";select 26-30:B/2.1;color[73,73,182]"));
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAtomSpec()
+ {
+ StructureCommandsI testee = new JmolCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, false), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, false), "2-4:A/1.1");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, false), "2-4:A/1.1|8:A/1.1");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-4:A/1.1|8:A/1.1|5-7:B/1.1");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-7:B/1.1");
+ model.addRange("2", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-7:B/1.1|1-4:B/2.1");
+ model.addRange("2", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-7:B/1.1|1-4:B/2.1|5-9:C/2.1");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-10:B/1.1|1-4:B/2.1|5-9:C/2.1");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-10:B/1.1|1-4:B/2.1|5-9:C/2.1");
+ model.addRange("2", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-10:B/1.1|1-4:B/2.1|3-10:C/2.1");
+ model.addRange("5", 25, 35, " ");
+ assertEquals(testee.getAtomSpec(model, false),
+ "2-5:A/1.1|8:A/1.1|5-10:B/1.1|1-4:B/2.1|3-10:C/2.1|25-35:/5.1");
+
+ }
+
+ @Test(groups = { "Functional" })
+ public void testColourBySequence()
+ {
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
+ JmolCommands.addAtomSpecRange(map, Color.blue, "1", 2, 5, "A");
+ JmolCommands.addAtomSpecRange(map, Color.blue, "1", 7, 7, "B");
+ JmolCommands.addAtomSpecRange(map, Color.blue, "1", 9, 23, "A");
+ JmolCommands.addAtomSpecRange(map, Color.blue, "2", 1, 1, "A");
+ JmolCommands.addAtomSpecRange(map, Color.blue, "2", 4, 7, "B");
+ JmolCommands.addAtomSpecRange(map, Color.yellow, "2", 8, 8, "A");
+ JmolCommands.addAtomSpecRange(map, Color.yellow, "2", 3, 5, "A");
+ JmolCommands.addAtomSpecRange(map, Color.red, "1", 3, 5, "A");
+ JmolCommands.addAtomSpecRange(map, Color.red, "1", 6, 9, "A");
+
+ // Colours should appear in the Jmol command in the order in which
+ // they were added; within colour, by model, by chain, ranges in start order
+ List<StructureCommandI> commands = new JmolCommands()
+ .colourBySequence(map);
+ assertEquals(commands.size(), 1);
+ String expected1 = "select 2-5:A/1.1|9-23:A/1.1|7:B/1.1|1:A/2.1|4-7:B/2.1;color[0,0,255]";
+ String expected2 = "select 3-5:A/2.1|8:A/2.1;color[255,255,0]";
+ String expected3 = "select 3-9:A/1.1;color[255,0,0]";
+ assertEquals(commands.get(0).getCommand(),
+ expected1 + ";" + expected2 + ";" + expected3);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testSuperposeStructures()
+ {
+ StructureCommandsI testee = new JmolCommands();
+ AtomSpecModel ref = new AtomSpecModel();
+ ref.addRange("1", 12, 14, "A");
+ ref.addRange("1", 18, 18, "B");
+ ref.addRange("1", 22, 23, "B");
+ AtomSpecModel toAlign = new AtomSpecModel();
+ toAlign.addRange("2", 15, 17, "B");
+ toAlign.addRange("2", 20, 21, "B");
+ toAlign.addRange("2", 22, 22, "C");
+ List<StructureCommandI> command = testee.superposeStructures(ref,
+ toAlign);
+ assertEquals(command.size(), 1);
+ String refSpec = "12-14:A/1.1|18:B/1.1|22-23:B/1.1";
+ String toAlignSpec = "15-17:B/2.1|20-21:B/2.1|22:C/2.1";
+ String expected = String.format(
+ "compare {2.1} {1.1} SUBSET {(*.CA | *.P) and conformation=1} ATOMS {%s}{%s} ROTATE TRANSLATE ;select %s|%s;cartoons",
+ toAlignSpec, refSpec, toAlignSpec, refSpec);
+ assertEquals(command.get(0).getCommand(), expected);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetModelStartNo()
+ {
+ StructureCommandsI testee = new JmolCommands();
+ assertEquals(testee.getModelStartNo(), 1);
}
}
--- /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.ext.pymol;
+
+import static org.testng.Assert.assertEquals;
+
+import java.awt.Color;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.Test;
+
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsI;
+
+public class PymolCommandsTest
+{
+
+ @Test(groups = { "Functional" })
+ public void testColourBySequence()
+ {
+
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
+ PymolCommands.addAtomSpecRange(map, Color.blue, "0", 2, 5, "A");
+ PymolCommands.addAtomSpecRange(map, Color.blue, "0", 7, 7, "B");
+ PymolCommands.addAtomSpecRange(map, Color.blue, "0", 9, 23, "A");
+ PymolCommands.addAtomSpecRange(map, Color.blue, "1", 1, 1, "A");
+ PymolCommands.addAtomSpecRange(map, Color.blue, "1", 4, 7, "B");
+ PymolCommands.addAtomSpecRange(map, Color.yellow, "1", 8, 8, "A");
+ PymolCommands.addAtomSpecRange(map, Color.yellow, "1", 3, 5, "A");
+ PymolCommands.addAtomSpecRange(map, Color.red, "0", 3, 5, "A");
+ PymolCommands.addAtomSpecRange(map, Color.red, "0", 6, 9, "A");
+
+ // Colours should appear in the Pymol command in the order in which
+ // they were added; within colour, by model, by chain, ranges in start order
+ List<StructureCommandI> commands = new PymolCommands()
+ .colourBySequence(map);
+ assertEquals(commands.size(), 3);
+ assertEquals(commands.get(0).toString(),
+ "color(0x0000ff,0//A/2-5+9-23/ 0//B/7/ 1//A/1/ 1//B/4-7/)");
+ assertEquals(commands.get(1).toString(), "color(0xffff00,1//A/3-5+8/)");
+ assertEquals(
+ commands.get(2).toString(), "color(0xff0000,0//A/3-9/)");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAtomSpec()
+ {
+ StructureCommandsI testee = new PymolCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, false), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, false), "1//A/2-4/");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, false), "1//A/2-4+8/");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, false), "1//A/2-4+8/ 1//B/5-7/");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, false), "1//A/2-5+8/ 1//B/5-7/");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 1//A/2-5+8/ 1//B/5-7/");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 0//C/5-9/ 1//A/2-5+8/ 1//B/5-7/");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 0//C/5-9/ 1//A/2-5+8/ 1//B/5-10/");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 0//C/5-9/ 1//A/2-5+8/ 1//B/5-10/");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 0//C/3-10/ 1//A/2-5+8/ 1//B/5-10/");
+ model.addRange("5", 25, 35, " ");
+ assertEquals(testee.getAtomSpec(model, false),
+ "0//B/1-4/ 0//C/3-10/ 1//A/2-5+8/ 1//B/5-10/ 5///25-35/");
+
+ }
+
+ @Test(groups = { "Functional" })
+ public void testSuperposeStructures()
+ {
+ StructureCommandsI testee = new PymolCommands();
+ AtomSpecModel ref = new AtomSpecModel();
+ ref.addRange("1", 12, 14, "A");
+ ref.addRange("1", 18, 18, "B");
+ ref.addRange("1", 22, 23, "B");
+ AtomSpecModel toAlign = new AtomSpecModel();
+ toAlign.addRange("2", 15, 17, "B");
+ toAlign.addRange("2", 20, 21, "B");
+ toAlign.addRange("2", 22, 22, "C");
+ List<StructureCommandI> commands = testee.superposeStructures(ref,
+ toAlign);
+ assertEquals(commands.size(), 2);
+ String refSpecCA = "1//A/12-14/CA 1//B/18+22-23/CA";
+ String toAlignSpecCA = "2//B/15-17+20-21/CA 2//C/22/CA";
+ String refSpec = "1//A/12-14/ 1//B/18+22-23/";
+ String toAlignSpec = "2//B/15-17+20-21/ 2//C/22/";
+ String expected1 = String.format("super(%s,%s)", refSpecCA,
+ toAlignSpecCA);
+ String expected2 = String.format("show(cartoon,%s %s)", refSpec,
+ toAlignSpec);
+ assertEquals(commands.get(0).toString(), expected1);
+ assertEquals(commands.get(1).toString(), expected2);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAtomSpec_alphaOnly()
+ {
+ StructureCommandsI testee = new PymolCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, true), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, true), "1//A/2-4/CA");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, true), "1//A/2-4+8/CA");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "1//A/2-4+8/CA 1//B/5-7/CA");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, true),
+ "1//A/2-5+8/CA 1//B/5-7/CA");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 1//A/2-5+8/CA 1//B/5-7/CA");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 0//C/5-9/CA 1//A/2-5+8/CA 1//B/5-7/CA");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 0//C/5-9/CA 1//A/2-5+8/CA 1//B/5-10/CA");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 0//C/5-9/CA 1//A/2-5+8/CA 1//B/5-10/CA");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 0//C/3-10/CA 1//A/2-5+8/CA 1//B/5-10/CA");
+ model.addRange("5", 25, 35, " ");
+ assertEquals(testee.getAtomSpec(model, true),
+ "0//B/1-4/CA 0//C/3-10/CA 1//A/2-5+8/CA 1//B/5-10/CA 5///25-35/CA");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetModelStartNo()
+ {
+ StructureCommandsI testee = new PymolCommands();
+ assertEquals(testee.getModelStartNo(), 0);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetResidueSpec()
+ {
+ PymolCommands testee = new PymolCommands();
+ assertEquals(testee.getResidueSpec("ALA"), "resn ALA");
+ }
+
+ @Test(groups = "Functional")
+ public void testShowBackbone()
+ {
+ PymolCommands testee = new PymolCommands();
+ List<StructureCommandI> cmds = testee.showBackbone();
+ assertEquals(cmds.size(), 2);
+ assertEquals(cmds.get(0).toString(), "hide(everything)");
+ assertEquals(cmds.get(1).toString(), "show(ribbon)");
+ }
+
+ @Test(groups = "Functional")
+ public void testColourByCharge()
+ {
+ PymolCommands testee = new PymolCommands();
+ List<StructureCommandI> cmds = testee.colourByCharge();
+ assertEquals(cmds.size(), 4);
+ assertEquals(cmds.get(0).toString(), "color(white,*)");
+ assertEquals(cmds.get(1).toString(), "color(red,resn ASP resn GLU)");
+ assertEquals(cmds.get(2).toString(), "color(blue,resn LYS resn ARG)");
+ assertEquals(cmds.get(3).toString(), "color(yellow,resn CYS)");
+ }
+
+ @Test(groups = "Functional")
+ public void testOpenCommandFile()
+ {
+ PymolCommands testee = new PymolCommands();
+ assertEquals(testee.openCommandFile("commands.pml").toString(),
+ "run(commands.pml)");
+ }
+
+ @Test(groups = "Functional")
+ public void testSaveSession()
+ {
+ PymolCommands testee = new PymolCommands();
+ assertEquals(testee.saveSession("somewhere.pse").toString(),
+ "save(somewhere.pse)");
+ }
+
+ @Test(groups = "Functional")
+ public void testColourByChain()
+ {
+ PymolCommands testee = new PymolCommands();
+ assertEquals(testee.colourByChain().toString(), "spectrum(chain)");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetColourCommand()
+ {
+ PymolCommands testee = new PymolCommands();
+ assertEquals(
+ testee.colourResidues("something", Color.MAGENTA).toString(),
+ "color(0xff00ff,something)");
+ }
+}
--- /dev/null
+package jalview.ext.pymol;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+import jalview.structure.StructureCommand;
+
+public class PymolManagerTest
+{
+ @Test(groups = "Functional")
+ public void testGetPostRequest()
+ {
+ String req = PymolManager
+ .getPostRequest(new StructureCommand("foobar"));
+ assertEquals(req,
+ "<methodCall><methodName>foobar</methodName><params></params></methodCall>");
+
+ req = PymolManager
+ .getPostRequest(new StructureCommand("foobar", "blue", "all"));
+ assertEquals(req, "<methodCall><methodName>foobar</methodName><params>"
+ + "<parameter><value>blue</value></parameter>"
+ + "<parameter><value>all</value></parameter>"
+ + "</params></methodCall>");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetPymolPaths()
+ {
+ /*
+ * OSX
+ */
+ List<String> paths = PymolManager.getPymolPaths("Mac OS X");
+ assertEquals(paths.size(), 1);
+ assertTrue(
+ paths.contains("/Applications/PyMOL.app/Contents/MacOS/PyMOL"));
+
+ /*
+ * Linux
+ */
+ paths = PymolManager.getPymolPaths("Linux i386 1.5.0");
+ assertTrue(paths.contains("/usr/local/pymol/bin/PyMOL"));
+ assertTrue(paths.contains("/usr/local/bin/PyMOL"));
+ assertTrue(paths.contains("/usr/bin/PyMOL"));
+ assertTrue(paths.contains("/usr/local/pymol/bin/PyMOL"));
+ assertTrue(paths
+ .contains(System.getProperty("user.home") + "/opt/bin/PyMOL"));
+
+ /*
+ * Windows
+ */
+ paths = PymolManager.getPymolPaths("Windows 10");
+ assertTrue(paths.isEmpty()); // TODO - Windows paths
+
+ /*
+ * Other
+ */
+ paths = PymolManager.getPymolPaths("VAX/VMS");
+ assertTrue(paths.isEmpty());
+ }
+}
+++ /dev/null
-package jalview.ext.rbvi.chimera;
-
-import static org.testng.Assert.assertEquals;
-
-import org.testng.annotations.Test;
-
-public class AtomSpecModelTest
-{
- @Test(groups = "Functional")
- public void testGetAtomSpec()
- {
- AtomSpecModel model = new AtomSpecModel();
- assertEquals(model.getAtomSpec(), "");
- model.addRange(1, 2, 4, "A");
- assertEquals(model.getAtomSpec(), "#1:2-4.A");
- model.addRange(1, 8, 8, "A");
- assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A");
- model.addRange(1, 5, 7, "B");
- assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A,5-7.B");
- model.addRange(1, 3, 5, "A");
- assertEquals(model.getAtomSpec(), "#1:2-5.A,8.A,5-7.B");
- model.addRange(0, 1, 4, "B");
- assertEquals(model.getAtomSpec(), "#0:1-4.B|#1:2-5.A,8.A,5-7.B");
- model.addRange(0, 5, 9, "C");
- assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B");
- model.addRange(1, 8, 10, "B");
- assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
- model.addRange(1, 8, 9, "B");
- assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
- model.addRange(0, 3, 10, "C"); // subsumes 5-9
- assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B");
- model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology
- // modelling
- assertEquals(model.getAtomSpec(),
- "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35.");
-
- }
-
-}
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
-import jalview.datamodel.Alignment;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.ColumnSelection;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.JvOptionPane;
-import jalview.gui.SequenceRenderer;
-import jalview.schemes.JalviewColourScheme;
-import jalview.structure.StructureMapping;
-import jalview.structure.StructureMappingcommandSet;
-import jalview.structure.StructureSelectionManager;
-
import java.awt.Color;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsI;
+
public class ChimeraCommandsTest
{
- @BeforeClass(alwaysRun = true)
- public void setUpJvOptionPane()
- {
- JvOptionPane.setInteractiveMode(false);
- JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
- }
-
@Test(groups = { "Functional" })
- public void testBuildColourCommands()
+ public void testColourBySequence()
{
- Map<Object, AtomSpecModel> map = new LinkedHashMap<Object, AtomSpecModel>();
- ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 2, 5, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 7, 7, "B");
- ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 9, 23, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 1, 1, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 4, 7, "B");
- ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 8, 8, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 3, 5, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 3, 5, "A");
- ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 6, 9, "A");
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "0", 2, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "0", 7, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "0", 9, 23, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "1", 1, 1, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "1", 4, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, "1", 8, 8, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, "1", 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, "0", 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, "0", 6, 9, "A");
// Colours should appear in the Chimera command in the order in which
// they were added; within colour, by model, by chain, ranges in start order
- String command = ChimeraCommands.buildColourCommands(map).get(0);
- assertEquals(
- command,
- "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:3-5.A,8.A; color #ff0000 #0:3-9.A");
+ List<StructureCommandI> commands = new ChimeraCommands()
+ .colourBySequence(map);
+ assertEquals(commands.size(), 1);
+ assertEquals(commands.get(0).getCommand(),
+ "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B;color #ffff00 #1:3-5.A,8.A;color #ff0000 #0:3-9.A");
}
@Test(groups = { "Functional" })
- public void testBuildSetAttributeCommands()
+ public void testSetAttributes()
{
/*
* make a map of { featureType, {featureValue, {residue range specification } } }
*/
- Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<String, Map<Object, AtomSpecModel>>();
- Map<Object, AtomSpecModel> featureValues = new HashMap<Object, AtomSpecModel>();
+ Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<>();
+ Map<Object, AtomSpecModel> featureValues = new HashMap<>();
/*
* start with just one feature/value...
*/
featuresMap.put("chain", featureValues);
- ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 8, 20, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 8, 20, "A");
- List<String> commands = ChimeraCommands
- .buildSetAttributeCommands(featuresMap);
+ ChimeraCommands commandGenerator = new ChimeraCommands();
+ List<StructureCommandI> commands = commandGenerator
+ .setAttributes(featuresMap);
assertEquals(1, commands.size());
/*
* feature name gets a jv_ namespace prefix
* feature value is quoted in case it contains spaces
*/
- assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
+ assertEquals(commands.get(0).getCommand(),
+ "setattr res jv_chain 'X' #0:8-20.A");
// add same feature value, overlapping range
- ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 3, 9, "A");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 3, 9, "A");
// same feature value, contiguous range
- ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "A");
- commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 21, 25, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
assertEquals(1, commands.size());
- assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
+ assertEquals(commands.get(0).getCommand(),
+ "setattr res jv_chain 'X' #0:3-25.A");
// same feature value and model, different chain
- ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "B");
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 21, 25, "B");
// same feature value and chain, different model
- ChimeraCommands.addAtomSpecRange(featureValues, "X", 1, 26, 30, "A");
- commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "1", 26, 30, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
assertEquals(1, commands.size());
- assertEquals(commands.get(0),
- "setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
+ String expected1 = "setattr res jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A";
+ assertEquals(commands.get(0).getCommand(), expected1);
// same feature, different value
- ChimeraCommands.addAtomSpecRange(featureValues, "Y", 0, 40, 50, "A");
- commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
+ ChimeraCommands.addAtomSpecRange(featureValues, "Y", "0", 40, 50, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
assertEquals(2, commands.size());
// commands are ordered by feature type but not by value
- // so use contains to test for the expected command:
- assertTrue(commands
- .contains("setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A"));
- assertTrue(commands.contains("setattr r jv_chain 'Y' #0:40-50.A"));
+ // so test for the expected command in either order
+ String cmd1 = commands.get(0).getCommand();
+ String cmd2 = commands.get(1).getCommand();
+ assertTrue(cmd1.equals(expected1) || cmd2.equals(expected1));
+ String expected2 = "setattr res jv_chain 'Y' #0:40-50.A";
+ assertTrue(cmd1.equals(expected2) || cmd2.equals(expected2));
featuresMap.clear();
featureValues.clear();
featuresMap.put("side-chain binding!", featureValues);
ChimeraCommands.addAtomSpecRange(featureValues,
- "<html>metal <a href=\"http:a.b.c/x\"> 'ion!", 0, 7, 15,
+ "<html>metal <a href=\"http:a.b.c/x\"> 'ion!", "0", 7, 15,
"A");
// feature names are sanitised to change non-alphanumeric to underscore
// feature values are sanitised to encode single quote characters
- commands = ChimeraCommands.buildSetAttributeCommands(featuresMap);
- assertTrue(commands
- .contains("setattr r jv_side_chain_binding_ '<html>metal <a href=\"http:a.b.c/x\"> 'ion!' #0:7-15.A"));
+ commands = commandGenerator.setAttributes(featuresMap);
+ assertEquals(commands.size(), 1);
+ String expected3 = "setattr res jv_side_chain_binding_ '<html>metal <a href=\"http:a.b.c/x\"> 'ion!' #0:7-15.A";
+ assertTrue(commands.get(0).getCommand().equals(expected3));
}
/**
* Tests for the method that prefixes and sanitises a feature name so it can
- * be used as a valid, namespaced attribute name in Chimera
+ * be used as a valid, namespaced attribute name in Chimera or PyMol
*/
@Test(groups = { "Functional" })
public void testMakeAttributeName()
{
- assertEquals(ChimeraCommands.makeAttributeName(null), "jv_");
- assertEquals(ChimeraCommands.makeAttributeName(""), "jv_");
- assertEquals(ChimeraCommands.makeAttributeName("helix"), "jv_helix");
- assertEquals(ChimeraCommands.makeAttributeName("Hello World 24"),
+ ChimeraCommands testee = new ChimeraCommands();
+ assertEquals(testee.makeAttributeName(null), "jv_");
+ assertEquals(testee.makeAttributeName(""), "jv_");
+ assertEquals(testee.makeAttributeName("helix"), "jv_helix");
+ assertEquals(testee.makeAttributeName(
+ "Hello World 24"),
"jv_Hello_World_24");
assertEquals(
- ChimeraCommands.makeAttributeName("!this is-a_very*{odd(name"),
+ testee.makeAttributeName(
+ "!this is-a_very*{odd(name"),
"jv__this_is_a_very__odd_name");
// name ending in color gets underscore appended
- assertEquals(ChimeraCommands.makeAttributeName("helixColor"),
+ assertEquals(testee.makeAttributeName("helixColor"),
"jv_helixColor_");
}
+ @Test(groups = "Functional")
+ public void testGetAtomSpec()
+ {
+ StructureCommandsI testee = new ChimeraCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, false), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1:2-4.A");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1:2-4.A,8.A");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, false), "#1:2-4.A,8.A,5-7.B");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1:2-5.A,8.A,5-7.B");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B|#1:2-5.A,8.A,5-7.B");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B");
+ model.addRange("5", 25, 35, " ");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35.");
+
+ }
+
@Test(groups = { "Functional" })
- public void testGetColourBySequenceCommands_hiddenColumns()
+ public void testSuperposeStructures()
{
- /*
- * load these sequences, coloured by Strand propensity,
- * with columns 2-4 hidden
- */
- SequenceI seq1 = new Sequence("seq1", "MHRSQSSSGG");
- SequenceI seq2 = new Sequence("seq2", "MVRSNGGSSS");
- AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
- AlignFrame af = new AlignFrame(al, 800, 500);
- af.changeColour_actionPerformed(JalviewColourScheme.Strand.toString());
- ColumnSelection cs = new ColumnSelection();
- cs.addElement(2);
- cs.addElement(3);
- cs.addElement(4);
- af.getViewport().setColumnSelection(cs);
- af.hideSelColumns_actionPerformed(null);
- SequenceRenderer sr = new SequenceRenderer(af.getViewport());
- SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
- String[] files = new String[] { "seq1.pdb", "seq2.pdb" };
- StructureSelectionManager ssm = new StructureSelectionManager();
+ StructureCommandsI testee = new ChimeraCommands();
+ AtomSpecModel ref = new AtomSpecModel();
+ ref.addRange("1", 12, 14, "A");
+ ref.addRange("1", 18, 18, "B");
+ ref.addRange("1", 22, 23, "B");
+ AtomSpecModel toAlign = new AtomSpecModel();
+ toAlign.addRange("2", 15, 17, "B");
+ toAlign.addRange("2", 20, 21, "B");
+ toAlign.addRange("2", 22, 22, "C");
+ List<StructureCommandI> command = testee.superposeStructures(ref,
+ toAlign);
+ // qualifier to restrict match to CA and no altlocs
+ String carbonAlphas = "@CA&~@.B-Z&~@.2-9";
+ String refSpec = "#1:12-14.A,18.B,22-23.B";
+ String toAlignSpec = "#2:15-17.B,20-21.B,22.C";
+ String expected = String.format(
+ "match %s%s %s%s; ribbon %s|%s; focus", toAlignSpec,
+ carbonAlphas, refSpec, carbonAlphas, toAlignSpec, refSpec);
+ assertEquals(command.get(0).getCommand(), expected);
+ }
- /*
- * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
- */
- HashMap<Integer, int[]> map = new HashMap<Integer, int[]>();
- for (int pos = 1; pos <= seq1.getLength(); pos++)
- {
- map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) });
- }
- StructureMapping sm1 = new StructureMapping(seq1, "seq1.pdb", "pdb1",
- "A", map, null);
- ssm.addStructureMapping(sm1);
- StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
- "B", map, null);
- ssm.addStructureMapping(sm2);
-
- StructureMappingcommandSet[] commands = ChimeraCommands
- .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel);
- assertEquals(1, commands.length);
- assertEquals(1, commands[0].commands.length);
- String theCommand = commands[0].commands[0];
- // M colour is #82827d (see strand.html help page)
- assertTrue(theCommand.contains("color #82827d #0:21.A|#1:21.B"));
- // H colour is #60609f
- assertTrue(theCommand.contains("color #60609f #0:22.A"));
- // V colour is #ffff00
- assertTrue(theCommand.contains("color #ffff00 #1:22.B"));
- // hidden columns are Gray (128, 128, 128)
- assertTrue(theCommand.contains("color #808080 #0:23-25.A|#1:23-25.B"));
- // S and G are both coloured #4949b6
- assertTrue(theCommand.contains("color #4949b6 #0:26-30.A|#1:26-30.B"));
+ @Test(groups = "Functional")
+ public void testGetAtomSpec_alphaOnly()
+ {
+ StructureCommandsI testee = new ChimeraCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, true), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#1:2-4.A@CA&~@.B-Z&~@.2-9");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#1:2-4.A,8.A@CA&~@.B-Z&~@.2-9");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#1:2-4.A,8.A,5-7.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#1:2-5.A,8.A,5-7.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-7.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B,5-9.C@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-7.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B,5-9.C@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-10.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B,5-9.C@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-10.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B,3-10.C@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-10.B@CA&~@.B-Z&~@.2-9");
+ model.addRange("5", 25, 35, " "); // empty chain code
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0:1-4.B,3-10.C@CA&~@.B-Z&~@.2-9|#1:2-5.A,8.A,5-10.B@CA&~@.B-Z&~@.2-9|#5:25-35.@CA&~@.B-Z&~@.2-9");
+
+ }
+
+ @Test(groups = "Functional")
+ public void testGetModelStartNo()
+ {
+ StructureCommandsI testee = new ChimeraCommands();
+ assertEquals(testee.getModelStartNo(), 0);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetResidueSpec()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ assertEquals(testee.getResidueSpec("ALA"), "::ALA");
+ }
+
+ @Test(groups = "Functional")
+ public void testShowBackbone()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ List<StructureCommandI> cmds = testee.showBackbone();
+ assertEquals(cmds.size(), 1);
+ assertEquals(cmds.get(0).getCommand(),
+ "~display all;~ribbon;chain @CA|P");
+ }
+
+ @Test(groups = "Functional")
+ public void testOpenCommandFile()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ assertEquals(testee.openCommandFile("nowhere").getCommand(),
+ "open cmd:nowhere");
+ }
+
+ @Test(groups = "Functional")
+ public void testSaveSession()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ assertEquals(testee.saveSession("somewhere").getCommand(),
+ "save somewhere");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetColourCommand()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ assertEquals(testee.colourResidues("something", Color.MAGENTA)
+ .getCommand(),
+ "color #ff00ff something");
+ }
+
+ @Test(groups = "Functional")
+ public void testSetAttribute()
+ {
+ ChimeraCommands testee = new ChimeraCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ model.addRange("1", 89, 92, "A");
+ model.addRange("2", 12, 20, "B");
+ model.addRange("2", 8, 9, "B");
+ assertEquals(testee.setAttribute("phi", "27.3", model).getCommand(),
+ "setattr res phi '27.3' #1:89-92.A|#2:8-9.B,12-20.B");
}
}
final StructureManager structureManager = new StructureManager(true);
ChimeraManager cm = new ChimeraManager(structureManager);
assertTrue("Couldn't launch chimera",
- cm.launchChimera(StructureManager.getChimeraPaths()));
+ cm.launchChimera(StructureManager.getChimeraPaths(false)));
assertTrue(cm.isChimeraLaunched()); // Chimera process is alive
// int n=0;
// not sure of the point of this is unless the tester is loading models
--- /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.ext.rbvi.chimera;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.Test;
+
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureCommandsI;
+
+public class ChimeraXCommandsTest
+{
+ @Test(groups = { "Functional" })
+ public void testColourByCharge()
+ {
+ List<StructureCommandI> cmd = new ChimeraXCommands().colourByCharge();
+ assertEquals(cmd.size(), 1);
+ assertEquals(cmd.get(0).getCommand(),
+ "color white;color :ASP,GLU red;color :LYS,ARG blue;color :CYS yellow");
+ }
+
+ @Test(groups = { "Functional" })
+ public void testColourByChain()
+ {
+ StructureCommandI cmd = new ChimeraXCommands().colourByChain();
+ assertEquals(cmd.getCommand(), "rainbow chain");
+ }
+
+ @Test(groups = { "Functional" })
+ public void testFocusView()
+ {
+ StructureCommandI cmd = new ChimeraXCommands().focusView();
+ assertEquals(cmd.getCommand(), "view");
+ }
+
+ @Test(groups = { "Functional" })
+ public void testColourBySequence()
+ {
+ Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "1", 2, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "1", 7, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "1", 9, 23, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "2", 1, 1, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.blue, "2", 4, 7, "B");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, "2", 8, 8, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.yellow, "2", 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, "1", 3, 5, "A");
+ ChimeraCommands.addAtomSpecRange(map, Color.red, "1", 6, 9, "A");
+
+ /*
+ * Colours should appear in the Chimera command in the order in which
+ * they were added; within colour, by model, by chain, ranges in start order
+ */
+ List<StructureCommandI> commands = new ChimeraXCommands()
+ .colourBySequence(map);
+ assertEquals(commands.size(), 1);
+ assertEquals(commands.get(0).getCommand(),
+ "color #1/A:2-5,9-23/B:7|#2/A:1/B:4-7 #0000ff;color #2/A:3-5,8 #ffff00;color #1/A:3-9 #ff0000");
+ }
+
+ @Test(groups = { "Functional" })
+ public void testSetAttributes()
+ {
+ /*
+ * make a map of { featureType, {featureValue, {residue range specification } } }
+ */
+ Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<>();
+ Map<Object, AtomSpecModel> featureValues = new HashMap<>();
+
+ /*
+ * start with just one feature/value...
+ */
+ featuresMap.put("chain", featureValues);
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 8, 20, "A");
+
+ ChimeraXCommands commandGenerator = new ChimeraXCommands();
+ List<StructureCommandI> commands = commandGenerator
+ .setAttributes(featuresMap);
+ assertEquals(commands.size(), 1);
+
+ /*
+ * feature name gets a jv_ namespace prefix
+ * feature value is quoted in case it contains spaces
+ */
+ assertEquals(commands.get(0).getCommand(),
+ "setattr #0/A:8-20 res jv_chain 'X' create true");
+
+ // add same feature value, overlapping range
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 3, 9, "A");
+ // same feature value, contiguous range
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 21, 25, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
+ assertEquals(commands.size(), 1);
+ assertEquals(commands.get(0).getCommand(),
+ "setattr #0/A:3-25 res jv_chain 'X' create true");
+
+ // same feature value and model, different chain
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "0", 21, 25, "B");
+ // same feature value and chain, different model
+ ChimeraCommands.addAtomSpecRange(featureValues, "X", "1", 26, 30, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
+ assertEquals(commands.size(), 1);
+ String expected1 = "setattr #0/A:3-25/B:21-25|#1/A:26-30 res jv_chain 'X' create true";
+ assertEquals(commands.get(0).getCommand(), expected1);
+
+ // same feature, different value
+ ChimeraCommands.addAtomSpecRange(featureValues, "Y", "0", 40, 50, "A");
+ commands = commandGenerator.setAttributes(featuresMap);
+ assertEquals(2, commands.size());
+ // commands are ordered by feature type but not by value
+ // so test for the expected command in either order
+ String cmd1 = commands.get(0).getCommand();
+ String cmd2 = commands.get(1).getCommand();
+ assertTrue(
+ cmd1.equals(expected1) || cmd2.equals(expected1));
+ String expected2 = "setattr #0/A:40-50 res jv_chain 'Y' create true";
+ assertTrue(
+ cmd1.equals(expected2) || cmd2.equals(expected2));
+
+ featuresMap.clear();
+ featureValues.clear();
+ featuresMap.put("side-chain binding!", featureValues);
+ ChimeraCommands.addAtomSpecRange(featureValues,
+ "<html>metal <a href=\"http:a.b.c/x\"> 'ion!", "0", 7, 15,
+ "A");
+ // feature names are sanitised to change non-alphanumeric to underscore
+ // feature values are sanitised to encode single quote characters
+ commands = commandGenerator.setAttributes(featuresMap);
+ assertEquals(commands.size(), 1);
+ String expected3 = "setattr #0/A:7-15 res jv_side_chain_binding_ '<html>metal <a href=\"http:a.b.c/x\"> 'ion!' create true";
+ assertTrue(commands.get(0).getCommand().equals(expected3));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testSuperposeStructures()
+ {
+ StructureCommandsI testee = new ChimeraXCommands();
+ AtomSpecModel ref = new AtomSpecModel();
+ ref.addRange("1", 12, 14, "A");
+ ref.addRange("1", 18, 18, "B");
+ ref.addRange("1", 22, 23, "B");
+ AtomSpecModel toAlign = new AtomSpecModel();
+ toAlign.addRange("2", 15, 17, "B");
+ toAlign.addRange("2", 20, 21, "B");
+ toAlign.addRange("2", 22, 22, "C");
+ List<StructureCommandI> command = testee.superposeStructures(ref,
+ toAlign);
+ assertEquals(command.size(), 1);
+ String cmd = command.get(0).getCommand();
+ String refSpec = "#1/A:12-14/B:18,22-23";
+ String toAlignSpec = "#2/B:15-17,20-21/C:22";
+
+ /*
+ * superposition arguments include AlphaCarbon restriction,
+ * ribbon command does not
+ */
+ String expected = String.format(
+ "align %s@CA toAtoms %s@CA; ribbon %s|%s; view",
+ toAlignSpec, refSpec, toAlignSpec, refSpec);
+ assertEquals(cmd, expected);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAtomSpec()
+ {
+ StructureCommandsI testee = new ChimeraXCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, false), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1/A:2-4");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1/A:2-4,8");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, false), "#1/A:2-4,8/B:5-7");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, false), "#1/A:2-5,8/B:5-7");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4|#1/A:2-5,8/B:5-7");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4/C:5-9|#1/A:2-5,8/B:5-7");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4/C:5-9|#1/A:2-5,8/B:5-10");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4/C:5-9|#1/A:2-5,8/B:5-10");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4/C:3-10|#1/A:2-5,8/B:5-10");
+ model.addRange("5", 25, 35, " ");
+ assertEquals(testee.getAtomSpec(model, false),
+ "#0/B:1-4/C:3-10|#1/A:2-5,8/B:5-10|#5/:25-35");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAtomSpec_alphaOnly()
+ {
+ StructureCommandsI testee = new ChimeraXCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ assertEquals(testee.getAtomSpec(model, true), "");
+ model.addRange("1", 2, 4, "A");
+ assertEquals(testee.getAtomSpec(model, true), "#1/A:2-4@CA");
+ model.addRange("1", 8, 8, "A");
+ assertEquals(testee.getAtomSpec(model, true), "#1/A:2-4,8@CA");
+ model.addRange("1", 5, 7, "B");
+ assertEquals(testee.getAtomSpec(model, true), "#1/A:2-4,8/B:5-7@CA");
+ model.addRange("1", 3, 5, "A");
+ assertEquals(testee.getAtomSpec(model, true), "#1/A:2-5,8/B:5-7@CA");
+ model.addRange("0", 1, 4, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4@CA|#1/A:2-5,8/B:5-7@CA");
+ model.addRange("0", 5, 9, "C");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4/C:5-9@CA|#1/A:2-5,8/B:5-7@CA");
+ model.addRange("1", 8, 10, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4/C:5-9@CA|#1/A:2-5,8/B:5-10@CA");
+ model.addRange("1", 8, 9, "B");
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4/C:5-9@CA|#1/A:2-5,8/B:5-10@CA");
+ model.addRange("0", 3, 10, "C"); // subsumes 5-9
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4/C:3-10@CA|#1/A:2-5,8/B:5-10@CA");
+ model.addRange("5", 25, 35, " "); // empty chain code
+ assertEquals(testee.getAtomSpec(model, true),
+ "#0/B:1-4/C:3-10@CA|#1/A:2-5,8/B:5-10@CA|#5/:25-35@CA");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetModelStartNo()
+ {
+ StructureCommandsI testee = new ChimeraXCommands();
+ assertEquals(testee.getModelStartNo(), 1);
+ }
+
+ @Test(groups = "Functional")
+ public void testGetResidueSpec()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ assertEquals(testee.getResidueSpec("ALA"), ":ALA");
+ }
+
+ @Test(groups = "Functional")
+ public void testShowBackbone()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ List<StructureCommandI> showBackbone = testee.showBackbone();
+ assertEquals(showBackbone.size(), 1);
+ assertEquals(showBackbone.get(0).getCommand(),
+ "~display all;~ribbon;show @CA|P atoms");
+ }
+
+ @Test(groups = "Functional")
+ public void testOpenCommandFile()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ assertEquals(testee.openCommandFile("nowhere").getCommand(),
+ "open nowhere");
+ }
+
+ @Test(groups = "Functional")
+ public void testSaveSession()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ assertEquals(testee.saveSession("somewhere").getCommand(),
+ "save session somewhere");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetColourCommand()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ assertEquals(testee.colourResidues("something", Color.MAGENTA)
+ .getCommand(),
+ "color something #ff00ff");
+ }
+
+ @Test(groups = "Functional")
+ public void testSetAttribute()
+ {
+ ChimeraCommands testee = new ChimeraXCommands();
+ AtomSpecModel model = new AtomSpecModel();
+ model.addRange("1", 89, 92, "A");
+ model.addRange("2", 12, 20, "B");
+ model.addRange("2", 8, 9, "B");
+ assertEquals(testee.setAttribute("phi", "27.3", model).getCommand(),
+ "setattr #1/A:89-92|#2/B:8-9,12-20 res phi '27.3' create true");
+ }
+}
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Vector;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
import jalview.api.FeatureRenderer;
import jalview.api.structures.JalviewStructureDisplayI;
import jalview.bin.Cache;
import jalview.gui.StructureViewer.ViewerType;
import jalview.io.DataSourceType;
import jalview.io.FileLoader;
+import jalview.structure.StructureCommand;
import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
import jalview.ws.sifts.SiftsClient;
import jalview.ws.sifts.SiftsException;
import jalview.ws.sifts.SiftsSettings;
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Vector;
-
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
@Test(singleThreaded = true)
public class JalviewChimeraView
{
}
}
- assertTrue(binding.isChimeraRunning(), "Failed to start Chimera");
+ assertTrue(binding.isViewerRunning(), "Failed to start Chimera");
assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
chimeraViewer.closeViewer(true);
}
} while (!binding.isFinishedInit());
- assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
+ assertTrue(binding.isViewerRunning(), "Failed to launch Chimera");
assertEquals(binding.getPdbCount(), 1);
/*
* ask Chimera for its residue attribute names
*/
- List<String> reply = binding.sendChimeraCommand("list resattr", true);
+ List<String> reply = binding
+ .executeCommand(new StructureCommand("list resattr"), true);
// prefixed and sanitised attribute names for Jalview features:
assertTrue(reply.contains("resattr jv_domain"));
assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
* ask Chimera for residues with an attribute
* 91 and 96 on sequence --> residues 40 and 45 on chains A and B
*/
- reply = binding.sendChimeraCommand(
- "list resi att jv_metal_ion_binding_site", true);
+ reply = binding.executeCommand(
+ new StructureCommand("list resi att jv_metal_ion_binding_site"),
+ true);
assertEquals(reply.size(), 4);
assertTrue(reply
.contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
* check attributes with score values
* sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
*/
- reply = binding.sendChimeraCommand("list resi att jv_kd", true);
+ reply = binding.executeCommand(
+ new StructureCommand("list resi att jv_kd"), true);
assertEquals(reply.size(), 4);
assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
/*
* list residues with positive kd score
*/
- reply = binding.sendChimeraCommand(
- "list resi spec :*/jv_kd>0 attr jv_kd", true);
+ reply = binding.executeCommand(
+ new StructureCommand("list resi spec :*/jv_kd>0 attr jv_kd"),
+ true);
assertEquals(reply.size(), 2);
assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
}
} while (!binding.isFinishedInit());
- assertTrue(binding.isChimeraRunning(), "Failed to launch Chimera");
+ assertTrue(binding.isViewerRunning(), "Failed to launch Chimera");
assertEquals(binding.getPdbCount(), 1);
/*
- * 'perform' menu action to copy visible features to
- * attributes in Chimera
+ * 'perform' menu action to copy Chimera attributes
+ * to features in Jalview
*/
// TODO rename and pull up method to binding interface
// once functionality is added for Jmol as well
binding.copyStructureAttributesToFeatures("phi", af.getViewport()
.getAlignPanel());
fr.setVisible("phi");
- List<SequenceFeature> fs = fer2Arath.getFeatures().findFeatures(54, 54);
- assertEquals(fs.size(), 3);
- /*
- * order of returned features is not guaranteed
- */
- assertTrue("RESNUM".equals(fs.get(0).getType())
- || "RESNUM".equals(fs.get(1).getType())
- || "RESNUM".equals(fs.get(2).getType()));
+ List<SequenceFeature> fs = fer2Arath.getFeatures().findFeatures(54, 54,
+ "phi");
+ assertEquals(fs.size(), 2);
assertTrue(fs.contains(new SequenceFeature("phi", "A", 54, 54,
-131.0713f, "Chimera")));
assertTrue(fs.contains(new SequenceFeature("phi", "B", 54, 54,
int res, String featureType)
{
String where = "at position " + res;
- List<SequenceFeature> fs = seq.getFeatures().findFeatures(res, res);
+ List<SequenceFeature> fs = seq.getFeatures().findFeatures(res, res,
+ featureType);
- assertEquals(fs.size(), 2, where);
- assertEquals(fs.get(0).getType(), "RESNUM", where);
- SequenceFeature sf = fs.get(1);
+ assertEquals(fs.size(), 1, where);
+ SequenceFeature sf = fs.get(0);
assertEquals(sf.getType(), featureType, where);
assertEquals(sf.getFeatureGroup(), "Chimera", where);
assertEquals(sf.getDescription(), "True", where);
--- /dev/null
+package jalview.structure;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class AtomSpecModelTest
+{
+ @Test(groups="Functional")
+ public void testGetRanges()
+ {
+ AtomSpecModel model = new AtomSpecModel();
+ assertFalse(model.getModels().iterator().hasNext());
+ List<int[]> ranges = model.getRanges("1", "A");
+ assertTrue(ranges.isEmpty());
+
+ model.addRange("1", 12, 14, "A");
+ assertTrue(model.getRanges("1", "B").isEmpty());
+ assertTrue(model.getRanges("2", "A").isEmpty());
+ ranges = model.getRanges("1", "A");
+ assertEquals(ranges.size(), 1);
+ int[] range = ranges.get(0);
+ assertEquals(range[0], 12);
+ assertEquals(range[1], 14);
+
+ /*
+ * add some ranges; they should be coalesced and
+ * ordered when retrieved
+ */
+ model.addRange("1", 25, 25, "A");
+ model.addRange("1", 20, 24, "A");
+ model.addRange("1", 6, 8, "A");
+ model.addRange("1", 13, 18, "A");
+ model.addRange("1", 5, 6, "A");
+ ranges = model.getRanges("1", "A");
+ assertEquals(ranges.size(), 3);
+ range = ranges.get(0);
+ assertEquals(range[0], 5);
+ assertEquals(range[1], 8);
+ range = ranges.get(1);
+ assertEquals(range[0], 12);
+ assertEquals(range[1], 18);
+ range = ranges.get(2);
+ assertEquals(range[0], 20);
+ assertEquals(range[1], 25);
+ }
+}
public class AtomSpecTest
{
@Test
- public void testFromChimeraAtomSpec()
+ public void testFromChimeraAtomSpec_chimera()
{
- AtomSpec as = AtomSpec.fromChimeraAtomspec("#1:12.B");
+ AtomSpec as = AtomSpec.fromChimeraAtomspec("#1:12.B", false);
assertEquals(as.getModelNumber(), 1);
assertEquals(as.getPdbResNum(), 12);
assertEquals(as.getChain(), "B");
assertNull(as.getPdbFile());
// no model - default to zero
- as = AtomSpec.fromChimeraAtomspec(":13.C");
+ as = AtomSpec.fromChimeraAtomspec(":13.C", false);
assertEquals(as.getModelNumber(), 0);
assertEquals(as.getPdbResNum(), 13);
assertEquals(as.getChain(), "C");
assertNull(as.getPdbFile());
// model.submodel
- as = AtomSpec.fromChimeraAtomspec("#3.2:15");
+ as = AtomSpec.fromChimeraAtomspec("#3.2:15", false);
assertEquals(as.getModelNumber(), 3);
assertEquals(as.getPdbResNum(), 15);
assertEquals(as.getChain(), "");
String spec = "3:12.B";
try
{
- as = AtomSpec.fromChimeraAtomspec(spec);
+ as = AtomSpec.fromChimeraAtomspec(spec, false);
fail("Expected exception for " + spec);
} catch (IllegalArgumentException e)
{
spec = "#3:12-14.B";
try
{
- as = AtomSpec.fromChimeraAtomspec(spec);
+ as = AtomSpec.fromChimeraAtomspec(spec, false);
fail("Expected exception for " + spec);
} catch (IllegalArgumentException e)
{
spec = "";
try
{
- as = AtomSpec.fromChimeraAtomspec(spec);
+ as = AtomSpec.fromChimeraAtomspec(spec, false);
fail("Expected exception for " + spec);
} catch (IllegalArgumentException e)
{
spec = null;
try
{
- as = AtomSpec.fromChimeraAtomspec(spec);
+ as = AtomSpec.fromChimeraAtomspec(spec, false);
+ fail("Expected exception for " + spec);
+ } catch (NullPointerException e)
+ {
+ // ok
+ }
+ }
+
+ @Test
+ public void testFromChimeraAtomSpec_chimeraX()
+ {
+ AtomSpec as = AtomSpec.fromChimeraAtomspec("#1/B:12", true);
+ assertEquals(as.getModelNumber(), 1);
+ assertEquals(as.getPdbResNum(), 12);
+ assertEquals(as.getChain(), "B");
+ assertNull(as.getPdbFile());
+
+ // no model - default to zero
+ as = AtomSpec.fromChimeraAtomspec("/C:13", true);
+ assertEquals(as.getModelNumber(), 0);
+ assertEquals(as.getPdbResNum(), 13);
+ assertEquals(as.getChain(), "C");
+ assertNull(as.getPdbFile());
+
+ // model.submodel
+ as = AtomSpec.fromChimeraAtomspec("#3.2/:15", true);
+ assertEquals(as.getModelNumber(), 3);
+ assertEquals(as.getPdbResNum(), 15);
+ assertEquals(as.getChain(), "");
+ assertNull(as.getPdbFile());
+
+ String spec = "3:12.B";
+ try
+ {
+ as = AtomSpec.fromChimeraAtomspec(spec, true);
+ fail("Expected exception for " + spec);
+ } catch (IllegalArgumentException e)
+ {
+ // ok
+ }
+
+ spec = "#3:12-14.B";
+ try
+ {
+ as = AtomSpec.fromChimeraAtomspec(spec, true);
+ fail("Expected exception for " + spec);
+ } catch (IllegalArgumentException e)
+ {
+ // ok
+ }
+
+ spec = "";
+ try
+ {
+ as = AtomSpec.fromChimeraAtomspec(spec, true);
+ fail("Expected exception for " + spec);
+ } catch (IllegalArgumentException e)
+ {
+ // ok
+ }
+
+ spec = null;
+ try
+ {
+ as = AtomSpec.fromChimeraAtomspec(spec, true);
fail("Expected exception for " + spec);
} catch (NullPointerException e)
{
acf3.addMap(new Sequence("s3", "ttt"), new Sequence("p3", "p"),
new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 1, 1));
- List<AlignedCodonFrame> set1 = new ArrayList<AlignedCodonFrame>();
+ List<AlignedCodonFrame> set1 = new ArrayList<>();
set1.add(acf1);
set1.add(acf2);
- List<AlignedCodonFrame> set2 = new ArrayList<AlignedCodonFrame>();
+ List<AlignedCodonFrame> set2 = new ArrayList<>();
set2.add(acf2);
set2.add(acf3);
assertEquals(1, pmap.getSeqs().size());
assertEquals("4IM2|A", pmap.getSeqs().get(0).getName());
- List<int[]> structuremap1 = new ArrayList(
+ List<int[]> structuremap1 = new ArrayList<>(
sm.getMapping(P4IM2_MISSING)[0]
.getPDBResNumRanges(seq.getStart(), seq.getEnd()));
// positional mapping to atoms for color by structure is still wrong, even
// though panel looks correct.
- StructureMappingcommandSet smcr[] = JmolCommands
- .getColourBySequenceCommand(apssm,
+ String[] smcr = new JmolCommands().colourBySequence(apssm,
new String[]
{ pdbe.getFile() },
new SequenceI[][]
new SequenceRenderer(alf.alignPanel.getAlignViewport()),
alf.alignPanel);
// Expected - all residues are white
- for (StructureMappingcommandSet smm : smcr)
+ for (String c : smcr)
{
- for (String c : smm.commands)
- {
- System.out.println(c);
- }
+ assertTrue(c.contains("color[255,255,255]"));
+ System.out.println(c);
}
}
*/
package jalview.structures.models;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
import jalview.api.AlignmentViewPanel;
-import jalview.api.FeatureRenderer;
import jalview.api.SequenceRenderer;
import jalview.datamodel.Alignment;
import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.ColumnSelection;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.PDBEntry.Type;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.ChimeraCommands;
+import jalview.gui.AlignFrame;
import jalview.gui.JvOptionPane;
+import jalview.gui.StructureViewer.ViewerType;
import jalview.io.DataSourceType;
import jalview.io.FileFormats;
-import jalview.schemes.ColourSchemeI;
+import jalview.io.FileLoader;
+import jalview.schemes.JalviewColourScheme;
import jalview.structure.AtomSpec;
-import jalview.structure.StructureMappingcommandSet;
+import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommandI;
+import jalview.structure.StructureMapping;
import jalview.structure.StructureSelectionManager;
-import jalview.structures.models.AAStructureBindingModel.SuperposeData;
-
-import java.awt.Color;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.List;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import junit.extensions.PA;
/**
* Unit tests for non-abstract methods of abstract base class
@Override
public void updateColours(Object source)
{
- // TODO Auto-generated method stub
-
}
@Override
public void releaseReferences(Object svl)
{
- // TODO Auto-generated method stub
-
}
@Override
public String[] getStructureFiles()
{
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public String superposeStructures(AlignmentI[] alignments,
- int[] structureIndices, HiddenColumns[] hiddenCols)
- {
- // TODO Auto-generated method stub
return null;
}
@Override
- public void setJalviewColourScheme(ColourSchemeI cs)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void setBackgroundColour(Color col)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
public void highlightAtoms(List<AtomSpec> atoms)
{
- // TODO Auto-generated method stub
-
}
@Override
public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
{
- // TODO Auto-generated method stub
return null;
}
-
+
@Override
- public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+ protected List<String> executeCommand(StructureCommandI command,
+ boolean getReply)
{
- // TODO Auto-generated method stub
return null;
}
-
+
@Override
- protected StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, AlignmentViewPanel avp)
+ protected String getModelIdForFile(String chainId)
{
- // TODO Auto-generated method stub
- return null;
+ return "";
}
-
+
@Override
- public List<String> getChainNames()
+ protected ViewerType getViewerType()
{
- // TODO Auto-generated method stub
return null;
}
-
- @Override
- protected void colourBySequence(
- StructureMappingcommandSet[] colourBySequenceCommands)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void colourByCharge()
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void colourByChain()
- {
- // TODO Auto-generated method stub
-
- }
};
String[][] chains = binder.getChains();
assertFalse(chains == null || chains[0] == null,
"No chains discovered by binding");
- assertEquals(2, chains[0].length);
- assertEquals("A", chains[0][0]);
- assertEquals("B", chains[0][1]);
+ assertEquals(chains[0].length, 2);
+ assertEquals(chains[0][0], "A");
+ assertEquals(chains[0][1], "B");
}
AAStructureBindingModel testee;
ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3,
DataSourceType.PASTE, null);
- testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null)
+ testee = newBindingModel(pdbFiles, seqs, ssm, null);
+ }
+
+ /**
+ * A helper method to construct the test target object
+ *
+ * @param pdbFiles
+ * @param seqs
+ * @param ssm
+ * @param alignPanel
+ */
+ protected AAStructureBindingModel newBindingModel(PDBEntry[] pdbFiles,
+ SequenceI[][] seqs,
+ StructureSelectionManager ssm, AlignmentViewPanel avp)
+ {
+ AAStructureBindingModel model = new AAStructureBindingModel(ssm,
+ pdbFiles, seqs, null)
{
@Override
public String[] getStructureFiles()
{
- return new String[] { "INLINE1YCS", "INLINE3A6S", "INLINE1OOT" };
+ String[] files = new String[getPdbCount()];
+ for (int i = 0; i < this.getPdbCount(); i++)
+ {
+ files[i] = getPdbEntry(i).getFile();
+ }
+ return files;
}
@Override
}
@Override
- public List<String> getChainNames()
- {
- return null;
- }
-
- @Override
- public void setJalviewColourScheme(ColourSchemeI cs)
- {
- }
-
- @Override
- public String superposeStructures(AlignmentI[] als, int[] alm,
- HiddenColumns[] alc)
- {
- return null;
- }
-
- @Override
- public void setBackgroundColour(Color col)
- {
- }
-
- @Override
- protected StructureMappingcommandSet[] getColourBySequenceCommands(
- String[] files, SequenceRenderer sr, AlignmentViewPanel avp)
- {
- return null;
- }
-
- @Override
public SequenceRenderer getSequenceRenderer(
- AlignmentViewPanel alignment)
+ AlignmentViewPanel avp)
{
- return null;
+ return avp == null ? null
+ : new jalview.gui.SequenceRenderer(
+ avp.getAlignViewport());
}
@Override
- protected void colourBySequence(
- StructureMappingcommandSet[] colourBySequenceCommands)
- {
- }
-
- @Override
- public void colourByChain()
+ protected List<String> executeCommand(StructureCommandI command,
+ boolean getReply)
{
+ return null;
}
+ /*
+ * for this test, let structure model ids be 0, 1, ...
+ * corresponding to first, second etc pdbfile
+ */
@Override
- public void colourByCharge()
+ protected String getModelIdForFile(String pdbfile)
{
+ for (int i = 0; i < this.getPdbCount(); i++)
+ {
+ if (pdbfile.equals(this.getPdbEntry(i).getFile()))
+ {
+ return String.valueOf(i);
+ }
+ }
+ return "";
}
@Override
- public FeatureRenderer getFeatureRenderer(
- AlignmentViewPanel alignment)
+ protected ViewerType getViewerType()
{
return null;
}
};
+ PA.setValue(model, "commandGenerator", new ChimeraCommands());
+ return model;
}
/**
/*
* create a data bean to hold data per structure file
*/
- SuperposeData[] structs = new SuperposeData[testee.getStructureFiles().length];
+ AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[testee.getStructureFiles().length];
for (int i = 0; i < structs.length; i++)
{
- structs[i] = testee.new SuperposeData(al.getWidth());
+ structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(), "0");
}
/*
* initialise BitSet of 'superposable columns' to true (would be false for
int refStructure = testee
.findSuperposableResidues(al, matched, structs);
- assertEquals(0, refStructure);
+ assertEquals(refStructure, 0);
/*
* only ungapped, structure-mapped columns are superposable
assertTrue(matched.get(4));
assertTrue(matched.get(5)); // gap in second sequence
- assertEquals("1YCS", structs[0].pdbId);
- assertEquals("3A6S", structs[1].pdbId);
- assertEquals("1OOT", structs[2].pdbId);
- assertEquals("A", structs[0].chain); // ? struct has chains A _and_ B
- assertEquals("B", structs[1].chain);
- assertEquals("A", structs[2].chain);
+ assertEquals(structs[0].pdbId, "1YCS");
+ assertEquals(structs[1].pdbId, "3A6S");
+ assertEquals(structs[2].pdbId, "1OOT");
+ assertEquals(structs[0].chain, "A"); // ? struct has chains A _and_ B
+ assertEquals(structs[1].chain, "B");
+ assertEquals(structs[2].chain, "A");
// the 0's for unsuperposable positions propagate down the columns:
- assertEquals("[0, 97, 98, 99, 100, 102]",
- Arrays.toString(structs[0].pdbResNo));
- assertEquals("[0, 2, 0, 3, 4, 5]", Arrays.toString(structs[1].pdbResNo));
- assertEquals("[0, 8, 0, 0, 10, 12]",
- Arrays.toString(structs[2].pdbResNo));
+ assertEquals(Arrays.toString(structs[0].pdbResNo),
+ "[0, 97, 98, 99, 100, 102]");
+ assertEquals(Arrays.toString(structs[1].pdbResNo),
+ "[0, 2, 0, 3, 4, 5]");
+ assertEquals(Arrays.toString(structs[2].pdbResNo),
+ "[0, 8, 0, 0, 10, 12]");
}
@Test(groups = { "Functional" })
public void testFindSuperposableResidues_hiddenColumn()
{
- SuperposeData[] structs = new SuperposeData[al.getHeight()];
+ AAStructureBindingModel.SuperposeData[] structs = new AAStructureBindingModel.SuperposeData[al.getHeight()];
for (int i = 0; i < structs.length; i++)
{
- structs[i] = testee.new SuperposeData(al.getWidth());
+ structs[i] = new AAStructureBindingModel.SuperposeData(al.getWidth(), "0");
}
/*
* initialise BitSet of 'superposable columns' to true (would be false for
int refStructure = testee
.findSuperposableResidues(al, matched, structs);
- assertEquals(0, refStructure);
+ assertEquals(refStructure, 0);
// only ungapped, structure-mapped columns are not superposable
assertFalse(matched.get(0));
assertFalse(matched.get(4)); // superposable, but hidden, column
assertTrue(matched.get(5));
}
-}
+
+ @Test(groups = { "Functional" })
+ public void testBuildColoursMap()
+ {
+ /*
+ * load these sequences, coloured by Strand propensity,
+ * with columns 2-4 hidden
+ */
+ String fasta = ">seq1\nMHRSQSSSGG\n>seq2\nMVRSNGGSSS";
+ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta,
+ DataSourceType.PASTE);
+ AlignmentI al = af.getViewport().getAlignment();
+ af.changeColour_actionPerformed(JalviewColourScheme.Strand.toString());
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(2);
+ cs.addElement(3);
+ cs.addElement(4);
+ af.getViewport().setColumnSelection(cs);
+ af.hideSelColumns_actionPerformed(null);
+ SequenceI seq1 = al.getSequenceAt(0);
+ SequenceI seq2 = al.getSequenceAt(1);
+ SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
+ PDBEntry[] pdbFiles = new PDBEntry[2];
+ pdbFiles[0] = new PDBEntry("PDB1", "A", Type.PDB, "seq1.pdb");
+ pdbFiles[1] = new PDBEntry("PDB2", "B", Type.PDB, "seq2.pdb");
+ StructureSelectionManager ssm = new StructureSelectionManager();
+
+ /*
+ * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
+ */
+ HashMap<Integer, int[]> map = new HashMap<>();
+ for (int pos = 1; pos <= seq1.getLength(); pos++)
+ {
+ map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) });
+ }
+ StructureMapping sm1 = new StructureMapping(seq1, "seq1.pdb", "pdb1",
+ "A", map, null);
+ ssm.addStructureMapping(sm1);
+ StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
+ "B", map, null);
+ ssm.addStructureMapping(sm2);
+
+ AAStructureBindingModel binding = newBindingModel(pdbFiles, seqs, ssm,
+ af.alignPanel);
+
+ /*
+ * method under test builds a map of structures residues by colour
+ * verify the map holds what it should
+ */
+ Map<Object, AtomSpecModel> colours = binding.buildColoursMap(ssm, seqs,
+ af.alignPanel);
+ ChimeraCommands helper = new ChimeraCommands();
+
+ /*
+ * M colour is #82827d (see strand.html help page)
+ * sequence residue 1 mapped to structure residue 21
+ */
+ Color mColor = new Color(0x82827d);
+ AtomSpecModel atomSpec = colours.get(mColor);
+ assertNotNull(atomSpec);
+ assertEquals(helper.getAtomSpec(atomSpec, false), "#0:21.A|#1:21.B");
+
+ /*
+ * H colour is #60609f, seq1.2 mapped to structure 0 residue 22
+ */
+ Color hColor = new Color(0x60609f);
+ atomSpec = colours.get(hColor);
+ assertNotNull(atomSpec);
+ assertEquals(helper.getAtomSpec(atomSpec, false), "#0:22.A");
+
+ /*
+ * V colour is #ffff00, seq2.2 mapped to structure 1 residue 22
+ */
+ Color vColor = new Color(0xffff00);
+ atomSpec = colours.get(vColor);
+ assertNotNull(atomSpec);
+ assertEquals(helper.getAtomSpec(atomSpec, false), "#1:22.B");
+
+ /*
+ * hidden columns are Gray (128, 128, 128)
+ * sequence positions 3-5 mapped to structure residues 23-25
+ */
+ Color gray = new Color(128, 128, 128);
+ atomSpec = colours.get(gray);
+ assertNotNull(atomSpec);
+ assertEquals(helper.getAtomSpec(atomSpec, false), "#0:23-25.A|#1:23-25.B");
+
+ /*
+ * S and G are both coloured #4949b6, structure residues 26-30
+ */
+ Color sgColour = new Color(0x4949b6);
+ atomSpec = colours.get(sgColour);
+ assertNotNull(atomSpec);
+ assertEquals(helper.getAtomSpec(atomSpec, false),
+ "#0:26-30.A|#1:26-30.B");
+ }
+}
\ No newline at end of file