//----------------------------------------------------------------------------//
// //
// S y m b o l G l y p h B o a r d //
// //
//----------------------------------------------------------------------------//
// <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.Shape;
import omr.glyph.ShapeSet;
import omr.glyph.facets.Glyph;
import omr.text.TextRole;
import omr.score.entity.Text.CreatorText.CreatorType;
import omr.score.entity.TimeRational;
import omr.score.entity.TimeSignature;
import omr.selection.GlyphEvent;
import omr.selection.GlyphSetEvent;
import omr.selection.MouseMovement;
import omr.selection.UserEvent;
import omr.sheet.ui.SheetsController;
import omr.text.TextRoleInfo;
import omr.text.TextWord;
import omr.ui.field.LComboBox;
import omr.ui.field.LDoubleField;
import omr.ui.field.LIntegerField;
import omr.ui.field.LTextField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.event.ActionEvent;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
/**
* Class {@code SymbolGlyphBoard} defines an extended glyph board,
* with characteristics (pitch position, stem number, etc) that are
* specific to a symbol, and an additional symbol glyph spinner.
* <ul>
* <li>A <b>symbolSpinner</b> to browse through all glyphs that are
* considered as symbols, that is built from aggregation of contiguous
* sections, or by combination of other symbols.
* Glyphs whose shape is set to {@link omr.glyph.Shape#NOISE}, that is too
* small glyphs, are not included in this spinner.</ul>
*
* <h4>Layout of an instance of SymbolGlyphBoard:<br/>
* <img src="doc-files/SymbolGlyphBoard.png"/>
* </h4>
*
* @author Hervé Bitteur
*/
public class SymbolGlyphBoard
extends GlyphBoard
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
SymbolGlyphBoard.class);
//~ Instance fields --------------------------------------------------------
/** Numerator of time signature */
private LIntegerField timeNum;
/** Denominator of time signature */
private LIntegerField timeDen;
/** ComboBox for text role */
private LComboBox<TextRole> roleCombo;
/** ComboBox for text role type */
private LComboBox<CreatorType> typeCombo;
/** Output : textual confidence */
protected LIntegerField confField;
/** Input/Output : textual content */
protected LTextField textField;
/** Glyph characteristics : position wrt staff */
private LDoubleField pitchPosition = new LDoubleField(
false,
"Pitch",
"Logical pitch position",
"%.3f");
/** Glyph characteristics : is there a ledger */
private LTextField ledger = new LTextField(
false,
"Ledger",
"Does this glyph intersect a ledger?");
/** Glyph characteristics : how many stems */
private LIntegerField stems = new LIntegerField(
false,
"Stems",
"Number of stems connected to this glyph");
/** Glyph characteristics : normalized weight */
private LDoubleField weight = new LDoubleField(
false,
"Weight",
"Normalized weight",
"%.3f");
/** Glyph characteristics : normalized width */
private LDoubleField width = new LDoubleField(
false,
"Width",
"Normalized width",
"%.3f");
/** Glyph characteristics : normalized height */
private LDoubleField height = new LDoubleField(
false,
"Height",
"Normalized height",
"%.3f");
/** Handling of entered / selected values */
private final Action paramAction;
/** To avoid unwanted events */
private boolean selfUpdatingText;
//~ Constructors -----------------------------------------------------------
//------------------//
// SymbolGlyphBoard //
//------------------//
/**
* Create the symbol glyph board.
*
* @param glyphsController the companion which handles glyph (de)assignments
* @param useSpinners true for use of spinners
* @param expanded true to initially expand this board
*/
public SymbolGlyphBoard (GlyphsController glyphsController,
boolean useSpinners,
boolean expanded)
{
// For all glyphs
super(glyphsController, useSpinners, true);
// Additional combo for text role
paramAction = new ParamAction();
roleCombo = new LComboBox<>(
"Role",
"Role of the Text",
TextRole.values());
roleCombo.getField().setMaximumRowCount(TextRole.values().length);
roleCombo.addActionListener(paramAction);
// Additional combo for text type
typeCombo = new LComboBox<>(
"Type",
"Type of the Text",
CreatorType.values());
typeCombo.addActionListener(paramAction);
// Confidence and Text fields
confField = new LIntegerField(false, "Conf", "Confidence in text value");
textField = new LTextField(true, "Text", "Content of a textual glyph");
textField.getField().setHorizontalAlignment(JTextField.LEFT);
// Time signature
timeNum = new LIntegerField("Num", "");
timeDen = new LIntegerField("Den", "");
defineSpecificLayout();
// Needed to process user input when RETURN/ENTER is pressed
getComponent().
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
put(KeyStroke.getKeyStroke("ENTER"), "TextAction");
getComponent().getActionMap().put("TextAction", paramAction);
}
//~ Methods ----------------------------------------------------------------
//---------//
// onEvent //
//---------//
/**
* Call-back triggered when Glyph Selection has been modified.
*
* @param event the (Glyph or glyph set) Selection
*/
@Override
public void onEvent (UserEvent event)
{
try {
// Ignore RELEASING
if (event.movement == MouseMovement.RELEASING) {
return;
}
super.onEvent(event);
if (event instanceof GlyphEvent) {
selfUpdating = true;
GlyphEvent glyphEvent = (GlyphEvent) event;
Glyph glyph = glyphEvent.getData();
Shape shape = (glyph != null) ? glyph.getShape() : null;
// Fill symbol characteristics
if (glyph != null) {
pitchPosition.setValue(glyph.getPitchPosition());
ledger.setText(Boolean.toString(glyph.isWithLedger()));
stems.setValue(glyph.getStemNumber());
weight.setValue(glyph.getNormalizedWeight());
width.setValue(glyph.getNormalizedWidth());
height.setValue(glyph.getNormalizedHeight());
} else {
ledger.setText("");
pitchPosition.setText("");
stems.setText("");
weight.setText("");
width.setText("");
height.setText("");
}
// Text info
if (roleCombo != null) {
if ((shape != null) && shape.isText()) {
selfUpdatingText = true;
confField.setVisible(false);
textField.setVisible(true);
roleCombo.setVisible(true);
typeCombo.setVisible(false);
roleCombo.setEnabled(true);
textField.setEnabled(true);
if (glyph.getTextValue() != null) {
textField.setText(glyph.getTextValue());
// Related word?
TextWord word = glyph.getTextWord();
if (word != null) {
confField.setValue(word.getConfidence());
confField.setVisible(true);
}
} else {
textField.setText("");
}
if (glyph.getTextRole() != null) {
roleCombo.setSelectedItem(glyph.getTextRole().role);
if (glyph.getTextRole().role == TextRole.Creator) {
typeCombo.setVisible(true);
typeCombo.setSelectedItem(
glyph.getTextRole().creatorType);
}
} else {
roleCombo.setSelectedItem(TextRole.UnknownRole);
}
selfUpdatingText = false;
} else {
confField.setVisible(false);
textField.setVisible(false);
roleCombo.setVisible(false);
typeCombo.setVisible(false);
}
}
// Time Signature info
if (timeNum != null) {
if (ShapeSet.Times.contains(shape)) {
timeNum.setVisible(true);
timeDen.setVisible(true);
timeNum.setEnabled(
shape == Shape.CUSTOM_TIME);
timeDen.setEnabled(
shape == Shape.CUSTOM_TIME);
TimeRational timeRational = (shape == Shape.CUSTOM_TIME)
? glyph.getTimeRational()
: TimeSignature.rationalOf(
shape);
if (timeRational != null) {
timeNum.setValue(timeRational.num);
timeDen.setValue(timeRational.den);
} else {
timeNum.setText("");
timeDen.setText("");
}
} else {
timeNum.setVisible(false);
timeDen.setVisible(false);
}
}
selfUpdating = false;
}
} catch (Exception ex) {
logger.warn(getClass().getName() + " onEvent error", ex);
}
}
//----------------------//
// defineSpecificLayout //
//----------------------//
/**
* Define a specific layout for this Symbol GlyphBoard
*/
private void defineSpecificLayout ()
{
int r = 1; // --------------------------------
// Glyph ---
r += 2; // --------------------------------
// shape
r += 2; // --------------------------------
// Glyph characteristics, first line
builder.add(pitchPosition.getLabel(), cst.xy(1, r));
builder.add(pitchPosition.getField(), cst.xy(3, r));
builder.add(ledger.getLabel(), cst.xy(5, r));
builder.add(ledger.getField(), cst.xy(7, r));
builder.add(stems.getLabel(), cst.xy(9, r));
builder.add(stems.getField(), cst.xy(11, r));
r += 2; // --------------------------------
// Glyph characteristics, second line
builder.add(weight.getLabel(), cst.xy(1, r));
builder.add(weight.getField(), cst.xy(3, r));
builder.add(width.getLabel(), cst.xy(5, r));
builder.add(width.getField(), cst.xy(7, r));
builder.add(height.getLabel(), cst.xy(9, r));
builder.add(height.getField(), cst.xy(11, r));
r += 2; // --------------------------------
// Text information, first line
if (textField != null) {
builder.add(confField.getLabel(), cst.xyw(1, r, 1));
builder.add(confField.getField(), cst.xyw(3, r, 1));
confField.setVisible(false);
builder.add(textField.getLabel(), cst.xyw(5, r, 1));
builder.add(textField.getField(), cst.xyw(7, r, 5));
textField.setVisible(false);
}
// or time signature parameters
if (timeNum != null) {
builder.add(timeNum.getLabel(), cst.xy(5, r));
builder.add(timeNum.getField(), cst.xy(7, r));
timeNum.setVisible(false);
builder.add(timeDen.getLabel(), cst.xy(9, r));
builder.add(timeDen.getField(), cst.xy(11, r));
timeDen.setVisible(false);
}
r += 2; // --------------------------------
// Text information, second line
if (roleCombo != null) {
builder.add(roleCombo.getLabel(), cst.xyw(1, r, 1));
builder.add(roleCombo.getField(), cst.xyw(3, r, 3));
roleCombo.setVisible(false);
builder.add(typeCombo.getLabel(), cst.xyw(7, r, 1));
builder.add(typeCombo.getField(), cst.xyw(9, r, 3));
typeCombo.setVisible(false);
}
}
//~ Inner Classes ----------------------------------------------------------
//-------------//
// ParamAction //
//-------------//
private class ParamAction
extends AbstractAction
{
//~ Methods ------------------------------------------------------------
// Method run whenever user presses Return/Enter in one of the parameter
// fields
@Override
public void actionPerformed (ActionEvent e)
{
// Discard irrelevant action events
if (selfUpdatingText) {
return;
}
// Get current glyph set
GlyphSetEvent glyphsEvent = (GlyphSetEvent) getSelectionService().
getLastEvent(
GlyphSetEvent.class);
Set<Glyph> glyphs = (glyphsEvent != null)
? glyphsEvent.getData() : null;
if ((glyphs != null) && !glyphs.isEmpty()) {
// Read shape information
String shapeName = shapeField.getText();
if (shapeName.isEmpty()) {
return;
}
Shape shape = Shape.valueOf(shapeName);
// Text?
if (shape.isText()) {
logger.debug("Text=''{}'' Role={}",
textField.getText().trim(),
roleCombo.getSelectedItem());
CreatorType type = null;
TextRole role = roleCombo.getSelectedItem();
if (role == TextRole.Creator) {
type = typeCombo.getSelectedItem();
}
TextRoleInfo roleInfo = new TextRoleInfo(role, type);
SheetsController.getCurrentSheet().getSymbolsController().
asyncAssignTexts(
glyphs,
roleInfo,
textField.getText());
} else // Custom time sig?
if (shape == Shape.CUSTOM_TIME) {
int num = timeNum.getValue();
int den = timeDen.getValue();
if ((num != 0) && (den != 0)) {
SheetsController.getCurrentSheet().
getSymbolsController().asyncAssignRationals(
glyphs,
new TimeRational(num, den));
} else {
logger.warn("Invalid time signature parameters");
}
}
}
}
}
}