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.JCheckBox;
25 import javax.swing.JInternalFrame;
26 import javax.swing.JLayeredPane;
27 import javax.swing.JPanel;
30 * A panel that allows the user to select which sequence-associated annotation
31 * rows to show or hide.
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 JCheckBox check = new JCheckBox(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 repaintAnnotations(false);
164 * Set visibility flags on annotation rows then repaint the alignment panel.
166 * Optionally, update all rows, including those not in the 'apply to' scope.
167 * This makes more sense when switching between selected and unselected
168 * sequences. When selecting annotation types, or show/hide, we only apply the
169 * settings to the selected sequences.
171 * Note this only affects sequence-specific annotations, others are left
174 protected void repaintAnnotations(boolean updateAllRows)
176 for (AlignmentAnnotation aa : this.ap.getAlignment()
177 .getAlignmentAnnotation())
179 if (aa.sequenceRef != null)
181 setAnnotationVisibility(aa, updateAllRows);
184 // copied from AnnotationLabel.actionPerformed (after show/hide row)...
185 // TODO should drive this functionality into AlignmentPanel
186 ap.updateAnnotation();
187 this.ap.annotationPanel.adjustPanelHeight();
188 this.ap.alabels.setSize(this.ap.alabels.getSize().width,
189 this.ap.annotationPanel.getSize().height);
191 this.ap.paintAlignment(true);
195 * Determine and set the visibility of the given annotation from the currently
198 * If its sequence is in the selected application scope
199 * (all/selected/unselected sequences), then we set its visibility.
201 * If the annotation type is one of those currently selected by checkbox, set
202 * its visibility to the selected value. If it is not currently selected, set
203 * it to the opposite value. So, unselecting an annotation type with 'hide'
204 * selected, will cause those annotations to be unhidden.
206 * If force update of all rows is wanted, then set rows not in the sequence
207 * selection scope to the opposite visibility to those in scope.
210 * @param updateAllRows
212 protected void setAnnotationVisibility(AlignmentAnnotation aa,
213 boolean updateAllRows)
215 boolean setToVisible = false;
216 if (this.selectedTypes.containsKey(aa.label))
218 setToVisible = this.showSelected;
222 setToVisible = !this.showSelected;
224 if (isInActionScope(aa))
226 aa.visible = setToVisible;
228 else if (updateAllRows)
230 aa.visible = !setToVisible;
232 // TODO force not visible if associated sequence is hidden?
233 // currently hiding a sequence does not hide its annotation rows
237 * Answers true if the annotation falls in the current selection criteria for
240 * It must be in the sequence selection group (for 'Apply to selection'), or
241 * not in it (for 'Apply except to selection'). No check needed for 'Apply to
247 protected boolean isInActionScope(AlignmentAnnotation aa)
249 boolean result = false;
250 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
252 // we don't care if the annotation's sequence is selected or not
255 else if (this.sg.getSequences().contains(aa.sequenceRef))
257 // annotation is for a member of the selection group
258 result = this.applyToSelectedSequences ? true : false;
262 // annotation is not associated with the selection group
263 result = this.applyToUnselectedSequences ? true : false;
269 * Get annotation 'types' for an alignment, optionally restricted to
270 * sequence-specific annotations only. The label is currently used for 'type'.
272 * TODO refactor to helper class. See
273 * AnnotationColourChooser.getAnnotationItems() for another client
276 * @param sequenceSpecific
279 public static List<String> getAnnotationTypes(AlignmentI alignment,
280 boolean sequenceSpecificOnly)
283 List<String> result = new ArrayList<String>();
284 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
286 if (!sequenceSpecificOnly || aa.sequenceRef != null)
288 String label = aa.label;
289 if (!result.contains(label))
299 * Construct the panel with options to:
301 * show or hide the selected annotation types
303 * do this for the current selection group or its inverse
307 protected JPanel buildShowHideOptionsPanel()
309 JPanel jp = new JPanel();
310 jp.setLayout(new BorderLayout());
312 JPanel showHideOptions = buildShowHidePanel();
313 jp.add(showHideOptions, BorderLayout.CENTER);
315 JPanel applyToOptions = buildApplyToOptionsPanel();
316 jp.add(applyToOptions, BorderLayout.SOUTH);
322 * Build a panel with radio buttons options for sequences to apply show/hide
323 * to. Options are all, current selection, all except current selection.
324 * Initial state has 'current selection' selected.
326 * If the sequence group is null, then we are acting on the whole alignment,
327 * and only 'all sequences' is enabled (and selected).
331 protected JPanel buildApplyToOptionsPanel()
333 final boolean wholeAlignment = this.sg == null;
334 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
335 CheckboxGroup actingOn = new CheckboxGroup();
337 String forAll = MessageManager.getString("label.all_sequences");
338 final Checkbox allSequences = new Checkbox(forAll, actingOn,
340 allSequences.addItemListener(new ItemListener()
343 public void itemStateChanged(ItemEvent evt)
345 if (evt.getStateChange() == ItemEvent.SELECTED) {
346 AnnotationChooser.this.setApplyToSelectedSequences(true);
347 AnnotationChooser.this.setApplyToUnselectedSequences(true);
348 AnnotationChooser.this.repaintAnnotations(true);
352 applyToOptions.add(allSequences);
354 String forSelected = MessageManager
355 .getString("label.selected_sequences");
356 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
358 selectedSequences.setEnabled(!wholeAlignment);
359 selectedSequences.addItemListener(new ItemListener()
362 public void itemStateChanged(ItemEvent evt)
364 if (evt.getStateChange() == ItemEvent.SELECTED)
366 AnnotationChooser.this.setApplyToSelectedSequences(true);
367 AnnotationChooser.this.setApplyToUnselectedSequences(false);
368 AnnotationChooser.this.repaintAnnotations(true);
372 applyToOptions.add(selectedSequences);
374 String exceptSelected = MessageManager
375 .getString("label.except_selected_sequences");
376 final Checkbox unselectedSequences = new Checkbox(exceptSelected, actingOn, false);
377 unselectedSequences.setEnabled(!wholeAlignment);
378 unselectedSequences.addItemListener(new ItemListener()
381 public void itemStateChanged(ItemEvent evt)
383 if (evt.getStateChange() == ItemEvent.SELECTED)
385 AnnotationChooser.this.setApplyToSelectedSequences(false);
386 AnnotationChooser.this.setApplyToUnselectedSequences(true);
387 AnnotationChooser.this.repaintAnnotations(true);
391 applyToOptions.add(unselectedSequences);
393 // set member variables to match the initial selection state
394 this.applyToSelectedSequences = selectedSequences.getState()
395 || allSequences.getState();
396 this.applyToUnselectedSequences = unselectedSequences.getState()
397 || allSequences.getState();
399 return applyToOptions;
403 * Build a panel with radio buttons options to show or hide selected
408 protected JPanel buildShowHidePanel()
410 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
411 CheckboxGroup showOrHide = new CheckboxGroup();
414 * Radio button 'Show selected annotations' - initially unselected
416 String showLabel = MessageManager
417 .getString("label.show_selected_annotations");
418 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
419 showOption.addItemListener(new ItemListener()
422 public void itemStateChanged(ItemEvent evt)
424 if (evt.getStateChange() == ItemEvent.SELECTED) {
425 AnnotationChooser.this.setShowSelected(true);
426 AnnotationChooser.this.repaintAnnotations(false);
430 showHideOptions.add(showOption);
433 * Radio button 'hide selected annotations'- initially selected
435 String hideLabel = MessageManager
436 .getString("label.hide_selected_annotations");
437 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
438 hideOption.addItemListener(new ItemListener()
441 public void itemStateChanged(ItemEvent evt)
443 if (evt.getStateChange() == ItemEvent.SELECTED)
445 AnnotationChooser.this.setShowSelected(false);
446 AnnotationChooser.this.repaintAnnotations(false);
450 showHideOptions.add(hideOption);
453 * Set member variable to match initial selection state
455 this.showSelected = showOption.getState();
457 return showHideOptions;
461 * Construct the panel with OK and Cancel buttons.
465 protected JPanel buildActionButtonsPanel()
467 JPanel jp = new JPanel();
468 final Font labelFont = JvSwingUtils.getLabelFont();
470 JButton ok = new JButton(MessageManager.getString("action.ok"));
471 ok.setFont(labelFont);
472 ok.addActionListener(new ActionListener()
475 public void actionPerformed(ActionEvent e)
477 close_actionPerformed();
482 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
483 cancel.setFont(labelFont);
484 cancel.addActionListener(new ActionListener()
487 public void actionPerformed(ActionEvent e)
489 cancel_actionPerformed();
498 * On 'Cancel' button, undo any changes.
500 protected void cancel_actionPerformed()
502 resetOriginalState();
504 close_actionPerformed();
508 * Restore annotation visibility to their state on entry here, and repaint
511 protected void resetOriginalState()
514 for (AlignmentAnnotation aa : this.ap.getAlignment()
515 .getAlignmentAnnotation())
517 aa.visible = this.resetState[i++];
522 * On 'Close' button, close the dialog.
524 protected void close_actionPerformed()
528 this.frame.setClosed(true);
529 } catch (Exception exe)
535 * Render a frame containing this panel.
537 private void showFrame()
539 frame = new JInternalFrame();
540 frame.setContentPane(this);
541 frame.setLayer(JLayeredPane.PALETTE_LAYER);
542 Desktop.addInternalFrame(frame,
543 MessageManager.getString("label.choose_annotations"),
544 MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
547 protected void setShowSelected(boolean showSelected)
549 this.showSelected = showSelected;
552 protected void setApplyToSelectedSequences(
553 boolean applyToSelectedSequences)
555 this.applyToSelectedSequences = applyToSelectedSequences;
558 protected void setApplyToUnselectedSequences(
559 boolean applyToUnselectedSequences)
561 this.applyToUnselectedSequences = applyToUnselectedSequences;