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.
23 import java.awt.BorderLayout;
24 import java.awt.Checkbox;
25 import java.awt.CheckboxGroup;
26 import java.awt.FlowLayout;
28 import java.awt.GridLayout;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.ItemEvent;
32 import java.awt.event.ItemListener;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
38 import javax.swing.JButton;
39 import javax.swing.JInternalFrame;
40 import javax.swing.JLayeredPane;
41 import javax.swing.JPanel;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.AlignmentI;
45 import jalview.datamodel.SequenceGroup;
46 import jalview.util.MessageManager;
49 * A panel that allows the user to select which sequence-associated annotation
50 * rows to show or hide.
55 @SuppressWarnings("serial")
56 public class AnnotationChooser extends JPanel
59 private static final Font CHECKBOX_FONT = new Font("Serif", Font.BOLD,
62 private static final int MY_FRAME_WIDTH = 600;
64 private static final int MY_FRAME_HEIGHT = 250;
66 private JInternalFrame frame;
68 private AlignmentPanel ap;
70 private SequenceGroup sg;
72 // all annotation rows' original visible state
73 private boolean[] resetState = null;
75 // is 'Show' selected?
76 private boolean showSelected;
78 // apply settings to selected (or all) sequences?
79 private boolean applyToSelectedSequences;
81 // apply settings to unselected (or all) sequences?
82 private boolean applyToUnselectedSequences;
84 // currently selected 'annotation type' checkboxes
85 private Map<String, String> selectedTypes = new HashMap<>();
92 public AnnotationChooser(AlignmentPanel alignPane)
96 this.sg = alignPane.av.getSelectionGroup();
97 saveResetState(alignPane.getAlignment());
102 } catch (Exception ex)
104 ex.printStackTrace();
110 * Save the initial show/hide state of all annotations to allow a Cancel
115 protected void saveResetState(AlignmentI alignment)
117 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
118 final int count = annotations.length;
119 this.resetState = new boolean[count];
120 for (int i = 0; i < count; i++)
122 this.resetState[i] = annotations[i].visible;
127 * Populate this frame with:
129 * checkboxes for the types of annotation to show or hide (i.e. any annotation
130 * type shown for any sequence in the whole alignment)
132 * option to show or hide selected types
134 * option to show/hide for the currently selected group, or its inverse
136 * OK and Cancel (reset) buttons
138 protected void jbInit()
140 setLayout(new GridLayout(3, 1));
141 add(buildAnnotationTypesPanel());
142 add(buildShowHideOptionsPanel());
143 add(buildActionButtonsPanel());
148 * Construct the panel with checkboxes for annotation types.
152 protected JPanel buildAnnotationTypesPanel()
154 JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
156 List<String> annotationTypes = getAnnotationTypes(
157 this.ap.getAlignment(), true);
159 for (final String type : annotationTypes)
161 final Checkbox check = new Checkbox(type);
162 check.setFont(CHECKBOX_FONT);
163 check.addItemListener(new ItemListener()
166 public void itemStateChanged(ItemEvent evt)
168 if (evt.getStateChange() == ItemEvent.SELECTED)
170 AnnotationChooser.this.selectedTypes.put(type, type);
174 AnnotationChooser.this.selectedTypes.remove(type);
176 changeTypeSelected_actionPerformed(type);
185 * Update display when scope (All/Selected sequences/Unselected) is changed.
187 * Set annotations (with one of the selected types) to the selected Show/Hide
188 * visibility, if they are in the new application scope. Set to the opposite
189 * if outside the scope.
191 * Note this only affects sequence-specific annotations, others are left
194 protected void changeApplyTo_actionPerformed()
196 setAnnotationVisibility(true);
198 ap.updateAnnotation();
202 * Update display when an annotation type is selected or deselected.
204 * If the type is selected, set visibility of annotations of that type which
205 * are in the application scope (all, selected or unselected sequences).
207 * If the type is unselected, set visibility to the opposite value. That is,
208 * treat select/deselect as a 'toggle' operation.
212 protected void changeTypeSelected_actionPerformed(String type)
214 boolean typeSelected = this.selectedTypes.containsKey(type);
215 for (AlignmentAnnotation aa : this.ap.getAlignment()
216 .getAlignmentAnnotation())
218 if (aa.sequenceRef != null && type.equals(aa.label)
219 && isInActionScope(aa))
221 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
224 ap.updateAnnotation();
228 * Update display on change of choice of Show or Hide
230 * For annotations of any selected type, set visibility of annotations of that
231 * type which are in the application scope (all, selected or unselected
234 * @param dataSourceType
236 protected void changeShowHide_actionPerformed()
238 setAnnotationVisibility(false);
240 ap.updateAnnotation();
244 * Update visibility flags on annotation rows as per the current user choices.
246 * @param updateAllRows
248 protected void setAnnotationVisibility(boolean updateAllRows)
250 for (AlignmentAnnotation aa : this.ap.getAlignment()
251 .getAlignmentAnnotation())
253 if (aa.sequenceRef != null)
255 setAnnotationVisibility(aa, updateAllRows);
261 * Determine and set the visibility of the given annotation from the currently
264 * Only update annotations whose type is one of the selected types.
266 * If its sequence is in the selected application scope
267 * (all/selected/unselected sequences), then we set its visibility according
268 * to the current choice of Show or Hide.
270 * If force update of all rows is wanted, then set rows not in the sequence
271 * selection scope to the opposite visibility to those in scope.
274 * @param updateAllRows
276 protected void setAnnotationVisibility(AlignmentAnnotation aa,
277 boolean updateAllRows)
279 if (this.selectedTypes.containsKey(aa.label))
281 if (isInActionScope(aa))
283 aa.visible = this.showSelected;
285 else if (updateAllRows)
287 aa.visible = !this.showSelected;
290 // TODO force not visible if associated sequence is hidden?
291 // currently hiding a sequence does not hide its annotation rows
295 * Answers true if the annotation falls in the current selection criteria for
298 * It must be in the sequence selection group (for 'Apply to selection'), or
299 * not in it (for 'Apply except to selection'). No check needed for 'Apply to
305 protected boolean isInActionScope(AlignmentAnnotation aa)
307 boolean result = false;
308 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
310 // we don't care if the annotation's sequence is selected or not
313 else if (this.sg == null)
315 // shouldn't happen - defensive programming
318 else if (this.sg.getSequences().contains(aa.sequenceRef))
320 // annotation is for a member of the selection group
321 result = this.applyToSelectedSequences ? true : false;
325 // annotation is not associated with the selection group
326 result = this.applyToUnselectedSequences ? true : false;
332 * Get annotation 'types' for an alignment, optionally restricted to
333 * sequence-specific annotations only. The label is currently used for 'type'.
335 * TODO refactor to helper class. See
336 * AnnotationColourChooser.getAnnotationItems() for another client
339 * @param sequenceSpecific
342 public static List<String> getAnnotationTypes(AlignmentI alignment,
343 boolean sequenceSpecificOnly)
345 List<String> result = new ArrayList<>();
346 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
348 if (!sequenceSpecificOnly || aa.sequenceRef != null)
350 String label = aa.label;
351 if (!result.contains(label))
361 * Construct the panel with options to:
363 * show or hide the selected annotation types
365 * do this for the current selection group or its inverse
369 protected JPanel buildShowHideOptionsPanel()
371 JPanel jp = new JPanel();
372 jp.setLayout(new BorderLayout());
374 JPanel showHideOptions = buildShowHidePanel();
375 jp.add(showHideOptions, BorderLayout.CENTER);
377 JPanel applyToOptions = buildApplyToOptionsPanel();
378 jp.add(applyToOptions, BorderLayout.SOUTH);
384 * Build a panel with radio buttons options for sequences to apply show/hide
385 * to. Options are all, current selection, all except current selection.
386 * Initial state has 'current selection' selected.
388 * If the sequence group is null, then we are acting on the whole alignment,
389 * and only 'all sequences' is enabled (and selected).
393 protected JPanel buildApplyToOptionsPanel()
395 final boolean wholeAlignment = this.sg == null;
396 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
397 CheckboxGroup actingOn = new CheckboxGroup();
399 String forAll = MessageManager.getString("label.all_sequences");
400 final Checkbox allSequences = new Checkbox(forAll, actingOn,
402 allSequences.addItemListener(new ItemListener()
405 public void itemStateChanged(ItemEvent evt)
407 if (evt.getStateChange() == ItemEvent.SELECTED)
409 AnnotationChooser.this.setApplyToSelectedSequences(true);
410 AnnotationChooser.this.setApplyToUnselectedSequences(true);
411 AnnotationChooser.this.changeApplyTo_actionPerformed();
415 applyToOptions.add(allSequences);
417 String forSelected = MessageManager
418 .getString("label.selected_sequences");
419 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
421 selectedSequences.setEnabled(!wholeAlignment);
422 selectedSequences.addItemListener(new ItemListener()
425 public void itemStateChanged(ItemEvent evt)
427 if (evt.getStateChange() == ItemEvent.SELECTED)
429 AnnotationChooser.this.setApplyToSelectedSequences(true);
430 AnnotationChooser.this.setApplyToUnselectedSequences(false);
431 AnnotationChooser.this.changeApplyTo_actionPerformed();
435 applyToOptions.add(selectedSequences);
437 String exceptSelected = MessageManager
438 .getString("label.except_selected_sequences");
439 final Checkbox unselectedSequences = new Checkbox(exceptSelected,
441 unselectedSequences.setEnabled(!wholeAlignment);
442 unselectedSequences.addItemListener(new ItemListener()
445 public void itemStateChanged(ItemEvent evt)
447 if (evt.getStateChange() == ItemEvent.SELECTED)
449 AnnotationChooser.this.setApplyToSelectedSequences(false);
450 AnnotationChooser.this.setApplyToUnselectedSequences(true);
451 AnnotationChooser.this.changeApplyTo_actionPerformed();
455 applyToOptions.add(unselectedSequences);
457 // set member variables to match the initial selection state
458 this.applyToSelectedSequences = selectedSequences.getState()
459 || allSequences.getState();
460 this.applyToUnselectedSequences = unselectedSequences.getState()
461 || allSequences.getState();
463 return applyToOptions;
467 * Build a panel with radio button options to show or hide selected
472 protected JPanel buildShowHidePanel()
474 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
475 CheckboxGroup showOrHide = new CheckboxGroup();
478 * Radio button 'Show selected annotations' - initially unselected
480 String showLabel = MessageManager
481 .getString("label.show_selected_annotations");
482 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
483 showOption.addItemListener(new ItemListener()
486 public void itemStateChanged(ItemEvent evt)
488 if (evt.getStateChange() == ItemEvent.SELECTED)
490 AnnotationChooser.this.setShowSelected(true);
491 AnnotationChooser.this.changeShowHide_actionPerformed();
495 showHideOptions.add(showOption);
498 * Radio button 'hide selected annotations'- initially selected
500 String hideLabel = MessageManager
501 .getString("label.hide_selected_annotations");
502 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
503 hideOption.addItemListener(new ItemListener()
506 public void itemStateChanged(ItemEvent evt)
508 if (evt.getStateChange() == ItemEvent.SELECTED)
510 AnnotationChooser.this.setShowSelected(false);
511 AnnotationChooser.this.changeShowHide_actionPerformed();
515 showHideOptions.add(hideOption);
518 * Set member variable to match initial selection state
520 this.showSelected = showOption.getState();
522 return showHideOptions;
526 * Construct the panel with OK and Cancel buttons.
530 protected JPanel buildActionButtonsPanel()
532 JPanel jp = new JPanel();
533 final Font labelFont = JvSwingUtils.getLabelFont();
535 JButton ok = new JButton(MessageManager.getString("action.ok"));
536 ok.setFont(labelFont);
537 ok.addActionListener(new ActionListener()
540 public void actionPerformed(ActionEvent e)
542 close_actionPerformed();
547 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
548 cancel.setFont(labelFont);
549 cancel.addActionListener(new ActionListener()
552 public void actionPerformed(ActionEvent e)
554 cancel_actionPerformed();
563 * On 'Cancel' button, undo any changes.
565 protected void cancel_actionPerformed()
567 resetOriginalState();
569 close_actionPerformed();
573 * Restore annotation visibility to their state on entry here, and repaint
576 protected void resetOriginalState()
579 for (AlignmentAnnotation aa : this.ap.getAlignment()
580 .getAlignmentAnnotation())
582 aa.visible = this.resetState[i++];
587 * On 'Close' button, close the dialog.
589 protected void close_actionPerformed()
593 this.frame.setClosed(true);
594 } catch (Exception exe)
600 * Render a frame containing this panel.
602 private void showFrame()
604 frame = new JInternalFrame();
605 frame.setFrameIcon(null);
606 frame.setContentPane(this);
607 frame.setLayer(JLayeredPane.PALETTE_LAYER);
608 Desktop.addInternalFrame(frame,
609 MessageManager.getString("label.choose_annotations"),
610 MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
613 protected void setShowSelected(boolean showSelected)
615 this.showSelected = showSelected;
618 protected void setApplyToSelectedSequences(
619 boolean applyToSelectedSequences)
621 this.applyToSelectedSequences = applyToSelectedSequences;
624 protected void setApplyToUnselectedSequences(
625 boolean applyToUnselectedSequences)
627 this.applyToUnselectedSequences = applyToUnselectedSequences;
630 protected boolean isShowSelected()
635 protected boolean isApplyToSelectedSequences()
637 return applyToSelectedSequences;
640 protected boolean isApplyToUnselectedSequences()
642 return applyToUnselectedSequences;