OSDN Git Service

リファクタリング(MIDIEditorのボタンをActionに置き換えるなど)
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Wed, 20 Nov 2013 18:00:47 +0000 (18:00 +0000)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Wed, 20 Nov 2013 18:00:47 +0000 (18:00 +0000)
git-svn-id: https://svn.sourceforge.jp/svnroot/midichordhelper/MIDIChordHelper@13 302f1594-2db2-43b1-aaa4-6307b5a2a2de

src/ChordHelperApplet.java
src/MIDIEditor.java

index 31f9324..5faedd2 100644 (file)
@@ -248,7 +248,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.20131118.1";\r
+               public static final String      VERSION = "Ver.20131121.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
@@ -626,7 +626,7 @@ public class ChordHelperApplet extends JApplet {
                                        add( new JSlider(deviceModelList.sequencerModel) );\r
                                        add( new TimeIndicator(deviceModelList.sequencerModel) );\r
                                        add( Box.createHorizontalStrut(5) );\r
-                                       add( new JButton(editorDialog.moveToTopAction) {{\r
+                                       add( new JButton(editorDialog.sequenceListTableModel.moveToTopAction) {{\r
                                                setMargin(ZERO_INSETS);\r
                                        }});\r
                                        add(new JButton(deviceModelList.sequencerModel.moveBackwardAction) {{\r
@@ -636,7 +636,7 @@ public class ChordHelperApplet extends JApplet {
                                        add(new JButton(deviceModelList.sequencerModel.moveForwardAction) {{\r
                                                setMargin(ZERO_INSETS);\r
                                        }});\r
-                                       add(new JButton(editorDialog.moveToBottomAction) {{\r
+                                       add(new JButton(editorDialog.sequenceListTableModel.moveToBottomAction) {{\r
                                                setMargin(ZERO_INSETS);\r
                                        }});\r
                                        add(new JToggleButton(deviceModelList.sequencerModel.toggleRepeatAction) {{\r
index 839a57a..2f3ceaa 100644 (file)
@@ -53,6 +53,7 @@ import javax.swing.BoundedRangeModel;
 import javax.swing.Box;\r
 import javax.swing.BoxLayout;\r
 import javax.swing.DefaultCellEditor;\r
+import javax.swing.DefaultListSelectionModel;\r
 import javax.swing.Icon;\r
 import javax.swing.JButton;\r
 import javax.swing.JCheckBox;\r
@@ -89,10 +90,7 @@ import javax.swing.table.TableModel;
  */\r
 class MidiEditor extends JDialog implements DropTargetListener, ActionListener {\r
        public static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
-       /**\r
-        * プレイリストのモデル\r
-        */\r
-       SequenceListTableModel sequenceListTableModel;\r
+       private static final Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);\r
        /**\r
         * このMIDIエディタの仮想MIDIデバイス\r
         */\r
@@ -111,136 +109,265 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        /**\r
         * 新しいMIDIシーケンスを生成するダイアログ\r
         */\r
-       NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this) {{\r
-               setChannels(virtualMidiDevice.getChannels());\r
-       }};\r
-       /**\r
-        * MIDIイベント入力ダイアログ\r
-        */\r
-       MidiEventDialog eventDialog = new MidiEventDialog();\r
-       /**\r
-        * MIDIシーケンス選択状態\r
-        */\r
-       ListSelectionModel seqSelectionModel;\r
+       NewSequenceDialog newSequenceDialog = new NewSequenceDialog(this) {\r
+               { setChannels(virtualMidiDevice.getChannels()); }\r
+       };\r
+\r
        /**\r
-        * MIDIトラック選択状態\r
+        * プレイリストのモデル\r
         */\r
-       private ListSelectionModel trackSelectionModel;\r
+       SequenceListTableModel sequenceListTableModel;\r
        /**\r
-        * MIDIイベント選択状態\r
+        * プレイリストのMIDIシーケンス選択状態\r
         */\r
-       private ListSelectionModel eventSelectionModel;\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
        /**\r
-        * MIDIシーケンスリストテーブルビュー\r
+        * 選択されたシーケンスへジャンプするアクション\r
         */\r
-       private JTable sequenceListTableView;\r
+       public Action jumpSequenceAction = new AbstractAction("Jump") {\r
+               {\r
+                       putValue(\r
+                               Action.SHORT_DESCRIPTION,\r
+                               "Move to selected song - 選択した曲へ進む"\r
+                       );\r
+               }\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       load(seqSelectionModel.getMinSelectionIndex());\r
+               }\r
+       };\r
        /**\r
-        * MIDIトラックリストテーブルビュー\r
+        * シーケンスを削除するアクション\r
         */\r
-       private JTable trackListTableView;\r
+       public Action deleteSequenceAction = new AbstractAction("Delete",deleteIcon) {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       if( midiFileChooser != null ) {\r
+                               // ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
+                               MidiSequenceTableModel seqModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               if( seqModel.isModified() ) {\r
+                                       // ファイル未保存の変更がある場合\r
+                                       String confirmMessage =\r
+                                               "Selected MIDI sequence not saved - delete it ?\n" +\r
+                                               "選択したMIDIシーケンスはまだ保存されていません。削除しますか?";\r
+                                       if( ! confirm(confirmMessage) ) {\r
+                                               // ユーザに確認してNoって言われた場合\r
+                                               return;\r
+                                       }\r
+                               }\r
+                       }\r
+                       // 削除を実行\r
+                       sequenceListTableModel.removeSequence(seqSelectionModel);\r
+               }\r
+       };\r
        /**\r
-        * MIDIイベントリストテーブルビュー\r
+        * BASE64テキスト入力ダイアログ\r
         */\r
-       private JTable eventListTableView;\r
+       Base64Dialog base64Dialog = new Base64Dialog(this);\r
        /**\r
-        * スクロール可能なMIDIイベントテーブルビュー\r
+        * BASE64エンコードボタン(ライブラリが見えている場合のみ有効)\r
         */\r
-       private JScrollPane scrollableEventTableView;\r
+       public Action base64EncodeAction;\r
        /**\r
-        * 全MIDIシーケンス合計時間表示ラベル\r
+        * ファイル選択ダイアログ(アプレットでは使用不可)\r
         */\r
-       private class TotalTimeLabel extends JLabel implements TableModelListener {\r
-               SequenceListTableModel model;\r
-               public TotalTimeLabel(SequenceListTableModel model) {\r
-                       (this.model = model).addTableModelListener(this);\r
-                       update();\r
-               }\r
-               private void update() {\r
-                       int sec = model.getTotalSeconds();\r
-                       String str = String.format("MIDI file playlist - Total length = %02d:%02d", sec/60, sec%60);\r
-                       setText(str);\r
+       private class MidiFileChooser extends JFileChooser {\r
+               {\r
+                       setFileFilter(\r
+                               new FileNameExtensionFilter("MIDI sequence (*.mid)", "mid")\r
+                       );\r
                }\r
                /**\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
+               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
-       }\r
+               };\r
+               /**\r
+                * ファイル保存アクション\r
+                */\r
+               public Action saveMidiFileAction = new AbstractAction("Save") {\r
+                       @Override\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               MidiSequenceTableModel sequenceTableModel =\r
+                                       sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               String filename = sequenceTableModel.getFilename();\r
+                               File midiFile;\r
+                               if( filename != null && ! filename.isEmpty() ) {\r
+                                       midiFile = new File(filename);\r
+                                       midiFileChooser.setSelectedFile(midiFile);\r
+                               }\r
+                               int resp = midiFileChooser.showSaveDialog(MidiEditor.this);\r
+                               if( resp != JFileChooser.APPROVE_OPTION ) {\r
+                                       return;\r
+                               }\r
+                               midiFile = midiFileChooser.getSelectedFile();\r
+                               if( midiFile.exists() && ! confirm(\r
+                                       "Overwrite " + midiFile.getName() + " ?\n"\r
+                                       + midiFile.getName()\r
+                                       + " を上書きしてよろしいですか?"\r
+                               ) ) {\r
+                                       return;\r
+                               }\r
+                               try ( FileOutputStream out = new FileOutputStream(midiFile) ) {\r
+                                       out.write(sequenceTableModel.getMIDIdata());\r
+                                       sequenceTableModel.setModified(false);\r
+                               }\r
+                               catch( IOException ex ) {\r
+                                       showError( ex.getMessage() );\r
+                                       ex.printStackTrace();\r
+                               }\r
+                       }\r
+               };\r
+       };\r
        /**\r
-        * MIDIトラック数表示ラベル\r
+        * ファイル選択ダイアログ(アプレットでは使用不可)\r
         */\r
-       private JLabel tracksLabel = new JLabel("Tracks");\r
+       private MidiFileChooser midiFileChooser;\r
+\r
        /**\r
-        * MIDIã\82¤ã\83\99ã\83³ã\83\88æ\95°è¡¨ç¤ºã\83©ã\83\99ã\83«\r
+        * MIDIã\83\88ã\83©ã\83\83ã\82¯é\81¸æ\8a\9eç\8a¶æ\85\8b\r
         */\r
-       private JLabel midiEventsLabel = new JLabel("No track selected");\r
+       private ListSelectionModel trackSelectionModel = new DefaultListSelectionModel() {{\r
+               setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
+               addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if( e.getValueIsAdjusting() ) return;\r
+                               MidiSequenceTableModel sequenceModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               if( sequenceModel == null || isSelectionEmpty() ) {\r
+                                       midiEventsLabel.setText("MIDI Events (No track selected)");\r
+                                       eventListTableView.setModel(new MidiTrackTableModel());\r
+                               }\r
+                               else {\r
+                                       int selIndex = getMinSelectionIndex();\r
+                                       MidiTrackTableModel trackModel = sequenceModel.getTrackModel(selIndex);\r
+                                       if( trackModel == null ) {\r
+                                               midiEventsLabel.setText("MIDI Events (No track selected)");\r
+                                               eventListTableView.setModel(new MidiTrackTableModel());\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
+                                               trackModel.sizeColumnWidthToFit(tcm);\r
+                                               tcm.getColumn(MidiTrackTableModel.Column.MESSAGE.ordinal()).setCellEditor(eventCellEditor);\r
+                                       }\r
+                               }\r
+                               updateButtonStatus();\r
+                               eventSelectionModel.setSelectionInterval(0,0);\r
+                       }\r
+               });\r
+       }};\r
        /**\r
-        * BASE64テキスト入力ダイアログ\r
+        * トラック追加アクション\r
         */\r
-       Base64Dialog base64Dialog = new Base64Dialog(this);\r
+       public Action addTrackAction = new AbstractAction("New") {\r
+               @Override\r
+               public void actionPerformed(ActionEvent e) {\r
+                       int index = sequenceListTableModel.getSequenceModel(seqSelectionModel).createTrack();\r
+                       trackSelectionModel.setSelectionInterval(index, index);\r
+                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
+               }\r
+       };\r
        /**\r
-        * BASE64エンコードボタン(ライブラリが見えている場合のみ有効)\r
+        * MIDIトラック除去アクション\r
         */\r
-       private JButton base64EncodeButton;\r
-\r
+       public Action removeTrackAction = new AbstractAction("Delete", deleteIcon) {\r
+               @Override\r
+               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
+               }\r
+       };\r
        /**\r
-        * ファイル選択ダイアログ(アプレットでは使用不可)\r
+        * MIDIトラックリストテーブルビュー\r
         */\r
-       private JFileChooser fileChooser;\r
+       private JTable trackListTableView;\r
+\r
        /**\r
-        * MIDIã\82·ã\83¼ã\82±ã\83³ã\82¹è¿½å\8a ã\83\9cã\82¿ã\83³ï¼\88ã\83\95ã\82¡ã\82¤ã\83«é\81¸æ\8a\9eã\83\80ã\82¤ã\82¢ã\83­ã\82°ã\81\8cå\88©ç\94¨ã\81§ã\81\8dã\82\8bå ´å\90\88ã\81®ã\81¿æ\9c\89å\8a¹ï¼\89\r
+        * MIDIã\82¤ã\83\99ã\83³ã\83\88é\81¸æ\8a\9eç\8a¶æ\85\8b\r
         */\r
-       private JButton addMidiFileButton;\r
+       private ListSelectionModel eventSelectionModel = new DefaultListSelectionModel() {{\r
+               setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
+               addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if( e.getValueIsAdjusting() ) return;\r
+                               if( ! isSelectionEmpty() ) {\r
+                                       MidiTrackTableModel trackModel = (MidiTrackTableModel)eventListTableView.getModel();\r
+                                       int minIndex = getMinSelectionIndex();\r
+                                       if( trackModel.hasTrack() ) {\r
+                                               MidiEvent midiEvent = trackModel.getMidiEvent(minIndex);\r
+                                               MidiMessage msg = midiEvent.getMessage();\r
+                                               if( msg instanceof ShortMessage ) {\r
+                                                       ShortMessage sm = (ShortMessage)msg;\r
+                                                       int cmd = sm.getCommand();\r
+                                                       if( cmd == 0x80 || cmd == 0x90 || cmd == 0xA0 ) {\r
+                                                               // ノート番号を持つ場合、音を鳴らす。\r
+                                                               MidiChannel outMidiChannels[] = virtualMidiDevice.getChannels();\r
+                                                               int ch = sm.getChannel();\r
+                                                               int note = sm.getData1();\r
+                                                               int vel = sm.getData2();\r
+                                                               outMidiChannels[ch].noteOn(note, vel);\r
+                                                               outMidiChannels[ch].noteOff(note, vel);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if( pairNoteCheckbox.isSelected() ) {\r
+                                               int maxIndex = getMaxSelectionIndex();\r
+                                               int partnerIndex;\r
+                                               for( int i=minIndex; i<=maxIndex; i++ )\r
+                                                       if(\r
+                                                               isSelectedIndex(i) &&\r
+                                                               (partnerIndex = trackModel.getIndexOfPartnerFor(i)) >= 0 &&\r
+                                                               ! isSelectedIndex(partnerIndex)\r
+                                                       ) addSelectionInterval(partnerIndex, partnerIndex);\r
+                                       }\r
+                               }\r
+                               updateButtonStatus();\r
+                       }\r
+               });\r
+       }};\r
        /**\r
-        * MIDIã\83\95ã\82¡ã\82¤ã\83«ä¿\9då­\98ã\83\9cã\82¿ã\83³ï¼\88ã\83\95ã\82¡ã\82¤ã\83«é\81¸æ\8a\9eã\83\80ã\82¤ã\82¢ã\83­ã\82°ã\81\8cå\88©ç\94¨ã\81§ã\81\8dã\82\8bå ´å\90\88ã\81®ã\81¿æ\9c\89å\8a¹ï¼\89\r
+        * MIDIã\82¤ã\83\99ã\83³ã\83\88ã\83ªã\82¹ã\83\88ã\83\86ã\83¼ã\83\96ã\83«ã\83\93ã\83¥ã\83¼\r
         */\r
-       private JButton saveMidiFileButton;\r
-\r
+       private JTable eventListTableView;\r
        /**\r
-        * MIDIシーケンス削除ボタン\r
+        * スクロール可能なMIDIイベントテーブルビュー\r
         */\r
-       private JButton deleteSequenceButton;\r
+       private JScrollPane scrollableEventTableView;\r
        /**\r
-        * MIDIã\82·ã\83¼ã\82±ã\83³ã\82¹ã\82¸ã\83£ã\83³ã\83\97ã\83\9cã\82¿ã\83³\r
+        * MIDIã\83\88ã\83©ã\83\83ã\82¯æ\95°è¡¨ç¤ºã\83©ã\83\99ã\83«\r
         */\r
-       private JButton jumpSequenceButton = new JButton("Jump") {{\r
-               setToolTipText("Move to selected song - 選択した曲へ進む");\r
-               setMargin(ZERO_INSETS);\r
-               addActionListener(\r
-                       new ActionListener() {\r
-                               public void actionPerformed(ActionEvent e) {\r
-                                       load(seqSelectionModel.getMinSelectionIndex());\r
-                               }\r
-                       }\r
-               );\r
-       }};\r
+       private JLabel tracksLabel = new JLabel("Tracks");\r
        /**\r
-        * MIDIã\83\88ã\83©ã\83\83ã\82¯è¿½å\8a ã\83\9cã\82¿ã\83³\r
+        * MIDIã\82¤ã\83\99ã\83³ã\83\88æ\95°è¡¨ç¤ºã\83©ã\83\99ã\83«\r
         */\r
-       private JButton addTrackButton = new JButton("New") {{\r
-               setMargin(ZERO_INSETS);\r
-               addActionListener(\r
-                       new ActionListener() {\r
-                               public void actionPerformed(ActionEvent e) {\r
-                                       int index = sequenceListTableModel.getSequenceModel(seqSelectionModel).createTrack();\r
-                                       trackSelectionModel.setSelectionInterval(index, index);\r
-                                       sequenceListTableModel.fireSequenceChanged(seqSelectionModel);\r
-                               }\r
-                       }\r
-               );\r
-       }};;\r
+       private JLabel midiEventsLabel = new JLabel("No track selected");\r
        /**\r
-        * MIDIã\83\88ã\83©ã\83\83ã\82¯é\99¤å\8e»ã\83\9cã\82¿ã\83³\r
+        * MIDIã\82¤ã\83\99ã\83³ã\83\88å\85¥å\8a\9bã\83\80ã\82¤ã\82¢ã\83­ã\82°\r
         */\r
-       private JButton removeTrackButton;\r
+       MidiEventDialog eventDialog = new MidiEventDialog();\r
        /**\r
         * MIDIイベント除去ボタン\r
         */\r
@@ -304,7 +431,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        }\r
                }\r
                /**\r
-                * キャンセルするアクション\r
+                * ã\82¤ã\83\99ã\83³ã\83\88å\85¥å\8a\9bã\82\92ã\82­ã\83£ã\83³ã\82»ã\83«ã\81\99ã\82\8bã\82¢ã\82¯ã\82·ã\83§ã\83³\r
                 */\r
                Action cancelAction = new AbstractAction() {\r
                        { putValue(NAME,"Cancel"); }\r
@@ -490,39 +617,6 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        }\r
 \r
        /**\r
-        * MIDIシーケンサモデル\r
-        */\r
-       private 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
         * MIDIイベントセルエディタ\r
         */\r
        private MidiEventCellEditor eventCellEditor = new MidiEventCellEditor();\r
@@ -588,207 +682,65 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                );\r
        }};\r
        /**\r
-        * 再生/一時停止ボタン\r
-        */\r
-       private JToggleButton playPauseButton;\r
-       /**\r
         * 新しい {@link MidiEditor} を構築します。\r
         * @param deviceModelList MIDIデバイスモデルリスト\r
         */\r
        public MidiEditor(MidiSequencerModel sequencerModel) {\r
-               MidiSequenceTableModel emptyTrackTableModel = new MidiSequenceTableModel(\r
-                       sequenceListTableModel = new SequenceListTableModel(\r
-                               this.sequencerModel = sequencerModel\r
-                       )\r
-               );\r
                setTitle("MIDI Editor/Playlist - MIDI Chord Helper");\r
                setBounds( 150, 200, 850, 500 );\r
                setLayout(new FlowLayout());\r
                new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this, true);\r
-               playPauseButton = new JToggleButton(sequencerModel.startStopAction);\r
-               sequenceListTableView = new JTable(sequenceListTableModel);\r
-               trackListTableView = new JTable(emptyTrackTableModel);\r
-               eventListTableView = new JTable(new MidiTrackTableModel());\r
-               //\r
-               sequenceListTableModel.sizeColumnWidthToFit(sequenceListTableView);\r
-               emptyTrackTableModel.sizeColumnWidthToFit(trackListTableView.getColumnModel());\r
-               //\r
-               seqSelectionModel = sequenceListTableView.getSelectionModel();\r
-               seqSelectionModel.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );\r
-               seqSelectionModel.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
-               trackSelectionModel = trackListTableView.getSelectionModel();\r
-               trackSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
-               trackSelectionModel.addListSelectionListener(new ListSelectionListener() {\r
-                       @Override\r
-                       public void valueChanged(ListSelectionEvent e) {\r
-                               if( e.getValueIsAdjusting() ) return;\r
-                               MidiSequenceTableModel sequenceModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
-                               if( sequenceModel == null || trackSelectionModel.isSelectionEmpty() ) {\r
-                                       midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                                       eventListTableView.setModel(new MidiTrackTableModel());\r
-                               }\r
-                               else {\r
-                                       int selIndex = trackSelectionModel.getMinSelectionIndex();\r
-                                       MidiTrackTableModel trackModel = sequenceModel.getTrackModel(selIndex);\r
-                                       if( trackModel == null ) {\r
-                                               midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                                               eventListTableView.setModel(new MidiTrackTableModel());\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
-                                               trackModel.sizeColumnWidthToFit(tcm);\r
-                                               tcm.getColumn(MidiTrackTableModel.Column.MESSAGE.ordinal()).setCellEditor(eventCellEditor);\r
-                                       }\r
-                               }\r
-                               updateButtonStatus();\r
-                               eventSelectionModel.setSelectionInterval(0,0);\r
-                       }\r
-               });\r
-               eventSelectionModel = eventListTableView.getSelectionModel();\r
-               eventSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
-               eventSelectionModel.addListSelectionListener(new ListSelectionListener() {\r
-                       @Override\r
-                       public void valueChanged(ListSelectionEvent e) {\r
-                               if( e.getValueIsAdjusting() ) return;\r
-                               if( ! eventSelectionModel.isSelectionEmpty() ) {\r
-                                       MidiTrackTableModel trackModel = (MidiTrackTableModel)eventListTableView.getModel();\r
-                                       int minIndex = eventSelectionModel.getMinSelectionIndex();\r
-                                       if( trackModel.hasTrack() ) {\r
-                                               MidiEvent midiEvent = trackModel.getMidiEvent(minIndex);\r
-                                               MidiMessage msg = midiEvent.getMessage();\r
-                                               if( msg instanceof ShortMessage ) {\r
-                                                       ShortMessage sm = (ShortMessage)msg;\r
-                                                       int cmd = sm.getCommand();\r
-                                                       if( cmd == 0x80 || cmd == 0x90 || cmd == 0xA0 ) {\r
-                                                               // ノート番号を持つ場合、音を鳴らす。\r
-                                                               MidiChannel outMidiChannels[] = virtualMidiDevice.getChannels();\r
-                                                               int ch = sm.getChannel();\r
-                                                               int note = sm.getData1();\r
-                                                               int vel = sm.getData2();\r
-                                                               outMidiChannels[ch].noteOn(note, vel);\r
-                                                               outMidiChannels[ch].noteOff(note, vel);\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                                       if( pairNoteCheckbox.isSelected() ) {\r
-                                               int maxIndex = eventSelectionModel.getMaxSelectionIndex();\r
-                                               int partnerIndex;\r
-                                               for( int i=minIndex; i<=maxIndex; i++ )\r
-                                                       if(\r
-                                                               eventSelectionModel.isSelectedIndex(i) &&\r
-                                                               (partnerIndex = trackModel.getIndexOfPartnerFor(i)) >= 0 &&\r
-                                                               ! eventSelectionModel.isSelectedIndex(partnerIndex)\r
-                                                       ) eventSelectionModel.addSelectionInterval(partnerIndex, partnerIndex);\r
-                                       }\r
-                               }\r
-                               updateButtonStatus();\r
-                       }\r
-               });\r
+               sequenceListTableModel = new SequenceListTableModel(sequencerModel);\r
+               eventListTableView = new JTable(\r
+                       new MidiTrackTableModel(), null, eventSelectionModel\r
+               );\r
                try {\r
-                       fileChooser = new JFileChooser() {{\r
-                               setFileFilter(new FileNameExtensionFilter(\r
-                                       "MIDI sequence (*.mid)", "mid"\r
-                               ));\r
-                       }};\r
+                       midiFileChooser = new MidiFileChooser();\r
                }\r
                catch( ExceptionInInitializerError|NoClassDefFoundError|AccessControlException e ) {\r
                        // アプレットの場合、Webクライアントマシンのローカルファイルには\r
                        // アクセスできないので、ファイル選択ダイアログは使用不可。\r
-                       fileChooser = null;\r
-               }\r
-               if( fileChooser != null ) {\r
-                       addMidiFileButton = new JButton("Open") {{\r
-                               setMargin(ZERO_INSETS);\r
-                               addActionListener(\r
-                                       new ActionListener() {\r
-                                               public void actionPerformed(ActionEvent e) {\r
-                                                       int resp = fileChooser.showOpenDialog(MidiEditor.this);\r
-                                                       if( resp == JFileChooser.APPROVE_OPTION )\r
-                                                               addSequence(fileChooser.getSelectedFile());\r
-                                               }\r
-                                       }\r
-                               );\r
-                       }};\r
-                       saveMidiFileButton = new JButton("Save") {{\r
-                               setMargin(ZERO_INSETS);\r
-                               addActionListener(new ActionListener() {\r
-                                       @Override\r
-                                       public void actionPerformed(ActionEvent e) {\r
-                                               MidiSequenceTableModel sequenceTableModel =\r
-                                                       sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
-                                               String filename = sequenceTableModel.getFilename();\r
-                                               File midiFile;\r
-                                               if( filename != null && ! filename.isEmpty() ) {\r
-                                                       midiFile = new File(filename);\r
-                                                       fileChooser.setSelectedFile(midiFile);\r
-                                               }\r
-                                               int resp = fileChooser.showSaveDialog(MidiEditor.this);\r
-                                               if( resp != JFileChooser.APPROVE_OPTION ) {\r
-                                                       return;\r
-                                               }\r
-                                               midiFile = fileChooser.getSelectedFile();\r
-                                               if( midiFile.exists() && ! confirm(\r
-                                                       "Overwrite " + midiFile.getName() + " ?\n"\r
-                                                       + midiFile.getName()\r
-                                                       + " を上書きしてよろしいですか?"\r
-                                               ) ) {\r
-                                                       return;\r
-                                               }\r
-                                               try ( FileOutputStream out = new FileOutputStream(midiFile) ) {\r
-                                                       out.write(sequenceTableModel.getMIDIdata());\r
-                                                       sequenceTableModel.setModified(false);\r
-                                               }\r
-                                               catch( IOException ex ) {\r
-                                                       showError( ex.getMessage() );\r
-                                                       ex.printStackTrace();\r
-                                               }\r
-                                       }\r
-                               });\r
-                       }};\r
+                       midiFileChooser = null;\r
                }\r
-               Icon deleteIcon = new ButtonIcon(ButtonIcon.X_ICON);\r
-               deleteSequenceButton = new JButton("Delete", deleteIcon) {{\r
-                       setMargin(ZERO_INSETS);\r
-                       addActionListener(new ActionListener() {\r
-                               public void actionPerformed(ActionEvent e) {\r
-                                       if( saveMidiFileButton != null ) {\r
-                                               // ファイルに保存できる場合(Javaアプレットではなく、Javaアプリとして動作している場合)\r
-                                               MidiSequenceTableModel seqModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
-                                               if( seqModel.isModified() ) {\r
-                                                       // ファイル未保存の変更がある場合\r
-                                                       String confirmMessage =\r
-                                                               "Selected MIDI sequence not saved - delete it ?\n" +\r
-                                                               "選択したMIDIシーケンスは保存されていませんが、削除しますか?";\r
-                                                       if( ! confirm(confirmMessage) ) {\r
-                                                               // ユーザに確認してNoって言われた場合\r
-                                                               return;\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                                       // 削除を実行\r
-                                       sequenceListTableModel.removeSequence(seqSelectionModel);\r
-                               }\r
-                       });\r
-               }};\r
                JPanel playlistPanel = new JPanel() {{\r
                        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
-                       add(new JScrollPane(sequenceListTableView));\r
+                       add(new JScrollPane(\r
+                               new JTable(sequenceListTableModel, null, seqSelectionModel) {{\r
+                                       sequenceListTableModel.sizeColumnWidthToFit(this);\r
+                               }}\r
+                       ));\r
                        add(Box.createRigidArea(new Dimension(0, 10)));\r
                        add(new JPanel() {{\r
                                setLayout( new BoxLayout(this, BoxLayout.LINE_AXIS ));\r
-                               add(new TotalTimeLabel(sequenceListTableModel));\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
                                add(Box.createRigidArea(new Dimension(10, 0)));\r
                                add(new JButton("New") {{\r
                                        setToolTipText("Generate new song - 新しい曲を生成");\r
@@ -801,68 +753,80 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                                                }\r
                                        );\r
                                }});\r
-                               if( addMidiFileButton != null ) {\r
+                               if( midiFileChooser != null ) {\r
                                        add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                                       add( addMidiFileButton );\r
+                                       add(new JButton(midiFileChooser.addMidiFileAction) {{\r
+                                               setMargin(ZERO_INSETS);\r
+                                       }});\r
                                }\r
+                               add(Box.createRigidArea(new Dimension(5, 0)));\r
+                               add(new JButton(sequenceListTableModel.moveToTopAction) {{\r
+                                       setMargin(ZERO_INSETS);\r
+                               }});\r
+                               add(Box.createRigidArea(new Dimension(5, 0)));\r
+                               add(new JToggleButton(\r
+                                       sequenceListTableModel.sequencerModel.startStopAction\r
+                               ));\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(moveToTopAction) {{setMargin(ZERO_INSETS);}} );\r
-                               add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add( playPauseButton );\r
-                               add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add( new JButton(moveToBottomAction) {{setMargin(ZERO_INSETS);}} );\r
-                               add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add( jumpSequenceButton );\r
-                               if( saveMidiFileButton != null ) {\r
-                                       add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                                       add( saveMidiFileButton );\r
+                               add(new JButton(jumpSequenceAction){{\r
+                                       setMargin(ZERO_INSETS);\r
+                               }});\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
-                                       add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                                       add( base64EncodeButton = new JButton("Base64 Encode") {{\r
+                                       base64EncodeAction = new AbstractAction("Base64 Encode") {\r
+                                               @Override\r
+                                               public void actionPerformed(ActionEvent e) {\r
+                                                       MidiSequenceTableModel mstm = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                                                       base64Dialog.setMIDIData(mstm.getMIDIdata(), mstm.getFilename());\r
+                                                       base64Dialog.setVisible(true);\r
+                                               }\r
+                                       };\r
+                                       add(Box.createRigidArea(new Dimension(5, 0)));\r
+                                       add(new JButton(base64EncodeAction) {{\r
                                                setMargin(ZERO_INSETS);\r
-                                               addActionListener(\r
-                                                       new ActionListener() {\r
-                                                               public void actionPerformed(ActionEvent e) {\r
-                                                                       MidiSequenceTableModel mstm = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
-                                                                       base64Dialog.setMIDIData(mstm.getMIDIdata(), mstm.getFilename());\r
-                                                                       base64Dialog.setVisible(true);\r
-                                                               }\r
-                                                       }\r
-                                               );\r
                                        }});\r
                                }\r
                                add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add( deleteSequenceButton );\r
+                               add(new JButton(deleteSequenceAction) {{\r
+                                       setMargin(ZERO_INSETS);\r
+                               }});\r
                                add( Box.createRigidArea(new Dimension(5, 0)) );\r
-                               add( new SequencerSpeedSlider(\r
-                                       MidiEditor.this.sequencerModel.speedSliderModel)\r
-                               );\r
+                               add(new SequencerSpeedSlider(\r
+                                       sequenceListTableModel.sequencerModel.speedSliderModel\r
+                               ));\r
                        }});\r
                        add( Box.createRigidArea(new Dimension(0, 10)) );\r
                }};\r
-               removeTrackButton = new JButton("Delete", deleteIcon) {{\r
-                       setMargin(ZERO_INSETS);\r
-                       addActionListener(\r
-                               new ActionListener() {\r
-                                       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
-                                       }\r
-                               }\r
-                       );\r
-               }};\r
                JPanel trackListPanel = new JPanel() {{\r
                        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\r
                        add(tracksLabel);\r
                        add(Box.createRigidArea(new Dimension(0, 5)));\r
-                       add(new JScrollPane(trackListTableView));\r
+                       add(new JScrollPane(\r
+                               trackListTableView = new JTable(\r
+                                       new MidiSequenceTableModel(sequenceListTableModel),\r
+                                       null,\r
+                                       trackSelectionModel\r
+                               ) {{\r
+                                       ((MidiSequenceTableModel)getModel()).sizeColumnWidthToFit(getColumnModel());\r
+                               }}\r
+                       ));\r
                        add(Box.createRigidArea(new Dimension(0, 5)));\r
                        add(new JPanel() {{\r
-                               add(addTrackButton);\r
-                               add(removeTrackButton);\r
+                               add(new JButton(addTrackAction) {{\r
+                                       setMargin(ZERO_INSETS);\r
+                               }});\r
+                               add(new JButton(removeTrackAction) {{\r
+                                       setMargin(ZERO_INSETS);\r
+                               }});\r
                        }});\r
                }};\r
                removeEventButton = new JButton("Delete", deleteIcon) {{\r
@@ -976,11 +940,11 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
        public void sequenceSelectionChanged() {\r
                MidiSequenceTableModel sequenceTableModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
                boolean loaded = (sequenceTableModel != null);\r
-               if(saveMidiFileButton != null) saveMidiFileButton.setEnabled(loaded);\r
-               if(base64EncodeButton != null) base64EncodeButton.setEnabled(loaded);\r
-               deleteSequenceButton.setEnabled(loaded);\r
-               jumpSequenceButton.setEnabled(loaded);\r
-               addTrackButton.setEnabled(loaded);\r
+               if(midiFileChooser != null) midiFileChooser.saveMidiFileAction.setEnabled(loaded);\r
+               if(base64EncodeAction != null) base64EncodeAction.setEnabled(loaded);\r
+               deleteSequenceAction.setEnabled(loaded);\r
+               jumpSequenceAction.setEnabled(loaded);\r
+               addTrackAction.setEnabled(loaded);\r
                if(loaded) {\r
                        int selectedIndex = seqSelectionModel.getMinSelectionIndex();\r
                        trackListTableView.setModel(sequenceTableModel);\r
@@ -1009,12 +973,12 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        sequenceListTableModel.getSequenceModel(seqSelectionModel) != null &&\r
                        sequenceListTableModel.getSequenceModel(seqSelectionModel).getRowCount() > 0\r
                );\r
-               removeTrackButton.setEnabled(isTrackSelected);\r
+               removeTrackAction.setEnabled(isTrackSelected);\r
                TableModel tm = eventListTableView.getModel();\r
                if( ! (tm instanceof MidiTrackTableModel) )\r
                        return;\r
                MidiTrackTableModel trackTableModel = (MidiTrackTableModel)tm;\r
-               jumpSequenceButton.setEnabled(\r
+               jumpSequenceAction.setEnabled(\r
                        trackTableModel != null && trackTableModel.getRowCount() > 0\r
                );\r
                boolean isEventSelected = (\r
@@ -1034,7 +998,9 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                );\r
        }\r
        public String getMIDIdataBase64() {\r
-               base64Dialog.setMIDIData(sequencerModel.getSequenceTableModel().getMIDIdata());\r
+               base64Dialog.setMIDIData(\r
+                       sequenceListTableModel.sequencerModel.getSequenceTableModel().getMIDIdata()\r
+               );\r
                return base64Dialog.getBase64Data();\r
        }\r
        /**\r
@@ -1045,9 +1011,9 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
         */\r
        public int addSequenceAndPlay(Sequence sequence) {\r
                int lastIndex = sequenceListTableModel.addSequence(sequence,"");\r
-               if( ! sequencerModel.getSequencer().isRunning() ) {\r
+               if( ! sequenceListTableModel.sequencerModel.getSequencer().isRunning() ) {\r
                        load(lastIndex);\r
-                       sequencerModel.start();\r
+                       sequenceListTableModel.sequencerModel.start();\r
                }\r
                return lastIndex;\r
        }\r
@@ -1119,6 +1085,7 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                if( seq == null ) return -1;\r
                return sequenceListTableModel.addSequence(seq, filename);\r
        }\r
+\r
        /**\r
         * 指定のインデックス位置にあるMIDIシーケンスをシーケンサーにロードします。\r
         * @param index MIDIシーケンスのインデックス(先頭が 0)\r
@@ -1150,12 +1117,12 @@ class MidiEditor extends JDialog implements DropTargetListener, ActionListener {
                        if( firstIndex == -1 )\r
                                firstIndex = lastIndex;\r
                }\r
-               if(sequencerModel.getSequencer().isRunning()) {\r
+               if(sequenceListTableModel.sequencerModel.getSequencer().isRunning()) {\r
                        setVisible(true);\r
                }\r
                else if( firstIndex >= 0 ) {\r
                        load(firstIndex);\r
-                       sequencerModel.start();\r
+                       sequenceListTableModel.sequencerModel.start();\r
                }\r
        }\r
        /**\r
@@ -1282,19 +1249,50 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                }\r
        }\r
        /**\r
-        * MIDIã\82·ã\83¼ã\82±ã\83³ã\82µã\83¼ã\83¢ã\83\87ã\83«\r
+        * MIDIシーケンサモデル\r
         */\r
-       protected MidiSequencerModel sequencerModel;\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 deviceManager MIDIデバイスマネージャ\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
+        * å\86\8dç\94\9f中ã\81®ã\82·ã\83¼ã\82±ã\83³ã\82µã\83¼ã\81®ç§\92ä½\8dç½®ã\81\8cå¤\89ã\82\8fã\81£ã\81\9fã\81¨ã\81\8dã\81«è¡¨ç¤ºã\82\92æ\9b´æ\96°ã\81\97ã\81¾ã\81\99ã\80\82\r
         */\r
        @Override\r
        public void stateChanged(ChangeEvent e) {\r