+++ /dev/null
-package camidion.chordhelper.midieditor;\r
-\r
-import java.awt.Component;\r
-import java.awt.Container;\r
-import java.awt.Dimension;\r
-import java.awt.FlowLayout;\r
-import java.awt.Insets;\r
-import java.awt.datatransfer.DataFlavor;\r
-import java.awt.datatransfer.Transferable;\r
-import java.awt.dnd.DnDConstants;\r
-import java.awt.dnd.DropTarget;\r
-import java.awt.dnd.DropTargetDragEvent;\r
-import java.awt.dnd.DropTargetDropEvent;\r
-import java.awt.dnd.DropTargetEvent;\r
-import java.awt.dnd.DropTargetListener;\r
-import java.awt.event.ActionEvent;\r
-import java.awt.event.ComponentAdapter;\r
-import java.awt.event.ComponentEvent;\r
-import java.awt.event.ComponentListener;\r
-import java.awt.event.ItemEvent;\r
-import java.awt.event.ItemListener;\r
-import java.awt.event.MouseEvent;\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.nio.charset.Charset;\r
-import java.security.AccessControlException;\r
-import java.util.EventObject;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import javax.sound.midi.InvalidMidiDataException;\r
-import javax.sound.midi.MidiChannel;\r
-import javax.sound.midi.MidiEvent;\r
-import javax.sound.midi.MidiMessage;\r
-import javax.sound.midi.Sequencer;\r
-import javax.sound.midi.ShortMessage;\r
-import javax.swing.AbstractAction;\r
-import javax.swing.AbstractCellEditor;\r
-import javax.swing.Action;\r
-import javax.swing.Box;\r
-import javax.swing.BoxLayout;\r
-import javax.swing.DefaultCellEditor;\r
-import javax.swing.Icon;\r
-import javax.swing.JButton;\r
-import javax.swing.JCheckBox;\r
-import javax.swing.JComboBox;\r
-import javax.swing.JDialog;\r
-import javax.swing.JFileChooser;\r
-import javax.swing.JLabel;\r
-import javax.swing.JOptionPane;\r
-import javax.swing.JPanel;\r
-import javax.swing.JScrollPane;\r
-import javax.swing.JSplitPane;\r
-import javax.swing.JTable;\r
-import javax.swing.JToggleButton;\r
-import javax.swing.ListSelectionModel;\r
-import javax.swing.event.ListSelectionEvent;\r
-import javax.swing.event.ListSelectionListener;\r
-import javax.swing.event.TableModelEvent;\r
-import javax.swing.filechooser.FileFilter;\r
-import javax.swing.filechooser.FileNameExtensionFilter;\r
-import javax.swing.table.JTableHeader;\r
-import javax.swing.table.TableCellEditor;\r
-import javax.swing.table.TableCellRenderer;\r
-import javax.swing.table.TableColumn;\r
-import javax.swing.table.TableColumnModel;\r
-import javax.swing.table.TableModel;\r
-\r
-import camidion.chordhelper.ButtonIcon;\r
-import camidion.chordhelper.ChordHelperApplet;\r
-import camidion.chordhelper.mididevice.AbstractVirtualMidiDevice;\r
-import camidion.chordhelper.mididevice.MidiSequencerModel;\r
-import camidion.chordhelper.mididevice.VirtualMidiDevice;\r
-import camidion.chordhelper.music.MIDISpec;\r
-\r
-/**\r
- * MIDIエディタ(MIDI Editor/Playlist for MIDI Chord Helper)\r
- *\r
- * @author\r
- * Copyright (C) 2006-2014 Akiyoshi Kamide\r
- * http://www.yk.rim.or.jp/~kamide/music/chordhelper/\r
- */\r
-public class MIDIEditor extends JDialog implements DropTargetListener {\r
- private static VirtualMidiDevice virtualMidiDevice = new AbstractVirtualMidiDevice() {\r
- {\r
- info = new MyInfo();\r
- setMaxReceivers(0); // 送信専用とする(MIDI IN はサポートしない)\r
- }\r
- /**\r
- * MIDIデバイス情報\r
- */\r
- protected MyInfo info;\r
- @Override\r
- public Info getDeviceInfo() {\r
- return info;\r
- }\r
- class MyInfo extends Info {\r
- protected MyInfo() {\r
- super("MIDI Editor","Unknown vendor","MIDI sequence editor","");\r
- }\r
- }\r
- };\r
- /**\r
- * このMIDIエディタの仮想MIDIデバイスを返します。\r
- */\r
- public VirtualMidiDevice getVirtualMidiDevice() {\r
- return virtualMidiDevice;\r
- }\r
- /**\r
- * このダイアログを表示するアクション\r
- */\r
- public Action openAction = new AbstractAction(\r
- "Edit/Playlist/Speed", new ButtonIcon(ButtonIcon.EDIT_ICON)\r
- ) {\r
- {\r
- String tooltip = "MIDIシーケンスの編集/プレイリスト/再生速度調整";\r
- putValue(Action.SHORT_DESCRIPTION, tooltip);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) { open(); }\r
- };\r
- /**\r
- * このダイアログを開きます。すでに開かれていた場合は前面に移動します。\r
- */\r
- public void open() {\r
- if( isVisible() ) toFront(); else setVisible(true);\r
- }\r
- /**\r
- * エラーメッセージダイアログを表示します。\r
- * @param message エラーメッセージ\r
- */\r
- public void showError(String message) {\r
- JOptionPane.showMessageDialog(\r
- this, message,\r
- ChordHelperApplet.VersionInfo.NAME,\r
- JOptionPane.ERROR_MESSAGE\r
- );\r
- }\r
- /**\r
- * 警告メッセージダイアログを表示します。\r
- * @param message 警告メッセージ\r
- */\r
- public void showWarning(String message) {\r
- JOptionPane.showMessageDialog(\r
- this, message,\r
- ChordHelperApplet.VersionInfo.NAME,\r
- JOptionPane.WARNING_MESSAGE\r
- );\r
- }\r
- /**\r
- * 確認ダイアログを表示します。\r
- * @param message 確認メッセージ\r
- * @return 確認OKのときtrue\r
- */\r
- boolean confirm(String message) {\r
- return JOptionPane.showConfirmDialog(\r
- this, message,\r
- ChordHelperApplet.VersionInfo.NAME,\r
- JOptionPane.YES_NO_OPTION,\r
- JOptionPane.WARNING_MESSAGE\r
- ) == JOptionPane.YES_OPTION ;\r
- }\r
- @Override\r
- public void dragEnter(DropTargetDragEvent event) {\r
- if( event.isDataFlavorSupported(DataFlavor.javaFileListFlavor) ) {\r
- event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);\r
- }\r
- }\r
- @Override\r
- public void dragExit(DropTargetEvent event) {}\r
- @Override\r
- public void dragOver(DropTargetDragEvent event) {}\r
- @Override\r
- public void dropActionChanged(DropTargetDragEvent event) {}\r
- @Override\r
- @SuppressWarnings("unchecked")\r
- public void drop(DropTargetDropEvent event) {\r
- event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);\r
- try {\r
- int action = event.getDropAction();\r
- if ( (action & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) {\r
- Transferable t = event.getTransferable();\r
- Object data = t.getTransferData(DataFlavor.javaFileListFlavor);\r
- loadAndPlay((List<File>)data);\r
- event.dropComplete(true);\r
- return;\r
- }\r
- event.dropComplete(false);\r
- }\r
- catch (Exception ex) {\r
- ex.printStackTrace();\r
- event.dropComplete(false);\r
- }\r
- }\r
- /**\r
- * 複数のMIDIファイルを読み込み、再生されていなかったら再生します。\r
- * すでに再生されていた場合、このエディタダイアログを表示します。\r
- *\r
- * @param fileList 読み込むMIDIファイルのリスト\r
- */\r
- public void loadAndPlay(List<File> fileList) {\r
- int firstIndex = -1;\r
- SequenceListTableModel playlist = sequenceListTable.getModel();\r
- try {\r
- firstIndex = playlist.addSequences(fileList);\r
- } catch(IOException|InvalidMidiDataException e) {\r
- showWarning(e.getMessage());\r
- } catch(AccessControlException e) {\r
- showError(e.getMessage());\r
- e.printStackTrace();\r
- }\r
- if(playlist.sequencerModel.getSequencer().isRunning()) {\r
- open();\r
- }\r
- else if( firstIndex >= 0 ) {\r
- playlist.loadToSequencer(firstIndex);\r
- playlist.sequencerModel.start();\r
- }\r
- }\r
- private static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
- private static final Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);\r
- /**\r
- * 新しいMIDIシーケンスを生成するダイアログ\r
- */\r
- public NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this);\r
- /**\r
- * BASE64テキスト入力ダイアログ\r
- */\r
- public Base64Dialog base64Dialog = new Base64Dialog(this);\r
- /**\r
- * プレイリストビュー(シーケンスリスト)\r
- */\r
- public SequenceListTable sequenceListTable;\r
- /**\r
- * MIDIトラックリストテーブルビュー(選択中のシーケンスの中身)\r
- */\r
- private TrackListTable trackListTable;\r
- /**\r
- * MIDIイベントリストテーブルビュー(選択中のトラックの中身)\r
- */\r
- private EventListTable eventListTable;\r
- /**\r
- * MIDIイベント入力ダイアログ(イベント入力とイベント送出で共用)\r
- */\r
- public MidiEventDialog eventDialog = new MidiEventDialog();\r
- /**\r
- * プレイリストビュー(シーケンスリスト)\r
- */\r
- public class SequenceListTable extends JTable {\r
- /**\r
- * ファイル選択ダイアログ(アプレットでは使用不可)\r
- */\r
- private MidiFileChooser midiFileChooser;\r
- /**\r
- * BASE64エンコードアクション(ライブラリが見えている場合のみ有効)\r
- */\r
- private Action base64EncodeAction;\r
- /**\r
- * プレイリストビューを構築します。\r
- * @param model プレイリストデータモデル\r
- */\r
- public SequenceListTable(SequenceListTableModel model) {\r
- super(model, null, model.sequenceListSelectionModel);\r
- try {\r
- midiFileChooser = new MidiFileChooser();\r
- }\r
- catch( ExceptionInInitializerError|NoClassDefFoundError|AccessControlException e ) {\r
- // アプレットの場合、Webクライアントマシンのローカルファイルには\r
- // アクセスできないので、ファイル選択ダイアログは使用不可。\r
- midiFileChooser = null;\r
- }\r
- // 再生ボタンを埋め込む\r
- new PlayButtonCellEditor();\r
- new PositionCellEditor();\r
- //\r
- // 文字コード選択をプルダウンにする\r
- int column = SequenceListTableModel.Column.CHARSET.ordinal();\r
- TableCellEditor ce = new DefaultCellEditor(new JComboBox<Charset>() {{\r
- Set<Map.Entry<String,Charset>> entrySet = Charset.availableCharsets().entrySet();\r
- for( Map.Entry<String,Charset> entry : entrySet ) addItem(entry.getValue());\r
- }});\r
- getColumnModel().getColumn(column).setCellEditor(ce);\r
- setAutoCreateColumnsFromModel(false);\r
- //\r
- // Base64エンコードアクションの生成\r
- if( base64Dialog.isBase64Available() ) {\r
- base64EncodeAction = new AbstractAction("Base64") {\r
- {\r
- String tooltip = "Base64 text conversion - Base64テキスト変換";\r
- putValue(Action.SHORT_DESCRIPTION, tooltip);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- SequenceTrackListTableModel mstm = getModel().getSelectedSequenceModel();\r
- byte[] data = null;\r
- String filename = null;\r
- if( mstm != null ) {\r
- data = mstm.getMIDIdata();\r
- filename = mstm.getFilename();\r
- }\r
- base64Dialog.setMIDIData(data, filename);\r
- base64Dialog.setVisible(true);\r
- }\r
- };\r
- }\r
- TableColumnModel colModel = getColumnModel();\r
- for( SequenceListTableModel.Column c : SequenceListTableModel.Column.values() ) {\r
- TableColumn tc = colModel.getColumn(c.ordinal());\r
- tc.setPreferredWidth(c.preferredWidth);\r
- if( c == SequenceListTableModel.Column.LENGTH ) {\r
- lengthColumn = tc;\r
- }\r
- }\r
- }\r
- private TableColumn lengthColumn;\r
- @Override\r
- public void tableChanged(TableModelEvent event) {\r
- super.tableChanged(event);\r
- //\r
- // タイトルに合計シーケンス長を表示\r
- if( lengthColumn != null ) {\r
- int sec = getModel().getTotalSeconds();\r
- String title = SequenceListTableModel.Column.LENGTH.title;\r
- title = String.format(title+" [%02d:%02d]", sec/60, sec%60);\r
- lengthColumn.setHeaderValue(title);\r
- }\r
- //\r
- // シーケンス削除時など、合計シーケンス長が変わっても\r
- // 列モデルからではヘッダタイトルが再描画されないことがある。\r
- // そこで、ヘッダビューから repaint() で突っついて再描画させる。\r
- JTableHeader th = getTableHeader();\r
- if( th != null ) {\r
- th.repaint();\r
- }\r
- }\r
- /**\r
- * 時間位置表示セルエディタ(ダブルクリック専用)\r
- */\r
- private class PositionCellEditor extends AbstractCellEditor\r
- implements TableCellEditor\r
- {\r
- public PositionCellEditor() {\r
- int column = SequenceListTableModel.Column.POSITION.ordinal();\r
- TableColumn tc = getColumnModel().getColumn(column);\r
- tc.setCellEditor(this);\r
- }\r
- /**\r
- * セルをダブルクリックしたときだけ編集モードに入るようにします。\r
- * @param e イベント(マウスイベント)\r
- * @return 編集可能になったらtrue\r
- */\r
- @Override\r
- public boolean isCellEditable(EventObject e) {\r
- // マウスイベント以外のイベントでは編集不可\r
- if( ! (e instanceof MouseEvent) ) return false;\r
- return ((MouseEvent)e).getClickCount() == 2;\r
- }\r
- @Override\r
- public Object getCellEditorValue() { return null; }\r
- /**\r
- * 編集モード時のコンポーネントを返すタイミングで\r
- * そのシーケンスをシーケンサーにロードしたあと、\r
- * すぐに編集モードを解除します。\r
- * @return 常にnull\r
- */\r
- @Override\r
- public Component getTableCellEditorComponent(\r
- JTable table, Object value, boolean isSelected,\r
- int row, int column\r
- ) {\r
- getModel().loadToSequencer(row);\r
- fireEditingStopped();\r
- return null;\r
- }\r
- }\r
- /**\r
- * プレイボタンを埋め込んだセルエディタ\r
- */\r
- private class PlayButtonCellEditor extends AbstractCellEditor\r
- implements TableCellEditor, TableCellRenderer\r
- {\r
- private JToggleButton playButton = new JToggleButton(\r
- getModel().sequencerModel.startStopAction\r
- );\r
- public PlayButtonCellEditor() {\r
- playButton.setMargin(ZERO_INSETS);\r
- int column = SequenceListTableModel.Column.PLAY.ordinal();\r
- TableColumn tc = getColumnModel().getColumn(column);\r
- tc.setCellRenderer(this);\r
- tc.setCellEditor(this);\r
- }\r
- /**\r
- * {@inheritDoc}\r
- *\r
- * <p>この実装では、クリックしたセルのシーケンスが\r
- * シーケンサーにロードされている場合に\r
- * trueを返してプレイボタンを押せるようにします。\r
- * そうでない場合はプレイボタンのないセルなので、\r
- * ダブルクリックされたときだけtrueを返します。\r
- * </p>\r
- */\r
- @Override\r
- public boolean isCellEditable(EventObject e) {\r
- if( ! (e instanceof MouseEvent) ) {\r
- // マウスイベント以外はデフォルトメソッドにお任せ\r
- return super.isCellEditable(e);\r
- }\r
- fireEditingStopped();\r
- MouseEvent me = (MouseEvent)e;\r
- // クリックされたセルの行を特定\r
- int row = rowAtPoint(me.getPoint());\r
- if( row < 0 )\r
- return false;\r
- SequenceListTableModel model = getModel();\r
- if( row >= model.getRowCount() )\r
- return false;\r
- if( model.sequenceList.get(row).isOnSequencer() ) {\r
- // プレイボタン表示中のセルはシングルクリックでもOK\r
- return true;\r
- }\r
- // プレイボタンのないセルはダブルクリックのみを受け付ける\r
- return me.getClickCount() == 2;\r
- }\r
- @Override\r
- public Object getCellEditorValue() { return null; }\r
- /**\r
- * {@inheritDoc}\r
- *\r
- * <p>この実装では、行の表すシーケンスが\r
- * シーケンサーにロードされている場合にプレイボタンを返します。\r
- * そうでない場合は、\r
- * そのシーケンスをシーケンサーにロードしてnullを返します。\r
- * </p>\r
- */\r
- @Override\r
- public Component getTableCellEditorComponent(\r
- JTable table, Object value, boolean isSelected, int row, int column\r
- ) {\r
- fireEditingStopped();\r
- SequenceListTableModel model = getModel();\r
- if( model.sequenceList.get(row).isOnSequencer() ) {\r
- return playButton;\r
- }\r
- model.loadToSequencer(row);\r
- return null;\r
- }\r
- @Override\r
- public Component getTableCellRendererComponent(\r
- JTable table, Object value, boolean isSelected,\r
- boolean hasFocus, int row, int column\r
- ) {\r
- SequenceListTableModel model = getModel();\r
- if(model.sequenceList.get(row).isOnSequencer()) return playButton;\r
- Class<?> cc = model.getColumnClass(column);\r
- TableCellRenderer defaultRenderer = table.getDefaultRenderer(cc);\r
- return defaultRenderer.getTableCellRendererComponent(\r
- table, value, isSelected, hasFocus, row, column\r
- );\r
- }\r
- }\r
- /**\r
- * このプレイリスト(シーケンスリスト)が表示するデータを提供する\r
- * プレイリストモデルを返します。\r
- * @return プレイリストモデル\r
- */\r
- @Override\r
- public SequenceListTableModel getModel() {\r
- return (SequenceListTableModel) super.getModel();\r
- }\r
- /**\r
- * シーケンスを削除するアクション\r
- */\r
- Action deleteSequenceAction = getModel().new SelectedSequenceAction(\r
- "Delete", MIDIEditor.deleteIcon,\r
- "Delete selected MIDI sequence - 選択した曲をプレイリストから削除"\r
- ) {\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- SequenceListTableModel model = getModel();\r
- if( midiFileChooser != null ) {\r
- // ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
- //\r
- SequenceTrackListTableModel seqModel = model.getSelectedSequenceModel();\r
- if( seqModel.isModified() ) {\r
- // ファイル未保存の変更がある場合\r
- //\r
- String message =\r
- "Selected MIDI sequence not saved - delete it ?\n" +\r
- "選択したMIDIシーケンスはまだ保存されていません。削除しますか?";\r
- if( ! confirm(message) ) {\r
- // 実は削除してほしくなかった場合\r
- return;\r
- }\r
- }\r
- }\r
- // 削除を実行\r
- model.removeSelectedSequence();\r
- }\r
- };\r
- /**\r
- * ファイル選択ダイアログ(アプレットでは使用不可)\r
- */\r
- private class MidiFileChooser extends JFileChooser {\r
- {\r
- String description = "MIDI sequence (*.mid)";\r
- String extension = "mid";\r
- FileFilter filter = new FileNameExtensionFilter(description, extension);\r
- setFileFilter(filter);\r
- }\r
- /**\r
- * ファイル保存アクション\r
- */\r
- public Action saveMidiFileAction = getModel().new SelectedSequenceAction(\r
- "Save",\r
- "Save selected MIDI sequence to file - 選択したMIDIシーケンスをファイルに保存"\r
- ) {\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- SequenceListTableModel model = getModel();\r
- SequenceTrackListTableModel sequenceModel = model.getSelectedSequenceModel();\r
- String filename = sequenceModel.getFilename();\r
- File selectedFile;\r
- if( filename != null && ! filename.isEmpty() ) {\r
- // プレイリスト上でファイル名が入っていたら、それを初期選択\r
- setSelectedFile(selectedFile = new File(filename));\r
- }\r
- int saveOption = showSaveDialog(MIDIEditor.this);\r
- if( saveOption != JFileChooser.APPROVE_OPTION ) {\r
- // 保存ダイアログでキャンセルされた場合\r
- return;\r
- }\r
- if( (selectedFile = getSelectedFile()).exists() ) {\r
- // 指定されたファイルがすでにあった場合\r
- String fn = selectedFile.getName();\r
- String message = "Overwrite " + fn + " ?\n";\r
- message += fn + " を上書きしてよろしいですか?";\r
- if( ! confirm(message) ) {\r
- // 上書きしてほしくなかった場合\r
- return;\r
- }\r
- }\r
- // 保存を実行\r
- try ( FileOutputStream out = new FileOutputStream(selectedFile) ) {\r
- out.write(sequenceModel.getMIDIdata());\r
- sequenceModel.setModified(false);\r
- }\r
- catch( IOException ex ) {\r
- showError( ex.getMessage() );\r
- ex.printStackTrace();\r
- }\r
- }\r
- };\r
- /**\r
- * ファイルを開くアクション\r
- */\r
- public Action openMidiFileAction = new AbstractAction("Open") {\r
- {\r
- String tooltip = "Open MIDI file - MIDIファイルを開く";\r
- putValue(Action.SHORT_DESCRIPTION, tooltip);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent event) {\r
- int openOption = showOpenDialog(MIDIEditor.this);\r
- if(openOption == JFileChooser.APPROVE_OPTION) {\r
- try {\r
- getModel().addSequence(getSelectedFile());\r
- } catch( IOException|InvalidMidiDataException e ) {\r
- showWarning(e.getMessage());\r
- } catch( AccessControlException e ) {\r
- showError(e.getMessage());\r
- e.printStackTrace();\r
- }\r
- }\r
- }\r
- };\r
- };\r
- }\r
-\r
- /**\r
- * シーケンス(トラックリスト)テーブルビュー\r
- */\r
- public class TrackListTable extends JTable {\r
- /**\r
- * トラックリストテーブルビューを構築します。\r
- * @param model シーケンス(トラックリスト)データモデル\r
- */\r
- public TrackListTable(SequenceTrackListTableModel model) {\r
- super(model, null, model.trackListSelectionModel);\r
- //\r
- // 録音対象のMIDIチャンネルをコンボボックスで選択できるようにする\r
- int colIndex = SequenceTrackListTableModel.Column.RECORD_CHANNEL.ordinal();\r
- TableColumn tc = getColumnModel().getColumn(colIndex);\r
- tc.setCellEditor(new DefaultCellEditor(new JComboBox<String>(){{\r
- addItem("OFF");\r
- for(int i=1; i <= MIDISpec.MAX_CHANNELS; i++)\r
- addItem(String.format("%d", i));\r
- addItem("ALL");\r
- }}));\r
- setAutoCreateColumnsFromModel(false);\r
- //\r
- trackSelectionListener = new TrackSelectionListener();\r
- titleLabel = new TitleLabel();\r
- model.sequenceListTableModel.sequenceListSelectionModel.addListSelectionListener(titleLabel);\r
- TableColumnModel colModel = getColumnModel();\r
- for( SequenceTrackListTableModel.Column c : SequenceTrackListTableModel.Column.values() )\r
- colModel.getColumn(c.ordinal()).setPreferredWidth(c.preferredWidth);\r
- }\r
- /**\r
- * このテーブルビューが表示するデータを提供する\r
- * シーケンス(トラックリスト)データモデルを返します。\r
- * @return シーケンス(トラックリスト)データモデル\r
- */\r
- @Override\r
- public SequenceTrackListTableModel getModel() {\r
- return (SequenceTrackListTableModel) super.getModel();\r
- }\r
- /**\r
- * タイトルラベル\r
- */\r
- TitleLabel titleLabel;\r
- /**\r
- * 親テーブルの選択シーケンスの変更に反応する\r
- * 曲番号表示付きタイトルラベル\r
- */\r
- private class TitleLabel extends JLabel implements ListSelectionListener {\r
- private static final String TITLE = "Tracks";\r
- public TitleLabel() { setText(TITLE); }\r
- @Override\r
- public void valueChanged(ListSelectionEvent event) {\r
- if( event.getValueIsAdjusting() )\r
- return;\r
- SequenceTrackListTableModel oldModel = getModel();\r
- SequenceTrackListTableModel newModel = oldModel.sequenceListTableModel.getSelectedSequenceModel();\r
- if( oldModel == newModel )\r
- return;\r
- //\r
- // MIDIチャンネル選択中のときはキャンセルする\r
- cancelCellEditing();\r
- //\r
- int index = oldModel.sequenceListTableModel.sequenceListSelectionModel.getMinSelectionIndex();\r
- String text = TITLE;\r
- if( index >= 0 ) {\r
- text = String.format(text+" - MIDI file No.%d", index);\r
- }\r
- setText(text);\r
- if( newModel == null ) {\r
- newModel = oldModel.sequenceListTableModel.emptyTrackListTableModel;\r
- addTrackAction.setEnabled(false);\r
- }\r
- else {\r
- addTrackAction.setEnabled(true);\r
- }\r
- oldModel.trackListSelectionModel.removeListSelectionListener(trackSelectionListener);\r
- setModel(newModel);\r
- setSelectionModel(newModel.trackListSelectionModel);\r
- newModel.trackListSelectionModel.addListSelectionListener(trackSelectionListener);\r
- trackSelectionListener.valueChanged(null);\r
- }\r
- }\r
- /**\r
- * トラック選択リスナー\r
- */\r
- TrackSelectionListener trackSelectionListener;\r
- /**\r
- * 選択トラックの変更に反応するリスナー\r
- */\r
- private class TrackSelectionListener implements ListSelectionListener {\r
- @Override\r
- public void valueChanged(ListSelectionEvent e) {\r
- if( e != null && e.getValueIsAdjusting() )\r
- return;\r
- ListSelectionModel tlsm = getModel().trackListSelectionModel;\r
- deleteTrackAction.setEnabled(! tlsm.isSelectionEmpty());\r
- eventListTable.titleLabel.update(tlsm, getModel());\r
- }\r
- }\r
- /**\r
- * {@inheritDoc}\r
- *\r
- * <p>このトラックリストテーブルのデータが変わったときに編集を解除します。\r
- * 例えば、イベントが編集された場合や、\r
- * シーケンサーからこのモデルが外された場合がこれに該当します。\r
- * </p>\r
- */\r
- @Override\r
- public void tableChanged(TableModelEvent e) {\r
- super.tableChanged(e);\r
- cancelCellEditing();\r
- }\r
- /**\r
- * このトラックリストテーブルが編集モードになっていたら解除します。\r
- */\r
- private void cancelCellEditing() {\r
- TableCellEditor currentCellEditor = getCellEditor();\r
- if( currentCellEditor != null )\r
- currentCellEditor.cancelCellEditing();\r
- }\r
- /**\r
- * トラック追加アクション\r
- */\r
- Action addTrackAction = new AbstractAction("New") {\r
- {\r
- String tooltip = "Append new track - 新しいトラックの追加";\r
- putValue(Action.SHORT_DESCRIPTION, tooltip);\r
- setEnabled(false);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- getModel().createTrack();\r
- }\r
- };\r
- /**\r
- * トラック削除アクション\r
- */\r
- Action deleteTrackAction = new AbstractAction("Delete", deleteIcon) {\r
- {\r
- String tooltip = "Delete selected track - 選択したトラックを削除";\r
- putValue(Action.SHORT_DESCRIPTION, tooltip);\r
- setEnabled(false);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- String message = "Do you want to delete selected track ?\n"\r
- + "選択したトラックを削除しますか?";\r
- if( confirm(message) ) getModel().deleteSelectedTracks();\r
- }\r
- };\r
- }\r
-\r
- /**\r
- * MIDIイベントリストテーブルビュー(選択中のトラックの中身)\r
- */\r
- public class EventListTable extends JTable {\r
- /**\r
- * 新しいイベントリストテーブルを構築します。\r
- * <p>データモデルとして一つのトラックのイベントリストを指定できます。\r
- * トラックを切り替えたいときは {@link #setModel(TableModel)}\r
- * でデータモデルを異なるトラックのものに切り替えます。\r
- * </p>\r
- *\r
- * @param model トラック(イベントリスト)データモデル\r
- */\r
- public EventListTable(TrackEventListTableModel model) {\r
- super(model, null, model.eventSelectionModel);\r
- //\r
- // 列モデルにセルエディタを設定\r
- eventCellEditor = new MidiEventCellEditor();\r
- setAutoCreateColumnsFromModel(false);\r
- //\r
- eventSelectionListener = new EventSelectionListener();\r
- titleLabel = new TitleLabel();\r
- //\r
- TableColumnModel colModel = getColumnModel();\r
- for( TrackEventListTableModel.Column c : TrackEventListTableModel.Column.values() )\r
- colModel.getColumn(c.ordinal()).setPreferredWidth(c.preferredWidth);\r
- }\r
- /**\r
- * このテーブルビューが表示するデータを提供する\r
- * トラック(イベントリスト)データモデルを返します。\r
- * @return トラック(イベントリスト)データモデル\r
- */\r
- @Override\r
- public TrackEventListTableModel getModel() {\r
- return (TrackEventListTableModel) super.getModel();\r
- }\r
- /**\r
- * タイトルラベル\r
- */\r
- TitleLabel titleLabel;\r
- /**\r
- * 親テーブルの選択トラックの変更に反応する\r
- * トラック番号つきタイトルラベル\r
- */\r
- private class TitleLabel extends JLabel {\r
- private static final String TITLE = "MIDI Events";\r
- public TitleLabel() { super(TITLE); }\r
- public void update(ListSelectionModel tlsm, SequenceTrackListTableModel sequenceModel) {\r
- String text = TITLE;\r
- TrackEventListTableModel oldTrackModel = getModel();\r
- int index = tlsm.getMinSelectionIndex();\r
- if( index >= 0 ) {\r
- text = String.format(TITLE+" - track No.%d", index);\r
- }\r
- setText(text);\r
- TrackEventListTableModel newTrackModel = sequenceModel.getSelectedTrackModel();\r
- if( oldTrackModel == newTrackModel )\r
- return;\r
- if( newTrackModel == null ) {\r
- newTrackModel = getModel().sequenceTrackListTableModel.sequenceListTableModel.emptyEventListTableModel;\r
- queryJumpEventAction.setEnabled(false);\r
- queryAddEventAction.setEnabled(false);\r
-\r
- queryPasteEventAction.setEnabled(false);\r
- copyEventAction.setEnabled(false);\r
- deleteEventAction.setEnabled(false);\r
- cutEventAction.setEnabled(false);\r
- }\r
- else {\r
- queryJumpEventAction.setEnabled(true);\r
- queryAddEventAction.setEnabled(true);\r
- }\r
- oldTrackModel.eventSelectionModel.removeListSelectionListener(eventSelectionListener);\r
- setModel(newTrackModel);\r
- setSelectionModel(newTrackModel.eventSelectionModel);\r
- newTrackModel.eventSelectionModel.addListSelectionListener(eventSelectionListener);\r
- }\r
- }\r
- /**\r
- * イベント選択リスナー\r
- */\r
- private EventSelectionListener eventSelectionListener;\r
- /**\r
- * 選択イベントの変更に反応するリスナー\r
- */\r
- private class EventSelectionListener implements ListSelectionListener {\r
- public EventSelectionListener() {\r
- getModel().eventSelectionModel.addListSelectionListener(this);\r
- }\r
- @Override\r
- public void valueChanged(ListSelectionEvent e) {\r
- if( e.getValueIsAdjusting() )\r
- return;\r
- if( getSelectionModel().isSelectionEmpty() ) {\r
- queryPasteEventAction.setEnabled(false);\r
- copyEventAction.setEnabled(false);\r
- deleteEventAction.setEnabled(false);\r
- cutEventAction.setEnabled(false);\r
- }\r
- else {\r
- copyEventAction.setEnabled(true);\r
- deleteEventAction.setEnabled(true);\r
- cutEventAction.setEnabled(true);\r
- TrackEventListTableModel trackModel = getModel();\r
- int minIndex = getSelectionModel().getMinSelectionIndex();\r
- MidiEvent midiEvent = trackModel.getMidiEvent(minIndex);\r
- if( midiEvent != null ) {\r
- MidiMessage msg = midiEvent.getMessage();\r
- if( msg instanceof ShortMessage ) {\r
- ShortMessage sm = (ShortMessage)msg;\r
- int cmd = sm.getCommand();\r
- if( cmd == 0x80 || cmd == 0x90 || cmd == 0xA0 ) {\r
- // ノート番号を持つ場合、音を鳴らす。\r
- MidiChannel outMidiChannels[] = virtualMidiDevice.getChannels();\r
- int ch = sm.getChannel();\r
- int note = sm.getData1();\r
- int vel = sm.getData2();\r
- outMidiChannels[ch].noteOn(note, vel);\r
- outMidiChannels[ch].noteOff(note, vel);\r
- }\r
- }\r
- }\r
- if( pairNoteOnOffModel.isSelected() ) {\r
- int maxIndex = getSelectionModel().getMaxSelectionIndex();\r
- int partnerIndex;\r
- for( int i=minIndex; i<=maxIndex; i++ ) {\r
- if( ! getSelectionModel().isSelectedIndex(i) ) continue;\r
- partnerIndex = trackModel.getIndexOfPartnerFor(i);\r
- if( partnerIndex >= 0 && ! getSelectionModel().isSelectedIndex(partnerIndex) )\r
- getSelectionModel().addSelectionInterval(partnerIndex, partnerIndex);\r
- }\r
- }\r
- }\r
- }\r
- }\r
- /**\r
- * Pair noteON/OFF トグルボタンモデル\r
- */\r
- private JToggleButton.ToggleButtonModel\r
- pairNoteOnOffModel = new JToggleButton.ToggleButtonModel() {\r
- {\r
- addItemListener(\r
- new ItemListener() {\r
- public void itemStateChanged(ItemEvent e) {\r
- eventDialog.midiMessageForm.durationForm.setEnabled(isSelected());\r
- }\r
- }\r
- );\r
- setSelected(true);\r
- }\r
- };\r
- private class EventEditContext {\r
- /**\r
- * 編集対象トラック\r
- */\r
- private TrackEventListTableModel trackModel;\r
- /**\r
- * tick位置入力モデル\r
- */\r
- private TickPositionModel tickPositionModel = new TickPositionModel();\r
- /**\r
- * 選択されたイベント\r
- */\r
- private MidiEvent selectedMidiEvent = null;\r
- /**\r
- * 選択されたイベントの場所\r
- */\r
- private int selectedIndex = -1;\r
- /**\r
- * 選択されたイベントのtick位置\r
- */\r
- private long currentTick = 0;\r
- /**\r
- * 上書きして削除対象にする変更前イベント(null可)\r
- */\r
- private MidiEvent[] midiEventsToBeOverwritten;\r
- /**\r
- * 選択したイベントを入力ダイアログなどに反映します。\r
- * @param model 対象データモデル\r
- */\r
- private void setSelectedEvent(TrackEventListTableModel trackModel) {\r
- this.trackModel = trackModel;\r
- SequenceTrackListTableModel sequenceTableModel = trackModel.sequenceTrackListTableModel;\r
- int ppq = sequenceTableModel.getSequence().getResolution();\r
- eventDialog.midiMessageForm.durationForm.setPPQ(ppq);\r
- tickPositionModel.setSequenceIndex(sequenceTableModel.getSequenceTickIndex());\r
-\r
- selectedIndex = trackModel.eventSelectionModel.getMinSelectionIndex();\r
- selectedMidiEvent = selectedIndex < 0 ? null : trackModel.getMidiEvent(selectedIndex);\r
- currentTick = selectedMidiEvent == null ? 0 : selectedMidiEvent.getTick();\r
- tickPositionModel.setTickPosition(currentTick);\r
- }\r
- public void setupForEdit(TrackEventListTableModel trackModel) {\r
- MidiEvent partnerEvent = null;\r
- eventDialog.midiMessageForm.setMessage(\r
- selectedMidiEvent.getMessage(),\r
- trackModel.sequenceTrackListTableModel.charset\r
- );\r
- if( eventDialog.midiMessageForm.isNote() ) {\r
- int partnerIndex = trackModel.getIndexOfPartnerFor(selectedIndex);\r
- if( partnerIndex < 0 ) {\r
- eventDialog.midiMessageForm.durationForm.setDuration(0);\r
- }\r
- else {\r
- partnerEvent = trackModel.getMidiEvent(partnerIndex);\r
- long partnerTick = partnerEvent.getTick();\r
- long duration = currentTick > partnerTick ?\r
- currentTick - partnerTick : partnerTick - currentTick ;\r
- eventDialog.midiMessageForm.durationForm.setDuration((int)duration);\r
- }\r
- }\r
- if(partnerEvent == null)\r
- midiEventsToBeOverwritten = new MidiEvent[] {selectedMidiEvent};\r
- else\r
- midiEventsToBeOverwritten = new MidiEvent[] {selectedMidiEvent, partnerEvent};\r
- }\r
- private Action jumpEventAction = new AbstractAction() {\r
- { putValue(NAME,"Jump"); }\r
- public void actionPerformed(ActionEvent e) {\r
- long tick = tickPositionModel.getTickPosition();\r
- scrollToEventAt(tick);\r
- eventDialog.setVisible(false);\r
- trackModel = null;\r
- }\r
- };\r
- private Action pasteEventAction = new AbstractAction() {\r
- { putValue(NAME,"Paste"); }\r
- public void actionPerformed(ActionEvent e) {\r
- long tick = tickPositionModel.getTickPosition();\r
- clipBoard.paste(trackModel, tick);\r
- scrollToEventAt(tick);\r
- // ペーストで曲の長さが変わったことをプレイリストに通知\r
- SequenceTrackListTableModel seqModel = trackModel.sequenceTrackListTableModel;\r
- seqModel.sequenceListTableModel.fireSequenceModified(seqModel);\r
- eventDialog.setVisible(false);\r
- trackModel = null;\r
- }\r
- };\r
- private boolean applyEvent() {\r
- long tick = tickPositionModel.getTickPosition();\r
- MidiMessageForm form = eventDialog.midiMessageForm;\r
- SequenceTrackListTableModel seqModel = trackModel.sequenceTrackListTableModel;\r
- MidiEvent newMidiEvent = new MidiEvent(form.getMessage(seqModel.charset), tick);\r
- if( midiEventsToBeOverwritten != null ) {\r
- // 上書き消去するための選択済イベントがあった場合\r
- trackModel.removeMidiEvents(midiEventsToBeOverwritten);\r
- }\r
- if( ! trackModel.addMidiEvent(newMidiEvent) ) {\r
- System.out.println("addMidiEvent failure");\r
- return false;\r
- }\r
- if(pairNoteOnOffModel.isSelected() && form.isNote()) {\r
- ShortMessage sm = form.createPartnerMessage();\r
- if(sm == null)\r
- scrollToEventAt( tick );\r
- else {\r
- int duration = form.durationForm.getDuration();\r
- if( form.isNote(false) ) {\r
- duration = -duration;\r
- }\r
- long partnerTick = tick + (long)duration;\r
- if( partnerTick < 0L ) partnerTick = 0L;\r
- MidiEvent partner = new MidiEvent((MidiMessage)sm, partnerTick);\r
- if( ! trackModel.addMidiEvent(partner) ) {\r
- System.out.println("addMidiEvent failure (note on/off partner message)");\r
- }\r
- scrollToEventAt(partnerTick > tick ? partnerTick : tick);\r
- }\r
- }\r
- seqModel.sequenceListTableModel.fireSequenceModified(seqModel);\r
- eventDialog.setVisible(false);\r
- return true;\r
- }\r
- }\r
- private EventEditContext editContext = new EventEditContext();\r
- /**\r
- * 指定のTick位置へジャンプするアクション\r
- */\r
- Action queryJumpEventAction = new AbstractAction() {\r
- {\r
- putValue(NAME,"Jump to ...");\r
- setEnabled(false);\r
- }\r
- public void actionPerformed(ActionEvent e) {\r
- editContext.setSelectedEvent(getModel());\r
- eventDialog.openTickForm("Jump selection to", editContext.jumpEventAction);\r
- }\r
- };\r
- /**\r
- * 新しいイベントの追加を行うアクション\r
- */\r
- Action queryAddEventAction = new AbstractAction() {\r
- {\r
- putValue(NAME,"New");\r
- setEnabled(false);\r
- }\r
- public void actionPerformed(ActionEvent e) {\r
- TrackEventListTableModel model = getModel();\r
- editContext.setSelectedEvent(model);\r
- editContext.midiEventsToBeOverwritten = null;\r
- eventDialog.openEventForm(\r
- "New MIDI event",\r
- eventCellEditor.applyEventAction,\r
- model.getChannel()\r
- );\r
- }\r
- };\r
- /**\r
- * MIDIイベントのコピー&ペーストを行うためのクリップボード\r
- */\r
- private class LocalClipBoard {\r
- private MidiEvent copiedEventsToPaste[];\r
- private int copiedEventsPPQ = 0;\r
- public void copy(TrackEventListTableModel model, boolean withRemove) {\r
- copiedEventsToPaste = model.getSelectedMidiEvents();\r
- copiedEventsPPQ = model.sequenceTrackListTableModel.getSequence().getResolution();\r
- if( withRemove ) model.removeMidiEvents(copiedEventsToPaste);\r
- boolean en = (copiedEventsToPaste != null && copiedEventsToPaste.length > 0);\r
- queryPasteEventAction.setEnabled(en);\r
- }\r
- public void cut(TrackEventListTableModel model) {copy(model,true);}\r
- public void copy(TrackEventListTableModel model){copy(model,false);}\r
- public void paste(TrackEventListTableModel model, long tick) {\r
- model.addMidiEvents(copiedEventsToPaste, tick, copiedEventsPPQ);\r
- }\r
- }\r
- private LocalClipBoard clipBoard = new LocalClipBoard();\r
- /**\r
- * 指定のTick位置へ貼り付けるアクション\r
- */\r
- Action queryPasteEventAction = new AbstractAction() {\r
- {\r
- putValue(NAME,"Paste to ...");\r
- setEnabled(false);\r
- }\r
- public void actionPerformed(ActionEvent e) {\r
- editContext.setSelectedEvent(getModel());\r
- eventDialog.openTickForm("Paste to", editContext.pasteEventAction);\r
- }\r
- };\r
- /**\r
- * イベントカットアクション\r
- */\r
- public Action cutEventAction = new AbstractAction("Cut") {\r
- {\r
- setEnabled(false);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- TrackEventListTableModel model = getModel();\r
- if( ! confirm("Do you want to cut selected event ?\n選択したMIDIイベントを切り取りますか?"))\r
- return;\r
- clipBoard.cut(model);\r
- }\r
- };\r
- /**\r
- * イベントコピーアクション\r
- */\r
- public Action copyEventAction = new AbstractAction("Copy") {\r
- {\r
- setEnabled(false);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- clipBoard.copy(getModel());\r
- }\r
- };\r
- /**\r
- * イベント削除アクション\r
- */\r
- public Action deleteEventAction = new AbstractAction("Delete", deleteIcon) {\r
- {\r
- setEnabled(false);\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- TrackEventListTableModel model = getModel();\r
- if( ! confirm("Do you want to delete selected event ?\n選択したMIDIイベントを削除しますか?"))\r
- return;\r
- model.removeSelectedMidiEvents();\r
- }\r
- };\r
- /**\r
- * MIDIイベント表のセルエディタ\r
- */\r
- private MidiEventCellEditor eventCellEditor;\r
- /**\r
- * MIDIイベント表のセルエディタ\r
- */\r
- class MidiEventCellEditor extends AbstractCellEditor implements TableCellEditor {\r
- /**\r
- * MIDIイベントセルエディタを構築します。\r
- */\r
- public MidiEventCellEditor() {\r
- eventDialog.midiMessageForm.setOutputMidiChannels(virtualMidiDevice.getChannels());\r
- eventDialog.tickPositionInputForm.setModel(editContext.tickPositionModel);\r
- int index = TrackEventListTableModel.Column.MESSAGE.ordinal();\r
- getColumnModel().getColumn(index).setCellEditor(this);\r
- }\r
- /**\r
- * セルをダブルクリックしないと編集できないようにします。\r
- * @param e イベント(マウスイベント)\r
- * @return 編集可能になったらtrue\r
- */\r
- @Override\r
- public boolean isCellEditable(EventObject e) {\r
- if( ! (e instanceof MouseEvent) )\r
- return super.isCellEditable(e);\r
- return ((MouseEvent)e).getClickCount() == 2;\r
- }\r
- @Override\r
- public Object getCellEditorValue() { return null; }\r
- /**\r
- * MIDIメッセージダイアログが閉じたときにセル編集を中止するリスナー\r
- */\r
- private ComponentListener dialogComponentListener = new ComponentAdapter() {\r
- @Override\r
- public void componentHidden(ComponentEvent e) {\r
- fireEditingCanceled();\r
- // 用が済んだら当リスナーを除去\r
- eventDialog.removeComponentListener(this);\r
- }\r
- };\r
- /**\r
- * 既存イベントを編集するアクション\r
- */\r
- private Action editEventAction = new AbstractAction() {\r
- public void actionPerformed(ActionEvent e) {\r
- TrackEventListTableModel model = getModel();\r
- editContext.setSelectedEvent(model);\r
- if( editContext.selectedMidiEvent == null )\r
- return;\r
- editContext.setupForEdit(model);\r
- eventDialog.addComponentListener(dialogComponentListener);\r
- eventDialog.openEventForm("Change MIDI event", applyEventAction);\r
- }\r
- };\r
- /**\r
- * イベント編集ボタン\r
- */\r
- private JButton editEventButton = new JButton(editEventAction){{\r
- setHorizontalAlignment(JButton.LEFT);\r
- }};\r
- @Override\r
- public Component getTableCellEditorComponent(\r
- JTable table, Object value, boolean isSelected, int row, int column\r
- ) {\r
- editEventButton.setText(value.toString());\r
- return editEventButton;\r
- }\r
- /**\r
- * 入力したイベントを反映するアクション\r
- */\r
- private Action applyEventAction = new AbstractAction() {\r
- {\r
- putValue(NAME,"OK");\r
- }\r
- public void actionPerformed(ActionEvent e) {\r
- if( editContext.applyEvent() ) fireEditingStopped();\r
- }\r
- };\r
- }\r
- /**\r
- * スクロール可能なMIDIイベントテーブルビュー\r
- */\r
- private JScrollPane scrollPane = new JScrollPane(this);\r
- /**\r
- * 指定の MIDI tick のイベントへスクロールします。\r
- * @param tick MIDI tick\r
- */\r
- public void scrollToEventAt(long tick) {\r
- int index = getModel().tickToIndex(tick);\r
- scrollPane.getVerticalScrollBar().setValue(index * getRowHeight());\r
- getSelectionModel().setSelectionInterval(index, index);\r
- }\r
- }\r
-\r
- /**\r
- * 新しい {@link MIDIEditor} を構築します。\r
- * @param deviceModelList MIDIデバイスモデルリスト\r
- */\r
- public MIDIEditor(MidiSequencerModel sequencerModel) {\r
- // テーブルモデルとテーブルビューの生成\r
- sequenceListTable = new SequenceListTable(\r
- new SequenceListTableModel(sequencerModel)\r
- );\r
- trackListTable = new TrackListTable(\r
- new SequenceTrackListTableModel(\r
- sequenceListTable.getModel(), null, null\r
- )\r
- );\r
- eventListTable = new EventListTable(\r
- new TrackEventListTableModel(trackListTable.getModel(), null)\r
- );\r
- // レイアウト\r
- setTitle("MIDI Editor/Playlist - MIDI Chord Helper");\r
- setBounds( 150, 200, 900, 500 );\r
- setLayout(new FlowLayout());\r
- new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this, true);\r
- JPanel playlistPanel = new JPanel() {{\r
- JPanel playlistOperationPanel = new JPanel() {{\r
- setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
- add(Box.createRigidArea(new Dimension(10, 0)));\r
- add(new JButton(newSequenceDialog.openAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- if( sequenceListTable.midiFileChooser != null ) {\r
- add( Box.createRigidArea(new Dimension(5, 0)) );\r
- add(new JButton(\r
- sequenceListTable.midiFileChooser.openMidiFileAction\r
- ) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- }\r
- if(sequenceListTable.base64EncodeAction != null) {\r
- add(Box.createRigidArea(new Dimension(5, 0)));\r
- add(new JButton(sequenceListTable.base64EncodeAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- }\r
- add(Box.createRigidArea(new Dimension(5, 0)));\r
- SequenceListTableModel sequenceListTableModel = sequenceListTable.getModel();\r
- add(new JButton(sequenceListTableModel.moveToTopAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(Box.createRigidArea(new Dimension(5, 0)));\r
- add(new JButton(sequenceListTableModel.moveToBottomAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- if( sequenceListTable.midiFileChooser != null ) {\r
- add(Box.createRigidArea(new Dimension(5, 0)));\r
- add(new JButton(\r
- sequenceListTable.midiFileChooser.saveMidiFileAction\r
- ) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- }\r
- add( Box.createRigidArea(new Dimension(5, 0)) );\r
- add(new JButton(sequenceListTable.deleteSequenceAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add( Box.createRigidArea(new Dimension(5, 0)) );\r
- add(new SequencerSpeedSlider(\r
- sequenceListTableModel.sequencerModel.speedSliderModel\r
- ));\r
- add( Box.createRigidArea(new Dimension(5, 0)) );\r
- add(new JPanel() {{\r
- add(new JLabel("SyncMode:"));\r
- add(new JLabel("Master"));\r
- add(new JComboBox<Sequencer.SyncMode>(\r
- sequenceListTable.getModel().sequencerModel.masterSyncModeModel\r
- ));\r
- add(new JLabel("Slave"));\r
- add(new JComboBox<Sequencer.SyncMode>(\r
- sequenceListTable.getModel().sequencerModel.slaveSyncModeModel\r
- ));\r
- }});\r
- }};\r
- setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
- add(new JScrollPane(sequenceListTable));\r
- add(Box.createRigidArea(new Dimension(0, 10)));\r
- add(playlistOperationPanel);\r
- add(Box.createRigidArea(new Dimension(0, 10)));\r
- }};\r
- JPanel trackListPanel = new JPanel() {{\r
- JPanel trackListOperationPanel = new JPanel() {{\r
- add(new JButton(trackListTable.addTrackAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(trackListTable.deleteTrackAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- }};\r
- setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\r
- add(trackListTable.titleLabel);\r
- add(Box.createRigidArea(new Dimension(0, 5)));\r
- add(new JScrollPane(trackListTable));\r
- add(Box.createRigidArea(new Dimension(0, 5)));\r
- add(trackListOperationPanel);\r
- }};\r
- JPanel eventListPanel = new JPanel() {{\r
- JPanel eventListOperationPanel = new JPanel() {{\r
- add(new JCheckBox("Pair NoteON/OFF") {{\r
- setModel(eventListTable.pairNoteOnOffModel);\r
- setToolTipText("NoteON/OFFをペアで同時選択する");\r
- }});\r
- add(new JButton(eventListTable.queryJumpEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(eventListTable.queryAddEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(eventListTable.copyEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(eventListTable.cutEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(eventListTable.queryPasteEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- add(new JButton(eventListTable.deleteEventAction) {{\r
- setMargin(ZERO_INSETS);\r
- }});\r
- }};\r
- setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
- add(eventListTable.titleLabel);\r
- add(eventListTable.scrollPane);\r
- add(eventListOperationPanel);\r
- }};\r
- Container cp = getContentPane();\r
- cp.setLayout(new BoxLayout(cp, BoxLayout.Y_AXIS));\r
- cp.add(Box.createVerticalStrut(2));\r
- cp.add(\r
- new JSplitPane(JSplitPane.VERTICAL_SPLIT, playlistPanel,\r
- new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, trackListPanel, eventListPanel) {{\r
- setDividerLocation(300);\r
- }}\r
- ) {{\r
- setDividerLocation(160);\r
- }}\r
- );\r
- }\r
-\r
-}\r