Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.gui

File AnnotationChooser.java

 

Coverage histogram

../../img/srcFileCovDistChart9.png
12% of files have more coverage

Code metrics

40
154
33
1
658
394
59
0.38
4.67
33
1.79

Classes

Class Line # Actions
AnnotationChooser 56 154 59 21
0.90748990.7%
 

Contributing tests

This file is covered by 17 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.gui;
22   
23    import jalview.datamodel.AlignmentAnnotation;
24    import jalview.datamodel.AlignmentI;
25    import jalview.datamodel.SequenceGroup;
26    import jalview.util.MessageManager;
27   
28    import java.awt.BorderLayout;
29    import java.awt.Checkbox;
30    import java.awt.CheckboxGroup;
31    import java.awt.FlowLayout;
32    import java.awt.Font;
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;
41    import java.util.Map;
42   
43    import javax.swing.JButton;
44    import javax.swing.JInternalFrame;
45    import javax.swing.JLayeredPane;
46    import javax.swing.JPanel;
47   
48    /**
49    * A panel that allows the user to select which sequence-associated annotation
50    * rows to show or hide.
51    *
52    * @author gmcarstairs
53    *
54    */
55    @SuppressWarnings("serial")
 
56    public class AnnotationChooser extends JPanel
57    {
58   
59    private static final Font CHECKBOX_FONT = new Font("Serif", Font.BOLD,
60    12);
61   
62    private static final int MY_FRAME_WIDTH = 600;
63   
64    private static final int MY_FRAME_HEIGHT = 250;
65   
66    private JInternalFrame frame;
67   
68    private AlignmentPanel ap;
69   
70    private SequenceGroup sg;
71   
72    // all annotation rows' original visible state
73    private boolean[] resetState = null;
74   
75    // is 'Show' selected?
76    private boolean showSelected;
77   
78    // apply settings to selected (or all) sequences?
79    private boolean applyToSelectedSequences;
80   
81    // apply settings to unselected (or all) sequences?
82    private boolean applyToUnselectedSequences;
83   
84    // currently selected 'annotation type' checkboxes
85    private Map<String, String> selectedTypes = new HashMap<>();
86   
87    /**
88    * Constructor.
89    *
90    * @param alignPane
91    */
 
92  17 toggle public AnnotationChooser(AlignmentPanel alignPane)
93    {
94  17 super();
95  17 this.ap = alignPane;
96  17 this.sg = alignPane.av.getSelectionGroup();
97  17 saveResetState(alignPane.getAlignment());
98   
99  17 try
100    {
101  17 jbInit();
102    } catch (Exception ex)
103    {
104  0 ex.printStackTrace();
105    }
106  17 showFrame();
107    }
108   
109    /**
110    * Save the initial show/hide state of all annotations to allow a Cancel
111    * operation.
112    *
113    * @param alignment
114    */
 
115  17 toggle protected void saveResetState(AlignmentI alignment)
116    {
117  17 AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation();
118  17 final int count = annotations.length;
119  17 this.resetState = new boolean[count];
120  170 for (int i = 0; i < count; i++)
121    {
122  153 this.resetState[i] = annotations[i].visible;
123    }
124    }
125   
126    /**
127    * Populate this frame with:
128    * <p>
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)
131    * <p>
132    * option to show or hide selected types
133    * <p>
134    * option to show/hide for the currently selected group, or its inverse
135    * <p>
136    * OK and Cancel (reset) buttons
137    */
 
138  17 toggle protected void jbInit()
139    {
140  17 setLayout(new GridLayout(3, 1));
141  17 add(buildAnnotationTypesPanel());
142  17 add(buildShowHideOptionsPanel());
143  17 add(buildActionButtonsPanel());
144  17 validate();
145    }
146   
147    /**
148    * Construct the panel with checkboxes for annotation types.
149    *
150    * @return
151    */
 
152  17 toggle protected JPanel buildAnnotationTypesPanel()
153    {
154  17 JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
155   
156  17 List<String> annotationTypes = getAnnotationTypes(
157    this.ap.getAlignment(), true);
158   
159  17 for (final String type : annotationTypes)
160    {
161  34 final Checkbox check = new Checkbox(type);
162  34 check.setFont(CHECKBOX_FONT);
163  34 check.addItemListener(new ItemListener()
164    {
 
165  14 toggle @Override
166    public void itemStateChanged(ItemEvent evt)
167    {
168  14 if (evt.getStateChange() == ItemEvent.SELECTED)
169    {
170  10 AnnotationChooser.this.selectedTypes.put(type, type);
171    }
172    else
173    {
174  4 AnnotationChooser.this.selectedTypes.remove(type);
175    }
176  14 changeTypeSelected_actionPerformed(type);
177    }
178    });
179  34 jp.add(check);
180    }
181  17 return jp;
182    }
183   
184    /**
185    * Update display when scope (All/Selected sequences/Unselected) is changed.
186    * <p>
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.
190    * <p>
191    * Note this only affects sequence-specific annotations, others are left
192    * unchanged.
193    */
 
194  8 toggle protected void changeApplyTo_actionPerformed()
195    {
196  8 setAnnotationVisibility(true);
197   
198    // copied from AnnotationLabel.actionPerformed (after show/hide row)...
199    // TODO should drive this functionality into AlignmentPanel
200  8 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  8 this.ap.paintAlignment(true, false);
206    }
207   
208    /**
209    * Update display when an annotation type is selected or deselected.
210    * <p>
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).
213    * <p>
214    * If the type is unselected, set visibility to the opposite value. That is,
215    * treat select/deselect as a 'toggle' operation.
216    *
217    * @param type
218    */
 
219  14 toggle protected void changeTypeSelected_actionPerformed(String type)
220    {
221  14 boolean typeSelected = this.selectedTypes.containsKey(type);
222  14 for (AlignmentAnnotation aa : this.ap.getAlignment()
223    .getAlignmentAnnotation())
224    {
225  126 if (aa.sequenceRef != null && type.equals(aa.label)
226    && isInActionScope(aa))
227    {
228  22 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
229    }
230    }
231  14 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  14 this.ap.paintAlignment(true, false);
237    }
238   
239    /**
240    * Update display on change of choice of Show or Hide
241    * <p>
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
244    * sequences).
245    *
246    * @param dataSourceType
247    */
 
248  11 toggle protected void changeShowHide_actionPerformed()
249    {
250  11 setAnnotationVisibility(false);
251   
252  11 this.ap.updateAnnotation();
253    // this.ap.annotationPanel.adjustPanelHeight();
254  11 this.ap.paintAlignment(true, false);
255    }
256   
257    /**
258    * Update visibility flags on annotation rows as per the current user choices.
259    *
260    * @param updateAllRows
261    */
 
262  19 toggle protected void setAnnotationVisibility(boolean updateAllRows)
263    {
264  19 for (AlignmentAnnotation aa : this.ap.getAlignment()
265    .getAlignmentAnnotation())
266    {
267  171 if (aa.sequenceRef != null)
268    {
269  76 setAnnotationVisibility(aa, updateAllRows);
270    }
271    }
272    }
273   
274    /**
275    * Determine and set the visibility of the given annotation from the currently
276    * selected options.
277    * <p>
278    * Only update annotations whose type is one of the selected types.
279    * <p>
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.
283    * <p>
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.
286    *
287    * @param aa
288    * @param updateAllRows
289    */
 
290  76 toggle protected void setAnnotationVisibility(AlignmentAnnotation aa,
291    boolean updateAllRows)
292    {
293  76 if (this.selectedTypes.containsKey(aa.label))
294    {
295  4 if (isInActionScope(aa))
296    {
297  3 aa.visible = this.showSelected;
298    }
299  1 else if (updateAllRows)
300    {
301  0 aa.visible = !this.showSelected;
302    }
303    }
304    // TODO force not visible if associated sequence is hidden?
305    // currently hiding a sequence does not hide its annotation rows
306    }
307   
308    /**
309    * Answers true if the annotation falls in the current selection criteria for
310    * show/hide.
311    * <p>
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
314    * all'.
315    *
316    * @param aa
317    * @return
318    */
 
319  42 toggle protected boolean isInActionScope(AlignmentAnnotation aa)
320    {
321  42 boolean result = false;
322  42 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
323    {
324    // we don't care if the annotation's sequence is selected or not
325  18 result = true;
326    }
327  24 else if (this.sg == null)
328    {
329    // shouldn't happen - defensive programming
330  0 result = true;
331    }
332  24 else if (this.sg.getSequences().contains(aa.sequenceRef))
333    {
334    // annotation is for a member of the selection group
335  11 result = this.applyToSelectedSequences ? true : false;
336    }
337    else
338    {
339    // annotation is not associated with the selection group
340  13 result = this.applyToUnselectedSequences ? true : false;
341    }
342  42 return result;
343    }
344   
345    /**
346    * Get annotation 'types' for an alignment, optionally restricted to
347    * sequence-specific annotations only. The label is currently used for 'type'.
348    *
349    * TODO refactor to helper class. See
350    * AnnotationColourChooser.getAnnotationItems() for another client
351    *
352    * @param alignment
353    * @param sequenceSpecific
354    * @return
355    */
 
356  19 toggle public static List<String> getAnnotationTypes(AlignmentI alignment,
357    boolean sequenceSpecificOnly)
358    {
359  19 List<String> result = new ArrayList<>();
360  19 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
361    {
362  171 if (!sequenceSpecificOnly || aa.sequenceRef != null)
363    {
364  81 String label = aa.label;
365  81 if (!result.contains(label))
366    {
367  43 result.add(label);
368    }
369    }
370    }
371  19 return result;
372    }
373   
374    /**
375    * Construct the panel with options to:
376    * <p>
377    * show or hide the selected annotation types
378    * <p>
379    * do this for the current selection group or its inverse
380    *
381    * @return
382    */
 
383  18 toggle protected JPanel buildShowHideOptionsPanel()
384    {
385  18 JPanel jp = new JPanel();
386  18 jp.setLayout(new BorderLayout());
387   
388  18 JPanel showHideOptions = buildShowHidePanel();
389  18 jp.add(showHideOptions, BorderLayout.CENTER);
390   
391  18 JPanel applyToOptions = buildApplyToOptionsPanel();
392  18 jp.add(applyToOptions, BorderLayout.SOUTH);
393   
394  18 return jp;
395    }
396   
397    /**
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.
401    * <p>
402    * If the sequence group is null, then we are acting on the whole alignment,
403    * and only 'all sequences' is enabled (and selected).
404    *
405    * @return
406    */
 
407  20 toggle protected JPanel buildApplyToOptionsPanel()
408    {
409  20 final boolean wholeAlignment = this.sg == null;
410  20 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
411  20 CheckboxGroup actingOn = new CheckboxGroup();
412   
413  20 String forAll = MessageManager.getString("label.all_sequences");
414  20 final Checkbox allSequences = new Checkbox(forAll, actingOn,
415    wholeAlignment);
416  20 allSequences.addItemListener(new ItemListener()
417    {
 
418  5 toggle @Override
419    public void itemStateChanged(ItemEvent evt)
420    {
421  5 if (evt.getStateChange() == ItemEvent.SELECTED)
422    {
423  5 AnnotationChooser.this.setApplyToSelectedSequences(true);
424  5 AnnotationChooser.this.setApplyToUnselectedSequences(true);
425  5 AnnotationChooser.this.changeApplyTo_actionPerformed();
426    }
427    }
428    });
429  20 applyToOptions.add(allSequences);
430   
431  20 String forSelected = MessageManager
432    .getString("label.selected_sequences");
433  20 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
434    !wholeAlignment);
435  20 selectedSequences.setEnabled(!wholeAlignment);
436  20 selectedSequences.addItemListener(new ItemListener()
437    {
 
438  2 toggle @Override
439    public void itemStateChanged(ItemEvent evt)
440    {
441  2 if (evt.getStateChange() == ItemEvent.SELECTED)
442    {
443  2 AnnotationChooser.this.setApplyToSelectedSequences(true);
444  2 AnnotationChooser.this.setApplyToUnselectedSequences(false);
445  2 AnnotationChooser.this.changeApplyTo_actionPerformed();
446    }
447    }
448    });
449  20 applyToOptions.add(selectedSequences);
450   
451  20 String exceptSelected = MessageManager
452    .getString("label.except_selected_sequences");
453  20 final Checkbox unselectedSequences = new Checkbox(exceptSelected,
454    actingOn, false);
455  20 unselectedSequences.setEnabled(!wholeAlignment);
456  20 unselectedSequences.addItemListener(new ItemListener()
457    {
 
458  1 toggle @Override
459    public void itemStateChanged(ItemEvent evt)
460    {
461  1 if (evt.getStateChange() == ItemEvent.SELECTED)
462    {
463  1 AnnotationChooser.this.setApplyToSelectedSequences(false);
464  1 AnnotationChooser.this.setApplyToUnselectedSequences(true);
465  1 AnnotationChooser.this.changeApplyTo_actionPerformed();
466    }
467    }
468    });
469  20 applyToOptions.add(unselectedSequences);
470   
471    // set member variables to match the initial selection state
472  20 this.applyToSelectedSequences = selectedSequences.getState()
473    || allSequences.getState();
474  20 this.applyToUnselectedSequences = unselectedSequences.getState()
475    || allSequences.getState();
476   
477  20 return applyToOptions;
478    }
479   
480    /**
481    * Build a panel with radio button options to show or hide selected
482    * annotations.
483    *
484    * @return
485    */
 
486  19 toggle protected JPanel buildShowHidePanel()
487    {
488  19 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
489  19 CheckboxGroup showOrHide = new CheckboxGroup();
490   
491    /*
492    * Radio button 'Show selected annotations' - initially unselected
493    */
494  19 String showLabel = MessageManager
495    .getString("label.show_selected_annotations");
496  19 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
497  19 showOption.addItemListener(new ItemListener()
498    {
 
499  4 toggle @Override
500    public void itemStateChanged(ItemEvent evt)
501    {
502  4 if (evt.getStateChange() == ItemEvent.SELECTED)
503    {
504  4 AnnotationChooser.this.setShowSelected(true);
505  4 AnnotationChooser.this.changeShowHide_actionPerformed();
506    }
507    }
508    });
509  19 showHideOptions.add(showOption);
510   
511    /*
512    * Radio button 'hide selected annotations'- initially selected
513    */
514  19 String hideLabel = MessageManager
515    .getString("label.hide_selected_annotations");
516  19 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
517  19 hideOption.addItemListener(new ItemListener()
518    {
 
519  7 toggle @Override
520    public void itemStateChanged(ItemEvent evt)
521    {
522  7 if (evt.getStateChange() == ItemEvent.SELECTED)
523    {
524  7 AnnotationChooser.this.setShowSelected(false);
525  7 AnnotationChooser.this.changeShowHide_actionPerformed();
526    }
527    }
528    });
529  19 showHideOptions.add(hideOption);
530   
531    /*
532    * Set member variable to match initial selection state
533    */
534  19 this.showSelected = showOption.getState();
535   
536  19 return showHideOptions;
537    }
538   
539    /**
540    * Construct the panel with OK and Cancel buttons.
541    *
542    * @return
543    */
 
544  18 toggle protected JPanel buildActionButtonsPanel()
545    {
546  18 JPanel jp = new JPanel();
547  18 final Font labelFont = JvSwingUtils.getLabelFont();
548   
549  18 JButton ok = new JButton(MessageManager.getString("action.ok"));
550  18 ok.setFont(labelFont);
551  18 ok.addActionListener(new ActionListener()
552    {
 
553  0 toggle @Override
554    public void actionPerformed(ActionEvent e)
555    {
556  0 close_actionPerformed();
557    }
558    });
559  18 jp.add(ok);
560   
561  18 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
562  18 cancel.setFont(labelFont);
563  18 cancel.addActionListener(new ActionListener()
564    {
 
565  0 toggle @Override
566    public void actionPerformed(ActionEvent e)
567    {
568  0 cancel_actionPerformed();
569    }
570    });
571  18 jp.add(cancel);
572   
573  18 return jp;
574    }
575   
576    /**
577    * On 'Cancel' button, undo any changes.
578    */
 
579  0 toggle protected void cancel_actionPerformed()
580    {
581  0 resetOriginalState();
582  0 this.ap.repaint();
583  0 close_actionPerformed();
584    }
585   
586    /**
587    * Restore annotation visibility to their state on entry here, and repaint
588    * alignment.
589    */
 
590  1 toggle protected void resetOriginalState()
591    {
592  1 int i = 0;
593  1 for (AlignmentAnnotation aa : this.ap.getAlignment()
594    .getAlignmentAnnotation())
595    {
596  9 aa.visible = this.resetState[i++];
597    }
598    }
599   
600    /**
601    * On 'Close' button, close the dialog.
602    */
 
603  0 toggle protected void close_actionPerformed()
604    {
605  0 try
606    {
607  0 this.frame.setClosed(true);
608    } catch (Exception exe)
609    {
610    }
611    }
612   
613    /**
614    * Render a frame containing this panel.
615    */
 
616  17 toggle private void showFrame()
617    {
618  17 frame = new JInternalFrame();
619  17 frame.setContentPane(this);
620  17 frame.setLayer(JLayeredPane.PALETTE_LAYER);
621  17 Desktop.addInternalFrame(frame,
622    MessageManager.getString("label.choose_annotations"),
623    MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
624    }
625   
 
626  11 toggle protected void setShowSelected(boolean showSelected)
627    {
628  11 this.showSelected = showSelected;
629    }
630   
 
631  8 toggle protected void setApplyToSelectedSequences(
632    boolean applyToSelectedSequences)
633    {
634  8 this.applyToSelectedSequences = applyToSelectedSequences;
635    }
636   
 
637  8 toggle protected void setApplyToUnselectedSequences(
638    boolean applyToUnselectedSequences)
639    {
640  8 this.applyToUnselectedSequences = applyToUnselectedSequences;
641    }
642   
 
643  2 toggle protected boolean isShowSelected()
644    {
645  2 return showSelected;
646    }
647   
 
648  3 toggle protected boolean isApplyToSelectedSequences()
649    {
650  3 return applyToSelectedSequences;
651    }
652   
 
653  3 toggle protected boolean isApplyToUnselectedSequences()
654    {
655  3 return applyToUnselectedSequences;
656    }
657   
658    }