//----------------------------------------------------------------------------// // // // G l y p h s M o d e l // // // //----------------------------------------------------------------------------// // <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.glyph; import omr.Main; import omr.glyph.facets.BasicGlyph; import omr.glyph.facets.Glyph; import omr.grid.StaffInfo; import omr.lag.Section; import omr.score.ui.ScoreActions; import omr.sheet.Sheet; import omr.sheet.SystemInfo; import omr.step.Step; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; /** * Class {@code GlyphsModel} is a common model for synchronous glyph * and section handling. * * <p>Nota: User gesture should trigger actions in GlyphsController which will * asynchronously delegate to this model. * * @author Hervé Bitteur */ public class GlyphsModel { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(GlyphsModel.class); //~ Instance fields -------------------------------------------------------- /** Underlying glyph nest */ protected final Nest nest; /** Related Sheet */ protected final Sheet sheet; /** Related Step */ protected final Step step; /** Latest shape assigned if any */ protected Shape latestShape; //~ Constructors ----------------------------------------------------------- //-------------// // GlyphsModel // //-------------// /** * Create an instance of GlyphsModel, with its underlying glyph lag. * * @param sheet the related sheet (can be null) * @param nest the related nest (cannot be null) * @param step the step after which update should be perform (can be null) */ public GlyphsModel (Sheet sheet, Nest nest, Step step) { // Null sheet is allowed (for GlyphVerifier use) this.sheet = sheet; if (nest == null) { throw new IllegalArgumentException( "Attempt to create a GlyphsModel with null underlying nest"); } else { this.nest = nest; } this.step = step; } //~ Methods ---------------------------------------------------------------- //--------------// // assignGlyphs // //--------------// /** * Assign a shape to the selected collection of glyphs. * * @param glyphs the collection of glyphs to be assigned * @param shape the shape to be assigned * @param compound flag to build one compound, rather than assign each * individual glyph * @param grade the grade we have wrt the assigned shape */ public void assignGlyphs (Collection<Glyph> glyphs, Shape shape, boolean compound, double grade) { if (compound) { // Build & insert one compound Glyph glyph; SystemInfo system = sheet.getSystemOf(glyphs); if (system != null) { glyph = system.buildTransientCompound(glyphs); } else { glyph = new BasicGlyph(sheet.getScale().getInterline()); for (Glyph g : glyphs) { glyph.stealSections(g); if (glyph.getNest() == null) { glyph.setNest(g.getNest()); } } } assignGlyph(glyph, shape, grade); } else { // Assign each glyph individually for (Glyph glyph : new ArrayList<>(glyphs)) { if (glyph.getShape() != Shape.NOISE) { assignGlyph(glyph, shape, grade); } } } } //----------------// // assignSections // //----------------// /** * Assign a shape to the selected collection of sections. * * @param sections the collection of sections to be aggregated as a glyph * @param shape the shape to be assigned * @param grade the grade we have wrt the assigned shape * @return the newly built glyph */ public Glyph assignSections (Collection<Section> sections, Shape shape, double grade) { // Build & insert one glyph out of the sections SystemInfo system = sections.iterator().next().getSystem(); Glyph glyph = system.buildGlyph(sections); return assignGlyph(glyph, shape, grade); } //----------------// // deassignGlyphs // //----------------// /** * De-Assign a collection of glyphs. * * @param glyphs the collection of glyphs to be de-assigned */ public void deassignGlyphs (Collection<Glyph> glyphs) { for (Glyph glyph : new ArrayList<>(glyphs)) { deassignGlyph(glyph); } } //--------------// // deleteGlyphs // //--------------// public void deleteGlyphs (Collection<Glyph> glyphs) { for (Glyph glyph : new ArrayList<>(glyphs)) { deleteGlyph(glyph); } } //--------------// // getGlyphById // //--------------// /** * Retrieve a glyph, knowing its id. * * @param id the glyph id * @return the glyph found, or null if not */ public Glyph getGlyphById (int id) { return nest.getGlyph(id); } //----------------// // getLatestShape // //----------------// /** * Report the latest non null shape that was assigned, or null if * none. * * @return latest shape assigned, or null if none */ public Shape getLatestShape () { return latestShape; } //---------// // getNest // //---------// /** * Report the underlying glyph nest. * * @return the related glyph nest */ public Nest getNest () { return nest; } //----------------// // getRelatedStep // //----------------// /** * Report the step this GlyphsModel is used for, so that we know * from which step updates must be propagated. * (we have to update the steps that follow this one) * * @return the step related to this glyphs model */ public Step getRelatedStep () { return step; } //----------// // getSheet // //----------// /** * Report the model underlying sheet. * * @return the underlying sheet instance */ public Sheet getSheet () { return sheet; } //----------------// // setLatestShape // //----------------// /** * Assign the latest useful shape. * * @param shape the current / latest shape */ public void setLatestShape (Shape shape) { if (shape != Shape.GLYPH_PART) { latestShape = shape; } } //-------------// // assignGlyph // //-------------// /** * Assign a Shape to a glyph, inserting the glyph to its containing * system and nest if it is still transient. * * @param glyph the glyph to be assigned * @param shape the assigned shape, which may be null * @param grade the grade about shape * @return the assigned glyph (perhaps an original glyph) */ protected Glyph assignGlyph (Glyph glyph, Shape shape, double grade) { if (glyph == null) { return null; } if (shape != null) { SystemInfo system = sheet.getSystemOf(glyph); if (system != null) { glyph = system.addGlyph(glyph); // System then nest } else { // Insert in nest directly, which assigns an id to the glyph glyph = nest.addGlyph(glyph); } boolean isTransient = glyph.isTransient(); logger.debug("Assign {}{} to {}", isTransient ? "compound " : "", glyph.idString(), shape); // Remember the latest shape assigned setLatestShape(shape); } // Do the assignment of the shape to the glyph glyph.setShape(shape, grade); // Should we persist the assigned glyph? if ((shape != null) && (grade == Evaluation.MANUAL) && (Main.getGui() != null) && ScoreActions.getInstance().isManualPersisted()) { // Record the glyph description to disk GlyphRepository.getInstance().recordOneGlyph(glyph, sheet); } return glyph; } //---------------// // deassignGlyph // //---------------// /** * Deassign the shape of a glyph. * * @param glyph the glyph to deassign */ protected void deassignGlyph (Glyph glyph) { // Assign the null shape to the glyph assignGlyph(glyph, null, Evaluation.ALGORITHM); } //-------------// // deleteGlyph // //-------------// protected void deleteGlyph (Glyph glyph) { if (glyph == null) { return; } if (!glyph.isVirtual()) { logger.warn("Attempt to delete non-virtual {}", glyph.idString()); return; } SystemInfo system = sheet.getSystemOf(glyph); // Special case for ledger glyph if (glyph.getShape() == Shape.LEDGER) { StaffInfo staff = system.getStaffAt(glyph.getAreaCenter()); staff.removeLedger(glyph); } if (system != null) { system.removeGlyph(glyph); } nest.removeVirtualGlyph((VirtualGlyph) glyph); } }