2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
24 import java.awt.BorderLayout;
25 import java.awt.CardLayout;
26 import java.awt.Color;
27 import java.awt.Dimension;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.FocusAdapter;
31 import java.awt.event.FocusEvent;
32 import java.awt.event.ItemEvent;
33 import java.awt.event.ItemListener;
34 import java.awt.event.KeyEvent;
36 import javax.swing.ButtonGroup;
37 import javax.swing.JCheckBox;
38 import javax.swing.JComboBox;
39 import javax.swing.JInternalFrame;
40 import javax.swing.JLayeredPane;
41 import javax.swing.JPanel;
42 import javax.swing.JRadioButton;
43 import javax.swing.border.TitledBorder;
45 import jalview.bin.Console;
46 import jalview.datamodel.AlignmentAnnotation;
47 import jalview.datamodel.HiddenColumns;
48 import jalview.io.cache.JvCacheableInputBox;
49 import jalview.schemes.AnnotationColourGradient;
50 import jalview.util.MessageManager;
51 import jalview.util.Platform;
52 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
53 import net.miginfocom.swing.MigLayout;
55 @SuppressWarnings("serial")
56 public class AnnotationColumnChooser extends AnnotationRowFilter
57 implements ItemListener
59 private JPanel switchableViewsPanel = new JPanel(new CardLayout());
61 private JPanel annotationComboBoxPanel = new JPanel();
63 private StructureFilterPanel gStructureFilterPanel;
65 private StructureFilterPanel ngStructureFilterPanel;
67 private StructureFilterPanel currentStructureFilterPanel;
69 private SearchPanel currentSearchPanel;
71 private SearchPanel gSearchPanel;
73 private SearchPanel ngSearchPanel;
75 private FurtherActionPanel currentFurtherActionPanel;
77 private FurtherActionPanel gFurtherActionPanel;
79 private FurtherActionPanel ngFurtherActionPanel;
81 public static final int ACTION_OPTION_SELECT = 1;
83 public static int ACTION_OPTION_HIDE = 2;
85 public static String NO_GRAPH_VIEW = "0";
87 public static String GRAPH_VIEW = "1";
89 private int actionOption = ACTION_OPTION_SELECT;
91 private HiddenColumns oldHiddenColumns;
93 protected static int MIN_WIDTH = (Platform.isJS() ? 370 : 420);
95 protected static int MIN_HEIGHT = (Platform.isJS() ? 370 : 430);
97 public AnnotationColumnChooser(AlignViewport av, final AlignmentPanel ap)
101 public AnnotationColumnChooser(AlignViewport av, final AlignmentPanel ap, AlignmentAnnotation selectedAnnotation)
104 frame = new JInternalFrame();
105 frame.setFrameIcon(null);
106 frame.setContentPane(this);
107 frame.setLayer(JLayeredPane.PALETTE_LAYER);
108 Desktop.addInternalFrame(frame,
109 MessageManager.getString("label.select_by_annotation"), 0, 0);
110 // BH note: MIGLayout ignores this completely,
111 // possibly creating a frame smaller than specified:
112 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
114 addSliderChangeListener();
115 addSliderMouseListeners();
117 if (av.getAlignment().getAlignmentAnnotation() == null)
121 setOldHiddenColumns(av.getAlignment().getHiddenColumns());
124 setAnnotations(new JComboBox<>(getAnnotationItems(false)));
125 populateThresholdComboBox(threshold);
126 AnnotationColumnChooser lastChooser = av
127 .getAnnotationColumnSelectionState();
128 // restore Object state from the previous session if one exists
129 if (lastChooser != null)
131 currentSearchPanel = lastChooser.getCurrentSearchPanel();
132 currentStructureFilterPanel = lastChooser
133 .getCurrentStructureFilterPanel();
134 annotations.setSelectedIndex(
135 lastChooser.getAnnotations().getSelectedIndex());
136 threshold.setSelectedIndex(
137 lastChooser.getThreshold().getSelectedIndex());
138 actionOption = lastChooser.getActionOption();
140 .setSelected(lastChooser.percentThreshold.isSelected());
142 if (selectedAnnotation!=null)
145 setCurrentAnnotation(selectedAnnotation);
146 annotations.setSelectedItem(getAnnotationMenuLabel(selectedAnnotation));
147 } catch (Exception x)
149 Console.error("Couldn't select annotation in column chooser",x);
156 } catch (Exception ex)
167 protected void jbInit()
171 JPanel thresholdPanel = new JPanel();
172 thresholdPanel.setBorder(new TitledBorder(
173 MessageManager.getString("label.threshold_filter")));
174 thresholdPanel.setBackground(Color.white);
175 thresholdPanel.setFont(JvSwingUtils.getLabelFont());
176 thresholdPanel.setLayout(new MigLayout("", "[left][right]", "[][]"));
178 percentThreshold.setBackground(Color.white);
179 percentThreshold.setFont(JvSwingUtils.getLabelFont());
181 JPanel actionPanel = new JPanel();
182 actionPanel.setBackground(Color.white);
183 actionPanel.setFont(JvSwingUtils.getLabelFont());
185 JPanel graphFilterView = new JPanel();
186 graphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]"));
187 graphFilterView.setBackground(Color.white);
189 JPanel noGraphFilterView = new JPanel();
190 noGraphFilterView.setLayout(new MigLayout("", "[left][right]", "[][]"));
191 noGraphFilterView.setBackground(Color.white);
193 annotationComboBoxPanel.setBackground(Color.white);
194 annotationComboBoxPanel.setFont(JvSwingUtils.getLabelFont());
196 gSearchPanel = new SearchPanel(this);
197 ngSearchPanel = new SearchPanel(this);
198 gFurtherActionPanel = new FurtherActionPanel(this);
199 ngFurtherActionPanel = new FurtherActionPanel(this);
200 gStructureFilterPanel = new StructureFilterPanel(this);
201 ngStructureFilterPanel = new StructureFilterPanel(this);
203 thresholdPanel.add(getThreshold());
204 thresholdPanel.add(percentThreshold, "wrap");
205 thresholdPanel.add(slider, "grow");
206 thresholdPanel.add(thresholdValue, "span, wrap");
209 actionPanel.add(cancel);
211 graphFilterView.add(gSearchPanel, "grow, span, wrap");
212 graphFilterView.add(gStructureFilterPanel, "grow, span, wrap");
213 graphFilterView.add(thresholdPanel, "grow, span, wrap");
214 graphFilterView.add(gFurtherActionPanel);
216 noGraphFilterView.add(ngSearchPanel, "grow, span, wrap");
217 noGraphFilterView.add(ngStructureFilterPanel, "grow, span, wrap");
218 noGraphFilterView.add(ngFurtherActionPanel);
220 annotationComboBoxPanel.add(getAnnotations());
221 switchableViewsPanel.add(noGraphFilterView,
222 AnnotationColumnChooser.NO_GRAPH_VIEW);
223 switchableViewsPanel.add(graphFilterView,
224 AnnotationColumnChooser.GRAPH_VIEW);
225 this.setLayout(new BorderLayout());
226 this.add(annotationComboBoxPanel, java.awt.BorderLayout.PAGE_START);
227 this.add(switchableViewsPanel, java.awt.BorderLayout.CENTER);
228 this.add(actionPanel, java.awt.BorderLayout.SOUTH);
230 selectedAnnotationChanged();
231 updateThresholdPanelToolTip();
235 protected void updateThresholdPanelToolTip()
237 thresholdValue.setToolTipText("");
238 slider.setToolTipText("");
240 String defaultTtip = MessageManager
241 .getString("info.change_threshold_mode_to_enable");
243 String thresh = getThreshold().getSelectedItem().toString();
244 if (thresh.equalsIgnoreCase("No Threshold"))
246 thresholdValue.setToolTipText(defaultTtip);
247 slider.setToolTipText(defaultTtip);
252 protected void reset()
254 if (this.getOldHiddenColumns() != null)
256 av.getColumnSelection().clear();
258 if (av.getAnnotationColumnSelectionState() != null)
260 HiddenColumns oldHidden = av.getAnnotationColumnSelectionState()
261 .getOldHiddenColumns();
262 av.getAlignment().setHiddenColumns(oldHidden);
265 ap.paintAlignment(true, true);
270 public void valueChanged(boolean updateAllAnnotation)
272 if (slider.isEnabled())
274 getCurrentAnnotation().threshold.value = getSliderValue();
276 propagateSeqAssociatedThreshold(updateAllAnnotation,
277 getCurrentAnnotation());
278 ap.paintAlignment(false, false);
283 public void updateView()
285 // Check if combobox is still adjusting
291 AnnotationFilterParameter filterParams = new AnnotationFilterParameter();
293 setCurrentAnnotation(av.getAlignment()
294 .getAlignmentAnnotation()[annmap[getAnnotations()
295 .getSelectedIndex()]]);
297 int selectedThresholdItem = getSelectedThresholdItem(
298 getThreshold().getSelectedIndex());
300 slider.setEnabled(true);
301 thresholdValue.setEnabled(true);
302 percentThreshold.setEnabled(true);
304 final AlignmentAnnotation currentAnnotation = getCurrentAnnotation();
305 if (selectedThresholdItem == AnnotationColourGradient.NO_THRESHOLD)
307 slider.setEnabled(false);
308 thresholdValue.setEnabled(false);
309 thresholdValue.setText("");
310 percentThreshold.setEnabled(false);
311 // build filter params
313 else if (selectedThresholdItem != AnnotationColourGradient.NO_THRESHOLD)
315 if (currentAnnotation.threshold == null)
317 currentAnnotation.setThreshold(new jalview.datamodel.GraphLine(
318 (currentAnnotation.graphMax - currentAnnotation.graphMin)
320 "Threshold", Color.black));
325 setSliderModel(currentAnnotation.graphMin, currentAnnotation.graphMax,
326 currentAnnotation.threshold.value);
328 setThresholdValueText();
330 slider.setEnabled(true);
331 thresholdValue.setEnabled(true);
334 // build filter params
335 filterParams.setThresholdType(
336 AnnotationFilterParameter.ThresholdType.NO_THRESHOLD);
337 if (currentAnnotation.isQuantitative())
339 filterParams.setThresholdValue(currentAnnotation.threshold.value);
341 if (selectedThresholdItem == AnnotationColourGradient.ABOVE_THRESHOLD)
343 filterParams.setThresholdType(
344 AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD);
346 else if (selectedThresholdItem == AnnotationColourGradient.BELOW_THRESHOLD)
348 filterParams.setThresholdType(
349 AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD);
354 updateThresholdPanelToolTip();
355 if (currentStructureFilterPanel != null)
357 if (currentStructureFilterPanel.alphaHelix.isSelected())
359 filterParams.setFilterAlphaHelix(true);
361 if (currentStructureFilterPanel.betaStrand.isSelected())
363 filterParams.setFilterBetaSheet(true);
365 if (currentStructureFilterPanel.turn.isSelected())
367 filterParams.setFilterTurn(true);
371 if (currentSearchPanel != null)
373 if (!currentSearchPanel.searchBox.getUserInput().isEmpty())
375 filterParams.setRegexString(
376 currentSearchPanel.searchBox.getUserInput());
377 if (currentSearchPanel.displayName.isSelected())
379 filterParams.addRegexSearchField(
380 AnnotationFilterParameter.SearchableAnnotationField.DISPLAY_STRING);
382 if (currentSearchPanel.description.isSelected())
384 filterParams.addRegexSearchField(
385 AnnotationFilterParameter.SearchableAnnotationField.DESCRIPTION);
390 // show hidden columns here, before changing the column selection in
391 // filterAnnotations, because showing hidden columns has the side effect of
392 // adding them to the selection
393 av.showAllHiddenColumns();
394 av.getColumnSelection().filterAnnotations(currentAnnotation,
397 boolean hideCols = getActionOption() == ACTION_OPTION_HIDE;
400 av.hideSelectedColumns();
405 av.setAnnotationColumnSelectionState(this);
406 // only update overview and structures if columns were hidden
407 ap.paintAlignment(hideCols, hideCols);
410 public HiddenColumns getOldHiddenColumns()
412 return oldHiddenColumns;
415 public void setOldHiddenColumns(HiddenColumns currentHiddenColumns)
417 if (currentHiddenColumns != null)
419 this.oldHiddenColumns = new HiddenColumns(currentHiddenColumns);
423 public FurtherActionPanel getCurrentFutherActionPanel()
425 return currentFurtherActionPanel;
428 public void setCurrentFutherActionPanel(
429 FurtherActionPanel currentFutherActionPanel)
431 this.currentFurtherActionPanel = currentFutherActionPanel;
434 public SearchPanel getCurrentSearchPanel()
436 return currentSearchPanel;
439 public void setCurrentSearchPanel(SearchPanel currentSearchPanel)
441 this.currentSearchPanel = currentSearchPanel;
444 public int getActionOption()
449 public void setActionOption(int actionOption)
451 this.actionOption = actionOption;
454 public StructureFilterPanel getCurrentStructureFilterPanel()
456 return currentStructureFilterPanel;
459 public void setCurrentStructureFilterPanel(
460 StructureFilterPanel currentStructureFilterPanel)
462 this.currentStructureFilterPanel = currentStructureFilterPanel;
465 public void select_action(ActionEvent actionEvent)
467 JRadioButton radioButton = (JRadioButton) actionEvent.getSource();
468 if (radioButton.isSelected())
470 setActionOption(ACTION_OPTION_SELECT);
475 public void hide_action(ActionEvent actionEvent)
477 JRadioButton radioButton = (JRadioButton) actionEvent.getSource();
478 if (radioButton.isSelected())
480 setActionOption(ACTION_OPTION_HIDE);
486 public void itemStateChanged(ItemEvent e)
488 selectedAnnotationChanged();
492 public void selectedAnnotationChanged()
494 String currentView = AnnotationColumnChooser.NO_GRAPH_VIEW;
495 if (av.getAlignment().getAlignmentAnnotation()[annmap[getAnnotations()
496 .getSelectedIndex()]].isQuantitative())
498 currentView = AnnotationColumnChooser.GRAPH_VIEW;
501 gSearchPanel.syncState();
502 gFurtherActionPanel.syncState();
503 gStructureFilterPanel.syncState();
505 ngSearchPanel.syncState();
506 ngFurtherActionPanel.syncState();
507 ngStructureFilterPanel.syncState();
509 CardLayout switchableViewsLayout = (CardLayout) switchableViewsPanel
511 switchableViewsLayout.show(switchableViewsPanel, currentView);
515 public class FurtherActionPanel extends JPanel
517 private AnnotationColumnChooser aColChooser;
519 private JRadioButton hideOption = new JRadioButton();
521 private JRadioButton selectOption = new JRadioButton();
523 private ButtonGroup optionsGroup = new ButtonGroup();
525 public FurtherActionPanel(AnnotationColumnChooser aColChooser)
527 this.aColChooser = aColChooser;
528 JvSwingUtils.jvInitComponent(selectOption, "action.select");
529 selectOption.addActionListener(new ActionListener()
532 public void actionPerformed(ActionEvent actionEvent)
534 selectRadioAction(actionEvent);
538 JvSwingUtils.jvInitComponent(hideOption, "action.hide");
539 hideOption.addActionListener(new ActionListener()
542 public void actionPerformed(ActionEvent actionEvent)
544 hideRadioAction(actionEvent);
548 optionsGroup.add(selectOption);
549 optionsGroup.add(hideOption);
550 optionsGroup.setSelected(selectOption.getModel(), true);
552 JvSwingUtils.jvInitComponent(this);
555 this.add(selectOption);
556 this.add(hideOption);
559 public void selectRadioAction(ActionEvent actionEvent)
561 aColChooser.setCurrentFutherActionPanel(this);
562 aColChooser.select_action(actionEvent);
565 public void hideRadioAction(ActionEvent actionEvent)
567 aColChooser.setCurrentFutherActionPanel(this);
568 aColChooser.hide_action(actionEvent);
571 public void syncState()
574 .getActionOption() == AnnotationColumnChooser.ACTION_OPTION_HIDE)
576 this.optionsGroup.setSelected(this.hideOption.getModel(), true);
580 this.optionsGroup.setSelected(this.selectOption.getModel(), true);
585 public class StructureFilterPanel extends JPanel
587 private AnnotationColumnChooser aColChooser;
589 private JCheckBox alphaHelix = new JCheckBox();
591 private JCheckBox betaStrand = new JCheckBox();
593 private JCheckBox turn = new JCheckBox();
595 private JCheckBox all = new JCheckBox();
597 public StructureFilterPanel(AnnotationColumnChooser aColChooser)
599 this.aColChooser = aColChooser;
601 JvSwingUtils.jvInitComponent(alphaHelix, "label.alpha_helix");
602 alphaHelix.addActionListener(new ActionListener()
605 public void actionPerformed(ActionEvent actionEvent)
607 alphaHelix_actionPerformed();
611 JvSwingUtils.jvInitComponent(betaStrand, "label.beta_strand");
612 betaStrand.addActionListener(new ActionListener()
615 public void actionPerformed(ActionEvent actionEvent)
617 betaStrand_actionPerformed();
621 JvSwingUtils.jvInitComponent(turn, "label.turn");
622 turn.addActionListener(new ActionListener()
625 public void actionPerformed(ActionEvent actionEvent)
627 turn_actionPerformed();
631 JvSwingUtils.jvInitComponent(all, "label.select_all");
632 all.addActionListener(new ActionListener()
635 public void actionPerformed(ActionEvent actionEvent)
637 all_actionPerformed();
641 this.setBorder(new TitledBorder(
642 MessageManager.getString("label.structures_filter")));
643 JvSwingUtils.jvInitComponent(this);
646 this.add(alphaHelix);
647 this.add(betaStrand);
651 public void alphaHelix_actionPerformed()
653 updateSelectAllState();
654 aColChooser.setCurrentStructureFilterPanel(this);
655 aColChooser.updateView();
658 public void betaStrand_actionPerformed()
660 updateSelectAllState();
661 aColChooser.setCurrentStructureFilterPanel(this);
662 aColChooser.updateView();
665 public void turn_actionPerformed()
667 updateSelectAllState();
668 aColChooser.setCurrentStructureFilterPanel(this);
669 aColChooser.updateView();
672 public void all_actionPerformed()
674 if (all.isSelected())
676 alphaHelix.setSelected(true);
677 betaStrand.setSelected(true);
678 turn.setSelected(true);
682 alphaHelix.setSelected(false);
683 betaStrand.setSelected(false);
684 turn.setSelected(false);
686 aColChooser.setCurrentStructureFilterPanel(this);
687 aColChooser.updateView();
690 public void updateSelectAllState()
692 if (alphaHelix.isSelected() && betaStrand.isSelected()
693 && turn.isSelected())
695 all.setSelected(true);
699 all.setSelected(false);
703 public void syncState()
705 StructureFilterPanel sfp = aColChooser
706 .getCurrentStructureFilterPanel();
709 alphaHelix.setSelected(sfp.alphaHelix.isSelected());
710 betaStrand.setSelected(sfp.betaStrand.isSelected());
711 turn.setSelected(sfp.turn.isSelected());
712 if (sfp.all.isSelected())
714 all.setSelected(true);
715 alphaHelix.setSelected(true);
716 betaStrand.setSelected(true);
717 turn.setSelected(true);
724 public class SearchPanel extends JPanel
726 private AnnotationColumnChooser aColChooser;
728 private JCheckBox displayName = new JCheckBox();
730 private JCheckBox description = new JCheckBox();
732 private static final String FILTER_BY_ANN_CACHE_KEY = "CACHE.SELECT_FILTER_BY_ANNOT";
734 public JvCacheableInputBox<String> searchBox = new JvCacheableInputBox<>(
735 FILTER_BY_ANN_CACHE_KEY, 23);
737 public SearchPanel(AnnotationColumnChooser aColChooser)
740 this.aColChooser = aColChooser;
741 JvSwingUtils.jvInitComponent(this);
742 this.setBorder(new TitledBorder(
743 MessageManager.getString("label.search_filter")));
745 searchBox.getComponent().setToolTipText(
746 MessageManager.getString("info.enter_search_text_here"));
747 searchBox.addKeyListener(new java.awt.event.KeyAdapter()
750 public void keyPressed(KeyEvent e)
752 if (e.getKeyCode() == KeyEvent.VK_ENTER)
755 searchStringAction();
759 searchBox.addFocusListener(new FocusAdapter()
762 public void focusLost(FocusEvent e)
764 searchStringAction();
768 JvSwingUtils.jvInitComponent(displayName, "label.label");
769 displayName.addActionListener(new ActionListener()
772 public void actionPerformed(ActionEvent actionEvent)
774 displayNameCheckboxAction();
778 JvSwingUtils.jvInitComponent(description, "label.description");
779 description.addActionListener(new ActionListener()
782 public void actionPerformed(ActionEvent actionEvent)
784 descriptionCheckboxAction();
789 this.add(searchBox.getComponent());
790 this.add(displayName);
791 this.add(description);
794 public void displayNameCheckboxAction()
796 aColChooser.setCurrentSearchPanel(this);
797 aColChooser.updateView();
800 public void descriptionCheckboxAction()
802 aColChooser.setCurrentSearchPanel(this);
803 aColChooser.updateView();
806 public void searchStringAction()
808 aColChooser.setCurrentSearchPanel(this);
809 aColChooser.updateView();
810 updateSearchPanelToolTips();
811 searchBox.updateCache();
814 public void syncState()
816 SearchPanel sp = aColChooser.getCurrentSearchPanel();
819 description.setEnabled(sp.description.isEnabled());
820 description.setSelected(sp.description.isSelected());
822 displayName.setEnabled(sp.displayName.isEnabled());
823 displayName.setSelected(sp.displayName.isSelected());
825 searchBox.setSelectedItem(sp.searchBox.getUserInput());
827 updateSearchPanelToolTips();
830 public void updateSearchPanelToolTips()
832 String defaultTtip = MessageManager
833 .getString("info.enter_search_text_to_enable");
834 String labelTtip = MessageManager.formatMessage(
835 "info.search_in_annotation_label",
836 annotations.getSelectedItem().toString());
837 String descTtip = MessageManager.formatMessage(
838 "info.search_in_annotation_description",
839 annotations.getSelectedItem().toString());
840 displayName.setToolTipText(
841 displayName.isEnabled() ? labelTtip : defaultTtip);
842 description.setToolTipText(
843 description.isEnabled() ? descTtip : defaultTtip);
848 public void ok_actionPerformed()
851 super.ok_actionPerformed();
855 public void cancel_actionPerformed()
858 super.cancel_actionPerformed();
861 private void saveCache()
863 gSearchPanel.searchBox.persistCache();
864 ngSearchPanel.searchBox.persistCache();
865 gSearchPanel.searchBox.updateCache();
866 ngSearchPanel.searchBox.updateCache();
869 public static void displayFor(AlignViewport av, AlignmentPanel ap,
870 AlignmentAnnotation alignmentAnnotation)
872 AnnotationColumnChooser colchooser = new AnnotationColumnChooser(av, ap, alignmentAnnotation);