import javax.swing.event.TableModelListener;\r
import javax.swing.filechooser.FileNameExtensionFilter;\r
import javax.swing.table.AbstractTableModel;\r
-import javax.swing.table.DefaultTableColumnModel;\r
import javax.swing.table.TableCellEditor;\r
import javax.swing.table.TableCellRenderer;\r
import javax.swing.table.TableColumn;\r
* Copyright (C) 2006-2013 Akiyoshi Kamide\r
* http://www.yk.rim.or.jp/~kamide/music/chordhelper/\r
*/\r
-class MidiEditor extends JDialog implements DropTargetListener, ActionListener {\r
+class MidiEditor extends JDialog implements DropTargetListener {\r
public static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
private static final Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);\r
/**\r
super("MIDI Editor","Unknown vendor","MIDI sequence editor","");\r
}\r
}\r
- {\r
- info = new MyInfo();\r
- // 送信のみなので MIDI IN はサポートしない\r
- setMaxReceivers(0);\r
- }\r
+ // 送信のみなので MIDI IN はサポートしない\r
+ { info = new MyInfo(); setMaxReceivers(0); }\r
};\r
/**\r
- * {@inheritDoc}\r
- * <p>すでに表示されていた場合に手前に表示する点を除き、\r
- * スーパークラスと同じです。\r
- * </p>\r
+ * このダイアログを開きます。すでに開かれていた場合は前面に移動します。\r
*/\r
- @Override\r
- public void setVisible(boolean isToVisible) {\r
- if( isToVisible && isVisible() ) {\r
- toFront();\r
- return;\r
- }\r
- super.setVisible(isToVisible);\r
+ public void open() {\r
+ if( isVisible() ) toFront(); else setVisible(true);\r
}\r
/**\r
- * {@inheritDoc}\r
- * <p>このダイアログを表示するアクションを実行します。\r
- * </p>\r
+ * このダイアログを表示するアクション\r
*/\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- setVisible(true);\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
/**\r
* 新しいMIDIシーケンスを生成するダイアログ\r
*/\r
- NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this) {\r
- { setChannels(virtualMidiDevice.getChannels()); }\r
- };\r
- /**\r
- * 新しいMIDIシーケンスを生成するアクション\r
- */\r
- public Action generateNewSongAction = new AbstractAction("New") {\r
- {\r
- putValue(\r
- Action.SHORT_DESCRIPTION,\r
- "Generate new song - 新しい曲を生成"\r
- );\r
- }\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- newSequenceDialog.setVisible(true);\r
- }\r
- };\r
+ NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this);\r
/**\r
* 選択されたシーケンスへジャンプするアクション\r
*/\r
- public Action jumpSequenceAction = new AbstractAction("Jump") {\r
+ public Action loadToSequencerAction = new AbstractAction("Load to sequencer") {\r
{\r
- putValue(Action.SHORT_DESCRIPTION, "Move to selected song - 選択した曲へ進む");\r
+ String tooltip = "Load selected MIDI sequence to sequencer - 選択した曲をシーケンサへロード";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
}\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
* シーケンスを削除するアクション\r
*/\r
public Action deleteSequenceAction = new AbstractAction("Delete",deleteIcon) {\r
+ {\r
+ String tooltip = "Delete selected MIDI sequence - 選択した曲をプレイリストから削除";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
if( midiFileChooser != null ) {\r
// ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
- SequenceTrackListTableModel seqModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ //\r
+ SequenceTrackListTableModel seqModel =\r
+ sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
if( seqModel.isModified() ) {\r
// ファイル未保存の変更がある場合\r
- String confirmMessage =\r
+ //\r
+ String message =\r
"Selected MIDI sequence not saved - delete it ?\n" +\r
"選択したMIDIシーケンスはまだ保存されていません。削除しますか?";\r
- if( ! confirm(confirmMessage) ) {\r
- // ユーザに確認してNoって言われた場合\r
+ if( ! confirm(message) ) {\r
+ // 実は削除してほしくなかった場合\r
return;\r
}\r
}\r
});\r
if( base64Dialog.isBase64Available() ) {\r
base64EncodeAction = new AbstractAction("Base64 Encode") {\r
+ {\r
+ String tooltip = "Encode selected sequence to Base64 textdata - 選択した曲をBase64テキストにエンコード";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
SequenceTrackListTableModel mstm = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
if(base64EncodeAction != null)\r
base64EncodeAction.setEnabled(isSelected);\r
deleteSequenceAction.setEnabled(isSelected);\r
- jumpSequenceAction.setEnabled(isSelected);\r
+ loadToSequencerAction.setEnabled(isSelected);\r
}\r
};\r
/**\r
* ファイル保存アクション\r
*/\r
public Action saveMidiFileAction = new AbstractAction("Save") {\r
+ {\r
+ String tooltip = "Save selected MIDI sequence to file - 選択したMIDIシーケンスをファイルに保存";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
SequenceTrackListTableModel sequenceTableModel =\r
String filename = sequenceTableModel.getFilename();\r
File midiFile;\r
if( filename != null && ! filename.isEmpty() ) {\r
- midiFile = new File(filename);\r
- setSelectedFile(midiFile);\r
+ // プレイリスト上でファイル名が入っていたら、それを初期選択\r
+ setSelectedFile(midiFile = new File(filename));\r
}\r
- int resp = showSaveDialog(MidiEditor.this);\r
- if( resp != JFileChooser.APPROVE_OPTION ) {\r
+ int response = showSaveDialog(MidiEditor.this);\r
+ if( response != JFileChooser.APPROVE_OPTION ) {\r
+ // 保存ダイアログでキャンセルされた場合\r
return;\r
}\r
- midiFile = getSelectedFile();\r
- if( midiFile.exists() && ! confirm(\r
- "Overwrite " + midiFile.getName() + " ?\n"\r
- + midiFile.getName()\r
- + " を上書きしてよろしいですか?"\r
- ) ) {\r
- return;\r
+ if( (midiFile = getSelectedFile()).exists() ) {\r
+ // 指定されたファイルがすでにあった場合\r
+ String fn = midiFile.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(midiFile) ) {\r
out.write(sequenceTableModel.getMIDIdata());\r
sequenceTableModel.setModified(false);\r
}\r
}\r
};\r
+ /**\r
+ * シーケンスの選択有無に応じて、保存ボタンのイネーブル状態を更新します。\r
+ */\r
private void updateEnabled() {\r
boolean en = (sequenceListSelectionModel.getMinSelectionIndex() >= 0);\r
saveMidiFileAction.setEnabled(en);\r
}\r
{\r
+ // ファイルフィルタの設定\r
setFileFilter(new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid"));\r
+ //\r
+ // 選択状態のリスニングを開始\r
sequenceListSelectionModel.addListSelectionListener(this);\r
updateEnabled();\r
}\r
/**\r
* ファイルを開くアクション\r
*/\r
- public Action addMidiFileAction = new AbstractAction("Open") {\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 e) {\r
- int resp = showOpenDialog(MidiEditor.this);\r
- if( resp == JFileChooser.APPROVE_OPTION )\r
+ if(showOpenDialog(MidiEditor.this) == JFileChooser.APPROVE_OPTION)\r
addSequence(getSelectedFile());\r
}\r
};\r
* トラック追加アクション\r
*/\r
public Action addTrackAction = new AbstractAction("New") {\r
+ {\r
+ String tooltip = "Append new track - 新しいトラックの追加";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
int index = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).createTrack();\r
}\r
};\r
/**\r
- * MIDIトラック除去アクション\r
+ * トラック削除アクション\r
*/\r
- public Action removeTrackAction = new AbstractAction("Delete", deleteIcon) {\r
+ public Action deleteTrackAction = new AbstractAction("Delete", deleteIcon) {\r
+ {\r
+ String tooltip = "Delete selected track - 選択したトラックを削除";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
if( ! confirm("Do you want to delete selected track ?\n選択したトラックを削除しますか?"))\r
private JTable trackListTableView = new JTable(\r
new SequenceTrackListTableModel(sequenceListTableModel),\r
null, trackSelectionModel\r
- );\r
- private TracksLabel trackListTitleLabel = new TracksLabel();\r
- private class TracksLabel extends JLabel implements ListSelectionListener {\r
+ ) {{\r
+ // 録音対象のMIDIチャンネルをコンボボックスで選択できるよう、\r
+ // セルエディタを差し替える。\r
+ getColumnModel().getColumn(\r
+ SequenceTrackListTableModel.Column.RECORD_CHANNEL.ordinal()\r
+ ).setCellEditor(\r
+ 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
+ );\r
+ // デフォルトでは、データモデルが差し替えられると列が再作成される。\r
+ // しかしこれでは、シーケンスモデルを差し替えたとたん、\r
+ // せっかく差し替えたセルエディタがデフォルトに戻ってしまう。\r
+ //\r
+ // そこで、一度列データモデルが自動作成されたら、\r
+ // 以後は自動作成しないようにする。\r
+ setAutoCreateColumnsFromModel(false);\r
+ }};\r
+ /**\r
+ * MIDIトラックリストのタイトルラベル\r
+ */\r
+ private JLabel trackListTitleLabel = new JLabel() {\r
private static final String TITLE = "Tracks";\r
- public TracksLabel() {\r
- super(TITLE);\r
- sequenceListSelectionModel.addListSelectionListener(this);\r
- }\r
- @Override\r
- public void valueChanged(ListSelectionEvent e) {\r
- if( e.getValueIsAdjusting() )\r
- return;\r
- int index = sequenceListSelectionModel.getMinSelectionIndex();\r
- String text = TITLE;\r
- if( index >= 0 )\r
- text = String.format(text+" - MIDI file No.%d", index);\r
- setText(text);\r
+ {\r
+ sequenceListSelectionModel.addListSelectionListener(\r
+ new ListSelectionListener() {\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if( e.getValueIsAdjusting() )\r
+ return;\r
+ int index = sequenceListSelectionModel.getMinSelectionIndex();\r
+ String text = TITLE;\r
+ if( index >= 0 )\r
+ text = String.format(text+" - MIDI file No.%d", index);\r
+ setText(text);\r
+ }\r
+ }\r
+ );\r
+ setText(TITLE);\r
}\r
- }\r
+ };\r
\r
/**\r
* MIDIイベント選択状態\r
* MIDIイベントリストテーブルビュー\r
*/\r
private JTable trackEventListTableView = new JTable(\r
- new TrackEventListTableModel(), null, eventSelectionModel\r
+ new TrackEventListTableModel(),\r
+ null,\r
+ eventSelectionModel\r
);\r
private MidiEventsLabel midiEventsLabel = new MidiEventsLabel();\r
private class MidiEventsLabel extends JLabel implements ListSelectionListener {\r
midiFileChooser = null;\r
}\r
sequenceListTableView = new JTable(\r
- sequenceListTableModel,\r
- new SequenceListTableColumnModel() {\r
- JToggleButton playButton = new JToggleButton(\r
- sequenceListTableModel.sequencerModel.startStopAction\r
- ){{ setMargin(ZERO_INSETS); }};\r
- class ButtonCellEditor extends AbstractCellEditor implements TableCellEditor {\r
+ sequenceListTableModel, null, sequenceListSelectionModel\r
+ ) {\r
+ private JToggleButton playButton = new JToggleButton(\r
+ sequenceListTableModel.sequencerModel.startStopAction\r
+ ){{\r
+ setMargin(ZERO_INSETS);\r
+ }};\r
+ {\r
+ sequenceListTableModel.addTableModelListener(new TableModelListener() {\r
+ /**\r
+ * 全シーケンスの合計時間長をヘッダータイトルに反映します。\r
+ * @param e テーブルモデルイベント\r
+ */\r
@Override\r
- public Object getCellEditorValue() { return ""; }\r
+ public void tableChanged(TableModelEvent e) {\r
+ int sec = sequenceListTableModel.getTotalSeconds();\r
+ SequenceListTableModel.Column c = SequenceListTableModel.Column.SEQ_LENGTH;\r
+ TableColumn tc = getColumnModel().getColumn(c.ordinal());\r
+ tc.setHeaderValue(String.format(c.title+" [%02d:%02d]", sec/60, sec%60));\r
+ //\r
+ // シーケンス削除時など、合計シーケンス長が変わっても\r
+ // 列モデルからではヘッダタイトルが再描画されないことがある。\r
+ // そこで、ヘッダビューから repaint() で突っついて再描画させる。\r
+ getTableHeader().repaint();\r
+ }\r
+ });\r
+ TableColumn tc = getColumnModel().getColumn(\r
+ SequenceListTableModel.Column.SEQ_PLAY.ordinal()\r
+ );\r
+ tc.setCellRenderer(new TableCellRenderer() {\r
@Override\r
- public Component getTableCellEditorComponent(\r
+ public Component getTableCellRendererComponent(\r
JTable table, Object value, boolean isSelected,\r
- int row, int column\r
+ boolean hasFocus, int row, int column\r
) {\r
- if(row == sequenceListTableModel.getLoadedIndex())\r
+ if(sequenceListTableModel.sequenceList.get(row).isOnSequencer()) {\r
+ // すでにロードされていたらボタンをレンダリング\r
return playButton;\r
- return null;\r
- }\r
- }\r
- {\r
- sequenceListTableModel.addTableModelListener(this);\r
- TableColumn tc = getColumn(Column.SEQ_PLAY.ordinal());\r
- tc.setCellRenderer(new TableCellRenderer() {\r
- @Override\r
- public Component getTableCellRendererComponent(\r
- JTable table, Object value, boolean isSelected,\r
- boolean hasFocus, int row, int column\r
- ) {\r
- if(row == sequenceListTableModel.getLoadedIndex())\r
- return playButton;\r
- return null;\r
}\r
- });\r
- tc.setCellEditor(new ButtonCellEditor());\r
- }\r
- },\r
- sequenceListSelectionModel\r
- ) {{\r
- sequenceListTableModel.addTableModelListener(\r
- new TableModelListener() {\r
- // シーケンス削除時など、合計シーケンス長が変わっても\r
- // 列モデルからではヘッダタイトルが再描画されないことがある。\r
- // そこで、ヘッダビューから repaint() で突っついて再描画させる。\r
- @Override\r
- public void tableChanged(TableModelEvent e) {\r
- getTableHeader().repaint();\r
+ // ロードされていなかった場合、\r
+ // デフォルトレンダラーでレンダリングする。こうすれば\r
+ // レンダラーを設定しなかった場合と全く同じ動作になる。\r
+ Class<?> columnClass =\r
+ sequenceListTableModel.getColumnClass(column);\r
+ TableCellRenderer defaultRenderer =\r
+ table.getDefaultRenderer(columnClass);\r
+ return defaultRenderer.getTableCellRendererComponent(\r
+ table, value, isSelected, hasFocus, row, column\r
+ );\r
}\r
+ });\r
+ tc.setCellEditor(new PlayButtonCellEditor());\r
+ setAutoCreateColumnsFromModel(false);\r
+ }\r
+ class PlayButtonCellEditor extends AbstractCellEditor implements TableCellEditor {\r
+ @Override\r
+ public Object getCellEditorValue() { return ""; }\r
+ @Override\r
+ public Component getTableCellEditorComponent(\r
+ JTable table, Object value, boolean isSelected,\r
+ int row, int column\r
+ ) {\r
+ return sequenceListTableModel.sequenceList.get(row).isOnSequencer() ? playButton : null;\r
}\r
- );\r
- }};\r
+ }\r
+ };\r
JPanel playlistPanel = new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
add(new JScrollPane(sequenceListTableView));\r
add(new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
add(Box.createRigidArea(new Dimension(10, 0)));\r
- add(new JButton(generateNewSongAction) {{setMargin(ZERO_INSETS);}});\r
+ add(new JButton(newSequenceDialog.openAction) {{\r
+ setMargin(ZERO_INSETS);\r
+ }});\r
if( midiFileChooser != null ) {\r
add( Box.createRigidArea(new Dimension(5, 0)) );\r
- add(new JButton(midiFileChooser.addMidiFileAction) {{\r
+ add(new JButton(midiFileChooser.openMidiFileAction) {{\r
setMargin(ZERO_INSETS);\r
}});\r
}\r
setMargin(ZERO_INSETS);\r
}});\r
add(Box.createRigidArea(new Dimension(5, 0)));\r
- add(new JButton(jumpSequenceAction){{ setMargin(ZERO_INSETS); }});\r
+ add(new JButton(loadToSequencerAction){{ setMargin(ZERO_INSETS); }});\r
add(Box.createRigidArea(new Dimension(5, 0)));\r
add(new JButton(sequenceListTableModel.moveToBottomAction) {{\r
setMargin(ZERO_INSETS);\r
add(Box.createRigidArea(new Dimension(0, 5)));\r
add(new JPanel() {{\r
add(new JButton(addTrackAction) {{ setMargin(ZERO_INSETS); }});\r
- add(new JButton(removeTrackAction) {{ setMargin(ZERO_INSETS); }});\r
+ add(new JButton(deleteTrackAction) {{ setMargin(ZERO_INSETS); }});\r
}});\r
}};\r
JPanel eventListPanel = new JPanel() {{\r
* ボタン状態の更新\r
*/\r
public void updateButtonStatus() {\r
- SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ SequenceTrackListTableModel sequenceModel =\r
+ sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
boolean isSequenceSelected = (sequenceModel != null);\r
if(isSequenceSelected) {\r
trackListTableView.setModel(sequenceModel);\r
- trackListTableView.getColumnModel().getColumn(\r
- SequenceTrackListTableModel.Column.RECORD_CHANNEL.ordinal()\r
- ).setCellEditor(\r
- new DefaultCellEditor(\r
- new JComboBox<String>() {\r
- {\r
- addItem("OFF");\r
- for(int i=1; i <= MIDISpec.MAX_CHANNELS; i++)\r
- addItem(String.format("%d", i));\r
- addItem("ALL");\r
- }\r
- }\r
- )\r
- );\r
}\r
else {\r
- SequenceTrackListTableModel m = new SequenceTrackListTableModel(sequenceListTableModel);\r
- trackListTableView.setModel(m);\r
+ trackListTableView.setModel(new SequenceTrackListTableModel(sequenceListTableModel));\r
}\r
addTrackAction.setEnabled(isSequenceSelected);\r
boolean isTrackSelected = (\r
&&\r
isSequenceSelected && sequenceModel.getRowCount() > 0\r
);\r
- removeTrackAction.setEnabled(isTrackSelected);\r
+ deleteTrackAction.setEnabled(isTrackSelected);\r
//\r
TableModel tm = trackEventListTableView.getModel();\r
if( ! (tm instanceof TrackEventListTableModel) )\r
firstIndex = lastIndex;\r
}\r
if(sequenceListTableModel.sequencerModel.getSequencer().isRunning()) {\r
- setVisible(true);\r
+ open();\r
}\r
else if( firstIndex >= 0 ) {\r
sequenceListTableModel.loadToSequencer(firstIndex);\r
}\r
}\r
\r
+\r
/**\r
- * プレイリスト(MIDIシーケンスリスト)のテーブル列モデル\r
+ * プレイリスト(MIDIシーケンスリスト)のテーブルデータモデル\r
*/\r
-class SequenceListTableColumnModel extends DefaultTableColumnModel\r
- implements TableModelListener\r
-{\r
+class SequenceListTableModel extends AbstractTableModel implements ChangeListener {\r
/**\r
* 列の列挙型\r
*/\r
/** 変更済みフラグ */\r
MODIFIED("Modified", Boolean.class),\r
/** 再生ボタン */\r
- SEQ_PLAY("", String.class) {\r
+ SEQ_PLAY("Sequencer", String.class) {\r
@Override\r
public boolean isCellEditable() { return true; }\r
},\r
}\r
public boolean isCellEditable() { return false; }\r
}\r
- public SequenceListTableColumnModel() {\r
- for( Column c : Column.values() ) {\r
- TableColumn tc = new TableColumn(c.ordinal());\r
- tc.setHeaderValue(c.title);\r
- addColumn(tc);\r
- }\r
- }\r
- /**\r
- * 全シーケンスの合計時間長をヘッダータイトルに反映します。\r
- * @param e テーブルモデルイベント\r
- */\r
- @Override\r
- public void tableChanged(TableModelEvent e) {\r
- SequenceListTableModel model = (SequenceListTableModel)e.getSource();\r
- int sec = model.getTotalSeconds();\r
- Column c = Column.SEQ_LENGTH;\r
- TableColumn tc = getColumn(c.ordinal());\r
- tc.setHeaderValue(String.format(c.title+" [%02d:%02d]", sec/60, sec%60));\r
- }\r
-}\r
-\r
-/**\r
- * プレイリスト(MIDIシーケンスリスト)のテーブルモデル\r
- */\r
-class SequenceListTableModel extends AbstractTableModel implements ChangeListener {\r
/**\r
* MIDIシーケンサモデル\r
*/\r
return;\r
// 秒が変わったときだけ更新(小数点以下は無視)\r
secondPosition = sec;\r
- fireTableCellUpdated(\r
- getLoadedIndex(),\r
- SequenceListTableColumnModel.Column.SEQ_POSITION.ordinal()\r
- );\r
+ fireTableCellUpdated(getLoadedIndex(), Column.SEQ_POSITION.ordinal());\r
}\r
- private List<SequenceTrackListTableModel> sequenceList = new Vector<>();\r
+ List<SequenceTrackListTableModel> sequenceList = new Vector<>();\r
@Override\r
public int getRowCount() { return sequenceList.size(); }\r
@Override\r
public int getColumnCount() {\r
- return SequenceListTableColumnModel.Column.values().length;\r
+ return Column.values().length;\r
}\r
@Override\r
public String getColumnName(int column) {\r
- return SequenceListTableColumnModel.Column.values()[column].title;\r
+ return Column.values()[column].title;\r
}\r
@Override\r
public Class<?> getColumnClass(int column) {\r
- return SequenceListTableColumnModel.Column.values()[column].columnClass;\r
+ return Column.values()[column].columnClass;\r
}\r
@Override\r
public boolean isCellEditable(int row, int column) {\r
- return SequenceListTableColumnModel.Column.values()[column].isCellEditable();\r
+ return Column.values()[column].isCellEditable();\r
}\r
@Override\r
public Object getValueAt(int row, int column) {\r
- switch(SequenceListTableColumnModel.Column.values()[column]) {\r
+ switch(Column.values()[column]) {\r
case SEQ_NUMBER: return row;\r
case MODIFIED:\r
return sequenceList.get(row).isModified();\r
}\r
@Override\r
public void setValueAt(Object val, int row, int column) {\r
- switch(SequenceListTableColumnModel.Column.values()[column]) {\r
+ switch(Column.values()[column]) {\r
case FILENAME:\r
// ファイル名の変更\r
String filename = (String)val;\r
case SEQ_NAME:\r
// シーケンス名の設定または変更\r
if( sequenceList.get(row).setName((String)val) )\r
- fireTableCellUpdated(row, SequenceListTableColumnModel.Column.MODIFIED.ordinal());\r
+ fireTableCellUpdated(row, Column.MODIFIED.ordinal());\r
break;\r
default:\r
break;\r
for( int i = minIndex; i <= maxIndex; i++ ) {\r
if( selModel.isSelectedIndex(i) ) {\r
sequenceList.get(i).setModified(isModified);\r
- fireTableCellUpdated(i, SequenceListTableColumnModel.Column.MODIFIED.ordinal());\r
+ fireTableCellUpdated(i, Column.MODIFIED.ordinal());\r
}\r
}\r
}\r
if( listSelectionModel.isSelectionEmpty() )\r
return;\r
int selectedIndex = listSelectionModel.getMinSelectionIndex();\r
- if( selectedIndex == getLoadedIndex() ) {\r
- // シーケンサーにロード済みのシーケンスだった場合はアンロードする\r
- sequencerModel.setSequenceTableModel(null);\r
+ if( sequenceList.remove(selectedIndex).isOnSequencer() ) {\r
+ // 削除したシーケンスが\r
+ // シーケンサーにロード済みだった場合、アンロードする。\r
+ sequencerModel.setSequenceTrackListTableModel(null);\r
}\r
- sequenceList.remove(selectedIndex);\r
fireTableRowsDeleted(selectedIndex, selectedIndex);\r
}\r
/**\r
int oldIndex = getLoadedIndex();\r
if(index == oldIndex)\r
return;\r
- SequenceTrackListTableModel sequenceTableModel = sequenceList.get(index);\r
- sequencerModel.setSequenceTableModel(sequenceTableModel);\r
- //\r
- // トラック内容の再表示\r
- sequenceTableModel.fireTableDataChanged();\r
- //\r
- // セルの再表示(シーケンサーのボタンと現在位置)\r
- fireTableRowsUpdated(oldIndex, index);\r
+ sequencerModel.setSequenceTrackListTableModel(sequenceList.get(index));\r
int columnIndices[] = {\r
- SequenceListTableColumnModel.Column.SEQ_PLAY.ordinal(),\r
- SequenceListTableColumnModel.Column.SEQ_POSITION.ordinal(),\r
+ Column.SEQ_PLAY.ordinal(),\r
+ Column.SEQ_POSITION.ordinal(),\r
};\r
for( int columnIndex : columnIndices ) {\r
fireTableCellUpdated(oldIndex, columnIndex);\r
int index = (loadedIndex < 0 ? 0 : loadedIndex + offset);\r
if( index < 0 || index >= sequenceList.size() )\r
return false;\r
- loadToSequencer( index );\r
+ loadToSequencer(index);\r
return true;\r
}\r
}\r
\r
/**\r
- * MIDIã\82·ã\83¼ã\82±ã\83³ã\82¹ï¼\88ã\83\88ã\83©ã\83\83ã\82¯ã\83ªã\82¹ã\83\88ï¼\89ã\82\92表ã\81\99ã\83\86ã\83¼ã\83\96ã\83«モデル\r
+ * MIDIã\82·ã\83¼ã\82±ã\83³ã\82¹ï¼\88ã\83\88ã\83©ã\83\83ã\82¯ã\83ªã\82¹ã\83\88ï¼\89ã\81®ã\83\86ã\83¼ã\83\96ã\83«ã\83\87ã\83¼ã\82¿モデル\r
*/\r
class SequenceTrackListTableModel extends AbstractTableModel {\r
/**\r
*/\r
public enum Column {\r
/** トラック番号 */\r
- TRACK_NUMBER("No.", 30, Integer.class),\r
+ TRACK_NUMBER("No.", Integer.class),\r
/** イベント数 */\r
- EVENTS("Events", 60, Integer.class),\r
+ EVENTS("Events", Integer.class),\r
/** Mute */\r
- MUTE("Mute", 40, Boolean.class),\r
+ MUTE("Mute", Boolean.class),\r
/** Solo */\r
- SOLO("Solo", 40, Boolean.class),\r
+ SOLO("Solo", Boolean.class),\r
/** 録音するMIDIチャンネル */\r
- RECORD_CHANNEL("RecCh", 60, String.class),\r
+ RECORD_CHANNEL("RecCh", String.class),\r
/** MIDIチャンネル */\r
- CHANNEL("Ch", 60, String.class),\r
+ CHANNEL("Ch", String.class),\r
/** トラック名 */\r
- TRACK_NAME("Track name", 200, String.class);\r
- private String title;\r
- private int widthRatio;\r
- private Class<?> columnClass;\r
+ TRACK_NAME("Track name", String.class);\r
+ String title;\r
+ Class<?> columnClass;\r
/**\r
* 列の識別子を構築します。\r
* @param title 列のタイトル\r
* @param widthRatio 幅の割合\r
* @param columnClass 列のクラス\r
*/\r
- private Column(String title, int widthRatio, Class<?> columnClass) {\r
+ private Column(String title, Class<?> columnClass) {\r
this.title = title;\r
- this.widthRatio = widthRatio;\r
this.columnClass = columnClass;\r
}\r
- /**\r
- * 幅の割合の合計を返します。\r
- * @return 幅の割合の合計\r
- */\r
- public static int totalWidthRatio() {\r
- int total = 0;\r
- for( Column c : values() ) total += c.widthRatio;\r
- return total;\r
- }\r
}\r
/**\r
+ * 親のプレイリスト\r
+ */\r
+ SequenceListTableModel sequenceListTableModel;\r
+ /**\r
* ラップされたMIDIシーケンス\r
*/\r
private Sequence sequence;\r
+ /**\r
+ * ラップされたMIDIシーケンスのtickインデックス\r
+ */\r
private SequenceTickIndex sequenceTickIndex;\r
/**\r
* MIDIファイル名\r
*/\r
private List<TrackEventListTableModel> trackModelList = new ArrayList<>();\r
/**\r
- * 親のプレイリスト\r
- */\r
- SequenceListTableModel sequenceListTableModel;\r
- /**\r
* 空の {@link SequenceTrackListTableModel} を構築します。\r
* @param sequenceListTableModel 親のプレイリスト\r
*/\r
return sequence == null ? 0 : sequence.getTracks().length;\r
}\r
@Override\r
- public int getColumnCount() { return Column.values().length; }\r
+ public int getColumnCount() {\r
+ return Column.values().length;\r
+ }\r
/**\r
* 列名を返します。\r
* @return 列名\r
if( ch == trackTableModel.getChannel() ) break;\r
trackTableModel.setChannel(ch);\r
setModified(true);\r
- fireTableCellUpdated(row,Column.EVENTS.ordinal());\r
+ fireTableCellUpdated(row, Column.EVENTS.ordinal());\r
break;\r
}\r
case TRACK_NAME:\r
fireTableCellUpdated(row,column);\r
}\r
/**\r
- * 列に合わせて幅を調整します。\r
- * @param columnModel テーブル列モデル\r
- */\r
- public void sizeColumnWidthToFit(TableColumnModel columnModel) {\r
- int totalWidth = columnModel.getTotalColumnWidth();\r
- int totalWidthRatio = Column.totalWidthRatio();\r
- for( Column c : Column.values() ) {\r
- int w = totalWidth * c.widthRatio / totalWidthRatio;\r
- columnModel.getColumn(c.ordinal()).setPreferredWidth(w);\r
- }\r
- }\r
- /**\r
* MIDIシーケンスを返します。\r
* @return MIDIシーケンス\r
*/\r
\r
import javax.sound.midi.MidiChannel;\r
import javax.sound.midi.Sequence;\r
+import javax.swing.AbstractAction;\r
+import javax.swing.Action;\r
import javax.swing.BoxLayout;\r
import javax.swing.JButton;\r
import javax.swing.JCheckBox;\r
};\r
private static final String INITIAL_CHORD_STRING =\r
"Key: C\nC G/B | Am Em/G | F C/E | Dm7 G7 C % | F G7 | Csus4 C\n";\r
- private JTextArea chordText;\r
- private JTextField seqNameText;\r
- private JComboBox<Integer> ppqComboBox;\r
- private TimeSignatureSelecter timesigSelecter;\r
- private TempoSelecter tempoSelecter;\r
- private MeasureSelecter measureSelecter;\r
- private TrackSpecPanel trackSpecPanel;\r
+ private JTextArea chordText = new JTextArea(INITIAL_CHORD_STRING, 18, 30);\r
+ private JTextField seqNameText = new JTextField();\r
+ private JComboBox<Integer> ppqComboBox = new JComboBox<Integer>(PPQList);\r
+ private TimeSignatureSelecter timesigSelecter = new TimeSignatureSelecter();\r
+ private TempoSelecter tempoSelecter = new TempoSelecter();\r
+ private MeasureSelecter measureSelecter = new MeasureSelecter();\r
+ private TrackSpecPanel trackSpecPanel = new TrackSpecPanel() {{\r
+ Music.DrumTrackSpec dts = new Music.DrumTrackSpec(9, "Percussion track");\r
+ dts.velocity = 127;\r
+ addTrackSpec(dts);\r
+ //\r
+ Music.MelodyTrackSpec mts;\r
+ mts = new Music.MelodyTrackSpec(0, "Bass track", new Music.Range(36,48));\r
+ mts.is_bass = true;\r
+ mts.velocity = 96;\r
+ addTrackSpec(mts);\r
+ mts = new Music.MelodyTrackSpec(1, "Chord track", new Music.Range(60,72));\r
+ addTrackSpec(mts);\r
+ mts = new Music.MelodyTrackSpec(2, "Melody track", new Music.Range(60,84));\r
+ mts.random_melody = true;\r
+ mts.beat_pattern = 0xFFFF;\r
+ mts.continuous_beat_pattern = 0x820A;\r
+ addTrackSpec(mts);\r
+ }};\r
+ /**\r
+ * ダイアログを開くアクション\r
+ */\r
+ public Action openAction = new AbstractAction("New") {\r
+ {\r
+ String tooltip = "Generate new song - 新しい曲を生成";\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ }\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) { setVisible(true); }\r
+ };\r
private MidiEditor midiEditor;\r
+ /**\r
+ * MIDIシーケンス生成アクション\r
+ */\r
+ public Action generateAction = new AbstractAction(\r
+ "Generate & Add to PlayList",\r
+ new ButtonIcon(ButtonIcon.EJECT_ICON)\r
+ ) {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ midiEditor.addSequenceAndPlay(getMidiSequence());\r
+ NewSequenceDialog.this.setVisible(false);\r
+ }\r
+ };\r
+ /**\r
+ * 新しいMIDIシーケンスを生成するダイアログを構築します。\r
+ * @param midiEditor シーケンス追加先エディタ\r
+ */\r
public NewSequenceDialog(MidiEditor midiEditor) {\r
this.midiEditor = midiEditor;\r
+ trackSpecPanel.setChannels(midiEditor.virtualMidiDevice.getChannels());\r
setTitle("Generate new sequence - " + ChordHelperApplet.VersionInfo.NAME);\r
add(new JTabbedPane() {{\r
add("Sequence", new JPanel() {{\r
add(new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
add(new JLabel("Sequence name:"));\r
- add(seqNameText = new JTextField());\r
+ add(seqNameText);\r
}});\r
add(new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
add(new JLabel("Resolution in PPQ ="));\r
- add(ppqComboBox = new JComboBox<Integer>(PPQList));\r
- add(measureSelecter = new MeasureSelecter());\r
+ add(ppqComboBox);\r
+ add(measureSelecter);\r
}});\r
add(new JButton("Randomize (Tempo, Time signature, Chord progression)") {{\r
setMargin(ZERO_INSETS);\r
}});\r
add(new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
- add(tempoSelecter = new TempoSelecter());\r
+ add(tempoSelecter);\r
add(new JPanel() {{\r
add(new JLabel("Time signature ="));\r
- add(timesigSelecter = new TimeSignatureSelecter());\r
+ add(timesigSelecter);\r
}});\r
}});\r
add(new JPanel() {{\r
});\r
}});\r
}});\r
- add(new JScrollPane(\r
- chordText = new JTextArea(INITIAL_CHORD_STRING, 18, 30)\r
- ));\r
+ add(new JScrollPane(chordText));\r
add(new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
- add(new JButton(\r
- "Generate & Add to PlayList",\r
- new ButtonIcon(ButtonIcon.EJECT_ICON)\r
- ) {{\r
- setMargin(ZERO_INSETS);\r
- addActionListener(new ActionListener() {\r
- @Override\r
- public void actionPerformed(ActionEvent e) {\r
- NewSequenceDialog.this.midiEditor.addSequenceAndPlay(getMidiSequence());\r
- NewSequenceDialog.this.setVisible(false);\r
- }\r
- });\r
- }});\r
+ add(new JButton(generateAction){{setMargin(ZERO_INSETS);}});\r
}});\r
}});\r
- add("Track", trackSpecPanel = new TrackSpecPanel());\r
+ add("Track", trackSpecPanel);\r
}});\r
setBounds( 250, 200, 600, 540 );\r
- //\r
- // Create track specs\r
- //\r
- Music.MelodyTrackSpec mts;\r
- Music.DrumTrackSpec dts;\r
- //\r
- dts = new Music.DrumTrackSpec( 9, "Percussion track" );\r
- dts.velocity = 127;\r
- trackSpecPanel.addTrackSpec(dts);\r
- //\r
- mts = new Music.MelodyTrackSpec(0, "Bass track", new Music.Range(36,48));\r
- mts.is_bass = true;\r
- mts.velocity = 96;\r
- trackSpecPanel.addTrackSpec(mts);\r
- //\r
- mts = new Music.MelodyTrackSpec(1, "Chord track", new Music.Range(60,72));\r
- trackSpecPanel.addTrackSpec(mts);\r
- //\r
- mts = new Music.MelodyTrackSpec(2, "Melody track", new Music.Range(60,84));\r
- mts.random_melody = true;\r
- mts.beat_pattern = 0xFFFF;\r
- mts.continuous_beat_pattern = 0x820A;\r
- trackSpecPanel.addTrackSpec(mts);\r
- }\r
- private Music.ChordProgression getChordProgression() {\r
- return new Music.ChordProgression(chordText.getText());\r
}\r
/**\r
- * 送信用のMIDIチャンネルを設定します。\r
- * @param midiChannels MIDIチャンネル\r
+ * 新しいコード進行を生成して返します。\r
+ * @return 新しいコード進行\r
*/\r
- public void setChannels(MidiChannel[] midiChannels) {\r
- trackSpecPanel.setChannels(midiChannels);\r
+ private Music.ChordProgression getChordProgression() {\r
+ return new Music.ChordProgression(chordText.getText());\r
}\r
/**\r
* MIDIシーケンスを生成して返します。\r