OSDN Git Service

・MIDI Editor 修正:再生ボタン、合計時間をプレイリストの表に埋め込み
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 23 Nov 2013 16:25:10 +0000 (16:25 +0000)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sat, 23 Nov 2013 16:25:10 +0000 (16:25 +0000)
・MIDI Editor 周辺のリファクタリング

git-svn-id: https://svn.sourceforge.jp/svnroot/midichordhelper/MIDIChordHelper@15 302f1594-2db2-43b1-aaa4-6307b5a2a2de

src/Base64Dialog.java
src/ChordHelperApplet.java
src/MIDIEditor.java
src/MIDISequencer.java
src/MidiChordHelper.java

index c43b13e..5709fcb 100644 (file)
@@ -1,9 +1,10 @@
 import java.awt.Dimension;\r
 import java.awt.Insets;\r
 import java.awt.event.ActionEvent;\r
-import java.awt.event.ActionListener;\r
 import java.util.regex.Pattern;\r
 \r
+import javax.swing.AbstractAction;\r
+import javax.swing.Action;\r
 import javax.swing.Box;\r
 import javax.swing.BoxLayout;\r
 import javax.swing.JButton;\r
@@ -19,11 +20,41 @@ import org.apache.commons.codec.binary.Base64;
  * Base64テキスト入力ダイアログ\r
  */\r
 public class Base64Dialog extends JDialog {\r
-       private static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
-       private Base64TextArea base64TextArea = null;\r
-       private JButton addBase64Button;\r
-       private JButton clearButton;\r
-       private boolean base64Available;\r
+       private Base64TextArea base64TextArea = new Base64TextArea(8,56);\r
+       private MidiEditor midiEditor;\r
+       /**\r
+        * Base64デコードアクション\r
+        */\r
+       public Action addBase64Action = new AbstractAction(\r
+               "Base64 Decode & Add to PlayList",\r
+               new ButtonIcon(ButtonIcon.EJECT_ICON)\r
+       ) {\r
+               {\r
+                       putValue(\r
+                               Action.SHORT_DESCRIPTION,\r
+                               "Base64デコードして、プレイリストへ追加"\r
+                       );\r
+               }\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int lastIndex = midiEditor.addSequence(getMIDIData(), null);\r
+                       if( lastIndex < 0 ) {\r
+                               base64TextArea.requestFocusInWindow();\r
+                               lastIndex = midiEditor.sequenceListTableModel.getRowCount() - 1;\r
+                       }\r
+                       midiEditor.sequenceListSelectionModel.setSelectionInterval(lastIndex, lastIndex);\r
+                       setVisible(false);\r
+               }\r
+       };\r
+       /**\r
+        * Base64テキストクリアアクション\r
+        */\r
+       public Action clearAction = new AbstractAction("Clear") {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       base64TextArea.setText(null);\r
+               }\r
+       };\r
        private static class Base64TextArea extends JTextArea {\r
                private static final Pattern headerLine =\r
                        Pattern.compile( "^.*:.*$", Pattern.MULTILINE );\r
@@ -46,6 +77,7 @@ public class Base64Dialog extends JDialog {
                        }\r
                }\r
        }\r
+       private static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
        /**\r
         * Base64テキスト入力ダイアログを構築します。\r
         * @param midiEditor 親画面となるMIDIエディタ\r
@@ -54,7 +86,7 @@ public class Base64Dialog extends JDialog {
                this.midiEditor = midiEditor;\r
                setTitle("Base64-encoded MIDI sequence - " + ChordHelperApplet.VersionInfo.NAME);\r
                try {\r
-                       Base64.decodeBase64( "".getBytes() );\r
+                       Base64.decodeBase64("".getBytes());\r
                        base64Available = true;\r
                } catch( NoClassDefFoundError e ) {\r
                        base64Available = false;\r
@@ -63,46 +95,18 @@ public class Base64Dialog extends JDialog {
                        add(new JPanel() {{\r
                                setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\r
                                add(new JPanel() {{\r
-                                       setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS ));\r
+                                       setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
                                        add(new JLabel("Base64-encoded MIDI sequence:"));\r
                                        add(Box.createRigidArea(new Dimension(10, 0)));\r
-                                       add(addBase64Button = new JButton(\r
-                                               "Base64 Decode & Add to PlayList",\r
-                                               new ButtonIcon(ButtonIcon.EJECT_ICON)\r
-                                       ) {{\r
-                                               setMargin(ZERO_INSETS);\r
-                                               setToolTipText("Base64デコードして、プレイリストへ追加");\r
-                                       }});\r
-                                       add(clearButton = new JButton("Clear") {{\r
-                                               setMargin(ZERO_INSETS);\r
-                                       }});\r
+                                       add(new JButton(addBase64Action){{setMargin(ZERO_INSETS);}});\r
+                                       add(new JButton(clearAction){{setMargin(ZERO_INSETS);}});\r
                                }});\r
-                               add(new JScrollPane(base64TextArea = new Base64TextArea(8,56)));\r
+                               add(new JScrollPane(base64TextArea));\r
                        }});\r
-                       addBase64Button.addActionListener(new ActionListener() {\r
-                               @Override\r
-                               public void actionPerformed(ActionEvent e) {\r
-                                       MidiEditor midiEditor = Base64Dialog.this.midiEditor;\r
-                                       int lastIndex = midiEditor.addSequence(getMIDIData(), null);\r
-                                       if( lastIndex < 0 ) {\r
-                                               base64TextArea.requestFocusInWindow();\r
-                                               lastIndex = midiEditor.sequenceListTableModel.getRowCount() - 1;\r
-                                       }\r
-                                       midiEditor.seqSelectionModel.setSelectionInterval(lastIndex, lastIndex);\r
-                                       setVisible(false);\r
-                               }\r
-                       });\r
-                       clearButton.addActionListener(new ActionListener() {\r
-                               @Override\r
-                               public void actionPerformed(ActionEvent e) {\r
-                                       base64TextArea.setText(null);\r
-                               }\r
-                       });\r
                }\r
-               // setLocationRelativeTo(applet);\r
                setBounds( 300, 250, 660, 300 );\r
        }\r
-       private MidiEditor midiEditor;\r
+       private boolean base64Available;\r
        /**\r
         * {@link Base64} が使用できるかどうかを返します。\r
         * @return Apache Commons Codec ライブラリが利用できる状態ならtrue\r
index dee1fbc..37ebea8 100644 (file)
@@ -123,7 +123,7 @@ public class ChordHelperApplet extends JApplet {
         * シーケンサへロードして再生します。\r
         */\r
        public void play() {\r
-               play(editorDialog.seqSelectionModel.getMinSelectionIndex());\r
+               play(editorDialog.sequenceListSelectionModel.getMinSelectionIndex());\r
        }\r
        /**\r
         * 指定されたインデックス値が示すプレイリスト上のMIDIシーケンスを、\r
@@ -131,7 +131,7 @@ public class ChordHelperApplet extends JApplet {
         * @param index インデックス値(0から始まる)\r
         */\r
        public void play(int index) {\r
-               editorDialog.load(index);\r
+               editorDialog.sequenceListTableModel.loadToSequencer(index);\r
                deviceModelList.sequencerModel.start();\r
        }\r
        /**\r
@@ -251,7 +251,7 @@ public class ChordHelperApplet extends JApplet {
         */\r
        public static class VersionInfo {\r
                public static final String      NAME = "MIDI Chord Helper";\r
-               public static final String      VERSION = "Ver.20131122.1";\r
+               public static final String      VERSION = "Ver.20131124.1";\r
                public static final String      COPYRIGHT = "Copyright (C) 2004-2013";\r
                public static final String      AUTHER = "@きよし - Akiyoshi Kamide";\r
                public static final String      URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";\r
@@ -442,7 +442,7 @@ public class ChordHelperApplet extends JApplet {
                editorDialog.setIconImage(iconImage);\r
                new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, editorDialog, true);\r
                deviceModelList.setMidiEditor(editorDialog);\r
-               keyboardPanel.eventDialog = editorDialog.eventDialog;\r
+               keyboardPanel.eventDialog = editorDialog.eventCellEditor.eventDialog;\r
                midiConnectionDialog = new MidiDeviceDialog(deviceModelList);\r
                midiConnectionDialog.setIconImage(iconImage);\r
                lyricDisplay = new ChordTextField() {{\r
index a2bf9f4..669cb1c 100644 (file)
@@ -77,7 +77,9 @@ import javax.swing.event.TableModelEvent;
 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
 import javax.swing.table.TableColumnModel;\r
 import javax.swing.table.TableModel;\r
@@ -108,43 +110,64 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                }\r
        };\r
        /**\r
-        * 新しいMIDIシーケンスを生成するダイアログ\r
+        * {@inheritDoc}\r
+        * <p>すでに表示されていた場合に手前に表示する点を除き、\r
+        * スーパークラスと同じです。\r
+        * </p>\r
         */\r
-       NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this) {\r
-               { setChannels(virtualMidiDevice.getChannels()); }\r
-       };\r
+       @Override\r
+       public void setVisible(boolean isToVisible) {\r
+               if( isToVisible && isVisible() ) {\r
+                       toFront();\r
+                       return;\r
+               }\r
+               super.setVisible(isToVisible);\r
+       }\r
+       /**\r
+        * {@inheritDoc}\r
+        * <p>このダイアログを表示するアクションを実行します。\r
+        * </p>\r
+        */\r
+       @Override\r
+       public void actionPerformed(ActionEvent e) {\r
+               setVisible(true);\r
+       }\r
 \r
        /**\r
-        * プレイリストのモデル\r
+        * ã\83\97ã\83¬ã\82¤ã\83ªã\82¹ã\83\88ã\81®ã\83\87ã\83¼ã\82¿ã\83¢ã\83\87ã\83«\r
         */\r
        SequenceListTableModel sequenceListTableModel;\r
        /**\r
-        * プレイリストのMIDIシーケンス選択状態\r
+        * 新しいMIDIシーケンスを生成するダイアログ\r
         */\r
-       ListSelectionModel seqSelectionModel = new DefaultListSelectionModel() {{\r
-               setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
-               addListSelectionListener(new ListSelectionListener() {\r
-                       @Override\r
-                       public void valueChanged(ListSelectionEvent e) {\r
-                               if( e.getValueIsAdjusting() ) return;\r
-                               sequenceSelectionChanged();\r
-                               trackSelectionModel.setSelectionInterval(0,0);\r
-                       }\r
-               });\r
-       }};\r
+       NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this) {\r
+               { setChannels(virtualMidiDevice.getChannels()); }\r
+       };\r
        /**\r
-        * 選択されたシーケンスへジャンプするアクション\r
+        * 新しいMIDIシーケンスを生成するアクション\r
         */\r
-       public Action jumpSequenceAction = new AbstractAction("Jump") {\r
+       public Action generateNewSongAction = new AbstractAction("New") {\r
                {\r
                        putValue(\r
                                Action.SHORT_DESCRIPTION,\r
-                               "Move to selected song - 選択した曲へ進む"\r
+                               "Generate new song - 新しい曲を生成"\r
                        );\r
                }\r
                @Override\r
                public void actionPerformed(ActionEvent e) {\r
-                       load(seqSelectionModel.getMinSelectionIndex());\r
+                       newSequenceDialog.setVisible(true);\r
+               }\r
+       };\r
+       /**\r
+        * 選択されたシーケンスへジャンプするアクション\r
+        */\r
+       public Action jumpSequenceAction = new AbstractAction("Jump") {\r
+               {\r
+                       putValue(Action.SHORT_DESCRIPTION, "Move to selected song - 選択した曲へ進む");\r
+               }\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       sequenceListTableModel.loadToSequencer(sequenceListSelectionModel.getMinSelectionIndex());\r
                }\r
        };\r
        /**\r
@@ -155,7 +178,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                public void actionPerformed(ActionEvent e) {\r
                        if( midiFileChooser != null ) {\r
                                // ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
-                               SequenceTrackListTableModel seqModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               SequenceTrackListTableModel seqModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                                if( seqModel.isModified() ) {\r
                                        // ファイル未保存の変更がある場合\r
                                        String confirmMessage =\r
@@ -168,7 +191,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                }\r
                        }\r
                        // 削除を実行\r
-                       sequenceListTableModel.removeSequence(seqSelectionModel);\r
+                       sequenceListTableModel.removeSequence(sequenceListSelectionModel);\r
                }\r
        };\r
        /**\r
@@ -178,7 +201,47 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        /**\r
         * BASE64エンコードボタン(ライブラリが見えている場合のみ有効)\r
         */\r
-       public Action base64EncodeAction;\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
+                                       @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
+                       jumpSequenceAction.setEnabled(isSelected);\r
+               }\r
+       };\r
+       /**\r
+        * プレイリストビュー\r
+        */\r
+       JTable sequenceListTableView;\r
        /**\r
         * ファイル選択ダイアログ(アプレットでは使用不可)\r
         */\r
@@ -186,23 +249,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        /**\r
         * ファイル選択ダイアログ(アプレットでは使用不可)\r
         */\r
-       private class MidiFileChooser extends JFileChooser {\r
-               {\r
-                       setFileFilter(\r
-                               new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid")\r
-                       );\r
-               }\r
-               /**\r
-                * ファイルを開くアクション\r
-                */\r
-               public Action addMidiFileAction = new AbstractAction("Open") {\r
-                       @Override\r
-                       public void actionPerformed(ActionEvent e) {\r
-                               int resp = midiFileChooser.showOpenDialog(MidiEditor.this);\r
-                               if( resp == JFileChooser.APPROVE_OPTION )\r
-                                       addSequence(midiFileChooser.getSelectedFile());\r
-                       }\r
-               };\r
+       private class MidiFileChooser extends JFileChooser implements ListSelectionListener {\r
                /**\r
                 * ファイル保存アクション\r
                 */\r
@@ -210,18 +257,18 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        @Override\r
                        public void actionPerformed(ActionEvent e) {\r
                                SequenceTrackListTableModel sequenceTableModel =\r
-                                       sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                                       sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                                String filename = sequenceTableModel.getFilename();\r
                                File midiFile;\r
                                if( filename != null && ! filename.isEmpty() ) {\r
                                        midiFile = new File(filename);\r
-                                       midiFileChooser.setSelectedFile(midiFile);\r
+                                       setSelectedFile(midiFile);\r
                                }\r
-                               int resp = midiFileChooser.showSaveDialog(MidiEditor.this);\r
+                               int resp = showSaveDialog(MidiEditor.this);\r
                                if( resp != JFileChooser.APPROVE_OPTION ) {\r
                                        return;\r
                                }\r
-                               midiFile = midiFileChooser.getSelectedFile();\r
+                               midiFile = getSelectedFile();\r
                                if( midiFile.exists() && ! confirm(\r
                                        "Overwrite " + midiFile.getName() + " ?\n"\r
                                        + midiFile.getName()\r
@@ -239,13 +286,35 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                }\r
                        }\r
                };\r
+               private void updateEnabled() {\r
+                       boolean en = (sequenceListSelectionModel.getMinSelectionIndex() >= 0);\r
+                       saveMidiFileAction.setEnabled(en);\r
+               }\r
+               {\r
+                       setFileFilter(new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid"));\r
+                       sequenceListSelectionModel.addListSelectionListener(this);\r
+                       updateEnabled();\r
+               }\r
+               @Override\r
+               public void valueChanged(ListSelectionEvent e) {\r
+                       if( e.getValueIsAdjusting() )\r
+                               return;\r
+                       updateEnabled();\r
+               }\r
+               /**\r
+                * ファイルを開くアクション\r
+                */\r
+               public Action addMidiFileAction = new AbstractAction("Open") {\r
+                       @Override\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               int resp = showOpenDialog(MidiEditor.this);\r
+                               if( resp == JFileChooser.APPROVE_OPTION )\r
+                                       addSequence(getSelectedFile());\r
+                       }\r
+               };\r
        };\r
 \r
        /**\r
-        * MIDIトラック数表示ラベル\r
-        */\r
-       private JLabel tracksLabel;\r
-       /**\r
         * MIDIトラック選択状態\r
         */\r
        private ListSelectionModel trackSelectionModel = new DefaultListSelectionModel() {{\r
@@ -253,31 +322,27 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                addListSelectionListener(new ListSelectionListener() {\r
                        @Override\r
                        public void valueChanged(ListSelectionEvent e) {\r
-                               if( e.getValueIsAdjusting() ) return;\r
-                               SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               if( e.getValueIsAdjusting() )\r
+                                       return;\r
+                               SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                                if( sequenceModel == null || isSelectionEmpty() ) {\r
-                                       midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                                       eventListTableView.setModel(new TrackEventListTableModel());\r
+                                       trackEventListTableView.setModel(new TrackEventListTableModel());\r
                                }\r
                                else {\r
                                        int selIndex = getMinSelectionIndex();\r
                                        TrackEventListTableModel trackModel = sequenceModel.getTrackModel(selIndex);\r
                                        if( trackModel == null ) {\r
-                                               midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                                               eventListTableView.setModel(new TrackEventListTableModel());\r
+                                               trackEventListTableView.setModel(new TrackEventListTableModel());\r
                                        }\r
                                        else {\r
-                                               midiEventsLabel.setText(\r
-                                                       String.format("MIDI Events (in track No.%d)", selIndex)\r
-                                               );\r
-                                               eventListTableView.setModel(trackModel);\r
-                                               TableColumnModel tcm = eventListTableView.getColumnModel();\r
+                                               trackEventListTableView.setModel(trackModel);\r
+                                               TableColumnModel tcm = trackEventListTableView.getColumnModel();\r
                                                trackModel.sizeColumnWidthToFit(tcm);\r
-                                               tcm.getColumn(TrackEventListTableModel.Column.MESSAGE.ordinal()).setCellEditor(eventCellEditor);\r
+                                               TableColumn midiMessageColumn = tcm.getColumn(TrackEventListTableModel.Column.MESSAGE.ordinal());\r
+                                               midiMessageColumn.setCellEditor(eventCellEditor);\r
                                        }\r
                                }\r
-                               setActionEnabled();\r
-                               eventSelectionModel.setSelectionInterval(0,0);\r
+                               updateButtonStatus();\r
                        }\r
                });\r
        }};\r
@@ -287,9 +352,9 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        public Action addTrackAction = new AbstractAction("New") {\r
                @Override\r
                public void actionPerformed(ActionEvent e) {\r
-                       int index = sequenceListTableModel.getSequenceModel(seqSelectionModel).createTrack();\r
+                       int index = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).createTrack();\r
                        trackSelectionModel.setSelectionInterval(index, index);\r
-                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+                       sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
                }\r
        };\r
        /**\r
@@ -300,10 +365,35 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                public void actionPerformed(ActionEvent e) {\r
                        if( ! confirm("Do you want to delete selected track ?\n選択したトラックを削除しますか?"))\r
                                return;\r
-                       sequenceListTableModel.getSequenceModel(seqSelectionModel).deleteTracks(trackSelectionModel);\r
-                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+                       sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).deleteTracks(trackSelectionModel);\r
+                       sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
                }\r
        };\r
+       /**\r
+        * MIDIトラックリストテーブルビュー(選択中のシーケンスの中身)\r
+        */\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
+               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
+       }\r
 \r
        /**\r
         * MIDIイベント選択状態\r
@@ -315,7 +405,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        public void valueChanged(ListSelectionEvent e) {\r
                                if( e.getValueIsAdjusting() ) return;\r
                                if( ! isSelectionEmpty() ) {\r
-                                       TrackEventListTableModel trackModel = (TrackEventListTableModel)eventListTableView.getModel();\r
+                                       TrackEventListTableModel trackModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
                                        int minIndex = getMinSelectionIndex();\r
                                        if( trackModel.hasTrack() ) {\r
                                                MidiEvent midiEvent = trackModel.getMidiEvent(minIndex);\r
@@ -345,36 +435,67 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                                        ) addSelectionInterval(partnerIndex, partnerIndex);\r
                                        }\r
                                }\r
-                               setActionEnabled();\r
+                               updateButtonStatus();\r
                        }\r
                });\r
        }};\r
        /**\r
         * MIDIイベントリストテーブルビュー\r
         */\r
-       private JTable eventListTableView;\r
+       private JTable trackEventListTableView = new JTable(\r
+               new TrackEventListTableModel(), null, eventSelectionModel\r
+       );\r
+       private MidiEventsLabel midiEventsLabel = new MidiEventsLabel();\r
+       private class MidiEventsLabel extends JLabel implements ListSelectionListener {\r
+               private static final String TITLE = "MIDI Events";\r
+               public MidiEventsLabel() {\r
+                       super(TITLE);\r
+                       trackSelectionModel.addListSelectionListener(this);\r
+               }\r
+               @Override\r
+               public void valueChanged(ListSelectionEvent e) {\r
+                       String text = TITLE;\r
+                       int index = trackSelectionModel.getMinSelectionIndex();\r
+                       if( index >= 0 )\r
+                               text = String.format(TITLE+" - track No.%d", index);\r
+                       setText(text);\r
+               }\r
+       }\r
        /**\r
         * スクロール可能なMIDIイベントテーブルビュー\r
         */\r
-       private JScrollPane scrollableEventTableView;\r
+       private JScrollPane scrollableEventTableView = new JScrollPane(trackEventListTableView);\r
        /**\r
-        * MIDIイベント数表示ラベル\r
+        * 指定の MIDI tick のイベントへスクロールします。\r
+        * @param tick MIDI tick\r
         */\r
-       private JLabel midiEventsLabel;\r
+       public void scrollToEventAt(long tick) {\r
+               TrackEventListTableModel trackModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
+               int index = trackModel.tickToIndex(tick);\r
+               scrollableEventTableView.getVerticalScrollBar().setValue(\r
+                       index * trackEventListTableView.getRowHeight()\r
+               );\r
+               eventSelectionModel.setSelectionInterval(index, index);\r
+       }\r
        /**\r
-        * MIDIイベント入力ダイアログ\r
+        * MIDIイベント表のセルエディタ\r
         */\r
-       MidiEventDialog eventDialog = new MidiEventDialog();\r
+       MidiEventCellEditor eventCellEditor = new MidiEventCellEditor();\r
        /**\r
         * Pair note on/off チェックボックス\r
         */\r
-       private JCheckBox pairNoteCheckbox;\r
-\r
+       private JCheckBox pairNoteCheckbox = new JCheckBox("Pair NoteON/OFF") {\r
+               { setModel(eventCellEditor.pairNoteOnOffModel); }\r
+       };\r
        /**\r
         * MIDIイベント表のセルエディタ\r
         */\r
        class MidiEventCellEditor extends AbstractCellEditor implements TableCellEditor {\r
                /**\r
+                * MIDIイベント入力ダイアログ\r
+                */\r
+               MidiEventDialog eventDialog = new MidiEventDialog();\r
+               /**\r
                 * 削除対象にする変更前イベント(null可)\r
                 */\r
                private MidiEvent[] midiEventsToBeRemoved;\r
@@ -406,16 +527,23 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                 * Pair noteON/OFF トグルボタンモデル\r
                 */\r
                private JToggleButton.ToggleButtonModel pairNoteOnOffModel =\r
-                       new JToggleButton.ToggleButtonModel();\r
+                       new JToggleButton.ToggleButtonModel() {{\r
+                               addItemListener(new ItemListener() {\r
+                                       public void itemStateChanged(ItemEvent e) {\r
+                                               eventDialog.midiMessageForm.durationForm.setEnabled(isSelected());\r
+                                       }\r
+                               });\r
+                               setSelected(true);\r
+                       }};\r
 \r
                private void setSelectedEvent() {\r
-                       sequenceTableModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                       sequenceTableModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                        eventDialog.midiMessageForm.durationForm.setPPQ(sequenceTableModel.getSequence().getResolution());\r
                        tickPositionModel.setSequenceIndex(sequenceTableModel.getSequenceTickIndex());\r
                        selectedIndex = -1;\r
                        currentTick = 0;\r
                        selectedMidiEvent = null;\r
-                       midiTrackTableModel = (TrackEventListTableModel)eventListTableView.getModel();\r
+                       midiTrackTableModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
                        if( ! eventSelectionModel.isSelectionEmpty() ) {\r
                                selectedIndex = eventSelectionModel.getMinSelectionIndex();\r
                                selectedMidiEvent = midiTrackTableModel.getMidiEvent(selectedIndex);\r
@@ -427,7 +555,10 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                 * イベント入力をキャンセルするアクション\r
                 */\r
                Action cancelAction = new AbstractAction() {\r
-                       { putValue(NAME,"Cancel"); }\r
+                       {\r
+                               putValue(NAME,"Cancel");\r
+                               eventDialog.cancelButton.setAction(this);\r
+                       }\r
                        public void actionPerformed(ActionEvent e) {\r
                                fireEditingCanceled();\r
                                eventDialog.setVisible(false);\r
@@ -461,11 +592,11 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                { putValue(NAME,"Paste"); }\r
                                public void actionPerformed(ActionEvent e) {\r
                                        long tick = tickPositionModel.getTickPosition();\r
-                                       ((TrackEventListTableModel)eventListTableView.getModel()).addMidiEvents(\r
+                                       ((TrackEventListTableModel)trackEventListTableView.getModel()).addMidiEvents(\r
                                                copiedEventsToPaste, tick, copiedEventsPPQ\r
                                        );\r
                                        scrollToEventAt(tick);\r
-                                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+                                       sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
                                        eventDialog.setVisible(false);\r
                                }\r
                        };\r
@@ -533,76 +664,63 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        }\r
                };\r
                /**\r
-                * イベント編集ボタン\r
-                */\r
-               private JButton editEventButton = new JButton() {{\r
-                       setHorizontalAlignment(JButton.LEFT);\r
-               }};\r
-               /**\r
-                * MIDIイベント表のセルエディタを構築します。\r
+                * イベント編集アクション\r
                 */\r
-               public MidiEventCellEditor() {\r
-                       eventDialog.cancelButton.setAction(cancelAction);\r
-                       eventDialog.midiMessageForm.setOutputMidiChannels(virtualMidiDevice.getChannels());\r
-                       eventDialog.tickPositionInputForm.setModel(tickPositionModel);\r
-                       editEventButton.addActionListener(\r
-                               new ActionListener() {\r
-                                       public void actionPerformed(ActionEvent e) {\r
-                                               setSelectedEvent();\r
-                                               if( selectedMidiEvent == null ) return;\r
-                                               MidiEvent partnerEvent = null;\r
-                                               eventDialog.midiMessageForm.setMessage(selectedMidiEvent.getMessage());\r
-                                               if( eventDialog.midiMessageForm.isNote() ) {\r
-                                                       int partnerIndex = midiTrackTableModel.getIndexOfPartnerFor(selectedIndex);\r
-                                                       if( partnerIndex < 0 ) {\r
-                                                               eventDialog.midiMessageForm.durationForm.setDuration(0);\r
-                                                       }\r
-                                                       else {\r
-                                                               partnerEvent = midiTrackTableModel.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
-                                               MidiEvent events[];\r
-                                               if( partnerEvent == null ) {\r
-                                                       events = new MidiEvent[1];\r
-                                                       events[0] = selectedMidiEvent;\r
-                                               }\r
-                                               else {\r
-                                                       events = new MidiEvent[2];\r
-                                                       events[0] = selectedMidiEvent;\r
-                                                       events[1] = partnerEvent;\r
-                                               }\r
-                                               midiEventsToBeRemoved = events;\r
-                                               eventDialog.setTitle("Change MIDI event");\r
-                                               eventDialog.okButton.setAction(addEventAction);\r
-                                               eventDialog.openEventForm();\r
+               private Action editEventAction = new AbstractAction() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               setSelectedEvent();\r
+                               if( selectedMidiEvent == null )\r
+                                       return;\r
+                               MidiEvent partnerEvent = null;\r
+                               eventDialog.midiMessageForm.setMessage(selectedMidiEvent.getMessage());\r
+                               if( eventDialog.midiMessageForm.isNote() ) {\r
+                                       int partnerIndex = midiTrackTableModel.getIndexOfPartnerFor(selectedIndex);\r
+                                       if( partnerIndex < 0 ) {\r
+                                               eventDialog.midiMessageForm.durationForm.setDuration(0);\r
                                        }\r
-                               }\r
-                       );\r
-                       pairNoteOnOffModel.addItemListener(\r
-                               new ItemListener() {\r
-                                       public void itemStateChanged(ItemEvent e) {\r
-                                               eventDialog.midiMessageForm.durationForm.setEnabled(\r
-                                                       pairNoteOnOffModel.isSelected()\r
-                                               );\r
+                                       else {\r
+                                               partnerEvent = midiTrackTableModel.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
-                       );\r
-                       pairNoteOnOffModel.setSelected(true);\r
+                               MidiEvent events[];\r
+                               if( partnerEvent == null ) {\r
+                                       events = new MidiEvent[1];\r
+                                       events[0] = selectedMidiEvent;\r
+                               }\r
+                               else {\r
+                                       events = new MidiEvent[2];\r
+                                       events[0] = selectedMidiEvent;\r
+                                       events[1] = partnerEvent;\r
+                               }\r
+                               midiEventsToBeRemoved = events;\r
+                               eventDialog.setTitle("Change MIDI event");\r
+                               eventDialog.okButton.setAction(addEventAction);\r
+                               eventDialog.openEventForm();\r
+                       }\r
+               };\r
+               public MidiEventCellEditor() {\r
+                       eventDialog.midiMessageForm.setOutputMidiChannels(virtualMidiDevice.getChannels());\r
+                       eventDialog.tickPositionInputForm.setModel(tickPositionModel);\r
                }\r
+               @Override\r
                public boolean isCellEditable(EventObject e) {\r
                        // ダブルクリックで編集\r
                        return e instanceof MouseEvent && ((MouseEvent)e).getClickCount() == 2;\r
                }\r
-               public Object getCellEditorValue() {\r
-                       return "";\r
-               }\r
+               @Override\r
+               public Object getCellEditorValue() { return ""; }\r
+               private JButton editEventButton = new JButton(editEventAction){\r
+                       {\r
+                               setHorizontalAlignment(JButton.LEFT);\r
+                       }\r
+               };\r
+               @Override\r
                public Component getTableCellEditorComponent(\r
-                       JTable table, Object value, boolean isSelected,\r
-                       int row, int column\r
+                       JTable table, Object value, boolean isSelected, int row, int column\r
                ) {\r
                        editEventButton.setText((String)value);\r
                        return editEventButton;\r
@@ -610,10 +728,6 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        }\r
 \r
        /**\r
-        * MIDIイベントセルエディタ\r
-        */\r
-       private MidiEventCellEditor eventCellEditor = new MidiEventCellEditor();\r
-       /**\r
         * ペースト用にコピーされたMIDIイベントの配列\r
         */\r
        private MidiEvent copiedEventsToPaste[];\r
@@ -629,11 +743,11 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                public void actionPerformed(ActionEvent e) {\r
                        if( ! confirm("Do you want to cut selected event ?\n選択したMIDIイベントを切り取りますか?"))\r
                                return;\r
-                       TrackEventListTableModel trackTableModel = (TrackEventListTableModel)eventListTableView.getModel();\r
+                       TrackEventListTableModel trackTableModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
                        copiedEventsToPaste = trackTableModel.getMidiEvents(eventSelectionModel);\r
-                       copiedEventsPPQ = sequenceListTableModel.getSequenceModel(seqSelectionModel).getSequence().getResolution();\r
+                       copiedEventsPPQ = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).getSequence().getResolution();\r
                        trackTableModel.removeMidiEvents(copiedEventsToPaste);\r
-                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+                       sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
                }\r
        };\r
        /**\r
@@ -642,10 +756,10 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        public Action copyEventAction = new AbstractAction("Copy") {\r
                @Override\r
                public void actionPerformed(ActionEvent e) {\r
-                       TrackEventListTableModel trackTableModel = (TrackEventListTableModel)eventListTableView.getModel();\r
+                       TrackEventListTableModel trackTableModel = (TrackEventListTableModel)trackEventListTableView.getModel();\r
                        copiedEventsToPaste = trackTableModel.getMidiEvents(eventSelectionModel);\r
-                       copiedEventsPPQ = sequenceListTableModel.getSequenceModel(seqSelectionModel).getSequence().getResolution();\r
-                       setActionEnabled();\r
+                       copiedEventsPPQ = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel).getSequence().getResolution();\r
+                       updateButtonStatus();\r
                }\r
        };\r
        /**\r
@@ -656,8 +770,8 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                public void actionPerformed(ActionEvent e) {\r
                        if( ! confirm("Do you want to delete selected event ?\n選択したMIDIイベントを削除しますか?"))\r
                                return;\r
-                       ((TrackEventListTableModel)eventListTableView.getModel()).removeMidiEvents(eventSelectionModel);\r
-                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+                       ((TrackEventListTableModel)trackEventListTableView.getModel()).removeMidiEvents(eventSelectionModel);\r
+                       sequenceListTableModel.fireSequenceChanged(sequenceListSelectionModel);\r
                }\r
        };\r
        /**\r
@@ -665,7 +779,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
         * @param deviceModelList MIDIデバイスモデルリスト\r
         */\r
        public MidiEditor(MidiSequencerModel sequencerModel) {\r
-               sequenceListTableModel = new SequenceListTableModel(sequencerModel);\r
+               sequenceListTableModel = new SequenceListTableModel(sequencerModel) ;\r
                setTitle("MIDI Editor/Playlist - MIDI Chord Helper");\r
                setBounds( 150, 200, 850, 500 );\r
                setLayout(new FlowLayout());\r
@@ -678,57 +792,64 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        // アクセスできないので、ファイル選択ダイアログは使用不可。\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
+                                       @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
+                                               if(row == sequenceListTableModel.getLoadedIndex())\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
+               }};\r
                JPanel playlistPanel = new JPanel() {{\r
                        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
-                       add(new JScrollPane(\r
-                               new JTable(sequenceListTableModel, null, seqSelectionModel) {{\r
-                                       sequenceListTableModel.sizeColumnWidthToFit(this);\r
-                               }}\r
-                       ));\r
+                       add(new JScrollPane(sequenceListTableView));\r
                        add(Box.createRigidArea(new Dimension(0, 10)));\r
                        add(new JPanel() {{\r
-                               setLayout( new BoxLayout(this, BoxLayout.LINE_AXIS ));\r
-                               add(new JLabel() {\r
-                                       private void update() {\r
-                                               int sec = sequenceListTableModel.getTotalSeconds();\r
-                                               String str = String.format(\r
-                                                       "MIDI file playlist - Total length = %02d:%02d",\r
-                                                       sec/60, sec%60\r
-                                               );\r
-                                               setText(str);\r
-                                       }\r
-                                       {\r
-                                               sequenceListTableModel.addTableModelListener(\r
-                                                       new TableModelListener() {\r
-                                                               /**\r
-                                                                * プレイリスト上でシーケンスが増減した場合、\r
-                                                                * 合計時間が変わるので表示を更新します。\r
-                                                                */\r
-                                                               @Override\r
-                                                               public void tableChanged(TableModelEvent e) {\r
-                                                                       switch( e.getType() ) {\r
-                                                                       case TableModelEvent.INSERT:\r
-                                                                       case TableModelEvent.DELETE: update(); break;\r
-                                                                       default: break;\r
-                                                                       }\r
-                                                               }\r
-                                                       }\r
-                                               );\r
-                                               update();\r
-                                       }\r
-                               });\r
+                               setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
                                add(Box.createRigidArea(new Dimension(10, 0)));\r
-                               add(new JButton("New") {{\r
-                                       setToolTipText("Generate new song - 新しい曲を生成");\r
-                                       setMargin(ZERO_INSETS);\r
-                                       addActionListener(\r
-                                               new ActionListener() {\r
-                                                       public void actionPerformed(ActionEvent e) {\r
-                                                               newSequenceDialog.setVisible(true);\r
-                                                       }\r
-                                               }\r
-                                       );\r
-                               }});\r
+                               add(new JButton(generateNewSongAction) {{setMargin(ZERO_INSETS);}});\r
                                if( midiFileChooser != null ) {\r
                                        add( Box.createRigidArea(new Dimension(5, 0)) );\r
                                        add(new JButton(midiFileChooser.addMidiFileAction) {{\r
@@ -740,30 +861,18 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                        setMargin(ZERO_INSETS);\r
                                }});\r
                                add(Box.createRigidArea(new Dimension(5, 0)));\r
-                               add(new JToggleButton(\r
-                                       sequenceListTableModel.sequencerModel.startStopAction\r
-                               ));\r
+                               add(new JButton(jumpSequenceAction){{ setMargin(ZERO_INSETS); }});\r
                                add(Box.createRigidArea(new Dimension(5, 0)));\r
                                add(new JButton(sequenceListTableModel.moveToBottomAction) {{\r
                                        setMargin(ZERO_INSETS);\r
                                }});\r
-                               add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add(new JButton(jumpSequenceAction){{ setMargin(ZERO_INSETS); }});\r
                                if( midiFileChooser != null ) {\r
                                        add(Box.createRigidArea(new Dimension(5, 0)));\r
                                        add(new JButton(midiFileChooser.saveMidiFileAction) {{\r
                                                setMargin(ZERO_INSETS);\r
                                        }});\r
                                }\r
-                               if( base64Dialog.isBase64Available() ) {\r
-                                       base64EncodeAction = new AbstractAction("Base64 Encode") {\r
-                                               @Override\r
-                                               public void actionPerformed(ActionEvent e) {\r
-                                                       SequenceTrackListTableModel mstm = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
-                                                       base64Dialog.setMIDIData(mstm.getMIDIdata(), mstm.getFilename());\r
-                                                       base64Dialog.setVisible(true);\r
-                                               }\r
-                                       };\r
+                               if(base64EncodeAction != null) {\r
                                        add(Box.createRigidArea(new Dimension(5, 0)));\r
                                        add(new JButton(base64EncodeAction) {{\r
                                                setMargin(ZERO_INSETS);\r
@@ -782,17 +891,9 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                }};\r
                JPanel trackListPanel = new JPanel() {{\r
                        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\r
-                       add(tracksLabel = new JLabel("Tracks"));\r
+                       add(trackListTitleLabel);\r
                        add(Box.createRigidArea(new Dimension(0, 5)));\r
-                       add(new JScrollPane(\r
-                               trackListTableView = new JTable(\r
-                                       new SequenceTrackListTableModel(sequenceListTableModel),\r
-                                       null,\r
-                                       trackSelectionModel\r
-                               ) {{\r
-                                       ((SequenceTrackListTableModel)getModel()).sizeColumnWidthToFit(getColumnModel());\r
-                               }}\r
-                       ));\r
+                       add(new JScrollPane(trackListTableView));\r
                        add(Box.createRigidArea(new Dimension(0, 5)));\r
                        add(new JPanel() {{\r
                                add(new JButton(addTrackAction) {{ setMargin(ZERO_INSETS); }});\r
@@ -800,18 +901,10 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        }});\r
                }};\r
                JPanel eventListPanel = new JPanel() {{\r
-                       add(midiEventsLabel = new JLabel("No track selected"));\r
-                       add(scrollableEventTableView = new JScrollPane(\r
-                               eventListTableView = new JTable(\r
-                                       new TrackEventListTableModel(), null, eventSelectionModel\r
-                               )\r
-                       ));\r
+                       add(midiEventsLabel);\r
+                       add(scrollableEventTableView);\r
                        add(new JPanel() {{\r
-                               add(\r
-                                       pairNoteCheckbox = new JCheckBox("Pair NoteON/OFF") {{\r
-                                               setModel(eventCellEditor.pairNoteOnOffModel);\r
-                                       }}\r
-                               );\r
+                               add(pairNoteCheckbox);\r
                                add(new JButton(eventCellEditor.queryJumpEventAction) {{\r
                                        setMargin(ZERO_INSETS);\r
                                }});\r
@@ -839,8 +932,10 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                setDividerLocation(160);\r
                        }}\r
                );\r
-               sequenceSelectionChanged();\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
@@ -867,6 +962,8 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        event.dropComplete(false);\r
                }\r
        }\r
+\r
+       // Short message dialogs\r
        private void showError(String message) {\r
                JOptionPane.showMessageDialog(\r
                        this, message,\r
@@ -889,82 +986,51 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        JOptionPane.WARNING_MESSAGE\r
                ) == JOptionPane.YES_OPTION ;\r
        }\r
-       @Override\r
-       public void setVisible(boolean isToVisible) {\r
-               if( isToVisible && isVisible() )\r
-                       toFront();\r
-               else\r
-                       super.setVisible(isToVisible);\r
-       }\r
-       @Override\r
-       public void actionPerformed(ActionEvent e) {\r
-               setVisible(true);\r
-       }\r
-       /**\r
-        * MIDIトラックリストテーブルビュー(選択中のシーケンスの中身)\r
-        */\r
-       private JTable trackListTableView;\r
+\r
        /**\r
-        * 選択されたシーケンスが変わったときの動作\r
+        * ボタン状態の更新\r
         */\r
-       public void sequenceSelectionChanged() {\r
-               SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+       public void updateButtonStatus() {\r
+               SequenceTrackListTableModel sequenceModel = sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                boolean isSequenceSelected = (sequenceModel != null);\r
-               //\r
-               // ボタンイネーブル状態の更新\r
-               if(midiFileChooser != null)\r
-                       midiFileChooser.saveMidiFileAction.setEnabled(isSequenceSelected);\r
-               if(base64EncodeAction != null)\r
-                       base64EncodeAction.setEnabled(isSequenceSelected);\r
-               deleteSequenceAction.setEnabled(isSequenceSelected);\r
-               jumpSequenceAction.setEnabled(isSequenceSelected);\r
-               addTrackAction.setEnabled(isSequenceSelected);\r
-               //\r
-               // トラックリストの対象シーケンスを切り替える\r
                if(isSequenceSelected) {\r
-                       int selectedIndex = seqSelectionModel.getMinSelectionIndex();\r
                        trackListTableView.setModel(sequenceModel);\r
-                       TableColumnModel trackColumnModel = trackListTableView.getColumnModel();\r
-                       sequenceModel.sizeColumnWidthToFit(trackColumnModel);\r
-                       int columnIndex = SequenceTrackListTableModel.Column.RECORD_CHANNEL.ordinal();\r
-                       TableColumn trackColumn = trackColumnModel.getColumn(columnIndex);\r
-                       trackColumn.setCellEditor(sequenceModel.new RecordChannelCellEditor());\r
-                       trackSelectionModel.setSelectionInterval(0,0);\r
-                       tracksLabel.setText(String.format("Tracks (in MIDI file No.%d)", selectedIndex));\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
-                       trackListTableView.setModel(new SequenceTrackListTableModel(sequenceListTableModel));\r
-                       tracksLabel.setText("Tracks (No MIDI file selected)");\r
+                       SequenceTrackListTableModel m = new SequenceTrackListTableModel(sequenceListTableModel);\r
+                       trackListTableView.setModel(m);\r
                }\r
-               setActionEnabled();\r
-       }\r
-       /**\r
-        * ボタン状態の更新\r
-        */\r
-       public void setActionEnabled() {\r
+               addTrackAction.setEnabled(isSequenceSelected);\r
                boolean isTrackSelected = (\r
-                       ! trackSelectionModel.isSelectionEmpty() &&\r
-                       sequenceListTableModel.getSequenceModel(seqSelectionModel) != null &&\r
-                       sequenceListTableModel.getSequenceModel(seqSelectionModel).getRowCount() > 0\r
+                       ! trackSelectionModel.isSelectionEmpty()\r
+                       &&\r
+                       isSequenceSelected && sequenceModel.getRowCount() > 0\r
                );\r
                removeTrackAction.setEnabled(isTrackSelected);\r
-               TableModel tm = eventListTableView.getModel();\r
+               //\r
+               TableModel tm = trackEventListTableView.getModel();\r
                if( ! (tm instanceof TrackEventListTableModel) )\r
                        return;\r
-               //\r
-               //\r
                TrackEventListTableModel trackTableModel = (TrackEventListTableModel)tm;\r
-               jumpSequenceAction.setEnabled(\r
-                       trackTableModel != null && trackTableModel.getRowCount() > 0\r
-               );\r
                boolean isEventSelected = (\r
-                       !(\r
-                               eventSelectionModel.isSelectionEmpty() ||\r
-                               trackTableModel == null || trackTableModel.getRowCount() == 0\r
-                       ) && isTrackSelected\r
+                       isTrackSelected &&\r
+                       ! eventSelectionModel.isSelectionEmpty() &&\r
+                       trackTableModel != null && trackTableModel.getRowCount() > 0\r
                );\r
-               //\r
-               // イベント操作のイネーブル状態更新\r
                copyEventAction.setEnabled(isEventSelected);\r
                deleteEventAction.setEnabled(isEventSelected);\r
                cutEventAction.setEnabled(isEventSelected);\r
@@ -988,7 +1054,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        public int addSequenceAndPlay(Sequence sequence) {\r
                int lastIndex = sequenceListTableModel.addSequence(sequence,"");\r
                if( ! sequenceListTableModel.sequencerModel.getSequencer().isRunning() ) {\r
-                       load(lastIndex);\r
+                       sequenceListTableModel.loadToSequencer(lastIndex);\r
                        sequenceListTableModel.sequencerModel.start();\r
                }\r
                return lastIndex;\r
@@ -1063,24 +1129,6 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        }\r
 \r
        /**\r
-        * 指定のインデックス位置にあるMIDIシーケンスをシーケンサーにロードします。\r
-        * @param index MIDIシーケンスのインデックス(先頭が 0)\r
-        */\r
-       public void load(int index) {\r
-               sequenceListTableModel.loadToSequencer(index);\r
-               sequenceSelectionChanged();\r
-       }\r
-       /**\r
-        * 次の曲をロードします。\r
-        * @param offset 何曲次へ進むかを表す数値\r
-        * @return 成功したらtrue\r
-        */\r
-       public boolean loadNext(int offset) {\r
-               boolean retval = sequenceListTableModel.loadNext(offset);\r
-               sequenceSelectionChanged();\r
-               return retval;\r
-       }\r
-       /**\r
         * 複数のMIDIファイルを読み込み、再生されていなかったら再生します。\r
         * すでに再生されていた場合、このエディタダイアログを表示します。\r
         *\r
@@ -1097,7 +1145,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        setVisible(true);\r
                }\r
                else if( firstIndex >= 0 ) {\r
-                       load(firstIndex);\r
+                       sequenceListTableModel.loadToSequencer(firstIndex);\r
                        sequenceListTableModel.sequencerModel.start();\r
                }\r
        }\r
@@ -1108,21 +1156,9 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
         */\r
        public boolean isRecordable() {\r
                SequenceTrackListTableModel sequenceTableModel =\r
-                       sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                       sequenceListTableModel.getSequenceModel(sequenceListSelectionModel);\r
                return sequenceTableModel == null ? false : sequenceTableModel.isRecordable();\r
        }\r
-       /**\r
-        * 指定の MIDI tick のイベントへスクロールします。\r
-        * @param tick MIDI tick\r
-        */\r
-       public void scrollToEventAt(long tick) {\r
-               TrackEventListTableModel trackModel = (TrackEventListTableModel)eventListTableView.getModel();\r
-               int index = trackModel.tickToIndex(tick);\r
-               scrollableEventTableView.getVerticalScrollBar().setValue(\r
-                       index * eventListTableView.getRowHeight()\r
-               );\r
-               eventSelectionModel.setSelectionInterval(index, index);\r
-       }\r
 }\r
 \r
 /**\r
@@ -1174,56 +1210,83 @@ class SequencerSpeedSlider extends JPanel {
 }\r
 \r
 /**\r
- * プレイリスト(MIDIシーケンスリスト)のテーブルモデル\r
+ * プレイリスト(MIDIシーケンスリスト)のテーブルモデル\r
  */\r
-class SequenceListTableModel extends AbstractTableModel implements ChangeListener {\r
+class SequenceListTableColumnModel extends DefaultTableColumnModel\r
+       implements TableModelListener\r
+{\r
        /**\r
         * 列の列挙型\r
         */\r
        public enum Column {\r
                /** MIDIシーケンスの番号 */\r
-               SEQ_NUMBER("No.", 2, Integer.class),\r
+               SEQ_NUMBER("No.", Integer.class),\r
                /** 変更済みフラグ */\r
-               MODIFIED("Modified", 6, Boolean.class),\r
+               MODIFIED("Modified", Boolean.class),\r
+               /** 再生ボタン */\r
+               SEQ_PLAY("", String.class) {\r
+                       @Override\r
+                       public boolean isCellEditable() { return true; }\r
+               },\r
                /** 再生中の時間位置(分:秒) */\r
-               SEQ_POSITION("Position", 6, String.class),\r
+               SEQ_POSITION("Position", String.class),\r
                /** シーケンスの時間長(分:秒) */\r
-               SEQ_LENGTH("Length", 6, String.class),\r
+               SEQ_LENGTH("Length", String.class),\r
                /** ファイル名 */\r
-               FILENAME("Filename", 16, String.class),\r
+               FILENAME("Filename", String.class) {\r
+                       @Override\r
+                       public boolean isCellEditable() { return true; }\r
+               },\r
                /** シーケンス名(最初のトラックの名前) */\r
-               SEQ_NAME("Sequence name", 40, String.class),\r
+               SEQ_NAME("Sequence name", String.class) {\r
+                       @Override\r
+                       public boolean isCellEditable() { return true; }\r
+               },\r
                /** タイミング解像度 */\r
-               RESOLUTION("Resolution", 6, Integer.class),\r
+               RESOLUTION("Resolution", Integer.class),\r
                /** トラック数 */\r
-               TRACKS("Tracks", 6, Integer.class),\r
+               TRACKS("Tracks", Integer.class),\r
                /** タイミング分割形式 */\r
-               DIVISION_TYPE("DivType", 6, String.class);\r
-               private String title;\r
-               private int widthRatio;\r
-               private Class<?> columnClass;\r
+               DIVISION_TYPE("DivType", 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
+               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
        MidiSequencerModel sequencerModel;\r
@@ -1232,10 +1295,10 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
         */\r
        public Action moveToTopAction = new AbstractAction() {\r
                {\r
-                       putValue( SHORT_DESCRIPTION,\r
+                       putValue(SHORT_DESCRIPTION,\r
                                "Move to top or previous song - 曲の先頭または前の曲へ戻る"\r
                        );\r
-                       putValue( LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.TOP_ICON) );\r
+                       putValue(LARGE_ICON_KEY, new ButtonIcon(ButtonIcon.TOP_ICON));\r
                }\r
                public void actionPerformed(ActionEvent event) {\r
                        if( sequencerModel.getSequencer().getTickPosition() <= 40 )\r
@@ -1248,8 +1311,8 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
         */\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
+                       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
@@ -1272,33 +1335,40 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
        @Override\r
        public void stateChanged(ChangeEvent e) {\r
                int sec = sequencerModel.getValue() / 1000;\r
-               if( secondPosition == sec )\r
+               if(secondPosition == sec)\r
                        return;\r
+               // 秒が変わったときだけ更新(小数点以下は無視)\r
                secondPosition = sec;\r
-               fireTableCellUpdated(getLoadedIndex(), Column.SEQ_POSITION.ordinal());\r
+               fireTableCellUpdated(\r
+                       getLoadedIndex(),\r
+                       SequenceListTableColumnModel.Column.SEQ_POSITION.ordinal()\r
+               );\r
        }\r
-       private List<SequenceTrackListTableModel> sequenceList = new ArrayList<>();\r
+       private List<SequenceTrackListTableModel> sequenceList = new Vector<>();\r
        @Override\r
-       public int getRowCount() {\r
-               return sequenceList.size();\r
-       }\r
+       public int getRowCount() { return sequenceList.size(); }\r
        @Override\r
        public int getColumnCount() {\r
-               return Column.values().length;\r
+               return SequenceListTableColumnModel.Column.values().length;\r
        }\r
        @Override\r
        public String getColumnName(int column) {\r
-               return Column.values()[column].title;\r
+               return SequenceListTableColumnModel.Column.values()[column].title;\r
        }\r
        @Override\r
        public Class<?> getColumnClass(int column) {\r
-               return Column.values()[column].columnClass;\r
+               return SequenceListTableColumnModel.Column.values()[column].columnClass;\r
+       }\r
+       @Override\r
+       public boolean isCellEditable(int row, int column) {\r
+               return SequenceListTableColumnModel.Column.values()[column].isCellEditable();\r
        }\r
        @Override\r
        public Object getValueAt(int row, int column) {\r
-               switch(Column.values()[column]) {\r
+               switch(SequenceListTableColumnModel.Column.values()[column]) {\r
                case SEQ_NUMBER: return row;\r
-               case MODIFIED: return sequenceList.get(row).isModified();\r
+               case MODIFIED:\r
+                       return sequenceList.get(row).isModified();\r
                case DIVISION_TYPE: {\r
                        float divType = sequenceList.get(row).getSequence().getDivisionType();\r
                        if( divType == Sequence.PPQ ) return "PPQ";\r
@@ -1313,8 +1383,7 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                case TRACKS:\r
                        return sequenceList.get(row).getSequence().getTracks().length;\r
                case SEQ_POSITION: {\r
-                       Sequence loadedSequence = sequencerModel.getSequencer().getSequence();\r
-                       if( loadedSequence != null && loadedSequence == sequenceList.get(row).getSequence() )\r
+                       if( getLoadedIndex() == row )\r
                                return String.format("%02d:%02d", secondPosition/60, secondPosition%60);\r
                        else\r
                                return "";\r
@@ -1336,13 +1405,8 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                }\r
        }\r
        @Override\r
-       public boolean isCellEditable( int row, int column ) {\r
-               Column c = Column.values()[column];\r
-               return c == Column.FILENAME || c == Column.SEQ_NAME ;\r
-       }\r
-       @Override\r
        public void setValueAt(Object val, int row, int column) {\r
-               switch(Column.values()[column]) {\r
+               switch(SequenceListTableColumnModel.Column.values()[column]) {\r
                case FILENAME:\r
                        // ファイル名の変更\r
                        String filename = (String)val;\r
@@ -1352,26 +1416,13 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                case SEQ_NAME:\r
                        // シーケンス名の設定または変更\r
                        if( sequenceList.get(row).setName((String)val) )\r
-                               fireTableCellUpdated(row, Column.MODIFIED.ordinal());\r
+                               fireTableCellUpdated(row, SequenceListTableColumnModel.Column.MODIFIED.ordinal());\r
                        break;\r
                default:\r
                        break;\r
                }\r
        }\r
        /**\r
-        * 列に合わせて幅を調整します。\r
-        * @param tableView テーブルビュー\r
-        */\r
-       public void sizeColumnWidthToFit(JTable tableView) {\r
-               TableColumnModel columnModel = tableView.getColumnModel();\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
         * このプレイリストに読み込まれた全シーケンスの合計時間長を返します。\r
         * @return 全シーケンスの合計時間長 [秒]\r
         */\r
@@ -1405,7 +1456,7 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                for( int i = minIndex; i <= maxIndex; i++ ) {\r
                        if( selModel.isSelectedIndex(i) ) {\r
                                sequenceList.get(i).setModified(isModified);\r
-                               fireTableCellUpdated(i, Column.MODIFIED.ordinal());\r
+                               fireTableCellUpdated(i, SequenceListTableColumnModel.Column.MODIFIED.ordinal());\r
                        }\r
                }\r
        }\r
@@ -1428,33 +1479,22 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
         */\r
        public void fireSequenceChanged(SequenceTrackListTableModel sequenceTableModel) {\r
                int index = sequenceList.indexOf(sequenceTableModel);\r
-               if( index < 0 ) return;\r
-               fireSequenceChanged(index,index);\r
+               if( index < 0 )\r
+                       return;\r
+               sequenceTableModel.setModified(true);\r
+               fireTableRowsUpdated(index, index);\r
        }\r
        /**\r
         * 指定された選択範囲のシーケンスが変更されたことを通知します。\r
         * @param selectionModel 選択状態\r
         */\r
        public void fireSequenceChanged(ListSelectionModel selectionModel) {\r
-               if( ! selectionModel.isSelectionEmpty() ) fireSequenceChanged(\r
-                       selectionModel.getMinSelectionIndex(),\r
-                       selectionModel.getMaxSelectionIndex()\r
-               );\r
-       }\r
-       /**\r
-        * 指定された範囲のシーケンスが変更されたことを通知します。\r
-        * @param minIndex 範囲の最小インデックス\r
-        * @param maxIndex 範囲の最大インデックス\r
-        */\r
-       public void fireSequenceChanged(int minIndex, int maxIndex) {\r
+               if( selectionModel.isSelectionEmpty() )\r
+                       return;\r
+               int minIndex = selectionModel.getMinSelectionIndex();\r
+               int maxIndex = selectionModel.getMaxSelectionIndex();\r
                for( int index = minIndex; index <= maxIndex; index++ ) {\r
-                       SequenceTrackListTableModel model = sequenceList.get(index);\r
-                       model.setModified(true);\r
-                       if( sequencerModel.getSequencer().getSequence() == model.getSequence() ) {\r
-                               // シーケンサーに対して、同じシーケンスを再度セットする。\r
-                               // (これをやらないと更新が反映されないため)\r
-                               sequencerModel.setSequenceTableModel(model);\r
-                       }\r
+                       sequenceList.get(index).setModified(true);\r
                }\r
                fireTableRowsUpdated(minIndex, maxIndex);\r
        }\r
@@ -1486,8 +1526,10 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                if( listSelectionModel.isSelectionEmpty() )\r
                        return;\r
                int selectedIndex = listSelectionModel.getMinSelectionIndex();\r
-               if(sequenceList.get(selectedIndex) == sequencerModel.getSequenceTableModel())\r
+               if( selectedIndex == getLoadedIndex() ) {\r
+                       // シーケンサーにロード済みのシーケンスだった場合はアンロードする\r
                        sequencerModel.setSequenceTableModel(null);\r
+               }\r
                sequenceList.remove(selectedIndex);\r
                fireTableRowsDeleted(selectedIndex, selectedIndex);\r
        }\r
@@ -1502,11 +1544,21 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                SequenceTrackListTableModel sequenceTableModel = sequenceList.get(index);\r
                sequencerModel.setSequenceTableModel(sequenceTableModel);\r
                //\r
-               // 表示更新のための変更通知\r
+               // トラック内容の再表示\r
                sequenceTableModel.fireTableDataChanged();\r
-               int columnIndex = Column.SEQ_POSITION.ordinal();\r
-               fireTableCellUpdated(oldIndex, columnIndex);\r
-               fireTableCellUpdated(index, columnIndex);\r
+               //\r
+               // セルの再表示(シーケンサーのボタンと現在位置)\r
+               fireTableRowsUpdated(oldIndex, index);\r
+               int columnIndices[] = {\r
+                       SequenceListTableColumnModel.Column.SEQ_PLAY.ordinal(),\r
+                       SequenceListTableColumnModel.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
+               }\r
        }\r
        /**\r
         * 現在シーケンサにロードされているシーケンスのインデックスを返します。\r
@@ -1591,26 +1643,9 @@ class SequenceTrackListTableModel extends AbstractTableModel {
         */\r
        private List<TrackEventListTableModel> trackModelList = new ArrayList<>();\r
        /**\r
-        * 記録するMIDIチャンネル\r
-        */\r
-       class RecordChannelCellEditor extends DefaultCellEditor {\r
-               public RecordChannelCellEditor() {\r
-                       super(\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
-       /**\r
         * 親のプレイリスト\r
         */\r
-       private SequenceListTableModel sequenceListTableModel;\r
+       SequenceListTableModel sequenceListTableModel;\r
        /**\r
         * 空の {@link SequenceTrackListTableModel} を構築します。\r
         * @param sequenceListTableModel 親のプレイリスト\r
@@ -1851,12 +1886,6 @@ class SequenceTrackListTableModel extends AbstractTableModel {
                int row = indexOf(track);\r
                if( row < 0 ) return;\r
                fireTableRowsUpdated(row, row);\r
-               fireSequenceChanged();\r
-       }\r
-       /**\r
-        * このシーケンスが変更されたことを通知します。\r
-        */\r
-       public void fireSequenceChanged() {\r
                sequenceListTableModel.fireSequenceChanged(this);\r
        }\r
        /**\r
@@ -2117,7 +2146,7 @@ class TrackEventListTableModel extends AbstractTableModel {
                fireTableDataChanged();\r
                if( MIDISpec.isEOT(msg) ) {\r
                        // EOTの場所が変わると曲の長さが変わるので、親モデルへ通知する。\r
-                       parent.fireSequenceChanged();\r
+                       parent.sequenceListTableModel.fireSequenceChanged(parent);\r
                }\r
        }\r
        /**\r
@@ -2151,7 +2180,7 @@ class TrackEventListTableModel extends AbstractTableModel {
                if(name.equals(toString()) || ! MIDISpec.setNameOf(track, name))\r
                        return false;\r
                parent.setModified(true);\r
-               parent.fireSequenceChanged();\r
+               parent.sequenceListTableModel.fireSequenceChanged(parent);\r
                fireTableDataChanged();\r
                return true;\r
        }\r
index 93f84d0..b745b54 100644 (file)
@@ -201,7 +201,7 @@ class MidiSequencerModel extends MidiConnecterListModel implements BoundedRangeM
                                        // そうでない場合、次の曲へ進んで再生する。\r
                                        // 次の曲がなければ、そこで終了。\r
                                        boolean isRepeatMode = (Boolean)toggleRepeatAction.getValue(Action.SELECTED_KEY);\r
-                                       if( isRepeatMode || MidiSequencerModel.this.deviceModelList.editorDialog.loadNext(1) ) {\r
+                                       if( isRepeatMode || MidiSequencerModel.this.deviceModelList.editorDialog.sequenceListTableModel.loadNext(1) ) {\r
                                                start();\r
                                        }\r
                                        else {\r
index d63991b..5b3d69d 100644 (file)
@@ -95,7 +95,7 @@ class AppletFrame extends JFrame implements
         */\r
        @Override\r
        public void tableChanged(TableModelEvent e) {\r
-               if( e.getColumn() == SequenceListTableModel.Column.FILENAME.ordinal() )\r
+               if( e.getColumn() == SequenceListTableColumnModel.Column.FILENAME.ordinal() )\r
                        setFilenameToTitle();\r
        }\r
        @Override\r