X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FAnnotationLabels.java;h=673f04dd2ca672bd6a21e1a05011318969315b03;hb=0751c58086542f9e0466201b624f84d1efd547bb;hp=2e70ca1bff37b4be255d41afc7b4ef49334e3547;hpb=51d3f6808e74f158d2ba4807ae4dbf6001b94e2b;p=jalview.git diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 2e70ca1..673f04d 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -20,22 +20,6 @@ */ package jalview.gui; -import jalview.analysis.AlignSeq; -import jalview.analysis.AlignmentUtils; -import jalview.datamodel.Alignment; -import jalview.datamodel.AlignmentAnnotation; -import jalview.datamodel.Annotation; -import jalview.datamodel.HiddenColumns; -import jalview.datamodel.Sequence; -import jalview.datamodel.SequenceGroup; -import jalview.datamodel.SequenceI; -import jalview.io.FileFormat; -import jalview.io.FormatAdapter; -import jalview.util.Comparison; -import jalview.util.MessageManager; -import jalview.util.Platform; -import jalview.workers.InformationThread; - import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; @@ -55,7 +39,6 @@ import java.awt.geom.AffineTransform; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; -import java.util.regex.Pattern; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenuItem; @@ -64,6 +47,22 @@ import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; +import jalview.analysis.AlignSeq; +import jalview.analysis.AlignmentUtils; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.Annotation; +import jalview.datamodel.HiddenColumns; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; +import jalview.io.FileFormat; +import jalview.io.FormatAdapter; +import jalview.util.Comparison; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.workers.InformationThread; + /** * The panel that holds the labels for alignment annotations, providing * tooltips, context menus, drag to reorder rows, and drag to adjust panel @@ -72,6 +71,10 @@ import javax.swing.ToolTipManager; public class AnnotationLabels extends JPanel implements MouseListener, MouseMotionListener, ActionListener { + private static final String HTML_END_TAG = ""; + + private static final String HTML_START_TAG = ""; + /** * width in pixels within which height adjuster arrows are shown and active */ @@ -82,9 +85,6 @@ public class AnnotationLabels extends JPanel */ private static int HEIGHT_ADJUSTER_HEIGHT = 10; - private static final Pattern LEFT_ANGLE_BRACKET_PATTERN = Pattern - .compile("<"); - private static final Font font = new Font("Arial", Font.PLAIN, 11); private static final String TOGGLE_LABELSCALE = MessageManager @@ -136,6 +136,7 @@ public class AnnotationLabels extends JPanel */ public AnnotationLabels(AlignmentPanel ap) { + this.ap = ap; av = ap.av; ToolTipManager.sharedInstance().registerComponent(this); @@ -209,41 +210,33 @@ public class AnnotationLabels extends JPanel AlignmentAnnotation[] aa = ap.av.getAlignment() .getAlignmentAnnotation(); - boolean fullRepaint = false; - if (evt.getActionCommand().equals(ADDNEW)) + String action = evt.getActionCommand(); + if (ADDNEW.equals(action)) { + /* + * non-returning dialog + */ AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null, null, new Annotation[ap.av.getAlignment().getWidth()]); - - if (!editLabelDescription(newAnnotation)) - { - return; - } - - ap.av.getAlignment().addAnnotation(newAnnotation); - ap.av.getAlignment().setAnnotationIndex(newAnnotation, 0); - fullRepaint = true; + editLabelDescription(newAnnotation, true); } - else if (evt.getActionCommand().equals(EDITNAME)) + else if (EDITNAME.equals(action)) { - String name = aa[selectedRow].label; - editLabelDescription(aa[selectedRow]); - if (!name.equalsIgnoreCase(aa[selectedRow].label)) - { - fullRepaint = true; - } + /* + * non-returning dialog + */ + editLabelDescription(aa[selectedRow], false); } - else if (evt.getActionCommand().equals(HIDE)) + else if (HIDE.equals(action)) { aa[selectedRow].visible = false; } - else if (evt.getActionCommand().equals(DELETE)) + else if (DELETE.equals(action)) { ap.av.getAlignment().deleteAnnotation(aa[selectedRow]); ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]); - fullRepaint = true; } - else if (evt.getActionCommand().equals(SHOWALL)) + else if (SHOWALL.equals(action)) { for (int i = 0; i < aa.length; i++) { @@ -252,13 +245,12 @@ public class AnnotationLabels extends JPanel aa[i].visible = true; } } - fullRepaint = true; } - else if (evt.getActionCommand().equals(OUTPUT_TEXT)) + else if (OUTPUT_TEXT.equals(action)) { new AnnotationExporter(ap).exportAnnotation(aa[selectedRow]); } - else if (evt.getActionCommand().equals(COPYCONS_SEQ)) + else if (COPYCONS_SEQ.equals(action)) { SequenceI cons = null; if (aa[selectedRow].groupRef != null) @@ -273,46 +265,54 @@ public class AnnotationLabels extends JPanel { copy_annotseqtoclipboard(cons); } - } - else if (evt.getActionCommand().equals(TOGGLE_LABELSCALE)) + else if (TOGGLE_LABELSCALE.equals(action)) { aa[selectedRow].scaleColLabel = !aa[selectedRow].scaleColLabel; } - ap.refresh(fullRepaint); - + ap.refresh(true); } /** - * DOCUMENT ME! + * Shows a dialog where the annotation name and description may be edited. If + * parameter addNew is true, then on confirmation, a new AlignmentAnnotation + * is added, else an existing annotation is updated. * - * @param e - * DOCUMENT ME! + * @param annotation + * @param addNew */ - boolean editLabelDescription(AlignmentAnnotation annotation) + void editLabelDescription(AlignmentAnnotation annotation, boolean addNew) { - // TODO i18n + String name = MessageManager.getString("label.annotation_name"); + String description = MessageManager + .getString("label.annotation_description"); + String title = MessageManager + .getString("label.edit_annotation_name_description"); EditNameDialog dialog = new EditNameDialog(annotation.label, - annotation.description, " Annotation Name ", - "Annotation Description ", "Edit Annotation Name/Description", - ap.alignFrame); - - if (!dialog.accept) - { - return false; - } - - annotation.label = dialog.getName(); + annotation.description, name, description); - String text = dialog.getDescription(); - if (text != null && text.length() == 0) - { - text = null; - } - annotation.description = text; - - return true; + dialog.showDialog(ap.alignFrame, title, + new Runnable() + { + @Override + public void run() + { + annotation.label = dialog.getName(); + String text = dialog.getDescription(); + if (text != null && text.length() == 0) + { + text = null; + } + annotation.description = text; + if (addNew) + { + ap.av.getAlignment().addAnnotation(annotation); + ap.av.getAlignment().setAnnotationIndex(annotation, 0); + } + ap.refresh(true); + } + }); } @Override @@ -366,9 +366,9 @@ public class AnnotationLabels extends JPanel // JAL-1264 hide all sequence-specific annotations of this type if (selectedRow < aa.length) { - if (isSequenceAnnotation) + if (aa[selectedRow].sequenceRef != null) { - final String label = ann.label; + final String label = aa[selectedRow].label; JMenuItem hideType = new JMenuItem(); String text = MessageManager.getString("label.hide_all") + " " + label; @@ -403,24 +403,28 @@ public class AnnotationLabels extends JPanel // property methods if (selectedRow < aa.length) { - final String label = ann.label; - if (!(ann.autoCalculated) + final String label = aa[selectedRow].label; + if (!(aa[selectedRow].autoCalculated) && !(InformationThread.HMM_CALC_ID.equals(ann.getCalcId()))) { - if (ann.graph == AlignmentAnnotation.NO_GRAPH) + if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH) { // display formatting settings for this row. pop.addSeparator(); // av and sequencegroup need to implement same interface for item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE, - ann.scaleColLabel); + aa[selectedRow].scaleColLabel); item.addActionListener(this); pop.add(item); } } else if (label.indexOf("Consensus") > -1) { - addConsensusMenu(pop, ann); + addConsensusMenuOptions(ap, aa[selectedRow], pop); + + final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ); + consclipbrd.addActionListener(this); + pop.add(consclipbrd); } else if (InformationThread.HMM_CALC_ID.equals(ann.getCalcId())) { @@ -457,13 +461,15 @@ public class AnnotationLabels extends JPanel if (!ann.groupRef.isUseInfoLetterHeight()) { ann.groupRef.setIgnoreBelowBackground(cbmi.getState()); + // todo and recompute group annotation } } else if (!ap.av.isInfoLetterHeight()) { ap.av.setIgnoreBelowBackground(cbmi.getState(), ap); + // todo and recompute annotation } - ap.alignmentChanged(); + ap.alignmentChanged(); // todo not like this } }); pop.add(cbmi); @@ -480,11 +486,13 @@ public class AnnotationLabels extends JPanel { ann.groupRef.setInfoLetterHeight((letterHeight.getState())); ann.groupRef.setIgnoreBelowBackground(true); + // todo and recompute group annotation } else { ap.av.setInfoLetterHeight(letterHeight.getState(), ap); ap.av.setIgnoreBelowBackground(true, ap); + // todo and recompute annotation } ap.alignmentChanged(); } @@ -513,7 +521,7 @@ public class AnnotationLabels extends JPanel @Override public void actionPerformed(ActionEvent e) { - ann.groupRef.setshowHMMSequenceLogo(cprofl.getState()); + ann.groupRef.setShowHMMSequenceLogo(cprofl.getState()); ap.repaint(); } }); @@ -529,7 +537,7 @@ public class AnnotationLabels extends JPanel ann.groupRef .setNormaliseHMMSequenceLogo(cproflnorm.getState()); // automatically enable logo display if we're clicked - ann.groupRef.setshowHMMSequenceLogo(true); + ann.groupRef.setShowHMMSequenceLogo(true); ap.repaint(); } }); @@ -581,30 +589,37 @@ public class AnnotationLabels extends JPanel } /** - * Adds context menu options for (alignment or group) Consensus annotation + * A helper method that adds menu options for calculation and visualisation of + * group and/or alignment consensus annotation to a popup menu. This is + * designed to be reusable for either unwrapped mode (popup menu is shown on + * component AnnotationLabels), or wrapped mode (popup menu is shown on + * IdPanel when the mouse is over an annotation label). * - * @param pop + * @param ap * @param ann + * @param pop */ - protected void addConsensusMenu(JPopupMenu pop, - final AlignmentAnnotation ann) + static void addConsensusMenuOptions(AlignmentPanel ap, + AlignmentAnnotation ann, + JPopupMenu pop) { - final boolean isGroupAnnotation = ann.groupRef != null; pop.addSeparator(); final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem( MessageManager.getString("label.ignore_gaps_consensus"), - (ann.groupRef != null) - ? ann.groupRef.isIgnoreGapsConsensus() + (ann.groupRef != null) ? ann.groupRef.getIgnoreGapsConsensus() : ap.av.isIgnoreGapsConsensus()); + final AlignmentAnnotation aaa = ann; cbmi.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (isGroupAnnotation) + if (aaa.groupRef != null) { - ann.groupRef.setIgnoreGapsConsensus(cbmi.getState()); + aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState()); + ap.getAnnotationPanel() + .paint(ap.getAnnotationPanel().getGraphics()); } else { @@ -614,7 +629,8 @@ public class AnnotationLabels extends JPanel } }); pop.add(cbmi); - if (isGroupAnnotation) + + if (aaa.groupRef != null) { /* * group consensus options @@ -627,7 +643,7 @@ public class AnnotationLabels extends JPanel @Override public void actionPerformed(ActionEvent e) { - ann.groupRef.setShowConsensusHistogram(chist.getState()); + aaa.groupRef.setShowConsensusHistogram(chist.getState()); ap.repaint(); } }); @@ -640,7 +656,7 @@ public class AnnotationLabels extends JPanel @Override public void actionPerformed(ActionEvent e) { - ann.groupRef.setshowSequenceLogo(cprofl.getState()); + aaa.groupRef.setshowSequenceLogo(cprofl.getState()); ap.repaint(); } }); @@ -653,9 +669,9 @@ public class AnnotationLabels extends JPanel @Override public void actionPerformed(ActionEvent e) { - ann.groupRef.setNormaliseSequenceLogo(cproflnorm.getState()); + aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState()); // automatically enable logo display if we're clicked - ann.groupRef.setshowSequenceLogo(true); + aaa.groupRef.setshowSequenceLogo(true); ap.repaint(); } }); @@ -668,48 +684,48 @@ public class AnnotationLabels extends JPanel */ final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( MessageManager.getString("label.show_histogram"), - av.isShowConsensusHistogram()); + ap.av.isShowConsensusHistogram()); chist.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - av.setShowConsensusHistogram(chist.getState()); + ap.av.setShowConsensusHistogram(chist.getState()); + ap.alignFrame.setMenusForViewport(); ap.repaint(); } }); pop.add(chist); final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem( MessageManager.getString("label.show_logo"), - av.isShowSequenceLogo()); + ap.av.isShowSequenceLogo()); cprof.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - av.setShowSequenceLogo(cprof.getState()); + ap.av.setShowSequenceLogo(cprof.getState()); + ap.alignFrame.setMenusForViewport(); ap.repaint(); } }); pop.add(cprof); final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem( MessageManager.getString("label.normalise_logo"), - av.isNormaliseSequenceLogo()); + ap.av.isNormaliseSequenceLogo()); cprofnorm.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - av.setShowSequenceLogo(true); - av.setNormaliseSequenceLogo(cprofnorm.getState()); + ap.av.setShowSequenceLogo(true); + ap.av.setNormaliseSequenceLogo(cprofnorm.getState()); + ap.alignFrame.setMenusForViewport(); ap.repaint(); } }); pop.add(cprofnorm); } - final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ); - consclipbrd.addActionListener(this); - pop.add(consclipbrd); } /** @@ -822,73 +838,123 @@ public class AnnotationLabels extends JPanel if (selectedRow > -1 && ap.av.getAlignment() .getAlignmentAnnotation().length > selectedRow) { - AlignmentAnnotation aa = ap.av.getAlignment() - .getAlignmentAnnotation()[selectedRow]; + AlignmentAnnotation[] anns = ap.av.getAlignment() + .getAlignmentAnnotation(); + AlignmentAnnotation aa = anns[selectedRow]; + + String desc = getTooltip(aa); + this.setToolTipText(desc); + String msg = getStatusMessage(aa, anns); + ap.alignFrame.setStatus(msg); + } + } - StringBuffer desc = new StringBuffer(); - if (aa.description != null - && !aa.description.equals("New description")) + /** + * Constructs suitable text to show in the status bar when over an annotation + * label, containing the associated sequence name (if any), and the annotation + * labels (or all labels for a graph group annotation) + * + * @param aa + * @param anns + * @return + */ + static String getStatusMessage(AlignmentAnnotation aa, + AlignmentAnnotation[] anns) + { + if (aa == null) + { + return null; + } + + StringBuilder msg = new StringBuilder(32); + if (aa.sequenceRef != null) + { + msg.append(aa.sequenceRef.getName()).append(" : "); + } + + if (aa.graphGroup == -1) + { + msg.append(aa.label); + } + else if (anns != null) + { + boolean first = true; + for (int i = anns.length - 1; i >= 0; i--) { - // TODO: we could refactor and merge this code with the code in - // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature - // tooltips - desc.append(aa.getDescription(true).trim()); - // check to see if the description is an html fragment. - if (desc.length() < 6 || (desc.substring(0, 6).toLowerCase() - .indexOf("") < 0)) - { - // clean the description ready for embedding in html - desc = new StringBuffer(LEFT_ANGLE_BRACKET_PATTERN.matcher(desc) - .replaceAll("<")); - desc.insert(0, ""); - } - else + if (anns[i].graphGroup == aa.graphGroup) { - // remove terminating html if any - int i = desc.substring(desc.length() - 7).toLowerCase() - .lastIndexOf(""); - if (i > -1) + if (!first) { - desc.setLength(desc.length() - 7 + i); + msg.append(", "); } + msg.append(anns[i].label); + first = false; } - if (aa.hasScore()) - { - desc.append("
"); - } - // if (aa.hasProperties()) - // { - // desc.append(""); - // for (String prop : aa.getProperties()) - // { - // desc.append(""); - // } - // desc.append("
" + prop + "" - // + aa.getProperty(prop) + "
"); - // } } - else + } + + return msg.toString(); + } + + /** + * Answers a tooltip, formatted as html, containing the annotation description + * (prefixed by associated sequence id if applicable), and the annotation + * (non-positional) score if it has one. Answers null if neither description + * nor score is found. + * + * @param aa + * @return + */ + static String getTooltip(AlignmentAnnotation aa) + { + if (aa == null) + { + return null; + } + StringBuilder tooltip = new StringBuilder(); + if (aa.description != null && !aa.description.equals("New description")) + { + // TODO: we could refactor and merge this code with the code in + // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature + // tooltips + String desc = aa.getDescription(true).trim(); + if (!desc.toLowerCase().startsWith(HTML_START_TAG)) { - // begin the tooltip's html fragment - desc.append(""); - if (aa.hasScore()) - { - // TODO: limit precision of score to avoid noise from imprecise - // doubles - // (64.7 becomes 64.7+/some tiny value). - desc.append(" Score: " + aa.score); - } + tooltip.append(HTML_START_TAG); + desc = desc.replace("<", "<"); } - if (desc.length() > 6) + else if (desc.toLowerCase().endsWith(HTML_END_TAG)) { - desc.append(""); - this.setToolTipText(desc.toString()); + desc = desc.substring(0, desc.length() - HTML_END_TAG.length()); } - else + tooltip.append(desc); + } + else + { + // begin the tooltip's html fragment + tooltip.append(HTML_START_TAG); + } + if (aa.hasScore()) + { + if (tooltip.length() > HTML_START_TAG.length()) { - this.setToolTipText(null); + tooltip.append("
"); } + // TODO: limit precision of score to avoid noise from imprecise + // doubles + // (64.7 becomes 64.7+/some tiny value). + tooltip.append(" Score: ").append(String.valueOf(aa.score)); } + + if (tooltip.length() > HTML_START_TAG.length()) + { + return tooltip.append(HTML_END_TAG).toString(); + } + + /* + * nothing in the tooltip (except "") + */ + return null; } /** @@ -900,12 +966,13 @@ public class AnnotationLabels extends JPanel protected void showOrHideAdjuster(MouseEvent evt) { boolean was = resizePanel; - resizePanel = evt.getY() < HEIGHT_ADJUSTER_HEIGHT && evt.getX() < HEIGHT_ADJUSTER_WIDTH; + resizePanel = evt.getY() < HEIGHT_ADJUSTER_HEIGHT + && evt.getX() < HEIGHT_ADJUSTER_WIDTH; if (resizePanel != was) { - setCursor(Cursor.getPredefinedCursor( - resizePanel ? Cursor.S_RESIZE_CURSOR + setCursor(Cursor + .getPredefinedCursor(resizePanel ? Cursor.S_RESIZE_CURSOR : Cursor.DEFAULT_CURSOR)); repaint(); } @@ -1067,7 +1134,7 @@ public class AnnotationLabels extends JPanel seqs, omitHidden, alignmentStartEnd); Toolkit.getDefaultToolkit().getSystemClipboard() - .setContents(new StringSelection(output), Desktop.instance); + .setContents(new StringSelection(output), Desktop.getInstance()); HiddenColumns hiddenColumns = null; @@ -1077,27 +1144,19 @@ public class AnnotationLabels extends JPanel av.getAlignment().getHiddenColumns()); } - Desktop.jalviewClipboard = new Object[] { seqs, ds, // what is the dataset - // of a consensus - // sequence ? need to - // flag - // sequence as special. + // what is the dataset of a consensus sequence? + // need to flag sequence as special. + Desktop.getInstance().jalviewClipboard = new Object[] { seqs, ds, hiddenColumns }; } - /** - * DOCUMENT ME! - * - * @param g1 - * DOCUMENT ME! - */ @Override public void paintComponent(Graphics g) { int width = getWidth(); if (width == 0) { - width = ap.calculateIdWidth().width + 4; + width = ap.calculateIdWidth().width; } Graphics2D g2 = (Graphics2D) g;