//------------------------------------------------------------------------------------------------// // // // S h a p e C o l o r C h o o s e r // // // //------------------------------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Audiveris 2018. 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 <http://www.gnu.org/licenses/>. //------------------------------------------------------------------------------------------------// // </editor-fold> package org.audiveris.omr.glyph.ui; import org.audiveris.omr.glyph.Shape; import org.audiveris.omr.glyph.ShapeSet; import org.audiveris.omr.ui.OmrGui; import org.jdesktop.application.Application; import org.jdesktop.application.ResourceMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.WindowConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * Class {@code ShapeColorChooser} offers a convenient user interface to choose proper * color for each glyph shape. * It is derived from the Sun Java tutorial ColorChooserDemo. * <p> * The right part of the panel is used by a classic color chooser * <p> * The left part of the panel is used by the shape at hand, with a way to * browse through the various defined shapes. * <p> * The strategy is the following: First the predefined shape ranges (such as "Physicals", "Bars", * "Clefs", ...) have their own color defined. Then, each individual shape within these shape ranges * has its color assigned by default of the containing range, unless a color is specifically * assigned to this individual shape. * * @author Hervé Bitteur */ public class ShapeColorChooser implements ChangeListener { private static final Logger logger = LoggerFactory.getLogger(ShapeColorChooser.class); private static JFrame frame; /** The classic color chooser utility */ private final JColorChooser colorChooser; /** Color chosen in the JColorChooser utility */ private Color chosenColor; /** UI component */ private final JPanel component; /** To select shape range */ private final RangesPane ranges; /** To select shape (within selected range) */ private final ShapesPane shapes; /** * Create an instance of ShapeColorChooser (should be improved to always * reuse the same instance. TODO) */ private ShapeColorChooser () { component = new JPanel(new BorderLayout()); // Range Panel ranges = new RangesPane(); shapes = new ShapesPane(); // Global Panel JPanel globalPanel = new JPanel(new BorderLayout()); globalPanel.add(ranges, BorderLayout.NORTH); globalPanel.add(shapes, BorderLayout.SOUTH); globalPanel.setPreferredSize(new Dimension(400, 400)); // Color chooser colorChooser = new JColorChooser(); colorChooser.getSelectionModel().addChangeListener(this); colorChooser.setBorder(BorderFactory.createTitledBorder("Choose Shape Color")); component.add(globalPanel, BorderLayout.CENTER); component.add(colorChooser, BorderLayout.EAST); } //-----------// // showFrame // //-----------// /** * Display the UI frame. */ public static void showFrame () { if (frame == null) { frame = new JFrame(); frame.setName("shapeColorChooserFrame"); frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); frame.add(new ShapeColorChooser().component); // Resources injection ResourceMap resource = Application.getInstance().getContext().getResourceMap( ShapeColorChooser.class); resource.injectComponents(frame); } OmrGui.getApplication().show(frame); } //--------------// // stateChanged // //--------------// /** * Triggered when color selection in the color chooser has changed. * * @param e not used */ @Override public void stateChanged (ChangeEvent e) { chosenColor = colorChooser.getColor(); ///logger.info("chosenColor: " + chosenColor); ranges.colorChanged(); shapes.colorChanged(); } //------// // Pane // //------// private abstract class Pane extends JPanel { public JLabel banner = new JLabel("", JLabel.CENTER); public JPopupMenu menu = new JPopupMenu(); public Pane (String title) { setLayout(new BorderLayout()); setBorder(BorderFactory.createTitledBorder(title)); banner.setForeground(Color.black); banner.setBackground(Color.white); banner.setOpaque(true); banner.setFont(new Font("SansSerif", Font.BOLD, 18)); add(banner, BorderLayout.CENTER); } public abstract void colorChanged (); protected abstract void refreshBanner (); } //------------// // RangesPane // //------------// private class RangesPane extends Pane { public ShapeSet current; private SelectAction select = new SelectAction(); private JButton selectButton = new JButton(select); private PasteAction paste = new PasteAction(); private ActionListener selectionListener = new ActionListener() { // Called when a range has been selected @Override public void actionPerformed (ActionEvent e) { JMenuItem source = (JMenuItem) e.getSource(); current = ShapeSet.valueOf(source.getText()); if (current != null) { banner.setText(current.getName()); Color color = current.getColor(); if (color != null) { //colorChooser.setColor(color); refreshBanner(); } else { banner.setForeground(Color.black); } paste.setEnabled(false); shapes.setRange(); } else { banner.setText(""); } } }; private JButton pasteButton = new JButton(paste); public RangesPane () { super("Shape Range"); add(selectButton, BorderLayout.NORTH); add(pasteButton, BorderLayout.SOUTH); paste.setEnabled(false); buildRangesMenu(); } // When color chooser selection has been made @Override public void colorChanged () { if (current != null) { paste.setEnabled(true); } } @Override protected void refreshBanner () { if (current != null) { banner.setForeground(current.getColor()); } } private void buildRangesMenu () { menu.removeAll(); ShapeSet.addAllShapeSets(menu, selectionListener); } private class PasteAction extends AbstractAction { public PasteAction () { super("Paste"); } @Override public void actionPerformed (ActionEvent e) { current.setConstantColor(chosenColor); setEnabled(false); refreshBanner(); buildRangesMenu(); // Forward to contained shapes ? shapes.setRange(); } } private class SelectAction extends AbstractAction { public SelectAction () { super("Select"); } @Override public void actionPerformed (ActionEvent e) { JButton button = (JButton) e.getSource(); menu.show(RangesPane.this, button.getX(), button.getY()); } } } //------------// // ShapesPane // //------------// private class ShapesPane extends Pane { public Shape current; private ActionListener selectionListener = new ActionListener() { // Called when a shape has been selected @Override public void actionPerformed (ActionEvent e) { JMenuItem source = (JMenuItem) e.getSource(); current = Shape.valueOf(source.getText()); banner.setText(current.toString()); // Check if a specific color is assigned Color color = current.getColor(); if (color == ranges.current.getColor()) { prepareDefaultOption(); refreshBanner(); } else { prepareSpecificOption(); banner.setForeground(ranges.current.getColor()); } } }; private final CopyAction copy = new CopyAction(); private final CutAction cut = new CutAction(); private final PasteAction paste = new PasteAction(); private final SelectAction select = new SelectAction(); private final JButton selectButton = new JButton(select); private boolean isSpecific; public ShapesPane () { super("Individual Shape"); selectButton.setText("-- Select Shape --"); // No range has been selected yet, so no shape can be selected select.setEnabled(false); add(selectButton, BorderLayout.NORTH); // A series of 3 buttons at the bottom JPanel buttons = new JPanel(new GridLayout(1, 3)); buttons.add(new JButton(cut)); buttons.add(new JButton(copy)); buttons.add(new JButton(paste)); add(buttons, BorderLayout.SOUTH); cut.setEnabled(false); copy.setEnabled(false); paste.setEnabled(false); } // When color chooser selection has been made @Override public void colorChanged () { updateActions(); } // When a new range has been selected public void setRange () { buildShapesMenu(); select.setEnabled(true); selectButton.setText("-- Select Shape from " + ranges.current.getName() + " --"); current = null; banner.setText(""); cut.setEnabled(false); copy.setEnabled(false); paste.setEnabled(false); } @Override protected void refreshBanner () { if (current != null) { banner.setForeground(current.getColor()); } } private void buildShapesMenu () { menu.removeAll(); // Add all shapes within current range ShapeSet.addSetShapes(ranges.current, menu, selectionListener); } private void prepareDefaultOption () { isSpecific = true; updateActions(); } private void prepareSpecificOption () { isSpecific = false; updateActions(); if (chosenColor != null) { paste.setEnabled(chosenColor != ranges.current.getColor()); } else { paste.setEnabled(false); } } private void updateActions () { if (current != null) { cut.setEnabled(isSpecific); copy.setEnabled(isSpecific); paste.setEnabled(!isSpecific); } else { cut.setEnabled(false); copy.setEnabled(false); paste.setEnabled(false); } } private class CopyAction extends AbstractAction { public CopyAction () { super("Copy"); } @Override public void actionPerformed (ActionEvent e) { colorChooser.setColor(current.getColor()); } } private class CutAction extends AbstractAction { public CutAction () { super("Cut"); } @Override public void actionPerformed (ActionEvent e) { // Drop specific for default current.setColor(ranges.current.getColor()); prepareSpecificOption(); refreshBanner(); buildShapesMenu(); } } private class PasteAction extends AbstractAction { public PasteAction () { super("Paste"); } @Override public void actionPerformed (ActionEvent e) { // Set a specific color current.setConstantColor(chosenColor); prepareDefaultOption(); refreshBanner(); buildShapesMenu(); } } private class SelectAction extends AbstractAction { public SelectAction () { super("Select"); } @Override public void actionPerformed (ActionEvent e) { JButton button = (JButton) e.getSource(); menu.show(ShapesPane.this, button.getX(), button.getY()); } } } }