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, 12);
61 private static final int MY_FRAME_WIDTH = 600;
63 private static final int MY_FRAME_HEIGHT = 250;
65 private JInternalFrame frame;
67 private AlignmentPanel ap;
69 private SequenceGroup sg;
71 // all annotation rows' original visible state
72 private boolean[] resetState = null;
74 // is 'Show' selected?
75 private boolean showSelected;
77 // apply settings to selected (or all) sequences?
78 private boolean applyToSelectedSequences;
80 // apply settings to unselected (or all) sequences?
81 private boolean applyToUnselectedSequences;
83 // currently selected 'annotation type' checkboxes
84 private Map<String, String> selectedTypes = new HashMap<String, String>();
91 public AnnotationChooser(AlignmentPanel alignPane)
95 this.sg = alignPane.av.getSelectionGroup();
96 saveResetState(alignPane.getAlignment());
101 } catch (Exception ex)
103 ex.printStackTrace();
109 * Save the initial show/hide state of all annotations to allow a Cancel
114 protected void saveResetState(AlignmentI alignment)
116 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
117 final int count = annotations.length;
118 this.resetState = new boolean[count];
119 for (int i = 0; i < count; i++)
121 this.resetState[i] = annotations[i].visible;
126 * Populate this frame with:
128 * checkboxes for the types of annotation to show or hide (i.e. any annotation
129 * type shown for any sequence in the whole alignment)
131 * option to show or hide selected types
133 * option to show/hide for the currently selected group, or its inverse
135 * OK and Cancel (reset) buttons
137 protected void jbInit()
139 setLayout(new GridLayout(3, 1));
140 add(buildAnnotationTypesPanel());
141 add(buildShowHideOptionsPanel());
142 add(buildActionButtonsPanel());
147 * Construct the panel with checkboxes for annotation types.
151 protected JPanel buildAnnotationTypesPanel()
153 JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
155 List<String> annotationTypes = getAnnotationTypes(
156 this.ap.getAlignment(), true);
158 for (final String type : annotationTypes)
160 final Checkbox check = new Checkbox(type);
161 check.setFont(CHECKBOX_FONT);
162 check.addItemListener(new ItemListener()
165 public void itemStateChanged(ItemEvent evt)
167 if (evt.getStateChange() == ItemEvent.SELECTED)
169 AnnotationChooser.this.selectedTypes.put(type, type);
173 AnnotationChooser.this.selectedTypes.remove(type);
175 changeTypeSelected_actionPerformed(type);
184 * Update display when scope (All/Selected sequences/Unselected) is changed.
186 * Set annotations (with one of the selected types) to the selected Show/Hide
187 * visibility, if they are in the new application scope. Set to the opposite
188 * if outside the scope.
190 * Note this only affects sequence-specific annotations, others are left
193 protected void changeApplyTo_actionPerformed()
195 setAnnotationVisibility(true);
197 // copied from AnnotationLabel.actionPerformed (after show/hide row)...
198 // TODO should drive this functionality into AlignmentPanel
199 ap.updateAnnotation();
200 // this.ap.annotationPanel.adjustPanelHeight();
201 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
202 // this.ap.annotationPanel.getSize().height);
203 // this.ap.validate();
204 this.ap.paintAlignment(true);
208 * Update display when an annotation type is selected or deselected.
210 * If the type is selected, set visibility of annotations of that type which
211 * are in the application scope (all, selected or unselected sequences).
213 * If the type is unselected, set visibility to the opposite value. That is,
214 * treat select/deselect as a 'toggle' operation.
218 protected void changeTypeSelected_actionPerformed(String type)
220 boolean typeSelected = this.selectedTypes.containsKey(type);
221 for (AlignmentAnnotation aa : this.ap.getAlignment()
222 .getAlignmentAnnotation())
224 if (aa.sequenceRef != null && type.equals(aa.label)
225 && isInActionScope(aa))
227 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
230 ap.updateAnnotation();
231 // // this.ap.annotationPanel.adjustPanelHeight();
232 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
233 // this.ap.annotationPanel.getSize().height);
234 // this.ap.validate();
235 this.ap.paintAlignment(true);
239 * Update display on change of choice of Show or Hide
241 * For annotations of any selected type, set visibility of annotations of that
242 * type which are in the application scope (all, selected or unselected
247 protected void changeShowHide_actionPerformed()
249 setAnnotationVisibility(false);
251 this.ap.updateAnnotation();
252 // this.ap.annotationPanel.adjustPanelHeight();
253 this.ap.paintAlignment(true);
257 * Update visibility flags on annotation rows as per the current user choices.
259 * @param updateAllRows
261 protected void setAnnotationVisibility(boolean updateAllRows)
263 for (AlignmentAnnotation aa : this.ap.getAlignment()
264 .getAlignmentAnnotation())
266 if (aa.sequenceRef != null)
268 setAnnotationVisibility(aa, updateAllRows);
274 * Determine and set the visibility of the given annotation from the currently
277 * Only update annotations whose type is one of the selected types.
279 * If its sequence is in the selected application scope
280 * (all/selected/unselected sequences), then we set its visibility according
281 * to the current choice of Show or Hide.
283 * If force update of all rows is wanted, then set rows not in the sequence
284 * selection scope to the opposite visibility to those in scope.
287 * @param updateAllRows
289 protected void setAnnotationVisibility(AlignmentAnnotation aa,
290 boolean updateAllRows)
292 if (this.selectedTypes.containsKey(aa.label))
294 if (isInActionScope(aa))
296 aa.visible = this.showSelected;
298 else if (updateAllRows)
300 aa.visible = !this.showSelected;
303 // TODO force not visible if associated sequence is hidden?
304 // currently hiding a sequence does not hide its annotation rows
308 * Answers true if the annotation falls in the current selection criteria for
311 * It must be in the sequence selection group (for 'Apply to selection'), or
312 * not in it (for 'Apply except to selection'). No check needed for 'Apply to
318 protected boolean isInActionScope(AlignmentAnnotation aa)
320 boolean result = false;
321 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
323 // we don't care if the annotation's sequence is selected or not
326 else if (this.sg == null)
328 // shouldn't happen - defensive programming
331 else if (this.sg.getSequences().contains(aa.sequenceRef))
333 // annotation is for a member of the selection group
334 result = this.applyToSelectedSequences ? true : false;
338 // annotation is not associated with the selection group
339 result = this.applyToUnselectedSequences ? true : false;
345 * Get annotation 'types' for an alignment, optionally restricted to
346 * sequence-specific annotations only. The label is currently used for 'type'.
348 * TODO refactor to helper class. See
349 * AnnotationColourChooser.getAnnotationItems() for another client
352 * @param sequenceSpecific
355 public static List<String> getAnnotationTypes(AlignmentI alignment,
356 boolean sequenceSpecificOnly)
358 List<String> result = new ArrayList<String>();
359 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
361 if (!sequenceSpecificOnly || aa.sequenceRef != null)
363 String label = aa.label;
364 if (!result.contains(label))
374 * Construct the panel with options to:
376 * show or hide the selected annotation types
378 * do this for the current selection group or its inverse
382 protected JPanel buildShowHideOptionsPanel()
384 JPanel jp = new JPanel();
385 jp.setLayout(new BorderLayout());
387 JPanel showHideOptions = buildShowHidePanel();
388 jp.add(showHideOptions, BorderLayout.CENTER);
390 JPanel applyToOptions = buildApplyToOptionsPanel();
391 jp.add(applyToOptions, BorderLayout.SOUTH);
397 * Build a panel with radio buttons options for sequences to apply show/hide
398 * to. Options are all, current selection, all except current selection.
399 * Initial state has 'current selection' selected.
401 * If the sequence group is null, then we are acting on the whole alignment,
402 * and only 'all sequences' is enabled (and selected).
406 protected JPanel buildApplyToOptionsPanel()
408 final boolean wholeAlignment = this.sg == null;
409 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
410 CheckboxGroup actingOn = new CheckboxGroup();
412 String forAll = MessageManager.getString("label.all_sequences");
413 final Checkbox allSequences = new Checkbox(forAll, actingOn,
415 allSequences.addItemListener(new ItemListener()
418 public void itemStateChanged(ItemEvent evt)
420 if (evt.getStateChange() == ItemEvent.SELECTED)
422 AnnotationChooser.this.setApplyToSelectedSequences(true);
423 AnnotationChooser.this.setApplyToUnselectedSequences(true);
424 AnnotationChooser.this.changeApplyTo_actionPerformed();
428 applyToOptions.add(allSequences);
430 String forSelected = MessageManager
431 .getString("label.selected_sequences");
432 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
434 selectedSequences.setEnabled(!wholeAlignment);
435 selectedSequences.addItemListener(new ItemListener()
438 public void itemStateChanged(ItemEvent evt)
440 if (evt.getStateChange() == ItemEvent.SELECTED)
442 AnnotationChooser.this.setApplyToSelectedSequences(true);
443 AnnotationChooser.this.setApplyToUnselectedSequences(false);
444 AnnotationChooser.this.changeApplyTo_actionPerformed();
448 applyToOptions.add(selectedSequences);
450 String exceptSelected = MessageManager
451 .getString("label.except_selected_sequences");
452 final Checkbox unselectedSequences = new Checkbox(exceptSelected,
454 unselectedSequences.setEnabled(!wholeAlignment);
455 unselectedSequences.addItemListener(new ItemListener()
458 public void itemStateChanged(ItemEvent evt)
460 if (evt.getStateChange() == ItemEvent.SELECTED)
462 AnnotationChooser.this.setApplyToSelectedSequences(false);
463 AnnotationChooser.this.setApplyToUnselectedSequences(true);
464 AnnotationChooser.this.changeApplyTo_actionPerformed();
468 applyToOptions.add(unselectedSequences);
470 // set member variables to match the initial selection state
471 this.applyToSelectedSequences = selectedSequences.getState()
472 || allSequences.getState();
473 this.applyToUnselectedSequences = unselectedSequences.getState()
474 || allSequences.getState();
476 return applyToOptions;
480 * Build a panel with radio button options to show or hide selected
485 protected JPanel buildShowHidePanel()
487 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
488 CheckboxGroup showOrHide = new CheckboxGroup();
491 * Radio button 'Show selected annotations' - initially unselected
493 String showLabel = MessageManager
494 .getString("label.show_selected_annotations");
495 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
496 showOption.addItemListener(new ItemListener()
499 public void itemStateChanged(ItemEvent evt)
501 if (evt.getStateChange() == ItemEvent.SELECTED)
503 AnnotationChooser.this.setShowSelected(true);
504 AnnotationChooser.this.changeShowHide_actionPerformed();
508 showHideOptions.add(showOption);
511 * Radio button 'hide selected annotations'- initially selected
513 String hideLabel = MessageManager
514 .getString("label.hide_selected_annotations");
515 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
516 hideOption.addItemListener(new ItemListener()
519 public void itemStateChanged(ItemEvent evt)
521 if (evt.getStateChange() == ItemEvent.SELECTED)
523 AnnotationChooser.this.setShowSelected(false);
524 AnnotationChooser.this.changeShowHide_actionPerformed();
528 showHideOptions.add(hideOption);
531 * Set member variable to match initial selection state
533 this.showSelected = showOption.getState();
535 return showHideOptions;
539 * Construct the panel with OK and Cancel buttons.
543 protected JPanel buildActionButtonsPanel()
545 JPanel jp = new JPanel();
546 final Font labelFont = JvSwingUtils.getLabelFont();
548 JButton ok = new JButton(MessageManager.getString("action.ok"));
549 ok.setFont(labelFont);
550 ok.addActionListener(new ActionListener()
553 public void actionPerformed(ActionEvent e)
555 close_actionPerformed();
560 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
561 cancel.setFont(labelFont);
562 cancel.addActionListener(new ActionListener()
565 public void actionPerformed(ActionEvent e)
567 cancel_actionPerformed();
576 * On 'Cancel' button, undo any changes.
578 protected void cancel_actionPerformed()
580 resetOriginalState();
582 close_actionPerformed();
586 * Restore annotation visibility to their state on entry here, and repaint
589 protected void resetOriginalState()
592 for (AlignmentAnnotation aa : this.ap.getAlignment()
593 .getAlignmentAnnotation())
595 aa.visible = this.resetState[i++];
600 * On 'Close' button, close the dialog.
602 protected void close_actionPerformed()
606 this.frame.setClosed(true);
607 } catch (Exception exe)
613 * Render a frame containing this panel.
615 private void showFrame()
617 frame = new JInternalFrame();
618 frame.setContentPane(this);
619 frame.setLayer(JLayeredPane.PALETTE_LAYER);
620 Desktop.addInternalFrame(frame,
621 MessageManager.getString("label.choose_annotations"),
622 MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
625 protected void setShowSelected(boolean showSelected)
627 this.showSelected = showSelected;
630 protected void setApplyToSelectedSequences(
631 boolean applyToSelectedSequences)
633 this.applyToSelectedSequences = applyToSelectedSequences;
636 protected void setApplyToUnselectedSequences(
637 boolean applyToUnselectedSequences)
639 this.applyToUnselectedSequences = applyToUnselectedSequences;
642 protected boolean isShowSelected()
647 protected boolean isApplyToSelectedSequences()
649 return applyToSelectedSequences;
652 protected boolean isApplyToUnselectedSequences()
654 return applyToUnselectedSequences;