//----------------------------------------------------------------------------//
// //
// S c o r e P a r a m e t e r s //
// //
//----------------------------------------------------------------------------//
// <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.score.ui;
import omr.plugin.PluginsManager;
import omr.run.AdaptiveDescriptor;
import omr.run.FilterDescriptor;
import omr.run.FilterKind;
import omr.run.GlobalDescriptor;
import omr.score.Score;
import omr.score.entity.Page;
import omr.score.entity.ScorePart;
import omr.score.entity.Tempo;
import omr.score.midi.MidiAbstractions;
import omr.script.ParametersTask;
import omr.script.ParametersTask.PartData;
import omr.script.ScriptActions;
import omr.sheet.Sheet;
import omr.step.Step;
import omr.step.Steps;
import omr.text.Language;
import omr.text.OCR.UnavailableOcrException;
import omr.ui.FileDropHandler;
import omr.ui.field.LTextField;
import omr.ui.field.SpinnerUtil;
import omr.ui.util.Panel;
import omr.util.OmrExecutors;
import omr.util.Param;
import omr.util.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* Class {@code ScoreParameters} is a dialog that allows the user to
* easily manage the most frequent parameters.
*
* <div style="float: right;">
* <img src="doc-files/ScoreParameters.png" />
* </div>
*
* <p>It addresses:
* <ul>
* <li>Text language specification</li>
* <li>Binarization parameters</li>
* <li>Step triggered by drag and drop</li>
* <li>Prompt for saving script on closing</li>
* <li>Call-stack printed on exception</li>
* <li>Parallelism allowed or not</li>
* <li>Name and instrument related to each score part</li>
* </ul>
*
* <p>The dialog is organized as a scope-based tabbed pane with:
* <ul>
* <li>a panel for the <b>default</b> scope,</li>
* <li>a panel for current <b>score</b> scope (provided that there is a
* selected score),</li>
* <li>and one panel for every <b>page</b> scope (provided that the
* score contains more than a single page).</li>
* </ul>
*
* <p>A panel is a vertical collection of panes, each pane being introduced
* by a check box and a label.
* Initially the box is unchecked and the pane content is disabled.
* <br/>Manually checking the box represents a selection and indicates the
* intention to modify the pane content (and thus enables the pane fields).
* <br/>Unchecking the box reverts the content to the value it had prior to
* the selection.
*
* <p>The selected modifications are actually performed (and this may launch
* some costly re-processing) only when the user presses the OK button.
*
* @author Hervé Bitteur
*/
public class ScoreParameters
implements ChangeListener
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
ScoreParameters.class);
//~ Instance fields --------------------------------------------------------
//
/** The swing component of this panel. */
private final JTabbedPane component = new JTabbedPane();
/** The related score, if any. */
private final Score score;
/** The related page, if any. */
private final Page page;
/** The panel dedicated to setting of defaults. */
private final MyPanel defaultPanel;
/** Related script task. */
private ParametersTask task;
//~ Constructors -----------------------------------------------------------
//
//-----------------//
// ScoreParameters //
//-----------------//
/**
* Create a ScoreParameters object.
*
* @param sheet the current sheet, or null
*/
public ScoreParameters (Sheet sheet)
{
if (sheet != null) {
this.page = sheet.getPage();
this.score = sheet.getScore();
} else {
score = null;
page = null;
}
component.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
// Allocate all required panels (default / score? / pages??)
MyPanel scorePanel;
MyPanel pagePanel = null;
// Default panel
TextPane defaultTextPane = createTextPane(
null, null, null, Language.defaultSpecification);
FilterPane defaultFilterPane = new FilterPane(
null, null, null, FilterDescriptor.defaultFilter);
TempoPane defaultTempoPane = new TempoPane(
null, null, Tempo.defaultTempo);
defaultPanel = new MyPanel("Default settings",
defaultTextPane,
defaultFilterPane,
defaultTempoPane,
new PluginPane(),
new DnDPane(),
new ScriptPane(),
new ParallelPane());
component.addTab("Default", null, defaultPanel, defaultPanel.getName());
// Score panel?
if (score != null) {
List<Pane> panes = new ArrayList<>();
TextPane scoreTextPane = createTextPane(
score, null, defaultTextPane, score.getTextParam());
panes.add(scoreTextPane);
FilterPane scoreFilterPane = new FilterPane(
score, null, defaultFilterPane, score.getFilterParam());
panes.add(scoreFilterPane);
panes.add(new TempoPane(
score, defaultTempoPane, score.getTempoParam()));
if (score.getPartList() != null) {
// Part by part information
panes.add(new PartsPane(score));
}
scorePanel = new MyPanel("Score settings", panes);
component.addTab(score.getRadix(), null, scorePanel, scorePanel.getName());
// Pages panels?
if (score.isMultiPage()) {
for (TreeNode pn : score.getPages()) {
Page aPage = (Page) pn;
MyPanel panel = new MyPanel("Page settings",
createTextPane(
null, aPage, scoreTextPane, page.getTextParam()),
new FilterPane(
null, aPage, scoreFilterPane, page.getFilterParam()));
component.addTab("P#" + aPage.getIndex(), null, panel, panel.getName());
if (aPage == page) {
pagePanel = panel;
}
}
}
} else {
scorePanel = null;
}
// Initially selected tab
component.addChangeListener(this);
component.setSelectedComponent((pagePanel != null) ? pagePanel
: (scorePanel != null) ? scorePanel
: defaultPanel);
}
//~ Methods ----------------------------------------------------------------
//--------------//
// getComponent //
//--------------//
/**
* Report the UI component.
*
* @return the concrete component
*/
public JTabbedPane getComponent ()
{
return component;
}
//--------//
// commit //
//--------//
/**
* Check the values and commit them if all are OK.
*
* @param sheet the related sheet
* @return true if committed, false otherwise
*/
public boolean commit (Sheet sheet)
{
if (dataIsValid()) {
try {
// Commit all specific values, if any, to their backup object
// Do this ONLY for the Default panel
MyPanel panel = defaultPanel;
//logger.info("{}", panel.getName());
for (Pane pane : panel.panes) {
pane.commit();
}
// Launch the prepared task (for score & pages)
if (sheet != null) {
task.launch(sheet);
}
} catch (Exception ex) {
logger.warn("Could not run ParametersTask", ex);
return false;
}
return true;
} else {
return false;
}
}
//-------------//
// dataIsValid //
//-------------//
/**
* Make sure every user-entered data is valid, and while doing so,
* feed a ParametersTask to be later run on the related score/sheet
*
* @return true if everything is OK, false otherwise
*/
private boolean dataIsValid ()
{
task = new ParametersTask();
// Loop on all panes of all panels
for (int t = 0, tBreak = component.getTabCount(); t < tBreak; t++) {
MyPanel panel = (MyPanel) component.getComponentAt(t);
for (Pane pane : panel.panes) {
if (pane.isSelected() && !pane.isValid()) {
task = null; // Cleaner
return false;
}
}
}
return true;
}
//--------------//
// stateChanged //
//--------------//
/**
* Method called when a new tab/Panel is selected
*
* @param e the event
*/
@Override
public void stateChanged (ChangeEvent e)
{
// Refresh the new current panel
MyPanel panel = (MyPanel) component.getSelectedComponent();
for (Pane pane : panel.panes) {
pane.display(pane.getTarget());
}
}
//----------------//
// createTextPane //
//----------------//
/**
* Factory method to get a TextPane, while handling exception when
* no OCR is available.
*
* @param score
* @param page
* @param parent
* @return A usable TextPane instance, or null otherwise
*/
private TextPane createTextPane (Score score,
Page page,
TextPane parent,
Param<String> backup)
{
// Caution: The language pane needs Tesseract up & running
try {
return new TextPane(score, page, parent, backup);
} catch (UnavailableOcrException ex) {
logger.info("No language pane for lack of OCR");
} catch (Throwable ex) {
logger.warn("Error creating language pane", ex);
}
return null;
}
//~ Inner Classes ----------------------------------------------------------
//
//---------//
// MyPanel //
//---------//
/**
* A panel corresponding to a tab.
*/
private final class MyPanel
extends Panel
{
/** Collection of individual data panes */
private final List<Pane> panes = new ArrayList<>();
public MyPanel (String name,
Pane... panes)
{
this(name, Arrays.asList(panes));
}
public MyPanel (String name,
List<Pane> panes)
{
setName(name);
for (Pane pane : panes) {
if (pane != null) {
this.panes.add(pane);
}
}
defineLayout();
// Initially, all panes are deselected
for (Pane pane : this.panes) {
pane.box.setSelected(false);
pane.actionPerformed(null);
}
}
public void defineLayout ()
{
// Compute the total number of logical rows
int logicalRowCount = 0;
for (Pane pane : panes) {
logicalRowCount += pane.getLogicalRowCount();
}
FormLayout layout = Panel.makeFormLayout(logicalRowCount, 3,
"right:", "30dlu", "35dlu");
PanelBuilder builder = new PanelBuilder(layout, this);
builder.setDefaultDialogBorder();
CellConstraints cst = new CellConstraints();
int r = 1;
for (Pane pane : panes) {
r = pane.defineLayout(builder, cst, r);
}
}
}
//------//
// Pane //
//------//
/**
* A pane is able to host data, check data validity and apply the
* requested modifications.
*/
private abstract class Pane<E>
extends Param<E>
implements ActionListener
{
//~ Instance fields ----------------------------------------------------
/** Backup parameter (cannot be null). */
protected final Param<E> backup;
/** Related score, if any. */
protected final Score score;
/** Related page, if any. */
protected final Page page;
/** Box for selecting specific vs inherited data. */
private final JCheckBox box;
/** Title for the pane. */
private final String title;
//~ Constructors -------------------------------------------------------
//
public Pane (String title,
Score score,
Page page,
Pane parent,
Param<E> backup)
{
super(parent);
if (backup == null) {
throw new IllegalArgumentException(
"Null backup for pane '" + title + "'");
}
this.backup = backup;
this.title = title;
this.score = score;
this.page = page;
box = new JCheckBox();
box.addActionListener(this);
}
//~ Methods ------------------------------------------------------------
/**
* Set the enabled flag for all data fields
*
* @param bool the flag value
*/
protected abstract void setEnabled (boolean bool);
/**
* Write the parameter into the fields content
*
* @param content the data to display
*/
protected abstract void display (E content);
/**
* Read the parameter as defined by the fields content.
*
* @return the pane parameter
*/
protected abstract E read ();
/**
* Commit the modifications, for the items that are not handled
* by the ParametersTask, which means all actions related to
* default values.
*/
public void commit ()
{
if (isSelected()) {
//logger.info(" {}: {}", title, read());
backup.setSpecific(read());
}
}
/**
* Build the related user interface
*
* @param builder the shared panel builder
* @param cst the cell constraints
* @param r initial row value
* @return final row value
*/
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
// Draw the specific/inherit box + separating line
builder.add(box, cst.xyw(1, r, 1));
builder.addSeparator(title, cst.xyw(3, r, 9));
r += 2;
return r;
}
/**
* Report the count of needed logical rows.
* Typically 2 (the label separator plus 1 line of data)
*/
public int getLogicalRowCount ()
{
return 2;
}
/**
* Check whether all the pane data are valid, and feed the
* ParametersTask accordingly with score or page information.
*
* @return true if everything is OK, false otherwise
*/
public boolean isValid ()
{
return true; // By default
}
/**
* User has selected (and enabled) this pane
*
* @return true if selected
*/
public boolean isSelected ()
{
return box.isSelected();
}
/**
* User selects (or deselects) this pane
*
* @param bool true for selection
*/
public void setSelected (boolean bool)
{
box.setSelected(bool);
}
/**
* Report the specific value for this pane, if any.
*
* @return the fields content when selected, otherwise the backup
* specific data if any.
*/
@Override
public E getSpecific ()
{
if (isSelected()) {
return read();
} else if (backup != null) {
return backup.getSpecific();
} else {
return null;
}
}
@Override
public void actionPerformed (ActionEvent e)
{
// Pane (de)selection (programmatic or manual)
boolean sel = isSelected();
setEnabled(sel);
if (!sel) {
display(getTarget());
}
}
}
//-------------//
// BooleanPane //
//-------------//
/**
* A template for pane with just one global boolean, and
* no score or page relationship.
* Scope can be: default.
*/
private abstract class BooleanPane
extends Pane<Boolean>
{
//~ Instance fields ----------------------------------------------------
/**
* Use a ComboBox for boolean, since current status is more readable
* than a plain CheckBox
*/
private final JComboBox<Boolean> box = new JComboBox(
new Boolean[]{Boolean.FALSE, Boolean.TRUE});
private final JLabel label;
//~ Constructors -------------------------------------------------------
public BooleanPane (String label,
String text,
String tip,
Param<Boolean> backup)
{
super(label, null, null, null, backup);
this.label = new JLabel(text, SwingConstants.RIGHT);
box.setToolTipText(tip);
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
builder.add(label, cst.xyw(5, r, 3));
builder.add(box, cst.xyw(9, r, 3));
return r + 2;
}
@Override
public void setEnabled (boolean bool)
{
box.setEnabled(bool);
label.setEnabled(bool);
}
@Override
protected void display (Boolean content)
{
box.setSelectedItem(content ? Boolean.TRUE : Boolean.FALSE);
}
@Override
protected Boolean read ()
{
return box.getItemAt(box.getSelectedIndex());
}
}
//------------//
// FilterPane //
//------------//
/**
* Pane to define the pixel binarization parameters.
* Scope can be: default, score, page.
*/
private class FilterPane
extends Pane<FilterDescriptor>
{
/** ComboBox for filter kind */
private final JComboBox<FilterKind> kindCombo = new JComboBox<>(FilterKind.values());
private final JLabel kindLabel = new JLabel("Filter", SwingConstants.RIGHT);
// Data for global
private final SpinData globalData = new SpinData("Threshold",
"Global threshold for foreground pixels",
new SpinnerNumberModel(0, 0, 255, 1));
// Data for local
private final SpinData localDataMean = new SpinData("Coeff for Mean",
"Coefficient for mean pixel value",
new SpinnerNumberModel(0.5, 0.5, 1.5, 0.1));
private final SpinData localDataDev = new SpinData("Coeff for StdDev",
"Coefficient for standard deviation value",
new SpinnerNumberModel(0.2, 0.2, 1.5, 0.1));
//~ Constructors -------------------------------------------------------
public FilterPane (Score score,
final Page page,
FilterPane parent,
Param<FilterDescriptor> backup)
{
super("Binarization", score, page, parent, backup);
// ComboBox for filter kind
kindCombo.setToolTipText("Specific filter on image pixels");
kindCombo.addActionListener(this);
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
builder.add(kindLabel, cst.xyw(5, r, 3));
builder.add(kindCombo, cst.xyw(9, r, 3));
r += 2;
// Layout global and local data as mutual overlays
globalData.defineLayout(builder, cst, r);
r = localDataMean.defineLayout(builder, cst, r);
r = localDataDev.defineLayout(builder, cst, r);
return r;
}
@Override
public int getLogicalRowCount ()
{
return 4;
}
@Override
public boolean isValid ()
{
task.setFilter(read(), page);
return true;
}
@Override
public void actionPerformed (ActionEvent e)
{
if (e != null && e.getSource() == kindCombo) {
// KindCombo: new kind
switch (readKind()) {
case GLOBAL:
localDataMean.setVisible(false);
localDataDev.setVisible(false);
globalData.setVisible(true);
// Use proper global data
display(GlobalDescriptor.getDefault());
break;
case ADAPTIVE:
globalData.setVisible(false);
localDataMean.setVisible(true);
localDataDev.setVisible(true);
// Use proper adaptive data
display(AdaptiveDescriptor.getDefault());
break;
default:
}
} else {
super.actionPerformed(e);
}
}
private FilterKind readKind ()
{
return kindCombo.getItemAt(kindCombo.getSelectedIndex());
}
@Override
protected FilterDescriptor read ()
{
commitSpinners();
return (readKind() == FilterKind.GLOBAL)
? new GlobalDescriptor(
(int) globalData.spinner.getValue())
: new AdaptiveDescriptor(
(double) localDataMean.spinner.getValue(),
(double) localDataDev.spinner.getValue());
}
/** This is needed to read data manually typed in spinners fields */
private void commitSpinners ()
{
try {
switch (readKind()) {
case GLOBAL:
globalData.spinner.commitEdit();
break;
case ADAPTIVE:
localDataMean.spinner.commitEdit();
localDataDev.spinner.commitEdit();
break;
default:
}
} catch (ParseException ignored) {
}
}
@Override
protected void display (FilterDescriptor desc)
{
FilterKind kind = desc.getKind();
kindCombo.setSelectedItem(kind);
switch (kind) {
case GLOBAL:
GlobalDescriptor globalDesc = (GlobalDescriptor) desc;
globalData.spinner.setValue(globalDesc.threshold);
break;
case ADAPTIVE:
AdaptiveDescriptor localDesc = (AdaptiveDescriptor) desc;
localDataMean.spinner.setValue(localDesc.meanCoeff);
localDataDev.spinner.setValue(localDesc.stdDevCoeff);
break;
default:
}
}
@Override
protected void setEnabled (boolean bool)
{
kindCombo.setEnabled(bool);
kindLabel.setEnabled(bool);
globalData.setEnabled(bool);
localDataMean.setEnabled(bool);
localDataDev.setEnabled(bool);
}
}
//-----------//
// PartPanel //
//-----------//
/**
* Panel for details of one score part.
*/
private class PartPanel
extends Panel
{
//~ Static fields/initializers -----------------------------------------
public static final int logicalRowCount = 3;
//~ Instance fields ----------------------------------------------------
//
private final JLabel label;
/** Id of the part */
private final LTextField id = new LTextField(
"Id",
"Id of the score part");
/** Name of the part */
private LTextField name = new LTextField(
true,
"Name",
"Name for the score part");
/** Midi Instrument */
private JLabel midiLabel = new JLabel("Midi");
private JComboBox<String> midiBox = new JComboBox<>(
MidiAbstractions.getProgramNames());
//~ Constructors -------------------------------------------------------
public PartPanel (ScorePart scorePart)
{
label = new JLabel("Part #" + scorePart.getId());
// Let's impose the id!
id.setText(scorePart.getPid());
}
//~ Methods ------------------------------------------------------------
public boolean checkPart ()
{
// Part name
if (name.getText().trim().length() == 0) {
logger.warn("Please supply a non empty part name");
return false;
} else {
task.addPart(name.getText(), midiBox.getSelectedIndex() + 1);
return true;
}
}
public PartData getData ()
{
return new PartData(name.getText(), midiBox.getSelectedIndex() + 1);
}
private int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
builder.add(label, cst.xyw(5, r, 7));
r += 2; // --
builder.add(id.getLabel(), cst.xy(5, r));
builder.add(id.getField(), cst.xy(7, r));
builder.add(name.getLabel(), cst.xy(9, r));
builder.add(name.getField(), cst.xy(11, r));
r += 2; // --
builder.add(midiLabel, cst.xy(5, r));
builder.add(midiBox, cst.xyw(7, r, 5));
return r;
}
private void setItemsEnabled (boolean sel)
{
label.setEnabled(sel);
id.setEnabled(sel);
name.setEnabled(sel);
midiLabel.setEnabled(sel);
midiBox.setEnabled(sel);
}
private void display (PartData partData)
{
// Setting for part name
name.setText(partData.name);
// Setting for part midi program
midiBox.setSelectedIndex(partData.program - 1);
}
}
//------------//
// ScriptPane //
//------------//
/**
* Should we prompt the user for saving the script when sheet is
* closed?.
* Scope can be: default.
*/
private class ScriptPane
extends BooleanPane
{
//~ Constructors -------------------------------------------------------
public ScriptPane ()
{
super(
"Script",
"Prompt for save",
"Should we prompt for saving the script on score closing",
ScriptActions.defaultPrompt);
}
}
//--------------//
// ParallelPane //
//--------------//
/**
* Should we use defaultParallelism as much as possible.
* Scope can be: default.
*/
private class ParallelPane
extends BooleanPane
{
//~ Constructors -------------------------------------------------------
public ParallelPane ()
{
super(
"Parallelism",
"Allowed",
"Should we use parallelism whenever possible",
OmrExecutors.defaultParallelism);
}
}
//---------//
// DnDPane //
//---------//
/**
* Which step should we trigger on Drag n' Drop?.
* Scope can be: default.
*/
private class DnDPane
extends Pane<Step>
{
//~ Instance fields ----------------------------------------------------
/** ComboBox for desired step */
private final JComboBox<Step> stepCombo;
private final JLabel stepLabel = new JLabel("Triggered step",
SwingConstants.RIGHT);
//~ Constructors -------------------------------------------------------
public DnDPane ()
{
super("Drag n' Drop", null, null, null, FileDropHandler.defaultStep);
// ComboBox for triggered step
stepCombo = new JComboBox<>(
Steps.values().toArray(new Step[0]));
stepCombo.setToolTipText("Step to trigger on Drag n' Drop");
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
builder.add(stepLabel, cst.xyw(5, r, 3));
builder.add(stepCombo, cst.xyw(9, r, 3));
return r + 2;
}
@Override
protected Step read ()
{
return stepCombo.getItemAt(stepCombo.getSelectedIndex());
}
@Override
protected void display (Step content)
{
stepCombo.setSelectedItem(content);
}
@Override
protected void setEnabled (boolean bool)
{
stepCombo.setEnabled(bool);
stepLabel.setEnabled(bool);
}
}
//------------//
// PluginPane //
//------------//
/**
* Which Plugin should be the default one.
* Scope can be: default.
*/
private class PluginPane
extends Pane<String>
{
//~ Instance fields ----------------------------------------------------
/** ComboBox for registered plugins */
private final JComboBox<String> pluginCombo;
private final JLabel pluginLabel = new JLabel("Default plugin",
SwingConstants.RIGHT);
//~ Constructors -------------------------------------------------------
public PluginPane ()
{
super("Plugin", null, null, null, PluginsManager.defaultPluginId);
// ComboBox for triggered step
pluginCombo = new JComboBox<>(
PluginsManager.getInstance().getPluginIds().toArray(new String[0]));
pluginCombo.setToolTipText("Default plugin to be launched");
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
builder.add(pluginLabel, cst.xyw(5, r, 3));
builder.add(pluginCombo, cst.xyw(9, r, 3));
return r + 2;
}
@Override
protected String read ()
{
return pluginCombo.getItemAt(pluginCombo.getSelectedIndex());
}
@Override
protected void display (String content)
{
pluginCombo.setSelectedItem(content);
}
@Override
protected void setEnabled (boolean bool)
{
pluginCombo.setEnabled(bool);
pluginLabel.setEnabled(bool);
}
}
//-----------//
// Tempopane //
//-----------//
/**
* Pane to set the dominant tempo value.
* Scope can be: default, score.
*/
private class TempoPane
extends Pane<Integer>
{
//~ Instance fields ----------------------------------------------------
// Tempo value
private final SpinData tempo = new SpinData("Quarters/Min",
"Tempo in quarters per minute",
new SpinnerNumberModel(20, 20, 400, 1));
//~ Constructors -------------------------------------------------------
public TempoPane (Score score,
Pane parent,
Param<Integer> backup)
{
super("Tempo", score, null, parent, backup);
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
return tempo.defineLayout(builder, cst, r);
}
@Override
protected Integer read ()
{
commitSpinners();
return (int) tempo.spinner.getValue();
}
@Override
protected void display (Integer content)
{
tempo.spinner.setValue(content);
}
@Override
protected void setEnabled (boolean bool)
{
tempo.setEnabled(bool);
}
@Override
public boolean isValid ()
{
task.setTempo(read());
return true;
}
private void commitSpinners ()
{
try {
tempo.spinner.commitEdit();
} catch (ParseException ignored) {
}
}
}
//----------//
// TextPane //
//----------//
/**
* Pane to set the dominant text language specification.
* Scope can be: default, score, page.
*/
private class TextPane
extends Pane<String>
implements ListSelectionListener
{
//~ Instance fields ----------------------------------------------------
/** Underlying language list model. */
Language.ListModel model = new Language.ListModel();
/** List for choosing elements of language specification. */
private final JList<String> langList = new JList<>(model);
/** Put the list into a scroll pane. */
private final JScrollPane langScroll = new JScrollPane(langList);
/** Resulting visible specification. */
private final JLabel langSpec = new JLabel("", SwingConstants.RIGHT);
//~ Constructors -------------------------------------------------------
public TextPane (Score score,
Page page,
TextPane parent,
Param<String> backup)
{
super("Language", score, page, parent, backup);
langList.setLayoutOrientation(JList.VERTICAL);
langList.setToolTipText("Dominant languages for textual items");
langList.setVisibleRowCount(5);
langList.addListSelectionListener(this);
langSpec.setToolTipText("Resulting specification");
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
builder.add(langSpec, cst.xyw(1, r, 7));
builder.add(langScroll, cst.xyw(9, r, 3));
return r + 2;
}
@Override
public boolean isValid ()
{
task.setLanguage(read(), page);
return true;
}
@Override
protected String read ()
{
return model.specOf(langList.getSelectedValuesList());
}
@Override
protected void display (String spec)
{
int[] indices = model.indicesOf(spec);
if (indices.length > 0 && indices[0] != -1) {
// Scroll to first index found?
String firstElement = model.getElementAt(indices[0]);
langList.setSelectedValue(firstElement, true);
// Flag all selected indices
langList.setSelectedIndices(indices);
}
langSpec.setText(spec);
}
@Override
protected void setEnabled (boolean bool)
{
langList.setEnabled(bool);
langSpec.setEnabled(bool);
}
@Override
public void valueChanged (ListSelectionEvent e)
{
langSpec.setText(read());
}
}
//-----------//
// PartsPane //
//-----------//
/**
* Pane to define the details for every part of the score.
* Scope can be: score.
*/
private class PartsPane
extends Pane<List<PartData>>
{
//~ Instance fields ----------------------------------------------------
/** All score part panes */
private final List<PartPanel> partPanels = new ArrayList<>();
//~ Constructors -------------------------------------------------------
public PartsPane (Score score)
{
super("Parts", score, null, null, score.getPartsParam());
}
//~ Methods ------------------------------------------------------------
@Override
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
r = super.defineLayout(builder, cst, r);
for (ScorePart scorePart : score.getPartList()) {
PartPanel partPanel = new PartPanel(scorePart);
r = partPanel.defineLayout(builder, cst, r);
partPanels.add(partPanel);
builder.add(partPanel, cst.xy(1, r));
r += 2;
}
return r;
}
@Override
public int getLogicalRowCount ()
{
return 2 + PartPanel.logicalRowCount * score.getPartList().size();
}
@Override
public boolean isValid ()
{
// Each score part
for (PartPanel partPanel : partPanels) {
if (!partPanel.checkPart()) {
return false;
}
}
return true;
}
@Override
protected void display (List<PartData> content)
{
for (int i = 0; i < content.size(); i++) {
PartPanel partPanel = partPanels.get(i);
PartData partData = content.get(i);
partPanel.display(partData);
}
}
@Override
protected List<PartData> read ()
{
List<PartData> data = new ArrayList<>();
for (PartPanel partPanel : partPanels) {
data.add(partPanel.getData());
}
return data;
}
@Override
protected void setEnabled (boolean bool)
{
for (PartPanel partPanel : partPanels) {
partPanel.setItemsEnabled(bool);
}
}
}
//----------//
// SpinData //
//----------//
/**
* A line with a labeled spinner.
*/
private class SpinData
{
protected final JLabel label;
protected final JSpinner spinner;
public SpinData (String label,
String tip,
SpinnerModel model)
{
this.label = new JLabel(label, SwingConstants.RIGHT);
spinner = new JSpinner(model);
SpinnerUtil.setRightAlignment(spinner);
SpinnerUtil.setEditable(spinner, true);
spinner.setToolTipText(tip);
}
public int defineLayout (PanelBuilder builder,
CellConstraints cst,
int r)
{
builder.add(label, cst.xyw(7, r, 3));
builder.add(spinner, cst.xyw(11, r, 1));
r += 2;
return r;
}
public void setVisible (boolean bool)
{
label.setVisible(bool);
spinner.setVisible(bool);
}
public void setEnabled (boolean bool)
{
label.setEnabled(bool);
spinner.setEnabled(bool);
}
}
}