//------------------------------------------------------------------------------------------------//
// //
// B o o k A c t i o n s //
// //
//------------------------------------------------------------------------------------------------//
//
//
// Copyright © NoteLite 2026. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify it under the terms of the
// GNU Affero General Public License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License along with this
// program. If not, see .
//------------------------------------------------------------------------------------------------//
//
package com.notelite.omr.sheet.ui;
import com.notelite.omr.Main;
import com.notelite.omr.OMR;
import com.notelite.omr.classifier.SampleRepository;
import com.notelite.omr.classifier.ui.SampleBrowser;
import com.notelite.omr.constant.Constant;
import com.notelite.omr.constant.ConstantSet;
import com.notelite.omr.log.LogUtil;
import com.notelite.omr.plugin.Plugin;
import com.notelite.omr.plugin.PluginsManager;
import com.notelite.omr.practice.PianoBooster;
import com.notelite.omr.practice.PracticeStudio;
import com.notelite.omr.score.MidiAbstractions;
import com.notelite.omr.score.MidiExporter;
import com.notelite.omr.score.PageRef;
import com.notelite.omr.score.Score;
import com.notelite.omr.score.ScoreReduction;
import com.notelite.omr.score.ui.BookParameters;
import com.notelite.omr.score.ui.LogicalPartsEditor;
import com.notelite.omr.score.ui.SheetScaling;
import com.notelite.omr.sheet.Book;
import com.notelite.omr.sheet.BookManager;
import com.notelite.omr.sheet.ExportPattern;
import com.notelite.omr.sheet.ScaleBuilder;
import com.notelite.omr.sheet.Sheet;
import com.notelite.omr.sheet.SheetStub;
import com.notelite.omr.sheet.Staff;
import com.notelite.omr.sheet.StaffManager;
import com.notelite.omr.sheet.grid.StaffProjector;
import com.notelite.omr.sheet.stem.StemScaler;
import com.notelite.omr.sig.ui.InterController;
import com.notelite.omr.step.OmrStep;
import com.notelite.omr.step.StepPause;
import com.notelite.omr.ui.BoardsPane;
import com.notelite.omr.ui.OmrGui;
import com.notelite.omr.ui.ViewParameters;
import static com.notelite.omr.ui.action.AdvancedTopics.swapProcessedSheets;
import com.notelite.omr.ui.util.OmrFileFilter;
import com.notelite.omr.ui.util.UIUtil;
import com.notelite.omr.ui.util.UserOpt;
import com.notelite.omr.ui.util.WaitingTask;
import com.notelite.omr.ui.util.WebBrowser;
import com.notelite.omr.ui.view.HistoryMenu;
import com.notelite.omr.ui.view.ScrollView;
import com.notelite.omr.util.FileUtil;
import com.notelite.omr.util.PathListTask;
import com.notelite.omr.util.SheetPath;
import com.notelite.omr.util.SheetPathHistory;
import com.notelite.omr.util.VoidTask;
import com.notelite.omr.util.WrappedBoolean;
import com.notelite.omr.util.param.Param;
import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.ApplicationAction;
import org.jdesktop.application.ApplicationActionMap;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.text.MessageFormat.format;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
/**
* Class BookActions gathers all UI actions related to current book.
*
* Swing EDT processes Runnable instances found in its event queue.
* Via {@link SwingUtilities} new Runnable instances can further be appended to the event queue.
* Unless log context is explicitly started and stopped in such Runnable, there is no reliable way
* to set log context for the EDT.
*
* By definition, all actions defined in this class are initiated on Swing EDT.
* Hence, if log context handling is wanted, the action must delegate processing to a separate task
* (log context can easily be handled in a non-EDT thread).
*
* @author NoteLite Contributors
*/
public class BookActions
extends StubDependent
{
//~ Static fields/initializers -----------------------------------------------------------------
private static final Constants constants = new Constants();
private static final Logger logger = LoggerFactory.getLogger(BookActions.class);
private static final ResourceMap resources = Application.getInstance().getContext()
.getResourceMap(BookActions.class);
private static final String doYouConfirm = "\n" + resources.getString("doYouConfirm");
/** Default parameter. */
public static final Param defaultPromptOnClosingUnsaved = new PromptOnClosingUnsaved();
//~ Instance fields ----------------------------------------------------------------------------
/** Sub-menu on images history. */
private final HistoryMenu imageHistoryMenu;
/** Sub-menu on books history. */
private final HistoryMenu bookHistoryMenu;
/** The action that toggles repetitive input mode. */
private final ApplicationAction repetitiveInputAction;
/** The action that toggles sheet validity. */
private final ApplicationAction toggleValidityAction;
//~ Constructors -------------------------------------------------------------------------------
/**
* Creates a new BookActions object.
*/
private BookActions ()
{
final BookManager mgr = BookManager.getInstance();
imageHistoryMenu = new HistoryMenu(mgr.getImageHistory(), LoadImageTask.class);
bookHistoryMenu = new HistoryMenu(mgr.getBookHistory(), LoadBookTask.class);
ApplicationActionMap actionMap = OmrGui.getApplication().getContext().getActionMap(this);
repetitiveInputAction = (ApplicationAction) actionMap.get("toggleRepetitiveInput");
toggleValidityAction = (ApplicationAction) actionMap.get("toggleSheetValidity");
updateSheetValidity(null);
}
//~ Methods ------------------------------------------------------------------------------------
/** Practice the current score with live instrument input in the local practice studio. */
@Action(enabledProperty = BOOK_IDLE)
public void openPracticeStudio (ActionEvent e)
{
PracticeStudio.open(StubsController.getCurrentBook());
}
/** Export the selected movement and open the mature MIDI-keyboard practice application. */
@Action(enabledProperty = BOOK_IDLE)
public void openPianoBooster (ActionEvent e)
{
PianoBooster.open(StubsController.getCurrentBook());
}
@Action
public void configurePianoBooster (ActionEvent e)
{
PianoBooster.configure();
}
/** Import a reviewed MusicXML result from another recognizer without opening a book first. */
@Action
public void importPracticeScore (ActionEvent e)
{
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle(resources.getString("importPracticeScore.Action.text"));
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setFileFilter(new OmrFileFilter("MusicXML (*.xml, *.musicxml, *.mxl)",
new String[] { ".xml", ".musicxml", ".mxl" }));
if (chooser.showOpenDialog(OMR.gui.getFrame()) == JFileChooser.APPROVE_OPTION) {
PracticeStudio.openImported(chooser.getSelectedFile().toPath());
}
}
@Action
public void importPianoBoosterMidi (ActionEvent e)
{
PianoBooster.importMidi();
}
/** Explain the supported file interchange; external products remain independently licensed. */
@Action
public void openExternalScoreTools (ActionEvent e)
{
Object[] choices = { resources.getString("externalScores.import"),
resources.getString("externalScores.scanScoreHelp"),
resources.getString("externalScores.close") };
int choice = JOptionPane.showOptionDialog(OMR.gui.getFrame(),
resources.getString("externalScores.message"),
resources.getString("openExternalScoreTools.Action.text"),
JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, choices, choices[0]);
if (choice == 0) {
importPracticeScore(e);
} else if (choice == 1) {
WebBrowser.getBrowser().launch(URI.create(
"https://scanscore.zendesk.com/hc/en-us/articles/16769352622610-Export"));
}
}
//--------------//
// annotateBook //
//--------------//
@Action(enabledProperty = BOOK_IDLE)
public Task annotateBook (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
return new VoidTask()
{
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
book.annotate(book.getValidSelectedStubs());
} catch (Throwable ex) {
logger.warn("Error in annotateBook {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
};
}
//---------------//
// annotateSheet //
//---------------//
@Action(enabledProperty = STUB_IDLE)
public Task annotateSheet (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
final Sheet sheet = stub.getSheet();
return new VoidTask()
{
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
sheet.annotate();
} catch (Throwable ex) {
logger.warn("Error in annotateSheet {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
};
}
//-------------//
// bookHistory //
//-------------//
@Action
public void bookHistory (ActionEvent e)
{
logger.info("bookHistory");
}
//------------//
// browseBook //
//------------//
/**
* Launch the tree display of the current book.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void browseBook (ActionEvent e)
{
OmrGui.getApplication().show(StubsController.getCurrentBook().getBrowserFrame());
}
//-------------------//
// browseBookSamples //
//-------------------//
/**
* Action to browse the separate sample repository of the current book.
*
* @param e the event that triggered this action
* @return the UI task to perform
*/
@Action(enabledProperty = STUB_AVAILABLE)
public Task browseBookSamples (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if (book != null) {
if (book.hasSpecificRepository()) {
return new SampleBrowser.Waiter(resources.getString("launchingBookSampleBrowser"))
{
@Override
protected SampleBrowser doInBackground ()
throws Exception
{
return SampleBrowser.getInstance(book);
}
};
} else {
logger.info(format(resources.getString("noBookRepo.pattern"), book.getRadix()));
}
}
return null;
}
//-----------------//
// choosePrintPath //
//-----------------//
private Path choosePrintPath (Book book,
String preExt)
{
final String ext = preExt + OMR.PRINT_EXTENSION;
Path defaultBookPath = BookManager.getDefaultPrintPath(book);
Path bookSansExt = FileUtil.avoidExtensions(defaultBookPath, OMR.PRINT_EXTENSION);
if (!preExt.isEmpty()) {
bookSansExt = FileUtil.avoidExtensions(bookSansExt, preExt);
}
defaultBookPath = Paths.get(bookSansExt + ext);
return UIUtil.pathChooser(
true,
OMR.gui.getFrame(),
defaultBookPath,
filter(ext),
resources.getString("chooseBookPrint"));
}
//-----------------//
// choosePrintPath //
//-----------------//
private Path choosePrintPath (SheetStub stub,
String preExt)
{
final Book book = stub.getBook();
final String ext = preExt + OMR.PRINT_EXTENSION;
final Path defaultBookPath = BookManager.getDefaultPrintPath(book);
final Path bookSansExt = FileUtil.avoidExtensions(defaultBookPath, OMR.PRINT_EXTENSION);
final String sheetSuffix = book.isMultiSheet() ? (OMR.SHEET_SUFFIX + stub.getNumber()) : "";
final Path defaultSheetPath = Paths.get(bookSansExt + sheetSuffix + ext);
return UIUtil.pathChooser(
true,
OMR.gui.getFrame(),
defaultSheetPath,
filter(ext),
resources.getString("chooseSheetPrint"));
}
//-----------//
// closeBook //
//-----------//
/**
* Action that handles the closing of the current book.
*
* @param e the event that triggered this action
* @return the task which will close the book
*/
@Action(enabledProperty = STUB_AVAILABLE)
public Task closeBook (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub != null) {
final Book book = stub.getBook();
if (checkStored(book)) {
// Pre-select the suitable "next" book tab, if any.
StubsController.getInstance().selectOtherBook(book);
// Now close the book (+ related tab)
return new CloseBookTask(stub);
}
}
return null;
}
//------------------//
// defineParameters //
//------------------//
/**
* Launch the dialog to set up book parameters.
*
* @param e the event that triggered this action
*/
@Action
public void defineParameters (ActionEvent e)
{
applyUserSettings(StubsController.getCurrentStub());
}
//--------------------//
// defineSheetScaling //
//--------------------//
/**
* Launch the dialog to check and modify sheet scaling data.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void defineSheetScaling (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
try {
final WrappedBoolean apply = new WrappedBoolean(false);
final SheetScaling sheetScaling = new SheetScaling(stub.getSheet());
final JOptionPane optionPane = new JOptionPane(
sheetScaling.getComponent(),
JOptionPane.QUESTION_MESSAGE,
JOptionPane.OK_CANCEL_OPTION);
final String frameTitle = sheetScaling.getTitle();
final JDialog dialog = new JDialog(OMR.gui.getFrame(), frameTitle, true); // Modal flag
dialog.setContentPane(optionPane);
dialog.setName("SheetScalingDialog"); // For SAF life cycle
optionPane.addPropertyChangeListener( (PropertyChangeEvent e1) -> {
String prop = e1.getPropertyName();
if (dialog.isVisible() && (e1.getSource() == optionPane) && (prop.equals(
JOptionPane.VALUE_PROPERTY))) {
Object obj = optionPane.getValue();
int value = (Integer) obj;
apply.set(value == JOptionPane.OK_OPTION);
// Exit only if user gives up or enters correct data
if (!apply.isSet() || sheetScaling.commit()) {
dialog.setVisible(false);
dialog.dispose();
} else {
// Incorrect data, so don't exit yet
try {
// TODO: Is there a more civilized way?
optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
} catch (Exception ignored) {}
}
}
});
dialog.pack();
OmrGui.getApplication().show(dialog);
} catch (Throwable ex) {
logger.warn("Error in defineSheetScaling {}", ex.toString(), ex);
}
}
//---------------//
// displayBinary //
//---------------//
/**
* Action that allows to display the view on binary image
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = BINARY_AVAILABLE)
public void displayBinary (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
try {
if (stub.isDone(OmrStep.BINARY)) {
final SheetAssembly assembly = stub.getAssembly();
final SheetTab tab = SheetTab.BINARY_TAB;
if (assembly.getView(tab.label) == null) {
stub.getSheet().createBinaryView();
} else {
assembly.selectViewTab(tab);
}
} else {
logger.info(resources.getString("noBinaryImage"));
}
} catch (Throwable ex) {
logger.warn("Error in displayBinary {}", ex.toString(), ex);
}
}
//-------------//
// displayData //
//-------------//
/**
* Action that allows to display the view on data.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = GRID_AVAILABLE)
public void displayData (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
try {
if (stub.isDone(OmrStep.GRID)) {
final SheetAssembly assembly = stub.getAssembly();
final SheetTab tab = SheetTab.DATA_TAB;
if (assembly.getView(tab.label) == null) {
stub.getSheet().displayDataTab();
} else {
assembly.selectViewTab(tab);
}
} else {
logger.info(resources.getString("noDataTab"));
}
} catch (Throwable ex) {
logger.warn("Error in displayData {}", ex.toString(), ex);
}
}
//-------------//
// displayGray //
//-------------//
/**
* Action that allows to display the view on initial gray image.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = GRAY_AVAILABLE)
public void displayGray (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
final SheetAssembly assembly = stub.getAssembly();
final SheetTab tab = SheetTab.GRAY_TAB;
if (assembly.getView(tab.label) == null) {
stub.getSheet().createGrayView();
} else {
assembly.selectViewTab(tab);
}
}
//----------------//
// displayNoStaff //
//----------------//
/**
* Action that allows to display the view on no-staff buffer
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = GRID_AVAILABLE)
public void displayNoStaff (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub.isDone(OmrStep.GRID)) {
final SheetAssembly assembly = stub.getAssembly();
final SheetTab tab = SheetTab.NO_STAFF_TAB;
if (assembly.getView(tab.label) == null) {
Sheet sheet = stub.getSheet(); // This may load the sheet...
assembly.addViewTab(
tab,
new ScrollImageView(sheet, new NoStaffView(sheet)),
new BoardsPane(new PixelBoard(sheet)));
} else {
assembly.selectViewTab(tab);
}
} else {
logger.info(resources.getString("noStaffLines"));
}
}
//------------------------//
// displayStaffLineGlyphs //
//------------------------//
/**
* Action that allows to display the view on staff underlying glyphs.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = GRID_AVAILABLE)
public void displayStaffLineGlyphs (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub.isDone(OmrStep.GRID)) {
final SheetAssembly assembly = stub.getAssembly();
final SheetTab tab = SheetTab.STAFF_LINE_TAB;
if (assembly.getView(tab.label) == null) {
Sheet sheet = stub.getSheet(); // This may load the sheet...
assembly.addViewTab(
tab,
new ScrollImageView(
sheet,
new ImageView(sheet.getPicture().buildStaffLineGlyphsImage())),
new BoardsPane(new PixelBoard(sheet)));
} else {
assembly.selectViewTab(tab);
}
} else {
logger.info(resources.getString("noStaffLines"));
}
}
//----------//
// dumpBook //
//----------//
/**
* Dump the internals of a book to system output.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void dumpBook (ActionEvent e)
{
logger.error("dumpBook() is not yet implemented.");
///BookController.getCurrentBook().dump();
}
//-------------------//
// dumpEventServices //
//-------------------//
/**
* Action to dump the content of all event services
*
* @param e the event which triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void dumpEventServices (ActionEvent e)
{
StubsController.getInstance().dumpCurrentSheetServices();
}
//------------//
// exportBook //
//------------//
/**
* Export the current book using MusicXML format
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_IDLE)
public Task exportBook (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
final Path exportPathSansExt = BookManager.getDefaultExportPathSansExt(book);
if (exportPathSansExt != null) {
//TODO: check/prompt for overwrite??? (perhaps several files)
return new ExportBookTask(book, exportPathSansExt);
} else {
return exportBookAs(e);
}
}
//--------------//
// exportBookAs //
//--------------//
/**
* Export the current book, in either MusicXML or MIDI format, to a
* user-chosen location. The format is determined by the file type
* dropdown (and the resulting extension) in the save dialog.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_IDLE)
public Task exportBookAs (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
final String ext = BookManager.getExportExtension();
final Path sansExt = BookManager.getDefaultExportPathSansExt(book);
final Path defaultPath = Paths.get(sansExt + ext);
final ExportTarget target = chooseExportTarget(
defaultPath, resources.getString("chooseBookExport"));
if ((target == null) || !isTargetConfirmed(target.path)) {
return null;
}
if (target.format == ExportFormat.MIDI) {
final Path bookPathSansMidiExt = stripExtension(
target.path, MidiAbstractions.MIDI_EXTENSION);
return new ExportBookMidiTask(book, bookPathSansMidiExt);
}
// Remove extensions if any (.opus.mxl, .mxl, .xml, .mvt#.mxl, .mvt#.xml)
final Path bookPathSansExt = ExportPattern.getPathSansExt(target.path);
return new ExportBookTask(book, bookPathSansExt);
}
//---------------//
// exportSheetAs //
//---------------//
/**
* Export the currently selected sheet in either MusicXML or MIDI format,
* to a user-chosen location. The format is picked from the file type
* dropdown in the save dialog.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = STUB_IDLE)
public Task exportSheetAs (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null; // Not likely to happen, but safer
}
final Book book = stub.getBook();
final Path bookSansExt = BookManager.getDefaultExportPathSansExt(book);
final boolean compressed = BookManager.useCompression();
final String ext = compressed ? OMR.COMPRESSED_SCORE_EXTENSION : OMR.SCORE_EXTENSION;
final String suffix = book.isMultiSheet() ? (OMR.SHEET_SUFFIX + stub.getNumber()) : "";
final Path defaultSheetPath = Paths.get(bookSansExt + suffix + ext);
final ExportTarget target = chooseExportTarget(
defaultSheetPath, resources.getString("chooseSheetExport"));
if ((target == null) || !isTargetConfirmed(target.path)) {
return null;
}
if (target.format == ExportFormat.MIDI) {
return new ExportSheetMidiTask(stub.getSheet(), target.path);
}
return new ExportSheetTask(stub.getSheet(), target.path);
}
//--------------------//
// chooseExportTarget //
//--------------------//
/**
* Prompt the user with a save dialog that offers a file-type dropdown for
* MusicXML and MIDI. Switching the dropdown live-rewrites the extension on
* the entered file name. The format is finally determined by the picked
* extension.
*
* @param defaultPath pre-filled path including default extension
* @param dialogTitle localized title
* @return chosen target with format, or null when the user cancels
*/
private static ExportTarget chooseExportTarget (Path defaultPath, String dialogTitle)
{
final String midiExt = MidiAbstractions.MIDI_EXTENSION;
final String mxlExt = OMR.COMPRESSED_SCORE_EXTENSION;
final String xmlExt = OMR.SCORE_EXTENSION;
final FileFilter mxlFilter = new OmrFileFilter(
resources.getString("filterMusicXml.description"),
new String[] { mxlExt, xmlExt });
final FileFilter midiFilter = new OmrFileFilter(
resources.getString("filterMidi.description"),
new String[] { midiExt });
final JFileChooser fc = new JFileChooser();
fc.setDialogTitle(dialogTitle);
fc.setAcceptAllFileFilterUsed(false);
fc.addChoosableFileFilter(mxlFilter);
fc.addChoosableFileFilter(midiFilter);
final File defaultFile = defaultPath.toFile();
if (defaultFile.getParentFile() != null) {
fc.setCurrentDirectory(defaultFile.getParentFile());
}
fc.setSelectedFile(defaultFile);
final String defaultName = defaultFile.getName().toLowerCase();
fc.setFileFilter(defaultName.endsWith(midiExt) ? midiFilter : mxlFilter);
// Live-rewrite the typed file name when the user switches file type.
fc.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, evt -> {
final File current = fc.getSelectedFile();
if (current == null) {
return;
}
final FileFilter newFilter = (FileFilter) evt.getNewValue();
final String newExt = (newFilter == midiFilter) ? midiExt
: (BookManager.useCompression() ? mxlExt : xmlExt);
final String name = current.getName();
final int dot = name.lastIndexOf('.');
final String stem = (dot > 0) ? name.substring(0, dot) : name;
fc.setSelectedFile(new File(current.getParentFile(), stem + newExt));
});
final int result = fc.showSaveDialog(OMR.gui.getFrame());
if (result != JFileChooser.APPROVE_OPTION) {
return null;
}
final File selected = fc.getSelectedFile();
if (selected == null) {
return null;
}
final String selectedName = selected.getName().toLowerCase();
Path resolved = selected.toPath();
ExportFormat format;
if (selectedName.endsWith(midiExt)) {
format = ExportFormat.MIDI;
} else if (selectedName.endsWith(mxlExt) || selectedName.endsWith(xmlExt)) {
format = ExportFormat.MUSIC_XML;
} else {
// No (or unknown) extension: trust the active filter and append.
final FileFilter active = fc.getFileFilter();
if (active == midiFilter) {
format = ExportFormat.MIDI;
resolved = resolved.resolveSibling(selected.getName() + midiExt);
} else {
format = ExportFormat.MUSIC_XML;
final String fallback = BookManager.useCompression() ? mxlExt : xmlExt;
resolved = resolved.resolveSibling(selected.getName() + fallback);
}
}
return new ExportTarget(resolved, format);
}
//----------------//
// stripExtension //
//----------------//
private static Path stripExtension (Path path, String ext)
{
final String name = path.getFileName().toString();
if (name.toLowerCase().endsWith(ext)) {
return path.resolveSibling(name.substring(0, name.length() - ext.length()));
}
return path;
}
//--------------------//
// getBookHistoryMenu //
//--------------------//
public HistoryMenu getBookHistoryMenu ()
{
return bookHistoryMenu;
}
//---------------------//
// getImageHistoryMenu //
//---------------------//
public HistoryMenu getImageHistoryMenu ()
{
return imageHistoryMenu;
}
//------------------------//
// hasValidSelectedSheets //
//------------------------//
private boolean hasValidSelectedSheets (Book book)
{
final List stubs = book.getValidSelectedStubs();
if (stubs.isEmpty()) {
logger.warn("No valid selected sheets in {}", book);
return false;
} else {
return true;
}
}
//---------------------//
// invokeDefaultPlugin //
//---------------------//
/**
* Action to invoke the default score external editor
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = STUB_IDLE)
public Task invokeDefaultPlugin (ActionEvent e)
{
Plugin defaultPlugin = PluginsManager.getInstance().getDefaultPlugin();
if (defaultPlugin == null) {
logger.warn("No default plugin defined");
return null;
}
// Current score export file
final Book book = StubsController.getCurrentBook();
if (book == null) {
return null;
} else {
return defaultPlugin.getTask(book);
}
}
//-----------//
// openBooks //
//-----------//
/**
* Action that let the user select one or several books.
*
* @param e the event that triggered this action
* @return the asynchronous task, or null
*/
@Action
public LoadFilesTask openBooks (ActionEvent e)
{
final Path[] paths = selectBookPaths(false, Paths.get(BookManager.getDefaultBookFolder()));
if (paths.length > 0) {
return new LoadFilesTask(Arrays.asList(paths));
}
return null;
}
//----------------//
// openImageFiles //
//----------------//
/**
* Action that let the user select one or several image files interactively.
*
* @param e the event that triggered this action
* @return the asynchronous task, or null
*/
@Action
public LoadFilesTask openImageFiles (ActionEvent e)
{
final Path[] paths = selectImagePaths();
if (paths.length > 0) {
return new LoadFilesTask(Arrays.asList(paths));
}
return null;
}
//----------------//
// openRecentBook //
//----------------//
/**
* Action that let the user open the book most recently closed.
*
* @param e the event that triggered this action
* @return the asynchronous task, or null
*/
@Action
public LoadBookTask openRecentBook (ActionEvent e)
{
final SheetPathHistory bookHistory = BookManager.getInstance().getBookHistory();
while (!bookHistory.isEmpty()) {
final SheetPath sheetPath = bookHistory.getFirst();
if (sheetPath != null) {
final Path path = sheetPath.getBookPath();
if (Files.exists(path)) {
return new LoadBookTask(sheetPath);
} else {
logger.warn(format(resources.getString("fileNotFound.pattern"), path));
bookHistory.remove(sheetPath);
}
}
}
return null;
}
//-----------//
// plotScale //
//-----------//
/**
* Action that allows to display the plot of Scale Builder.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void plotScale (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub != null) {
if (stub.isDone(OmrStep.BINARY)) {
new ScaleBuilder(stub.getSheet()).displayChart();
} else {
logger.info(resources.getString("noScaleData"));
}
}
}
//------------//
// plotStaves //
//------------//
/**
* Action that allows to display the horizontal projection of a selected staff.
*
* We need a sub-menu to select proper staff.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void plotStaves (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
final Sheet sheet = stub.getSheet();
final StaffManager staffManager = sheet.getStaffManager();
if (staffManager.getStaffCount() == 0) {
logger.info(resources.getString("noStaffData"));
return;
}
final JPopupMenu popup = new JPopupMenu("Staves IDs");
// Menu title
final JMenuItem title = new JMenuItem("Select staff ID:");
title.setHorizontalAlignment(SwingConstants.CENTER);
title.setEnabled(false);
popup.add(title);
popup.addSeparator();
final ActionListener listener = (ActionEvent e1) -> {
final int index = Integer.decode(e1.getActionCommand()) - 1;
final Staff staff = staffManager.getStaff(index);
new StaffProjector(sheet, staff, null).plot();
};
// Populate popup
for (Staff staff : staffManager.getStaves()) {
final JMenuItem item = new JMenuItem("" + staff.getId());
item.addActionListener(listener);
popup.add(item);
}
// Display popup menu
final JFrame frame = OMR.gui.getFrame();
popup.show(frame, frame.getWidth() / 6, frame.getHeight() / 4);
}
//----------//
// plotStem //
//----------//
/**
* Action that allows to display the plot of stem scaler.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void plotStem (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
if (stub.isDone(OmrStep.STEM_SEEDS)) {
new StemScaler(stub.getSheet()).displayChart();
} else {
logger.info(resources.getString("noStemData"));
}
}
//-----------//
// printBook //
//-----------//
/**
* Print the current book, as a PDF file
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_IDLE)
public Task printBook (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
final Path bookPrintPath = BookManager.getDefaultPrintPath(book);
if ((bookPrintPath != null) && isTargetConfirmed(bookPrintPath)) {
return new PrintBookTask(book, bookPrintPath);
}
return printBookAs(e);
}
//-------------//
// printBookAs //
//-------------//
/**
* Write the current book, using PDF format, to a user-provided file.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_IDLE)
public Task printBookAs (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
// Select target book print path
final Path bookPrintPath = choosePrintPath(book, "");
if ((bookPrintPath == null) || !isTargetConfirmed(bookPrintPath)) {
return null;
}
return new PrintBookTask(book, bookPrintPath);
}
//--------------//
// printSheetAs //
//--------------//
/**
* Write the currently selected sheet, using PDF format, to a user-provided location.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = STUB_IDLE)
public Task printSheetAs (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
// Let the user select a PDF output file
final Path sheetPrintPath = choosePrintPath(stub, "");
if ((sheetPrintPath == null) || !isTargetConfirmed(sheetPrintPath)) {
return null;
}
return new PrintSheetTask(stub.getSheet(), sheetPrintPath);
}
//-----------------//
// printSheetWatch //
//-----------------//
/**
* Print out the watch on sheet processing.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_IDLE)
public void printSheetWatch (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub != null) {
stub.printWatch(true);
}
}
//---------------//
// rebuildScores //
//---------------//
/**
* Action to rebuild book score(s) from scratch.
*/
@Action(enabledProperty = BOOK_IDLE)
public void rebuildScores ()
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return;
}
final String msg = format(resources.getString("rebuildScores.pattern"), book.getRadix());
if (!OMR.gui.displayConfirmation(msg + doYouConfirm)) {
return;
}
book.clearScores();
book.updateScores(null);
}
//------//
// redo //
//------//
/**
* Action to redo undone user modification.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = REDOABLE)
public void redo (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
Sheet sheet = stub.getSheet();
InterController controller = sheet.getInterController();
controller.redo();
}
//-----------//
// resetBook //
//-----------//
private Task resetBook (OmrStep step)
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return null;
}
final String msg = format(
resources.getString("resetBookToEnd.pattern"),
book.getRadix(),
step);
if (!OMR.gui.displayConfirmation(msg + doYouConfirm)) {
return null;
}
return new ResetBookTask(book, step);
}
//-------------------//
// resetBookToBinary //
//-------------------//
/**
* Action that resets to BINARY all (valid selected) sheets of current book.
*
* @param e the event that triggered this action
* @return the background task
*/
@Action(enabledProperty = BOOK_IDLE)
public Task resetBookToBinary (ActionEvent e)
{
return resetBook(OmrStep.BINARY);
}
//-----------------//
// resetBookToGray //
//-----------------//
/**
* Action that resets all (valid selected) sheets of current book to gray.
*
* @param e the event that triggered this action
* @return the background task
*/
@Action(enabledProperty = BOOK_IDLE)
public Task resetBookToGray (ActionEvent e)
{
// Check book input path still exists
final Book book = StubsController.getCurrentBook();
if (book != null) {
final Path inputPath = book.getInputPath(); // Null for a compound book
if ((inputPath != null) && !Files.exists(inputPath)) {
OMR.gui.displayWarning("Cannot find " + inputPath, "Source images not available");
return null;
}
}
return resetBook(OmrStep.LOAD);
}
//------------//
// sampleBook //
//------------//
@Action(enabledProperty = BOOK_IDLE)
public Task sampleBook (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if ((book == null) || !hasValidSelectedSheets(book)) {
return null;
}
return new SampleBookTask(book);
}
//-------------//
// sampleSheet //
//-------------//
@Action(enabledProperty = STUB_IDLE)
public Task sampleSheet (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
return new SampleSheetTask(stub.getSheet());
}
//----------//
// saveBook //
//----------//
/**
* Action to save the internals of the current book.
*
* @param e the event that triggered this action
* @return the UI task to perform
*/
@Action(enabledProperty = BOOK_MODIFIED_OR_UPGRADED)
public Task saveBook (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return null;
}
try {
final Path bookPath = BookManager.getDefaultSavePath(book);
if ((book.getBookPath() != null) && (bookPath.toAbsolutePath().equals(
book.getBookPath().toAbsolutePath()) || isTargetConfirmed(bookPath))) {
return new StoreBookTask(book, bookPath);
}
return saveBookAs(e);
} catch (Throwable ex) {
logger.warn("Error in saveBook {}", ex.toString(), ex);
}
return null;
}
//------------//
// saveBookAs //
//------------//
@Action(enabledProperty = STUB_AVAILABLE)
public Task saveBookAs (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return null;
}
try {
// Let the user select a book output file
final Path defaultBookPath = BookManager.getDefaultSavePath(book);
final Path targetPath = selectBookPath(true, defaultBookPath);
final Path ownPath = book.getBookPath();
if ((targetPath != null) && (((ownPath != null) && ownPath.toAbsolutePath().equals(
targetPath.toAbsolutePath())) || isTargetConfirmed(targetPath))) {
return new StoreBookTask(book, targetPath);
}
} catch (Throwable ex) {
logger.warn("Error in saveBookAs {}", ex.toString(), ex);
}
return null;
}
//--------------------//
// saveBookRepository //
//--------------------//
/**
* Action to save the separate repository of the current book.
*
* @param e the event that triggered this action
* @return the UI task to perform
*/
@Action(enabledProperty = BOOK_MODIFIED_OR_UPGRADED)
public Task saveBookRepository (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return null;
}
try {
if (book.hasAllocatedRepository()) {
SampleRepository repo = book.getSampleRepository();
if (repo.isModified()) {
repo.storeRepository();
}
}
} catch (Throwable ex) {
logger.warn("Error in saveBookRepository {}", ex.toString(), ex);
}
return null;
}
//--------------//
// selectSheets //
//--------------//
/**
* Specify sheets selection.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = BOOK_IDLE)
public void selectSheets (ActionEvent e)
{
Book book = StubsController.getCurrentBook();
if (book == null) {
return;
}
try {
if (new StubsSelection(book).getSheetsSpec()) {
book.clearScores();
}
} catch (Throwable ex) {
logger.warn("Error in selectSheets {}", ex.toString(), ex);
}
}
//---------------//
// splitAndMerge //
//---------------//
/**
* Action that let the user build a new book from various pieces.
*
* @param e the event that triggered this action
*/
@Action
public void splitAndMerge (ActionEvent e)
{
OmrGui.getApplication().show(new SplitAndMerge(null).getComponent());
}
//---------------//
// splitAndMerge //
//---------------//
/**
* Action that let the user build a new book from various pieces.
*
* @param playListPath path to the provided playList to start with
*/
public void splitAndMerge (Path playListPath)
{
OmrGui.getApplication().show(new SplitAndMerge(playListPath).getComponent());
}
//-------------------//
// stopTranscription //
//-------------------//
/**
* Stop the book transcription ASAP.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = BOOK_PAUSABLE)
public void stopTranscription (ActionEvent e)
{
final Book book = StubsController.getCurrentBook();
if (book == null) {
return;
}
book.setPauseRequired(true);
logger.info("Pause required for book {} ...", book.getRadix());
}
//------------//
// swapSheets //
//------------//
/**
* Swap out (most of) book sheets.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = BOOK_IDLE)
public void swapSheets (ActionEvent e)
{
Book book = StubsController.getCurrentBook();
if (book == null) {
return;
}
try {
book.swapAllSheets();
} catch (Throwable ex) {
logger.warn("Error in swapSheets {}", ex.toString(), ex);
}
}
//-----------------------//
// toggleRepetitiveInput //
//-----------------------//
/**
* Toggle repetitive input mode.
*
* When this mode is active, focus is on manual insertion of items, with as few user actions as
* possible.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = REPETITIVE_INPUT_SELECTABLE)
public void toggleRepetitiveInput (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
final SheetEditor sheetEditor = stub.getSheet().getSheetEditor();
sheetEditor.toggleRepetitiveInputMode();
repetitiveInputAction.setSelected(sheetEditor.isRepetitiveInputMode());
}
//---------------------//
// toggleSheetValidity //
//---------------------//
/**
* Action that toggles the validity of currently selected sheet.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_IDLE)
public void toggleSheetValidity (ActionEvent e)
{
final StubsController controller = StubsController.getInstance();
final SheetStub stub = controller.getSelectedStub();
if (stub != null) {
final boolean isValid = stub.isValid();
final String pattern = resources.getString(
isValid ? "setSheetInvalid.pattern" : "setSheetValid.pattern");
final String msg = format(pattern, stub.getId());
if (!OMR.gui.displayConfirmation(msg + doYouConfirm)) {
return;
}
if (isValid) {
stub.invalidate();
if (ViewParameters.getInstance().isInvalidSheetDisplay() == false) {
controller.removeAssembly(stub);
}
} else {
stub.validate();
}
}
}
//----------------//
// transcribeBook //
//----------------//
/**
* Launch or complete the transcription of all sheets and merge them at book level.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_TRANSCRIBABLE)
public Task transcribeBook (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
boolean swap = false;
if (swapProcessedSheets() || Main.getCli().isSwap()) {
// Make sure we will be able to save book incrementally
final Book book = stub.getBook();
final Path defPath = BookManager.getDefaultSavePath(book);
final Path bookPath = book.getBookPath();
if (((bookPath != null) && (defPath.toAbsolutePath().equals(bookPath.toAbsolutePath()))
|| isTargetConfirmed(defPath))) {
swap = true;
logger.info("Processed sheets will be swapped out");
} else {
logger.info("No swap out for processed sheets");
}
}
return new TranscribeBookTask(stub, swap);
}
//-----------------//
// transcribeSheet //
//-----------------//
/**
* Launch or complete sheet transcription.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = STUB_TRANSCRIBABLE)
public Task transcribeSheet (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
return new TranscribeSheetTask(stub.getSheet());
}
//------//
// undo //
//------//
/**
* Action to undo last user modification.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = UNDOABLE)
public void undo (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
Sheet sheet = stub.getSheet();
InterController controller = sheet.getInterController();
controller.undo();
}
//-----------------------//
// updateRepetitiveInput //
//-----------------------//
/**
* Update checkbox of repetitiveInput mode according to the current sheet
*
* @param sheet the current sheet
*/
public void updateRepetitiveInput (Sheet sheet)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == sheet.getStub()) {
repetitiveInputAction.setSelected(sheet.getSheetEditor().isRepetitiveInputMode());
}
}
//---------------------//
// updateSheetValidity //
//---------------------//
/**
* Update menu item according to sheet validity status.
*
* @param stub the sheet stub to process
*/
public final void updateSheetValidity (SheetStub stub)
{
if (stub == null) {
toggleValidityAction.putValue(
javax.swing.Action.SMALL_ICON,
resources.getImageIcon("toggleSheetValidity.Action.smallIcon.true"));
toggleValidityAction.putValue(
javax.swing.Action.NAME,
resources.getString("toggleSheetValidity.Action.text.none"));
toggleValidityAction.putValue(
javax.swing.Action.SHORT_DESCRIPTION,
resources.getString("toggleSheetValidity.Action.shortDescription.none"));
} else if (stub == StubsController.getCurrentStub()) {
final boolean isValid = stub.isValid();
toggleValidityAction.putValue(
javax.swing.Action.SMALL_ICON,
resources.getImageIcon("toggleSheetValidity.Action.smallIcon." + isValid));
toggleValidityAction.putValue(
javax.swing.Action.NAME,
resources.getString("toggleSheetValidity.Action.text." + isValid));
toggleValidityAction.putValue(
javax.swing.Action.SHORT_DESCRIPTION,
resources.getString("toggleSheetValidity.Action.shortDescription." + isValid));
}
}
//-------------//
// upgradeBook //
//-------------//
/**
* Complete the upgrade of all sheets.
*
* @param e the event that triggered this action
* @return the task to launch in background
*/
@Action(enabledProperty = BOOK_UPGRADABLE)
public Task upgradeBook (ActionEvent e)
{
final SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return null;
}
return new UpgradeBookTask(stub.getBook());
}
//------------//
// zoomHeight //
//------------//
/**
* Action that allows to adjust the display zoom, so that the full height is shown.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void zoomHeight (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
SheetAssembly assembly = stub.getAssembly();
if (assembly == null) {
return;
}
ScrollView scrollView = assembly.getSelectedScrollView();
if (scrollView == null) {
return;
}
scrollView.fitHeight();
}
//-----------//
// zoomWidth //
//-----------//
/**
* Action that allows to adjust the display zoom, so that the full width is shown.
*
* @param e the event that triggered this action
*/
@Action(enabledProperty = STUB_AVAILABLE)
public void zoomWidth (ActionEvent e)
{
SheetStub stub = StubsController.getCurrentStub();
if (stub == null) {
return;
}
SheetAssembly assembly = stub.getAssembly();
if (assembly == null) {
return;
}
ScrollView scrollView = assembly.getSelectedScrollView();
if (scrollView == null) {
return;
}
scrollView.fitWidth();
}
//~ Static Methods -----------------------------------------------------------------------------
//-------------------//
// applyUserSettings //
//-------------------//
/**
* Prompt the user for interactive confirmation or modification of book/sheet
* parameters.
*
* @param stub the current sheet stub, or null
*/
public static void applyUserSettings (final SheetStub stub)
{
final Book book = (stub != null) ? stub.getBook() : null;
if (book != null) {
// Dialog already active?
final JDialog activeDialog = book.getParameterDialog();
if (activeDialog != null) {
activeDialog.setVisible(true);
return;
}
}
// Create a brand new dialog
try {
final BookParameters bookParameters = new BookParameters(stub);
final String frameTitle = bookParameters.getTitle();
final JDialog dialog = new JDialog(OMR.gui.getFrame(), frameTitle, false); // Non modal
// For SAF life cycle (to save dialog size and location across application runs)
dialog.setName("ScoreParamsDialog");
// To avoid memory leak when user closes window via the upper right cross
dialog.addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing (WindowEvent we)
{
if (book != null) {
book.setParameterDialog(null);
}
dialog.dispose();
}
});
if (book != null) {
book.setParameterDialog(dialog);
}
// User actions on buttons OK, Apply, Cancel
final JOptionPane optionPane = new JOptionPane(
bookParameters.getComponent(),
JOptionPane.PLAIN_MESSAGE,
JOptionPane.DEFAULT_OPTION,
null,
new Object[] { UserOpt.OK, UserOpt.Apply, UserOpt.Cancel });
optionPane.addPropertyChangeListener(e -> {
if (dialog.isVisible() && (e.getSource() == optionPane) && (e.getPropertyName()
.equals(JOptionPane.VALUE_PROPERTY))) {
final Object choice = optionPane.getValue();
final boolean exit;
if (choice == UserOpt.Cancel) {
exit = true;
} else if (choice == UserOpt.Apply) {
bookParameters.commit(book);
exit = false;
} else if (choice == UserOpt.OK) {
exit = bookParameters.commit(book);
} else {
exit = false;
}
if (exit) {
if (book != null) {
book.setParameterDialog(null);
}
dialog.setVisible(false);
dialog.dispose();
} else {
optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
}
}
});
dialog.setContentPane(optionPane);
dialog.pack();
OmrGui.getApplication().show(dialog);
} catch (Exception ex) {
logger.warn("Error in BookParameters {}", ex.toString(), ex);
}
}
//-----------------//
// checkParameters //
//-----------------//
/**
* Make sure that the book parameters are properly set up, even by
* prompting the user for them, otherwise return false
*
* @param book the provided book
* @return true if OK, false otherwise
*/
public static boolean checkParameters (Book book)
{
// if (constants.promptParameters.getValue()) {
// return applyUserSettings(sheet);
// } else {
// return true;
// ///return fillParametersWithDefaults(sheet.getBook());
// }
return true;
}
//-----------------//
// checkParameters //
//-----------------//
/**
* Make sure that the book parameters are properly set up, even by
* prompting the user for them, otherwise return false
*
* @param sheet the provided sheet
* @return true if OK, false otherwise
*/
public static boolean checkParameters (Sheet sheet)
{
// if (constants.promptParameters.getValue()) {
// return applyUserSettings(sheet);
// } else {
// return true;
// ///return fillParametersWithDefaults(sheet.getBook());
// }
return true;
}
//-------------//
// checkStored //
//-------------//
/**
* Check whether the provided book has been saved if needed
* (and therefore, if it can be closed)
*
* @param book the book to check
* @return true if close is allowed, false if not
*/
public static boolean checkStored (Book book)
{
// Check the book scores
for (Score score : book.getScores()) {
final LogicalPartsEditor editor = score.getLogicalsEditor();
if (editor != null && !editor.canClose()) {
return false;
}
}
final String bookStatus = book.isModified() ? resources.getString("modified")
: (book.isUpgraded() ? resources.getString("upgraded") : null);
if ((bookStatus != null) && defaultPromptOnClosingUnsaved.getValue()) {
final int answer = JOptionPane.showConfirmDialog(
OMR.gui.getFrame(),
format(resources.getString("saveBook.pattern"), bookStatus, book.getRadix()));
if (answer == JOptionPane.YES_OPTION) {
Path bookPath;
if (book.getBookPath() == null) {
// Find a suitable target file
bookPath = BookManager.getDefaultSavePath(book);
// Check the target is fine
if (!isTargetConfirmed(bookPath)) {
// Let the user select an alternate output file
bookPath = selectBookPath(true, BookManager.getDefaultSavePath(book));
if ((bookPath == null) || !isTargetConfirmed(bookPath)) {
return false; // No suitable target found
}
}
} else {
bookPath = book.getBookPath();
}
try {
// Save the book to target file
book.store(bookPath, false);
return true; // Book successfully saved
} catch (Exception ex) {
logger.warn("Error saving book", ex);
return false; // Saving failed
}
}
// Check whether user specifically chose NOT to save the book
return answer == JOptionPane.NO_OPTION;
} else {
return true;
}
}
//--------//
// filter //
//--------//
public static OmrFileFilter filter (String ext)
{
return new OmrFileFilter(ext, new String[] { ext });
}
//-------------//
// getInstance //
//-------------//
/**
* Report the single instance of BookActions in the application.
*
* @return the instance
*/
public static BookActions getInstance ()
{
return LazySingleton.INSTANCE;
}
//-------------------//
// isTargetConfirmed //
//-------------------//
/**
* Check whether we have user confirmation to overwrite the target path.
*
* This is a no-op if target does not already exist.
*
* @param target the path to be checked
* @return false if explicitly not isTargetConfirmed, true otherwise
*/
public static boolean isTargetConfirmed (Path target)
{
return (!Files.exists(target)) || OMR.gui.displayConfirmation(
format(resources.getString("overwriteFile.pattern"), target));
}
//-----------------------//
// preOpenBookParameters //
//-----------------------//
/**
* Check whether we should pre-open book parameters dialog at any book creation.
*
* @return true if so
*/
public static boolean preOpenBookParameters ()
{
return constants.preOpenBookParameters.isSet();
}
//------------------------//
// selectBookOrImagePaths //
//------------------------//
/**
* Let the user interactively select book/image paths for reading.
*
* @param startPath starting path
* @return the array of selected paths, perhaps empty but not null
*/
public static Path[] selectBookOrImagePaths (Path startPath)
{
final String suffixes = constants.validImageExtensions.getValue();
final String allSuffixes = OMR.BOOK_EXTENSION + " " + suffixes + " " + suffixes
.toUpperCase();
final OmrFileFilter filter = new OmrFileFilter(
resources.getString("bookAnd") + " " + resources.getString("majorImageFiles") + " ("
+ OMR.BOOK_EXTENSION + " " + suffixes + ")",
allSuffixes.split("\\s"));
return selectPaths(false, startPath, filter);
}
//----------------//
// selectBookPath //
//----------------//
/**
* Let the user interactively select a book path.
*
* @param save true for write, false for read
* @param startPath starting path
* @return the selected path or null
*/
public static Path selectBookPath (boolean save,
Path startPath)
{
return selectPath(save, startPath, filter(OMR.BOOK_EXTENSION));
}
//-----------------//
// selectBookPaths //
//-----------------//
/**
* Let the user interactively select book paths.
*
* @param save true for write, false for read
* @param startPath starting path
* @return the array of selected paths, perhaps empty but not null
*/
public static Path[] selectBookPaths (boolean save,
Path startPath)
{
return selectPaths(save, startPath, filter(OMR.BOOK_EXTENSION));
}
//-----------------//
// selectImagePath //
//-----------------//
public static Path selectImagePath ()
{
final String suffixes = constants.validImageExtensions.getValue();
final String allSuffixes = suffixes + " " + suffixes.toUpperCase();
final OmrFileFilter filter = new OmrFileFilter(
resources.getString("majorImageFiles") + " (" + suffixes + ")",
allSuffixes.split("\\s"));
return selectPath(false, Paths.get(BookManager.getDefaultImageFolder()), filter);
}
//------------------//
// selectImagePaths //
//------------------//
public static Path[] selectImagePaths ()
{
final String suffixes = constants.validImageExtensions.getValue();
final String allSuffixes = suffixes + " " + suffixes.toUpperCase();
final OmrFileFilter filter = new OmrFileFilter(
resources.getString("majorImageFiles") + " (" + suffixes + ")",
allSuffixes.split("\\s"));
return selectPaths(false, Paths.get(BookManager.getDefaultImageFolder()), filter);
}
//------------//
// selectPath //
//------------//
/**
* Let the user interactively select a path.
*
* @param save true for write, false for read
* (for read, path will be checked to exist and not be a directory)
* @param startPath the starting path
* @param filter filter on file extensions
* @return the selected path or null
*/
public static Path selectPath (boolean save,
Path startPath,
OmrFileFilter filter)
{
final Path path = UIUtil.pathChooser(save, OMR.gui.getFrame(), startPath, filter);
if (path == null) {
return null;
}
if (save) {
return path;
}
if (!Files.exists(path)) {
logger.warn(format(resources.getString("fileNotFound.pattern"), path));
return null;
}
if (Files.isDirectory(path)) {
logger.warn(format(resources.getString("isDirectory.pattern"), path));
return null;
}
return path;
}
//-------------//
// selectPaths //
//-------------//
/**
* Let the user interactively select an array of paths.
*
* @param save true for write, false for read
* (for read, path will be checked to exist and not be a directory)
* @param startPath the starting path
* @param filter filter on file extensions
* @return the array of selected paths, perhaps empty but not null
*/
public static Path[] selectPaths (boolean save,
Path startPath,
OmrFileFilter filter)
{
final Path[] paths = UIUtil.pathsChooser(save, OMR.gui.getFrame(), startPath, filter);
if (paths.length == 0) {
return paths;
}
if (save) {
return paths;
}
for (Path path : paths) {
if (!Files.exists(path)) {
logger.warn(format(resources.getString("fileNotFound.pattern"), path));
return new Path[0];
}
if (Files.isDirectory(path)) {
logger.warn(format(resources.getString("isDirectory.pattern"), path));
return new Path[0];
}
}
return paths;
}
//~ Inner Classes ------------------------------------------------------------------------------
//---------------//
// CloseBookTask //
//---------------//
private static class CloseBookTask
extends VoidTask
{
final SheetStub stub;
/**
* Create an asynchronous task to close the book.
*
* @param stub the current stub of the book to close
*/
CloseBookTask (SheetStub stub)
{
this.stub = stub;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
final Book book = stub.getBook();
try {
LogUtil.start(book);
book.close(stub.getNumber());
} catch (Throwable ex) {
logger.warn("Error in CloseBookTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//-----------//
// Constants //
//-----------//
private static class Constants
extends ConstantSet
{
private final Constant.String validImageExtensions = new Constant.String(
".bmp .gif .jpg .jpeg .png .tiff .tif .pdf",
"Valid image file extensions, whitespace-separated");
private final Constant.Boolean closeConfirmation = new Constant.Boolean(
true,
"Should we ask confirmation for closing an unsaved book?");
private final Constant.Boolean preOpenBookParameters = new Constant.Boolean(
false,
"Automatically open book parameters dialog at book creation?");
}
//----------------//
// ExportBookTask //
//----------------//
private static class ExportBookTask
extends VoidTask
{
final Book book;
/**
* Create an asynchronous task to export the book.
*
* @param book the book to export
* @param bookPathSansExt (non-null) the target export book path with no extension
*/
ExportBookTask (Book book,
Path bookPathSansExt)
{
this.book = book;
book.setExportPathSansExt(bookPathSansExt);
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
if (checkParameters(book)) {
book.export(book.getValidSelectedStubs(), book.getScores());
}
} catch (Throwable ex) {
logger.warn("Error in ExportBookTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//-----------------//
// ExportSheetTask //
//-----------------//
private static class ExportSheetTask
extends VoidTask
{
final Sheet sheet;
final Path sheetExportPath;
ExportSheetTask (Sheet sheet,
Path sheetExportPath)
{
this.sheet = sheet;
this.sheetExportPath = sheetExportPath;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
if (checkParameters(sheet)) {
sheet.getStub().reachStep(OmrStep.PAGE, false);
sheet.export(sheetExportPath);
}
} catch (StepPause sp) {
logger.info("ExportSheetTask stopped by user.");
} catch (Exception ex) {
logger.warn("Error in ExportSheetTask {}", ex.toString(), ex);
} finally {
LogUtil.stopStub();
}
return null;
}
}
//--------------------//
// ExportBookMidiTask //
//--------------------//
private static class ExportBookMidiTask
extends VoidTask
{
final Book book;
final Path bookPathSansExt;
ExportBookMidiTask (Book book, Path bookPathSansExt)
{
this.book = book;
this.bookPathSansExt = bookPathSansExt;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
final boolean swap = (OMR.gui == null) || Main.getCli().isSwap()
|| swapProcessedSheets();
final boolean ok = book.transcribe(
book.getValidSelectedStubs(), book.getScores(), swap);
if (!ok) {
logger.warn("MIDI export aborted: transcription incomplete");
return null;
}
final List scores = book.getScores();
final boolean multi = scores.size() > 1;
final String ext = MidiAbstractions.MIDI_EXTENSION;
for (Score score : scores) {
final String suffix = multi
? (OMR.MOVEMENT_EXTENSION + score.getId()) : "";
final Path target = bookPathSansExt.resolveSibling(
bookPathSansExt.getFileName().toString() + suffix + ext);
try {
new MidiExporter(score).export(target);
logger.info("MIDI exported to {}", target);
} catch (Exception ex) {
logger.warn("Could not export MIDI for movement {}: {}",
score.getId(), ex.toString(), ex);
}
}
} catch (Throwable ex) {
logger.warn("Error in ExportBookMidiTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//---------------------//
// ExportSheetMidiTask //
//---------------------//
private static class ExportSheetMidiTask
extends VoidTask
{
final Sheet sheet;
final Path sheetExportPath;
ExportSheetMidiTask (Sheet sheet, Path sheetExportPath)
{
this.sheet = sheet;
this.sheetExportPath = sheetExportPath;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
if (!checkParameters(sheet)) {
return null;
}
sheet.getStub().reachStep(OmrStep.PAGE, false);
final SheetStub stub = sheet.getStub();
final Book book = stub.getBook();
final List pageRefs = stub.getPageRefs();
if (pageRefs.isEmpty()) {
logger.warn("Sheet has no pages; nothing to export");
return null;
}
final Path folder = sheetExportPath.getParent();
if (folder != null && !Files.exists(folder)) {
Files.createDirectories(folder);
}
final String ext = MidiAbstractions.MIDI_EXTENSION;
final String baseName = stripExtension(
sheetExportPath.getFileName().toString(), ext);
if (pageRefs.size() > 1) {
for (PageRef pageRef : pageRefs) {
final Score score = new Score();
score.setBook(book);
score.addPageNumber(stub.getNumber(), pageRef);
new ScoreReduction(score).reduce(Arrays.asList(stub));
final String movementName = baseName
+ OMR.MOVEMENT_EXTENSION + pageRef.getId();
final Path target = sheetExportPath.resolveSibling(movementName + ext);
new MidiExporter(score).export(target);
logger.info("MIDI exported to {}", target);
}
} else {
final Score score = new Score();
score.setBook(book);
score.addPageNumber(stub.getNumber(), stub.getFirstPageRef());
new ScoreReduction(score).reduce(Arrays.asList(stub));
final Path target = sheetExportPath.resolveSibling(baseName + ext);
new MidiExporter(score).export(target);
logger.info("MIDI exported to {}", target);
}
} catch (StepPause sp) {
logger.info("ExportSheetMidiTask stopped by user.");
} catch (Exception ex) {
logger.warn("Error in ExportSheetMidiTask {}", ex.toString(), ex);
} finally {
LogUtil.stopStub();
}
return null;
}
private static String stripExtension (String name, String ext)
{
return name.toLowerCase().endsWith(ext)
? name.substring(0, name.length() - ext.length()) : name;
}
}
//--------------//
// ExportFormat //
//--------------//
private enum ExportFormat
{
MUSIC_XML,
MIDI
}
//--------------//
// ExportTarget //
//--------------//
private static final class ExportTarget
{
final Path path;
final ExportFormat format;
ExportTarget (Path path, ExportFormat format)
{
this.path = path;
this.format = format;
}
}
//---------------//
// LazySingleton //
//---------------//
private static class LazySingleton
{
static final BookActions INSTANCE = new BookActions();
private LazySingleton ()
{
}
}
//--------------//
// LoadBookTask //
//--------------//
/**
* Task that loads a book (.omr project) file in background.
*
* If load is successful, GUI allocates all stub tabs and then focuses on:
*
* - Either the desired stub if its number is provided,
*
- Or the first valid stub encountered.
*
*/
public static class LoadBookTask
extends WaitingTask
{
/** Underlying path. */
protected Path path;
/** Desired sheet number, if any. */
protected Integer sheetNumber;
// Constructor needed for creation of HistoryMenu
public LoadBookTask ()
{
super(OmrGui.getApplication(), "Loading");
}
/**
* Creates a new LoadBookTaskobject, with a plain book path
*
* @param path plain book path
*/
public LoadBookTask (Path path)
{
super(OmrGui.getApplication(), "Loading " + path + " ...");
this.path = path;
}
/**
* Creates a new LoadBookTaskobject, with a desired sheet number.
*
* @param sheetPath the desired sheet number to focus upon
*/
public LoadBookTask (SheetPath sheetPath)
{
super(OmrGui.getApplication(), "Loading " + sheetPath.getBookPath() + " ...");
path = sheetPath.getBookPath();
sheetNumber = sheetPath.getSheetNumber();
}
@Override
protected Book doInBackground ()
throws Exception
{
Book book = null;
try {
// Actually open the book zip system
book = OMR.engine.loadBook(path);
} catch (Throwable ex) {
logger.warn("Error in {} {}", getClass().getSimpleName(), ex.toString(), ex);
}
return book;
}
/**
* Set the path value.
*
* @param path the path used by the task
*/
public void setPath (Path path)
{
this.path = path;
}
/**
* Set the sheet path value (path#sheet) .
*
* @param sheetPath the sheet path used by the task
*/
public void setPath (SheetPath sheetPath)
{
setPath(sheetPath.getBookPath());
sheetNumber = sheetPath.getSheetNumber();
}
@Override
protected void succeeded (Book book)
{
if (book != null) {
final StubsController controller = StubsController.getInstance();
// Insert all tabs in GUI stubsPane
controller.displayStubs(book, sheetNumber);
// Select stub
final SheetStub stub = (sheetNumber != null) ? book.getStub(sheetNumber)
: book.getFirstValidStub();
controller.selectAssembly(stub);
}
}
}
//---------------//
// LoadFilesTask //
//---------------//
/**
* Task that loads a list of files (possibly a mix of book files and input files).
*/
public static class LoadFilesTask
extends PathListTask, Void>
{
public LoadFilesTask (Collection extends Path> paths)
{
super(paths);
}
@Override
protected List doInBackground ()
throws Exception
{
final List bookList = new ArrayList<>();
for (Path path : pathList) {
Book book = null;
try {
final String ext = FileUtil.getExtension(path.getFileName());
if (ext.equalsIgnoreCase(OMR.BOOK_EXTENSION)) {
// Book file
book = OMR.engine.loadBook(path);
} else {
// Assumed image file
book = OMR.engine.loadInput(path);
book.createStubs(); // Read just the count of sheets in input file
}
} catch (Throwable ex) {
logger.warn("Error in {} {}", getClass().getSimpleName(), ex.toString(), ex);
}
bookList.add(book); // book can be null
}
return bookList;
}
@Override
protected void succeeded (List bookList)
{
final StubsController controller = StubsController.getInstance();
for (Book book : bookList) {
if (book != null) {
try {
// Insert all tabs in GUI stubsPane
controller.displayStubs(book, null);
// Select stub
controller.selectAssembly(book.getFirstValidStub());
if (book.isImage()) {
// Pre-open Book Parameters dialog on this brand new book?
if (preOpenBookParameters()) {
BookActions.applyUserSettings(book.getFirstValidStub());
}
}
} catch (Throwable ex) {
logger.warn(
"Error in {} {}",
getClass().getSimpleName(),
ex.toString(),
ex);
}
}
}
}
}
//---------------//
// LoadImageTask //
//---------------//
/**
* Task that opens an image file.
*/
public static class LoadImageTask
extends LoadBookTask
{
// Constructor needed for creation of HistoryMenu
public LoadImageTask ()
{
}
/**
* Creates a LoadBookTask object on an input file assumed to exist.
*
* @param path the path to input file
*/
public LoadImageTask (Path path)
{
super(path);
}
@Override
protected Book doInBackground ()
throws InterruptedException
{
try {
// Actually open the image file
Book book = OMR.engine.loadInput(path);
LogUtil.start(book);
book.createStubs(); // Read just the count of sheets in input file
return book;
} catch (Exception ex) {
logger.warn("Error opening path " + path + " " + ex, ex);
} finally {
LogUtil.stopBook();
}
return null;
}
@Override
protected void succeeded (Book book)
{
if (book != null) {
try {
super.succeeded(book);
// Pre-open Book Parameters dialog on this brand new book?
if (preOpenBookParameters()) {
BookActions.applyUserSettings(book.getFirstValidStub());
}
} catch (Throwable ex) {
logger.warn("Error in {} {}", getClass().getSimpleName(), ex.toString(), ex);
}
}
}
}
//---------------//
// PrintBookTask //
//---------------//
public static class PrintBookTask
extends VoidTask
{
final Book book;
final Path bookPrintPath;
public PrintBookTask (Book book,
Path bookPrintPath)
{
this.book = book;
this.bookPrintPath = bookPrintPath;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
book.setPrintPath(bookPrintPath);
book.print(book.getValidSelectedStubs());
} catch (Throwable ex) {
logger.warn("Error in PrintBookTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//----------------//
// PrintSheetTask //
//----------------//
public static class PrintSheetTask
extends VoidTask
{
final Sheet sheet;
final Path sheetPrintPath;
public PrintSheetTask (Sheet sheet,
Path sheetPrintPath)
{
this.sheet = sheet;
this.sheetPrintPath = sheetPrintPath;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
sheet.print(sheetPrintPath);
} catch (Throwable ex) {
logger.warn("Error in PrintSheetTask {}", ex.toString(), ex);
} finally {
LogUtil.stopStub();
}
return null;
}
}
//------------------------//
// PromptOnClosingUnsaved //
//------------------------//
private static class PromptOnClosingUnsaved
extends Param
{
public PromptOnClosingUnsaved ()
{
super(GLOBAL_SCOPE);
}
@Override
public Boolean getSpecific ()
{
if (isSpecific()) {
return getValue();
} else {
return null;
}
}
@Override
public Boolean getValue ()
{
return constants.closeConfirmation.getValue();
}
@Override
public boolean isSpecific ()
{
return !constants.closeConfirmation.isSourceValue();
}
@Override
public boolean setSpecific (Boolean specific)
{
if (!getValue().equals(specific)) {
constants.closeConfirmation.setValue(specific);
logger.info(resources.getString("promptOnClose." + specific));
return true;
} else {
return false;
}
}
}
//---------------//
// ResetBookTask //
//---------------//
/**
* Task that resets all valid selected sheets of book to gray or binary.
*/
private class ResetBookTask
extends VoidTask
{
final Book book;
final OmrStep step;
public ResetBookTask (Book book,
OmrStep step)
{
this.book = book;
this.step = step;
}
@Override
protected Void doInBackground ()
throws Exception
{
book.resetTo(step);
setBookTranscribable(true);
setBookModifiedOrUpgraded(true);
return null;
}
}
//----------------//
// SampleBookTask //
//----------------//
private static class SampleBookTask
extends VoidTask
{
final Book book;
SampleBookTask (Book book)
{
this.book = book;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
book.sample(book.getValidSelectedStubs());
} catch (Throwable ex) {
logger.warn("Error in SampleBookTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//-----------------//
// SampleSheetTask //
//-----------------//
public static class SampleSheetTask
extends VoidTask
{
final Sheet sheet;
public SampleSheetTask (Sheet sheet)
{
this.sheet = sheet;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
sheet.sample();
} catch (Throwable ex) {
logger.warn("Error in SampleSheetTask {}", ex.toString(), ex);
} finally {
LogUtil.stopStub();
}
return null;
}
}
//---------------//
// StoreBookTask //
//---------------//
private static class StoreBookTask
extends VoidTask
{
final Book book;
final Path bookPath;
/**
* Create an asynchronous task to store the book.
*
* @param book the book to export
* @param bookPath (non-null) the target to store book path
*/
StoreBookTask (Book book,
Path bookPath)
{
this.book = book;
this.bookPath = bookPath;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
final Path oldBookPath = book.getBookPath();
book.store(bookPath, false);
BookActions.getInstance().setBookModifiedOrUpgraded(false);
// Update books map
OMR.engine.renameBook(book, oldBookPath);
} catch (Throwable ex) {
logger.warn("Error in StoreBookTask {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
//--------------------//
// TranscribeBookTask //
//--------------------//
private static class TranscribeBookTask
extends VoidTask
{
private final SheetStub stub;
private final Book book;
private final boolean swap;
TranscribeBookTask (SheetStub stub,
boolean swap)
{
this.stub = stub;
this.book = stub.getBook();
this.swap = swap;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
if (swap) {
book.store(BookManager.getDefaultSavePath(book), false);
}
book.transcribe(book.getValidSelectedStubs(), book.getScores(), swap);
} catch (Throwable ex) {
logger.warn("Could not transcribe book {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
@Override
protected void succeeded (Void result)
{
StubsController.getInstance().callAboutStub(stub);
}
}
//---------------------//
// TranscribeSheetTask //
//---------------------//
private static class TranscribeSheetTask
extends VoidTask
{
private final Sheet sheet;
TranscribeSheetTask (Sheet sheet)
{
this.sheet = sheet;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(sheet.getStub());
sheet.getStub().transcribe();
} catch (Throwable ex) {
logger.warn("Could not transcribe sheet {}", ex.toString(), ex);
} finally {
LogUtil.stopStub();
}
return null;
}
}
//-----------------//
// UpgradeBookTask //
//-----------------//
private static class UpgradeBookTask
extends VoidTask
{
private final Book book;
UpgradeBookTask (Book book)
{
this.book = book;
}
@Override
protected Void doInBackground ()
throws InterruptedException
{
try {
LogUtil.start(book);
book.upgradeStubs();
} catch (Throwable ex) {
logger.warn("Error while upgrading book {}", ex.toString(), ex);
} finally {
LogUtil.stopBook();
}
return null;
}
}
}