//----------------------------------------------------------------------------// // // // P a g e M e n u // // // //----------------------------------------------------------------------------// // <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.score.ui; import omr.glyph.facets.Glyph; import omr.glyph.ui.SymbolMenu; import omr.glyph.ui.SymbolsController; import omr.score.entity.Chord; import omr.score.entity.Measure; import omr.score.entity.Note; import omr.score.entity.Page; import omr.score.entity.ScoreSystem; import omr.score.entity.Slot; import omr.sheet.Sheet; import omr.sheet.SystemInfo; import omr.sheet.ui.BoundaryEditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; /** * Class {@code PageMenu} defines the popup menu which is linked to the * current selection in page editor view. * <p>It points to several sub-menus: measure, slot, chords, glyphs, boundaries * </p> * * @author Hervé Bitteur */ public class PageMenu { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(PageMenu.class); //~ Instance fields -------------------------------------------------------- // /** The related page. */ private final Page page; /** Actions to update according to current selections. */ private final Collection<DynAction> dynActions = new HashSet<>(); /** Concrete popup menu. */ private final JPopupMenu popup; /** Measure submenu. */ private final MeasureMenu measureMenu = new MeasureMenu(); /** Slot submenu. */ private final SlotMenu slotMenu = new SlotMenu(); /** Chord submenu. */ private final ChordMenu chordMenu = new ChordMenu(); /** Glyph submenu. */ private final SymbolMenu symbolMenu; /** Boundary submenu. */ private final BoundaryEditor boundaryEditor; // Context // /** Selected system. */ private ScoreSystem system; /** Selected measure. */ private Measure measure; /** Selected slot. */ private Slot slot; /** Selected chords. */ private Set<Chord> selectedChords; /** Number of chords referred to. */ private int chordNb = 0; /** Number of glyphs in the selection. */ private int glyphNb = 0; //~ Constructors ----------------------------------------------------------- // //----------// // PageMenu // //----------// /** * Create the page menu. * * @param page the related page */ public PageMenu (Page page, SymbolMenu symbolMenu) { this.page = page; this.symbolMenu = symbolMenu; boundaryEditor = page.getSheet().getBoundaryEditor(); popup = new JPopupMenu(); defineLayout(); // Initialize all dynamic actions for (DynAction action : dynActions) { action.update(); } } //~ Methods ---------------------------------------------------------------- // //----------// // getPopup // //----------// /** * Report the concrete popup menu. */ public JPopupMenu getPopup () { return popup; } //------------// // updateMenu // //------------// /** * Update the popup menu according to the currently selected glyphs. * * @param point the point designated in the page display */ public void updateMenu (Point point) { // Analyze the context to retrieve designated system, measure & slot Sheet sheet = page.getSheet(); List<SystemInfo> systems = sheet.getSystems(); if (systems != null) { SystemInfo systemInfo = sheet.getSystemOf(point); if (systemInfo != null) { system = systemInfo.getScoreSystem(); } slot = sheet.getSymbolsEditor().getSlotAt(point); if (slot != null) { measure = slot.getMeasure(); } else { measure = null; } // Update all dynamic actions accordingly for (DynAction action : dynActions) { action.update(); } } // Update symbol menu glyphNb = symbolMenu.updateMenu(); // Update chord menu chordNb = chordMenu.updateMenu(); // Update boundary menu boundaryEditor.updateMenu(); defineLayout(); } //--------------// // defineLayout // //--------------// private void defineLayout () { popup.removeAll(); // Measure if (measure != null) { popup.add(measureMenu.getMenu()); } // Slot if (slot != null) { popup.add(slotMenu.getMenu()); } // Related chords? if (chordNb > 0) { popup.add(chordMenu.getMenu()); } // Symbol if (glyphNb > 0) { popup.add(symbolMenu.getMenu()); } // Boundary popup.add(boundaryEditor.getMenu()); } //~ Inner Classes ---------------------------------------------------------- // //-----------// // DynAction // //-----------// /** * Base implementation, to register the dynamic actions that need * to be updated according to the current glyph selection context. */ public abstract class DynAction extends AbstractAction { //~ Constructors ------------------------------------------------------- public DynAction () { // Record the instance dynActions.add(this); } //~ Methods ------------------------------------------------------------ public abstract void update (); } //-------------// // MeasureMenu // //-------------// private class MeasureMenu extends DynAction { //~ Instance fields ---------------------------------------------------- /** Concrete menu */ private final JMenu menu; //~ Constructors ------------------------------------------------------- /** * Create the popup menu * * @param page the related page */ public MeasureMenu () { menu = new JMenu("Measure"); defineLayout(); } //~ Methods ------------------------------------------------------------ @Override public void actionPerformed (ActionEvent e) { // Default action is to open the menu assert false; } public JMenu getMenu () { return menu; } @Override public void update () { menu.setEnabled(measure != null); if (measure != null) { menu.setText("Measure #" + measure.getScoreId() + " ..."); } else { menu.setText("no measure"); } } private void defineLayout () { // Measure ///menu.add(new JMenuItem(new PlayAction())); menu.add(new JMenuItem(new DumpAction())); } //~ Inner Classes ------------------------------------------------------ //------------// // DumpAction // //------------// /** * Dump the current measure */ private class DumpAction extends DynAction { //~ Constructors --------------------------------------------------- public DumpAction () { putValue(NAME, "Dump voices"); putValue(SHORT_DESCRIPTION, "Dump the voices of the selected measure"); } //~ Methods -------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { measure.printVoices(null); } @Override public void update () { setEnabled(measure != null); } } } //-----------// // ChordMenu // //-----------// /** * Dump the chords that translate the selected glyphs. */ private class ChordMenu extends DynAction { //~ Instance fields ---------------------------------------------------- /** Concrete menu */ private final JMenu menu; //~ Constructors ------------------------------------------------------- public ChordMenu () { menu = new JMenu("Chord"); defineLayout(); } public JMenuItem getMenu () { return menu; } @Override public void update () { if (chordNb > 0) { menu.setText("Chords"); } else { menu.setText("no chords"); } } @Override public void actionPerformed (ActionEvent e) { // Void } public int updateMenu () { SymbolsController controller = page.getSheet().getSymbolsController(); Set<Glyph> glyphs = controller.getNest().getSelectedGlyphSet(); selectedChords = new HashSet<>(); if (glyphs != null) { for (Glyph glyph : glyphs) { for (Object obj : glyph.getTranslations()) { if (obj instanceof Note) { Note note = (Note) obj; Chord chord = note.getChord(); if (chord != null) { selectedChords.add(chord); } } else if (obj instanceof Chord) { selectedChords.add((Chord) obj); } } } if (!selectedChords.isEmpty()) { List<Chord> chordList = new ArrayList<>(selectedChords); Collections.sort(chordList, Chord.byAbscissa); StringBuilder sb = new StringBuilder(); for (Chord chord : chordList) { if (sb.length() > 0) { sb.append(", "); } sb.append("Chord #") .append(chord.getId()); } sb.append(" ..."); menu.setText(sb.toString()); } } return selectedChords.size(); } private void defineLayout () { menu.add(new JMenuItem(new DumpAction())); } //~ Inner Classes ------------------------------------------------------ //------------// // DumpAction // //------------// /** * Dump the current chord(s) */ private class DumpAction extends DynAction { //~ Constructors --------------------------------------------------- public DumpAction () { putValue(NAME, "Dump chord(s)"); putValue(SHORT_DESCRIPTION, "Dump the selected chord(s)"); } //~ Methods -------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { // Dump the selected chords for (Chord chord : selectedChords) { logger.info(chord.toString()); } } @Override public void update () { setEnabled(chordNb > 0); } } } //----------// // SlotMenu // //----------// private class SlotMenu extends DynAction { //~ Instance fields ---------------------------------------------------- /** Concrete menu */ private final JMenu menu; //~ Constructors ------------------------------------------------------- public SlotMenu () { menu = new JMenu("Slot"); defineLayout(); } //~ Methods ------------------------------------------------------------ @Override public void actionPerformed (ActionEvent e) { // Default action is to open the menu assert false; } public JMenu getMenu () { return menu; } @Override public void update () { menu.setEnabled(slot != null); if (slot != null) { menu.setText("Slot #" + slot.getId() + " ..."); } else { menu.setText("no slot"); } } //--------------// // defineLayout // //--------------// private void defineLayout () { // Slot menu.add(new JMenuItem(new DumpSlotChordsAction())); menu.add(new JMenuItem(new DumpVoicesAction())); } //~ Inner Classes ------------------------------------------------------ // //----------------------// // DumpSlotChordsAction // //----------------------// /** * Dump the chords of the current slot */ private class DumpSlotChordsAction extends DynAction { //~ Constructors --------------------------------------------------- public DumpSlotChordsAction () { putValue(NAME, "Dump chords"); putValue(SHORT_DESCRIPTION, "Dump the chords of the selected slot"); } //~ Methods -------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { logger.info(slot.toChordString()); } @Override public void update () { setEnabled(slot != null); } } //------------------// // DumpVoicesAction // //------------------// /** * Dump the voices of the current slot */ private class DumpVoicesAction extends DynAction { //~ Constructors --------------------------------------------------- public DumpVoicesAction () { putValue(NAME, "Dump voices"); putValue(SHORT_DESCRIPTION, "Dump the voices of the selected slot"); } //~ Methods -------------------------------------------------------- @Override public void actionPerformed (ActionEvent e) { logger.info(slot.toVoiceString()); } @Override public void update () { setEnabled(slot != null); } } } }