3 import jalview.datamodel.AlignmentAnnotation;
4 import jalview.datamodel.AlignmentI;
5 import jalview.datamodel.SequenceGroup;
6 import jalview.util.MessageManager;
8 import java.awt.BorderLayout;
9 import java.awt.Checkbox;
10 import java.awt.CheckboxGroup;
11 import java.awt.FlowLayout;
13 import java.awt.GridLayout;
14 import java.awt.event.ActionEvent;
15 import java.awt.event.ActionListener;
16 import java.awt.event.ItemEvent;
17 import java.awt.event.ItemListener;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
23 import javax.swing.JButton;
24 import javax.swing.JInternalFrame;
25 import javax.swing.JLayeredPane;
26 import javax.swing.JPanel;
29 * A panel that allows the user to select which sequence-associated annotation
30 * rows to show or hide.
35 @SuppressWarnings("serial")
36 public class AnnotationChooser extends JPanel
39 private static final Font CHECKBOX_FONT = new Font("Serif", Font.BOLD, 12);
41 private static final int MY_FRAME_WIDTH = 600;
43 private static final int MY_FRAME_HEIGHT = 250;
45 private JInternalFrame frame;
47 private AlignmentPanel ap;
49 private SequenceGroup sg;
51 // all annotation rows' original visible state
52 private boolean[] resetState = null;
54 // is 'Show' selected?
55 private boolean showSelected;
57 // apply settings to selected (or all) sequences?
58 private boolean applyToSelectedSequences;
60 // apply settings to unselected (or all) sequences?
61 private boolean applyToUnselectedSequences;
63 // currently selected 'annotation type' checkboxes
64 private Map<String, String> selectedTypes = new HashMap<String, String>();
71 public AnnotationChooser(AlignmentPanel alignPane)
75 this.sg = alignPane.av.getSelectionGroup();
76 saveResetState(alignPane.getAlignment());
81 } catch (Exception ex)
89 * Save the initial show/hide state of all annotations to allow a Cancel
94 protected void saveResetState(AlignmentI alignment)
96 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
97 final int count = annotations.length;
98 this.resetState = new boolean[count];
99 for (int i = 0; i < count; i++)
101 this.resetState[i] = annotations[i].visible;
106 * Populate this frame with:
108 * checkboxes for the types of annotation to show or hide (i.e. any annotation
109 * type shown for any sequence in the whole alignment)
111 * option to show or hide selected types
113 * option to show/hide for the currently selected group, or its inverse
115 * OK and Cancel (reset) buttons
117 protected void jbInit()
119 setLayout(new GridLayout(3, 1));
120 add(buildAnnotationTypesPanel());
121 add(buildShowHideOptionsPanel());
122 add(buildActionButtonsPanel());
127 * Construct the panel with checkboxes for annotation types.
131 protected JPanel buildAnnotationTypesPanel()
133 JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
135 List<String> annotationTypes = getAnnotationTypes(
136 this.ap.getAlignment(), true);
138 for (final String type : annotationTypes)
140 final Checkbox check = new Checkbox(type);
141 check.setFont(CHECKBOX_FONT);
142 check.addItemListener(new ItemListener()
145 public void itemStateChanged(ItemEvent evt)
147 if (evt.getStateChange() == ItemEvent.SELECTED)
149 AnnotationChooser.this.selectedTypes.put(type, type);
153 AnnotationChooser.this.selectedTypes.remove(type);
155 changeTypeSelected_actionPerformed(type);
164 * Update display when scope (All/Selected sequences/Unselected) is changed.
166 * Set annotations (with one of the selected types) to the selected Show/Hide
167 * visibility, if they are in the new application scope. Set to the opposite
168 * if outside the scope.
170 * Note this only affects sequence-specific annotations, others are left
173 protected void changeApplyTo_actionPerformed()
175 setAnnotationVisibility(true);
177 // copied from AnnotationLabel.actionPerformed (after show/hide row)...
178 // TODO should drive this functionality into AlignmentPanel
179 ap.updateAnnotation();
180 // this.ap.annotationPanel.adjustPanelHeight();
181 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
182 // this.ap.annotationPanel.getSize().height);
183 // this.ap.validate();
184 this.ap.paintAlignment(true);
188 * Update display when an annotation type is selected or deselected.
190 * If the type is selected, set visibility of annotations of that type which
191 * are in the application scope (all, selected or unselected sequences).
193 * If the type is unselected, set visibility to the opposite value. That is,
194 * treat select/deselect as a 'toggle' operation.
198 protected void changeTypeSelected_actionPerformed(String type)
200 boolean typeSelected = this.selectedTypes.containsKey(type);
201 for (AlignmentAnnotation aa : this.ap.getAlignment()
202 .getAlignmentAnnotation())
204 if (aa.sequenceRef != null && type.equals(aa.label)
205 && isInActionScope(aa))
207 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
210 ap.updateAnnotation();
211 // // this.ap.annotationPanel.adjustPanelHeight();
212 // this.ap.alabels.setSize(this.ap.alabels.getSize().width,
213 // this.ap.annotationPanel.getSize().height);
214 // this.ap.validate();
215 this.ap.paintAlignment(true);
219 * Update display on change of choice of Show or Hide
221 * For annotations of any selected type, set visibility of annotations of that
222 * type which are in the application scope (all, selected or unselected
227 protected void changeShowHide_actionPerformed()
229 setAnnotationVisibility(false);
231 this.ap.updateAnnotation();
232 // this.ap.annotationPanel.adjustPanelHeight();
233 this.ap.paintAlignment(true);
237 * Update visibility flags on annotation rows as per the current user choices.
239 * @param updateAllRows
241 protected void setAnnotationVisibility(boolean updateAllRows)
243 for (AlignmentAnnotation aa : this.ap.getAlignment()
244 .getAlignmentAnnotation())
246 if (aa.sequenceRef != null)
248 setAnnotationVisibility(aa, updateAllRows);
254 * Determine and set the visibility of the given annotation from the currently
257 * Only update annotations whose type is one of the selected types.
259 * If its sequence is in the selected application scope
260 * (all/selected/unselected sequences), then we set its visibility according
261 * to the current choice of Show or Hide.
263 * If force update of all rows is wanted, then set rows not in the sequence
264 * selection scope to the opposite visibility to those in scope.
267 * @param updateAllRows
269 protected void setAnnotationVisibility(AlignmentAnnotation aa,
270 boolean updateAllRows)
272 if (this.selectedTypes.containsKey(aa.label))
274 if (isInActionScope(aa))
276 aa.visible = this.showSelected;
278 else if (updateAllRows)
280 aa.visible = !this.showSelected;
283 // TODO force not visible if associated sequence is hidden?
284 // currently hiding a sequence does not hide its annotation rows
288 * Answers true if the annotation falls in the current selection criteria for
291 * It must be in the sequence selection group (for 'Apply to selection'), or
292 * not in it (for 'Apply except to selection'). No check needed for 'Apply to
298 protected boolean isInActionScope(AlignmentAnnotation aa)
300 boolean result = false;
301 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
303 // we don't care if the annotation's sequence is selected or not
306 else if (this.sg == null)
308 // shouldn't happen - defensive programming
311 else if (this.sg.getSequences().contains(aa.sequenceRef))
313 // annotation is for a member of the selection group
314 result = this.applyToSelectedSequences ? true : false;
318 // annotation is not associated with the selection group
319 result = this.applyToUnselectedSequences ? true : false;
325 * Get annotation 'types' for an alignment, optionally restricted to
326 * sequence-specific annotations only. The label is currently used for 'type'.
328 * TODO refactor to helper class. See
329 * AnnotationColourChooser.getAnnotationItems() for another client
332 * @param sequenceSpecific
335 public static List<String> getAnnotationTypes(AlignmentI alignment,
336 boolean sequenceSpecificOnly)
338 List<String> result = new ArrayList<String>();
339 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
341 if (!sequenceSpecificOnly || aa.sequenceRef != null)
343 String label = aa.label;
344 if (!result.contains(label))
354 * Construct the panel with options to:
356 * show or hide the selected annotation types
358 * do this for the current selection group or its inverse
362 protected JPanel buildShowHideOptionsPanel()
364 JPanel jp = new JPanel();
365 jp.setLayout(new BorderLayout());
367 JPanel showHideOptions = buildShowHidePanel();
368 jp.add(showHideOptions, BorderLayout.CENTER);
370 JPanel applyToOptions = buildApplyToOptionsPanel();
371 jp.add(applyToOptions, BorderLayout.SOUTH);
377 * Build a panel with radio buttons options for sequences to apply show/hide
378 * to. Options are all, current selection, all except current selection.
379 * Initial state has 'current selection' selected.
381 * If the sequence group is null, then we are acting on the whole alignment,
382 * and only 'all sequences' is enabled (and selected).
386 protected JPanel buildApplyToOptionsPanel()
388 final boolean wholeAlignment = this.sg == null;
389 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
390 CheckboxGroup actingOn = new CheckboxGroup();
392 String forAll = MessageManager.getString("label.all_sequences");
393 final Checkbox allSequences = new Checkbox(forAll, actingOn,
395 allSequences.addItemListener(new ItemListener()
398 public void itemStateChanged(ItemEvent evt)
400 if (evt.getStateChange() == ItemEvent.SELECTED) {
401 AnnotationChooser.this.setApplyToSelectedSequences(true);
402 AnnotationChooser.this.setApplyToUnselectedSequences(true);
403 AnnotationChooser.this.changeApplyTo_actionPerformed();
407 applyToOptions.add(allSequences);
409 String forSelected = MessageManager
410 .getString("label.selected_sequences");
411 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
413 selectedSequences.setEnabled(!wholeAlignment);
414 selectedSequences.addItemListener(new ItemListener()
417 public void itemStateChanged(ItemEvent evt)
419 if (evt.getStateChange() == ItemEvent.SELECTED)
421 AnnotationChooser.this.setApplyToSelectedSequences(true);
422 AnnotationChooser.this.setApplyToUnselectedSequences(false);
423 AnnotationChooser.this.changeApplyTo_actionPerformed();
427 applyToOptions.add(selectedSequences);
429 String exceptSelected = MessageManager
430 .getString("label.except_selected_sequences");
431 final Checkbox unselectedSequences = new Checkbox(exceptSelected, actingOn, false);
432 unselectedSequences.setEnabled(!wholeAlignment);
433 unselectedSequences.addItemListener(new ItemListener()
436 public void itemStateChanged(ItemEvent evt)
438 if (evt.getStateChange() == ItemEvent.SELECTED)
440 AnnotationChooser.this.setApplyToSelectedSequences(false);
441 AnnotationChooser.this.setApplyToUnselectedSequences(true);
442 AnnotationChooser.this.changeApplyTo_actionPerformed();
446 applyToOptions.add(unselectedSequences);
448 // set member variables to match the initial selection state
449 this.applyToSelectedSequences = selectedSequences.getState()
450 || allSequences.getState();
451 this.applyToUnselectedSequences = unselectedSequences.getState()
452 || allSequences.getState();
454 return applyToOptions;
458 * Build a panel with radio button options to show or hide selected
463 protected JPanel buildShowHidePanel()
465 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
466 CheckboxGroup showOrHide = new CheckboxGroup();
469 * Radio button 'Show selected annotations' - initially unselected
471 String showLabel = MessageManager
472 .getString("label.show_selected_annotations");
473 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
474 showOption.addItemListener(new ItemListener()
477 public void itemStateChanged(ItemEvent evt)
479 if (evt.getStateChange() == ItemEvent.SELECTED) {
480 AnnotationChooser.this.setShowSelected(true);
481 AnnotationChooser.this.changeShowHide_actionPerformed();
485 showHideOptions.add(showOption);
488 * Radio button 'hide selected annotations'- initially selected
490 String hideLabel = MessageManager
491 .getString("label.hide_selected_annotations");
492 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
493 hideOption.addItemListener(new ItemListener()
496 public void itemStateChanged(ItemEvent evt)
498 if (evt.getStateChange() == ItemEvent.SELECTED)
500 AnnotationChooser.this.setShowSelected(false);
501 AnnotationChooser.this.changeShowHide_actionPerformed();
505 showHideOptions.add(hideOption);
508 * Set member variable to match initial selection state
510 this.showSelected = showOption.getState();
512 return showHideOptions;
516 * Construct the panel with OK and Cancel buttons.
520 protected JPanel buildActionButtonsPanel()
522 JPanel jp = new JPanel();
523 final Font labelFont = JvSwingUtils.getLabelFont();
525 JButton ok = new JButton(MessageManager.getString("action.ok"));
526 ok.setFont(labelFont);
527 ok.addActionListener(new ActionListener()
530 public void actionPerformed(ActionEvent e)
532 close_actionPerformed();
537 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
538 cancel.setFont(labelFont);
539 cancel.addActionListener(new ActionListener()
542 public void actionPerformed(ActionEvent e)
544 cancel_actionPerformed();
553 * On 'Cancel' button, undo any changes.
555 protected void cancel_actionPerformed()
557 resetOriginalState();
559 close_actionPerformed();
563 * Restore annotation visibility to their state on entry here, and repaint
566 protected void resetOriginalState()
569 for (AlignmentAnnotation aa : this.ap.getAlignment()
570 .getAlignmentAnnotation())
572 aa.visible = this.resetState[i++];
577 * On 'Close' button, close the dialog.
579 protected void close_actionPerformed()
583 this.frame.setClosed(true);
584 } catch (Exception exe)
590 * Render a frame containing this panel.
592 private void showFrame()
594 frame = new JInternalFrame();
595 frame.setContentPane(this);
596 frame.setLayer(JLayeredPane.PALETTE_LAYER);
597 Desktop.addInternalFrame(frame,
598 MessageManager.getString("label.choose_annotations"),
599 MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
602 protected void setShowSelected(boolean showSelected)
604 this.showSelected = showSelected;
607 protected void setApplyToSelectedSequences(
608 boolean applyToSelectedSequences)
610 this.applyToSelectedSequences = applyToSelectedSequences;
613 protected void setApplyToUnselectedSequences(
614 boolean applyToUnselectedSequences)
616 this.applyToUnselectedSequences = applyToUnselectedSequences;
619 protected boolean isShowSelected()
624 protected boolean isApplyToSelectedSequences()
626 return applyToSelectedSequences;
629 protected boolean isApplyToUnselectedSequences()
631 return applyToUnselectedSequences;