Merge branch 'develop' into feature/JAL-3551Pymol
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 19 May 2020 16:27:32 +0000 (17:27 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 19 May 2020 16:27:32 +0000 (17:27 +0100)
Conflicts:
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/StructureViewerBase.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/project/Jalview2XML.java

20 files changed:
1  2 
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmol.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.java
src/jalview/appletgui/UserDefinedColours.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/Preferences.java
src/jalview/gui/StructureViewerBase.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureViewer.java
src/jalview/project/Jalview2XML.java
test/jalview/structure/StructureSelectionManagerTest.java

@@@ -268,11 -268,11 +268,11 @@@ label.autoadd_secstr = Add secondary st
  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
@@@ -363,7 -363,8 +363,8 @@@ label.open_saved_vamsas_session = Open 
  label.groovy_console = Groovy Console...
  label.lineart = Lineart
  label.dont_ask_me_again = Don't ask me again
- label.select_eps_character_rendering_style = Select EPS character rendering style
+ label.select_character_rendering_style = {0} character rendering style
+ label.select_character_style_title = {0} Rendering options
  label.invert_selection = Invert Selection
  label.optimise_order = Optimise Order
  label.seq_sort_by_score = Sequence sort by Score
@@@ -412,7 -413,7 +413,7 @@@ label.input_alignment_from_url = Input 
  label.input_alignment = Input Alignment
  label.couldnt_import_as_vamsas_session = Couldn't import {0} as a new vamsas session.
  label.vamsas_document_import_failed = Vamsas Document Import Failed
- label.couldnt_locate = Could not locate {0}
+ label.couldnt_locate = Couldn''t locate {0}
  label.url_not_found = URL not found
  label.new_sequence_url_link = New sequence URL link
  label.cannot_edit_annotations_in_wrapped_view = Cannot edit annotations in wrapped view
@@@ -504,7 -505,7 +505,7 @@@ label.sequence_details = Sequence Detai
  label.jmol_help = Jmol Help
  label.chimera_help = Chimera 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
@@@ -598,7 -599,7 +599,7 @@@ label.check_for_questionnaires = Check 
  label.check_for_latest_version = Check for latest version
  label.url_linkfrom_sequence_id = URL link from Sequence ID
  label.use_proxy_server = Use a proxy server
- label.eps_rendering_style = EPS rendering style
+ label.rendering_style = {0} rendering style
  label.append_start_end = Append /start-end (/15-380)
  label.full_sequence_id = Full Sequence Id
  label.smooth_font = Smooth Font
@@@ -623,6 -624,7 +624,6 @@@ label.editing = Editin
  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
@@@ -678,7 -680,7 +679,7 @@@ label.sequence_details_for = Sequence D
  label.sequence_name = Sequence Name
  label.sequence_description = Sequence Description
  label.edit_sequence_name_description = Edit Sequence Name/Description
- label.spaces_converted_to_backslashes = Spaces have been converted to _
+ label.spaces_converted_to_underscores = Spaces have been converted to _
  label.no_spaces_allowed_sequence_name = No spaces allowed in Sequence Name
  label.select_outline_colour = Select Outline Colour
  label.web_browser_not_found_unix = Unixers\: Couldn't find default web browser.\nAdd the full path to your browser in Preferences."
@@@ -709,8 -711,7 +710,8 @@@ label.associate_nodes_with = Associate 
  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
@@@ -884,8 -885,6 +885,6 @@@ label.save_feature_colours = Save Featu
  label.select_startup_file = Select startup file
  label.select_default_browser = Select default web browser
  label.save_tree_as_newick = Save tree as newick file
- label.create_eps_from_tree = Create EPS file from tree
- label.create_png_from_tree = Create PNG image from tree
  label.save_colour_scheme = Save colour scheme
  label.edit_params_for = Edit parameters for {0}
  label.choose_filename_for_param_file = Choose a filename for this parameter file
@@@ -941,8 -940,6 +940,6 @@@ error.call_setprogressbar_before_regist
  label.cancelled_params = Cancelled {0}
  error.implementation_error_cannot_show_view_alignment_frame = Implementation error: cannot show a view from another alignment in an AlignFrame.
  error.implementation_error_dont_know_about_threshold_setting = Implementation error: don't know about threshold setting for current AnnotationColourGradient.
- error.eps_generation_not_implemented = EPS Generation not yet implemented
- error.png_generation_not_implemented = PNG Generation not yet implemented
  error.try_join_vamsas_session_another = Trying to join a vamsas session when another is already connected
  error.invalid_vamsas_session_id = Invalid vamsas session id
  label.groovy_support_failed = Jalview Groovy Support Failed
@@@ -1014,7 -1011,7 +1011,7 @@@ label.pca_recalculating = Recalculatin
  label.pca_calculating = Calculating PCA
  label.select_foreground_colour = Choose foreground colour
  label.select_colour_for_text = Select Colour for Text
- label.adjunst_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold
+ label.adjust_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold
  label.select_subtree_colour = Select Sub-Tree Colour
  label.create_new_sequence_features = Create New Sequence Feature(s)
  label.amend_delete_features = Amend/Delete Features for {0}
@@@ -1086,7 -1083,6 +1083,6 @@@ error.implementation_error_cannot_find_
  exception.jobsubmission_invalid_params_set = Invalid parameter set. Check Jalview implementation
  exception.notvaliddata_group_contains_less_than_min_seqs = Group contains less than {0} sequences.
  exception.outofmemory_loading_pdb_file = Out of memory loading PDB File
- exception.eps_coudnt_write_output_file = Could not write to the output file: {0}
  exception.eps_method_not_supported = Method not currently supported by EpsGraphics2D version {0}
  exception.eps_unable_to_get_inverse_matrix = Unable to get inverse of matrix: {0}
  warn.job_cannot_be_cancelled_close_window = This job cannot be cancelled.\nJust close the window.
@@@ -1111,8 -1107,7 +1107,7 @@@ status.searching_for_sequences_from = S
  status.finished_searching_for_sequences_from = Finished searching for sequences from {0}
  label.eps_file = EPS file
  label.png_image = PNG image
- status.saving_file = Saving {0}
- status.export_complete = {0} Export completed.
+ status.export_complete = {0} Export completed
  status.fetching_pdb = Fetching PDB {0}
  status.refreshing_news = Refreshing news
  status.importing_vamsas_session_from = Importing VAMSAS session from {0}
@@@ -1129,7 -1124,7 +1124,7 @@@ status.fetching_db_refs = Fetching db r
  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}
@@@ -1236,7 -1231,6 +1231,6 @@@ exception.fts_server_unreachable = Jalv
  label.nw_mapping = Needleman & Wunsch Alignment
  label.sifts_mapping = SIFTs Mapping
  label.mapping_method = Sequence \u27f7 Structure mapping method
- status.waiting_for_user_to_select_output_file = Waiting for user to select {0} file
  status.cancelled_image_export_operation = Cancelled {0} export operation
  info.error_creating_file = Error creating {0} file
  exception.outofmemory_loading_mmcif_file = Out of memory loading mmCIF File
@@@ -1319,7 -1313,7 +1313,7 @@@ label.delete_condition = Delete this co
  label.score = Score
  label.colour_by_label = Colour by label
  label.variable_colour = Variable colour...
- label.select_colour = Select colour
+ label.select_colour_for = Select colour for {0}
  option.enable_disable_autosearch = When ticked, search is performed automatically
  option.autosearch = Autosearch
  label.retrieve_ids = Retrieve IDs
@@@ -1340,6 -1334,13 +1334,13 @@@ label.most_bound_molecules = Most Boun
  label.most_polymer_residues = Most Polymer Residues
  label.cached_structures = Cached Structures
  label.free_text_search = Free Text Search
+ label.annotation_name = Annotation Name
+ label.annotation_description = Annotation Description 
+ label.edit_annotation_name_description = Edit Annotation Name/Description
+ label.alignment = alignment
+ label.pca = PCA
+ label.create_image_of = Create {0} image of {1}
+ label.click_to_edit = Click to edit, right-click for menu
  label.backupfiles_confirm_delete = Confirm delete
  label.backupfiles_confirm_delete_old_files = Delete the following older backup files? (see the Backups tab in Preferences for more options)
  label.backupfiles_confirm_save_file = Confirm save file
@@@ -328,7 -328,8 +328,8 @@@ label.open_saved_vamsas_session = Abri
  label.groovy_console = Consola Groovy 
  label.lineart = Lineart
  label.dont_ask_me_again = No volver a preguntar
- label.select_eps_character_rendering_style = Seleccionar el carácter EPS como estilo de visualización 
+ label.select_character_rendering_style = Estilo de visualización para carácter {0} 
+ label.select_character_style_title = Opciones de visualización {0}
  label.invert_selection = Invertir selección
  label.optimise_order = Optimizar orden
  label.seq_sort_by_score = Ordenar las secuencias por puntuación
@@@ -550,7 -551,7 +551,7 @@@ label.check_for_questionnaires = Compro
  label.check_for_latest_version = Comprobar la Ãºltima versión
  label.url_linkfrom_sequence_id = URL del enlace del ID de la secuencia
  label.use_proxy_server = Utilizar un servidor proxy
- label.eps_rendering_style = Estilo de visualización EPS
+ label.rendering_style = Estilo de visualización {0}
  label.append_start_end = Añadir /inicio-fin (/15-380)
  label.full_sequence_id = ID de la secuencia completo
  label.smooth_font = Fuente alargada
@@@ -626,7 -627,7 +627,7 @@@ label.sequence_details_for = Detalles d
  label.sequence_name = Nombre de la secuencia
  label.sequence_description = Descripción de la secuencia
  label.edit_sequence_name_description = Editar el nombre/descripción de la secuencia
- label.spaces_converted_to_backslashes = Los espacios se han convertido en _
+ label.spaces_converted_to_underscores = Los espacios se han convertido en _
  label.no_spaces_allowed_sequence_name = No se permiten espacios en el nombre de la secuencia
  label.select_outline_colour = Seleccionar el color del límite
  label.web_browser_not_found_unix = Unixers\: No es posible encontrar el navegador web por defecto.\nA\u00F1ada la ruta completa de su navegador en la pesta\u00F1a de Preferencias.
@@@ -803,8 -804,6 +804,6 @@@ label.save_feature_colours = Guardar es
  label.select_startup_file = Seleccionar fichero de arranque
  label.select_default_browser = Seleccionar navegador web por defecto
  label.save_tree_as_newick = Guardar Ã¡rbol como fichero newick
- label.create_eps_from_tree = Crear un fichero EPS a partir de un Ã¡rbol
- label.create_png_from_tree = Crear una imagen PNG a partir de un Ã¡rbol
  label.save_colour_scheme = Guardar esquema cromático
  label.edit_params_for = Editar los parámetros de {0}
  label.choose_filename_for_param_file = Escoja un nombre de fichero para este fichero de parámetros
@@@ -860,8 -859,6 +859,6 @@@ error.call_setprogressbar_before_regist
  label.cancelled_params = {0} cancelado
  error.implementation_error_cannot_show_view_alignment_frame = Error de implementación: no es posible mostrar una vista de otro alineamiento en un AlignFrame.
  error.implementation_error_dont_know_about_threshold_setting = Error de implementación: no se conoce la configuración del umbral para el AnnotationColourGradient actual.
- error.eps_generation_not_implemented = La generación de EPS no se ha implementado todavía
- error.png_generation_not_implemented = La generación de PNG no se ha implementado todavía
  error.try_join_vamsas_session_another = Tratando de establecer una sesión VAMSAS cuando ya había otra conectada
  error.invalid_vamsas_session_id = Identificador de sesión VAMSAS no válido
  label.groovy_support_failed = El soporte Groovy de Jalview ha fallado
@@@ -933,7 -930,7 +930,7 @@@ label.pca_recalculating = Recalculando 
  label.pca_calculating = Calculando ACP
  label.select_foreground_colour = Escoger color del primer plano
  label.select_colour_for_text = Seleccione el color del texto
- label.adjunst_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano
+ label.adjust_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano
  label.select_subtree_colour = Seleccioanr el color del sub-árbol
  label.create_new_sequence_features = Crear nueva(s) característica(s) de secuencia
  label.amend_delete_features = Arrelgar/Borrar características de {0}
@@@ -1005,7 -1002,6 +1002,6 @@@ error.implementation_error_cannot_find_
  exception.jobsubmission_invalid_params_set = Conjunto de parámetros no válido. Comprueba la implementación de Jalview
  exception.notvaliddata_group_contains_less_than_min_seqs = El grupo contiene menos de {0} secuencias.
  exception.outofmemory_loading_pdb_file = Sin memoria al cargar el fichero PDB
- exception.eps_coudnt_write_output_file = No es posible escribir el fichero de salida: {0}
  exception.eps_method_not_supported = Método actualmente no suportado por la versión {0} de EpsGraphics2D
  exception.eps_unable_to_get_inverse_matrix = Imposible obtener la inversa de la matrix: {0}
  warn.job_cannot_be_cancelled_close_window = Este trabajo no se puede cancelar.\nSimplemente, cierre la ventana.
@@@ -1027,8 -1023,7 +1023,7 @@@ status.searching_for_sequences_from = B
  status.finished_searching_for_sequences_from = Finalizada la búsqueda de secuencias en {0}
  label.eps_file = Fichero EPS
  label.png_image = Imagen PNG
- status.saving_file = Guardando {0}
- status.export_complete = Exportación completada.
+ status.export_complete = Exportación completada
  status.fetching_pdb = Recuperando PDB {0}
  status.refreshing_news = Refrescando noticias
  status.importing_vamsas_session_from = Importando sesión VAMSAS de {0}
@@@ -1145,7 -1140,7 +1140,7 @@@ label.invalid_search=Texto de búsqueda 
  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
@@@ -1158,6 -1153,7 +1153,6 @@@ action.export_features=Exportar Caracte
  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
@@@ -1165,11 -1161,11 +1160,11 @@@ label.add_reference_annotations=Añadir 
  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.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
@@@ -1180,6 -1176,7 +1175,6 @@@ tooltip.aacon_settings=Cambiar ajustes 
  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
@@@ -1192,19 -1189,16 +1187,19 @@@ label.protein=Proteín
  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
@@@ -1220,6 -1214,7 +1215,6 @@@ label.aacon_settings=Cambiar Ajustes AA
  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
@@@ -1314,7 -1309,7 +1309,7 @@@ label.join_conditions = Combinar condic
  label.score = Puntuación
  label.colour_by_label = Colorear por texto
  label.variable_colour = Color variable...
- label.select_colour = Seleccionar color
+ label.select_colour_for = Seleccionar color para {0}
  option.enable_disable_autosearch = Marcar para buscar automáticamente
  option.autosearch = Auto búsqueda
  label.retrieve_ids = Recuperar IDs
@@@ -1335,6 -1330,13 +1330,13 @@@ label.most_bound_molecules = Más Molécu
  label.most_polymer_residues = Más Residuos de Polímeros
  label.cached_structures = Estructuras en Caché
  label.free_text_search = Búsqueda de texto libre
+ label.annotation_name = Nombre de la anotación
+ label.annotation_description = Descripción de la anotación 
+ label.edit_annotation_name_description = Editar el nombre/descripción de la anotación
+ label.alignment = alineamiento
+ label.pca = ACP
+ label.create_image_of = Crear imagen {0} de {1}
+ label.click_to_edit = Haga clic para editar, clic en el botón derecho para ver el menú  
  label.backupfiles_confirm_delete = Confirmar borrar
  label.backupfiles_confirm_delete_old_files = Â¿Borrar los siguientes archivos? (ver la pestaña 'Copias' de la ventana de Preferencias para más opciones)
  label.backupfiles_confirm_save_file = Confirmar guardar archivo
@@@ -418,7 -418,7 +418,7 @@@ public class AlignFrame extends Embmenu
          viewport.featureSettings.refreshTable();
        }
        alignPanel.paintAlignment(true, true);
-       statusBar.setText(MessageManager
+       setStatus(MessageManager
                .getString("label.successfully_added_features_alignment"));
      }
      return featuresFile;
  
      case KeyEvent.VK_F2:
        viewport.cursorMode = !viewport.cursorMode;
-       statusBar.setText(MessageManager
+       setStatus(MessageManager
                .formatMessage("label.keyboard_editing_mode", new String[]
                { (viewport.cursorMode ? "on" : "off") }));
        if (viewport.cursorMode)
              seqs, 0, viewport.getAlignment().getWidth(),
              viewport.getAlignment()));
  
-     viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight());
+     viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight() - 1); // BH
+                                                                              // 2019.04.18
      viewport.getAlignment().getWidth();
      viewport.firePropertyChange("alignment", null,
              viewport.getAlignment().getSequences());
                  column, al);
        }
  
-       statusBar.setText(MessageManager
+       setStatus(MessageManager
                .formatMessage("label.removed_columns", new String[]
                { Integer.valueOf(trimRegion.getSize()).toString() }));
        addHistoryItem(trimRegion);
  
      addHistoryItem(removeGapCols);
  
-     statusBar.setText(MessageManager
+     setStatus(MessageManager
              .formatMessage("label.removed_empty_columns", new String[]
              { Integer.valueOf(removeGapCols.getSize()).toString() }));
  
       */
      statusBar.setBackground(Color.white);
      statusBar.setFont(new java.awt.Font("Verdana", 0, 11));
-     statusBar.setText(MessageManager.getString("label.status_bar"));
+     setStatus(MessageManager.getString("label.status_bar"));
      this.add(statusBar, BorderLayout.SOUTH);
    }
  
     * 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
      }
      else
      {
-       new MCview.AppletPDBViewer(pdb, seqs, chains, alignPanel, protocol);
+       new mc_view.AppletPDBViewer(pdb, seqs, chains, alignPanel, protocol);
      }
  
    }
   */
  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;
@@@ -45,25 -64,6 +45,25 @@@ import java.util.ArrayList
  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
        {
              }
              FileParse fp = new FileParse(pdbentry.getFile(), protocol);
              fp.mark();
-             // reader = new MCview.PDBfile(fp);
+             // reader = new mc_view.PDBfile(fp);
              // could set ID, etc.
              // if (!reader.isValid())
              // {
              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)
@@@ -32,7 -32,7 +32,7 @@@ import java.util.Map
  
  import org.jmol.api.JmolAppConsoleInterface;
  import org.jmol.console.AppletConsole;
- import org.jmol.java.BS;
+ import javajs.util.BS;
  
  class AppletJmolBinding extends JalviewJmolBinding
  {
    }
  
    @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;
    }
  
@@@ -21,6 -21,7 +21,6 @@@
  package jalview.appletgui;
  
  import jalview.api.AlignmentViewPanel;
 -import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
@@@ -34,7 -35,7 +34,7 @@@ import java.util.Map
  import java.util.Vector;
  
  import org.jmol.api.JmolAppConsoleInterface;
- import org.jmol.java.BS;
+ import javajs.util.BS;
  import org.jmol.viewer.Viewer;
  
  /**
@@@ -75,6 -76,21 +75,6 @@@ public class ExtJmol extends JalviewJmo
    }
  
    @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;
    }
 -
  }
@@@ -70,7 -70,7 +70,7 @@@ public class UserDefinedColours extend
  
    Frame frame;
  
-   MCview.AppletPDBCanvas pdbcanvas;
+   mc_view.AppletPDBCanvas pdbcanvas;
  
    AppletJmol jmol;
  
      init();
    }
  
-   public UserDefinedColours(MCview.AppletPDBCanvas pdb)
+   public UserDefinedColours(mc_view.AppletPDBCanvas pdb)
    {
      this.pdbcanvas = pdb;
      init();
      }
      else if (jmol != null)
      {
 -      jmol.setJalviewColourScheme(ucs);
 +      jmol.colourByJalviewColourScheme(ucs);
      }
      else if (pdbcanvas != null)
      {
   */
  package jalview.datamodel.features;
  
- import jalview.datamodel.SequenceFeature;
- import jalview.io.gff.SequenceOntologyFactory;
- import jalview.io.gff.SequenceOntologyI;
  import java.util.ArrayList;
+ import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
@@@ -33,6 -30,9 +30,9 @@@ import java.util.Set
  import java.util.TreeMap;
  
  import intervalstore.api.IntervalI;
+ import jalview.datamodel.SequenceFeature;
+ import jalview.io.gff.SequenceOntologyFactory;
+ import jalview.io.gff.SequenceOntologyI;
  
  /**
   * A class that stores sequence features in a way that supports efficient
@@@ -44,7 -44,6 +44,6 @@@
   */
  public class SequenceFeatures implements SequenceFeaturesI
  {
    /*
     * map from feature type to structured store of features for that type
     * null types are permitted (but not a good idea!)
    public static void sortFeatures(List<? extends IntervalI> features,
            final boolean forwardStrand)
    {
-     IntervalI.sortIntervals(features, forwardStrand);
+     Collections.sort(features,
 -            forwardStrand ? IntervalI.COMPARE_BEGIN_ASC_END_ASC
++            forwardStrand
++                    ? IntervalI.COMPARE_BEGIN_ASC_END_DESC
+                     : IntervalI.COMPARE_END_DESC);
    }
  
    /**
   */
  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;
@@@ -37,49 -58,50 +37,50 @@@ import org.jmol.api.JmolSelectionListen
  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.FeatureRenderer;
++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;
 +
  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;
 +  private Vector<String> atomsPicked = new Vector<>();
  
 -  // protected JmolGenericPopup jmolpopup; // not used - remove?
 +  private String lastCommand;
  
 -  String lastCommand;
 +  private boolean loadedInline;
  
 -  boolean loadedInline;
 +  private StringBuffer resetLastRes = new StringBuffer();
  
 -  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)
++  private String jmolScript(String script)
+   {
 -    superposeStructures(alignment, -1, null);
 -  }
++    Cache.log.debug(">>Jmol>> " + script);
++    String s = jmolViewer.evalStringQuiet(script);
++    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)
 -   */
 -  public void superposeStructures(AlignmentI alignment, int refStructure)
 -  {
 -    superposeStructures(alignment, refStructure, 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)
 -   * @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))
      {
-       jmolViewer.evalStringQuiet(cmd + "\n");
 -      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);
          }
      {
        if (resetLastRes.length() > 0)
        {
-         jmolViewer.evalStringQuiet(resetLastRes.toString());
+         jmolScript(resetLastRes.toString());
          resetLastRes.setLength(0);
        }
        for (AtomSpec atom : atoms)
    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;");
  
-     jmolViewer.evalStringQuiet(cmd.toString());
+     jmolScript(cmd.toString());
      jmolHistory(true);
 -
    }
  
 -  boolean debug = true;
 +  private boolean debug = true;
  
    private void jmolHistory(boolean enable)
    {
-     jmolViewer.evalStringQuiet("History " + ((debug || enable) ? "on" : "off"));
+     jmolScript("History " + ((debug || enable) ? "on" : "off"));
    }
  
    public void loadInline(String string)
      // 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);
      }
    }
  
    {
      /**
       * this implements the toggle label behaviour copied from the original
-      * structure viewer, MCView
+      * structure viewer, mc_view
       */
      if (strData != null)
      {
  
      if (!atomsPicked.contains(picked))
      {
-       jmolViewer.evalStringQuiet("select " + picked + ";label %n %r:%c");
+       jmolScript("select " + picked + ";label %n %r:%c");
        atomsPicked.addElement(picked);
      }
      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();
      // first check if we've lost any structures
          // 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
            // see JAL-623 - need method of matching pasted data up
            {
              pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
-                     pdbfile, DataSourceType.PASTE,
-                     getIProgressIndicator());
+                     pdbfile, DataSourceType.PASTE, getIProgressIndicator());
              getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
              matches = true;
              foundEntry = true;
          }
          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
      // }
      if (!isLoadingFromArchive())
      {
-       jmolViewer.evalStringQuiet(
+       jmolScript(
                "model *; select backbone;restrict;cartoon;wireframe off;spacefill off");
      }
      // register ourselves as a listener and notify the gui that it needs 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://jmol.sourceforge.net/docs/JmolUserGuide/", "jmolHelp");
+     showUrl("http://wiki.jmol.org"
+     // BH 2018 "http://jmol.sourceforge.net/docs/JmolUserGuide/"
+             , "jmolHelp");
    }
  
    /**
    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
     */
    public abstract void showConsole(boolean show);
  
+   public static Viewer getJmolData(JmolParser jmolParser)
+   {
+     return (Viewer) JmolViewer.allocateViewer(null, null, null, null, null,
+             "-x -o -n", jmolParser);
+   }
    /**
+    * 
+    * 
+    * 
     * @param renderPanel
     * @param jmolfileio
     *          - when true will initialise jmol's file IO system (should be false
     * @param consolePanel
     *          - panel to contain Jmol console
     * @param buttonsToShow
-    *          - buttons to show on the console, in ordr
+    *          - buttons to show on the console, in order
     */
    public void allocateViewer(Container renderPanel, boolean jmolfileio,
            String htmlName, URL documentBase, URL codeBase,
            String commandOptions, final Container consolePanel,
            String buttonsToShow)
    {
+     System.err.println("Allocating Jmol Viewer: " + commandOptions);
      if (commandOptions == null)
      {
        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
  
-     console = createJmolConsole(consolePanel, buttonsToShow);
+     try
+     {
+       console = createJmolConsole(consolePanel, buttonsToShow);
+     } catch (Throwable e)
+     {
+       System.err.println("Could not create Jmol application console. "
+               + e.getMessage());
+       e.printStackTrace();
+     }
      if (consolePanel != null)
      {
        consolePanel.addComponentListener(this);
    protected abstract JmolAppConsoleInterface createJmolConsole(
            Container consolePanel, String buttonsToShow);
  
+   // BH 2018 -- Jmol console is not working due to problems with styled
+   // documents.
    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";
 +  }
  }
   */
  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;
@@@ -34,417 -28,186 +34,436 @@@ import jalview.datamodel.AlignmentI
  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;
  
  /**
 - * 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());
 +  }
 +
-   @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<String> cset = new ArrayList<>();
-     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-     {
-       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-       StringBuilder command = new StringBuilder(128);
-       List<String> str = new ArrayList<>();
-       if (mapping == null || mapping.length < 1)
-       {
-         continue;
-       }
-       for (int s = 0; s < sequence[pdbfnum].length; s++)
-       {
-         for (int sp, m = 0; m < mapping.length; m++)
-         {
-           if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                   && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
-           {
-             int lastPos = StructureMapping.UNASSIGNED_VALUE;
-             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 == lastPos)
-               {
-                 continue;
-               }
-               if (pos == StructureMapping.UNASSIGNED_VALUE)
-               {
-                 // terminate current colour op
-                 if (command.length() > 0
-                         && command.charAt(command.length() - 1) != ';')
-                 {
-                   command.append(";");
-                 }
-                 // reset lastPos
-                 lastPos = StructureMapping.UNASSIGNED_VALUE;
-                 continue;
-               }
-               lastPos = pos;
-               Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
-                       finder);
-               /*
-                * shade hidden regions darker
-                */
-               if (!cs.isVisible(r))
-               {
-                 col = Color.GRAY;
-               }
-               String newSelcom = (mapping[m].getChain() != " "
-                       ? ":" + mapping[m].getChain()
-                       : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
-                       + getColourString(col);
-               if (command.length() > newSelcom.length() && command
-                       .substring(command.length() - newSelcom.length())
-                       .equals(newSelcom))
-               {
-                 command = JmolCommands.condenseCommand(command, pos);
-                 continue;
-               }
-               // TODO: deal with case when buffer is too large for Jmol to parse
-               // - execute command and flush
-               if (command.length() > 0
-                       && command.charAt(command.length() - 1) != ';')
-               {
-                 command.append(";");
-               }
-               if (command.length() > 51200)
-               {
-                 // add another chunk
-                 str.add(command.toString());
-                 command.setLength(0);
-               }
-               command.append("select " + pos);
-               command.append(newSelcom);
-             }
-             // break;
-           }
-         }
-       }
-       {
-         // add final chunk
-         str.add(command.toString());
-         command.setLength(0);
-       }
-       cset.addAll(str);
-     }
-     return cset.toArray(new String[cset.size()]);
-   }
-   public static StringBuilder condenseCommand(StringBuilder command,
-           int pos)
-   {
-     // work back to last 'select'
-     int p = command.length(), q = p;
-     do
-     {
-       p -= 6;
-       if (p < 1)
-       {
-         p = 0;
-       }
-       ;
-     } while ((q = command.indexOf("select", p)) == -1 && p > 0);
-     StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
-     command = command.delete(0, q + 7);
-     String start;
-     if (command.indexOf("-") > -1)
-     {
-       start = command.substring(0, command.indexOf("-"));
-     }
-     else
-     {
-       start = command.substring(0, command.indexOf(":"));
-     }
-     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
-     return sb;
-   }
 +  @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));
 +  }
  
    /**
 -   * Jmol utility which constructs the commands to colour chains by the given
 -   * alignment
 +   * 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>
     * 
 -   * @returns Object[] { Object[] { <model being coloured>,
 +   * 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
     */
 -  public static StructureMappingcommandSet[] getColourBySequenceCommand(
 -          StructureSelectionManager ssm, String[] files,
 -          SequenceI[][] sequence, SequenceRenderer sr,
 +  @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 getColourCommand(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;
 +  }
 +
 +  /**
 +   * Generates a Jmol atomspec string like
 +   * 
 +   * <pre>
 +   * 2-5:A/1.1,8:A/1.1,5-10:B/2.1
 +   * </pre>
 +   * 
 +   * 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)
 +  {
 +    return null;
 +  }
++
++  /**
++   * Obsolete method, only referenced from
++   * jalview.javascript.MouseOverStructureListener
++   * 
++   * @param ssm
++   * @param files
++   * @param sequence
++   * @param sr
++   * @param viewPanel
++   * @return
++   */
++  @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)
+       {
+         continue;
+       }
+       for (int s = 0; s < sequence[pdbfnum].length; s++)
+       {
+         for (int sp, m = 0; m < mapping.length; m++)
+         {
+           if (mapping[m].getSequence() == sequence[pdbfnum][s]
+                   && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
+           {
+             int lastPos = StructureMapping.UNASSIGNED_VALUE;
+             SequenceI asp = al.getSequenceAt(sp);
+             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;
+               }
+               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
+               if (pos == lastPos)
+               {
+                 continue;
+               }
+               if (pos == StructureMapping.UNASSIGNED_VALUE)
+               {
+                 // terminate current colour op
+                 if (command.length() > 0
+                         && command.charAt(command.length() - 1) != ';')
+                 {
+                   command.append(";");
+                 }
+                 // reset lastPos
+                 lastPos = StructureMapping.UNASSIGNED_VALUE;
+                 continue;
+               }
+               lastPos = pos;
+               Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                       finder);
+               /*
+                * shade hidden regions darker
+                */
+               if (!cs.isVisible(r))
+               {
+                 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))
+               {
+                 command = JmolCommands.condenseCommand(command, pos);
+                 continue;
+               }
+               // TODO: deal with case when buffer is too large for Jmol to parse
+               // - execute command and flush
+               if (command.length() > 0
+                       && command.charAt(command.length() - 1) != ';')
+               {
+                 command.append(";");
+               }
+               if (command.length() > 51200)
+               {
+                 // add another chunk
+                 str.add(command.toString());
+                 command.setLength(0);
+               }
+               command.append("select " + pos);
+               command.append(newSelcom);
+             }
+             // break;
+           }
+         }
+       }
+       {
+         // add final chunk
+         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'
+     int p = command.length(), q = p;
+     do
+     {
+       p -= 6;
+       if (p < 1)
+       {
+         p = 0;
+       }
+       ;
+     } while ((q = command.indexOf("select", p)) == -1 && p > 0);
+     StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
+     command = command.delete(0, q + 7);
+     String start;
+     if (command.indexOf("-") > -1)
+     {
+       start = command.substring(0, command.indexOf("-"));
+     }
+     else
+     {
+       start = command.substring(0, command.indexOf(":"));
+     }
+     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
+     return sb;
+   }
 -
  }
   */
  package jalview.gui;
  
+ import java.awt.BorderLayout;
+ import java.awt.Color;
+ import java.awt.Component;
+ import java.awt.Rectangle;
+ import java.awt.Toolkit;
+ import java.awt.datatransfer.Clipboard;
+ import java.awt.datatransfer.DataFlavor;
+ import java.awt.datatransfer.StringSelection;
+ import java.awt.datatransfer.Transferable;
+ import java.awt.dnd.DnDConstants;
+ import java.awt.dnd.DropTargetDragEvent;
+ import java.awt.dnd.DropTargetDropEvent;
+ import java.awt.dnd.DropTargetEvent;
+ import java.awt.dnd.DropTargetListener;
+ import java.awt.event.ActionEvent;
+ import java.awt.event.ActionListener;
+ import java.awt.event.FocusAdapter;
+ import java.awt.event.FocusEvent;
+ import java.awt.event.ItemEvent;
+ import java.awt.event.ItemListener;
+ import java.awt.event.KeyAdapter;
+ import java.awt.event.KeyEvent;
+ import java.awt.event.MouseEvent;
+ import java.awt.print.PageFormat;
+ import java.awt.print.PrinterJob;
+ import java.beans.PropertyChangeEvent;
+ import java.io.File;
+ import java.io.FileWriter;
+ import java.io.PrintWriter;
+ import java.net.URL;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Deque;
+ import java.util.Enumeration;
+ import java.util.Hashtable;
+ import java.util.List;
+ import java.util.Vector;
+ import javax.swing.ButtonGroup;
+ import javax.swing.JCheckBoxMenuItem;
+ import javax.swing.JComponent;
+ import javax.swing.JEditorPane;
+ import javax.swing.JInternalFrame;
+ import javax.swing.JLabel;
+ import javax.swing.JLayeredPane;
+ import javax.swing.JMenu;
+ import javax.swing.JMenuItem;
+ import javax.swing.JPanel;
+ import javax.swing.JScrollPane;
+ import javax.swing.SwingUtilities;
+ import ext.vamsas.ServiceHandle;
  import jalview.analysis.AlignmentSorter;
  import jalview.analysis.AlignmentUtils;
  import jalview.analysis.CrossRef;
@@@ -27,7 -79,7 +79,7 @@@ import jalview.analysis.Dna
  import jalview.analysis.GeneticCodeI;
  import jalview.analysis.ParseProperties;
  import jalview.analysis.SequenceIdMatcher;
- import jalview.api.AlignExportSettingI;
+ import jalview.api.AlignExportSettingsI;
  import jalview.api.AlignViewControllerGuiI;
  import jalview.api.AlignViewControllerI;
  import jalview.api.AlignViewportI;
@@@ -47,6 -99,7 +99,7 @@@ import jalview.commands.RemoveGapColCom
  import jalview.commands.RemoveGapsCommand;
  import jalview.commands.SlideSequencesCommand;
  import jalview.commands.TrimRegionCommand;
+ import jalview.datamodel.AlignExportSettingsAdapter;
  import jalview.datamodel.AlignedCodonFrame;
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentAnnotation;
@@@ -56,7 -109,6 +109,6 @@@ import jalview.datamodel.AlignmentOrder
  import jalview.datamodel.AlignmentView;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.HiddenColumns;
- import jalview.datamodel.HiddenSequences;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SeqCigar;
  import jalview.datamodel.Sequence;
@@@ -86,11 -138,14 +138,14 @@@ import jalview.io.ScoreMatrixFile
  import jalview.io.TCoffeeScoreFile;
  import jalview.io.vcf.VCFLoader;
  import jalview.jbgui.GAlignFrame;
+ import jalview.project.Jalview2XML;
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemes;
  import jalview.schemes.ResidueColourScheme;
  import jalview.schemes.TCoffeeColourScheme;
+ import jalview.util.ImageMaker.TYPE;
  import jalview.util.MessageManager;
+ import jalview.util.Platform;
  import jalview.viewmodel.AlignmentViewport;
  import jalview.viewmodel.ViewportRanges;
  import jalview.ws.DBRefFetcher;
@@@ -100,59 -155,13 +155,13 @@@ import jalview.ws.jws2.Jws2Discoverer
  import jalview.ws.jws2.jabaws2.Jws2Instance;
  import jalview.ws.seqfetcher.DbSourceProxy;
  
- import java.awt.BorderLayout;
- import java.awt.Component;
- import java.awt.Rectangle;
- import java.awt.Toolkit;
- import java.awt.datatransfer.Clipboard;
- import java.awt.datatransfer.DataFlavor;
- import java.awt.datatransfer.StringSelection;
- import java.awt.datatransfer.Transferable;
- import java.awt.dnd.DnDConstants;
- import java.awt.dnd.DropTargetDragEvent;
- import java.awt.dnd.DropTargetDropEvent;
- import java.awt.dnd.DropTargetEvent;
- import java.awt.dnd.DropTargetListener;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.event.FocusAdapter;
- import java.awt.event.FocusEvent;
- import java.awt.event.ItemEvent;
- import java.awt.event.ItemListener;
- import java.awt.event.KeyAdapter;
- import java.awt.event.KeyEvent;
- import java.awt.event.MouseEvent;
- import java.awt.print.PageFormat;
- import java.awt.print.PrinterJob;
- import java.beans.PropertyChangeEvent;
- import java.io.File;
- import java.io.FileWriter;
- import java.io.PrintWriter;
- import java.net.URL;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Deque;
- import java.util.Enumeration;
- import java.util.Hashtable;
- import java.util.List;
- import java.util.Vector;
- import javax.swing.ButtonGroup;
- import javax.swing.JCheckBoxMenuItem;
- import javax.swing.JEditorPane;
- import javax.swing.JInternalFrame;
- import javax.swing.JLayeredPane;
- import javax.swing.JMenu;
- import javax.swing.JMenuItem;
- import javax.swing.JScrollPane;
- import javax.swing.SwingUtilities;
  /**
   * DOCUMENT ME!
   * 
   * @author $author$
   * @version $Revision$
   */
+ @SuppressWarnings("serial")
  public class AlignFrame extends GAlignFrame implements DropTargetListener,
          IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
  {
     */
    String fileName = null;
  
+   File fileObject;
    /**
     * Creates a new AlignFrame object with specific width and height.
     * 
     */
    void init()
    {
+ //      setBackground(Color.white); // BH 2019
+                 
      if (!Jalview.isHeadlessMode())
      {
        progressBar = new ProgressBar(this.statusPanel, this.statusBar);
      if (Desktop.desktop != null)
      {
        this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
-       addServiceListeners();
+       if (!Platform.isJS())
+       {
+         addServiceListeners();
+       }
        setGUINucleotide();
      }
  
  
      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");
    }
  
    /**
+    * JavaScript will have this, maybe others. More dependable than a file name
+    * and maintains a reference to the actual bytes loaded.
+    * 
+    * @param file
+    */
+   public void setFileObject(File file)
+   {
+     this.fileObject = file;
+   }
+   /**
     * Add a KeyListener with handlers for various KeyPressed and KeyReleased
     * events
     */
          case KeyEvent.VK_BACK_SPACE:
            if (!viewport.cursorMode)
            {
-             cut_actionPerformed(null);
+             cut_actionPerformed();
            }
            else
            {
  
          case KeyEvent.VK_F2:
            viewport.cursorMode = !viewport.cursorMode;
-           statusBar.setText(MessageManager
+           setStatus(MessageManager
                    .formatMessage("label.keyboard_editing_mode", new String[]
                    { (viewport.cursorMode ? "on" : "off") }));
            if (viewport.cursorMode)
          Desktop.instance.removeJalviewPropertyChangeListener("services",
                  thisListener);
          closeMenuItem_actionPerformed(true);
-       };
+       }
      });
      // Finally, build the menu once to get current service state
      new Thread(new Runnable()
    }
  
    @Override
-   public void fetchSequence_actionPerformed(ActionEvent e)
+   public void fetchSequence_actionPerformed()
    {
-     new jalview.gui.SequenceFetcher(this);
+     new SequenceFetcher(this);
    }
  
    @Override
          Rectangle bounds = this.getBounds();
  
          FileLoader loader = new FileLoader();
-         DataSourceType protocol = fileName.startsWith("http:")
-                 ? DataSourceType.URL
-                 : DataSourceType.FILE;
-         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
-                 protocol, currentFileFormat);
+         AlignFrame newframe = null;
+         if (fileObject == null)
+         {
+           DataSourceType protocol = (fileName.startsWith("http:")
+                   ? DataSourceType.URL
+                   : DataSourceType.FILE);
+           newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
+                   currentFileFormat);
+         }
+         else
+         {
+           newframe = loader.LoadFileWaitTillLoaded(fileObject,
+                   DataSourceType.FILE, currentFileFormat);
+         }
  
          newframe.setBounds(bounds);
          if (featureSettings != null && featureSettings.isShowing())
      if (fileName == null || (currentFileFormat == null)
              || fileName.startsWith("http"))
      {
-       saveAs_actionPerformed(null);
+       saveAs_actionPerformed();
      }
      else
      {
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Saves the alignment to a file with a name chosen by the user, if necessary
+    * warning if a file would be overwritten
     */
    @Override
-   public void saveAs_actionPerformed(ActionEvent e)
+   public void saveAs_actionPerformed()
    {
      String format = currentFileFormat == null ? null
              : currentFileFormat.getName();
  
      int value = chooser.showSaveDialog(this);
  
-     if (value == JalviewFileChooser.APPROVE_OPTION)
+     if (value != JalviewFileChooser.APPROVE_OPTION)
+     {
+       return;
+     }
+     currentFileFormat = chooser.getSelectedFormat();
+     // todo is this (2005) test now obsolete - value is never null?
+     while (currentFileFormat == null)
      {
+       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+               MessageManager
+                       .getString("label.select_file_format_before_saving"),
+               MessageManager.getString("label.file_format_not_specified"),
+               JvOptionPane.WARNING_MESSAGE);
        currentFileFormat = chooser.getSelectedFormat();
-       while (currentFileFormat == null)
+       value = chooser.showSaveDialog(this);
+       if (value != JalviewFileChooser.APPROVE_OPTION)
        {
-         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                 MessageManager.getString(
-                         "label.select_file_format_before_saving"),
-                 MessageManager.getString("label.file_format_not_specified"),
-                 JvOptionPane.WARNING_MESSAGE);
-         currentFileFormat = chooser.getSelectedFormat();
-         value = chooser.showSaveDialog(this);
-         if (value != JalviewFileChooser.APPROVE_OPTION)
-         {
-           return;
-         }
+         return;
        }
+     }
  
-       fileName = chooser.getSelectedFile().getPath();
+     fileName = chooser.getSelectedFile().getPath();
  
-       Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
+     Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
+     Cache.setProperty("LAST_DIRECTORY", fileName);
+     saveAlignment(fileName, currentFileFormat);
+   }
+   boolean lastSaveSuccessful = false;
+   FileFormatI lastFormatSaved;
+   String lastFilenameSaved;
+   /**
+    * Raise a dialog or status message for the last call to saveAlignment.
+    *
+    * @return true if last call to saveAlignment(file, format) was successful.
+    */
+   public boolean isSaveAlignmentSuccessful()
+   {
+     if (!lastSaveSuccessful)
+     {
+       JvOptionPane.showInternalMessageDialog(this, MessageManager
+               .formatMessage("label.couldnt_save_file", new Object[]
+               { lastFilenameSaved }),
+               MessageManager.getString("label.error_saving_file"),
+               JvOptionPane.WARNING_MESSAGE);
+     }
+     else
+     {
+       setStatus(MessageManager.formatMessage(
+               "label.successfully_saved_to_file_in_format", new Object[]
+               { lastFilenameSaved, lastFormatSaved }));
  
-       Cache.setProperty("LAST_DIRECTORY", fileName);
-       saveAlignment(fileName, currentFileFormat);
      }
+     return lastSaveSuccessful;
    }
  
-   public boolean saveAlignment(String file, FileFormatI format)
+   /**
+    * Saves the alignment to the specified file path, in the specified format,
+    * which may be an alignment format, or Jalview project format. If the
+    * alignment has hidden regions, or the format is one capable of including
+    * non-sequence data (features, annotations, groups), then the user may be
+    * prompted to specify what to include in the output.
+    * 
+    * @param file
+    * @param format
+    */
+   public void saveAlignment(String file, FileFormatI format)
    {
-     boolean success = true;
+     lastSaveSuccessful = true;
+     lastFilenameSaved = file;
+     lastFormatSaved = format;
  
      if (FileFormat.Jalview.equals(format))
      {
        String shortName = title;
-       if (shortName.indexOf(java.io.File.separatorChar) > -1)
+       if (shortName.indexOf(File.separatorChar) > -1)
        {
          shortName = shortName.substring(
-                 shortName.lastIndexOf(java.io.File.separatorChar) + 1);
+                 shortName.lastIndexOf(File.separatorChar) + 1);
        }
-       success = new jalview.project.Jalview2XML().saveAlignment(this, file,
-               shortName);
+       lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file, shortName);
+       
        statusBar.setText(MessageManager.formatMessage(
                "label.successfully_saved_to_file_in_format", new Object[]
                { fileName, format }));
+       
+       return;
      }
-     else
+     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
+     Runnable cancelAction = new Runnable()
      {
-       AlignmentExportData exportData = getAlignmentForExport(format,
-               viewport, null);
-       if (exportData.getSettings().isCancelled())
-       {
-         return false;
-       }
-       FormatAdapter f = new FormatAdapter(alignPanel,
-               exportData.getSettings());
-       String output = f.formatSequences(format, exportData.getAlignment(), // class
-                                                                            // cast
-                                                                            // exceptions
-                                                                            // will
-               // occur in the distant future
-               exportData.getOmitHidden(), exportData.getStartEndPostions(),
-               f.getCacheSuffixDefault(format),
-               viewport.getAlignment().getHiddenColumns());
-       if (output == null)
+       @Override
+       public void run()
        {
-         success = false;
+         lastSaveSuccessful = false;
        }
-       else
+     };
+     Runnable outputAction = new Runnable()
+     {
+       @Override
+       public void run()
        {
-         // create backupfiles object and get new temp filename destination
-         BackupFiles backupfiles = new BackupFiles(file);
-         try
+         // todo defer this to inside formatSequences (or later)
+         AlignmentExportData exportData = viewport
+                 .getAlignExportData(options);
+         String output = new FormatAdapter(alignPanel, options)
+                 .formatSequences(format, exportData.getAlignment(),
+                         exportData.getOmitHidden(),
+                         exportData.getStartEndPostions(),
+                         viewport.getAlignment().getHiddenColumns());
+         if (output == null)
+         {
+           lastSaveSuccessful = false;
+         }
+         else
          {
-           PrintWriter out = new PrintWriter(
-                   new FileWriter(backupfiles.getTempFilePath()));
+           // create backupfiles object and get new temp filename destination
+           boolean doBackup = BackupFiles.getEnabled();
+           BackupFiles backupfiles = doBackup ? new BackupFiles(file) : null;
+           try
+           {
+             String tempFilePath = doBackup ? backupfiles.getTempFilePath() : file;
+                       PrintWriter out = new PrintWriter(
+                     new FileWriter(tempFilePath));
  
-           out.print(output);
-           out.close();
-           this.setTitle(file);
-           statusBar.setText(MessageManager.formatMessage(
+             out.print(output);
+             out.close();
+             AlignFrame.this.setTitle(file);
+             statusBar.setText(MessageManager.formatMessage(
                    "label.successfully_saved_to_file_in_format", new Object[]
                    { fileName, format.getName() }));
-         } catch (Exception ex)
-         {
-           success = false;
-           ex.printStackTrace();
-         }
-         backupfiles.setWriteSuccess(success);
-         // do the backup file roll and rename the temp file to actual file
-         success = backupfiles.rollBackupsAndRenameTempFile();
+             lastSaveSuccessful = true;
+           } catch (Exception ex)
+           {
+             lastSaveSuccessful = false;
+             ex.printStackTrace();
+           }
  
+           if (doBackup)
+           {
+             backupfiles.setWriteSuccess(lastSaveSuccessful);
+             // do the backup file roll and rename the temp file to actual file
+             lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
+           }
+         }
        }
-     }
+     };
  
-     if (!success)
-     {
-       JvOptionPane.showInternalMessageDialog(this, MessageManager
-               .formatMessage("label.couldnt_save_file", new Object[]
-               { fileName }),
-               MessageManager.getString("label.error_saving_file"),
-               JvOptionPane.WARNING_MESSAGE);
-     }
-     return success;
-   }
-   private void warningMessage(String warning, String title)
-   {
-     if (new jalview.util.Platform().isHeadless())
+     /*
+      * show dialog with export options if applicable; else just do it
+      */
+     if (AlignExportOptions.isNeeded(viewport, format))
      {
-       System.err.println("Warning: " + title + "\nWarning: " + warning);
+       AlignExportOptions choices = new AlignExportOptions(
+               alignPanel.getAlignViewport(), format, options);
+       choices.setResponseAction(0, outputAction);
+       choices.setResponseAction(1, cancelAction);
+       choices.showDialog();
      }
      else
      {
-       JvOptionPane.showInternalMessageDialog(this, warning, title,
-               JvOptionPane.WARNING_MESSAGE);
+       outputAction.run();
      }
    }
  
    /**
-    * DOCUMENT ME!
+    * Outputs the alignment to textbox in the requested format, if necessary
+    * first prompting the user for whether to include hidden regions or
+    * non-sequence data
     * 
-    * @param e
-    *          DOCUMENT ME!
+    * @param fileFormatName
     */
    @Override
-   protected void outputText_actionPerformed(ActionEvent e)
+   protected void outputText_actionPerformed(String fileFormatName)
    {
      FileFormatI fileFormat = FileFormats.getInstance()
-             .forName(e.getActionCommand());
-     AlignmentExportData exportData = getAlignmentForExport(fileFormat,
-             viewport, null);
-     if (exportData.getSettings().isCancelled())
-     {
-       return;
-     }
-     CutAndPasteTransfer cap = new CutAndPasteTransfer();
-     cap.setForInput(null);
-     try
-     {
-       FileFormatI format = fileFormat;
-       cap.setText(new FormatAdapter(alignPanel, exportData.getSettings())
-               .formatSequences(format, exportData.getAlignment(),
-                       exportData.getOmitHidden(),
-                       exportData.getStartEndPostions(),
-                       viewport.getAlignment().getHiddenColumns()));
-       Desktop.addInternalFrame(cap, MessageManager
-               .formatMessage("label.alignment_output_command", new Object[]
-               { e.getActionCommand() }), 600, 500);
-     } catch (OutOfMemoryError oom)
+             .forName(fileFormatName);
+     AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
+     Runnable outputAction = new Runnable()
      {
-       new OOMWarning("Outputting alignment as " + e.getActionCommand(),
-               oom);
-       cap.dispose();
-     }
-   }
-   public static AlignmentExportData getAlignmentForExport(
-           FileFormatI format, AlignViewportI viewport,
-           AlignExportSettingI exportSettings)
-   {
-     AlignmentI alignmentToExport = null;
-     AlignExportSettingI settings = exportSettings;
-     String[] omitHidden = null;
-     HiddenSequences hiddenSeqs = viewport.getAlignment()
-             .getHiddenSequences();
-     alignmentToExport = viewport.getAlignment();
-     boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
-     if (settings == null)
-     {
-       settings = new AlignExportSettings(hasHiddenSeqs,
-               viewport.hasHiddenColumns(), format);
-     }
-     // settings.isExportAnnotations();
-     if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns())
-     {
-       omitHidden = viewport.getViewAsString(false,
-               settings.isExportHiddenSequences());
-     }
+       @Override
+       public void run()
+       {
+         // todo defer this to inside formatSequences (or later)
+         AlignmentExportData exportData = viewport
+                 .getAlignExportData(options);
+         CutAndPasteTransfer cap = new CutAndPasteTransfer();
+         cap.setForInput(null);
+         try
+         {
+           FileFormatI format = fileFormat;
+           cap.setText(new FormatAdapter(alignPanel, options)
+                   .formatSequences(format, exportData.getAlignment(),
+                           exportData.getOmitHidden(),
+                           exportData.getStartEndPostions(),
+                           viewport.getAlignment().getHiddenColumns()));
+           Desktop.addInternalFrame(cap, MessageManager.formatMessage(
+                   "label.alignment_output_command", new Object[]
+                   { fileFormat.getName() }), 600, 500);
+         } catch (OutOfMemoryError oom)
+         {
+           new OOMWarning("Outputting alignment as " + fileFormat.getName(),
+                   oom);
+           cap.dispose();
+         }
+       }
+     };
  
-     int[] alignmentStartEnd = new int[2];
-     if (hasHiddenSeqs && settings.isExportHiddenSequences())
+     /*
+      * show dialog with export options if applicable; else just do it
+      */
+     if (AlignExportOptions.isNeeded(viewport, fileFormat))
      {
-       alignmentToExport = hiddenSeqs.getFullAlignment();
+       AlignExportOptions choices = new AlignExportOptions(
+               alignPanel.getAlignViewport(), fileFormat, options);
+       choices.setResponseAction(0, outputAction);
+       choices.showDialog();
      }
      else
      {
-       alignmentToExport = viewport.getAlignment();
+       outputAction.run();
      }
-     alignmentStartEnd = viewport.getAlignment().getHiddenColumns()
-             .getVisibleStartAndEndIndex(alignmentToExport.getWidth());
-     AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
-             omitHidden, alignmentStartEnd, settings);
-     return ed;
    }
  
    /**
    }
  
    /**
-    * DOCUMENT ME!
+    * Creates a PNG image of the alignment and writes it to the given file. If
+    * the file is null, the user is prompted to choose a file.
     * 
-    * @param e
-    *          DOCUMENT ME!
+    * @param f
     */
    @Override
    public void createPNG(File f)
    {
-     alignPanel.makePNG(f);
+     alignPanel.makeAlignmentImage(TYPE.PNG, f);
    }
  
    /**
-    * DOCUMENT ME!
+    * Creates an EPS image of the alignment and writes it to the given file. If
+    * the file is null, the user is prompted to choose a file.
     * 
-    * @param e
-    *          DOCUMENT ME!
+    * @param f
     */
    @Override
    public void createEPS(File f)
    {
-     alignPanel.makeEPS(f);
+     alignPanel.makeAlignmentImage(TYPE.EPS, f);
    }
  
+   /**
+    * Creates an SVG image of the alignment and writes it to the given file. If
+    * the file is null, the user is prompted to choose a file.
+    * 
+    * @param f
+    */
    @Override
    public void createSVG(File f)
    {
-     alignPanel.makeSVG(f);
+     alignPanel.makeAlignmentImage(TYPE.SVG, f);
    }
  
    @Override
    @Override
    public void associatedData_actionPerformed(ActionEvent e)
    {
-     // Pick the tree file
-     JalviewFileChooser chooser = new JalviewFileChooser(
+     final JalviewFileChooser chooser = new JalviewFileChooser(
              jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
      chooser.setFileView(new JalviewFileView());
-     chooser.setDialogTitle(
-             MessageManager.getString("label.load_jalview_annotations"));
-     chooser.setToolTipText(
-             MessageManager.getString("label.load_jalview_annotations"));
-     int value = chooser.showOpenDialog(null);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
+     String tooltip = MessageManager.getString("label.load_jalview_annotations");
+     chooser.setDialogTitle(tooltip);
+     chooser.setToolTipText(tooltip);
+     chooser.setResponseHandler(0, new Runnable()
      {
-       String choice = chooser.getSelectedFile().getPath();
-       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
-       loadJalviewDataFile(choice, null, null, null);
-     }
+       @Override
+       public void run()
+       {
+         String choice = chooser.getSelectedFile().getPath();
+         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
+         loadJalviewDataFile(chooser.getSelectedFile(), null, null, null);
+       }
+     });
  
+     chooser.showOpenDialog(this);
    }
  
    /**
     *          DOCUMENT ME!
     */
    @Override
-   protected void copy_actionPerformed(ActionEvent e)
+   protected void copy_actionPerformed()
    {
      if (viewport.getSelectionGroup() == null)
      {
  
      Desktop.jalviewClipboard = new Object[] { seqs,
          viewport.getAlignment().getDataset(), hiddenColumns };
-     statusBar.setText(MessageManager.formatMessage(
+     setStatus(MessageManager.formatMessage(
              "label.copied_sequences_to_clipboard", new Object[]
              { Integer.valueOf(seqs.length).toString() }));
    }
                  && Desktop.jalviewClipboard[1] != alignment.getDataset();
          // importDs==true instructs us to copy over new dataset sequences from
          // an existing alignment
-         Vector newDs = (importDs) ? new Vector() : null; // used to create
+         Vector<SequenceI> newDs = (importDs) ? new Vector<>() : null; // used to
+                                                                       // create
          // minimum dataset set
  
          for (int i = 0; i < sequences.length; i++)
        {
  
          // propagate alignment changed.
-         viewport.getRanges().setEndSeq(alignment.getHeight());
+         viewport.getRanges().setEndSeq(alignment.getHeight() - 1);
          if (annotationAdded)
          {
            // Duplicate sequence annotation in all views.
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Action Cut (delete and copy) the selected region
     */
    @Override
-   protected void cut_actionPerformed(ActionEvent e)
+   protected void cut_actionPerformed()
    {
-     copy_actionPerformed(null);
-     delete_actionPerformed(null);
+     copy_actionPerformed();
+     delete_actionPerformed();
    }
  
    /**
-    * DOCUMENT ME!
-    * 
-    * @param e
-    *          DOCUMENT ME!
+    * Performs menu option to Delete the currently selected region
     */
    @Override
-   protected void delete_actionPerformed(ActionEvent evt)
+   protected void delete_actionPerformed()
    {
  
      SequenceGroup sg = viewport.getSelectionGroup();
        return;
      }
  
+     Runnable okAction = new Runnable() 
+     {
+               @Override
+               public void run() 
+               {
+                   SequenceI[] cut = sg.getSequences()
+                           .toArray(new SequenceI[sg.getSize()]);
+                   addHistoryItem(new EditCommand(
+                           MessageManager.getString("label.cut_sequences"), Action.CUT,
+                           cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
+                           viewport.getAlignment()));
+                   viewport.setSelectionGroup(null);
+                   viewport.sendSelection();
+                   viewport.getAlignment().deleteGroup(sg);
+                   viewport.firePropertyChange("alignment", null,
+                           viewport.getAlignment().getSequences());
+                   if (viewport.getAlignment().getHeight() < 1)
+                   {
+                     try
+                     {
+                       AlignFrame.this.setClosed(true);
+                     } catch (Exception ex)
+                     {
+                     }
+                   }
+               }};
      /*
-      * If the cut affects all sequences, warn, remove highlighted columns
+      * If the cut affects all sequences, prompt for confirmation
       */
-     if (sg.getSize() == viewport.getAlignment().getHeight())
-     {
-       boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes())
-               + 1) == viewport.getAlignment().getWidth()) ? true : false;
-       if (isEntireAlignWidth)
-       {
-         int confirm = JvOptionPane.showConfirmDialog(this,
-                 MessageManager.getString("warn.delete_all"), // $NON-NLS-1$
-                 MessageManager.getString("label.delete_all"), // $NON-NLS-1$
-                 JvOptionPane.OK_CANCEL_OPTION);
-         if (confirm == JvOptionPane.CANCEL_OPTION
-                 || confirm == JvOptionPane.CLOSED_OPTION)
-         {
-           return;
-         }
-       }
-       viewport.getColumnSelection().removeElements(sg.getStartRes(),
-               sg.getEndRes() + 1);
-     }
-     SequenceI[] cut = sg.getSequences()
-             .toArray(new SequenceI[sg.getSize()]);
-     addHistoryItem(new EditCommand(
-             MessageManager.getString("label.cut_sequences"), Action.CUT,
-             cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
-             viewport.getAlignment()));
-     viewport.setSelectionGroup(null);
-     viewport.sendSelection();
-     viewport.getAlignment().deleteGroup(sg);
-     viewport.firePropertyChange("alignment", null,
-             viewport.getAlignment().getSequences());
-     if (viewport.getAlignment().getHeight() < 1)
-     {
-       try
-       {
-         this.setClosed(true);
-       } catch (Exception ex)
-       {
-       }
-     }
+     boolean wholeHeight = sg.getSize() == viewport.getAlignment().getHeight();
+     boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes())
+             + 1) == viewport.getAlignment().getWidth()) ? true : false;
+       if (wholeHeight && wholeWidth)
+       {
+           JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
+               dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
+           Object[] options = new Object[] { MessageManager.getString("action.ok"),
+                   MessageManager.getString("action.cancel") };
+               dialog.showDialog(MessageManager.getString("warn.delete_all"),
+                   MessageManager.getString("label.delete_all"),
+                   JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
+                   options, options[0]);
+       } else 
+       {
+               okAction.run();
+       }
    }
  
    /**
                  column, viewport.getAlignment());
        }
  
-       statusBar.setText(MessageManager
+       setStatus(MessageManager
                .formatMessage("label.removed_columns", new String[]
                { Integer.valueOf(trimRegion.getSize()).toString() }));
  
  
      addHistoryItem(removeGapCols);
  
-     statusBar.setText(MessageManager
+     setStatus(MessageManager
              .formatMessage("label.removed_empty_columns", new Object[]
              { Integer.valueOf(removeGapCols.getSize()).toString() }));
  
     * @param toggleSeqs
     * @param toggleCols
     */
-   private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
+   protected void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
    {
  
      boolean hide = false;
    @Override
    public void alignmentProperties()
    {
-     JEditorPane editPane = new JEditorPane("text/html", "");
-     editPane.setEditable(false);
+     JComponent pane;
      StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
              .formatAsHtml();
-     editPane.setText(
-             MessageManager.formatMessage("label.html_content", new Object[]
-             { contents.toString() }));
+     String content = MessageManager.formatMessage("label.html_content",
+             new Object[]
+             { contents.toString() });
+     contents = null;
+     if (Platform.isJS())
+     {
+       JLabel textLabel = new JLabel();
+       textLabel.setText(content);
+       textLabel.setBackground(Color.WHITE);
+       
+       pane = new JPanel(new BorderLayout());
+       ((JPanel) pane).setOpaque(true);
+       pane.setBackground(Color.WHITE);
+       ((JPanel) pane).add(textLabel, BorderLayout.NORTH);
+     }
+     else
+     /**
+      * Java only
+      * 
+      * @j2sIgnore
+      */
+     {
+       JEditorPane editPane = new JEditorPane("text/html", "");
+       editPane.setEditable(false);
+       editPane.setText(content);
+       pane = editPane;
+     }
      JInternalFrame frame = new JInternalFrame();
-     frame.getContentPane().add(new JScrollPane(editPane));
+     frame.getContentPane().add(new JScrollPane(pane));
  
      Desktop.addInternalFrame(frame, MessageManager
              .formatMessage("label.alignment_properties", new Object[]
                {
                  overview.dispose();
                  alignPanel.setOverviewPanel(null);
-               };
+               }
              });
      if (getKeyListeners().length > 0)
      {
      {
        sortByAnnotScore.removeAll();
        // almost certainly a quicker way to do this - but we keep it simple
-       Hashtable scoreSorts = new Hashtable();
+       Hashtable<String, String> scoreSorts = new Hashtable<>();
        AlignmentAnnotation aann[];
        for (SequenceI sqa : viewport.getAlignment().getSequences())
        {
            }
          }
        }
-       Enumeration labels = scoreSorts.keys();
+       Enumeration<String> labels = scoreSorts.keys();
        while (labels.hasMoreElements())
        {
          addSortByAnnotScoreMenuItem(sortByAnnotScore,
-                 (String) labels.nextElement());
+                 labels.nextElement());
        }
        sortByAnnotScore.setVisible(scoreSorts.size() > 0);
        scoreSorts.clear();
      chooser.setToolTipText(
              MessageManager.getString("label.load_tree_file"));
  
-     int value = chooser.showOpenDialog(null);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
+     chooser.setResponseHandler(0,new Runnable()
      {
-       String filePath = chooser.getSelectedFile().getPath();
-       Cache.setProperty("LAST_DIRECTORY", filePath);
-       NewickFile fin = null;
-       try
-       {
-         fin = new NewickFile(filePath, DataSourceType.FILE);
-         viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
-       } catch (Exception ex)
-       {
-         JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
-                 MessageManager.getString("label.problem_reading_tree_file"),
-                 JvOptionPane.WARNING_MESSAGE);
-         ex.printStackTrace();
-       }
-       if (fin != null && fin.hasWarningMessage())
+       @Override
+       public void run()
        {
-         JvOptionPane.showMessageDialog(Desktop.desktop,
-                 fin.getWarningMessage(),
-                 MessageManager
-                         .getString("label.possible_problem_with_tree_file"),
-                 JvOptionPane.WARNING_MESSAGE);
+         String filePath = chooser.getSelectedFile().getPath();
+         Cache.setProperty("LAST_DIRECTORY", filePath);
+         NewickFile fin = null;
+         try
+         {
+           fin = new NewickFile(new FileParse(chooser.getSelectedFile(),
+                   DataSourceType.FILE));
+           viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
+         } catch (Exception ex)
+         {
+           JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
+                   MessageManager
+                           .getString("label.problem_reading_tree_file"),
+                   JvOptionPane.WARNING_MESSAGE);
+           ex.printStackTrace();
+         }
+         if (fin != null && fin.hasWarningMessage())
+         {
+           JvOptionPane.showMessageDialog(Desktop.desktop,
+                   fin.getWarningMessage(),
+                   MessageManager.getString(
+                           "label.possible_problem_with_tree_file"),
+                   JvOptionPane.WARNING_MESSAGE);
+         }
        }
-     }
+     });
+     chooser.showOpenDialog(this);
    }
  
    public TreePanel showNewickTree(NewickFile nf, String treeTitle)
              // No MSAWS used any more:
              // Vector msaws = null; // (Vector)
              // Discoverer.services.get("MsaWS");
-             Vector secstrpr = (Vector) Discoverer.services
+             Vector<ServiceHandle> secstrpr = Discoverer.services
                      .get("SecStrPred");
              if (secstrpr != null)
              {
                // Add any secondary structure prediction services
                for (int i = 0, j = secstrpr.size(); i < j; i++)
                {
-                 final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
+                 final ext.vamsas.ServiceHandle sh = secstrpr
                          .get(i);
                  jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
                          .getServiceClient(sh);
     * 
     * @param webService
     */
-   private void build_urlServiceMenu(JMenu webService)
+   protected void build_urlServiceMenu(JMenu webService)
    {
      // TODO: remove this code when 2.7 is released
      // DEBUG - alignmentView
     * Try to load a features file onto the alignment.
     * 
     * @param file
-    *          contents or path to retrieve file
+    *          contents or path to retrieve file or a File object
     * @param sourceType
     *          access mode of file (see jalview.io.AlignFile)
     * @return true if features file was parsed correctly.
     */
-   public boolean parseFeaturesFile(String file, DataSourceType sourceType)
+   public boolean parseFeaturesFile(Object file, DataSourceType sourceType)
    {
+     // BH 2018
      return avc.parseFeaturesFile(file, sourceType,
              Cache.getDefault("RELAXEDSEQIDMATCHING", false));
  
      // Java's Transferable for native dnd
      evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
      Transferable t = evt.getTransferable();
      final AlignFrame thisaf = this;
-     final List<String> files = new ArrayList<>();
+     final List<Object> files = new ArrayList<>();
      List<DataSourceType> protocols = new ArrayList<>();
  
      try
               * Object[] { String,SequenceI}
               */
              ArrayList<Object[]> filesmatched = new ArrayList<>();
-             ArrayList<String> filesnotmatched = new ArrayList<>();
+             ArrayList<Object> filesnotmatched = new ArrayList<>();
              for (int i = 0; i < files.size(); i++)
              {
-               String file = files.get(i).toString();
+               // BH 2018
+               Object file = files.get(i);
+               String fileName = file.toString();
                String pdbfn = "";
-               DataSourceType protocol = FormatAdapter.checkProtocol(file);
+               DataSourceType protocol = (file instanceof File
+                       ? DataSourceType.FILE
+                       : FormatAdapter.checkProtocol(fileName));
                if (protocol == DataSourceType.FILE)
                {
-                 File fl = new File(file);
+                 File fl;
+                 if (file instanceof File) {
+                   fl = (File) file;
+                   Platform.cacheFileData(fl);
+                 } else {
+                   fl = new File(fileName);
+                 }
                  pdbfn = fl.getName();
                }
                else if (protocol == DataSourceType.URL)
                {
-                 URL url = new URL(file);
+                 URL url = new URL(fileName);
                  pdbfn = url.getFile();
                }
                if (pdbfn.length() > 0)
                  }
                  if (mtch != null)
                  {
-                   FileFormatI type = null;
+                   FileFormatI type;
                    try
                    {
                      type = new IdentifyFile().identify(file, protocol);
                    for (SequenceI toassoc : (SequenceI[]) fm[2])
                    {
                      PDBEntry pe = new AssociatePdbFileWithSeq()
-                             .associatePdbWithSeq((String) fm[0],
+                             .associatePdbWithSeq(fm[0].toString(),
                                      (DataSourceType) fm[1], toassoc, false,
                                      Desktop.instance);
                      if (pe != null)
                      {
                        System.err.println("Associated file : "
-                               + ((String) fm[0]) + " with "
+                               + (fm[0].toString()) + " with "
                                + toassoc.getDisplayId(true));
                        assocfiles++;
                      }
                   */
                  for (Object[] o : filesmatched)
                  {
-                   filesnotmatched.add((String) o[0]);
+                   filesnotmatched.add(o[0]);
                  }
                }
              }
                {
                  return;
                }
-               for (String fn : filesnotmatched)
+               for (Object fn : filesnotmatched)
                {
                  loadJalviewDataFile(fn, null, null, null);
                }
     * @param file
     *          either a filename or a URL string.
     */
-   public void loadJalviewDataFile(String file, DataSourceType sourceType,
+   public void loadJalviewDataFile(Object file, DataSourceType sourceType,
            FileFormatI format, SequenceI assocSeq)
    {
+     // BH 2018 was String file
      try
      {
        if (sourceType == null)
                changeColour(
                        new TCoffeeColourScheme(viewport.getAlignment()));
                isAnnotation = true;
-               statusBar.setText(MessageManager.getString(
+               setStatus(MessageManager.getString(
                        "label.successfully_pasted_tcoffee_scores_to_alignment"));
              }
              else
                      new FileParse(file, sourceType));
              sm.parse();
              // todo: i18n this message
-             statusBar.setText(MessageManager.formatMessage(
+             setStatus(MessageManager.formatMessage(
                      "label.successfully_loaded_matrix",
                      sm.getMatrixName()));
            }
      if (e.isPopupTrigger())
      {
        String msg = MessageManager.getString("label.enter_view_name");
-       String reply = JvOptionPane.showInternalInputDialog(this, msg, msg,
-               JvOptionPane.QUESTION_MESSAGE);
+       String ttl = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());
+       String reply = JvOptionPane.showInputDialog(msg, ttl);
  
        if (reply != null)
        {
          trimrs.setSelected(trimrs.isSelected());
          Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
                  Boolean.valueOf(trimrs.isSelected()).toString());
-       };
+       }
      });
      rfetch.add(trimrs);
      JMenuItem fetchr = new JMenuItem(
  
      });
      rfetch.add(fetchr);
-     final AlignFrame me = this;
      new Thread(new Runnable()
      {
        @Override
        public void run()
        {
          final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
-                 .getSequenceFetcherSingleton(me);
+                 .getSequenceFetcherSingleton();
          javax.swing.SwingUtilities.invokeLater(new Runnable()
          {
            @Override
            public void run()
            {
-             String[] dbclasses = sf.getOrderedSupportedSources();
-             // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
-             // jalview.util.QuickSort.sort(otherdb, otherdb);
+             String[] dbclasses = sf.getNonAlignmentSources();
              List<DbSourceProxy> otherdb;
              JMenu dfetch = new JMenu();
              JMenu ifetch = new JMenu();
                {
                  continue;
                }
-               // List<DbSourceProxy> dbs=otherdb;
-               // otherdb=new ArrayList<DbSourceProxy>();
-               // for (DbSourceProxy db:dbs)
-               // {
-               // if (!db.isA(DBRefSource.ALIGNMENTDB)
-               // }
                if (mname == null)
                {
                  mname = "From " + dbclass;
      chooser.setFileView(new JalviewFileView());
      chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file"));
      chooser.setToolTipText(MessageManager.getString("label.load_vcf_file"));
-     int value = chooser.showOpenDialog(null);
-     if (value == JalviewFileChooser.APPROVE_OPTION)
+     final AlignFrame us = this;
+     chooser.setResponseHandler(0, new Runnable()
      {
-       String choice = chooser.getSelectedFile().getPath();
-       Cache.setProperty("LAST_DIRECTORY", choice);
-       SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
-       new VCFLoader(choice).loadVCF(seqs, this);
-     }
+       @Override
+       public void run()
+       {
+         String choice = chooser.getSelectedFile().getPath();
+         Cache.setProperty("LAST_DIRECTORY", choice);
+         SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
+         new VCFLoader(choice).loadVCF(seqs, us);
+       }
+     });
+     chooser.showOpenDialog(null);
  
    }
  
   */
  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.io.File;
  import java.util.ArrayList;
  import java.util.List;
 -import java.util.Vector;
  
 -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.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
              .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;
        } catch (Exception ex)
        {
          Cache.log.error("Couldn't open Jmol viewer!", ex);
+         ex.printStackTrace();
+         return;
        }
      }
      else
        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!",
                  oomerror);
          Cache.log.debug("File locations are " + filesString);
+         return;
        } catch (Exception ex)
        {
          Cache.log.error("Couldn't add files to Jmol viewer!", ex);
+         ex.printStackTrace();
+         return;
        }
      }
  
      }
  
      // 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)
            {
      return files;
    }
  
+   /**
+    * Outputs the Jmol viewer image as an image file, after prompting the user to
+    * choose a file and (for EPS) choice of Text or Lineart character rendering
+    * (unless a preference for this is set)
+    * 
+    * @param type
+    */
 +  @Override
-   public void eps_actionPerformed()
-   {
-     makePDBImage(ImageMaker.TYPE.EPS);
-   }
-   @Override
-   public void png_actionPerformed()
-   {
-     makePDBImage(ImageMaker.TYPE.PNG);
-   }
-   void makePDBImage(ImageMaker.TYPE type)
+   public void makePDBImage(ImageMaker.TYPE type)
    {
      int width = getWidth();
      int height = getHeight();
-     ImageMaker im;
-     if (type == ImageMaker.TYPE.PNG)
+     ImageWriterI writer = new ImageWriterI()
      {
-       im = new ImageMaker(this, ImageMaker.TYPE.PNG,
-               "Make PNG image from view",
-               width, height, null, null, null, 0, false);
-     }
-     else if (type == ImageMaker.TYPE.EPS)
-     {
-       im = new ImageMaker(this, ImageMaker.TYPE.EPS,
-               "Make EPS file from view",
-               width, height, null, this.getTitle(), null, 0, false);
-     }
-     else
-     {
-       im = new jalview.util.ImageMaker(this,
-               ImageMaker.TYPE.SVG, "Make SVG file from PCA",
-               width, height, null, this.getTitle(), null, 0, false);
-     }
-     if (im.getGraphics() != null)
-     {
-       jmb.jmolViewer.renderScreenImage(im.getGraphics(), width, height);
-       im.writeImage();
-     }
+       @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
      {
-       BrowserLauncher
-               .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
+       BrowserLauncher // BH 2018
+               .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.io.FileWriter;
- import java.io.IOException;
++import java.util.List;
 +import java.util.Map;
 +
 +import javax.swing.JComponent;
 +
 +import org.jmol.api.JmolAppConsoleInterface;
- import org.jmol.java.BS;
 +import org.openscience.jmol.app.jmolpanel.console.AppConsole;
 +
  import jalview.api.AlignmentViewPanel;
  import jalview.api.structures.JalviewStructureDisplayI;
  import jalview.bin.Cache;
@@@ -40,15 -28,33 +38,17 @@@ import jalview.datamodel.SequenceI
  import jalview.ext.jmol.JalviewJmolBinding;
  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 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);
-     return new AppConsole(jmolViewer, consolePanel, buttonsToShow);
++    // 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;
    }
  
-   /**
-    * Overrides the default method to save a session to file, in order to
-    * guarantee it is done synchronously. Jmol command 'write STATE path' would
-    * execute asynchronously, so instead we get the state and write it directly
-    * here.
-    */
--  @Override
-   protected void saveSession(File f)
 -  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)
    {
-     String state = jmolViewer.getStateInfo();
-     if (state != null)
+     if (files == null)
      {
-       try
-       {
-         FileWriter fw = new FileWriter(f);
-         fw.write(state);
-         fw.close();
-       } catch (IOException e)
-       {
-         Cache.log.error("Error writing Jmol state: " + e.toString());
-       }
+       return;
      }
-     else
+     for (File f : files)
      {
-       Cache.log.error("Error requesting Jmol state to save");
+       Platform.cacheFileData(f);
      }
    }
  }
   */
  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.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
 -import java.util.Random;
  
 -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.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;
 +
  /**
   * GUI elements for handling an external chimera display
   * 
@@@ -60,6 -67,8 +61,6 @@@ public class ChimeraViewFrame extends S
  {
    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
@@@ -67,6 -76,8 +68,6 @@@
     */
    private String chimeraSessionFile = null;
  
 -  private Random random = new Random();
 -
    private int myWidth = 500;
  
    private int myHeight = 150;
  
      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);
     */
    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>
 +   * The syntax is: setattr r &lt;attName&gt; &lt;attValue&gt; &lt;atomSpec&gt;
     * <p>
 -   * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A
 +   * For example: setattr r jv_chain "Ferredoxin-1, Chloroplastic" #0:94.A
     */
    protected void sendFeaturesToChimera()
    {
     */
    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
      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 eps_actionPerformed()
+   public void makePDBImage(TYPE imageType)
    {
-     throw new Error(MessageManager
-             .getString("error.eps_generation_not_implemented"));
-   }
-   @Override
-   public void png_actionPerformed()
-   {
-     throw new Error(MessageManager
-             .getString("error.png_generation_not_implemented"));
+     throw new UnsupportedOperationException(
+             "Image export for Chimera is not implemented");
    }
  
    @Override
 -  public void showHelp_actionPerformed(ActionEvent actionEvent)
 +  public void showHelp_actionPerformed()
    {
      try
      {
 -      BrowserLauncher
 -              .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide");
 +      String url = jmb.getHelpURL();
 +      BrowserLauncher.openURL(url);
      } catch (IOException ex)
      {
 +      System.err
 +              .println("Show Chimera help failed with: " + ex.getMessage());
      }
    }
  
      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;
 -  }
  }
   */
  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;
@@@ -33,7 -56,7 +33,7 @@@ import java.util.ArrayList
  import java.util.List;
  
  import javax.help.HelpSetException;
- import javax.swing.JColorChooser;
+ import javax.swing.JComboBox;
  import javax.swing.JFileChooser;
  import javax.swing.JInternalFrame;
  import javax.swing.JPanel;
@@@ -51,29 -74,6 +51,29 @@@ import javax.swing.table.TableModel
  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!
@@@ -105,10 -105,6 +105,10 @@@ public class Preferences extends GPrefe
  
    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";
      super();
      frame = new JInternalFrame();
      frame.setContentPane(this);
-     wsPrefs = new WsPreferences();
-     wsTab.add(wsPrefs, BorderLayout.CENTER);
+     if (!Platform.isJS())
+     /**
+      * Java only
+      * 
+      * @j2sIgnore
+      */
+     {
+       wsPrefs = new WsPreferences();
+       wsTab.add(wsPrefs, BorderLayout.CENTER);
+     }
      int width = 500, height = 450;
-     new jalview.util.Platform();
-     if (Platform.isAMac())
+     if (Platform.isAMacAndNotJS())
      {
        width = 570;
        height = 480;
              .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());
 +        }
        }
      });
  
              new RowSorter.SortKey(m.getNameColumn(), SortOrder.ASCENDING));
  
      sorter.setSortKeys(sortKeys);
-     sorter.sort();
+     // BH 2018 setSortKeys will do the sort
+     // sorter.sort();
  
      // set up filtering
      ActionListener onReset;
      doReset.addActionListener(onReset);
  
      // filter to display only custom urls
 -    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
 +    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<>()
      {
        @Override
        public boolean include(
      /*
       * Set Output tab defaults
       */
-     epsRendering.addItem(promptEachTimeOpt);
-     epsRendering.addItem(lineArtOpt);
-     epsRendering.addItem(textOpt);
-     String defaultEPS = Cache.getDefault("EPS_RENDERING",
-             "Prompt each time");
-     if (defaultEPS.equalsIgnoreCase("Text"))
-     {
-       epsRendering.setSelectedItem(textOpt);
-     }
-     else if (defaultEPS.equalsIgnoreCase("Lineart"))
-     {
-       epsRendering.setSelectedItem(lineArtOpt);
-     }
-     else
-     {
-       epsRendering.setSelectedItem(promptEachTimeOpt);
-     }
+     setupOutputCombo(epsRendering, "EPS_RENDERING");
+     setupOutputCombo(htmlRendering, "HTML_RENDERING");
+     setupOutputCombo(svgRendering, "SVG_RENDERING");
      autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
      userIdWidth.setEnabled(!autoIdWidth.isSelected());
      userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
    }
  
    /**
+    * A helper method that sets the items and initial selection in a character
+    * rendering option list (Prompt each time/Lineart/Text)
+    * 
+    * @param comboBox
+    * @param propertyKey
+    */
+   protected void setupOutputCombo(JComboBox<Object> comboBox,
+           String propertyKey)
+   {
+     comboBox.addItem(promptEachTimeOpt);
+     comboBox.addItem(lineArtOpt);
+     comboBox.addItem(textOpt);
+     
+     /*
+      * JalviewJS doesn't support Lineart so force it to Text
+      */
+     String defaultOption = Platform.isJS() ? "Text"
+             : Cache.getDefault(propertyKey, "Prompt each time");
+     if (defaultOption.equalsIgnoreCase("Text"))
+     {
+       comboBox.setSelectedItem(textOpt);
+     }
+     else if (defaultOption.equalsIgnoreCase("Lineart"))
+     {
+       comboBox.setSelectedItem(lineArtOpt);
+     }
+     else
+     {
+       comboBox.setSelectedItem(promptEachTimeOpt);
+     }
+   }
+   /**
     * Save user selections on the Preferences tabs to the Cache and write out to
     * file.
     * 
              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());
       */
      Cache.applicationProperties.setProperty("EPS_RENDERING",
              ((OptionsParam) epsRendering.getSelectedItem()).getCode());
+     Cache.applicationProperties.setProperty("HTML_RENDERING",
+             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
+     Cache.applicationProperties.setProperty("SVG_RENDERING",
+             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
  
      /*
       * Save Connections settings
      Cache.applicationProperties.setProperty("PAD_GAPS",
              Boolean.toString(padGaps.isSelected()));
  
-     wsPrefs.updateAndRefreshWsMenuConfig(false);
+     if (!Platform.isJS())
+     {
+       wsPrefs.updateAndRefreshWsMenuConfig(false);
+     }
  
      /*
       * Save Backups settings
    @Override
    protected boolean validateStructure()
    {
 -    return validateChimeraPath();
 +    return validateViewerPath();
  
    }
  
    @Override
    public void startupFileTextfield_mouseClicked()
    {
+     // TODO: JAL-3048 not needed for Jalview-JS
      String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
      JalviewFileChooser chooser = JalviewFileChooser
              .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
    {
      try
      {
-       wsPrefs.updateWsMenuConfig(true);
-       wsPrefs.refreshWs_actionPerformed(e);
+       if (!Platform.isJS())
+       {
+         wsPrefs.updateWsMenuConfig(true);
+         wsPrefs.refreshWs_actionPerformed(e);
+       }
        frame.setClosed(true);
      } catch (Exception ex)
      {
    @Override
    public void defaultBrowser_mouseClicked(MouseEvent e)
    {
-     JFileChooser chooser = new JFileChooser(".");
-     chooser.setDialogTitle(
-             MessageManager.getString("label.select_default_browser"));
+     // TODO: JAL-3048 not needed for j2s
+     if (!Platform.isJS()) // BH 2019
+     /**
+      * Java only
+      * 
+      * @j2sIgnore
+      */
+     {
+       JFileChooser chooser = new JFileChooser(".");
+       chooser.setDialogTitle(
+               MessageManager.getString("label.select_default_browser"));
  
-     int value = chooser.showOpenDialog(this);
+       int value = chooser.showOpenDialog(this);
  
-     if (value == JFileChooser.APPROVE_OPTION)
-     {
-       defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
+       if (value == JFileChooser.APPROVE_OPTION)
+       {
+         defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
+       }
      }
    }
  
    /*
    @Override
    public void minColour_actionPerformed(JPanel panel)
    {
-     Color col = JColorChooser.showDialog(this,
+     JalviewColourChooser.showColourChooser(this,
              MessageManager.getString("label.select_colour_minimum_value"),
-             minColour.getBackground());
-     if (col != null)
-     {
-       panel.setBackground(col);
-     }
-     panel.repaint();
+             panel);
    }
  
    @Override
    public void maxColour_actionPerformed(JPanel panel)
    {
-     Color col = JColorChooser.showDialog(this,
+     JalviewColourChooser.showColourChooser(this,
              MessageManager.getString("label.select_colour_maximum_value"),
-             maxColour.getBackground());
-     if (col != null)
-     {
-       panel.setBackground(col);
-     }
-     panel.repaint();
+             panel);
    }
  
    @Override
    {
      if (!useLegacyGap.isSelected())
      {
-       Color col = JColorChooser.showDialog(this,
+       JalviewColourChooser.showColourChooser(this,
                MessageManager.getString("label.select_gap_colour"),
-               gapColour.getBackground());
-       if (col != null)
-       {
-         gap.setBackground(col);
-       }
-       gap.repaint();
+               gap);
      }
    }
  
    @Override
    public void hiddenColour_actionPerformed(JPanel hidden)
    {
-     Color col = JColorChooser.showDialog(this,
+     JalviewColourChooser.showColourChooser(this,
              MessageManager.getString("label.select_hidden_colour"),
-             hiddenColour.getBackground());
-     if (col != null)
-     {
-       hidden.setBackground(col);
-     }
-     hidden.repaint();
+             hidden);
    }
  
    @Override
        }
      } catch (NumberFormatException x)
      {
+       userIdWidth.setText("");
        JvOptionPane.showInternalMessageDialog(Desktop.desktop,
                MessageManager
                        .getString("warn.user_defined_width_requirements"),
                MessageManager.getString("label.invalid_id_column_width"),
                JvOptionPane.WARNING_MESSAGE);
-       userIdWidth.setText("");
      }
    }
  
    }
  
    /**
 -   * 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)
   */
  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;
@@@ -34,36 -54,16 +34,36 @@@ import java.io.IOException
  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.JCheckBoxMenuItem;
- import javax.swing.JColorChooser;
  import javax.swing.JMenu;
  import javax.swing.JMenuItem;
  import javax.swing.JRadioButtonMenuItem;
  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.MessageManager;
 +import jalview.ws.dbsources.Pdb;
 +
  /**
   * Base class with common functionality for JMol, Chimera or other structure
   * viewers.
@@@ -90,13 -90,13 +90,13 @@@ public abstract class StructureViewerBa
    /**
     * 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);
 +                  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();
    }
  
 -  @Override
 -  public void setJalviewColourScheme(ColourSchemeI cs)
 -  {
 -    getBinding().setJalviewColourScheme(cs);
 -  }
 -
    /**
     * Sends commands to the structure viewer to superimpose structures based on
     * currently associated alignments. May optionally return an error message for
     * 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);
      return reply;
    }
  
+   /**
+    * Opens a colour chooser dialog, and applies the chosen colour to the
+    * background of the structure viewer
+    */
    @Override
 -  public void background_actionPerformed(ActionEvent actionEvent)
 +  public void background_actionPerformed()
    {
-     Color col = JColorChooser.showDialog(this,
-             MessageManager.getString("label.select_background_colour"),
-             null);
-     if (col != null)
+     String ttl = MessageManager.getString("label.select_background_colour");
+     ColourChooserListener listener = new ColourChooserListener()
      {
-       getBinding().setBackgroundColour(col);
-     }
+       @Override
+       public void colourSelected(Color c)
+       {
+         getBinding().setBackgroundColour(c);
+       }
+     };
+     JalviewColourChooser.showColourChooser(this, ttl, null, listener);
    }
  
    @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(
              Cache.getProperty("LAST_DIRECTORY"));
  
    }
  
    @Override
 -  public void viewMapping_actionPerformed(ActionEvent actionEvent)
 +  public void viewMapping_actionPerformed()
    {
      CutAndPasteTransfer cap = new CutAndPasteTransfer();
      try
  
      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();
 +  }
 +
  }
   */
  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;
@@@ -71,21 -87,6 +71,22 @@@ import javax.swing.event.ChangeListener
  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.
   * 
@@@ -179,9 -180,7 +180,9 @@@ public class GPreferences extends JPane
  
    protected JComboBox<String> structViewer = new JComboBox<>();
  
 -  protected JTextField chimeraPath = new JTextField();
 +  protected JLabel structureViewerPathLabel;
 +
 +  protected JTextField structureViewerPath = new JTextField();
  
    protected ButtonGroup mappingMethod = new ButtonGroup();
  
     */
    protected JComboBox<Object> epsRendering = new JComboBox<>();
  
+   protected JComboBox<Object> htmlRendering = new JComboBox<>();
+   protected JComboBox<Object> svgRendering = new JComboBox<>();
    protected JLabel userIdWidthlabel = new JLabel();
  
    protected JCheckBox autoIdWidth = new JCheckBox();
      tabbedPane.add(initConnectionsTab(),
              MessageManager.getString("label.connections"));
  
-     tabbedPane.add(initBackupsTab(),
-             MessageManager.getString("label.backups"));
+       if (!Platform.isJS()) 
+       {
+         tabbedPane.add(initBackupsTab(), 
+                       MessageManager.getString("label.backups"));
+       }
  
      tabbedPane.add(initLinksTab(),
              MessageManager.getString("label.urllinks"));
      /*
       * See WsPreferences for the real work of configuring this tab.
       */
-     wsTab.setLayout(new BorderLayout());
-     tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
+     if (!Platform.isJS())
+     {
+       wsTab.setLayout(new BorderLayout());
+       tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
+     }
  
      /*
       * Handler to validate a tab before leaving it - currently only for
    }
  
    /**
-    * Initialises the Output tabbed panel.
+    * Initialises the Output tab
     * 
     * @return
     */
    {
      JPanel outputTab = new JPanel();
      outputTab.setLayout(null);
-     JLabel epsLabel = new JLabel();
+     JLabel epsLabel = new JLabel(
+             MessageManager.formatMessage("label.rendering_style", "EPS"));
      epsLabel.setFont(LABEL_FONT);
      epsLabel.setHorizontalAlignment(SwingConstants.RIGHT);
-     epsLabel.setText(MessageManager.getString("label.eps_rendering_style"));
-     epsLabel.setBounds(new Rectangle(9, 31, 140, 24));
+     epsLabel.setBounds(new Rectangle(9, 31, 160, 24));
      epsRendering.setFont(LABEL_FONT);
-     epsRendering.setBounds(new Rectangle(154, 34, 187, 21));
+     epsRendering.setBounds(new Rectangle(174, 34, 187, 21));
+     JLabel htmlLabel = new JLabel(
+             MessageManager.formatMessage("label.rendering_style", "HTML"));
+     htmlLabel.setFont(LABEL_FONT);
+     htmlLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+     htmlLabel.setBounds(new Rectangle(9, 55, 160, 24));
+     htmlRendering.setFont(LABEL_FONT);
+     htmlRendering.setBounds(new Rectangle(174, 58, 187, 21));
+     JLabel svgLabel = new JLabel(
+             MessageManager.formatMessage("label.rendering_style", "SVG"));
+     svgLabel.setFont(LABEL_FONT);
+     svgLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+     svgLabel.setBounds(new Rectangle(9, 79, 160, 24));
+     svgRendering.setFont(LABEL_FONT);
+     svgRendering.setBounds(new Rectangle(174, 82, 187, 21));
      JLabel jLabel1 = new JLabel();
      jLabel1.setFont(LABEL_FONT);
      jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
      jLabel1.setText(MessageManager.getString("label.append_start_end"));
      jLabel1.setFont(LABEL_FONT);
      fastajv.setFont(LABEL_FONT);
      fastajv.setHorizontalAlignment(SwingConstants.LEFT);
      clustaljv.setText(MessageManager.getString("label.clustal") + "     ");
      TitledBorder titledBorder2 = new TitledBorder(
              MessageManager.getString("label.file_output"));
      jPanel11.setBorder(titledBorder2);
-     jPanel11.setBounds(new Rectangle(30, 72, 196, 182));
+     jPanel11.setBounds(new Rectangle(30, 120, 196, 182));
      GridLayout gridLayout3 = new GridLayout();
      jPanel11.setLayout(gridLayout3);
      gridLayout3.setRows(8);
              MessageManager.getString("label.automatically_set_id_width"));
      autoIdWidth.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager
              .getString("label.adjusts_width_generated_eps_png")));
-     autoIdWidth.setBounds(new Rectangle(228, 96, 188, 23));
+     autoIdWidth.setBounds(new Rectangle(228, 144, 320, 23));
      autoIdWidth.addActionListener(new ActionListener()
      {
  
      userIdWidthlabel.setToolTipText(
              JvSwingUtils.wrapTooltip(true, MessageManager.getString(
                      "label.manually_specify_width_left_column")));
-     userIdWidthlabel.setBounds(new Rectangle(236, 120, 168, 23));
+     userIdWidthlabel.setBounds(new Rectangle(236, 168, 320, 23));
      userIdWidth.setFont(JvSwingUtils.getTextAreaFont());
      userIdWidth.setText("");
-     userIdWidth.setBounds(new Rectangle(232, 144, 84, 23));
+     userIdWidth.setBounds(new Rectangle(232, 192, 84, 23));
      userIdWidth.addActionListener(new ActionListener()
      {
  
      modellerOutput.setFont(LABEL_FONT);
      modellerOutput
              .setText(MessageManager.getString("label.use_modeller_output"));
-     modellerOutput.setBounds(new Rectangle(228, 226, 168, 23));
+     modellerOutput.setBounds(new Rectangle(228, 274, 320, 23));
      embbedBioJSON.setFont(LABEL_FONT);
      embbedBioJSON.setText(MessageManager.getString("label.embbed_biojson"));
-     embbedBioJSON.setBounds(new Rectangle(228, 200, 250, 23));
+     embbedBioJSON.setBounds(new Rectangle(228, 248, 250, 23));
  
      jPanel11.add(jLabel1);
      jPanel11.add(blcjv);
      outputTab.add(userIdWidth);
      outputTab.add(userIdWidthlabel);
      outputTab.add(modellerOutput);
-     outputTab.add(embbedBioJSON);
-     outputTab.add(epsLabel);
-     outputTab.add(epsRendering);
+     if (!Platform.isJS())
+     {
+       /*
+        * JalviewJS doesn't support Lineart option or SVG output
+        */
+       outputTab.add(embbedBioJSON);
+       outputTab.add(epsLabel);
+       outputTab.add(epsRendering);
+       outputTab.add(htmlLabel);
+       outputTab.add(htmlRendering);
+       outputTab.add(svgLabel);
+       outputTab.add(svgRendering);
+     }
      outputTab.add(jPanel11);
      return outputTab;
    }
      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);
  
+     /*
+      * hide Chimera options in JalviewJS
+      */
+     if (Platform.isJS()) 
+     {
 -      pathLabel.setVisible(false);
 -      chimeraPath.setVisible(false);
++      structureViewerPathLabel.setVisible(false);
++      structureViewerPath.setVisible(false);
+       viewerLabel.setVisible(false);
+       structViewer.setVisible(false);
+     }
+     
      return structureTab;
    }
  
      visualTab.add(fontNameCB);
      visualTab.add(fontSizeCB);
      visualTab.add(fontStyleCB);
+     
+     if (Platform.isJS())
+     {
+       startupCheckbox.setVisible(false);
+       startupFileTextfield.setVisible(false);
+     }
+     
      return visualTab;
    }
  
      BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
              .getSavedBackupEntry();
      enableBackupFiles
-             .setSelected(Cache.getDefault(BackupFiles.ENABLED, true));
+             .setSelected(Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
  
      BackupFilesPresetEntry backupfilesCustomEntry = BackupFilesPresetEntry
              .createBackupFilesPresetEntry(Cache
   */
  package jalview.jbgui;
  
--import jalview.api.structures.JalviewStructureDisplayI;
--import jalview.gui.ColourMenuHelper.ColourChangeListener;
- import jalview.util.MessageManager;
 -import jalview.util.ImageMaker.TYPE;
 -import jalview.util.MessageManager;
 -
  import java.awt.BorderLayout;
  import java.awt.GridLayout;
  import java.awt.event.ActionEvent;
@@@ -37,6 -38,7 +33,12 @@@ import javax.swing.JMenuItem
  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
  {
@@@ -86,6 -88,9 +88,9 @@@
  
    private void jbInit() throws Exception
    {
+     setName("jalview-structureviewer");
      JMenuBar menuBar = new JMenuBar();
      this.setJMenuBar(menuBar);
  
        @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
 -        pdbFile_actionPerformed(actionEvent);
 +        pdbFile_actionPerformed();
        }
      });
  
        @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
-         png_actionPerformed();
+         makePDBImage(TYPE.PNG);
        }
      });
  
        @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
-         eps_actionPerformed();
+         makePDBImage(TYPE.EPS);
        }
      });
  
        @Override
        public void actionPerformed(ActionEvent actionEvent)
        {
 -        viewMapping_actionPerformed(actionEvent);
 +        viewMapping_actionPerformed();
        }
      });
  
        @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 png_actionPerformed()
-   {
-   }
-   public void eps_actionPerformed()
+   public void makePDBImage(TYPE imageType)
    {
  
    }
  
 -  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()
    {
  
    }
@@@ -24,55 -24,6 +24,56 @@@ import static jalview.math.RotatableMat
  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.DataOutputStream;
 +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;
@@@ -102,21 -53,17 +103,21 @@@ import jalview.datamodel.features.Featu
  import jalview.datamodel.features.FeatureMatcherI;
  import jalview.datamodel.features.FeatureMatcherSet;
  import jalview.datamodel.features.FeatureMatcherSetI;
 +import jalview.ext.rbvi.chimera.JalviewChimeraBinding;
  import jalview.ext.varna.RnaModel;
  import jalview.gui.AlignFrame;
  import jalview.gui.AlignViewport;
  import jalview.gui.AlignmentPanel;
  import jalview.gui.AppVarna;
  import jalview.gui.ChimeraViewFrame;
 +import jalview.gui.ChimeraXViewFrame;
  import jalview.gui.Desktop;
 +import jalview.gui.JalviewChimeraXBindingModel;
  import jalview.gui.JvOptionPane;
  import jalview.gui.OOMWarning;
  import jalview.gui.PCAPanel;
  import jalview.gui.PaintRefresher;
 +import jalview.gui.PymolViewer;
  import jalview.gui.SplitFrame;
  import jalview.gui.StructureViewer;
  import jalview.gui.StructureViewer.ViewerType;
@@@ -203,6 -150,55 +204,6 @@@ import jalview.xml.binding.jalview.Sequ
  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.
   * 
   */
  public class Jalview2XML
  {
+   // BH 2018 we add the .jvp binary extension to J2S so that
+   // it will declare that binary when we do the file save from the browser
+   static
+   {
+     Platform.addJ2SBinaryType(".jvp?");
+   }
    private static final String VIEWER_PREFIX = "viewer_";
  
    private static final String RNA_PREFIX = "rna_";
        public boolean isResolvable()
        {
          return super.isResolvable() && mp.getTo() != null;
-       };
+       }
  
        @Override
        boolean resolve()
        } catch (Exception foo)
        {
        }
-       ;
        jout.close();
      } catch (Exception ex)
      {
      try
      {
        // create backupfiles object and get new temp filename destination
-       BackupFiles backupfiles = new BackupFiles(jarFile);
-       FileOutputStream fos = new FileOutputStream(
-               backupfiles.getTempFilePath());
+       boolean doBackup = BackupFiles.getEnabled();
+       BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
+       FileOutputStream fos = new FileOutputStream(doBackup ? 
+               backupfiles.getTempFilePath() : jarFile);
  
        JarOutputStream jout = new JarOutputStream(fos);
        List<AlignFrame> frames = new ArrayList<>();
        } catch (Exception foo)
        {
        }
        jout.close();
        boolean success = true;
  
-       backupfiles.setWriteSuccess(success);
-       success = backupfiles.rollBackupsAndRenameTempFile();
+       if (doBackup)
+       {
+         backupfiles.setWriteSuccess(success);
+         success = backupfiles.rollBackupsAndRenameTempFile();
+       }
  
        return success;
      } catch (Exception ex)
                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));
 +                }
 +                else
                  {
 -                  System.err.println(
 -                          "Error saving viewer state: " + e.getMessage());
 +                  Cache.log.error("Failed to save viewer state for "
 +                          +
 +                          viewFrame.getViewerType().toString());
                  }
                }
              }
        // using save and then load
        try
        {
+       fileName = fileName.replace('\\', '/');
          System.out.println("Writing jar entry " + fileName);
          JarEntry entry = new JarEntry(fileName);
          jout.putNextEntry(entry);
    protected void copyFileToJar(JarOutputStream jout, String infilePath,
            String jarEntryName)
    {
 -    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);
 +        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
 -        }
 -      }
      }
    }
  
    {
      if (jout != null)
      {
+       jarEntryName = jarEntryName.replace('\\','/');
        System.out.println("Writing jar entry " + jarEntryName);
        jout.putNextEntry(new JarEntry(jarEntryName));
        DataOutputStream dout = new DataOutputStream(jout);
    }
  
    /**
 +   * Copies input to output, in 4K buffers; handles any data (text or binary)
 +   * 
 +   * @param in
 +   * @param out
 +   * @throws IOException
 +   */
 +  protected void copyAll(InputStream in, OutputStream out)
 +          throws IOException
 +  {
 +    byte[] buffer = new byte[4096];
 +    int bytesRead = 0;
 +    while ((bytesRead = in.read(buffer)) != -1)
 +    {
 +      out.write(buffer, 0, bytesRead);
 +    }
 +  }
 +
 +  /**
     * Save the state of a structure viewer
     * 
     * @param ap
            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);
      vamsasSeq.setName(jds.getName());
      vamsasSeq.setSequence(jds.getSequenceAsString());
      vamsasSeq.setDescription(jds.getDescription());
-     jalview.datamodel.DBRefEntry[] dbrefs = null;
+     List<DBRefEntry> dbrefs = null;
      if (jds.getDatasetSequence() != null)
      {
        vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
       */
      if (dbrefs != null)
      {
-       for (int d = 0; d < dbrefs.length; d++)
+       for (int d = 0, nd = dbrefs.size(); d < nd; d++)
        {
          DBRef dbref = new DBRef();
-         DBRefEntry dbRefEntry = dbrefs[d];
-         dbref.setSource(dbRefEntry.getSource());
-         dbref.setVersion(dbRefEntry.getVersion());
-         dbref.setAccessionId(dbRefEntry.getAccessionId());
-         if (dbRefEntry instanceof GeneLocus)
+         DBRefEntry ref = dbrefs.get(d);
+         dbref.setSource(ref.getSource());
+         dbref.setVersion(ref.getVersion());
+         dbref.setAccessionId(ref.getAccessionId());
+         if (ref instanceof GeneLocus)
          {
            dbref.setLocus(true);
          }
-         if (dbRefEntry.hasMap())
+         if (ref.hasMap())
          {
-           Mapping mp = createVamsasMapping(dbRefEntry.getMap(), parentseq,
+           Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
                    jds, recurse);
            dbref.setMapping(mp);
          }
     * @param file
     *          - HTTP URL or filename
     */
-   public AlignFrame loadJalviewAlign(final String file)
+   public AlignFrame loadJalviewAlign(final Object file)
    {
  
      jalview.gui.AlignFrame af = null;
            public void run()
            {
              setLoadingFinishedForNewStructureViewers();
-           };
+           }
          });
        } catch (Exception x)
        {
      return af;
    }
  
-   private jarInputStreamProvider createjarInputStreamProvider(
-           final String file) throws MalformedURLException
-   {
-     URL url = null;
-     errorMessage = null;
-     uniqueSetSuffix = null;
-     seqRefIds = null;
-     viewportsAdded.clear();
-     frefedSequence = null;
-     if (file.startsWith("http://"))
-     {
-       url = new URL(file);
-     }
-     final URL _url = url;
-     return new jarInputStreamProvider()
-     {
-       @Override
-       public JarInputStream getJarInputStream() throws IOException
-       {
-         if (_url != null)
-         {
-           return new JarInputStream(_url.openStream());
-         }
-         else
-         {
-           return new JarInputStream(new FileInputStream(file));
-         }
-       }
-       @Override
-       public String getFilename()
-       {
-         return file;
-       }
-     };
-   }
+       @SuppressWarnings("unused")
+       private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException {
+               // BH 2018 allow for bytes already attached to File object
+               try {
+                       String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString());
+       byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
+               : null;
+                       URL url = null;
+                       errorMessage = null;
+                       uniqueSetSuffix = null;
+                       seqRefIds = null;
+                       viewportsAdded.clear();
+                       frefedSequence = null;
+                       if (file.startsWith("http://")) {
+                               url = new URL(file);
+                       }
+                       final URL _url = url;
+                       return new jarInputStreamProvider() {
+                               @Override
+                               public JarInputStream getJarInputStream() throws IOException {
+                                       if (bytes != null) {
+ //                                            System.out.println("Jalview2XML: opening byte jarInputStream for bytes.length=" + bytes.length);
+                                               return new JarInputStream(new ByteArrayInputStream(bytes));
+                                       }
+                                       if (_url != null) {
+ //                                            System.out.println("Jalview2XML: opening url jarInputStream for " + _url);
+                                               return new JarInputStream(_url.openStream());
+                                       } else {
+ //                                            System.out.println("Jalview2XML: opening file jarInputStream for " + file);
+                                               return new JarInputStream(new FileInputStream(file));
+                                       }
+                               }
+                               @Override
+                               public String getFilename() {
+                                       return file;
+                               }
+                       };
+               } catch (IOException e) {
+                       e.printStackTrace();
+                       return null;
+               }
+       }
  
    /**
     * Recover jalview session from a jalview project archive. Caller may
  
          if (jarentry != null && jarentry.getName().endsWith(".xml"))
          {
-           InputStreamReader in = new InputStreamReader(jin, UTF_8);
-           // JalviewModel object = new JalviewModel();
            JAXBContext jc = JAXBContext
                    .newInstance("jalview.xml.binding.jalview");
            XMLStreamReader streamReader = XMLInputFactory.newInstance()
                    .unmarshal(streamReader, JalviewModel.class);
            JalviewModel object = jbe.getValue();
  
-           /*
-           Unmarshaller unmar = new Unmarshaller(object);
-           unmar.setValidation(false);
-           object = (JalviewModel) unmar.unmarshal(in);
-           */
            if (true) // !skipViewport(object))
            {
              _af = loadFromObject(object, file, true, jprovider);
    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;
       * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry
       * "viewer_"+stateData.viewId
       */
 -    if (ViewerType.CHIMERA.toString().equals(stateData.getType()))
 +    String type = stateData.getType();
 +    if (type == null)
      {
 -      createChimeraViewer(viewerData, af, jprovider);
 +      type = ViewerType.JMOL.toString();
      }
 -    else
 +    try
      {
 -      /*
 -       * else Jmol (if pre-2.9, stateData contains JMOL state string)
 -       */
 -      createJmolViewer(viewerData, af, jprovider);
 +      ViewerType viewerType = ViewerType.valueOf(type);
 +      switch (viewerType)
 +      {
 +      case CHIMERA:
 +        createChimeraViewer(viewerData, af, jprovider, false);
 +        break;
 +      case CHIMERAX:
 +        createChimeraViewer(viewerData, af, jprovider, true);
 +        break;
 +      case PYMOL:
 +        createPymolViewer(viewerData, af, jprovider);
 +        break;
 +      case JMOL:
 +        createJmolViewer(viewerData, af, jprovider);
 +      }
 +    } catch (IllegalArgumentException | NullPointerException e)
 +    {
 +      Cache.log.error(
 +              "Invalid structure viewer type: " + type);
      }
    }
  
    /**
 -   * Create a new Chimera viewer.
 +   * Create a new Chimera or ChimeraX viewer
     * 
     * @param data
     * @param af
     * @param jprovider
 +   * @param isChimeraX
     */
    protected void createChimeraViewer(
            Entry<String, StructureViewerModel> viewerData, AlignFrame af,
 -          jarInputStreamProvider jprovider)
 +          jarInputStreamProvider jprovider, boolean isChimeraX)
    {
      StructureViewerModel data = viewerData.getValue();
      String chimeraSessionFile = data.getStateData();
       * 'uniquified' sviewid used to reconstruct the viewer here
       */
      String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
 +    String extension = isChimeraX
 +            ? JalviewChimeraXBindingModel.CHIMERAX_SESSION_EXTENSION
 +            : JalviewChimeraBinding.CHIMERA_SESSION_EXTENSION;
      chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
 -            "chimera", ".py");
 +            "chimera", extension);
  
      Set<Entry<File, StructureData>> fileData = data.getFileData()
              .entrySet();
              .toArray(new SequenceI[allseqs.size()][]);
      String newViewId = viewerData.getKey();
  
 -    ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
 -            af.alignPanel, pdbArray, seqsArray, colourByChimera,
 -            colourBySequence, newViewId);
 +    ChimeraViewFrame cvf = isChimeraX
 +            ? new ChimeraXViewFrame(chimeraSessionFile, af.alignPanel,
 +                    pdbArray, seqsArray, colourByChimera, colourBySequence,
 +                    newViewId)
 +            : new ChimeraViewFrame(chimeraSessionFile, af.alignPanel,
 +                    pdbArray, seqsArray, colourByChimera, colourBySequence,
 +                    newViewId);
      cvf.setSize(data.getWidth(), data.getHeight());
      cvf.setLocation(data.getX(), data.getY());
    }
    {
      AlignFrame af = null;
      af = new AlignFrame(al, safeInt(view.getWidth()),
-             safeInt(view.getHeight()), uniqueSeqSetId, viewId);
+             safeInt(view.getHeight()), uniqueSeqSetId, viewId) 
+ //    {
+ //            
+ //            @Override
+ //            protected void processKeyEvent(java.awt.event.KeyEvent e) {
+ //                    System.out.println("Jalview2XML   AF " + e);
+ //                    super.processKeyEvent(e);
+ //                    
+ //            }
+ //            
+ //    }
+     ;
  
      af.setFileName(file, FileFormat.Jalview);
  
    }
  
    /**
 +   * Create a new PyMol viewer
 +   * 
 +   * @param data
 +   * @param af
 +   * @param jprovider
 +   */
 +  protected void createPymolViewer(
 +          Entry<String, StructureViewerModel> viewerData, AlignFrame af,
 +          jarInputStreamProvider jprovider)
 +  {
 +    StructureViewerModel data = viewerData.getValue();
 +    String pymolSessionFile = data.getStateData();
 +  
 +    /*
 +     * Copy PyMol 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());
 +    pymolSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
 +            "pymol", ".pse");
 +  
 +    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 colourByPymol = 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();
 +  
 +    PymolViewer pv = new PymolViewer(pymolSessionFile,
 +            af.alignPanel, pdbArray, seqsArray, colourByPymol,
 +            colourBySequence, newViewId);
 +    pv.setSize(data.getWidth(), data.getHeight());
 +    pv.setLocation(data.getX(), data.getY());
 +  }
 +
 +  /**
     * Populates an XML model of the feature colour scheme for one feature type
     * 
     * @param featureType
@@@ -124,10 -124,10 +124,10 @@@ public class StructureSelectionManagerT
      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);
      }
    }
  
              PDBID);
  
      AlignmentAnnotation subseq_tf=null;
-     assertTrue(seq.getDBRefs() != null && seq.getDBRefs().length > 0);
+     assertTrue(seq.getDBRefs() != null && seq.getDBRefs().size() > 0);
      
      if (!al.findAnnotations(seq, null, TEMP_FACTOR_AA).iterator().hasNext())
      {