*/\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
+ static final Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);\r
/**\r
* このMIDIエディタの仮想MIDIデバイス\r
*/\r
{ info = new MyInfo(); setMaxReceivers(0); }\r
};\r
/**\r
- * このダイアログを開きます。すでに開かれていた場合は前面に移動します。\r
- */\r
- public void open() {\r
- if( isVisible() ) toFront(); else setVisible(true);\r
- }\r
- /**\r
* このダイアログを表示するアクション\r
*/\r
public Action openAction = new AbstractAction(\r
public void actionPerformed(ActionEvent e) { open(); }\r
};\r
/**\r
- * ã\83\97ã\83¬ã\82¤ã\83ªã\82¹ã\83\88ã\81®ã\83\87ã\83¼ã\82¿ã\83¢ã\83\87ã\83«\r
+ * ã\81\93ã\81®ã\83\80ã\82¤ã\82¢ã\83ã\82°ã\82\92é\96\8bã\81\8dã\81¾ã\81\99ã\80\82ã\81\99ã\81§ã\81«é\96\8bã\81\8bã\82\8cã\81¦ã\81\84ã\81\9få ´å\90\88ã\81¯å\89\8dé\9d¢ã\81«ç§»å\8b\95ã\81\97ã\81¾ã\81\99ã\80\82\r
*/\r
- SequenceListTableModel sequenceListTableModel;\r
+ public void open() {\r
+ if( isVisible() ) toFront(); else setVisible(true);\r
+ }\r
/**\r
- * 新しいMIDIシーケンスを生成するダイアログ\r
+ * エラーメッセージダイアログを表示します。\r
+ * @param message エラーメッセージ\r
*/\r
- NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this);\r
+ private void showError(String message) {\r
+ JOptionPane.showMessageDialog(\r
+ this, message,\r
+ ChordHelperApplet.VersionInfo.NAME,\r
+ JOptionPane.ERROR_MESSAGE\r
+ );\r
+ }\r
/**\r
- * 選択されたシーケンスへジャンプするアクション\r
+ * 警告メッセージダイアログを表示します。\r
+ * @param message 警告メッセージ\r
*/\r
- public Action loadToSequencerAction = new AbstractAction("Load to sequencer") {\r
- {\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
- sequenceListTableModel.loadToSequencer(sequenceListSelectionModel.getMinSelectionIndex());\r
- }\r
- };\r
+ private void showWarning(String message) {\r
+ JOptionPane.showMessageDialog(\r
+ this, message,\r
+ ChordHelperApplet.VersionInfo.NAME,\r
+ JOptionPane.WARNING_MESSAGE\r
+ );\r
+ }\r
/**\r
- * シーケンスを削除するアクション\r
+ * 確認ダイアログを表示します。\r
+ * @param message 確認メッセージ\r
+ * @return 確認OKのときtrue\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
- //\r
- SequenceTrackListTableModel seqModel =\r
- sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\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
- sequenceListTableModel.removeSequence(sequenceListSelectionModel);\r
- }\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
+\r
+ /**\r
+ * プレイリストのデータモデル\r
+ */\r
+ SequenceListTableModel sequenceListTableModel;\r
+ /**\r
+ * 新しいMIDIシーケンスを生成するダイアログ\r
+ */\r
+ NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this);\r
/**\r
* BASE64テキスト入力ダイアログ\r
*/\r
*/\r
private Action base64EncodeAction;\r
/**\r
- * プレイリストのMIDIシーケンス選択状態\r
- */\r
- ListSelectionModel sequenceListSelectionModel = new DefaultListSelectionModel() {\r
- {\r
- setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
- addListSelectionListener(new ListSelectionListener() {\r
- @Override\r
- public void valueChanged(ListSelectionEvent e) {\r
- if( e.getValueIsAdjusting() )\r
- return;\r
- updateButtonStatus();\r
- updateEnabled();\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
- base64Dialog.setMIDIData(mstm.getMIDIdata(), mstm.getFilename());\r
- base64Dialog.setVisible(true);\r
- }\r
- };\r
- }\r
- updateEnabled();\r
- }\r
- private void updateEnabled() {\r
- int selIndex = getMinSelectionIndex();\r
- boolean isSelected = (selIndex >= 0);\r
- if(base64EncodeAction != null)\r
- base64EncodeAction.setEnabled(isSelected);\r
- deleteSequenceAction.setEnabled(isSelected);\r
- loadToSequencerAction.setEnabled(isSelected);\r
- }\r
- };\r
- /**\r
* プレイリストビュー\r
*/\r
JTable sequenceListTableView;\r
/**\r
+ * シーケンスを削除するアクション\r
+ */\r
+ public Action deleteSequenceAction;\r
+ /**\r
* ファイル選択ダイアログ(アプレットでは使用不可)\r
*/\r
private MidiFileChooser midiFileChooser;\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
SequenceTrackListTableModel sequenceTableModel =\r
- sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
String filename = sequenceTableModel.getFilename();\r
File midiFile;\r
if( filename != null && ! filename.isEmpty() ) {\r
* シーケンスの選択有無に応じて、保存ボタンのイネーブル状態を更新します。\r
*/\r
private void updateEnabled() {\r
- boolean en = (sequenceListSelectionModel.getMinSelectionIndex() >= 0);\r
+ boolean en = (sequenceListTableModel.selectionModel.getMinSelectionIndex() >= 0);\r
saveMidiFileAction.setEnabled(en);\r
}\r
{\r
setFileFilter(new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid"));\r
//\r
// 選択状態のリスニングを開始\r
- sequenceListSelectionModel.addListSelectionListener(this);\r
+ sequenceListTableModel.selectionModel.addListSelectionListener(this);\r
updateEnabled();\r
}\r
@Override\r
putValue(Action.SHORT_DESCRIPTION, tooltip);\r
}\r
@Override\r
- public void actionPerformed(ActionEvent e) {\r
- if(showOpenDialog(MidiEditor.this) == JFileChooser.APPROVE_OPTION)\r
- addSequence(getSelectedFile());\r
+ public void actionPerformed(ActionEvent event) {\r
+ if(showOpenDialog(MidiEditor.this) == JFileChooser.APPROVE_OPTION) {\r
+ try {\r
+ sequenceListTableModel.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
+ // Drag & drop\r
+ public void dragEnter(DropTargetDragEvent event) {\r
+ if( event.isDataFlavorSupported(DataFlavor.javaFileListFlavor) ) {\r
+ event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);\r
+ }\r
+ }\r
+ public void dragExit(DropTargetEvent event) {}\r
+ public void dragOver(DropTargetDragEvent event) {}\r
+ public void dropActionChanged(DropTargetDragEvent event) {}\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
/**\r
* MIDIトラック選択状態\r
public void valueChanged(ListSelectionEvent e) {\r
if( e.getValueIsAdjusting() )\r
return;\r
- SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ SequenceTrackListTableModel sequenceModel =\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
if( sequenceModel == null || isSelectionEmpty() ) {\r
trackEventListTableView.setModel(new TrackEventListTableModel());\r
}\r
}\r
@Override\r
public void actionPerformed(ActionEvent e) {\r
- int index = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).createTrack();\r
+ int index = sequenceListTableModel.getSelectedSequenceModel().createTrack();\r
trackSelectionModel.setSelectionInterval(index, index);\r
- sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
}\r
};\r
/**\r
public void actionPerformed(ActionEvent e) {\r
if( ! confirm("Do you want to delete selected track ?\n選択したトラックを削除しますか?"))\r
return;\r
- sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).deleteTracks(trackSelectionModel);\r
- sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
+ sequenceListTableModel.getSelectedSequenceModel().deleteTracks(trackSelectionModel);\r
}\r
};\r
/**\r
* MIDIトラックリストテーブルビュー(選択中のシーケンスの中身)\r
*/\r
- private JTable trackListTableView = new JTable(\r
- new SequenceTrackListTableModel(sequenceListTableModel),\r
- null, trackSelectionModel\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
- {\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
+ private JTable trackListTableView;\r
\r
/**\r
* MIDIイベント選択状態\r
}};\r
\r
private void setSelectedEvent() {\r
- sequenceTableModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ sequenceTableModel = sequenceListTableModel.getSelectedSequenceModel();\r
eventDialog.midiMessageForm.durationForm.setPPQ(sequenceTableModel.getSequence().getResolution());\r
tickPositionModel.setSequenceIndex(sequenceTableModel.getSequenceTickIndex());\r
selectedIndex = -1;\r
copiedEventsToPaste, tick, copiedEventsPPQ\r
);\r
scrollToEventAt(tick);\r
- sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
+ // プレイリストの曲の長さ表示を更新\r
+ sequenceListTableModel.fireSelectedSequenceChanged();\r
eventDialog.setVisible(false);\r
}\r
};\r
scrollToEventAt( partnerTick > tick ? partnerTick : tick );\r
}\r
}\r
- sequenceListTableModel.fireSequenceChanged(sequenceTableModel);\r
+ sequenceListTableModel.fireSequenceModified(sequenceTableModel);\r
eventDialog.setVisible(false);\r
fireEditingStopped();\r
}\r
return;\r
TrackEventListTableModel trackTableModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
copiedEventsToPaste = trackTableModel.getMidiEvents(eventSelectionModel);\r
- copiedEventsPPQ = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).getSequence().getResolution();\r
+ copiedEventsPPQ = sequenceListTableModel.getSelectedSequenceModel().getSequence().getResolution();\r
trackTableModel.removeMidiEvents(copiedEventsToPaste);\r
- sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
+ // プレイリストの曲の長さ表示を更新\r
+ sequenceListTableModel.fireSelectedSequenceChanged();\r
}\r
};\r
/**\r
public void actionPerformed(ActionEvent e) {\r
TrackEventListTableModel trackTableModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
copiedEventsToPaste = trackTableModel.getMidiEvents(eventSelectionModel);\r
- copiedEventsPPQ = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).getSequence().getResolution();\r
+ copiedEventsPPQ = sequenceListTableModel.getSelectedSequenceModel().getSequence().getResolution();\r
updateButtonStatus();\r
}\r
};\r
if( ! confirm("Do you want to delete selected event ?\n選択したMIDIイベントを削除しますか?"))\r
return;\r
((TrackEventListTableModel)trackEventListTableView.getModel()).removeMidiEvents(eventSelectionModel);\r
- sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
+ // プレイリストの曲の長さ表示を更新\r
+ sequenceListTableModel.fireSelectedSequenceChanged();\r
}\r
};\r
+\r
/**\r
* 新しい {@link MidiEditor} を構築します。\r
* @param deviceModelList MIDIデバイスモデルリスト\r
// アクセスできないので、ファイル選択ダイアログは使用不可。\r
midiFileChooser = null;\r
}\r
+ if( base64Dialog.isBase64Available() ) {\r
+ base64EncodeAction = sequenceListTableModel.new SelectedSequenceAction(\r
+ "Base64 Encode",\r
+ "Encode selected sequence to Base64 textdata - 選択した曲をBase64テキストにエンコード"\r
+ ) {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ SequenceTrackListTableModel mstm =\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
+ base64Dialog.setMIDIData(mstm.getMIDIdata(), mstm.getFilename());\r
+ base64Dialog.setVisible(true);\r
+ }\r
+ };\r
+ }\r
+ deleteSequenceAction = sequenceListTableModel.new SelectedSequenceAction(\r
+ "Delete", MidiEditor.deleteIcon,\r
+ "Delete selected MIDI sequence - 選択した曲をプレイリストから削除"\r
+ ) {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ if( midiFileChooser != null ) {\r
+ // ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
+ //\r
+ SequenceTrackListTableModel seqModel =\r
+ sequenceListTableModel.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
+ sequenceListTableModel.removeSelectedSequence();\r
+ }\r
+ };\r
sequenceListTableView = new JTable(\r
- sequenceListTableModel, null, sequenceListSelectionModel\r
+ sequenceListTableModel, null, sequenceListTableModel.selectionModel\r
) {\r
private JToggleButton playButton = new JToggleButton(\r
sequenceListTableModel.sequencerModel.startStopAction\r
setMargin(ZERO_INSETS);\r
}};\r
{\r
+ sequenceListTableModel.selectionModel.addListSelectionListener(\r
+ new ListSelectionListener() {\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if( e.getValueIsAdjusting() )\r
+ return;\r
+ updateButtonStatus();\r
+ }\r
+ }\r
+ );\r
sequenceListTableModel.addTableModelListener(new TableModelListener() {\r
/**\r
* 全シーケンスの合計時間長をヘッダータイトルに反映します。\r
setMargin(ZERO_INSETS);\r
}});\r
add(Box.createRigidArea(new Dimension(5, 0)));\r
- add(new JButton(loadToSequencerAction){{ setMargin(ZERO_INSETS); }});\r
+ add(new JButton(sequenceListTableModel.loadToSequencerAction){{\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
JPanel trackListPanel = new JPanel() {{\r
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\r
- add(trackListTitleLabel);\r
+ add(new JLabel() {\r
+ private static final String TITLE = "Tracks";\r
+ {\r
+ sequenceListTableModel.selectionModel.addListSelectionListener(\r
+ new ListSelectionListener() {\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if( e.getValueIsAdjusting() )\r
+ return;\r
+ int index = sequenceListTableModel.selectionModel.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
add(Box.createRigidArea(new Dimension(0, 5)));\r
- add(new JScrollPane(trackListTableView));\r
+ add(new JScrollPane(trackListTableView = new JTable(\r
+ new SequenceTrackListTableModel(sequenceListTableModel),\r
+ null, trackSelectionModel\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
add(Box.createRigidArea(new Dimension(0, 5)));\r
add(new JPanel() {{\r
add(new JButton(addTrackAction) {{ setMargin(ZERO_INSETS); }});\r
updateButtonStatus();\r
}\r
\r
- // Drag & drop\r
- public void dragEnter(DropTargetDragEvent event) {\r
- if( event.isDataFlavorSupported(DataFlavor.javaFileListFlavor) )\r
- event.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);\r
- }\r
- public void dragExit(DropTargetEvent event) {}\r
- public void dragOver(DropTargetDragEvent event) {}\r
- public void dropActionChanged(DropTargetDragEvent event) {}\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
- // Short message dialogs\r
- private void showError(String message) {\r
- JOptionPane.showMessageDialog(\r
- this, message,\r
- ChordHelperApplet.VersionInfo.NAME,\r
- JOptionPane.ERROR_MESSAGE\r
- );\r
- }\r
- private void showWarning(String message) {\r
- JOptionPane.showMessageDialog(\r
- this, message,\r
- ChordHelperApplet.VersionInfo.NAME,\r
- JOptionPane.WARNING_MESSAGE\r
- );\r
- }\r
- private 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
-\r
/**\r
* ボタン状態の更新\r
*/\r
public void updateButtonStatus() {\r
SequenceTrackListTableModel sequenceModel =\r
- sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
boolean isSequenceSelected = (sequenceModel != null);\r
if(isSequenceSelected) {\r
trackListTableView.setModel(sequenceModel);\r
int lastIndex;\r
try (InputStream in = new ByteArrayInputStream(data)) {\r
Sequence seq = MidiSystem.getSequence(in);\r
- lastIndex =sequenceListTableModel.addSequence(seq, filename);\r
- } catch( IOException|InvalidMidiDataException e ) {\r
- showWarning(e.getMessage());\r
- return -1;\r
- }\r
- return lastIndex;\r
- }\r
- /**\r
- * MIDIファイルから読み込んだシーケンスを追加します。\r
- * ファイルが null の場合、空のMIDIシーケンスを追加します。\r
- * @param midiFile MIDIファイル\r
- * @return 追加先インデックス(先頭が 0、失敗した場合は -1)\r
- */\r
- public int addSequence(File midiFile) {\r
- if( midiFile == null ) {\r
- return sequenceListTableModel.addDefaultSequence();\r
- }\r
- int lastIndex;\r
- try (FileInputStream in = new FileInputStream(midiFile)) {\r
- Sequence seq = MidiSystem.getSequence(in);\r
- String filename = midiFile.getName();\r
lastIndex = sequenceListTableModel.addSequence(seq, filename);\r
} catch( IOException|InvalidMidiDataException e ) {\r
showWarning(e.getMessage());\r
return -1;\r
- } catch( AccessControlException e ) {\r
- showError(e.getMessage());\r
- e.printStackTrace();\r
- return -1;\r
}\r
return lastIndex;\r
}\r
*/\r
public void loadAndPlay(List<File> fileList) {\r
int firstIndex = -1;\r
- for( File file : fileList ) {\r
- int lastIndex = addSequence(file);\r
- if( firstIndex == -1 )\r
- firstIndex = lastIndex;\r
+ try {\r
+ firstIndex = sequenceListTableModel.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(sequenceListTableModel.sequencerModel.getSequencer().isRunning()) {\r
open();\r
*/\r
public boolean isRecordable() {\r
SequenceTrackListTableModel sequenceTableModel =\r
- sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
return sequenceTableModel == null ? false : sequenceTableModel.isRecordable();\r
}\r
}\r
/**\r
* プレイリスト(MIDIシーケンスリスト)のテーブルデータモデル\r
*/\r
-class SequenceListTableModel extends AbstractTableModel implements ChangeListener {\r
+class SequenceListTableModel extends AbstractTableModel {\r
+ /**\r
+ * 行の選択状態\r
+ */\r
+ ListSelectionModel selectionModel = new DefaultListSelectionModel() {{\r
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+ }};\r
+ /**\r
+ * 行が選択されているときだけイネーブルになるアクション\r
+ */\r
+ public abstract class SelectedSequenceAction extends AbstractAction\r
+ implements ListSelectionListener\r
+ {\r
+ public SelectedSequenceAction(String name, Icon icon, String tooltip) {\r
+ super(name,icon); init(tooltip);\r
+ }\r
+ public SelectedSequenceAction(String name, String tooltip) {\r
+ super(name); init(tooltip);\r
+ }\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if( e.getValueIsAdjusting() ) return;\r
+ setEnebledBySelection();\r
+ }\r
+ protected void setEnebledBySelection() {\r
+ int index = selectionModel.getMinSelectionIndex();\r
+ setEnabled(index >= 0);\r
+ }\r
+ private void init(String tooltip) {\r
+ putValue(Action.SHORT_DESCRIPTION, tooltip);\r
+ selectionModel.addListSelectionListener(this);\r
+ setEnebledBySelection();\r
+ }\r
+ }\r
+ /**\r
+ * 選択されたシーケンスへジャンプするアクション\r
+ */\r
+ public Action loadToSequencerAction = new SelectedSequenceAction(\r
+ "Load to sequencer",\r
+ "Load selected MIDI sequence to sequencer - 選択した曲をシーケンサへロード"\r
+ ) {\r
+ @Override\r
+ public void actionPerformed(ActionEvent e) {\r
+ loadToSequencer(selectionModel.getMinSelectionIndex());\r
+ }\r
+ };\r
+\r
+ private class SecondPosition implements ChangeListener {\r
+ private int value = 0;\r
+ /**\r
+ * 再生中のシーケンサーの秒位置が変わったときに表示を更新します。\r
+ */\r
+ @Override\r
+ public void stateChanged(ChangeEvent e) {\r
+ if( e.getSource() instanceof MidiSequencerModel ) {\r
+ MidiSequencerModel m = (MidiSequencerModel)e.getSource();\r
+ int newValue = m.getValue() / 1000;\r
+ if(value == newValue)\r
+ return;\r
+ // 秒が変わったときだけ更新(小数点以下は無視)\r
+ value = newValue;\r
+ fireTableCellUpdated(\r
+ indexOfSequenceOnSequencer(), Column.SEQ_POSITION.ordinal()\r
+ );\r
+ }\r
+ }\r
+ @Override\r
+ public String toString() {\r
+ return String.format("%02d:%02d", value/60, value%60);\r
+ }\r
+ }\r
+ private SecondPosition secondPosition = new SecondPosition();\r
+ /**\r
+ * 新しいプレイリストのテーブルモデルを構築します。\r
+ * @param sequencerModel MIDIシーケンサーモデル\r
+ */\r
+ public SequenceListTableModel(MidiSequencerModel sequencerModel) {\r
+ this.sequencerModel = sequencerModel;\r
+ sequencerModel.addChangeListener(secondPosition);\r
+ }\r
+ /**\r
+ * MIDIシーケンサモデル\r
+ */\r
+ MidiSequencerModel sequencerModel;\r
+ /**\r
+ * 曲の先頭または前の曲へ戻るアクション\r
+ */\r
+ public Action moveToTopAction = new AbstractAction() {\r
+ {\r
+ putValue(SHORT_DESCRIPTION,\r
+ "Move to top or previous song - 曲の先頭または前の曲へ戻る"\r
+ );\r
+ putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.TOP_ICON));\r
+ }\r
+ public void actionPerformed(ActionEvent event) {\r
+ if( sequencerModel.getSequencer().getTickPosition() <= 40 )\r
+ loadNext(-1);\r
+ sequencerModel.setValue(0);\r
+ }\r
+ };\r
+ /**\r
+ * 次の曲へ進むアクション\r
+ */\r
+ public Action moveToBottomAction = new AbstractAction() {\r
+ {\r
+ putValue(SHORT_DESCRIPTION, "Move to next song - 次の曲へ進む");\r
+ putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.BOTTOM_ICON));\r
+ }\r
+ public void actionPerformed(ActionEvent event) {\r
+ if(loadNext(1)) sequencerModel.setValue(0);\r
+ }\r
+ };\r
+\r
/**\r
* 列の列挙型\r
*/\r
}\r
public boolean isCellEditable() { return false; }\r
}\r
- /**\r
- * MIDIシーケンサモデル\r
- */\r
- MidiSequencerModel sequencerModel;\r
- /**\r
- * 曲の先頭または前の曲へ戻るアクション\r
- */\r
- public Action moveToTopAction = new AbstractAction() {\r
- {\r
- putValue(SHORT_DESCRIPTION,\r
- "Move to top or previous song - 曲の先頭または前の曲へ戻る"\r
- );\r
- putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.TOP_ICON));\r
- }\r
- public void actionPerformed(ActionEvent event) {\r
- if( sequencerModel.getSequencer().getTickPosition() <= 40 )\r
- loadNext(-1);\r
- sequencerModel.setValue(0);\r
- }\r
- };\r
- /**\r
- * 次の曲へ進むアクション\r
- */\r
- public Action moveToBottomAction = new AbstractAction() {\r
- {\r
- putValue(SHORT_DESCRIPTION, "Move to next song - 次の曲へ進む");\r
- putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.BOTTOM_ICON));\r
- }\r
- public void actionPerformed(ActionEvent event) {\r
- if(loadNext(1)) sequencerModel.setValue(0);\r
- }\r
- };\r
- /**\r
- * 新しいプレイリストのテーブルモデルを構築します。\r
- * @param sequencerModel MIDIシーケンサーモデル\r
- */\r
- public SequenceListTableModel(MidiSequencerModel sequencerModel) {\r
- (this.sequencerModel = sequencerModel).addChangeListener(this);\r
- }\r
- /**\r
- * シーケンサーの秒位置\r
- */\r
- private int secondPosition = 0;\r
- /**\r
- * 再生中のシーケンサーの秒位置が変わったときに表示を更新します。\r
- */\r
- @Override\r
- public void stateChanged(ChangeEvent e) {\r
- int sec = sequencerModel.getValue() / 1000;\r
- if(secondPosition == sec)\r
- return;\r
- // 秒が変わったときだけ更新(小数点以下は無視)\r
- secondPosition = sec;\r
- fireTableCellUpdated(getLoadedIndex(), Column.SEQ_POSITION.ordinal());\r
- }\r
List<SequenceTrackListTableModel> sequenceList = new Vector<>();\r
@Override\r
public int getRowCount() { return sequenceList.size(); }\r
@Override\r
- public int getColumnCount() {\r
- return Column.values().length;\r
- }\r
+ public int getColumnCount() { return Column.values().length; }\r
@Override\r
public String getColumnName(int column) {\r
return Column.values()[column].title;\r
}\r
@Override\r
public Object getValueAt(int row, int column) {\r
- switch(Column.values()[column]) {\r
- case SEQ_NUMBER: return row;\r
- case MODIFIED:\r
- return sequenceList.get(row).isModified();\r
+ Column c = Column.values()[column];\r
+ if( c == Column.SEQ_NUMBER ) return row;\r
+ SequenceTrackListTableModel sequenceModel = sequenceList.get(row);\r
+ switch(c) {\r
+ case MODIFIED: return sequenceModel.isModified();\r
case DIVISION_TYPE: {\r
- float divType = sequenceList.get(row).getSequence().getDivisionType();\r
+ float divType = sequenceModel.getSequence().getDivisionType();\r
if( divType == Sequence.PPQ ) return "PPQ";\r
else if( divType == Sequence.SMPTE_24 ) return "SMPTE_24";\r
else if( divType == Sequence.SMPTE_25 ) return "SMPTE_25";\r
else if( divType == Sequence.SMPTE_30DROP ) return "SMPTE_30DROP";\r
else return "[Unknown]";\r
}\r
- case RESOLUTION:\r
- return sequenceList.get(row).getSequence().getResolution();\r
- case TRACKS:\r
- return sequenceList.get(row).getSequence().getTracks().length;\r
- case SEQ_POSITION: {\r
- if( getLoadedIndex() == row )\r
- return String.format("%02d:%02d", secondPosition/60, secondPosition%60);\r
- else\r
- return "";\r
- }\r
+ case RESOLUTION: return sequenceModel.getSequence().getResolution();\r
+ case TRACKS: return sequenceModel.getSequence().getTracks().length;\r
+ case SEQ_POSITION: return sequenceModel.isOnSequencer() ? secondPosition : "";\r
case SEQ_LENGTH: {\r
- long usec = sequenceList.get(row).getSequence().getMicrosecondLength();\r
+ long usec = sequenceModel.getSequence().getMicrosecondLength();\r
int sec = (int)( (usec < 0 ? usec += 0x100000000L : usec) / 1000L / 1000L );\r
return String.format( "%02d:%02d", sec/60, sec%60 );\r
}\r
case FILENAME: {\r
- String filename = sequenceList.get(row).getFilename();\r
+ String filename = sequenceModel.getFilename();\r
return filename == null ? "" : filename;\r
}\r
case SEQ_NAME: {\r
- String name = sequenceList.get(row).toString();\r
+ String name = sequenceModel.toString();\r
return name == null ? "" : name;\r
}\r
default: return "";\r
// シーケンス名の設定または変更\r
if( sequenceList.get(row).setName((String)val) )\r
fireTableCellUpdated(row, Column.MODIFIED.ordinal());\r
+ fireTableCellUpdated(row, column);\r
break;\r
default:\r
break;\r
* @param selModel 選択状態\r
* @param isModified 未保存の修正内容があるときtrue\r
*/\r
- public void setModified(ListSelectionModel selModel, boolean isModified) {\r
- int minIndex = selModel.getMinSelectionIndex();\r
- int maxIndex = selModel.getMaxSelectionIndex();\r
+ public void setModified(boolean isModified) {\r
+ int minIndex = selectionModel.getMinSelectionIndex();\r
+ int maxIndex = selectionModel.getMaxSelectionIndex();\r
for( int i = minIndex; i <= maxIndex; i++ ) {\r
- if( selModel.isSelectedIndex(i) ) {\r
+ if( selectionModel.isSelectedIndex(i) ) {\r
sequenceList.get(i).setModified(isModified);\r
fireTableCellUpdated(i, Column.MODIFIED.ordinal());\r
}\r
* @param selectionModel 選択状態\r
* @return 選択されたMIDIシーケンスのテーブルモデル\r
*/\r
- public SequenceTrackListTableModel getSequenceModel(ListSelectionModel selectionModel) {\r
+ public SequenceTrackListTableModel getSelectedSequenceModel() {\r
if( selectionModel.isSelectionEmpty() )\r
return null;\r
int selectedIndex = selectionModel.getMinSelectionIndex();\r
return sequenceList.get(selectedIndex);\r
}\r
/**\r
- * 指定されたシーケンスが変更されたことを通知します。\r
+ * 指定されたシーケンスが修正されたことを通知します。\r
* @param sequenceTableModel MIDIシーケンスモデル\r
*/\r
- public void fireSequenceChanged(SequenceTrackListTableModel sequenceTableModel) {\r
+ public void fireSequenceModified(SequenceTrackListTableModel sequenceTableModel) {\r
int index = sequenceList.indexOf(sequenceTableModel);\r
if( index < 0 )\r
return;\r
* 指定された選択範囲のシーケンスが変更されたことを通知します。\r
* @param selectionModel 選択状態\r
*/\r
- public void fireSequenceChanged(ListSelectionModel selectionModel) {\r
+ public void fireSelectedSequenceChanged() {\r
if( selectionModel.isSelectionEmpty() )\r
return;\r
int minIndex = selectionModel.getMinSelectionIndex();\r
fireTableRowsUpdated(minIndex, maxIndex);\r
}\r
/**\r
- * デフォルトの内容でシーケンスを作成して追加します。\r
- * @return 追加されたシーケンスのインデックス(先頭が 0)\r
- */\r
- public int addDefaultSequence() {\r
- Sequence seq = (new Music.ChordProgression()).toMidiSequence();\r
- return seq == null ? -1 : addSequence(seq,null);\r
- }\r
- /**\r
- * 指定のシーケンスを追加します。\r
+ * MIDIシーケンスを追加します。\r
* @param seq MIDIシーケンス\r
* @param filename ファイル名\r
* @return 追加されたシーケンスのインデックス(先頭が 0)\r
return lastIndex;\r
}\r
/**\r
+ * デフォルトの内容でMIDIシーケンスを作成して追加します。\r
+ * @return 追加されたMIDIシーケンスのインデックス(先頭が 0)\r
+ */\r
+ public int addDefaultSequence() {\r
+ Sequence seq = (new Music.ChordProgression()).toMidiSequence();\r
+ return seq == null ? -1 : addSequence(seq,null);\r
+ }\r
+ /**\r
+ * MIDIファイルを追加します。\r
+ * ファイルが null の場合、空のMIDIシーケンスを追加します。\r
+ * @param midiFile MIDIファイル\r
+ * @return 追加先インデックス(先頭が 0)\r
+ * @throws InvalidMidiDataException ファイル内のMIDIデータが正しくない場合\r
+ * @throws IOException ファイル入出力に失敗した場合\r
+ */\r
+ public int addSequence(File midiFile) throws InvalidMidiDataException, IOException {\r
+ if( midiFile == null ) return addDefaultSequence();\r
+ int lastIndex;\r
+ try (FileInputStream in = new FileInputStream(midiFile)) {\r
+ Sequence seq = MidiSystem.getSequence(in);\r
+ String filename = midiFile.getName();\r
+ lastIndex = addSequence(seq, filename);\r
+ } catch( InvalidMidiDataException|IOException e ) {\r
+ throw e;\r
+ }\r
+ return lastIndex;\r
+ }\r
+ /**\r
+ * 複数のMIDIファイルを追加します。\r
+ * @param fileList 追加するMIDIファイルのリスト\r
+ * @return 追加先の最初のインデックス(先頭が 0、追加されなかった場合は -1)\r
+ * @throws InvalidMidiDataException ファイル内のMIDIデータが正しくない場合\r
+ * @throws IOException ファイル入出力に失敗した場合\r
+ */\r
+ public int addSequences(List<File> fileList) throws InvalidMidiDataException, IOException {\r
+ int firstIndex = -1;\r
+ for( File file : fileList ) {\r
+ int lastIndex = addSequence(file);\r
+ if( firstIndex == -1 )\r
+ firstIndex = lastIndex;\r
+ }\r
+ return firstIndex;\r
+ }\r
+ /**\r
* 選択したシーケンスを除去します。\r
* @param listSelectionModel 選択状態\r
*/\r
- public void removeSequence(ListSelectionModel listSelectionModel) {\r
- if( listSelectionModel.isSelectionEmpty() )\r
+ public void removeSelectedSequence() {\r
+ if( selectionModel.isSelectionEmpty() )\r
return;\r
- int selectedIndex = listSelectionModel.getMinSelectionIndex();\r
+ int selectedIndex = selectionModel.getMinSelectionIndex();\r
if( sequenceList.remove(selectedIndex).isOnSequencer() ) {\r
// 削除したシーケンスが\r
// シーケンサーにロード済みだった場合、アンロードする。\r
}\r
/**\r
* 指定したインデックス位置のシーケンスをシーケンサーにロードします。\r
- * @param index シーケンスのインデックス位置\r
+ * @param index シーケンスのインデックス位置(-1 を指定するとアンロードされます)\r
*/\r
public void loadToSequencer(int index) {\r
- int oldIndex = getLoadedIndex();\r
- if(index == oldIndex)\r
+ SequenceTrackListTableModel oldSeq = sequencerModel.getSequenceTableModel();\r
+ SequenceTrackListTableModel newSeq = (index < 0 ? null : sequenceList.get(index));\r
+ if(oldSeq == newSeq)\r
return;\r
- sequencerModel.setSequenceTrackListTableModel(sequenceList.get(index));\r
+ sequencerModel.setSequenceTrackListTableModel(newSeq);\r
int columnIndices[] = {\r
Column.SEQ_PLAY.ordinal(),\r
Column.SEQ_POSITION.ordinal(),\r
};\r
- for( int columnIndex : columnIndices ) {\r
- fireTableCellUpdated(oldIndex, columnIndex);\r
- }\r
- for( int columnIndex : columnIndices ) {\r
- fireTableCellUpdated(index, columnIndex);\r
+ if( oldSeq != null ) {\r
+ int oldIndex = sequenceList.indexOf(oldSeq);\r
+ for( int columnIndex : columnIndices )\r
+ fireTableCellUpdated(oldIndex, columnIndex);\r
}\r
+ if( newSeq != null )\r
+ for( int columnIndex : columnIndices )\r
+ fireTableCellUpdated(index, columnIndex);\r
}\r
/**\r
* 現在シーケンサにロードされているシーケンスのインデックスを返します。\r
* ロードされていない場合は -1 を返します。\r
* @return 現在シーケンサにロードされているシーケンスのインデックス\r
*/\r
- public int getLoadedIndex() {\r
+ public int indexOfSequenceOnSequencer() {\r
return sequenceList.indexOf(sequencerModel.getSequenceTableModel());\r
}\r
/**\r
* @return 成功したらtrue\r
*/\r
public boolean loadNext(int offset) {\r
- int loadedIndex = getLoadedIndex();\r
+ int loadedIndex = indexOfSequenceOnSequencer();\r
int index = (loadedIndex < 0 ? 0 : loadedIndex + offset);\r
if( index < 0 || index >= sequenceList.size() )\r
return false;\r
int row = indexOf(track);\r
if( row < 0 ) return;\r
fireTableRowsUpdated(row, row);\r
- sequenceListTableModel.fireSequenceChanged(this);\r
+ sequenceListTableModel.fireSequenceModified(this);\r
}\r
/**\r
* 指定のインデックスのトラックモデルを返します。\r
trackModelList.add(new TrackEventListTableModel(sequence.createTrack(), this));\r
int lastRow = sequence.getTracks().length - 1;\r
fireTableRowsInserted(lastRow, lastRow);\r
+ // 親のプレイリストのトラック数表示を更新\r
+ sequenceListTableModel.fireSelectedSequenceChanged();\r
return lastRow;\r
}\r
/**\r
trackModelList.remove(i);\r
}\r
fireTableRowsDeleted(minIndex, maxIndex);\r
+ // 親のプレイリストのトラック数表示を更新\r
+ sequenceListTableModel.fireSelectedSequenceChanged();\r
}\r
/**\r
* MIDIシーケンサを返します。\r
fireTableDataChanged();\r
if( MIDISpec.isEOT(msg) ) {\r
// EOTの場所が変わると曲の長さが変わるので、親モデルへ通知する。\r
- parent.sequenceListTableModel.fireSequenceChanged(parent);\r
+ parent.sequenceListTableModel.fireSequenceModified(parent);\r
}\r
}\r
/**\r
if(name.equals(toString()) || ! MIDISpec.setNameOf(track, name))\r
return false;\r
parent.setModified(true);\r
- parent.sequenceListTableModel.fireSequenceChanged(parent);\r
+ parent.sequenceListTableModel.fireSequenceModified(parent);\r
fireTableDataChanged();\r
return true;\r
}\r