Clover icon

Coverage Report

  1. Project Clover database Thu Nov 28 2024 11:45:30 GMT
  2. Package jalview.gui

File AnnotationChooser.java

 

Coverage histogram

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

Code metrics

40
152
33
1
645
392
59
0.39
4.61
33
1.79

Classes

Class Line # Actions
AnnotationChooser 56 152 59
0.906666790.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 java.awt.BorderLayout;
24    import java.awt.Checkbox;
25    import java.awt.CheckboxGroup;
26    import java.awt.FlowLayout;
27    import java.awt.Font;
28    import java.awt.GridLayout;
29    import java.awt.event.ActionEvent;
30    import java.awt.event.ActionListener;
31    import java.awt.event.ItemEvent;
32    import java.awt.event.ItemListener;
33    import java.util.ArrayList;
34    import java.util.HashMap;
35    import java.util.List;
36    import java.util.Map;
37   
38    import javax.swing.JButton;
39    import javax.swing.JInternalFrame;
40    import javax.swing.JLayeredPane;
41    import javax.swing.JPanel;
42   
43    import jalview.datamodel.AlignmentAnnotation;
44    import jalview.datamodel.AlignmentI;
45    import jalview.datamodel.SequenceGroup;
46    import jalview.util.MessageManager;
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  8 ap.updateAnnotation();
199    }
200   
201    /**
202    * Update display when an annotation type is selected or deselected.
203    * <p>
204    * If the type is selected, set visibility of annotations of that type which
205    * are in the application scope (all, selected or unselected sequences).
206    * <p>
207    * If the type is unselected, set visibility to the opposite value. That is,
208    * treat select/deselect as a 'toggle' operation.
209    *
210    * @param type
211    */
 
212  14 toggle protected void changeTypeSelected_actionPerformed(String type)
213    {
214  14 boolean typeSelected = this.selectedTypes.containsKey(type);
215  14 for (AlignmentAnnotation aa : this.ap.getAlignment()
216    .getAlignmentAnnotation())
217    {
218  126 if (aa.sequenceRef != null && type.equals(aa.label)
219    && isInActionScope(aa))
220    {
221  22 aa.visible = typeSelected ? this.showSelected : !this.showSelected;
222    }
223    }
224  14 ap.updateAnnotation();
225    }
226   
227    /**
228    * Update display on change of choice of Show or Hide
229    * <p>
230    * For annotations of any selected type, set visibility of annotations of that
231    * type which are in the application scope (all, selected or unselected
232    * sequences).
233    *
234    * @param dataSourceType
235    */
 
236  11 toggle protected void changeShowHide_actionPerformed()
237    {
238  11 setAnnotationVisibility(false);
239   
240  11 ap.updateAnnotation();
241    }
242   
243    /**
244    * Update visibility flags on annotation rows as per the current user choices.
245    *
246    * @param updateAllRows
247    */
 
248  19 toggle protected void setAnnotationVisibility(boolean updateAllRows)
249    {
250  19 for (AlignmentAnnotation aa : this.ap.getAlignment()
251    .getAlignmentAnnotation())
252    {
253  171 if (aa.sequenceRef != null)
254    {
255  76 setAnnotationVisibility(aa, updateAllRows);
256    }
257    }
258    }
259   
260    /**
261    * Determine and set the visibility of the given annotation from the currently
262    * selected options.
263    * <p>
264    * Only update annotations whose type is one of the selected types.
265    * <p>
266    * If its sequence is in the selected application scope
267    * (all/selected/unselected sequences), then we set its visibility according
268    * to the current choice of Show or Hide.
269    * <p>
270    * If force update of all rows is wanted, then set rows not in the sequence
271    * selection scope to the opposite visibility to those in scope.
272    *
273    * @param aa
274    * @param updateAllRows
275    */
 
276  76 toggle protected void setAnnotationVisibility(AlignmentAnnotation aa,
277    boolean updateAllRows)
278    {
279  76 if (this.selectedTypes.containsKey(aa.label))
280    {
281  4 if (isInActionScope(aa))
282    {
283  3 aa.visible = this.showSelected;
284    }
285  1 else if (updateAllRows)
286    {
287  0 aa.visible = !this.showSelected;
288    }
289    }
290    // TODO force not visible if associated sequence is hidden?
291    // currently hiding a sequence does not hide its annotation rows
292    }
293   
294    /**
295    * Answers true if the annotation falls in the current selection criteria for
296    * show/hide.
297    * <p>
298    * It must be in the sequence selection group (for 'Apply to selection'), or
299    * not in it (for 'Apply except to selection'). No check needed for 'Apply to
300    * all'.
301    *
302    * @param aa
303    * @return
304    */
 
305  42 toggle protected boolean isInActionScope(AlignmentAnnotation aa)
306    {
307  42 boolean result = false;
308  42 if (this.applyToSelectedSequences && this.applyToUnselectedSequences)
309    {
310    // we don't care if the annotation's sequence is selected or not
311  18 result = true;
312    }
313  24 else if (this.sg == null)
314    {
315    // shouldn't happen - defensive programming
316  0 result = true;
317    }
318  24 else if (this.sg.getSequences().contains(aa.sequenceRef))
319    {
320    // annotation is for a member of the selection group
321  11 result = this.applyToSelectedSequences ? true : false;
322    }
323    else
324    {
325    // annotation is not associated with the selection group
326  13 result = this.applyToUnselectedSequences ? true : false;
327    }
328  42 return result;
329    }
330   
331    /**
332    * Get annotation 'types' for an alignment, optionally restricted to
333    * sequence-specific annotations only. The label is currently used for 'type'.
334    *
335    * TODO refactor to helper class. See
336    * AnnotationColourChooser.getAnnotationItems() for another client
337    *
338    * @param alignment
339    * @param sequenceSpecific
340    * @return
341    */
 
342  19 toggle public static List<String> getAnnotationTypes(AlignmentI alignment,
343    boolean sequenceSpecificOnly)
344    {
345  19 List<String> result = new ArrayList<>();
346  19 for (AlignmentAnnotation aa : alignment.getAlignmentAnnotation())
347    {
348  171 if (!sequenceSpecificOnly || aa.sequenceRef != null)
349    {
350  81 String label = aa.label;
351  81 if (!result.contains(label))
352    {
353  43 result.add(label);
354    }
355    }
356    }
357  19 return result;
358    }
359   
360    /**
361    * Construct the panel with options to:
362    * <p>
363    * show or hide the selected annotation types
364    * <p>
365    * do this for the current selection group or its inverse
366    *
367    * @return
368    */
 
369  18 toggle protected JPanel buildShowHideOptionsPanel()
370    {
371  18 JPanel jp = new JPanel();
372  18 jp.setLayout(new BorderLayout());
373   
374  18 JPanel showHideOptions = buildShowHidePanel();
375  18 jp.add(showHideOptions, BorderLayout.CENTER);
376   
377  18 JPanel applyToOptions = buildApplyToOptionsPanel();
378  18 jp.add(applyToOptions, BorderLayout.SOUTH);
379   
380  18 return jp;
381    }
382   
383    /**
384    * Build a panel with radio buttons options for sequences to apply show/hide
385    * to. Options are all, current selection, all except current selection.
386    * Initial state has 'current selection' selected.
387    * <p>
388    * If the sequence group is null, then we are acting on the whole alignment,
389    * and only 'all sequences' is enabled (and selected).
390    *
391    * @return
392    */
 
393  20 toggle protected JPanel buildApplyToOptionsPanel()
394    {
395  20 final boolean wholeAlignment = this.sg == null;
396  20 JPanel applyToOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
397  20 CheckboxGroup actingOn = new CheckboxGroup();
398   
399  20 String forAll = MessageManager.getString("label.all_sequences");
400  20 final Checkbox allSequences = new Checkbox(forAll, actingOn,
401    wholeAlignment);
402  20 allSequences.addItemListener(new ItemListener()
403    {
 
404  5 toggle @Override
405    public void itemStateChanged(ItemEvent evt)
406    {
407  5 if (evt.getStateChange() == ItemEvent.SELECTED)
408    {
409  5 AnnotationChooser.this.setApplyToSelectedSequences(true);
410  5 AnnotationChooser.this.setApplyToUnselectedSequences(true);
411  5 AnnotationChooser.this.changeApplyTo_actionPerformed();
412    }
413    }
414    });
415  20 applyToOptions.add(allSequences);
416   
417  20 String forSelected = MessageManager
418    .getString("label.selected_sequences");
419  20 final Checkbox selectedSequences = new Checkbox(forSelected, actingOn,
420    !wholeAlignment);
421  20 selectedSequences.setEnabled(!wholeAlignment);
422  20 selectedSequences.addItemListener(new ItemListener()
423    {
 
424  2 toggle @Override
425    public void itemStateChanged(ItemEvent evt)
426    {
427  2 if (evt.getStateChange() == ItemEvent.SELECTED)
428    {
429  2 AnnotationChooser.this.setApplyToSelectedSequences(true);
430  2 AnnotationChooser.this.setApplyToUnselectedSequences(false);
431  2 AnnotationChooser.this.changeApplyTo_actionPerformed();
432    }
433    }
434    });
435  20 applyToOptions.add(selectedSequences);
436   
437  20 String exceptSelected = MessageManager
438    .getString("label.except_selected_sequences");
439  20 final Checkbox unselectedSequences = new Checkbox(exceptSelected,
440    actingOn, false);
441  20 unselectedSequences.setEnabled(!wholeAlignment);
442  20 unselectedSequences.addItemListener(new ItemListener()
443    {
 
444  1 toggle @Override
445    public void itemStateChanged(ItemEvent evt)
446    {
447  1 if (evt.getStateChange() == ItemEvent.SELECTED)
448    {
449  1 AnnotationChooser.this.setApplyToSelectedSequences(false);
450  1 AnnotationChooser.this.setApplyToUnselectedSequences(true);
451  1 AnnotationChooser.this.changeApplyTo_actionPerformed();
452    }
453    }
454    });
455  20 applyToOptions.add(unselectedSequences);
456   
457    // set member variables to match the initial selection state
458  20 this.applyToSelectedSequences = selectedSequences.getState()
459    || allSequences.getState();
460  20 this.applyToUnselectedSequences = unselectedSequences.getState()
461    || allSequences.getState();
462   
463  20 return applyToOptions;
464    }
465   
466    /**
467    * Build a panel with radio button options to show or hide selected
468    * annotations.
469    *
470    * @return
471    */
 
472  19 toggle protected JPanel buildShowHidePanel()
473    {
474  19 JPanel showHideOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
475  19 CheckboxGroup showOrHide = new CheckboxGroup();
476   
477    /*
478    * Radio button 'Show selected annotations' - initially unselected
479    */
480  19 String showLabel = MessageManager
481    .getString("label.show_selected_annotations");
482  19 final Checkbox showOption = new Checkbox(showLabel, showOrHide, false);
483  19 showOption.addItemListener(new ItemListener()
484    {
 
485  4 toggle @Override
486    public void itemStateChanged(ItemEvent evt)
487    {
488  4 if (evt.getStateChange() == ItemEvent.SELECTED)
489    {
490  4 AnnotationChooser.this.setShowSelected(true);
491  4 AnnotationChooser.this.changeShowHide_actionPerformed();
492    }
493    }
494    });
495  19 showHideOptions.add(showOption);
496   
497    /*
498    * Radio button 'hide selected annotations'- initially selected
499    */
500  19 String hideLabel = MessageManager
501    .getString("label.hide_selected_annotations");
502  19 final Checkbox hideOption = new Checkbox(hideLabel, showOrHide, true);
503  19 hideOption.addItemListener(new ItemListener()
504    {
 
505  7 toggle @Override
506    public void itemStateChanged(ItemEvent evt)
507    {
508  7 if (evt.getStateChange() == ItemEvent.SELECTED)
509    {
510  7 AnnotationChooser.this.setShowSelected(false);
511  7 AnnotationChooser.this.changeShowHide_actionPerformed();
512    }
513    }
514    });
515  19 showHideOptions.add(hideOption);
516   
517    /*
518    * Set member variable to match initial selection state
519    */
520  19 this.showSelected = showOption.getState();
521   
522  19 return showHideOptions;
523    }
524   
525    /**
526    * Construct the panel with OK and Cancel buttons.
527    *
528    * @return
529    */
 
530  18 toggle protected JPanel buildActionButtonsPanel()
531    {
532  18 JPanel jp = new JPanel();
533  18 final Font labelFont = JvSwingUtils.getLabelFont();
534   
535  18 JButton ok = new JButton(MessageManager.getString("action.ok"));
536  18 ok.setFont(labelFont);
537  18 ok.addActionListener(new ActionListener()
538    {
 
539  0 toggle @Override
540    public void actionPerformed(ActionEvent e)
541    {
542  0 close_actionPerformed();
543    }
544    });
545  18 jp.add(ok);
546   
547  18 JButton cancel = new JButton(MessageManager.getString("action.cancel"));
548  18 cancel.setFont(labelFont);
549  18 cancel.addActionListener(new ActionListener()
550    {
 
551  0 toggle @Override
552    public void actionPerformed(ActionEvent e)
553    {
554  0 cancel_actionPerformed();
555    }
556    });
557  18 jp.add(cancel);
558   
559  18 return jp;
560    }
561   
562    /**
563    * On 'Cancel' button, undo any changes.
564    */
 
565  0 toggle protected void cancel_actionPerformed()
566    {
567  0 resetOriginalState();
568  0 this.ap.repaint();
569  0 close_actionPerformed();
570    }
571   
572    /**
573    * Restore annotation visibility to their state on entry here, and repaint
574    * alignment.
575    */
 
576  1 toggle protected void resetOriginalState()
577    {
578  1 int i = 0;
579  1 for (AlignmentAnnotation aa : this.ap.getAlignment()
580    .getAlignmentAnnotation())
581    {
582  9 aa.visible = this.resetState[i++];
583    }
584    }
585   
586    /**
587    * On 'Close' button, close the dialog.
588    */
 
589  0 toggle protected void close_actionPerformed()
590    {
591  0 try
592    {
593  0 this.frame.setClosed(true);
594    } catch (Exception exe)
595    {
596    }
597    }
598   
599    /**
600    * Render a frame containing this panel.
601    */
 
602  17 toggle private void showFrame()
603    {
604  17 frame = new JInternalFrame();
605  17 frame.setFrameIcon(null);
606  17 frame.setContentPane(this);
607  17 frame.setLayer(JLayeredPane.PALETTE_LAYER);
608  17 Desktop.addInternalFrame(frame,
609    MessageManager.getString("label.choose_annotations"),
610    MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
611    }
612   
 
613  11 toggle protected void setShowSelected(boolean showSelected)
614    {
615  11 this.showSelected = showSelected;
616    }
617   
 
618  8 toggle protected void setApplyToSelectedSequences(
619    boolean applyToSelectedSequences)
620    {
621  8 this.applyToSelectedSequences = applyToSelectedSequences;
622    }
623   
 
624  8 toggle protected void setApplyToUnselectedSequences(
625    boolean applyToUnselectedSequences)
626    {
627  8 this.applyToUnselectedSequences = applyToUnselectedSequences;
628    }
629   
 
630  2 toggle protected boolean isShowSelected()
631    {
632  2 return showSelected;
633    }
634   
 
635  3 toggle protected boolean isApplyToSelectedSequences()
636    {
637  3 return applyToSelectedSequences;
638    }
639   
 
640  3 toggle protected boolean isApplyToUnselectedSequences()
641    {
642  3 return applyToUnselectedSequences;
643    }
644   
645    }