//----------------------------------------------------------------------------//
// //
// S c o r e s M a n a g 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.score;
import omr.Main;
import omr.WellKnowns;
import omr.constant.Constant;
import omr.constant.ConstantSet;
import omr.score.ui.SheetPdfOutput;
import omr.script.ScriptActions;
import omr.util.NameSet;
import org.jdesktop.application.Application.ExitListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
/**
* Class {@code ScoresManager} is a singleton which provides
* administrative features for score instances.
* <p>It handles the collection of all loaded score instances.</p>
* <p>It handles the history of scores previously loaded.</p>
*
* @author Hervé Bitteur
* @author Brenton Partridge
*/
public class ScoresManager
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(ScoresManager.class);
/** The extension used for score output files: {@value} */
public static final String SCORE_EXTENSION = ".xml";
/** The extension used for score bench files: {@value} */
public static final String BENCH_EXTENSION = ".bench.properties";
/** The single instance of this class */
private static volatile ScoresManager INSTANCE;
//~ Instance fields --------------------------------------------------------
/** Instances of Score */
private List<Score> instances = new ArrayList<>();
/** Image file history (filled only when images are successfully loaded) */
private NameSet history;
//~ Constructors -----------------------------------------------------------
//---------------//
// ScoresManager //
//---------------//
/**
* Private constructor
*/
private ScoresManager ()
{
if (Main.getGui() != null) {
Main.getGui().addExitListener(
new ExitListener()
{
@Override
public boolean canExit (EventObject e)
{
// Are all scripts stored (or explicitly ignored)?
for (Score score : instances) {
if (!ScriptActions.checkStored(
score.getScript())) {
return false;
}
}
return true;
}
@Override
public void willExit (EventObject e)
{
// Close all sheets, to record their bench data
closeAllScores();
}
});
}
}
//~ Methods ----------------------------------------------------------------
//-------------//
// addInstance //
//-------------//
/**
* Insert this new score in the set of score instances.
*
* @param score the score to insert
*/
public synchronized void addInstance (Score score)
{
logger.debug("addInstance {}", score);
// Remove duplicate if any
for (Iterator<Score> it = instances.iterator(); it.hasNext();) {
Score s = it.next();
String path = s.getImagePath();
if (path.equals(score.getImagePath())) {
logger.debug("Removing duplicate {}", s);
it.remove();
s.close();
break;
}
}
// Insert new score instance
instances.add(score);
}
//--------//
// export //
//--------//
/**
* Export a score using the partwise structure of MusicXML to the
* default file for the provided score.
*
* @param score the score to export
*/
public void export (Score score)
{
export(score, score.getExportFile(), null);
}
//--------//
// export //
//--------//
/**
* Export a score using the partwise structure of MusicXML to the
* provided file.
*
* @param score the score to export
* @param file the xml file to write, or null
* @param injectSignature should we inject our signature?
*/
public void export (Score score,
File file,
Boolean injectSignature)
{
if (Main.getExportPath() != null) {
File path = new File(Main.getExportPath());
if (path.isDirectory()) {
file = getActualFile(file, getDefaultExportFile(path, score));
} else {
file = getActualFile(file, path);
}
} else {
file = getActualFile(file, getDefaultExportFile(null, score));
}
// Actually export the score material
try {
ScoreExporter exporter = new ScoreExporter(score);
if (injectSignature != null) {
exporter.export(file, injectSignature);
} else {
exporter.export(
file,
constants.defaultInjectSignature.getValue());
}
logger.info("Score exported to {}", file);
// Remember (even across runs) the selected directory
constants.defaultExportDirectory.setValue(file.getParent());
// Remember the file in the score itself
score.setExportFile(file);
} catch (Exception ex) {
logger.warn("Error storing score to " + file, ex);
}
}
//---------------------//
// getDefaultBenchFile //
//---------------------//
/**
* Report the file to which the bench data would be written by default.
*
* @param folder the target folder if any
* @param score the score to export
* @return the default file
*/
public File getDefaultBenchFile (File folder,
Score score)
{
String child = score.getRadix() + BENCH_EXTENSION;
if (folder != null) {
return new File(folder, child);
} else {
return new File(constants.defaultBenchDirectory.getValue(), child);
}
}
//---------------------------//
// getDefaultDewarpDirectory //
//---------------------------//
/**
* Report the directory to which dewarped images would be saved by default.
*
* @return the default file
*/
public File getDefaultDewarpDirectory ()
{
return new File(constants.defaultDewarpDirectory.getValue());
}
//-------------//
// getInstance //
//-------------//
/**
* Report the single instance of this class.
*
* @return the single instance
*/
public static ScoresManager getInstance ()
{
if (INSTANCE == null) {
INSTANCE = new ScoresManager();
}
return INSTANCE;
}
//--------------//
// isMultiScore //
//--------------//
/**
* Report whether we are handling more than one score.
*
* @return true if more than one score
*/
public static boolean isMultiScore ()
{
return getInstance().instances.size() > 1;
}
//----------------------//
// getDefaultExportFile //
//----------------------//
/**
* Report the file to which the score would be written by default.
*
* @param folder the target folder if any
* @param score the score to export
* @return the default file
*/
public File getDefaultExportFile (File folder,
Score score)
{
if (score.getExportFile() != null) {
return score.getExportFile();
}
String child = score.getRadix() + SCORE_EXTENSION;
if (folder != null) {
return new File(folder, child);
} else {
return new File(constants.defaultExportDirectory.getValue(), child);
}
}
//--------------------------//
// getDefaultInputDirectory //
//--------------------------//
/**
* Report the directory where images should be found.
*
* @return the latest image directory
*/
public String getDefaultInputDirectory ()
{
return constants.defaultInputDirectory.getValue();
}
//---------------------//
// getDefaultPrintFile //
//---------------------//
/**
* Report the file to which the sheet PDF data would be written by default.
*
* @param folder the target folder if any
* @param score the score to export
* @return the default file
*/
public File getDefaultPrintFile (File folder,
Score score)
{
if (score.getPrintFile() != null) {
return score.getPrintFile();
}
String child = score.getRadix() + ".sheet.pdf";
if (folder != null) {
return new File(folder, child);
} else {
return new File(constants.defaultPrintDirectory.getValue(), child);
}
}
//------------//
// getHistory //
//------------//
/**
* Get access to the list of previously handled images.
*
* @return the history set of image files
*/
public NameSet getHistory ()
{
if (history == null) {
history = new NameSet(
"Images History",
constants.imagesHistory,
constants.historySize.getValue());
}
return history;
}
// //-----------//
// // midiClose //
// //-----------//
// /**
// * Cut any relationship between the provided score and the Midi
// * interface (MidiAgent & MidiReceiver) if any.
// * @param score the score being closed
// */
// public void midiClose (Score score)
// {
// try {
// if (MidiAgentFactory.hasAgent()) {
// MidiAgent agent = MidiAgentFactory.getAgent();
//
// if (agent.getScore() == score) {
// agent.setScore(null);
// }
// }
// } catch (Exception ex) {
// logger.warn("Error closing Midi interface ", ex);
// }
// }
//
// //-----------//
// // midiWrite //
// //-----------//
// /**
// * Write the Midi sequence of the score into the provided midi file.
// * @param score the provided score
// * @param file the Midi file to write
// * @throws Exception if the writing goes wrong
// */
// public void midiWrite (Score score,
// File file)
// throws Exception
// {
// if (!ScoreActions.checkParameters(score)) {
// return;
// }
//
// if (Main.getMidiPath() != null) {
// File path = new File(Main.getMidiPath());
//
// if (path.isDirectory()) {
// file = getActualFile(file, getDefaultMidiFile(path, score));
// } else {
// file = getActualFile(file, path);
// }
// } else {
// file = getActualFile(file, getDefaultMidiFile(null, score));
// }
//
// // Actually write the Midi file
// try {
// MidiAgent agent = MidiAgentFactory.getAgent();
//
// if (agent.getScore() != score) {
// agent.setScore(score);
// }
//
// agent.write(file);
// score.setMidiFile(file);
// logger.info("Midi written to " + file);
//
// // Remember (even across runs) the selected directory
// constants.defaultMidiDirectory.setValue(file.getParent());
// } catch (Exception ex) {
// logger.warn("Cannot write Midi to " + file, ex);
// throw ex;
// }
// }
//----------------//
// removeInstance //
//----------------//
/**
* Remove the provided score from the collection of instances.
*
* @param score the score to remove
*/
public synchronized void removeInstance (Score score)
{
logger.debug("removeInstance {}", score);
instances.remove(score);
}
//--------------------------//
// setDefaultInputDirectory //
//--------------------------//
/**
* Remember the directory where images should be found.
*
* @param directory the latest image directory
*/
public void setDefaultInputDirectory (String directory)
{
constants.defaultInputDirectory.setValue(directory);
}
//------------//
// storeBench //
//------------//
/**
* Store the sheet bench.
*
* @param bench the bench to write to disk
* @param file the written file, or null
* @param complete true if we need to complete the bench data
*/
public void storeBench (ScoreBench bench,
File file,
boolean complete)
{
// Check if we do save bench data
if ((Main.getBenchPath() == null)
&& !constants.saveBenchToDisk.getValue()) {
return;
}
if (Main.getBenchPath() != null) {
File path = new File(Main.getBenchPath());
if (path.isDirectory()) {
file = getActualFile(
file,
getDefaultBenchFile(path, bench.getScore()));
} else {
file = getActualFile(file, path);
}
} else {
file = getActualFile(
file,
getDefaultBenchFile(null, bench.getScore()));
}
// Actually store the score bench
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
bench.store(fos, complete);
if (complete) {
logger.info("Complete score bench stored as {}", file);
}
// Remember (even across runs) the selected directory
constants.defaultBenchDirectory.setValue(file.getParent());
} catch (Exception ex) {
logger.warn("Error storing score bench to " + file, ex);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException ignored) {
}
}
}
}
//------------------//
// writePhysicalPdf //
//------------------//
/**
* Print the score physical appearance into the provided PDF file.
*
* @param score the provided score
* @param file the PDF file to write
*/
public void writePhysicalPdf (Score score,
File file)
{
if (Main.getPrintPath() != null) {
File path = new File(Main.getPrintPath());
if (path.isDirectory()) {
file = getActualFile(file, getDefaultPrintFile(path, score));
} else {
file = getActualFile(file, path);
}
} else {
file = getActualFile(file, getDefaultPrintFile(null, score));
}
// Actually write the PDF file
try {
new SheetPdfOutput(score, file).write();
score.setPrintFile(file);
logger.info("Score printed to {}", file);
// Remember (even across runs) the selected directory
constants.defaultPrintDirectory.setValue(file.getParent());
} catch (Exception ex) {
logger.warn("Cannot write PDF to " + file, ex);
}
}
//----------------//
// closeAllScores //
//----------------//
/**
* Close all score instances.
*/
private void closeAllScores ()
{
int count = 0;
// NB: Use a COPY of instances, to avoid concurrent modification
for (Score score : new ArrayList<>(instances)) {
score.close();
count++;
}
logger.debug("{} score(s) closed", count);
}
//---------------//
// getActualFile //
//---------------//
/**
* Report the actual file to be used as target, using the provided
* target file if any, otherwise the score default, and making sure
* the file parent folder really exists.
*
* @param targetFile the provided target candidate, or null
* @param defaultFile the default target
* @return the file to use
*/
private File getActualFile (File targetFile,
File defaultFile)
{
try {
if (targetFile == null) {
targetFile = defaultFile;
}
File canon = new File(targetFile.getCanonicalPath());
// Make sure the folder exists
File folder = new File(canon.getParent());
if (folder.mkdirs()) {
logger.info("Creating folder {}", folder);
}
return canon;
} catch (IOException ex) {
logger.warn("Cannot getCanonicalPath for " + targetFile, ex);
return null;
}
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Constant.String defaultExportDirectory = new Constant.String(
WellKnowns.DEFAULT_SCORES_FOLDER.toString(),
"Default directory for saved scores");
Constant.Boolean saveBenchToDisk = new Constant.Boolean(
false,
"Should we save bench data to disk");
Constant.String defaultBenchDirectory = new Constant.String(
WellKnowns.DEFAULT_BENCHES_FOLDER.toString(),
"Default directory for saved benches");
Constant.String defaultPrintDirectory = new Constant.String(
WellKnowns.DEFAULT_PRINT_FOLDER.toString(),
"Default directory for printing sheet files");
Constant.Boolean defaultInjectSignature = new Constant.Boolean(
true,
"Should we inject our signature in the exported scores?");
Constant.String imagesHistory = new Constant.String(
"",
"History of loaded images");
Constant.Integer historySize = new Constant.Integer(
"count",
10,
"Maximum number of files names kept in history");
Constant.String defaultInputDirectory = new Constant.String(
WellKnowns.EXAMPLES_FOLDER.toString(),
"Default directory for selection of image files");
Constant.String defaultDewarpDirectory = new Constant.String(
WellKnowns.TEMP_FOLDER.toString(),
"Default directory for saved dewarped images");
}
}