From: Jim Procter Date: Fri, 3 Jun 2022 10:37:14 +0000 (+0100) Subject: JAL-1953 2.11.2 with Archeopteryx! X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=4a3def9f59cefe629c9a33d87483283aee085928;hp=-c;p=jalview.git JAL-1953 2.11.2 with Archeopteryx! Minimal patches and Merge branch 'kjvdh/features/PhylogenyViewer' into merge/2_11_2/PhylogenyViewer also restored last kjvdh build of forester to j8 and j11lib directories Conflicts: .classpath j8lib/forester.jar resources/lang/Messages.properties src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java src/jalview/fts/core/GFTSPanel.java src/jalview/fts/service/pdb/PDBFTSPanel.java src/jalview/gui/AlignFrame.java src/jalview/gui/DasSourceBrowser.java src/jalview/gui/Desktop.java src/jalview/gui/PopupMenu.java src/jalview/gui/SplashScreen.java src/jalview/gui/StructureViewerBase.java src/jalview/io/BioJsHTMLOutput.java src/jalview/io/FileLoader.java src/jalview/io/HtmlSvgOutput.java src/jalview/project/Jalview2XML.java src/jalview/schemabinding/version2/JalviewModelSequence.java src/jalview/util/DBRefUtils.java src/jalview/viewmodel/AlignmentViewport.java src/jalview/ws/DBRefFetcher.java src/jalview/ws/DasSequenceFeatureFetcher.java src/jalview/ws/jws2/Jws2Discoverer.java utils/jalviewjs/buildxml/build.xml --- 4a3def9f59cefe629c9a33d87483283aee085928 diff --combined j11lib/forester.jar index afe77b2,534564c..534564c Binary files differ diff --combined j8lib/forester.jar index afe77b2,0000000..534564c mode 100644,000000..100644 Binary files differ diff --combined resources/lang/Messages.properties index 291052a,90379d2..c5d566f --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@@ -30,9 -30,7 +30,9 @@@ action.minimize_associated_windows = Mi action.close_all = Close all action.load_project = Load Project action.save_project = Save Project +action.save_project_as = Save Project as... action.quit = Quit +label.quit_jalview = Quit Jalview? action.expand_views = Expand Views action.gather_views = Gather Views action.page_setup = Page Setup... @@@ -118,11 -116,12 +118,11 @@@ action.paste_annotations = Paste Annota action.format = Format action.select = Select action.new_view = New View +action.new_structure_view_with = Open new structure view with {0} action.close = Close action.add = Add -action.save_as_default = Save as default action.save_as = Save as... action.save = Save -action.cancel_fetch = Cancel Fetch action.change_font = Change Font action.change_font_tree_panel = Change Font (Tree Panel) action.colour = Colour @@@ -133,8 -132,6 +133,8 @@@ tooltip.select_highlighted_columns = Pr action.deselect_all = Deselect all action.invert_selection = Invert selection action.using_jmol = Using Jmol +action.undo_changes_to_feature_settings = Undo all unapplied changes to feature settings +action.undo_changes_to_feature_settings_and_close_the_dialog = Undo all pending changes and close the feature settings dialog action.link = Link action.group_link = Group Link action.show_chain = Show Chain @@@ -143,6 -140,7 +143,6 @@@ action.fetch_db_references = Fetch DB R action.view_flanking_regions = Show flanking regions label.view_flanking_regions = Show sequence data either side of the subsequences involved in this alignment label.structures_manager = Structures Manager -label.nickname = Nickname: label.url = URL label.url\: = URL: label.input_file_url = Enter URL or Input File @@@ -164,6 -162,7 +164,6 @@@ label.current_parameter_set_name = Curr label.service_action = Service Action: label.post_url = POST URL: label.url_suffix = URL Suffix -label.sequence_source = Sequence Source label.per_seq = per Sequence label.result_vertically_separable = Results are vertically separable label.amend = Amend @@@ -173,9 -172,12 +173,12 @@@ label.principal_component_analysis = Pr label.average_distance_identity = Average Distance Using % Identity label.neighbour_joining_identity = Neighbour Joining Using % Identity label.choose_calculation = Choose Calculation +label.calc_title = {0} Using {1} + label.treecalc_title = {0} Using {1} + label.aptx_title = Archaeopteryx Tree View + label.of_x = of {0} label.tree_calc_av = Average Distance label.tree_calc_nj = Neighbour Joining -label.select_score_model = Select score model label.score_model_pid = % Identity label.score_model_blosum62 = BLOSUM62 label.score_model_pam250 = PAM 250 @@@ -188,22 -190,21 +191,22 @@@ label.out_to_textbox = Output to Textbo label.occupancy = Occupancy # delete Clustal - use FileFormat name instead label.clustal = Clustal -# label.colourScheme_ as in JalviewColourScheme +# label.colourScheme_ as in JalviewColourScheme, spaces removed label.colourScheme_clustal = Clustalx label.colourScheme_blosum62 = BLOSUM62 Score -label.colourScheme_%_identity = Percentage Identity +label.colourScheme_%identity = Percentage Identity label.colourScheme_zappo = Zappo label.colourScheme_taylor = Taylor label.colourScheme_hydrophobic = Hydrophobicity -label.colourScheme_helix_propensity = Helix Propensity -label.colourScheme_strand_propensity = Strand Propensity -label.colourScheme_turn_propensity = Turn Propensity -label.colourScheme_buried_index = Buried Index +label.colourScheme_helixpropensity = Helix Propensity +label.colourScheme_strandpropensity = Strand Propensity +label.colourScheme_turnpropensity = Turn Propensity +label.colourScheme_buriedindex = Buried Index label.colourScheme_purine/pyrimidine = Purine/Pyrimidine label.colourScheme_nucleotide = Nucleotide -label.colourScheme_t-coffee_scores = T-Coffee Scores -label.colourScheme_rna_helices = By RNA Helices +label.colourScheme_t-coffeescores = T-Coffee Scores +label.colourScheme_rnahelices = By RNA Helices +label.colourScheme_sequenceid = Sequence ID Colour label.blc = BLC label.fasta = Fasta label.msf = MSF @@@ -231,7 -232,6 +234,7 @@@ label.nucleotide = Nucleotid label.protein = Protein label.nucleotides = Nucleotides label.proteins = Proteins +label.CDS = CDS label.to_new_alignment = To New Alignment label.to_this_alignment = Add To This Alignment label.apply_colour_to_all_groups = Apply Colour To All Groups @@@ -244,6 -244,7 +247,6 @@@ label.documentation = Documentatio label.about = About... label.show_sequence_limits = Show Sequence Limits action.feature_settings = Feature Settings... -label.feature_settings = Feature Settings label.all_columns = All Columns label.all_sequences = All Sequences label.selected_columns = Selected Columns @@@ -268,15 -269,13 +271,15 @@@ label.use_rnaview = Use RNAView for sec label.autoadd_secstr = Add secondary structure annotation to alignment label.autoadd_temp = Add Temperature Factor annotation to alignment label.structure_viewer = Default structure viewer -label.chimera_path = Path to Chimera program -label.chimera_path_tip = Jalview will first try any path entered here, else standard installation locations.
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.
Please enter the path to Chimera (if installed),
or download and install UCSF Chimera. -label.chimera_failed = Error opening Chimera - is it installed?\nCheck path in Preferences, Structure +label.double_click_to_browse = Double-click to browse for file +label.viewer_path = Path to {0} program +label.viewer_path_tip = Jalview will first try any path entered here, else standard installation locations.
Double-click to browse for file. +label.invalid_viewer_path = Path not found or not executable +label.viewer_missing = Structure viewer not found.
Please enter the path to the executable (if installed),
or download and install the program. +label.open_viewer_failed = Error opening {0} - is it installed?\nCheck configured path in Structure tab of Jalview''s Preferences label.min_colour = Minimum Colour label.max_colour = Maximum Colour +label.no_colour = No Colour label.use_original_colours = Use Original Colours label.threshold_minmax = Threshold is min/max label.represent_group_with = Represent Group with {0} @@@ -284,9 -283,9 +287,9 @@@ label.selection = Selectio label.group_colour = Group Colour label.sequence = Sequence label.view_pdb_structure = View PDB Structure -label.min = Min: -label.max = Max: -label.colour_by_label = Colour by label +label.min_value = Min value +label.max_value = Max value +label.no_value = No value label.new_feature = New Feature label.match_case = Match Case label.view_alignment_editor = View in alignment editor @@@ -303,6 -302,10 +306,10 @@@ label.mark_unassociated_leaves = Mark U label.fit_to_window = Fit To Window label.newick_format = Newick Format label.select_tree_file = Select a tree file + label.treebase_study = TreeBASE Study + label.treebase = TreeBASE + label.treefam = TreeFam + label.tree_of_life = Tree of Life label.colours = Colours label.view_mapping = View Mapping label.wireframe = Wireframe @@@ -329,7 -332,6 +336,7 @@@ label.successfully_pasted_alignment_fil label.paste_your_alignment_file = Paste your alignment file here label.paste_your = Paste your label.finished_searching = Finished searching +label.subsequence_matches_found = {0} subsequence matches found label.search_results= Search results {0} : {1} label.found_match_for = Found match for {0} label.font = Font: @@@ -356,25 -358,30 +363,25 @@@ label.status = Statu label.channels = Channels label.channel_title_item_count = {0} ({1}) label.blog_item_published_on_date = {0} {1} 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 label.load_colours = Load Colours label.save_colours = Save Colours -label.fetch_das_features = Fetch DAS Features +label.load_colours_tooltip = Load feature colours and filters from file +label.save_colours_tooltip = Save feature colours and filters to file label.selected_database_to_fetch_from = Selected {0} database {1} to fetch from {2} label.database_param = Database: {0} label.example = Example label.example_param = Example: {0} label.select_file_format_before_saving = You must select a file format before saving! label.file_format_not_specified = File format not specified -label.couldnt_save_file = Couldn't save file: {0} +label.couldnt_save_file = Couldn''t save file: {0} label.error_saving_file = Error Saving File label.remove_from_default_list = Remove from default list? label.remove_user_defined_colour = Remove user defined colour @@@ -386,7 -393,9 +393,9 @@@ label.not_enough_sequences = Not enoug label.selected_region_to_tree_may_only_contain_residues_or_gaps = The selected region to create a tree may\nonly contain residues or gaps.\nTry using the Pad function in the edit menu,\nor one of the multiple sequence alignment web services. label.sequences_selection_not_aligned = Sequences in selection are not aligned label.problem_reading_tree_file = Problem reading tree file + label.tabs_detected_archaeopteryx = Warning, multiple trees detected in a single tree viewer instance. This will cause problems! label.possible_problem_with_tree_file = Possible problem with tree file + label.aptx_config_not_found = Warning: tree viewer configuration file not found, continue anyway? (this WILL cause the viewer to look different) label.tree_url_example = Please enter a complete URL, for example \"http://www.jalview.org/examples/ferredoxin.nw\" label.from_database = From Database... label.load_tree_url = Tree from URL @@@ -402,18 -411,28 +411,18 @@@ label.view_name_original = Origina label.enter_view_name = Enter View Name label.enter_label = Enter label label.enter_label_for_the_structure = Enter a label for the structure -label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ? -label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0} -label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n -label.align_to_existing_structure_view = Align to existing structure view label.pdb_entries_couldnt_be_retrieved = The following pdb entries could not be retrieved from the PDB\:\n{0}\nPlease retry, or try downloading them manually. label.couldnt_load_file = Couldn't load file label.couldnt_find_pdb_id_in_file = Couldn't find a PDB id in the file supplied. Please enter an Id to identify this structure. label.no_pdb_id_in_file = No PDB Id in File -label.couldnt_read_pasted_text = Couldn't read the pasted text {0} +label.couldnt_read_pasted_text = Couldn''t read the pasted text {0} label.error_parsing_text = Error parsing text -label.enter_local_das_source = Enter Nickname & URL of Local DAS Source -label.you_can_only_edit_or_remove_local_das_sources = You can only edit or remove local DAS Sources! -label.public_das_source = Public DAS source - not editable label.input_alignment_from_url = Input Alignment From URL 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 -label.wrapped_view_no_edit = Wrapped view - no edit label.error_retrieving_data = Error Retrieving Data label.user_colour_scheme_must_have_name = User colour scheme must have a name label.no_name_colour_scheme = No name for colour scheme @@@ -421,6 -440,8 +430,6 @@@ label.invalid_url = Invalid URL label.error_loading_file = Error loading file label.problems_opening_file = Encountered problems opening {0}!! label.file_open_error = File open error -label.no_das_sources_selected_warn = No das sources were selected.\nPlease select some sources and\ntry again. -label.no_das_sources_selected_title = No DAS Sources Selected label.colour_scheme_exists_overwrite = Colour scheme {0} exists.\nContinue saving colour scheme as {1}?" label.duplicate_scheme_name = Duplicate scheme name label.jalview_new_questionnaire = There is a new Questionnaire available. Would you like to complete it now ?\n @@@ -480,10 -501,6 +489,10 @@@ label.settings_for_type = Settings for label.view_full_application = View in Full Application label.load_associated_tree = Load Associated Tree... label.load_features_annotations = Load Features/Annotations... +label.load_vcf = Load SNP variants from plain text or indexed VCF data +label.load_vcf_file = Load VCF File +label.searching_vcf = Loading VCF variants... +label.added_vcf = Added {0} VCF variants to {1} sequence(s) label.export_features = Export Features... label.export_annotations = Export Annotations... label.to_upper_case = To Upper Case @@@ -493,14 -510,11 +502,14 @@@ label.edit_name_description = Edit Name label.create_sequence_feature = Create Sequence Feature... label.edit_sequence = Edit Sequence label.edit_sequences = Edit Sequences +label.insert_gap = Insert 1 gap +label.insert_gaps = Insert {0} gaps +label.delete_gap = Delete 1 gap +label.delete_gaps = Delete {0} gaps label.sequence_details = Sequence Details -label.jmol_help = Jmol Help -label.chimera_help = Chimera Help +label.viewer_help = {0} Help label.close_viewer = Close Viewer -label.confirm_close_chimera = This will close Jalview''s connection to {0}.
Do you want to close the Chimera window as well? +label.confirm_close_viewer = This will close Jalview''s connection to {0}.
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 @@@ -516,20 -530,16 +525,20 @@@ label.load_tree_file = Load a tree fil label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences = Retrieve and parse sequence database records for the alignment or the currently selected sequences label.standard_databases = Standard Databases label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources +label.fetch_uniprot_references = Fetch Uniprot references +label.search_3dbeacons = 3D-Beacons Search +label.find_models_from_3dbeacons = Search 3D-Beacons for 3D structures and models +label.3dbeacons = 3D-Beacons +label.fetch_references_for = Fetch database references for {0} sequences ? +label.fetch_references_for_3dbeacons = 3D Beacons needs Uniprot References. Fetch database references for {0} sequences ? label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences. label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s) -label.connect_to_session = Connect to session {0} label.threshold_feature_display_by_score = Threshold the feature display by score. label.threshold_feature_no_threshold = No Threshold label.threshold_feature_above_threshold = Above Threshold label.threshold_feature_below_threshold = Below Threshold label.adjust_threshold = Adjust threshold label.toggle_absolute_relative_display_threshold = Toggle between absolute and relative display threshold. -label.display_features_same_type_different_label_using_different_colour = Display features of the same type with a different label using a different colour. (e.g. domain features) label.select_colour_minimum_value = Select Colour for Minimum Value label.select_colour_maximum_value = Select Colour for Maximum Value label.open_url_param = Open URL {0} @@@ -557,7 -567,10 +566,7 @@@ label.right_align_sequence_id = Right A label.sequence_id_tooltip = Sequence ID Tooltip label.no_services = label.select_copy_raw_html = Select this if you want to copy raw html -label.share_data_vamsas_applications = Share data with other vamsas applications -label.connect_to = Connect to -label.join_existing_vamsas_session = Join an existing vamsas session -label.from_url = From URL +label.from_url = from URL label.any_trees_calculated_or_loaded_alignment_automatically_sort = When selected, any trees calculated or loaded onto the alignment will automatically sort the alignment label.sort_with_new_tree = Sort With New Tree label.from_textbox = From Textbox @@@ -566,6 -579,7 +575,6 @@@ label.preferences = Preference label.tools = Tools label.fetch_sequences = Fetch Sequences action.fetch_sequences = Fetch Sequences... -label.stop_vamsas_session = Stop Vamsas Session label.collect_garbage = Collect Garbage label.show_memory_usage = Show Memory Usage label.show_java_console = Show Java Console @@@ -588,22 -602,14 +597,22 @@@ label.gap_symbol = Gap Symbo label.prot_alignment_colour = Protein Alignment Colour label.nuc_alignment_colour = Nucleotide Alignment Colour label.address = Address +label.host = Host label.port = Port -label.default_browser_unix = Default Browser (Unix) +label.default_browser_unix_windows = Default Browser (Unix, Windows) label.send_usage_statistics = Send usage statistics label.check_for_questionnaires = Check for questionnaires 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.no_proxy = No proxy servers +label.system_proxy = System proxy servers (http={0}; https={1}) +label.use_proxy_server = Use these proxy servers +label.auth_required = Authentication required +label.username = Username +label.password = Password +label.proxy_password_required = Proxy password required +label.not_stored = not stored in Preferences file +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 @@@ -625,11 -631,13 +634,11 @@@ label.visual = Visua label.connections = Connections label.output = Output label.editing = Editing 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.fetch_viewer_attributes = Fetch {0} attributes +label.fetch_viewer_attributes_tip = Copy {0} attribute to Jalview feature label.marks_leaves_tree_not_associated_with_sequence = Marks leaves of tree not associated with a sequence label.index_web_services_menu_by_host_site = Index web services in menu by the host site label.option_want_informed_web_service_URL_cannot_be_accessed_jalview_when_starts_up = Check this option if you want to be informed
when a web service URL cannot be accessed by Jalview
when it starts up @@@ -639,7 -647,11 +648,7 @@@ label.delete_service_url = Delete Servi label.details = Details label.options = Options label.parameters = Parameters -label.available_das_sources = Available DAS Sources -label.full_details = Full Details -label.authority = Authority -label.type = Type -label.proxy_server = Proxy Server +label.proxy_servers = Proxy Servers label.file_output = File Output label.select_input_type = Select input type label.set_options_for_type = Set options for type @@@ -683,7 -695,7 +692,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." @@@ -707,6 -719,9 +716,6 @@@ label.sort_alignment_new_tree = Sort Al label.add_sequences = Add Sequences label.new_window = New Window label.split_window = Split Window -label.refresh_available_sources = Refresh Available Sources -label.use_registry = Use Registry -label.add_local_source = Add Local Source label.set_as_default = Set as Default label.show_labels = Show labels action.background_colour = Background Colour... @@@ -714,14 -729,15 +723,14 @@@ 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 -label.jmol = Jmol -label.chimera = Chimera -label.create_chimera_attributes = Write Jalview features -label.create_chimera_attributes_tip = Set Chimera residue attributes for visible features -label.attributes_set = {0} attribute values set on Chimera +label.create_viewer_attributes = Write Jalview features +label.create_viewer_attributes_tip = Set structure residue attributes for Jalview features +label.attributes_set = {0} attribute values set on {1} label.sort_alignment_by_tree = Sort Alignment By Tree label.mark_unlinked_leaves = Mark Unlinked Leaves label.associate_leaves_with = Associate Leaves With @@@ -764,21 -780,18 +773,21 @@@ label.run_with_preset_params = Run {0} label.view_and_change_parameters_before_running_calculation = View and change parameters before running calculation label.view_documentation = View documentation label.select_return_type = Select return type -label.translation_of_params = Translation of {0} +label.translation_of_params = Translation of {0} (Table {1}) label.features_for_params = Features for - {0} label.annotations_for_params = Annotations for - {0} label.generating_features_for_params = Generating features for - {0} label.generating_annotations_for_params = Generating annotations for - {0} label.varna_params = VARNA - {0} label.sequence_feature_settings = Sequence Feature Settings +label.sequence_feature_settings_for = Sequence Feature Settings for {0} +label.sequence_feature_settings_for_view = Sequence Feature Settings for view "{0}" +label.sequence_feature_settings_for_CDS_and_Protein = Sequence Feature Settings for CDS and Protein label.pairwise_aligned_sequences = Pairwise Aligned Sequences label.original_data_for_params = Original Data for {0} label.points_for_params = Points for {0} label.transformed_points_for_params = Transformed points for {0} -label.graduated_color_for_params = Graduated Feature Colour for {0} +label.variable_color_for = Variable Feature Colour for {0} label.select_background_colour = Select Background Colour label.invalid_font = Invalid Font label.separate_multiple_accession_ids = Enter one or more accession IDs separated by a semi-colon ";" @@@ -849,6 -862,7 +858,6 @@@ label.multiharmony = Multi-Harmon label.unable_start_web_service_analysis = Unable to start web service analysis label.job_couldnt_be_started_check_input = The Job couldn't be started. Please check your input, and the Jalview console for any warning messages. label.prompt_each_time = Prompt each time -label.use_source = Use Source label.couldnt_save_project = Couldn't save project label.error_whilst_saving_current_state_to = Error whilst saving current state to {0} label.error_whilst_loading_project_from = Error whilst loading project from {0} @@@ -858,13 -872,13 +867,13 @@@ label.invalid_name = Invalid nam label.set_proxy_settings = Please set up your proxy settings in the 'Connections' tab of the Preferences window label.proxy_authorization_failed = Proxy Authorization Failed label.internal_jalview_error = Internal Jalview Error -label.secondary_structure_prediction_service_couldnt_be_located = The Secondary Structure Prediction Service named {0} at {1} couldn't be located. +label.secondary_structure_prediction_service_couldnt_be_located = The Secondary Structure Prediction Service named {0} at {1} couldn''t be located. label.service_called_is_not_msa_service = The Service called \n{0}\nis not a \nMultiple Sequence Alignment Service\! label.msa_service_is_unknown = The Multiple Sequence Alignment Service named {0} is unknown label.service_called_is_not_seq_search_service = The Service called \n{0}\nis not a \nSequence Search Service\! label.seq_search_service_is_unknown = The Sequence Search Service named {0} is unknown label.feature_type = Feature Type -label.display = Display +label.show = Show label.service_url = Service URL label.copied_sequences = Copied sequences label.cut_sequences = Cut Sequences @@@ -874,16 -888,22 +883,16 @@@ label.error_unsupported_owwner_user_col label.save_alignment_to_file = Save Alignment to file label.save_features_to_file = Save Features to File label.save_annotation_to_file = Save Annotation to File -label.no_features_on_alignment = No features found on alignment label.save_pdb_file = Save PDB File label.save_text_to_file = Save Text to File label.save_state = Save State label.restore_state = Restore State label.saving_jalview_project = Saving jalview project {0} -label.loading_jalview_project = Loading jalview project {0} -label.save_vamsas_document_archive = Save Vamsas Document Archive -label.saving_vamsas_doc = Saving VAMSAS Document to {0} label.load_feature_colours = Load Feature Colours label.save_feature_colours = Save Feature Colour Scheme 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 @@@ -898,9 -918,12 +907,11 @@@ label.visible = Visibl label.select_unselect_visible_regions_from = select and unselected {0} regions from {1} label.visible_region_of = visible region of label.webservice_job_title_on = {0} using {1} on {2} label.loading_file = Loading File: {0} label.edit_params = Edit {0} label.as_percentage = As Percentage + error.database_id_has_letters = Database identifier ({0}) should contain only digits + error.phyloxml_validation = phyloXML XSD-based validation is turned off (enable with line 'validate_against_phyloxml_xsd_schem: true' in configuration file) error.not_implemented = Not implemented error.no_such_method_as_clone1_for = No such method as clone1 for {0} error.null_from_clone1 = Null from clone1! @@@ -938,20 -961,29 +949,20 @@@ 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 label.couldnt_create_groovy_shell = Couldn't create the groovy Shell. Check the error log for the details of what went wrong. error.unsupported_version_calcIdparam = Unsupported Version for calcIdparam {0} error.implementation_error_cant_reorder_tree = Implementation Error: Can't reorder this tree. Not DefaultMutableTreeNode. -error.invalid_value_for_option = Invalid value {0} for option {1} +error.invalid_value_for_option = Invalid value ''{0}'' for option ''{1}'' error.implementation_error_cannot_import_vamsas_doc = Implementation Error - cannot import existing vamsas document into an existing session, Yet! label.vamsas_doc_couldnt_be_opened_as_new_session = VAMSAS Document could not be opened as a new session - please choose another -error.implementation_error_vamsas_operation_not_init = Impementation error! Vamsas Operations when client not initialised and connected -error.jalview_no_connected_vamsas_session = Jalview not connected to Vamsas session -error.implementation_error_cannot_recover_vamsas_object_mappings = IMPLEMENTATION ERROR: Cannot recover vamsas object mappings - no backup was made error.setstatus_called_non_existent_job_pane = setStatus called for non-existent job pane {0} error.implementation_error_cannot_find_marshaller_for_param_set =Implementation error: Can't find a marshaller for the parameter set error.implementation_error_old_jalview_object_not_bound =IMPLEMENTATION ERROR: old jalview object is not bound ! ({0}) error.implementation_error_vamsas_doc_class_should_bind_to_type = Implementation Error: Vamsas Document Class {0} should bind to a {1} (found a {2}) error.invalid_vamsas_rangetype_cannot_resolve_lists = Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice! -error.implementation_error_maplist_is_null = Implementation error. MapList is null for initMapType. error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key error.implementation_error_null_fileparse = Implementation error. Null FileParse in copy constructor -error.implementation_error_cannot_map_alignment_sequences = IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document. error.implementation_error_structure_selection_manager_null = Implementation error. Structure selection manager's context is 'null' exception.ssm_context_is_null = SSM context is null error.idstring_seqstrings_only_one_per_sequence = idstrings and seqstrings contain one string each per sequence @@@ -966,13 -998,14 +977,13 @@@ error.implementation_error_minlen_must_ error.implementation_error_msawbjob_called = Implementation error - StartJob(MsaWSJob) called on a WSJobInstance {0} error.implementation_error_cannot_attach_ws_menu_entry = IMPLEMENTATION ERROR: cannot attach WS Menu Entry without service handle reference! error.parameter_migration_not_implemented_yet = Parameter migration not implemented yet -error.implementation_error_cannot_set_jaba_option = Implementation error: cannot set Jaba Option to a value outside its allowed value range! error.implementation_error_valuetype_doesnt_support_jabaws_type = IMPLEMENTATION ERROR: jalview.ws.params.ValueConstrainI.ValueType does not support the JABAWS type : {0} error.cannot_create_jabaws_param_set = Cannot create a JabaWSParamSet from non-JabaWS parameters error.cannot_set_arguments_to_jabaws_param_set = Cannot set arguments to a JabaWSParamSet that are not JabaWS arguments error.implementation_error_runner_config_not_available = Implementation Error: Runner Config not available for a JABAWS service of type {0} ({1}) error.implementation_error_cannot_handle_jaba_param = Implementation Error: Cannot handle Jaba parameter object {0} error.implementation_error_attempt_to_delete_service_preset = Implementation error: Attempt to delete a service preset! -error.implementation_error_cannot_locate_oldname_presetname = Implementation error: Can't locate either oldname ({0}) or presetName ({1}in the datastore!" +error.implementation_error_cannot_locate_oldname_presetname = Implementation error: Can''t locate either oldname ({0}) or presetName ({1}) in the datastore!" error.implementation_error_jabaws_param_set_only_handled_by = Implementation error: JabaWsParamSets can only be handled by JabaParamStore error.cannot_set_source_file_for = Cannot set source file for {0} error.mismatch_service_instance_preset = Probable mismatch between service instance and preset! @@@ -980,20 -1013,18 +991,20 @@@ error.cannot_set_params_for_ws_preset error.implementation_error_can_only_instantiate_jaba_param_sets = Implementation error: Can only instantiate Jaba parameter sets error.no_aacon_service_found = No AACon service found error.implementation_error_couldnt_copy_value_constraint = Implementation error: could not copy ValueConstrain! -error.couldnt_encode_as_utf8 = Couldn't encode {0} as UTF-8. +error.couldnt_encode_as_utf8 = Couldn''t encode {0} as UTF-8. error.tree_inputtype_not_yet_implemented = Tree InputType not yet implemented error.implementation_error_need_to_have_httpresponse = Implementation Error: need to have an HttpResponse to process error.dbrefsource_implementation_exception =DBRefSource Implementation Exception error.implementation_error_dbinstance_must_implement_interface = Implmentation Error - getDbInstances must be given a class that implements jalview.ws.seqfetcher.DbSourceProxy (was given{0}) error.implementation_error_must_init_dbsources =Implementation error. Must initialise dbSources label.view_controller_toggled_marked = {0} {1} columns {2} features of type {3} across {4} sequence(s) +label.no_highlighted_regions_marked = No highlighted regions marked label.toggled = Toggled label.marked = Marked label.containing = containing label.not_containing = not containing -label.no_feature_of_type_found = No features of type {0} found. +label.no_feature_of_type_found = No features of type {0} found +label.no_feature_found_selection = No features of type {0} found in selection label.submission_params = Submission {0} label.empty_alignment_job = Empty Alignment Job label.add_new_sbrs_service = Add a new Simple Bioinformatics Rest Service @@@ -1002,7 -1033,7 +1013,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} @@@ -1037,24 -1068,22 +1048,24 @@@ error.implementation_error_reset_called exception.number_of_residues_in_query_sequence_differ_from_prediction = Number of residues in {0} supposed query sequence ({1}\n{2})\ndiffer from number of prediction sites in prediction ({3}) label.mapped = mapped exception.jpredconcide_entry_has_unexpected_number_of_columns = JPredConcise: Entry ({0}) has an unexpected number of columns -exception.couldnt_parse_concise_annotation_for_prediction = Couldn't parse concise annotation for prediction profile.\n{0} +exception.couldnt_parse_concise_annotation_for_prediction = Couldn''t parse concise annotation for prediction profile.\n{0} exception.newfile = NewickFile\: {0}\n label.no_tree_read_in = No Tree read in -exception.rnaml_couldnt_access_datasource = Couldn't access datasource ({0}) -exception.ranml_couldnt_process_data = Couldn't process data as RNAML file ({0}) +exception.rnaml_couldnt_access_datasource = Couldn''t access datasource ({0}) +exception.ranml_couldnt_process_data = Couldn''t process data as RNAML file ({0}) exception.ranml_invalid_file = Invalid RNAML file ({0}) exception.ranml_problem_parsing_data = Problem parsing data as RNAML ({0}) exception.pfam_no_sequences_found = No sequences found (PFAM input) exception.stockholm_invalid_format = This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM' exception.couldnt_parse_sequence_line = Could not parse sequence line: {0} exception.unknown_annotation_detected = Unknown annotation detected: {0} {1} -exception.couldnt_store_sequence_mappings = Couldn't store sequence mappings for {0} +exception.couldnt_store_sequence_mappings = Couldn''t store sequence mappings for {0} exception.matrix_too_many_iteration = Too many iterations in {0} (max is {1}) exception.invalid_matrix_identifier = Sequence identifier {0} not found in distance matrix. exception.browser_not_found = Exception in finding browser: {0} +exception.browser_unable_to_launch = Unable to launch browser: {0} exception.browser_unable_to_locate = Unable to locate browser: {0} +exception.browser_os_not_supported = Launching browser on this operating system not supported. Use URL\n{0} exception.invocation_target_exception_creating_aedesc = InvocationTargetException while creating AEDesc: {0} exception.illegal_access_building_apple_evt= IllegalAccessException while building AppleEvent: {0} exception.unable_to_launch_url = Unable to launch URL: {0} @@@ -1062,6 -1091,9 +1073,6 @@@ exception.unable_to_create_internet_con exception.invocation_target_calling_url = InvocationTargetException while calling openURL: {0} exception.illegal_access_calling_url = IllegalAccessException while calling openURL: {0} exception.interrupted_launching_browser = InterruptedException while launching browser: {0} -exception.das_source_doesnt_support_sequence_command = Source {0} does not support the sequence command. -exception.invalid_das_source = Invalid das source: {0} -exception.ebiembl_retrieval_failed_on = EBI EMBL XML retrieval failed on {0}:{1} exception.no_pdb_records_for_chain = No PDB Records for {0} chain {1} exception.unexpected_handling_rnaml_translation_for_pdb = Unexpected exception when handling RNAML translation of PDB data exception.couldnt_recover_sequence_properties_for_alignment = Couldn't recover sequence properties for alignment @@@ -1076,6 -1108,7 +1087,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. @@@ -1083,7 -1116,7 +1094,7 @@@ warn.service_not_supported = Service no warn.input_is_too_big = Input is too big! warn.invalid_job_param_set = Invalid job parameter set! warn.oneseq_msainput_selection = The current selection only contains a single sequence. Do you want to submit all sequences for alignment instead ? -info.job_couldnt_be_run_server_doesnt_support_program = Job could not be run because the server doesn't support this program.\n{0} +info.job_couldnt_be_run_server_doesnt_support_program = Job could not be run because the server doesn''t support this program.\n{0} info.job_couldnt_be_run_exceeded_hard_limit = Job could not be run because it exceeded a hard limit on the server.\n{0} info.job_couldnt_be_run_incorrect_param_setting = Job could not be run because some of the parameter settings are not supported by the server.\n{0}\nPlease check to make sure you have used the correct parameter set for this service\!\n info.no_jobs_ran = No jobs ran @@@ -1100,36 -1133,48 +1111,36 @@@ 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} status.opening_params = Opening {0} -status.waiting_sequence_database_fetchers_init = Waiting for Sequence Database Fetchers to initialise -status.init_sequence_database_fetchers = Initialising Sequence Database Fetchers status.fetching_sequence_queries_from = Fetching {0} sequence queries from {1} status.finshed_querying = Finished querying status.parsing_results = Parsing results. status.processing = Processing... status.refreshing_web_service_menus = Refreshing Web Service Menus status.collecting_job_results = Collecting job results. status.fetching_db_refs = Fetching db refs status.loading_cached_pdb_entries = Loading Cached PDB Entries status.searching_for_pdb_structures = Searching for PDB Structures +status.searching_3d_beacons = Searching 3D Beacons +status.no_structures_discovered_from_3d_beacons = No models discovered from 3D Beacons status.opening_file_for = opening file for -status.colouring_chimera = Colouring Chimera +status.colouring_structures = Colouring structures label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data label.font_too_small = Font size is too small -label.error_loading_file_params = Error loading file {0} -label.error_loading_jalview_file = Error loading Jalview file warn.out_of_memory_when_action = Out of memory when {0}\!\!\nSee help files for increasing Java Virtual Machine memory. warn.out_of_memory_loading_file = Out of memory loading file {0}\!\!\nSee help files for increasing Java Virtual Machine memory. label.out_of_memory = Out of memory label.invalid_id_column_width = Invalid ID Column width warn.user_defined_width_requirements = The user defined width for the\nannotation and sequence ID columns\nin exported figures must be\nat least 12 pixels wide. -label.couldnt_create_sequence_fetcher = Couldn't create SequenceFetcher -warn.couldnt_create_sequence_fetcher_client = Could not create the sequence fetcher client. Check error logs for details. warn.server_didnt_pass_validation = Service did not pass validation.\nCheck the Jalview Console for more details. warn.url_must_contain = Sequence URL must contain $SEQUENCE_ID$, $DB_ACCESSION$, or a regex warn.urls_not_contacted = URLs that could not be contacted warn.urls_no_jaba = URLs without any JABA Services info.validate_jabaws_server = Validate JabaWS Server ?\n(Look in console output for results) label.test_server = Test Server? -info.you_want_jalview_to_find_uniprot_accessions = Do you want Jalview to find\nUniprot Accession ids for given sequence names? -label.find_uniprot_accession_ids = Find Uniprot Accession Ids label.new_sequence_fetcher = New Sequence Fetcher label.additional_sequence_fetcher = Additional Sequence Fetcher label.select_database_retrieval_source = Select Database Retrieval Source @@@ -1148,7 -1193,6 +1159,7 @@@ label.add_annotations_for = Add annotat action.choose_annotations = Choose Annotations... label.choose_annotations = Choose Annotations label.find = Find +label.in = in label.invalid_search = Search string invalid error.invalid_regex = Invalid regular expression label.ignore_gaps_consensus = Ignore Gaps In Consensus @@@ -1183,6 -1227,7 +1194,6 @@@ label.pdb_sequence_fetcher = PDB Sequen label.result = result label.results = results label.structure_chooser = Structure Chooser -label.select = Select : label.invert = Invert label.select_pdb_file = Select PDB File info.select_filter_option = Select Filter Option/Manual Entry @@@ -1216,10 -1261,14 +1227,10 @@@ label.structure_chooser_filter_time = S label.structure_chooser_no_of_structures = Structure Chooser - {0} Found ({1}) info.no_pdb_entry_found_for = No PDB entry found for {0} exception.unable_to_detect_internet_connection = Jalview is unable to detect an internet connection -exception.fts_rest_service_no_longer_available = {0} rest services no longer available! -exception.resource_not_be_found = The requested resource could not be found -exception.fts_server_error = There seems to be an error from the {0} server exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \nPlease ensure that you are connected to the internet and try again. 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 @@@ -1231,6 -1280,8 +1242,6 @@@ action.next_page= > action.prev_page= << label.next_page_tooltip=Next Page label.prev_page_tooltip=Previous Page -exception.bad_request=Bad request. There is a problem with your input. -exception.service_not_available=Service not available. The server is being updated, try again later. status.launching_3d_structure_viewer = Launching 3D Structure viewer... status.fetching_3d_structures_for_selected_entries = Fetching 3D Structures for selected entries... status.fetching_dbrefs_for_sequences_without_valid_refs = Fetching db refs for {0} sequence(s) without valid db ref required for SIFTS mapping @@@ -1246,6 -1297,7 +1257,6 @@@ label.SEQUENCE_ID_for_DB_ACCESSION1 = P label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'. label.do_not_display_again = Do not display this message again exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line -label.filter = Filter text: action.customfilter = Custom only action.showall = Show All label.insert = Insert: @@@ -1260,6 -1312,7 +1271,6 @@@ label.edit_sequence_url_link = Edit seq warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot be MIRIAM ids label.output_seq_details = Output Sequence Details to list all database references label.urllinks = Links -label.default_cache_size = Default Cache Size action.clear_cached_items = Clear Cached Items label.togglehidden = Show hidden regions label.quality_descr = Alignment Quality based on Blosum62 scores @@@ -1281,40 -1334,9 +1292,40 @@@ label.select_hidden_colour = Select hid label.overview = Overview label.reset_to_defaults = Reset to defaults label.oview_calc = Recalculating overview... +label.feature_details = Feature details +label.matchCondition_contains = Contains +label.matchCondition_notcontains = Does not contain +label.matchCondition_matches = Matches +label.matchCondition_notmatches = Does not match +label.matchCondition_present = Is present +label.matchCondition_notpresent = Is not present +label.matchCondition_eq = = +label.matchCondition_ne = not = +label.matchCondition_lt = < +label.matchCondition_le = <= +label.matchCondition_gt = > +label.matchCondition_ge = >= +label.numeric_required = The value should be numeric +label.filter = Filter +label.filters = Filters +label.join_conditions = Join conditions with +label.delete_condition = Delete this condition +label.score = Score +label.colour_by_label = Colour by label +label.variable_colour = Variable 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 +label.display_settings_for = Display settings for {0} features +label.simple_colour = Simple Colour +label.colour_by_text = Colour by text +label.graduated_colour = Graduated Colour +label.by_text_of = By text of +label.by_range_of = By range of +label.or = Or +label.and = And +label.sequence_feature_colours = Sequence Feature Colours label.best_quality = Best Quality label.best_resolution = Best Resolution label.most_protein_chain = Most Protein Chain @@@ -1322,97 -1344,3 +1333,97 @@@ 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 +label.backupfiles_confirm_save_file_backupfiles_roll_wrong = Something possibly went wrong with the backups of this file. +label.backupfiles_confirm_save_new_saved_file_ok = The new saved file seems okay. +label.backupfiles_confirm_save_new_saved_file_not_ok = The new saved file might not be okay. +label.continue_operation = Continue operation? +label.backups = Backups +label.backup = Backup +label.backup_files = Backup Files +label.enable_backupfiles = Enable backup files +label.backup_filename_strategy = Backup filename strategy +label.append_to_filename = Append to filename (%n is replaced by the backup number) +label.append_to_filename_tooltip = %n in the text will be replaced by the backup number. The text will appear after the filename. See the summary box above. +label.index_digits = Number of digits to use for the backup number (%n) +label.scheme_examples = Scheme examples +label.increment_index = Increase appended text numbers - newest file has largest number. +label.reverse_roll = "Roll" appended text numbers - newest backup file is always number 1. +label.keep_files = Deleting old backup files +label.keep_all_backup_files = Do not delete old backup files +label.keep_only_this_number_of_backup_files = Keep only this number of most recent backup files +label.autodelete_old_backup_files = Auto-delete old backup files: +label.always_ask = Always ask +label.auto_delete = Automatically delete +label.filename = filename +label.braced_oldest = (oldest) +label.braced_newest = (most recent) +label.configuration = Configuration +label.configure_feature_tooltip = Click to configure variable colour or filters +label.schemes = Schemes +label.customise = Customise +label.custom = Custom +label.default = Default +label.single_file = Single backup +label.keep_all_versions = Keep all versions +label.rolled_backups = Rolled backup files +label.customise_description = Select Customise, make changes, and click on OK to save your own custom scheme +label.custom_description = Your own saved scheme +label.default_description = Keep the last three versions of the file +label.single_file_description = Keep the last version of the file +label.keep_all_versions_description = Keep all previous versions of the file +label.rolled_backups_description = Keep the last nine versions of the file from _bak.1 (newest) to _bak.9 (oldest) +label.cancel_changes_description = Cancel changes made to your last saved Custom scheme +label.no_backup_files = NO BACKUP FILES +label.include_backup_files = Include backup files +label.cancel_changes = Cancel changes +label.warning_confirm_change_reverse = Warning!\nIf you change the increment/decrement of the backup filename number, without changing the suffix or number of digits,\nthis may cause loss of backup files created with the previous backup filename scheme.\nAre you sure you wish to do this? +label.change_increment_decrement = Change increment/decrement? +label.newerdelete_replacement_line = Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted and replaced by apparently older file\n''{1}''\t(modified {3}, size {5}). +label.confirm_deletion_or_rename = Confirm deletion of ''{0}'' or rename to ''{1}''? +label.newerdelete_line = Backup file\n''{0}''\t(modified {2}, size {4})\nis to be deleted but is newer than the oldest remaining backup file\n''{1}''\t(modified {3}, size {5}). +label.confirm_deletion = Confirm deletion of ''{0}''? +label.delete = Delete +label.rename = Rename +label.keep = Keep +label.file_info = (modified {0}, size {1}) +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.by_annotation_tooltip = Annotation Colour is configured from the main Colour menu +label.show_linked_features = Show {0} features +label.on_top = on top +label.include_linked_features = Include {0} features +label.include_linked_tooltip = Include visible {0} features
converted to local sequence coordinates +label.features_not_shown = {0} feature(s) not shown +label.no_features_to_sort_by = No features to sort by +label.ignore_hidden = Ignore hidden columns +label.ignore_hidden_tooltip = Ignore any characters in hidden columns when matching +label.log_level = Log level +label.log_level_tooltip = Temporarily set the log level for this console. The log level will revert to {0} when this Java console is closed. +label.copy_to_clipboard = Copy to clipboard +label.copy_to_clipboard_tooltip = Copy all of the log text in this console to the system clipboard +label.startup = Startup +label.memory = Memory +label.customise_memory_settings = Customise maximum memory settings +label.memory_setting_text = New memory settings will only come into effect the next time you start Jalview +label.maximum_memory_used = Maximum memory limited to both +label.percent_of_physical_memory = Maximum percent of physical memory +label.maximum_memory = Maximum absolute memory +label.maximum_memory_tooltip = Enter memory as an integer number optionally followed by 'b', 'k', 'm', 'g' or 't' +label.adjustments_for_this_computer = Adjustments for this computer +label.memory_example_text = Maximum memory that would be used with these settings on this computer +label.memory_example_tooltip = The memory allocated to Jalview is the smaller of the percentage of physical memory (default 90%) and the maximum absolute memory (default 32GB). If your computer's memory cannot be ascertained then the maximum absolute memory defaults to 8GB (if not customised).
Jalview will always try and reserve 512MB for the OS and at least 512MB for itself. diff --combined src/jalview/analysis/AlignmentSorter.java index 81bddc2,9f9b774..6005208 --- a/src/jalview/analysis/AlignmentSorter.java +++ b/src/jalview/analysis/AlignmentSorter.java @@@ -29,12 -29,15 +29,15 @@@ import jalview.datamodel.SequenceFeatur import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.datamodel.SequenceNode; + import jalview.ext.treeviewer.TreeI; + import jalview.ext.treeviewer.TreeNodeI; import jalview.util.QuickSort; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; + import java.util.Map; /** * Routines for manipulating the order of a multiple sequence alignment TODO: @@@ -69,6 -72,8 +72,8 @@@ public class AlignmentSorte static TreeModel lastTree = null; + static TreeI lastExternalTree = null; + static boolean sortTreeAscending = true; /* @@@ -143,8 -148,8 +148,8 @@@ } // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work - List asq; - synchronized (asq = align.getSequences()) + List asq = align.getSequences(); + synchronized (asq) { for (int i = 0; i < len; i++) { @@@ -179,8 -184,8 +184,8 @@@ public static void setOrder(AlignmentI align, SequenceI[] seqs) { // NOTE: DO NOT USE align.setSequenceAt() here - it will NOT work - List algn; - synchronized (algn = align.getSequences()) + List algn = align.getSequences(); + synchronized (algn) { List tmp = new ArrayList<>(); @@@ -438,7 -443,7 +443,7 @@@ List tmp = new ArrayList<>(); - tmp = _sortByTree(tree.getTopNode(), tmp, align.getSequences()); + tmp = _sortByTree(tree.getTopNode(), tmp); if (tmp.size() != nSeq) { @@@ -461,6 -466,27 +466,27 @@@ return tmp; } + + + private static List getOrderByTree(TreeI aptxTree, + Map nodesWithBoundSeqs) + { + List seqsByTreeOrder = new ArrayList<>(); + if (!aptxTree.isEmpty()) + { + for (final Iterator iter = aptxTree + .iterateInPreOrder(); iter.hasNext();) + { + TreeNodeI treeNode = iter.next(); + seqsByTreeOrder.add(nodesWithBoundSeqs.get(treeNode)); + } + + } + return seqsByTreeOrder; + + + } + /** * Sorts the alignment by a given tree * @@@ -496,6 -522,48 +522,48 @@@ } /** + * Sorts the alignment by a given tree from Archaeopteryx + * + * @param align + * alignment to order + * @param tree + * tree which has + */ + public static void sortByTree(AlignmentI align, + Map nodesBoundToSequences, + TreeI treeI) throws IllegalArgumentException + { + List tmp = getOrderByTree(treeI, nodesBoundToSequences); + + if (!tmp.isEmpty()) + { + if (lastExternalTree != treeI) + { + sortTreeAscending = true; + lastExternalTree = treeI; + } + else + { + sortTreeAscending = !sortTreeAscending; + } + + if (sortTreeAscending) + { + setOrder(align, tmp); + } + else + { + setReverseOrder(align, + vectorSubsetToArray(tmp, align.getSequences())); + } + } + else + { + throw new IllegalArgumentException(); + } + } + + /** * DOCUMENT ME! * * @param align @@@ -535,7 -603,7 +603,7 @@@ * @return DOCUMENT ME! */ private static List _sortByTree(SequenceNode node, - List tmp, List seqset) + List tmp) { if (node == null) { @@@ -564,13 -632,15 +632,15 @@@ } else { - _sortByTree(left, tmp, seqset); - _sortByTree(right, tmp, seqset); + _sortByTree(left, tmp); + _sortByTree(right, tmp); } return tmp; } + + // Ordering Objects // Alignment.sortBy(OrderObj) - sequence of sequence pointer refs in // appropriate order @@@ -586,7 -656,7 +656,7 @@@ for (int i = 0; i < alignment.length; i++) { - ids[i] = (new Float(alignment[i].getName().substring(8))) + ids[i] = (Float.valueOf(alignment[i].getName().substring(8))) .floatValue(); } @@@ -708,15 -778,14 +778,15 @@@ if (method != FEATURE_SCORE && method != FEATURE_LABEL && method != FEATURE_DENSITY) { - String msg = String - .format("Implementation Error - sortByFeature method must be either '%s' or '%s'", - FEATURE_SCORE, FEATURE_DENSITY); + String msg = String.format( + "Implementation Error - sortByFeature method must be either '%s' or '%s'", + FEATURE_SCORE, FEATURE_DENSITY); System.err.println(msg); return; } - flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol, endCol); + flipFeatureSortIfUnchanged(method, featureTypes, groups, startCol, + endCol); SequenceI[] seqs = alignment.getSequencesArray(); @@@ -735,8 -804,8 +805,8 @@@ * get sequence residues overlapping column region * and features for residue positions and specified types */ - String[] types = featureTypes == null ? null : featureTypes - .toArray(new String[featureTypes.size()]); + String[] types = featureTypes == null ? null + : featureTypes.toArray(new String[featureTypes.size()]); List sfs = seqs[i].findFeatures(startCol + 1, endCol + 1, types); diff --combined src/jalview/appletgui/OverviewPanel.java index 09e54e7,7f9f670..685b8c9 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@@ -44,8 -44,6 +44,8 @@@ import java.awt.event.MouseListener import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; +import javax.swing.SwingUtilities; + public class OverviewPanel extends Panel implements Runnable, MouseMotionListener, MouseListener, ViewportListenerI { @@@ -116,8 -114,8 +116,8 @@@ @Override public void mouseClicked(MouseEvent evt) { - if ((evt.getModifiers() - & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) + if ((evt.getModifiersEx() + & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK) { showPopupMenu(evt); } @@@ -128,23 -126,23 +128,23 @@@ { if (od.isPositionInBox(evt.getX(), evt.getY())) { - // display drag cursor at mouse position - setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + this.getParent() + .setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { - // reset cursor - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + this.getParent().setCursor( + Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } } @Override public void mousePressed(MouseEvent evt) { - if ((evt.getModifiers() - & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) + if ((evt.getModifiersEx() + & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK) { - if (!Platform.isAMac()) + if (!Platform.isMac()) // BH was excluding JavaScript { showPopupMenu(evt); } @@@ -155,17 -153,11 +155,17 @@@ // (wait to see if it's a drag instead) // otherwise update the viewport if (!od.isPositionInBox(evt.getX(), evt.getY())) - { - draggingBox = false; + { + draggingBox = false; + + // display drag cursor at mouse position + setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); + getParent() + .setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { @@@ -180,38 -172,37 +180,38 @@@ @Override public void mouseReleased(MouseEvent evt) { + draggingBox = false; } @Override public void mouseDragged(MouseEvent evt) { - if ((evt.getModifiers() - & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) + if (Platform.isWinRightButton(evt)) { - if (!Platform.isAMac()) - { - showPopupMenu(evt); - } + showPopupMenu(evt); + return; + } + + if (SwingUtilities.isRightMouseButton(evt)) + { + return; + } + + if (draggingBox) + { + // set the mouse position as a fixed point in the box + // and drag relative to that position + od.adjustViewportFromMouse(evt.getX(), evt.getY(), + av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); } else { - if (draggingBox) - { - // set the mouse position as a fixed point in the box - // and drag relative to that position - od.adjustViewportFromMouse(evt.getX(), evt.getY(), - av.getAlignment().getHiddenSequences(), - av.getAlignment().getHiddenColumns()); - } - else - { - od.updateViewportFromMouse(evt.getX(), evt.getY(), - av.getAlignment().getHiddenSequences(), - av.getAlignment().getHiddenColumns()); - } - ap.paintAlignment(false, false); + od.updateViewportFromMouse(evt.getX(), evt.getY(), + av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); } + ap.paintAlignment(false, false); } /** @@@ -244,7 -235,7 +244,7 @@@ updateRunning = true; } - Thread thread = new Thread(this, "OverviewUpdateThread"); + Thread thread = new Thread(this, "OverviewUpdate"); thread.start(); repaint(); updateRunning = false; @@@ -338,10 -329,6 +338,10 @@@ } finally { av = null; + if (oviewCanvas != null) + { + oviewCanvas.dispose(); + } oviewCanvas = null; ap = null; od = null; diff --combined src/jalview/appletgui/PCAPanel.java index fe9d69b,6c0ed73..0a4e1c5 --- a/src/jalview/appletgui/PCAPanel.java +++ b/src/jalview/appletgui/PCAPanel.java @@@ -117,7 -117,7 +117,7 @@@ public class PCAPanel extends EmbmenuFr MessageManager.getString("label.principal_component_analysis"), 475, 400); - Thread worker = new Thread(this, "PCACalcThread"); + Thread worker = new Thread(this, "PCACalc"); worker.start(); } @@@ -134,7 -134,7 +134,7 @@@ { nuclSetting.setState(pcaModel.isNucleotide()); protSetting.setState(!pcaModel.isNucleotide()); - pcaModel.run(); + pcaModel.calculate(); // //////////////// xCombobox.select(0); yCombobox.select(1); @@@ -167,7 -167,9 +167,7 @@@ int dim2 = top - yCombobox.getSelectedIndex(); int dim3 = top - zCombobox.getSelectedIndex(); pcaModel.updateRcView(dim1, dim2, dim3); - rc.img = null; - rc.rotmat.setIdentity(); - rc.initAxes(); + rc.resetView(); rc.paint(rc.getGraphics()); } @@@ -218,7 -220,7 +218,7 @@@ ScoreModelI scoreModel = ScoreModels.getInstance() .getDefaultModel(false); pcaModel.setScoreModel(scoreModel); - new Thread(this, "PCARecalcThread").start(); + new Thread(this, "PCARecalc").start(); } } else if (evt.getSource() == protSetting) @@@ -229,7 -231,7 +229,7 @@@ ScoreModelI scoreModel = ScoreModels.getInstance() .getDefaultModel(true); pcaModel.setScoreModel(scoreModel); - new Thread(this, "PCARecalcThread").start(); + new Thread(this, "PCARecalc").start(); } } } @@@ -279,7 -281,7 +279,7 @@@ { } ; - Object[] alAndColsel = pcaModel.getSeqtrings() + Object[] alAndColsel = pcaModel.getInputData() .getAlignmentAndHiddenColumns(gc); if (alAndColsel != null && alAndColsel[0] != null) diff --combined src/jalview/bin/Jalview.java index efa5d6f,80b03ce..ea61024 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@@ -20,6 -20,27 +20,6 @@@ */ package jalview.bin; -import jalview.ext.so.SequenceOntology; -import jalview.gui.AlignFrame; -import jalview.gui.Desktop; -import jalview.gui.PromptUserConfig; -import jalview.io.AppletFormatAdapter; -import jalview.io.BioJsHTMLOutput; -import jalview.io.DataSourceType; -import jalview.io.FileFormat; -import jalview.io.FileFormatException; -import jalview.io.FileFormatI; -import jalview.io.FileLoader; -import jalview.io.HtmlSvgOutput; -import jalview.io.IdentifyFile; -import jalview.io.NewickFile; -import jalview.io.gff.SequenceOntologyFactory; -import jalview.schemes.ColourSchemeI; -import jalview.schemes.ColourSchemeProperty; -import jalview.util.MessageManager; -import jalview.util.Platform; -import jalview.ws.jws2.Jws2Discoverer; - import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@@ -37,44 -58,10 +37,44 @@@ import java.security.PermissionCollecti import java.security.Permissions; import java.security.Policy; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Vector; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.UIManager; +import javax.swing.UIManager.LookAndFeelInfo; + +import com.threerings.getdown.util.LaunchUtil; + +//import edu.stanford.ejalbert.launching.IBrowserLaunching; +import groovy.lang.Binding; +import groovy.util.GroovyScriptEngine; +import jalview.ext.so.SequenceOntology; +import jalview.gui.AlignFrame; +import jalview.gui.Desktop; +import jalview.gui.PromptUserConfig; +import jalview.io.AppletFormatAdapter; +import jalview.io.BioJsHTMLOutput; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.FileFormatException; +import jalview.io.FileFormatI; +import jalview.io.FileFormats; +import jalview.io.FileLoader; +import jalview.io.HtmlSvgOutput; +import jalview.io.IdentifyFile; +import jalview.io.NewickFile; +import jalview.io.gff.SequenceOntologyFactory; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.util.ChannelProperties; +import jalview.util.HttpUtils; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.ws.jws2.Jws2Discoverer; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; @@@ -82,29 -69,13 +82,29 @@@ /** * Main class for Jalview Application
*
- * start with java -Djava.ext.dirs=$PATH_TO_LIB$ jalview.bin.Jalview + * start with: java -classpath "$PATH_TO_LIB$/*:$PATH_TO_CLASSES$" \ + * jalview.bin.Jalview + * + * or on Windows: java -classpath "$PATH_TO_LIB$/*;$PATH_TO_CLASSES$" \ + * jalview.bin.Jalview jalview.bin.Jalview + * + * (ensure -classpath arg is quoted to avoid shell expansion of '*' and do not + * embellish '*' to e.g. '*.jar') * * @author $author$ * @version $Revision$ */ public class Jalview { + static + { + Platform.getURLCommandArguments(); + Platform.addJ2SDirectDatabaseCall("https://www.jalview.org"); + Platform.addJ2SDirectDatabaseCall("http://www.jalview.org"); + Platform.addJ2SDirectDatabaseCall("http://www.compbio.dundee.ac.uk"); + Platform.addJ2SDirectDatabaseCall("https://www.compbio.dundee.ac.uk"); + } + /* * singleton instance of this class */ @@@ -116,30 -87,22 +116,30 @@@ static { - // grab all the rights we can the JVM - Policy.setPolicy(new Policy() + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ { - @Override - public PermissionCollection getPermissions(CodeSource codesource) + // grab all the rights we can for the JVM + Policy.setPolicy(new Policy() { - Permissions perms = new Permissions(); - perms.add(new AllPermission()); - return (perms); - } + @Override + public PermissionCollection getPermissions(CodeSource codesource) + { + Permissions perms = new Permissions(); + perms.add(new AllPermission()); + return (perms); + } - @Override - public void refresh() - { - } - }); + @Override + public void refresh() + { + } + }); + } } /** @@@ -151,8 -114,8 +151,8 @@@ class FeatureFetcher { /* - * TODO: generalise to track all jalview events to orchestrate batch - * processing events. + * TODO: generalise to track all jalview events to orchestrate batch processing + * events. */ private int queued = 0; @@@ -185,13 -148,14 +185,13 @@@ af.setProgressBar(MessageManager .getString("status.das_features_being_retrived"), id); af.featureSettings_actionPerformed(null); - af.featureSettings.fetchDasFeatures(dasSources, true); af.setProgressBar(null, id); synchronized (us) { running--; } } - }, "FeatureFetcherThread").start(); + }, "FeatureFetcher").start(); } public synchronized boolean allFinished() @@@ -214,90 -178,36 +214,90 @@@ */ public static void main(String[] args) { + // setLogging(); // BH - for event debugging in JavaScript instance = new Jalview(); instance.doMain(args); } + private static void logClass(String name) + { + // BH - for event debugging in JavaScript + ConsoleHandler consoleHandler = new ConsoleHandler(); + consoleHandler.setLevel(Level.ALL); + Logger logger = Logger.getLogger(name); + logger.setLevel(Level.ALL); + logger.addHandler(consoleHandler); + } + + @SuppressWarnings("unused") + private static void setLogging() + { + + /** + * @j2sIgnore + * + */ + { + System.out.println("not in js"); + } + + // BH - for event debugging in JavaScript (Java mode only) + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ + { + Logger.getLogger("").setLevel(Level.ALL); + logClass("java.awt.EventDispatchThread"); + logClass("java.awt.EventQueue"); + logClass("java.awt.Component"); + logClass("java.awt.focus.Component"); + logClass("java.awt.focus.DefaultKeyboardFocusManager"); + } + + } + /** * @param args */ void doMain(String[] args) { - System.setSecurityManager(null); + + if (!Platform.isJS()) + { + System.setSecurityManager(null); + } + System.out .println("Java version: " + System.getProperty("java.version")); + System.out.println("Java Home: " + System.getProperty("java.home")); System.out.println(System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version")); - - ArgsParser aparser = new ArgsParser(args); - boolean headless = false; - - if (aparser.contains("help") || aparser.contains("h")) + String val = System.getProperty("sys.install4jVersion"); + if (val != null) { - showUsage(); - System.exit(0); + System.out.println("Install4j version: " + val); } - if (aparser.contains("nodisplay") || aparser.contains("nogui") - || aparser.contains("headless")) + val = System.getProperty("installer_template_version"); + if (val != null) { - System.setProperty("java.awt.headless", "true"); - headless = true; + System.out.println("Install4j template version: " + val); } + val = System.getProperty("launcher_version"); + if (val != null) + { + System.out.println("Launcher version: " + val); + } + + // report Jalview version + Cache.loadBuildProperties(true); + + ArgsParser aparser = new ArgsParser(args); + boolean headless = false; + String usrPropsFile = aparser.getValue("props"); Cache.loadProperties(usrPropsFile); // must do this before if (usrPropsFile != null) @@@ -306,42 -216,20 +306,42 @@@ "CMD [-props " + usrPropsFile + "] executed successfully!"); } - // anything else! - - final String jabawsUrl = aparser.getValue("jabaws"); - if (jabawsUrl != null) + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ { - try + if (aparser.contains("help") || aparser.contains("h")) { - Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl); - System.out.println( - "CMD [-jabaws " + jabawsUrl + "] executed successfully!"); - } catch (MalformedURLException e) + showUsage(); + System.exit(0); + } + if (aparser.contains("nodisplay") || aparser.contains("nogui") + || aparser.contains("headless")) { - System.err.println( - "Invalid jabaws parameter: " + jabawsUrl + " ignored"); + System.setProperty("java.awt.headless", "true"); + headless = true; + } + // anything else! + + // allow https handshakes to download intermediate certs if necessary + System.setProperty("com.sun.security.enableAIAcaIssuers", "true"); + + final String jabawsUrl = aparser.getValue("jabaws"); + if (jabawsUrl != null) + { + try + { + Jws2Discoverer.getDiscoverer().setPreferredUrl(jabawsUrl); + System.out.println( + "CMD [-jabaws " + jabawsUrl + "] executed successfully!"); + } catch (MalformedURLException e) + { + System.err.println( + "Invalid jabaws parameter: " + jabawsUrl + " ignored"); + } } } @@@ -356,10 -244,6 +356,10 @@@ else { System.out.println("Executing setprop argument: " + defs); + if (Platform.isJS()) + { + Cache.setProperty(defs.substring(0, p), defs.substring(p + 1)); + } // DISABLED FOR SECURITY REASONS // TODO: add a property to allow properties to be overriden by cli args // Cache.setProperty(defs.substring(0,p), defs.substring(p+1)); @@@ -373,150 -257,114 +373,150 @@@ } System.setProperty("http.agent", "Jalview Desktop/" + Cache.getDefault("VERSION", "Unknown")); + try { - Cache.initLogger(); + Console.initLogger(); } catch (NoClassDefFoundError error) { error.printStackTrace(); System.out.println("\nEssential logging libraries not found." - + "\nUse: java -Djava.ext.dirs=$PATH_TO_LIB$ jalview.bin.Jalview"); + + "\nUse: java -classpath \"$PATH_TO_LIB$/*:$PATH_TO_CLASSES$\" jalview.bin.Jalview"); System.exit(0); } desktop = null; - try - { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ex) - { - } - if (Platform.isAMac()) - { - System.setProperty("com.apple.mrj.application.apple.menu.about.name", - "Jalview"); - System.setProperty("apple.laf.useScreenMenuBar", "true"); - try - { - UIManager.setLookAndFeel( - ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel()); - } catch (Throwable e) - { - System.err.println( - "Failed to set QuaQua look and feel: " + e.toString()); - } - } + setLookAndFeel(); /* - * configure 'full' SO model if preferences say to, - * else use the default (SO Lite) + * configure 'full' SO model if preferences say to, else use the default (full SO) + * - as JS currently doesn't have OBO parsing, it must use 'Lite' version */ - if (Cache.getDefault("USE_FULL_SO", false)) + boolean soDefault = !Platform.isJS(); + if (Cache.getDefault("USE_FULL_SO", soDefault)) { SequenceOntologyFactory.setInstance(new SequenceOntology()); } if (!headless) { + Desktop.nosplash = aparser.contains("nosplash"); desktop = new Desktop(); desktop.setInBatchMode(true); // indicate we are starting up - desktop.setVisible(true); - desktop.startServiceDiscovery(); - if (!aparser.contains("nousagestats")) + + try { - startUsageStats(desktop); - } - else + JalviewTaskbar.setTaskbar(this); + } catch (Exception e) { - System.err.println("CMD [-nousagestats] executed successfully!"); + Console.info("Cannot set Taskbar"); + Console.error(e.getMessage()); + // e.printStackTrace(); + } catch (Throwable t) + { + Console.info("Cannot set Taskbar"); + Console.error(t.getMessage()); + // t.printStackTrace(); } - if (!aparser.contains("noquestionnaire")) + // set Proxy settings before all the internet calls + Cache.setProxyPropertiesFromPreferences(); + + desktop.setVisible(true); + + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ { - String url = aparser.getValue("questionnaire"); - if (url != null) + if (!aparser.contains("nowebservicediscovery")) { - // Start the desktop questionnaire prompter with the specified - // questionnaire - Cache.log.debug("Starting questionnaire url at " + url); - desktop.checkForQuestionnaire(url); - System.out.println( - "CMD questionnaire[-" + url + "] executed successfully!"); + desktop.startServiceDiscovery(); + } + if (!aparser.contains("nousagestats")) + { + startUsageStats(desktop); } else { - if (Cache.getProperty("NOQUESTIONNAIRES") == null) + System.err.println("CMD [-nousagestats] executed successfully!"); + } + + if (!aparser.contains("noquestionnaire")) + { + String url = aparser.getValue("questionnaire"); + if (url != null) { // Start the desktop questionnaire prompter with the specified // questionnaire - // String defurl = - // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl"; - // // - String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl"; - Cache.log.debug( - "Starting questionnaire with default url: " + defurl); - desktop.checkForQuestionnaire(defurl); + Console.debug("Starting questionnaire url at " + url); + desktop.checkForQuestionnaire(url); + System.out.println("CMD questionnaire[-" + url + + "] executed successfully!"); + } + else + { + if (Cache.getProperty("NOQUESTIONNAIRES") == null) + { + // Start the desktop questionnaire prompter with the specified + // questionnaire + // String defurl = + // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl"; + // // + String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl"; + Console.debug( + "Starting questionnaire with default url: " + defurl); + desktop.checkForQuestionnaire(defurl); + } } } - } - else - { - System.err.println("CMD [-noquestionnaire] executed successfully!"); - } + else + { + System.err + .println("CMD [-noquestionnaire] executed successfully!"); + } - if (!aparser.contains("nonews")) - { - desktop.checkForNews(); + if (!aparser.contains("nonews") + || Cache.getProperty("NONEWS") == null) + { + desktop.checkForNews(); + } + + if (!aparser.contains("nohtmltemplates") + || Cache.getProperty("NOHTMLTEMPLATES") == null) + { + BioJsHTMLOutput.updateBioJS(); + } } + } - BioJsHTMLOutput.updateBioJS(); + // Move any new getdown-launcher-new.jar into place over old + // getdown-launcher.jar + String appdirString = System.getProperty("getdownappdir"); + if (appdirString != null && appdirString.length() > 0) + { + final File appdir = new File(appdirString); + new Thread() + { + @Override + public void run() + { + LaunchUtil.upgradeGetdown( + new File(appdir, "getdown-launcher-old.jar"), + new File(appdir, "getdown-launcher.jar"), + new File(appdir, "getdown-launcher-new.jar")); + } + }.start(); } String file = null, data = null; FileFormatI format = null; DataSourceType protocol = null; FileLoader fileLoader = new FileLoader(!headless); - Vector getFeatures = null; // vector of das source nicknames to - // fetch - // features from - // loading is done. + String groovyscript = null; // script to execute after all loading is // completed one way or another // extract groovy argument and execute if necessary @@@ -528,6 -376,91 +528,6 @@@ System.out.println("No files to open!"); System.exit(1); } - String vamsasImport = aparser.getValue("vdoc"); - String vamsasSession = aparser.getValue("vsess"); - if (vamsasImport != null || vamsasSession != null) - { - if (desktop == null || headless) - { - System.out.println( - "Headless vamsas sessions not yet supported. Sorry."); - System.exit(1); - } - // if we have a file, start a new session and import it. - boolean inSession = false; - if (vamsasImport != null) - { - try - { - DataSourceType viprotocol = AppletFormatAdapter - .checkProtocol(vamsasImport); - if (viprotocol == DataSourceType.FILE) - { - inSession = desktop.vamsasImport(new File(vamsasImport)); - } - else if (viprotocol == DataSourceType.URL) - { - inSession = desktop.vamsasImport(new URL(vamsasImport)); - } - - } catch (Exception e) - { - System.err.println("Exeption when importing " + vamsasImport - + " as a vamsas document."); - e.printStackTrace(); - } - if (!inSession) - { - System.err.println("Failed to import " + vamsasImport - + " as a vamsas document."); - } - else - { - System.out.println("Imported Successfully into new session " - + desktop.getVamsasApplication().getCurrentSession()); - } - } - if (vamsasSession != null) - { - if (vamsasImport != null) - { - // close the newly imported session and import the Jalview specific - // remnants into the new session later on. - desktop.vamsasStop_actionPerformed(null); - } - // now join the new session - try - { - if (desktop.joinVamsasSession(vamsasSession)) - { - System.out.println( - "Successfully joined vamsas session " + vamsasSession); - } - else - { - System.err.println("WARNING: Failed to join vamsas session " - + vamsasSession); - } - } catch (Exception e) - { - System.err.println( - "ERROR: Failed to join vamsas session " + vamsasSession); - e.printStackTrace(); - } - if (vamsasImport != null) - { - // the Jalview specific remnants can now be imported into the new - // session at the user's leisure. - Cache.log.info( - "Skipping Push for import of data into existing vamsas session."); // TODO: - // enable - // this - // when - // debugged - // desktop.getVamsasApplication().push_update(); - } - } - } long progress = -1; // Finally, deal with the remaining input data. if (file != null) @@@ -541,22 -474,14 +541,22 @@@ } System.out.println("CMD [-open " + file + "] executed successfully!"); - if (!file.startsWith("http://")) + if (!Platform.isJS()) + /** + * ignore in JavaScript -- can't just file existence - could load it? + * + * @j2sIgnore + */ { - if (!(new File(file)).exists()) + if (!HttpUtils.startsWithHttpOrHttps(file)) { - System.out.println("Can't find " + file); - if (headless) + if (!(new File(file)).exists()) { - System.exit(1); + System.out.println("Can't find " + file); + if (headless) + { + System.exit(1); + } } } } @@@ -585,8 -510,8 +585,8 @@@ { data.replaceAll("%20", " "); - ColourSchemeI cs = ColourSchemeProperty - .getColourScheme(af.getViewport().getAlignment(), data); + ColourSchemeI cs = ColourSchemeProperty.getColourScheme( + af.getViewport(), af.getViewport().getAlignment(), data); if (cs != null) { @@@ -670,6 -595,27 +670,6 @@@ // TODO - load PDB structure(s) to alignment JAL-629 // (associate with identical sequence in alignment, or a specified // sequence) - - getFeatures = checkDasArguments(aparser); - if (af != null && getFeatures != null) - { - FeatureFetcher ff = startFeatureFetching(getFeatures); - if (ff != null) - { - while (!ff.allFinished() || af.operationInProgress()) - { - // wait around until fetching is finished. - try - { - Thread.sleep(100); - } catch (Exception e) - { - - } - } - } - getFeatures = null; // have retrieved features - forget them now. - } if (groovyscript != null) { // Execute the groovy script after we've done all the rendering stuff @@@ -746,37 -692,16 +746,37 @@@ af.createEPS(outputFile); continue; } - - if (af.saveAlignment(file, format)) + FileFormatI outFormat = null; + try { - System.out.println("Written alignment in " + format - + " format to " + file); + outFormat = FileFormats.getInstance().forName(outputFormat); + } catch (Exception formatP) + { + System.out.println("Couldn't parse " + outFormat + + " as a valid Jalview format string."); } - else + if (outFormat != null) { - System.out.println("Error writing file " + file + " in " - + format + " format!!"); + if (!outFormat.isWritable()) + { + System.out.println( + "This version of Jalview does not support alignment export as " + + outputFormat); + } + else + { + af.saveAlignment(file, outFormat); + if (af.isSaveAlignmentSuccessful()) + { + System.out.println("Written alignment in " + + outFormat.getName() + " format to " + file); + } + else + { + System.out.println("Error writing file " + file + " in " + + outFormat.getName() + " format!!"); + } + } } } @@@ -792,30 -717,28 +792,30 @@@ // And the user // //////////////////// - if (!headless && file == null && vamsasImport == null - && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true)) + if (!Platform.isJS() && !headless && file == null + && Cache.getDefault("SHOW_STARTUP_FILE", true)) + /** + * Java only + * + * @j2sIgnore + */ { - file = jalview.bin.Cache.getDefault("STARTUP_FILE", - jalview.bin.Cache.getDefault("www.jalview.org", - "http://www.jalview.org") - + "/examples/exampleFile_2_7.jar"); - if (file.equals( - "http://www.jalview.org/examples/exampleFile_2_3.jar")) + file = Cache.getDefault("STARTUP_FILE", + Cache.getDefault("www.jalview.org", "https://www.jalview.org") + + "/examples/exampleFile_2_7.jvp"); + if (file.equals("http://www.jalview.org/examples/exampleFile_2_3.jar") + || file.equals( + "http://www.jalview.org/examples/exampleFile_2_7.jar")) { + file.replace("http:", "https:"); // hardwire upgrade of the startup file - file.replace("_2_3.jar", "_2_7.jar"); + file.replace("_2_3", "_2_7"); + file.replace("2_7.jar", "2_7.jvp"); // and remove the stale setting - jalview.bin.Cache.removeProperty("STARTUP_FILE"); + Cache.removeProperty("STARTUP_FILE"); } - protocol = DataSourceType.FILE; - - if (file.indexOf("http:") > -1) - { - protocol = DataSourceType.URL; - } + protocol = AppletFormatAdapter.checkProtocol(file); if (file.endsWith(".jar")) { @@@ -834,9 -757,20 +834,9 @@@ startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol, format); - getFeatures = checkDasArguments(aparser); // extract groovy arguments before anything else. } - // If the user has specified features to be retrieved, - // or a groovy script to be executed, do them if they - // haven't been done already - // fetch features for the default alignment - if (getFeatures != null) - { - if (startUpAlframe != null) - { - startFeatureFetching(getFeatures); - } - } + // Once all other stuff is done, execute any groovy scripts (in order) if (groovyscript != null) { @@@ -863,210 -797,6 +863,210 @@@ } } + private static void setLookAndFeel() + { + // property laf = "crossplatform", "system", "gtk", "metal", "nimbus" or + // "mac" + // If not set (or chosen laf fails), use the normal SystemLaF and if on Mac, + // try Quaqua/Vaqua. + String lafProp = System.getProperty("laf"); + String lafSetting = Cache.getDefault("PREFERRED_LAF", null); + String laf = "none"; + if (lafProp != null) + { + laf = lafProp; + } + else if (lafSetting != null) + { + laf = lafSetting; + } + boolean lafSet = false; + switch (laf) + { + case "crossplatform": + lafSet = setCrossPlatformLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "system": + lafSet = setSystemLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "gtk": + lafSet = setGtkLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "metal": + lafSet = setMetalLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "nimbus": + lafSet = setNimbusLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "quaqua": + lafSet = setQuaquaLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "vaqua": + lafSet = setVaquaLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "mac": + lafSet = setMacLookAndFeel(); + if (!lafSet) + { + Console.error("Could not set requested laf=" + laf); + } + break; + case "none": + break; + default: + Console.error("Requested laf=" + laf + " not implemented"); + } + if (!lafSet) + { + setSystemLookAndFeel(); + if (Platform.isLinux()) + { + setMetalLookAndFeel(); + } + if (Platform.isMac()) + { + setMacLookAndFeel(); + } + } + } + + private static boolean setCrossPlatformLookAndFeel() + { + boolean set = false; + try + { + UIManager.setLookAndFeel( + UIManager.getCrossPlatformLookAndFeelClassName()); + set = true; + } catch (Exception ex) + { + Console.error("Unexpected Look and Feel Exception"); + Console.error(ex.getMessage()); + Console.debug(Cache.getStackTraceString(ex)); + } + return set; + } + + private static boolean setSystemLookAndFeel() + { + boolean set = false; + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + set = true; + } catch (Exception ex) + { + Console.error("Unexpected Look and Feel Exception"); + Console.error(ex.getMessage()); + Console.debug(Cache.getStackTraceString(ex)); + } + return set; + } + + private static boolean setSpecificLookAndFeel(String name, + String className, boolean nameStartsWith) + { + boolean set = false; + try + { + for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) + { + if (info.getName() != null && nameStartsWith + ? info.getName().toLowerCase(Locale.ROOT) + .startsWith(name.toLowerCase(Locale.ROOT)) + : info.getName().toLowerCase(Locale.ROOT) + .equals(name.toLowerCase(Locale.ROOT))) + { + className = info.getClassName(); + break; + } + } + UIManager.setLookAndFeel(className); + set = true; + } catch (Exception ex) + { + Console.error("Unexpected Look and Feel Exception"); + Console.error(ex.getMessage()); + Console.debug(Cache.getStackTraceString(ex)); + } + return set; + } + + private static boolean setGtkLookAndFeel() + { + return setSpecificLookAndFeel("gtk", + "com.sun.java.swing.plaf.gtk.GTKLookAndFeel", true); + } + + private static boolean setMetalLookAndFeel() + { + return setSpecificLookAndFeel("metal", + "javax.swing.plaf.metal.MetalLookAndFeel", false); + } + + private static boolean setNimbusLookAndFeel() + { + return setSpecificLookAndFeel("nimbus", + "javax.swing.plaf.nimbus.NimbusLookAndFeel", false); + } + + private static boolean setQuaquaLookAndFeel() + { + return setSpecificLookAndFeel("quaqua", + ch.randelshofer.quaqua.QuaquaManager.getLookAndFeel().getClass() + .getName(), + false); + } + + private static boolean setVaquaLookAndFeel() + { + return setSpecificLookAndFeel("vaqua", + "org.violetlib.aqua.AquaLookAndFeel", false); + } + + private static boolean setMacLookAndFeel() + { + boolean set = false; + System.setProperty("com.apple.mrj.application.apple.menu.about.name", + ChannelProperties.getProperty("app_name")); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + set = setQuaquaLookAndFeel(); + if ((!set) || !UIManager.getLookAndFeel().getClass().toString() + .toLowerCase(Locale.ROOT).contains("quaqua")) + { + set = setVaquaLookAndFeel(); + } + return set; + } + private static void showUsage() { System.out.println( @@@ -1103,11 -833,16 +1103,11 @@@ // (quote the 'PROPERTY=VALUE' pair to ensure spaces are // passed in correctly)" + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n" - + "-dasserver nickname=URL\tAdd and enable a das server with given nickname\n\t\t\t(alphanumeric or underscores only) for retrieval of features for all alignments.\n" - + "\t\t\tSources that also support the sequence command may be specified by prepending the URL with sequence:\n" - + "\t\t\t e.g. sequence:http://localdas.somewhere.org/das/source)\n" + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n" - // + - // "-vdoc vamsas-document\tImport vamsas document into new - // session or join existing session with same URN\n" - // + "-vses vamsas-session\tJoin session with given URN\n" + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n" - + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n"); + + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n" + + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n" + + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n"); } private static void startUsageStats(final Desktop desktop) @@@ -1125,17 -860,17 +1125,17 @@@ @Override public void run() { - Cache.log.debug( + Console.debug( "Initialising googletracker for usage stats."); Cache.initGoogleTracker(); - Cache.log.debug("Tracking enabled."); + Console.debug("Tracking enabled."); } }, new Runnable() { @Override public void run() { - Cache.log.debug("Not enabling Google Tracking."); + Console.debug("Not enabling Google Tracking."); } }, null, true); desktop.addDialogThread(prompter); @@@ -1235,7 -970,7 +1235,7 @@@ } try { - Map vbinding = new HashMap<>(); + Map vbinding = new HashMap<>(); vbinding.put("Jalview", this); if (af != null) { @@@ -1259,6 -994,94 +1259,6 @@@ } } - /** - * Check commandline for any das server definitions or any fetchfrom switches - * - * @return vector of DAS source nicknames to retrieve from - */ - private static Vector checkDasArguments(ArgsParser aparser) - { - Vector source = null; - String data; - String locsources = Cache.getProperty(Cache.DAS_LOCAL_SOURCE); - while ((data = aparser.getValue("dasserver", true)) != null) - { - String nickname = null; - String url = null; - int pos = data.indexOf('='); - // determine capabilities - if (pos > 0) - { - nickname = data.substring(0, pos); - } - url = data.substring(pos + 1); - if (url != null && (url.startsWith("http:") - || url.startsWith("sequence:http:"))) - { - if (nickname == null) - { - nickname = url; - } - if (locsources == null) - { - locsources = ""; - } - else - { - locsources += "\t"; - } - locsources = locsources + nickname + "|" + url; - System.err.println( - "NOTE! dasserver parameter not yet really supported (got args of " - + nickname + "|" + url); - if (source == null) - { - source = new Vector<>(); - } - source.addElement(nickname); - } - System.out.println( - "CMD [-dasserver " + data + "] executed successfully!"); - } // loop until no more server entries are found. - if (locsources != null && locsources.indexOf('|') > -1) - { - Cache.log.debug("Setting local source list in properties file to:\n" - + locsources); - Cache.setProperty(Cache.DAS_LOCAL_SOURCE, locsources); - } - while ((data = aparser.getValue("fetchfrom", true)) != null) - { - System.out.println("adding source '" + data + "'"); - if (source == null) - { - source = new Vector<>(); - } - source.addElement(data); - } - return source; - } - - /** - * start a feature fetcher for every alignment frame - * - * @param dasSources - */ - private FeatureFetcher startFeatureFetching( - final Vector dasSources) - { - FeatureFetcher ff = new FeatureFetcher(); - AlignFrame afs[] = Desktop.getAlignFrames(); - if (afs == null || afs.length == 0) - { - return null; - } - for (int i = 0; i < afs.length; i++) - { - ff.addFetcher(afs[i], dasSources); - } - return ff; - } - public static boolean isHeadlessMode() { String isheadless = System.getProperty("java.awt.headless"); diff --combined src/jalview/ext/archaeopteryx/AptxFrame.java index 0000000,e321c90..fca6e49 mode 000000,100644..100644 --- a/src/jalview/ext/archaeopteryx/AptxFrame.java +++ b/src/jalview/ext/archaeopteryx/AptxFrame.java @@@ -1,0 -1,466 +1,510 @@@ + package jalview.ext.archaeopteryx; + + import jalview.bin.Cache; + import jalview.ext.treeviewer.TreeControlsI; + import jalview.ext.treeviewer.TreeFrameI; + import jalview.ext.treeviewer.TreeI; + import jalview.ext.treeviewer.TreePanelI; + import jalview.ext.treeviewer.TreeViewerBindingI; + import jalview.ext.treeviewer.TreeViewerUtils; + import jalview.gui.Desktop; -import jalview.gui.EPSOptions; ++import jalview.gui.LineartOptions; + import jalview.io.JalviewFileChooser; + import jalview.io.JalviewFileView; + import jalview.util.ImageMaker; + import jalview.util.MessageManager; ++import jalview.util.Platform; ++import jalview.util.ImageMaker.TYPE; + + import java.awt.Component; + import java.awt.Container; + import java.awt.Dimension; + import java.awt.Event; + import java.awt.Font; + import java.awt.Image; + import java.awt.MenuComponent; + import java.awt.event.ActionEvent; + import java.awt.event.ActionListener; + import java.io.FileOutputStream; ++import java.util.concurrent.atomic.AtomicBoolean; + + import javax.accessibility.AccessibleContext; + import javax.swing.JLayeredPane; + import javax.swing.JMenu; + import javax.swing.JMenuBar; + import javax.swing.JMenuItem; + import javax.swing.JRootPane; + import javax.swing.JSeparator; + import javax.swing.event.InternalFrameListener; + + import org.forester.archaeopteryx.Archaeopteryx; + import org.forester.archaeopteryx.Configuration; + import org.forester.archaeopteryx.MainFrame; + import org.forester.phylogeny.Phylogeny; + import org.jibble.epsgraphics.EpsGraphics2D; + + public class AptxFrame implements TreeFrameI + { + private final MainFrame aptxFrame; + + private TreeViewerBindingI viewBinding; + + private TreePanelI aptxPanel; + + private TreeControlsI aptxControls; + + + public AptxFrame(Phylogeny tree, Configuration aptxConfig, + String treeTitle) + { + this(Archaeopteryx.createApplication(tree, + aptxConfig, + treeTitle)); + + } + + + public AptxFrame(MainFrame aptx) + { + + aptxFrame = aptx; + aptxPanel = new AptxTreePanel( + aptxFrame.getMainPanel().getCurrentTreePanel()); + aptxControls = new AptxControlPanel( + aptxFrame.getMainPanel().getControlPanel()); + adaptAptxGui(aptxFrame); + + } + + /** + * Hides certain redundant Archaeopteryx GUI elements such as the menu items + * for reading in trees and adds extra items related to Jalview such as the + * tree sorting menu item. + * + * + * @param aptxFrame + */ + protected void adaptAptxGui(MainFrame aptxFrame) + { + JMenuBar frameBar = aptxFrame.getJMenuBar(); + boolean epsAdded = false; + for (int i = 0; i < frameBar.getMenuCount();i++) { + JMenu menu = frameBar.getMenu(i); + int menuCount = menu.getMenuComponentCount(); + + if (menu.getText().contains("File")) + { + // hide all "Read from ..." and "New" menu items and any Separators that + // come directly after them + Component previousComp = null; + for (int x = 0; x < menuCount; x++) + { + Component menuItem = menu.getMenuComponent(x); + if (previousComp instanceof JMenuItem) + { + JMenuItem previousMenuItem = (JMenuItem) previousComp; + if (previousMenuItem.getText().startsWith("Read") + || previousMenuItem.getText() + .startsWith("New") + || previousMenuItem.getText() + .startsWith("Close Tab")) + { + previousComp.setVisible(false); + + if (menuItem instanceof JSeparator) + { + menuItem.setVisible(false); + } + + } + + if ((!epsAdded) && previousMenuItem.getText() + .startsWith("Export to")) + { + JMenuItem exportEps = new JMenuItem("Export to EPS file..."); + menu.add(exportEps, x); + exportEps.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + epsTree_actionPerformed(e); + + } + + }); + epsAdded = true; + + } + } + previousComp = menuItem; + } + } + else if (menu.getText().contains("Inference")) + { + menu.setVisible(false); + } + else if (menu.getText().contains("View")) + { + menu.addSeparator(); + + JMenuItem sortByTree = new JMenuItem("Sort alignment by tree"); + JMenuItem refreshJalview = new JMenuItem( + "Filter alignment to show only currently visible sequences"); + JMenuItem hideCollapsed = new JMenuItem( + "Hide sequences of collapsed nodes"); + + refreshJalview.setFont(menu.getFont()); + refreshJalview.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + TreeViewerBindingI bindingManager = TreeViewerUtils + .getActiveTreeViews().get(AptxFrame.this); + bindingManager.actionPerformed(e); + } + }); + + sortByTree.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + TreeViewerBindingI bindingManager = TreeViewerUtils + .getActiveTreeViews().get(AptxFrame.this); + bindingManager.sortByTree_actionPerformed(); + + } + + }); + + hideCollapsed.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + TreeViewerBindingI bindingManager = TreeViewerUtils + .getActiveTreeViews().get(AptxFrame.this); + bindingManager.hideCollapsedSequences_actionPerformed(); + + } + + }); + + menu.add(sortByTree); + menu.add(refreshJalview); + menu.add(hideCollapsed); + + sortByTree.setFont(menu.getFont()); + + + + } + + } + // aptxFrame.validate(); + } + + public void epsTree_actionPerformed(ActionEvent e) + { + boolean accurateText = true; ++ final long messageId = System.currentTimeMillis(); + + String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING", + "Prompt each time"); ++ if (Platform.isJS()) ++ { ++ renderStyle = "Text"; ++ } ++ ++ AtomicBoolean textSelected = new AtomicBoolean( ++ !"Lineart".equals(renderStyle)); + + // If we need to prompt, and if the GUI is visible then + // Prompt for EPS rendering style + if (renderStyle.equalsIgnoreCase("Prompt each time") - && !(System.getProperty("java.awt.headless") != null && System - .getProperty("java.awt.headless").equals("true"))) ++ && !Platform.isHeadless()) ++ // && !(System.getProperty("java.awt.headless") != null && System ++ // .getProperty("java.awt.headless").equals("true"))) + { - EPSOptions eps = new EPSOptions(); - renderStyle = eps.getValue(); - - if (renderStyle == null || eps.cancelled) ++ LineartOptions epsOption = new LineartOptions(TYPE.EPS.getName(), ++ textSelected); ++ epsOption.setResponseAction(1, new Runnable() + { - return; - } ++ @Override ++ public void run() ++ { ++ // report canceled ++ // setStatus(MessageManager.formatMessage( ++ // "status.cancelled_image_export_operation", ++ // TYPE.EPS.getName()), messageId); ++ } ++ }); ++ epsOption.setResponseAction(0, new Runnable() ++ { ++ @Override ++ public void run() ++ { ++ // TODO Auto-generated method stub + - } ++ String renderStyle = epsOption.getValue(); + - if (renderStyle.equalsIgnoreCase("text")) ++ if (renderStyle == null) ++ { ++ return; ++ } ++ ++ boolean accurateText = true; ++ if (renderStyle.equalsIgnoreCase("text")) ++ { ++ accurateText = false; ++ } ++ doExport(accurateText); ++ ++ } ++ ++ }); ++ epsOption.showDialog(); ++ } ++ else + { - accurateText = false; ++ doExport(accurateText); + } ++ } + ++ protected void doExport(boolean accurateText) ++ { + int width = getTreePanel().getWidth(); + int height = getTreePanel().getHeight(); + + try + { + JalviewFileChooser chooser = new JalviewFileChooser( + ImageMaker.EPS_EXTENSION, ImageMaker.EPS_EXTENSION); + chooser.setFileView(new JalviewFileView()); + chooser.setDialogTitle( + MessageManager.getString("label.create_eps_from_tree")); + chooser.setToolTipText(MessageManager.getString("action.save")); + + int value = chooser.showSaveDialog(aptxFrame); + + if (value != JalviewFileChooser.APPROVE_OPTION) + { + return; + } + + Cache.setProperty("LAST_DIRECTORY", + chooser.getSelectedFile().getParent()); + + FileOutputStream out = new FileOutputStream( + chooser.getSelectedFile()); + EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, + height); + + pg.setAccurateTextMode(accurateText); + + getTreePanel().paintToFile(pg, width, height); + + pg.flush(); + pg.close(); + } catch (Exception ex) + { + ex.printStackTrace(); + } + } + @Override + public TreePanelI getTreePanel() + { + return aptxPanel; + } + + @Override + public TreeI getTree() + { + return aptxPanel.getTree(); + } + + @Override + public void enableMultipleTrees() + { + aptxFrame.activateSaveAllIfNeeded(); + + } + + @Override + public int getNumberOfTrees() + { + return aptxFrame.getMainPanel().getTabbedPane().getTabCount(); + } + + @Override + public TreeControlsI getTreeControls() + { + return aptxControls; + } + + @Override + public AccessibleContext getAccessibleContext() + { + return aptxFrame.getAccessibleContext(); + } + + @Override + public JRootPane getRootPane() + { + return aptxFrame.getRootPane(); + } + + @Override + public void setContentPane(Container contentPane) + { + aptxFrame.setContentPane(contentPane); + + } + + @Override + public Container getContentPane() + { + return aptxFrame.getContentPane(); + } + + @Override + public void setLayeredPane(JLayeredPane layeredPane) + { + aptxFrame.setLayeredPane(layeredPane); + + } + + @Override + public JLayeredPane getLayeredPane() + { + return aptxFrame.getLayeredPane(); + } + + @Override + public void setGlassPane(Component glassPane) + { + aptxFrame.setGlassPane(glassPane); + + } + + @Override + public Component getGlassPane() + { + return aptxFrame.getGlassPane(); + } + + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, + int width, int height) + { + return aptxFrame.imageUpdate(img, infoflags, x, y, width, height); + } + + @Override + public Font getFont() + { + return aptxFrame.getFont(); + } + + @Override + public void remove(MenuComponent comp) + { + aptxFrame.remove(comp); + + } + + @Deprecated + @Override + public boolean postEvent(Event evt) + { + return aptxFrame.postEvent(evt); + } + + @Override + public void addFrameListener(InternalFrameListener listener) + { + aptxFrame.addInternalFrameListener(listener); + + } + + @Override + public void removeFrameListener(InternalFrameListener listener) + { + aptxFrame.removeInternalFrameListener(listener); + + } + + @Override + public InternalFrameListener[] getFrameListeners() + { + return aptxFrame.getInternalFrameListeners(); + + } + + @Override + public void repaint() + { + aptxFrame.repaint(); + + } + + @Override + public void setMinimumSize(Dimension dimension) + { + aptxFrame.setMinimumSize(dimension); + + } + + @Override + public boolean isShowing() + { + return aptxFrame.isShowing(); + } + + @Override + public Container getTopLevelAncestor() + { + return aptxFrame.getTopLevelAncestor(); + } + + @Override + public void addFrameToJalview(String title, boolean makeVisible, + int width, int height, boolean resizable, boolean ignoreMinSize) + { + Desktop.addInternalFrame(aptxFrame, title, makeVisible, width, height, + resizable, ignoreMinSize); + + } + + @Override + public TreeViewerBindingI getViewBinding() + { + return viewBinding; + } + + @Override + public void setViewBinding(TreeViewerBindingI alignmentBinding) + { + viewBinding = alignmentBinding; + } + + + @Override + public void setMaximumSize(Dimension maximumSize) + { + aptxFrame.setMaximumSize(maximumSize); + + } + + @Override + public void setPreferredSize(Dimension preferredSize) + { + aptxFrame.setPreferredSize(preferredSize); + + } + + } diff --combined src/jalview/ext/treeviewer/JalviewBinding.java index 0000000,987e3a6..fd1735e mode 000000,100644..100644 --- a/src/jalview/ext/treeviewer/JalviewBinding.java +++ b/src/jalview/ext/treeviewer/JalviewBinding.java @@@ -1,0 -1,763 +1,763 @@@ + package jalview.ext.treeviewer; + + import jalview.analysis.AlignmentSorter; + import jalview.analysis.Conservation; + import jalview.api.AlignViewportI; + import jalview.commands.CommandI; + import jalview.commands.OrderCommand; + import jalview.datamodel.AlignmentI; + import jalview.datamodel.ColumnSelection; + import jalview.datamodel.HiddenColumns; + import jalview.datamodel.SequenceGroup; + import jalview.datamodel.SequenceI; + import jalview.gui.AlignViewport; + import jalview.gui.AlignmentPanel; + import jalview.gui.Desktop; + import jalview.gui.JvOptionPane; + import jalview.gui.PaintRefresher; + import jalview.schemes.ColourSchemeI; + import jalview.schemes.ColourSchemeProperty; + import jalview.schemes.UserColourScheme; + import jalview.structure.SelectionSource; + import jalview.structure.StructureSelectionManager; + import jalview.util.MappingUtils; + import jalview.util.MessageManager; + import jalview.viewmodel.AlignmentViewport; + + import java.awt.Color; + import java.awt.Rectangle; + import java.awt.event.ActionEvent; + import java.awt.event.InputEvent; + import java.awt.event.MouseEvent; + import java.util.ArrayList; + import java.util.Collection; + import java.util.HashSet; + import java.util.List; + import java.util.Map; + + import javax.swing.SwingUtilities; + import javax.swing.event.InternalFrameAdapter; + import javax.swing.event.InternalFrameEvent; + + /** + * Class for binding the tree viewer to the Jalview alignment that it originates + * from, meaning that selecting sequences in the tree viewer also selects them + * in the alignment view and vice versa. + * + * @author kjvanderheide + * + */ + public final class JalviewBinding + implements TreeViewerBindingI + { + private final TreeFrameI aptxFrame; + + private TreePanelI treeView; + + private AlignmentViewport parentAvport; + + private final StructureSelectionManager ssm; + + private Map sequencesBoundToNodes; + + private Map nodesBoundToSequences; + + private float rootX; + + private float furthestNodeX; + + private int nrTreeGroups = 0; + + private boolean applyToAllViews = false; + + /** + * + * @param archaeopteryx + * + * @param jalviewAlignmentViewport + * alignment viewport from which the tree was calculated. + * + * @param alignMappedToNodes + * map with sequences used to calculate the tree and matching tree + * nodes as key, value pair respectively. + * + * @param nodesMappedToAlign + * map with tree nodes and matching sequences used to calculate the + * tree as key, value pair respectively. + */ + public JalviewBinding(final TreeFrameI archaeopteryx, + final AlignmentViewport jalviewAlignmentViewport, + final Map alignMappedToNodes, + final Map nodesMappedToAlign) + { + + if (archaeopteryx.getNumberOfTrees() > 1) + { + JvOptionPane.showMessageDialog(Desktop.desktop, + MessageManager.getString("label.tabs_detected_archaeopteryx"), + MessageManager.getString("label.problem_reading_tree_file"), + JvOptionPane.WARNING_MESSAGE); + + } + + // deal with/prohibit null values here as that will cause problems + aptxFrame = archaeopteryx; + parentAvport = jalviewAlignmentViewport; + sequencesBoundToNodes = alignMappedToNodes; + nodesBoundToSequences = nodesMappedToAlign; + + treeView = archaeopteryx.getTreePanel(); + ssm = parentAvport.getStructureSelectionManager(); + + aptxFrame.setViewBinding(this); + ssm.addSelectionListener(this); + treeView.addMouseListener(this); + treeView.registerWithPaintRefresher( + parentAvport.getSequenceSetId()); + + aptxFrame.addFrameListener(new InternalFrameAdapter() + { + + @Override + public void internalFrameClosed(InternalFrameEvent e) + { + TreeViewerUtils.getActiveTreeViews().remove(aptxFrame); + ssm.removeSelectionListener(JalviewBinding.this); + } + + }); + + // treeTabs.addChangeListener(new ChangeListener() + // { + // + // @Override + // public void stateChanged(ChangeEvent e) + // { + // + // SwingUtilities.invokeLater(new Runnable() + // { + // + // @Override + // /** + // * Resend the selection to the tree view when tabs get switched, this + // * has to be buried in invokeLater as Forester first resets the tree + // * view on switching tabs, without invokeLater this would get called + // * before Forester resets which would nullify the selection. + // */ + // public void run() + // { + // treeView = archaeopteryx.getMainPanel().getCurrentTreePanel(); + // parentAvport.sendSelection(); + // // PaintRefresher.Refresh(treeView, + // // parentAvport.getSequenceSetId()); + // + // } + // }); + // + // } + // + // }); + + } + + @Override + public void actionPerformed(ActionEvent e) + { + // reset hidden sequences first + parentAvport.showAllHiddenSeqs(); + + if (treeView.showingSubTree()) + { + LoadedTreeSequenceAssociation bindAptxNodes = new LoadedTreeSequenceAssociation( + parentAvport.getAlignment().getSequencesArray(), + treeView.getTree()); + bindAptxNodes.associateNodesToSequences(); + sequencesBoundToNodes = bindAptxNodes.getAlignmentWithNodes(); + nodesBoundToSequences = bindAptxNodes.getNodesWithAlignment(); + TreeViewerUtils.associateNodesWithJalviewSequences(aptxFrame, + parentAvport, sequencesBoundToNodes, nodesBoundToSequences); + + + for (SequenceI seq : parentAvport.getAlignment().getSequencesArray()) + { + if (!sequencesBoundToNodes.containsKey(seq)) + { + parentAvport.hideSequence(new SequenceI[] { seq }); + + } + } + } + + else + { + + Rectangle visibleView = treeView.getVisibleArea(); + + for (TreeNodeI node : treeView.getTree().getRoot() + .getAllDescendants()) + { + if (!(node.getXcoord() > visibleView.getMinX() + && node.getXcoord() < visibleView.getMaxX() + && node.getYcoord() > visibleView.getMinY() + && node.getYcoord() < visibleView.getMaxY())) + { + parentAvport + .hideSequence(new SequenceI[] + { nodesBoundToSequences.get(node) }); + } + } + + } + + + + } + + @Override + public void mouseClicked(MouseEvent e) + { + SwingUtilities.invokeLater(new Runnable() { + + @Override + /** + * invokeLater so that this always runs after Forester's mouseClicked + */ + public void run() + { + final TreeNodeI node = treeView.findNode(e.getX(), + e.getY()); + if (node != null) + { + if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) // clear previous + // selection if shift + // IS NOT pressed + { + parentAvport.setSelectionGroup(null); + + } + showNodeSelectionOnAlign(node); + } + else + { + + partitionTree(e.getX()); + } + treeView.notifyPaintRefresher(parentAvport.getSequenceSetId(), + false, false); + treeView.repaint(); + + + + } + }); + + + } + + @Override + public void mousePressed(final MouseEvent e) + { + + } + @Override + public void mouseReleased(MouseEvent e) + { + } + + @Override + public void mouseEntered(MouseEvent e) + { + } + + @Override + public void mouseExited(MouseEvent e) + { + } + + + @Override + public void selection(final SequenceGroup seqsel, + final ColumnSelection colsel, final HiddenColumns hidden, + final SelectionSource source) + { + if (source == parentAvport) // check if source is alignment from where the + // tree originates + { + treeView.setMatchingNodes( + new HashSet(seqsel.getSequences().size())); + + + for (SequenceI selectedSequence : seqsel.getSequences()) + { + TreeNodeI matchingNode = sequencesBoundToNodes + .get(selectedSequence); + if (matchingNode != null) + { + treeView.addToMatchingNodes(matchingNode); + + + // if (!matchingNode.getBranchData().isHasBranchColor()) + // { + // // Color foundNodesColour = treeView.getTreeColorSet() + // // .getFoundColor0(); + // // matchingNode.getBranchData() + // // .setBranchColor(new BranchColor(foundNodesColour)); + // + // } + + } + + } + + treeView.repaint(); + } + + + } + + /** + * Partially refactored from TreeCanvas + */ + @Override + public void partitionTree(final int x) + { + TreeI tree = treeView.getTree(); + + if (!tree.isEmpty()) + { + // should be calculated on each partition as the tree can theoretically + // change in the meantime + TreeNodeI furthestNode = tree.getFurthestNode(); + furthestNodeX = furthestNode.getXcoord(); + rootX = tree.getRoot().getXcoord(); + + // don't bother if 0 distance tree or clicked x lies outside of tree + // if (furthestNodeX != rootX && !(x > furthestNodeX)) + + float threshold = (x - rootX) / (furthestNodeX - rootX); + List foundNodes = getNodesAboveThreshold( + threshold, + tree.getRoot()); + + + } + + + } + + public List getNodesAboveThreshold(float threshold, + TreeNodeI node) + { + + List nodesAboveThreshold = new ArrayList<>(); + + parentAvport.setSelectionGroup(null); + parentAvport.getAlignment().deleteAllGroups(); + parentAvport.clearSequenceColours(); + if (parentAvport.getCodingComplement() != null) + { + parentAvport.getCodingComplement().setSelectionGroup(null); + parentAvport.getCodingComplement().getAlignment().deleteAllGroups(); + parentAvport.getCodingComplement().clearSequenceColours(); + } + + + colourNodesAboveThreshold(nodesAboveThreshold, threshold, + node); + return nodesAboveThreshold; + + } + + /** + * Partially refactored from TreeCanvas colourGroups (can be made nicer). + * + * @param nodeList + * @param threshold + * @param treeLength + * @param node + * @return + */ + private List colourNodesAboveThreshold( + List nodeList, float threshold, + TreeNodeI node) + { + + for (TreeNodeI childNode : node.getDirectChildren()) + { + childNode.setBranchColor(Color.black); + float nodeCutoff = (childNode.getXcoord() - rootX) + / (furthestNodeX - rootX); + + if (nodeCutoff > threshold) + { + nodeList.add(childNode); + + Color randomColour = new Color((int) (Math.random() * 255), + (int) (Math.random() * 255), (int) (Math.random() * 255)); + childNode.setBranchColor(randomColour); + + List groupSeqs = new ArrayList<>(); + SequenceI seq = nodesBoundToSequences.get(childNode); + if (seq != null) + { + groupSeqs.add(seq); + parentAvport.setSequenceColour(seq, randomColour); + } + + List descendantNodes = childNode + .getAllDescendants(); + // .forEach instead? + for (TreeNodeI descNode : descendantNodes) + { + seq = nodesBoundToSequences.get(descNode); + if (seq != null) + { + groupSeqs.add(seq); + parentAvport.setSequenceColour(seq, randomColour); + } + + descNode.setBranchColor(randomColour); + } + + if (groupSeqs != null) + { + nrTreeGroups++; + groupThresholdSequences(groupSeqs, randomColour); + }} + + else + { + colourNodesAboveThreshold(nodeList, threshold, childNode); + } + } + + for (AlignmentPanel associatedPanel : getAssociatedPanels()) + { + + associatedPanel.updateAnnotation(); + + final AlignViewportI codingComplement = associatedPanel.getAlignViewport() + .getCodingComplement(); + if (codingComplement != null) + { + // GROSS + ((AlignViewport) codingComplement).getAlignPanel() + .updateAnnotation(); + } + } + + + return nodeList; + } + + public void groupThresholdSequences(List groupedSeqs, + Color groupColour) + { + SequenceGroup treeGroup = new SequenceGroup(groupedSeqs, null, null, + true, true, false, 0, + parentAvport.getAlignment().getWidth() - 1); + + ColourSchemeI cs = null; + if (parentAvport.getGlobalColourScheme() != null) + { + if (parentAvport.getGlobalColourScheme() instanceof UserColourScheme) + { + cs = new UserColourScheme( + ((UserColourScheme) parentAvport.getGlobalColourScheme()) + .getColours()); + } + else + { - cs = ColourSchemeProperty.getColourScheme(treeGroup, ++ cs = ColourSchemeProperty.getColourScheme(parentAvport,treeGroup, + ColourSchemeProperty.getColourName( + parentAvport.getGlobalColourScheme())); + } + + } + treeGroup.setColourScheme(cs); + treeGroup.getGroupColourScheme().setThreshold( + parentAvport.getResidueShading().getThreshold(), + parentAvport.isIgnoreGapsConsensus()); + + treeGroup.setName("Tree Group " + nrTreeGroups); + treeGroup.setIdColour(groupColour); + + for (AlignmentPanel associatedPanel : getAssociatedPanels()) + { + AlignViewportI altViewport = associatedPanel + .getAlignViewport(); + + if (altViewport.getGlobalColourScheme() != null + && altViewport.getResidueShading() + .conservationApplied()) + { + Conservation conserv = new Conservation(treeGroup.getName(), + treeGroup.getSequences(null), treeGroup.getStartRes(), + treeGroup.getEndRes()); + conserv.calculate(); + conserv.verdict(false, altViewport.getConsPercGaps()); + treeGroup.getGroupColourScheme().setConservation(conserv); + } + + altViewport.getAlignment().addGroup(treeGroup); + // TODO can we push all of the below into AlignViewportI? + final AlignViewportI codingComplement = altViewport + .getCodingComplement(); + if (codingComplement != null) + { + SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(treeGroup, + parentAvport, codingComplement); + if (mappedGroup.getSequences().size() > 0) + { + codingComplement.getAlignment().addGroup(mappedGroup); + for (SequenceI seq : mappedGroup.getSequences()) + { + codingComplement.setSequenceColour(seq, groupColour.brighter()); + } + } + } + + } + + } + + + @Override + public void showNodeSelectionOnAlign(final TreeNodeI node) + { + + if (node.isInternal()) + { + showMatchingChildSequences(node); + } + + else + { + showMatchingSequence(node); + } + + + } + + + + + + @Override + public void showMatchingSequence(final TreeNodeI nodeToMatch) + { + SequenceI matchingSequence = nodesBoundToSequences.get(nodeToMatch); + if (matchingSequence != null) + { + long nodeId = nodeToMatch.getId(); + addOrRemoveInCollection(treeView.getMatchingNodesIds(), nodeId); + treeSelectionChanged(matchingSequence); + parentAvport.sendSelection(); + + } + } + + @Override + public void showMatchingChildSequences(final TreeNodeI parentNode) + { + // redundancy here, Forester already iterates through tree to get all + // descendants + List childNodes = parentNode.getAllDescendants(); + + + for (TreeNodeI childNode : childNodes) + { + SequenceI matchingSequence = nodesBoundToSequences.get(childNode); + if (matchingSequence != null) + { + long nodeId = childNode.getId(); + addOrRemoveInCollection(treeView.getMatchingNodesIds(), nodeId); + + treeSelectionChanged(matchingSequence); + + } + + } + parentAvport.sendSelection(); + + + } + + + @Override + public void treeSelectionChanged(final SequenceI sequence) + { + if (!parentAvport.isClosed()) // alignment view could be closed + { + SequenceGroup selected = parentAvport.getSelectionGroup(); + + if (selected == null) + { + selected = new SequenceGroup(); + parentAvport.setSelectionGroup(selected); + } + + selected.setEndRes(parentAvport.getAlignment().getWidth() - 1); + selected.addOrRemove(sequence, true); + } + + } + + @Override + public void sortByTree_actionPerformed() + { + + // if (applyToAllViews) + + final ArrayList commands = new ArrayList<>(); + for (AlignmentPanel ap : PaintRefresher + .getAssociatedPanels(parentAvport.getSequenceSetId())) + { + commands.add(sortAlignmentIn(ap.av.getAlignPanel())); + ap.alignFrame.addHistoryItem(new CommandI() + { + + @Override + public void undoCommand(AlignmentI[] views) + { + for (CommandI tsort : commands) + { + tsort.undoCommand(views); + } + } + + @Override + public int getSize() + { + return commands.size(); + } + + @Override + public String getDescription() + { + return "Tree Sort (many views)"; + } + + @Override + public void doCommand(AlignmentI[] views) + { + + for (CommandI tsort : commands) + { + tsort.doCommand(views); + } + } + }); + + ap.alignFrame.updateEditMenuBar(); + } + } + // else + // { + // alignPanel.alignFrame.addHistoryItem(sortAlignmentIn(alignPanel)); + // } + + + + @Override + public CommandI sortAlignmentIn(AlignmentPanel ap) + { + AlignmentViewport viewport = ap.av; + SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray(); + try + { + AlignmentSorter.sortByTree(viewport.getAlignment(), + nodesBoundToSequences, + treeView.getTree()); + CommandI undo; + undo = new OrderCommand("Tree Sort", oldOrder, + viewport.getAlignment()); + + ap.paintAlignment(true, false); + return undo; + + } catch (Exception e) + { + System.err.println(e.getMessage()); + } + return null; + + } + + + + /** + * TO BE MOVED + * + * @param collection + * @param nodeId + */ + public static void addOrRemoveInCollection(Collection collection, + long nodeId) + { + if (collection.contains(nodeId)) + { + collection.remove(nodeId); + } + else + { + collection.add(nodeId); + } + + } + + public AlignmentViewport getParentAvport() + { + return parentAvport; + } + + public void setParentAvport(final AlignmentViewport parentAvport) + { + this.parentAvport = parentAvport; + } + + public AlignmentPanel[] getAssociatedPanels() + { + return PaintRefresher + .getAssociatedPanels(parentAvport.getSequenceSetId()); + } + + @Override + public Map getAlignmentWithNodes() + { + return sequencesBoundToNodes; + } + + @Override + public Map getNodesWithAlignment() + { + return nodesBoundToSequences; + } + + @Override + public void hideCollapsedSequences_actionPerformed() + { + parentAvport.showAllHiddenSeqs(); + + for (TreeNodeI node : treeView.getTree().getAllNodes()) + { + if (node.isCollapsed()) + { + SequenceI seqToHide = nodesBoundToSequences.get(node); + if (seqToHide != null) + { + parentAvport.hideSequence(new SequenceI[] { seqToHide }); + + + } + + } + } + + + } + + } + + + diff --combined src/jalview/fts/core/GFTSPanel.java index ea206e9,0d0cce6..62262d5 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@@ -21,7 -21,6 +21,7 @@@ package jalview.fts.core; +import jalview.bin.Cache; import jalview.fts.api.FTSDataColumnI; import jalview.fts.api.GFTSPanelI; import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource; @@@ -31,12 -30,10 +31,12 @@@ import jalview.gui.JvSwingUtils import jalview.gui.SequenceFetcher; import jalview.io.cache.JvCacheableInputBox; import jalview.util.MessageManager; +import jalview.util.Platform; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Dimension; +import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; @@@ -76,6 -73,7 +76,6 @@@ import javax.swing.event.DocumentListen import javax.swing.event.InternalFrameEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; -import javax.swing.text.JTextComponent; /** * This class provides the swing GUI layout for FTS Panel and implements most of @@@ -88,13 -86,10 +88,13 @@@ @SuppressWarnings("serial") public abstract class GFTSPanel extends JPanel implements GFTSPanelI { + private static final Font VERDANA_12 = new Font("Verdana", 0, 12); + protected JInternalFrame mainFrame = new JInternalFrame( getFTSFrameTitle()); protected JTabbedPane tabs = new JTabbedPane(); + protected IProgressIndicator progressIndicator; protected JComboBox cmb_searchTarget = new JComboBox<>(); @@@ -136,7 -131,7 +136,7 @@@ protected JLabel lbl_blank = new JLabel(balnkPlaceholderImage); - private JTabbedPane tabbedPane = new JTabbedPane(); + JTabbedPane tabbedPane = new JTabbedPane(); private JPanel pnl_actions = new JPanel(); @@@ -264,7 -259,8 +264,7 @@@ { tabs.addTab(MessageManager.getString("label.retrieve_ids"), fetcher); - fetcher.setDatabaseChooserVisible(false); - fetcher.embedWithFTSPanel(this); + fetcher.embedIn(this); } mainFrame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); final JPanel ftsPanel = this; @@@ -274,9 -270,10 +274,9 @@@ public void focusGained(FocusEvent e) { // TODO: make selected tab gain focus in correct widget - if (tabs != null - && tabs.getSelectedComponent() == ftsPanel) + if (tabs != null && tabs.getSelectedComponent() == ftsPanel) { - txt_search.requestFocusInWindow(); + txt_search.getComponent().requestFocusInWindow(); } } }); @@@ -296,18 -293,18 +296,18 @@@ private void jbInit() throws Exception { - txt_search = new JvCacheableInputBox<>(getCacheKey()); + txt_search = new JvCacheableInputBox<>(getCacheKey(), 45); populateCmbSearchTargetOptions(); Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800 : getTempUserPrefs().get("FTSPanel.width"); Integer height = getTempUserPrefs().get("FTSPanel.height") == null ? 400 : getTempUserPrefs().get("FTSPanel.height"); lbl_warning.setVisible(false); - lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12)); + lbl_warning.setFont(VERDANA_12); lbl_loading.setVisible(false); - lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12)); + lbl_loading.setFont(VERDANA_12); lbl_blank.setVisible(true); - lbl_blank.setFont(new java.awt.Font("Verdana", 0, 12)); + lbl_blank.setFont(VERDANA_12); tbl_summary.setAutoCreateRowSorter(true); tbl_summary.getTableHeader().setReorderingAllowed(false); @@@ -360,35 -357,21 +360,35 @@@ } }); + JButton txt_help = new JButton("?"); + txt_help.setFont(VERDANA_12); + txt_help.setPreferredSize(new Dimension(15, 15)); + txt_help.setToolTipText(MessageManager.getString("action.help")); + txt_help.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showHelp(); + } + }); + btn_autosearch.setText(MessageManager.getString("option.autosearch")); btn_autosearch.setToolTipText( MessageManager.getString("option.enable_disable_autosearch")); - btn_autosearch.setSelected( - jalview.bin.Cache.getDefault(getAutosearchPreference(), true)); + // disable autosearch by default + btn_autosearch.setSelected(!Platform.isJS() + && Cache.getDefault(getAutosearchPreference(), false)); btn_autosearch.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - jalview.bin.Cache.setProperty(getAutosearchPreference(), + Cache.setProperty(getAutosearchPreference(), Boolean.toString(btn_autosearch.isSelected())); } }); - btn_back.setFont(new java.awt.Font("Verdana", 0, 12)); + btn_back.setFont(VERDANA_12); btn_back.setText(MessageManager.getString("action.back")); btn_back.addActionListener(new java.awt.event.ActionListener() { @@@ -411,7 -394,7 +411,7 @@@ }); btn_ok.setEnabled(false); - btn_ok.setFont(new java.awt.Font("Verdana", 0, 12)); + btn_ok.setFont(VERDANA_12); btn_ok.setText(MessageManager.getString("action.ok")); btn_ok.addActionListener(new java.awt.event.ActionListener() { @@@ -435,7 -418,7 +435,7 @@@ btn_next_page.setEnabled(false); btn_next_page.setToolTipText( MessageManager.getString("label.next_page_tooltip")); - btn_next_page.setFont(new java.awt.Font("Verdana", 0, 12)); + btn_next_page.setFont(VERDANA_12); btn_next_page.setText(MessageManager.getString("action.next_page")); btn_next_page.addActionListener(new java.awt.event.ActionListener() { @@@ -460,7 -443,7 +460,7 @@@ btn_prev_page.setEnabled(false); btn_prev_page.setToolTipText( MessageManager.getString("label.prev_page_tooltip")); - btn_prev_page.setFont(new java.awt.Font("Verdana", 0, 12)); + btn_prev_page.setFont(VERDANA_12); btn_prev_page.setText(MessageManager.getString("action.prev_page")); btn_prev_page.addActionListener(new java.awt.event.ActionListener() { @@@ -493,7 -476,7 +493,7 @@@ btn_next_page.setVisible(false); } - btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12)); + btn_cancel.setFont(VERDANA_12); btn_cancel.setText(MessageManager.getString("action.cancel")); btn_cancel.addActionListener(new java.awt.event.ActionListener() { @@@ -516,7 -499,7 +516,7 @@@ }); scrl_searchResult.setPreferredSize(new Dimension(width, height)); - cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12)); + cmb_searchTarget.setFont(VERDANA_12); cmb_searchTarget.addItemListener(new ItemListener() { @Override @@@ -542,42 -525,43 +542,42 @@@ "label.separate_multiple_query_values", new Object[] { getCmbSearchTarget().getSelectedItem().toString() }); } - txt_search.setToolTipText( + txt_search.getComponent().setToolTipText( JvSwingUtils.wrapTooltip(true, tooltipText)); searchAction(true); } } }); - txt_search.setFont(new java.awt.Font("Verdana", 0, 12)); + txt_search.getComponent().setFont(VERDANA_12); - txt_search.getEditor().getEditorComponent() - .addKeyListener(new KeyAdapter() - { - @Override - public void keyPressed(KeyEvent e) - { - if (e.getKeyCode() == KeyEvent.VK_ENTER) - { - if (getTypedText() == null || getTypedText().isEmpty()) - { - return; - } - String primaryKeyName = getFTSRestClient() - .getPrimaryKeyColumn().getName(); - if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget() - .getSelectedItem().toString())) - { - // TODO: nicer to show the list in the result set before - // viewing in Jalview perhaps ? - transferToSequenceFetcher(getTypedText()); - } - else - { - performSearchAction(); - } - } - } - }); + txt_search.addKeyListener(new KeyAdapter() + { + @Override + public void keyPressed(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + { + if (getTypedText() == null || getTypedText().isEmpty()) + { + return; + } + String primaryKeyName = getFTSRestClient().getPrimaryKeyColumn() + .getName(); + if (primaryKeyName.equalsIgnoreCase( + getCmbSearchTarget().getSelectedItem().toString())) + { + // TODO: nicer to show the list in the result set before + // viewing in Jalview perhaps ? + transferToSequenceFetcher(getTypedText()); + } + else + { + performSearchAction(); + } + } + } + }); final DeferredTextInputListener listener = new DeferredTextInputListener( 1500, new ActionListener() { @@@ -591,7 -575,8 +591,7 @@@ } } }, false); - ((JTextComponent) txt_search.getEditor().getEditorComponent()) - .getDocument().addDocumentListener(listener); + txt_search.addDocumentListener(listener); txt_search.addFocusListener(new FocusListener() { @@@ -610,6 -595,7 +610,6 @@@ txt_search.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { @@@ -642,7 -628,7 +642,7 @@@ btn_ok.setEnabled(false); btn_next_page.setEnabled(false); btn_prev_page.setEnabled(false); - txt_search.setEnabled(false); + txt_search.getComponent().setEnabled(false); cmb_searchTarget.setEnabled(false); previousWantedFields = getFTSRestClient() .getAllDefaultDisplayedFTSDataColumns() @@@ -653,7 -639,7 +653,7 @@@ btn_back.setEnabled(true); btn_cancel.setEnabled(true); refreshPaginatorState(); - txt_search.setEnabled(true); + txt_search.getComponent().setEnabled(true); cmb_searchTarget.setEnabled(true); if (wantedFieldsUpdated()) { @@@ -679,8 -665,7 +679,8 @@@ pnl_results.add(tabbedPane); pnl_inputs.add(cmb_searchTarget); - pnl_inputs.add(txt_search); + pnl_inputs.add(txt_search.getComponent()); + pnl_inputs.add(txt_help); pnl_inputs.add(btn_autosearch); pnl_inputs.add(lbl_loading); pnl_inputs.add(lbl_warning); @@@ -725,8 -710,6 +725,8 @@@ Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), width, height); } + abstract protected void showHelp(); + protected void closeAction() { getTempUserPrefs().put("FTSPanel.width", this.getWidth()); @@@ -821,6 -804,11 +821,6 @@@ return cmb_searchTarget; } - public JComboBox getTxtSearch() - { - return txt_search; - } - public JInternalFrame getMainFrame() { return mainFrame; @@@ -861,10 -849,6 +861,10 @@@ } } + /** + * Action on Back button is to close this panel and open a new Sequence + * Fetcher panel + */ public void btn_back_ActionPerformed() { closeAction(); @@@ -916,8 -900,8 +916,8 @@@ public void transferToSequenceFetcher(String ids) { - seqFetcher.getTextArea().setText(ids); + seqFetcher.setQuery(ids); - Thread worker = new Thread(seqFetcher); + Thread worker = new Thread(seqFetcher, "GFTSSeqFetcher"); worker.start(); } @@@ -941,7 -925,7 +941,7 @@@ lbl_blank.setVisible(true); btn_ok.setEnabled(false); mainFrame.setTitle(getFTSFrameTitle()); - referesh(); + refresh(); tbl_summary.setModel(new DefaultTableModel()); tbl_summary.setVisible(false); } @@@ -1078,11 -1062,9 +1078,11 @@@ } } - public void referesh() + public void refresh() { mainFrame.setTitle(getFTSFrameTitle()); } + @Override + public abstract void okAction(); } diff --combined src/jalview/fts/service/pdb/PDBFTSPanel.java index 15a68e6,adb910b..0083971 --- a/src/jalview/fts/service/pdb/PDBFTSPanel.java +++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java @@@ -26,8 -26,6 +26,8 @@@ import jalview.fts.api.FTSRestClientI import jalview.fts.core.FTSRestRequest; import jalview.fts.core.FTSRestResponse; import jalview.fts.core.GFTSPanel; +import jalview.gui.Help; +import jalview.gui.Help.HelpId; import jalview.gui.SequenceFetcher; import jalview.util.MessageManager; @@@ -35,8 -33,6 +35,8 @@@ import java.util.HashMap import java.util.HashSet; import java.util.Map; +import javax.help.HelpSetException; + @SuppressWarnings("serial") public class PDBFTSPanel extends GFTSPanel { @@@ -125,15 -121,16 +125,15 @@@ if (isPaginationEnabled() && resultSetCount > 0) { + String f1 = totalNumberformatter + .format(Integer.valueOf(offSet + 1)); + String f2 = totalNumberformatter + .format(Integer.valueOf(offSet + resultSetCount)); + String f3 = totalNumberformatter + .format(Integer.valueOf(totalResultSetCount)); updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result - + " " - + totalNumberformatter.format((Number) (offSet + 1)) - + " to " - + totalNumberformatter - .format((Number) (offSet + resultSetCount)) - + " of " - + totalNumberformatter - .format((Number) totalResultSetCount) - + " " + " (" + (endTime - startTime) + " milli secs)"); + + " " + f1 + " to " + f2 + " of " + f3 + " " + " (" + + (endTime - startTime) + " milli secs)"); } else { @@@ -223,8 -220,9 +223,8 @@@ } String ids = selectedIds.toString(); - // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids); - seqFetcher.getTextArea().setText(ids); + seqFetcher.setQuery(ids); - Thread worker = new Thread(seqFetcher, "PDBFTSSeqFetcherThread"); + Thread worker = new Thread(seqFetcher, "PDBFTSSeqFetcher"); worker.start(); delayAndEnableActionButtons(); } @@@ -290,16 -288,4 +290,16 @@@ { return PDB_AUTOSEARCH; } -} + + @Override + protected void showHelp() + { + try + { + Help.showHelpWindow(HelpId.PdbFts); + } catch (HelpSetException e1) + { + e1.printStackTrace(); + } + } +} diff --combined src/jalview/gui/AlignFrame.java index 89f4036,7029289..f8bce9d --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@@ -20,83 -20,22 +20,88 @@@ */ package jalview.gui; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridLayout; +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.IOException; +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.Locale; ++import java.util.Map; ++import java.util.Map.Entry; +import java.util.StringTokenizer; +import java.util.Vector; + +import javax.swing.ButtonGroup; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +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 org.forester.archaeopteryx.webservices.PhylogeniesWebserviceClient; ++import org.forester.archaeopteryx.webservices.WebservicesManager; ++ +import ext.vamsas.ServiceHandle; import jalview.analysis.AlignmentSorter; import jalview.analysis.AlignmentUtils; import jalview.analysis.CrossRef; import jalview.analysis.Dna; +import jalview.analysis.GeneticCodeI; import jalview.analysis.ParseProperties; import jalview.analysis.SequenceIdMatcher; import jalview.analysis.TreeModel; -import jalview.api.AlignExportSettingI; +import jalview.api.AlignExportSettingsI; import jalview.api.AlignViewControllerGuiI; import jalview.api.AlignViewControllerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureSettingsControllerI; +import jalview.api.FeatureSettingsModelI; import jalview.api.SplitContainerI; import jalview.api.ViewStyleI; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.bin.Jalview; import jalview.commands.CommandI; import jalview.commands.EditCommand; @@@ -106,7 -45,6 +111,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; @@@ -115,7 -53,9 +120,8 @@@ import jalview.datamodel.AlignmentI import jalview.datamodel.AlignmentOrder; import jalview.datamodel.AlignmentView; import jalview.datamodel.ColumnSelection; + import jalview.datamodel.DBRefEntry; import jalview.datamodel.HiddenColumns; -import jalview.datamodel.HiddenSequences; import jalview.datamodel.PDBEntry; import jalview.datamodel.SeqCigar; import jalview.datamodel.Sequence; @@@ -123,12 -63,13 +129,14 @@@ import jalview.datamodel.SequenceGroup import jalview.datamodel.SequenceI; import jalview.ext.archaeopteryx.AptxInit; import jalview.ext.forester.io.SupportedTreeFileFilter; - import jalview.ext.forester.io.TreeParser; + import jalview.ext.treeviewer.TreeFrameI; + import jalview.ext.treeviewer.TreeViewerBindingI; + import jalview.ext.treeviewer.TreeViewerUtils; import jalview.gui.ColourMenuHelper.ColourChangeListener; import jalview.gui.ViewSelectionMenu.ViewSetProvider; import jalview.io.AlignmentProperties; import jalview.io.AnnotationFile; +import jalview.io.BackupFiles; import jalview.io.BioJsHTMLOutput; import jalview.io.DataSourceType; import jalview.io.FileFormat; @@@ -146,17 -87,13 +154,18 @@@ import jalview.io.JnetAnnotationMaker import jalview.io.NewickFile; 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.DBRefUtils; +import jalview.util.HttpUtils; +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; @@@ -166,13 -103,70 +175,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.Dimension; -import java.awt.GridLayout; -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.IOException; -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.Map; -import java.util.Map.Entry; -import java.util.StringTokenizer; -import java.util.Vector; - -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JComboBox; -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 org.forester.archaeopteryx.webservices.PhylogeniesWebserviceClient; -import org.forester.archaeopteryx.webservices.WebservicesManager; - /** * DOCUMENT ME! * * @author $author$ * @version $Revision$ */ +@SuppressWarnings("serial") public class AlignFrame extends GAlignFrame implements DropTargetListener, IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener { @@@ -202,8 -196,6 +211,8 @@@ */ String fileName = null; + File fileObject; + /** * Creates a new AlignFrame object with specific width and height. * @@@ -356,8 -348,6 +365,8 @@@ */ void init() { + // setBackground(Color.white); // BH 2019 + if (!Jalview.isHeadlessMode()) { progressBar = new ProgressBar(this.statusPanel, this.statusBar); @@@ -375,7 -365,8 +384,7 @@@ // modifyPID.setEnabled(false); } - String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT", - "No sort"); + String sortby = Cache.getDefault("SORT_ALIGNMENT", "No sort"); if (sortby.equals("Id")) { @@@ -405,10 -396,7 +414,10 @@@ if (Desktop.desktop != null) { this.setDropTarget(new java.awt.dnd.DropTarget(this, this)); - addServiceListeners(); + if (!Platform.isJS()) + { + addServiceListeners(); + } setGUINucleotide(); } @@@ -417,14 -405,14 +426,14 @@@ wrapMenuItem_actionPerformed(null); } - if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false)) + if (Cache.getDefault("SHOW_OVERVIEW", false)) { this.overviewMenuItem_actionPerformed(null); } addKeyListener(); - final List selviews = new ArrayList<>(); + final List selviews = new ArrayList<>(); final List origview = new ArrayList<>(); final String menuLabel = MessageManager .getString("label.copy_format_from"); @@@ -492,10 -480,10 +501,10 @@@ } } }); - if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase() + if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase(Locale.ROOT) .indexOf("devel") > -1 - || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase() - .indexOf("test") > -1) + || Cache.getDefault("VERSION", "DEVELOPMENT") + .toLowerCase(Locale.ROOT).indexOf("test") > -1) { formatMenu.add(vsel); } @@@ -527,17 -515,6 +536,17 @@@ } /** + * 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 */ @@@ -573,7 -550,7 +582,7 @@@ } if (viewport.cursorMode) { - alignPanel.getSeqPanel().moveCursor(0, 1); + alignPanel.getSeqPanel().moveCursor(0, 1, evt.isShiftDown()); } break; @@@ -584,7 -561,7 +593,7 @@@ } if (viewport.cursorMode) { - alignPanel.getSeqPanel().moveCursor(0, -1); + alignPanel.getSeqPanel().moveCursor(0, -1, evt.isShiftDown()); } break; @@@ -597,7 -574,7 +606,7 @@@ } else { - alignPanel.getSeqPanel().moveCursor(-1, 0); + alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown()); } break; @@@ -609,7 -586,7 +618,7 @@@ } else { - alignPanel.getSeqPanel().moveCursor(1, 0); + alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown()); } break; @@@ -636,7 -613,7 +645,7 @@@ case KeyEvent.VK_BACK_SPACE: if (!viewport.cursorMode) { - cut_actionPerformed(null); + cut_actionPerformed(); } else { @@@ -688,7 -665,7 +697,7 @@@ 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) @@@ -774,9 -751,9 +783,9 @@@ int aSize = alignPanels.size(); - tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null); + tabbedPane.setVisible(aSize > 1 || ap.av.getViewName() != null); - if (aSize == 1 && ap.av.viewName == null) + if (aSize == 1 && ap.av.getViewName() == null) { this.getContentPane().add(ap, BorderLayout.CENTER); } @@@ -789,7 -766,7 +798,7 @@@ expandViews.setEnabled(true); gatherViews.setEnabled(true); - tabbedPane.addTab(ap.av.viewName, ap); + tabbedPane.addTab(ap.av.getViewName(), ap); ap.setVisible(false); } @@@ -812,7 -789,7 +821,7 @@@ gatherViews.setEnabled(true); tabbedPane.setVisible(true); AlignmentPanel first = alignPanels.get(0); - tabbedPane.addTab(first.av.viewName, first); + tabbedPane.addTab(first.av.getViewName(), first); this.getContentPane().add(tabbedPane, BorderLayout.CENTER); } @@@ -859,7 -836,7 +868,7 @@@ Desktop.instance.removeJalviewPropertyChangeListener("services", thisListener); closeMenuItem_actionPerformed(true); - }; + } }); // Finally, build the menu once to get current service state new Thread(new Runnable() @@@ -881,7 -858,6 +890,7 @@@ AlignmentI al = getViewport().getAlignment(); boolean nucleotide = al.isNucleotide(); + loadVcf.setVisible(nucleotide); showTranslation.setVisible(nucleotide); showReverse.setVisible(nucleotide); showReverseComplement.setVisible(nucleotide); @@@ -913,7 -889,7 +922,7 @@@ * @param av * AlignViewport */ - void setMenusFromViewport(AlignViewport av) + public void setMenusFromViewport(AlignViewport av) { padGapsMenuitem.setSelected(av.isPadGaps()); colourTextMenuItem.setSelected(av.isShowColourText()); @@@ -1004,15 -980,10 +1013,15 @@@ return progressBar.operationInProgress(); } + /** + * Sets the text of the status bar. Note that setting a null or empty value + * will cause the status bar to be hidden, with possibly undesirable flicker + * of the screen layout. + */ @Override public void setStatus(String text) { - statusBar.setText(text); + statusBar.setText(text == null || text.isEmpty() ? " " : text); } /* @@@ -1020,7 -991,7 +1029,7 @@@ */ public String getVersion() { - return jalview.bin.Cache.getProperty("VERSION"); + return Cache.getProperty("VERSION"); } public FeatureRenderer getFeatureRenderer() @@@ -1029,9 -1000,9 +1038,9 @@@ } @Override - public void fetchSequence_actionPerformed(ActionEvent e) + public void fetchSequence_actionPerformed() { - new jalview.gui.SequenceFetcher(this); + new SequenceFetcher(this); } @Override @@@ -1071,7 -1042,7 +1080,7 @@@ Desktop.instance.closeAssociatedWindows(); FileLoader loader = new FileLoader(); - DataSourceType protocol = fileName.startsWith("http:") + DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName) ? DataSourceType.URL : DataSourceType.FILE; loader.LoadFile(viewport, fileName, protocol, currentFileFormat); @@@ -1081,22 -1052,11 +1090,22 @@@ 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 = HttpUtils.startsWithHttpOrHttps( + fileName) ? 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()) @@@ -1139,9 -1099,9 +1148,9 @@@ public void save_actionPerformed(ActionEvent e) { if (fileName == null || (currentFileFormat == null) - || fileName.startsWith("http")) + || HttpUtils.startsWithHttpOrHttps(fileName)) { - saveAs_actionPerformed(null); + saveAs_actionPerformed(); } else { @@@ -1150,11 -1110,13 +1159,11 @@@ } /** - * 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(); @@@ -1168,278 -1130,204 +1177,278 @@@ 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("LAST_DIRECTORY", fileName); + saveAlignment(fileName, currentFileFormat); + } - Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName()); + 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) + { + if (!Platform.isHeadless()) + { + JvOptionPane.showInternalMessageDialog(this, MessageManager + .formatMessage("label.couldnt_save_file", new Object[] + { lastFilenameSaved }), + MessageManager.getString("label.error_saving_file"), + JvOptionPane.WARNING_MESSAGE); + } + else + { + Console.error(MessageManager + .formatMessage("label.couldnt_save_file", new Object[] + { lastFilenameSaved })); + } + } + 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 = shortName + .substring(shortName.lastIndexOf(File.separatorChar) + 1); } - - success = new 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 })); + { file, 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() { - try - { - PrintWriter out = new PrintWriter(new FileWriter(file)); - - out.print(output); - out.close(); - this.setTitle(file); - statusBar.setText(MessageManager.formatMessage( - "label.successfully_saved_to_file_in_format", new Object[] - { fileName, format.getName() })); - } catch (Exception ex) + // 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) { - success = false; - ex.printStackTrace(); + lastSaveSuccessful = false; } - } - } + else + { + // create backupfiles object and get new temp filename destination + boolean doBackup = BackupFiles.getEnabled(); + BackupFiles backupfiles = null; + if (doBackup) + { + Console.trace( + "ALIGNFRAME making backupfiles object for " + file); + backupfiles = new BackupFiles(file); + } + try + { + String tempFilePath = doBackup ? backupfiles.getTempFilePath() + : file; + Console.trace("ALIGNFRAME setting PrintWriter"); + PrintWriter out = new PrintWriter(new FileWriter(tempFilePath)); - if (!success) - { - JvOptionPane.showInternalMessageDialog(this, MessageManager - .formatMessage("label.couldnt_save_file", new Object[] - { fileName }), - MessageManager.getString("label.error_saving_file"), - JvOptionPane.WARNING_MESSAGE); - } + if (backupfiles != null) + { + Console.trace("ALIGNFRAME about to write to temp file " + + backupfiles.getTempFilePath()); + } - return success; - } + out.print(output); + Console.trace("ALIGNFRAME about to close file"); + out.close(); + Console.trace("ALIGNFRAME closed file"); + AlignFrame.this.setTitle(file); + statusBar.setText(MessageManager.formatMessage( + "label.successfully_saved_to_file_in_format", + new Object[] + { fileName, format.getName() })); + lastSaveSuccessful = true; + } catch (IOException e) + { + lastSaveSuccessful = false; + Console.error( + "ALIGNFRAME Something happened writing the temp file"); + Console.error(e.getMessage()); + Console.debug(Cache.getStackTraceString(e)); + } catch (Exception ex) + { + lastSaveSuccessful = false; + Console.error( + "ALIGNFRAME Something unexpected happened writing the temp file"); + Console.error(ex.getMessage()); + Console.debug(Cache.getStackTraceString(ex)); + } - private void warningMessage(String warning, String title) - { - if (new jalview.util.Platform().isHeadless()) - { - System.err.println("Warning: " + title + "\nWarning: " + warning); + if (doBackup) + { + backupfiles.setWriteSuccess(lastSaveSuccessful); + Console.debug("ALIGNFRAME writing temp file was " + + (lastSaveSuccessful ? "" : "NOT ") + "successful"); + // do the backup file roll and rename the temp file to actual file + Console.trace( + "ALIGNFRAME about to rollBackupsAndRenameTempFile"); + lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile(); + Console.debug( + "ALIGNFRAME performed rollBackupsAndRenameTempFile " + + (lastSaveSuccessful ? "" : "un") + + "successfully"); + } + } + } + }; + /* + * show dialog with export options if applicable; else just do it + */ + if (AlignExportOptions.isNeeded(viewport, format)) + { + 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 + .forName(fileFormatName); + AlignExportSettingsI options = new AlignExportSettingsAdapter(false); + Runnable outputAction = new Runnable() { - 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) - { - 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; } /** @@@ -1468,39 -1356,33 +1477,39 @@@ } /** - * 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 @@@ -1527,37 -1409,36 +1536,37 @@@ @Override public void exportFeatures_actionPerformed(ActionEvent e) { - new AnnotationExporter().exportFeatures(alignPanel); + new AnnotationExporter(alignPanel).exportFeatures(); } @Override public void exportAnnotations_actionPerformed(ActionEvent e) { - new AnnotationExporter().exportAnnotations(alignPanel); + new AnnotationExporter(alignPanel).exportAnnotations(); } @Override public void associatedData_actionPerformed(ActionEvent e) { - // Pick the tree file - JalviewFileChooser chooser = new JalviewFileChooser( - jalview.bin.Cache.getProperty("LAST_DIRECTORY")); + final JalviewFileChooser chooser = new JalviewFileChooser( + 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(); + Cache.setProperty("LAST_DIRECTORY", choice); + loadJalviewDataFile(chooser.getSelectedFile(), null, null, null); + } + }); + chooser.showOpenDialog(this); } /** @@@ -1596,13 -1477,9 +1605,13 @@@ closeView(alignPanel); } } - if (closeAllTabs) { + if (featureSettings != null && featureSettings.isOpen()) + { + featureSettings.close(); + featureSettings = null; + } /* * this will raise an INTERNAL_FRAME_CLOSED event and this method will * be called recursively, with the frame now in 'closed' state @@@ -1643,7 -1520,7 +1652,7 @@@ /** * DOCUMENT ME! */ - void updateEditMenuBar() + public void updateEditMenuBar() { if (viewport.getHistoryList().size() > 0) @@@ -1739,7 -1616,7 +1748,7 @@@ { if (originalSource != viewport) { - Cache.log.warn( + Console.warn( "Implementation worry: mismatch of viewport origin for undo"); } originalSource.updateHiddenColumns(); @@@ -1779,7 -1656,7 +1788,7 @@@ if (originalSource != viewport) { - Cache.log.warn( + Console.warn( "Implementation worry: mismatch of viewport origin for redo"); } originalSource.updateHiddenColumns(); @@@ -1836,11 -1713,10 +1845,11 @@@ } /** - * DOCUMENT ME! + * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection + * or the sequence under cursor in keyboard mode * * @param up - * DOCUMENT ME! + * or down (if !up) */ public void moveSelectedSequences(boolean up) { @@@ -1848,25 -1724,8 +1857,25 @@@ if (sg == null) { + if (viewport.cursorMode) + { + sg = new SequenceGroup(); + sg.addSequence(viewport.getAlignment().getSequenceAt( + alignPanel.getSeqPanel().seqCanvas.cursorY), false); + } + else + { + return; + } + } + + if (sg.getSize() < 1) + { return; } + + // TODO: JAL-3733 - add an event to the undo buffer for this ! + viewport.getAlignment().moveSelectedSequencesByOne(sg, viewport.getHiddenRepSequences(), up); alignPanel.paintAlignment(true, false); @@@ -1984,8 -1843,9 +1993,8 @@@ * DOCUMENT ME! */ @Override - protected void copy_actionPerformed(ActionEvent e) + protected void copy_actionPerformed() { - System.gc(); if (viewport.getSelectionGroup() == null) { return; @@@ -2021,22 -1881,28 +2030,22 @@@ return; } - ArrayList hiddenColumns = null; + HiddenColumns hiddenColumns = null; if (viewport.hasHiddenColumns()) { - hiddenColumns = new ArrayList<>(); int hiddenOffset = viewport.getSelectionGroup().getStartRes(); int hiddenCutoff = viewport.getSelectionGroup().getEndRes(); - ArrayList hiddenRegions = viewport.getAlignment() - .getHiddenColumns().getHiddenColumnsCopy(); - for (int[] region : hiddenRegions) - { - if (region[0] >= hiddenOffset && region[1] <= hiddenCutoff) - { - hiddenColumns - .add(new int[] - { region[0] - hiddenOffset, region[1] - hiddenOffset }); - } - } + + // create new HiddenColumns object with copy of hidden regions + // between startRes and endRes, offset by startRes + hiddenColumns = new HiddenColumns( + viewport.getAlignment().getHiddenColumns(), hiddenOffset, + hiddenCutoff, hiddenOffset); } 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() })); } @@@ -2159,8 -2025,7 +2168,8 @@@ && 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 newDs = (importDs) ? new Vector<>() : null; // used to + // create // minimum dataset set for (int i = 0; i < sequences.length; i++) @@@ -2233,7 -2098,7 +2242,7 @@@ newGraphGroups.add(q, null); } newGraphGroups.set(newann.graphGroup, - new Integer(++fgroup)); + Integer.valueOf(++fgroup)); } newann.graphGroup = newGraphGroups.get(newann.graphGroup) .intValue(); @@@ -2280,7 -2145,7 +2289,7 @@@ newGraphGroups.add(q, null); } newGraphGroups.set(newann.graphGroup, - new Integer(++fgroup)); + Integer.valueOf(++fgroup)); } newann.graphGroup = newGraphGroups.get(newann.graphGroup) .intValue(); @@@ -2299,7 -2164,7 +2308,7 @@@ { // propagate alignment changed. - viewport.getRanges().setEndSeq(alignment.getHeight()); + viewport.getRanges().setEndSeq(alignment.getHeight() - 1); if (annotationAdded) { // Duplicate sequence annotation in all views. @@@ -2361,8 -2226,11 +2370,8 @@@ if (Desktop.jalviewClipboard != null && Desktop.jalviewClipboard[2] != null) { - List hc = (List) Desktop.jalviewClipboard[2]; - for (int[] region : hc) - { - af.viewport.hideColumns(region[0], region[1]); - } + HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2]; + af.viewport.setHiddenColumns(hc); } // >>>This is a fix for the moment, until a better solution is @@@ -2417,8 -2285,11 +2426,8 @@@ if (Desktop.jalviewClipboard != null && Desktop.jalviewClipboard[2] != null) { - List hc = (List) Desktop.jalviewClipboard[2]; - for (int region[] : hc) - { - af.viewport.hideColumns(region[0], region[1]); - } + HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2]; + af.viewport.setHiddenColumns(hc); } // >>>This is a fix for the moment, until a better solution is @@@ -2454,20 -2325,26 +2463,20 @@@ } /** - * 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(); @@@ -2476,60 -2353,51 +2485,60 @@@ return; } - /* - * If the cut affects all sequences, warn, remove highlighted columns - */ - if (sg.getSize() == viewport.getAlignment().getHeight()) + Runnable okAction = new Runnable() { - boolean isEntireAlignWidth = (((sg.getEndRes() - sg.getStartRes()) - + 1) == viewport.getAlignment().getWidth()) ? true : false; - if (isEntireAlignWidth) + @Override + public void run() { - 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); + 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); - if (confirm == JvOptionPane.CANCEL_OPTION - || confirm == JvOptionPane.CLOSED_OPTION) + viewport.firePropertyChange("alignment", null, + viewport.getAlignment().getSequences()); + if (viewport.getAlignment().getHeight() < 1) { - return; + try + { + AlignFrame.this.setClosed(true); + } catch (Exception ex) + { + } } } - 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) + /* + * If the cut affects all sequences, prompt for confirmation + */ + 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 { - try - { - this.setClosed(true); - } catch (Exception ex) - { - } + okAction.run(); } } @@@ -2559,12 -2427,15 +2568,12 @@@ @Override public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e) { - SequenceGroup sg = new SequenceGroup(); - - for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++) - { - sg.addSequence(viewport.getAlignment().getSequenceAt(i), false); - } + SequenceGroup sg = new SequenceGroup( + viewport.getAlignment().getSequences()); sg.setEndRes(viewport.getAlignment().getWidth() - 1); viewport.setSelectionGroup(sg); + viewport.isSelectionGroupChanged(true); viewport.sendSelection(); // JAL-2034 - should delegate to // alignPanel to decide if overview needs @@@ -2589,7 -2460,7 +2598,7 @@@ } viewport.setSelectionGroup(null); viewport.getColumnSelection().clear(); - viewport.setSelectionGroup(null); + viewport.setSearchResults(null); alignPanel.getIdPanel().getIdCanvas().searchResults = null; // JAL-2034 - should delegate to // alignPanel to decide if overview needs @@@ -2702,8 -2573,8 +2711,8 @@@ column, viewport.getAlignment()); } - statusBar.setText(MessageManager - .formatMessage("label.removed_columns", new String[] + setStatus(MessageManager.formatMessage("label.removed_columns", + new String[] { Integer.valueOf(trimRegion.getSize()).toString() })); addHistoryItem(trimRegion); @@@ -2752,8 -2623,8 +2761,8 @@@ addHistoryItem(removeGapCols); - statusBar.setText(MessageManager - .formatMessage("label.removed_empty_columns", new Object[] + setStatus(MessageManager.formatMessage("label.removed_empty_columns", + new Object[] { Integer.valueOf(removeGapCols.getSize()).toString() })); // This is to maintain viewport position on first residue @@@ -2826,14 -2697,15 +2835,14 @@@ } /** - * DOCUMENT ME! + * Opens a Finder dialog * * @param e - * DOCUMENT ME! */ @Override public void findMenuItem_actionPerformed(ActionEvent e) { - new Finder(); + new Finder(alignPanel, false, null); } /** @@@ -2859,8 -2731,7 +2868,8 @@@ /* * Create a new AlignmentPanel (with its own, new Viewport) */ - AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel); + AlignmentPanel newap = new jalview.project.Jalview2XML() + .copyAlignPanel(alignPanel); if (!copyAnnotation) { /* @@@ -2872,10 -2743,10 +2881,10 @@@ newap.av.setGatherViewsHere(false); - if (viewport.viewName == null) + if (viewport.getViewName() == null) { - viewport.viewName = MessageManager - .getString("label.view_name_original"); + viewport.setViewName( + MessageManager.getString("label.view_name_original")); } /* @@@ -2885,12 -2756,6 +2894,12 @@@ newap.av.setRedoList(viewport.getRedoList()); /* + * copy any visualisation settings that are not saved in the project + */ + newap.av.setColourAppliesToAllGroups( + viewport.getColourAppliesToAllGroups()); + + /* * Views share the same mappings; need to deregister any new mappings * created by copyAlignPanel, and register the new reference to the shared * mappings @@@ -2905,7 -2770,7 +2914,7 @@@ newap.refresh(true); // adjust layout of annotations } - newap.av.viewName = getNewViewName(viewTitle); + newap.av.setViewName(getNewViewName(viewTitle)); addAlignmentPanel(newap, true); newap.alignmentChanged(); @@@ -2968,9 -2833,9 +2977,9 @@@ if (comp instanceof AlignmentPanel) { AlignmentPanel ap = (AlignmentPanel) comp; - if (!existingNames.contains(ap.av.viewName)) + if (!existingNames.contains(ap.av.getViewName())) { - existingNames.add(ap.av.viewName); + existingNames.add(ap.av.getViewName()); } } } @@@ -3053,7 -2918,7 +3062,7 @@@ viewport.setFollowHighlight(state); if (state) { - alignPanel.scrollToPosition(viewport.getSearchResults(), false); + alignPanel.scrollToPosition(viewport.getSearchResults()); } } @@@ -3112,7 -2977,7 +3121,7 @@@ * @param toggleSeqs * @param toggleCols */ - private void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols) + protected void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols) { boolean hide = false; @@@ -3200,7 -3065,6 +3209,7 @@@ viewport.expandColSelection(sg, false); viewport.hideAllSelectedSeqs(); viewport.hideSelectedColumns(); + alignPanel.updateLayout(); alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@@ -3225,7 -3089,6 +3234,7 @@@ public void hideSelColumns_actionPerformed(ActionEvent e) { viewport.hideSelectedColumns(); + alignPanel.updateLayout(); alignPanel.paintAlignment(true, true); viewport.sendSelection(); } @@@ -3247,7 -3110,7 +3256,7 @@@ protected void scaleAbove_actionPerformed(ActionEvent e) { viewport.setScaleAboveWrapped(scaleAbove.isSelected()); - // TODO: do we actually need to update overview for scale above change ? + alignPanel.updateLayout(); alignPanel.paintAlignment(true, false); } @@@ -3261,7 -3124,6 +3270,7 @@@ protected void scaleLeft_actionPerformed(ActionEvent e) { viewport.setScaleLeftWrapped(scaleLeft.isSelected()); + alignPanel.updateLayout(); alignPanel.paintAlignment(true, false); } @@@ -3275,7 -3137,6 +3284,7 @@@ protected void scaleRight_actionPerformed(ActionEvent e) { viewport.setScaleRightWrapped(scaleRight.isSelected()); + alignPanel.updateLayout(); alignPanel.paintAlignment(true, false); } @@@ -3329,15 -3190,9 +3338,15 @@@ @Override public void featureSettings_actionPerformed(ActionEvent e) { + showFeatureSettingsUI(); + } + + @Override + public FeatureSettingsControllerI showFeatureSettingsUI() + { if (featureSettings != null) { - featureSettings.close(); + featureSettings.closeOldSettings(); featureSettings = null; } if (!showSeqFeatures.isSelected()) @@@ -3347,7 -3202,6 +3356,7 @@@ showSeqFeatures_actionPerformed(null); } featureSettings = new FeatureSettings(this); + return featureSettings; } /** @@@ -3387,42 -3241,15 +3396,42 @@@ @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[] @@@ -3461,12 -3288,8 +3470,12 @@@ { overview.dispose(); alignPanel.setOverviewPanel(null); - }; + } }); + if (getKeyListeners().length > 0) + { + frame.addKeyListener(getKeyListeners()[0]); + } alignPanel.setOverviewPanel(overview); } @@@ -3531,8 -3354,7 +3540,8 @@@ * otherwise set the chosen colour scheme (or null for 'None') */ ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name, - viewport.getAlignment(), viewport.getHiddenRepSequences()); + viewport, viewport.getAlignment(), + viewport.getHiddenRepSequences()); changeColour(cs); } @@@ -3801,9 -3623,9 +3810,9 @@@ frameTitle += " from "; - if (viewport.viewName != null) + if (viewport.getViewName() != null) { - frameTitle += viewport.viewName + " of "; + frameTitle += viewport.getViewName() + " of "; } frameTitle += this.title; @@@ -3899,7 -3721,7 +3908,7 @@@ { sortByAnnotScore.removeAll(); // almost certainly a quicker way to do this - but we keep it simple - Hashtable scoreSorts = new Hashtable(); + Hashtable scoreSorts = new Hashtable<>(); AlignmentAnnotation aann[]; for (SequenceI sqa : viewport.getAlignment().getSequences()) { @@@ -3912,10 -3734,11 +3921,10 @@@ } } } - Enumeration labels = scoreSorts.keys(); + Enumeration labels = scoreSorts.keys(); while (labels.hasMoreElements()) { - addSortByAnnotScoreMenuItem(sortByAnnotScore, - (String) labels.nextElement()); + addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement()); } sortByAnnotScore.setVisible(scoreSorts.size() > 0); scoreSorts.clear(); @@@ -3925,6 -3748,7 +3934,7 @@@ } } + /** * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a * TreePanel with an appropriate jalview.analysis.AlignmentSorter @@@ -3940,15 -3764,21 +3950,21 @@@ List comps = PaintRefresher.components .get(viewport.getSequenceSetId()); List treePanels = new ArrayList<>(); + + Map aptxFrames = TreeViewerUtils + .getActiveTreeViews(); + for (Component comp : comps) { + // old treepanels if (comp instanceof TreePanel) { treePanels.add((TreePanel) comp); } + } - if (treePanels.size() < 1) + if (treePanels.isEmpty() && aptxFrames.isEmpty()) { sortByTreeMenu.setVisible(false); return; @@@ -3956,6 -3786,42 +3972,42 @@@ sortByTreeMenu.setVisible(true); + for (Entry aptxFrameWithBinding : aptxFrames + .entrySet()) + { + TreeFrameI aptxFrame = aptxFrameWithBinding.getKey(); + TreeViewerBindingI binding = aptxFrameWithBinding.getValue(); + + // future support for multiple tabs + // for (org.forester.archaeopteryx.TreePanel aptxTree : aptxFrame + // .getMainPanel().getTreePanels()) + { + final JMenuItem item = new JMenuItem( + aptxFrame.getTree().getTreeName()); + + item.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + binding.sortByTree_actionPerformed(); // redundant here?? + addHistoryItem(binding.sortAlignmentIn(alignPanel)); + } + + }); + sortByTreeMenu.add(item); + } + + + + } + + + + + + // old treepanels for (final TreePanel tp : treePanels) { final JMenuItem item = new JMenuItem(tp.getTitle()); @@@ -3964,7 -3830,6 +4016,6 @@@ @Override public void actionPerformed(ActionEvent e) { - // adapt to Aptx tp.sortByTree_actionPerformed(); addHistoryItem(tp.sortAlignmentIn(alignPanel)); @@@ -4086,44 -3951,59 +4137,59 @@@ @Override protected void loadTreeBaseStudy_actionPerformed(ActionEvent e) { - chooseTreeDb(); + chooseTreeDb(0, null); } @Override - protected void loadTreeOfLife_actionPerformed(ActionEvent e) + protected void loadTreeBase_actionPerformed(ActionEvent e) { - chooseTreeDb(); + chooseTreeDb(1, null); } - @Override - protected void loadTreeFam_actionPerformed(ActionEvent e) + protected void loadTreePfam_actionPerformed(ActionEvent e) { - chooseTreeDb(); - } + // only DBRefs of first sequence are checked for matching DB for now, + // iterating through them all seems excessive + SequenceI seq = viewport.getAlignment().getSequenceAt(0); + String dbId = null; + for (DBRefEntry pfamRef : DBRefUtils + .searchRefsForSource(seq.getDBRefs(), "pfam")) + { + if (pfamRef.getAccessionId().startsWith("PF")) + { + dbId = pfamRef.getAccessionId().replaceAll("[A-Za-z]", ""); + } + + } + chooseTreeDb(2, dbId); + } @Override - protected void loadTreePfam_actionPerformed(ActionEvent e) + protected void loadTreeFam_actionPerformed(ActionEvent e) { - chooseTreeDb(); + chooseTreeDb(3, null); } @Override - protected void loadTreeBase_actionPerformed(ActionEvent e) + protected void loadTreeOfLife_actionPerformed(ActionEvent e) { - chooseTreeDb(); + chooseTreeDb(4, null); } + + + public void chooseTreeFile() { // Pick the tree file JalviewFileChooser chooser = new JalviewFileChooser( - jalview.bin.Cache.getProperty("LAST_DIRECTORY")); + Cache.getProperty("LAST_DIRECTORY")); chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle( MessageManager.getString("label.select_tree_file")); // modify @@@ -4134,38 -4014,33 +4200,35 @@@ { chooser.setFileFilter(treeFormat.getTreeFilter()); } -- - int value = chooser.showOpenDialog(null); - - if (value == JalviewFileChooser.APPROVE_OPTION) ++ final AlignFrame us=this; + chooser.setResponseHandler(0, new Runnable() { + @Override + public void run() + { - String filePath = chooser.getSelectedFile().getPath(); - Cache.setProperty("LAST_DIRECTORY", filePath); - TreeParser treeParser = null; - try { - treeParser = new TreeParser(filePath); - treeParser.loadTree(viewport); - } catch (Exception ex) - { - JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(), - MessageManager - .getString("label.problem_reading_tree_file"), - JvOptionPane.WARNING_MESSAGE); - ex.printStackTrace(); - } - // TODO: handle any other warnings from treeParser ? - // if (treeParser != null && treeParser.fin.hasWarningMessage()) - // { - // 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; // old tree + try + { + AptxInit.createInstancesFromFile(filePath, viewport); + + // fin = new NewickFile(filePath, DataSourceType.FILE); + // viewport.setCurrentTree(viewport.getAlignPanel().alignFrame + // .showNewickTree(fin, filePath).getTree()); + + } catch (Exception ex) + { - JvOptionPane.showMessageDialog(this, ex.getMessage(), ++ JvOptionPane.showMessageDialog(us, ex.getMessage(), + MessageManager.getString("label.problem_reading_tree_file"), + JvOptionPane.WARNING_MESSAGE); + ex.printStackTrace(); + } - - } + } + }); + chooser.showOpenDialog(this); } /** @@@ -4217,7 -4092,7 +4280,7 @@@ format = new IdentifyFile().identify(urlString, DataSourceType.URL); // add actual use for the format identification (jalview .jar files) treeUrl = new URL(urlString); - AptxInit.createInstanceFromUrl(treeUrl, viewport); + AptxInit.createInstancesFromUrl(treeUrl, viewport); } catch (IOException | RuntimeException e) { @@@ -4235,8 -4110,26 +4298,26 @@@ } } - public void chooseTreeDb() + /** + * Disgustingly hardcoded atm. + * + * @param databaseIndex + */ + public void chooseTreeDb(int databaseIndex, String defaultIdentifier) { + final WebservicesManager webservices_manager = WebservicesManager + .getInstance(); + final PhylogeniesWebserviceClient client = webservices_manager + .getAvailablePhylogeniesWebserviceClient(databaseIndex); + String identifier = JvOptionPane + .showInternalInputDialog(Desktop.desktop, + client.getInstructions() + "\n(Reference: " + + client.getReference() + ")", + client.getDescription(), JvOptionPane.QUESTION_MESSAGE, + null, null, defaultIdentifier) + .toString(); + + AptxInit.createInstancesFromDb(client, identifier, viewport); } public TreePanel showNewickTree(NewickFile nf, String treeTitle) @@@ -4302,6 -4195,10 +4383,10 @@@ private boolean buildingMenu = false; + public void BuildTreeDbMenu() + { + + } /** * Generates menu items and listener event actions for web service clients * @@@ -4368,14 -4265,15 +4453,14 @@@ // No MSAWS used any more: // Vector msaws = null; // (Vector) // Discoverer.services.get("MsaWS"); - Vector secstrpr = (Vector) Discoverer.services + Vector 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 - .get(i); + final ext.vamsas.ServiceHandle sh = secstrpr.get(i); jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer .getServiceClient(sh); int p = secstrmenu.getItemCount(); @@@ -4420,15 -4318,9 +4505,15 @@@ webService.add(me.webServiceNoServices); } // TODO: move into separate menu builder class. - boolean new_sspred = false; - if (Cache.getDefault("SHOW_JWS2_SERVICES", true)) { + // logic for 2.11.1.4 is + // always look to see if there is a discover. if there isn't + // we can't show any Jws2 services + // if there are services available, show them - regardless of + // the 'show JWS2 preference' + // if the discoverer is running then say so + // otherwise offer to trigger discovery if 'show JWS2' is not + // enabled Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer(); if (jws2servs != null) { @@@ -4437,8 -4329,7 +4522,8 @@@ jws2servs.attachWSMenuEntry(webService, me); for (Jws2Instance sv : jws2servs.getServices()) { - if (sv.description.toLowerCase().contains("jpred")) + if (sv.description.toLowerCase(Locale.ROOT) + .contains("jpred")) { for (JMenuItem jmi : legacyItems) { @@@ -4446,8 -4337,8 +4531,8 @@@ } } } - } + if (jws2servs.isRunning()) { JMenuItem tm = new JMenuItem( @@@ -4455,26 -4346,6 +4540,26 @@@ tm.setEnabled(false); webService.add(tm); } + else if (!Cache.getDefault("SHOW_JWS2_SERVICES", true)) + { + JMenuItem enableJws2 = new JMenuItem( + "Discover Web Services"); + enableJws2.setToolTipText( + "Select to start JABA Web Service discovery (or enable option in Web Service preferences)"); + enableJws2.setEnabled(true); + enableJws2.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + // start service discoverer, but ignore preference + Desktop.instance.startServiceDiscovery(false, + true); + } + }); + webService.add(enableJws2); + } } } build_urlServiceMenu(me.webService); @@@ -4492,7 -4363,7 +4577,7 @@@ } } catch (Exception e) { - Cache.log.debug( + Console.debug( "Exception during web service menu building process.", e); } @@@ -4503,7 -4374,7 +4588,7 @@@ } buildingMenu = false; } - }, "BuildWebServiceThread").start(); + }, "BuildWebService").start(); } @@@ -4512,7 -4383,7 +4597,7 @@@ * * @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 @@@ -4589,7 -4460,7 +4674,7 @@@ showProducts.setEnabled(showp); } catch (Exception e) { - Cache.log.warn( + Console.warn( "canShowProducts threw an exception - please report to help@jalview.org", e); return false; @@@ -4611,8 -4482,8 +4696,8 @@@ protected void showProductsFor(final SequenceI[] sel, final boolean _odna, final String source) { - new Thread(CrossRefAction.showProductsFor(sel, _odna, source, this), + new Thread(CrossRefAction.getHandlerFor(sel, _odna, source, this), - "CrossReferencesThread") + "CrossReferences") .start(); } @@@ -4621,18 -4492,18 +4706,18 @@@ * frame's DNA sequences to their aligned protein (amino acid) equivalents. */ @Override - public void showTranslation_actionPerformed(ActionEvent e) + public void showTranslation_actionPerformed(GeneticCodeI codeTable) { AlignmentI al = null; try { Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true)); - al = dna.translateCdna(); + al = dna.translateCdna(codeTable); } catch (Exception ex) { - jalview.bin.Cache.log.error( - "Exception during translation. Please report this !", ex); + Console.error("Exception during translation. Please report this !", + ex); final String msg = MessageManager.getString( "label.error_when_translating_sequences_submit_bug_report"); final String errorTitle = MessageManager @@@ -4657,7 -4528,7 +4742,7 @@@ af.setFileFormat(this.currentFileFormat); final String newTitle = MessageManager .formatMessage("label.translation_of_params", new Object[] - { this.getTitle() }); + { this.getTitle(), codeTable.getId() }); af.setTitle(newTitle); if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true)) { @@@ -4686,14 -4557,13 +4771,14 @@@ * 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)); @@@ -4740,9 -4610,8 +4825,9 @@@ // Java's Transferable for native dnd evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); Transferable t = evt.getTransferable(); + final AlignFrame thisaf = this; - final List files = new ArrayList<>(); + final List files = new ArrayList<>(); List protocols = new ArrayList<>(); try @@@ -4770,33 -4639,20 +4855,33 @@@ * Object[] { String,SequenceI} */ ArrayList filesmatched = new ArrayList<>(); - ArrayList filesnotmatched = new ArrayList<>(); + ArrayList 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) @@@ -4818,7 -4674,7 +4903,7 @@@ } if (mtch != null) { - FileFormatI type = null; + FileFormatI type; try { type = new IdentifyFile().identify(file, protocol); @@@ -4840,22 -4696,17 +4925,22 @@@ int assocfiles = 0; if (filesmatched.size() > 0) { - if (Cache.getDefault("AUTOASSOCIATE_PDBANDSEQS", false) - || JvOptionPane.showConfirmDialog(thisaf, - MessageManager.formatMessage( - "label.automatically_associate_structure_files_with_sequences_same_name", - new Object[] - { Integer.valueOf(filesmatched.size()) - .toString() }), - MessageManager.getString( - "label.automatically_associate_structure_files_by_name"), - JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION) - + boolean autoAssociate = Cache + .getDefault("AUTOASSOCIATE_PDBANDSEQS", false); + if (!autoAssociate) + { + String msg = MessageManager.formatMessage( + "label.automatically_associate_structure_files_with_sequences_same_name", + new Object[] + { Integer.valueOf(filesmatched.size()) + .toString() }); + String ttl = MessageManager.getString( + "label.automatically_associate_structure_files_by_name"); + int choice = JvOptionPane.showConfirmDialog(thisaf, msg, + ttl, JvOptionPane.YES_NO_OPTION); + autoAssociate = choice == JvOptionPane.YES_OPTION; + } + if (autoAssociate) { for (Object[] fm : filesmatched) { @@@ -4865,13 -4716,13 +4950,13 @@@ 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++; } @@@ -4881,16 -4732,6 +4966,16 @@@ alignPanel.paintAlignment(true, false); } } + else + { + /* + * add declined structures as sequences + */ + for (Object[] o : filesmatched) + { + filesnotmatched.add(o[0]); + } + } } if (filesnotmatched.size() > 0) { @@@ -4910,7 -4751,7 +4995,7 @@@ { return; } - for (String fn : filesnotmatched) + for (Object fn : filesnotmatched) { loadJalviewDataFile(fn, null, null, null); } @@@ -4921,7 -4762,7 +5006,7 @@@ ex.printStackTrace(); } } - }, "DropFileThread").start(); + }, "DropFile").start(); } } @@@ -4937,10 -4778,9 +5022,10 @@@ * @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) @@@ -4971,7 -4811,7 +5056,7 @@@ changeColour( new TCoffeeColourScheme(viewport.getAlignment())); isAnnotation = true; - statusBar.setText(MessageManager.getString( + setStatus(MessageManager.getString( "label.successfully_pasted_tcoffee_scores_to_alignment")); } else @@@ -4994,7 -4834,7 +5079,7 @@@ } } catch (Exception x) { - Cache.log.debug( + Console.debug( "Exception when processing data source as T-COFFEE score file", x); tcf = null; @@@ -5014,7 -4854,7 +5099,7 @@@ new FileParse(file, sourceType)); sm.parse(); // todo: i18n this message - statusBar.setText(MessageManager.formatMessage( + setStatus(MessageManager.formatMessage( "label.successfully_loaded_matrix", sm.getMatrixName())); } @@@ -5024,7 -4864,11 +5109,7 @@@ new JnetAnnotationMaker(); JnetAnnotationMaker.add_annotation(predictions, viewport.getAlignment(), 0, false); - SequenceI repseq = viewport.getAlignment().getSequenceAt(0); - viewport.getAlignment().setSeqrep(repseq); - HiddenColumns cs = new HiddenColumns(); - cs.hideInsertionsFor(repseq); - viewport.getAlignment().setHiddenColumns(cs); + viewport.getAlignment().setupJPredAlignment(); isAnnotation = true; } // else if (IdentifyFile.FeaturesFile.equals(format)) @@@ -5032,15 -4876,7 +5117,15 @@@ { if (parseFeaturesFile(file, sourceType)) { - alignPanel.paintAlignment(true, true); + SplitFrame splitFrame = (SplitFrame) getSplitViewContainer(); + if (splitFrame != null) + { + splitFrame.repaint(); + } + else + { + alignPanel.paintAlignment(true, true); + } } } else @@@ -5096,21 -4932,6 +5181,21 @@@ viewport = alignPanel.av; avc.setViewportAndAlignmentPanel(viewport, alignPanel); setMenusFromViewport(viewport); + if (featureSettings != null && featureSettings.isOpen() + && featureSettings.fr.getViewport() != viewport) + { + if (viewport.isShowSequenceFeatures()) + { + // refresh the featureSettings to reflect UI change + showFeatureSettingsUI(); + } + else + { + // close feature settings for this view. + featureSettings.close(); + } + } + } /* @@@ -5163,12 -4984,12 +5248,12 @@@ 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) { - viewport.viewName = reply; + viewport.setViewName(reply); // TODO warn if reply is in getExistingViewNames()? tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply); } @@@ -5269,17 -5090,16 +5354,17 @@@ MessageManager.getString("option.trim_retrieved_seqs")); trimrs.setToolTipText( MessageManager.getString("label.trim_retrieved_sequences")); - trimrs.setSelected(Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true)); + trimrs.setSelected( + Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true)); trimrs.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { trimrs.setSelected(trimrs.isSelected()); - Cache.setProperty("TRIM_FETCHED_DATASET_SEQS", + Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, Boolean.valueOf(trimrs.isSelected()).toString()); - }; + } }); rfetch.add(trimrs); JMenuItem fetchr = new JMenuItem( @@@ -5308,37 -5128,33 +5393,37 @@@ @Override public void finished() { + + for (FeatureSettingsModelI srcSettings : dbRefFetcher + .getFeatureSettingsModels()) + { + + alignPanel.av.mergeFeaturesStyle(srcSettings); + } AlignFrame.this.setMenusForViewport(); } }); dbRefFetcher.fetchDBRefs(false); } - }, "BuildFetchDBMenuThread").start(); + }, "BuildFetchDBMenu").start(); } }); 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 otherdb; JMenu dfetch = new JMenu(); JMenu ifetch = new JMenu(); @@@ -5355,6 -5171,12 +5440,6 @@@ { continue; } - // List dbs=otherdb; - // otherdb=new ArrayList(); - // for (DbSourceProxy db:dbs) - // { - // if (!db.isA(DBRefSource.ALIGNMENTDB) - // } if (mname == null) { mname = "From " + dbclass; @@@ -5391,10 -5213,6 +5476,10 @@@ @Override public void finished() { + FeatureSettingsModelI srcSettings = dassource[0] + .getFeatureColourScheme(); + alignPanel.av.mergeFeaturesStyle( + srcSettings); AlignFrame.this.setMenusForViewport(); } }); @@@ -5687,8 -5505,7 +5772,8 @@@ { PaintRefresher.Refresh(this, viewport.getSequenceSetId()); alignPanel.updateAnnotation(); - alignPanel.paintAlignment(true, true); + alignPanel.paintAlignment(true, + viewport.needToUpdateStructureViews()); } } @@@ -5709,10 -5526,6 +5794,10 @@@ { if (avc.createGroup()) { + if (applyAutoAnnotationSettings.isSelected()) + { + alignPanel.updateAnnotation(true, false); + } alignPanel.alignmentChanged(); } } @@@ -5803,9 -5616,7 +5888,9 @@@ */ public List getAlignPanels() { - return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels; + // alignPanels is never null + // return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels; + return alignPanels; } /** @@@ -5974,16 -5785,15 +6059,16 @@@ colourMenu.add(textColour); colourMenu.addSeparator(); - ColourMenuHelper.addMenuItems(colourMenu, this, viewport.getAlignment(), - false); + ButtonGroup bg = ColourMenuHelper.addMenuItems(colourMenu, this, + viewport.getAlignment(), false); + colourMenu.add(annotationColour); + bg.add(annotationColour); colourMenu.addSeparator(); colourMenu.add(conservationMenuItem); colourMenu.add(modifyConservation); colourMenu.add(abovePIDThreshold); colourMenu.add(modifyPID); - colourMenu.add(annotationColour); ColourSchemeI colourScheme = viewport.getGlobalColourScheme(); ColourMenuHelper.setColourSelected(colourMenu, colourScheme); @@@ -6000,44 -5810,6 +6085,44 @@@ new CalculationChooser(AlignFrame.this); } } + + @Override + protected void loadVcf_actionPerformed() + { + JalviewFileChooser chooser = new JalviewFileChooser( + Cache.getProperty("LAST_DIRECTORY")); + chooser.setFileView(new JalviewFileView()); + chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file")); + chooser.setToolTipText(MessageManager.getString("label.load_vcf_file")); + final AlignFrame us = this; + chooser.setResponseHandler(0, new Runnable() + { + @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); + + } + + private Rectangle lastFeatureSettingsBounds = null; + + @Override + public void setFeatureSettingsGeometry(Rectangle bounds) + { + lastFeatureSettingsBounds = bounds; + } + + @Override + public Rectangle getFeatureSettingsGeometry() + { + return lastFeatureSettingsBounds; + } } class PrintThread extends Thread diff --combined src/jalview/gui/AlignViewport.java index 30ccdbe,c696765..ac295a1 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@@ -29,7 -29,6 +29,7 @@@ import jalview.api.FeatureSettingsModel import jalview.api.FeaturesDisplayedI; import jalview.api.ViewStyleI; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.commands.CommandI; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; @@@ -48,7 -47,6 +48,7 @@@ import jalview.schemes.UserColourScheme import jalview.structure.SelectionSource; import jalview.structure.StructureSelectionManager; import jalview.structure.VamsasSource; +import jalview.util.ColorUtils; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; import jalview.ws.params.AutoCalcSetting; @@@ -58,7 -56,6 +58,7 @@@ import java.awt.Dimension import java.awt.Font; import java.awt.FontMetrics; import java.awt.Rectangle; +import java.util.ArrayList; import java.util.Hashtable; import java.util.List; @@@ -79,9 -76,9 +79,9 @@@ public class AlignViewport extends Alig boolean antiAlias = false; - private Rectangle explodedGeometry; + private Rectangle explodedGeometry = null; - String viewName; + private String viewName = null; /* * Flag set true on the view that should 'gather' multiple views of the same @@@ -124,14 -121,14 +124,14 @@@ sequenceSetID = seqsetid; viewId = viewid; // TODO remove these once 2.4.VAMSAS release finished - if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null) + if (seqsetid != null) { - Cache.log.debug( + Console.debug( "Setting viewport's sequence set id : " + sequenceSetID); } - if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null) + if (viewId != null) { - Cache.log.debug("Setting viewport's view id : " + viewId); + Console.debug("Setting viewport's view id : " + viewId); } init(); @@@ -186,14 -183,14 +186,14 @@@ sequenceSetID = seqsetid; viewId = viewid; // TODO remove these once 2.4.VAMSAS release finished - if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null) + if (seqsetid != null) { - Cache.log.debug( + Console.debug( "Setting viewport's sequence set id : " + sequenceSetID); } - if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null) + if (viewId != null) { - Cache.log.debug("Setting viewport's view id : " + viewId); + Console.debug("Setting viewport's view id : " + viewId); } if (hiddenColumns != null) @@@ -208,7 -205,7 +208,7 @@@ */ private void applyViewProperties() { - antiAlias = Cache.getDefault("ANTI_ALIAS", false); + antiAlias = Cache.getDefault("ANTI_ALIAS", true); viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true)); setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true)); @@@ -291,8 -288,8 +291,8 @@@ schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR, ResidueColourScheme.NONE); } - ColourSchemeI colourScheme = ColourSchemeProperty - .getColourScheme(alignment, schemeName); + ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(this, + alignment, schemeName); residueShading = new ResidueShader(colourScheme); if (colourScheme instanceof UserColourScheme) @@@ -306,7 -303,6 +306,7 @@@ { residueShading.setConsensus(hconsensus); } + setColourAppliesToAllGroups(true); } boolean validCharWidth; @@@ -446,6 -442,31 +446,6 @@@ } /** - * returns the visible column regions of the alignment - * - * @param selectedRegionOnly - * true to just return the contigs intersecting with the selected - * area - * @return - */ - public int[] getViewAsVisibleContigs(boolean selectedRegionOnly) - { - int[] viscontigs = null; - int start = 0, end = 0; - if (selectedRegionOnly && selectionGroup != null) - { - start = selectionGroup.getStartRes(); - end = selectionGroup.getEndRes() + 1; - } - else - { - end = alignment.getWidth(); - } - viscontigs = alignment.getHiddenColumns().getVisibleContigs(start, end); - return viscontigs; - } - - /** * get hash of undo and redo list for the alignment * * @return long[] { historyList.hashCode, redoList.hashCode }; @@@ -520,7 -541,6 +520,6 @@@ * return the alignPanel containing the given viewport. Use this to get the * components currently handling the given viewport. * - * @param av * @return null or an alignPanel guaranteed to have non-null alignFrame * reference */ @@@ -595,7 -615,7 +594,7 @@@ // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass()) if (needsUpdate) { - Cache.log.debug("trigger update for " + calcId); + Console.debug("trigger update for " + calcId); } } @@@ -685,20 -705,16 +684,20 @@@ { if (AlignmentUtils.isMappable(toAdd, getAlignment())) { - if (openLinkedAlignment(toAdd, title)) - { - return; - } + openLinkedAlignment(toAdd, title); + return; } } + addDataToAlignment(toAdd); + } - /* - * No mappings, or offer declined - add sequences to this alignment - */ + /** + * adds sequences to this alignment + * + * @param toAdd + */ + void addDataToAlignment(AlignmentI toAdd) + { // TODO: JAL-407 regardless of above - identical sequences (based on ID and // provenance) should share the same dataset sequence @@@ -720,7 -736,7 +719,7 @@@ } } - ranges.setEndSeq(getAlignment().getHeight()); + ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18 firePropertyChange("alignment", null, getAlignment().getSequences()); } @@@ -733,58 -749,30 +732,58 @@@ * @param al * @param title */ - protected boolean openLinkedAlignment(AlignmentI al, String title) + protected void openLinkedAlignment(AlignmentI al, String title) { String[] options = new String[] { MessageManager.getString("action.no"), MessageManager.getString("label.split_window"), MessageManager.getString("label.new_window"), }; final String question = JvSwingUtils.wrapTooltip(true, MessageManager.getString("label.open_split_window?")); - int response = JvOptionPane.showOptionDialog(Desktop.desktop, question, + final AlignViewport us = this; + + /* + * options No, Split Window, New Window correspond to + * dialog responses 0, 1, 2 (even though JOptionPane shows them + * in reverse order) + */ + JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop) + .setResponseHandler(0, new Runnable() + { + @Override + public void run() + { + addDataToAlignment(al); + } + }).setResponseHandler(1, new Runnable() + { + @Override + public void run() + { + us.openLinkedAlignmentAs(al, title, true); + } + }).setResponseHandler(2, new Runnable() + { + @Override + public void run() + { + us.openLinkedAlignmentAs(al, title, false); + } + }); + dialog.showDialog(question, MessageManager.getString("label.open_split_window"), JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null, options, options[0]); + } - if (response != 1 && response != 2) - { - return false; - } - final boolean openSplitPane = (response == 1); - final boolean openInNewWindow = (response == 2); - + protected void openLinkedAlignmentAs(AlignmentI al, String title, + boolean newWindowOrSplitPane) + { /* * Identify protein and dna alignments. Make a copy of this one if opening * in a new split pane. */ - AlignmentI thisAlignment = openSplitPane ? new Alignment(getAlignment()) + AlignmentI thisAlignment = newWindowOrSplitPane + ? new Alignment(getAlignment()) : getAlignment(); AlignmentI protein = al.isNucleotide() ? thisAlignment : al; final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment; @@@ -805,7 -793,7 +804,7 @@@ AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); newAlignFrame.setTitle(title); - newAlignFrame.statusBar.setText(MessageManager + newAlignFrame.setStatus(MessageManager .formatMessage("label.successfully_loaded_file", new Object[] { title })); @@@ -816,7 -804,7 +815,7 @@@ // alignFrame.setFileName(file, format); // } - if (openInNewWindow) + if (!newWindowOrSplitPane) { Desktop.addInternalFrame(newAlignFrame, title, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); @@@ -824,16 -812,19 +823,16 @@@ try { - newAlignFrame.setMaximum( - jalview.bin.Cache.getDefault("SHOW_FULLSCREEN", false)); + newAlignFrame.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false)); } catch (java.beans.PropertyVetoException ex) { } - if (openSplitPane) + if (newWindowOrSplitPane) { al.alignAs(thisAlignment); protein = openSplitFrame(newAlignFrame, thisAlignment); } - - return true; } /** @@@ -895,9 -886,10 +894,9 @@@ if (ap != null) { // modify GUI elements to reflect geometry change - Dimension idw = getAlignPanel().getIdPanel().getIdCanvas() - .getPreferredSize(); + Dimension idw = ap.getIdPanel().getIdCanvas().getPreferredSize(); idw.width = i; - getAlignPanel().getIdPanel().getIdCanvas().setPreferredSize(idw); + ap.getIdPanel().getIdCanvas().setPreferredSize(idw); } } @@@ -986,35 -978,6 +985,35 @@@ @Override public void applyFeaturesStyle(FeatureSettingsModelI featureSettings) { + transferFeaturesStyles(featureSettings, false); + } + + /** + * Applies the supplied feature settings descriptor to currently known + * features. This supports an 'initial configuration' of feature colouring + * based on a preset or user favourite. This may then be modified in the usual + * way using the Feature Settings dialogue. + * + * @param featureSettings + */ + @Override + public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings) + { + transferFeaturesStyles(featureSettings, true); + } + + /** + * when mergeOnly is set, then group and feature visibility or feature colours + * are not modified for features and groups already known to the feature + * renderer. Feature ordering is always adjusted, and transparency is always + * set regardless. + * + * @param featureSettings + * @param mergeOnly + */ + private void transferFeaturesStyles(FeatureSettingsModelI featureSettings, + boolean mergeOnly) + { if (featureSettings == null) { return; @@@ -1022,25 -985,12 +1021,25 @@@ FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas .getFeatureRenderer(); + List origRenderOrder = new ArrayList<>(); + List origGroups = new ArrayList<>(); + // preserve original render order - allows differentiation between user + // configured colours and autogenerated ones + origRenderOrder.addAll(fr.getRenderOrder()); + origGroups.addAll(fr.getFeatureGroups()); + fr.findAllFeatures(true); List renderOrder = fr.getRenderOrder(); FeaturesDisplayedI displayed = fr.getFeaturesDisplayed(); - displayed.clear(); + if (!mergeOnly) + { + // only clear displayed features if we are mergeing + // displayed.clear(); + } // TODO this clears displayed.featuresRegistered - do we care? - + // + // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where + // feature visibility has already been configured is not very friendly /* * set feature colour if specified by feature settings * set visibility of all features @@@ -1049,29 -999,13 +1048,29 @@@ { FeatureColourI preferredColour = featureSettings .getFeatureColour(type); - if (preferredColour != null) - { - fr.setColour(type, preferredColour); - } - if (featureSettings.isFeatureDisplayed(type)) + FeatureColourI origColour = fr.getFeatureStyle(type); + if (!mergeOnly || (!origRenderOrder.contains(type) + || origColour == null + || (!origColour.isGraduatedColour() + && origColour.getColour() != null + && origColour.getColour().equals( + ColorUtils.createColourFromName(type))))) { - displayed.setVisible(type); + // if we are merging, only update if there wasn't already a colour + // defined for + // this type + if (preferredColour != null) + { + fr.setColour(type, preferredColour); + } + if (featureSettings.isFeatureDisplayed(type)) + { + displayed.setVisible(type); + } + else if (featureSettings.isFeatureHidden(type)) + { + displayed.setHidden(type); + } } } @@@ -1080,13 -1014,7 +1079,13 @@@ */ for (String group : fr.getFeatureGroups()) { - fr.setGroupVisibility(group, featureSettings.isGroupDisplayed(group)); + if (!mergeOnly || !origGroups.contains(group)) + { + // when merging, display groups only if the aren't already marked as not + // visible + fr.setGroupVisibility(group, + featureSettings.isGroupDisplayed(group)); + } } /* @@@ -1101,17 -1029,5 +1100,17 @@@ fr.orderFeatures(featureSettings); } fr.setTransparency(featureSettings.getTransparency()); + + fr.notifyFeaturesChanged(); + } + + public String getViewName() + { + return viewName; + } + + public void setViewName(String viewName) + { + this.viewName = viewName; } } diff --combined src/jalview/gui/AppJmol.java index 8447faf,3dd9571..64ea4dc --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@@ -20,39 -20,37 +20,39 @@@ */ package jalview.gui; -import jalview.bin.Cache; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.gui.StructureViewer.ViewerType; -import jalview.structures.models.AAStructureBindingModel; -import jalview.util.BrowserLauncher; -import jalview.util.MessageManager; -import jalview.util.Platform; -import jalview.ws.dbsources.Pdb; +import java.util.Locale; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; import java.io.File; -import java.util.ArrayList; import java.util.List; -import java.util.Vector; +import java.util.Map; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JInternalFrame; 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.Console; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.datamodel.StructureViewerModel; +import jalview.datamodel.StructureViewerModel.StructureData; +import jalview.fts.service.alphafold.AlphafoldRestClient; +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; + public class AppJmol extends StructureViewerBase { // ms to wait for Jmol to load files @@@ -60,7 -58,7 +60,7 @@@ private static final String SPACE = " "; - private static final String BACKSLASH = "\""; + private static final String QUOTE = "\""; AppJmolBinding jmb; @@@ -89,55 -87,48 +89,55 @@@ * @param bounds * @param viewid */ - public AppJmol(String[] files, String[] ids, SequenceI[][] seqs, - AlignmentPanel ap, boolean usetoColour, boolean useToAlign, - boolean leaveColouringToJmol, String loadStatus, Rectangle bounds, - String viewid) + public AppJmol(StructureViewerModel viewerModel, AlignmentPanel ap, + String sessionFile, String viewid) { - PDBEntry[] pdbentrys = new PDBEntry[files.length]; - for (int i = 0; i < pdbentrys.length; i++) - { - // PDBEntry pdbentry = new PDBEntry(files[i], ids[i]); - PDBEntry pdbentry = new PDBEntry(ids[i], null, PDBEntry.Type.PDB, - files[i]); + Map pdbData = viewerModel.getFileData(); + PDBEntry[] pdbentrys = new PDBEntry[pdbData.size()]; + SequenceI[][] seqs = new SequenceI[pdbData.size()][]; + int i = 0; + for (StructureData data : pdbData.values()) + { + PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null, + PDBEntry.Type.PDB, data.getFilePath()); pdbentrys[i] = pdbentry; + List sequencesForPdb = data.getSeqList(); + seqs[i] = sequencesForPdb + .toArray(new SequenceI[sequencesForPdb.size()]); + i++; } - // / TODO: check if protocol is needed to be set, and if chains are + + // TODO: check if protocol is needed to be set, and if chains are // autodiscovered. jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(), pdbentrys, seqs, null); jmb.setLoadingFromArchive(true); addAlignmentPanel(ap); - if (useToAlign) + if (viewerModel.isAlignWithPanel()) { useAlignmentPanelForSuperposition(ap); } initMenus(); - if (leaveColouringToJmol || !usetoColour) + boolean useToColour = viewerModel.isColourWithAlignPanel(); + boolean leaveColouringToJmol = viewerModel.isColourByViewer(); + if (leaveColouringToJmol || !useToColour) { jmb.setColourBySequence(false); seqColour.setSelected(false); viewerColour.setSelected(true); } - else if (usetoColour) + else if (useToColour) { useAlignmentPanelForColourbyseq(ap); jmb.setColourBySequence(true); seqColour.setSelected(true); viewerColour.setSelected(false); } - this.setBounds(bounds); + + this.setBounds(viewerModel.getX(), viewerModel.getY(), + viewerModel.getWidth(), viewerModel.getHeight()); setViewId(viewid); - // jalview.gui.Desktop.addInternalFrame(this, "Loading File", - // bounds.width,bounds.height); this.addInternalFrameListener(new InternalFrameAdapter() { @@@ -148,10 -139,7 +148,10 @@@ closeViewer(false); } }); - initJmol(loadStatus); // pdbentry, seq, JBPCHECK! + StringBuilder cmd = new StringBuilder(); + cmd.append("load FILES ").append(QUOTE) + .append(Platform.escapeBackslashes(sessionFile)).append(QUOTE); + initJmol(cmd.toString()); } @Override @@@ -159,14 -147,23 +159,14 @@@ { super.initMenus(); - viewerActionMenu.setText(MessageManager.getString("label.jmol")); - viewerColour .setText(MessageManager.getString("label.colour_with_jmol")); viewerColour.setToolTipText(MessageManager .getString("label.let_jmol_manage_structure_colours")); } - IProgressIndicator progressBar = null; - - @Override - protected IProgressIndicator getIProgressIndicator() - { - return progressBar; - } /** - * add a single PDB structure to a new or existing Jmol view + * display a single PDB structure in a new Jmol view * * @param pdbentry * @param seq @@@ -176,33 -173,52 +176,33 @@@ public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains, final AlignmentPanel ap) { - progressBar = ap.alignFrame; - String pdbId = pdbentry.getId(); - - /* - * If the PDB file is already loaded, the user may just choose to add to an - * existing viewer (or cancel) - */ - if (addAlreadyLoadedFile(seq, chains, ap, pdbId)) - { - return; - } - - /* - * Check if there are other Jmol views involving this alignment and prompt - * user about adding this molecule to one of them - */ - if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId)) - { - return; - } + setProgressIndicator(ap.alignFrame); - /* - * If the options above are declined or do not apply, open a new viewer - */ - openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq }); + openNewJmol(ap, alignAddedStructures, new PDBEntry[] { pdbentry }, + new SequenceI[][] + { seq }); } - private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys, - SequenceI[][] seqs) + private void openNewJmol(AlignmentPanel ap, boolean alignAdded, + 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; if (pdbentrys.length > 1) { - alignAddedStructures = true; useAlignmentPanelForSuperposition(ap); } + jmb.setColourBySequence(true); setSize(400, 400); // probably should be a configurable/dynamic default here initMenus(); addingStructures = false; - worker = new Thread(this, "OpenJmolThread"); + worker = new Thread(this, "OpenJmol"); worker.start(); this.addInternalFrameListener(new InternalFrameAdapter() @@@ -218,19 -234,40 +218,19 @@@ } /** - * create a new Jmol containing several structures superimposed using the - * given alignPanel. + * create a new Jmol containing several structures optionally superimposed + * using the given alignPanel. * * @param ap + * @param alignAdded + * - true to superimpose * @param pe * @param seqs */ - public AppJmol(AlignmentPanel ap, PDBEntry[] pe, SequenceI[][] seqs) - { - openNewJmol(ap, pe, seqs); - } - - /** - * Returns a list of any Jmol viewers. The list is restricted to those linked - * to the given alignment panel if it is not null. - */ - @Override - protected List getViewersFor(AlignmentPanel apanel) + public AppJmol(AlignmentPanel ap, boolean alignAdded, PDBEntry[] pe, + SequenceI[][] seqs) { - List result = new ArrayList<>(); - JInternalFrame[] frames = Desktop.instance.getAllFrames(); - - for (JInternalFrame frame : frames) - { - if (frame instanceof AppJmol) - { - if (apanel == null - || ((StructureViewerBase) frame).isLinkedWith(apanel)) - { - result.add((StructureViewerBase) frame); - } - } - } - return result; + openNewJmol(ap, alignAdded, pe, seqs); } void initJmol(String command) @@@ -258,18 -295,55 +258,18 @@@ { 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); } - boolean allChainsSelected = false; - - @Override - void showSelectedChains() - { - Vector 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; try { - List files = fetchPdbFiles(); + List files = jmb.fetchPdbFiles(this); if (files.size() > 0) { showFilesInViewer(files); @@@ -294,8 -368,8 +294,8 @@@ StringBuilder fileList = new StringBuilder(); for (String s : files) { - fileList.append(SPACE).append(BACKSLASH) - .append(Platform.escapeString(s)).append(BACKSLASH); + fileList.append(SPACE).append(QUOTE) + .append(Platform.escapeBackslashes(s)).append(QUOTE); } String filesString = fileList.toString(); @@@ -307,12 -381,10 +307,12 @@@ } catch (OutOfMemoryError oomerror) { new OOMWarning("When trying to open the Jmol viewer!", oomerror); - Cache.log.debug("File locations are " + filesString); + Console.debug("File locations are " + filesString); } catch (Exception ex) { - Cache.log.error("Couldn't open Jmol viewer!", ex); + Console.error("Couldn't open Jmol viewer!", ex); + ex.printStackTrace(); + return; } } else @@@ -321,23 -393,20 +321,23 @@@ 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); + Console.debug("File locations are " + filesString); + return; } catch (Exception ex) { - Cache.log.error("Couldn't add files to Jmol viewer!", ex); + Console.error("Couldn't add files to Jmol viewer!", ex); + ex.printStackTrace(); + return; } } @@@ -351,11 -420,9 +351,11 @@@ { try { - Cache.log.debug("Waiting around for jmb notify."); - Thread.sleep(waitFor); + Console.debug("Waiting around for jmb notify."); waitTotal += waitFor; + + // Thread.sleep() throws an exception in JS + Thread.sleep(waitFor); } catch (Exception e) { } @@@ -372,12 -439,12 +372,12 @@@ } // refresh the sequence colours for the new structure(s) - for (AlignmentPanel ap : _colourwith) + for (AlignmentViewPanel ap : _colourwith) { jmb.updateColours(ap); } // do superposition if asked to - if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures) + if (alignAddedStructures) { alignAddedStructures(); } @@@ -394,7 -461,7 +394,7 @@@ @Override public void run() { - if (jmb.viewer.isScriptExecuting()) + if (jmb.jmolViewer.isScriptExecuting()) { SwingUtilities.invokeLater(this); try @@@ -407,56 -474,184 +407,56 @@@ } else { - alignStructs_withAllAlignPanels(); + alignStructsWithAllAlignPanels(); } } }); - alignAddedStructures = false; + } /** - * Retrieves and saves as file any modelled PDB entries for which we do not - * already have a file saved. Returns a list of absolute paths to structure - * files which were either retrieved, or already stored but not modelled in - * the structure viewer (i.e. files to add to the viewer display). + * 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) * - * @return + * @param type */ - List fetchPdbFiles() - { - // todo - record which pdbids were successfully imported. - StringBuilder errormsgs = new StringBuilder(); - - List files = new ArrayList<>(); - String pdbid = ""; - try - { - String[] filesInViewer = jmb.getStructureFiles(); - // TODO: replace with reference fetching/transfer code (validate PDBentry - // as a DBRef?) - Pdb pdbclient = new Pdb(); - for (int pi = 0; pi < jmb.getPdbCount(); pi++) - { - String file = jmb.getPdbEntry(pi).getFile(); - if (file == null) - { - // retrieve the pdb and store it locally - 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); - } - try - { - pdbseq = pdbclient.getSequenceRecords(pdbid); - } catch (OutOfMemoryError oomerror) - { - new OOMWarning("Retrieving PDB id " + pdbid, oomerror); - } catch (Exception ex) - { - ex.printStackTrace(); - errormsgs.append("'").append(pdbid).append("'"); - } finally - { - if (progressBar != null) - { - progressBar.setProgressBar( - MessageManager.getString("label.state_completed"), - hdl); - } - } - if (pdbseq != null) - { - // just transfer the file name from the first sequence's first - // PDBEntry - file = new File(pdbseq.getSequenceAt(0).getAllPDBEntries() - .elementAt(0).getFile()).getAbsolutePath(); - jmb.getPdbEntry(pi).setFile(file); - files.add(file); - } - else - { - errormsgs.append("'").append(pdbid).append("' "); - } - } - else - { - if (filesInViewer != null && filesInViewer.length > 0) - { - addingStructures = true; // already files loaded. - for (int c = 0; c < filesInViewer.length; c++) - { - if (filesInViewer[c].equals(file)) - { - file = null; - break; - } - } - } - if (file != null) - { - files.add(file); - } - } - } - } catch (OutOfMemoryError oomerror) - { - new OOMWarning("Retrieving PDB files: " + pdbid, oomerror); - } catch (Exception ex) - { - ex.printStackTrace(); - errormsgs.append("When retrieving pdbfiles : current was: '") - .append(pdbid).append("'"); - } - if (errormsgs.length() > 0) - { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.pdb_entries_couldnt_be_retrieved", new String[] - { errormsgs.toString() }), - MessageManager.getString("label.couldnt_load_file"), - JvOptionPane.ERROR_MESSAGE); - } - return files; - } - - @Override - public void eps_actionPerformed(ActionEvent e) - { - makePDBImage(jalview.util.ImageMaker.TYPE.EPS); - } - @Override - public void png_actionPerformed(ActionEvent e) - { - makePDBImage(jalview.util.ImageMaker.TYPE.PNG); - } - - void makePDBImage(jalview.util.ImageMaker.TYPE type) + public void makePDBImage(ImageMaker.TYPE type) { int width = getWidth(); int height = getHeight(); - - jalview.util.ImageMaker im; - - if (type == jalview.util.ImageMaker.TYPE.PNG) - { - im = new jalview.util.ImageMaker(this, - jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from view", - width, height, null, null, null, 0, false); - } - else if (type == jalview.util.ImageMaker.TYPE.EPS) - { - im = new jalview.util.ImageMaker(this, - jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from view", - width, height, null, this.getTitle(), null, 0, false); - } - else - { - - im = new jalview.util.ImageMaker(this, - jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA", - width, height, null, this.getTitle(), null, 0, false); - } - - if (im.getGraphics() != null) + ImageWriterI writer = new ImageWriterI() { - jmb.viewer.renderScreenImage(im.getGraphics(), width, height); - im.writeImage(); - } + @Override + public void exportImage(Graphics g) throws Exception + { + jmb.jmolViewer.renderScreenImage(g, width, height); + } + }; + String view = MessageManager.getString("action.view") + .toLowerCase(Locale.ROOT); + ImageExporter exporter = new ImageExporter(writer, + 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) @@@ -522,8 -717,7 +522,8 @@@ } } } - 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); @@@ -534,7 -728,7 +534,7 @@@ } else { - jmb.viewer.renderScreenImage(g, currentSize.width, + jmb.jmolViewer.renderScreenImage(g, currentSize.width, currentSize.height); } } @@@ -547,6 -741,12 +547,6 @@@ } @Override - public String getStateInfo() - { - return jmb == null ? null : jmb.viewer.getStateInfo(); - } - - @Override public ViewerType getViewerType() { return ViewerType.JMOL; diff --combined src/jalview/gui/CalculationChooser.java index c43d59b,bae4986..ae92168 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@@ -27,7 -27,6 +27,7 @@@ import jalview.analysis.scoremodels.Sco import jalview.analysis.scoremodels.SimilarityParams; import jalview.api.analysis.ScoreModelI; import jalview.api.analysis.SimilarityParamsI; +import jalview.bin.Cache; import jalview.datamodel.SequenceGroup; import jalview.ext.archaeopteryx.AptxInit; import jalview.util.MessageManager; @@@ -47,6 -46,7 +47,7 @@@ import java.awt.event.FocusListener import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@@ -159,15 -159,12 +160,15 @@@ public class CalculationChooser extend pca = new JRadioButton( MessageManager.getString("label.principal_component_analysis")); pca.setOpaque(false); + neighbourJoining = new JRadioButton( MessageManager.getString("label.tree_calc_nj")); neighbourJoining.setSelected(true); + neighbourJoining.setOpaque(false); + averageDistance = new JRadioButton( MessageManager.getString("label.tree_calc_av")); - neighbourJoining.setOpaque(false); + averageDistance.setOpaque(false); JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); calcChoicePanel.setOpaque(false); @@@ -176,8 -173,8 +177,8 @@@ JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); treePanel.setOpaque(false); - treePanel.setBorder(BorderFactory - .createTitledBorder(MessageManager.getString("label.tree"))); + JvSwingUtils.createTitledBorder(treePanel, + MessageManager.getString("label.tree"), true); // then copy the inset dimensions for the border-less PCA panel JPanel pcaBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); @@@ -277,9 -274,9 +278,9 @@@ setMinimumSize(new Dimension(325, height - 10)); String title = MessageManager.getString("label.choose_calculation"); - if (af.getViewport().viewName != null) + if (af.getViewport().getViewName() != null) { - title = title + " (" + af.getViewport().viewName + ")"; + title = title + " (" + af.getViewport().getViewName() + ")"; } Desktop.addInternalFrame(frame, title, width, height, false); @@@ -297,7 -294,6 +298,7 @@@ }; }); + validateCalcTypes(); frame.setLayer(JLayeredPane.PALETTE_LAYER); } @@@ -427,40 -423,38 +428,40 @@@ Object curSel = comboBox.getSelectedItem(); toolTips.clear(); DefaultComboBoxModel model = new DefaultComboBoxModel<>(); + /* + * select the score models applicable to the alignment type + */ + boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); + List models = getApplicableScoreModels(nucleotide, + pca.isSelected()); /* * now we can actually add entries to the combobox, * remembering their descriptions for tooltips */ - ScoreModels scoreModels = ScoreModels.getInstance(); boolean selectedIsPresent = false; - for (ScoreModelI sm : scoreModels.getModels()) + for (ScoreModelI sm : models) { - boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); - if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide) + if (curSel != null && sm.getName().equals(curSel)) + { + selectedIsPresent = true; + curSel = sm.getName(); + } + model.addElement(sm.getName()); + + /* + * tooltip is description if provided, else text lookup with + * fallback on the model name + */ + String tooltip = sm.getDescription(); + if (tooltip == null) { - if (curSel != null && sm.getName().equals(curSel)) - { - selectedIsPresent = true; - curSel = sm.getName(); - } - model.addElement(sm.getName()); - - /* - * tooltip is description if provided, else text lookup with - * fallback on the model name - */ - String tooltip = sm.getDescription(); - if (tooltip == null) - { - tooltip = MessageManager.getStringOrReturn("label.score_model_", - sm.getName()); - } - toolTips.add(tooltip); + tooltip = MessageManager.getStringOrReturn("label.score_model_", + sm.getName()); } + toolTips.add(tooltip); } + if (selectedIsPresent) { model.setSelectedItem(curSel); @@@ -470,48 -464,9 +471,50 @@@ } /** + * Builds a list of score models which are applicable for the alignment and + * calculation type (peptide or generic models for protein, nucleotide or + * generic models for nucleotide). + *

+ * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA. + * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62 + * was the only score matrix supported. This is included if property + * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file. + * + * @param nucleotide + * @param forPca + * @return + */ + protected static List getApplicableScoreModels( + boolean nucleotide, boolean forPca) + { + List filtered = new ArrayList<>(); + + ScoreModels scoreModels = ScoreModels.getInstance(); + for (ScoreModelI sm : scoreModels.getModels()) + { + if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA()) + { + filtered.add(sm); + } + } + + /* + * special case: add BLOSUM62 as last option for nucleotide PCA, + * for backwards compatibility with Jalview < 2.8 (JAL-2962) + */ + if (nucleotide && forPca + && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false)) + { + filtered.add(scoreModels.getBlosum62()); + } + + return filtered; + } + + /** * Open and calculate the selected tree or PCA on 'OK' + * + * @throws IOException */ protected void calculate_actionPerformed() { @@@ -525,7 -480,14 +528,14 @@@ } else { - createTree(substitutionMatrix, params); + try + { + createTree(substitutionMatrix, params); + } catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } @@@ -538,17 -500,23 +548,23 @@@ } protected void createTree(String substitutionMatrix, - SimilarityParamsI params) + SimilarityParamsI params) throws IOException { String treeAlgo = determineTreeAlgo(); TreeCalculator treeCalculator = new TreeCalculator(treeAlgo, substitutionMatrix, params); TreeBuilder calculatedTree = treeCalculator.makeTree(af.getViewport()); - AptxInit.createInstance(calculatedTree); + // AptxInit.createInstanceFromCalculation(calculatedTree); TreeModel tree = new TreeModel(calculatedTree); - openTreePanel(tree, treeAlgo, substitutionMatrix); + jalview.io.NewickFile newick = new jalview.io.NewickFile( + tree.getTopNode()); + String output = newick.print(tree.hasBootstrap(), tree.hasDistances(), + tree.hasRootDistance()); + AptxInit.createInstanceFromNhx(af.getTitle(), output, + af.getViewport()); + // openTreePanel(tree, treeAlgo, substitutionMatrix); } @@@ -624,13 -592,7 +640,13 @@@ JvOptionPane.WARNING_MESSAGE); return; } + + /* + * construct the panel and kick off its calculation thread + */ pcaPanel = new PCAPanel(af.alignPanel, modelName, params); + new Thread(pcaPanel).start(); + } /** diff --combined src/jalview/gui/ChimeraViewFrame.java index 367ca7f,4b45459..e78938a --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@@ -20,38 -20,42 +20,38 @@@ */ 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.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.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Random; +import java.util.Map; -import javax.swing.JCheckBoxMenuItem; import javax.swing.JInternalFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; +import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureRenderer; +import jalview.bin.Console; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.datamodel.StructureViewerModel; +import jalview.datamodel.StructureViewerModel.StructureData; +import jalview.ext.rbvi.chimera.JalviewChimeraBinding; +import jalview.gui.StructureViewer.ViewerType; +import jalview.io.DataSourceType; +import jalview.io.StructureFile; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.ImageMaker.TYPE; +import jalview.util.MessageManager; +import jalview.util.Platform; + /** * GUI elements for handling an external chimera display * @@@ -62,6 -66,8 +62,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 @@@ -69,14 -75,12 +69,14 @@@ */ private String chimeraSessionFile = null; - private Random random = new Random(); - private int myWidth = 500; private int myHeight = 150; + private JMenuItem writeFeatures = null; + + private JMenu fetchAttributes = null; + /** * Initialise menu options. */ @@@ -85,93 -89,120 +85,93 @@@ { super.initMenus(); savemenu.setVisible(false); // not yet implemented viewMenu.add(fitToWindow); - /* - * exchange of Jalview features and Chimera attributes is for now - * an optionally enabled experimental feature - */ - if (Desktop.instance.showExperimental()) + writeFeatures = new JMenuItem( + MessageManager.getString("label.create_viewer_attributes")); + writeFeatures.setToolTipText( + MessageManager.getString("label.create_viewer_attributes_tip")); + writeFeatures.addActionListener(new ActionListener() { - JMenuItem writeFeatures = new JMenuItem( - MessageManager.getString("label.create_chimera_attributes")); - writeFeatures.setToolTipText(MessageManager - .getString("label.create_chimera_attributes_tip")); - writeFeatures.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent e) { - @Override - public void actionPerformed(ActionEvent e) - { - sendFeaturesToChimera(); - } - }); - viewerActionMenu.add(writeFeatures); + sendFeaturesToChimera(); + } + }); + viewerActionMenu.add(writeFeatures); - final JMenu fetchAttributes = new JMenu( - MessageManager.getString("label.fetch_chimera_attributes")); - fetchAttributes.setToolTipText(MessageManager - .getString("label.fetch_chimera_attributes_tip")); - fetchAttributes.addMouseListener(new MouseAdapter() - { + fetchAttributes = new JMenu(MessageManager.formatMessage( + "label.fetch_viewer_attributes", getViewerName())); + fetchAttributes.setToolTipText(MessageManager.formatMessage( + "label.fetch_viewer_attributes_tip", getViewerName())); + fetchAttributes.addMouseListener(new MouseAdapter() + { - @Override - public void mouseEntered(MouseEvent e) - { - buildAttributesMenu(fetchAttributes); - } - }); - viewerActionMenu.add(fetchAttributes); - } + @Override + public void mouseEntered(MouseEvent e) + { + buildAttributesMenu(fetchAttributes); + } + }); + viewerActionMenu.add(fetchAttributes); } + @Override + protected void buildActionMenu() + { + super.buildActionMenu(); + // add these back in after menu is refreshed + viewerActionMenu.add(writeFeatures); + viewerActionMenu.add(fetchAttributes); + + }; + /** - * Query Chimera for its residue attribute names and add them as items off the - * attributes menu + * Query the structure viewer for its residue attribute names and add them as + * items off the attributes menu * * @param attributesMenu */ protected void buildAttributesMenu(JMenu attributesMenu) { - List atts = jmb.sendChimeraCommand("list resattr", true); - if (atts == null) - { - return; - } + List 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) + if (getBinding().copyStructureAttributesToFeatures(attName, + getAlignmentPanel()) > 0) { - getChimeraAttributes(attName); + getAlignmentPanel().getFeatureRenderer().featuresAdded(); } - }); - attributesMenu.add(menuItem); - } + } + }); + attributesMenu.add(menuItem); } } /** - * Read residues in Chimera with the given attribute name, and set as features - * on the corresponding sequence positions (if any) - * - * @param attName - */ - protected void getChimeraAttributes(String attName) - { - jmb.copyStructureAttributesToFeatures(attName, getAlignmentPanel()); - } - - /** - * Send a command to Chimera to create residue attributes for Jalview features - *

- * The syntax is: setattr r - *

- * For example: setattr r jv:chain "Ferredoxin-1, Chloroplastic" #0:94.A + * Sends command(s) to the structure viewer to create residue attributes for + * visible Jalview features */ protected void sendFeaturesToChimera() { + // todo pull up? int count = jmb.sendFeaturesToViewer(getAlignmentPanel()); - statusBar.setText( - MessageManager.formatMessage("label.attributes_set", count)); + statusBar.setText(MessageManager.formatMessage("label.attributes_set", + count, getViewerName())); } /** - * add a single PDB structure to a new or existing Chimera view + * open a single PDB structure in a new Chimera view * * @param pdbentry * @param seq @@@ -182,7 -213,30 +182,7 @@@ String[] chains, final AlignmentPanel ap) { this(); - String pdbId = pdbentry.getId(); - /* - * If the PDB file is already loaded, the user may just choose to add to an - * existing viewer (or cancel) - */ - if (addAlreadyLoadedFile(seq, chains, ap, pdbId)) - { - return; - } - - /* - * Check if there are other Chimera views involving this alignment and give - * user the option to add and align this molecule to one of them (or cancel) - */ - if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId)) - { - return; - } - - /* - * If the options above are declined or do not apply, show the structure in - * a new viewer - */ openNewChimera(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq }); @@@ -193,9 -247,9 +193,9 @@@ */ protected void createProgressBar() { - if (progressBar == null) + if (getProgressIndicator() == null) { - progressBar = new ProgressBar(statusPanel, statusBar); + setProgressIndicator(new ProgressBar(statusPanel, statusBar)); } } @@@ -203,12 -257,14 +203,12 @@@ SequenceI[][] seqs) { createProgressBar(); - jmb = new JalviewChimeraBindingModel(this, - ap.getStructureSelectionManager(), pdbentrys, seqs, null); + jmb = newBindingModel(ap, pdbentrys, seqs); addAlignmentPanel(ap); useAlignmentPanelForColourbyseq(ap); if (pdbentrys.length > 1) { - alignAddedStructures = true; useAlignmentPanelForSuperposition(ap); } jmb.setColourBySequence(true); @@@ -216,7 -272,7 +216,7 @@@ initMenus(); addingStructures = false; - worker = new Thread(this, "OpenChimeraThread"); + worker = new Thread(this, "OpenChimera"); worker.start(); this.addInternalFrameListener(new InternalFrameAdapter() @@@ -231,13 -287,6 +231,13 @@@ } + 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 @@@ -250,34 -299,22 +250,34 @@@ * @param colourBySequence * @param newViewId */ - public ChimeraViewFrame(String chimeraSessionFile, - AlignmentPanel alignPanel, PDBEntry[] pdbArray, - SequenceI[][] seqsArray, boolean colourByChimera, - boolean colourBySequence, String newViewId) + public ChimeraViewFrame(StructureViewerModel viewerData, + AlignmentPanel alignPanel, String sessionFile, String vid) { this(); - setViewId(newViewId); - this.chimeraSessionFile = chimeraSessionFile; + setViewId(vid); + this.chimeraSessionFile = sessionFile; + Map pdbData = viewerData.getFileData(); + PDBEntry[] pdbArray = new PDBEntry[pdbData.size()]; + SequenceI[][] seqsArray = new SequenceI[pdbData.size()][]; + int i = 0; + for (StructureData data : pdbData.values()) + { + PDBEntry pdbentry = new PDBEntry(data.getPdbId(), null, + PDBEntry.Type.PDB, data.getFilePath()); + pdbArray[i] = pdbentry; + List sequencesForPdb = data.getSeqList(); + seqsArray[i] = sequencesForPdb + .toArray(new SequenceI[sequencesForPdb.size()]); + i++; + } openNewChimera(alignPanel, pdbArray, seqsArray); - if (colourByChimera) + if (viewerData.isColourByViewer()) { jmb.setColourBySequence(false); seqColour.setSelected(false); viewerColour.setSelected(true); } - else if (colourBySequence) + else if (viewerData.isColourWithAlignPanel()) { jmb.setColourBySequence(true); seqColour.setSelected(true); @@@ -286,18 -323,17 +286,18 @@@ } /** - * create a new viewer containing several structures superimposed using the - * given alignPanel. + * create a new viewer containing several structures, optionally superimposed + * using the given alignPanel. * * @param pe * @param seqs * @param ap */ - public ChimeraViewFrame(PDBEntry[] pe, SequenceI[][] seqs, - AlignmentPanel ap) + public ChimeraViewFrame(PDBEntry[] pe, boolean alignAdded, + SequenceI[][] seqs, AlignmentPanel ap) { this(); + setAlignAddedStructures(alignAdded); openNewChimera(ap, pe, seqs); } @@@ -316,6 -352,29 +316,6 @@@ } /** - * Returns a list of any Chimera viewers in the desktop. The list is - * restricted to those linked to the given alignment panel if it is not null. - */ - @Override - protected List getViewersFor(AlignmentPanel ap) - { - List result = new ArrayList<>(); - JInternalFrame[] frames = Desktop.instance.getAllFrames(); - - for (JInternalFrame frame : frames) - { - if (frame instanceof ChimeraViewFrame) - { - if (ap == null || ((StructureViewerBase) frame).isLinkedWith(ap)) - { - result.add((StructureViewerBase) frame); - } - } - } - return result; - } - - /** * Launch Chimera. If we have a chimera session file name, send Chimera the * command to open its saved session file. */ @@@ -329,11 -388,9 +329,11 @@@ 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); + jmb.closeViewer(true); this.dispose(); return; } @@@ -352,6 -409,71 +352,6 @@@ } /** - * Show only the selected chain(s) in the viewer - */ - @Override - void showSelectedChains() - { - List 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). */ @@@ -410,7 -532,7 +410,7 @@@ { filePDB.add(thePdbEntry); filePDBpos.add(Integer.valueOf(pi)); - files.append(" \"" + Platform.escapeString(file) + "\""); + files.append(" \"" + Platform.escapeBackslashes(file) + "\""); } } } catch (OutOfMemoryError oomerror) @@@ -444,15 -566,9 +444,15 @@@ initChimera(); } catch (Exception ex) { - Cache.log.error("Couldn't open Chimera viewer!", ex); + Console.error("Couldn't open Chimera viewer!", ex); } } + if (!jmb.isViewerRunning()) + { + // nothing to do + // TODO: ensure we tidy up JAL-3619 + return; + } int num = -1; for (PDBEntry pe : filePDB) { @@@ -485,8 -601,8 +485,8 @@@ 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) { @@@ -495,12 -611,12 +495,12 @@@ oomerror); } catch (Exception ex) { - Cache.log.error( + Console.error( "Couldn't open " + pe.getFile() + " in Chimera viewer!", ex); } finally { - Cache.log.debug("File locations are " + files); + Console.debug("File locations are " + files); } } } @@@ -511,8 -627,7 +511,8 @@@ /* * ensure that any newly discovered features (e.g. RESNUM) - * are added to any open feature settings dialog + * are notified to the FeatureRenderer (and added to any + * open feature settings dialog) */ FeatureRenderer fr = getBinding().getFeatureRenderer(null); if (fr != null) @@@ -521,21 -636,22 +521,21 @@@ } // refresh the sequence colours for the new structure(s) - for (AlignmentPanel ap : _colourwith) + for (AlignmentViewPanel ap : _colourwith) { jmb.updateColours(ap); } // do superposition if asked to - if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures) + if (alignAddedStructures) { new Thread(new Runnable() { @Override public void run() { - alignStructs_withAllAlignPanels(); + alignStructsWithAllAlignPanels(); } }).start(); - alignAddedStructures = false; } addingStructures = false; } @@@ -543,11 -659,130 +543,11 @@@ 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 - { - // FIXME: this is duplicated code with Jmol frame ? - 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(ActionEvent e) - { - throw new Error(MessageManager - .getString("error.eps_generation_not_implemented")); - } - @Override - public void png_actionPerformed(ActionEvent e) + public void makePDBImage(TYPE imageType) { - throw new Error(MessageManager - .getString("error.png_generation_not_implemented")); - } - - @Override - public void showHelp_actionPerformed(ActionEvent actionEvent) - { - try - { - BrowserLauncher - .openURL("https://www.cgl.ucsf.edu/chimera/docs/UsersGuide"); - } catch (IOException ex) - { - } + throw new UnsupportedOperationException( + "Image export for Chimera is not implemented"); } @Override @@@ -556,6 -791,82 +556,6 @@@ 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() - { - jmb.focusView(); - } - @Override public ViewerType getViewerType() { @@@ -567,4 -878,26 +567,4 @@@ { 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; - } } diff --combined src/jalview/gui/Console.java index 592f666,9d847a8..140f6cc --- a/src/jalview/gui/Console.java +++ b/src/jalview/gui/Console.java @@@ -20,20 -20,15 +20,20 @@@ */ package jalview.gui; -import jalview.util.MessageManager; - import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Dimension; import java.awt.GraphicsEnvironment; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.Rectangle; import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; @@@ -42,23 -37,12 +42,23 @@@ import java.io.PipedInputStream import java.io.PipedOutputStream; import java.io.PrintStream; +import javax.swing.BorderFactory; import javax.swing.JButton; +import javax.swing.JComboBox; import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; +import javax.swing.border.Border; +import javax.swing.text.DefaultCaret; -import org.apache.log4j.SimpleLayout; +import jalview.log.JLoggerI.LogLevel; +import jalview.log.JLoggerLog4j; +import jalview.log.JalviewAppender; +import jalview.util.ChannelProperties; +import jalview.util.MessageManager; +import jalview.util.Platform; /** * Simple Jalview Java Console. Version 1 - allows viewing of console output @@@ -104,10 -88,6 +104,10 @@@ public class Console extends WindowAdap private int MIN_HEIGHT = 250; + private JComboBox logLevelCombo = new JComboBox(); + + protected LogLevel startingLogLevel = LogLevel.INFO; + public Console() { // create all components and add them @@@ -135,138 -115,17 +135,138 @@@ // textArea = cpt.getTextArea(); textArea = new JTextArea(); textArea.setEditable(false); - JButton button = new JButton(MessageManager.getString("action.clear")); + // autoscroll + DefaultCaret caret = (DefaultCaret) textArea.getCaret(); + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + // toggle autoscroll by clicking on the text area + Border pausedBorder = BorderFactory.createMatteBorder(2, 2, 2, 2, + textArea.getForeground()); + Border noBorder = BorderFactory.createEmptyBorder(2, 2, 2, 2); + JScrollPane scrollPane = new JScrollPane(textArea); + scrollPane.setBorder(noBorder); + textArea.addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if (e.getButton() == MouseEvent.BUTTON1) + { + if (caret.getUpdatePolicy() == DefaultCaret.ALWAYS_UPDATE) + { + caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); + scrollPane.setBorder(pausedBorder); + } + else + { + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + textArea.setCaretPosition(textArea.getDocument().getLength()); + scrollPane.setBorder(noBorder); + } + } + } + }); + + JButton clearButton = new JButton( + MessageManager.getString("action.clear")); + JButton copyToClipboardButton = new JButton( + MessageManager.getString("label.copy_to_clipboard")); + copyToClipboardButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + copyConsoleTextToClipboard(); + } + }); + copyToClipboardButton.addMouseListener(new MouseAdapter() + { + private Color bg = textArea.getBackground(); + + private Color fg = textArea.getForeground(); + + public void mousePressed(MouseEvent e) + { + textArea.setBackground(textArea.getSelectionColor()); + textArea.setForeground(textArea.getSelectedTextColor()); + } + + public void mouseReleased(MouseEvent e) + { + textArea.setBackground(bg); + textArea.setForeground(fg); + } + + }); + copyToClipboardButton.setToolTipText( + MessageManager.getString("label.copy_to_clipboard_tooltip")); + + JLabel logLevelLabel = new JLabel( + MessageManager.getString("label.log_level") + ":"); + + // logLevelCombo.addItem(LogLevel.ALL); + logLevelCombo.addItem(LogLevel.TRACE); + logLevelCombo.addItem(LogLevel.DEBUG); + logLevelCombo.addItem(LogLevel.INFO); + logLevelCombo.addItem(LogLevel.WARN); + // logLevelCombo.addItem(LogLevel.ERROR); + // logLevelCombo.addItem(LogLevel.FATAL); + // logLevelCombo.addItem(LogLevel.ERROR); + // logLevelCombo.addItem(LogLevel.OFF); + // set startingLogLevel + startingLogLevel = jalview.bin.Console.log == null ? LogLevel.INFO + : jalview.bin.Console.log.getLevel(); + setChosenLogLevelCombo(); + logLevelCombo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (jalview.bin.Console.log != null) + { + jalview.bin.Console.log + .setLevel((LogLevel) logLevelCombo.getSelectedItem()); + } + } + + }); // frame = cpt; frame.getContentPane().setLayout(new BorderLayout()); - frame.getContentPane().add(new JScrollPane(textArea), - BorderLayout.CENTER); - frame.getContentPane().add(button, BorderLayout.SOUTH); + frame.getContentPane().add(scrollPane, BorderLayout.CENTER); + JPanel southPanel = new JPanel(); + southPanel.setLayout(new GridBagLayout()); + + JPanel logLevelPanel = new JPanel(); + logLevelPanel.setAlignmentX(JPanel.LEFT_ALIGNMENT); + logLevelPanel.add(logLevelLabel); + logLevelPanel.add(logLevelCombo); + String logLevelTooltip = MessageManager.formatMessage( + "label.log_level_tooltip", startingLogLevel.toString()); + logLevelLabel.setToolTipText(logLevelTooltip); + logLevelCombo.setToolTipText(logLevelTooltip); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 1; + gbc.gridheight = 1; + gbc.weightx = 0.1; + southPanel.add(logLevelPanel, gbc); + + gbc.gridx++; + gbc.weightx = 0.8; + gbc.fill = GridBagConstraints.HORIZONTAL; + southPanel.add(clearButton, gbc); + + gbc.gridx++; + gbc.weightx = 0.1; + gbc.fill = GridBagConstraints.NONE; + southPanel.add(copyToClipboardButton, gbc); + + southPanel.setVisible(true); + frame.getContentPane().add(southPanel, BorderLayout.SOUTH); frame.setVisible(visible); updateConsole = visible; frame.addWindowListener(this); - button.addActionListener(this); + clearButton.addActionListener(this); + if (redirect) { redirectStreams(); @@@ -279,67 -138,17 +279,67 @@@ // Starting two seperate threads to read from the PipedInputStreams // - reader = new Thread(this, "ConsoleReader1Thread"); + reader = new Thread(this, "ConsoleReader1"); reader.setDaemon(true); reader.start(); // - reader2 = new Thread(this, "ConsoleReader2Thread"); + reader2 = new Thread(this, "ConsoleReader2"); reader2.setDaemon(true); reader2.start(); // and a thread to append text to the textarea - textAppender = new Thread(this, "ConsoleTextAppendThread"); + textAppender = new Thread(this, "ConsoleTextAppend"); textAppender.setDaemon(true); textAppender.start(); + + // set icons + frame.setIconImages(ChannelProperties.getIconList()); + } + + private void setChosenLogLevelCombo() + { + setChosenLogLevelCombo(startingLogLevel); + } + + private void setChosenLogLevelCombo(LogLevel setLogLevel) + { + logLevelCombo.setSelectedItem(setLogLevel); + if (!logLevelCombo.getSelectedItem().equals(setLogLevel)) + { + // setLogLevel not (yet) in list + if (setLogLevel != null && setLogLevel instanceof LogLevel) + { + // add new item to list (might be set via .jalview_properties) + boolean added = false; + for (int i = 0; i < logLevelCombo.getItemCount(); i++) + { + LogLevel l = (LogLevel) logLevelCombo.getItemAt(i); + if (l.compareTo(setLogLevel) >= 0) + { + logLevelCombo.insertItemAt(setLogLevel, i); + added = true; + break; + } + } + if (!added) // lower priority than others or some confusion -- add to + // end of list + { + logLevelCombo.addItem(setLogLevel); + } + logLevelCombo.setSelectedItem(setLogLevel); + } + else + { + logLevelCombo.setSelectedItem(LogLevel.INFO); + } + } + } + + private void copyConsoleTextToClipboard() + { + String consoleText = textArea.getText(); + StringSelection consoleTextSelection = new StringSelection(consoleText); + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + cb.setContents(consoleTextSelection, null); } PipedOutputStream pout = null, perr = null; @@@ -446,7 -255,7 +446,7 @@@ // We do it with a seperate Thread becasue we don't wan't to break a Thread // used by the Console. System.out.println("\nLets throw an error on this console"); - errorThrower = new Thread(this, "ConsoleErrorLogThread"); + errorThrower = new Thread(this, "ConsoleErrorLog"); errorThrower.setDaemon(true); errorThrower.start(); } @@@ -491,32 -300,21 +491,32 @@@ Rectangle bounds = desktop.getLastKnownDimensions("JAVA_CONSOLE_"); if (bounds == null) { - frame = initFrame("Jalview Java Console", desktop.getWidth() / 2, - desktop.getHeight() / 4, desktop.getX(), desktop.getY()); + frame = initFrame( + ChannelProperties.getProperty("app_name") + " Java Console", + desktop.getWidth() / 2, desktop.getHeight() / 4, + desktop.getX(), desktop.getY()); } else { - frame = initFrame("Jalview Java Console", bounds.width, bounds.height, - bounds.x, bounds.y); + frame = initFrame( + ChannelProperties.getProperty("app_name") + " Java Console", + bounds.width, bounds.height, bounds.x, bounds.y); } frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); // desktop.add(frame); initConsole(false); - JalviewAppender jappender = new JalviewAppender(); - jappender.setLayout(new SimpleLayout()); - JalviewAppender.setTextArea(textArea); - org.apache.log4j.Logger.getRootLogger().addAppender(jappender); + LogLevel level = (LogLevel) logLevelCombo.getSelectedItem(); + if (!Platform.isJS()) + { + JalviewAppender jappender = new JalviewAppender(level); + JalviewAppender.setTextArea(textArea); + jappender.start(); + if (jalview.bin.Console.log != null + && jalview.bin.Console.log instanceof JLoggerLog4j) + { + JLoggerLog4j.addAppender(jalview.bin.Console.log, jappender); + } + } } public synchronized void stopConsole() @@@ -699,6 -497,7 +699,6 @@@ } catch (InterruptedException e) { } - ; } } else @@@ -834,10 -633,6 +834,10 @@@ return input; } + /** + * @j2sIgnore + * @param arg + */ public static void main(String[] arg) { new Console().test(); // create console with not reference @@@ -849,19 -644,12 +849,19 @@@ frame.setVisible(selected); if (selected == true) { + setChosenLogLevelCombo(); redirectStreams(); updateConsole = true; frame.toFront(); } else { + // reset log level to what it was before + if (jalview.bin.Console.log != null) + { + jalview.bin.Console.log.setLevel(startingLogLevel); + } + unredirectStreams(); updateConsole = false; } diff --combined src/jalview/gui/Desktop.java index 1a42f58,6eea78d..92cc153 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@@ -20,14 -20,38 +20,14 @@@ */ package jalview.gui; -import static jalview.util.UrlConstants.SEQUENCE_ID; - -import jalview.api.AlignViewportI; -import jalview.api.AlignmentViewPanel; -import jalview.bin.Cache; -import jalview.bin.Jalview; -import jalview.io.DataSourceType; -import jalview.io.FileFormat; -import jalview.io.FileFormatException; -import jalview.io.FileFormatI; -import jalview.io.FileFormats; -import jalview.io.FileLoader; -import jalview.io.IdentifyFile; -import jalview.io.JalviewFileChooser; -import jalview.io.JalviewFileView; -import jalview.jbgui.GSplitFrame; -import jalview.jbgui.GStructureViewer; -import jalview.structure.StructureSelectionManager; -import jalview.urls.IdOrgSettings; -import jalview.util.ImageMaker; -import jalview.util.MessageManager; -import jalview.util.Platform; -import jalview.util.UrlConstants; -import jalview.viewmodel.AlignmentViewport; -import jalview.ws.params.ParamManager; -import jalview.ws.utils.UrlDownloadClient; +import java.util.Locale; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Point; import java.awt.Rectangle; @@@ -44,91 -68,53 +44,91 @@@ import java.awt.dnd.DropTargetEvent import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.io.BufferedInputStream; import java.io.File; -import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; +import java.lang.reflect.Field; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.ListIterator; -import java.util.StringTokenizer; import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultDesktopManager; import javax.swing.DesktopManager; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDesktopPane; -import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; +import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; -import javax.swing.event.MenuEvent; -import javax.swing.event.MenuListener; + +import org.stackoverflowusers.file.WindowsShortcut; + +import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; +import jalview.bin.Cache; +import jalview.bin.Jalview; +import jalview.gui.ImageExporter.ImageWriterI; +import jalview.io.BackupFiles; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.FileFormatException; +import jalview.io.FileFormatI; +import jalview.io.FileFormats; +import jalview.io.FileLoader; +import jalview.io.FormatAdapter; +import jalview.io.IdentifyFile; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; +import jalview.jbgui.GSplitFrame; +import jalview.jbgui.GStructureViewer; +import jalview.project.Jalview2XML; +import jalview.structure.StructureSelectionManager; +import jalview.urls.IdOrgSettings; +import jalview.util.BrowserLauncher; +import jalview.util.ChannelProperties; +import jalview.util.ImageMaker.TYPE; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.util.ShortcutKeyMaskExWrapper; +import jalview.util.UrlConstants; +import jalview.viewmodel.AlignmentViewport; +import jalview.ws.params.ParamManager; +import jalview.ws.utils.UrlDownloadClient; /** * Jalview Desktop @@@ -141,38 -127,6 +141,38 @@@ public class Desktop extends jalview.jb implements DropTargetListener, ClipboardOwner, IProgressIndicator, jalview.api.StructureSelectionManagerProvider { + private static final String CITATION; + static + { + URL bg_logo_url = ChannelProperties.getImageURL( + "bg_logo." + String.valueOf(SplashScreen.logoSize)); + URL uod_logo_url = ChannelProperties.getImageURL( + "uod_banner." + String.valueOf(SplashScreen.logoSize)); + boolean logo = (bg_logo_url != null || uod_logo_url != null); + StringBuilder sb = new StringBuilder(); + sb.append( + "

Jalview is free software released under GPLv3.

Development is managed by The Barton Group, University of Dundee, Scotland, UK."); + if (logo) + { + sb.append("
"); + } + sb.append(bg_logo_url == null ? "" + : "\"Barton"); + sb.append(uod_logo_url == null ? "" + : " \"University"); + sb.append( + "

For help, see www.jalview.org/faq and join discourse.jalview.org"); + sb.append("

If you use Jalview, please cite:" + + "
Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)" + + "
Jalview Version 2 - a multiple sequence alignment editor and analysis workbench" + + "
Bioinformatics doi: 10.1093/bioinformatics/btp033"); + CITATION = sb.toString(); + } + + private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)"; + private static int DEFAULT_MIN_WIDTH = 300; private static int DEFAULT_MIN_HEIGHT = 250; @@@ -183,14 -137,8 +183,14 @@@ private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES"; + protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT"; + + public static HashMap savingFiles = new HashMap(); + private JalviewChangeSupport changeSupport = new JalviewChangeSupport(); + public static boolean nosplash = false; + /** * news reader - null if it was never started. */ @@@ -238,13 -186,6 +238,13 @@@ public static MyDesktopPane desktop; + public static MyDesktopPane getDesktop() + { + // BH 2018 could use currentThread() here as a reference to a + // Hashtable in JavaScript + return desktop; + } + static int openFrameCount = 0; static final int xOffset = 30; @@@ -387,124 -328,41 +387,124 @@@ */ public Desktop() { + super(); /** * A note to implementors. It is ESSENTIAL that any activities that might * block are spawned off as threads rather than waited for during this * constructor. */ instance = this; - doVamsasClientCheck(); doConfigureStructurePrefs(); - setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION")); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE", - false); - boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE", - false); + setTitle(ChannelProperties.getProperty("app_name") + " " + + Cache.getProperty("VERSION")); + + /** + * Set taskbar "grouped windows" name for linux desktops (works in GNOME and + * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not + * officially documented or guaranteed to exist, so we access it via + * reflection. There appear to be unfathomable criteria about what this + * string can contain, and it if doesn't meet those criteria then "java" + * (KDE) or "jalview-bin-Jalview" (GNOME) is used. "Jalview", "Jalview + * Develop" and "Jalview Test" seem okay, but "Jalview non-release" does + * not. The reflection access may generate a warning: WARNING: An illegal + * reflective access operation has occurred WARNING: Illegal reflective + * access by jalview.gui.Desktop () to field + * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided. + */ + if (Platform.isLinux()) + { + try + { + Toolkit xToolkit = Toolkit.getDefaultToolkit(); + Field[] declaredFields = xToolkit.getClass().getDeclaredFields(); + Field awtAppClassNameField = null; + + if (Arrays.stream(declaredFields) + .anyMatch(f -> f.getName().equals("awtAppClassName"))) + { + awtAppClassNameField = xToolkit.getClass() + .getDeclaredField("awtAppClassName"); + } + + String title = ChannelProperties.getProperty("app_name"); + if (awtAppClassNameField != null) + { + awtAppClassNameField.setAccessible(true); + awtAppClassNameField.set(xToolkit, title); + } + else + { + jalview.bin.Console.debug("XToolkit: awtAppClassName not found"); + } + } catch (Exception e) + { + jalview.bin.Console.debug("Error setting awtAppClassName"); + jalview.bin.Console.trace(Cache.getStackTraceString(e)); + } + } + + /** + * APQHandlers sets handlers for About, Preferences and Quit actions + * peculiar to macOS's application menu. APQHandlers will check to see if a + * handler is supported before setting it. + */ + try + { + APQHandlers.setAPQHandlers(this); + } catch (Exception e) + { + System.out.println("Cannot set APQHandlers"); + // e.printStackTrace(); + } catch (Throwable t) + { + jalview.bin.Console + .warn("Error setting APQHandlers: " + t.toString()); + jalview.bin.Console.trace(Cache.getStackTraceString(t)); + } + setIconImages(ChannelProperties.getIconList()); + + addWindowListener(new WindowAdapter() + { + + @Override + public void windowClosing(WindowEvent ev) + { + quit(); + } + }); + + boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false); + + boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE", false); desktop = new MyDesktopPane(selmemusage); + showMemusage.setSelected(selmemusage); desktop.setBackground(Color.white); + getContentPane().setLayout(new BorderLayout()); // alternate config - have scrollbars - see notes in JAL-153 // JScrollPane sp = new JScrollPane(); // sp.getViewport().setView(desktop); // getContentPane().add(sp, BorderLayout.CENTER); + + // BH 2018 - just an experiment to try unclipped JInternalFrames. + if (Platform.isJS()) + { + getRootPane().putClientProperty("swingjs.overflow.hidden", "false"); + } + getContentPane().add(desktop, BorderLayout.CENTER); desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); // This line prevents Windows Look&Feel resizing all new windows to maximum // if previous window was maximised - desktop.setDesktopManager( - new MyDesktopManager( - (Platform.isWindows() ? new DefaultDesktopManager() - : Platform.isAMac() - ? new AquaInternalFrameManager( - desktop.getDesktopManager()) - : desktop.getDesktopManager()))); + desktop.setDesktopManager(new MyDesktopManager( + (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager() + : Platform.isAMacAndNotJS() + ? new AquaInternalFrameManager( + desktop.getDesktopManager()) + : desktop.getDesktopManager()))); Rectangle dims = getLastKnownDimensions(""); if (dims != null) @@@ -514,75 -372,33 +514,75 @@@ else { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setBounds((screenSize.width - 900) / 2, (screenSize.height - 650) / 2, - 900, 650); + int xPos = Math.max(5, (screenSize.width - 900) / 2); + int yPos = Math.max(5, (screenSize.height - 650) / 2); + setBounds(xPos, yPos, 900, 650); } - jconsole = new Console(this, showjconsole); - // add essential build information - jconsole.setHeader( - "Jalview Version: " + jalview.bin.Cache.getProperty("VERSION") - + "\n" + "Jalview Installation: " - + jalview.bin.Cache.getDefault("INSTALLATION", - "unknown") - + "\n" + "Build Date: " - + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") - + "\n" + "Java version: " - + System.getProperty("java.version") + "\n" - + System.getProperty("os.arch") + " " - + System.getProperty("os.name") + " " - + System.getProperty("os.version")); - showConsole(showjconsole); + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ + { + jconsole = new Console(this, showjconsole); + jconsole.setHeader(Cache.getVersionDetailsForConsole()); + showConsole(showjconsole); + + showNews.setVisible(false); - showNews.setVisible(false); + experimentalFeatures.setSelected(showExperimental()); + + getIdentifiersOrgData(); + + checkURLLinks(); + + // Spawn a thread that shows the splashscreen + if (!nosplash) + { + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + new SplashScreen(true); + } + }); + } - experimentalFeatures.setSelected(showExperimental()); + // Thread off a new instance of the file chooser - this reduces the time + // it + // takes to open it later on. + new Thread(new Runnable() + { + @Override + public void run() + { + jalview.bin.Console.debug("Filechooser init thread started."); + String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT"); + JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"), + fileFormat); + jalview.bin.Console.debug("Filechooser init thread finished."); + } + }).start(); + // Add the service change listener + changeSupport.addJalviewPropertyChangeListener("services", + new PropertyChangeListener() + { - getIdentifiersOrgData(); + @Override + public void propertyChange(PropertyChangeEvent evt) + { + jalview.bin.Console + .debug("Firing service changed event for " + + evt.getNewValue()); + JalviewServicesChanged(evt); + } + }); + } - checkURLLinks(); + this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); this.addWindowListener(new WindowAdapter() { @@@ -615,6 -431,46 +615,6 @@@ } }); desktop.addMouseListener(ma); - - this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this)); - // Spawn a thread that shows the splashscreen - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - new SplashScreen(); - } - }); - - // Thread off a new instance of the file chooser - this reduces the time it - // takes to open it later on. - new Thread(new Runnable() - { - @Override - public void run() - { - Cache.log.debug("Filechooser init thread started."); - String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT"); - JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"), - fileFormat); - Cache.log.debug("Filechooser init thread finished."); - } - }, "InitFileChooser").start(); - // Add the service change listener - changeSupport.addJalviewPropertyChangeListener("services", - new PropertyChangeListener() - { - - @Override - public void propertyChange(PropertyChangeEvent evt) - { - Cache.log.debug("Firing service changed event for " - + evt.getNewValue()); - JalviewServicesChanged(evt); - } - - }); } /** @@@ -635,15 -491,14 +635,15 @@@ // configure services StructureSelectionManager ssm = StructureSelectionManager .getStructureSelectionManager(this); - if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true)) + if (Cache.getDefault(Preferences.ADD_SS_ANN, true)) { - ssm.setAddTempFacAnnot(jalview.bin.Cache - .getDefault(Preferences.ADD_TEMPFACT_ANN, true)); - ssm.setProcessSecondaryStructure(jalview.bin.Cache - .getDefault(Preferences.STRUCT_FROM_PDB, true)); + ssm.setAddTempFacAnnot( + Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true)); + ssm.setProcessSecondaryStructure( + Cache.getDefault(Preferences.STRUCT_FROM_PDB, true)); + // JAL-3915 - RNAView is no longer an option so this has no effect ssm.setSecStructServices( - jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true)); + Cache.getDefault(Preferences.USE_RNAVIEW, false)); } else { @@@ -657,44 -512,41 +657,44 @@@ { final Desktop me = this; // Thread off the news reader, in case there are connection problems. - addDialogThread(new Runnable() + new Thread(new Runnable() { @Override public void run() { - Cache.log.debug("Starting news thread."); - + jalview.bin.Console.debug("Starting news thread."); jvnews = new BlogReader(me); showNews.setVisible(true); - Cache.log.debug("Completed news thread."); + jalview.bin.Console.debug("Completed news thread."); } - }); + }).start(); } public void getIdentifiersOrgData() { - // Thread off the identifiers fetcher - addDialogThread(new Runnable() - { - @Override - public void run() + if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null) + {// Thread off the identifiers fetcher + new Thread(new Runnable() { - Cache.log.debug("Downloading data from identifiers.org"); - UrlDownloadClient client = new UrlDownloadClient(); - try - { - client.download(IdOrgSettings.getUrl(), - IdOrgSettings.getDownloadLocation()); - } catch (IOException e) + @Override + public void run() { - Cache.log.debug("Exception downloading identifiers.org data" - + e.getMessage()); + jalview.bin.Console + .debug("Downloading data from identifiers.org"); + try + { + UrlDownloadClient.download(IdOrgSettings.getUrl(), + IdOrgSettings.getDownloadLocation()); + } catch (IOException e) + { + jalview.bin.Console + .debug("Exception downloading identifiers.org data" + + e.getMessage()); + } } - } - }); + }).start(); + ; + } } @Override @@@ -705,23 -557,26 +705,23 @@@ void showNews(boolean visible) { + jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news."); + showNews.setSelected(visible); + if (visible && !jvnews.isVisible()) { - Cache.log.debug((visible ? "Showing" : "Hiding") + " news."); - showNews.setSelected(visible); - if (visible && !jvnews.isVisible()) + new Thread(new Runnable() { - new Thread(new Runnable() + @Override + public void run() { - @Override - public void run() - { - long now = System.currentTimeMillis(); - Desktop.instance.setProgressBar( - MessageManager.getString("status.refreshing_news"), - now); - jvnews.refreshNews(); - Desktop.instance.setProgressBar(null, now); - jvnews.showNews(); - } - }, "ShowNewsWindow").start(); - } + long now = System.currentTimeMillis(); + Desktop.instance.setProgressBar( + MessageManager.getString("status.refreshing_news"), now); + jvnews.refreshNews(); + Desktop.instance.setProgressBar(null, now); + jvnews.showNews(); + } + }, "ShowNewsWindowThread").start(); } } @@@ -737,23 -592,25 +737,23 @@@ { // TODO: lock aspect ratio for scaling desktop Bug #0058199 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X"); - String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y"); - String width = jalview.bin.Cache - .getProperty(windowName + "SCREEN_WIDTH"); - String height = jalview.bin.Cache - .getProperty(windowName + "SCREEN_HEIGHT"); + String x = Cache.getProperty(windowName + "SCREEN_X"); + String y = Cache.getProperty(windowName + "SCREEN_Y"); + String width = Cache.getProperty(windowName + "SCREEN_WIDTH"); + String height = Cache.getProperty(windowName + "SCREEN_HEIGHT"); if ((x != null) && (y != null) && (width != null) && (height != null)) { int ix = Integer.parseInt(x), iy = Integer.parseInt(y), iw = Integer.parseInt(width), ih = Integer.parseInt(height); - if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null) + if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null) { // attempt #1 - try to cope with change in screen geometry - this // version doesn't preserve original jv aspect ratio. // take ratio of current screen size vs original screen size. - double sw = ((1f * screenSize.width) / (1f * Integer.parseInt( - jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH")))); - double sh = ((1f * screenSize.height) / (1f * Integer.parseInt( - jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT")))); + double sw = ((1f * screenSize.width) / (1f * Integer + .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH")))); + double sh = ((1f * screenSize.height) / (1f * Integer + .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT")))); // rescale the bounds depending upon the current screen geometry. ix = (int) (ix * sw); iw = (int) (iw * sw); @@@ -761,17 -618,17 +761,17 @@@ ih = (int) (ih * sh); while (ix >= screenSize.width) { - jalview.bin.Cache.log.debug( + jalview.bin.Console.debug( "Window geometry location recall error: shifting horizontal to within screenbounds."); ix -= screenSize.width; } while (iy >= screenSize.height) { - jalview.bin.Cache.log.debug( + jalview.bin.Console.debug( "Window geometry location recall error: shifting vertical to within screenbounds."); iy -= screenSize.height; } - jalview.bin.Cache.log.debug( + jalview.bin.Console.debug( "Got last known dimensions for " + windowName + ": x:" + ix + " y:" + iy + " width:" + iw + " height:" + ih); } @@@ -781,6 -638,46 +781,6 @@@ return null; } - private void doVamsasClientCheck() - { - if (jalview.bin.Cache.vamsasJarsPresent()) - { - setupVamsasDisconnectedGui(); - VamsasMenu.setVisible(true); - final Desktop us = this; - VamsasMenu.addMenuListener(new MenuListener() - { - // this listener remembers when the menu was first selected, and - // doesn't rebuild the session list until it has been cleared and - // reselected again. - boolean refresh = true; - - @Override - public void menuCanceled(MenuEvent e) - { - refresh = true; - } - - @Override - public void menuDeselected(MenuEvent e) - { - refresh = true; - } - - @Override - public void menuSelected(MenuEvent e) - { - if (refresh) - { - us.buildVamsasStMenu(); - refresh = false; - } - } - }); - vamsasStart.setVisible(true); - } - } - void showPasteMenu(int x, int y) { JPopupMenu popup = new JPopupMenu(); @@@ -952,7 -849,6 +952,7 @@@ frame.setResizable(resizable); frame.setMaximizable(resizable); frame.setIconifiable(resizable); + frame.setOpaque(Platform.isJS()); if (frame.getX() < 1 && frame.getY() < 1) { @@@ -961,8 -857,8 +961,8 @@@ } /* - * add an entry for the new frame in the Window menu - * (and remove it when the frame is closed) + * add an entry for the new frame in the Window menu (and remove it when the + * frame is closed) */ final JMenuItem menuItem = new JMenuItem(title); frame.addInternalFrameListener(new InternalFrameAdapter() @@@ -987,7 -883,8 +987,7 @@@ PaintRefresher.RemoveComponent(frame); /* - * defensive check to prevent frames being - * added half off the window + * defensive check to prevent frames being added half off the window */ if (openFrameCount > 0) { @@@ -1002,7 -899,9 +1002,7 @@@ menuItem.removeActionListener(menuItem.getActionListeners()[0]); } windowMenu.remove(menuItem); - - System.gc(); - }; + } }); menuItem.addActionListener(new ActionListener() @@@ -1021,8 -920,6 +1021,8 @@@ } }); + setKeyBindings(frame); + desktop.add(frame); windowMenu.add(menuItem); @@@ -1036,48 -933,12 +1036,48 @@@ { } catch (java.lang.ClassCastException cex) { - Cache.log.warn( - "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869", + jalview.bin.Console.warn( + "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869", cex); } } + /** + * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close + * the window + * + * @param frame + */ + private static void setKeyBindings(JInternalFrame frame) + { + @SuppressWarnings("serial") + final Action closeAction = new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + frame.dispose(); + } + }; + + /* + * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action + */ + KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, + InputEvent.CTRL_DOWN_MASK); + KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, + ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()); + + InputMap inputMap = frame + .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + String ctrlW = ctrlWKey.toString(); + inputMap.put(ctrlWKey, ctrlW); + inputMap.put(cmdWKey, ctrlW); + + ActionMap actionMap = frame.getActionMap(); + actionMap.put(ctrlW, closeAction); + } + @Override public void lostOwnership(Clipboard clipboard, Transferable contents) { @@@ -1123,7 -984,7 +1123,7 @@@ // Java's Transferable for native dnd evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); Transferable t = evt.getTransferable(); - List files = new ArrayList<>(); + List files = new ArrayList<>(); List protocols = new ArrayList<>(); try @@@ -1141,15 -1002,13 +1141,15 @@@ { for (int i = 0; i < files.size(); i++) { - String file = files.get(i).toString(); + // BH 2018 File or String + Object file = files.get(i); + String fileName = file.toString(); DataSourceType protocol = (protocols == null) ? DataSourceType.FILE : protocols.get(i); FileFormatI format = null; - if (file.endsWith(".jar")) + if (fileName.endsWith(".jar")) { format = FileFormat.Jalview; @@@ -1158,11 -1017,8 +1158,11 @@@ { format = new IdentifyFile().identify(file, protocol); } - - new FileLoader().LoadFile(file, protocol, format); + if (file instanceof File) + { + Platform.cacheFileData((File) file); + } + new FileLoader().LoadFile(null, file, protocol, format); } } catch (Exception ex) @@@ -1184,53 -1040,57 +1184,53 @@@ public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport) { String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT"); - JalviewFileChooser chooser = JalviewFileChooser - .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat); + JalviewFileChooser chooser = JalviewFileChooser.forRead( + Cache.getProperty("LAST_DIRECTORY"), fileFormat, + BackupFiles.getEnabled()); chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle( MessageManager.getString("label.open_local_file")); chooser.setToolTipText(MessageManager.getString("action.open")); - int value = chooser.showOpenDialog(this); - - if (value == JalviewFileChooser.APPROVE_OPTION) + chooser.setResponseHandler(0, new Runnable() { - String choice = chooser.getSelectedFile().getPath(); - Cache.setProperty("LAST_DIRECTORY", - chooser.getSelectedFile().getParent()); + @Override + public void run() + { + File selectedFile = chooser.getSelectedFile(); + Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); - FileFormatI format = chooser.getSelectedFormat(); + FileFormatI format = chooser.getSelectedFormat(); - /* - * Call IdentifyFile to verify the file contains what its extension implies. - * Skip this step for dynamically added file formats, because - * IdentifyFile does not know how to recognise them. - */ - if (FileFormats.getInstance().isIdentifiable(format)) - { - try - { - format = new IdentifyFile().identify(choice, DataSourceType.FILE); - } catch (FileFormatException e) + /* + * Call IdentifyFile to verify the file contains what its extension implies. + * Skip this step for dynamically added file formats, because IdentifyFile does + * not know how to recognise them. + */ + if (FileFormats.getInstance().isIdentifiable(format)) { - // format = null; //?? + try + { + format = new IdentifyFile().identify(selectedFile, + DataSourceType.FILE); + } catch (FileFormatException e) + { + // format = null; //?? + } } - } - if (viewport != null) - { - new FileLoader().LoadFile(viewport, choice, DataSourceType.FILE, - format); - } - else - { - new FileLoader().LoadFile(choice, DataSourceType.FILE, format); + new FileLoader().LoadFile(viewport, selectedFile, + DataSourceType.FILE, format); } - } + }); + chooser.showOpenDialog(this); } /** - * DOCUMENT ME! + * Shows a dialog for input of a URL at which to retrieve alignment data * - * @param e - * DOCUMENT ME! + * @param viewport */ @Override public void inputURLMenuItem_actionPerformed(AlignViewport viewport) @@@ -1239,111 -1099,88 +1239,111 @@@ // for viewing JLabel label = new JLabel( MessageManager.getString("label.input_file_url")); JPanel panel = new JPanel(new GridLayout(2, 1)); panel.add(label); - panel.add(history); - history.setPreferredSize(new Dimension(400, 20)); - history.setEditable(true); - history.addItem("http://www."); - - String historyItems = jalview.bin.Cache.getProperty("RECENT_URL"); - StringTokenizer st; - - if (historyItems != null) + /* + * the URL to fetch is input in Java: an editable combobox with history JS: + * (pending JAL-3038) a plain text field + */ + JComponent history; + String urlBase = "https://www."; + if (Platform.isJS()) { - st = new StringTokenizer(historyItems, "\t"); - - while (st.hasMoreTokens()) - { - history.addItem(st.nextElement()); - } - } - - int reply = JvOptionPane.showInternalConfirmDialog(desktop, panel, - MessageManager.getString("label.input_alignment_from_url"), - JvOptionPane.OK_CANCEL_OPTION); - - if (reply != JvOptionPane.OK_OPTION) - { - return; + history = new JTextField(urlBase, 35); } - - String url = history.getSelectedItem().toString(); - - if (url.toLowerCase().endsWith(".jar")) + else + /** + * Java only + * + * @j2sIgnore + */ { - if (viewport != null) + JComboBox asCombo = new JComboBox<>(); + asCombo.setPreferredSize(new Dimension(400, 20)); + asCombo.setEditable(true); + asCombo.addItem(urlBase); + String historyItems = Cache.getProperty("RECENT_URL"); + if (historyItems != null) { - new FileLoader().LoadFile(viewport, url, DataSourceType.URL, - FileFormat.Jalview); - } - else - { - new FileLoader().LoadFile(url, DataSourceType.URL, - FileFormat.Jalview); + for (String token : historyItems.split("\\t")) + { + asCombo.addItem(token); + } } + history = asCombo; } - else + panel.add(history); + + Object[] options = new Object[] { MessageManager.getString("action.ok"), + MessageManager.getString("action.cancel") }; + Runnable action = new Runnable() { - FileFormatI format = null; - try - { - format = new IdentifyFile().identify(url, DataSourceType.URL); - } catch (FileFormatException e) + @Override + public void run() { - // TODO revise error handling, distinguish between - // URL not found and response not valid - } + @SuppressWarnings("unchecked") + String url = (history instanceof JTextField + ? ((JTextField) history).getText() + : ((JComboBox) history).getEditor().getItem() + .toString().trim()); - if (format == null) - { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.couldnt_locate", - new String[] - { url }), - MessageManager.getString("label.url_not_found"), - JvOptionPane.WARNING_MESSAGE); + if (url.toLowerCase(Locale.ROOT).endsWith(".jar")) + { + if (viewport != null) + { + new FileLoader().LoadFile(viewport, url, DataSourceType.URL, + FileFormat.Jalview); + } + else + { + new FileLoader().LoadFile(url, DataSourceType.URL, + FileFormat.Jalview); + } + } + else + { + FileFormatI format = null; + try + { + format = new IdentifyFile().identify(url, DataSourceType.URL); + } catch (FileFormatException e) + { + // TODO revise error handling, distinguish between + // URL not found and response not valid + } - return; - } + if (format == null) + { + String msg = MessageManager + .formatMessage("label.couldnt_locate", url); + JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg, + MessageManager.getString("label.url_not_found"), + JvOptionPane.WARNING_MESSAGE); - if (viewport != null) - { - new FileLoader().LoadFile(viewport, url, DataSourceType.URL, - format); - } - else - { - new FileLoader().LoadFile(url, DataSourceType.URL, format); + return; + } + + if (viewport != null) + { + new FileLoader().LoadFile(viewport, url, DataSourceType.URL, + format); + } + else + { + new FileLoader().LoadFile(url, DataSourceType.URL, format); + } + } } - } + }; + String dialogOption = MessageManager + .getString("label.input_alignment_from_url"); + JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action) + .showInternalDialog(panel, dialogOption, + JvOptionPane.YES_NO_CANCEL_OPTION, + JvOptionPane.PLAIN_MESSAGE, null, options, + MessageManager.getString("action.ok")); } /** @@@ -1371,8 -1208,10 +1371,8 @@@ public void quit() { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); - jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH", - screen.width + ""); - jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT", - screen.height + ""); + Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + ""); + Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + ""); storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y, getWidth(), getHeight())); @@@ -1403,14 -1242,14 +1403,14 @@@ private void storeLastKnownDimensions(String string, Rectangle jc) { - jalview.bin.Cache.log.debug("Storing last known dimensions for " - + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width + jalview.bin.Console.debug("Storing last known dimensions for " + string + + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width + " height:" + jc.height); - jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + ""); - jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + ""); - jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + ""); - jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + ""); + Cache.setProperty(string + "SCREEN_X", jc.x + ""); + Cache.setProperty(string + "SCREEN_Y", jc.y + ""); + Cache.setProperty(string + "SCREEN_WIDTH", jc.width + ""); + Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + ""); } /** @@@ -1422,44 -1261,53 +1422,44 @@@ @Override public void aboutMenuItem_actionPerformed(ActionEvent e) { - // StringBuffer message = getAboutMessage(false); - // JvOptionPane.showInternalMessageDialog(Desktop.desktop, - // - // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE); new Thread(new Runnable() { @Override public void run() { - new SplashScreen(true); + new SplashScreen(false); } - }, "ShowAboutMenuThread").start(); + }, "ShowAboutMenu").start(); } - public StringBuffer getAboutMessage(boolean shortv) + /** + * Returns the html text for the About screen, including any available version + * number, build details, author details and citation reference, but without + * the enclosing {@code html} tags + * + * @return + */ + public String getAboutMessage() { - StringBuffer message = new StringBuffer(); - message.append(""); - if (shortv) - { - message.append("

Version: " - + jalview.bin.Cache.getProperty("VERSION") - + "

"); - message.append("Last Updated: " - + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") - + ""); - - } - else - { - - message.append("Version " - + jalview.bin.Cache.getProperty("VERSION") - + "; last updated: " - + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")); - } + StringBuilder message = new StringBuilder(1024); + message.append("
") + .append("

Version: ") + .append(Cache.getProperty("VERSION")).append("

") + .append("Built: ") + .append(Cache.getDefault("BUILD_DATE", "unknown")) + .append(" from ").append(Cache.getBuildDetailsForSplash()) + .append(""); - if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking") - .equals("Checking")) + String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking"); + if (latestVersion.equals("Checking")) { - message.append("
...Checking latest version...
"); + // JBP removed this message for 2.11: May be reinstated in future version + // message.append("
...Checking latest version...
"); } - else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking") - .equals(jalview.bin.Cache.getProperty("VERSION"))) + else if (!latestVersion.equals(Cache.getProperty("VERSION"))) { boolean red = false; - if (jalview.bin.Cache.getProperty("VERSION").toLowerCase() + if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT) .indexOf("automated build") == -1) { red = true; @@@ -1468,50 -1316,45 +1468,50 @@@ message.append("
"); } - message.append("
!! Version " - + jalview.bin.Cache.getDefault("LATEST_VERSION", - "..Checking..") - + " is available for download from " - + jalview.bin.Cache.getDefault("www.jalview.org", - "http://www.jalview.org") - + " !!"); + message.append("
!! Version ") + .append(Cache.getDefault("LATEST_VERSION", "..Checking..")) + .append(" is available for download from ") + .append(Cache.getDefault("www.jalview.org", + "https://www.jalview.org")) + .append(" !!"); if (red) { message.append("
"); } } - message.append("
Authors: " + jalview.bin.Cache.getDefault( - "AUTHORFNAMES", - "The Jalview Authors (See AUTHORS file for current list)") - + "

Development managed by The Barton Group, University of Dundee, Scotland, UK.
" - + "

For help, see the FAQ at www.jalview.org/faq and/or join the jalview-discuss@jalview.org mailing list" - + "

If you use Jalview, please cite:" - + "
Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)" - + "
Jalview Version 2 - a multiple sequence alignment editor and analysis workbench" - + "
Bioinformatics doi: 10.1093/bioinformatics/btp033" - + ""); - return message; + message.append("
Authors: "); + message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS)); + message.append(CITATION); + + message.append("
"); + + return message.toString(); } /** - * DOCUMENT ME! - * - * @param e - * DOCUMENT ME! + * Action on requesting Help documentation */ @Override - public void documentationMenuItem_actionPerformed(ActionEvent e) + public void documentationMenuItem_actionPerformed() { try { - Help.showHelpWindow(); + if (Platform.isJS()) + { + BrowserLauncher.openURL("https://www.jalview.org/help.html"); + } + else + /** + * Java only + * + * @j2sIgnore + */ + { + Help.showHelpWindow(); + } } catch (Exception ex) { + System.err.println("Error opening help: " + ex.getMessage()); } } @@@ -1531,6 -1374,10 +1531,6 @@@ } Jalview.setCurrentAlignFrame(null); System.out.println("ALL CLOSED"); - if (v_client != null) - { - // TODO clear binding to vamsas document objects on close_all - } /* * reset state of singleton objects as appropriate (clear down session state @@@ -1542,6 -1389,7 +1542,6 @@@ { ssm.resetAll(); } - System.gc(); } @Override @@@ -1571,16 -1419,17 +1571,16 @@@ protected void garbageCollect_actionPerformed(ActionEvent e) { // We simply collect the garbage - jalview.bin.Cache.log.debug("Collecting garbage..."); + jalview.bin.Console.debug("Collecting garbage..."); System.gc(); - jalview.bin.Cache.log.debug("Finished garbage collection."); + jalview.bin.Console.debug("Finished garbage collection."); } /* * (non-Javadoc) * - * @see - * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent - * ) + * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event. + * ActionEvent ) */ @Override protected void showMemusage_actionPerformed(ActionEvent e) @@@ -1610,14 -1459,11 +1610,14 @@@ */ void showConsole(boolean selected) { - showConsole.setSelected(selected); // TODO: decide if we should update properties file - Cache.setProperty("SHOW_JAVA_CONSOLE", - Boolean.valueOf(selected).toString()); - jconsole.setVisible(selected); + if (jconsole != null) // BH 2018 + { + showConsole.setSelected(selected); + Cache.setProperty("SHOW_JAVA_CONSOLE", + Boolean.valueOf(selected).toString()); + jconsole.setVisible(selected); + } } void reorderAssociatedWindows(boolean minimize, boolean close) @@@ -1706,53 -1552,32 +1706,53 @@@ @Override protected void preferences_actionPerformed(ActionEvent e) { - new Preferences(); + Preferences.openPreferences(); } /** - * DOCUMENT ME! - * - * @param e - * DOCUMENT ME! + * Prompts the user to choose a file and then saves the Jalview state as a + * Jalview project file */ @Override - public void saveState_actionPerformed(ActionEvent e) + public void saveState_actionPerformed() { - JalviewFileChooser chooser = new JalviewFileChooser("jvp", - "Jalview Project"); + saveState_actionPerformed(false); + } - chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle(MessageManager.getString("label.save_state")); + public void saveState_actionPerformed(boolean saveAs) + { + java.io.File projectFile = getProjectFile(); + // autoSave indicates we already have a file and don't need to ask + boolean autoSave = projectFile != null && !saveAs + && BackupFiles.getEnabled(); - int value = chooser.showSaveDialog(this); + // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"', + // saveAs="+saveAs+", Backups + // "+(BackupFiles.getEnabled()?"enabled":"disabled")); - if (value == JalviewFileChooser.APPROVE_OPTION) + boolean approveSave = false; + if (!autoSave) { - final Desktop me = this; - final java.io.File choice = chooser.getSelectedFile(); - setProjectFile(choice); + JalviewFileChooser chooser = new JalviewFileChooser("jvp", + "Jalview Project"); + chooser.setFileView(new JalviewFileView()); + chooser.setDialogTitle(MessageManager.getString("label.save_state")); + + int value = chooser.showSaveDialog(this); + + if (value == JalviewFileChooser.APPROVE_OPTION) + { + projectFile = chooser.getSelectedFile(); + setProjectFile(projectFile); + approveSave = true; + } + } + + if (approveSave || autoSave) + { + final Desktop me = this; + final java.io.File chosenFile = projectFile; new Thread(new Runnable() { @Override @@@ -1761,52 -1586,38 +1761,52 @@@ // TODO: refactor to Jalview desktop session controller action. setProgressBar(MessageManager.formatMessage( "label.saving_jalview_project", new Object[] - { choice.getName() }), choice.hashCode()); - jalview.bin.Cache.setProperty("LAST_DIRECTORY", - choice.getParent()); + { chosenFile.getName() }), chosenFile.hashCode()); + Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent()); // TODO catch and handle errors for savestate // TODO prevent user from messing with the Desktop whilst we're saving try { - new Jalview2XML().saveState(choice); + boolean doBackup = BackupFiles.getEnabled(); + BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) + : null; + + new Jalview2XML().saveState( + doBackup ? backupfiles.getTempFile() : chosenFile); + + if (doBackup) + { + backupfiles.setWriteSuccess(true); + backupfiles.rollBackupsAndRenameTempFile(); + } } catch (OutOfMemoryError oom) { - new OOMWarning( - "Whilst saving current state to " + choice.getName(), - oom); + new OOMWarning("Whilst saving current state to " + + chosenFile.getName(), oom); } catch (Exception ex) { - Cache.log.error( - "Problems whilst trying to save to " + choice.getName(), - ex); + jalview.bin.Console.error("Problems whilst trying to save to " + + chosenFile.getName(), ex); JvOptionPane.showMessageDialog(me, MessageManager.formatMessage( "label.error_whilst_saving_current_state_to", new Object[] - { choice.getName() }), + { chosenFile.getName() }), MessageManager.getString("label.couldnt_save_project"), JvOptionPane.WARNING_MESSAGE); } - setProgressBar(null, choice.hashCode()); + setProgressBar(null, chosenFile.hashCode()); } - }, "SaveJalviewProjectThread").start(); + }, "SaveJalviewProject").start(); } } + @Override + public void saveAsState_actionPerformed(ActionEvent e) + { + saveState_actionPerformed(true); + } + private void setProjectFile(File choice) { this.projectFile = choice; @@@ -1818,62 -1629,61 +1818,67 @@@ } /** - * DOCUMENT ME! - * - * @param e - * DOCUMENT ME! + * Shows a file chooser dialog and tries to read in the selected file as a + * Jalview project */ @Override - public void loadState_actionPerformed(ActionEvent e) + public void loadState_actionPerformed() { + final String[] suffix = new String[] { "jvp", "jar" }; + final String[] desc = new String[] { "Jalview Project", + "Jalview Project (old)" }; JalviewFileChooser chooser = new JalviewFileChooser( - Cache.getProperty("LAST_DIRECTORY"), new String[] - { "jvp", "jar" }, - new String[] - { "Jalview Project", "Jalview Project (old)" }, - "Jalview Project"); + Cache.getProperty("LAST_DIRECTORY"), suffix, desc, + "Jalview Project", true, BackupFiles.getEnabled()); // last two + // booleans: + // allFiles, + // allowBackupFiles chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle(MessageManager.getString("label.restore_state")); - - int value = chooser.showOpenDialog(this); - - if (value == JalviewFileChooser.APPROVE_OPTION) + chooser.setResponseHandler(0, new Runnable() { - final File selectedFile = chooser.getSelectedFile(); - setProjectFile(selectedFile); - final String choice = selectedFile.getAbsolutePath(); - Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); - new Thread(new Runnable() + @Override + public void run() { - @Override - public void run() + File selectedFile = chooser.getSelectedFile(); + setProjectFile(selectedFile); - String choice = selectedFile.getAbsolutePath(); ++ final String choice = selectedFile.getAbsolutePath(); + Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent()); + new Thread(new Runnable() { - setProgressBar(MessageManager.formatMessage( - "label.loading_jalview_project", new Object[] - { choice }), choice.hashCode()); - try - { - new Jalview2XML().loadJalviewAlign(choice); - } catch (OutOfMemoryError oom) - { - new OOMWarning("Whilst loading project from " + choice, oom); - } catch (Exception ex) + @Override + public void run() { - Cache.log.error( - "Problems whilst loading project from " + choice, ex); - JvOptionPane.showMessageDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.error_whilst_loading_project_from", - new Object[] - { choice }), - MessageManager.getString("label.couldnt_load_project"), - JvOptionPane.WARNING_MESSAGE); - } ++ setProgressBar(MessageManager.formatMessage( ++ "label.loading_jalview_project", new Object[] ++ { choice }), choice.hashCode()); ++ + try + { + new Jalview2XML().loadJalviewAlign(selectedFile); + } catch (OutOfMemoryError oom) + { + new OOMWarning("Whilst loading project from " + choice, oom); + } catch (Exception ex) + { + jalview.bin.Console.error( + "Problems whilst loading project from " + choice, ex); + JvOptionPane.showMessageDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.error_whilst_loading_project_from", + new Object[] + { choice }), + MessageManager + .getString("label.couldnt_load_project"), + JvOptionPane.WARNING_MESSAGE); + } + setProgressBar(null, choice.hashCode()); - } - }, "LoadJalviewProject").start(); - } + } + }, "Project Loader").start(); + } + }); + + chooser.showOpenDialog(this); } @Override @@@ -1886,7 -1696,7 +1891,7 @@@ ArrayList fileLoadingPanels = new ArrayList<>(); - public void startLoading(final String fileName) + public void startLoading(final Object fileName) { if (fileLoadingCount == 0) { @@@ -2015,161 -1825,476 +2020,161 @@@ if (desktop != null) { AlignFrame[] frames = Desktop.getAlignFrames(); - - for (AlignFrame afr : frames) - { - if (sequenceSetId == null || afr.getViewport().getSequenceSetId() - .equals(sequenceSetId)) - { - if (afr.alignPanels != null) - { - for (AlignmentPanel ap : afr.alignPanels) - { - if (sequenceSetId == null - || sequenceSetId.equals(ap.av.getSequenceSetId())) - { - viewp.add(ap.av); - } - } - } - else - { - viewp.add(afr.getViewport()); - } - } - } - if (viewp.size() > 0) - { - return viewp.toArray(new AlignmentViewport[viewp.size()]); - } - } - return null; - } - - /** - * Explode the views in the given frame into separate AlignFrame - * - * @param af - */ - public static void explodeViews(AlignFrame af) - { - int size = af.alignPanels.size(); - if (size < 2) - { - return; - } - - for (int i = 0; i < size; i++) - { - AlignmentPanel ap = af.alignPanels.get(i); - AlignFrame newaf = new AlignFrame(ap); - - /* - * Restore the view's last exploded frame geometry if known. Multiple - * views from one exploded frame share and restore the same (frame) - * position and size. - */ - Rectangle geometry = ap.av.getExplodedGeometry(); - if (geometry != null) - { - newaf.setBounds(geometry); - } - - ap.av.setGatherViewsHere(false); - - addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH, - AlignFrame.DEFAULT_HEIGHT); - } - - af.alignPanels.clear(); - af.closeMenuItem_actionPerformed(true); - - } - - /** - * Gather expanded views (separate AlignFrame's) with the same sequence set - * identifier back in to this frame as additional views, and close the - * expanded views. Note the expanded frames may themselves have multiple - * views. We take the lot. - * - * @param source - */ - public void gatherViews(AlignFrame source) - { - source.viewport.setGatherViewsHere(true); - source.viewport.setExplodedGeometry(source.getBounds()); - JInternalFrame[] frames = desktop.getAllFrames(); - String viewId = source.viewport.getSequenceSetId(); - - for (int t = 0; t < frames.length; t++) - { - if (frames[t] instanceof AlignFrame && frames[t] != source) - { - AlignFrame af = (AlignFrame) frames[t]; - boolean gatherThis = false; - for (int a = 0; a < af.alignPanels.size(); a++) - { - AlignmentPanel ap = af.alignPanels.get(a); - if (viewId.equals(ap.av.getSequenceSetId())) - { - gatherThis = true; - ap.av.setGatherViewsHere(false); - ap.av.setExplodedGeometry(af.getBounds()); - source.addAlignmentPanel(ap, false); - } - } - - if (gatherThis) - { - af.alignPanels.clear(); - af.closeMenuItem_actionPerformed(true); - } - } - } - - } - - jalview.gui.VamsasApplication v_client = null; - - @Override - public void vamsasImport_actionPerformed(ActionEvent e) - { - if (v_client == null) - { - // Load and try to start a session. - JalviewFileChooser chooser = new JalviewFileChooser( - jalview.bin.Cache.getProperty("LAST_DIRECTORY")); - - chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle( - MessageManager.getString("label.open_saved_vamsas_session")); - chooser.setToolTipText(MessageManager.getString( - "label.select_vamsas_session_opened_as_new_vamsas_session")); - - int value = chooser.showOpenDialog(this); - - if (value == JalviewFileChooser.APPROVE_OPTION) - { - String fle = chooser.getSelectedFile().toString(); - if (!vamsasImport(chooser.getSelectedFile())) - { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.couldnt_import_as_vamsas_session", - new Object[] - { fle }), - MessageManager - .getString("label.vamsas_document_import_failed"), - JvOptionPane.ERROR_MESSAGE); - } - } - } - else - { - jalview.bin.Cache.log.error( - "Implementation error - load session from a running session is not supported."); - } - } - - /** - * import file into a new vamsas session (uses jalview.gui.VamsasApplication) - * - * @param file - * @return true if import was a success and a session was started. - */ - public boolean vamsasImport(URL url) - { - // TODO: create progress bar - if (v_client != null) - { - - jalview.bin.Cache.log.error( - "Implementation error - load session from a running session is not supported."); - return false; - } - - try - { - // copy the URL content to a temporary local file - // TODO: be a bit cleverer here with nio (?!) - File file = File.createTempFile("vdocfromurl", ".vdj"); - FileOutputStream fos = new FileOutputStream(file); - BufferedInputStream bis = new BufferedInputStream(url.openStream()); - byte[] buffer = new byte[2048]; - int ln; - while ((ln = bis.read(buffer)) > -1) - { - fos.write(buffer, 0, ln); - } - bis.close(); - fos.close(); - v_client = new jalview.gui.VamsasApplication(this, file, - url.toExternalForm()); - } catch (Exception ex) - { - jalview.bin.Cache.log.error( - "Failed to create new vamsas session from contents of URL " - + url, - ex); - return false; - } - setupVamsasConnectedGui(); - v_client.initial_update(); // TODO: thread ? - return v_client.inSession(); - } - - /** - * import file into a new vamsas session (uses jalview.gui.VamsasApplication) - * - * @param file - * @return true if import was a success and a session was started. - */ - public boolean vamsasImport(File file) - { - if (v_client != null) - { - - jalview.bin.Cache.log.error( - "Implementation error - load session from a running session is not supported."); - return false; - } - - setProgressBar(MessageManager.formatMessage( - "status.importing_vamsas_session_from", new Object[] - { file.getName() }), file.hashCode()); - try - { - v_client = new jalview.gui.VamsasApplication(this, file, null); - } catch (Exception ex) - { - setProgressBar(MessageManager.formatMessage( - "status.importing_vamsas_session_from", new Object[] - { file.getName() }), file.hashCode()); - jalview.bin.Cache.log.error( - "New vamsas session from existing session file failed:", ex); - return false; - } - setupVamsasConnectedGui(); - v_client.initial_update(); // TODO: thread ? - setProgressBar(MessageManager.formatMessage( - "status.importing_vamsas_session_from", new Object[] - { file.getName() }), file.hashCode()); - return v_client.inSession(); - } - - public boolean joinVamsasSession(String mysesid) - { - if (v_client != null) - { - throw new Error(MessageManager - .getString("error.try_join_vamsas_session_another")); - } - if (mysesid == null) - { - throw new Error( - MessageManager.getString("error.invalid_vamsas_session_id")); - } - v_client = new VamsasApplication(this, mysesid); - setupVamsasConnectedGui(); - v_client.initial_update(); - return (v_client.inSession()); - } - - @Override - public void vamsasStart_actionPerformed(ActionEvent e) - { - if (v_client == null) - { - // Start a session. - // we just start a default session for moment. - /* - * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache. - * getProperty("LAST_DIRECTORY")); - * - * chooser.setFileView(new JalviewFileView()); - * chooser.setDialogTitle("Load Vamsas file"); - * chooser.setToolTipText("Import"); - * - * int value = chooser.showOpenDialog(this); - * - * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new - * jalview.gui.VamsasApplication(this, chooser.getSelectedFile()); - */ - v_client = new VamsasApplication(this); - setupVamsasConnectedGui(); - v_client.initial_update(); // TODO: thread ? - } - else - { - // store current data in session. - v_client.push_update(); // TODO: thread - } - } - - protected void setupVamsasConnectedGui() - { - vamsasStart.setText(MessageManager.getString("label.session_update")); - vamsasSave.setVisible(true); - vamsasStop.setVisible(true); - vamsasImport.setVisible(false); // Document import to existing session is - // not possible for vamsas-client-1.0. - } - - protected void setupVamsasDisconnectedGui() - { - vamsasSave.setVisible(false); - vamsasStop.setVisible(false); - vamsasImport.setVisible(true); - vamsasStart - .setText(MessageManager.getString("label.new_vamsas_session")); - } - - @Override - public void vamsasStop_actionPerformed(ActionEvent e) - { - if (v_client != null) - { - v_client.end_session(); - v_client = null; - setupVamsasDisconnectedGui(); - } - } - - protected void buildVamsasStMenu() - { - if (v_client == null) - { - String[] sess = null; - try - { - sess = VamsasApplication.getSessionList(); - } catch (Exception e) + + for (AlignFrame afr : frames) { - jalview.bin.Cache.log.warn("Problem getting current sessions list.", - e); - sess = null; - } - if (sess != null) - { - jalview.bin.Cache.log.debug( - "Got current sessions list: " + sess.length + " entries."); - VamsasStMenu.removeAll(); - for (int i = 0; i < sess.length; i++) - { - JMenuItem sessit = new JMenuItem(); - sessit.setText(sess[i]); - sessit.setToolTipText(MessageManager - .formatMessage("label.connect_to_session", new Object[] - { sess[i] })); - final Desktop dsktp = this; - final String mysesid = sess[i]; - sessit.addActionListener(new ActionListener() + if (sequenceSetId == null || afr.getViewport().getSequenceSetId() + .equals(sequenceSetId)) + { + if (afr.alignPanels != null) { - - @Override - public void actionPerformed(ActionEvent e) + for (AlignmentPanel ap : afr.alignPanels) { - if (dsktp.v_client == null) + if (sequenceSetId == null + || sequenceSetId.equals(ap.av.getSequenceSetId())) { - Thread rthr = new Thread(new Runnable() - { - - @Override - public void run() - { - dsktp.v_client = new VamsasApplication(dsktp, mysesid); - dsktp.setupVamsasConnectedGui(); - dsktp.v_client.initial_update(); - } - - }, "VamsasSession"); - rthr.start(); + viewp.add(ap.av); } - }; - }); - VamsasStMenu.add(sessit); + } + } + else + { + viewp.add(afr.getViewport()); + } } - // don't show an empty menu. - VamsasStMenu.setVisible(sess.length > 0); - } - else + if (viewp.size() > 0) { - jalview.bin.Cache.log.debug("No current vamsas sessions."); - VamsasStMenu.removeAll(); - VamsasStMenu.setVisible(false); + return viewp.toArray(new AlignmentViewport[viewp.size()]); } } - else - { - // Not interested in the content. Just hide ourselves. - VamsasStMenu.setVisible(false); - } + return null; } - @Override - public void vamsasSave_actionPerformed(ActionEvent e) + /** + * Explode the views in the given frame into separate AlignFrame + * + * @param af + */ + public static void explodeViews(AlignFrame af) { - if (v_client != null) + int size = af.alignPanels.size(); + if (size < 2) { - // TODO: VAMSAS DOCUMENT EXTENSION is VDJ - JalviewFileChooser chooser = new JalviewFileChooser("vdj", - "Vamsas Document"); + return; + } - chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle(MessageManager - .getString("label.save_vamsas_document_archive")); + // FIXME: ideally should use UI interface API + FeatureSettings viewFeatureSettings = (af.featureSettings != null + && af.featureSettings.isOpen()) ? af.featureSettings : null; + Rectangle fsBounds = af.getFeatureSettingsGeometry(); + for (int i = 0; i < size; i++) + { + AlignmentPanel ap = af.alignPanels.get(i); - int value = chooser.showSaveDialog(this); + AlignFrame newaf = new AlignFrame(ap); - if (value == JalviewFileChooser.APPROVE_OPTION) + // transfer reference for existing feature settings to new alignFrame + if (ap == af.alignPanel) { - java.io.File choice = chooser.getSelectedFile(); - JPanel progpanel = addProgressPanel(MessageManager - .formatMessage("label.saving_vamsas_doc", new Object[] - { choice.getName() })); - Cache.setProperty("LAST_DIRECTORY", choice.getParent()); - String warnmsg = null; - String warnttl = null; - try + if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap) { - v_client.vclient.storeDocument(choice); - } catch (Error ex) - { - warnttl = "Serious Problem saving Vamsas Document"; - warnmsg = ex.toString(); - jalview.bin.Cache.log - .error("Error Whilst saving document to " + choice, ex); + newaf.featureSettings = viewFeatureSettings; + } + newaf.setFeatureSettingsGeometry(fsBounds); + } - } catch (Exception ex) - { - warnttl = "Problem saving Vamsas Document."; - warnmsg = ex.toString(); - jalview.bin.Cache.log.warn( - "Exception Whilst saving document to " + choice, ex); + /* + * Restore the view's last exploded frame geometry if known. Multiple views from + * one exploded frame share and restore the same (frame) position and size. + */ + Rectangle geometry = ap.av.getExplodedGeometry(); + if (geometry != null) + { + newaf.setBounds(geometry); + } - } - removeProgressPanel(progpanel); - if (warnmsg != null) - { - JvOptionPane.showInternalMessageDialog(Desktop.desktop, + ap.av.setGatherViewsHere(false); - warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE); - } + addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + // and materialise a new feature settings dialog instance for the new + // alignframe + // (closes the old as if 'OK' was pressed) + if (ap == af.alignPanel && newaf.featureSettings != null + && newaf.featureSettings.isOpen() + && af.alignPanel.getAlignViewport().isShowSequenceFeatures()) + { + newaf.showFeatureSettingsUI(); } } - } - JPanel vamUpdate = null; + af.featureSettings = null; + af.alignPanels.clear(); + af.closeMenuItem_actionPerformed(true); + + } /** - * hide vamsas user gui bits when a vamsas document event is being handled. + * Gather expanded views (separate AlignFrame's) with the same sequence set + * identifier back in to this frame as additional views, and close the + * expanded views. Note the expanded frames may themselves have multiple + * views. We take the lot. * - * @param b - * true to hide gui, false to reveal gui + * @param source */ - public void setVamsasUpdate(boolean b) + public void gatherViews(AlignFrame source) { - Cache.log.debug("Setting gui for Vamsas update " - + (b ? "in progress" : "finished")); - - if (vamUpdate != null) + source.viewport.setGatherViewsHere(true); + source.viewport.setExplodedGeometry(source.getBounds()); + JInternalFrame[] frames = desktop.getAllFrames(); + String viewId = source.viewport.getSequenceSetId(); + for (int t = 0; t < frames.length; t++) { - this.removeProgressPanel(vamUpdate); + if (frames[t] instanceof AlignFrame && frames[t] != source) + { + AlignFrame af = (AlignFrame) frames[t]; + boolean gatherThis = false; + for (int a = 0; a < af.alignPanels.size(); a++) + { + AlignmentPanel ap = af.alignPanels.get(a); + if (viewId.equals(ap.av.getSequenceSetId())) + { + gatherThis = true; + ap.av.setGatherViewsHere(false); + ap.av.setExplodedGeometry(af.getBounds()); + source.addAlignmentPanel(ap, false); + } + } + + if (gatherThis) + { + if (af.featureSettings != null && af.featureSettings.isOpen()) + { + if (source.featureSettings == null) + { + // preserve the feature settings geometry for this frame + source.featureSettings = af.featureSettings; + source.setFeatureSettingsGeometry( + af.getFeatureSettingsGeometry()); + } + else + { + // close it and forget + af.featureSettings.close(); + } + } + af.alignPanels.clear(); + af.closeMenuItem_actionPerformed(true); + } + } } - if (b) + + // refresh the feature setting UI for the source frame if it exists + if (source.featureSettings != null && source.featureSettings.isOpen()) { - vamUpdate = this.addProgressPanel( - MessageManager.getString("label.updating_vamsas_session")); + source.showFeatureSettingsUI(); } - vamsasStart.setVisible(!b); - vamsasStop.setVisible(!b); - vamsasSave.setVisible(!b); } public JInternalFrame[] getAllFrames() @@@ -2187,7 -2312,7 +2192,7 @@@ { UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url); // javax.swing.SwingUtilities.invokeLater(jvq); - new Thread(jvq, "CheckQuestionnaireThread").start(); + new Thread(jvq, "CheckQuestionnaire").start(); } public void checkURLLinks() @@@ -2213,7 -2338,7 +2218,7 @@@ while (li.hasNext()) { String link = li.next(); - if (link.contains(SEQUENCE_ID) + if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID) && !UrlConstants.isDefaultString(link)) { check = true; @@@ -2278,6 -2403,7 +2283,6 @@@ */ public class MyDesktopPane extends JDesktopPane implements Runnable { - private static final float ONE_MB = 1048576f; boolean showMemoryUsage = false; @@@ -2299,7 -2425,7 +2304,7 @@@ this.showMemoryUsage = showMemory; if (showMemory) { - Thread worker = new Thread(this, "ShowMemoryUsageThread"); + Thread worker = new Thread(this, "ShowMemoryUsage"); worker.start(); } repaint(); @@@ -2363,12 -2489,10 +2368,12 @@@ 10, getHeight() - fm.getHeight()); } } + + // output debug scale message. Important for jalview.bin.HiDPISettingTest2 + Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics()); } } - /** * Accessor method to quickly get all the AlignmentFrames loaded. * @@@ -2462,7 -2586,7 +2467,7 @@@ openGroovyConsole(); } catch (Exception ex) { - jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex); + jalview.bin.Console.error("Groovy Shell Creation failed.", ex); JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager.getString("label.couldnt_create_groovy_shell"), @@@ -2484,9 -2608,9 +2489,9 @@@ /* * We allow only one console at a time, so that AlignFrame menu option - * 'Calculate | Run Groovy script' is unambiguous. - * Disable 'Groovy Console', and enable 'Run script', when the console is - * opened, and the reverse when it is closed + * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and + * enable 'Run script', when the console is opened, and the reverse when it is + * closed */ Window window = (Window) groovyConsole.getFrame(); window.addWindowListener(new WindowAdapter() @@@ -2509,8 -2633,8 +2514,8 @@@ ((Window) groovyConsole.getFrame()).setVisible(true); /* - * if we got this far, enable 'Run Groovy' in AlignFrame menus - * and disable opening a second console + * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable + * opening a second console */ enableExecuteGroovy(true); } @@@ -2521,12 -2645,9 +2526,12 @@@ */ protected void addQuitHandler() { - getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) - .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + getRootPane() + .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke + .getKeyStroke(KeyEvent.VK_Q, + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx()), "Quit"); getRootPane().getActionMap().put("Quit", new AbstractAction() { @@@ -2547,8 -2668,8 +2552,8 @@@ public void enableExecuteGroovy(boolean enabled) { /* - * disable opening a second Groovy console - * (or re-enable when the console is closed) + * disable opening a second Groovy console (or re-enable when the console is + * closed) */ groovyShell.setEnabled(!enabled); @@@ -2583,18 -2704,18 +2588,18 @@@ progressBarHandlers = new Hashtable<>(); } - if (progressBars.get(new Long(id)) != null) + if (progressBars.get(Long.valueOf(id)) != null) { - JPanel panel = progressBars.remove(new Long(id)); - if (progressBarHandlers.contains(new Long(id))) + JPanel panel = progressBars.remove(Long.valueOf(id)); + if (progressBarHandlers.contains(Long.valueOf(id))) { - progressBarHandlers.remove(new Long(id)); + progressBarHandlers.remove(Long.valueOf(id)); } removeProgressPanel(panel); } else { - progressBars.put(new Long(id), addProgressPanel(message)); + progressBars.put(Long.valueOf(id), addProgressPanel(message)); } } @@@ -2609,13 -2730,13 +2614,13 @@@ final IProgressIndicatorHandler handler) { if (progressBarHandlers == null - || !progressBars.containsKey(new Long(id))) + || !progressBars.containsKey(Long.valueOf(id))) { throw new Error(MessageManager.getString( "error.call_setprogressbar_before_registering_handler")); } - progressBarHandlers.put(new Long(id), handler); - final JPanel progressPanel = progressBars.get(new Long(id)); + progressBarHandlers.put(Long.valueOf(id), handler); + final JPanel progressPanel = progressBars.get(Long.valueOf(id)); if (handler.canCancel()) { JButton cancel = new JButton( @@@ -2679,8 -2800,7 +2684,8 @@@ public VamsasApplication getVamsasApplication() { - return v_client; + // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS + return null; } @@@ -2709,36 -2829,13 +2714,36 @@@ this.inBatchMode = inBatchMode; } + /** + * start service discovery and wait till it is done + */ public void startServiceDiscovery() { startServiceDiscovery(false); } + /** + * start service discovery threads - blocking or non-blocking + * + * @param blocking + */ public void startServiceDiscovery(boolean blocking) { + startServiceDiscovery(blocking, false); + } + + /** + * start service discovery threads + * + * @param blocking + * - false means call returns immediately + * @param ignore_SHOW_JWS2_SERVICES_preference + * - when true JABA services are discovered regardless of user's JWS2 + * discovery preference setting + */ + public void startServiceDiscovery(boolean blocking, + boolean ignore_SHOW_JWS2_SERVICES_preference) + { boolean alive = true; Thread t0 = null, t1 = null, t2 = null; // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release @@@ -2753,11 -2850,10 +2758,11 @@@ } // JAL-940 - disabled JWS1 service configuration - always start discoverer // until we phase out completely - (t0 = new Thread(discoverer, "DiscovererThread")).start(); + (t0 = new Thread(discoverer, "Discoverer")).start(); } - if (Cache.getDefault("SHOW_JWS2_SERVICES", true)) + if (ignore_SHOW_JWS2_SERVICES_preference + || Cache.getDefault("SHOW_JWS2_SERVICES", true)) { t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer() .startDiscoverer(changeSupport); @@@ -2810,19 -2906,24 +2815,19 @@@ /* * JalviewDialog jd =new JalviewDialog() { * - * @Override protected void cancelPressed() { // TODO - * Auto-generated method stub + * @Override protected void cancelPressed() { // TODO Auto-generated method stub * - * }@Override protected void okPressed() { // TODO - * Auto-generated method stub + * }@Override protected void okPressed() { // TODO Auto-generated method stub * - * }@Override protected void raiseClosed() { // TODO - * Auto-generated method stub + * }@Override protected void raiseClosed() { // TODO Auto-generated method stub * - * } }; jd.initDialogFrame(new - * JLabel("
" + ermsg + + * } }; jd.initDialogFrame(new JLabel("
" + + * ermsg + * "
It may be that you have invalid JABA URLs in your web service preferences," * + " or mis-configured HTTP proxy settings.
" + - * "Check the Connections and Web services tab of the" - * + - * " Tools->Preferences dialog box to change them.
" - * ), true, true, "Web Service Configuration Problem", 450, - * 400); + * "Check the Connections and Web services tab of the" + + * " Tools->Preferences dialog box to change them.
" ), + * true, true, "Web Service Configuration Problem", 450, 400); * * jd.waitForInput(); */ @@@ -2844,7 -2945,7 +2849,7 @@@ } else { - Cache.log.error( + jalview.bin.Console.error( "Errors reported by JABA discovery service. Check web services preferences.\n" + ermsg); } @@@ -2905,7 -3006,7 +2910,7 @@@ progress.setProgressBar(null, this.hashCode()); } } - }, "OpenURLThread").start(); + }, "OpenURL").start(); } public static WsParamSetManager wsparamManager = null; @@@ -2937,8 -3038,15 +2942,8 @@@ { if (url != null) { - if (Cache.log != null) - { - Cache.log.error("Couldn't handle string " + url + " as a URL."); - } - else - { - System.err.println( - "Couldn't handle string " + url + " as a URL."); - } + jalview.bin.Console + .error("Couldn't handle string " + url + " as a URL."); } // ignore any exceptions due to dud links. } @@@ -2983,6 -3091,7 +2988,6 @@@ } catch (InterruptedException x) { } - ; } if (instance == null) { @@@ -2993,8 -3102,7 +2998,8 @@@ SwingUtilities.invokeAndWait(prompter); } catch (Exception q) { - Cache.log.warn("Unexpected Exception in dialog thread.", q); + jalview.bin.Console.warn("Unexpected Exception in dialog thread.", + q); } } }); @@@ -3008,39 -3116,28 +3013,39 @@@ block.release(); } + /** + * Outputs an image of the desktop to file in EPS format, after prompting the + * user for choice of Text or Lineart character rendering (unless a preference + * has been set). The file name is generated as + * + *
 +   * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
 +   * 
+ */ @Override protected void snapShotWindow_actionPerformed(ActionEvent e) { + // currently the menu option to do this is not shown invalidate(); - File of; - ImageMaker im = new jalview.util.ImageMaker( - this, ImageMaker.TYPE.EPS, "View of Desktop", getWidth(), - getHeight(), of = new File("Jalview_snapshot" - + System.currentTimeMillis() + ".eps"), - "View of desktop", null, 0, false); - try - { - paintAll(im.getGraphics()); - im.writeImage(); - } catch (Exception q) + + int width = getWidth(); + int height = getHeight(); + File of = new File( + "Jalview_snapshot_" + System.currentTimeMillis() + ".eps"); + ImageWriterI writer = new ImageWriterI() { - Cache.log.error("Couldn't write snapshot to " + of.getAbsolutePath(), - q); - return; - } - Cache.log.info("Successfully written snapshot to file " - + of.getAbsolutePath()); + @Override + public void exportImage(Graphics g) throws Exception + { + paintAll(g); + jalview.bin.Console.info("Successfully written snapshot to file " + + of.getAbsolutePath()); + } + }; + String title = "View of desktop"; + ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS, + title); + exporter.doExport(of, this, width, height, title); } /** @@@ -3067,15 -3164,16 +3072,15 @@@ } /* - * Processing in reverse order works, forwards order leaves the first panels - * not visible. I don't know why! + * Processing in reverse order works, forwards order leaves the first panels not + * visible. I don't know why! */ for (int i = viewCount - 1; i >= 0; i--) { /* - * Make new top and bottom frames. These take over the respective - * AlignmentPanel objects, including their AlignmentViewports, so the - * cdna/protein relationships between the viewports is carried over to the - * new split frames. + * Make new top and bottom frames. These take over the respective AlignmentPanel + * objects, including their AlignmentViewports, so the cdna/protein + * relationships between the viewports is carried over to the new split frames. * * explodedGeometry holds the (x, y) position of the previously exploded * SplitFrame, and the (width, height) of the AlignFrame component @@@ -3114,8 -3212,8 +3119,8 @@@ } /* - * Clear references to the panels (now relocated in the new SplitFrames) - * before closing the old SplitFrame. + * Clear references to the panels (now relocated in the new SplitFrames) before + * closing the old SplitFrame. */ topPanels.clear(); bottomPanels.clear(); @@@ -3196,75 -3294,21 +3201,75 @@@ return groovyConsole; } - public static void transferFromDropTarget(List files, + /** + * handles the payload of a drag and drop event. + * + * TODO refactor to desktop utilities class + * + * @param files + * - Data source strings extracted from the drop event + * @param protocols + * - protocol for each data source extracted from the drop event + * @param evt + * - the drop event + * @param t + * - the payload from the drop event + * @throws Exception + */ + public static void transferFromDropTarget(List files, List protocols, DropTargetDropEvent evt, Transferable t) throws Exception { DataFlavor uriListFlavor = new DataFlavor( - "text/uri-list;class=java.lang.String"); + "text/uri-list;class=java.lang.String"), urlFlavour = null; + try + { + urlFlavour = new DataFlavor( + "application/x-java-url; class=java.net.URL"); + } catch (ClassNotFoundException cfe) + { + jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.", + cfe); + } + + if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour)) + { + + try + { + java.net.URL url = (URL) t.getTransferData(urlFlavour); + // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099 + // means url may be null. + if (url != null) + { + protocols.add(DataSourceType.URL); + files.add(url.toString()); + jalview.bin.Console.debug("Drop handled as URL dataflavor " + + files.get(files.size() - 1)); + return; + } + else + { + if (Platform.isAMacAndNotJS()) + { + System.err.println( + "Please ignore plist error - occurs due to problem with java 8 on OSX"); + } + } + } catch (Throwable ex) + { + jalview.bin.Console.debug("URL drop handler failed.", ex); + } + } if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { // Works on Windows and MacOSX - Cache.log.debug("Drop handled as javaFileListFlavor"); + jalview.bin.Console.debug("Drop handled as javaFileListFlavor"); for (Object file : (List) t .getTransferData(DataFlavor.javaFileListFlavor)) { - files.add(((File) file).toString()); + files.add(file); protocols.add(DataSourceType.FILE); } } @@@ -3275,127 -3319,72 +3280,127 @@@ String data = null; if (t.isDataFlavorSupported(uriListFlavor)) { - Cache.log.debug("Drop handled as uriListFlavor"); + jalview.bin.Console.debug("Drop handled as uriListFlavor"); // This is used by Unix drag system data = (String) t.getTransferData(uriListFlavor); } if (data == null) { // fallback to text: workaround - on OSX where there's a JVM bug - Cache.log.debug("standard URIListFlavor failed. Trying text"); + jalview.bin.Console + .debug("standard URIListFlavor failed. Trying text"); // try text fallback - data = (String) t.getTransferData( - new DataFlavor("text/plain;class=java.lang.String")); - if (Cache.log.isDebugEnabled()) + DataFlavor textDf = new DataFlavor( + "text/plain;class=java.lang.String"); + if (t.isDataFlavorSupported(textDf)) { - Cache.log.debug("fallback returned " + data); + data = (String) t.getTransferData(textDf); } + + jalview.bin.Console.debug("Plain text drop content returned " + + (data == null ? "Null - failed" : data)); + } - while (protocols.size() < files.size()) - { - Cache.log.debug("Adding missing FILE protocol for " - + files.get(protocols.size())); - protocols.add(DataSourceType.FILE); - } - for (java.util.StringTokenizer st = new java.util.StringTokenizer( - data, "\r\n"); st.hasMoreTokens();) + if (data != null) { - added = true; - String s = st.nextToken(); - if (s.startsWith("#")) - { - // the line is a comment (as per the RFC 2483) - continue; - } - java.net.URI uri = new java.net.URI(s); - if (uri.getScheme().toLowerCase().startsWith("http")) + while (protocols.size() < files.size()) { - protocols.add(DataSourceType.URL); - files.add(uri.toString()); + jalview.bin.Console.debug("Adding missing FILE protocol for " + + files.get(protocols.size())); + protocols.add(DataSourceType.FILE); } - else + for (java.util.StringTokenizer st = new java.util.StringTokenizer( + data, "\r\n"); st.hasMoreTokens();) { - // otherwise preserve old behaviour: catch all for file objects - java.io.File file = new java.io.File(uri); - protocols.add(DataSourceType.FILE); - files.add(file.toString()); + added = true; + String s = st.nextToken(); + if (s.startsWith("#")) + { + // the line is a comment (as per the RFC 2483) + continue; + } + java.net.URI uri = new java.net.URI(s); + if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http")) + { + protocols.add(DataSourceType.URL); + files.add(uri.toString()); + } + else + { + // otherwise preserve old behaviour: catch all for file objects + java.io.File file = new java.io.File(uri); + protocols.add(DataSourceType.FILE); + files.add(file.toString()); + } } } - if (Cache.log.isDebugEnabled()) + + if (jalview.bin.Console.isDebugEnabled()) { if (data == null || !added) { - Cache.log.debug( - "Couldn't resolve drop data. Here are the supported flavors:"); - for (DataFlavor fl : t.getTransferDataFlavors()) + + if (t.getTransferDataFlavors() != null + && t.getTransferDataFlavors().length > 0) { - Cache.log.debug( - "Supported transfer dataflavor: " + fl.toString()); - Object df = t.getTransferData(fl); - if (df != null) - { - Cache.log.debug("Retrieves: " + df); - } - else + jalview.bin.Console.debug( + "Couldn't resolve drop data. Here are the supported flavors:"); + for (DataFlavor fl : t.getTransferDataFlavors()) { - Cache.log.debug("Retrieved nothing"); + jalview.bin.Console.debug( + "Supported transfer dataflavor: " + fl.toString()); + Object df = t.getTransferData(fl); + if (df != null) + { + jalview.bin.Console.debug("Retrieves: " + df); + } + else + { + jalview.bin.Console.debug("Retrieved nothing"); + } } } + else + { + jalview.bin.Console + .debug("Couldn't resolve dataflavor for drop: " + + t.toString()); + } + } + } + } + if (Platform.isWindowsAndNotJS()) + { + jalview.bin.Console + .debug("Scanning dropped content for Windows Link Files"); + + // resolve any .lnk files in the file drop + for (int f = 0; f < files.size(); f++) + { + String source = files.get(f).toString().toLowerCase(Locale.ROOT); + if (protocols.get(f).equals(DataSourceType.FILE) + && (source.endsWith(".lnk") || source.endsWith(".url") + || source.endsWith(".site"))) + { + try + { + Object obj = files.get(f); + File lf = (obj instanceof File ? (File) obj + : new File((String) obj)); + // process link file to get a URL + jalview.bin.Console.debug("Found potential link file: " + lf); + WindowsShortcut wscfile = new WindowsShortcut(lf); + String fullname = wscfile.getRealFilename(); + protocols.set(f, FormatAdapter.checkProtocol(fullname)); + files.set(f, fullname); + jalview.bin.Console.debug("Parsed real filename " + fullname + + " to extract protocol: " + protocols.get(f)); + } catch (Exception ex) + { + jalview.bin.Console.error( + "Couldn't parse " + files.get(f) + " as a link file.", + ex); + } } } } @@@ -3410,74 -3399,4 +3415,74 @@@ { Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected)); } + + /** + * Answers a (possibly empty) list of any structure viewer frames (currently + * for either Jmol or Chimera) which are currently open. This may optionally + * be restricted to viewers of a specified class, or viewers linked to a + * specified alignment panel. + * + * @param apanel + * if not null, only return viewers linked to this panel + * @param structureViewerClass + * if not null, only return viewers of this class + * @return + */ + public List getStructureViewers( + AlignmentPanel apanel, + Class structureViewerClass) + { + List result = new ArrayList<>(); + JInternalFrame[] frames = Desktop.instance.getAllFrames(); + + for (JInternalFrame frame : frames) + { + if (frame instanceof StructureViewerBase) + { + if (structureViewerClass == null + || structureViewerClass.isInstance(frame)) + { + if (apanel == null + || ((StructureViewerBase) frame).isLinkedWith(apanel)) + { + result.add((StructureViewerBase) frame); + } + } + } + } + return result; + } + + public static final String debugScaleMessage = "Desktop graphics transform scale="; + + private static boolean debugScaleMessageDone = false; + + public static void debugScaleMessage(Graphics g) + { + if (debugScaleMessageDone) + { + return; + } + // output used by tests to check HiDPI scaling settings in action + try + { + Graphics2D gg = (Graphics2D) g; + if (gg != null) + { + AffineTransform t = gg.getTransform(); + double scaleX = t.getScaleX(); + double scaleY = t.getScaleY(); + jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)"); + jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)"); + debugScaleMessageDone = true; + } + else + { + jalview.bin.Console.debug("Desktop graphics null"); + } + } catch (Exception e) + { + jalview.bin.Console.debug(Cache.getStackTraceString(e)); + } + } } diff --combined src/jalview/gui/JalviewDialog.java index b1f868e,ccbe1ee..4237ee6 --- a/src/jalview/gui/JalviewDialog.java +++ b/src/jalview/gui/JalviewDialog.java @@@ -27,8 -27,8 +27,8 @@@ import java.awt.Dimension import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; import javax.swing.JButton; import javax.swing.JDialog; @@@ -66,7 -66,7 +66,7 @@@ public abstract class JalviewDialog ext frame.setVisible(true); } - }, "UnblockedDialogThread").start(); + }, "UnblockedDialog").start(); } else { @@@ -118,14 -118,55 +118,14 @@@ closeDialog(); } }); - frame.addWindowListener(new WindowListener() + frame.addWindowListener(new WindowAdapter() { - - @Override - public void windowOpened(WindowEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void windowIconified(WindowEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void windowDeiconified(WindowEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void windowDeactivated(WindowEvent e) - { - // TODO Auto-generated method stub - - } - @Override public void windowClosing(WindowEvent e) { // user has cancelled the dialog closeDialog(); } - - @Override - public void windowClosed(WindowEvent e) - { - } - - @Override - public void windowActivated(WindowEvent e) - { - // TODO Auto-generated method stub - - } }); } @@@ -136,8 -177,8 +136,8 @@@ { try { - frame.dispose(); raiseClosed(); + frame.dispose(); } catch (Exception ex) { } diff --combined src/jalview/gui/OverviewPanel.java index d7ef76a,0fbc04a..b3e3e66 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@@ -55,25 -55,24 +55,25 @@@ import javax.swing.SwingUtilities * @author $author$ * @version $Revision$ */ +@SuppressWarnings("serial") public class OverviewPanel extends JPanel implements Runnable, ViewportListenerI { - private OverviewDimensions od; + protected OverviewDimensions od; private OverviewCanvas oviewCanvas; - private AlignViewport av; + protected AlignViewport av; private AlignmentPanel ap; - private JCheckBoxMenuItem displayToggle; + protected JCheckBoxMenuItem displayToggle; - private boolean showHidden = true; + protected boolean showHidden = true; - private boolean draggingBox = false; + protected boolean draggingBox = false; - private ProgressPanel progressPanel; + protected ProgressPanel progressPanel; /** * Creates a new OverviewPanel object. @@@ -87,12 -86,12 +87,12 @@@ this.ap = alPanel; showHidden = Cache.getDefault(Preferences.SHOW_OV_HIDDEN_AT_START, - true); + false); if (showHidden) { od = new OverviewDimensionsShowHidden(av.getRanges(), - (av.isShowAnnotation() - && av.getAlignmentConservationAnnotation() != null)); + (av.isShowAnnotation() + && av.getAlignmentConservationAnnotation() != null)); } else { @@@ -173,21 -172,15 +173,21 @@@ { if (od.isPositionInBox(evt.getX(), evt.getY())) { - // display drag cursor at mouse position - setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + /* + * using HAND_CURSOR rather than DRAG_CURSOR + * as the latter is not supported on Mac + */ + getParent().setCursor( + Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { // reset cursor - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + getParent().setCursor( + Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } } + }); addMouseListener(new MouseAdapter() @@@ -195,38 -188,32 +195,38 @@@ @Override public void mousePressed(MouseEvent evt) { + + if (Platform.isWinRightButton(evt)) + { + showPopupMenu(evt); + return; + } if (SwingUtilities.isRightMouseButton(evt)) { - if (!Platform.isAMac()) - { - showPopupMenu(evt); - } + return; + } + // don't do anything if the mouse press is in the overview's box + // (wait to see if it's a drag instead) + // otherwise update the viewport + if (!od.isPositionInBox(evt.getX(), evt.getY())) + { + draggingBox = false; + + // display drag cursor at mouse position + setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + + od.updateViewportFromMouse(evt.getX(), evt.getY(), + av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); + getParent().setCursor( + Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { - // don't do anything if the mouse press is in the overview's box - // (wait to see if it's a drag instead) - // otherwise update the viewport - if (!od.isPositionInBox(evt.getX(), evt.getY())) - { - draggingBox = false; - od.updateViewportFromMouse(evt.getX(), evt.getY(), - av.getAlignment().getHiddenSequences(), - av.getAlignment().getHiddenColumns()); - } - else - { - draggingBox = true; - od.setDragPoint(evt.getX(), evt.getY(), - av.getAlignment().getHiddenSequences(), - av.getAlignment().getHiddenColumns()); - } + draggingBox = true; + od.setDragPoint(evt.getX(), evt.getY(), + av.getAlignment().getHiddenSequences(), + av.getAlignment().getHiddenColumns()); } } @@@ -238,29 -225,13 +238,29 @@@ showPopupMenu(evt); } } + + @Override + public void mouseReleased(MouseEvent evt) + { + draggingBox = false; + } + }); + + /* + * Javascript does not call componentResized on initial display, + * so do the update here + */ + if (Platform.isJS()) + { + updateOverviewImage(); + } } /* * Displays the popup menu and acts on user input */ - private void showPopupMenu(MouseEvent e) + protected void showPopupMenu(MouseEvent e) { JPopupMenu popup = new JPopupMenu(); ActionListener menuListener = new ActionListener() @@@ -285,7 -256,7 +285,7 @@@ /* * Toggle overview display between showing hidden columns and hiding hidden columns */ - private void toggleHiddenColumns() + protected void toggleHiddenColumns() { if (showHidden) { @@@ -324,7 -295,7 +324,7 @@@ od.setWidth(getWidth()); od.setHeight(getHeight() - progressPanel.getHeight()); } - + setPreferredSize(new Dimension(od.getWidth(), od.getHeight() + progressPanel.getHeight())); @@@ -333,10 -304,11 +333,10 @@@ return; } - Thread thread = new Thread(this, "UpdateOverviewThread"); + Thread thread = new Thread(this, "UpdateOverview"); thread.start(); repaint(); - } @Override @@@ -407,9 -379,8 +407,9 @@@ * close the parent frame (which also removes it from the * Desktop Windows menu) */ - ((JInternalFrame) SwingUtilities.getAncestorOfClass( - JInternalFrame.class, (this))).setClosed(true); + ((JInternalFrame) SwingUtilities + .getAncestorOfClass(JInternalFrame.class, (this))) + .setClosed(true); } catch (PropertyVetoException e) { // ignore diff --combined src/jalview/gui/RedundancyPanel.java index b248ec4,aa3d5b1..ad07497 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@@ -96,7 -96,7 +96,7 @@@ public class RedundancyPanel extends GS slider.setMaximum(100); slider.setValue(100); - Thread worker = new Thread(this, "CreateRedundancyPanelThread"); + Thread worker = new Thread(this, "CreateRedundancyPanel"); worker.start(); frame = new JInternalFrame(); @@@ -104,7 -104,7 +104,7 @@@ Desktop.addInternalFrame(frame, MessageManager .getString("label.redundancy_threshold_selection"), - 400, 100, false); + true, FRAME_WIDTH, FRAME_HEIGHT, false, true); frame.addInternalFrameListener(new InternalFrameAdapter() { @Override @@@ -214,7 -214,7 +214,7 @@@ @Override public void applyButton_actionPerformed(ActionEvent e) { - Vector del = new Vector(); + List del = new ArrayList<>(); undoButton.setEnabled(true); @@@ -225,12 -225,13 +225,12 @@@ { if (value <= redundancy[i]) { - del.addElement(originalSequences[i]); + del.add(originalSequences[i]); } } // This has to be done before the restoreHistoryItem method of alignFrame - // will - // actually restore these sequences. + // will actually restore these sequences. if (del.size() > 0) { SequenceI[] deleted = new SequenceI[del.size()]; @@@ -238,7 -239,7 +238,7 @@@ int width = 0; for (int i = 0; i < del.size(); i++) { - deleted[i] = (SequenceI) del.elementAt(i); + deleted[i] = del.get(i); if (deleted[i].getLength() > width) { width = deleted[i].getLength(); diff --combined src/jalview/gui/StructureViewerBase.java index 474b63c,c0769a0..4de11dd --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@@ -20,6 -20,23 +20,6 @@@ */ package jalview.gui; -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.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.structures.models.AAStructureBindingModel; -import jalview.util.MessageManager; - import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; @@@ -34,40 -51,17 +34,40 @@@ 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.bin.Console; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.gui.JalviewColourChooser.ColourChooserListener; +import jalview.gui.StructureViewer.ViewerType; +import jalview.gui.ViewSelectionMenu.ViewSetProvider; +import jalview.io.DataSourceType; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; +import jalview.jbgui.GStructureViewer; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemes; +import jalview.structure.StructureMapping; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.BrowserLauncher; +import jalview.util.MessageManager; +import jalview.ws.dbsources.EBIAlfaFold; +import jalview.ws.dbsources.Pdb; +import jalview.ws.utils.UrlDownloadClient; + /** * Base class with common functionality for JMol, Chimera or other structure * viewers. @@@ -94,13 -88,13 +94,13 @@@ public abstract class StructureViewerBa /** * list of alignment panels to use for superposition */ - protected Vector _alignwith = new Vector<>(); + protected Vector _alignwith = new Vector<>(); /** * list of alignment panels that are used for colouring structures by aligned * sequences */ - protected Vector _colourwith = new Vector<>(); + protected Vector _colourwith = new Vector<>(); private String viewId = null; @@@ -108,9 -102,9 +108,9 @@@ protected boolean alignAddedStructures = false; - protected boolean _started = false; + protected volatile boolean _started = false; - protected boolean addingStructures = false; + protected volatile boolean addingStructures = false; protected Thread worker = null; @@@ -119,17 -113,6 +119,17 @@@ protected JMenu viewSelectionMenu; /** + * set after sequence colouring has been applied for this structure viewer. + * used to determine if the final sequence/structure mapping has been + * determined + */ + protected volatile boolean seqColoursApplied = false; + + private IProgressIndicator progressBar = null; + + private Random random = new Random(); + + /** * Default constructor */ public StructureViewerBase() @@@ -138,37 -121,6 +138,37 @@@ } /** + * @return true if added structures should be aligned to existing one(s) + */ + @Override + public boolean isAlignAddedStructures() + { + return alignAddedStructures; + } + + /** + * + * @param true + * if added structures should be aligned to existing one(s) + */ + @Override + public void setAlignAddedStructures(boolean alignAdded) + { + alignAddedStructures = alignAdded; + } + + /** + * called by the binding model to indicate when adding structures is happening + * or has been completed + * + * @param addingStructures + */ + public synchronized void setAddingStructures(boolean addingStructures) + { + this.addingStructures = addingStructures; + } + + /** * * @param ap2 * @return true if this Jmol instance is linked with the given alignPanel @@@ -178,14 -130,13 +178,14 @@@ 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); } @@@ -213,6 -164,8 +213,6 @@@ this.viewId = viewId; } - public abstract String getStateInfo(); - protected void buildActionMenu() { if (_alignwith == null) @@@ -224,10 -177,6 +224,10 @@@ _alignwith.add(ap); } ; + // TODO: refactor to allow concrete classes to register buttons for adding + // here + // currently have to override to add buttons back in after they are cleared + // in this loop for (Component c : viewerActionMenu.getMenuComponents()) { if (c != alignStructs) @@@ -237,7 -186,6 +237,7 @@@ } } + @Override public AlignmentPanel getAlignmentPanel() { return ap; @@@ -290,8 -238,7 +290,8 @@@ * * @param nap */ - public void removeAlignmentPanel(AlignmentPanel nap) + @Override + public void removeAlignmentPanel(AlignmentViewPanel nap) { try { @@@ -363,6 -310,8 +363,6 @@@ 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. @@@ -377,7 -326,7 +377,7 @@@ */ protected void addStructure(final PDBEntry pdbentry, final SequenceI[] seqs, final String[] chains, - final boolean align, final IProgressIndicator alignFrame) + final IProgressIndicator alignFrame) { if (pdbentry.getFile() == null) { @@@ -401,9 -350,9 +401,9 @@@ } } // and call ourselves again. - addStructure(pdbentry, seqs, chains, align, alignFrame); + addStructure(pdbentry, seqs, chains, alignFrame); } - }, "Adding3DStructureQueueThread").start(); + }, "Adding3DStructureQueue").start(); return; } } @@@ -413,43 -362,87 +413,43 @@@ { seqs }, new String[][] { chains }); addingStructures = true; _started = false; - worker = new Thread(this, "Adding3DStructureThread"); - alignAddedStructures = align; + worker = new Thread(this, "Adding3DStructure"); worker.start(); return; } - /** - * Presents a dialog with the option to add an align a structure to an - * existing structure view - * - * @param pdbId - * @param view - * @return YES, NO or CANCEL JvOptionPane code - */ - protected int chooseAlignStructureToViewer(String pdbId, - StructureViewerBase view) - { - int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage("label.add_pdbentry_to_view", - new Object[] - { pdbId, view.getTitle() }), - MessageManager - .getString("label.align_to_existing_structure_view"), - JvOptionPane.YES_NO_CANCEL_OPTION); - return option; - } - protected boolean hasPdbId(String pdbId) { return getBinding().hasPdbId(pdbId); } - protected abstract List getViewersFor( - AlignmentPanel alp); - /** - * Check for any existing views involving this alignment and give user the - * option to add and align this molecule to one of them - * - * @param pdbentry - * @param seq - * @param chains - * @param apanel - * @param pdbId - * @return true if user adds to a view, or cancels entirely, else false + * Returns a list of any viewer of the instantiated type. The list is + * restricted to those linked to the given alignment panel if it is not null. */ - protected boolean addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq, - String[] chains, final AlignmentPanel apanel, String pdbId) + protected List getViewersFor(AlignmentPanel alp) { - for (StructureViewerBase view : getViewersFor(apanel)) - { - // TODO: highlight the view somehow - /* - * JAL-1742 exclude view with this structure already mapped (don't offer - * to align chain B to chain A of the same structure) - */ - if (view.hasPdbId(pdbId)) - { - continue; - } - int option = chooseAlignStructureToViewer(pdbId, view); - if (option == JvOptionPane.CANCEL_OPTION) - { - return true; - } - else if (option == JvOptionPane.YES_OPTION) - { - view.useAlignmentPanelForSuperposition(apanel); - view.addStructure(pdbentry, seq, chains, true, apanel.alignFrame); - return true; - } - else - { - // NO_OPTION - offer the next viewer if any - } - } + return Desktop.instance.getStructureViewers(alp, this.getClass()); + } + @Override + public void addToExistingViewer(PDBEntry pdbentry, SequenceI[] seq, + String[] chains, final AlignmentViewPanel apanel, String pdbId) + { /* - * nothing offered and selected + * JAL-1742 exclude view with this structure already mapped (don't offer + * to align chain B to chain A of the same structure); code may defend + * against this possibility before we reach here */ - return false; + if (hasPdbId(pdbId)) + { + return; + } + AlignmentPanel alignPanel = (AlignmentPanel) apanel; // Implementation error + // if this + // cast fails + useAlignmentPanelForSuperposition(alignPanel); + addStructure(pdbentry, seq, chains, alignPanel.alignFrame); } /** @@@ -461,18 -454,15 +461,18 @@@ * @param apanel * @param pdbFilename */ - protected void addSequenceMappingsToStructure(SequenceI[] seq, - String[] chains, final AlignmentPanel apanel, String pdbFilename) + public void addSequenceMappingsToStructure(SequenceI[] seq, + String[] chains, final AlignmentViewPanel alpanel, + String pdbFilename) { + AlignmentPanel apanel = (AlignmentPanel) alpanel; + // TODO : Fix multiple seq to one chain issue here. /* * 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 @@@ -513,20 -503,47 +513,20 @@@ } } - /** - * Check if the PDB file is already loaded, if so offer to add it to the - * existing viewer - * - * @param seq - * @param chains - * @param apanel - * @param pdbId - * @return true if the user chooses to add to a viewer, or to cancel entirely - */ - protected boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains, - final AlignmentPanel apanel, String pdbId) + @Override + public boolean addAlreadyLoadedFile(SequenceI[] seq, String[] chains, + final AlignmentViewPanel apanel, String pdbId) { String alreadyMapped = apanel.getStructureSelectionManager() .alreadyMappedToFile(pdbId); - if (alreadyMapped != null) - { - /* - * the PDB file is already loaded - */ - int option = JvOptionPane.showInternalConfirmDialog(Desktop.desktop, - MessageManager.formatMessage( - "label.pdb_entry_is_already_displayed", new Object[] - { pdbId }), - MessageManager.formatMessage( - "label.map_sequences_to_visible_window", new Object[] - { pdbId }), - JvOptionPane.YES_NO_CANCEL_OPTION); - if (option == JvOptionPane.CANCEL_OPTION) - { - finished = true; - } - else if (option == JvOptionPane.YES_OPTION) - { - addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped); - finished = true; - } + if (alreadyMapped == null) + { + return false; } - return finished; + + addSequenceMappingsToStructure(seq, chains, apanel, alreadyMapped); + return true; } void setChainMenuItems(List chainNames) @@@ -577,6 -594,8 +577,6 @@@ } } - abstract void showSelectedChains(); - /** * Action on selecting one of Jalview's registered colour schemes */ @@@ -584,9 -603,9 +584,9 @@@ public void changeColour_actionPerformed(String colourSchemeName) { AlignmentI al = getAlignmentPanel().av.getAlignment(); - ColourSchemeI cs = ColourSchemes.getInstance() - .getColourScheme(colourSchemeName, al, null); - getBinding().setJalviewColourScheme(cs); + ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme( + colourSchemeName, getAlignmentPanel().av, al, null); + getBinding().colourByJalviewColourScheme(cs); } /** @@@ -620,7 -639,7 +620,7 @@@ @Override public void actionPerformed(ActionEvent actionEvent) { - viewerColour_actionPerformed(actionEvent); + viewerColour_actionPerformed(); } }); colourMenu.add(viewerColour); @@@ -636,7 -655,7 +636,7 @@@ @Override public void actionPerformed(ActionEvent actionEvent) { - background_actionPerformed(actionEvent); + background_actionPerformed(); } }); colourMenu.add(backGround); @@@ -667,7 -686,7 +667,7 @@@ @Override public void actionPerformed(ActionEvent actionEvent) { - seqColour_actionPerformed(actionEvent); + seqColour_actionPerformed(); } }); @@@ -679,7 -698,7 +679,7 @@@ @Override public void actionPerformed(ActionEvent actionEvent) { - chainColour_actionPerformed(actionEvent); + chainColour_actionPerformed(); } }); @@@ -691,15 -710,12 +691,15 @@@ @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()); @@@ -725,8 -741,8 +725,8 @@@ } else { - // update the Chimera display now. - seqColour_actionPerformed(null); + // update the viewer display now. + seqColour_actionPerformed(); } } }); @@@ -737,18 -753,10 +737,18 @@@ @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( @@@ -775,11 -783,13 +775,11 @@@ } }); - buildColourMenu(); - } + viewerActionMenu.setText(getViewerName()); + helpItem.setText(MessageManager.formatMessage("label.viewer_help", + getViewerName())); - @Override - public void setJalviewColourScheme(ColourSchemeI cs) - { - getBinding().setJalviewColourScheme(cs); + buildColourMenu(); } /** @@@ -788,7 -798,12 +788,7 @@@ * the operation. */ @Override - protected String alignStructs_actionPerformed(ActionEvent actionEvent) - { - return alignStructs_withAllAlignPanels(); - } - - protected String alignStructs_withAllAlignPanels() + protected String alignStructsWithAllAlignPanels() { if (getAlignmentPanel() == null) { @@@ -803,8 -818,19 +803,8 @@@ 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 ap : _alignwith) - { - als[a] = ap.av.getAlignment(); - alm[a] = -1; - alc[a++] = ap.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); @@@ -813,37 -839,30 +813,37 @@@ } catch (Exception e) { StringBuffer sp = new StringBuffer(); - for (AlignmentPanel ap : _alignwith) + for (AlignmentViewPanel alignPanel : _alignwith) { - sp.append("'" + ap.alignFrame.getTitle() + "' "); + sp.append("'" + alignPanel.getViewName() + "' "); } - Cache.log.info("Couldn't align structures with the " + sp.toString() + Console.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()) { @@@ -853,21 -872,21 +853,21 @@@ } @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()); @@@ -886,18 -905,16 +886,18 @@@ } } // Set the colour using the current view for the associated alignframe - for (AlignmentPanel ap : _colourwith) + for (AlignmentViewPanel alignPanel : _colourwith) { - binding.colourBySequence(ap); + binding.colourBySequence(alignPanel); } + seqColoursApplied = true; } } @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")); @@@ -947,7 -964,7 +947,7 @@@ } @Override - public void viewMapping_actionPerformed(ActionEvent actionEvent) + public void viewMapping_actionPerformed() { CutAndPasteTransfer cap = new CutAndPasteTransfer(); try @@@ -971,7 -988,6 +971,7 @@@ /** * Configures the title and menu items of the viewer panel. */ + @Override public void updateTitleAndMenus() { AAStructureBindingModel binding = getBinding(); @@@ -988,7 -1004,7 +988,7 @@@ * enable 'Superpose with' if more than one mapped structure */ viewSelectionMenu.setEnabled(false); - if (getBinding().getStructureFiles().length > 1 + if (getBinding().getMappedStructureCount() > 1 && getBinding().getSequence().length > 1) { viewSelectionMenu.setEnabled(true); @@@ -1009,324 -1025,7 +1009,324 @@@ if (!binding.isLoadingFromArchive()) { - seqColour_actionPerformed(null); + seqColour_actionPerformed(); + } + } + + @Override + public String toString() + { + return getTitle(); + } + + @Override + public boolean hasMapping() + { + if (worker != null && (addingStructures || _started)) + { + return false; + } + if (getBinding() == null) + { + if (_aps == null || _aps.size() == 0) + { + // viewer has been closed, but we did at some point run. + return true; + } + return false; + } + String[] pdbids = getBinding().getStructureFiles(); + if (pdbids == null) + { + return false; + } + int p = 0; + for (String pdbid : pdbids) + { + StructureMapping sm[] = getBinding().getSsm().getMapping(pdbid); + if (sm != null && sm.length > 0 && sm[0] != null) + { + p++; + } + } + // only return true if there is a mapping for every structure file we have + // loaded + if (p == 0 || p != pdbids.length) + { + return false; + } + // and that coloring has been applied + return seqColoursApplied; + } + + @Override + public void raiseViewer() + { + 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; + } + + public 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 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(); + EBIAlfaFold afclient = new EBIAlfaFold(); + 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 + { + if (afclient.isValidReference(pdbid)) + { + pdbseq = afclient.getSequenceRecords(pdbid, + processingEntry.getRetrievalUrl()); + } + else + { + if (processingEntry.hasRetrievalUrl()) + { + String safePDBId = java.net.URLEncoder.encode(pdbid, "UTF-8") + .replace("%", "__"); + + // retrieve from URL to new local tmpfile + File tmpFile = File.createTempFile(safePDBId, + "." + (PDBEntry.Type.MMCIF.toString().equals( + processingEntry.getType().toString()) ? "cif" + : "pdb")); + String fromUrl = processingEntry.getRetrievalUrl(); + UrlDownloadClient.download(fromUrl, tmpFile); + + // may not need this check ? + String file = tmpFile.getAbsolutePath(); + if (file != null) + { + pdbseq = EBIAlfaFold.importDownloadedStructureFromUrl(fromUrl, + tmpFile, pdbid, null, null, null); + } + } + else + { + 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() + { + if (getBinding() == null) + { + return null; + } + File session = getBinding().saveSession(); + long l = session.length(); + int wait = 50; + do + { + try + { + Thread.sleep(5); + } catch (InterruptedException e) + { + } + long nextl = session.length(); + if (nextl != l) + { + wait = 50; + l = nextl; + } + } while (--wait > 0); + return session; + } + + /** + * 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; + } + } + if (binding != null) + { + binding.closeViewer(forceClose); + } + setAlignmentPanel(null); + _aps.clear(); + _alignwith.clear(); + _colourwith.clear(); + // TODO: check for memory leaks where instance isn't finalised because jmb + // holds a reference to the window + // jmb = null; + dispose(); + } + + @Override + public void showHelp_actionPerformed() + { + /* + try + { + */ + String url = getBinding().getHelpURL(); + if (url != null) + { + BrowserLauncher.openURL(url); + } + /* + } + catch (IOException ex) + { + System.err + .println("Show " + getViewerName() + " failed with: " + + ex.getMessage()); + } + */ + } + + @Override + public boolean hasViewerActionsMenu() + { + return viewerActionMenu != null && viewerActionMenu.isEnabled() + && viewerActionMenu.getItemCount() > 0 + && viewerActionMenu.isVisible(); } } diff --combined src/jalview/gui/TreeCanvas.java index 06b06fa,ec5d28f..6617277 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@@ -20,6 -20,21 +20,6 @@@ */ package jalview.gui; -import jalview.analysis.Conservation; -import jalview.analysis.TreeModel; -import jalview.api.AlignViewportI; -import jalview.datamodel.Sequence; -import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; -import jalview.datamodel.SequenceNode; -import jalview.schemes.ColourSchemeI; -import jalview.schemes.ColourSchemeProperty; -import jalview.schemes.UserColourScheme; -import jalview.structure.SelectionSource; -import jalview.util.Format; -import jalview.util.MappingUtils; -import jalview.util.MessageManager; - import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@@ -36,30 -51,17 +36,30 @@@ import java.awt.print.PageFormat import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; -import java.util.Enumeration; import java.util.Hashtable; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Vector; -import javax.swing.JColorChooser; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; +import jalview.analysis.Conservation; +import jalview.analysis.TreeModel; +import jalview.api.AlignViewportI; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.datamodel.SequenceNode; +import jalview.gui.JalviewColourChooser.ColourChooserListener; +import jalview.schemes.ColourSchemeI; +import jalview.structure.SelectionSource; +import jalview.util.Format; +import jalview.util.MessageManager; + /** * DOCUMENT ME! * @@@ -78,9 -80,9 +78,9 @@@ public class TreeCanvas extends JPanel TreePanel tp; - AlignViewport av; + private AlignViewport av; - AlignmentPanel ap; + private AlignmentPanel ap; Font font; @@@ -98,15 -100,15 +98,15 @@@ int offy; - float threshold; + private float threshold; String longestName; int labelLength = -1; - Hashtable nameHash = new Hashtable(); + Map nameHash = new Hashtable<>(); - Hashtable nodeHash = new Hashtable(); + Map nodeHash = new Hashtable<>(); SequenceNode highlightNode; @@@ -128,7 -130,7 +128,7 @@@ { this.tp = tp; this.av = ap.av; - this.ap = ap; + this.setAssociatedPanel(ap); font = av.getFont(); scrollPane = scroller; addMouseListener(this); @@@ -379,25 -381,31 +379,25 @@@ */ public Object findElement(int x, int y) { - Enumeration keys = nameHash.keys(); - - while (keys.hasMoreElements()) + for (Entry entry : nameHash.entrySet()) { - Object ob = keys.nextElement(); - Rectangle rect = (Rectangle) nameHash.get(ob); + Rectangle rect = entry.getValue(); if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) && (y <= (rect.y + rect.height))) { - return ob; + return entry.getKey(); } } - keys = nodeHash.keys(); - - while (keys.hasMoreElements()) + for (Entry entry : nodeHash.entrySet()) { - Object ob = keys.nextElement(); - Rectangle rect = (Rectangle) nodeHash.get(ob); + Rectangle rect = entry.getValue(); if ((x >= rect.x) && (x <= (rect.x + rect.width)) && (y >= rect.y) && (y <= (rect.y + rect.height))) { - return ob; + return entry.getKey(); } } @@@ -459,8 -467,9 +459,8 @@@ if ((node.left() == null) && (node.right() == null)) { double height = node.height; - double dist = node.dist; - - int xstart = (int) ((height - dist) * wscale) + offx; + // double dist = node.dist; + // int xstart = (int) ((height - dist) * wscale) + offx; int xend = (int) (height * wscale) + offx; int ypos = (int) (node.ycount * chunk) + offy; @@@ -503,21 -512,29 +503,21 @@@ return; } - if ((node.left() == null) && (node.right() == null)) // TODO: internal node + node.color = c; + if (node.element() instanceof SequenceI) { - node.color = c; - - if (node.element() instanceof SequenceI) + final SequenceI seq = (SequenceI) node.element(); + AlignmentPanel[] aps = getAssociatedPanels(); + if (aps != null) { - AlignmentPanel[] aps = getAssociatedPanels(); - if (aps != null) + for (int a = 0; a < aps.length; a++) { - for (int a = 0; a < aps.length; a++) - { - final SequenceI seq = (SequenceI) node.element(); - aps[a].av.setSequenceColour(seq, c); - } + aps[a].av.setSequenceColour(seq, c); } } } - else - { - node.color = c; - setColor((SequenceNode) node.left(), c); - setColor((SequenceNode) node.right(), c); - } + setColor((SequenceNode) node.left(), c); + setColor((SequenceNode) node.right(), c); } /** @@@ -525,7 -542,7 +525,7 @@@ */ void startPrinting() { - Thread thread = new Thread(this, "PrintTreeCanvasThread"); + Thread thread = new Thread(this, "PrintTreeCanvas"); thread.start(); } @@@ -645,14 -662,13 +645,14 @@@ { fm = g.getFontMetrics(font); - if (nameHash.size() == 0) + int nameCount = nameHash.size(); + if (nameCount == 0) { repaint(); } if (fitToWindow || (!fitToWindow && (scrollPane - .getHeight() > ((fm.getHeight() * nameHash.size()) + offy)))) + .getHeight() > ((fm.getHeight() * nameCount) + offy)))) { draw(g, scrollPane.getWidth(), scrollPane.getHeight()); setPreferredSize(null); @@@ -660,8 -676,8 +660,8 @@@ else { setPreferredSize(new Dimension(scrollPane.getWidth(), - fm.getHeight() * nameHash.size())); - draw(g, scrollPane.getWidth(), fm.getHeight() * nameHash.size()); + fm.getHeight() * nameCount)); + draw(g, scrollPane.getWidth(), fm.getHeight() * nameCount); } scrollPane.revalidate(); @@@ -825,19 -841,15 +825,19 @@@ */ void chooseSubtreeColour() { - Color col = JColorChooser.showDialog(this, - MessageManager.getString("label.select_subtree_colour"), - highlightNode.color); - if (col != null) + String ttl = MessageManager.getString("label.select_subtree_colour"); + ColourChooserListener listener = new ColourChooserListener() { - setColor(highlightNode, col); - PaintRefresher.Refresh(tp, ap.av.getSequenceSetId()); - repaint(); - } + @Override + public void colourSelected(Color c) + { + setColor(highlightNode, c); + PaintRefresher.Refresh(tp, ap.av.getSequenceSetId()); + repaint(); + } + }; + JalviewColourChooser.showColourChooser(this, ttl, highlightNode.color, + listener); } @Override @@@ -916,8 -928,7 +916,8 @@@ if (ob instanceof SequenceI) { treeSelectionChanged((Sequence) ob); - PaintRefresher.Refresh(tp, ap.av.getSequenceSetId()); + PaintRefresher.Refresh(tp, + getAssociatedPanel().av.getSequenceSetId()); repaint(); av.sendSelection(); return; @@@ -949,22 -960,11 +949,22 @@@ .deleteAllGroups(); aps[a].av.getCodingComplement().clearSequenceColours(); } + aps[a].av.setUpdateStructures(true); } colourGroups(groups); + + /* + * clear partition (don't show vertical line) if + * it is to the right of all nodes + */ + if (groups.isEmpty()) + { + threshold = 0f; + } } - PaintRefresher.Refresh(tp, ap.av.getSequenceSetId()); + PaintRefresher.Refresh(tp, + getAssociatedPanel().av.getSequenceSetId()); repaint(); } @@@ -994,48 -994,76 +994,48 @@@ } ColourSchemeI cs = null; - SequenceGroup sg = new SequenceGroup(sequences, null, cs, true, true, + SequenceGroup _sg = new SequenceGroup(sequences, null, cs, true, true, false, 0, av.getAlignment().getWidth() - 1); - if (av.getGlobalColourScheme() != null) - { - if (av.getGlobalColourScheme() instanceof UserColourScheme) - { - cs = new UserColourScheme( - ((UserColourScheme) av.getGlobalColourScheme()) - .getColours()); - - } - else - { - cs = ColourSchemeProperty.getColourScheme(sg, ColourSchemeProperty - .getColourName(av.getGlobalColourScheme())); - } - // cs is null if shading is an annotationColourGradient - // if (cs != null) - // { - // cs.setThreshold(av.getViewportColourScheme().getThreshold(), - // av.isIgnoreGapsConsensus()); - // } - } - sg.setColourScheme(cs); - sg.getGroupColourScheme().setThreshold( - av.getResidueShading().getThreshold(), - av.isIgnoreGapsConsensus()); - // sg.recalcConservation(); - sg.setName("JTreeGroup:" + sg.hashCode()); - sg.setIdColour(col); + _sg.setName("JTreeGroup:" + _sg.hashCode()); + _sg.setIdColour(col); for (int a = 0; a < aps.length; a++) { - if (aps[a].av.getGlobalColourScheme() != null - && aps[a].av.getResidueShading().conservationApplied()) - { - Conservation c = new Conservation("Group", sg.getSequences(null), - sg.getStartRes(), sg.getEndRes()); - c.calculate(); - c.verdict(false, aps[a].av.getConsPercGaps()); - sg.cs.setConservation(c); - } + SequenceGroup sg = new SequenceGroup(_sg); + AlignViewport viewport = aps[a].av; - aps[a].av.getAlignment().addGroup(new SequenceGroup(sg)); - // TODO can we push all of the below into AlignViewportI? - final AlignViewportI codingComplement = aps[a].av - .getCodingComplement(); - if (codingComplement != null) + // Propagate group colours in each view + if (viewport.getGlobalColourScheme() != null) { - SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av, - codingComplement); - if (mappedGroup.getSequences().size() > 0) + cs = viewport.getGlobalColourScheme().getInstance(viewport, sg); + sg.setColourScheme(cs); + sg.getGroupColourScheme().setThreshold( + viewport.getResidueShading().getThreshold(), + viewport.isIgnoreGapsConsensus()); + + if (viewport.getResidueShading().conservationApplied()) { - codingComplement.getAlignment().addGroup(mappedGroup); - for (SequenceI seq : mappedGroup.getSequences()) - { - codingComplement.setSequenceColour(seq, col.brighter()); - } + Conservation c = new Conservation("Group", + sg.getSequences(null), sg.getStartRes(), + sg.getEndRes()); + c.calculate(); + c.verdict(false, viewport.getConsPercGaps()); + sg.cs.setConservation(c); } } + // indicate that associated structure views will need an update + viewport.setUpdateStructures(true); + // propagate structure view update and sequence group to complement view + viewport.addSequenceGroup(sg); } } - // notify the panel(s) to redo any group specific stuff. + // notify the panel(s) to redo any group specific stuff + // also updates structure views if necessary for (int a = 0; a < aps.length; a++) { aps[a].updateAnnotation(); - // TODO: JAL-868 - need to ensure view colour change message is broadcast - // to any Jmols listening in final AlignViewportI codingComplement = aps[a].av .getCodingComplement(); if (codingComplement != null) @@@ -1090,47 -1118,7 +1090,47 @@@ } else { - return new AlignmentPanel[] { ap }; + return new AlignmentPanel[] { getAssociatedPanel() }; } } + + public AlignmentPanel getAssociatedPanel() + { + return ap; + } + + public void setAssociatedPanel(AlignmentPanel ap) + { + this.ap = ap; + } + + public AlignViewport getViewport() + { + return av; + } + + public void setViewport(AlignViewport av) + { + this.av = av; + } + + public float getThreshold() + { + return threshold; + } + + public void setThreshold(float threshold) + { + this.threshold = threshold; + } + + public boolean isApplyToAllViews() + { + return this.applyToAllViews; + } + + public void setApplyToAllViews(boolean applyToAllViews) + { + this.applyToAllViews = applyToAllViews; + } } diff --combined src/jalview/gui/VamsasApplication.java index df63be1,6003416..dabec6d --- a/src/jalview/gui/VamsasApplication.java +++ b/src/jalview/gui/VamsasApplication.java @@@ -21,7 -21,6 +21,7 @@@ package jalview.gui; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; @@@ -202,7 -201,8 +202,7 @@@ public class VamsasApplication implemen } catch (Exception e) { - jalview.bin.Cache.log.error("Couldn't instantiate vamsas client !", - e); + Console.error("Couldn't instantiate vamsas client !", e); return false; } return true; @@@ -225,12 -225,12 +225,12 @@@ } } catch (Error e) { - Cache.log.warn( + Console.warn( "Probable SERIOUS VAMSAS client incompatibility - carrying on regardless", e); } catch (Exception e) { - Cache.log.warn( + Console.warn( "Probable VAMSAS client incompatibility - carrying on regardless", e); } @@@ -244,7 -244,7 +244,7 @@@ private ClientHandle getJalviewHandle() { return new ClientHandle("jalview.bin.Jalview", - jalview.bin.Cache.getProperty("VERSION")); + Cache.getProperty("VERSION")); } /** @@@ -264,18 -264,19 +264,18 @@@ { if (!inSession()) { - throw new Error(MessageManager.getString( - "error.implementation_error_vamsas_operation_not_init")); + throw new Error( + "Implementation error! Vamsas Operations when client not initialised and connected"); } addDocumentUpdateHandler(); addStoreDocumentHandler(); startSession(); inInitialUpdate = true; - Cache.log.debug( - "Jalview loading the Vamsas Session for the first time."); + Console.debug("Jalview loading the Vamsas Session for the first time."); dealWithDocumentUpdate(false); // we don't push an update out to the inInitialUpdate = false; // document yet. - Cache.log.debug("... finished update for the first time."); + Console.debug("... finished update for the first time."); } /** @@@ -305,7 -306,7 +305,7 @@@ } } catch (Exception e) { - Cache.log.warn( + Console.warn( "Exception whilst refreshing jalview windows after a vamsas document update.", e); } @@@ -319,13 -320,13 +319,13 @@@ @Override public void run() { - Cache.log.info("Jalview updating to the Vamsas Session."); + Console.info("Jalview updating to the Vamsas Session."); dealWithDocumentUpdate(true); - Cache.log.info("Jalview finished updating to the Vamsas Session."); + Console.info("Jalview finished updating to the Vamsas Session."); } - }, "UpdateVamsasThread"); + }, "UpdateVamsas"); udthread.start(); } @@@ -350,9 -351,10 +350,9 @@@ { if (!inSession()) { - throw new Error(MessageManager - .getString("error.jalview_no_connected_vamsas_session")); + throw new Error("Jalview not connected to Vamsas session"); } - Cache.log.info("Jalview disconnecting from the Vamsas Session."); + Console.info("Jalview disconnecting from the Vamsas Session."); try { if (joinedSession) @@@ -360,12 -362,12 +360,12 @@@ boolean ourprompt = this.promptUser; this.promptUser = promptUser; vclient.finalizeClient(); - Cache.log.info("Jalview has left the session."); + Console.info("Jalview has left the session."); this.promptUser = ourprompt; // restore default value } else { - Cache.log.warn( + Console.warn( "JV Client leaving a session that's its not joined yet."); } joinedSession = false; @@@ -376,13 -378,13 +376,13 @@@ vobj2jv = null; } catch (Exception e) { - Cache.log.error("Vamsas Session finalization threw exceptions!", e); + Console.error("Vamsas Session finalization threw exceptions!", e); } } public void updateJalview(IClientDocument cdoc) { - Cache.log.debug("Jalview updating from sesion document .."); + Console.debug("Jalview updating from sesion document .."); ensureJvVamsas(); VamsasAppDatastore vds = new VamsasAppDatastore(cdoc, vobj2jv, jv2vobj, baseProvEntry(), alRedoState); @@@ -391,7 -393,7 +391,7 @@@ vds.updateToJalview(); } catch (Exception e) { - Cache.log.error("Failed to update Jalview from vamsas document.", e); + Console.error("Failed to update Jalview from vamsas document.", e); } try { @@@ -403,10 -405,10 +403,10 @@@ } } catch (Exception e) { - Cache.log.error( + Console.error( "Exception when updating Jalview settings from Appdata.", e); } - Cache.log.debug(".. finished updating from sesion document."); + Console.debug(".. finished updating from sesion document."); } @@@ -480,9 -482,10 +480,9 @@@ } catch (Exception e) { errorsDuringUpdate = true; - Cache.log.error("Exception synchronizing " + af.getTitle() - + " " - + (af.getViewport().viewName == null ? "" - : " view " + af.getViewport().viewName) + Console.error("Exception synchronizing " + af.getTitle() + " " + + (af.getViewport().getViewName() == null ? "" + : " view " + af.getViewport().getViewName()) + " to document.", e); stored = false; } @@@ -517,7 -520,7 +517,7 @@@ } } catch (Exception e) { - Cache.log.error("Exception synchronizing Views to Document :", e); + Console.error("Exception synchronizing Views to Document :", e); errorsDuringUpdate = true; } @@@ -534,7 -537,7 +534,7 @@@ } } catch (Exception e) { - Cache.log.error("Client Appdata Write exception", e); + Console.error("Client Appdata Write exception", e); errorsDuringAppUpdate = true; } vds.clearSkipList(); @@@ -562,32 -565,32 +562,32 @@@ { int storedviews = 0; // called by update handler for document update. - Cache.log.debug("Updating jalview from changed vamsas document."); + Console.debug("Updating jalview from changed vamsas document."); disableGui(true); try { long time = System.currentTimeMillis(); IClientDocument cdoc = vclient.getClientDocument(); - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug("Time taken to get ClientDocument = " + Console.debug("Time taken to get ClientDocument = " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); } if (fromJalview) { storedviews += updateVamsasDocument(cdoc); - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug( + Console.debug( "Time taken to update Vamsas Document from jalview\t= " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); } cdoc.setVamsasRoots(cdoc.getVamsasRoots()); - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug("Time taken to set Document Roots\t\t= " + Console.debug("Time taken to set Document Roots\t\t= " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); } @@@ -595,9 -598,9 +595,9 @@@ else { updateJalview(cdoc); - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug( + Console.debug( "Time taken to update Jalview from vamsas document Roots\t= " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); @@@ -605,9 -608,9 +605,9 @@@ } vclient.updateDocument(cdoc); - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug("Time taken to update Session Document\t= " + Console.debug("Time taken to update Session Document\t= " + (System.currentTimeMillis() - time)); time = System.currentTimeMillis(); } @@@ -621,7 -624,7 +621,7 @@@ recover_objectMappingBackup(); storedviews = 0; } - Cache.log.debug("Finished updating from document change."); + Console.debug("Finished updating from document change."); disableGui(false); return storedviews; } @@@ -634,12 -637,12 +634,12 @@@ @Override public void propertyChange(PropertyChangeEvent evt) { - Cache.log.debug("Dealing with document update event."); + Console.debug("Dealing with document update event."); client.dealWithDocumentUpdate(false); - Cache.log.debug("finished dealing with event."); + Console.debug("finished dealing with event."); } }); - Cache.log.debug("Added Jalview handler for vamsas document updates."); + Console.debug("Added Jalview handler for vamsas document updates."); } private void addStoreDocumentHandler() @@@ -654,7 -657,7 +654,7 @@@ { if (client.promptUser) { - Cache.log.debug( + Console.debug( "Asking user if the vamsas session should be stored."); int reply = JvOptionPane.showInternalConfirmDialog( Desktop.desktop, @@@ -665,27 -668,27 +665,27 @@@ if (reply == JvOptionPane.YES_OPTION) { - Cache.log.debug("Prompting for vamsas store filename."); + Console.debug("Prompting for vamsas store filename."); Desktop.instance.vamsasSave_actionPerformed(null); - Cache.log - .debug("Finished attempt at storing document."); + Console.debug("Finished attempt at storing document."); } - Cache.log.debug( + Console.debug( "finished dealing with REQUESTTOCLOSE event."); } else { - Cache.log.debug( + Console.debug( "Ignoring store document request (promptUser==false)"); } } }); - Cache.log.debug("Added Jalview handler for vamsas document updates."); + Console.debug("Added Jalview handler for vamsas document updates."); } public void disableGui(boolean b) { - Desktop.instance.setVamsasUpdate(b); + // JAL-3311 TODO: remove this class! + // Desktop.instance.setVamsasUpdate(b); } Hashtable _backup_vobj2jv; @@@ -718,8 -721,8 +718,8 @@@ return; } - throw new Error(MessageManager.getString( - "error.implementation_error_cannot_recover_vamsas_object_mappings")); + throw new Error( + "IMPLEMENTATION ERROR: Cannot recover vamsas object mappings - no backup was made"); } jv2vobj.clear(); Iterator el = _backup_jv2vobj.entrySet().iterator(); @@@ -753,7 -756,7 +753,7 @@@ } catch (Exception e) { // Complain to GUI - Cache.log.error("Failed to join vamsas session.", e); + Console.error("Failed to join vamsas session.", e); vclient = null; } try @@@ -782,16 -785,16 +782,16 @@@ { return; } - // if (Cache.log.isDebugEnabled()) + // if (Cache.isDebugEnabled()) // { - // Cache.log.debug("Received MouseOverMessage "+mm.getVorbaID()+" + // Cache.debug("Received MouseOverMessage "+mm.getVorbaID()+" // "+mm.getPosition()); // } Object jvobj = vobj2jv.get(mm.getVorbaID()); if (jvobj != null && jvobj instanceof SequenceI) { last = mstring; - // Cache.log.debug("Handling Mouse over "+mm.getVorbaID()+" + // Cache.debug("Handling Mouse over "+mm.getVorbaID()+" // bound to "+jvobj+" at "+mm.getPosition()); // position is character position in aligned sequence ssm.mouseOverVamsasSequence((SequenceI) jvobj, @@@ -977,7 -980,7 +977,7 @@@ if (v != null) { // this should really be a trace message. - // Cache.log.debug("Mouse over " + v.getId() + " bound to " + // Cache.debug("Mouse over " + v.getId() + " bound to " // + seq + " at " + index); last = seq; i = index; @@@ -998,7 -1001,7 +998,7 @@@ { if (vobj2jv == null) { - Cache.log.warn( + Console.warn( "Selection listener still active for dead session."); // not in a session. return; @@@ -1071,16 -1074,16 +1071,16 @@@ } else { - // int[] intervals = colsel.getVisibleContigs( - // seqsel.getStartRes(), seqsel.getEndRes() + 1); - int[] intervals = hidden.getVisibleContigs( - seqsel.getStartRes(), seqsel.getEndRes() + 1); - for (int iv = 0; iv < intervals.length; iv += 2) + Iterator intervals = hidden + .getVisContigsIterator(seqsel.getStartRes(), + seqsel.getEndRes() + 1, false); + while (intervals.hasNext()) { + int[] region = intervals.next(); Seg s = new Seg(); - s.setStart(intervals[iv] + 1); // vamsas indices begin at - // 1, not zero. - s.setEnd(intervals[iv + 1] + 1); + s.setStart(region[0] + 1); // vamsas indices begin at 1, + // not zero. + s.setEnd(region[1] + 1); s.setInclusive(true); range.addSeg(s); } @@@ -1098,7 -1101,7 +1098,7 @@@ if (sm != null) { sm.validate(); // debug - Cache.log.debug("Selection Message\n" + sm.getRawMessage()); + Console.debug("Selection Message\n" + sm.getRawMessage()); pm.sendMessage(sm); } } @@@ -1109,7 -1112,7 +1109,7 @@@ ssm.addSelectionListener(selecter); } catch (Exception e) { - Cache.log.error("Failed to init Vamsas Picking", e); + Console.error("Failed to init Vamsas Picking", e); } } } diff --combined src/jalview/gui/WebserviceInfo.java index 034cbad,a0191a5..7a5d693 --- a/src/jalview/gui/WebserviceInfo.java +++ b/src/jalview/gui/WebserviceInfo.java @@@ -20,17 -20,18 +20,17 @@@ */ package jalview.gui; -import jalview.jbgui.GWebserviceInfo; -import jalview.util.MessageManager; -import jalview.ws.WSClientI; +import java.util.Locale; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Font; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.Image; import java.awt.MediaTracker; +import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.util.Vector; @@@ -44,16 -45,9 +44,16 @@@ import javax.swing.JTabbedPane import javax.swing.JTextArea; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; +import jalview.jbgui.GWebserviceInfo; +import jalview.util.ChannelProperties; +import jalview.util.MessageManager; +import jalview.ws.WSClientI; + /** * Base class for web service client thread and gui TODO: create StAX parser to * extract html body content reliably when preparing html formatted job statuses @@@ -87,7 -81,7 +87,7 @@@ public class WebserviceInfo extends GWe Image image; - int angle = 0; + float angle = 0f; String title = ""; @@@ -104,7 -98,7 +104,7 @@@ { super.setVisible(aFlag); frame.setVisible(aFlag); - }; + } JTabbedPane subjobs = null; @@@ -333,7 -327,9 +333,7 @@@ this.title = title; setInfoText(info); - java.net.URL url = getClass() - .getResource("/images/Jalview_Logo_small.png"); - image = java.awt.Toolkit.getDefaultToolkit().createImage(url); + image = ChannelProperties.getImage("rotatable_logo.48"); MediaTracker mt = new MediaTracker(this); mt.addImage(image, 0); @@@ -346,27 -342,26 +346,27 @@@ } AnimatedPanel ap = new AnimatedPanel(); - titlePanel.add(ap, BorderLayout.CENTER); + ap.setPreferredSize(new Dimension(60, 60)); + titlePanel.add(ap, BorderLayout.WEST); + titlePanel.add(titleText, BorderLayout.CENTER); + setStatus(currentStatus); - Thread thread = new Thread(ap, "AnimatedPanelThread"); + Thread thread = new Thread(ap, "AnimatedPanel"); thread.start(); final WebserviceInfo thisinfo = this; - frame.addInternalFrameListener( - new javax.swing.event.InternalFrameAdapter() - { - @Override - public void internalFrameClosed( - javax.swing.event.InternalFrameEvent evt) - { - // System.out.println("Shutting down webservice client"); - WSClientI service = thisinfo.getthisService(); - if (service != null && service.isCancellable()) - { - service.cancelJob(); - } - }; - }); + frame.addInternalFrameListener(new InternalFrameAdapter() + { + @Override + public void internalFrameClosed(InternalFrameEvent evt) + { + // System.out.println("Shutting down webservice client"); + WSClientI service = thisinfo.getthisService(); + if (service != null && service.isCancellable()) + { + service.cancelJob(); + } + } + }); frame.validate(); } @@@ -380,36 -375,6 +380,36 @@@ public void setStatus(int status) { currentStatus = status; + + String message = null; + switch (currentStatus) + { + case STATE_QUEUING: + message = MessageManager.getString("label.state_queueing"); + break; + + case STATE_RUNNING: + message = MessageManager.getString("label.state_running"); + break; + + case STATE_STOPPED_OK: + message = MessageManager.getString("label.state_completed"); + break; + + case STATE_CANCELLED_OK: + message = MessageManager.getString("label.state_job_cancelled"); + break; + + case STATE_STOPPED_ERROR: + message = MessageManager.getString("label.state_job_error"); + break; + + case STATE_STOPPED_SERVERERROR: + message = MessageManager.getString("label.server_error_try_later"); + break; + } + titleText.setText(title + (message == null ? "" : " - " + message)); + titleText.repaint(); } /** @@@ -547,7 -512,7 +547,7 @@@ { return null; } - String lowertxt = text.toLowerCase(); + String lowertxt = text.toLowerCase(Locale.ROOT); int htmlpos = leaveFirst ? -1 : lowertxt.indexOf("= STATE_STOPPED_OK) { + park(); angle = 0; } @@@ -837,62 -789,82 +837,62 @@@ cancel.setEnabled(false); } + public void park() + { + startTime = System.currentTimeMillis(); + + while (angle < 360) + { + float invSpeed = 5f; + float factor = 1f; + try + { + Thread.sleep(25); + + float delta = (System.currentTimeMillis() - startTime) / invSpeed; + angle += delta * factor; + startTime = System.currentTimeMillis(); + + if (angle >= 360) + { + angle = 360; + } + + repaint(); + } catch (Exception ex) + { + } + } + + } + void drawPanel() { if (offscreen == null || offscreen.getWidth(this) != getWidth() || offscreen.getHeight(this) != getHeight()) { offscreen = new BufferedImage(getWidth(), getHeight(), - BufferedImage.TYPE_INT_ARGB); + BufferedImage.TYPE_INT_RGB); } Graphics2D g = (Graphics2D) offscreen.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); if (image != null) { int x = image.getWidth(this) / 2, y = image.getHeight(this) / 2; - g.rotate(Math.toRadians(angle), 10 + x, 10 + y); - g.drawImage(image, 10, 10, this); - g.rotate(-Math.toRadians(angle), 10 + x, 10 + y); + g.rotate(3.14159 / 180 * (angle), x, y); + g.drawImage(image, 0, 0, this); + g.rotate(-3.14159 / 180 * (angle), x, y); } } diff --combined src/jalview/gui/WsPreferences.java index 7b8bc51,2053091..64ea1b1 --- a/src/jalview/gui/WsPreferences.java +++ b/src/jalview/gui/WsPreferences.java @@@ -20,6 -20,12 +20,6 @@@ */ package jalview.gui; -import jalview.bin.Cache; -import jalview.jbgui.GWsPreferences; -import jalview.util.MessageManager; -import jalview.ws.jws2.Jws2Discoverer; -import jalview.ws.rest.RestServiceDescription; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@@ -37,12 -43,6 +37,12 @@@ import javax.swing.JTextField import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; +import jalview.bin.Cache; +import jalview.jbgui.GWsPreferences; +import jalview.util.MessageManager; +import jalview.ws.jws2.Jws2Discoverer; +import jalview.ws.rest.RestServiceDescription; + public class WsPreferences extends GWsPreferences { @@@ -122,7 -122,7 +122,7 @@@ for (String url : wsUrls) { int status = Jws2Discoverer.getDiscoverer().getServerStatusFor(url); - tdat[r][1] = new Integer(status); + tdat[r][1] = Integer.valueOf(status); tdat[r++][0] = url; } @@@ -567,7 -567,7 +567,7 @@@ updateWsMenuConfig(false); refreshWsMenu(true); } - }, "RefreshWebServicesThread").start(); + }, "RefreshWebServices").start(); } @@@ -603,7 -603,7 +603,7 @@@ progressBar.setVisible(false); validate(); } - }, "RefreshWebServicesMenuProgressBarThread").start(); + }, "RefreshWebServicesMenuProgressBar").start(); } else @@@ -626,16 -626,14 +626,16 @@@ Desktop.instance.setProgressBar(null, ct); } - }, "RefreshWebServicesMenuThread").start(); + }, "RefreshWebServicesMenu").start(); } } /** * state counters for ensuring that updates only happen if config has changed. */ - private long update = 0, lastrefresh = 0; + protected long update = 0; + + private long lastrefresh = 0; /* * (non-Javadoc) @@@ -677,7 -675,7 +677,7 @@@ updateWsMenuConfig(false); refreshWsMenu(showProgressInDialog); } - }, "UpdateAndRefreshWebServicesMenuThread").start(); + }, "UpdateAndRefreshWebServicesMenu").start(); } } diff --combined src/jalview/io/FileFormat.java index b701963,ceb72be..361d699 --- a/src/jalview/io/FileFormat.java +++ b/src/jalview/io/FileFormat.java @@@ -20,14 -20,14 +20,15 @@@ */ package jalview.io; +import java.io.IOException; + import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; + import jalview.ext.forester.io.PhyloXmlFile; import jalview.ext.jmol.JmolParser; import jalview.structure.StructureImportSettings; -import java.io.IOException; - public enum FileFormat implements FileFormatI { Fasta("Fasta", "fa, fasta, mfa, fastq", true, true) @@@ -244,37 -244,6 +245,37 @@@ return new PhylipFile(); } }, + GenBank("GenBank Flatfile", "gb, gbk", true, false) + { + @Override + public AlignmentFileReaderI getReader(FileParse source) + throws IOException + { + return new GenBankFile(source, "GenBank"); + } + + @Override + public AlignmentFileWriterI getWriter(AlignmentI al) + { + return null; + } + }, + Embl("ENA Flatfile", "txt", true, false) + { + @Override + public AlignmentFileReaderI getReader(FileParse source) + throws IOException + { + // Always assume we import from EMBL for now + return new EmblFlatFile(source, DBRefSource.EMBL); + } + + @Override + public AlignmentFileWriterI getWriter(AlignmentI al) + { + return null; + } + }, Jnet("JnetFile", "", false, false) { @Override @@@ -338,7 -307,7 +339,7 @@@ else { StructureImportSettings.setShowSeqFeatures(true); - return new MCview.PDBfile( + return new mc_view.PDBfile( StructureImportSettings.isVisibleChainAnnotation(), StructureImportSettings.isProcessSecondaryStructure(), StructureImportSettings.isExternalSecondaryStructure(), @@@ -379,7 -348,7 +380,7 @@@ return true; } }, - Jalview("Jalview", "jar,jvp", true, true) + Jalview("Jalview", "jvp, jar", true, true) { @Override public AlignmentFileReaderI getReader(FileParse source) @@@ -403,10 -372,69 +404,69 @@@ @Override public boolean isIdentifiable() { - return false; + return true; } - }; + }, + // Nexus("Nexus", "nex,nexus,nx,tre", true, true) + // { + // + // @Override + // public AlignmentFileReaderI getReader(FileParse source) + // throws IOException + // { + // return new NexusFile(source); + // } + // + // @Override + // public AlignmentFileWriterI getWriter(AlignmentI al) + // { + // // handle within Aptx? + // return null; + // } + // + // @Override + // public boolean isTextFormat() + // { + // return true; + // } + // + // @Override + // public boolean isTreeFile() + // { + // return true; + // } + // + // }, + PhyloXML("PhyloXML", "phyloxml,phylo.xml,pxml", true, true) + { + + @Override + public AlignmentFileReaderI getReader(FileParse source) + throws IOException + { + return new PhyloXmlFile(source); + } + + @Override + public AlignmentFileWriterI getWriter(AlignmentI al) + { + // handle within Aptx? + return null; + } + + @Override + public boolean isTextFormat() + { + return true; + } + + @Override + public boolean isTreeFile() + { + return true; + } + }; private boolean writable; private boolean readable; @@@ -440,10 -468,7 +500,10 @@@ * @param extensions * comma-separated list of file extensions associated with the format * @param isReadable + * - can be recognised by IdentifyFile and imported with the given + * reader * @param isWritable + * - can be exported with the returned writer */ private FileFormat(String shortName, String extensions, boolean isReadable, boolean isWritable) @@@ -482,6 -507,12 +542,12 @@@ return false; } + @Override + public boolean isTreeFile() + { + return false; + } + /** * By default, answers true, indicating the format is one that can be * identified by IdentifyFile. Formats that cannot be identified should diff --combined src/jalview/io/FileLoader.java index fec0b3a,8e7de3f..4cf77de --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@@ -20,13 -20,6 +20,13 @@@ */ package jalview.io; +import java.io.File; +import java.io.IOException; +import java.util.StringTokenizer; +import java.util.Vector; + +import javax.swing.SwingUtilities; + import jalview.api.ComplexAlignFile; import jalview.api.FeatureSettingsModelI; import jalview.api.FeaturesDisplayedI; @@@ -37,17 -30,25 +37,18 @@@ import jalview.datamodel.AlignmentI import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; + import jalview.ext.archaeopteryx.AptxInit; import jalview.gui.AlignFrame; import jalview.gui.AlignViewport; import jalview.gui.Desktop; -import jalview.gui.Jalview2XML; import jalview.gui.JvOptionPane; import jalview.json.binding.biojson.v1.ColourSchemeMapper; +import jalview.project.Jalview2XML; import jalview.schemes.ColourSchemeI; import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; import jalview.ws.utils.UrlDownloadClient; -import java.io.File; -import java.io.IOException; -import java.util.StringTokenizer; -import java.util.Vector; - -import javax.swing.SwingUtilities; - public class FileLoader implements Runnable { String file; @@@ -71,8 -72,6 +72,8 @@@ boolean raiseGUI = true; + private File selectedFile; + /** * default constructor always raised errors in GUI dialog boxes */ @@@ -92,16 -91,11 +93,16 @@@ this.raiseGUI = raiseGUI; } - public void LoadFile(AlignViewport viewport, String file, + public void LoadFile(AlignViewport viewport, Object file, DataSourceType protocol, FileFormatI format) { this.viewport = viewport; - LoadFile(file, protocol, format); + if (file instanceof File) + { + this.selectedFile = (File) file; + file = selectedFile.getPath(); + } + LoadFile(file.toString(), protocol, format); } public void LoadFile(String file, DataSourceType protocol, @@@ -111,7 -105,7 +112,7 @@@ this.protocol = protocol; this.format = format; - final Thread loader = new Thread(this, "LoadFileThread"); + final Thread loader = new Thread(this, "LoadFile"); SwingUtilities.invokeLater(new Runnable() { @@@ -165,24 -159,6 +166,24 @@@ } /** + * Load alignment from (file, protocol) of type format and wait till loaded + * + * @param file + * @param sourceType + * @param format + * @return alignFrame constructed from file contents + */ + public AlignFrame LoadFileWaitTillLoaded(File file, + DataSourceType sourceType, FileFormatI format) + { + this.selectedFile = file; + this.file = file.getPath(); + this.protocol = sourceType; + this.format = format; + return _LoadFileWaitTillLoaded(); + } + + /** * Load alignment from FileParse source of type format and wait till loaded * * @param source @@@ -201,20 -177,33 +202,21 @@@ } /** - * start thread and wait until finished, then return the alignFrame that's - * (hopefully) been read. + * runs the 'run' method (in this thread), then return the alignFrame that's + * (hopefully) been read * * @return */ protected AlignFrame _LoadFileWaitTillLoaded() { - Thread loader = new Thread(this, "LoadFileWithWaiting"); - loader.start(); - - while (loader.isAlive()) - { - try - { - Thread.sleep(500); - } catch (Exception ex) - { - } - } - + this.run(); return alignFrame; } + // add support for recently opened Aptx trees public void updateRecentlyOpened() { - Vector recent = new Vector(); + Vector recent = new Vector<>(); if (protocol == DataSourceType.PASTE) { // do nothing if the file was pasted in as text... there is no filename to @@@ -230,7 -219,7 +232,7 @@@ String type = protocol == DataSourceType.FILE ? "RECENT_FILE" : "RECENT_URL"; - String historyItems = jalview.bin.Cache.getProperty(type); + String historyItems = Cache.getProperty(type); StringTokenizer st; @@@ -240,7 -229,7 +242,7 @@@ while (st.hasMoreTokens()) { - recent.addElement(st.nextElement().toString().trim()); + recent.addElement(st.nextToken().trim()); } } @@@ -285,10 -274,6 +287,10 @@@ format = new IdentifyFile().identify(source, false); // identify stream and rewind rather than close } + else if (selectedFile != null) + { + format = new IdentifyFile().identify(selectedFile, protocol); + } else { format = new IdentifyFile().identify(file, protocol); @@@ -334,9 -319,7 +336,9 @@@ "IMPLEMENTATION ERROR: Cannot read consecutive Jalview XML projects from a stream."); // We read the data anyway - it might make sense. } - alignFrame = new Jalview2XML(raiseGUI).loadJalviewAlign(file); + // BH 2018 switch to File object here instead of filename + alignFrame = new Jalview2XML(raiseGUI).loadJalviewAlign( + selectedFile == null ? file : selectedFile); } else { @@@ -364,29 -347,16 +366,29 @@@ file.lastIndexOf(".")); String tempStructureFileStr = createNamedJvTempFile( urlLeafName, structExt); - UrlDownloadClient.download(file, tempStructureFileStr); - al = fa.readFile(tempStructureFileStr, DataSourceType.FILE, - format); + + // BH - switching to File object here so as to hold + // ._bytes array directly + File tempFile = new File(tempStructureFileStr); + UrlDownloadClient.download(file, tempFile); + + al = fa.readFile(tempFile, DataSourceType.FILE, format); source = fa.getAlignFile(); } else { - al = fa.readFile(file, protocol, format); + if (selectedFile == null) + { + al = fa.readFile(file, protocol, format); + + } + else + { + al = fa.readFile(selectedFile, protocol, format); + } source = fa.getAlignFile(); // keep reference for later if - // necessary. + + // necessary. } } } catch (java.io.IOException ex) @@@ -420,9 -390,12 +422,9 @@@ .getFeatureColourScheme(); if (viewport != null) { - if (proxyColourScheme != null) - { - viewport.applyFeaturesStyle(proxyColourScheme); - } // append to existing alignment viewport.addAlignment(al, title); + viewport.applyFeaturesStyle(proxyColourScheme); } else { @@@ -463,14 -436,20 +465,21 @@@ if (!(protocol == DataSourceType.PASTE)) { alignFrame.setFileName(file, format); + alignFrame.setFileObject(selectedFile); // BH 2018 SwingJS } if (proxyColourScheme != null) { alignFrame.getViewport() .applyFeaturesStyle(proxyColourScheme); } + if (format.isTreeFile()) + { + // make generic instead of Aptx specific? + AptxInit.createInstancesFromFile(file, + alignFrame.getViewport()); + + } - alignFrame.statusBar.setText(MessageManager.formatMessage( + alignFrame.setStatus(MessageManager.formatMessage( "label.successfully_loaded_file", new String[] { title })); @@@ -485,10 -464,11 +494,11 @@@ AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); } + try { - alignFrame.setMaximum(jalview.bin.Cache - .getDefault("SHOW_FULLSCREEN", false)); + alignFrame.setMaximum( + Cache.getDefault("SHOW_FULLSCREEN", false)); } catch (java.beans.PropertyVetoException ex) { } diff --combined src/jalview/io/IdentifyFile.java index c21127e,1d0a3d2..d2c586a --- a/src/jalview/io/IdentifyFile.java +++ b/src/jalview/io/IdentifyFile.java @@@ -20,9 -20,6 +20,9 @@@ */ package jalview.io; +import java.util.Locale; + +import java.io.File; import java.io.IOException; /** @@@ -33,42 -30,6 +33,42 @@@ */ public class IdentifyFile { + + public FileFormatI identify(Object file, DataSourceType protocol) + throws FileFormatException + { + // BH 2018 + return (file instanceof File ? identify((File) file, protocol) + : identify((String) file, protocol)); + + } + + public FileFormatI identify(File file, DataSourceType sourceType) + throws FileFormatException + { + // BH 2018 + String emessage = "UNIDENTIFIED FILE PARSING ERROR"; + FileParse parser = null; + try + { + parser = new FileParse(file, sourceType); + if (parser.isValid()) + { + return identify(parser); + } + } catch (Exception e) + { + System.err.println("Error whilst identifying " + file); + e.printStackTrace(System.err); + emessage = e.getMessage(); + } + if (parser != null) + { + throw new FileFormatException(parser.errormessage); + } + throw new FileFormatException(emessage); + } + /** * Identify a datasource's file content. * @@@ -94,7 -55,7 +94,7 @@@ } } catch (Exception e) { - System.err.println("Error whilst identifying"); + System.err.println("Error whilst identifying " + file); e.printStackTrace(System.err); emessage = e.getMessage(); } @@@ -169,39 -130,26 +169,39 @@@ if (source.inFile != null) { String fileStr = source.inFile.getName(); - // possibly a Jalview archive. - if (fileStr.lastIndexOf(".jar") > -1 - || fileStr.lastIndexOf(".zip") > -1) + if (fileStr.contains(".jar") || fileStr.contains(".zip") + || fileStr.contains(".jvp")) { + // possibly a Jalview archive (but check further) reply = FileFormat.Jalview; } } if (!lineswereskipped && data.startsWith("PK")) { - reply = FileFormat.Jalview; // archive. + reply = FileFormat.Jalview; // archive break; } } - data = data.toUpperCase(); + data = data.toUpperCase(Locale.ROOT); if (data.startsWith(ScoreMatrixFile.SCOREMATRIX)) { reply = FileFormat.ScoreMatrix; break; } + if (data.startsWith("LOCUS")) + { + reply = FileFormat.GenBank; + break; + } + if (data.startsWith("ID ")) + { + if (data.substring(2).trim().split(";").length == 7) + { + reply = FileFormat.Embl; + break; + } + } if (data.startsWith("H ") && !aaIndexHeaderRead) { aaIndexHeaderRead = true; @@@ -336,7 -284,7 +336,7 @@@ if ((lessThan > -1)) // possible Markup Language data i.e HTML, // RNAML, XML { - String upper = data.toUpperCase(); + String upper = data.toUpperCase(Locale.ROOT); if (upper.substring(lessThan).startsWith(" [ ...]"); } } + } diff --combined src/jalview/jbgui/GAlignFrame.java index 4d2850b,2d3eaa4..870aa89 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@@ -21,20 -21,18 +21,20 @@@ package jalview.jbgui; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; +import jalview.analysis.GeneticCodeI; +import jalview.analysis.GeneticCodes; import jalview.api.SplitContainerI; import jalview.bin.Cache; import jalview.gui.JvSwingUtils; import jalview.gui.Preferences; import jalview.io.FileFormats; +import jalview.schemes.ResidueColourScheme; import jalview.util.MessageManager; import jalview.util.Platform; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; -import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; @@@ -61,18 -59,15 +61,18 @@@ import javax.swing.event.ChangeEvent import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; +@SuppressWarnings("serial") public class GAlignFrame extends JInternalFrame { protected JMenuBar alignFrameMenuBar = new JMenuBar(); protected JMenuItem closeMenuItem = new JMenuItem(); - protected JMenu webService = new JMenu(); + public JMenu webService = new JMenu();// BH 2019 was protected, but not + // sufficient for AlignFrame thread run - protected JMenuItem webServiceNoServices; + public JMenuItem webServiceNoServices;// BH 2019 was protected, but not + // sufficient for AlignFrame thread run protected JCheckBoxMenuItem viewBoxesMenuItem = new JCheckBoxMenuItem(); @@@ -80,9 -75,7 +80,9 @@@ protected JMenu sortByAnnotScore = new JMenu(); - public JLabel statusBar = new JLabel(); + public JLabel statusBar = new JLabel(); // BH 2019 was protected, but not + // sufficient for + // AlignFrame.printWriter protected JMenu outputTextboxMenu = new JMenu(); @@@ -130,7 -123,7 +130,7 @@@ protected JMenuItem modifyPID; - protected JMenuItem annotationColour; + protected JRadioButtonMenuItem annotationColour; protected JMenu sortByTreeMenu = new JMenu(); @@@ -144,7 -137,7 +144,7 @@@ protected JCheckBoxMenuItem showDbRefsMenuitem = new JCheckBoxMenuItem(); - protected JMenuItem showTranslation = new JMenuItem(); + protected JMenu showTranslation = new JMenu(); protected JMenuItem showReverse = new JMenuItem(); @@@ -154,8 -147,6 +154,8 @@@ protected JMenuItem runGroovy = new JMenuItem(); + protected JMenuItem loadVcf; + protected JCheckBoxMenuItem autoCalculate = new JCheckBoxMenuItem(); protected JCheckBoxMenuItem sortByTree = new JCheckBoxMenuItem(); @@@ -204,8 -195,6 +204,8 @@@ protected JCheckBoxMenuItem applyAutoAnnotationSettings = new JCheckBoxMenuItem(); + protected JMenuItem openFeatureSettings; + private SequenceAnnotationOrder annotationSortOrder; private boolean showAutoCalculatedAbove = false; @@@ -218,10 -207,6 +218,10 @@@ { try { + + // for Web-page embedding using id=align-frame-div + setName("jalview-alignment"); + jbInit(); setJMenuBar(alignFrameMenuBar); @@@ -235,7 -220,7 +235,7 @@@ @Override public void actionPerformed(ActionEvent e) { - outputText_actionPerformed(e); + outputText_actionPerformed(e.getActionCommand()); } }); @@@ -246,7 -231,7 +246,7 @@@ System.err.println(e.toString()); } - if (!Platform.isAMac()) + if (Platform.allowMnemonics()) // was "not mac and not JS" { closeMenuItem.setMnemonic('C'); outputTextboxMenu.setMnemonic('T'); @@@ -270,22 -255,20 +270,22 @@@ @Override public void actionPerformed(ActionEvent e) { - saveAs_actionPerformed(e); + saveAs_actionPerformed(); } }; // FIXME getDefaultToolkit throws an exception in Headless mode KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - | KeyEvent.SHIFT_MASK, + jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() + | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK, false); addMenuActionAndAccelerator(keyStroke, saveAs, al); closeMenuItem.setText(MessageManager.getString("action.close")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -308,9 -291,7 +308,9 @@@ JMenuItem selectAllSequenceMenuItem = new JMenuItem( MessageManager.getString("action.select_all")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -337,9 -318,7 +337,9 @@@ JMenuItem invertSequenceMenuItem = new JMenuItem( MessageManager.getString("action.invert_sequence_selection")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -375,9 -354,7 +375,9 @@@ JMenuItem remove2LeftMenuItem = new JMenuItem( MessageManager.getString("action.remove_left")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -391,9 -368,7 +391,9 @@@ JMenuItem remove2RightMenuItem = new JMenuItem( MessageManager.getString("action.remove_right")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -407,9 -382,7 +407,9 @@@ JMenuItem removeGappedColumnMenuItem = new JMenuItem( MessageManager.getString("action.remove_empty_columns")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -423,8 -396,8 +423,8 @@@ JMenuItem removeAllGapsMenuItem = new JMenuItem( MessageManager.getString("action.remove_all_gaps")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - | KeyEvent.SHIFT_MASK, + jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() + | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK, false); al = new ActionListener() { @@@ -531,9 -504,7 +531,9 @@@ JMenuItem removeRedundancyMenuItem = new JMenuItem( MessageManager.getString("action.remove_redundancy")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -708,9 -679,7 +708,9 @@@ undoMenuItem.setEnabled(false); undoMenuItem.setText(MessageManager.getString("action.undo")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -724,9 -693,7 +724,9 @@@ redoMenuItem.setEnabled(false); redoMenuItem.setText(MessageManager.getString("action.redo")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -750,9 -717,7 +750,9 @@@ JMenuItem printMenuItem = new JMenuItem( MessageManager.getString("action.print")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -778,9 -743,7 +778,9 @@@ JMenuItem findMenuItem = new JMenuItem( MessageManager.getString("action.find")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true, MessageManager.getString("label.find_tip"))); al = new ActionListener() @@@ -941,9 -904,7 +941,9 @@@ JMenuItem deleteGroups = new JMenuItem( MessageManager.getString("action.undefine_groups")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -968,9 -929,7 +968,9 @@@ JMenuItem createGroup = new JMenuItem( MessageManager.getString("action.create_group")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -984,8 -943,8 +984,8 @@@ JMenuItem unGroup = new JMenuItem( MessageManager.getString("action.remove_group")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - | KeyEvent.SHIFT_MASK, + jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() + | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK, false); al = new ActionListener() { @@@ -999,31 -958,27 +999,31 @@@ copy.setText(MessageManager.getString("action.copy")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - copy_actionPerformed(e); + copy_actionPerformed(); } }; addMenuActionAndAccelerator(keyStroke, copy, al); cut.setText(MessageManager.getString("action.cut")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - cut_actionPerformed(e); + cut_actionPerformed(); } }; addMenuActionAndAccelerator(keyStroke, cut, al); @@@ -1035,7 -990,7 +1035,7 @@@ @Override public void actionPerformed(ActionEvent e) { - delete_actionPerformed(e); + delete_actionPerformed(); } }); @@@ -1043,8 -998,8 +1043,8 @@@ JMenuItem pasteNew = new JMenuItem( MessageManager.getString("label.to_new_alignment")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - | KeyEvent.SHIFT_MASK, + jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() + | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK, false); al = new ActionListener() { @@@ -1059,9 -1014,7 +1059,9 @@@ JMenuItem pasteThis = new JMenuItem( MessageManager.getString("label.to_this_alignment")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -1095,7 -1048,7 +1095,7 @@@ }); seqLimits.setText( MessageManager.getString("label.show_sequence_limits")); - seqLimits.setState(jalview.bin.Cache.getDefault("SHOW_JVSUFFIX", true)); + seqLimits.setState(Cache.getDefault("SHOW_JVSUFFIX", true)); seqLimits.addActionListener(new ActionListener() { @Override @@@ -1150,7 -1103,7 +1150,7 @@@ JMenuItem loadTreeBaseStudy = new JMenuItem( - MessageManager.getString("treebase study")); + MessageManager.getString("label.treebase_study")); loadTreeBaseStudy.addActionListener(new ActionListener() { @@@ -1164,7 -1117,7 +1164,7 @@@ JMenuItem loadTreeBase = new JMenuItem( - MessageManager.getString("treebase")); + MessageManager.getString("label.treebase")); loadTreeBase.addActionListener(new ActionListener() { @@@ -1178,7 -1131,7 +1178,7 @@@ }); JMenuItem loadTreePfam = new JMenuItem( - MessageManager.getString("pfam")); + MessageManager.getString("label.pfam")); loadTreePfam.addActionListener(new ActionListener() { @@@ -1191,7 -1144,7 +1191,7 @@@ }); JMenuItem loadTreeFam = new JMenuItem( - MessageManager.getString("treefam")); + MessageManager.getString("label.treefam")); loadTreeFam.addActionListener(new ActionListener() { @@@ -1205,7 -1158,7 +1205,7 @@@ }); JMenuItem loadTreeOfLife = new JMenuItem( - MessageManager.getString("tree_of_life")); + MessageManager.getString("label.tree_of_life")); loadTreeOfLife.addActionListener(new ActionListener() { @@@ -1327,7 -1280,8 +1327,7 @@@ .setText(MessageManager.getString("action.calculate_tree_pca")); padGapsMenuitem.setText(MessageManager.getString("label.pad_gaps")); - padGapsMenuitem - .setState(jalview.bin.Cache.getDefault("PAD_GAPS", false)); + padGapsMenuitem.setState(Cache.getDefault("PAD_GAPS", false)); padGapsMenuitem.addActionListener(new ActionListener() { @Override @@@ -1347,33 -1301,16 +1347,33 @@@ vamsasStore_actionPerformed(e); } }); + + /* + * Translate as cDNA with sub-menu of translation tables + */ showTranslation .setText(MessageManager.getString("label.translate_cDNA")); - showTranslation.addActionListener(new ActionListener() + boolean first = true; + for (final GeneticCodeI table : GeneticCodes.getInstance() + .getCodeTables()) { - @Override - public void actionPerformed(ActionEvent e) + JMenuItem item = new JMenuItem(table.getId() + " " + table.getName()); + showTranslation.add(item); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showTranslation_actionPerformed(table); + } + }); + if (first) { - showTranslation_actionPerformed(e); + showTranslation.addSeparator(); } - }); + first = false; + } + showReverse.setText(MessageManager.getString("label.reverse")); showReverse.addActionListener(new ActionListener() { @@@ -1422,7 -1359,7 +1422,7 @@@ } }); - JMenuItem openFeatureSettings = new JMenuItem( + openFeatureSettings = new JMenuItem( MessageManager.getString("action.feature_settings")); openFeatureSettings.addActionListener(new ActionListener() { @@@ -1432,10 -1369,6 +1432,10 @@@ featureSettings_actionPerformed(e); } }); + + /* + * add sub-menu of database we can fetch from + */ JMenuItem fetchSequence = new JMenuItem( MessageManager.getString("label.fetch_sequences")); fetchSequence.addActionListener(new ActionListener() @@@ -1443,7 -1376,7 +1443,7 @@@ @Override public void actionPerformed(ActionEvent e) { - fetchSequence_actionPerformed(e); + fetchSequence_actionPerformed(); } }); @@@ -1457,20 -1390,10 +1457,20 @@@ associatedData_actionPerformed(e); } }); + loadVcf = new JMenuItem( + MessageManager.getString("label.load_vcf_file")); + loadVcf.setToolTipText(MessageManager.getString("label.load_vcf")); + loadVcf.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + loadVcf_actionPerformed(); + } + }); autoCalculate.setText( MessageManager.getString("label.autocalculate_consensus")); - autoCalculate.setState( - jalview.bin.Cache.getDefault("AUTO_CALC_CONSENSUS", true)); + autoCalculate.setState(Cache.getDefault("AUTO_CALC_CONSENSUS", true)); autoCalculate.addActionListener(new ActionListener() { @Override @@@ -1483,7 -1406,8 +1483,7 @@@ MessageManager.getString("label.sort_alignment_new_tree")); sortByTree.setToolTipText("" + MessageManager.getString( "label.enable_automatically_sort_alignment_when_open_new_tree")); - sortByTree - .setState(jalview.bin.Cache.getDefault("SORT_BY_TREE", false)); + sortByTree.setState(Cache.getDefault("SORT_BY_TREE", false)); sortByTree.addActionListener(new ActionListener() { @Override @@@ -1657,8 -1581,8 +1657,8 @@@ JMenuItem invertColSel = new JMenuItem( MessageManager.getString("action.invert_column_selection")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - | KeyEvent.ALT_MASK, + jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() + | jalview.util.ShortcutKeyMaskExWrapper.ALT_DOWN_MASK, false); al = new ActionListener() { @@@ -1721,9 -1645,7 +1721,9 @@@ JMenuItem save = new JMenuItem(MessageManager.getString("action.save")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -1748,9 -1670,7 +1748,9 @@@ JMenuItem newView = new JMenuItem( MessageManager.getString("action.new_view")); keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + jalview.util.ShortcutKeyMaskExWrapper + .getMenuShortcutKeyMaskEx(), + false); al = new ActionListener() { @Override @@@ -1874,10 -1794,7 +1874,10 @@@ alignFrameMenuBar.add(formatMenu); alignFrameMenuBar.add(colourMenu); alignFrameMenuBar.add(calculateMenu); - alignFrameMenuBar.add(webService); + if (!Platform.isJS()) + { + alignFrameMenuBar.add(webService); + } fileMenu.add(fetchSequence); fileMenu.add(addSequenceMenu); @@@ -1895,10 -1812,6 +1895,10 @@@ fileMenu.add(exportAnnotations); fileMenu.add(loadTreeMenu); fileMenu.add(associatedData); + if (!Platform.isJS()) + { + fileMenu.add(loadVcf); + } fileMenu.addSeparator(); fileMenu.add(closeMenuItem); @@@ -1993,26 -1906,17 +1993,26 @@@ calculateMenu.addSeparator(); calculateMenu.add(expandAlignment); calculateMenu.add(extractScores); - calculateMenu.addSeparator(); - calculateMenu.add(runGroovy); + if (!Platform.isJS()) + { + calculateMenu.addSeparator(); + calculateMenu.add(runGroovy); + } webServiceNoServices = new JMenuItem( MessageManager.getString("label.no_services")); webService.add(webServiceNoServices); - exportImageMenu.add(htmlMenuItem); + if (!Platform.isJS()) + { + exportImageMenu.add(htmlMenuItem); + } exportImageMenu.add(epsFile); exportImageMenu.add(createPNG); - exportImageMenu.add(createBioJS); - exportImageMenu.add(createSVG); + if (!Platform.isJS()) + { + exportImageMenu.add(createBioJS); + exportImageMenu.add(createSVG); + } addSequenceMenu.add(addFromFile); addSequenceMenu.add(addFromText); addSequenceMenu.add(addFromURL); @@@ -2085,10 -1989,6 +2085,10 @@@ } + protected void loadVcf_actionPerformed() + { + } + /** * Constructs the entries on the Colour menu (but does not add them to the * menu). @@@ -2160,9 -2060,8 +2160,9 @@@ } }); - annotationColour = new JMenuItem( + annotationColour = new JRadioButtonMenuItem( MessageManager.getString("action.by_annotation")); + annotationColour.setName(ResidueColourScheme.ANNOTATION_COLOUR); annotationColour.addActionListener(new ActionListener() { @Override @@@ -2382,7 -2281,7 +2382,7 @@@ { } - protected void outputText_actionPerformed(ActionEvent e) + protected void outputText_actionPerformed(String formatName) { } @@@ -2548,15 -2447,15 +2548,15 @@@ { } - protected void copy_actionPerformed(ActionEvent e) + protected void copy_actionPerformed() { } - protected void cut_actionPerformed(ActionEvent e) + protected void cut_actionPerformed() { } - protected void delete_actionPerformed(ActionEvent e) + protected void delete_actionPerformed() { } @@@ -2647,7 -2546,7 +2647,7 @@@ { } - protected void saveAs_actionPerformed(ActionEvent e) + protected void saveAs_actionPerformed() { } @@@ -2665,7 -2564,7 +2665,7 @@@ } - public void showTranslation_actionPerformed(ActionEvent e) + public void showTranslation_actionPerformed(GeneticCodeI codeTable) { } @@@ -2675,7 -2574,7 +2675,7 @@@ } - public void fetchSequence_actionPerformed(ActionEvent e) + public void fetchSequence_actionPerformed() { } diff --combined src/jalview/project/Jalview2XML.java index d4b2c04,0000000..1a48a16 mode 100644,000000..100644 --- a/src/jalview/project/Jalview2XML.java +++ b/src/jalview/project/Jalview2XML.java @@@ -1,6540 -1,0 +1,6596 @@@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.project; + +import static jalview.math.RotatableMatrix.Axis.X; +import static jalview.math.RotatableMatrix.Axis.Y; +import static jalview.math.RotatableMatrix.Axis.Z; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; + +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.Marshaller; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + +import jalview.analysis.Conservation; +import jalview.analysis.PCA; +import jalview.analysis.scoremodels.ScoreModels; +import jalview.analysis.scoremodels.SimilarityParams; +import jalview.api.FeatureColourI; +import jalview.api.ViewStyleI; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.api.structures.JalviewStructureDisplayI; +import jalview.bin.Cache; +import jalview.bin.Console; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.GeneLocus; +import jalview.datamodel.GraphLine; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.Point; +import jalview.datamodel.RnaViewerModel; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.datamodel.StructureViewerModel; +import jalview.datamodel.StructureViewerModel.StructureData; +import jalview.datamodel.features.FeatureMatcher; +import jalview.datamodel.features.FeatureMatcherI; +import jalview.datamodel.features.FeatureMatcherSet; +import jalview.datamodel.features.FeatureMatcherSetI; ++import jalview.ext.archaeopteryx.AptxInit; ++import jalview.ext.treeviewer.TreeFrameI; ++import jalview.ext.treeviewer.TreeI; ++import jalview.ext.treeviewer.TreeViewerUtils; +import jalview.ext.varna.RnaModel; +import jalview.gui.AlignFrame; +import jalview.gui.AlignViewport; +import jalview.gui.AlignmentPanel; +import jalview.gui.AppVarna; +import jalview.gui.Desktop; +import jalview.gui.JvOptionPane; +import jalview.gui.OOMWarning; +import jalview.gui.PCAPanel; +import jalview.gui.PaintRefresher; +import jalview.gui.SplitFrame; +import jalview.gui.StructureViewer; +import jalview.gui.StructureViewer.ViewerType; +import jalview.gui.StructureViewerBase; +import jalview.gui.TreePanel; +import jalview.io.BackupFiles; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.NewickFile; +import jalview.math.Matrix; +import jalview.math.MatrixI; +import jalview.renderer.ResidueShaderI; +import jalview.schemes.AnnotationColourGradient; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.schemes.FeatureColour; +import jalview.schemes.ResidueProperties; +import jalview.schemes.UserColourScheme; +import jalview.structure.StructureSelectionManager; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.Format; +import jalview.util.HttpUtils; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.util.StringUtils; +import jalview.util.jarInputStreamProvider; +import jalview.util.matcher.Condition; +import jalview.viewmodel.AlignmentViewport; +import jalview.viewmodel.PCAModel; +import jalview.viewmodel.ViewportRanges; +import jalview.viewmodel.seqfeatures.FeatureRendererModel; +import jalview.viewmodel.seqfeatures.FeatureRendererSettings; +import jalview.viewmodel.seqfeatures.FeaturesDisplayed; +import jalview.ws.jws2.Jws2Discoverer; +import jalview.ws.jws2.dm.AAConSettings; +import jalview.ws.jws2.jabaws2.Jws2Instance; +import jalview.ws.params.ArgumentI; +import jalview.ws.params.AutoCalcSetting; +import jalview.ws.params.WsParamSetI; +import jalview.xml.binding.jalview.AlcodonFrame; +import jalview.xml.binding.jalview.AlcodonFrame.AlcodMap; +import jalview.xml.binding.jalview.Annotation; +import jalview.xml.binding.jalview.Annotation.ThresholdLine; +import jalview.xml.binding.jalview.AnnotationColourScheme; +import jalview.xml.binding.jalview.AnnotationElement; +import jalview.xml.binding.jalview.DoubleMatrix; +import jalview.xml.binding.jalview.DoubleVector; +import jalview.xml.binding.jalview.Feature; +import jalview.xml.binding.jalview.Feature.OtherData; +import jalview.xml.binding.jalview.FeatureMatcherSet.CompoundMatcher; +import jalview.xml.binding.jalview.FilterBy; +import jalview.xml.binding.jalview.JalviewModel; +import jalview.xml.binding.jalview.JalviewModel.FeatureSettings; +import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Group; +import jalview.xml.binding.jalview.JalviewModel.FeatureSettings.Setting; +import jalview.xml.binding.jalview.JalviewModel.JGroup; +import jalview.xml.binding.jalview.JalviewModel.JSeq; +import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids; +import jalview.xml.binding.jalview.JalviewModel.JSeq.Pdbids.StructureState; +import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer; +import jalview.xml.binding.jalview.JalviewModel.JSeq.RnaViewer.SecondaryStructure; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.Axis; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMax; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SeqPointMin; +import jalview.xml.binding.jalview.JalviewModel.PcaViewer.SequencePoint; +import jalview.xml.binding.jalview.JalviewModel.Tree; +import jalview.xml.binding.jalview.JalviewModel.UserColours; +import jalview.xml.binding.jalview.JalviewModel.Viewport; +import jalview.xml.binding.jalview.JalviewModel.Viewport.CalcIdParam; +import jalview.xml.binding.jalview.JalviewModel.Viewport.HiddenColumns; +import jalview.xml.binding.jalview.JalviewUserColours; +import jalview.xml.binding.jalview.JalviewUserColours.Colour; +import jalview.xml.binding.jalview.MapListType.MapListFrom; +import jalview.xml.binding.jalview.MapListType.MapListTo; +import jalview.xml.binding.jalview.Mapping; +import jalview.xml.binding.jalview.NoValueColour; +import jalview.xml.binding.jalview.ObjectFactory; +import jalview.xml.binding.jalview.PcaDataType; +import jalview.xml.binding.jalview.Pdbentry.Property; +import jalview.xml.binding.jalview.Sequence; +import jalview.xml.binding.jalview.Sequence.DBRef; +import jalview.xml.binding.jalview.SequenceSet; +import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties; +import jalview.xml.binding.jalview.ThresholdType; +import jalview.xml.binding.jalview.VAMSAS; + +/** + * Write out the current jalview desktop state as a Jalview XML stream. + * + * Note: the vamsas objects referred to here are primitive versions of the + * VAMSAS project schema elements - they are not the same and most likely never + * will be :) + * + * @author $author$ + * @version $Revision: 1.134 $ + */ +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_"; + + private static final String UTF_8 = "UTF-8"; + + /** + * prefix for recovering datasets for alignments with multiple views where + * non-existent dataset IDs were written for some views + */ + private static final String UNIQSEQSETID = "uniqueSeqSetId."; + + // use this with nextCounter() to make unique names for entities + private int counter = 0; + + /* + * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps + * of sequence objects are created. + */ + IdentityHashMap seqsToIds = null; + + /** + * jalview XML Sequence ID to jalview sequence object reference (both dataset + * and alignment sequences. Populated as XML reps of sequence objects are + * created.) + */ + Map seqRefIds = null; + + Map incompleteSeqs = null; + + List frefedSequence = null; + + boolean raiseGUI = true; // whether errors are raised in dialog boxes or not + + /* + * Map of reconstructed AlignFrame objects that appear to have come from + * SplitFrame objects (have a dna/protein complement view). + */ + private Map splitFrameCandidates = new HashMap<>(); + + /* + * Map from displayed rna structure models to their saved session state jar + * entry names + */ + private Map rnaSessions = new HashMap<>(); + + /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the boolean value, or false + * if null. + * + * @param b + * @return + */ + public static boolean safeBoolean(Boolean b) + { + return b == null ? false : b.booleanValue(); + } + + /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the integer value, or zero + * if null. + * + * @param i + * @return + */ + public static int safeInt(Integer i) + { + return i == null ? 0 : i.intValue(); + } + + /** + * A helper method for safely using the value of an optional attribute that + * may be null if not present in the XML. Answers the float value, or zero if + * null. + * + * @param f + * @return + */ + public static float safeFloat(Float f) + { + return f == null ? 0f : f.floatValue(); + } + + /** + * create/return unique hash string for sq + * + * @param sq + * @return new or existing unique string for sq + */ + String seqHash(SequenceI sq) + { + if (seqsToIds == null) + { + initSeqRefs(); + } + if (seqsToIds.containsKey(sq)) + { + return seqsToIds.get(sq); + } + else + { + // create sequential key + String key = "sq" + (seqsToIds.size() + 1); + key = makeHashCode(sq, key); // check we don't have an external reference + // for it already. + seqsToIds.put(sq, key); + return key; + } + } + + void initSeqRefs() + { + if (seqsToIds == null) + { + seqsToIds = new IdentityHashMap<>(); + } + if (seqRefIds == null) + { + seqRefIds = new HashMap<>(); + } + if (incompleteSeqs == null) + { + incompleteSeqs = new HashMap<>(); + } + if (frefedSequence == null) + { + frefedSequence = new ArrayList<>(); + } + } + + public Jalview2XML() + { + } + + public Jalview2XML(boolean raiseGUI) + { + this.raiseGUI = raiseGUI; + } + + /** + * base class for resolving forward references to sequences by their ID + * + * @author jprocter + * + */ + abstract class SeqFref + { + String sref; + + String type; + + public SeqFref(String _sref, String type) + { + sref = _sref; + this.type = type; + } + + public String getSref() + { + return sref; + } + + public SequenceI getSrefSeq() + { + return seqRefIds.get(sref); + } + + public boolean isResolvable() + { + return seqRefIds.get(sref) != null; + } + + public SequenceI getSrefDatasetSeq() + { + SequenceI sq = seqRefIds.get(sref); + if (sq != null) + { + while (sq.getDatasetSequence() != null) + { + sq = sq.getDatasetSequence(); + } + } + return sq; + } + + /** + * @return true if the forward reference was fully resolved + */ + abstract boolean resolve(); + + @Override + public String toString() + { + return type + " reference to " + sref; + } + } + + /** + * create forward reference for a mapping + * + * @param sref + * @param _jmap + * @return + */ + public SeqFref newMappingRef(final String sref, + final jalview.datamodel.Mapping _jmap) + { + SeqFref fref = new SeqFref(sref, "Mapping") + { + public jalview.datamodel.Mapping jmap = _jmap; + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + jmap.setTo(seq); + return true; + } + }; + return fref; + } + + public SeqFref newAlcodMapRef(final String sref, + final AlignedCodonFrame _cf, + final jalview.datamodel.Mapping _jmap) + { + + SeqFref fref = new SeqFref(sref, "Codon Frame") + { + AlignedCodonFrame cf = _cf; + + public jalview.datamodel.Mapping mp = _jmap; + + @Override + public boolean isResolvable() + { + return super.isResolvable() && mp.getTo() != null; + } + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + cf.addMap(seq, mp.getTo(), mp.getMap()); + return true; + } + }; + return fref; + } + + public void resolveFrefedSequences() + { + Iterator nextFref = frefedSequence.iterator(); + int toresolve = frefedSequence.size(); + int unresolved = 0, failedtoresolve = 0; + while (nextFref.hasNext()) + { + SeqFref ref = nextFref.next(); + if (ref.isResolvable()) + { + try + { + if (ref.resolve()) + { + nextFref.remove(); + } + else + { + failedtoresolve++; + } + } catch (Exception x) + { + System.err.println( + "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence " + + ref.getSref()); + x.printStackTrace(); + failedtoresolve++; + } + } + else + { + unresolved++; + } + } + if (unresolved > 0) + { + System.err.println("Jalview Project Import: There were " + unresolved + + " forward references left unresolved on the stack."); + } + if (failedtoresolve > 0) + { + System.err.println("SERIOUS! " + failedtoresolve + + " resolvable forward references failed to resolve."); + } + if (incompleteSeqs != null && incompleteSeqs.size() > 0) + { + System.err.println( + "Jalview Project Import: There are " + incompleteSeqs.size() + + " sequences which may have incomplete metadata."); + if (incompleteSeqs.size() < 10) + { + for (SequenceI s : incompleteSeqs.values()) + { + System.err.println(s.toString()); + } + } + else + { + System.err.println( + "Too many to report. Skipping output of incomplete sequences."); + } + } + } + + /** + * This maintains a map of viewports, the key being the seqSetId. Important to + * set historyItem and redoList for multiple views + */ + Map viewportsAdded = new HashMap<>(); + + Map annotationIds = new HashMap<>(); + + String uniqueSetSuffix = ""; + + /** + * List of pdbfiles added to Jar + */ + List pdbfiles = null; + + // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE + public void saveState(File statefile) + { + FileOutputStream fos = null; + + try + { + + fos = new FileOutputStream(statefile); + + JarOutputStream jout = new JarOutputStream(fos); + saveState(jout); + fos.close(); + + } catch (Exception e) + { + Console.error("Couln't write Jalview state to " + statefile, e); + // TODO: inform user of the problem - they need to know if their data was + // not saved ! + if (errorMessage == null) + { + errorMessage = "Did't write Jalview Archive to output file '" + + statefile + "' - See console error log for details"; + } + else + { + errorMessage += "(Didn't write Jalview Archive to output file '" + + statefile + ")"; + } + e.printStackTrace(); + } finally + { + if (fos != null) + { + try + { + fos.close(); + } catch (IOException e) + { + // ignore + } + } + } + reportErrors(); + } + + /** + * Writes a jalview project archive to the given Jar output stream. + * + * @param jout + */ + public void saveState(JarOutputStream jout) + { + AlignFrame[] frames = Desktop.getAlignFrames(); + + if (frames == null) + { + return; + } + saveAllFrames(Arrays.asList(frames), jout); + } + + /** + * core method for storing state for a set of AlignFrames. + * + * @param frames + * - frames involving all data to be exported (including containing + * splitframes) + * @param jout + * - project output stream + */ + private void saveAllFrames(List frames, JarOutputStream jout) + { + Hashtable dsses = new Hashtable<>(); + + /* + * ensure cached data is clear before starting + */ + // todo tidy up seqRefIds, seqsToIds initialisation / reset + rnaSessions.clear(); + splitFrameCandidates.clear(); + + try + { + + // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS + // ////////////////////////////////////////////////// + + List shortNames = new ArrayList<>(); + List viewIds = new ArrayList<>(); + + // REVERSE ORDER + for (int i = frames.size() - 1; i > -1; i--) + { + AlignFrame af = frames.get(i); + // skip ? + if (skipList != null && skipList + .containsKey(af.getViewport().getSequenceSetId())) + { + continue; + } + + String shortName = makeFilename(af, shortNames); + + int apSize = af.getAlignPanels().size(); + + for (int ap = 0; ap < apSize; ap++) + { + AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels() + .get(ap); + String fileName = apSize == 1 ? shortName : ap + shortName; + if (!fileName.endsWith(".xml")) + { + fileName = fileName + ".xml"; + } + + saveState(apanel, fileName, jout, viewIds); + + String dssid = getDatasetIdRef( + af.getViewport().getAlignment().getDataset()); + if (!dsses.containsKey(dssid)) + { + dsses.put(dssid, af); + } + } + } + + writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix, + jout); + + try + { + jout.flush(); + } catch (Exception foo) + { + } + jout.close(); + } catch (Exception ex) + { + // TODO: inform user of the problem - they need to know if their data was + // not saved ! + if (errorMessage == null) + { + errorMessage = "Couldn't write Jalview Archive - see error output for details"; + } + ex.printStackTrace(); + } + } + + /** + * Generates a distinct file name, based on the title of the AlignFrame, by + * appending _n for increasing n until an unused name is generated. The new + * name (without its extension) is added to the list. + * + * @param af + * @param namesUsed + * @return the generated name, with .xml extension + */ + protected String makeFilename(AlignFrame af, List namesUsed) + { + String shortName = af.getTitle(); + + if (shortName.indexOf(File.separatorChar) > -1) + { + shortName = shortName + .substring(shortName.lastIndexOf(File.separatorChar) + 1); + } + + int count = 1; + + while (namesUsed.contains(shortName)) + { + if (shortName.endsWith("_" + (count - 1))) + { + shortName = shortName.substring(0, shortName.lastIndexOf("_")); + } + + shortName = shortName.concat("_" + count); + count++; + } + + namesUsed.add(shortName); + + if (!shortName.endsWith(".xml")) + { + shortName = shortName + ".xml"; + } + return shortName; + } + + // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW + public boolean saveAlignment(AlignFrame af, String jarFile, + String fileName) + { + try + { + // create backupfiles object and get new temp filename destination + 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 frames = new ArrayList<>(); + + // resolve splitframes + if (af.getViewport().getCodingComplement() != null) + { + frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames(); + } + else + { + frames.add(af); + } + saveAllFrames(frames, jout); + try + { + jout.flush(); + } catch (Exception foo) + { + } + jout.close(); + boolean success = true; + + if (doBackup) + { + backupfiles.setWriteSuccess(success); + success = backupfiles.rollBackupsAndRenameTempFile(); + } + + return success; + } catch (Exception ex) + { + errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details"; + ex.printStackTrace(); + return false; + } + } + + private void writeDatasetFor(Hashtable dsses, + String fileName, JarOutputStream jout) + { + + for (String dssids : dsses.keySet()) + { + AlignFrame _af = dsses.get(dssids); + String jfileName = fileName + " Dataset for " + _af.getTitle(); + if (!jfileName.endsWith(".xml")) + { + jfileName = jfileName + ".xml"; + } + saveState(_af.alignPanel, jfileName, true, jout, null); + } + } + + /** + * create a JalviewModel from an alignment view and marshall it to a + * JarOutputStream + * + * @param ap + * panel to create jalview model for + * @param fileName + * name of alignment panel written to output stream + * @param jout + * jar output stream + * @param viewIds + * @param out + * jar entry name + */ + public JalviewModel saveState(AlignmentPanel ap, String fileName, + JarOutputStream jout, List viewIds) + { + return saveState(ap, fileName, false, jout, viewIds); + } + + /** + * create a JalviewModel from an alignment view and marshall it to a + * JarOutputStream + * + * @param ap + * panel to create jalview model for + * @param fileName + * name of alignment panel written to output stream + * @param storeDS + * when true, only write the dataset for the alignment, not the data + * associated with the view. + * @param jout + * jar output stream + * @param out + * jar entry name + */ + public JalviewModel saveState(AlignmentPanel ap, String fileName, + boolean storeDS, JarOutputStream jout, List viewIds) + { + if (viewIds == null) + { + viewIds = new ArrayList<>(); + } + + initSeqRefs(); + + List userColours = new ArrayList<>(); + + AlignViewport av = ap.av; + ViewportRanges vpRanges = av.getRanges(); + + final ObjectFactory objectFactory = new ObjectFactory(); + JalviewModel object = objectFactory.createJalviewModel(); + object.setVamsasModel(new VAMSAS()); + + // object.setCreationDate(new java.util.Date(System.currentTimeMillis())); + try + { + GregorianCalendar c = new GregorianCalendar(); + DatatypeFactory datatypeFactory = DatatypeFactory.newInstance(); + XMLGregorianCalendar now = datatypeFactory.newXMLGregorianCalendar(c);// gregorianCalendar); + object.setCreationDate(now); + } catch (DatatypeConfigurationException e) + { + System.err.println("error writing date: " + e.toString()); + } + object.setVersion(Cache.getDefault("VERSION", "Development Build")); + + /** + * rjal is full height alignment, jal is actual alignment with full metadata + * but excludes hidden sequences. + */ + jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal; + + if (av.hasHiddenRows()) + { + rjal = jal.getHiddenSequences().getFullAlignment(); + } + + SequenceSet vamsasSet = new SequenceSet(); + Sequence vamsasSeq; + // JalviewModelSequence jms = new JalviewModelSequence(); + + vamsasSet.setGapChar(jal.getGapCharacter() + ""); + + if (jal.getDataset() != null) + { + // dataset id is the dataset's hashcode + vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset())); + if (storeDS) + { + // switch jal and the dataset + jal = jal.getDataset(); + rjal = jal; + } + } + if (jal.getProperties() != null) + { + Enumeration en = jal.getProperties().keys(); + while (en.hasMoreElements()) + { + String key = en.nextElement().toString(); + SequenceSetProperties ssp = new SequenceSetProperties(); + ssp.setKey(key); + ssp.setValue(jal.getProperties().get(key).toString()); + // vamsasSet.addSequenceSetProperties(ssp); + vamsasSet.getSequenceSetProperties().add(ssp); + } + } + + JSeq jseq; + Set calcIdSet = new HashSet<>(); + // record the set of vamsas sequence XML POJO we create. + HashMap vamsasSetIds = new HashMap<>(); + // SAVE SEQUENCES + for (final SequenceI jds : rjal.getSequences()) + { + final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds + : jds.getDatasetSequence(); + String id = seqHash(jds); + if (vamsasSetIds.get(id) == null) + { + if (seqRefIds.get(id) != null && !storeDS) + { + // This happens for two reasons: 1. multiple views are being + // serialised. + // 2. the hashCode has collided with another sequence's code. This + // DOES + // HAPPEN! (PF00072.15.stk does this) + // JBPNote: Uncomment to debug writing out of files that do not read + // back in due to ArrayOutOfBoundExceptions. + // System.err.println("vamsasSeq backref: "+id+""); + // System.err.println(jds.getName()+" + // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString()); + // System.err.println("Hashcode: "+seqHash(jds)); + // SequenceI rsq = (SequenceI) seqRefIds.get(id + ""); + // System.err.println(rsq.getName()+" + // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString()); + // System.err.println("Hashcode: "+seqHash(rsq)); + } + else + { + vamsasSeq = createVamsasSequence(id, jds); + // vamsasSet.addSequence(vamsasSeq); + vamsasSet.getSequence().add(vamsasSeq); + vamsasSetIds.put(id, vamsasSeq); + seqRefIds.put(id, jds); + } + } + jseq = new JSeq(); + jseq.setStart(jds.getStart()); + jseq.setEnd(jds.getEnd()); + jseq.setColour(av.getSequenceColour(jds).getRGB()); + + jseq.setId(id); // jseq id should be a string not a number + if (!storeDS) + { + // Store any sequences this sequence represents + if (av.hasHiddenRows()) + { + // use rjal, contains the full height alignment + jseq.setHidden( + av.getAlignment().getHiddenSequences().isHidden(jds)); + + if (av.isHiddenRepSequence(jds)) + { + jalview.datamodel.SequenceI[] reps = av + .getRepresentedSequences(jds).getSequencesInOrder(rjal); + + for (int h = 0; h < reps.length; h++) + { + if (reps[h] != jds) + { + // jseq.addHiddenSequences(rjal.findIndex(reps[h])); + jseq.getHiddenSequences().add(rjal.findIndex(reps[h])); + } + } + } + } + // mark sequence as reference - if it is the reference for this view + if (jal.hasSeqrep()) + { + jseq.setViewreference(jds == jal.getSeqrep()); + } + } + + // TODO: omit sequence features from each alignment view's XML dump if we + // are storing dataset + List sfs = jds.getSequenceFeatures(); + for (SequenceFeature sf : sfs) + { + // Features features = new Features(); + Feature features = new Feature(); + + features.setBegin(sf.getBegin()); + features.setEnd(sf.getEnd()); + features.setDescription(sf.getDescription()); + features.setType(sf.getType()); + features.setFeatureGroup(sf.getFeatureGroup()); + features.setScore(sf.getScore()); + if (sf.links != null) + { + for (int l = 0; l < sf.links.size(); l++) + { + OtherData keyValue = new OtherData(); + keyValue.setKey("LINK_" + l); + keyValue.setValue(sf.links.elementAt(l).toString()); + // features.addOtherData(keyValue); + features.getOtherData().add(keyValue); + } + } + if (sf.otherDetails != null) + { + /* + * save feature attributes, which may be simple strings or + * map valued (have sub-attributes) + */ + for (Entry entry : sf.otherDetails.entrySet()) + { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map) + { + for (Entry subAttribute : ((Map) value) + .entrySet()) + { + OtherData otherData = new OtherData(); + otherData.setKey(key); + otherData.setKey2(subAttribute.getKey()); + otherData.setValue(subAttribute.getValue().toString()); + // features.addOtherData(otherData); + features.getOtherData().add(otherData); + } + } + else + { + OtherData otherData = new OtherData(); + otherData.setKey(key); + otherData.setValue(value.toString()); + // features.addOtherData(otherData); + features.getOtherData().add(otherData); + } + } + } + + // jseq.addFeatures(features); + jseq.getFeatures().add(features); + } + + if (jdatasq.getAllPDBEntries() != null) + { + Enumeration en = jdatasq.getAllPDBEntries().elements(); + while (en.hasMoreElements()) + { + Pdbids pdb = new Pdbids(); + jalview.datamodel.PDBEntry entry = en.nextElement(); + + String pdbId = entry.getId(); + pdb.setId(pdbId); + pdb.setType(entry.getType()); + + /* + * Store any structure views associated with this sequence. This + * section copes with duplicate entries in the project, so a dataset + * only view *should* be coped with sensibly. + */ + // This must have been loaded, is it still visible? + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + String matchedFile = null; + for (int f = frames.length - 1; f > -1; f--) + { + if (frames[f] instanceof StructureViewerBase) + { + StructureViewerBase viewFrame = (StructureViewerBase) frames[f]; + matchedFile = saveStructureViewer(ap, jds, pdb, entry, + viewIds, matchedFile, viewFrame); + /* + * Only store each structure viewer's state once in the project + * jar. First time through only (storeDS==false) + */ + String viewId = viewFrame.getViewId(); + String viewerType = viewFrame.getViewerType().toString(); + if (!storeDS && !viewIds.contains(viewId)) + { + viewIds.add(viewId); + File viewerState = viewFrame.saveSession(); + if (viewerState != null) + { + copyFileToJar(jout, viewerState.getPath(), + getViewerJarEntryName(viewId), viewerType); + } + else + { + Console.error( + "Failed to save viewer state for " + viewerType); + } + } + } + } + + if (matchedFile != null || entry.getFile() != null) + { + if (entry.getFile() != null) + { + // use entry's file + matchedFile = entry.getFile(); + } + pdb.setFile(matchedFile); // entry.getFile()); + if (pdbfiles == null) + { + pdbfiles = new ArrayList<>(); + } + + if (!pdbfiles.contains(pdbId)) + { + pdbfiles.add(pdbId); + copyFileToJar(jout, matchedFile, pdbId, pdbId); + } + } + + Enumeration props = entry.getProperties(); + if (props.hasMoreElements()) + { + // PdbentryItem item = new PdbentryItem(); + while (props.hasMoreElements()) + { + Property prop = new Property(); + String key = props.nextElement(); + prop.setName(key); + prop.setValue(entry.getProperty(key).toString()); + // item.addProperty(prop); + pdb.getProperty().add(prop); + } + // pdb.addPdbentryItem(item); + } + + // jseq.addPdbids(pdb); + jseq.getPdbids().add(pdb); + } + } + + saveRnaViewers(jout, jseq, jds, viewIds, ap, storeDS); + + // jms.addJSeq(jseq); + object.getJSeq().add(jseq); + } + + if (!storeDS && av.hasHiddenRows()) + { + jal = av.getAlignment(); + } + // SAVE MAPPINGS + // FOR DATASET + if (storeDS && jal.getCodonFrames() != null) + { + List jac = jal.getCodonFrames(); + for (AlignedCodonFrame acf : jac) + { + AlcodonFrame alc = new AlcodonFrame(); + if (acf.getProtMappings() != null + && acf.getProtMappings().length > 0) + { + boolean hasMap = false; + SequenceI[] dnas = acf.getdnaSeqs(); + jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); + for (int m = 0; m < pmaps.length; m++) + { + AlcodMap alcmap = new AlcodMap(); + alcmap.setDnasq(seqHash(dnas[m])); + alcmap.setMapping( + createVamsasMapping(pmaps[m], dnas[m], null, false)); + // alc.addAlcodMap(alcmap); + alc.getAlcodMap().add(alcmap); + hasMap = true; + } + if (hasMap) + { + // vamsasSet.addAlcodonFrame(alc); + vamsasSet.getAlcodonFrame().add(alc); + } + } + // TODO: delete this ? dead code from 2.8.3->2.9 ? + // { + // AlcodonFrame alc = new AlcodonFrame(); + // vamsasSet.addAlcodonFrame(alc); + // for (int p = 0; p < acf.aaWidth; p++) + // { + // Alcodon cmap = new Alcodon(); + // if (acf.codons[p] != null) + // { + // // Null codons indicate a gapped column in the translated peptide + // // alignment. + // cmap.setPos1(acf.codons[p][0]); + // cmap.setPos2(acf.codons[p][1]); + // cmap.setPos3(acf.codons[p][2]); + // } + // alc.addAlcodon(cmap); + // } + // if (acf.getProtMappings() != null + // && acf.getProtMappings().length > 0) + // { + // SequenceI[] dnas = acf.getdnaSeqs(); + // jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); + // for (int m = 0; m < pmaps.length; m++) + // { + // AlcodMap alcmap = new AlcodMap(); + // alcmap.setDnasq(seqHash(dnas[m])); + // alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null, + // false)); + // alc.addAlcodMap(alcmap); + // } + // } + } + } + + // SAVE TREES + // ///////////////////////////////// + if (!storeDS && av.getCurrentTree() != null) + { + // FIND ANY ASSOCIATED TREES + // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT + if (Desktop.desktop != null) + { + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + + for (int t = 0; t < frames.length; t++) + { + if (frames[t] instanceof TreePanel) + { + TreePanel tp = (TreePanel) frames[t]; + + if (tp.getTreeCanvas().getViewport().getAlignment() == jal) + { + JalviewModel.Tree tree = new JalviewModel.Tree(); + tree.setTitle(tp.getTitle()); + tree.setCurrentTree((av.getCurrentTree() == tp.getTree())); + tree.setNewick(tp.getTree().print()); + tree.setThreshold(tp.getTreeCanvas().getThreshold()); + + tree.setFitToWindow(tp.fitToWindow.getState()); + tree.setFontName(tp.getTreeFont().getName()); + tree.setFontSize(tp.getTreeFont().getSize()); + tree.setFontStyle(tp.getTreeFont().getStyle()); + tree.setMarkUnlinked(tp.placeholdersMenu.getState()); + + tree.setShowBootstrap(tp.bootstrapMenu.getState()); + tree.setShowDistances(tp.distanceMenu.getState()); + + tree.setHeight(tp.getHeight()); + tree.setWidth(tp.getWidth()); + tree.setXpos(tp.getX()); + tree.setYpos(tp.getY()); + tree.setId(makeHashCode(tp, null)); + tree.setLinkToAllViews( + tp.getTreeCanvas().isApplyToAllViews()); + + // jms.addTree(tree); + object.getTree().add(tree); + } + } ++ + } + } + } ++ if (!storeDS && av.getCurrentExtTree() != null) ++ { ++ Set externalTreeViews = TreeViewerUtils ++ .getActiveTreeViews() ++ .keySet(); ++ for (TreeFrameI treeView : externalTreeViews) ++ { ++ TreeI tree = treeView.getTree(); ++ try ++ { ++ tree.outputAsFile(new File("word")); ++ copyFileToJar(jout, "word", "aptx-test","Archeopteryx Tree Session"); ++ ++ ++ } catch (IOException e) ++ { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ ++ } ++ ++ } ++ ++ + + /* + * save PCA viewers + */ + if (!storeDS && Desktop.desktop != null) + { + for (JInternalFrame frame : Desktop.desktop.getAllFrames()) + { + if (frame instanceof PCAPanel) + { + PCAPanel panel = (PCAPanel) frame; + if (panel.getAlignViewport().getAlignment() == jal) + { + savePCA(panel, object); + } + } + } + } + + // SAVE ANNOTATIONS + /** + * store forward refs from an annotationRow to any groups + */ + IdentityHashMap groupRefs = new IdentityHashMap<>(); + if (storeDS) + { + for (SequenceI sq : jal.getSequences()) + { + // Store annotation on dataset sequences only + AlignmentAnnotation[] aa = sq.getAnnotation(); + if (aa != null && aa.length > 0) + { + storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS, + vamsasSet); + } + } + } + else + { + if (jal.getAlignmentAnnotation() != null) + { + // Store the annotation shown on the alignment. + AlignmentAnnotation[] aa = jal.getAlignmentAnnotation(); + storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS, + vamsasSet); + } + } + // SAVE GROUPS + if (jal.getGroups() != null) + { + JGroup[] groups = new JGroup[jal.getGroups().size()]; + int i = -1; + for (jalview.datamodel.SequenceGroup sg : jal.getGroups()) + { + JGroup jGroup = new JGroup(); + groups[++i] = jGroup; + + jGroup.setStart(sg.getStartRes()); + jGroup.setEnd(sg.getEndRes()); + jGroup.setName(sg.getName()); + if (groupRefs.containsKey(sg)) + { + // group has references so set its ID field + jGroup.setId(groupRefs.get(sg)); + } + ColourSchemeI colourScheme = sg.getColourScheme(); + if (colourScheme != null) + { + ResidueShaderI groupColourScheme = sg.getGroupColourScheme(); + if (groupColourScheme.conservationApplied()) + { + jGroup.setConsThreshold(groupColourScheme.getConservationInc()); + + if (colourScheme instanceof jalview.schemes.UserColourScheme) + { + jGroup.setColour(setUserColourScheme(colourScheme, + userColours, object)); + } + else + { + jGroup.setColour(colourScheme.getSchemeName()); + } + } + else if (colourScheme instanceof jalview.schemes.AnnotationColourGradient) + { + jGroup.setColour("AnnotationColourGradient"); + jGroup.setAnnotationColours(constructAnnotationColours( + (jalview.schemes.AnnotationColourGradient) colourScheme, + userColours, object)); + } + else if (colourScheme instanceof jalview.schemes.UserColourScheme) + { + jGroup.setColour( + setUserColourScheme(colourScheme, userColours, object)); + } + else + { + jGroup.setColour(colourScheme.getSchemeName()); + } + + jGroup.setPidThreshold(groupColourScheme.getThreshold()); + } + + jGroup.setOutlineColour(sg.getOutlineColour().getRGB()); + jGroup.setDisplayBoxes(sg.getDisplayBoxes()); + jGroup.setDisplayText(sg.getDisplayText()); + jGroup.setColourText(sg.getColourText()); + jGroup.setTextCol1(sg.textColour.getRGB()); + jGroup.setTextCol2(sg.textColour2.getRGB()); + jGroup.setTextColThreshold(sg.thresholdTextColour); + jGroup.setShowUnconserved(sg.getShowNonconserved()); + jGroup.setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus()); + jGroup.setShowConsensusHistogram(sg.isShowConsensusHistogram()); + jGroup.setShowSequenceLogo(sg.isShowSequenceLogo()); + jGroup.setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo()); + for (SequenceI seq : sg.getSequences()) + { + // jGroup.addSeq(seqHash(seq)); + jGroup.getSeq().add(seqHash(seq)); + } + } + + // jms.setJGroup(groups); + Object group; + for (JGroup grp : groups) + { + object.getJGroup().add(grp); + } + } + if (!storeDS) + { + // /////////SAVE VIEWPORT + Viewport view = new Viewport(); + view.setTitle(ap.alignFrame.getTitle()); + view.setSequenceSetId( + makeHashCode(av.getSequenceSetId(), av.getSequenceSetId())); + view.setId(av.getViewId()); + if (av.getCodingComplement() != null) + { + view.setComplementId(av.getCodingComplement().getViewId()); + } + view.setViewName(av.getViewName()); + view.setGatheredViews(av.isGatherViewsHere()); + + Rectangle size = ap.av.getExplodedGeometry(); + Rectangle position = size; + if (size == null) + { + size = ap.alignFrame.getBounds(); + if (av.getCodingComplement() != null) + { + position = ((SplitFrame) ap.alignFrame.getSplitViewContainer()) + .getBounds(); + } + else + { + position = size; + } + } + view.setXpos(position.x); + view.setYpos(position.y); + + view.setWidth(size.width); + view.setHeight(size.height); + + view.setStartRes(vpRanges.getStartRes()); + view.setStartSeq(vpRanges.getStartSeq()); + + if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme) + { + view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(), + userColours, object)); + } + else if (av + .getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient) + { + AnnotationColourScheme ac = constructAnnotationColours( + (jalview.schemes.AnnotationColourGradient) av + .getGlobalColourScheme(), + userColours, object); + + view.setAnnotationColours(ac); + view.setBgColour("AnnotationColourGradient"); + } + else + { + view.setBgColour(ColourSchemeProperty + .getColourName(av.getGlobalColourScheme())); + } + + ResidueShaderI vcs = av.getResidueShading(); + ColourSchemeI cs = av.getGlobalColourScheme(); + + if (cs != null) + { + if (vcs.conservationApplied()) + { + view.setConsThreshold(vcs.getConservationInc()); + if (cs instanceof jalview.schemes.UserColourScheme) + { + view.setBgColour(setUserColourScheme(cs, userColours, object)); + } + } + view.setPidThreshold(vcs.getThreshold()); + } + + view.setConservationSelected(av.getConservationSelected()); + view.setPidSelected(av.getAbovePIDThreshold()); + final Font font = av.getFont(); + view.setFontName(font.getName()); + view.setFontSize(font.getSize()); + view.setFontStyle(font.getStyle()); + view.setScaleProteinAsCdna(av.getViewStyle().isScaleProteinAsCdna()); + view.setRenderGaps(av.isRenderGaps()); + view.setShowAnnotation(av.isShowAnnotation()); + view.setShowBoxes(av.getShowBoxes()); + view.setShowColourText(av.getColourText()); + view.setShowFullId(av.getShowJVSuffix()); + view.setRightAlignIds(av.isRightAlignIds()); + view.setShowSequenceFeatures(av.isShowSequenceFeatures()); + view.setShowText(av.getShowText()); + view.setShowUnconserved(av.getShowUnconserved()); + view.setWrapAlignment(av.getWrapAlignment()); + view.setTextCol1(av.getTextColour().getRGB()); + view.setTextCol2(av.getTextColour2().getRGB()); + view.setTextColThreshold(av.getThresholdTextColour()); + view.setShowConsensusHistogram(av.isShowConsensusHistogram()); + view.setShowSequenceLogo(av.isShowSequenceLogo()); + view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo()); + view.setShowGroupConsensus(av.isShowGroupConsensus()); + view.setShowGroupConservation(av.isShowGroupConservation()); + view.setShowNPfeatureTooltip(av.isShowNPFeats()); + view.setShowDbRefTooltip(av.isShowDBRefs()); + view.setFollowHighlight(av.isFollowHighlight()); + view.setFollowSelection(av.followSelection); + view.setIgnoreGapsinConsensus(av.isIgnoreGapsConsensus()); + view.setShowComplementFeatures(av.isShowComplementFeatures()); + view.setShowComplementFeaturesOnTop( + av.isShowComplementFeaturesOnTop()); + if (av.getFeaturesDisplayed() != null) + { + FeatureSettings fs = new FeatureSettings(); + + FeatureRendererModel fr = ap.getSeqPanel().seqCanvas + .getFeatureRenderer(); + String[] renderOrder = fr.getRenderOrder().toArray(new String[0]); + + Vector settingsAdded = new Vector<>(); + if (renderOrder != null) + { + for (String featureType : renderOrder) + { + FeatureSettings.Setting setting = new FeatureSettings.Setting(); + setting.setType(featureType); + + /* + * save any filter for the feature type + */ + FeatureMatcherSetI filter = fr.getFeatureFilter(featureType); + if (filter != null) + { + Iterator filters = filter.getMatchers() + .iterator(); + FeatureMatcherI firstFilter = filters.next(); + setting.setMatcherSet(Jalview2XML.marshalFilter(firstFilter, + filters, filter.isAnded())); + } + + /* + * save colour scheme for the feature type + */ + FeatureColourI fcol = fr.getFeatureStyle(featureType); + if (!fcol.isSimpleColour()) + { + setting.setColour(fcol.getMaxColour().getRGB()); + setting.setMincolour(fcol.getMinColour().getRGB()); + setting.setMin(fcol.getMin()); + setting.setMax(fcol.getMax()); + setting.setColourByLabel(fcol.isColourByLabel()); + if (fcol.isColourByAttribute()) + { + String[] attName = fcol.getAttributeName(); + setting.getAttributeName().add(attName[0]); + if (attName.length > 1) + { + setting.getAttributeName().add(attName[1]); + } + } + setting.setAutoScale(fcol.isAutoScaled()); + setting.setThreshold(fcol.getThreshold()); + Color noColour = fcol.getNoColour(); + if (noColour == null) + { + setting.setNoValueColour(NoValueColour.NONE); + } + else if (noColour.equals(fcol.getMaxColour())) + { + setting.setNoValueColour(NoValueColour.MAX); + } + else + { + setting.setNoValueColour(NoValueColour.MIN); + } + // -1 = No threshold, 0 = Below, 1 = Above + setting.setThreshstate(fcol.isAboveThreshold() ? 1 + : (fcol.isBelowThreshold() ? 0 : -1)); + } + else + { + setting.setColour(fcol.getColour().getRGB()); + } + + setting.setDisplay( + av.getFeaturesDisplayed().isVisible(featureType)); + float rorder = fr.getOrder(featureType); + if (rorder > -1) + { + setting.setOrder(rorder); + } + /// fs.addSetting(setting); + fs.getSetting().add(setting); + settingsAdded.addElement(featureType); + } + } + + // is groups actually supposed to be a map here ? + Iterator en = fr.getFeatureGroups().iterator(); + Vector groupsAdded = new Vector<>(); + while (en.hasNext()) + { + String grp = en.next(); + if (groupsAdded.contains(grp)) + { + continue; + } + Group g = new Group(); + g.setName(grp); + g.setDisplay(((Boolean) fr.checkGroupVisibility(grp, false)) + .booleanValue()); + // fs.addGroup(g); + fs.getGroup().add(g); + groupsAdded.addElement(grp); + } + // jms.setFeatureSettings(fs); + object.setFeatureSettings(fs); + } + + if (av.hasHiddenColumns()) + { + jalview.datamodel.HiddenColumns hidden = av.getAlignment() + .getHiddenColumns(); + if (hidden == null) + { + Console.warn( + "REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this."); + } + else + { + Iterator hiddenRegions = hidden.iterator(); + while (hiddenRegions.hasNext()) + { + int[] region = hiddenRegions.next(); + HiddenColumns hc = new HiddenColumns(); + hc.setStart(region[0]); + hc.setEnd(region[1]); + // view.addHiddenColumns(hc); + view.getHiddenColumns().add(hc); + } + } + } + if (calcIdSet.size() > 0) + { + for (String calcId : calcIdSet) + { + if (calcId.trim().length() > 0) + { + CalcIdParam cidp = createCalcIdParam(calcId, av); + // Some calcIds have no parameters. + if (cidp != null) + { + // view.addCalcIdParam(cidp); + view.getCalcIdParam().add(cidp); + } + } + } + } + + // jms.addViewport(view); + object.getViewport().add(view); + } + // object.setJalviewModelSequence(jms); + // object.getVamsasModel().addSequenceSet(vamsasSet); + object.getVamsasModel().getSequenceSet().add(vamsasSet); + + if (jout != null && fileName != null) + { + // We may not want to write the object to disk, + // eg we can copy the alignViewport to a new view object + // using save and then load + try + { + fileName = fileName.replace('\\', '/'); + System.out.println("Writing jar entry " + fileName); + JarEntry entry = new JarEntry(fileName); + jout.putNextEntry(entry); + PrintWriter pout = new PrintWriter( + new OutputStreamWriter(jout, UTF_8)); + JAXBContext jaxbContext = JAXBContext + .newInstance(JalviewModel.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + + // output pretty printed + // jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal( + new ObjectFactory().createJalviewModel(object), pout); + + // jaxbMarshaller.marshal(object, pout); + // marshaller.marshal(object); + pout.flush(); + jout.closeEntry(); + } catch (Exception ex) + { + // TODO: raise error in GUI if marshalling failed. + System.err.println("Error writing Jalview project"); + ex.printStackTrace(); + } + } + return object; + } + + /** + * Writes PCA viewer attributes and computed values to an XML model object and + * adds it to the JalviewModel. Any exceptions are reported by logging. + */ + protected void savePCA(PCAPanel panel, JalviewModel object) + { + try + { + PcaViewer viewer = new PcaViewer(); + viewer.setHeight(panel.getHeight()); + viewer.setWidth(panel.getWidth()); + viewer.setXpos(panel.getX()); + viewer.setYpos(panel.getY()); + viewer.setTitle(panel.getTitle()); + PCAModel pcaModel = panel.getPcaModel(); + viewer.setScoreModelName(pcaModel.getScoreModelName()); + viewer.setXDim(panel.getSelectedDimensionIndex(X)); + viewer.setYDim(panel.getSelectedDimensionIndex(Y)); + viewer.setZDim(panel.getSelectedDimensionIndex(Z)); + viewer.setBgColour( + panel.getRotatableCanvas().getBackgroundColour().getRGB()); + viewer.setScaleFactor(panel.getRotatableCanvas().getScaleFactor()); + float[] spMin = panel.getRotatableCanvas().getSeqMin(); + SeqPointMin spmin = new SeqPointMin(); + spmin.setXPos(spMin[0]); + spmin.setYPos(spMin[1]); + spmin.setZPos(spMin[2]); + viewer.setSeqPointMin(spmin); + float[] spMax = panel.getRotatableCanvas().getSeqMax(); + SeqPointMax spmax = new SeqPointMax(); + spmax.setXPos(spMax[0]); + spmax.setYPos(spMax[1]); + spmax.setZPos(spMax[2]); + viewer.setSeqPointMax(spmax); + viewer.setShowLabels(panel.getRotatableCanvas().isShowLabels()); + viewer.setLinkToAllViews( + panel.getRotatableCanvas().isApplyToAllViews()); + SimilarityParamsI sp = pcaModel.getSimilarityParameters(); + viewer.setIncludeGaps(sp.includeGaps()); + viewer.setMatchGaps(sp.matchGaps()); + viewer.setIncludeGappedColumns(sp.includeGappedColumns()); + viewer.setDenominateByShortestLength(sp.denominateByShortestLength()); + + /* + * sequence points on display + */ + for (jalview.datamodel.SequencePoint spt : pcaModel + .getSequencePoints()) + { + SequencePoint point = new SequencePoint(); + point.setSequenceRef(seqHash(spt.getSequence())); + point.setXPos(spt.coord.x); + point.setYPos(spt.coord.y); + point.setZPos(spt.coord.z); + viewer.getSequencePoint().add(point); + } + + /* + * (end points of) axes on display + */ + for (Point p : panel.getRotatableCanvas().getAxisEndPoints()) + { + + Axis axis = new Axis(); + axis.setXPos(p.x); + axis.setYPos(p.y); + axis.setZPos(p.z); + viewer.getAxis().add(axis); + } + + /* + * raw PCA data (note we are not restoring PCA inputs here - + * alignment view, score model, similarity parameters) + */ + PcaDataType data = new PcaDataType(); + viewer.setPcaData(data); + PCA pca = pcaModel.getPcaData(); + + DoubleMatrix pm = new DoubleMatrix(); + saveDoubleMatrix(pca.getPairwiseScores(), pm); + data.setPairwiseMatrix(pm); + + DoubleMatrix tm = new DoubleMatrix(); + saveDoubleMatrix(pca.getTridiagonal(), tm); + data.setTridiagonalMatrix(tm); + + DoubleMatrix eigenMatrix = new DoubleMatrix(); + data.setEigenMatrix(eigenMatrix); + saveDoubleMatrix(pca.getEigenmatrix(), eigenMatrix); + + object.getPcaViewer().add(viewer); + } catch (Throwable t) + { + Console.error("Error saving PCA: " + t.getMessage()); + } + } + + /** + * Stores values from a matrix into an XML element, including (if present) the + * D or E vectors + * + * @param m + * @param xmlMatrix + * @see #loadDoubleMatrix(DoubleMatrix) + */ + protected void saveDoubleMatrix(MatrixI m, DoubleMatrix xmlMatrix) + { + xmlMatrix.setRows(m.height()); + xmlMatrix.setColumns(m.width()); + for (int i = 0; i < m.height(); i++) + { + DoubleVector row = new DoubleVector(); + for (int j = 0; j < m.width(); j++) + { + row.getV().add(m.getValue(i, j)); + } + xmlMatrix.getRow().add(row); + } + if (m.getD() != null) + { + DoubleVector dVector = new DoubleVector(); + for (double d : m.getD()) + { + dVector.getV().add(d); + } + xmlMatrix.setD(dVector); + } + if (m.getE() != null) + { + DoubleVector eVector = new DoubleVector(); + for (double e : m.getE()) + { + eVector.getV().add(e); + } + xmlMatrix.setE(eVector); + } + } + + /** + * Loads XML matrix data into a new Matrix object, including the D and/or E + * vectors (if present) + * + * @param mData + * @return + * @see Jalview2XML#saveDoubleMatrix(MatrixI, DoubleMatrix) + */ + protected MatrixI loadDoubleMatrix(DoubleMatrix mData) + { + int rows = mData.getRows(); + double[][] vals = new double[rows][]; + + for (int i = 0; i < rows; i++) + { + List dVector = mData.getRow().get(i).getV(); + vals[i] = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vals[i][dvi++] = d; + } + } + + MatrixI m = new Matrix(vals); + + if (mData.getD() != null) + { + List dVector = mData.getD().getV(); + double[] vec = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vec[dvi++] = d; + } + m.setD(vec); + } + if (mData.getE() != null) + { + List dVector = mData.getE().getV(); + double[] vec = new double[dVector.size()]; + int dvi = 0; + for (Double d : dVector) + { + vec[dvi++] = d; + } + m.setE(vec); + } + + return m; + } + + /** + * Save any Varna viewers linked to this sequence. Writes an rnaViewer element + * for each viewer, with + *
    + *
  • viewer geometry (position, size, split pane divider location)
  • + *
  • index of the selected structure in the viewer (currently shows gapped + * or ungapped)
  • + *
  • the id of the annotation holding RNA secondary structure
  • + *
  • (currently only one SS is shown per viewer, may be more in future)
  • + *
+ * Varna viewer state is also written out (in native Varna XML) to separate + * project jar entries. A separate entry is written for each RNA structure + * displayed, with the naming convention + *
    + *
  • rna_viewId_sequenceId_annotationId_[gapped|trimmed]
  • + *
+ * + * @param jout + * @param jseq + * @param jds + * @param viewIds + * @param ap + * @param storeDataset + */ + protected void saveRnaViewers(JarOutputStream jout, JSeq jseq, + final SequenceI jds, List viewIds, AlignmentPanel ap, + boolean storeDataset) + { + if (Desktop.desktop == null) + { + return; + } + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + for (int f = frames.length - 1; f > -1; f--) + { + if (frames[f] instanceof AppVarna) + { + AppVarna varna = (AppVarna) frames[f]; + /* + * link the sequence to every viewer that is showing it and is linked to + * its alignment panel + */ + if (varna.isListeningFor(jds) && ap == varna.getAlignmentPanel()) + { + String viewId = varna.getViewId(); + RnaViewer rna = new RnaViewer(); + rna.setViewId(viewId); + rna.setTitle(varna.getTitle()); + rna.setXpos(varna.getX()); + rna.setYpos(varna.getY()); + rna.setWidth(varna.getWidth()); + rna.setHeight(varna.getHeight()); + rna.setDividerLocation(varna.getDividerLocation()); + rna.setSelectedRna(varna.getSelectedIndex()); + // jseq.addRnaViewer(rna); + jseq.getRnaViewer().add(rna); + + /* + * Store each Varna panel's state once in the project per sequence. + * First time through only (storeDataset==false) + */ + // boolean storeSessions = false; + // String sequenceViewId = viewId + seqsToIds.get(jds); + // if (!storeDataset && !viewIds.contains(sequenceViewId)) + // { + // viewIds.add(sequenceViewId); + // storeSessions = true; + // } + for (RnaModel model : varna.getModels()) + { + if (model.seq == jds) + { + /* + * VARNA saves each view (sequence or alignment secondary + * structure, gapped or trimmed) as a separate XML file + */ + String jarEntryName = rnaSessions.get(model); + if (jarEntryName == null) + { + + String varnaStateFile = varna.getStateInfo(model.rna); + jarEntryName = RNA_PREFIX + viewId + "_" + nextCounter(); + copyFileToJar(jout, varnaStateFile, jarEntryName, "Varna"); + rnaSessions.put(model, jarEntryName); + } + SecondaryStructure ss = new SecondaryStructure(); + String annotationId = varna.getAnnotation(jds).annotationId; + ss.setAnnotationId(annotationId); + ss.setViewerState(jarEntryName); + ss.setGapped(model.gapped); + ss.setTitle(model.title); + // rna.addSecondaryStructure(ss); + rna.getSecondaryStructure().add(ss); + } + } + } + } + } + } + + /** + * Copy the contents of a file to a new entry added to the output jar + * + * @param jout + * @param infilePath + * @param jarEntryName + * @param msg + * additional identifying info to log to the console + */ + protected void copyFileToJar(JarOutputStream jout, String infilePath, + String jarEntryName, String msg) + { + try (InputStream is = new FileInputStream(infilePath)) + { + File file = new File(infilePath); + if (file.exists() && jout != null) + { + System.out.println( + "Writing jar entry " + jarEntryName + " (" + msg + ")"); + jout.putNextEntry(new JarEntry(jarEntryName)); + copyAll(is, jout); + jout.closeEntry(); + // dis = new DataInputStream(new FileInputStream(file)); + // byte[] data = new byte[(int) file.length()]; + // dis.readFully(data); + // writeJarEntry(jout, jarEntryName, data); + } + } catch (Exception ex) + { + ex.printStackTrace(); + } + } + + /** + * 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 + * @param jds + * @param pdb + * the archive XML element under which to save the state + * @param entry + * @param viewIds + * @param matchedFile + * @param viewFrame + * @return + */ + protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds, + Pdbids pdb, PDBEntry entry, List viewIds, + String matchedFile, StructureViewerBase viewFrame) + { + final AAStructureBindingModel bindingModel = viewFrame.getBinding(); + + /* + * Look for any bindings for this viewer to the PDB file of interest + * (including part matches excluding chain id) + */ + for (int peid = 0; peid < bindingModel.getPdbCount(); peid++) + { + final PDBEntry pdbentry = bindingModel.getPdbEntry(peid); + final String pdbId = pdbentry.getId(); + if (!pdbId.equals(entry.getId()) && !(entry.getId().length() > 4 + && entry.getId().toLowerCase(Locale.ROOT) + .startsWith(pdbId.toLowerCase(Locale.ROOT)))) + { + /* + * not interested in a binding to a different PDB entry here + */ + continue; + } + if (matchedFile == null) + { + matchedFile = pdbentry.getFile(); + } + else if (!matchedFile.equals(pdbentry.getFile())) + { + Console.warn( + "Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): " + + pdbentry.getFile()); + } + // record the + // file so we + // can get at it if the ID + // match is ambiguous (e.g. + // 1QIP==1qipA) + + for (int smap = 0; smap < viewFrame.getBinding() + .getSequence()[peid].length; smap++) + { + // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1) + if (jds == viewFrame.getBinding().getSequence()[peid][smap]) + { + StructureState state = new StructureState(); + state.setVisible(true); + state.setXpos(viewFrame.getX()); + state.setYpos(viewFrame.getY()); + state.setWidth(viewFrame.getWidth()); + state.setHeight(viewFrame.getHeight()); + final String viewId = viewFrame.getViewId(); + state.setViewId(viewId); + state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap)); + state.setColourwithAlignPanel(viewFrame.isUsedForColourBy(ap)); + state.setColourByJmol(viewFrame.isColouredByViewer()); + state.setType(viewFrame.getViewerType().toString()); + // pdb.addStructureState(state); + pdb.getStructureState().add(state); + } + } + } + return matchedFile; + } + + /** + * Populates the AnnotationColourScheme xml for save. This captures the + * settings of the options in the 'Colour by Annotation' dialog. + * + * @param acg + * @param userColours + * @param jm + * @return + */ + private AnnotationColourScheme constructAnnotationColours( + AnnotationColourGradient acg, List userColours, + JalviewModel jm) + { + AnnotationColourScheme ac = new AnnotationColourScheme(); + ac.setAboveThreshold(acg.getAboveThreshold()); + ac.setThreshold(acg.getAnnotationThreshold()); + // 2.10.2 save annotationId (unique) not annotation label + ac.setAnnotation(acg.getAnnotation().annotationId); + if (acg.getBaseColour() instanceof UserColourScheme) + { + ac.setColourScheme( + setUserColourScheme(acg.getBaseColour(), userColours, jm)); + } + else + { + ac.setColourScheme( + ColourSchemeProperty.getColourName(acg.getBaseColour())); + } + + ac.setMaxColour(acg.getMaxColour().getRGB()); + ac.setMinColour(acg.getMinColour().getRGB()); + ac.setPerSequence(acg.isSeqAssociated()); + ac.setPredefinedColours(acg.isPredefinedColours()); + return ac; + } + + private void storeAlignmentAnnotation(AlignmentAnnotation[] aa, + IdentityHashMap groupRefs, + AlignmentViewport av, Set calcIdSet, boolean storeDS, + SequenceSet vamsasSet) + { + + for (int i = 0; i < aa.length; i++) + { + Annotation an = new Annotation(); + + AlignmentAnnotation annotation = aa[i]; + if (annotation.annotationId != null) + { + annotationIds.put(annotation.annotationId, annotation); + } + + an.setId(annotation.annotationId); + + an.setVisible(annotation.visible); + + an.setDescription(annotation.description); + + if (annotation.sequenceRef != null) + { + // 2.9 JAL-1781 xref on sequence id rather than name + an.setSequenceRef(seqsToIds.get(annotation.sequenceRef)); + } + if (annotation.groupRef != null) + { + String groupIdr = groupRefs.get(annotation.groupRef); + if (groupIdr == null) + { + // make a locally unique String + groupRefs.put(annotation.groupRef, + groupIdr = ("" + System.currentTimeMillis() + + annotation.groupRef.getName() + + groupRefs.size())); + } + an.setGroupRef(groupIdr.toString()); + } + + // store all visualization attributes for annotation + an.setGraphHeight(annotation.graphHeight); + an.setCentreColLabels(annotation.centreColLabels); + an.setScaleColLabels(annotation.scaleColLabel); + an.setShowAllColLabels(annotation.showAllColLabels); + an.setBelowAlignment(annotation.belowAlignment); + + if (annotation.graph > 0) + { + an.setGraph(true); + an.setGraphType(annotation.graph); + an.setGraphGroup(annotation.graphGroup); + if (annotation.getThreshold() != null) + { + ThresholdLine line = new ThresholdLine(); + line.setLabel(annotation.getThreshold().label); + line.setValue(annotation.getThreshold().value); + line.setColour(annotation.getThreshold().colour.getRGB()); + an.setThresholdLine(line); + } + } + else + { + an.setGraph(false); + } + + an.setLabel(annotation.label); + + if (annotation == av.getAlignmentQualityAnnot() + || annotation == av.getAlignmentConservationAnnotation() + || annotation == av.getAlignmentConsensusAnnotation() + || annotation.autoCalculated) + { + // new way of indicating autocalculated annotation - + an.setAutoCalculated(annotation.autoCalculated); + } + if (annotation.hasScore()) + { + an.setScore(annotation.getScore()); + } + + if (annotation.getCalcId() != null) + { + calcIdSet.add(annotation.getCalcId()); + an.setCalcId(annotation.getCalcId()); + } + if (annotation.hasProperties()) + { + for (String pr : annotation.getProperties()) + { + jalview.xml.binding.jalview.Annotation.Property prop = new jalview.xml.binding.jalview.Annotation.Property(); + prop.setName(pr); + prop.setValue(annotation.getProperty(pr)); + // an.addProperty(prop); + an.getProperty().add(prop); + } + } + + AnnotationElement ae; + if (annotation.annotations != null) + { + an.setScoreOnly(false); + for (int a = 0; a < annotation.annotations.length; a++) + { + if ((annotation == null) || (annotation.annotations[a] == null)) + { + continue; + } + + ae = new AnnotationElement(); + if (annotation.annotations[a].description != null) + { + ae.setDescription(annotation.annotations[a].description); + } + if (annotation.annotations[a].displayCharacter != null) + { + ae.setDisplayCharacter( + annotation.annotations[a].displayCharacter); + } + + if (!Float.isNaN(annotation.annotations[a].value)) + { + ae.setValue(annotation.annotations[a].value); + } + + ae.setPosition(a); + if (annotation.annotations[a].secondaryStructure > ' ') + { + ae.setSecondaryStructure( + annotation.annotations[a].secondaryStructure + ""); + } + + if (annotation.annotations[a].colour != null + && annotation.annotations[a].colour != java.awt.Color.black) + { + ae.setColour(annotation.annotations[a].colour.getRGB()); + } + + // an.addAnnotationElement(ae); + an.getAnnotationElement().add(ae); + if (annotation.autoCalculated) + { + // only write one non-null entry into the annotation row - + // sufficient to get the visualization attributes necessary to + // display data + continue; + } + } + } + else + { + an.setScoreOnly(true); + } + if (!storeDS || (storeDS && !annotation.autoCalculated)) + { + // skip autocalculated annotation - these are only provided for + // alignments + // vamsasSet.addAnnotation(an); + vamsasSet.getAnnotation().add(an); + } + } + + } + + private CalcIdParam createCalcIdParam(String calcId, AlignViewport av) + { + AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId); + if (settings != null) + { + CalcIdParam vCalcIdParam = new CalcIdParam(); + vCalcIdParam.setCalcId(calcId); + // vCalcIdParam.addServiceURL(settings.getServiceURI()); + vCalcIdParam.getServiceURL().add(settings.getServiceURI()); + // generic URI allowing a third party to resolve another instance of the + // service used for this calculation + for (String url : settings.getServiceURLs()) + { + // vCalcIdParam.addServiceURL(urls); + vCalcIdParam.getServiceURL().add(url); + } + vCalcIdParam.setVersion("1.0"); + if (settings.getPreset() != null) + { + WsParamSetI setting = settings.getPreset(); + vCalcIdParam.setName(setting.getName()); + vCalcIdParam.setDescription(setting.getDescription()); + } + else + { + vCalcIdParam.setName(""); + vCalcIdParam.setDescription("Last used parameters"); + } + // need to be able to recover 1) settings 2) user-defined presets or + // recreate settings from preset 3) predefined settings provided by + // service - or settings that can be transferred (or discarded) + vCalcIdParam.setParameters( + settings.getWsParamFile().replace("\n", "|\\n|")); + vCalcIdParam.setAutoUpdate(settings.isAutoUpdate()); + // todo - decide if updateImmediately is needed for any projects. + + return vCalcIdParam; + } + return null; + } + + private boolean recoverCalcIdParam(CalcIdParam calcIdParam, + AlignViewport av) + { + if (calcIdParam.getVersion().equals("1.0")) + { + final String[] calcIds = calcIdParam.getServiceURL() + .toArray(new String[0]); + Jws2Instance service = Jws2Discoverer.getDiscoverer() + .getPreferredServiceFor(calcIds); + if (service != null) + { + WsParamSetI parmSet = null; + try + { + parmSet = service.getParamStore().parseServiceParameterFile( + calcIdParam.getName(), calcIdParam.getDescription(), + calcIds, + calcIdParam.getParameters().replace("|\\n|", "\n")); + } catch (IOException x) + { + Console.warn("Couldn't parse parameter data for " + + calcIdParam.getCalcId(), x); + return false; + } + List argList = null; + if (calcIdParam.getName().length() > 0) + { + parmSet = service.getParamStore() + .getPreset(calcIdParam.getName()); + if (parmSet != null) + { + // TODO : check we have a good match with settings in AACon - + // otherwise we'll need to create a new preset + } + } + else + { + argList = parmSet.getArguments(); + parmSet = null; + } + AAConSettings settings = new AAConSettings( + calcIdParam.isAutoUpdate(), service, parmSet, argList); + av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings, + calcIdParam.isNeedsUpdate()); + return true; + } + else + { + Console.warn( + "Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server."); + return false; + } + } + throw new Error(MessageManager.formatMessage( + "error.unsupported_version_calcIdparam", new Object[] + { calcIdParam.toString() })); + } + + /** + * External mapping between jalview objects and objects yielding a valid and + * unique object ID string. This is null for normal Jalview project IO, but + * non-null when a jalview project is being read or written as part of a + * vamsas session. + */ + IdentityHashMap jv2vobj = null; + + /** + * Construct a unique ID for jvobj using either existing bindings or if none + * exist, the result of the hashcode call for the object. + * + * @param jvobj + * jalview data object + * @return unique ID for referring to jvobj + */ + private String makeHashCode(Object jvobj, String altCode) + { + if (jv2vobj != null) + { + Object id = jv2vobj.get(jvobj); + if (id != null) + { + return id.toString(); + } + // check string ID mappings + if (jvids2vobj != null && jvobj instanceof String) + { + id = jvids2vobj.get(jvobj); + } + if (id != null) + { + return id.toString(); + } + // give up and warn that something has gone wrong + Console.warn( + "Cannot find ID for object in external mapping : " + jvobj); + } + return altCode; + } + + /** + * return local jalview object mapped to ID, if it exists + * + * @param idcode + * (may be null) + * @return null or object bound to idcode + */ + private Object retrieveExistingObj(String idcode) + { + if (idcode != null && vobj2jv != null) + { + return vobj2jv.get(idcode); + } + return null; + } + + /** + * binding from ID strings from external mapping table to jalview data model + * objects. + */ + private Hashtable vobj2jv; + + private Sequence createVamsasSequence(String id, SequenceI jds) + { + return createVamsasSequence(true, id, jds, null); + } + + private Sequence createVamsasSequence(boolean recurse, String id, + SequenceI jds, SequenceI parentseq) + { + Sequence vamsasSeq = new Sequence(); + vamsasSeq.setId(id); + vamsasSeq.setName(jds.getName()); + vamsasSeq.setSequence(jds.getSequenceAsString()); + vamsasSeq.setDescription(jds.getDescription()); + List dbrefs = null; + if (jds.getDatasetSequence() != null) + { + vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence())); + } + else + { + // seqId==dsseqid so we can tell which sequences really are + // dataset sequences only + vamsasSeq.setDsseqid(id); + dbrefs = jds.getDBRefs(); + if (parentseq == null) + { + parentseq = jds; + } + } + + /* + * save any dbrefs; special subclass GeneLocus is flagged as 'locus' + */ + if (dbrefs != null) + { + for (int d = 0, nd = dbrefs.size(); d < nd; d++) + { + DBRef dbref = new DBRef(); + DBRefEntry ref = dbrefs.get(d); + dbref.setSource(ref.getSource()); + dbref.setVersion(ref.getVersion()); + dbref.setAccessionId(ref.getAccessionId()); + dbref.setCanonical(ref.isCanonical()); + if (ref instanceof GeneLocus) + { + dbref.setLocus(true); + } + if (ref.hasMap()) + { + Mapping mp = createVamsasMapping(ref.getMap(), parentseq, jds, + recurse); + dbref.setMapping(mp); + } + vamsasSeq.getDBRef().add(dbref); + } + } + return vamsasSeq; + } + + private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp, + SequenceI parentseq, SequenceI jds, boolean recurse) + { + Mapping mp = null; + if (jmp.getMap() != null) + { + mp = new Mapping(); + + jalview.util.MapList mlst = jmp.getMap(); + List r = mlst.getFromRanges(); + for (int[] range : r) + { + MapListFrom mfrom = new MapListFrom(); + mfrom.setStart(range[0]); + mfrom.setEnd(range[1]); + // mp.addMapListFrom(mfrom); + mp.getMapListFrom().add(mfrom); + } + r = mlst.getToRanges(); + for (int[] range : r) + { + MapListTo mto = new MapListTo(); + mto.setStart(range[0]); + mto.setEnd(range[1]); + // mp.addMapListTo(mto); + mp.getMapListTo().add(mto); + } + mp.setMapFromUnit(BigInteger.valueOf(mlst.getFromRatio())); + mp.setMapToUnit(BigInteger.valueOf(mlst.getToRatio())); + if (jmp.getTo() != null) + { + // MappingChoice mpc = new MappingChoice(); + + // check/create ID for the sequence referenced by getTo() + + String jmpid = ""; + SequenceI ps = null; + if (parentseq != jmp.getTo() + && parentseq.getDatasetSequence() != jmp.getTo()) + { + // chaining dbref rather than a handshaking one + jmpid = seqHash(ps = jmp.getTo()); + } + else + { + jmpid = seqHash(ps = parentseq); + } + // mpc.setDseqFor(jmpid); + mp.setDseqFor(jmpid); + if (!seqRefIds.containsKey(jmpid)) + { + Console.debug("creatign new DseqFor ID"); + seqRefIds.put(jmpid, ps); + } + else + { + Console.debug("reusing DseqFor ID"); + } + + // mp.setMappingChoice(mpc); + } + } + return mp; + } + + String setUserColourScheme(jalview.schemes.ColourSchemeI cs, + List userColours, JalviewModel jm) + { + String id = null; + jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs; + boolean newucs = false; + if (!userColours.contains(ucs)) + { + userColours.add(ucs); + newucs = true; + } + id = "ucs" + userColours.indexOf(ucs); + if (newucs) + { + // actually create the scheme's entry in the XML model + java.awt.Color[] colours = ucs.getColours(); + UserColours uc = new UserColours(); + // UserColourScheme jbucs = new UserColourScheme(); + JalviewUserColours jbucs = new JalviewUserColours(); + + for (int i = 0; i < colours.length; i++) + { + Colour col = new Colour(); + col.setName(ResidueProperties.aa[i]); + col.setRGB(jalview.util.Format.getHexString(colours[i])); + // jbucs.addColour(col); + jbucs.getColour().add(col); + } + if (ucs.getLowerCaseColours() != null) + { + colours = ucs.getLowerCaseColours(); + for (int i = 0; i < colours.length; i++) + { + Colour col = new Colour(); + col.setName(ResidueProperties.aa[i].toLowerCase(Locale.ROOT)); + col.setRGB(jalview.util.Format.getHexString(colours[i])); + // jbucs.addColour(col); + jbucs.getColour().add(col); + } + } + + uc.setId(id); + uc.setUserColourScheme(jbucs); + // jm.addUserColours(uc); + jm.getUserColours().add(uc); + } + + return id; + } + + jalview.schemes.UserColourScheme getUserColourScheme(JalviewModel jm, + String id) + { + List uc = jm.getUserColours(); + UserColours colours = null; + /* + for (int i = 0; i < uc.length; i++) + { + if (uc[i].getId().equals(id)) + { + colours = uc[i]; + break; + } + } + */ + for (UserColours c : uc) + { + if (c.getId().equals(id)) + { + colours = c; + break; + } + } + + java.awt.Color[] newColours = new java.awt.Color[24]; + + for (int i = 0; i < 24; i++) + { + newColours[i] = new java.awt.Color(Integer.parseInt( + // colours.getUserColourScheme().getColour(i).getRGB(), 16)); + colours.getUserColourScheme().getColour().get(i).getRGB(), + 16)); + } + + jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme( + newColours); + + if (colours.getUserColourScheme().getColour().size()/*Count()*/ > 24) + { + newColours = new java.awt.Color[23]; + for (int i = 0; i < 23; i++) + { + newColours[i] = new java.awt.Color( + Integer.parseInt(colours.getUserColourScheme().getColour() + .get(i + 24).getRGB(), 16)); + } + ucs.setLowerCaseColours(newColours); + } + + return ucs; + } + + /** + * contains last error message (if any) encountered by XML loader. + */ + String errorMessage = null; + + /** + * flag to control whether the Jalview2XML_V1 parser should be deferred to if + * exceptions are raised during project XML parsing + */ + public boolean attemptversion1parse = false; + + /** + * Load a jalview project archive from a jar file + * + * @param file + * - HTTP URL or filename + */ + public AlignFrame loadJalviewAlign(final Object file) + { + + jalview.gui.AlignFrame af = null; + + try + { + // create list to store references for any new Jmol viewers created + newStructureViewers = new Vector<>(); + // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING + // Workaround is to make sure caller implements the JarInputStreamProvider + // interface + // so we can re-open the jar input stream for each entry. + + jarInputStreamProvider jprovider = createjarInputStreamProvider(file); + af = loadJalviewAlign(jprovider); + if (af != null) + { + af.setMenusForViewport(); + } + } catch (MalformedURLException e) + { + errorMessage = "Invalid URL format for '" + file + "'"; + reportErrors(); + } finally + { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run() + { + setLoadingFinishedForNewStructureViewers(); + } + }); + } catch (Exception x) + { + System.err.println("Error loading alignment: " + x.getMessage()); + } + } + return af; + } + + @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 (HttpUtils.startsWithHttpOrHttps(file)) + { + 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 + * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence + * themselves. Any null fields will be initialised with default values, + * non-null fields are left alone. + * + * @param jprovider + * @return + */ + public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider) + { + errorMessage = null; + if (uniqueSetSuffix == null) + { + uniqueSetSuffix = System.currentTimeMillis() % 100000 + ""; + } + if (seqRefIds == null) + { + initSeqRefs(); + } + AlignFrame af = null, _af = null; + IdentityHashMap importedDatasets = new IdentityHashMap<>(); + Map gatherToThisFrame = new HashMap<>(); + final String file = jprovider.getFilename(); + try + { + JarInputStream jin = null; + JarEntry jarentry = null; + int entryCount = 1; + + do + { + jin = jprovider.getJarInputStream(); + for (int i = 0; i < entryCount; i++) + { + jarentry = jin.getNextJarEntry(); + } + + if (jarentry != null && jarentry.getName().endsWith(".xml")) + { + JAXBContext jc = JAXBContext + .newInstance("jalview.xml.binding.jalview"); + XMLStreamReader streamReader = XMLInputFactory.newInstance() + .createXMLStreamReader(jin); + javax.xml.bind.Unmarshaller um = jc.createUnmarshaller(); + JAXBElement jbe = um.unmarshal(streamReader, + JalviewModel.class); + JalviewModel object = jbe.getValue(); + + if (true) // !skipViewport(object)) + { + _af = loadFromObject(object, file, true, jprovider); + if (_af != null && object.getViewport().size() > 0) + // getJalviewModelSequence().getViewportCount() > 0) + { + if (af == null) + { + // store a reference to the first view + af = _af; + } + if (_af.getViewport().isGatherViewsHere()) + { + // if this is a gathered view, keep its reference since + // after gathering views, only this frame will remain + af = _af; + gatherToThisFrame.put(_af.getViewport().getSequenceSetId(), + _af); + } + // Save dataset to register mappings once all resolved + importedDatasets.put( + af.getViewport().getAlignment().getDataset(), + af.getViewport().getAlignment().getDataset()); + } + } + entryCount++; + } + else if (jarentry != null) + { + // Some other file here. + entryCount++; + } + } while (jarentry != null); + jin.close(); + resolveFrefedSequences(); + } catch (IOException ex) + { + ex.printStackTrace(); + errorMessage = "Couldn't locate Jalview XML file : " + file; + System.err.println( + "Exception whilst loading jalview XML file : " + ex + "\n"); + } catch (Exception ex) + { + System.err.println("Parsing as Jalview Version 2 file failed."); + ex.printStackTrace(System.err); + if (attemptversion1parse) + { + // used to attempt to parse as V1 castor-generated xml + } + if (Desktop.instance != null) + { + Desktop.instance.stopLoading(); + } + if (af != null) + { + System.out.println("Successfully loaded archive file"); + return af; + } + ex.printStackTrace(); + + System.err.println( + "Exception whilst loading jalview XML file : " + ex + "\n"); + } catch (OutOfMemoryError e) + { + // Don't use the OOM Window here + errorMessage = "Out of memory loading jalview XML file"; + System.err.println("Out of memory whilst loading jalview XML file"); + e.printStackTrace(); + } + + /* + * Regather multiple views (with the same sequence set id) to the frame (if + * any) that is flagged as the one to gather to, i.e. convert them to tabbed + * views instead of separate frames. Note this doesn't restore a state where + * some expanded views in turn have tabbed views - the last "first tab" read + * in will play the role of gatherer for all. + */ + for (AlignFrame fr : gatherToThisFrame.values()) + { + Desktop.instance.gatherViews(fr); + } + + restoreSplitFrames(); + for (AlignmentI ds : importedDatasets.keySet()) + { + if (ds.getCodonFrames() != null) + { + StructureSelectionManager + .getStructureSelectionManager(Desktop.instance) + .registerMappings(ds.getCodonFrames()); + } + } + if (errorMessage != null) + { + reportErrors(); + } + + if (Desktop.instance != null) + { + Desktop.instance.stopLoading(); + } + + return af; + } + + /** + * Try to reconstruct and display SplitFrame windows, where each contains + * complementary dna and protein alignments. Done by pairing up AlignFrame + * objects (created earlier) which have complementary viewport ids associated. + */ + protected void restoreSplitFrames() + { + List gatherTo = new ArrayList<>(); + List addedToSplitFrames = new ArrayList<>(); + Map dna = new HashMap<>(); + + /* + * Identify the DNA alignments + */ + for (Entry candidate : splitFrameCandidates + .entrySet()) + { + AlignFrame af = candidate.getValue(); + if (af.getViewport().getAlignment().isNucleotide()) + { + dna.put(candidate.getKey().getId(), af); + } + } + + /* + * Try to match up the protein complements + */ + for (Entry candidate : splitFrameCandidates + .entrySet()) + { + AlignFrame af = candidate.getValue(); + if (!af.getViewport().getAlignment().isNucleotide()) + { + String complementId = candidate.getKey().getComplementId(); + // only non-null complements should be in the Map + if (complementId != null && dna.containsKey(complementId)) + { + final AlignFrame dnaFrame = dna.get(complementId); + SplitFrame sf = createSplitFrame(dnaFrame, af); + addedToSplitFrames.add(dnaFrame); + addedToSplitFrames.add(af); + dnaFrame.setMenusForViewport(); + af.setMenusForViewport(); + if (af.getViewport().isGatherViewsHere()) + { + gatherTo.add(sf); + } + } + } + } + + /* + * Open any that we failed to pair up (which shouldn't happen!) as + * standalone AlignFrame's. + */ + for (Entry candidate : splitFrameCandidates + .entrySet()) + { + AlignFrame af = candidate.getValue(); + if (!addedToSplitFrames.contains(af)) + { + Viewport view = candidate.getKey(); + Desktop.addInternalFrame(af, view.getTitle(), + safeInt(view.getWidth()), safeInt(view.getHeight())); + af.setMenusForViewport(); + System.err.println("Failed to restore view " + view.getTitle() + + " to split frame"); + } + } + + /* + * Gather back into tabbed views as flagged. + */ + for (SplitFrame sf : gatherTo) + { + Desktop.instance.gatherViews(sf); + } + + splitFrameCandidates.clear(); + } + + /** + * Construct and display one SplitFrame holding DNA and protein alignments. + * + * @param dnaFrame + * @param proteinFrame + * @return + */ + protected SplitFrame createSplitFrame(AlignFrame dnaFrame, + AlignFrame proteinFrame) + { + SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame); + String title = MessageManager.getString("label.linked_view_title"); + int width = (int) dnaFrame.getBounds().getWidth(); + int height = (int) (dnaFrame.getBounds().getHeight() + + proteinFrame.getBounds().getHeight() + 50); + + /* + * SplitFrame location is saved to both enclosed frames + */ + splitFrame.setLocation(dnaFrame.getX(), dnaFrame.getY()); + Desktop.addInternalFrame(splitFrame, title, width, height); + + /* + * And compute cDNA consensus (couldn't do earlier with consensus as + * mappings were not yet present) + */ + proteinFrame.getViewport().alignmentChanged(proteinFrame.alignPanel); + + return splitFrame; + } + + /** + * check errorMessage for a valid error message and raise an error box in the + * GUI or write the current errorMessage to stderr and then clear the error + * state. + */ + protected void reportErrors() + { + reportErrors(false); + } + + protected void reportErrors(final boolean saving) + { + if (errorMessage != null) + { + final String finalErrorMessage = errorMessage; + if (raiseGUI) + { + javax.swing.SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + JvOptionPane.showInternalMessageDialog(Desktop.desktop, + finalErrorMessage, + "Error " + (saving ? "saving" : "loading") + + " Jalview file", + JvOptionPane.WARNING_MESSAGE); + } + }); + } + else + { + System.err.println("Problem loading Jalview file: " + errorMessage); + } + } + errorMessage = null; + } + + Map alreadyLoadedPDB = new HashMap<>(); + + /** + * when set, local views will be updated from view stored in JalviewXML + * Currently (28th Sep 2008) things will go horribly wrong in vamsas document + * sync if this is set to true. + */ + private final boolean updateLocalViews = false; + + /** + * Returns the path to a temporary file holding the PDB file for the given PDB + * id. The first time of asking, searches for a file of that name in the + * Jalview project jar, and copies it to a new temporary file. Any repeat + * requests just return the path to the file previously created. + * + * @param jprovider + * @param pdbId + * @return + */ + String loadPDBFile(jarInputStreamProvider jprovider, String pdbId, + String origFile) + { + if (alreadyLoadedPDB.containsKey(pdbId)) + { + return alreadyLoadedPDB.get(pdbId).toString(); + } + + String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb", + origFile); + if (tempFile != null) + { + alreadyLoadedPDB.put(pdbId, tempFile); + } + return tempFile; + } + + /** + * Copies the jar entry of given name to a new temporary file and returns the + * path to the file, or null if the entry is not found. + * + * @param jprovider + * @param jarEntryName + * @param prefix + * a prefix for the temporary file name, must be at least three + * characters long + * @param suffixModel + * null or original file - so new file can be given the same suffix + * as the old one + * @return + */ + protected String copyJarEntry(jarInputStreamProvider jprovider, + String jarEntryName, String prefix, String suffixModel) + { + String suffix = ".tmp"; + if (suffixModel == null) + { + suffixModel = jarEntryName; + } + int sfpos = suffixModel.lastIndexOf("."); + if (sfpos > -1 && sfpos < (suffixModel.length() - 1)) + { + suffix = "." + suffixModel.substring(sfpos + 1); + } + + 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)); + File outFile = File.createTempFile(prefix, suffix); + outFile.deleteOnExit(); + try (OutputStream os = new FileOutputStream(outFile)) + { + copyAll(jin, os); + } + String t = outFile.getAbsolutePath(); + return t; + } + else + { + Console.warn( + "Couldn't find entry in Jalview Jar for " + jarEntryName); + } + } catch (Exception ex) + { + ex.printStackTrace(); + } + + return null; + } + + private class JvAnnotRow + { + public JvAnnotRow(int i, AlignmentAnnotation jaa) + { + order = i; + template = jaa; + } + + /** + * persisted version of annotation row from which to take vis properties + */ + public jalview.datamodel.AlignmentAnnotation template; + + /** + * original position of the annotation row in the alignment + */ + public int order; + } + + /** + * Load alignment frame from jalview XML DOM object + * + * @param jalviewModel + * DOM + * @param file + * filename source string + * @param loadTreesAndStructures + * when false only create Viewport + * @param jprovider + * data source provider + * @return alignment frame created from view stored in DOM + */ + AlignFrame loadFromObject(JalviewModel jalviewModel, String file, + boolean loadTreesAndStructures, jarInputStreamProvider jprovider) + { + SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet() + .get(0); + List vamsasSeqs = vamsasSet.getSequence(); + + // JalviewModelSequence jms = object.getJalviewModelSequence(); + + // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0) + // : null; + Viewport view = (jalviewModel.getViewport().size() > 0) + ? jalviewModel.getViewport().get(0) + : null; + + // //////////////////////////////// + // INITIALISE ALIGNMENT SEQUENCESETID AND VIEWID + // + // + // If we just load in the same jar file again, the sequenceSetId + // will be the same, and we end up with multiple references + // to the same sequenceSet. We must modify this id on load + // so that each load of the file gives a unique id + + /** + * used to resolve correct alignment dataset for alignments with multiple + * views + */ + String uniqueSeqSetId = null; + String viewId = null; + if (view != null) + { + uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix; + viewId = (view.getId() == null ? null + : view.getId() + uniqueSetSuffix); + } + + // //////////////////////////////// + // LOAD SEQUENCES + + List hiddenSeqs = null; + + List tmpseqs = new ArrayList<>(); + + boolean multipleView = false; + SequenceI referenceseqForView = null; + // JSeq[] jseqs = object.getJalviewModelSequence().getJSeq(); + List jseqs = jalviewModel.getJSeq(); + int vi = 0; // counter in vamsasSeq array + for (int i = 0; i < jseqs.size(); i++) + { + JSeq jseq = jseqs.get(i); + String seqId = jseq.getId(); + + SequenceI tmpSeq = seqRefIds.get(seqId); + if (tmpSeq != null) + { + if (!incompleteSeqs.containsKey(seqId)) + { + // may not need this check, but keep it for at least 2.9,1 release + if (tmpSeq.getStart() != jseq.getStart() + || tmpSeq.getEnd() != jseq.getEnd()) + { + System.err.println(String.format( + "Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d", + tmpSeq.getName(), tmpSeq.getStart(), tmpSeq.getEnd(), + jseq.getStart(), jseq.getEnd())); + } + } + else + { + incompleteSeqs.remove(seqId); + } + if (vamsasSeqs.size() > vi + && vamsasSeqs.get(vi).getId().equals(seqId)) + { + // most likely we are reading a dataset XML document so + // update from vamsasSeq section of XML for this sequence + tmpSeq.setName(vamsasSeqs.get(vi).getName()); + tmpSeq.setDescription(vamsasSeqs.get(vi).getDescription()); + tmpSeq.setSequence(vamsasSeqs.get(vi).getSequence()); + vi++; + } + else + { + // reading multiple views, so vamsasSeq set is a subset of JSeq + multipleView = true; + } + tmpSeq.setStart(jseq.getStart()); + tmpSeq.setEnd(jseq.getEnd()); + tmpseqs.add(tmpSeq); + } + else + { + Sequence vamsasSeq = vamsasSeqs.get(vi); + tmpSeq = new jalview.datamodel.Sequence(vamsasSeq.getName(), + vamsasSeq.getSequence()); + tmpSeq.setDescription(vamsasSeq.getDescription()); + tmpSeq.setStart(jseq.getStart()); + tmpSeq.setEnd(jseq.getEnd()); + tmpSeq.setVamsasId(uniqueSetSuffix + seqId); + seqRefIds.put(vamsasSeq.getId(), tmpSeq); + tmpseqs.add(tmpSeq); + vi++; + } + + if (safeBoolean(jseq.isViewreference())) + { + referenceseqForView = tmpseqs.get(tmpseqs.size() - 1); + } + + if (jseq.isHidden() != null && jseq.isHidden().booleanValue()) + { + if (hiddenSeqs == null) + { + hiddenSeqs = new ArrayList<>(); + } + + hiddenSeqs.add(tmpSeq); + } + } + + // / + // Create the alignment object from the sequence set + // /////////////////////////////// + SequenceI[] orderedSeqs = tmpseqs + .toArray(new SequenceI[tmpseqs.size()]); + + AlignmentI al = null; + // so we must create or recover the dataset alignment before going further + // /////////////////////////////// + if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "") + { + // older jalview projects do not have a dataset - so creat alignment and + // dataset + al = new Alignment(orderedSeqs); + al.setDataset(null); + } + else + { + boolean isdsal = jalviewModel.getViewport().isEmpty(); + if (isdsal) + { + // we are importing a dataset record, so + // recover reference to an alignment already materialsed as dataset + al = getDatasetFor(vamsasSet.getDatasetId()); + } + if (al == null) + { + // materialse the alignment + al = new Alignment(orderedSeqs); + } + if (isdsal) + { + addDatasetRef(vamsasSet.getDatasetId(), al); + } + + // finally, verify all data in vamsasSet is actually present in al + // passing on flag indicating if it is actually a stored dataset + recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId); + } + + if (referenceseqForView != null) + { + al.setSeqrep(referenceseqForView); + } + // / Add the alignment properties + for (int i = 0; i < vamsasSet.getSequenceSetProperties().size(); i++) + { + SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties() + .get(i); + al.setProperty(ssp.getKey(), ssp.getValue()); + } + + // /////////////////////////////// + + Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this?? + if (!multipleView) + { + // load sequence features, database references and any associated PDB + // structures for the alignment + // + // prior to 2.10, this part would only be executed the first time a + // sequence was encountered, but not afterwards. + // now, for 2.10 projects, this is also done if the xml doc includes + // dataset sequences not actually present in any particular view. + // + for (int i = 0; i < vamsasSeqs.size(); i++) + { + JSeq jseq = jseqs.get(i); + if (jseq.getFeatures().size() > 0) + { + List features = jseq.getFeatures(); + for (int f = 0; f < features.size(); f++) + { + Feature feat = features.get(f); + SequenceFeature sf = new SequenceFeature(feat.getType(), + feat.getDescription(), feat.getBegin(), feat.getEnd(), + safeFloat(feat.getScore()), feat.getFeatureGroup()); + sf.setStatus(feat.getStatus()); + + /* + * load any feature attributes - include map-valued attributes + */ + Map> mapAttributes = new HashMap<>(); + for (int od = 0; od < feat.getOtherData().size(); od++) + { + OtherData keyValue = feat.getOtherData().get(od); + String attributeName = keyValue.getKey(); + String attributeValue = keyValue.getValue(); + if (attributeName.startsWith("LINK")) + { + sf.addLink(attributeValue); + } + else + { + String subAttribute = keyValue.getKey2(); + if (subAttribute == null) + { + // simple string-valued attribute + sf.setValue(attributeName, attributeValue); + } + else + { + // attribute 'key' has sub-attribute 'key2' + if (!mapAttributes.containsKey(attributeName)) + { + mapAttributes.put(attributeName, new HashMap<>()); + } + mapAttributes.get(attributeName).put(subAttribute, + attributeValue); + } + } + } + for (Entry> mapAttribute : mapAttributes + .entrySet()) + { + sf.setValue(mapAttribute.getKey(), mapAttribute.getValue()); + } + + // adds feature to datasequence's feature set (since Jalview 2.10) + al.getSequenceAt(i).addSequenceFeature(sf); + } + } + if (vamsasSeqs.get(i).getDBRef().size() > 0) + { + // adds dbrefs to datasequence's set (since Jalview 2.10) + addDBRefs( + al.getSequenceAt(i).getDatasetSequence() == null + ? al.getSequenceAt(i) + : al.getSequenceAt(i).getDatasetSequence(), + vamsasSeqs.get(i)); + } + if (jseq.getPdbids().size() > 0) + { + List ids = jseq.getPdbids(); + for (int p = 0; p < ids.size(); p++) + { + Pdbids pdbid = ids.get(p); + jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry(); + entry.setId(pdbid.getId()); + if (pdbid.getType() != null) + { + if (PDBEntry.Type.getType(pdbid.getType()) != null) + { + entry.setType(PDBEntry.Type.getType(pdbid.getType())); + } + else + { + entry.setType(PDBEntry.Type.FILE); + } + } + // jprovider is null when executing 'New View' + if (pdbid.getFile() != null && jprovider != null) + { + if (!pdbloaded.containsKey(pdbid.getFile())) + { + entry.setFile(loadPDBFile(jprovider, pdbid.getId(), + pdbid.getFile())); + } + else + { + entry.setFile(pdbloaded.get(pdbid.getId()).toString()); + } + } + /* + if (pdbid.getPdbentryItem() != null) + { + for (PdbentryItem item : pdbid.getPdbentryItem()) + { + for (Property pr : item.getProperty()) + { + entry.setProperty(pr.getName(), pr.getValue()); + } + } + } + */ + for (Property prop : pdbid.getProperty()) + { + entry.setProperty(prop.getName(), prop.getValue()); + } + StructureSelectionManager + .getStructureSelectionManager(Desktop.instance) + .registerPDBEntry(entry); + // adds PDBEntry to datasequence's set (since Jalview 2.10) + if (al.getSequenceAt(i).getDatasetSequence() != null) + { + al.getSequenceAt(i).getDatasetSequence().addPDBId(entry); + } + else + { + al.getSequenceAt(i).addPDBId(entry); + } + } + } + } + } // end !multipleview + + // /////////////////////////////// + // LOAD SEQUENCE MAPPINGS + + if (vamsasSet.getAlcodonFrame().size() > 0) + { + // TODO Potentially this should only be done once for all views of an + // alignment + List alc = vamsasSet.getAlcodonFrame(); + for (int i = 0; i < alc.size(); i++) + { + AlignedCodonFrame cf = new AlignedCodonFrame(); + if (alc.get(i).getAlcodMap().size() > 0) + { + List maps = alc.get(i).getAlcodMap(); + for (int m = 0; m < maps.size(); m++) + { + AlcodMap map = maps.get(m); + SequenceI dnaseq = seqRefIds.get(map.getDnasq()); + // Load Mapping + jalview.datamodel.Mapping mapping = null; + // attach to dna sequence reference. + if (map.getMapping() != null) + { + mapping = addMapping(map.getMapping()); + if (dnaseq != null && mapping.getTo() != null) + { + cf.addMap(dnaseq, mapping.getTo(), mapping.getMap()); + } + else + { + // defer to later + frefedSequence + .add(newAlcodMapRef(map.getDnasq(), cf, mapping)); + } + } + } + al.addCodonFrame(cf); + } + } + } + + // //////////////////////////////// + // LOAD ANNOTATIONS + List autoAlan = new ArrayList<>(); + + /* + * store any annotations which forward reference a group's ID + */ + Map> groupAnnotRefs = new Hashtable<>(); + + if (vamsasSet.getAnnotation().size()/*Count()*/ > 0) + { + List an = vamsasSet.getAnnotation(); + + for (int i = 0; i < an.size(); i++) + { + Annotation annotation = an.get(i); + + /** + * test if annotation is automatically calculated for this view only + */ + boolean autoForView = false; + if (annotation.getLabel().equals("Quality") + || annotation.getLabel().equals("Conservation") + || annotation.getLabel().equals("Consensus")) + { + // Kludge for pre 2.5 projects which lacked the autocalculated flag + autoForView = true; + // JAXB has no has() test; schema defaults value to false + // if (!annotation.hasAutoCalculated()) + // { + // annotation.setAutoCalculated(true); + // } + } + if (autoForView || annotation.isAutoCalculated()) + { + // remove ID - we don't recover annotation from other views for + // view-specific annotation + annotation.setId(null); + } + + // set visibility for other annotation in this view + String annotationId = annotation.getId(); + if (annotationId != null && annotationIds.containsKey(annotationId)) + { + AlignmentAnnotation jda = annotationIds.get(annotationId); + // in principle Visible should always be true for annotation displayed + // in multiple views + if (annotation.isVisible() != null) + { + jda.visible = annotation.isVisible(); + } + + al.addAnnotation(jda); + + continue; + } + // Construct new annotation from model. + List ae = annotation.getAnnotationElement(); + jalview.datamodel.Annotation[] anot = null; + java.awt.Color firstColour = null; + int anpos; + if (!annotation.isScoreOnly()) + { + anot = new jalview.datamodel.Annotation[al.getWidth()]; + for (int aa = 0; aa < ae.size() && aa < anot.length; aa++) + { + AnnotationElement annElement = ae.get(aa); + anpos = annElement.getPosition(); + + if (anpos >= anot.length) + { + continue; + } + + float value = safeFloat(annElement.getValue()); + anot[anpos] = new jalview.datamodel.Annotation( + annElement.getDisplayCharacter(), + annElement.getDescription(), + (annElement.getSecondaryStructure() == null + || annElement.getSecondaryStructure() + .length() == 0) + ? ' ' + : annElement + .getSecondaryStructure() + .charAt(0), + value); + anot[anpos].colour = new Color(safeInt(annElement.getColour())); + if (firstColour == null) + { + firstColour = anot[anpos].colour; + } + } + } + jalview.datamodel.AlignmentAnnotation jaa = null; + + if (annotation.isGraph()) + { + float llim = 0, hlim = 0; + // if (autoForView || an[i].isAutoCalculated()) { + // hlim=11f; + // } + jaa = new jalview.datamodel.AlignmentAnnotation( + annotation.getLabel(), annotation.getDescription(), anot, + llim, hlim, safeInt(annotation.getGraphType())); + + jaa.graphGroup = safeInt(annotation.getGraphGroup()); + jaa._linecolour = firstColour; + if (annotation.getThresholdLine() != null) + { + jaa.setThreshold(new jalview.datamodel.GraphLine( + safeFloat(annotation.getThresholdLine().getValue()), + annotation.getThresholdLine().getLabel(), + new java.awt.Color(safeInt( + annotation.getThresholdLine().getColour())))); + } + if (autoForView || annotation.isAutoCalculated()) + { + // Hardwire the symbol display line to ensure that labels for + // histograms are displayed + jaa.hasText = true; + } + } + else + { + jaa = new jalview.datamodel.AlignmentAnnotation( + annotation.getLabel(), annotation.getDescription(), anot); + jaa._linecolour = firstColour; + } + // register new annotation + if (annotation.getId() != null) + { + annotationIds.put(annotation.getId(), jaa); + jaa.annotationId = annotation.getId(); + } + // recover sequence association + String sequenceRef = annotation.getSequenceRef(); + if (sequenceRef != null) + { + // from 2.9 sequenceRef is to sequence id (JAL-1781) + SequenceI sequence = seqRefIds.get(sequenceRef); + if (sequence == null) + { + // in pre-2.9 projects sequence ref is to sequence name + sequence = al.findName(sequenceRef); + } + if (sequence != null) + { + jaa.createSequenceMapping(sequence, 1, true); + sequence.addAlignmentAnnotation(jaa); + } + } + // and make a note of any group association + if (annotation.getGroupRef() != null + && annotation.getGroupRef().length() > 0) + { + List aal = groupAnnotRefs + .get(annotation.getGroupRef()); + if (aal == null) + { + aal = new ArrayList<>(); + groupAnnotRefs.put(annotation.getGroupRef(), aal); + } + aal.add(jaa); + } + + if (annotation.getScore() != null) + { + jaa.setScore(annotation.getScore().doubleValue()); + } + if (annotation.isVisible() != null) + { + jaa.visible = annotation.isVisible().booleanValue(); + } + + if (annotation.isCentreColLabels() != null) + { + jaa.centreColLabels = annotation.isCentreColLabels() + .booleanValue(); + } + + if (annotation.isScaleColLabels() != null) + { + jaa.scaleColLabel = annotation.isScaleColLabels().booleanValue(); + } + if (annotation.isAutoCalculated()) + { + // newer files have an 'autoCalculated' flag and store calculation + // state in viewport properties + jaa.autoCalculated = true; // means annotation will be marked for + // update at end of load. + } + if (annotation.getGraphHeight() != null) + { + jaa.graphHeight = annotation.getGraphHeight().intValue(); + } + jaa.belowAlignment = annotation.isBelowAlignment(); + jaa.setCalcId(annotation.getCalcId()); + if (annotation.getProperty().size() > 0) + { + for (Annotation.Property prop : annotation.getProperty()) + { + jaa.setProperty(prop.getName(), prop.getValue()); + } + } + if (jaa.autoCalculated) + { + autoAlan.add(new JvAnnotRow(i, jaa)); + } + else + // if (!autoForView) + { + // add autocalculated group annotation and any user created annotation + // for the view + al.addAnnotation(jaa); + } + } + } + // /////////////////////// + // LOAD GROUPS + // Create alignment markup and styles for this view + if (jalviewModel.getJGroup().size() > 0) + { + List groups = jalviewModel.getJGroup(); + boolean addAnnotSchemeGroup = false; + for (int i = 0; i < groups.size(); i++) + { + JGroup jGroup = groups.get(i); + ColourSchemeI cs = null; + if (jGroup.getColour() != null) + { + if (jGroup.getColour().startsWith("ucs")) + { + cs = getUserColourScheme(jalviewModel, jGroup.getColour()); + } + else if (jGroup.getColour().equals("AnnotationColourGradient") + && jGroup.getAnnotationColours() != null) + { + addAnnotSchemeGroup = true; + } + else + { + cs = ColourSchemeProperty.getColourScheme(null, al, + jGroup.getColour()); + } + } + int pidThreshold = safeInt(jGroup.getPidThreshold()); + + Vector seqs = new Vector<>(); + + for (int s = 0; s < jGroup.getSeq().size(); s++) + { + String seqId = jGroup.getSeq().get(s); + SequenceI ts = seqRefIds.get(seqId); + + if (ts != null) + { + seqs.addElement(ts); + } + } + + if (seqs.size() < 1) + { + continue; + } + + SequenceGroup sg = new SequenceGroup(seqs, jGroup.getName(), cs, + safeBoolean(jGroup.isDisplayBoxes()), + safeBoolean(jGroup.isDisplayText()), + safeBoolean(jGroup.isColourText()), + safeInt(jGroup.getStart()), safeInt(jGroup.getEnd())); + sg.getGroupColourScheme().setThreshold(pidThreshold, true); + sg.getGroupColourScheme() + .setConservationInc(safeInt(jGroup.getConsThreshold())); + sg.setOutlineColour(new Color(safeInt(jGroup.getOutlineColour()))); + + sg.textColour = new Color(safeInt(jGroup.getTextCol1())); + sg.textColour2 = new Color(safeInt(jGroup.getTextCol2())); + sg.setShowNonconserved(safeBoolean(jGroup.isShowUnconserved())); + sg.thresholdTextColour = safeInt(jGroup.getTextColThreshold()); + // attributes with a default in the schema are never null + sg.setShowConsensusHistogram(jGroup.isShowConsensusHistogram()); + sg.setshowSequenceLogo(jGroup.isShowSequenceLogo()); + sg.setNormaliseSequenceLogo(jGroup.isNormaliseSequenceLogo()); + sg.setIgnoreGapsConsensus(jGroup.isIgnoreGapsinConsensus()); + if (jGroup.getConsThreshold() != null + && jGroup.getConsThreshold().intValue() != 0) + { + Conservation c = new Conservation("All", sg.getSequences(null), 0, + sg.getWidth() - 1); + c.calculate(); + c.verdict(false, 25); + sg.cs.setConservation(c); + } + + if (jGroup.getId() != null && groupAnnotRefs.size() > 0) + { + // re-instate unique group/annotation row reference + List jaal = groupAnnotRefs + .get(jGroup.getId()); + if (jaal != null) + { + for (AlignmentAnnotation jaa : jaal) + { + jaa.groupRef = sg; + if (jaa.autoCalculated) + { + // match up and try to set group autocalc alignment row for this + // annotation + if (jaa.label.startsWith("Consensus for ")) + { + sg.setConsensus(jaa); + } + // match up and try to set group autocalc alignment row for this + // annotation + if (jaa.label.startsWith("Conservation for ")) + { + sg.setConservationRow(jaa); + } + } + } + } + } + al.addGroup(sg); + if (addAnnotSchemeGroup) + { + // reconstruct the annotation colourscheme + sg.setColourScheme( + constructAnnotationColour(jGroup.getAnnotationColours(), + null, al, jalviewModel, false)); + } + } + } + if (view == null) + { + // only dataset in this model, so just return. + return null; + } + // /////////////////////////////// + // LOAD VIEWPORT + + AlignFrame af = null; + AlignViewport av = null; + // now check to see if we really need to create a new viewport. + if (multipleView && viewportsAdded.size() == 0) + { + // We recovered an alignment for which a viewport already exists. + // TODO: fix up any settings necessary for overlaying stored state onto + // state recovered from another document. (may not be necessary). + // we may need a binding from a viewport in memory to one recovered from + // XML. + // and then recover its containing af to allow the settings to be applied. + // TODO: fix for vamsas demo + System.err.println( + "About to recover a viewport for existing alignment: Sequence set ID is " + + uniqueSeqSetId); + Object seqsetobj = retrieveExistingObj(uniqueSeqSetId); + if (seqsetobj != null) + { + if (seqsetobj instanceof String) + { + uniqueSeqSetId = (String) seqsetobj; + System.err.println( + "Recovered extant sequence set ID mapping for ID : New Sequence set ID is " + + uniqueSeqSetId); + } + else + { + System.err.println( + "Warning : Collision between sequence set ID string and existing jalview object mapping."); + } + + } + } + /** + * indicate that annotation colours are applied across all groups (pre + * Jalview 2.8.1 behaviour) + */ + boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1", + jalviewModel.getVersion()); + + AlignmentPanel ap = null; + boolean isnewview = true; + if (viewId != null) + { + // Check to see if this alignment already has a view id == viewId + jalview.gui.AlignmentPanel views[] = Desktop + .getAlignmentPanels(uniqueSeqSetId); + if (views != null && views.length > 0) + { + for (int v = 0; v < views.length; v++) + { + if (views[v].av.getViewId().equalsIgnoreCase(viewId)) + { + // recover the existing alignpanel, alignframe, viewport + af = views[v].alignFrame; + av = views[v].av; + ap = views[v]; + // TODO: could even skip resetting view settings if we don't want to + // change the local settings from other jalview processes + isnewview = false; + } + } + } + } + + if (isnewview) + { + af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view, + uniqueSeqSetId, viewId, autoAlan); + av = af.getViewport(); + ap = af.alignPanel; + } + + /* + * Load any trees, PDB structures and viewers + * + * Not done if flag is false (when this method is used for New View) + */ + if (loadTreesAndStructures) + { + loadTrees(jalviewModel, view, af, av, ap); ++ loadExternalTrees(jprovider, jalviewModel, av); + loadPCAViewers(jalviewModel, ap); + loadPDBStructures(jprovider, jseqs, af, ap); + loadRnaViewers(jprovider, jseqs, ap); + } + // and finally return. + return af; + } ++ ++ private void loadExternalTrees(jarInputStreamProvider jprovider, ++ JalviewModel jms, AlignViewport av) ++ { ++ // TODO: allow more than one archeopteryx session per project ++ String treeFile = copyJarEntry(jprovider, "aptx-test", "aptx", null); ++ if (treeFile != null) ++ { ++ try ++ { ++ AptxInit.createInstancesFromFile(treeFile, av); ++ } catch (IOException e) ++ { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } ++ ++ } ++ ++ + + /** + * Instantiate and link any saved RNA (Varna) viewers. The state of the Varna + * panel is restored from separate jar entries, two (gapped and trimmed) per + * sequence and secondary structure. + * + * Currently each viewer shows just one sequence and structure (gapped and + * trimmed), however this method is designed to support multiple sequences or + * structures in viewers if wanted in future. + * + * @param jprovider + * @param jseqs + * @param ap + */ + private void loadRnaViewers(jarInputStreamProvider jprovider, + List jseqs, AlignmentPanel ap) + { + /* + * scan the sequences for references to viewers; create each one the first + * time it is referenced, add Rna models to existing viewers + */ + for (JSeq jseq : jseqs) + { + for (int i = 0; i < jseq.getRnaViewer().size(); i++) + { + RnaViewer viewer = jseq.getRnaViewer().get(i); + AppVarna appVarna = findOrCreateVarnaViewer(viewer, uniqueSetSuffix, + ap); + + for (int j = 0; j < viewer.getSecondaryStructure().size(); j++) + { + SecondaryStructure ss = viewer.getSecondaryStructure().get(j); + SequenceI seq = seqRefIds.get(jseq.getId()); + AlignmentAnnotation ann = this.annotationIds + .get(ss.getAnnotationId()); + + /* + * add the structure to the Varna display (with session state copied + * from the jar to a temporary file) + */ + boolean gapped = safeBoolean(ss.isGapped()); + String rnaTitle = ss.getTitle(); + String sessionState = ss.getViewerState(); + String tempStateFile = copyJarEntry(jprovider, sessionState, + "varna", null); + RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped); + appVarna.addModelSession(rna, rnaTitle, tempStateFile); + } + appVarna.setInitialSelection(safeInt(viewer.getSelectedRna())); + } + } + } + + /** + * Locate and return an already instantiated matching AppVarna, or create one + * if not found + * + * @param viewer + * @param viewIdSuffix + * @param ap + * @return + */ + protected AppVarna findOrCreateVarnaViewer(RnaViewer viewer, + String viewIdSuffix, AlignmentPanel ap) + { + /* + * on each load a suffix is appended to the saved viewId, to avoid conflicts + * if load is repeated + */ + String postLoadId = viewer.getViewId() + viewIdSuffix; + for (JInternalFrame frame : getAllFrames()) + { + if (frame instanceof AppVarna) + { + AppVarna varna = (AppVarna) frame; + if (postLoadId.equals(varna.getViewId())) + { + // this viewer is already instantiated + // could in future here add ap as another 'parent' of the + // AppVarna window; currently just 1-to-many + return varna; + } + } + } + + /* + * viewer not found - make it + */ + RnaViewerModel model = new RnaViewerModel(postLoadId, viewer.getTitle(), + safeInt(viewer.getXpos()), safeInt(viewer.getYpos()), + safeInt(viewer.getWidth()), safeInt(viewer.getHeight()), + safeInt(viewer.getDividerLocation())); + AppVarna varna = new AppVarna(model, ap); + + return varna; + } + + /** + * Load any saved trees + * + * @param jm + * @param view + * @param af + * @param av + * @param ap + */ + protected void loadTrees(JalviewModel jm, Viewport view, AlignFrame af, + AlignViewport av, AlignmentPanel ap) + { + // TODO result of automated refactoring - are all these parameters needed? + try + { + for (int t = 0; t < jm.getTree().size(); t++) + { + + Tree tree = jm.getTree().get(t); + ++ TreeFrameI externalViewer = AptxInit.createInstanceFromNhx( ++ tree.getTitle(), tree.getNewick(), ++ av); ++ + TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId()); + if (tp == null) + { + tp = af.showNewickTree(new NewickFile(tree.getNewick()), + tree.getTitle(), safeInt(tree.getWidth()), + safeInt(tree.getHeight()), safeInt(tree.getXpos()), + safeInt(tree.getYpos())); + if (tree.getId() != null) + { + // perhaps bind the tree id to something ? + } + } + else + { + // update local tree attributes ? + // TODO: should check if tp has been manipulated by user - if so its + // settings shouldn't be modified + tp.setTitle(tree.getTitle()); + tp.setBounds(new Rectangle(safeInt(tree.getXpos()), + safeInt(tree.getYpos()), safeInt(tree.getWidth()), + safeInt(tree.getHeight()))); + tp.setViewport(av); // af.viewport; + // TODO: verify 'associate with all views' works still + tp.getTreeCanvas().setViewport(av); // af.viewport; + tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel; + } + tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews()); + if (tp == null) + { + Console.warn( + "There was a problem recovering stored Newick tree: \n" + + tree.getNewick()); + continue; + } + + tp.fitToWindow.setState(safeBoolean(tree.isFitToWindow())); + tp.fitToWindow_actionPerformed(null); + + if (tree.getFontName() != null) + { + tp.setTreeFont( + new Font(tree.getFontName(), safeInt(tree.getFontStyle()), + safeInt(tree.getFontSize()))); + } + else + { + tp.setTreeFont( + new Font(view.getFontName(), safeInt(view.getFontStyle()), + safeInt(view.getFontSize()))); + } + + tp.showPlaceholders(safeBoolean(tree.isMarkUnlinked())); + tp.showBootstrap(safeBoolean(tree.isShowBootstrap())); + tp.showDistances(safeBoolean(tree.isShowDistances())); + + tp.getTreeCanvas().setThreshold(safeFloat(tree.getThreshold())); + + if (safeBoolean(tree.isCurrentTree())) + { + af.getViewport().setCurrentTree(tp.getTree()); + } + } + + } catch (Exception ex) + { + ex.printStackTrace(); + } + } + + /** + * Load and link any saved structure viewers. + * + * @param jprovider + * @param jseqs + * @param af + * @param ap + */ + protected void loadPDBStructures(jarInputStreamProvider jprovider, + List jseqs, AlignFrame af, AlignmentPanel ap) + { + /* + * Run through all PDB ids on the alignment, and collect mappings between + * distinct view ids and all sequences referring to that view. + */ + Map structureViewers = new LinkedHashMap<>(); + + for (int i = 0; i < jseqs.size(); i++) + { + JSeq jseq = jseqs.get(i); + if (jseq.getPdbids().size() > 0) + { + List ids = jseq.getPdbids(); + for (int p = 0; p < ids.size(); p++) + { + Pdbids pdbid = ids.get(p); + final int structureStateCount = pdbid.getStructureState().size(); + for (int s = 0; s < structureStateCount; s++) + { + // check to see if we haven't already created this structure view + final StructureState structureState = pdbid.getStructureState() + .get(s); + String sviewid = (structureState.getViewId() == null) ? null + : structureState.getViewId() + uniqueSetSuffix; + jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry(); + // Originally : pdbid.getFile() + // : TODO: verify external PDB file recovery still works in normal + // jalview project load + jpdb.setFile( + loadPDBFile(jprovider, pdbid.getId(), pdbid.getFile())); + jpdb.setId(pdbid.getId()); + + int x = safeInt(structureState.getXpos()); + int y = safeInt(structureState.getYpos()); + int width = safeInt(structureState.getWidth()); + int height = safeInt(structureState.getHeight()); + + // Probably don't need to do this anymore... + // Desktop.desktop.getComponentAt(x, y); + // TODO: NOW: check that this recovers the PDB file correctly. + String pdbFile = loadPDBFile(jprovider, pdbid.getId(), + pdbid.getFile()); + jalview.datamodel.SequenceI seq = seqRefIds + .get(jseq.getId() + ""); + if (sviewid == null) + { + sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width + "," + + height; + } + if (!structureViewers.containsKey(sviewid)) + { + String viewerType = structureState.getType(); + if (viewerType == null) // pre Jalview 2.9 + { + viewerType = ViewerType.JMOL.toString(); + } + structureViewers.put(sviewid, + new StructureViewerModel(x, y, width, height, false, + false, true, structureState.getViewId(), + viewerType)); + // Legacy pre-2.7 conversion JAL-823 : + // do not assume any view has to be linked for colour by + // sequence + } + + // assemble String[] { pdb files }, String[] { id for each + // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, { + // seqs_file 2}, boolean[] { + // linkAlignPanel,superposeWithAlignpanel}} from hash + StructureViewerModel jmoldat = structureViewers.get(sviewid); + jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel() + || structureState.isAlignwithAlignPanel()); + + /* + * Default colour by linked panel to false if not specified (e.g. + * for pre-2.7 projects) + */ + boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel(); + colourWithAlignPanel |= structureState.isColourwithAlignPanel(); + jmoldat.setColourWithAlignPanel(colourWithAlignPanel); + + /* + * Default colour by viewer to true if not specified (e.g. for + * pre-2.7 projects) + */ + boolean colourByViewer = jmoldat.isColourByViewer(); + colourByViewer &= structureState.isColourByJmol(); + jmoldat.setColourByViewer(colourByViewer); + + if (jmoldat.getStateData().length() < structureState.getValue() + /*Content()*/.length()) + { + jmoldat.setStateData(structureState.getValue());// Content()); + } + if (pdbid.getFile() != null) + { + File mapkey = new File(pdbid.getFile()); + StructureData seqstrmaps = jmoldat.getFileData().get(mapkey); + if (seqstrmaps == null) + { + jmoldat.getFileData().put(mapkey, + seqstrmaps = jmoldat.new StructureData(pdbFile, + pdbid.getId())); + } + if (!seqstrmaps.getSeqList().contains(seq)) + { + seqstrmaps.getSeqList().add(seq); + // TODO and chains? + } + } + else + { + errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747"); + Console.warn(errorMessage); + } + } + } + } + } + // Instantiate the associated structure views + for (Entry entry : structureViewers + .entrySet()) + { + try + { + createOrLinkStructureViewer(entry, af, ap, jprovider); + } catch (Exception e) + { + System.err.println( + "Error loading structure viewer: " + e.getMessage()); + // failed - try the next one + } + } + } + + /** + * + * @param viewerData + * @param af + * @param ap + * @param jprovider + */ + protected void createOrLinkStructureViewer( + Entry viewerData, AlignFrame af, + AlignmentPanel ap, jarInputStreamProvider jprovider) + { + final StructureViewerModel stateData = viewerData.getValue(); + + /* + * Search for any viewer windows already open from other alignment views + * that exactly match the stored structure state + */ + StructureViewerBase comp = findMatchingViewer(viewerData); + + if (comp != null) + { + linkStructureViewer(ap, comp, stateData); + return; + } + + String type = stateData.getType(); + try + { + ViewerType viewerType = ViewerType.valueOf(type); + createStructureViewer(viewerType, viewerData, af, jprovider); + } catch (IllegalArgumentException | NullPointerException e) + { + // TODO JAL-3619 show error dialog / offer an alternative viewer + Console.error("Invalid structure viewer type: " + type); + } + } + + /** + * Generates a name for the entry in the project jar file to hold state + * information for a structure viewer + * + * @param viewId + * @return + */ + protected String getViewerJarEntryName(String viewId) + { + return VIEWER_PREFIX + viewId; + } + + /** + * Returns any open frame that matches given structure viewer data. The match + * is based on the unique viewId, or (for older project versions) the frame's + * geometry. + * + * @param viewerData + * @return + */ + protected StructureViewerBase findMatchingViewer( + Entry viewerData) + { + final String sviewid = viewerData.getKey(); + final StructureViewerModel svattrib = viewerData.getValue(); + StructureViewerBase comp = null; + JInternalFrame[] frames = getAllFrames(); + for (JInternalFrame frame : frames) + { + if (frame instanceof StructureViewerBase) + { + /* + * Post jalview 2.4 schema includes structure view id + */ + if (sviewid != null && ((StructureViewerBase) frame).getViewId() + .equals(sviewid)) + { + comp = (StructureViewerBase) frame; + break; // break added in 2.9 + } + /* + * Otherwise test for matching position and size of viewer frame + */ + else if (frame.getX() == svattrib.getX() + && frame.getY() == svattrib.getY() + && frame.getHeight() == svattrib.getHeight() + && frame.getWidth() == svattrib.getWidth()) + { + comp = (StructureViewerBase) frame; + // no break in faint hope of an exact match on viewId + } + } + } + return comp; + } + + /** + * Link an AlignmentPanel to an existing structure viewer. + * + * @param ap + * @param viewer + * @param oldFiles + * @param useinViewerSuperpos + * @param usetoColourbyseq + * @param viewerColouring + */ + protected void linkStructureViewer(AlignmentPanel ap, + StructureViewerBase viewer, StructureViewerModel stateData) + { + // NOTE: if the jalview project is part of a shared session then + // view synchronization should/could be done here. + + final boolean useinViewerSuperpos = stateData.isAlignWithPanel(); + final boolean usetoColourbyseq = stateData.isColourWithAlignPanel(); + final boolean viewerColouring = stateData.isColourByViewer(); + Map oldFiles = stateData.getFileData(); + + /* + * Add mapping for sequences in this view to an already open viewer + */ + final AAStructureBindingModel binding = viewer.getBinding(); + for (File id : oldFiles.keySet()) + { + // add this and any other pdb files that should be present in the + // viewer + StructureData filedat = oldFiles.get(id); + String pdbFile = filedat.getFilePath(); + SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]); + binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE, + null); + binding.addSequenceForStructFile(pdbFile, seq); + } + // and add the AlignmentPanel's reference to the view panel + viewer.addAlignmentPanel(ap); + if (useinViewerSuperpos) + { + viewer.useAlignmentPanelForSuperposition(ap); + } + else + { + viewer.excludeAlignmentPanelForSuperposition(ap); + } + if (usetoColourbyseq) + { + viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring); + } + else + { + viewer.excludeAlignmentPanelForColourbyseq(ap); + } + } + + /** + * Get all frames within the Desktop. + * + * @return + */ + protected JInternalFrame[] getAllFrames() + { + JInternalFrame[] frames = null; + // TODO is this necessary - is it safe - risk of hanging? + do + { + try + { + frames = Desktop.desktop.getAllFrames(); + } catch (ArrayIndexOutOfBoundsException e) + { + // occasional No such child exceptions are thrown here... + try + { + Thread.sleep(10); + } catch (InterruptedException f) + { + } + } + } while (frames == null); + return frames; + } + + /** + * Answers true if 'version' is equal to or later than 'supported', where each + * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix + * changes. Development and test values for 'version' are leniently treated + * i.e. answer true. + * + * @param supported + * - minimum version we are comparing against + * @param version + * - version of data being processsed + * @return + */ + public static boolean isVersionStringLaterThan(String supported, + String version) + { + if (supported == null || version == null + || version.equalsIgnoreCase("DEVELOPMENT BUILD") + || version.equalsIgnoreCase("Test") + || version.equalsIgnoreCase("AUTOMATED BUILD")) + { + System.err.println("Assuming project file with " + + (version == null ? "null" : version) + + " is compatible with Jalview version " + supported); + return true; + } + else + { + return StringUtils.compareVersions(version, supported, "b") >= 0; + } + } + + Vector newStructureViewers = null; + + protected void addNewStructureViewer(JalviewStructureDisplayI sview) + { + if (newStructureViewers != null) + { + sview.getBinding().setFinishedLoadingFromArchive(false); + newStructureViewers.add(sview); + } + } + + protected void setLoadingFinishedForNewStructureViewers() + { + if (newStructureViewers != null) + { + for (JalviewStructureDisplayI sview : newStructureViewers) + { + sview.getBinding().setFinishedLoadingFromArchive(true); + } + newStructureViewers.clear(); + newStructureViewers = null; + } + } + + AlignFrame loadViewport(String file, List JSEQ, + List hiddenSeqs, AlignmentI al, JalviewModel jm, + Viewport view, String uniqueSeqSetId, String viewId, + List autoAlan) + { + AlignFrame af = null; + af = new AlignFrame(al, safeInt(view.getWidth()), + 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); + + final AlignViewport viewport = af.getViewport(); + for (int i = 0; i < JSEQ.size(); i++) + { + int colour = safeInt(JSEQ.get(i).getColour()); + viewport.setSequenceColour(viewport.getAlignment().getSequenceAt(i), + new Color(colour)); + } + + if (al.hasSeqrep()) + { + viewport.setColourByReferenceSeq(true); + viewport.setDisplayReferenceSeq(true); + } + + viewport.setGatherViewsHere(safeBoolean(view.isGatheredViews())); + + if (view.getSequenceSetId() != null) + { + AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId); + + viewport.setSequenceSetId(uniqueSeqSetId); + if (av != null) + { + // propagate shared settings to this new view + viewport.setHistoryList(av.getHistoryList()); + viewport.setRedoList(av.getRedoList()); + } + else + { + viewportsAdded.put(uniqueSeqSetId, viewport); + } + // TODO: check if this method can be called repeatedly without + // side-effects if alignpanel already registered. + PaintRefresher.Register(af.alignPanel, uniqueSeqSetId); + } + // apply Hidden regions to view. + if (hiddenSeqs != null) + { + for (int s = 0; s < JSEQ.size(); s++) + { + SequenceGroup hidden = new SequenceGroup(); + boolean isRepresentative = false; + for (int r = 0; r < JSEQ.get(s).getHiddenSequences().size(); r++) + { + isRepresentative = true; + SequenceI sequenceToHide = al + .getSequenceAt(JSEQ.get(s).getHiddenSequences().get(r)); + hidden.addSequence(sequenceToHide, false); + // remove from hiddenSeqs list so we don't try to hide it twice + hiddenSeqs.remove(sequenceToHide); + } + if (isRepresentative) + { + SequenceI representativeSequence = al.getSequenceAt(s); + hidden.addSequence(representativeSequence, false); + viewport.hideRepSequences(representativeSequence, hidden); + } + } + + SequenceI[] hseqs = hiddenSeqs + .toArray(new SequenceI[hiddenSeqs.size()]); + viewport.hideSequence(hseqs); + + } + // recover view properties and display parameters + + viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation())); + viewport.setAbovePIDThreshold(safeBoolean(view.isPidSelected())); + final int pidThreshold = safeInt(view.getPidThreshold()); + viewport.setThreshold(pidThreshold); + + viewport.setColourText(safeBoolean(view.isShowColourText())); + + viewport.setConservationSelected( + safeBoolean(view.isConservationSelected())); + viewport.setIncrement(safeInt(view.getConsThreshold())); + viewport.setShowJVSuffix(safeBoolean(view.isShowFullId())); + viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds())); + viewport.setFont(new Font(view.getFontName(), + safeInt(view.getFontStyle()), safeInt(view.getFontSize())), + true); + ViewStyleI vs = viewport.getViewStyle(); + vs.setScaleProteinAsCdna(view.isScaleProteinAsCdna()); + viewport.setViewStyle(vs); + // TODO: allow custom charWidth/Heights to be restored by updating them + // after setting font - which means set above to false + viewport.setRenderGaps(safeBoolean(view.isRenderGaps())); + viewport.setWrapAlignment(safeBoolean(view.isWrapAlignment())); + viewport.setShowAnnotation(safeBoolean(view.isShowAnnotation())); + + viewport.setShowBoxes(safeBoolean(view.isShowBoxes())); + + viewport.setShowText(safeBoolean(view.isShowText())); + + viewport.setTextColour(new Color(safeInt(view.getTextCol1()))); + viewport.setTextColour2(new Color(safeInt(view.getTextCol2()))); + viewport.setThresholdTextColour(safeInt(view.getTextColThreshold())); + viewport.setShowUnconserved(view.isShowUnconserved()); + viewport.getRanges().setStartRes(safeInt(view.getStartRes())); + + if (view.getViewName() != null) + { + viewport.setViewName(view.getViewName()); + af.setInitialTabVisible(); + } + af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()), + safeInt(view.getWidth()), safeInt(view.getHeight())); + // startSeq set in af.alignPanel.updateLayout below + af.alignPanel.updateLayout(); + ColourSchemeI cs = null; + // apply colourschemes + if (view.getBgColour() != null) + { + if (view.getBgColour().startsWith("ucs")) + { + cs = getUserColourScheme(jm, view.getBgColour()); + } + else if (view.getBgColour().startsWith("Annotation")) + { + AnnotationColourScheme viewAnnColour = view.getAnnotationColours(); + cs = constructAnnotationColour(viewAnnColour, af, al, jm, true); + + // annpos + + } + else + { + cs = ColourSchemeProperty.getColourScheme(af.getViewport(), al, + view.getBgColour()); + } + } + + /* + * turn off 'alignment colour applies to all groups' + * while restoring global colour scheme + */ + viewport.setColourAppliesToAllGroups(false); + viewport.setGlobalColourScheme(cs); + viewport.getResidueShading().setThreshold(pidThreshold, + view.isIgnoreGapsinConsensus()); + viewport.getResidueShading() + .setConsensus(viewport.getSequenceConsensusHash()); + if (safeBoolean(view.isConservationSelected()) && cs != null) + { + viewport.getResidueShading() + .setConservationInc(safeInt(view.getConsThreshold())); + } + af.changeColour(cs); + viewport.setColourAppliesToAllGroups(true); + + viewport.setShowSequenceFeatures( + safeBoolean(view.isShowSequenceFeatures())); + + viewport.setCentreColumnLabels(view.isCentreColumnLabels()); + viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null); + viewport.setFollowHighlight(view.isFollowHighlight()); + viewport.followSelection = view.isFollowSelection(); + viewport.setShowConsensusHistogram(view.isShowConsensusHistogram()); + viewport.setShowSequenceLogo(view.isShowSequenceLogo()); + viewport.setNormaliseSequenceLogo(view.isNormaliseSequenceLogo()); + viewport.setShowDBRefs(safeBoolean(view.isShowDbRefTooltip())); + viewport.setShowNPFeats(safeBoolean(view.isShowNPfeatureTooltip())); + viewport.setShowGroupConsensus(view.isShowGroupConsensus()); + viewport.setShowGroupConservation(view.isShowGroupConservation()); + viewport.setShowComplementFeatures(view.isShowComplementFeatures()); + viewport.setShowComplementFeaturesOnTop( + view.isShowComplementFeaturesOnTop()); + + // recover feature settings + if (jm.getFeatureSettings() != null) + { + FeatureRendererModel fr = af.alignPanel.getSeqPanel().seqCanvas + .getFeatureRenderer(); + FeaturesDisplayed fdi; + viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed()); + String[] renderOrder = new String[jm.getFeatureSettings().getSetting() + .size()]; + Map featureColours = new Hashtable<>(); + Map featureOrder = new Hashtable<>(); + + for (int fs = 0; fs < jm.getFeatureSettings().getSetting() + .size(); fs++) + { + Setting setting = jm.getFeatureSettings().getSetting().get(fs); + String featureType = setting.getType(); + + /* + * restore feature filters (if any) + */ + jalview.xml.binding.jalview.FeatureMatcherSet filters = setting + .getMatcherSet(); + if (filters != null) + { + FeatureMatcherSetI filter = Jalview2XML.parseFilter(featureType, + filters); + if (!filter.isEmpty()) + { + fr.setFeatureFilter(featureType, filter); + } + } + + /* + * restore feature colour scheme + */ + Color maxColour = new Color(setting.getColour()); + if (setting.getMincolour() != null) + { + /* + * minColour is always set unless a simple colour + * (including for colour by label though it doesn't use it) + */ + Color minColour = new Color(setting.getMincolour().intValue()); + Color noValueColour = minColour; + NoValueColour noColour = setting.getNoValueColour(); + if (noColour == NoValueColour.NONE) + { + noValueColour = null; + } + else if (noColour == NoValueColour.MAX) + { + noValueColour = maxColour; + } + float min = safeFloat(safeFloat(setting.getMin())); + float max = setting.getMax() == null ? 1f + : setting.getMax().floatValue(); + FeatureColourI gc = new FeatureColour(maxColour, minColour, + maxColour, noValueColour, min, max); + if (setting.getAttributeName().size() > 0) + { + gc.setAttributeName(setting.getAttributeName().toArray( + new String[setting.getAttributeName().size()])); + } + if (setting.getThreshold() != null) + { + gc.setThreshold(setting.getThreshold().floatValue()); + int threshstate = safeInt(setting.getThreshstate()); + // -1 = None, 0 = Below, 1 = Above threshold + if (threshstate == 0) + { + gc.setBelowThreshold(true); + } + else if (threshstate == 1) + { + gc.setAboveThreshold(true); + } + } + gc.setAutoScaled(true); // default + if (setting.isAutoScale() != null) + { + gc.setAutoScaled(setting.isAutoScale()); + } + if (setting.isColourByLabel() != null) + { + gc.setColourByLabel(setting.isColourByLabel()); + } + // and put in the feature colour table. + featureColours.put(featureType, gc); + } + else + { + featureColours.put(featureType, new FeatureColour(maxColour)); + } + renderOrder[fs] = featureType; + if (setting.getOrder() != null) + { + featureOrder.put(featureType, setting.getOrder().floatValue()); + } + else + { + featureOrder.put(featureType, Float.valueOf( + fs / jm.getFeatureSettings().getSetting().size())); + } + if (safeBoolean(setting.isDisplay())) + { + fdi.setVisible(featureType); + } + } + Map fgtable = new Hashtable<>(); + for (int gs = 0; gs < jm.getFeatureSettings().getGroup().size(); gs++) + { + Group grp = jm.getFeatureSettings().getGroup().get(gs); + fgtable.put(grp.getName(), Boolean.valueOf(grp.isDisplay())); + } + // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder, + // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ? + // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder); + FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder, + fgtable, featureColours, 1.0f, featureOrder); + fr.transferSettings(frs); + } + + if (view.getHiddenColumns().size() > 0) + { + for (int c = 0; c < view.getHiddenColumns().size(); c++) + { + final HiddenColumns hc = view.getHiddenColumns().get(c); + viewport.hideColumns(safeInt(hc.getStart()), + safeInt(hc.getEnd()) /* +1 */); + } + } + if (view.getCalcIdParam() != null) + { + for (CalcIdParam calcIdParam : view.getCalcIdParam()) + { + if (calcIdParam != null) + { + if (recoverCalcIdParam(calcIdParam, viewport)) + { + } + else + { + Console.warn("Couldn't recover parameters for " + + calcIdParam.getCalcId()); + } + } + } + } + af.setMenusFromViewport(viewport); + af.setTitle(view.getTitle()); + // TODO: we don't need to do this if the viewport is aready visible. + /* + * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it + * has a 'cdna/protein complement' view, in which case save it in order to + * populate a SplitFrame once all views have been read in. + */ + String complementaryViewId = view.getComplementId(); + if (complementaryViewId == null) + { + Desktop.addInternalFrame(af, view.getTitle(), + safeInt(view.getWidth()), safeInt(view.getHeight())); + // recompute any autoannotation + af.alignPanel.updateAnnotation(false, true); + reorderAutoannotation(af, al, autoAlan); + af.alignPanel.alignmentChanged(); + } + else + { + splitFrameCandidates.put(view, af); + } + return af; + } + + /** + * Reads saved data to restore Colour by Annotation settings + * + * @param viewAnnColour + * @param af + * @param al + * @param model + * @param checkGroupAnnColour + * @return + */ + private ColourSchemeI constructAnnotationColour( + AnnotationColourScheme viewAnnColour, AlignFrame af, + AlignmentI al, JalviewModel model, boolean checkGroupAnnColour) + { + boolean propagateAnnColour = false; + AlignmentI annAlignment = af != null ? af.getViewport().getAlignment() + : al; + if (checkGroupAnnColour && al.getGroups() != null + && al.getGroups().size() > 0) + { + // pre 2.8.1 behaviour + // check to see if we should transfer annotation colours + propagateAnnColour = true; + for (SequenceGroup sg : al.getGroups()) + { + if (sg.getColourScheme() instanceof AnnotationColourGradient) + { + propagateAnnColour = false; + } + } + } + + /* + * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId + */ + String annotationId = viewAnnColour.getAnnotation(); + AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId); + + /* + * pre 2.10.2: saved annotationId is AlignmentAnnotation.label + */ + if (matchedAnnotation == null + && annAlignment.getAlignmentAnnotation() != null) + { + for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++) + { + if (annotationId + .equals(annAlignment.getAlignmentAnnotation()[i].label)) + { + matchedAnnotation = annAlignment.getAlignmentAnnotation()[i]; + break; + } + } + } + if (matchedAnnotation == null) + { + System.err.println("Failed to match annotation colour scheme for " + + annotationId); + return null; + } + if (matchedAnnotation.getThreshold() == null) + { + matchedAnnotation.setThreshold( + new GraphLine(safeFloat(viewAnnColour.getThreshold()), + "Threshold", Color.black)); + } + + AnnotationColourGradient cs = null; + if (viewAnnColour.getColourScheme().equals("None")) + { + cs = new AnnotationColourGradient(matchedAnnotation, + new Color(safeInt(viewAnnColour.getMinColour())), + new Color(safeInt(viewAnnColour.getMaxColour())), + safeInt(viewAnnColour.getAboveThreshold())); + } + else if (viewAnnColour.getColourScheme().startsWith("ucs")) + { + cs = new AnnotationColourGradient(matchedAnnotation, + getUserColourScheme(model, viewAnnColour.getColourScheme()), + safeInt(viewAnnColour.getAboveThreshold())); + } + else + { + cs = new AnnotationColourGradient(matchedAnnotation, + ColourSchemeProperty.getColourScheme(af.getViewport(), al, + viewAnnColour.getColourScheme()), + safeInt(viewAnnColour.getAboveThreshold())); + } + + boolean perSequenceOnly = safeBoolean(viewAnnColour.isPerSequence()); + boolean useOriginalColours = safeBoolean( + viewAnnColour.isPredefinedColours()); + cs.setSeqAssociated(perSequenceOnly); + cs.setPredefinedColours(useOriginalColours); + + if (propagateAnnColour && al.getGroups() != null) + { + // Also use these settings for all the groups + for (int g = 0; g < al.getGroups().size(); g++) + { + SequenceGroup sg = al.getGroups().get(g); + if (sg.getGroupColourScheme() == null) + { + continue; + } + + AnnotationColourGradient groupScheme = new AnnotationColourGradient( + matchedAnnotation, sg.getColourScheme(), + safeInt(viewAnnColour.getAboveThreshold())); + sg.setColourScheme(groupScheme); + groupScheme.setSeqAssociated(perSequenceOnly); + groupScheme.setPredefinedColours(useOriginalColours); + } + } + return cs; + } + + private void reorderAutoannotation(AlignFrame af, AlignmentI al, + List autoAlan) + { + // copy over visualization settings for autocalculated annotation in the + // view + if (al.getAlignmentAnnotation() != null) + { + /** + * Kludge for magic autoannotation names (see JAL-811) + */ + String[] magicNames = new String[] { "Consensus", "Quality", + "Conservation" }; + JvAnnotRow nullAnnot = new JvAnnotRow(-1, null); + Hashtable visan = new Hashtable<>(); + for (String nm : magicNames) + { + visan.put(nm, nullAnnot); + } + for (JvAnnotRow auan : autoAlan) + { + visan.put(auan.template.label + + (auan.template.getCalcId() == null ? "" + : "\t" + auan.template.getCalcId()), + auan); + } + int hSize = al.getAlignmentAnnotation().length; + List reorder = new ArrayList<>(); + // work through any autoCalculated annotation already on the view + // removing it if it should be placed in a different location on the + // annotation panel. + List remains = new ArrayList<>(visan.keySet()); + for (int h = 0; h < hSize; h++) + { + jalview.datamodel.AlignmentAnnotation jalan = al + .getAlignmentAnnotation()[h]; + if (jalan.autoCalculated) + { + String k; + JvAnnotRow valan = visan.get(k = jalan.label); + if (jalan.getCalcId() != null) + { + valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId()); + } + + if (valan != null) + { + // delete the auto calculated row from the alignment + al.deleteAnnotation(jalan, false); + remains.remove(k); + hSize--; + h--; + if (valan != nullAnnot) + { + if (jalan != valan.template) + { + // newly created autoannotation row instance + // so keep a reference to the visible annotation row + // and copy over all relevant attributes + if (valan.template.graphHeight >= 0) + + { + jalan.graphHeight = valan.template.graphHeight; + } + jalan.visible = valan.template.visible; + } + reorder.add(new JvAnnotRow(valan.order, jalan)); + } + } + } + } + // Add any (possibly stale) autocalculated rows that were not appended to + // the view during construction + for (String other : remains) + { + JvAnnotRow othera = visan.get(other); + if (othera != nullAnnot && othera.template.getCalcId() != null + && othera.template.getCalcId().length() > 0) + { + reorder.add(othera); + } + } + // now put the automatic annotation in its correct place + int s = 0, srt[] = new int[reorder.size()]; + JvAnnotRow[] rws = new JvAnnotRow[reorder.size()]; + for (JvAnnotRow jvar : reorder) + { + rws[s] = jvar; + srt[s++] = jvar.order; + } + reorder.clear(); + jalview.util.QuickSort.sort(srt, rws); + // and re-insert the annotation at its correct position + for (JvAnnotRow jvar : rws) + { + al.addAnnotation(jvar.template, jvar.order); + } + af.alignPanel.adjustAnnotationHeight(); + } + } + + Hashtable skipList = null; + + /** + * TODO remove this method + * + * @param view + * @return AlignFrame bound to sequenceSetId from view, if one exists. private + * AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) { + * throw new Error("Implementation Error. No skipList defined for this + * Jalview2XML instance."); } return (AlignFrame) + * skipList.get(view.getSequenceSetId()); } + */ + + /** + * Check if the Jalview view contained in object should be skipped or not. + * + * @param object + * @return true if view's sequenceSetId is a key in skipList + */ + private boolean skipViewport(JalviewModel object) + { + if (skipList == null) + { + return false; + } + String id = object.getViewport().get(0).getSequenceSetId(); + if (skipList.containsKey(id)) + { + Console.debug("Skipping seuqence set id " + id); + return true; + } + return false; + } + + public void addToSkipList(AlignFrame af) + { + if (skipList == null) + { + skipList = new Hashtable(); + } + skipList.put(af.getViewport().getSequenceSetId(), af); + } + + public void clearSkipList() + { + if (skipList != null) + { + skipList.clear(); + skipList = null; + } + } + + private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al, + boolean ignoreUnrefed, String uniqueSeqSetId) + { + jalview.datamodel.AlignmentI ds = getDatasetFor( + vamsasSet.getDatasetId()); + AlignmentI xtant_ds = ds; + if (xtant_ds == null) + { + // good chance we are about to create a new dataset, but check if we've + // seen some of the dataset sequence IDs before. + // TODO: skip this check if we are working with project generated by + // version 2.11 or later + xtant_ds = checkIfHasDataset(vamsasSet.getSequence()); + if (xtant_ds != null) + { + ds = xtant_ds; + addDatasetRef(vamsasSet.getDatasetId(), ds); + } + } + Vector dseqs = null; + if (!ignoreUnrefed) + { + // recovering an alignment View + AlignmentI seqSetDS = getDatasetFor(UNIQSEQSETID + uniqueSeqSetId); + if (seqSetDS != null) + { + if (ds != null && ds != seqSetDS) + { + Console.warn( + "JAL-3171 regression: Overwriting a dataset reference for an alignment" + + " - CDS/Protein crossreference data may be lost"); + if (xtant_ds != null) + { + // This can only happen if the unique sequence set ID was bound to a + // dataset that did not contain any of the sequences in the view + // currently being restored. + Console.warn( + "JAL-3171 SERIOUS! TOTAL CONFUSION - please consider contacting the Jalview Development team so they can investigate why your project caused this message to be displayed."); + } + } + ds = seqSetDS; + addDatasetRef(vamsasSet.getDatasetId(), ds); + } + } + if (ds == null) + { + // try even harder to restore dataset + AlignmentI xtantDS = checkIfHasDataset(vamsasSet.getSequence()); + // create a list of new dataset sequences + dseqs = new Vector<>(); + } + for (int i = 0, iSize = vamsasSet.getSequence().size(); i < iSize; i++) + { + Sequence vamsasSeq = vamsasSet.getSequence().get(i); + ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i); + } + // create a new dataset + if (ds == null) + { + SequenceI[] dsseqs = new SequenceI[dseqs.size()]; + dseqs.copyInto(dsseqs); + ds = new jalview.datamodel.Alignment(dsseqs); + Console.debug("Created new dataset " + vamsasSet.getDatasetId() + + " for alignment " + System.identityHashCode(al)); + addDatasetRef(vamsasSet.getDatasetId(), ds); + } + // set the dataset for the newly imported alignment. + if (al.getDataset() == null && !ignoreUnrefed) + { + al.setDataset(ds); + // register dataset for the alignment's uniqueSeqSetId for legacy projects + addDatasetRef(UNIQSEQSETID + uniqueSeqSetId, ds); + } + updateSeqDatasetBinding(vamsasSet.getSequence(), ds); + } + + /** + * XML dataset sequence ID to materialised dataset reference + */ + HashMap seqToDataset = new HashMap<>(); + + /** + * @return the first materialised dataset reference containing a dataset + * sequence referenced in the given view + * @param list + * - sequences from the view + */ + AlignmentI checkIfHasDataset(List list) + { + for (Sequence restoredSeq : list) + { + AlignmentI datasetFor = seqToDataset.get(restoredSeq.getDsseqid()); + if (datasetFor != null) + { + return datasetFor; + } + } + return null; + } + + /** + * Register ds as the containing dataset for the dataset sequences referenced + * by sequences in list + * + * @param list + * - sequences in a view + * @param ds + */ + void updateSeqDatasetBinding(List list, AlignmentI ds) + { + for (Sequence restoredSeq : list) + { + AlignmentI prevDS = seqToDataset.put(restoredSeq.getDsseqid(), ds); + if (prevDS != null && prevDS != ds) + { + Console.warn("Dataset sequence appears in many datasets: " + + restoredSeq.getDsseqid()); + // TODO: try to merge! + } + } + } + + /** + * + * @param vamsasSeq + * sequence definition to create/merge dataset sequence for + * @param ds + * dataset alignment + * @param dseqs + * vector to add new dataset sequence to + * @param ignoreUnrefed + * - when true, don't create new sequences from vamsasSeq if it's id + * doesn't already have an asssociated Jalview sequence. + * @param vseqpos + * - used to reorder the sequence in the alignment according to the + * vamsasSeq array ordering, to preserve ordering of dataset + */ + private void ensureJalviewDatasetSequence(Sequence vamsasSeq, + AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, + int vseqpos) + { + // JBP TODO: Check this is called for AlCodonFrames to support recovery of + // xRef Codon Maps + SequenceI sq = seqRefIds.get(vamsasSeq.getId()); + boolean reorder = false; + SequenceI dsq = null; + if (sq != null && sq.getDatasetSequence() != null) + { + dsq = sq.getDatasetSequence(); + } + else + { + reorder = true; + } + if (sq == null && ignoreUnrefed) + { + return; + } + String sqid = vamsasSeq.getDsseqid(); + if (dsq == null) + { + // need to create or add a new dataset sequence reference to this sequence + if (sqid != null) + { + dsq = seqRefIds.get(sqid); + } + // check again + if (dsq == null) + { + // make a new dataset sequence + dsq = sq.createDatasetSequence(); + if (sqid == null) + { + // make up a new dataset reference for this sequence + sqid = seqHash(dsq); + } + dsq.setVamsasId(uniqueSetSuffix + sqid); + seqRefIds.put(sqid, dsq); + if (ds == null) + { + if (dseqs != null) + { + dseqs.addElement(dsq); + } + } + else + { + ds.addSequence(dsq); + } + } + else + { + if (sq != dsq) + { // make this dataset sequence sq's dataset sequence + sq.setDatasetSequence(dsq); + // and update the current dataset alignment + if (ds == null) + { + if (dseqs != null) + { + if (!dseqs.contains(dsq)) + { + dseqs.add(dsq); + } + } + else + { + if (ds.findIndex(dsq) < 0) + { + ds.addSequence(dsq); + } + } + } + } + } + } + // TODO: refactor this as a merge dataset sequence function + // now check that sq (the dataset sequence) sequence really is the union of + // all references to it + // boolean pre = sq.getStart() < dsq.getStart(); + // boolean post = sq.getEnd() > dsq.getEnd(); + // if (pre || post) + if (sq != dsq) + { + // StringBuffer sb = new StringBuffer(); + String newres = jalview.analysis.AlignSeq.extractGaps( + jalview.util.Comparison.GapChars, sq.getSequenceAsString()); + if (!newres.equalsIgnoreCase(dsq.getSequenceAsString()) + && newres.length() > dsq.getLength()) + { + // Update with the longer sequence. + synchronized (dsq) + { + /* + * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() - + * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) { + * sb.append(newres.substring(newres.length() - sq.getEnd() - + * dsq.getEnd())); dsq.setEnd(sq.getEnd()); } + */ + dsq.setSequence(newres); + } + // TODO: merges will never happen if we 'know' we have the real dataset + // sequence - this should be detected when id==dssid + System.err.println( + "DEBUG Notice: Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // (" + // + (pre ? "prepended" : "") + " " + // + (post ? "appended" : "")); + } + } + else + { + // sequence refs are identical. We may need to update the existing dataset + // alignment with this one, though. + if (ds != null && dseqs == null) + { + int opos = ds.findIndex(dsq); + SequenceI tseq = null; + if (opos != -1 && vseqpos != opos) + { + // remove from old position + ds.deleteSequence(dsq); + } + if (vseqpos < ds.getHeight()) + { + if (vseqpos != opos) + { + // save sequence at destination position + tseq = ds.getSequenceAt(vseqpos); + ds.replaceSequenceAt(vseqpos, dsq); + ds.addSequence(tseq); + } + } + else + { + ds.addSequence(dsq); + } + } + } + } + + /* + * TODO use AlignmentI here and in related methods - needs + * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment + */ + Hashtable datasetIds = null; + + IdentityHashMap dataset2Ids = null; + + private AlignmentI getDatasetFor(String datasetId) + { + if (datasetIds == null) + { + datasetIds = new Hashtable<>(); + return null; + } + if (datasetIds.containsKey(datasetId)) + { + return datasetIds.get(datasetId); + } + return null; + } + + private void addDatasetRef(String datasetId, AlignmentI dataset) + { + if (datasetIds == null) + { + datasetIds = new Hashtable<>(); + } + datasetIds.put(datasetId, dataset); + } + + /** + * make a new dataset ID for this jalview dataset alignment + * + * @param dataset + * @return + */ + private String getDatasetIdRef(AlignmentI dataset) + { + if (dataset.getDataset() != null) + { + Console.warn( + "Serious issue! Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment..."); + } + String datasetId = makeHashCode(dataset, null); + if (datasetId == null) + { + // make a new datasetId and record it + if (dataset2Ids == null) + { + dataset2Ids = new IdentityHashMap<>(); + } + else + { + datasetId = dataset2Ids.get(dataset); + } + if (datasetId == null) + { + datasetId = "ds" + dataset2Ids.size() + 1; + dataset2Ids.put(dataset, datasetId); + } + } + return datasetId; + } + + /** + * Add any saved DBRefEntry's to the sequence. An entry flagged as 'locus' is + * constructed as a special subclass GeneLocus. + * + * @param datasetSequence + * @param sequence + */ + private void addDBRefs(SequenceI datasetSequence, Sequence sequence) + { + for (int d = 0; d < sequence.getDBRef().size(); d++) + { + DBRef dr = sequence.getDBRef().get(d); + DBRefEntry entry; + if (dr.isLocus()) + { + entry = new GeneLocus(dr.getSource(), dr.getVersion(), + dr.getAccessionId()); + } + else + { + entry = new DBRefEntry(dr.getSource(), dr.getVersion(), + dr.getAccessionId()); + } + if (dr.getMapping() != null) + { + entry.setMap(addMapping(dr.getMapping())); + } + entry.setCanonical(dr.isCanonical()); + datasetSequence.addDBRef(entry); + } + } + + private jalview.datamodel.Mapping addMapping(Mapping m) + { + SequenceI dsto = null; + // Mapping m = dr.getMapping(); + int fr[] = new int[m.getMapListFrom().size() * 2]; + Iterator from = m.getMapListFrom().iterator();// enumerateMapListFrom(); + for (int _i = 0; from.hasNext(); _i += 2) + { + MapListFrom mf = from.next(); + fr[_i] = mf.getStart(); + fr[_i + 1] = mf.getEnd(); + } + int fto[] = new int[m.getMapListTo().size() * 2]; + Iterator to = m.getMapListTo().iterator();// enumerateMapListTo(); + for (int _i = 0; to.hasNext(); _i += 2) + { + MapListTo mf = to.next(); + fto[_i] = mf.getStart(); + fto[_i + 1] = mf.getEnd(); + } + jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto, fr, + fto, m.getMapFromUnit().intValue(), + m.getMapToUnit().intValue()); + + /* + * (optional) choice of dseqFor or Sequence + */ + if (m.getDseqFor() != null) + { + String dsfor = m.getDseqFor(); + if (seqRefIds.containsKey(dsfor)) + { + /* + * recover from hash + */ + jmap.setTo(seqRefIds.get(dsfor)); + } + else + { + frefedSequence.add(newMappingRef(dsfor, jmap)); + } + } + else if (m.getSequence() != null) + { + /* + * local sequence definition + */ + Sequence ms = m.getSequence(); + SequenceI djs = null; + String sqid = ms.getDsseqid(); + if (sqid != null && sqid.length() > 0) + { + /* + * recover dataset sequence + */ + djs = seqRefIds.get(sqid); + } + else + { + System.err.println( + "Warning - making up dataset sequence id for DbRef sequence map reference"); + sqid = ((Object) ms).toString(); // make up a new hascode for + // undefined dataset sequence hash + // (unlikely to happen) + } + + if (djs == null) + { + /** + * make a new dataset sequence and add it to refIds hash + */ + djs = new jalview.datamodel.Sequence(ms.getName(), + ms.getSequence()); + djs.setStart(jmap.getMap().getToLowest()); + djs.setEnd(jmap.getMap().getToHighest()); + djs.setVamsasId(uniqueSetSuffix + sqid); + jmap.setTo(djs); + incompleteSeqs.put(sqid, djs); + seqRefIds.put(sqid, djs); + + } + Console.debug("about to recurse on addDBRefs."); + addDBRefs(djs, ms); + + } + + return jmap; + } + + /** + * Provides a 'copy' of an alignment view (on action New View) by 'saving' the + * view as XML (but not to file), and then reloading it + * + * @param ap + * @return + */ + public AlignmentPanel copyAlignPanel(AlignmentPanel ap) + { + initSeqRefs(); + JalviewModel jm = saveState(ap, null, null, null); + + addDatasetRef( + jm.getVamsasModel().getSequenceSet().get(0).getDatasetId(), + ap.getAlignment().getDataset()); + + uniqueSetSuffix = ""; + // jm.getJalviewModelSequence().getViewport(0).setId(null); + jm.getViewport().get(0).setId(null); + // we don't overwrite the view we just copied + + if (this.frefedSequence == null) + { + frefedSequence = new Vector<>(); + } + + viewportsAdded.clear(); + + AlignFrame af = loadFromObject(jm, null, false, null); + af.getAlignPanels().clear(); + af.closeMenuItem_actionPerformed(true); + + /* + * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0; + * i(); + } + if (seqsToIds == null) + { + seqsToIds = new IdentityHashMap<>(); + } + seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj); + seqsToIds.put((SequenceI) jvobj, id); + } + else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation) + { + String anid; + AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj; + annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann); + if (jvann.annotationId == null) + { + jvann.annotationId = anid; + } + if (!jvann.annotationId.equals(anid)) + { + // TODO verify that this is the correct behaviour + Console.warn("Overriding Annotation ID for " + anid + + " from different id : " + jvann.annotationId); + jvann.annotationId = anid; + } + } + else if (jvobj instanceof String) + { + if (jvids2vobj == null) + { + jvids2vobj = new Hashtable(); + jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString()); + } + } + else + { + Console.debug("Ignoring " + jvobj.getClass() + " (ID = " + id); + } + } + } + + /** + * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview + * objects created from the project archive. If string is null (default for + * construction) then suffix will be set automatically. + * + * @param string + */ + public void setUniqueSetSuffix(String string) + { + uniqueSetSuffix = string; + + } + + /** + * uses skipList2 as the skipList for skipping views on sequence sets + * associated with keys in the skipList + * + * @param skipList2 + */ + public void setSkipList(Hashtable skipList2) + { + skipList = skipList2; + } + + /** + * Reads the jar entry of given name and returns its contents, or null if the + * entry is not found. + * + * @param jprovider + * @param jarEntryName + * @return + */ + protected String readJarEntry(jarInputStreamProvider jprovider, + String jarEntryName) + { + String result = null; + BufferedReader in = null; + + try + { + /* + * Reopen the jar input stream and traverse its entries to find a matching + * name + */ + JarInputStream jin = jprovider.getJarInputStream(); + JarEntry entry = null; + do + { + entry = jin.getNextJarEntry(); + } while (entry != null && !entry.getName().equals(jarEntryName)); + + if (entry != null) + { + StringBuilder out = new StringBuilder(256); + in = new BufferedReader(new InputStreamReader(jin, UTF_8)); + String data; + + while ((data = in.readLine()) != null) + { + out.append(data); + } + result = out.toString(); + } + else + { + Console.warn( + "Couldn't find entry in Jalview Jar for " + jarEntryName); + } + } catch (Exception ex) + { + ex.printStackTrace(); + } finally + { + if (in != null) + { + try + { + in.close(); + } catch (IOException e) + { + // ignore + } + } + } + + return result; + } + + /** + * Returns an incrementing counter (0, 1, 2...) + * + * @return + */ + private synchronized int nextCounter() + { + return counter++; + } + + /** + * Loads any saved PCA viewers + * + * @param jms + * @param ap + */ + protected void loadPCAViewers(JalviewModel model, AlignmentPanel ap) + { + try + { + List pcaviewers = model.getPcaViewer(); + for (PcaViewer viewer : pcaviewers) + { + String modelName = viewer.getScoreModelName(); + SimilarityParamsI params = new SimilarityParams( + viewer.isIncludeGappedColumns(), viewer.isMatchGaps(), + viewer.isIncludeGaps(), + viewer.isDenominateByShortestLength()); + + /* + * create the panel (without computing the PCA) + */ + PCAPanel panel = new PCAPanel(ap, modelName, params); + + panel.setTitle(viewer.getTitle()); + panel.setBounds(new Rectangle(viewer.getXpos(), viewer.getYpos(), + viewer.getWidth(), viewer.getHeight())); + + boolean showLabels = viewer.isShowLabels(); + panel.setShowLabels(showLabels); + panel.getRotatableCanvas().setShowLabels(showLabels); + panel.getRotatableCanvas() + .setBgColour(new Color(viewer.getBgColour())); + panel.getRotatableCanvas() + .setApplyToAllViews(viewer.isLinkToAllViews()); + + /* + * load PCA output data + */ + ScoreModelI scoreModel = ScoreModels.getInstance() + .getScoreModel(modelName, ap); + PCA pca = new PCA(null, scoreModel, params); + PcaDataType pcaData = viewer.getPcaData(); + + MatrixI pairwise = loadDoubleMatrix(pcaData.getPairwiseMatrix()); + pca.setPairwiseScores(pairwise); + + MatrixI triDiag = loadDoubleMatrix(pcaData.getTridiagonalMatrix()); + pca.setTridiagonal(triDiag); + + MatrixI result = loadDoubleMatrix(pcaData.getEigenMatrix()); + pca.setEigenmatrix(result); + + panel.getPcaModel().setPCA(pca); + + /* + * we haven't saved the input data! (JAL-2647 to do) + */ + panel.setInputData(null); + + /* + * add the sequence points for the PCA display + */ + List seqPoints = new ArrayList<>(); + for (SequencePoint sp : viewer.getSequencePoint()) + { + String seqId = sp.getSequenceRef(); + SequenceI seq = seqRefIds.get(seqId); + if (seq == null) + { + throw new IllegalStateException( + "Unmatched seqref for PCA: " + seqId); + } + Point pt = new Point(sp.getXPos(), sp.getYPos(), sp.getZPos()); + jalview.datamodel.SequencePoint seqPoint = new jalview.datamodel.SequencePoint( + seq, pt); + seqPoints.add(seqPoint); + } + panel.getRotatableCanvas().setPoints(seqPoints, seqPoints.size()); + + /* + * set min-max ranges and scale after setPoints (which recomputes them) + */ + panel.getRotatableCanvas().setScaleFactor(viewer.getScaleFactor()); + SeqPointMin spMin = viewer.getSeqPointMin(); + float[] min = new float[] { spMin.getXPos(), spMin.getYPos(), + spMin.getZPos() }; + SeqPointMax spMax = viewer.getSeqPointMax(); + float[] max = new float[] { spMax.getXPos(), spMax.getYPos(), + spMax.getZPos() }; + panel.getRotatableCanvas().setSeqMinMax(min, max); + + // todo: hold points list in PCAModel only + panel.getPcaModel().setSequencePoints(seqPoints); + + panel.setSelectedDimensionIndex(viewer.getXDim(), X); + panel.setSelectedDimensionIndex(viewer.getYDim(), Y); + panel.setSelectedDimensionIndex(viewer.getZDim(), Z); + + // is this duplication needed? + panel.setTop(seqPoints.size() - 1); + panel.getPcaModel().setTop(seqPoints.size() - 1); + + /* + * add the axes' end points for the display + */ + for (int i = 0; i < 3; i++) + { + Axis axis = viewer.getAxis().get(i); + panel.getRotatableCanvas().getAxisEndPoints()[i] = new Point( + axis.getXPos(), axis.getYPos(), axis.getZPos()); + } + + Desktop.addInternalFrame(panel, MessageManager.formatMessage( + "label.calc_title", "PCA", modelName), 475, 450); + } + } catch (Exception ex) + { + Console.error("Error loading PCA: " + ex.toString()); + } + } + + /** + * Creates a new structure viewer window + * + * @param viewerType + * @param viewerData + * @param af + * @param jprovider + */ + protected void createStructureViewer(ViewerType viewerType, + final Entry viewerData, + AlignFrame af, jarInputStreamProvider jprovider) + { + final StructureViewerModel viewerModel = viewerData.getValue(); + String sessionFilePath = null; + + if (viewerType == ViewerType.JMOL) + { + sessionFilePath = rewriteJmolSession(viewerModel, jprovider); + } + else + { + String viewerJarEntryName = getViewerJarEntryName( + viewerModel.getViewId()); + sessionFilePath = copyJarEntry(jprovider, viewerJarEntryName, + "viewerSession", ".tmp"); + } + final String sessionPath = sessionFilePath; + final String sviewid = viewerData.getKey(); + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + @Override + public void run() + { + JalviewStructureDisplayI sview = null; + try + { + sview = StructureViewer.createView(viewerType, af.alignPanel, + viewerModel, sessionPath, sviewid); + addNewStructureViewer(sview); + } catch (OutOfMemoryError ex) + { + new OOMWarning("Restoring structure view for " + viewerType, + (OutOfMemoryError) ex.getCause()); + if (sview != null && sview.isVisible()) + { + sview.closeViewer(false); + sview.setVisible(false); + sview.dispose(); + } + } + } + }); + } catch (InvocationTargetException | InterruptedException ex) + { + Console.warn("Unexpected error when opening " + viewerType + + " structure viewer", ex); + } + } + + /** + * Rewrites a Jmol session script, saves it to a temporary file, and returns + * the path of the file. "load file" commands are rewritten to change the + * original PDB file names to those created as the Jalview project is loaded. + * + * @param svattrib + * @param jprovider + * @return + */ + private String rewriteJmolSession(StructureViewerModel svattrib, + jarInputStreamProvider jprovider) + { + String state = svattrib.getStateData(); // Jalview < 2.9 + if (state == null || state.isEmpty()) // Jalview >= 2.9 + { + String jarEntryName = getViewerJarEntryName(svattrib.getViewId()); + state = readJarEntry(jprovider, jarEntryName); + } + // TODO or simpler? for each key in oldFiles, + // replace key.getPath() in state with oldFiles.get(key).getFilePath() + // (allowing for different path escapings) + StringBuilder rewritten = new StringBuilder(state.length()); + int cp = 0, ncp, ecp; + Map oldFiles = svattrib.getFileData(); + while ((ncp = state.indexOf("load ", cp)) > -1) + { + do + { + // look for next filename in load statement + rewritten.append(state.substring(cp, + ncp = (state.indexOf("\"", ncp + 1) + 1))); + String oldfilenam = state.substring(ncp, + ecp = state.indexOf("\"", ncp)); + // recover the new mapping data for this old filename + // have to normalize filename - since Jmol and jalview do + // filename translation differently. + StructureData filedat = oldFiles.get(new File(oldfilenam)); + if (filedat == null) + { + String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\"); + filedat = oldFiles.get(new File(reformatedOldFilename)); + } + rewritten.append(Platform.escapeBackslashes(filedat.getFilePath())); + rewritten.append("\""); + cp = ecp + 1; // advance beyond last \" and set cursor so we can + // look for next file statement. + } while ((ncp = state.indexOf("/*file*/", cp)) > -1); + } + if (cp > 0) + { + // just append rest of state + rewritten.append(state.substring(cp)); + } + else + { + System.err.print("Ignoring incomplete Jmol state for PDB ids: "); + rewritten = new StringBuilder(state); + rewritten.append("; load append "); + for (File id : oldFiles.keySet()) + { + // add pdb files that should be present in the viewer + StructureData filedat = oldFiles.get(id); + rewritten.append(" \"").append(filedat.getFilePath()).append("\""); + } + rewritten.append(";"); + } + + if (rewritten.length() == 0) + { + return null; + } + final String history = "history = "; + int historyIndex = rewritten.indexOf(history); + if (historyIndex > -1) + { + /* + * change "history = [true|false];" to "history = [1|0];" + */ + historyIndex += history.length(); + String val = rewritten.substring(historyIndex, historyIndex + 5); + if (val.startsWith("true")) + { + rewritten.replace(historyIndex, historyIndex + 4, "1"); + } + else if (val.startsWith("false")) + { + rewritten.replace(historyIndex, historyIndex + 5, "0"); + } + } + + try + { + File tmp = File.createTempFile("viewerSession", ".tmp"); + try (OutputStream os = new FileOutputStream(tmp)) + { + InputStream is = new ByteArrayInputStream( + rewritten.toString().getBytes()); + copyAll(is, os); + return tmp.getAbsolutePath(); + } + } catch (IOException e) + { + Console.error("Error restoring Jmol session: " + e.toString()); + } + return null; + } + + /** + * Populates an XML model of the feature colour scheme for one feature type + * + * @param featureType + * @param fcol + * @return + */ + public static Colour marshalColour(String featureType, + FeatureColourI fcol) + { + Colour col = new Colour(); + if (fcol.isSimpleColour()) + { + col.setRGB(Format.getHexString(fcol.getColour())); + } + else + { + col.setRGB(Format.getHexString(fcol.getMaxColour())); + col.setMin(fcol.getMin()); + col.setMax(fcol.getMax()); + col.setMinRGB(jalview.util.Format.getHexString(fcol.getMinColour())); + col.setAutoScale(fcol.isAutoScaled()); + col.setThreshold(fcol.getThreshold()); + col.setColourByLabel(fcol.isColourByLabel()); + col.setThreshType(fcol.isAboveThreshold() ? ThresholdType.ABOVE + : (fcol.isBelowThreshold() ? ThresholdType.BELOW + : ThresholdType.NONE)); + if (fcol.isColourByAttribute()) + { + final String[] attName = fcol.getAttributeName(); + col.getAttributeName().add(attName[0]); + if (attName.length > 1) + { + col.getAttributeName().add(attName[1]); + } + } + Color noColour = fcol.getNoColour(); + if (noColour == null) + { + col.setNoValueColour(NoValueColour.NONE); + } + else if (noColour == fcol.getMaxColour()) + { + col.setNoValueColour(NoValueColour.MAX); + } + else + { + col.setNoValueColour(NoValueColour.MIN); + } + } + col.setName(featureType); + return col; + } + + /** + * Populates an XML model of the feature filter(s) for one feature type + * + * @param firstMatcher + * the first (or only) match condition) + * @param filter + * remaining match conditions (if any) + * @param and + * if true, conditions are and-ed, else or-ed + */ + public static jalview.xml.binding.jalview.FeatureMatcherSet marshalFilter( + FeatureMatcherI firstMatcher, Iterator filters, + boolean and) + { + jalview.xml.binding.jalview.FeatureMatcherSet result = new jalview.xml.binding.jalview.FeatureMatcherSet(); + + if (filters.hasNext()) + { + /* + * compound matcher + */ + CompoundMatcher compound = new CompoundMatcher(); + compound.setAnd(and); + jalview.xml.binding.jalview.FeatureMatcherSet matcher1 = marshalFilter( + firstMatcher, Collections.emptyIterator(), and); + // compound.addMatcherSet(matcher1); + compound.getMatcherSet().add(matcher1); + FeatureMatcherI nextMatcher = filters.next(); + jalview.xml.binding.jalview.FeatureMatcherSet matcher2 = marshalFilter( + nextMatcher, filters, and); + // compound.addMatcherSet(matcher2); + compound.getMatcherSet().add(matcher2); + result.setCompoundMatcher(compound); + } + else + { + /* + * single condition matcher + */ + // MatchCondition matcherModel = new MatchCondition(); + jalview.xml.binding.jalview.FeatureMatcher matcherModel = new jalview.xml.binding.jalview.FeatureMatcher(); + matcherModel.setCondition( + firstMatcher.getMatcher().getCondition().getStableName()); + matcherModel.setValue(firstMatcher.getMatcher().getPattern()); + if (firstMatcher.isByAttribute()) + { + matcherModel.setBy(FilterBy.BY_ATTRIBUTE); + // matcherModel.setAttributeName(firstMatcher.getAttribute()); + String[] attName = firstMatcher.getAttribute(); + matcherModel.getAttributeName().add(attName[0]); // attribute + if (attName.length > 1) + { + matcherModel.getAttributeName().add(attName[1]); // sub-attribute + } + } + else if (firstMatcher.isByLabel()) + { + matcherModel.setBy(FilterBy.BY_LABEL); + } + else if (firstMatcher.isByScore()) + { + matcherModel.setBy(FilterBy.BY_SCORE); + } + result.setMatchCondition(matcherModel); + } + + return result; + } + + /** + * Loads one XML model of a feature filter to a Jalview object + * + * @param featureType + * @param matcherSetModel + * @return + */ + public static FeatureMatcherSetI parseFilter(String featureType, + jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel) + { + FeatureMatcherSetI result = new FeatureMatcherSet(); + try + { + parseFilterConditions(result, matcherSetModel, true); + } catch (IllegalStateException e) + { + // mixing AND and OR conditions perhaps + System.err.println( + String.format("Error reading filter conditions for '%s': %s", + featureType, e.getMessage())); + // return as much as was parsed up to the error + } + + return result; + } + + /** + * Adds feature match conditions to matcherSet as unmarshalled from XML + * (possibly recursively for compound conditions) + * + * @param matcherSet + * @param matcherSetModel + * @param and + * if true, multiple conditions are AND-ed, else they are OR-ed + * @throws IllegalStateException + * if AND and OR conditions are mixed + */ + protected static void parseFilterConditions(FeatureMatcherSetI matcherSet, + jalview.xml.binding.jalview.FeatureMatcherSet matcherSetModel, + boolean and) + { + jalview.xml.binding.jalview.FeatureMatcher mc = matcherSetModel + .getMatchCondition(); + if (mc != null) + { + /* + * single condition + */ + FilterBy filterBy = mc.getBy(); + Condition cond = Condition.fromString(mc.getCondition()); + String pattern = mc.getValue(); + FeatureMatcherI matchCondition = null; + if (filterBy == FilterBy.BY_LABEL) + { + matchCondition = FeatureMatcher.byLabel(cond, pattern); + } + else if (filterBy == FilterBy.BY_SCORE) + { + matchCondition = FeatureMatcher.byScore(cond, pattern); + + } + else if (filterBy == FilterBy.BY_ATTRIBUTE) + { + final List attributeName = mc.getAttributeName(); + String[] attNames = attributeName + .toArray(new String[attributeName.size()]); + matchCondition = FeatureMatcher.byAttribute(cond, pattern, + attNames); + } + + /* + * note this throws IllegalStateException if AND-ing to a + * previously OR-ed compound condition, or vice versa + */ + if (and) + { + matcherSet.and(matchCondition); + } + else + { + matcherSet.or(matchCondition); + } + } + else + { + /* + * compound condition + */ + List matchers = matcherSetModel + .getCompoundMatcher().getMatcherSet(); + boolean anded = matcherSetModel.getCompoundMatcher().isAnd(); + if (matchers.size() == 2) + { + parseFilterConditions(matcherSet, matchers.get(0), anded); + parseFilterConditions(matcherSet, matchers.get(1), anded); + } + else + { + System.err.println("Malformed compound filter condition"); + } + } + } + + /** + * Loads one XML model of a feature colour to a Jalview object + * + * @param colourModel + * @return + */ + public static FeatureColourI parseColour(Colour colourModel) + { + FeatureColourI colour = null; + + if (colourModel.getMax() != null) + { + Color mincol = null; + Color maxcol = null; + Color noValueColour = null; + + try + { + mincol = new Color(Integer.parseInt(colourModel.getMinRGB(), 16)); + maxcol = new Color(Integer.parseInt(colourModel.getRGB(), 16)); + } catch (Exception e) + { + Console.warn("Couldn't parse out graduated feature color.", e); + } + + NoValueColour noCol = colourModel.getNoValueColour(); + if (noCol == NoValueColour.MIN) + { + noValueColour = mincol; + } + else if (noCol == NoValueColour.MAX) + { + noValueColour = maxcol; + } + + colour = new FeatureColour(maxcol, mincol, maxcol, noValueColour, + safeFloat(colourModel.getMin()), + safeFloat(colourModel.getMax())); + final List attributeName = colourModel.getAttributeName(); + String[] attributes = attributeName + .toArray(new String[attributeName.size()]); + if (attributes != null && attributes.length > 0) + { + colour.setAttributeName(attributes); + } + if (colourModel.isAutoScale() != null) + { + colour.setAutoScaled(colourModel.isAutoScale().booleanValue()); + } + if (colourModel.isColourByLabel() != null) + { + colour.setColourByLabel( + colourModel.isColourByLabel().booleanValue()); + } + if (colourModel.getThreshold() != null) + { + colour.setThreshold(colourModel.getThreshold().floatValue()); + } + ThresholdType ttyp = colourModel.getThreshType(); + if (ttyp == ThresholdType.ABOVE) + { + colour.setAboveThreshold(true); + } + else if (ttyp == ThresholdType.BELOW) + { + colour.setBelowThreshold(true); + } + } + else + { + Color color = new Color(Integer.parseInt(colourModel.getRGB(), 16)); + colour = new FeatureColour(color); + } + + return colour; + } +} diff --combined src/jalview/util/AWTConsole.java index 64c8e89,8f45eaa..751f940 --- a/src/jalview/util/AWTConsole.java +++ b/src/jalview/util/AWTConsole.java @@@ -125,11 -125,11 +125,11 @@@ public class AWTConsole extends WindowA // Starting two seperate threads to read from the PipedInputStreams // - reader = new Thread(this, "AWTConsoleReader1Thread"); + reader = new Thread(this, "AWTConsoleReader1"); reader.setDaemon(true); reader.start(); // - reader2 = new Thread(this, "AWTConsoleReader2Thread"); + reader2 = new Thread(this, "AWTConsoleReader2"); reader2.setDaemon(true); reader2.start(); @@@ -150,7 -150,7 +150,7 @@@ // We do it with a seperate Thread becasue we don't wan't to break a Thread // used by the Console. System.out.println("\nLets throw an error on this console"); - errorThrower = new Thread(this, "AWTConsoleErrorLogThread"); + errorThrower = new Thread(this, "AWTConsoleErrorLog"); errorThrower.setDaemon(true); errorThrower.start(); } @@@ -271,11 -271,6 +271,11 @@@ return input; } + /** + * + * @param arg + * @j2sIgnore + */ public static void main(String[] arg) { new AWTConsole(); // create console with not reference diff --combined src/jalview/util/DBRefUtils.java index 5337852,203b4e7..77a64e8 --- a/src/jalview/util/DBRefUtils.java +++ b/src/jalview/util/DBRefUtils.java @@@ -20,23 -20,21 +20,23 @@@ */ package jalview.util; -import jalview.datamodel.DBRefEntry; -import jalview.datamodel.DBRefSource; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; +import java.util.Locale; import java.util.ArrayList; -import java.util.Arrays; +import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import com.stevesoft.pat.Regex; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.DBRefSource; +import jalview.datamodel.Mapping; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; + /** * Utilities for handling DBRef objects and their collections. */ @@@ -47,18 -45,7 +47,18 @@@ public class DBRefUtil */ private static Map canonicalSourceNameLookup = new HashMap<>(); - private static Map dasCoordinateSystemsLookup = new HashMap<>(); + public final static int DB_SOURCE = 1; + + public final static int DB_VERSION = 2; + + public final static int DB_ID = 4; + + public final static int DB_MAP = 8; + + public final static int SEARCH_MODE_NO_MAP_NO_VERSION = DB_SOURCE | DB_ID; + + public final static int SEARCH_MODE_FULL = DB_SOURCE | DB_VERSION | DB_ID + | DB_MAP; static { @@@ -78,12 -65,20 +78,15 @@@ canonicalSourceNameLookup.put("ensembl-tr", DBRefSource.ENSEMBL); canonicalSourceNameLookup.put("ensembl-gn", DBRefSource.ENSEMBL); ++ // TODO keep ? (in phyloviewer branch only) + canonicalSourceNameLookup.put("pfam", DBRefSource.PFAM); + - // Make sure we have lowercase entries for all canonical string lookups - Set keys = canonicalSourceNameLookup.keySet(); - for (String k : keys) + // guarantee we always have lowercase entries for canonical string lookups + for (String k : canonicalSourceNameLookup.keySet()) { - canonicalSourceNameLookup.put(k.toLowerCase(), + canonicalSourceNameLookup.put(k.toLowerCase(Locale.ROOT), canonicalSourceNameLookup.get(k)); } - - dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB); - dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT); - dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBL); - // dasCoordinateSystemsLookup.put("embl", DBRefSource.EMBLCDS); } /** @@@ -97,85 -92,58 +100,85 @@@ * array of sources to select * @return */ - public static DBRefEntry[] selectRefs(DBRefEntry[] dbrefs, + public static List selectRefs(List dbrefs, String[] sources) { if (dbrefs == null || sources == null) { return dbrefs; } - HashSet srcs = new HashSet<>(); + + // BH TODO (what?) + HashSet srcs = new HashSet(); for (String src : sources) { - srcs.add(src.toUpperCase()); + srcs.add(src.toUpperCase(Locale.ROOT)); } - List res = new ArrayList<>(); - for (DBRefEntry dbr : dbrefs) + int nrefs = dbrefs.size(); + List res = new ArrayList(); + for (int ib = 0; ib < nrefs; ib++) { + DBRefEntry dbr = dbrefs.get(ib); String source = getCanonicalName(dbr.getSource()); - if (srcs.contains(source.toUpperCase())) + if (srcs.contains(source.toUpperCase(Locale.ROOT))) { res.add(dbr); } } - if (res.size() > 0) { - DBRefEntry[] reply = new DBRefEntry[res.size()]; - return res.toArray(reply); + // List reply = new DBRefEntry[res.size()]; + return res;// .toArray(reply); } return null; } + private static boolean selectRefsBS(List dbrefs, + int sourceKeys, BitSet bsSelect) + { + if (dbrefs == null || sourceKeys == 0) + { + return false; + } + for (int i = 0, n = dbrefs.size(); i < n; i++) + { + DBRefEntry dbr = dbrefs.get(i); + if ((dbr.getSourceKey() & sourceKeys) != 0) + { + bsSelect.clear(i); + } + } + return !bsSelect.isEmpty(); + } + /** - * isDasCoordinateSystem + * Returns a (possibly empty) list of those references that match the given + * entry, according to the given comparator. * - * @param string - * String - * @param dBRefEntry - * DBRefEntry - * @return boolean true if Source DBRefEntry is compatible with DAS - * CoordinateSystem name + * @param refs + * an array of database references to search + * @param entry + * an entry to compare against + * @param comparator + * @return */ - - public static boolean isDasCoordinateSystem(String string, - DBRefEntry dBRefEntry) + static List searchRefs(DBRefEntry[] refs, DBRefEntry entry, + DbRefComp comparator) { - if (string == null || dBRefEntry == null) + List rfs = new ArrayList<>(); + if (refs == null || entry == null) { - return false; + return rfs; } - String coordsys = dasCoordinateSystemsLookup.get(string.toLowerCase()); - return coordsys == null ? false - : coordsys.equals(dBRefEntry.getSource()); + for (int i = 0; i < refs.length; i++) + { + if (comparator.matches(entry, refs[i])) + { + rfs.add(refs[i]); + } + } + return rfs; } /** @@@ -193,8 -161,7 +196,8 @@@ { return null; } - String canonical = canonicalSourceNameLookup.get(source.toLowerCase()); + String canonical = canonicalSourceNameLookup + .get(source.toLowerCase(Locale.ROOT)); return canonical == null ? source : canonical; } @@@ -211,15 -178,13 +214,15 @@@ * Set of references to search * @param entry * pattern to match + * @param mode + * SEARCH_MODE_FULL for all; SEARCH_MODE_NO_MAP_NO_VERSION optional * @return */ - public static List searchRefs(DBRefEntry[] ref, - DBRefEntry entry) + public static List searchRefs(List ref, + DBRefEntry entry, int mode) { return searchRefs(ref, entry, - matchDbAndIdAndEitherMapOrEquivalentMapList); + matchDbAndIdAndEitherMapOrEquivalentMapList, mode); } /** @@@ -236,25 -201,9 +239,25 @@@ * accession id to match * @return */ - public static List searchRefs(DBRefEntry[] refs, String accId) + public static List searchRefs(List refs, + String accId) { - return searchRefs(refs, new DBRefEntry("", "", accId), matchId); + List rfs = new ArrayList(); + if (refs == null || accId == null) + { + return rfs; + } + for (int i = 0, n = refs.size(); i < n; i++) + { + DBRefEntry e = refs.get(i); + if (accId.equals(e.getAccessionId())) + { + rfs.add(e); + } + } + return rfs; + // return searchRefs(refs, new DBRefEntry("", "", accId), matchId, + // SEARCH_MODE_FULL); } /** @@@ -266,24 -215,21 +269,24 @@@ * @param entry * an entry to compare against * @param comparator + * @param mode + * SEARCH_MODE_FULL for all; SEARCH_MODE_NO_MAP_NO_VERSION optional * @return */ - static List searchRefs(DBRefEntry[] refs, DBRefEntry entry, - DbRefComp comparator) + static List searchRefs(List refs, + DBRefEntry entry, DbRefComp comparator, int mode) { - List rfs = new ArrayList(); + List rfs = new ArrayList<>(); if (refs == null || entry == null) { return rfs; } - for (int i = 0; i < refs.length; i++) + for (int i = 0, n = refs.size(); i < n; i++) { - if (comparator.matches(entry, refs[i])) + DBRefEntry e = refs.get(i); + if (comparator.matches(entry, e, SEARCH_MODE_FULL)) { - rfs.add(refs[i]); + rfs.add(e); } } return rfs; @@@ -291,36 -237,30 +294,36 @@@ interface DbRefComp { - public boolean matches(DBRefEntry refa, DBRefEntry refb); + default public boolean matches(DBRefEntry refa, DBRefEntry refb) + { + return matches(refa, refb, SEARCH_MODE_FULL); + }; + + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode); } /** * match on all non-null fields in refa */ - // TODO unused - remove? + // TODO unused - remove? would be broken by equating "" with null public static DbRefComp matchNonNullonA = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { - if (refa.getSource() == null + if ((mode & DB_SOURCE) != 0 && (refa.getSource() == null || DBRefUtils.getCanonicalName(refb.getSource()).equals( - DBRefUtils.getCanonicalName(refa.getSource()))) + DBRefUtils.getCanonicalName(refa.getSource())))) { - if (refa.getVersion() == null - || refb.getVersion().equals(refa.getVersion())) + if ((mode & DB_VERSION) != 0 && (refa.getVersion() == null + || refb.getVersion().equals(refa.getVersion()))) { - if (refa.getAccessionId() == null - || refb.getAccessionId().equals(refa.getAccessionId())) + if ((mode & DB_ID) != 0 && (refa.getAccessionId() == null + || refb.getAccessionId().equals(refa.getAccessionId()))) { - if (refa.getMap() == null || (refb.getMap() != null - && refb.getMap().equals(refa.getMap()))) + if ((mode & DB_MAP) != 0 + && (refa.getMap() == null || (refb.getMap() != null + && refb.getMap().equals(refa.getMap())))) { return true; } @@@ -339,7 -279,7 +342,7 @@@ public static DbRefComp matchEitherNonNull = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { if (nullOrEqualSource(refa.getSource(), refb.getSource()) && nullOrEqual(refa.getVersion(), refb.getVersion()) @@@ -350,79 -290,9 +353,79 @@@ } return false; } + }; /** + * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the + * database is PDB. + *

+ * Used by file parsers to generate DBRefs from annotation within file (eg + * Stockholm) + * + * @param dbname + * @param version + * @param acn + * @param seq + * where to annotate with reference + * @return parsed version of entry that was added to seq (if any) + */ + public static DBRefEntry parseToDbRef(SequenceI seq, String dbname, + String version, String acn) + { + DBRefEntry ref = null; + if (dbname != null) + { + String locsrc = DBRefUtils.getCanonicalName(dbname); + if (locsrc.equals(DBRefSource.PDB)) + { + /* + * Check for PFAM style stockhom PDB accession id citation e.g. + * "1WRI A; 7-80;" + */ + Regex r = new com.stevesoft.pat.Regex( + "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)"); + if (r.search(acn.trim())) + { + String pdbid = r.stringMatched(1); + String chaincode = r.stringMatched(2); + if (chaincode == null) + { + chaincode = " "; + } + // String mapstart = r.stringMatched(3); + // String mapend = r.stringMatched(4); + if (chaincode.equals(" ")) + { + chaincode = "_"; + } + // construct pdb ref. + ref = new DBRefEntry(locsrc, version, pdbid + chaincode); + PDBEntry pdbr = new PDBEntry(); + pdbr.setId(pdbid); + pdbr.setType(PDBEntry.Type.PDB); + pdbr.setChainCode(chaincode); + seq.addPDBId(pdbr); + } + else + { + System.err.println("Malformed PDB DR line:" + acn); + } + } + else + { + // default: + ref = new DBRefEntry(locsrc, version, acn.trim()); + } + } + if (ref != null) + { + seq.addDBRef(ref); + } + return ref; + } + + /** * accession ID and DB must be identical. Version is ignored. Map is either * not defined or is a match (or is compatible?) */ @@@ -430,7 -300,7 +433,7 @@@ public static DbRefComp matchDbAndIdAndEitherMap = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource()).equals( @@@ -462,7 -332,7 +465,7 @@@ public static DbRefComp matchDbAndIdAndComplementaryMapList = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource()).equals( @@@ -500,7 -370,7 +503,7 @@@ public static DbRefComp matchDbAndIdAndEquivalentMapList = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource()).equals( @@@ -541,13 -411,14 +544,13 @@@ public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp() { @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) + public boolean matches(DBRefEntry refa, DBRefEntry refb, int mode) { if (refa.getSource() != null && refb.getSource() != null && DBRefUtils.getCanonicalName(refb.getSource()).equals( DBRefUtils.getCanonicalName(refa.getSource()))) { // We dont care about version - if (refa.getAccessionId() == null || refa.getAccessionId().equals(refb.getAccessionId())) { @@@ -572,28 -443,89 +575,28 @@@ }; /** - * accession ID only must be identical. - */ - public static DbRefComp matchId = new DbRefComp() - { - @Override - public boolean matches(DBRefEntry refa, DBRefEntry refb) - { - if (refa.getAccessionId() != null && refb.getAccessionId() != null - && refb.getAccessionId().equals(refa.getAccessionId())) - { - return true; - } - return false; - } - }; - - /** - * Parses a DBRefEntry and adds it to the sequence, also a PDBEntry if the - * database is PDB. - *

- * Used by file parsers to generate DBRefs from annotation within file (eg - * Stockholm) + * Returns the (possibly empty) list of those supplied dbrefs which have the + * specified source database, with a case-insensitive match of source name * - * @param dbname - * @param version - * @param acn - * @param seq - * where to annotate with reference - * @return parsed version of entry that was added to seq (if any) + * @param dbRefs + * @param source + * @return */ - public static DBRefEntry parseToDbRef(SequenceI seq, String dbname, - String version, String acn) + public static List searchRefsForSource(DBRefEntry[] dbRefs, + String source) { - DBRefEntry ref = null; - if (dbname != null) + List matches = new ArrayList<>(); + if (dbRefs != null && source != null) { - String locsrc = DBRefUtils.getCanonicalName(dbname); - if (locsrc.equals(DBRefSource.PDB)) + for (DBRefEntry dbref : dbRefs) { - /* - * Check for PFAM style stockhom PDB accession id citation e.g. - * "1WRI A; 7-80;" - */ - Regex r = new com.stevesoft.pat.Regex( - "([0-9][0-9A-Za-z]{3})\\s*(.?)\\s*;\\s*([0-9]+)-([0-9]+)"); - if (r.search(acn.trim())) - { - String pdbid = r.stringMatched(1); - String chaincode = r.stringMatched(2); - if (chaincode == null) - { - chaincode = " "; - } - // String mapstart = r.stringMatched(3); - // String mapend = r.stringMatched(4); - if (chaincode.equals(" ")) - { - chaincode = "_"; - } - // construct pdb ref. - ref = new DBRefEntry(locsrc, version, pdbid + chaincode); - PDBEntry pdbr = new PDBEntry(); - pdbr.setId(pdbid); - pdbr.setType(PDBEntry.Type.PDB); - pdbr.setChainCode(chaincode); - seq.addPDBId(pdbr); - } - else + if (source.equalsIgnoreCase(dbref.getSource())) { - System.err.println("Malformed PDB DR line:" + acn); + matches.add(dbref); } } - else - { - // default: - ref = new DBRefEntry(locsrc, version, acn); - } - } - if (ref != null) - { - seq.addDBRef(ref); } - return ref; + return matches; } /** @@@ -643,8 -575,8 +646,8 @@@ * a set of references to select from * @return */ - public static DBRefEntry[] selectDbRefs(boolean selectDna, - DBRefEntry[] refs) + public static List selectDbRefs(boolean selectDna, + List refs) { return selectRefs(refs, selectDna ? DBRefSource.DNACODINGDBS : DBRefSource.PROTEINDBS); @@@ -661,10 -593,10 +664,10 @@@ * @param source * @return */ - public static List searchRefsForSource(DBRefEntry[] dbRefs, - String source) + public static List searchRefsForSource( + List dbRefs, String source) { - List matches = new ArrayList(); + List matches = new ArrayList<>(); if (dbRefs != null && source != null) { for (DBRefEntry dbref : dbRefs) @@@ -706,109 -638,89 +709,109 @@@ * * @param sequence */ - public static void ensurePrimaries(SequenceI sequence) + public static void ensurePrimaries(SequenceI sequence, + List pr) { - List pr = sequence.getPrimaryDBRefs(); if (pr.size() == 0) { // nothing to do return; } - List selfs = new ArrayList<>(); - { - DBRefEntry[] selfArray = selectDbRefs(!sequence.isProtein(), - sequence.getDBRefs()); - if (selfArray == null || selfArray.length == 0) - { - // nothing to do - return; - } - selfs.addAll(Arrays.asList(selfArray)); - } + int sstart = sequence.getStart(); + int send = sequence.getEnd(); + boolean isProtein = sequence.isProtein(); + BitSet bsSelect = new BitSet(); + + // List selfs = new ArrayList(); + // { + + // List selddfs = selectDbRefs(!isprot, sequence.getDBRefs()); + // if (selfs == null || selfs.size() == 0) + // { + // // nothing to do + // return; + // } + + List dbrefs = sequence.getDBRefs(); + bsSelect.set(0, dbrefs.size()); + + if (!selectRefsBS(dbrefs, isProtein ? DBRefSource.PROTEIN_MASK + : DBRefSource.DNA_CODING_MASK, bsSelect)) + return; + + // selfs.addAll(selfArray); + // } // filter non-primary refs - for (DBRefEntry p : pr) + for (int ip = pr.size(); --ip >= 0;) { - while (selfs.contains(p)) + DBRefEntry p = pr.get(ip); + for (int i = bsSelect.nextSetBit(0); i >= 0; i = bsSelect + .nextSetBit(i + 1)) { - selfs.remove(p); + if (dbrefs.get(i) == p) + bsSelect.clear(i); } + // while (selfs.contains(p)) + // { + // selfs.remove(p); + // } } - List toPromote = new ArrayList<>(); + // List toPromote = new ArrayList(); - for (DBRefEntry p : pr) + for (int ip = pr.size(), keys = 0; --ip >= 0 + && keys != DBRefSource.PRIMARY_MASK;) { - List promType = new ArrayList<>(); - if (sequence.isProtein()) + DBRefEntry p = pr.get(ip); + if (isProtein) { switch (getCanonicalName(p.getSource())) { case DBRefSource.UNIPROT: - // case DBRefSource.UNIPROTKB: - // case DBRefSource.UP_NAME: - // search for and promote ensembl - promType.add(DBRefSource.ENSEMBL); + keys |= DBRefSource.UNIPROT_MASK; break; case DBRefSource.ENSEMBL: - // search for and promote Uniprot - promType.add(DBRefSource.UNIPROT); + keys |= DBRefSource.ENSEMBL_MASK; break; } } else { - // TODO: promote transcript refs + // TODO: promote transcript refs ?? } - - // collate candidates and promote them - DBRefEntry[] candidates = selectRefs(selfs.toArray(new DBRefEntry[0]), - promType.toArray(new String[0])); - if (candidates != null) + if (keys == 0 || !selectRefsBS(dbrefs, keys, bsSelect)) + return; + // if (candidates != null) { - for (DBRefEntry cand : candidates) + for (int ic = bsSelect.nextSetBit(0); ic >= 0; ic = bsSelect + .nextSetBit(ic + 1)) + // for (int ic = 0, n = candidates.size(); ic < n; ic++) { + DBRefEntry cand = dbrefs.get(ic);// candidates.get(ic); if (cand.hasMap()) { - if (cand.getMap().getTo() != null - && cand.getMap().getTo() != sequence) + Mapping map = cand.getMap(); + SequenceI cto = map.getTo(); + if (cto != null && cto != sequence) { // can't promote refs with mappings to other sequences continue; } - if (cand.getMap().getMap().getFromLowest() != sequence - .getStart() - && cand.getMap().getMap().getFromHighest() != sequence - .getEnd()) + MapList mlist = map.getMap(); + if (mlist.getFromLowest() != sstart + && mlist.getFromHighest() != send) { // can't promote refs with mappings from a region of this sequence // - eg CDS continue; } } - // and promote + // and promote - not that version must be non-null here, + // as p must have passed isPrimaryCandidate() cand.setVersion(p.getVersion() + " (promoted)"); - selfs.remove(cand); - toPromote.add(cand); + bsSelect.clear(ic); + // selfs.remove(cand); + // toPromote.add(cand); if (!cand.isPrimaryCandidate()) { System.out.println( diff --combined src/jalview/util/MappingUtils.java index 05fc1c6,78a833e..dbbf8a0 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@@ -20,28 -20,18 +20,28 @@@ */ package jalview.util; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import jalview.analysis.AlignmentSorter; import jalview.api.AlignViewportI; +import jalview.bin.Console; import jalview.commands.CommandI; import jalview.commands.EditCommand; import jalview.commands.EditCommand.Action; import jalview.commands.EditCommand.Edit; import jalview.commands.OrderCommand; import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenColumns; +import jalview.datamodel.Mapping; import jalview.datamodel.SearchResultMatchI; import jalview.datamodel.SearchResults; import jalview.datamodel.SearchResultsI; @@@ -49,6 -39,13 +49,6 @@@ import jalview.datamodel.Sequence import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - /** * Helper methods for manipulations involving sequence mappings. * @@@ -81,7 -78,7 +81,7 @@@ public final class MappingUtil action = action.getUndoAction(); } // TODO write this - System.err.println("MappingUtils.mapCutOrPaste not yet implemented"); + Console.error("MappingUtils.mapCutOrPaste not yet implemented"); } /** @@@ -364,17 -361,19 +364,17 @@@ */ int startResiduePos = selected.findPosition(firstUngappedPos); int endResiduePos = selected.findPosition(lastUngappedPos); - - for (AlignedCodonFrame acf : codonFrames) + for (SequenceI seq : mapTo.getAlignment().getSequences()) { - SequenceI mappedSequence = targetIsNucleotide - ? acf.getDnaForAaSeq(selected) - : acf.getAaForDnaSeq(selected); - if (mappedSequence != null) + int mappedStartResidue = 0; + int mappedEndResidue = 0; + for (AlignedCodonFrame acf : codonFrames) { - for (SequenceI seq : mapTo.getAlignment().getSequences()) + // rather than use acf.getCoveringMapping() we iterate through all + // mappings to make sure all CDS are selected for a protein + for (SequenceToSequenceMapping map : acf.getMappings()) { - int mappedStartResidue = 0; - int mappedEndResidue = 0; - if (seq.getDatasetSequence() == mappedSequence) + if (map.covers(selected) && map.covers(seq)) { /* * Found a sequence mapping. Locate the start/end mapped residues. @@@ -382,7 -381,6 +382,7 @@@ List mapping = Arrays .asList(new AlignedCodonFrame[] { acf }); + // locate start SearchResultsI sr = buildSearchResults(selected, startResiduePos, mapping); for (SearchResultMatchI m : sr.getResults()) @@@ -390,7 -388,6 +390,7 @@@ mappedStartResidue = m.getStart(); mappedEndResidue = m.getEnd(); } + // locate end - allowing for adjustment of start range sr = buildSearchResults(selected, endResiduePos, mapping); for (SearchResultMatchI m : sr.getResults()) { @@@ -452,21 -449,18 +452,21 @@@ { for (AlignedCodonFrame acf : mappings) { - SequenceI mappedSeq = mappingToNucleotide ? acf.getDnaForAaSeq(seq) - : acf.getAaForDnaSeq(seq); - if (mappedSeq != null) + for (SequenceI seq2 : mapTo.getSequences()) { - for (SequenceI seq2 : mapTo.getSequences()) + /* + * the corresponding peptide / CDS is the one for which there is + * a complete ('covering') mapping to 'seq' + */ + SequenceI peptide = mappingToNucleotide ? seq2 : seq; + SequenceI cds = mappingToNucleotide ? seq : seq2; + SequenceToSequenceMapping s2s = acf.getCoveringMapping(cds, + peptide); + if (s2s != null) { - if (seq2.getDatasetSequence() == mappedSeq) - { - mappedOrder.add(seq2); - j++; - break; - } + mappedOrder.add(seq2); + j++; + break; } } } @@@ -530,7 -524,7 +530,7 @@@ if (colsel == null) { - return; // mappedColumns; + return; } char fromGapChar = mapFrom.getAlignment().getGapCharacter(); @@@ -548,13 -542,12 +548,13 @@@ toSequences, fromGapChar); } - for (int[] hidden : hiddencols.getHiddenColumnsCopy()) + Iterator regions = hiddencols.iterator(); + while (regions.hasNext()) { - mapHiddenColumns(hidden, codonFrames, newHidden, fromSequences, - toSequences, fromGapChar); + mapHiddenColumns(regions.next(), codonFrames, newHidden, + fromSequences, toSequences, fromGapChar); } - return; // mappedColumns; + return; } /** @@@ -672,9 -665,7 +672,9 @@@ */ for (SequenceI toSeq : toSequences) { - if (toSeq.getDatasetSequence() == mappedSeq) + if (toSeq.getDatasetSequence() == mappedSeq + && mappedStartResidue >= toSeq.getStart() + && mappedEndResidue <= toSeq.getEnd()) { int mappedStartCol = toSeq.findIndex(mappedStartResidue); int mappedEndCol = toSeq.findIndex(mappedEndResidue); @@@ -843,7 -834,7 +843,7 @@@ { if (range.length % 2 != 0) { - System.err.println( + Console.error( "Error unbalance start/end ranges: " + ranges.toString()); return 0; } @@@ -950,34 -941,6 +950,34 @@@ } /** + * Answers true if range's start-end positions include those of queryRange, + * where either range might be in reverse direction, else false + * + * @param range + * a start-end range + * @param queryRange + * a candidate subrange of range (start2-end2) + * @return + */ + public static boolean rangeContains(int[] range, int[] queryRange) + { + if (range == null || queryRange == null || range.length != 2 + || queryRange.length != 2) + { + /* + * invalid arguments + */ + return false; + } + + int min = Math.min(range[0], range[1]); + int max = Math.max(range[0], range[1]); + + return (min <= queryRange[0] && max >= queryRange[0] + && min <= queryRange[1] && max >= queryRange[1]); + } + + /** * Removes the specified number of positions from the given ranges. Provided * to allow a stop codon to be stripped from a CDS sequence so that it matches * the peptide translation length. @@@ -987,7 -950,8 +987,7 @@@ * a list of (single) [start, end] ranges * @return */ - public static void removeEndPositions(int positions, - List ranges) + public static void removeEndPositions(int positions, List ranges) { int toRemove = positions; Iterator it = new ReverseListIterator<>(ranges); @@@ -999,8 -963,8 +999,8 @@@ /* * not coded for [start1, end1, start2, end2, ...] */ - System.err - .println("MappingUtils.removeEndPositions doesn't handle multiple ranges"); + Console.error( + "MappingUtils.removeEndPositions doesn't handle multiple ranges"); return; } @@@ -1010,8 -974,8 +1010,8 @@@ /* * not coded for a reverse strand range (end < start) */ - System.err - .println("MappingUtils.removeEndPositions doesn't handle reverse strand"); + Console.error( + "MappingUtils.removeEndPositions doesn't handle reverse strand"); return; } if (length > toRemove) @@@ -1027,89 -991,7 +1027,89 @@@ } } + /** + * Converts a list of {@code start-end} ranges to a single array of + * {@code start1, end1, start2, ... } ranges + * + * @param ranges + * @return + */ + public static int[] rangeListToArray(List ranges) + { + int rangeCount = ranges.size(); + int[] result = new int[rangeCount * 2]; + int j = 0; + for (int i = 0; i < rangeCount; i++) + { + int[] range = ranges.get(i); + result[j++] = range[0]; + result[j++] = range[1]; + } + return result; + } + + /* + * Returns the maximal start-end positions in the given (ordered) list of + * ranges which is overlapped by the given begin-end range, or null if there + * is no overlap. + * + *

 +   * Examples:
 +   *   if ranges is {[4, 8], [10, 12], [16, 19]}
 +   * then
 +   *   findOverlap(ranges, 1, 20) == [4, 19]
 +   *   findOverlap(ranges, 6, 11) == [6, 11]
 +   *   findOverlap(ranges, 9, 15) == [10, 12]
 +   *   findOverlap(ranges, 13, 15) == null
 +   * 
+ * + * @param ranges + * @param begin + * @param end + * @return + */ + protected static int[] findOverlap(List ranges, final int begin, + final int end) + { + boolean foundStart = false; + int from = 0; + int to = 0; + /* + * traverse the ranges to find the first position (if any) >= begin, + * and the last position (if any) <= end + */ + for (int[] range : ranges) + { + if (!foundStart) + { + if (range[0] >= begin) + { + /* + * first range that starts with, or follows, begin + */ + foundStart = true; + from = Math.max(range[0], begin); + } + else if (range[1] >= begin) + { + /* + * first range that contains begin + */ + foundStart = true; + from = begin; + } + } + + if (range[0] <= end) + { + to = Math.min(end, range[1]); + } + } + + return foundStart && to >= from ? new int[] { from, to } : null; + } + public static Map putWithDuplicationCheck(Map map, K key, V value) { @@@ -1119,11 -1001,12 +1119,11 @@@ } else { -- jalview.bin.Cache.log.warn( ++ Console.warn( "Attempt to add duplicate entry detected for map with key: " + key.toString() + " and value: " + value.toString()); } return map; - } } diff --combined src/jalview/viewmodel/AlignmentViewport.java index d7ba417,d79cdce..b4e33c8 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@@ -24,7 -24,6 +24,7 @@@ import jalview.analysis.AnnotationSorte import jalview.analysis.Conservation; import jalview.analysis.TreeModel; import jalview.api.AlignCalcManagerI; +import jalview.api.AlignExportSettingsI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeaturesDisplayedI; @@@ -32,7 -31,6 +32,7 @@@ import jalview.api.ViewStyleI import jalview.commands.CommandI; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentExportData; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.Annotation; @@@ -45,6 -43,7 +45,7 @@@ import jalview.datamodel.Sequence import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; + import jalview.ext.treeviewer.TreeI; import jalview.renderer.ResidueShader; import jalview.renderer.ResidueShaderI; import jalview.schemes.ColourSchemeI; @@@ -69,7 -68,6 +70,7 @@@ import java.util.BitSet import java.util.Deque; import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Map; @@@ -664,7 -662,7 +665,7 @@@ public abstract class AlignmentViewpor * retain any colour thresholds per group while * changing choice of colour scheme (JAL-2386) */ - sg.setColourScheme(cs); + sg.setColourScheme(cs == null ? null : cs.getInstance(this, sg)); if (cs != null) { sg.getGroupColourScheme().alignmentChanged(sg, @@@ -710,13 -708,13 +711,13 @@@ /** * results of cDNA complement consensus visible portion of view */ - protected Hashtable[] hcomplementConsensus = null; + protected Hashtable[] hcomplementConsensus = null; /** * results of secondary structure base pair consensus for visible portion of * view */ - protected Hashtable[] hStrucConsensus = null; + protected Hashtable[] hStrucConsensus = null; protected Conservation hconservation = null; @@@ -745,8 -743,7 +746,8 @@@ } @Override - public void setComplementConsensusHash(Hashtable[] hconsensus) + public void setComplementConsensusHash( + Hashtable[] hconsensus) { this.hcomplementConsensus = hconsensus; } @@@ -758,20 -755,19 +759,20 @@@ } @Override - public Hashtable[] getComplementConsensusHash() + public Hashtable[] getComplementConsensusHash() { return hcomplementConsensus; } @Override - public Hashtable[] getRnaStructureConsensusHash() + public Hashtable[] getRnaStructureConsensusHash() { return hStrucConsensus; } @Override - public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus) + public void setRnaStructureConsensusHash( + Hashtable[] hStrucConsensus) { this.hStrucConsensus = hStrucConsensus; @@@ -961,8 -957,8 +962,9 @@@ changeSupport = null; ranges = null; currentTree = null; + currentExtTree = null; selectionGroup = null; + colSel = null; setAlignment(null); } @@@ -1641,7 -1637,6 +1643,7 @@@ public void invertColumnSelection() { colSel.invertColumnSelection(0, alignment.getWidth(), alignment); + isColSelChanged(true); } @Override @@@ -1747,12 -1742,8 +1749,12 @@@ if (alignment.getHiddenColumns() != null && alignment.getHiddenColumns().hasHiddenColumns()) { - selection = alignment.getHiddenColumns() - .getVisibleSequenceStrings(start, end, seqs); + for (i = 0; i < iSize; i++) + { + Iterator blocks = alignment.getHiddenColumns() + .getVisContigsIterator(start, end + 1, false); + selection[i] = seqs[i].getSequenceStringFromIterator(blocks); + } } else { @@@ -1779,10 -1770,10 +1781,10 @@@ { if (start == 0) { - start = hidden.adjustForHiddenColumns(start); + start = hidden.visibleToAbsoluteColumn(start); } - end = hidden.getHiddenBoundaryRight(start); + end = hidden.getNextHiddenBoundary(false, start); if (start == end) { end = max; @@@ -1797,12 -1788,12 +1799,12 @@@ if (hidden != null && hidden.hasHiddenColumns()) { - start = hidden.adjustForHiddenColumns(end); - start = hidden.getHiddenBoundaryLeft(start) + 1; + start = hidden.visibleToAbsoluteColumn(end); + start = hidden.getNextHiddenBoundary(true, start) + 1; } } while (end < max); - int[][] startEnd = new int[regions.size()][2]; + // int[][] startEnd = new int[regions.size()][2]; return regions; } @@@ -1820,12 -1811,13 +1822,12 @@@ AlignmentAnnotation clone = new AlignmentAnnotation(annot); if (selectedOnly && selectionGroup != null) { - alignment.getHiddenColumns().makeVisibleAnnotation( - selectionGroup.getStartRes(), selectionGroup.getEndRes(), - clone); + clone.makeVisibleAnnotation(selectionGroup.getStartRes(), + selectionGroup.getEndRes(), alignment.getHiddenColumns()); } else { - alignment.getHiddenColumns().makeVisibleAnnotation(clone); + clone.makeVisibleAnnotation(alignment.getHiddenColumns()); } ala.add(clone); } @@@ -2156,7 -2148,7 +2158,7 @@@ * TODO reorder the annotation rows according to group/sequence ordering on * alignment */ - boolean sortg = true; + // boolean sortg = true; // remove old automatic annotation // add any new annotation @@@ -2266,7 -2258,7 +2268,7 @@@ public void clearSequenceColours() { sequenceColours.clear(); - }; + } @Override public AlignViewportI getCodingComplement() @@@ -2719,30 -2711,6 +2721,30 @@@ viewStyle.setProteinFontAsCdna(b); } + @Override + public void setShowComplementFeatures(boolean b) + { + viewStyle.setShowComplementFeatures(b); + } + + @Override + public boolean isShowComplementFeatures() + { + return viewStyle.isShowComplementFeatures(); + } + + @Override + public void setShowComplementFeaturesOnTop(boolean b) + { + viewStyle.setShowComplementFeaturesOnTop(b); + } + + @Override + public boolean isShowComplementFeaturesOnTop() + { + return viewStyle.isShowComplementFeaturesOnTop(); + } + /** * @return true if view should scroll to show the highlighted region of a * sequence @@@ -2815,7 -2783,7 +2817,7 @@@ int lastSeq = alignment.getHeight() - 1; List seqMappings = null; for (int seqNo = ranges - .getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++) + .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++) { sequence = getAlignment().getSequenceAt(seqNo); if (hiddenSequences != null && hiddenSequences.isHidden(sequence)) @@@ -2913,6 -2881,8 +2915,8 @@@ protected TreeModel currentTree = null; + protected TreeI currentExtTree = null; + @Override public boolean hasSearchResults() { @@@ -2984,116 -2954,14 +2988,126 @@@ return currentTree; } + @Override + public AlignmentExportData getAlignExportData( + AlignExportSettingsI options) + { + AlignmentI alignmentToExport = null; + String[] omitHidden = null; + alignmentToExport = null; + + if (hasHiddenColumns() && !options.isExportHiddenColumns()) + { + omitHidden = getViewAsString(false, + options.isExportHiddenSequences()); + } + + int[] alignmentStartEnd = new int[2]; + if (hasHiddenRows() && options.isExportHiddenSequences()) + { + alignmentToExport = getAlignment().getHiddenSequences() + .getFullAlignment(); + } + else + { + alignmentToExport = getAlignment(); + } + alignmentStartEnd = getAlignment().getHiddenColumns() + .getVisibleStartAndEndIndex(alignmentToExport.getWidth()); + AlignmentExportData ed = new AlignmentExportData(alignmentToExport, + omitHidden, alignmentStartEnd); + return ed; + } + + /** + * flag set to indicate if structure views might be out of sync with sequences + * in the alignment + */ + + private boolean needToUpdateStructureViews = false; + + @Override + public boolean isUpdateStructures() + { + return needToUpdateStructureViews; + } + + @Override + public void setUpdateStructures(boolean update) + { + needToUpdateStructureViews = update; + } + + @Override + public boolean needToUpdateStructureViews() + { + boolean update = needToUpdateStructureViews; + needToUpdateStructureViews = false; + return update; + } + + @Override + public void addSequenceGroup(SequenceGroup sequenceGroup) + { + alignment.addGroup(sequenceGroup); + + Color col = sequenceGroup.idColour; + if (col != null) + { + col = col.brighter(); + + for (SequenceI sq : sequenceGroup.getSequences()) + { + setSequenceColour(sq, col); + } + } + + if (codingComplement != null) + { + SequenceGroup mappedGroup = MappingUtils + .mapSequenceGroup(sequenceGroup, this, codingComplement); + if (mappedGroup.getSequences().size() > 0) + { + codingComplement.getAlignment().addGroup(mappedGroup); + + if (col != null) + { + for (SequenceI seq : mappedGroup.getSequences()) + { + codingComplement.setSequenceColour(seq, col); + } + } + } + // propagate the structure view update flag according to our own setting + codingComplement.setUpdateStructures(needToUpdateStructureViews); + } + } + + @Override + public Iterator getViewAsVisibleContigs(boolean selectedRegionOnly) + { + int start = 0; + int end = 0; + if (selectedRegionOnly && selectionGroup != null) + { + start = selectionGroup.getStartRes(); + end = selectionGroup.getEndRes() + 1; + } + else + { + end = alignment.getWidth(); + } + return (alignment.getHiddenColumns().getVisContigsIterator(start, end, + false)); + } + public TreeI getCurrentExtTree() + { + return currentExtTree; + } + + public void setCurrentExtTree(TreeI externalTree) + { + currentExtTree = externalTree; + } + } diff --combined src/jalview/ws/DBRefFetcher.java index eb66016,c01b99c..00bd074 --- a/src/jalview/ws/DBRefFetcher.java +++ b/src/jalview/ws/DBRefFetcher.java @@@ -20,37 -20,33 +20,37 @@@ */ package jalview.ws; +import java.util.Locale; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import jalview.analysis.AlignSeq; +import jalview.api.FeatureSettingsModelI; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.Mapping; import jalview.datamodel.SequenceI; import jalview.gui.CutAndPasteTransfer; -import jalview.gui.DasSourceBrowser; import jalview.gui.Desktop; import jalview.gui.FeatureSettings; import jalview.gui.IProgressIndicator; import jalview.gui.OOMWarning; import jalview.util.DBRefUtils; import jalview.util.MessageManager; -import jalview.ws.dbsources.das.api.jalviewSourceI; -import jalview.ws.dbsources.das.datamodel.DasSequenceSource; import jalview.ws.seqfetcher.DbSourceProxy; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.List; -import java.util.StringTokenizer; -import java.util.Vector; - import uk.ac.ebi.picr.model.UPEntry; import uk.ac.ebi.www.picr.AccessionMappingService.AccessionMapperServiceLocator; @@@ -65,8 -61,6 +65,8 @@@ public class DBRefFetcher implements Ru { private static final String NEWLINE = System.lineSeparator(); + public static final String TRIM_RETRIEVED_SEQUENCES = "TRIM_FETCHED_DATASET_SEQS"; + public interface FetchFinishedListenerI { void finished(); @@@ -78,6 -72,8 +78,6 @@@ CutAndPasteTransfer output = new CutAndPasteTransfer(); - boolean running = false; - /** * picr client instance */ @@@ -139,10 -135,11 +139,10 @@@ } this.dataset = ds; // TODO Jalview 2.5 lots of this code should be in the gui package! - sfetcher = jalview.gui.SequenceFetcher - .getSequenceFetcherSingleton(progressIndicatorFrame); + sfetcher = jalview.gui.SequenceFetcher.getSequenceFetcherSingleton(); // set default behaviour for transferring excess sequence data to the // dataset - trimDsSeqs = Cache.getDefault("TRIM_FETCHED_DATASET_SEQS", true); + trimDsSeqs = Cache.getDefault(TRIM_RETRIEVED_SEQUENCES, true); if (sources == null) { setDatabaseSources(featureSettings, isNucleotide); @@@ -167,6 -164,22 +167,6 @@@ // af.featureSettings_actionPerformed(null); String[] defdb = null; List selsources = new ArrayList<>(); - Vector dasselsrc = (featureSettings != null) - ? featureSettings.getSelectedSources() - : new DasSourceBrowser().getSelectedSources(); - - for (jalviewSourceI src : dasselsrc) - { - List sp = src.getSequenceSourceProxies(); - if (sp != null) - { - selsources.addAll(sp); - if (sp.size() > 1) - { - Cache.log.debug("Added many Db Sources for :" + src.getTitle()); - } - } - } // select appropriate databases based on alignFrame context. if (forNucleotide) { @@@ -220,6 -233,30 +220,6 @@@ } /** - * retrieve all the das sequence sources and add them to the list of db - * sources to retrieve from - */ - public void appendAllDasSources() - { - if (dbSources == null) - { - dbSources = new DbSourceProxy[0]; - } - // append additional sources - DbSourceProxy[] otherdb = sfetcher - .getDbSourceProxyInstances(DasSequenceSource.class); - if (otherdb != null && otherdb.length > 0) - { - DbSourceProxy[] newsrc = new DbSourceProxy[dbSources.length - + otherdb.length]; - System.arraycopy(dbSources, 0, newsrc, 0, dbSources.length); - System.arraycopy(otherdb, 0, newsrc, dbSources.length, - otherdb.length); - dbSources = newsrc; - } - } - - /** * start the fetcher thread * * @param waitTillFinished @@@ -227,13 -264,24 +227,13 @@@ */ public void fetchDBRefs(boolean waitTillFinished) { - // TODO can we not simply write - // if (waitTillFinished) { run(); } else { new Thread(this).start(); } - - Thread thread = new Thread(this, "FetchDBRef"); - thread.start(); - running = true; - if (waitTillFinished) { - while (running) - { - try - { - Thread.sleep(500); - } catch (Exception ex) - { - } - } + run(); + } + else + { - new Thread(this).start(); ++ new Thread(this,"FetchDBRef").start(); } } @@@ -248,7 -296,7 +248,7 @@@ */ void addSeqId(SequenceI seq, String key) { - key = key.toUpperCase(); + key = key.toUpperCase(Locale.ROOT); Vector seqs; if (seqRefs.containsKey(key)) @@@ -286,6 -334,7 +286,6 @@@ throw new Error(MessageManager .getString("error.implementation_error_must_init_dbsources")); } - running = true; long startTime = System.currentTimeMillis(); if (progressWindow != null) { @@@ -306,12 -355,10 +306,12 @@@ e.printStackTrace(); } - Vector sdataset = new Vector<>( - Arrays.asList(dataset)); + Vector sdataset = new Vector<>(Arrays.asList(dataset)); List warningMessages = new ArrayList<>(); + // clear any old feature display settings recorded from past sessions + featureDisplaySettings = null; + int db = 0; while (sdataset.size() > 0 && db < dbSources.length) { @@@ -362,10 -409,10 +362,10 @@@ AlignmentI retrieved = null; try { - if (Cache.log.isDebugEnabled()) + if (Console.isDebugEnabled()) { - Cache.log.debug("Querying " + dbsource.getDbName() - + " with : '" + queryString.toString() + "'"); + Console.debug("Querying " + dbsource.getDbName() + " with : '" + + queryString.toString() + "'"); } retrieved = dbsource.getSequenceRecords(queryString.toString()); } catch (Exception ex) @@@ -378,8 -425,8 +378,8 @@@ } if (retrieved != null) { - transferReferences(sdataset, dbsource.getDbSource(), retrieved, - trimDsSeqs, warningMessages); + transferReferences(sdataset, dbsource, retrieved, trimDsSeqs, + warningMessages); } } else @@@ -389,31 -436,28 +389,31 @@@ && (i < 50); seqIndex++, i++) { SequenceI sequence = dataset[seqIndex]; - DBRefEntry[] uprefs = DBRefUtils + List uprefs = DBRefUtils .selectRefs(sequence.getDBRefs(), new String[] { dbsource.getDbSource() }); // jalview.datamodel.DBRefSource.UNIPROT // }); // check for existing dbrefs to use - if (uprefs != null && uprefs.length > 0) + if (uprefs != null && uprefs.size() > 0) { - for (int j = 0; j < uprefs.length; j++) + for (int j = 0, n = uprefs.size(); j < n; j++) { - addSeqId(sequence, uprefs[j].getAccessionId()); + DBRefEntry upref = uprefs.get(j); + addSeqId(sequence, upref.getAccessionId()); queries.addElement( - uprefs[j].getAccessionId().toUpperCase()); + upref.getAccessionId().toUpperCase(Locale.ROOT)); } } else { + Pattern possibleIds = Pattern.compile("[A-Za-z0-9_]+"); // generate queries from sequence ID string - StringTokenizer st = new StringTokenizer(sequence.getName(), - "|"); - while (st.hasMoreTokens()) + Matcher tokens = possibleIds.matcher(sequence.getName()); + int p = 0; + while (tokens.find(p)) { - String token = st.nextToken(); + String token = tokens.group(); + p = tokens.end(); UPEntry[] presp = null; if (picrClient != null) { @@@ -442,7 -486,7 +442,7 @@@ "Validated ID against PICR... (for what its worth):" + token); addSeqId(sequence, token); - queries.addElement(token.toUpperCase()); + queries.addElement(token.toUpperCase(Locale.ROOT)); } else { @@@ -450,7 -494,7 +450,7 @@@ // System.out.println("Not querying source with // token="+token+"\n"); addSeqId(sequence, token); - queries.addElement(token.toUpperCase()); + queries.addElement(token.toUpperCase(Locale.ROOT)); } } } @@@ -489,6 -533,7 +489,6 @@@ { listener.finished(); } - running = false; } /** @@@ -508,9 -553,9 +508,9 @@@ * @param warningMessages * a list of messages to add to */ - boolean transferReferences(Vector sdataset, String dbSource, - AlignmentI retrievedAl, boolean trimDatasetSeqs, - List warningMessages) + boolean transferReferences(Vector sdataset, + DbSourceProxy dbSourceProxy, AlignmentI retrievedAl, + boolean trimDatasetSeqs, List warningMessages) { // System.out.println("trimming ? " + trimDatasetSeqs); if (retrievedAl == null || retrievedAl.getHeight() == 0) @@@ -518,7 -563,6 +518,7 @@@ return false; } + String dbSource = dbSourceProxy.getDbName(); boolean modified = false; SequenceI[] retrieved = recoverDbSequences( retrievedAl.getSequencesArray()); @@@ -530,7 -574,7 +530,7 @@@ // taking into account all accessionIds and names in the file Vector sequenceMatches = new Vector<>(); // look for corresponding accession ids - DBRefEntry[] entryRefs = DBRefUtils + List entryRefs = DBRefUtils .selectRefs(retrievedSeq.getDBRefs(), new String[] { dbSource }); if (entryRefs == null) @@@ -540,12 -584,11 +540,12 @@@ + dbSource + " on " + retrievedSeq.getName()); continue; } - for (int j = 0; j < entryRefs.length; j++) + for (int j = 0, n = entryRefs.size(); j < n; j++) { - String accessionId = entryRefs[j].getAccessionId(); + DBRefEntry ref = entryRefs.get(j); + String accessionId = ref.getAccessionId(); // match up on accessionId - if (seqRefs.containsKey(accessionId.toUpperCase())) + if (seqRefs.containsKey(accessionId.toUpperCase(Locale.ROOT))) { Vector seqs = seqRefs.get(accessionId); for (int jj = 0; jj < seqs.size(); jj++) @@@ -581,7 -624,7 +581,7 @@@ // could be useful to extend this so we try to find any 'significant' // information in common between two sequence objects. /* - * DBRefEntry[] entryRefs = + * List entryRefs = * jalview.util.DBRefUtils.selectRefs(entry.getDBRef(), new String[] { * dbSource }); for (int j = 0; j < entry.getName().size(); j++) { String * name = entry.getName().elementAt(j).toString(); if @@@ -590,14 -633,10 +590,14 @@@ * seqs.elementAt(jj); if (!sequenceMatches.contains(sequence)) { * sequenceMatches.addElement(sequence); } } } } */ + if (sequenceMatches.size() > 0) + { + addFeatureSettings(dbSourceProxy); + } // sequenceMatches now contains the set of all sequences associated with // the returned db record final String retrievedSeqString = retrievedSeq.getSequenceAsString(); - String entrySeq = retrievedSeqString.toUpperCase(); + String entrySeq = retrievedSeqString.toUpperCase(Locale.ROOT); for (int m = 0; m < sequenceMatches.size(); m++) { sequence = sequenceMatches.elementAt(m); @@@ -606,7 -645,7 +606,7 @@@ // TODO: test for legacy where uniprot or EMBL refs exist but no // mappings are made (but content matches retrieved set) boolean updateRefFrame = sequence.getDBRefs() == null - || sequence.getDBRefs().length == 0; + || sequence.getDBRefs().size() == 0; // TODO: // verify sequence against the entry sequence @@@ -616,7 -655,7 +616,7 @@@ boolean remoteEnclosesLocal = false; String nonGapped = AlignSeq .extractGaps("-. ", sequence.getSequenceAsString()) - .toUpperCase(); + .toUpperCase(Locale.ROOT); int absStart = entrySeq.indexOf(nonGapped); if (absStart == -1) { @@@ -672,8 -711,7 +672,8 @@@ int startShift = absStart - sequenceStart + 1; if (startShift != 0) { - modified |= sequence.getFeatures().shiftFeatures(startShift); + modified |= sequence.getFeatures().shiftFeatures(1, + startShift); } } } @@@ -736,11 -774,10 +736,11 @@@ String ngAlsq = AlignSeq .extractGaps("-. ", alseqs[alsq].getSequenceAsString()) - .toUpperCase(); + .toUpperCase(Locale.ROOT); int oldstrt = alseqs[alsq].getStart(); alseqs[alsq].setStart(sequence.getSequenceAsString() - .toUpperCase().indexOf(ngAlsq) + sequence.getStart()); + .toUpperCase(Locale.ROOT).indexOf(ngAlsq) + + sequence.getStart()); if (oldstrt != alseqs[alsq].getStart()) { alseqs[alsq].setEnd( @@@ -758,40 -795,13 +758,40 @@@ // and remove it from the rest // TODO: decide if we should remove annotated sequence from set sdataset.remove(sequence); - // TODO: should we make a note of sequences that have received new DB - // ids, so we can query all enabled DAS servers for them ? } } return modified; } + Map featureDisplaySettings = null; + + private void addFeatureSettings(DbSourceProxy dbSourceProxy) + { + FeatureSettingsModelI fsettings = dbSourceProxy + .getFeatureColourScheme(); + if (fsettings != null) + { + if (featureDisplaySettings == null) + { + featureDisplaySettings = new HashMap<>(); + } + featureDisplaySettings.put(dbSourceProxy.getDbName(), fsettings); + } + } + + /** + * + * @return any feature settings associated with sources that have provided + * sequences + */ + public List getFeatureSettingsModels() + { + return featureDisplaySettings == null + ? Arrays.asList(new FeatureSettingsModelI[0]) + : Arrays.asList(featureDisplaySettings.values() + .toArray(new FeatureSettingsModelI[1])); + } + /** * Adds the message to the list unless it already contains it * @@@ -814,36 -824,28 +814,36 @@@ */ private SequenceI[] recoverDbSequences(SequenceI[] sequencesArray) { - Vector nseq = new Vector<>(); - for (int i = 0; sequencesArray != null - && i < sequencesArray.length; i++) + int n; + if (sequencesArray == null || (n = sequencesArray.length) == 0) { - nseq.addElement(sequencesArray[i]); - DBRefEntry[] dbr = sequencesArray[i].getDBRefs(); + return sequencesArray; + } + ArrayList nseq = new ArrayList<>(); + for (int i = 0; i < n; i++) + { + nseq.add(sequencesArray[i]); + List dbr = sequencesArray[i].getDBRefs(); Mapping map = null; - for (int r = 0; (dbr != null) && r < dbr.length; r++) + if (dbr != null) { - if ((map = dbr[r].getMap()) != null) + for (int r = 0, rn = dbr.size(); r < rn; r++) { - if (map.getTo() != null && !nseq.contains(map.getTo())) + if ((map = dbr.get(r).getMap()) != null) { - nseq.addElement(map.getTo()); + if (map.getTo() != null && !nseq.contains(map.getTo())) + { + nseq.add(map.getTo()); + } } } } } + // BH 2019.01.25 question here if this is the right logic. Return the + // original if nothing found? if (nseq.size() > 0) { - sequencesArray = new SequenceI[nseq.size()]; - nseq.toArray(sequencesArray); + return nseq.toArray(new SequenceI[nseq.size()]); } return sequencesArray; } diff --combined src/jalview/ws/jws2/Jws2Discoverer.java index 7bf5a97,9bc8713..1fe9495 --- a/src/jalview/ws/jws2/Jws2Discoverer.java +++ b/src/jalview/ws/jws2/Jws2Discoverer.java @@@ -21,7 -21,6 +21,7 @@@ package jalview.ws.jws2; import jalview.bin.Cache; +import jalview.bin.Console; import jalview.gui.AlignFrame; import jalview.gui.Desktop; import jalview.gui.JvSwingUtils; @@@ -165,7 -164,7 +165,7 @@@ public class Jws2Discoverer implements { try { - Cache.log.debug( + Console.debug( "Waiting around for old discovery thread to finish."); // wait around until old discoverer dies Thread.sleep(100); @@@ -174,7 -173,7 +174,7 @@@ } } aborted = false; - Cache.log.debug("Old discovery thread has finished."); + Console.debug("Old discovery thread has finished."); } running = true; @@@ -238,7 -237,7 +238,7 @@@ } qrys.add(squery); - new Thread(squery, "JabaQueryThread").start(); + new Thread(squery, "JabaQuery").start(); } boolean finished = true; do @@@ -250,6 -249,7 +250,6 @@@ } catch (Exception e) { } - ; for (JabaWsServerQuery squery : qrys) { if (squery.isRunning()) @@@ -259,7 -259,7 +259,7 @@@ } if (aborted) { - Cache.log.debug( + Console.debug( "Aborting " + qrys.size() + " JABAWS discovery threads."); for (JabaWsServerQuery squery : qrys) { @@@ -458,7 -458,8 +458,7 @@@ changeSupport.firePropertyChange("services", new Vector(), services); }; - }, "LoadPreferredServiceThread").start(); + }, "LoadPreferredService").start(); - } }); } @@@ -491,7 -492,8 +491,7 @@@ ArrayList hostservices = hosts.get(service.getHost()); if (hostservices == null) { - hosts.put(service.getHost(), - hostservices = new ArrayList<>()); + hosts.put(service.getHost(), hostservices = new ArrayList<>()); hostlist.add(service.getHost()); } hostservices.add(service); @@@ -575,11 -577,6 +575,11 @@@ } } + /** + * + * @param args + * @j2sIgnore + */ public static void main(String[] args) { if (args.length > 0) @@@ -589,6 -586,7 +589,6 @@@ { testUrls.add(url); } - ; } Thread runner = getDiscoverer() .startDiscoverer(new PropertyChangeListener() @@@ -620,6 -618,7 +620,6 @@@ } catch (InterruptedException e) { } - ; } try { @@@ -710,23 -709,23 +710,23 @@@ } else { - Cache.log.warn("Ignoring duplicate url " + url + " in " + Console.warn("Ignoring duplicate url " + url + " in " + JWS2HOSTURLS + " list"); } } catch (MalformedURLException ex) { - Cache.log.warn("Problem whilst trying to make a URL from '" + Console.warn("Problem whilst trying to make a URL from '" + ((url != null) ? url : "") + "'"); - Cache.log.warn( + Console.warn( "This was probably due to a malformed comma separated list" + " in the " + JWS2HOSTURLS + " entry of $(HOME)/.jalview_properties)"); - Cache.log.debug("Exception was ", ex); + Console.debug("Exception was ", ex); } } } catch (Exception ex) { - Cache.log.warn("Error parsing comma separated list of urls in " + Console.warn("Error parsing comma separated list of urls in " + JWS2HOSTURLS + " preference.", ex); } return urls; @@@ -975,7 -974,7 +975,7 @@@ return match; } - Map> preferredServiceMap = new HashMap<>();; + Map> preferredServiceMap = new HashMap<>(); /** * get current preferred service of the given type, or global default diff --combined test/jalview/gui/FreeUpMemoryTest.java index 8a6d688,4398cf1..56f8652 --- a/test/jalview/gui/FreeUpMemoryTest.java +++ b/test/jalview/gui/FreeUpMemoryTest.java @@@ -1,36 -1,8 +1,36 @@@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ package jalview.gui; -import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + import jalview.analysis.AlignmentGenerator; import jalview.bin.Cache; import jalview.bin.Jalview; @@@ -38,97 -10,58 +38,97 @@@ import jalview.datamodel.AlignmentI import jalview.datamodel.SequenceGroup; import jalview.io.DataSourceType; import jalview.io.FileLoader; +import junit.extensions.PA; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; - -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - +/** + * Provides a simple test that memory is released when all windows are closed. + *
    + *
  • generates a reasonably large alignment and loads it
  • + *
  • performs various operations on the alignment
  • + *
  • closes all windows
  • + *
  • requests garbage collection
  • + *
  • asserts that the remaining memory footprint (heap usage) is 'not large' + *
  • + *
+ * If the test fails, this means that reference(s) to large object(s) have + * failed to be garbage collected. In this case: + *
    + *
  • set a breakpoint just before the test assertion in + * {@code checkUsedMemory}
  • + *
  • if the test fails intermittently, make this breakpoint conditional on + * {@code usedMemory > expectedMax}
  • + *
  • run the test to this point (and check that it is about to fail i.e. + * {@code usedMemory > expectedMax})
  • + *
  • use visualvm to obtain a heap + * dump from the suspended process (and kill the test or let it fail)
  • + *
  • inspect the heap dump using visualvm for large objects and their + * referers
  • + *
  • Tips:
  • + *
      + *
    • Perform GC from the Monitor view in visualvm before requesting the heap + * dump - test failure might be simply a delay to GC
    • + *
    • View 'Objects' and filter classes to {@code jalview}. Sort columns by + * Count, or Size, and look for anything suspicious. For example, if the object + * count for {@code Sequence} is non-zero (it shouldn't be), pick any instance, + * and follow the chain of {@code references} to find which class(es) still hold + * references to sequence objects
    • + *
    • If this chain is impracticably long, re-run the test with a smaller + * alignment (set width=100, height=10 in {@code generateAlignment()}), to + * capture a heap which is qualitatively the same, but much smaller, so easier + * to analyse; note this requires an unconditional breakpoint
    • + *
    + *
+ *

+ *

Fixing memory leaks

+ *

+ * Experience shows that often a reference is retained (directly or indirectly) + * by a Swing (or related) component (for example a {@code MouseListener} or + * {@code ActionListener}). There are two possible approaches to fixing: + *

    + *
  • Purist: ensure that all listeners and similar objects are removed when no + * longer needed. May be difficult, to achieve and to maintain as code + * changes.
  • + *
  • Pragmatic: null references to potentially large objects from Jalview + * application classes when no longer needed, typically when a panel is closed. + * This ensures that even if the JVM keeps a reference to a panel or viewport, + * it does not retain a large heap footprint. This is the approach taken in, for + * example, {@code AlignmentPanel.closePanel()} and + * {@code AnnotationPanel.dispose()}.
  • + *
  • Adjust code if necessary; for example an {@code ActionListener} should + * act on {@code av.getAlignment()} and not directly on {@code alignment}, as + * the latter pattern could leave persistent references to the alignment
  • + *
+ * Add code to 'null unused large object references' until the test passes. For + * a final sanity check, capture the heap dump for a passing test, and satisfy + * yourself that only 'small' or 'harmless' {@code jalview} object instances + * (such as enums or singletons) are left in the heap. + */ public class FreeUpMemoryTest { private static final int ONE_MB = 1000 * 1000; + /* + * maximum retained heap usage (in MB) for a passing test + */ + private static int MAX_RESIDUAL_HEAP = 45; + /** * Configure (read-only) Jalview property settings for test */ @BeforeClass(alwaysRun = true) public void setUp() { - Jalview.main(new String[] { "-nonews", "-props", - "test/jalview/testProps.jvprops" }); - Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", - Boolean.TRUE.toString()); - Cache.applicationProperties.setProperty("SHOW_QUALITY", - Boolean.TRUE.toString()); - Cache.applicationProperties.setProperty("SHOW_CONSERVATION", - Boolean.TRUE.toString()); - Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", - Boolean.TRUE.toString()); - Cache.applicationProperties.setProperty("SHOW_IDENTITY", - Boolean.TRUE.toString()); + Jalview.main( + new String[] + { "-nonews", "-props", "test/jalview/testProps.jvprops" }); + String True = Boolean.TRUE.toString(); + Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True); + Cache.applicationProperties.setProperty("SHOW_QUALITY", True); + Cache.applicationProperties.setProperty("SHOW_CONSERVATION", True); + Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", True); + Cache.applicationProperties.setProperty("SHOW_IDENTITY", True); } - /** - * A simple test that memory is released when all windows are closed. - *
    - *
  • generates a reasonably large alignment and loads it
  • - *
  • performs various operations on the alignment
  • - *
  • closes all windows
  • - *
  • requests garbage collection
  • - *
  • asserts that the remaining memory footprint (heap usage) is 'not large' - *
  • - *
- * If the test fails, this suggests that a reference to some large object - * (perhaps the alignment data, or some annotation / Tree / PCA data) has - * failed to be garbage collected. If this is the case, the heap will need to - * be inspected manually (suggest using jvisualvm) in order to track down - * where large objects are still referenced. The code (for example - * AlignmentViewport.dispose()) should then be updated to ensure references to - * large objects are set to null when they are no longer required. - * - * @throws IOException - */ @Test(groups = "Memory") public void testFreeMemoryOnClose() throws IOException { @@@ -139,22 -72,7 +139,22 @@@ Desktop.instance.closeAll_actionPerformed(null); - checkUsedMemory(35L); + checkUsedMemory(MAX_RESIDUAL_HEAP); + } + + /** + * Returns the current total used memory (available memory - free memory), + * rounded down to the nearest MB + * + * @return + */ + private static int getUsedMemory() + { + long availableMemory = Runtime.getRuntime().totalMemory(); + long freeMemory = Runtime.getRuntime().freeMemory(); + long usedMemory = availableMemory - freeMemory; + + return (int) (usedMemory / ONE_MB); } /** @@@ -163,45 -81,47 +163,45 @@@ * * @param expectedMax */ - protected void checkUsedMemory(long expectedMax) + protected void checkUsedMemory(int expectedMax) { /* - * request garbage collection and wait briefly for it to run; + * request garbage collection and wait for it to run (up to 3 times); * NB there is no guarantee when, or whether, it will do so */ - System.gc(); - waitFor(100); - - /* - * a second gc() call should not be necessary - but it is! - * the test passes with it, and fails without it - */ - System.gc(); - waitFor(100); - - /* - * check used memory is 'reasonably low' - */ - long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB; - long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB; - long usedMemory = availableMemory - freeMemory; - - /* - * sanity check - fails if any frame was added after - * closeAll_actionPerformed - */ - assertEquals(Desktop.instance.getAllFrames().length, 0); + long usedMemory = 0L; + Long minUsedMemory = null; + int gcCount = 0; + while (gcCount < 3) + { + gcCount++; + System.gc(); + waitFor(1500); + usedMemory = getUsedMemory(); + if (minUsedMemory == null || usedMemory < minUsedMemory) + { + minUsedMemory = usedMemory; + } + if (usedMemory < expectedMax) + { + break; + } + } /* - * if this assertion fails - * - set a breakpoint here - * - run jvisualvm to inspect a heap dump of Jalview - * - identify large objects in the heap and their referers + * if this assertion fails (reproducibly!) + * - set a breakpoint here, conditional on (usedMemory > expectedMax) + * - run VisualVM to inspect the heap usage, and run GC from VisualVM to check + * it is not simply delayed garbage collection causing the test failure + * - take a heap dump and identify large objects in the heap and their referers * - fix code as necessary to null the references on close */ - System.out.println("Used memory after gc = " + usedMemory + "MB"); - assertTrue(usedMemory < expectedMax, String.format( + System.out.println("(Minimum) Used memory after " + gcCount + + " call(s) to gc() = " + minUsedMemory + "MB (should be <=" + + expectedMax + ")"); + assertTrue(usedMemory <= expectedMax, String.format( "Used memory %d should be less than %d (Recommend running test manually to verify)", - usedMemory, - expectedMax)); + usedMemory, expectedMax)); } /** @@@ -222,20 -142,6 +222,20 @@@ } /* + * open an Overview window + */ + af.overviewMenuItem_actionPerformed(null); + assertNotNull(af.alignPanel.overviewPanel); + + /* + * exercise the pop-up menu in the Overview Panel (JAL-2864) + */ + Object[] args = new Object[] { + new MouseEvent(af, 0, 0, 0, 0, 0, 1, true) }; + PA.invokeMethod(af.alignPanel.overviewPanel, + "showPopupMenu(java.awt.event.MouseEvent)", args); + + /* * set a selection group - potential memory leak if it retains * a reference to the alignment */ @@@ -255,13 -161,20 +255,20 @@@ af.openTreePcaDialog(); CalculationChooser dialog = af.alignPanel.getCalculationDialog(); dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true)); - dialog.createTree("BLOSUM62",dialog.getSimilarityParameters(false)); + try + { + dialog.createTree("BLOSUM62", dialog.getSimilarityParameters(false)); + } catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } /* * wait until Tree and PCA have been computed */ while (af.viewport.getCurrentTree() == null - && dialog.getPcaPanel().isWorking()) + || dialog.getPcaPanel().isWorking()) { waitFor(10); } @@@ -305,7 -218,6 +312,7 @@@ int width = 100000; int height = 100; ag.generate(width, height, 0, 10, 15); + ps.close(); return f; } }