//----------------------------------------------------------------------------// // // // B a s i c L a g // // // //----------------------------------------------------------------------------// // <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.lag; import omr.glyph.facets.Glyph; import omr.glyph.ui.ViewParameters; import omr.graph.BasicDigraph; import omr.run.Orientation; import omr.run.Run; import omr.run.RunsTable; import omr.selection.GlyphEvent; import omr.selection.LagEvent; import omr.selection.LocationEvent; import omr.selection.MouseMovement; import omr.selection.RunEvent; import omr.selection.SectionEvent; import omr.selection.SectionIdEvent; import omr.selection.SectionSetEvent; import omr.selection.SelectionHint; import omr.selection.SelectionService; import omr.selection.UserEvent; import omr.util.Predicate; import org.bushe.swing.event.EventSubscriber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * Class {@code BasicLag} is a basic implementation of {@link Lag} * interface. * * @author Hervé Bitteur */ public class BasicLag extends BasicDigraph<Lag, Section> implements Lag, EventSubscriber<UserEvent> { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(BasicLag.class); /** Events read on location service */ public static final Class[] locEventsRead = new Class<?>[]{LocationEvent.class}; /** Events read on run service */ public static final Class[] runEventsRead = new Class<?>[]{RunEvent.class}; /** Events read on section service */ public static final Class[] sctEventsRead = new Class<?>[]{ SectionIdEvent.class, SectionEvent.class }; //~ Instance fields -------------------------------------------------------- /** Orientation of the lag */ private final Orientation orientation; /** Underlying runs table */ private RunsTable runsTable; /** Location service */ private SelectionService locationService; /** Hosted section service */ protected final SelectionService lagService; /** Scene service */ private SelectionService glyphService; //~ Constructors ----------------------------------------------------------- //----------// // BasicLag // //----------// /** * Constructor with specified orientation * * @param name the distinguished name for this instance * @param orientation the desired orientation of the lag */ public BasicLag (String name, Orientation orientation) { this(name, BasicSection.class, orientation); } //----------// // BasicLag // //----------// /** * Constructor with specified orientation and section class * * @param name the distinguished name for this instance * @param orientation the desired orientation of the lag */ public BasicLag (String name, Class<? extends Section> sectionClass, Orientation orientation) { super(name, sectionClass); this.orientation = orientation; lagService = new SelectionService(name, Lag.eventsWritten); } //~ Methods ---------------------------------------------------------------- //---------// // addRuns // //---------// @Override public void addRuns (RunsTable runsTable) { if (this.runsTable == null) { this.runsTable = runsTable.copy(); } else { // Add runs into the existing table this.runsTable.include(runsTable); } } //---------------// // createSection // //---------------// @Override public Section createSection (int firstPos, Run firstRun) { if (firstRun == null) { throw new IllegalArgumentException("null first run"); } Section section = createVertex(); section.setFirstPos(firstPos); section.append(firstRun); return section; } //----------------// // getOrientation // //----------------// @Override public Orientation getOrientation () { return orientation; } //----------// // getRunAt // //----------// @Override public final Run getRunAt (int x, int y) { return runsTable.getRunAt(x, y); } //---------------// // getRunService // //---------------// @Override public SelectionService getRunService () { return runsTable.getRunService(); } //---------// // getRuns // //---------// @Override public RunsTable getRuns () { return runsTable; } //-------------------// // getSectionService // //-------------------// @Override public SelectionService getSectionService () { return lagService; } //-------------// // getSections // //-------------// @Override public final Collection<Section> getSections () { return getVertices(); } //--------------------// // getSelectedSection // //--------------------// @Override public Section getSelectedSection () { return (Section) getSectionService().getSelection(SectionEvent.class); } //-----------------------// // getSelectedSectionSet // //-----------------------// @Override @SuppressWarnings("unchecked") public Set<Section> getSelectedSectionSet () { return (Set<Section>) getSectionService().getSelection( SectionSetEvent.class); } //------------// // isVertical // //------------// /** * Predicate on lag orientation * * @return true if vertical, false if horizontal */ public boolean isVertical () { return orientation.isVertical(); } //---------------------------// // lookupIntersectedSections // //---------------------------// @Override public Set<Section> lookupIntersectedSections (Rectangle rect) { return Sections.lookupIntersectedSections(rect, getSections()); } //----------------// // lookupSections // //----------------// @Override public Set<Section> lookupSections (Rectangle rect) { return Sections.lookupSections(rect, getSections()); } //---------// // onEvent // //---------// @Override public void onEvent (UserEvent event) { try { // Ignore RELEASING if (event.movement == MouseMovement.RELEASING) { return; } if (event instanceof LocationEvent) { // Location => lassoed Section(s) handleEvent((LocationEvent) event); } else if (event instanceof RunEvent) { // Run => Section handleEvent((RunEvent) event); } else if (event instanceof SectionIdEvent) { // Section ID => Section handleEvent((SectionIdEvent) event); } else if (event instanceof SectionEvent) { // Section => contour & SectionSet update + Glyph? handleEvent((SectionEvent) event); } } catch (Exception ex) { logger.warn(getClass().getName() + " onEvent error", ex); } } //---------// // publish // //---------// /** * Publish on Lag selection service * * @param event the event to publish */ public void publish (LagEvent event) { lagService.publish(event); } //---------// // publish // //---------// /** * Publish a RunEvent on RunsTable service * * @param event the event to publish */ public void publish (RunEvent event) { // Delegate to RunsTable getRunService().publish(event); } //---------// // publish // //---------// public void publish (LocationEvent locationEvent) { locationService.publish(locationEvent); } //---------------// // purgeSections // //---------------// @Override public List<Section> purgeSections (Predicate<Section> predicate) { // List of sections to be purged (to avoid concurrent modifications) List<Section> purges = new ArrayList<>(2000); // Iterate on all sections for (Section section : getSections()) { // Check predicate on the current section if (predicate.check(section)) { logger.debug("Purging {}", section); purges.add(section); } } // Now, actually perform the needed removals for (Section section : purges) { section.delete(); // Remove the related runs from the underlying runsTable int pos = section.getFirstPos(); for (Run run : section.getRuns()) { runsTable.removeRun(pos++, run); } } // Return the sections purged return purges; } //---------// // setRuns // //---------// @Override public void setRuns (RunsTable runsTable) { if (this.runsTable != null) { throw new RuntimeException("Attempt to overwrite lag runs table"); } else { this.runsTable = runsTable; } } //-------------// // setServices // //-------------// @Override public void setServices (SelectionService locationService, SelectionService sceneService) { this.locationService = locationService; this.glyphService = sceneService; runsTable.setLocationService(locationService); for (Class<?> eventClass : locEventsRead) { locationService.subscribeStrongly(eventClass, this); } for (Class<?> eventClass : runEventsRead) { getRunService().subscribeStrongly(eventClass, this); } for (Class<?> eventClass : sctEventsRead) { lagService.subscribeStrongly(eventClass, this); } } //-------------// // cutServices // //-------------// @Override public void cutServices () { runsTable.cutLocationService(locationService); for (Class<?> eventClass : locEventsRead) { locationService.unsubscribe(eventClass, this); } for (Class<?> eventClass : runEventsRead) { getRunService().unsubscribe(eventClass, this); } for (Class<?> eventClass : sctEventsRead) { lagService.unsubscribe(eventClass, this); } } //-----------------// // internalsString // //-----------------// @Override protected String internalsString () { StringBuilder sb = new StringBuilder(super.internalsString()); // Orientation sb.append(" ").append(orientation); // // Runs // if (runsTable != null) { // sb.append((" runs:")) // .append(runsTable.getRunCount()); // } return sb.toString(); } //-------------// // handleEvent // //-------------// /** * Interest in lasso SheetLocation => Section(s) * * @param sheetLocation */ private void handleEvent (LocationEvent locationEvent) { logger.debug("Lag. sheetLocation:{}", locationEvent); Rectangle rect = locationEvent.getData(); if (rect == null) { return; } SelectionHint hint = locationEvent.hint; MouseMovement movement = locationEvent.movement; if (!hint.isLocation() && !hint.isContext()) { return; } // Section selection mode? if (ViewParameters.getInstance().isSectionMode()) { // Non-degenerated rectangle? if ((rect.width > 0) && (rect.height > 0)) { // Look for enclosed sections Set<Section> sectionsFound = lookupSections(rect); // Publish (first) Section found Section section = sectionsFound.isEmpty() ? null : sectionsFound.iterator().next(); publish(new SectionEvent(this, hint, movement, section)); // Publish whole SectionSet publish( new SectionSetEvent(this, hint, movement, sectionsFound)); } } } //-------------// // handleEvent // //-------------// /** * Interest in Run => Section * * @param run */ private void handleEvent (RunEvent runEvent) { logger.debug("Lag. run:{}", runEvent); // Lookup for Section linked to this Run // Search and forward section info Run run = runEvent.getData(); SelectionHint hint = runEvent.hint; MouseMovement movement = runEvent.movement; if (!hint.isLocation() && !hint.isContext()) { return; } // Publish Section information Section section = (run != null) ? run.getSection() : null; publish(new SectionEvent(this, hint, movement, section)); } //-------------// // handleEvent // //-------------// /** * Interest in SectionId => Section * * @param idEvent */ private void handleEvent (SectionIdEvent idEvent) { Integer id = idEvent.getData(); if ((id == null) || (id == 0)) { return; } SelectionHint hint = idEvent.hint; MouseMovement movement = idEvent.movement; // Always publish a null Run publish(new RunEvent(this, hint, movement, null)); // Lookup a lag section with proper ID publish(new SectionEvent(this, hint, movement, getVertexById(id))); } //-------------// // handleEvent // //-------------// /** * Interest in Section => section contour + update SectionSet * * @param sectionEvent */ private void handleEvent (SectionEvent sectionEvent) { SelectionHint hint = sectionEvent.hint; MouseMovement movement = sectionEvent.movement; Section section = sectionEvent.getData(); if (hint == SelectionHint.SECTION_INIT) { // Publish section contour publish( new LocationEvent( this, hint, null, (section != null) ? section.getBounds() : null)); } // In section-selection mode, update section set if (ViewParameters.getInstance().isSectionMode()) { // Section mode: Update section set Set<Section> sections = getSelectedSectionSet(); if (sections == null) { sections = new LinkedHashSet<>(); } if (hint == SelectionHint.LOCATION_ADD) { if (section != null) { if (movement == MouseMovement.PRESSING) { // Adding to (or Removing from) the set of sections if (sections.contains(section)) { sections.remove(section); } else { sections.add(section); } } else if (movement == MouseMovement.DRAGGING) { // Always adding to the set of sections sections.add(section); } } } else { // Overwriting the set of sections if (section != null) { // Make a one-section set sections.clear(); sections.add(section); } else if (!sections.isEmpty()) { // Empty the section set sections.clear(); } } logger.debug("{}. Publish section set {}", getName(), sections); publish(new SectionSetEvent(this, hint, movement, sections)); } else if (glyphService != null) { // Section -> Glyph if (hint.isLocation() || hint.isContext() || hint.isSection()) { // Select related Glyph if any Glyph glyph = (section != null) ? section.getGlyph() : null; if (glyph != null) { logger.debug("{}. Publish glyph {}", getName(), glyph); glyphService.publish( new GlyphEvent(this, hint, movement, glyph)); } } } } }