From bf484926c15117ef740812a9992698f88a710615 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 28 Aug 2018 16:01:52 +0100 Subject: [PATCH] JAL-3089 safer popup menu actions on AnnotationLabels --- src/jalview/gui/AnnotationLabels.java | 574 ++++++++++++++++++--------------- 1 file changed, 312 insertions(+), 262 deletions(-) diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 294b8db..aede9e0 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -70,7 +70,7 @@ import javax.swing.ToolTipManager; * height */ public class AnnotationLabels extends JPanel - implements MouseListener, MouseMotionListener, ActionListener + implements MouseListener, MouseMotionListener { /** * width in pixels within which height adjuster arrows are shown and active @@ -113,7 +113,7 @@ public class AnnotationLabels extends JPanel private final boolean debugRedraw = false; - private AlignmentPanel ap; + AlignmentPanel ap; AlignViewport av; @@ -198,78 +198,19 @@ public class AnnotationLabels extends JPanel } /** - * DOCUMENT ME! - * - * @param evt - * DOCUMENT ME! + * Set all annotations visible */ - @Override - public void actionPerformed(ActionEvent evt) + void showAll() { AlignmentAnnotation[] aa = ap.av.getAlignment() .getAlignmentAnnotation(); - - String action = evt.getActionCommand(); - if (ADDNEW.equals(action)) - { - /* - * non-returning dialog - */ - AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null, - null, new Annotation[ap.av.getAlignment().getWidth()]); - editLabelDescription(newAnnotation, true); - } - else if (EDITNAME.equals(action)) - { - /* - * non-returning dialog - */ - editLabelDescription(aa[selectedRow], false); - } - else if (HIDE.equals(action)) - { - aa[selectedRow].visible = false; - } - else if (DELETE.equals(action)) - { - ap.av.getAlignment().deleteAnnotation(aa[selectedRow]); - ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]); - } - else if (SHOWALL.equals(action)) - { - for (int i = 0; i < aa.length; i++) - { - if (!aa[i].visible && aa[i].annotations != null) - { - aa[i].visible = true; - } - } - } - else if (OUTPUT_TEXT.equals(action)) - { - new AnnotationExporter(ap).exportAnnotation(aa[selectedRow]); - } - else if (COPYCONS_SEQ.equals(action)) + for (int i = 0; i < aa.length; i++) { - SequenceI cons = null; - if (aa[selectedRow].groupRef != null) - { - cons = aa[selectedRow].groupRef.getConsensusSeq(); - } - else + if (!aa[i].visible && aa[i].annotations != null) { - cons = av.getConsensusSeq(); - } - if (cons != null) - { - copy_annotseqtoclipboard(cons); + aa[i].visible = true; } } - else if (TOGGLE_LABELSCALE.equals(action)) - { - aa[selectedRow].scaleColLabel = !aa[selectedRow].scaleColLabel; - } - ap.refresh(true); } @@ -321,7 +262,7 @@ public class AnnotationLabels extends JPanel oldY = evt.getY(); if (evt.isPopupTrigger()) { - showPopupMenu(evt); + showPopupMenu(evt, selectedRow); } } @@ -329,41 +270,93 @@ public class AnnotationLabels extends JPanel * Build and show the Pop-up menu at the right-click mouse position * * @param evt + * @param row */ - void showPopupMenu(MouseEvent evt) + void showPopupMenu(MouseEvent evt, final int row) { evt.consume(); final AlignmentAnnotation[] aa = ap.av.getAlignment() .getAlignmentAnnotation(); - JPopupMenu pop = new JPopupMenu( MessageManager.getString("label.annotations")); + + /* + * add new row + */ JMenuItem item = new JMenuItem(ADDNEW); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null, + null, new Annotation[ap.av.getAlignment().getWidth()]); + editLabelDescription(newAnnotation, true); + ap.refresh(true); + }}); pop.add(item); - if (selectedRow < 0) + + /* + * if no rows shown, add option to show all + * (if rows are hidden), nothing else to add + */ + if (row < 0) { if (hasHiddenRows) - { // let the user make everything visible again + { item = new JMenuItem(SHOWALL); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showAll(); + } + }); pop.add(item); } pop.show(this, evt.getX(), evt.getY()); return; } + + /* + * Edit Name/Description + */ item = new JMenuItem(EDITNAME); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + editLabelDescription(aa[row], false); + ap.refresh(false); + } + }); pop.add(item); + + /* + * hide row + */ item = new JMenuItem(HIDE); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + aa[row].visible = false; + ap.refresh(true); + } + }); pop.add(item); - // JAL-1264 hide all sequence-specific annotations of this type - if (selectedRow < aa.length) + + /* + * JAL-1264 hide all sequence-specific annotations of this type + */ + if (row < aa.length) { - if (aa[selectedRow].sequenceRef != null) + if (aa[row].sequenceRef != null) { - final String label = aa[selectedRow].label; + final String label = aa[row].label; JMenuItem hideType = new JMenuItem(); String text = MessageManager.getString("label.hide_all") + " " + label; @@ -376,221 +369,264 @@ public class AnnotationLabels extends JPanel AlignmentUtils.showOrHideSequenceAnnotations( ap.av.getAlignment(), Collections.singleton(label), null, false, false); - // for (AlignmentAnnotation ann : ap.av.getAlignment() - // .getAlignmentAnnotation()) - // { - // if (ann.sequenceRef != null && ann.label != null - // && ann.label.equals(label)) - // { - // ann.visible = false; - // } - // } ap.refresh(true); } }); pop.add(hideType); } } + + /* + * delete annotation + */ item = new JMenuItem(DELETE); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + ap.av.getAlignment().deleteAnnotation(aa[row]); + ap.av.getCalcManager().removeWorkerForAnnotation(aa[row]); + ap.refresh(true); + } + }); pop.add(item); + + /* + * show hidden rows + */ if (hasHiddenRows) { item = new JMenuItem(SHOWALL); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + showAll(); + } + }); pop.add(item); } + + /* + * export annotation + */ item = new JMenuItem(OUTPUT_TEXT); - item.addActionListener(this); + item.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + new AnnotationExporter(ap).exportAnnotation(aa[row]); + } + }); pop.add(item); + // TODO: annotation object should be typed for autocalculated/derived // property methods - if (selectedRow < aa.length) + if (row < aa.length) { - final String label = aa[selectedRow].label; - if (!aa[selectedRow].autoCalculated) + final String label = aa[row].label; + if (!aa[row].autoCalculated) { - if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH) + if (aa[row].graph == AlignmentAnnotation.NO_GRAPH) { // display formatting settings for this row. pop.addSeparator(); - // av and sequencegroup need to implement same interface for + + /* + * scale label to column + */ item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE, - aa[selectedRow].scaleColLabel); - item.addActionListener(this); - pop.add(item); - } - } - else if (label.indexOf("Consensus") > -1) - { - pop.addSeparator(); - // av and sequencegroup need to implement same interface for - final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem( - MessageManager.getString("label.ignore_gaps_consensus"), - (aa[selectedRow].groupRef != null) - ? aa[selectedRow].groupRef.getIgnoreGapsConsensus() - : ap.av.isIgnoreGapsConsensus()); - final AlignmentAnnotation aaa = aa[selectedRow]; - cbmi.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - if (aaa.groupRef != null) - { - // TODO: pass on reference to ap so the view can be updated. - aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState()); - ap.getAnnotationPanel() - .paint(ap.getAnnotationPanel().getGraphics()); - } - else - { - ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap); - } - ap.alignmentChanged(); - } - }); - pop.add(cbmi); - // av and sequencegroup need to implement same interface for - if (aaa.groupRef != null) - { - final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( - MessageManager.getString("label.show_group_histogram"), - aa[selectedRow].groupRef.isShowConsensusHistogram()); - chist.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - aaa.groupRef.setShowConsensusHistogram(chist.getState()); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); - } - }); - pop.add(chist); - final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem( - MessageManager.getString("label.show_group_logo"), - aa[selectedRow].groupRef.isShowSequenceLogo()); - cprofl.addActionListener(new ActionListener() + aa[row].scaleColLabel); + item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - aaa.groupRef.setshowSequenceLogo(cprofl.getState()); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); + aa[row].scaleColLabel = !aa[row].scaleColLabel; + ap.refresh(false); } }); - pop.add(cprofl); - final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem( - MessageManager.getString("label.normalise_group_logo"), - aa[selectedRow].groupRef.isNormaliseSequenceLogo()); - cproflnorm.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { + pop.add(item); + } + } + else if (label.indexOf("Consensus") > -1) + { + addConsensusMenuItems(pop, aa[row]); + } + } + pop.show(this, evt.getX(), evt.getY()); + } - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState()); - // automatically enable logo display if we're clicked - aaa.groupRef.setshowSequenceLogo(true); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); - } - }); - pop.add(cproflnorm); + /** + * Helper method to add Consensus-specific menu items, for alignment or group + * consensus annotation + * + * @param menu + * @param aa + */ + private void addConsensusMenuItems(JPopupMenu menu, + final AlignmentAnnotation aa) + { + menu.addSeparator(); + + /* + * ignore gaps in consensus + */ + final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem( + MessageManager.getString("label.ignore_gaps_consensus"), + (aa.groupRef != null) + ? aa.groupRef.getIgnoreGapsConsensus() + : ap.av.isIgnoreGapsConsensus()); + cbmi.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + if (aa.groupRef != null) + { + aa.groupRef.setIgnoreGapsConsensus(cbmi.getState()); + // AnnotationLabels.this.repaint(); + // ap.getAnnotationPanel() + // .paint(ap.getAnnotationPanel().getGraphics()); } else { - final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( - MessageManager.getString("label.show_histogram"), - av.isShowConsensusHistogram()); - chist.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - av.setShowConsensusHistogram(chist.getState()); - ap.alignFrame.setMenusForViewport(); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); - } - }); - pop.add(chist); - final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem( - MessageManager.getString("label.show_logo"), - av.isShowSequenceLogo()); - cprof.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - av.setShowSequenceLogo(cprof.getState()); - ap.alignFrame.setMenusForViewport(); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); - } - }); - pop.add(cprof); - final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem( - MessageManager.getString("label.normalise_logo"), - av.isNormaliseSequenceLogo()); - cprofnorm.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - // TODO: pass on reference - // to ap - // so the - // view - // can be - // updated. - av.setShowSequenceLogo(true); - av.setNormaliseSequenceLogo(cprofnorm.getState()); - ap.alignFrame.setMenusForViewport(); - ap.repaint(); - // ap.annotationPanel.paint(ap.annotationPanel.getGraphics()); - } - }); - pop.add(cprofnorm); + ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap); } - final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ); - consclipbrd.addActionListener(this); - pop.add(consclipbrd); + ap.alignmentChanged(); } + }); + menu.add(cbmi); + + if (aa.groupRef != null) + { + /* + * show group histogram + */ + final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( + MessageManager.getString("label.show_group_histogram"), + aa.groupRef.isShowConsensusHistogram()); + chist.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + aa.groupRef.setShowConsensusHistogram(chist.getState()); + ap.repaint(); + } + }); + menu.add(chist); + + /* + * show group logo + */ + final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem( + MessageManager.getString("label.show_group_logo"), + aa.groupRef.isShowSequenceLogo()); + cprofl.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + aa.groupRef.setshowSequenceLogo(cprofl.getState()); + ap.repaint(); + } + }); + menu.add(cprofl); + + /* + * normalise group logo + */ + final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem( + MessageManager.getString("label.normalise_group_logo"), + aa.groupRef.isNormaliseSequenceLogo()); + cproflnorm.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + aa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState()); + // automatically enable logo display if we're clicked + aa.groupRef.setshowSequenceLogo(true); + ap.repaint(); + } + }); + menu.add(cproflnorm); } - pop.show(this, evt.getX(), evt.getY()); + else + { + /* + * show histogram + */ + final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( + MessageManager.getString("label.show_histogram"), + av.isShowConsensusHistogram()); + chist.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + av.setShowConsensusHistogram(chist.getState()); + ap.alignFrame.setMenusForViewport(); + ap.repaint(); + } + }); + menu.add(chist); + + /* + * show logo + */ + final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem( + MessageManager.getString("label.show_logo"), + av.isShowSequenceLogo()); + cprof.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + av.setShowSequenceLogo(cprof.getState()); + ap.alignFrame.setMenusForViewport(); + ap.repaint(); + } + }); + menu.add(cprof); + + /* + * normalise logo + */ + final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem( + MessageManager.getString("label.normalise_logo"), + av.isNormaliseSequenceLogo()); + cprofnorm.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + av.setShowSequenceLogo(true); + av.setNormaliseSequenceLogo(cprofnorm.getState()); + ap.alignFrame.setMenusForViewport(); + ap.repaint(); + } + }); + menu.add(cprofnorm); + } + + /* + * copy consensus sequence + */ + final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ); + consclipbrd.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + copy_annotseqtoclipboard(aa); + } + }); + menu.add(consclipbrd); } /** @@ -603,7 +639,7 @@ public class AnnotationLabels extends JPanel { if (evt.isPopupTrigger()) { - showPopupMenu(evt); + showPopupMenu(evt, selectedRow); return; } @@ -915,11 +951,25 @@ public class AnnotationLabels extends JPanel /** * do a single sequence copy to jalview and the system clipboard * - * @param sq - * sequence to be copied to clipboard + * @param cons + * Consensus annotation */ - protected void copy_annotseqtoclipboard(SequenceI sq) + protected void copy_annotseqtoclipboard(AlignmentAnnotation cons) { + SequenceI sq = null; + if (cons.groupRef != null) + { + sq = cons.groupRef.getConsensusSeq(); + } + else + { + sq = av.getConsensusSeq(); + } + if (sq == null) + { + return; + } + SequenceI[] seqs = new SequenceI[] { sq }; String[] omitHidden = null; SequenceI[] dseqs = new SequenceI[] { sq.getDatasetSequence() }; -- 1.7.10.2