//----------------------------------------------------------------------------//
// //
// G l y p h s C o n t r o l l e 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.glyph.ui;
import omr.glyph.Evaluation;
import omr.glyph.Glyphs;
import omr.glyph.GlyphsModel;
import omr.glyph.Nest;
import omr.glyph.Shape;
import omr.glyph.ShapeSet;
import omr.glyph.facets.Glyph;
import omr.script.AssignTask;
import omr.script.BarlineTask;
import omr.script.DeleteTask;
import omr.selection.GlyphEvent;
import omr.selection.SelectionHint;
import omr.selection.SelectionService;
import omr.sheet.Sheet;
import org.jdesktop.application.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Set;
/**
* Class {@code GlyphsController} is a common basis for glyph handling,
* used by any user interface which needs to act on the actual glyph
* data.
*
* <p>There are two main methods in this class ({@link #asyncAssignGlyphs} and
* {@link #asyncDeassignGlyphs}). They share common characteristics:
* <ul>
* <li>They are processed asynchronously</li>
* <li>Their action is recorded in the sheet script</li>
* <li>They update the following steps, if any</li>
* </ul>
*
* <p>Since the bus of user selections is used, the methods of this class are
* meant to be used from within a user action, otherwise you must use a direct
* access to similar synchronous actions in the underlying {@link GlyphsModel}.
*
* @author Hervé Bitteur
*/
public class GlyphsController
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
GlyphsController.class);
//~ Instance fields --------------------------------------------------------
/** Related model */
protected final GlyphsModel model;
/** Cached sheet */
protected final Sheet sheet;
//~ Constructors -----------------------------------------------------------
//------------------//
// GlyphsController //
//------------------//
/**
* Create an instance of GlyphsController, with its underlying
* GlyphsModel instance.
*
* @param model the related glyphs model
*/
public GlyphsController (GlyphsModel model)
{
this.model = model;
sheet = model.getSheet();
}
//~ Methods ----------------------------------------------------------------
//-------------------//
// asyncAssignGlyphs //
//-------------------//
/**
* Asynchronouly assign a shape to the selected collection of glyphs
* and record this action in the script.
*
* @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
* @return the task that carries out the processing
*/
public Task<Void, Void> asyncAssignGlyphs (Collection<Glyph> glyphs,
Shape shape,
boolean compound)
{
// Safety check: we cannot alter virtual glyphs
for (Glyph glyph : glyphs) {
if (glyph.isVirtual()) {
logger.warn("Cannot alter VirtualGlyph#{}", glyph.getId());
return null;
}
}
if (ShapeSet.Barlines.contains(shape)
|| Glyphs.containsBarline(glyphs)) {
// Special case for barlines assignment or deassignment
return new BarlineTask(sheet, shape, compound, glyphs).launch(
sheet);
} else {
// Normal symbol processing
return new AssignTask(sheet, shape, compound, glyphs).launch(sheet);
}
}
//---------------------//
// asyncDeassignGlyphs //
//---------------------//
/**
* Asynchronously de-Assign a collection of glyphs and record this
* action in the script.
*
* @param glyphs the collection of glyphs to be de-assigned
* @return the task that carries out the processing
*/
public Task<Void, Void> asyncDeassignGlyphs (Collection<Glyph> glyphs)
{
return asyncAssignGlyphs(glyphs, null, false);
}
//--------------------------//
// asyncDeleteVirtualGlyphs //
//--------------------------//
public Task<Void, Void> asyncDeleteVirtualGlyphs (Collection<Glyph> glyphs)
{
return new DeleteTask(sheet, glyphs).launch(sheet);
}
//--------------//
// 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 model.getGlyphById(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 getLatestShapeAssigned ()
{
return model.getLatestShape();
}
//--------------------//
// getLocationService //
//--------------------//
/**
* Report the event service to use for LocationEvent.
* When no sheet is available, override this method to point to another
* service
*
* @return the event service to use for LocationEvent
*/
public SelectionService getLocationService ()
{
return model.getSheet()
.getLocationService();
}
//----------//
// getModel //
//----------//
/**
* Report the underlying model.
*
* @return the underlying glpyhs model
*/
public GlyphsModel getModel ()
{
return model;
}
//---------//
// getNest //
//---------//
/**
* Report the underlying glyph nest.
*
* @return the related glyph nest
*/
public Nest getNest ()
{
return model.getNest();
}
//----------------//
// setLatestShape //
//----------------//
/**
* Assign the latest shape.
*
* @param shape the latest shape
*/
public void setLatestShapeAssigned (Shape shape)
{
model.setLatestShape(shape);
}
//------------//
// syncAssign //
//------------//
/**
* Process synchronously the assignment defined in the provided
* context.
*
* @param context the context of the assignment
*/
public void syncAssign (AssignTask context)
{
final boolean compound = context.isCompound();
final Shape shape = context.getAssignedShape();
logger.debug("syncAssign {} compound:{}", context, compound);
Set<Glyph> glyphs = context.getInitialGlyphs();
if (shape != null) { // Assignment
// Persistent?
model.assignGlyphs(
glyphs,
context.getAssignedShape(),
compound,
Evaluation.MANUAL);
// Publish modifications (about new glyph)
Glyph firstGlyph = glyphs.iterator()
.next();
if (firstGlyph != null) {
publish(firstGlyph.getMembers().first().getGlyph());
}
} else { // Deassignment
model.deassignGlyphs(glyphs);
// Publish modifications (about current glyph)
publish(glyphs.iterator().next());
}
}
//------------//
// syncDelete //
//------------//
/**
* Process synchronously the deletion defined in the provided
* context.
*
* @param context the context of the deletion
*/
public void syncDelete (DeleteTask context)
{
logger.debug("syncDelete{}", context);
model.deleteGlyphs(context.getInitialGlyphs());
publish((Glyph) null);
}
//---------//
// publish //
//---------//
protected void publish (Glyph glyph)
{
// Update immediately the glyph info as displayed
if (model.getSheet() != null) {
getNest()
.getGlyphService()
.publish(
new GlyphEvent(this, SelectionHint.GLYPH_MODIFIED, null, glyph));
}
}
}