//----------------------------------------------------------------------------// // // // M a i n G u i // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.ui; import omr.Main; import omr.WellKnowns; import omr.action.ActionManager; import omr.action.Actions; import omr.constant.Constant; import omr.constant.ConstantManager; import omr.constant.ConstantSet; import omr.log.LogPane; import omr.plugin.PluginsManager; import omr.score.Score; import omr.score.ScoreExporter; import omr.selection.MouseMovement; import omr.selection.SheetEvent; import omr.sheet.Sheet; import omr.sheet.picture.jai.JaiLoader; import omr.sheet.ui.SheetActions; import omr.sheet.ui.SheetsController; import omr.step.StepMenu; import omr.step.Stepping; import omr.ui.dnd.GhostGlassPane; import omr.ui.symbol.MusicFont; import omr.ui.util.ModelessOptionPane; import omr.ui.util.Panel; import omr.ui.util.SeparableMenu; import omr.ui.util.UIUtil; import omr.util.OmrExecutors; import omr.util.WeakPropertyChangeListener; import org.bushe.swing.event.EventSubscriber; import org.jdesktop.application.Application; import org.jdesktop.application.ResourceMap; import org.jdesktop.application.SingleFrameApplication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.GridLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.EventObject; import java.util.concurrent.Callable; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; /** * Class {@code MainGui} is the Java User Interface, the main class for * displaying a score, the related sheet, the message log and the * various tools. * * @author Hervé Bitteur */ public class MainGui extends SingleFrameApplication implements EventSubscriber<SheetEvent>, PropertyChangeListener { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(MainGui.class); //~ Instance fields -------------------------------------------------------- // /** Official name of the application. */ private String appName; /** Sheet tabbed pane, which may contain several views. */ public SheetsController sheetsController; /** The related concrete frame. */ private JFrame frame; /** Bottom pane split betwen the logPane and the errorsPane. */ private JSplitPane bottomPane; /** Log pane, which displays logging info. */ private LogPane logPane; /** Boards pane, which displays a specific set of boards per sheet. */ private BoardsScrollPane boardsScrollPane; /** GlassPane needed to handle drag and drop from shape palette. */ private GhostGlassPane glassPane = new GhostGlassPane(); /** Main pane with Sheet on top and Log+Errors on bottom. */ private JSplitPane mainPane; /** Application pane with Main pane on left and Boeards on right. */ private Panel appPane; /** Step menu. */ private StepMenu stepMenu; //~ Constructors ----------------------------------------------------------- //---------// // MainGui // //---------// /** * Creates a new {@code MainGui} instance, to handle any user * display and interaction. */ public MainGui () { } //~ Methods ---------------------------------------------------------------- //-------------// // getInstance // //-------------// /** * Report the single instance of this application. * * @return the SingleFrameApplication instance */ public static SingleFrameApplication getInstance () { return (SingleFrameApplication) Application.getInstance(); } // //----------// // clearLog // //----------// /** * Erase the content of the log window. */ public void clearLog () { logPane.clearLog(); } //---------------------// // displayConfirmation // //---------------------// /** * Allow to display a modal confirmation dialog with a message. * * @param message the message asking for confirmation * @return true if confirmed, false otherwise */ public boolean displayConfirmation (String message) { int answer = JOptionPane.showConfirmDialog( frame, message, "Confirm - " + appName, JOptionPane.WARNING_MESSAGE); return answer == JOptionPane.YES_OPTION; } //--------------// // displayError // //--------------// /** * Allow to display a modal dialog with an error message. * * @param message the error message */ public void displayError (String message) { JOptionPane.showMessageDialog( frame, message, "Error - " + appName, JOptionPane.ERROR_MESSAGE); } //----------------// // displayMessage // //----------------// /** * Allow to display a modal dialog with an html content. * * @param htmlStr the HTML string */ public void displayMessage (String htmlStr) { JEditorPane htmlPane = new JEditorPane("text/html", htmlStr); htmlPane.setEditable(false); JOptionPane.showMessageDialog( frame, htmlPane, appName, JOptionPane.INFORMATION_MESSAGE); } //------------------------// // displayModelessConfirm // //------------------------// /** * Allow to display a non-modal confirmation dialog. * * @param message the confirmation message * @return the option chosen */ public int displayModelessConfirm (String message) { return ModelessOptionPane.showModelessConfirmDialog( frame, message, "Confirm - " + appName, JOptionPane.YES_NO_OPTION); } //----------------// // displayWarning // //----------------// /** * Allow to display a modal dialog with a message. * * @param message the warning message */ public void displayWarning (String message) { JOptionPane.showMessageDialog( frame, message, "Warning - " + appName, JOptionPane.WARNING_MESSAGE); } //----------// // getFrame // //----------// /** * Report the concrete frame. * * @return the ui frame */ public JFrame getFrame () { return frame; } //--------------// // getGlassPane // //--------------// /** * Report the main window glassPane, needed for shape drag and drop. * * @return the ghost glass pane */ public GhostGlassPane getGlassPane () { return glassPane; } //--------------// // getIconsRoot // //--------------// public String getIconsRoot () { ResourceMap resource = Application.getInstance() .getContext() .getResourceMap(getClass()); return resource.getString("icons.root"); } //---------// // getName // //---------// /** * Report an Observer name. * * @return observer name */ public String getName () { return "MainGui"; } //-------------// // getStepMenu // //-------------// /** * Report the menu dedicated to steps * * @return the step menu */ public StepMenu getStepMenu () { return stepMenu; } //------------// // hideErrors // //------------// /** * Remove the specific component of the errors pane. * * @param component the precise component to remove */ public void hideErrors (JComponent component) { // To avoid race conditions, check we remove the proper component if ((component != null) && (component == bottomPane.getRightComponent())) { bottomPane.setRightComponent(null); } } //-----------// // notifyLog // //-----------// /** * Tell that one or several new log records are waiting for display. */ public void notifyLog () { logPane.notifyLog(); } //---------// // onEvent // //---------// /** * Notification of sheet selection, to update frame title. * * @param sheetEvent the event about selected sheet */ @Override public void onEvent (SheetEvent sheetEvent) { try { // Ignore RELEASING if (sheetEvent.movement == MouseMovement.RELEASING) { return; } final Sheet sheet = sheetEvent.getData(); SwingUtilities.invokeLater( new Runnable() { @Override public void run () { final StringBuilder sb = new StringBuilder(); if (sheet != null) { Score score = sheet.getScore(); // Frame title tells score name sb.append(score.getImageFile().getName()); } // Update frame title sb.append(" - "); ResourceMap resource = Application.getInstance() .getContext() .getResourceMap( getClass()); sb.append(resource.getString("mainFrame.title")); frame.setTitle(sb.toString()); } }); } catch (Exception ex) { logger.warn(getClass().getName() + " onEvent error", ex); } } //----------------// // propertyChange // //----------------// /** * Called when notified from GuiActions. * * @param evt the event details */ @Override public void propertyChange (PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); Boolean display = (Boolean) evt.getNewValue(); switch (propertyName) { case GuiActions.BOARDS_DISPLAYED: // Toggle display of boards if (display) { appPane.add(boardsScrollPane, BorderLayout.EAST); } else { appPane.remove(boardsScrollPane); } appPane.revalidate(); break; case GuiActions.LOG_DISPLAYED: // Toggle display of log if (display) { bottomPane.setLeftComponent(logPane.getComponent()); } else { bottomPane.setLeftComponent(null); } break; case GuiActions.ERRORS_DISPLAYED: // Toggle display of errors if (display) { JComponent comp = null; Sheet sheet = sheetsController.getSelectedSheet(); if (sheet != null) { ErrorsEditor editor = sheet.getErrorsEditor(); if (editor != null) { comp = editor.getComponent(); } } bottomPane.setRightComponent(comp); } else { bottomPane.setRightComponent(null); } break; } // BottomPane = LogPane | ErrorsPane // Totally remove it when it displays no log and no errors if (needBottomPane()) { mainPane.setBottomComponent(bottomPane); } else { mainPane.setBottomComponent(null); } } //------------------// // removeBoardsPane // //------------------// /** * Remove the current boardsScrollPane, if any. */ public void removeBoardsPane () { boardsScrollPane.setBoards(null); } //---------------// // setBoardsPane // //---------------// /** * Set a new boardspane to the boards holder. * * @param boards the boards pane to be shown */ public void setBoardsPane (JComponent boards) { boardsScrollPane.setBoards(boards); } //------------// // showErrors // //------------// /** * Show the provided errors. * * @param errorsPane the errors to be shown */ public void showErrors (JComponent errorsPane) { bottomPane.setRightComponent(errorsPane); } //------------// // initialize // //------------// /** {@inheritDoc} */ @Override protected void initialize (String[] args) { logger.debug("MainGui. 1/initialize"); // Launch background pre-loading tasks? if (constants.preloadCostlyPackages.getValue()) { JaiLoader.preload(); ScoreExporter.preload(); } } //-------// // ready // //-------// /** {@inheritDoc} */ @Override protected void ready () { logger.debug("MainGui. 3/ready"); // Weakly listen to GUI Actions parameters PropertyChangeListener weak = new WeakPropertyChangeListener(this); GuiActions.getInstance() .addPropertyChangeListener(weak); // Make the GUI instance available for the other classes Main.setGui(this); // Check MusicFont is loaded MusicFont.checkMusicFont(); // Just in case we already have messages pending notifyLog(); // Launch scores for (Callable<Void> task : Main.getFilesTasks()) { OmrExecutors.getCachedLowExecutor() .submit(task); } // Launch scripts for (Callable<Void> task : Main.getScriptsTasks()) { OmrExecutors.getCachedLowExecutor() .submit(task); } } //---------// // startup // //---------// /** {@inheritDoc} */ @Override protected void startup () { logger.debug("MainGui. 2/startup"); frame = getMainFrame(); sheetsController = SheetsController.getInstance(); sheetsController.subscribe(this); defineMenus(); defineLayout(); // Allow dropping files frame.setTransferHandler(new FileDropHandler()); // Handle ghost drop from shape palette frame.setGlassPane(glassPane); // Use the defined application name appName = getContext() .getResourceMap() .getString("Application.name"); // Define an exit listener addExitListener( new ExitListener() { @Override public boolean canExit (EventObject e) { return true; } @Override public void willExit (EventObject e) { // Store latest constant values on disk ConstantManager.getInstance() .storeResource(); } }); // Here we go... show(frame); } //--------------// // defineLayout // //--------------// private void defineLayout () { /* * +=============================================================+ * |toolKeyPanel . . . . . . . . . . . . . . . . . . . . . . . . | * |+=================+=============================+===========+| * || toolBar . . . . . . . . . .| progressBar . . .| Memory . .|| * |+=================+=============================+===========+| * +=============================================================+ * | horiSplitPane . . . . . . . . . . . . . . . . . . . . . . . | * |+=========================================+=================+| * | . . . . . . . . . . . . . . . . . . . . .|boardsScrollPane || * | +========================================+ . . . . . . . . || * | | sheetController . . . . . . . . . . . .| . . . . . . . . || * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * | | . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |v| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |e| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |r| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |t| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |S| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |p| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |l| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |i| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |t| . . . . . . . . . . . . . . . . . . . .| . . . . . . . . || * |P+=====================+==================+ . . . . . . . . || * |a| logPane . . . . . . | errors . . . . . | . . . . . . . . || * |n| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || * |e| . . . . . . . . . . |. . . . . . . . . | . . . . . . . . || * | +=====================+==================+=================+| * +=============================================================+ */ // Individual panes logPane = new LogPane(); boardsScrollPane = new BoardsScrollPane(); boardsScrollPane.setPreferredSize(new Dimension(350, 500)); // Bottom = Log & Errors bottomPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); bottomPane.setBorder(null); bottomPane.setDividerSize(1); bottomPane.setResizeWeight(0.5d); // Cut in half initially // mainPane = sheetsController / bottomPane mainPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT, sheetsController.getComponent(), null); mainPane.setBorder(null); mainPane.setDividerSize(1); mainPane.setResizeWeight(0.9d); // Give bulk space to upper part // horiSplitPane = mainPane | boards appPane = new Panel(); appPane.setNoInsets(); appPane.setBorder(null); appPane.setLayout(new BorderLayout()); appPane.add(mainPane, BorderLayout.CENTER); // + boardsScrollPane later appPane.setName("appPane"); // Global layout: Use a toolbar on top and a double split pane below ///toolBar.add(toolKeyPanel); Container content = frame.getContentPane(); content.setLayout(new BorderLayout()); content.add( ActionManager.getInstance().getToolBar(), BorderLayout.NORTH); content.add(appPane, BorderLayout.CENTER); // Suppress all internal borders, recursively ///UIUtilities.suppressBorders(frame.getContentPane()); // Display the boards pane? if (GuiActions.getInstance() .isBoardsDisplayed()) { appPane.add(boardsScrollPane, BorderLayout.EAST); } // Display the log pane? if (GuiActions.getInstance() .isLogDisplayed()) { bottomPane.setLeftComponent(logPane.getComponent()); } // Display the errors pane? if (GuiActions.getInstance() .isErrorsDisplayed()) { bottomPane.setRightComponent(null); } // BottomPane = Log & Errors if (needBottomPane()) { mainPane.setBottomComponent(bottomPane); } } //-------------// // defineMenus // //-------------// private void defineMenus () { // Specific sheet menu JMenu sheetMenu = new SeparableMenu(); // Specific history sub-menu of sheet menu sheetMenu.add(SheetActions.HistoryMenu.getInstance().getMenu()); // Specific step menu stepMenu = new StepMenu(new SeparableMenu()); // Specific plugin menu JMenu pluginMenu = PluginsManager.getInstance() .getMenu(null); // For some specific top-level menus ActionManager mgr = ActionManager.getInstance(); mgr.injectMenu(Actions.Domain.FILE.name(), sheetMenu); mgr.injectMenu(Actions.Domain.STEP.name(), stepMenu.getMenu()); mgr.injectMenu(Actions.Domain.PLUGIN.name(), pluginMenu); // All other commands (which may populate the toolBar as well) mgr.loadAllDescriptors(); mgr.registerAllActions(); // Menu bar JMenuBar innerBar = mgr.getMenuBar(); // Gauges = progress + memory JPanel gauges = new JPanel(); gauges.setLayout(new BorderLayout()); gauges.add( Stepping.createMonitor().getComponent(), BorderLayout.CENTER); gauges.add(new MemoryMeter().getComponent(), BorderLayout.EAST); // Outer bar = menu + gauges JMenuBar outerBar = new JMenuBar(); outerBar.setLayout(new GridLayout(1, 0)); outerBar.add(innerBar); outerBar.add(gauges); // Remove useless borders UIUtil.suppressBorders(gauges); innerBar.setBorder(null); outerBar.setBorder(null); frame.setJMenuBar(outerBar); // Mac Application menu if (WellKnowns.MAC_OS_X) { MacApplication.setupMacMenus(); } } //----------------// // needBottomPane // //----------------// /** * Check whether we should keep the bottomPane. * * @return true if bottomPane is not empty */ private boolean needBottomPane () { return GuiActions.getInstance() .isLogDisplayed() || GuiActions.getInstance() .isErrorsDisplayed(); } //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- private final Constant.Boolean preloadCostlyPackages = new Constant.Boolean( true, "Should we preload costly packages in the background?"); } // //------------------// // BoardsScrollPane // //------------------// /** * Just a scrollPane to host the pane of user boards, trying to offer * enough room for the boards. */ private class BoardsScrollPane extends JScrollPane { //~ Methods ------------------------------------------------------------ public void setBoards (JComponent boards) { setViewportView(boards); revalidate(); } } }