/* * 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 jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.HiddenColumns; import jalview.io.cache.JvCacheableInputBox; import jalview.schemes.AnnotationColourGradient; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.annotationfilter.AnnotationFilterParameter; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Dimension; 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.KeyEvent; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.border.TitledBorder; import net.miginfocom.swing.MigLayout; @SuppressWarnings("serial") public class AnnotationColumnChooser extends AnnotationRowFilter implements ItemListener { private JPanel switchableViewsPanel = new JPanel(new CardLayout()); private JPanel annotationComboBoxPanel = new JPanel(); private StructureFilterPanel gStructureFilterPanel; private StructureFilterPanel ngStructureFilterPanel; private StructureFilterPanel currentStructureFilterPanel; private SearchPanel currentSearchPanel; private SearchPanel gSearchPanel; private SearchPanel ngSearchPanel; private FurtherActionPanel currentFurtherActionPanel; private FurtherActionPanel gFurtherActionPanel; private FurtherActionPanel ngFurtherActionPanel; public static final int ACTION_OPTION_SELECT = 1; public static int ACTION_OPTION_HIDE = 2; public static String NO_GRAPH_VIEW = "0"; public static String GRAPH_VIEW = "1"; private int actionOption = ACTION_OPTION_SELECT; private HiddenColumns oldHiddenColumns; protected static int MIN_WIDTH = (Platform.isJS() ? 370 : 420); protected static int MIN_HEIGHT = (Platform.isJS() ? 370 : 430); public AnnotationColumnChooser(AlignViewport av, final AlignmentPanel ap) { super(av, ap); frame = new JInternalFrame(); frame.setContentPane(this); frame.setLayer(JLayeredPane.PALETTE_LAYER); Desktop.addInternalFrame(frame, MessageManager.getString("label.select_by_annotation"), 0, 0); // BH note: MIGLayout ignores this completely, // possibly creating a frame smaller than specified: frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); addSliderChangeListener(); addSliderMouseListeners(); if (av.getAlignment().getAlignmentAnnotation() == null) { return; } setOldHiddenColumns(av.getAlignment().getHiddenColumns()); adjusting = true; setAnnotations(new JComboBox<>(getAnnotationItems(false))); populateThresholdComboBox(threshold); AnnotationColumnChooser lastChooser = av .getAnnotationColumnSelectionState(); // restore Object state from the previous session if one exists if (lastChooser != null) { currentSearchPanel = lastChooser.getCurrentSearchPanel(); currentStructureFilterPanel = lastChooser .getCurrentStructureFilterPanel(); annotations.setSelectedIndex( lastChooser.getAnnotations().getSelectedIndex()); threshold.setSelectedIndex( lastChooser.getThreshold().getSelectedIndex()); actionOption = lastChooser.getActionOption(); percentThreshold .setSelected(lastChooser.percentThreshold.isSelected()); } try { jbInit(); } catch (Exception ex) { } adjusting = false; updateView(); frame.invalidate(); frame.pack(); } @Override protected void jbInit() { super.jbInit(); JPanel thresholdPanel = new JPanel(); thresholdPanel.setBorder(new TitledBorder( MessageManager.getString("label.threshold_filter"))); thresholdPanel.setBackground(Color.white); thresholdPanel.setFont(JvSwingUtils.getLabelFont()); thresholdPanel.setLayout(new MigLayout("", "[left][right]", "[][]")); percentThreshold.setBackground(Color.white); percentThreshold.setFont(JvSwingUtils.getLabelFont()); JPanel actionPanel = new JPanel(); actionPanel.setBackground(Color.white); actionPanel.setFont(JvSwingUtils.getLabelFont()); JPanel graphFilterView = new JPanel(); graphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]")); graphFilterView.setBackground(Color.white); JPanel noGraphFilterView = new JPanel(); noGraphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]")); noGraphFilterView.setBackground(Color.white); annotationComboBoxPanel.setBackground(Color.white); annotationComboBoxPanel.setFont(JvSwingUtils.getLabelFont()); gSearchPanel = new SearchPanel(this); ngSearchPanel = new SearchPanel(this); gFurtherActionPanel = new FurtherActionPanel(this); ngFurtherActionPanel = new FurtherActionPanel(this); gStructureFilterPanel = new StructureFilterPanel(this); ngStructureFilterPanel = new StructureFilterPanel(this); thresholdPanel.add(getThreshold()); thresholdPanel.add(percentThreshold, "wrap"); thresholdPanel.add(slider, "grow"); thresholdPanel.add(thresholdValue, "span, wrap"); actionPanel.add(ok); actionPanel.add(cancel); graphFilterView.add(gSearchPanel, "grow, span, wrap"); graphFilterView.add(gStructureFilterPanel, "grow, span, wrap"); graphFilterView.add(thresholdPanel, "grow, span, wrap"); graphFilterView.add(gFurtherActionPanel); noGraphFilterView.add(ngSearchPanel, "grow, span, wrap"); noGraphFilterView.add(ngStructureFilterPanel, "grow, span, wrap"); noGraphFilterView.add(ngFurtherActionPanel); annotationComboBoxPanel.add(getAnnotations()); switchableViewsPanel.add(noGraphFilterView, AnnotationColumnChooser.NO_GRAPH_VIEW); switchableViewsPanel.add(graphFilterView, AnnotationColumnChooser.GRAPH_VIEW); this.setLayout(new BorderLayout()); this.add(annotationComboBoxPanel, java.awt.BorderLayout.PAGE_START); this.add(switchableViewsPanel, java.awt.BorderLayout.CENTER); this.add(actionPanel, java.awt.BorderLayout.SOUTH); selectedAnnotationChanged(); updateThresholdPanelToolTip(); this.validate(); } protected void updateThresholdPanelToolTip() { thresholdValue.setToolTipText(""); slider.setToolTipText(""); String defaultTtip = MessageManager .getString("info.change_threshold_mode_to_enable"); String thresh = getThreshold().getSelectedItem().toString(); if (thresh.equalsIgnoreCase("No Threshold")) { thresholdValue.setToolTipText(defaultTtip); slider.setToolTipText(defaultTtip); } } @Override protected void reset() { if (this.getOldHiddenColumns() != null) { av.getColumnSelection().clear(); if (av.getAnnotationColumnSelectionState() != null) { HiddenColumns oldHidden = av.getAnnotationColumnSelectionState() .getOldHiddenColumns(); av.getAlignment().setHiddenColumns(oldHidden); } av.sendSelection(); ap.paintAlignment(true, true); } } @Override public void valueChanged(boolean updateAllAnnotation) { if (slider.isEnabled()) { getCurrentAnnotation().threshold.value = getSliderValue(); updateView(); propagateSeqAssociatedThreshold(updateAllAnnotation, getCurrentAnnotation()); ap.paintAlignment(false, false); } } @Override public void updateView() { // Check if combobox is still adjusting if (adjusting) { return; } AnnotationFilterParameter filterParams = new AnnotationFilterParameter(); setCurrentAnnotation(av.getAlignment() .getAlignmentAnnotation()[annmap[getAnnotations() .getSelectedIndex()]]); int selectedThresholdItem = getSelectedThresholdItem( getThreshold().getSelectedIndex()); slider.setEnabled(true); thresholdValue.setEnabled(true); percentThreshold.setEnabled(true); final AlignmentAnnotation currentAnnotation = getCurrentAnnotation(); if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD) { slider.setEnabled(false); thresholdValue.setEnabled(false); thresholdValue.setText(""); percentThreshold.setEnabled(false); // build filter params } else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD) { if (currentAnnotation.threshold == null) { currentAnnotation.setThreshold(new jalview.datamodel.GraphLine( (currentAnnotation.graphMax - currentAnnotation.graphMin) / 2f, "Threshold", Color.black)); } adjusting = true; setSliderModel(currentAnnotation.graphMin, currentAnnotation.graphMax, currentAnnotation.threshold.value); setThresholdValueText(); slider.setEnabled(true); thresholdValue.setEnabled(true); adjusting = false; // build filter params filterParams.setThresholdType( AnnotationFilterParameter.ThresholdType.NO_THRESHOLD); if (currentAnnotation.isQuantitative()) { filterParams.setThresholdValue(currentAnnotation.threshold.value); if (selectedThresholdItem == AnnotationColourGradient.ABOVE_THRESHOLD) { filterParams.setThresholdType( AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD); } else if (selectedThresholdItem == AnnotationColourGradient.BELOW_THRESHOLD) { filterParams.setThresholdType( AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD); } } } updateThresholdPanelToolTip(); if (currentStructureFilterPanel != null) { if (currentStructureFilterPanel.alphaHelix.isSelected()) { filterParams.setFilterAlphaHelix(true); } if (currentStructureFilterPanel.betaStrand.isSelected()) { filterParams.setFilterBetaSheet(true); } if (currentStructureFilterPanel.turn.isSelected()) { filterParams.setFilterTurn(true); } } if (currentSearchPanel != null) { if (!currentSearchPanel.searchBox.getUserInput().isEmpty()) { filterParams.setRegexString( currentSearchPanel.searchBox.getUserInput()); if (currentSearchPanel.displayName.isSelected()) { filterParams.addRegexSearchField( AnnotationFilterParameter.SearchableAnnotationField.DISPLAY_STRING); } if (currentSearchPanel.description.isSelected()) { filterParams.addRegexSearchField( AnnotationFilterParameter.SearchableAnnotationField.DESCRIPTION); } } } // show hidden columns here, before changing the column selection in // filterAnnotations, because showing hidden columns has the side effect of // adding them to the selection av.showAllHiddenColumns(); av.getColumnSelection().filterAnnotations(currentAnnotation.annotations, filterParams); boolean hideCols = getActionOption() == ACTION_OPTION_HIDE; if (hideCols) { av.hideSelectedColumns(); } av.sendSelection(); filterParams = null; av.setAnnotationColumnSelectionState(this); // only update overview and structures if columns were hidden ap.paintAlignment(hideCols, hideCols); } public HiddenColumns getOldHiddenColumns() { return oldHiddenColumns; } public void setOldHiddenColumns(HiddenColumns currentHiddenColumns) { if (currentHiddenColumns != null) { this.oldHiddenColumns = new HiddenColumns(currentHiddenColumns); } } public FurtherActionPanel getCurrentFutherActionPanel() { return currentFurtherActionPanel; } public void setCurrentFutherActionPanel( FurtherActionPanel currentFutherActionPanel) { this.currentFurtherActionPanel = currentFutherActionPanel; } public SearchPanel getCurrentSearchPanel() { return currentSearchPanel; } public void setCurrentSearchPanel(SearchPanel currentSearchPanel) { this.currentSearchPanel = currentSearchPanel; } public int getActionOption() { return actionOption; } public void setActionOption(int actionOption) { this.actionOption = actionOption; } public StructureFilterPanel getCurrentStructureFilterPanel() { return currentStructureFilterPanel; } public void setCurrentStructureFilterPanel( StructureFilterPanel currentStructureFilterPanel) { this.currentStructureFilterPanel = currentStructureFilterPanel; } public void select_action(ActionEvent actionEvent) { JRadioButton radioButton = (JRadioButton) actionEvent.getSource(); if (radioButton.isSelected()) { setActionOption(ACTION_OPTION_SELECT); updateView(); } } public void hide_action(ActionEvent actionEvent) { JRadioButton radioButton = (JRadioButton) actionEvent.getSource(); if (radioButton.isSelected()) { setActionOption(ACTION_OPTION_HIDE); updateView(); } } @Override public void itemStateChanged(ItemEvent e) { selectedAnnotationChanged(); } @Override public void selectedAnnotationChanged() { String currentView = AnnotationColumnChooser.NO_GRAPH_VIEW; if (av.getAlignment().getAlignmentAnnotation()[annmap[getAnnotations() .getSelectedIndex()]].isQuantitative()) { currentView = AnnotationColumnChooser.GRAPH_VIEW; } saveCache(); gSearchPanel.syncState(); gFurtherActionPanel.syncState(); gStructureFilterPanel.syncState(); ngSearchPanel.syncState(); ngFurtherActionPanel.syncState(); ngStructureFilterPanel.syncState(); CardLayout switchableViewsLayout = (CardLayout) switchableViewsPanel .getLayout(); switchableViewsLayout.show(switchableViewsPanel, currentView); updateView(); } public class FurtherActionPanel extends JPanel { private AnnotationColumnChooser aColChooser; private JRadioButton hideOption = new JRadioButton(); private JRadioButton selectOption = new JRadioButton(); private ButtonGroup optionsGroup = new ButtonGroup(); public FurtherActionPanel(AnnotationColumnChooser aColChooser) { this.aColChooser = aColChooser; JvSwingUtils.jvInitComponent(selectOption, "action.select"); selectOption.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { selectRadioAction(actionEvent); } }); JvSwingUtils.jvInitComponent(hideOption, "action.hide"); hideOption.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { hideRadioAction(actionEvent); } }); optionsGroup.add(selectOption); optionsGroup.add(hideOption); optionsGroup.setSelected(selectOption.getModel(), true); JvSwingUtils.jvInitComponent(this); syncState(); this.add(selectOption); this.add(hideOption); } public void selectRadioAction(ActionEvent actionEvent) { aColChooser.setCurrentFutherActionPanel(this); aColChooser.select_action(actionEvent); } public void hideRadioAction(ActionEvent actionEvent) { aColChooser.setCurrentFutherActionPanel(this); aColChooser.hide_action(actionEvent); } public void syncState() { if (aColChooser .getActionOption() == AnnotationColumnChooser.ACTION_OPTION_HIDE) { this.optionsGroup.setSelected(this.hideOption.getModel(), true); } else { this.optionsGroup.setSelected(this.selectOption.getModel(), true); } } } public class StructureFilterPanel extends JPanel { private AnnotationColumnChooser aColChooser; private JCheckBox alphaHelix = new JCheckBox(); private JCheckBox betaStrand = new JCheckBox(); private JCheckBox turn = new JCheckBox(); private JCheckBox all = new JCheckBox(); public StructureFilterPanel(AnnotationColumnChooser aColChooser) { this.aColChooser = aColChooser; JvSwingUtils.jvInitComponent(alphaHelix, "label.alpha_helix"); alphaHelix.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { alphaHelix_actionPerformed(); } }); JvSwingUtils.jvInitComponent(betaStrand, "label.beta_strand"); betaStrand.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { betaStrand_actionPerformed(); } }); JvSwingUtils.jvInitComponent(turn, "label.turn"); turn.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { turn_actionPerformed(); } }); JvSwingUtils.jvInitComponent(all, "label.select_all"); all.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { all_actionPerformed(); } }); this.setBorder(new TitledBorder( MessageManager.getString("label.structures_filter"))); JvSwingUtils.jvInitComponent(this); this.add(all); this.add(alphaHelix); this.add(betaStrand); this.add(turn); } public void alphaHelix_actionPerformed() { updateSelectAllState(); aColChooser.setCurrentStructureFilterPanel(this); aColChooser.updateView(); } public void betaStrand_actionPerformed() { updateSelectAllState(); aColChooser.setCurrentStructureFilterPanel(this); aColChooser.updateView(); } public void turn_actionPerformed() { updateSelectAllState(); aColChooser.setCurrentStructureFilterPanel(this); aColChooser.updateView(); } public void all_actionPerformed() { if (all.isSelected()) { alphaHelix.setSelected(true); betaStrand.setSelected(true); turn.setSelected(true); } else { alphaHelix.setSelected(false); betaStrand.setSelected(false); turn.setSelected(false); } aColChooser.setCurrentStructureFilterPanel(this); aColChooser.updateView(); } public void updateSelectAllState() { if (alphaHelix.isSelected() && betaStrand.isSelected() && turn.isSelected()) { all.setSelected(true); } else { all.setSelected(false); } } public void syncState() { StructureFilterPanel sfp = aColChooser .getCurrentStructureFilterPanel(); if (sfp != null) { alphaHelix.setSelected(sfp.alphaHelix.isSelected()); betaStrand.setSelected(sfp.betaStrand.isSelected()); turn.setSelected(sfp.turn.isSelected()); if (sfp.all.isSelected()) { all.setSelected(true); alphaHelix.setSelected(true); betaStrand.setSelected(true); turn.setSelected(true); } } } } public class SearchPanel extends JPanel { private AnnotationColumnChooser aColChooser; private JCheckBox displayName = new JCheckBox(); private JCheckBox description = new JCheckBox(); private static final String FILTER_BY_ANN_CACHE_KEY = "CACHE.SELECT_FILTER_BY_ANNOT"; public JvCacheableInputBox searchBox = new JvCacheableInputBox<>( FILTER_BY_ANN_CACHE_KEY, 23); public SearchPanel(AnnotationColumnChooser aColChooser) { this.aColChooser = aColChooser; JvSwingUtils.jvInitComponent(this); this.setBorder(new TitledBorder( MessageManager.getString("label.search_filter"))); searchBox.getComponent().setToolTipText( MessageManager.getString("info.enter_search_text_here")); searchBox.addKeyListener(new java.awt.event.KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { e.consume(); searchStringAction(); } } }); searchBox.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { searchStringAction(); } }); JvSwingUtils.jvInitComponent(displayName, "label.label"); displayName.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { displayNameCheckboxAction(); } }); JvSwingUtils.jvInitComponent(description, "label.description"); description.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { descriptionCheckboxAction(); } }); syncState(); this.add(searchBox.getComponent()); this.add(displayName); this.add(description); } public void displayNameCheckboxAction() { aColChooser.setCurrentSearchPanel(this); aColChooser.updateView(); } public void descriptionCheckboxAction() { aColChooser.setCurrentSearchPanel(this); aColChooser.updateView(); } public void searchStringAction() { aColChooser.setCurrentSearchPanel(this); aColChooser.updateView(); updateSearchPanelToolTips(); searchBox.updateCache(); } public void syncState() { SearchPanel sp = aColChooser.getCurrentSearchPanel(); if (sp != null) { description.setEnabled(sp.description.isEnabled()); description.setSelected(sp.description.isSelected()); displayName.setEnabled(sp.displayName.isEnabled()); displayName.setSelected(sp.displayName.isSelected()); searchBox.setSelectedItem(sp.searchBox.getUserInput()); } updateSearchPanelToolTips(); } public void updateSearchPanelToolTips() { String defaultTtip = MessageManager .getString("info.enter_search_text_to_enable"); String labelTtip = MessageManager.formatMessage( "info.search_in_annotation_label", annotations.getSelectedItem().toString()); String descTtip = MessageManager.formatMessage( "info.search_in_annotation_description", annotations.getSelectedItem().toString()); displayName.setToolTipText( displayName.isEnabled() ? labelTtip : defaultTtip); description.setToolTipText( description.isEnabled() ? descTtip : defaultTtip); } } @Override public void ok_actionPerformed() { saveCache(); super.ok_actionPerformed(); } @Override public void cancel_actionPerformed() { saveCache(); super.cancel_actionPerformed(); } private void saveCache() { gSearchPanel.searchBox.persistCache(); ngSearchPanel.searchBox.persistCache(); gSearchPanel.searchBox.updateCache(); ngSearchPanel.searchBox.updateCache(); } }