/* * Copyright (C) 2013-2017 たんらる */ package fourthline.mabiicco.ui; import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Rectangle; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import javax.swing.JTextField; import javax.swing.JButton; import fourthline.mabiicco.ActionDispatcher; import fourthline.mabiicco.AppResource; import static fourthline.mabiicco.AppResource.appText; import fourthline.mabiicco.MabiIccoProperties; import fourthline.mabiicco.ui.PianoRollView.PaintMode; import fourthline.mabiicco.ui.editor.NoteAlign; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Collections; import java.util.function.IntSupplier; import javax.swing.AbstractAction; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JSeparator; import javax.swing.KeyStroke; import javax.swing.JToolBar; import javax.swing.JComboBox; import java.awt.Color; import java.awt.event.KeyEvent; import java.awt.event.InputEvent; import java.io.File; /** * メイン画面. * Menu, ツールバー, 主表示部 {@link MMLSeqView} を生成する. */ public final class MainFrame extends JFrame implements ComponentListener, ActionListener { private static final long serialVersionUID = -7484797594534384422L; private final JPanel contentPane; private final JTextField statusField; private final MMLSeqView mmlSeqView; private final JComboBox<NoteAlign> noteTypeSelect = new JComboBox<>(NoteAlign.createAlignList()); private final JComboBox<PaintMode> paintModeSelect = new JComboBox<>(PaintMode.values()); private final JLabel timeView = new JLabel("time MM:SS/MM:SS (120)"); private final ActionListener listener; /** シーケンス再生中に無効化する機能のリスト */ private final ArrayList<JComponent> noplayFunctions = new ArrayList<>(); /** 状態が変化するメニューたち */ private JMenuItem reloadMenuItem = null; private JMenuItem undoMenu = null; private JMenuItem redoMenu = null; private JMenuItem saveMenuItem = null; private JMenuItem cutMenu = null; private JMenuItem copyMenu = null; private JMenuItem pasteMenu = null; private JMenuItem deleteMenu = null; private JButton loopButton = null; private MenuWithIndex fileHistory[] = new MenuWithIndex[ MabiIccoProperties.MAX_FILE_HISTORY ]; /** * Create the frame. * @param listener 関連付けるActionListener */ public MainFrame(ActionListener listener) { this.listener = listener; setTitleAndFileName(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); loadWindowPeoperties(); addComponentListener(this); setJMenuBar(createMenuBar()); contentPane = new JPanel(); initKeyAction(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(new BorderLayout(0, 0)); JPanel northPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); contentPane.add(northPanel, BorderLayout.NORTH); mmlSeqView = new MMLSeqView(this); mmlSeqView.setTimeView(timeView); mmlSeqView.setNoteAlignChanger(t -> { if ( (t >= 0) && (t < noteTypeSelect.getItemCount()) ) noteTypeSelect.setSelectedIndex(t); }); contentPane.add(mmlSeqView.getPanel(), BorderLayout.CENTER); contentPane.setFocusable(false); JToolBar toolBar = createToolBar(); toolBar.setFloatable(false); northPanel.add(toolBar); JPanel southPanel = new JPanel(); contentPane.add(southPanel, BorderLayout.SOUTH); southPanel.setLayout(new BorderLayout(0, 0)); statusField = new JTextField(); statusField.setEditable(false); statusField.setFocusable(false); southPanel.add(statusField, BorderLayout.SOUTH); statusField.setColumns(10); setCanReloadFile(false); setCanUndo(false); setCanRedo(false); setCanSaveFile(false); setSelectedEdit(false); setPasteEnable(false); // 閉じるボタンへのアクション設定 setDefaultCloseOperation( DO_NOTHING_ON_CLOSE ); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { quitEvent(); } }); } private void quitEvent() { ActionEvent event = new ActionEvent(this, 0, ActionDispatcher.QUIT); this.listener.actionPerformed(event); } private JMenuItem createMenuItem(JMenu menu, String name, String actionCommand) { return createMenuItem(menu, name, actionCommand, false, null); } private JMenuItem createMenuItem(JMenu menu, String name, String actionCommand, boolean noplayFunction) { return createMenuItem(menu, name, actionCommand, noplayFunction, null); } private JMenuItem createMenuItem(JMenu menu, String name, String actionCommand, KeyStroke keyStroke) { return createMenuItem(menu, name, actionCommand, false, keyStroke); } private JMenuItem createMenuItem(JMenu menu, String name, String actionCommand, boolean noplayFunction, KeyStroke keyStroke) { JMenuItem menuItem = new JMenuItem(appText(name)); String iconName = appText(name+".icon"); if (!iconName.equals(name+".icon")) { Icon icon = AppResource.getImageIcon(iconName); if (icon != null) { menuItem.setIcon(icon); } } return createMenuItem(menu, menuItem, actionCommand, noplayFunction, keyStroke); } private JMenuItem createMenuItem(JMenu menu, JMenuItem menuItem, String actionCommand, boolean noplayFunction, KeyStroke keyStroke) { menuItem.addActionListener(listener); menuItem.setActionCommand(actionCommand); if (noplayFunction) { noplayFunctions.add(menuItem); } if (keyStroke != null) { menuItem.setAccelerator(keyStroke); } menu.add(menuItem); return menuItem; } private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); /************************* File Menu *************************/ JMenu fileMenu = new JMenu(appText("menu.file")); menuBar.add(fileMenu); createMenuItem(fileMenu, "menu.newFile", ActionDispatcher.NEW_FILE, true, KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK)); createMenuItem(fileMenu, "menu.openFile", ActionDispatcher.FILE_OPEN, true, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)); reloadMenuItem = createMenuItem(fileMenu, "menu.reloadFile", ActionDispatcher.RELOAD_FILE, true); saveMenuItem = createMenuItem(fileMenu, "menu.saveFile", ActionDispatcher.SAVE_FILE, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK)); createMenuItem(fileMenu, "menu.saveAsFile", ActionDispatcher.SAVEAS_FILE, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK | InputEvent.SHIFT_DOWN_MASK)); fileMenu.add(new JSeparator()); createMenuItem(fileMenu, "mml.input.import", ActionDispatcher.FILE_IMPORT, true); createMenuItem(fileMenu, "menu.midiExport", ActionDispatcher.MIDI_EXPORT); createMenuItem(fileMenu, "wavout", ActionDispatcher.WAVOUT, true); createMenuItem(fileMenu, "menu.scoreProperty", ActionDispatcher.SCORE_PROPERTY); fileMenu.add(new JSeparator()); for (int i = 0; i < fileHistory.length; i++) { fileHistory[i] = new MenuWithIndex(); createMenuItem(fileMenu, fileHistory[i], ActionDispatcher.FILE_OPEN_WITH_HISTORY, true, null); } updateFileHistoryMenu(); fileMenu.add(new JSeparator()); createMenuItem(fileMenu, "menu.quit", ActionDispatcher.QUIT); /************************* Edit Menu *************************/ JMenu editMenu = new JMenu(appText("menu.edit")); menuBar.add(editMenu); undoMenu = createMenuItem(editMenu, "menu.undo", ActionDispatcher.UNDO, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK)); redoMenu = createMenuItem(editMenu, "menu.redo", ActionDispatcher.REDO, KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK)); editMenu.add(new JSeparator()); cutMenu = createMenuItem(editMenu, "menu.cut", ActionDispatcher.CUT, KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK)); copyMenu = createMenuItem(editMenu, "menu.copy", ActionDispatcher.COPY, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK)); pasteMenu = createMenuItem(editMenu, "menu.paste", ActionDispatcher.PASTE, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK)); deleteMenu = createMenuItem(editMenu, "menu.delete", ActionDispatcher.DELETE, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); editMenu.add(new JSeparator()); createMenuItem(editMenu, "menu.selectAll", ActionDispatcher.SELECT_ALL, KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK)); editMenu.add(new JSeparator()); createMenuItem(editMenu, "menu.changePart", ActionDispatcher.PART_CHANGE, true); createMenuItem(editMenu, "menu.addMeasure", ActionDispatcher.ADD_MEASURE, true); createMenuItem(editMenu, "menu.removeMeasure", ActionDispatcher.REMOVE_MEASURE, true); createMenuItem(editMenu, "menu.addBeat", ActionDispatcher.ADD_BEAT, true); createMenuItem(editMenu, "menu.removeBeat", ActionDispatcher.REMOVE_BEAT, true); createMenuItem(editMenu, "edit.transpose", ActionDispatcher.TRANSPOSE, true); editMenu.add(new JSeparator()); createMenuItem(editMenu, "edit.allClearTempo", ActionDispatcher.ALL_CLEAR_TEMPO, true); createMenuItem(editMenu, "mml.generate", ActionDispatcher.MML_GENERATE, true); createMenuItem(editMenu, "edit.keyboard.input", ActionDispatcher.KEYBOARD_INPUT, true, KeyStroke.getKeyStroke(KeyEvent.VK_K, InputEvent.CTRL_MASK)); /************************* Track Menu *************************/ JMenu trackMenu = new JMenu(appText("menu.track")); menuBar.add(trackMenu); createMenuItem(trackMenu, "menu.addTrack", ActionDispatcher.ADD_TRACK, true, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK)); createMenuItem(trackMenu, "menu.removeTrack", ActionDispatcher.REMOVE_TRACK, true, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)); trackMenu.add(new JSeparator()); createMenuItem(trackMenu, "menu.trackProperty", ActionDispatcher.TRACK_PROPERTY, true, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.ALT_MASK)); trackMenu.add(new JSeparator()); createMenuItem(trackMenu, "menu.mml_import", ActionDispatcher.MML_IMPORT, true, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)); createMenuItem(trackMenu, "menu.mml_export", ActionDispatcher.MML_EXPORT, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)); /************************* Play Menu *************************/ JMenu playMenu = new JMenu(appText("menu.operate")); menuBar.add(playMenu); createMenuItem(playMenu, "menu.head", ActionDispatcher.SET_START_POSITION, KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)); createMenuItem(playMenu, "menu.play", ActionDispatcher.PLAY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); createMenuItem(playMenu, "menu.stop", ActionDispatcher.STOP, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); createMenuItem(playMenu, "menu.pause", ActionDispatcher.PAUSE); playMenu.add(new JSeparator()); createMenuItem(playMenu, "menu.prev", ActionDispatcher.PREV_TIME, KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0)); createMenuItem(playMenu, "menu.next", ActionDispatcher.NEXT_TIME, KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0)); /************************* Setting Menu *************************/ MabiIccoProperties properties = MabiIccoProperties.getInstance(); JMenu settingMenu = new JMenu(appText("menu.setting")); menuBar.add(settingMenu); createNoteHeightMenu(settingMenu); createCheckMenu(settingMenu, "clickPlayMenu", properties.enableClickPlay); createCheckMenu(settingMenu, "view.marker", properties.enableViewMarker); createCheckMenu(settingMenu, "view.range", properties.viewRange); createCheckMenu(settingMenu, "view.showAllVelocity", properties.showAllVelocity); createCheckMenu(settingMenu, "view.velocity", properties.viewVelocityLine); createCheckMenu(settingMenu, "edit.enable", properties.enableEdit); createCheckMenu(settingMenu, "edit.active_part_switch", properties.activePartSwitch); createCheckMenu(settingMenu, "mml.optimize", properties.enableMMLOptimize); createMenuItem(settingMenu, "menu.clear_dls", ActionDispatcher.CLEAR_DLS); /************************* Help Menu *************************/ JMenu helpMenu = new JMenu(appText("menu.help")); menuBar.add(helpMenu); createMenuItem(helpMenu, "menu.about", ActionDispatcher.ABOUT); return menuBar; } private class CheckBoxMenuWithIndex extends JCheckBoxMenuItem implements IntSupplier { private static final long serialVersionUID = -2688552233736202101L; private final int index; private CheckBoxMenuWithIndex(String text, int index) { super(text); this.index = index; } @Override public int getAsInt() { return this.index; } } private class MenuWithIndex extends JMenuItem implements IntSupplier { private static final long serialVersionUID = -7944526274796801310L; private int index; private MenuWithIndex() { super(); } public void set(int index) { this.index = index; } @Override public int getAsInt() { return this.index; } } private void createNoteHeightMenu(JMenu settingMenu) { JMenu noteHeightMenu = new JMenu(appText("menu.noteHeight")); settingMenu.add(noteHeightMenu); ButtonGroup group = new ButtonGroup(); int index = 0; for (int value : PianoRollView.NOTE_HEIGHT_TABLE) { CheckBoxMenuWithIndex menu = new CheckBoxMenuWithIndex(value+"px", index++); menu.setActionCommand(ActionDispatcher.CHANGE_NOTE_HEIGHT_INT); menu.addActionListener(listener); noteHeightMenu.add(menu); group.add(menu); } MabiIccoProperties properties = MabiIccoProperties.getInstance(); index = properties.getPianoRollViewHeightScaleProperty(); Collections.list(group.getElements()).get(index).setSelected(true); } /** * 有効/無効の設定ボタンを作成します. * @param settingMenu */ private void createCheckMenu(JMenu settingMenu, String itemName, MabiIccoProperties.Property<Boolean> property) { JCheckBoxMenuItem clickPlayMenu = new JCheckBoxMenuItem(appText(itemName)); settingMenu.add(clickPlayMenu); clickPlayMenu.setSelected( property.get() ); clickPlayMenu.addActionListener((e) -> { boolean b = property.get(); property.set(!b); clickPlayMenu.setSelected(!b); if (mmlSeqView != null) { mmlSeqView.repaint(); } }); } private JButton createToolButton(JComponent target, String title, String command, boolean noplayFunction) { JButton button = new JButton(""); Icon icon = null; String iconName = appText(title+".icon"); if (!iconName.equals(title+".icon")) { icon = AppResource.getImageIcon(appText(iconName)); } if (icon == null) { button.setText(appText(title)); } else { button.setToolTipText(appText(title)); button.setIcon(icon); } button.setFocusable(false); button.addActionListener(listener); button.setActionCommand(command); if (noplayFunction) { noplayFunctions.add(button); } target.add(button); return button; } private JToolBar createToolBar() { JToolBar toolBar = new JToolBar(); createToolButton(toolBar, "menu.newFile", ActionDispatcher.NEW_FILE, true); createToolButton(toolBar, "menu.openFile", ActionDispatcher.FILE_OPEN, true); toolBar.add(newToolBarSeparator()); createToolButton(toolBar, "menu.head", ActionDispatcher.SET_START_POSITION, false); createToolButton(toolBar, "menu.prev", ActionDispatcher.PREV_TIME, false); createToolButton(toolBar, "menu.play", ActionDispatcher.PLAY, false); createToolButton(toolBar, "menu.next", ActionDispatcher.NEXT_TIME, false); createToolButton(toolBar, "menu.pause", ActionDispatcher.PAUSE, false); createToolButton(toolBar, "menu.stop", ActionDispatcher.STOP, false); loopButton = createToolButton(toolBar, "menu.loop", ActionDispatcher.TOGGLE_LOOP, false); toolBar.add(newToolBarSeparator()); createToolButton(toolBar, "menu.addMeasure", ActionDispatcher.ADD_MEASURE, true); createToolButton(toolBar, "menu.removeMeasure", ActionDispatcher.REMOVE_MEASURE, true); createToolButton(toolBar, "menu.addBeat", ActionDispatcher.ADD_BEAT, true); createToolButton(toolBar, "menu.removeBeat", ActionDispatcher.REMOVE_BEAT, true); toolBar.add(newToolBarSeparator()); createToolButton(toolBar, "menu.inputMML", ActionDispatcher.INPUT_FROM_CLIPBOARD, true); createToolButton(toolBar, "menu.outputMML", ActionDispatcher.OUTPUT_TO_CLIPBOARD, false); toolBar.add(newToolBarSeparator()); // ビューの拡大/縮小ツールボタン createToolButton(toolBar, "view.scale.up", ActionDispatcher.VIEW_SCALE_UP, false); createToolButton(toolBar, "view.scale.down", ActionDispatcher.VIEW_SCALE_DOWN, false); // 編集ノートタイプ noteTypeSelect.setFocusable(false); noteTypeSelect.addActionListener(this); // MainFrameでAction処理します. noteTypeSelect.setSelectedIndex(NoteAlign.DEFAULT_ALIGN_INDEX); setEditAlign(); toolBar.add(noteTypeSelect); // Paint Mode paintModeSelect.setFocusable(false); paintModeSelect.addActionListener(this); paintModeSelect.setSelectedItem( mmlSeqView.getPaintMode() ); toolBar.add(paintModeSelect); toolBar.add(newToolBarSeparator()); timeView.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); toolBar.add(timeView); return toolBar; } private JSeparator newToolBarSeparator() { JSeparator separator = new JToolBar.Separator(); separator.setForeground(Color.DARK_GRAY); return separator; } public void setTitleAndFileName(String filename) { String fileTitle = ""; if (filename != null) { fileTitle = filename; } setTitle(AppResource.getAppTitle() + " [" + fileTitle + "]"); } private void loadWindowPeoperties() { MabiIccoProperties properties = MabiIccoProperties.getInstance(); Rectangle rect = properties.getWindowRect(); if (rect.getX() < 0.0) { setSize(850, 650); setLocationRelativeTo(null); } else { setBounds(rect); } if (properties.windowMaximize.get()) { this.setExtendedState(MAXIMIZED_BOTH); } } private void updateWindowProperties() { int extendedState = this.getExtendedState(); MabiIccoProperties properties = MabiIccoProperties.getInstance(); if ( extendedState == MAXIMIZED_BOTH ) { properties.windowMaximize.set(true); } else { properties.windowMaximize.set(false); properties.setWindowRect(this.getBounds()); } } public MMLSeqView getMMLSeqView() { return mmlSeqView; } // JFrameのイベント @Override public void componentHidden(ComponentEvent e) {} @Override public void componentMoved(ComponentEvent e) { updateWindowProperties(); } @Override public void componentResized(ComponentEvent e) { updateWindowProperties(); } @Override public void componentShown(ComponentEvent e) {} /** * 再生中に各機能を無効化する。 */ public void disableNoplayItems() { for (JComponent component : noplayFunctions) { component.setEnabled(false); } } /** * 再生中に無効化されている機能を有効にする。 */ public void enableNoplayItems() { for (JComponent component : noplayFunctions) { component.setEnabled(true); } EventQueue.invokeLater(() -> { mmlSeqView.resetViewPosition(); mmlSeqView.repaint(); }); } private void setEditAlign() { NoteAlign noteAlign = (NoteAlign) noteTypeSelect.getSelectedItem(); int alignTick = noteAlign.getAlign(); mmlSeqView.setEditAlign(alignTick); } @Override public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source.equals(noteTypeSelect)) { setEditAlign(); } else if (source.equals(paintModeSelect)) { PaintMode mode = (PaintMode) paintModeSelect.getSelectedItem(); mmlSeqView.setPaintMode(mode); mmlSeqView.repaint(); } } /** * 再読み込みUIの有効化 * @param b trueで再読み込みUIが有効 */ public void setCanReloadFile(boolean b) { reloadMenuItem.setEnabled(b); } /** * Undo-UIの有効化 * @param b trueでUndo-UIが有効 */ public void setCanUndo(boolean b) { undoMenu.setEnabled(b); } /** * Redo-UIの有効化 * @param b trueでRedo-UIが有効 */ public void setCanRedo(boolean b) { redoMenu.setEnabled(b); } /** * 上書き保存UIの有効化 * @param b trueで上書き保存UIが有効 */ public void setCanSaveFile(boolean b) { saveMenuItem.setEnabled(b); } public void setSelectedEdit(boolean b) { cutMenu.setEnabled(b); copyMenu.setEnabled(b); deleteMenu.setEnabled(b); } public void setPasteEnable(boolean b) { pasteMenu.setEnabled(b); } public void updateLoop(boolean b) { loopButton.setSelected(b); } public void updateFileHistoryMenu() { File fileList[] = MabiIccoProperties.getInstance().getFileHistory(); for (int i = 0; i < fileHistory.length; i++) { if ( (i < fileList.length) && (fileList[i] != null) ) { fileHistory[i].setText( (i+1) + " " + fileList[i].getName() ); fileHistory[i].set(i); fileHistory[i].setVisible(true); } else { fileHistory[i].setVisible(false); } } } public void setStatusText(String text) { statusField.setText(text); } private void initKeyAction() { createKeyAction("select_paintMode.activePart", KeyStroke.getKeyStroke(KeyEvent.VK_F10, 0), () -> paintModeSelect.setSelectedItem(PaintMode.ACTIVE_PART)); createKeyAction("select_paintMode.activeTrack", KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0), () -> paintModeSelect.setSelectedItem(PaintMode.ACTIVE_TRACK)); createKeyAction("select_paintMode.allTrack", KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), () -> paintModeSelect.setSelectedItem(PaintMode.ALL_TRACK)); createKeyAction(ActionDispatcher.SWITCH_TRACK_NEXT, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.CTRL_MASK), () -> this.listener.actionPerformed(new ActionEvent(this, 0, ActionDispatcher.SWITCH_TRACK_NEXT))); createKeyAction(ActionDispatcher.SWITCH_TRACK_PREV, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.CTRL_MASK), () -> this.listener.actionPerformed(new ActionEvent(this, 0, ActionDispatcher.SWITCH_TRACK_PREV))); createKeyAction(ActionDispatcher.SWITCH_MMLPART_NEXT, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_MASK), () -> this.listener.actionPerformed(new ActionEvent(this, 0, ActionDispatcher.SWITCH_MMLPART_NEXT))); createKeyAction(ActionDispatcher.SWITCH_MMLPART_PREV, KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_MASK), () -> this.listener.actionPerformed(new ActionEvent(this, 0, ActionDispatcher.SWITCH_MMLPART_PREV))); createKeyAction(ActionDispatcher.PLAY, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), () -> this.listener.actionPerformed(new ActionEvent(this, 0, ActionDispatcher.PLAY))); } private void createKeyAction(String name, KeyStroke stroke, Runnable func) { new KeyAction(name, stroke, contentPane, func); } private final class KeyAction extends AbstractAction { private static final long serialVersionUID = -5439131294063926971L; private Runnable function; private KeyAction() {} private KeyAction(String name, KeyStroke stroke, JComponent rootPane, Runnable func) { this.function = func; rootPane.getInputMap().put(stroke, name); rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(stroke, name); rootPane.getActionMap().put(name, this); } @Override public void actionPerformed(ActionEvent e) { this.function.run(); } } }