From 67bc7e8491d5f9eb886eed0bced2ce561358d53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Bitteur?= Date: Sun, 5 Jan 2025 20:04:28 +0100 Subject: [PATCH] New preferences dialog, with new policy for the selection of output folder --- app/src/main/java/org/audiveris/omr/CLI.java | 11 + app/src/main/java/org/audiveris/omr/Main.java | 2 +- .../org/audiveris/omr/sheet/BookManager.java | 22 +- .../audiveris/omr/ui/action/Preferences.java | 199 +++++++++++++----- .../action/resources/Preferences.properties | 10 +- .../resources/Preferences_fr.properties | 10 +- .../java/org/audiveris/omr/ui/util/Panel.java | 3 +- .../org/audiveris/omr/ui/util/UIUtil.java | 103 ++++++--- 8 files changed, 266 insertions(+), 94 deletions(-) diff --git a/app/src/main/java/org/audiveris/omr/CLI.java b/app/src/main/java/org/audiveris/omr/CLI.java index 93f2a280c..1be9554b6 100644 --- a/app/src/main/java/org/audiveris/omr/CLI.java +++ b/app/src/main/java/org/audiveris/omr/CLI.java @@ -368,6 +368,17 @@ public void printUsage () logger.info(buf.toString()); } + //---------------------// + // nullifyOutputFolder // + //---------------------// + /** + * Nullify the output folder on the CLI. + */ + public void nullifyOutputFolder () + { + params.outputFolder = null; + } + //~ Inner classes ------------------------------------------------------------------------------ //----------// diff --git a/app/src/main/java/org/audiveris/omr/Main.java b/app/src/main/java/org/audiveris/omr/Main.java index 943463b40..acb808f91 100644 --- a/app/src/main/java/org/audiveris/omr/Main.java +++ b/app/src/main/java/org/audiveris/omr/Main.java @@ -439,7 +439,7 @@ private static class Constants "Should we show environment?"); private final Constant.Boolean showAllEnvironmentVariables = new Constant.Boolean( - true, + false, "Should we show all environment variables?"); private final Constant.String locale = new Constant.String( diff --git a/app/src/main/java/org/audiveris/omr/sheet/BookManager.java b/app/src/main/java/org/audiveris/omr/sheet/BookManager.java index 43bed0219..00478a118 100644 --- a/app/src/main/java/org/audiveris/omr/sheet/BookManager.java +++ b/app/src/main/java/org/audiveris/omr/sheet/BookManager.java @@ -455,7 +455,7 @@ public static Path getBaseFolder () * * @return the output base folder string */ - public static String getBaseFolderString () + private static String getBaseFolderString () { return constants.baseFolder.getValue().trim(); } @@ -618,6 +618,22 @@ public static boolean isMultiBook () return getInstance().books.size() > 1; } + //---------------// + // setBaseFolder // + //---------------// + /** + * Set the base for output folders. + *

+ * This is meant for interactive setting, overriding a potential cliOutput. + * + * @param dir the output base folder + */ + public static void setBaseFolder (Path dir) + { + Main.getCli().nullifyOutputFolder(); + constants.baseFolder.setValue(dir.toString()); + } + //----------------// // useCompression // //----------------// @@ -696,11 +712,11 @@ private static class Constants "Should we compress the MusicXML output?"); private final Constant.Boolean useSeparateBookFolders = new Constant.Boolean( - true, + false, "Should we use a separate folder for each book?"); private final Constant.Boolean useInputBookFolder = new Constant.Boolean( - false, + true, "Should we store book outputs next to book input?"); private final Constant.String baseFolder = new Constant.String( diff --git a/app/src/main/java/org/audiveris/omr/ui/action/Preferences.java b/app/src/main/java/org/audiveris/omr/ui/action/Preferences.java index e3f4c435a..3bb240bb0 100644 --- a/app/src/main/java/org/audiveris/omr/ui/action/Preferences.java +++ b/app/src/main/java/org/audiveris/omr/ui/action/Preferences.java @@ -43,20 +43,27 @@ import com.jgoodies.forms.builder.FormBuilder; import com.jgoodies.forms.layout.FormLayout; +import java.awt.Component; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.File; import java.util.Collection; import java.util.List; import java.util.Locale; -import javax.swing.BorderFactory; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -76,16 +83,20 @@ public abstract class Preferences private static final Logger logger = LoggerFactory.getLogger(Preferences.class); - /** Layout for 2 items. */ - private static final FormLayout layout2 = new FormLayout("pref,15dlu,pref", "pref"); + /** Layouts for 2 items. */ + private static final FormLayout layout2 = new FormLayout("3dlu,70dlu,10dlu,pref", "pref"); + + private static final FormLayout layout2b = new FormLayout("70dlu,10dlu,250dlu", "pref"); /** Layout for 3 items. */ - private static final FormLayout layout3 = new FormLayout("12dlu,1dlu,pref,10dlu,pref", "pref"); + private static final FormLayout layout3 = new FormLayout("9dlu,1dlu,60dlu,10dlu,pref", "pref"); private static final ApplicationContext context = Application.getInstance().getContext(); private static final ResourceMap resource = context.getResourceMap(Preferences.class); + private static final Insets titledInsets = new Insets(15, 6, 6, 6); + //~ Constructors ------------------------------------------------------------------------------- /** Not meant to be instantiated. */ @@ -95,6 +106,22 @@ private Preferences () //~ Static Methods ----------------------------------------------------------------------------- + //--------------------// + // createTitledBorder // + //--------------------// + private static TitledBorder createTitledBorder (String title) + { + return new TitledBorder(title) + { + @Override + public Insets getBorderInsets (Component c) + { + return titledInsets; + } + }; + + } + //------------// // getMessage // //------------// @@ -110,7 +137,7 @@ private static JPanel getMessage () final FormLayout layout = new FormLayout( "pref", - "pref, 1dlu, pref, 1dlu, pref, 1dlu, pref"); + "pref, 5dlu, pref, 5dlu, pref, 5dlu, pref"); final FormBuilder builder = FormBuilder.create().layout(layout).panel(panel); int r = 1; builder.addRaw(new EarlyPane()).xy(1, r); @@ -146,12 +173,10 @@ public static void show () private static class AdvancedTopicsPane extends JPanel { - AdvancedTopicsPane () + public AdvancedTopicsPane () { final String className = getClass().getSimpleName(); - setBorder( - BorderFactory.createTitledBorder( - resource.getString(className + ".titledBorder.text"))); + setBorder(createTitledBorder(resource.getString(className + ".titledBorder.text"))); // Localized values of Topic enum type final LabeledEnum[] localeTopics = LabeledEnum.values( @@ -162,7 +187,7 @@ private static class AdvancedTopicsPane // Layout final FormLayout layout = new FormLayout( "pref", - Panel.makeRows(2 + Topic.values().length)); + "28,1," + "26,1," + "22,1,22,1,22,1,22,1,22,1,22"); final FormBuilder builder = FormBuilder.create().layout(layout).panel(this); int r = 1; @@ -176,7 +201,7 @@ private static class AdvancedTopicsPane // Switches for (Topic topic : Topic.values()) { - String topicName = LabeledEnum.valueOf(topic, localeTopics).label; + final String topicName = LabeledEnum.valueOf(topic, localeTopics).label; builder.addRaw(new TopicPane(topic, topicName)).xy(1, r); r += 2; } @@ -190,15 +215,16 @@ private static class AdvancedTopicsPane * Which step should we trigger on any input image. */ private static class EarlyPane - extends Panel + extends JPanel implements ActionListener { - // ComboBox for desired step - private final JComboBox stepBox; + private final JComboBox stepBox; // ComboBox for desired step - EarlyPane () + public EarlyPane () { final String className = getClass().getSimpleName(); + setBorder(createTitledBorder(resource.getString(className + ".titledBorder.text"))); + final String tip = resource.getString(className + ".stepBox.toolTipText"); // Define stepBox @@ -208,8 +234,8 @@ private static class EarlyPane // Layout final FormBuilder builder = FormBuilder.create().layout(layout2).panel(this); - builder.addRaw(stepBox).xy(1, 1); - builder.addRaw(new JLabel(tip)).xy(3, 1); + builder.addRaw(stepBox).xy(2, 1); + builder.addRaw(new JLabel(tip)).xy(4, 1); // Initial status stepBox.setSelectedItem(StubsController.getEarlyStep()); @@ -218,7 +244,7 @@ private static class EarlyPane @Override public void actionPerformed (ActionEvent e) { - OmrStep step = stepBox.getItemAt(stepBox.getSelectedIndex()); + final OmrStep step = stepBox.getItemAt(stepBox.getSelectedIndex()); StubsController.setEarlyStep(step); } } @@ -235,10 +261,9 @@ private static class LocalePane { private static final List locales = Main.getSupportedLocales(); - // ComboBox for supported locales - private final JComboBox localeBox; + private final JComboBox localeBox; // ComboBox for supported locales - LocalePane () + public LocalePane () { final String className = getClass().getSimpleName(); final String tip = resource.getString(className + ".localeBox.toolTipText"); @@ -260,28 +285,92 @@ private static class LocalePane @Override public void actionPerformed (ActionEvent e) { - Locale locale = localeBox.getItemAt(localeBox.getSelectedIndex()); + final Locale locale = localeBox.getItemAt(localeBox.getSelectedIndex()); Main.setLocale(locale); } } + //-------------------// + // DefaultOutputPane // + //-------------------// + /** + * Choosing the default output folder. + */ + private static class DefaultOutputPane + extends Panel + { + private final String className; + + private final JButton browse; // To choose a folder + + private final JTextField field; // Current folder path + + public DefaultOutputPane () + { + className = getClass().getSimpleName(); + browse = new JButton(new BrowseAction()); + field = new JTextField(); + field.setText(BookManager.getBaseFolder().toString()); + field.setToolTipText(resource.getString(className + ".toolTipText")); + + // Layout + final FormBuilder builder = FormBuilder.create().layout(layout2b).panel(this); + builder.addRaw(browse).xy(1, 1); + builder.addRaw(field).xy(3, 1); + } + + @Override + public void setEnabled (boolean enabled) + { + super.setEnabled(enabled); + browse.setEnabled(enabled); + field.setEnabled(enabled); + } + + private class BrowseAction + extends AbstractAction + { + public BrowseAction () + { + super(resource.getString(className + ".text")); + putValue(Action.SHORT_DESCRIPTION, "Browse for output folder"); + } + + @Override + public void actionPerformed (ActionEvent e) + { + final File dir = UIUtil.directoryChooser( + true, + DefaultOutputPane.this, + BookManager.getBaseFolder().toFile(), + resource.getString(className + ".title")); + + if (dir != null) { + field.setText(dir.toString()); + BookManager.setBaseFolder(dir.toPath()); + logger.info("Output folder is now {}", dir); + } + } + } + } + //------------// // OutputPane // //------------// /** - * Handling of output switch. + * Handling of an output switch. */ private static abstract class OutputPane extends Panel implements ActionListener { - final JCheckBox box; + protected final JCheckBox box; - final JLabel name; + private final JLabel name; - final JLabel desc; + private final JLabel desc; - OutputPane () + public OutputPane () { box = new JCheckBox(); box.addActionListener(this); @@ -315,26 +404,28 @@ public void setEnabled (boolean enabled) * Where should outputs be stored. */ private static class OutputsPane - extends Panel + extends JPanel { final SeparatePane separatePane = new SeparatePane(); - final SiblingPane siblingPane = new SiblingPane(separatePane); + final DefaultOutputPane defaultPane = new DefaultOutputPane(); + + final SiblingPane siblingPane = new SiblingPane(defaultPane, separatePane); - OutputsPane () + public OutputsPane () { - setInsets(12, 6, 6, 6); final String className = getClass().getSimpleName(); - setBorder( - BorderFactory.createTitledBorder( - resource.getString(className + ".titledBorder.text"))); + setBorder(createTitledBorder(resource.getString(className + ".titledBorder.text"))); // Layout - final FormLayout layout = new FormLayout("pref", Panel.makeRows(2)); + final FormLayout layout = new FormLayout("pref", "22,1," + "26,1," + "22"); final FormBuilder builder = FormBuilder.create().layout(layout).panel(this); int r = 1; builder.addRaw(siblingPane).xy(1, r); + r += 2; + builder.addRaw(defaultPane).xy(1, r); + r += 2; builder.addRaw(separatePane).xy(1, r); } @@ -347,15 +438,15 @@ private static class OutputsPane * Which plugin should be the default one. */ private static class PluginPane - extends Panel + extends JPanel implements ActionListener { - // ComboBox for registered plugins - private final JComboBox pluginBox; + private final JComboBox pluginBox; // ComboBox for registered plugins - PluginPane () + public PluginPane () { final String className = getClass().getSimpleName(); + setBorder(createTitledBorder(resource.getString(className + ".titledBorder.text"))); final String tip = resource.getString(className + ".pluginBox.toolTipText"); // Define pluginBox @@ -366,8 +457,8 @@ private static class PluginPane // Layout final FormBuilder builder = FormBuilder.create().layout(layout2).panel(this); - builder.addRaw(pluginBox).xy(1, 1); - builder.addRaw(new JLabel(tip)).xy(3, 1); + builder.addRaw(pluginBox).xy(2, 1); + builder.addRaw(new JLabel(tip)).xy(4, 1); // Initial status pluginBox.setSelectedItem(PluginsManager.defaultPluginId.getValue()); @@ -403,9 +494,9 @@ private static class ScalingPane private final String sliderText; - ScalingPane () + public ScalingPane () { - String className = getClass().getSimpleName(); + final String className = getClass().getSimpleName(); sliderText = resource.getString(className + ".slider.text"); // Define slider @@ -468,7 +559,7 @@ private int tickOf (double ratio) private static class SeparatePane extends OutputPane { - SeparatePane () + public SeparatePane () { box.setSelected(BookManager.useSeparateBookFolders().isSet()); } @@ -489,13 +580,19 @@ public void actionPerformed (ActionEvent e) private static class SiblingPane extends OutputPane { + final DefaultOutputPane defaultPane; + final SeparatePane separatePane; - SiblingPane (SeparatePane separatePane) + public SiblingPane (DefaultOutputPane defaultPane, + SeparatePane separatePane) { + this.defaultPane = defaultPane; this.separatePane = separatePane; + final boolean isSet = BookManager.useInputBookFolder().isSet(); box.setSelected(isSet); + defaultPane.setEnabled(!isSet); separatePane.setEnabled(!isSet); } @@ -504,6 +601,7 @@ public void actionPerformed (ActionEvent e) { final boolean isSet = box.isSelected(); BookManager.useInputBookFolder().setValue(isSet); + defaultPane.setEnabled(!isSet); separatePane.setEnabled(!isSet); } } @@ -559,8 +657,7 @@ private static class TopicPane extends Panel implements ActionListener { - // Handled topic - private final Topic topic; + private final Topic topic; // Handled topic /** * Build a pane for one topic. @@ -568,8 +665,8 @@ private static class TopicPane * @param topic enum Topic * @param topicName translated value of enum topic */ - TopicPane (Topic topic, - String topicName) + public TopicPane (Topic topic, + String topicName) { this.topic = topic; @@ -579,7 +676,7 @@ private static class TopicPane desc = topic.getDescription(); } - JCheckBox box = new JCheckBox(); + final JCheckBox box = new JCheckBox(); box.addActionListener(this); box.setSelected(topic.isSet()); @@ -593,7 +690,7 @@ private static class TopicPane @Override public void actionPerformed (ActionEvent e) { - JCheckBox box = (JCheckBox) e.getSource(); + final JCheckBox box = (JCheckBox) e.getSource(); topic.set(box.isSelected()); } } diff --git a/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences.properties b/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences.properties index 014c59617..c53ba6790 100644 --- a/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences.properties +++ b/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences.properties @@ -22,16 +22,22 @@ Preferences.title=Preferences #---------- EarlyPane.stepBox.toolTipText = Step triggered on image input +EarlyPane.titledBorder.text = Early steps # PluginPane #----------- PluginPane.pluginBox.toolTipText = Plugin launched on MusicXML output +PluginPane.titledBorder.text = Default plugin # OutputsPane -#----------- +#------------ + +OutputsPane.titledBorder.text = Output folder -OutputsPane.titledBorder.text = Target output folders +DefaultOutputPane.text = Browse +DefaultOutputPane.toolTipText = Default output folder. +DefaultOutputPane.title = Selection of default output folder SiblingPane.text = Input sibling SiblingPane.desc = Use same folder as input diff --git a/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences_fr.properties b/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences_fr.properties index 804fc7b4a..a48fbe68f 100644 --- a/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences_fr.properties +++ b/app/src/main/java/org/audiveris/omr/ui/action/resources/Preferences_fr.properties @@ -35,16 +35,22 @@ Topic.DEBUG.toolTipText = Fonctions de debug #---------- EarlyPane.stepBox.toolTipText = Etape d\u00e9clench\u00e9e sur une lecture d'image +EarlyPane.titledBorder.text = Etapes initiales # PluginPane #----------- PluginPane.pluginBox.toolTipText = Plugin lanc\u00e9 sur la sortie MusicXML +PluginPane.titledBorder.text = Plugin par d\u00e9faut # OutputsPane -#----------- +#------------ + +OutputsPane.titledBorder.text = R\u00e9pertoire de sortie -OutputsPane.titledBorder.text = R\u00e9pertoires de sortie +DefaultOutputPane.text = Parcourir +DefaultOutputPane.toolTipText = R\u00e9pertoire de sortie par d\u00e9faut +DefaultOutputPane.title = Selection of default output folder SiblingPane.text = Idem entr\u00e9e SiblingPane.desc = Utiliser le m\u00eame r\u00e9pertoire que l'entr\u00e9e diff --git a/app/src/main/java/org/audiveris/omr/ui/util/Panel.java b/app/src/main/java/org/audiveris/omr/ui/util/Panel.java index 5939737ca..9ab41af3e 100644 --- a/app/src/main/java/org/audiveris/omr/ui/util/Panel.java +++ b/app/src/main/java/org/audiveris/omr/ui/util/Panel.java @@ -114,8 +114,7 @@ public Insets getInsets () // paintComponent // //----------------// /** - * This method is redefined to give a chance to draw the cell boundaries - * if so desired. + * This method is redefined to give a chance to draw the cell boundaries if so desired. * * @param g the graphic context */ diff --git a/app/src/main/java/org/audiveris/omr/ui/util/UIUtil.java b/app/src/main/java/org/audiveris/omr/ui/util/UIUtil.java index 24aeab797..e59ddf9c9 100644 --- a/app/src/main/java/org/audiveris/omr/ui/util/UIUtil.java +++ b/app/src/main/java/org/audiveris/omr/ui/util/UIUtil.java @@ -192,13 +192,12 @@ public static void adjustDefaultTexts () UIUtil.class); // OptionPane texts - final String[] keys = new String[] - { - "OptionPane.inputDialogTitle", - "OptionPane.messageDialogTitle", - "OptionPane.titleText", - "OptionPane.cancelButtonText", - "OptionPane.noButtonText", + final String[] keys = new String[] { // + "OptionPane.inputDialogTitle", // + "OptionPane.messageDialogTitle", // + "OptionPane.titleText", // + "OptionPane.cancelButtonText", // + "OptionPane.noButtonText", // "OptionPane.yesButtonText" }; for (String key : keys) { @@ -244,18 +243,18 @@ public static Color complementaryColor (Color color) /** * Let the user select a directory. * + * @param save true for a SAVE dialog, false for a LOAD dialog * @param parent the parent component for the dialog * @param startDir the starting directory * @param title specific dialog title if any, null otherwise * @return the chosen directory, or null */ - public static File directoryChooser (Component parent, + public static File directoryChooser (boolean save, + Component parent, File startDir, String title) { - // String oldMacProperty = System.getProperty("apple.awt.fileDialogForDirectories", "false"); - // System.setProperty("apple.awt.fileDialogForDirectories", "true"); - OmrFileFilter filter = new OmrFileFilter("Directories", new String[] {}) + final OmrFileFilter filter = new OmrFileFilter("Directories", new String[] {}) { @Override public boolean accept (File f) @@ -264,32 +263,71 @@ public boolean accept (File f) } }; - final JFileChooser fc = new JFileChooser(); - fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); // FILES needed if dir doesn't exist - fc.addChoosableFileFilter(filter); // To display directories list - fc.setFileFilter(filter); // To display this list by default - ///fc.setAcceptAllFileFilterUsed(false); // Don't display the AllFiles list + File dir = null; - if (title != null) { - fc.setDialogTitle(title); - } + if (WellKnowns.MAC_OS_X) { + if ((parent == null) && (org.audiveris.omr.OMR.gui != null)) { + parent = org.audiveris.omr.OMR.gui.getFrame(); + } - /// - if (startDir != null) { - // Pre-select the proposed directory - File parentDir = startDir.getParentFile(); - fc.setCurrentDirectory(parentDir); - fc.setSelectedFile(startDir); - } + Component parentFrame = parent; + + if (parentFrame != null) { + while (parentFrame.getParent() != null) { + parentFrame = parentFrame.getParent(); + } + } + + try { + final FileDialog fd = new FileDialog((Frame) parentFrame); + + if (startDir != null) { + fd.setDirectory(startDir.getPath()); + } + + fd.setMode(save ? FileDialog.SAVE : FileDialog.LOAD); + fd.setFilenameFilter(filter); + + if (title == null) { + title = save ? "Saving: " : "Loading: "; + title += filter.getDescription(); + } - final int result = fc.showSaveDialog(parent); + fd.setTitle(title); + fd.setVisible(true); - // System.setProperty("apple.awt.fileDialogForDirectories", oldMacProperty); - if (result == JFileChooser.APPROVE_OPTION) { - return fc.getSelectedFile(); + final String dirName = fd.getDirectory(); + + if (dirName != null) { + dir = new File(dirName); + } + } catch (ClassCastException e) { + logger.warn("no ancestor is Frame"); + } } else { - return null; + final JFileChooser fc = new JFileChooser(); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + // Pre-select the directory + if (startDir != null) { + fc.setCurrentDirectory(startDir); + } + + fc.addChoosableFileFilter(filter); + fc.setFileFilter(filter); + + if (title != null) { + fc.setDialogTitle(title); + } + + int result = save ? fc.showSaveDialog(parent) : fc.showOpenDialog(parent); + + if (result == JFileChooser.APPROVE_OPTION) { + dir = fc.getSelectedFile(); + } } + + return dir; } //---------------// @@ -792,8 +830,7 @@ public static Stroke setAbsoluteDashedStroke (Graphics g, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, - new float[] - { 6.0f / (float) ratio }, + new float[] { 6.0f / (float) ratio }, 0.0f); g2.setStroke(stroke);