//----------------------------------------------------------------------------// // // // B o a r d // // // //----------------------------------------------------------------------------// // <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.selection.SelectionService; import omr.selection.UserEvent; import omr.ui.util.Panel; import omr.util.ClassUtil; import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; import org.bushe.swing.event.EventSubscriber; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Component; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTextField; /** * Class {@code Board} defines the common properties of any user board * such as PixelBoard, SectionBoard, and the like. * * <p>Each board has a standard header composed of a title, a horizontal * separator and perhaps a dump button. * The board body is handled by the subclass.</p> * * <p>Any board can be (de)selected in its containing {@link BoardsPane}. * This can be done programmatically using {@link #setSelected(boolean)} * and manually (via a right-click in the BoardsPane).</p> * * <p>Only selected boards can be seen in the BoardsPane display. A selected * board can be made currently (in)visible, programmatically using * {@link #setVisible(boolean)}. * Typically, {@link omr.check.CheckBoard}'s are visible only when they carry * glyph information.</p> * * <p>By default, any board can have a related SelectionService, used for * subscribe (input) and publish (output). When {@link #connect} is called, the * board instance is subscribed to its SelectionService for a specific * collection of event classes. Similarly, {@link #disconnect} unsubscribes the * Board instance from the same event classes.</p> * * <p>This is still an abstract class, since the onEvent() method must be * provided by every subclass.</p> * * @author Hervé Bitteur */ public abstract class Board implements EventSubscriber<UserEvent>, Comparable<Board> { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Board.class); // Predefined boards names with preferred display positions public static final Desc PIXEL = new Desc("Pixel", 100); public static final Desc RUN = new Desc("Run", 200); public static final Desc SECTION = new Desc("Section", 250); public static final Desc SAMPLE = new Desc("Sample", 400); public static final Desc GLYPH = new Desc("Glyph", 500); public static final Desc FOCUS = new Desc("Focus", 600); public static final Desc EVAL = new Desc("Eval", 700); public static final Desc SHAPE = new Desc("Shape", 800); public static final Desc CHECK = new Desc("Check", 900); //~ Instance fields -------------------------------------------------------- /** The board instance name */ private final String name; /** The hosting BoardsPane, if any */ private BoardsPane parent; /** The board header */ private final Header header; /** The body part of the component */ private final Panel body = new Panel(); /** The swing component of the board instance */ private final Panel component = new Panel(); /** The event service this board interacts with */ private final SelectionService selectionService; /** The collection of event classes to be observed */ private final Class<?>[] eventsRead; /** The preferred position in BoardsPane sequence */ private final int position; /** Board is selected? */ private boolean selected; //~ Constructors ----------------------------------------------------------- //-------// // Board // //-------// /** * Create a board. * * @param desc the board descriptor * @param selectionService the related selection service for input & output * @param eventList the collection of event classes to observe * @param withDump true for a dump button * @param selected true to make the board initially selected */ public Board (Desc desc, SelectionService selectionService, Class<?>[] eventList, boolean withDump, boolean selected) { this( desc.name, desc.position, selectionService, eventList, withDump, selected); } //-------// // Board // //-------// /** * Create a board. * * @param name a name assigned to the board * @param position the preferred position within BoardsPane display * @param selectionService the related selection service for input & output * @param eventList the collection of event classes to observe * @param withDump true for a dump button * @param selected true to make the board initially selected */ public Board (String name, int position, SelectionService selectionService, Class[] eventList, boolean withDump, boolean selected) { this.name = name; this.position = position; this.selectionService = selectionService; this.eventsRead = eventList; this.selected = selected; // Layout header and body parts header = new Header(name, withDump); defineBoardLayout(); } //~ Methods ---------------------------------------------------------------- //-----------// // compareTo // //-----------// /** * Allow to sort boards according to their preferred display position. * * @param that the other board to compare to * @return comparison result */ @Override public int compareTo (Board that) { return Integer.signum(this.position - that.position); } //---------// // connect // //---------// /** * Connect to input selections. */ public void connect () { if (eventsRead != null) { for (Class<?> eventClass : eventsRead) { selectionService.subscribeStrongly(eventClass, this); // Refresh with latest data for this event class UserEvent event = (UserEvent) selectionService.getLastEvent( eventClass); if (event != null) { event.movement = null; onEvent(event); } } } } //------------// // disconnect // //------------// /** * Disconnect from input selections. */ public void disconnect () { if (eventsRead != null) { for (Class<?> eventClass : eventsRead) { selectionService.unsubscribe(eventClass, this); } } } //-------------// // emptyFields // //-------------// /** * Convenient method to empty all the text fields of a given JComponent. * * @param component the component to "blank". */ public static void emptyFields (JComponent component) { for (Component comp : component.getComponents()) { if (comp instanceof JTextField) { ((JTextField) comp).setText(""); } } } //--------------// // getComponent // //--------------// /** * Report the UI component. * * @return the concrete component */ public JPanel getComponent () { return component; } //---------// // getName // //---------// /** * Report the name for this board instance. * * @return an instance name */ public String getName () { return name; } //------------// // isSelected // //------------// /** * Report whether this board is currently selected. * * @return true if selected */ public boolean isSelected () { return selected; } //-----------// // setParent // //-----------// public void setParent (BoardsPane parent) { this.parent = parent; } //-------------// // setSelected // //-------------// /** * Select or not this board in its containing BoardsPane. * * @param bool true for selected */ public void setSelected (boolean bool) { selected = bool; if (parent != null) { parent.update(); } } //------------// // setVisible // //------------// /** * Make this board visible or not. * * @param bool true for visible */ public void setVisible (boolean bool) { component.setVisible(bool); } //----------// // toString // //----------// @Override public String toString () { return "{" + ClassUtil.nameOf(this) + " " + name + "}"; } //---------// // getBody // //---------// /** * Report the body part of the board. * * @return the body */ protected JPanel getBody () { return body; } //---------------// // getDumpButton // //---------------// /** * Report the Dump button of the board, if any. * * @return the dump button, or null */ protected JButton getDumpButton () { return header.dumpButton; } //---------------------// // getSelectionService // //---------------------// /** * Report the selection service this board is linked to. * * @return the selectionService */ protected SelectionService getSelectionService () { return selectionService; } //-------------------// // defineBoardLayout // //-------------------// private void defineBoardLayout () { component.setNoInsets(); body.setNoInsets(); CellConstraints cst = new CellConstraints(); FormLayout layout = new FormLayout( "pref", "pref," + Panel.getFieldInterline() + ",pref"); PanelBuilder builder = new PanelBuilder(layout, component); builder.add(header, cst.xy(1, 1)); builder.add(body, cst.xy(1, 3)); } //~ Inner Classes ---------------------------------------------------------- //------// // Desc // //------// /** * A way to describe a board kind. */ public static class Desc { //~ Instance fields ---------------------------------------------------- /** Default name for this board */ public final String name; /** Preferred position within its containing BoardsPane */ public final int position; //~ Constructors ------------------------------------------------------- public Desc (String name, int position) { this.name = name; this.position = position; } } //--------// // Header // //--------// /** * The board header is a horizontal line with the board title, * and perhaps a dump button. */ private static class Header extends Panel { //~ Instance fields ---------------------------------------------------- /** The board title */ private final String title; /** Dump button */ public final JButton dumpButton; //~ Constructors ------------------------------------------------------- public Header (String title, boolean withDump) { this.title = title; dumpButton = withDump ? new JButton("Dump") : null; setNoInsets(); defineLayout(); } //~ Methods ------------------------------------------------------------ private void defineLayout () { CellConstraints cst = new CellConstraints(); FormLayout layout = new FormLayout( "152" + "dlu," + Panel.getFieldInterval() + ",35dlu", "pref"); PanelBuilder builder = new PanelBuilder(layout, this); if (dumpButton != null) { builder.addSeparator(title, cst.xy(1, 1)); builder.add(dumpButton, cst.xyw(3, 1, 1)); } else { builder.addSeparator(title, cst.xyw(1, 1, 3)); } } } }