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 jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.SequenceGroup;
26 import jalview.util.MessageManager;
28 import java.awt.BorderLayout;
29 import java.awt.Checkbox;
30 import java.awt.CheckboxGroup;
31 import java.awt.FlowLayout;
33 import java.awt.GridLayout;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.ActionListener;
36 import java.awt.event.ItemEvent;
37 import java.awt.event.ItemListener;
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
43 import javax.swing.JButton;
44 import javax.swing.JInternalFrame;
45 import javax.swing.JLayeredPane;
46 import javax.swing.JPanel;
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 // copied from AnnotationLabel.actionPerformed (after show/hide row)...
199 // TODO should drive this functionality into AlignmentPanel
200 ap.updateAnnotation();
201 // this.ap.annotationPanel.adjustPanelHeight();
202 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
203 // this.ap.annotationPanel.getSize().height);
204 // this.ap.validate();
205 this.ap.paintAlignment(true, false);
209 * Update display when an annotation type is selected or deselected.
211 * If the type is selected, set visibility of annotations of that type which
212 * are in the application scope (all, selected or unselected sequences).
214 * If the type is unselected, set visibility to the opposite value. That is,
215 * treat select/deselect as a 'toggle' operation.
219 protected void changeTypeSelected_actionPerformed(String type)
221 boolean typeSelected = this.selectedTypes.containsKey(type);
222 for (AlignmentAnnotation aa : this.ap.getAlignment()
223 .getAlignmentAnnotation())
225 if (aa.sequenceRef != null && type.equals(aa.label)
226 && isInActionScope(aa))
228 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
231 ap.updateAnnotation();
232 // // this.ap.annotationPanel.adjustPanelHeight();
233 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
234 // this.ap.annotationPanel.getSize().height);
235 // this.ap.validate();
236 this.ap.paintAlignment(true, false);
240 * Update display on change of choice of Show or Hide
242 * For annotations of any selected type, set visibility of annotations of that
243 * type which are in the application scope (all, selected or unselected
246 * @param dataSourceType
248 protected void changeShowHide_actionPerformed()
250 setAnnotationVisibility(false);
252 this.ap.updateAnnotation();
253 // this.ap.annotationPanel.adjustPanelHeight();
254 this.ap.paintAlignment(true, false);
258 * Update visibility flags on annotation rows as per the current user choices.
260 * @param updateAllRows
262 protected void setAnnotationVisibility(boolean updateAllRows)
264 for (AlignmentAnnotation aa : this.ap.getAlignment()
265 .getAlignmentAnnotation())
267 if (aa.sequenceRef != null)
269 setAnnotationVisibility(aa, updateAllRows);
275 * Determine and set the visibility of the given annotation from the currently
278 * Only update annotations whose type is one of the selected types.
280 * If its sequence is in the selected application scope
281 * (all/selected/unselected sequences), then we set its visibility according
282 * to the current choice of Show or Hide.
284 * If force update of all rows is wanted, then set rows not in the sequence
285 * selection scope to the opposite visibility to those in scope.
288 * @param updateAllRows
290 protected void setAnnotationVisibility(AlignmentAnnotation aa,
291 boolean updateAllRows)
293 if (this.selectedTypes.containsKey(aa.label))
295 if (isInActionScope(aa))
297 aa.visible = this.showSelected;
299 else if (updateAllRows)
301 aa.visible = !this.showSelected;
304 // TODO force not visible if associated sequence is hidden?
305 // currently hiding a sequence does not hide its annotation rows
309 * Answers true if the annotation falls in the current selection criteria for
312 * It must be in the sequence selection group (for 'Apply to selection'), or
313 * not in it (for 'Apply except to selection'). No check needed for 'Apply to
319 protected boolean isInActionScope(AlignmentAnnotation aa)
321 boolean result = false;
322 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
324 // we don't care if the annotation's sequence is selected or not
327 else if (this.sg == null)
329 // shouldn't happen - defensive programming
332 else if (this.sg.getSequences().contains(aa.sequenceRef))
334 // annotation is for a member of the selection group
335 result = this.applyToSelectedSequences ? true : false;
339 // annotation is not associated with the selection group
340 result = this.applyToUnselectedSequences ? true : false;
346 * Get annotation 'types' for an alignment, optionally restricted to
347 * sequence-specific annotations only. The label is currently used for 'type'.
349 * TODO refactor to helper class. See
350 * AnnotationColourChooser.getAnnotationItems() for another client
353 * @param sequenceSpecific
356 public static List<String> getAnnotationTypes(AlignmentI alignment,
357 boolean sequenceSpecificOnly)
359 List<String> result = new ArrayList<>();
360 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
362 if (!sequenceSpecificOnly || aa.sequenceRef != null)
364 String label = aa.label;
365 if (!result.contains(label))
375 * Construct the panel with options to:
377 * show or hide the selected annotation types
379 * do this for the current selection group or its inverse
383 protected JPanel buildShowHideOptionsPanel()
385 JPanel jp = new JPanel();
386 jp.setLayout(new BorderLayout());
388 JPanel showHideOptions = buildShowHidePanel();
389 jp.add(showHideOptions, BorderLayout.CENTER);
391 JPanel applyToOptions = buildApplyToOptionsPanel();
392 jp.add(applyToOptions, BorderLayout.SOUTH);
398 * Build a panel with radio buttons options for sequences to apply show/hide
399 * to. Options are all, current selection, all except current selection.
400 * Initial state has 'current selection' selected.
402 * If the sequence group is null, then we are acting on the whole alignment,
403 * and only 'all sequences' is enabled (and selected).
407 protected JPanel buildApplyToOptionsPanel()
409 final boolean wholeAlignment = this.sg == null;
410 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
411 CheckboxGroup actingOn = new CheckboxGroup();
413 String forAll = MessageManager.getString("label.all_sequences");
414 final Checkbox allSequences = new Checkbox(forAll, actingOn,
416 allSequences.addItemListener(new ItemListener()
419 public void itemStateChanged(ItemEvent evt)
421 if (evt.getStateChange() == ItemEvent.SELECTED)
423 AnnotationChooser.this.setApplyToSelectedSequences(true);
424 AnnotationChooser.this.setApplyToUnselectedSequences(true);
425 AnnotationChooser.this.changeApplyTo_actionPerformed();
429 applyToOptions.add(allSequences);
431 String forSelected = MessageManager
432 .getString("label.selected_sequences");
433 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
435 selectedSequences.setEnabled(!wholeAlignment);
436 selectedSequences.addItemListener(new ItemListener()
439 public void itemStateChanged(ItemEvent evt)
441 if (evt.getStateChange() == ItemEvent.SELECTED)
443 AnnotationChooser.this.setApplyToSelectedSequences(true);
444 AnnotationChooser.this.setApplyToUnselectedSequences(false);
445 AnnotationChooser.this.changeApplyTo_actionPerformed();
449 applyToOptions.add(selectedSequences);
451 String exceptSelected = MessageManager
452 .getString("label.except_selected_sequences");
453 final Checkbox unselectedSequences = new Checkbox(exceptSelected,
455 unselectedSequences.setEnabled(!wholeAlignment);
456 unselectedSequences.addItemListener(new ItemListener()
459 public void itemStateChanged(ItemEvent evt)
461 if (evt.getStateChange() == ItemEvent.SELECTED)
463 AnnotationChooser.this.setApplyToSelectedSequences(false);
464 AnnotationChooser.this.setApplyToUnselectedSequences(true);
465 AnnotationChooser.this.changeApplyTo_actionPerformed();
469 applyToOptions.add(unselectedSequences);
471 // set member variables to match the initial selection state
472 this.applyToSelectedSequences = selectedSequences.getState()
473 || allSequences.getState();
474 this.applyToUnselectedSequences = unselectedSequences.getState()
475 || allSequences.getState();
477 return applyToOptions;
481 * Build a panel with radio button options to show or hide selected
486 protected JPanel buildShowHidePanel()
488 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
489 CheckboxGroup showOrHide = new CheckboxGroup();
492 * Radio button 'Show selected annotations' - initially unselected
494 String showLabel = MessageManager
495 .getString("label.show_selected_annotations");
496 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
497 showOption.addItemListener(new ItemListener()
500 public void itemStateChanged(ItemEvent evt)
502 if (evt.getStateChange() == ItemEvent.SELECTED)
504 AnnotationChooser.this.setShowSelected(true);
505 AnnotationChooser.this.changeShowHide_actionPerformed();
509 showHideOptions.add(showOption);
512 * Radio button 'hide selected annotations'- initially selected
514 String hideLabel = MessageManager
515 .getString("label.hide_selected_annotations");
516 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
517 hideOption.addItemListener(new ItemListener()
520 public void itemStateChanged(ItemEvent evt)
522 if (evt.getStateChange() == ItemEvent.SELECTED)
524 AnnotationChooser.this.setShowSelected(false);
525 AnnotationChooser.this.changeShowHide_actionPerformed();
529 showHideOptions.add(hideOption);
532 * Set member variable to match initial selection state
534 this.showSelected = showOption.getState();
536 return showHideOptions;
540 * Construct the panel with OK and Cancel buttons.
544 protected JPanel buildActionButtonsPanel()
546 JPanel jp = new JPanel();
547 final Font labelFont = JvSwingUtils.getLabelFont();
549 JButton ok = new JButton(MessageManager.getString("action.ok"));
550 ok.setFont(labelFont);
551 ok.addActionListener(new ActionListener()
554 public void actionPerformed(ActionEvent e)
556 close_actionPerformed();
561 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
562 cancel.setFont(labelFont);
563 cancel.addActionListener(new ActionListener()
566 public void actionPerformed(ActionEvent e)
568 cancel_actionPerformed();
577 * On 'Cancel' button, undo any changes.
579 protected void cancel_actionPerformed()
581 resetOriginalState();
583 close_actionPerformed();
587 * Restore annotation visibility to their state on entry here, and repaint
590 protected void resetOriginalState()
593 for (AlignmentAnnotation aa : this.ap.getAlignment()
594 .getAlignmentAnnotation())
596 aa.visible = this.resetState[i++];
601 * On 'Close' button, close the dialog.
603 protected void close_actionPerformed()
607 this.frame.setClosed(true);
608 } catch (Exception exe)
614 * Render a frame containing this panel.
616 private void showFrame()
618 frame = new JInternalFrame();
619 frame.setContentPane(this);
620 frame.setLayer(JLayeredPane.PALETTE_LAYER);
621 Desktop.addInternalFrame(frame,
622 MessageManager.getString("label.choose_annotations"),
623 MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
626 protected void setShowSelected(boolean showSelected)
628 this.showSelected = showSelected;
631 protected void setApplyToSelectedSequences(
632 boolean applyToSelectedSequences)
634 this.applyToSelectedSequences = applyToSelectedSequences;
637 protected void setApplyToUnselectedSequences(
638 boolean applyToUnselectedSequences)
640 this.applyToUnselectedSequences = applyToUnselectedSequences;
643 protected boolean isShowSelected()
648 protected boolean isApplyToSelectedSequences()
650 return applyToSelectedSequences;
653 protected boolean isApplyToUnselectedSequences()
655 return applyToUnselectedSequences;