//----------------------------------------------------------------------------// // // // E r r o r s E d i t o r // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.ui; import omr.glyph.facets.Glyph; import omr.score.entity.Measure; import omr.score.entity.MeasureNode; import omr.score.entity.SystemNode; import omr.selection.GlyphEvent; import omr.selection.LocationEvent; import omr.selection.SelectionHint; import omr.sheet.Sheet; import omr.step.Step; import omr.step.Steps; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; import java.awt.Rectangle; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.DefaultListModel; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; /** * Class {@code ErrorsEditor} handles the set of error messages * recorded during the translation from sheet to score, allowing the * user to interactively browse the errors and go to the related * location in the sheet view. * * @author Hervé Bitteur */ public class ErrorsEditor { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(ErrorsEditor.class); //~ Instance fields -------------------------------------------------------- /** Related sheet */ private final Sheet sheet; /** The list of displayed errors */ private final JList<Record> list; /** The scrolling area */ private final JScrollPane scrollPane; /** Selection listener */ private final ListSelectionListener listener = new MyListener(); /** Set of error records */ private final SortedSet<Record> recordSet = new TreeSet<>(); /** Facade model for the JList */ private final DefaultListModel<Record> model = new DefaultListModel<>(); //~ Constructors ----------------------------------------------------------- //--------------// // ErrorsEditor // //--------------// /** * Create an instance of ErrorsEditor (one per sheet / score). * * @param sheet the related sheet */ public ErrorsEditor (Sheet sheet) { this.sheet = sheet; list = new JList<>(model); scrollPane = new JScrollPane(list); scrollPane.setBorder(null); list.addListSelectionListener(listener); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } //~ Methods ---------------------------------------------------------------- //----------// // addError // //----------// /** * Record an error within a SystemNode entity. * * @param node the node in the score hierarchy * @param text the message text */ public void addError (SystemNode node, String text) { addError(node, null, text); } //----------// // addError // //----------// /** * Record an error within a SystemNode entity, and related to a * specific glyph. * * @param node the containing node in score hierarchy * @param glyph the related glyph * @param text the message text */ public void addError (final SystemNode node, final Glyph glyph, final String text) { final Step step = getCurrentStep(); SwingUtilities.invokeLater( new Runnable() { // This part is run on swing thread @Override public void run () { if (recordSet.add(new Record(step, node, glyph, text))) { // Update the model model.removeAllElements(); for (Record record : recordSet) { model.addElement(record); } } } }); } //-------// // clear // //-------// /** * Remove all errors from the editor. (Not used?) */ public void clear () { SwingUtilities.invokeLater( new Runnable() { // This part is run on swing thread @Override public void run () { recordSet.clear(); model.removeAllElements(); } }); } //-----------// // clearStep // //-----------// /** * Clear all messages related to the provided step. * * @param step the step we are interested in */ public void clearStep (final Step step) { SwingUtilities.invokeLater( new Runnable() { // This part is run on swing thread @Override public void run () { logger.debug("Clearing errors for {}", step); for (Iterator<Record> it = recordSet.iterator(); it.hasNext();) { Record record = it.next(); if (record.step == step) { it.remove(); } } // Update the model model.removeAllElements(); for (Record record : recordSet) { model.addElement(record); } } }); } //-------------// // clearSystem // //-------------// /** * Clear all messages related to the provided system id. * (we use system id rather than system, since a system may be reallocated * by SystemsBuilder) * * @param step the step we are interested in * @param systemId the id of system to clear */ public void clearSystem (final Step step, final int systemId) { SwingUtilities.invokeLater( new Runnable() { // This part is run on swing thread @Override public void run () { logger.debug("Clearing errors for {} system {}", step, systemId); for (Iterator<Record> it = recordSet.iterator(); it.hasNext();) { Record record = it.next(); if ((record.step == step) && (record.node.getSystem().getId() == systemId)) { it.remove(); } } // Update the model model.removeAllElements(); for (Record record : recordSet) { model.addElement(record); } } }); } //--------------// // getComponent // //--------------// /** * Give access to the real component. * * @return the concrete component */ public JComponent getComponent () { return scrollPane; } //----------------// // getCurrentStep // //----------------// /** * Retrieve the step being performed on the sheet. * Beware, during SCORE step and following stepq, just the first sheet * has a current step assigned. * * @return the step being done */ private Step getCurrentStep () { Step step = sheet.getCurrentStep(); if (step == null) { ///step = sheet.getScore().getFirstPage().getSheet().getCurrentStep(); step = Steps.valueOf(Steps.SCORE); } return step; } //~ Inner Classes ---------------------------------------------------------- //--------// // Record // //--------// /** * A structure to hold the various pieces of an error message. */ private static class Record implements Comparable<Record> { //~ Instance fields ---------------------------------------------------- final Step step; final SystemNode node; final Glyph glyph; final String text; //~ Constructors ------------------------------------------------------- public Record (Step step, SystemNode node, Glyph glyph, String text) { this.step = step; this.node = node; this.glyph = glyph; this.text = text; } //~ Methods ------------------------------------------------------------ @Override public int compareTo (Record other) { // Very basic indeed !!! return toString().compareTo(other.toString()); } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append(node.getContextString()); if (glyph != null) { sb.append(" [").append(glyph.idString()).append("]"); } if (step != null) { sb.append(" ").append(step); } sb.append(" ").append(text); return sb.toString(); } } //------------// // MyListener // //------------// /** * A specific listener to handle user selection in the list of * errors. */ private class MyListener implements ListSelectionListener { //~ Methods ------------------------------------------------------------ @Override public void valueChanged (ListSelectionEvent e) { if ((e.getSource() == list) && !e.getValueIsAdjusting()) { Record record = list.getSelectedValue(); if (record != null) { logger.debug("value={}", record); // Use glyph location if available if (record.glyph != null) { sheet.getNest().getGlyphService().publish( new GlyphEvent( this, SelectionHint.GLYPH_INIT, null, record.glyph)); } else { // Otherwise use node location as possible try { Point pixPt = null; try { pixPt = record.node.getCenter(); } catch (Exception ex) { } if (pixPt == null) { if (record.node instanceof MeasureNode) { MeasureNode mn = (MeasureNode) record.node; Measure measure = mn.getMeasure(); if (measure != null) { pixPt = measure.getCenter(); } } } sheet.getLocationService().publish( new LocationEvent( ErrorsEditor.this, SelectionHint.LOCATION_INIT, null, new Rectangle(pixPt))); } catch (Exception ex) { logger.warn( "Failed pointing to " + record.node, ex); } } } } } } }