OSDN Git Service

・歌詞テキスト表示欄に誤った音名の(A~Gではない)コードを入れたときにダイアログを出すようにした
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / chordmatrix / ChordMatrix.java
1 package camidion.chordhelper.chordmatrix;\r
2 \r
3 import java.awt.Color;\r
4 import java.awt.Component;\r
5 import java.awt.Dimension;\r
6 import java.awt.Font;\r
7 import java.awt.Graphics;\r
8 import java.awt.Graphics2D;\r
9 import java.awt.GridLayout;\r
10 import java.awt.event.ActionEvent;\r
11 import java.awt.event.ActionListener;\r
12 import java.awt.event.FocusEvent;\r
13 import java.awt.event.FocusListener;\r
14 import java.awt.event.InputEvent;\r
15 import java.awt.event.ItemEvent;\r
16 import java.awt.event.ItemListener;\r
17 import java.awt.event.KeyEvent;\r
18 import java.awt.event.KeyListener;\r
19 import java.awt.event.MouseEvent;\r
20 import java.awt.event.MouseListener;\r
21 import java.awt.event.MouseMotionListener;\r
22 import java.awt.event.MouseWheelEvent;\r
23 import java.awt.event.MouseWheelListener;\r
24 import java.util.ArrayList;\r
25 \r
26 import javax.swing.ComboBoxModel;\r
27 import javax.swing.DefaultComboBoxModel;\r
28 import javax.swing.JComponent;\r
29 import javax.swing.JLabel;\r
30 import javax.swing.JOptionPane;\r
31 import javax.swing.JPanel;\r
32 \r
33 import camidion.chordhelper.ButtonIcon;\r
34 import camidion.chordhelper.ChordDisplayLabel;\r
35 import camidion.chordhelper.chorddiagram.CapoSelecterView;\r
36 import camidion.chordhelper.midieditor.SequenceTickIndex;\r
37 import camidion.chordhelper.music.Chord;\r
38 import camidion.chordhelper.music.Key;\r
39 import camidion.chordhelper.music.Music;\r
40 import camidion.chordhelper.music.NoteSymbol;\r
41 import camidion.chordhelper.music.SymbolLanguage;\r
42 \r
43 /**\r
44  * MIDI Chord Helper 用のコードボタンマトリクス\r
45  *\r
46  * @author\r
47  *      Copyright (C) 2004-2013 Akiyoshi Kamide\r
48  *      http://www.yk.rim.or.jp/~kamide/music/chordhelper/\r
49  */\r
50 public class ChordMatrix extends JPanel\r
51         implements MouseListener, KeyListener, MouseMotionListener, MouseWheelListener\r
52 {\r
53         /**\r
54          * 列数\r
55          */\r
56         public static final int N_COLUMNS = Music.SEMITONES_PER_OCTAVE * 2 + 1;\r
57         /**\r
58          * 行数\r
59          */\r
60         public static final int CHORD_BUTTON_ROWS = 3;\r
61         /**\r
62          * 調号ボタン\r
63          */\r
64         public Co5Label keysigLabels[] = new Co5Label[ N_COLUMNS ];\r
65         /**\r
66          * コードボタン\r
67          */\r
68         public ChordLabel chordLabels[] = new ChordLabel[N_COLUMNS * CHORD_BUTTON_ROWS];\r
69         /**\r
70          * コードボタンの下のコード表示部\r
71          */\r
72         public ChordDisplayLabel chordDisplay = new ChordDisplayLabel("Chord Pad", this, null);\r
73 \r
74         private static class ChordLabelSelection {\r
75                 ChordLabel chordLabel;\r
76                 int bitIndex;\r
77                 boolean isSus4;\r
78                 public ChordLabelSelection(ChordLabel chordLabel, int bitIndex) {\r
79                         this.chordLabel = chordLabel;\r
80                         this.bitIndex = bitIndex;\r
81                         this.isSus4 = chordLabel.isSus4;\r
82                 }\r
83                 public void setCheckBit(boolean isOn) {\r
84                         chordLabel.setCheckBit(isOn, bitIndex);\r
85                 }\r
86                 public boolean setBassCheckBit(boolean isOn) {\r
87                         if( bitIndex == 0 && ! isSus4 ) {\r
88                                 chordLabel.setCheckBit(isOn, 6);\r
89                                 return true;\r
90                         }\r
91                         return false;\r
92                 }\r
93         }\r
94         private static class ChordLabelSelections {\r
95                 int weight = 0;\r
96                 int bass_weight = 0;\r
97                 boolean is_active = false;\r
98                 boolean is_bass_active = false;\r
99                 private ChordLabelSelection acls[];\r
100                 public ChordLabelSelections(ArrayList<ChordLabelSelection> al) {\r
101                         acls = al.toArray(new ChordLabelSelection[al.size()]);\r
102                 }\r
103                 void addWeight(int weight_diff) {\r
104                         if( (weight += weight_diff) < 0 ) weight = 0;\r
105                         if( (weight > 0) != is_active ) {\r
106                                 is_active = !is_active;\r
107                                 for( ChordLabelSelection cls : acls ) {\r
108                                         cls.setCheckBit(is_active);\r
109                                 }\r
110                         }\r
111                 }\r
112                 void addBassWeight(int weight_diff) {\r
113                         if( (bass_weight += weight_diff) < 0 ) bass_weight = 0;\r
114                         if( (bass_weight > 0) != is_bass_active ) {\r
115                                 is_bass_active = !is_bass_active;\r
116                                 for( ChordLabelSelection cls : acls ) {\r
117                                         if( ! cls.setBassCheckBit(is_bass_active) ) {\r
118                                                 // No more root major/minor\r
119                                                 break;\r
120                                         }\r
121                                 }\r
122                         }\r
123                         addWeight(weight_diff);\r
124                 }\r
125                 void clearWeight() {\r
126                         weight = bass_weight = 0;\r
127                         is_active = is_bass_active = false;\r
128                         for( ChordLabelSelection cls : acls ) {\r
129                                 cls.setCheckBit(false);\r
130                                 cls.setBassCheckBit(false);\r
131                         }\r
132                 }\r
133         }\r
134         private ChordLabelSelections chordLabelSelections[] =\r
135                 new ChordLabelSelections[Music.SEMITONES_PER_OCTAVE];\r
136         /**\r
137          * 発音中のノート表示をクリアします。\r
138          */\r
139         public void clearIndicators() {\r
140                 for( int i=0; i<chordLabelSelections.length; i++ ) {\r
141                         chordLabelSelections[i].clearWeight();\r
142                 }\r
143                 repaint();\r
144         }\r
145         /**\r
146          * MIDIのノートイベント(ON/OFF)を受け取ります。\r
147          * @param isNoteOn ONのときtrue\r
148          * @param noteNumber ノート番号\r
149          */\r
150         public void note(boolean isNoteOn, int noteNumber) {\r
151                 int weightDiff = (isNoteOn ? 1 : -1);\r
152                 ChordLabelSelections cls = chordLabelSelections[Music.mod12(noteNumber)];\r
153                 if( noteNumber < 49 )\r
154                         cls.addBassWeight(weightDiff);\r
155                 else\r
156                         cls.addWeight(weightDiff);\r
157         }\r
158 \r
159         /**\r
160          * 調号ボタン\r
161          */\r
162         private class Co5Label extends JLabel {\r
163                 public boolean isSelected = false;\r
164                 public int co5Value = 0;\r
165                 private Color indicatorColor;\r
166                 public Co5Label(int v) {\r
167                         Key key = new Key(co5Value = v);\r
168                         setOpaque(true);\r
169                         setBackground(false);\r
170                         setForeground( currentColorset.foregrounds[0] );\r
171                         setHorizontalAlignment( JLabel.CENTER );\r
172                         String tip = "Key signature: ";\r
173                         if( v != key.toCo5() ) {\r
174                                 tip += "out of range" ;\r
175                         }\r
176                         else {\r
177                                 tip += key.signatureDescription() + " " +\r
178                                         key.toStringIn(SymbolLanguage.IN_JAPANESE);\r
179                                 if( v == 0 ) {\r
180                                         setIcon(new ButtonIcon(ButtonIcon.NATURAL_ICON));\r
181                                 }\r
182                                 else {\r
183                                         setFont( getFont().deriveFont(Font.PLAIN) );\r
184                                         setText( key.signature() );\r
185                                 }\r
186                         }\r
187                         setToolTipText(tip);\r
188                 }\r
189                 public void paint(Graphics g) {\r
190                         super.paint(g);\r
191                         Dimension d = getSize();\r
192                         if( ChordMatrix.this.isFocusOwner() && isSelected ) {\r
193                                 g.setColor( currentColorset.focus[1] );\r
194                                 g.drawRect( 0, 0, d.width-1, d.height-1 );\r
195                         }\r
196                         if( !isSelected || !isPlaying || currentBeat+1 == timesigUpper ) {\r
197                                 return;\r
198                         }\r
199                         if( currentBeat == 0 ) {\r
200                                 g.setColor( indicatorColor );\r
201                                 g.drawRect( 2, 2, d.width-5, d.height-5 );\r
202                                 g.setColor( isDark ? indicatorColor.darker() : indicatorColor.brighter() );\r
203                                 g.drawRect( 0, 0, d.width-1, d.height-1 );\r
204                                 return;\r
205                         }\r
206                         Color color = currentColorset.indicators[0];\r
207                         g.setColor( color );\r
208                         if( currentBeat == 1 ) {\r
209                                 //\r
210                                 // ||__ii\r
211                                 g.drawLine( 2, d.height-3, d.width-3, d.height-3 );\r
212                                 g.drawLine( d.width-3, d.height*3/4, d.width-3, d.height-3 );\r
213                                 g.drawLine( 2, 2, 2, d.height-3 );\r
214                                 g.setColor( isDark ? color.darker() : color.brighter() );\r
215                                 g.drawLine( 0, d.height-1, d.width-1, d.height-1 );\r
216                                 g.drawLine( d.width-1, d.height*3/4, d.width-1, d.height-1 );\r
217                                 g.drawLine( 0, 0, 0, d.height-1 );\r
218                         }\r
219                         else {\r
220                                 //\r
221                                 // ii__\r
222                                 //\r
223                                 int vertical_top = (d.height-1) * (currentBeat-1) / (timesigUpper-2) ;\r
224                                 g.drawLine( 2, vertical_top == 0 ? 2 : vertical_top, 2, d.height-3 );\r
225                                 g.setColor( isDark ? color.darker() : color.brighter() );\r
226                                 g.drawLine( 0, vertical_top, 0, d.height-1 );\r
227                         }\r
228                 }\r
229                 public void setBackground(boolean isActive) {\r
230                         super.setBackground(currentColorset.backgrounds[isActive?2:0]);\r
231                         setIndicatorColor();\r
232                         setOpaque(true);\r
233                 }\r
234                 public void setSelection(boolean isSelected) {\r
235                         this.isSelected = isSelected;\r
236                         setSelection();\r
237                 }\r
238                 public void setSelection() {\r
239                         setForeground(currentColorset.foregrounds[isSelected?1:0]);\r
240                 }\r
241                 public void setIndicatorColor() {\r
242                         if( co5Value < 0 ) {\r
243                                 indicatorColor = currentColorset.indicators[2];\r
244                         }\r
245                         else if( co5Value > 0 ) {\r
246                                 indicatorColor = currentColorset.indicators[1];\r
247                         }\r
248                         else {\r
249                                 indicatorColor = currentColorset.foregrounds[1];\r
250                         }\r
251                 }\r
252         }\r
253         /**\r
254          * コードボタン\r
255          */\r
256         private class ChordLabel extends JLabel {\r
257                 public byte checkBits = 0;\r
258                 public int co5Value;\r
259                 public boolean isMinor;\r
260                 public boolean isSus4;\r
261                 public boolean isSelected = false;\r
262                 public Chord chord;\r
263 \r
264                 private boolean inActiveZone = true;\r
265                 private Font boldFont;\r
266                 private Font plainFont;\r
267                 private int indicatorColorIndices[] = new int[5];\r
268                 private byte indicatorBits = 0;\r
269 \r
270                 public ChordLabel(Chord chord) {\r
271                         this.chord = chord;\r
272                         isMinor = chord.isSet(Chord.Interval.MINOR);\r
273                         isSus4 = chord.isSet(Chord.Interval.SUS4);\r
274                         co5Value = chord.rootNoteSymbol().toCo5();\r
275                         if( isMinor ) co5Value -= 3;\r
276                         String labelText = ( isSus4 ? chord.symbolSuffix() : chord.toString() );\r
277                         if( isMinor && labelText.length() > 3 ) {\r
278                                 float small_point_size = getFont().getSize2D() - 2;\r
279                                 boldFont = getFont().deriveFont(Font.BOLD, small_point_size);\r
280                                 plainFont = getFont().deriveFont(Font.PLAIN, small_point_size);\r
281                         }\r
282                         else {\r
283                                 boldFont = getFont().deriveFont(Font.BOLD);\r
284                                 plainFont = getFont().deriveFont(Font.PLAIN);\r
285                         }\r
286                         setOpaque(true);\r
287                         setBackground(0);\r
288                         setForeground( currentColorset.foregrounds[0] );\r
289                         setBold(false);\r
290                         setHorizontalAlignment( JLabel.CENTER );\r
291                         setText(labelText);\r
292                         setToolTipText( "Chord: " + chord.toName() );\r
293                 }\r
294                 public void paint(Graphics g) {\r
295                         super.paint(g);\r
296                         Dimension d = getSize();\r
297                         Graphics2D g2 = (Graphics2D) g;\r
298                         Color color = null;\r
299 \r
300                         if( ! inActiveZone ) {\r
301                                 g2.setColor( Color.gray );\r
302                         }\r
303 \r
304                         if( (indicatorBits & 32) != 0 ) {\r
305                                 //\r
306                                 // Draw square  []  with 3rd/sus4th note color\r
307                                 //\r
308                                 if( inActiveZone ) {\r
309                                         color = currentColorset.indicators[indicatorColorIndices[1]];\r
310                                         g2.setColor( color );\r
311                                 }\r
312                                 g2.drawRect( 0, 0, d.width-1, d.height-1 );\r
313                                 g2.drawRect( 2, 2, d.width-5, d.height-5 );\r
314                         }\r
315                         if( (indicatorBits & 1) != 0 ) {\r
316                                 //\r
317                                 // Draw  ||__  with root note color\r
318                                 //\r
319                                 if( inActiveZone ) {\r
320                                         color = currentColorset.indicators[indicatorColorIndices[0]];\r
321                                         g2.setColor( color );\r
322                                 }\r
323                                 g2.drawLine( 0, 0, 0, d.height-1 );\r
324                                 g2.drawLine( 2, 2, 2, d.height-3 );\r
325                         }\r
326                         if( (indicatorBits & 64) != 0 ) {\r
327                                 // Draw bass mark with root note color\r
328                                 //\r
329                                 if( inActiveZone ) {\r
330                                         color = currentColorset.indicators[indicatorColorIndices[0]];\r
331                                         g2.setColor( color );\r
332                                 }\r
333                                 g2.fillRect( 6, d.height-7, d.width-12, 2 );\r
334                         }\r
335                         if( (indicatorBits & 4) != 0 ) {\r
336                                 //\r
337                                 // Draw short  __ii  with parfect 5th color\r
338                                 //\r
339                                 if( inActiveZone ) {\r
340                                         color = currentColorset.indicators[indicatorColorIndices[2]];\r
341                                         g2.setColor( color );\r
342                                 }\r
343                                 g2.drawLine( d.width-1, d.height*3/4, d.width-1, d.height-1 );\r
344                                 g2.drawLine( d.width-3, d.height*3/4, d.width-3, d.height-3 );\r
345                         }\r
346                         if( (indicatorBits & 2) != 0 ) {\r
347                                 //\r
348                                 // Draw  __  with 3rd note color\r
349                                 //\r
350                                 if( inActiveZone ) {\r
351                                         color = currentColorset.indicators[indicatorColorIndices[1]];\r
352                                         g2.setColor( color );\r
353                                 }\r
354                                 g2.drawLine( 0, d.height-1, d.width-1, d.height-1 );\r
355                                 g2.drawLine( 2, d.height-3, d.width-3, d.height-3 );\r
356                         }\r
357                         if( (indicatorBits & 8) != 0 ) {\r
358                                 //\r
359                                 // Draw circle with diminished 5th color\r
360                                 //\r
361                                 if( inActiveZone ) {\r
362                                         g2.setColor( currentColorset.indicators[indicatorColorIndices[3]] );\r
363                                 }\r
364                                 g2.drawOval( 1, 1, d.width-2, d.height-2 );\r
365                         }\r
366                         if( (indicatorBits & 16) != 0 ) {\r
367                                 //\r
368                                 // Draw + with augument 5th color\r
369                                 //\r
370                                 if( inActiveZone ) {\r
371                                         g2.setColor( currentColorset.indicators[indicatorColorIndices[4]] );\r
372                                 }\r
373                                 g2.drawLine( 1, 3, d.width-3, 3 );\r
374                                 g2.drawLine( 1, 4, d.width-3, 4 );\r
375                                 g2.drawLine( d.width/2-1, 0, d.width/2-1, 7 );\r
376                                 g2.drawLine( d.width/2, 0, d.width/2, 7 );\r
377                         }\r
378                 }\r
379                 public void setCheckBit( boolean is_on, int bit_index ) {\r
380                         //\r
381                         // Check bits: x6x43210\r
382                         //   6:BassRoot\r
383                         //   4:Augumented5th, 3:Diminished5th, 2:Parfect5th,\r
384                         //   1:Major3rd/minor3rd/sus4th, 0:Root\r
385                         //\r
386                         byte mask = ((byte)(1<<bit_index));\r
387                         byte old_check_bits = checkBits;\r
388                         if( is_on ) {\r
389                                 checkBits |= mask;\r
390                         }\r
391                         else {\r
392                                 checkBits &= ~mask;\r
393                         }\r
394                         if( old_check_bits == checkBits ) {\r
395                                 // No bits changed\r
396                                 return;\r
397                         }\r
398                         // Indicator bits: x6543210     6:Bass||_  5:[]  4:+  3:O  2:_ii  1:__  0:||_\r
399                         //\r
400                         byte indicator_bits = 0;\r
401                         if( (checkBits & 1) != 0 ) {\r
402                                 if( (checkBits & 7) == 7 ) { // All triad notes appared\r
403                                         //\r
404                                         // Draw square\r
405                                         indicator_bits |= 0x20;\r
406                                         //\r
407                                         // Draw different-colored vertical lines\r
408                                         if( indicatorColorIndices[0] != indicatorColorIndices[1] ) {\r
409                                                 indicator_bits |= 1;\r
410                                         }\r
411                                         if( indicatorColorIndices[2] != indicatorColorIndices[1] ) {\r
412                                                 indicator_bits |= 4;\r
413                                         }\r
414                                 }\r
415                                 else if( !isSus4 ) {\r
416                                         //\r
417                                         // Draw vertical lines  || ii\r
418                                         indicator_bits |= 5;\r
419                                         //\r
420                                         if( (checkBits & 2) != 0 && (!isMinor || (checkBits & 0x18) != 0) ) {\r
421                                                 //\r
422                                                 // Draw horizontal bottom lines __\r
423                                                 indicator_bits |= 2;\r
424                                         }\r
425                                 }\r
426                                 if( !isSus4 ) {\r
427                                         if( isMinor || (checkBits & 2) != 0 ) {\r
428                                                 indicator_bits |= (byte)(checkBits & 0x18);  // Copy bit 3 and bit 4\r
429                                         }\r
430                                         if( (checkBits & 0x40) != 0 ) {\r
431                                                 indicator_bits |= 0x40; // Bass\r
432                                         }\r
433                                 }\r
434                         }\r
435                         if( this.indicatorBits == indicator_bits ) {\r
436                                 // No shapes changed\r
437                                 return;\r
438                         }\r
439                         this.indicatorBits = indicator_bits;\r
440                         repaint();\r
441                 }\r
442                 public void setBackground(int i) {\r
443                         switch( i ) {\r
444                         case  0:\r
445                         case  1:\r
446                         case  2:\r
447                         case  3:\r
448                                 super.setBackground(currentColorset.backgrounds[i]);\r
449                                 setOpaque(true);\r
450                                 break;\r
451                         default: return;\r
452                         }\r
453                 }\r
454                 public void setSelection(boolean is_selected) {\r
455                         this.isSelected = is_selected;\r
456                         setSelection();\r
457                 }\r
458                 public void setSelection() {\r
459                         setForeground(currentColorset.foregrounds[this.isSelected?1:0]);\r
460                 }\r
461                 public void setBold(boolean is_bold) {\r
462                         setFont( is_bold ? boldFont : plainFont );\r
463                 }\r
464                 public void keyChanged() {\r
465                         int co5_key = capoKey.toCo5();\r
466                         int co5_offset = co5Value - co5_key;\r
467                         inActiveZone = (co5_offset <= 6 && co5_offset >= -6) ;\r
468                         int root_note = chord.rootNoteSymbol().toNoteNumber();\r
469                         //\r
470                         // Reconstruct color index\r
471                         //\r
472                         // Root\r
473                         indicatorColorIndices[0] = Music.isOnScale(\r
474                                 root_note, co5_key\r
475                         ) ? 0 : co5_offset > 0 ? 1 : 2;\r
476                         //\r
477                         // 3rd / sus4\r
478                         indicatorColorIndices[1] = Music.isOnScale(\r
479                                 root_note+(isMinor?3:isSus4?5:4), co5_key\r
480                         ) ? 0 : co5_offset > 0 ? 1 : 2;\r
481                         //\r
482                         // P5th\r
483                         indicatorColorIndices[2] = Music.isOnScale(\r
484                                 root_note+7, co5_key\r
485                         ) ? 0 : co5_offset > 0 ? 1 : 2;\r
486                         //\r
487                         // dim5th\r
488                         indicatorColorIndices[3] = Music.isOnScale(\r
489                                 root_note+6, co5_key\r
490                         ) ? 0 : co5_offset > 4 ? 1 : 2;\r
491                         //\r
492                         // aug5th\r
493                         indicatorColorIndices[4] = Music.isOnScale(\r
494                                 root_note+8, co5_key\r
495                         ) ? 0 : co5_offset > -3 ? 1 : 2;\r
496                 }\r
497         }\r
498 \r
499         /**\r
500          * 色セット(ダークモード切替対応)\r
501          */\r
502         public class ColorSet {\r
503                 Color[] focus = new Color[2];   // 0:lost 1:gained\r
504                 Color[] foregrounds = new Color[2];     // 0:unselected 1:selected\r
505                 public Color[] backgrounds = new Color[4]; // 0:remote 1:left 2:local 3:right\r
506                 Color[] indicators = new Color[3];      // 0:natural 1:sharp 2:flat\r
507         }\r
508         public ColorSet normalModeColorset = new ColorSet() {\r
509                 {\r
510                         foregrounds[0] = null;\r
511                         foregrounds[1] = new Color(0xFF,0x3F,0x3F);\r
512                         backgrounds[0] = new Color(0xCF,0xFF,0xCF);\r
513                         backgrounds[1] = new Color(0x9F,0xFF,0xFF);\r
514                         backgrounds[2] = new Color(0xFF,0xCF,0xCF);\r
515                         backgrounds[3] = new Color(0xFF,0xFF,0x9F);\r
516                         indicators[0] = new Color(0xFF,0x3F,0x3F);\r
517                         indicators[1] = new Color(0xCF,0x6F,0x00);\r
518                         indicators[2] = new Color(0x3F,0x3F,0xFF);\r
519                         focus[0] = null;\r
520                         focus[1] = getBackground().darker();\r
521                 }\r
522         };\r
523         public ColorSet darkModeColorset = new ColorSet() {\r
524                 {\r
525                         foregrounds[0] = Color.gray.darker();\r
526                         foregrounds[1] = Color.pink.brighter();\r
527                         backgrounds[0] = Color.black;\r
528                         backgrounds[1] = new Color(0x00,0x18,0x18);\r
529                         backgrounds[2] = new Color(0x20,0x00,0x00);\r
530                         backgrounds[3] = new Color(0x18,0x18,0x00);\r
531                         indicators[0] = Color.pink;\r
532                         indicators[1] = Color.yellow;\r
533                         indicators[2] = Color.cyan;\r
534                         focus[0] = Color.black;\r
535                         focus[1] = getForeground().brighter();\r
536                 }\r
537         };\r
538         private ColorSet currentColorset = normalModeColorset;\r
539 \r
540         /**\r
541          * カポ値選択コンボボックスのデータモデル\r
542          * (コードボタン側とコードダイアグラム側の両方から参照される)\r
543          */\r
544         public ComboBoxModel<Integer> capoValueModel =\r
545                 new DefaultComboBoxModel<Integer>() {\r
546                         {\r
547                                 for( int i=1; i<=Music.SEMITONES_PER_OCTAVE-1; i++ )\r
548                                         addElement(i);\r
549                         }\r
550                 };\r
551         /**\r
552          * カポ値選択コンボボックス(コードボタン側ビュー)\r
553          */\r
554         public CapoSelecterView capoSelecter = new CapoSelecterView(capoValueModel) {\r
555                 private void capoChanged() {\r
556                         ChordMatrix.this.capoChanged(getCapo());\r
557                 }\r
558                 {\r
559                         checkbox.addItemListener(\r
560                                 new ItemListener() {\r
561                                         public void itemStateChanged(ItemEvent e) {capoChanged();}\r
562                                 }\r
563                         );\r
564                         valueSelecter.addActionListener(\r
565                                 new ActionListener() {\r
566                                         public void actionPerformed(ActionEvent e) {capoChanged();}\r
567                                 }\r
568                         );\r
569                 }\r
570         };\r
571 \r
572         /**\r
573          * コードボタンマトリクスの構築\r
574          */\r
575         public ChordMatrix() {\r
576                 int i, v;\r
577                 Dimension buttonSize = new Dimension(28,26);\r
578                 //\r
579                 // Make key-signature labels and chord labels\r
580                 Co5Label l;\r
581                 for (i=0, v= -Music.SEMITONES_PER_OCTAVE; i<N_COLUMNS; i++, v++) {\r
582                         l = new Co5Label(v);\r
583                         l.addMouseListener(this);\r
584                         l.addMouseMotionListener(this);\r
585                         add( keysigLabels[i] = l );\r
586                         l.setPreferredSize(buttonSize);\r
587                 }\r
588                 int row;\r
589                 for (i=0; i < N_COLUMNS * CHORD_BUTTON_ROWS; i++) {\r
590                         row = i / N_COLUMNS;\r
591                         v = i - (N_COLUMNS * row) - 12;\r
592                         Chord chord = new Chord(\r
593                                 new NoteSymbol(row==2 ? v+3 : v)\r
594                         );\r
595                         if( row==0 ) chord.set(Chord.Interval.SUS4);\r
596                         else if( row==2 ) chord.set(Chord.Interval.MINOR);\r
597                         ChordLabel cl = new ChordLabel(chord);\r
598                         cl.addMouseListener(this);\r
599                         cl.addMouseMotionListener(this);\r
600                         cl.addMouseWheelListener(this);\r
601                         add(chordLabels[i] = cl);\r
602                         cl.setPreferredSize(buttonSize);\r
603                 }\r
604                 setFocusable(true);\r
605                 setOpaque(true);\r
606                 addKeyListener(this);\r
607                 addFocusListener(new FocusListener() {\r
608                         public void focusGained(FocusEvent e) {\r
609                                 repaint();\r
610                         }\r
611                         public void focusLost(FocusEvent e) {\r
612                                 selectedChord = selectedChordCapo = null;\r
613                                 fireChordChanged();\r
614                                 repaint();\r
615                         }\r
616                 });\r
617                 setLayout(new GridLayout( 4, N_COLUMNS, 2, 2 ));\r
618                 setKeySignature( new Key() );\r
619                 //\r
620                 // Make chord label selections index\r
621                 //\r
622                 int noteIndex;\r
623                 ArrayList<ChordLabelSelection> al;\r
624                 Chord chord;\r
625                 for( int note_no=0; note_no<chordLabelSelections.length; note_no++ ) {\r
626                         al = new ArrayList<ChordLabelSelection>();\r
627                         //\r
628                         // Root major/minor chords\r
629                         for( ChordLabel cl : chordLabels ) {\r
630                                 if( ! cl.isSus4 && cl.chord.indexOf(note_no) == 0 ) {\r
631                                         al.add(new ChordLabelSelection( cl, 0 )); // Root\r
632                                 }\r
633                         }\r
634                         // Root sus4 chords\r
635                         for( ChordLabel cl : chordLabels ) {\r
636                                 if( cl.isSus4 && cl.chord.indexOf(note_no) == 0 ) {\r
637                                         al.add(new ChordLabelSelection( cl, 0 )); // Root\r
638                                 }\r
639                         }\r
640                         // 3rd,sus4th,5th included chords\r
641                         for( ChordLabel cl : chordLabels ) {\r
642                                 noteIndex = cl.chord.indexOf(note_no);\r
643                                 if( noteIndex == 1 || noteIndex == 2 ) {\r
644                                         al.add(new ChordLabelSelection( cl, noteIndex )); // 3rd,sus4,P5\r
645                                 }\r
646                         }\r
647                         // Diminished chords (major/minor chord button only)\r
648                         for( ChordLabel cl : chordLabels ) {\r
649                                 if( cl.isSus4 ) continue;\r
650                                 (chord = cl.chord.clone()).set(Chord.Interval.FLAT5);\r
651                                 if( chord.indexOf(note_no) == 2 ) {\r
652                                         al.add(new ChordLabelSelection( cl, 3 ));\r
653                                 }\r
654                         }\r
655                         // Augumented chords (major chord button only)\r
656                         for( ChordLabel cl : chordLabels ) {\r
657                                 if( cl.isSus4 || cl.isMinor ) continue;\r
658                                 (chord = cl.chord.clone()).set(Chord.Interval.SHARP5);\r
659                                 if( chord.indexOf(note_no) == 2 ) {\r
660                                         al.add(new ChordLabelSelection( cl, 4 ));\r
661                                 }\r
662                         }\r
663                         chordLabelSelections[note_no] = new ChordLabelSelections(al);\r
664                 }\r
665         }\r
666         //\r
667         // MouseListener\r
668         public void mousePressed(MouseEvent e) {\r
669                 Component obj = e.getComponent();\r
670                 if( obj instanceof ChordLabel ) {\r
671                         ChordLabel cl = (ChordLabel)obj;\r
672                         Chord chord = cl.chord.clone();\r
673                         if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {\r
674                                 if( e.isShiftDown() )\r
675                                         chord.set(Chord.Interval.MAJOR_SEVENTH);\r
676                                 else\r
677                                         chord.set(Chord.Interval.SEVENTH);\r
678                         }\r
679                         else if( e.isShiftDown() )\r
680                                 chord.set(Chord.Interval.SIXTH);\r
681                         if( e.isControlDown() )\r
682                                 chord.set(Chord.Interval.NINTH);\r
683                         else\r
684                                 chord.clear(Chord.OffsetIndex.NINTH);\r
685 \r
686                         if( e.isAltDown() ) {\r
687                                 if( cl.isSus4 ) {\r
688                                         chord.set(Chord.Interval.MAJOR); // To cancel sus4\r
689                                         chord.set(Chord.Interval.SHARP5);\r
690                                 }\r
691                                 else chord.set(Chord.Interval.FLAT5);\r
692                         }\r
693                         if( selectedChordLabel != null ) {\r
694                                 selectedChordLabel.setSelection(false);\r
695                         }\r
696                         (selectedChordLabel = cl).setSelection(true);\r
697                         setSelectedChord(chord);\r
698                 }\r
699                 else if( obj instanceof Co5Label ) {\r
700                         int v = ((Co5Label)obj).co5Value;\r
701                         if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {\r
702                                 setKeySignature( new Key(Music.oppositeCo5(v)) );\r
703                         }\r
704                         else if ( v == key.toCo5() ) {\r
705                                 //\r
706                                 // Cancel selected chord\r
707                                 //\r
708                                 setSelectedChord( (Chord)null );\r
709                         }\r
710                         else {\r
711                                 // Change key\r
712                                 setKeySignature( new Key(v) );\r
713                         }\r
714                 }\r
715                 requestFocusInWindow();\r
716                 repaint();\r
717         }\r
718         public void mouseReleased(MouseEvent e) { destinationChordLabel = null; }\r
719         public void mouseEntered(MouseEvent e) { }\r
720         public void mouseExited(MouseEvent e) { }\r
721         public void mouseClicked(MouseEvent e) { }\r
722         public void mouseDragged(MouseEvent e) {\r
723                 Component obj = e.getComponent();\r
724                 if( obj instanceof ChordLabel ) {\r
725                         ChordLabel l_src = (ChordLabel)obj;\r
726                         Component obj2 = this.getComponentAt(\r
727                                 l_src.getX() + e.getX(),\r
728                                 l_src.getY() + e.getY()\r
729                         );\r
730                         if( obj2 == this ) {\r
731                                 //\r
732                                 // Entered gap between chord buttons - do nothing\r
733                                 //\r
734                                 return;\r
735                         }\r
736                         ChordLabel l_dst =\r
737                                 ( (obj2 instanceof ChordLabel ) ? (ChordLabel)obj2 : null );\r
738                         if( l_dst == l_src ) {\r
739                                 //\r
740                                 // Returned to original chord button\r
741                                 //\r
742                                 destinationChordLabel = null;\r
743                                 return;\r
744                         }\r
745                         if( destinationChordLabel != null ) {\r
746                                 //\r
747                                 // Already touched another chord button\r
748                                 //\r
749                                 return;\r
750                         }\r
751                         Chord chord = l_src.chord.clone();\r
752                         if( l_src.isMinor ) {\r
753                                 if( l_dst == null ) { // Out of chord buttons\r
754                                         // mM7\r
755                                         chord.set(Chord.Interval.MAJOR_SEVENTH);\r
756                                 }\r
757                                 else if( l_src.co5Value < l_dst.co5Value ) { // Right\r
758                                         // m6\r
759                                         chord.set(Chord.Interval.SIXTH);\r
760                                 }\r
761                                 else { // Left or up from minor to major\r
762                                         // m7\r
763                                         chord.set(Chord.Interval.SEVENTH);\r
764                                 }\r
765                         }\r
766                         else if( l_src.isSus4 ) {\r
767                                 if( l_dst == null ) { // Out of chord buttons\r
768                                         return;\r
769                                 }\r
770                                 else if( ! l_dst.isSus4 ) { // Down from sus4 to major\r
771                                         chord.set(Chord.Interval.MAJOR);\r
772                                 }\r
773                                 else if( l_src.co5Value < l_dst.co5Value ) { // Right\r
774                                         chord.set(Chord.Interval.NINTH);\r
775                                 }\r
776                                 else { // Left\r
777                                         // 7sus4\r
778                                         chord.set(Chord.Interval.SEVENTH);\r
779                                 }\r
780                         }\r
781                         else {\r
782                                 if( l_dst == null ) { // Out of chord buttons\r
783                                         return;\r
784                                 }\r
785                                 else if( l_dst.isSus4 ) { // Up from major to sus4\r
786                                         chord.set(Chord.Interval.NINTH);\r
787                                 }\r
788                                 else if( l_src.co5Value < l_dst.co5Value ) { // Right\r
789                                         // M7\r
790                                         chord.set(Chord.Interval.MAJOR_SEVENTH);\r
791                                 }\r
792                                 else if( l_dst.isMinor ) { // Down from major to minor\r
793                                         // 6\r
794                                         chord.set(Chord.Interval.SIXTH);\r
795                                 }\r
796                                 else { // Left\r
797                                         // 7\r
798                                         chord.set(Chord.Interval.SEVENTH);\r
799                                 }\r
800                         }\r
801                         if( chord.isSet(Chord.OffsetIndex.NINTH) || (l_src.isSus4 && (l_dst == null || ! l_dst.isSus4) ) ) {\r
802                                 if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {\r
803                                         if( e.isShiftDown() ) {\r
804                                                 chord.set(Chord.Interval.MAJOR_SEVENTH);\r
805                                         }\r
806                                         else {\r
807                                                 chord.set(Chord.Interval.SEVENTH);\r
808                                         }\r
809                                 }\r
810                                 else if( e.isShiftDown() ) {\r
811                                         chord.set(Chord.Interval.SIXTH);\r
812                                 }\r
813                         }\r
814                         else {\r
815                                 if( e.isControlDown() )\r
816                                         chord.set(Chord.Interval.NINTH);\r
817                                 else\r
818                                         chord.clear(Chord.OffsetIndex.NINTH);\r
819                         }\r
820                         if( e.isAltDown() ) {\r
821                                 if( l_src.isSus4 ) {\r
822                                         chord.set(Chord.Interval.MAJOR);\r
823                                         chord.set(Chord.Interval.SHARP5);\r
824                                 }\r
825                                 else {\r
826                                         chord.set(Chord.Interval.FLAT5);\r
827                                 }\r
828                         }\r
829                         setSelectedChord(chord);\r
830                         destinationChordLabel = (l_dst == null ? l_src : l_dst ) ;\r
831                 }\r
832                 else if( obj instanceof Co5Label ) {\r
833                         Co5Label l_src = (Co5Label)obj;\r
834                         Component obj2 = this.getComponentAt(\r
835                                 l_src.getX() + e.getX(),\r
836                                 l_src.getY() + e.getY()\r
837                         );\r
838                         if( !(obj2 instanceof Co5Label) ) {\r
839                                 return;\r
840                         }\r
841                         Co5Label l_dst = (Co5Label)obj2;\r
842                         int v = l_dst.co5Value;\r
843                         if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {\r
844                                 setKeySignature( new Key(Music.oppositeCo5(v)) );\r
845                         }\r
846                         else {\r
847                                 setKeySignature( new Key(v) );\r
848                         }\r
849                         repaint();\r
850                 }\r
851         }\r
852         public void mouseMoved(MouseEvent e) { }\r
853         public void mouseWheelMoved(MouseWheelEvent e) {\r
854                 if( selectedChord != null ) {\r
855                         if( e.getWheelRotation() > 0 ) { // Wheel moved down\r
856                                 if( --selectedNoteIndex < 0 ) {\r
857                                         selectedNoteIndex = selectedChord.numberOfNotes() - 1;\r
858                                 }\r
859                         }\r
860                         else { // Wheel moved up\r
861                                 if( ++selectedNoteIndex >= selectedChord.numberOfNotes() ) {\r
862                                         selectedNoteIndex = 0;\r
863                                 }\r
864                         }\r
865                         fireChordChanged();\r
866                 }\r
867         }\r
868         private Chord.Interval pcKeyNextShift7;\r
869         public void keyPressed(KeyEvent e) {\r
870                 int i = -1, i_col = -1, i_row = 1;\r
871                 boolean shiftPressed = false; // True if Shift-key pressed or CapsLocked\r
872                 char keyChar = e.getKeyChar();\r
873                 int keyCode = e.getKeyCode();\r
874                 ChordLabel cl = null;\r
875                 Chord chord = null;\r
876                 int key_co5 = key.toCo5();\r
877                 // System.out.println( keyChar + " Pressed on chord matrix" );\r
878                 //\r
879                 if( (i = "6 ".indexOf(keyChar)) >= 0 ) {\r
880                         selectedChord = selectedChordCapo = null;\r
881                         fireChordChanged();\r
882                         pcKeyNextShift7 = null;\r
883                         return;\r
884                 }\r
885                 else if( (i = "asdfghjkl;:]".indexOf(keyChar)) >= 0 ) {\r
886                         i_col = i + key_co5 + 7;\r
887                 }\r
888                 else if( (i = "ASDFGHJKL+*}".indexOf(keyChar)) >= 0 ) {\r
889                         i_col = i + key_co5 + 7;\r
890                         shiftPressed = true;\r
891                 }\r
892                 else if( (i = "zxcvbnm,./\\".indexOf(keyChar)) >=0 ) {\r
893                         i_col = i + key_co5 + 7;\r
894                         i_row = 2;\r
895                 }\r
896                 else if( (i = "ZXCVBNM<>?_".indexOf(keyChar)) >=0 ) {\r
897                         i_col = i + key_co5 + 7;\r
898                         i_row = 2;\r
899                         shiftPressed = true;\r
900                 }\r
901                 else if( (i = "qwertyuiop@[".indexOf(keyChar)) >= 0 ) {\r
902                         i_col = i + key_co5 + 7;\r
903                         i_row = 0;\r
904                 }\r
905                 else if( (i = "QWERTYUIOP`{".indexOf(keyChar)) >= 0 ) {\r
906                         i_col = i + key_co5 + 7;\r
907                         i_row = 0;\r
908                         shiftPressed = true;\r
909                 }\r
910                 else if( keyChar == '5' ) {\r
911                         pcKeyNextShift7 = Chord.Interval.MAJOR_SEVENTH; return;\r
912                 }\r
913                 else if( keyChar == '7' ) {\r
914                         pcKeyNextShift7 = Chord.Interval.SEVENTH; return;\r
915                 }\r
916                 // Shift current key-signature\r
917                 else if( keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_KP_LEFT ) {\r
918                         // Add a flat\r
919                         setKeySignature( new Key(key_co5-1) );\r
920                         return;\r
921                 }\r
922                 else if( keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_KP_RIGHT ) {\r
923                         // Add a sharp\r
924                         setKeySignature( new Key(key_co5+1) );\r
925                         return;\r
926                 }\r
927                 else if( keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_KP_DOWN ) {\r
928                         // Semitone down\r
929                         Key key = new Key(key_co5);\r
930                         key.transpose(-1);\r
931                         setKeySignature(key);\r
932                         return;\r
933                 }\r
934                 else if( keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_KP_UP ) {\r
935                         // Semitone up\r
936                         Key key = new Key(key_co5);\r
937                         key.transpose(1);\r
938                         setKeySignature(key);\r
939                         return;\r
940                 }\r
941                 if( i < 0 ) // No key char found\r
942                         return;\r
943                 if( i_col < 0 ) i_col += 12; else if( i_col > N_COLUMNS ) i_col -= 12;\r
944                 cl = chordLabels[i_col + N_COLUMNS * i_row];\r
945                 chord = cl.chord.clone();\r
946                 if( shiftPressed ) {\r
947                         chord.set(Chord.Interval.SEVENTH);\r
948                 }\r
949                 // specify by previous key\r
950                 else if( pcKeyNextShift7 == null ) {\r
951                         chord.clear(Chord.OffsetIndex.SEVENTH);\r
952                 }\r
953                 else {\r
954                         chord.set(pcKeyNextShift7);\r
955                 }\r
956                 if( e.isAltDown() ) {\r
957                         if( cl.isSus4 ) {\r
958                                 chord.set(Chord.Interval.MAJOR); // To cancel sus4\r
959                                 chord.set(Chord.Interval.SHARP5);\r
960                         }\r
961                         else {\r
962                                 chord.set(Chord.Interval.FLAT5);\r
963                         }\r
964                 }\r
965                 if( e.isControlDown() ) { // Cannot use for ninth ?\r
966                         chord.set(Chord.Interval.NINTH);\r
967                 }\r
968                 if( selectedChordLabel != null ) clear();\r
969                 (selectedChordLabel = cl).setSelection(true);\r
970                 setSelectedChord(chord);\r
971                 pcKeyNextShift7 = null;\r
972                 return;\r
973         }\r
974         public void keyReleased(KeyEvent e) { }\r
975         public void keyTyped(KeyEvent e) { }\r
976 \r
977         public void addChordMatrixListener(ChordMatrixListener l) {\r
978                 listenerList.add(ChordMatrixListener.class, l);\r
979         }\r
980         public void removeChordMatrixListener(ChordMatrixListener l) {\r
981                 listenerList.remove(ChordMatrixListener.class, l);\r
982         }\r
983         protected void fireChordChanged() {\r
984                 Object[] listeners = listenerList.getListenerList();\r
985                 for (int i = listeners.length-2; i>=0; i-=2) {\r
986                         if (listeners[i]==ChordMatrixListener.class) {\r
987                                 ((ChordMatrixListener)listeners[i+1]).chordChanged();\r
988                         }\r
989                 }\r
990                 if( selectedChord == null ) clearIndicators();\r
991         }\r
992         public void fireKeySignatureChanged() {\r
993                 Object[] listeners = listenerList.getListenerList();\r
994                 for (int i = listeners.length-2; i>=0; i-=2) {\r
995                         if (listeners[i]==ChordMatrixListener.class) {\r
996                                 ((ChordMatrixListener)listeners[i+1]).keySignatureChanged();\r
997                         }\r
998                 }\r
999         }\r
1000         private Key key = null;\r
1001         private Key capoKey = null;\r
1002         public Key getKeySignature() { return key; }\r
1003         public Key getKeySignatureCapo() { return capoKey; }\r
1004         public void setKeySignature( Key key ) {\r
1005                 if( key == null || this.key != null && key.equals(this.key) )\r
1006                         return;\r
1007                 int i;\r
1008                 // Clear old value\r
1009                 if( this.key == null ) {\r
1010                         for( i = 0; i < keysigLabels.length; i++ ) {\r
1011                                 keysigLabels[i].setBackground(false);\r
1012                         }\r
1013                 }\r
1014                 else {\r
1015                         keysigLabels[this.key.toCo5() + 12].setSelection(false);\r
1016                         for( i = Music.mod12(this.key.toCo5()); i < N_COLUMNS; i+=12 ) {\r
1017                                 keysigLabels[i].setBackground(false);\r
1018                         }\r
1019                 }\r
1020                 // Set new value\r
1021                 keysigLabels[i = key.toCo5() + 12].setSelection(true);\r
1022                 for( i = Music.mod12(key.toCo5()); i < N_COLUMNS; i+=12 ) {\r
1023                         keysigLabels[i].setBackground(true);\r
1024                 }\r
1025                 // Change chord-label's color & font\r
1026                 int i_color, old_i_color;\r
1027                 for( ChordLabel cl : chordLabels ) {\r
1028                         i_color = ((cl.co5Value - key.toCo5() + 31)/3) & 3;\r
1029                         if( this.key != null ) {\r
1030                                 old_i_color = ((cl.co5Value - this.key.toCo5() + 31)/3) & 3;\r
1031                                 if( i_color != old_i_color ) {\r
1032                                         cl.setBackground(i_color);\r
1033                                 }\r
1034                         }\r
1035                         else cl.setBackground(i_color);\r
1036                         if( !(cl.isSus4) ) {\r
1037                                 if( this.key != null && Music.mod12(cl.co5Value - this.key.toCo5()) == 0)\r
1038                                         cl.setBold(false);\r
1039                                 if( Music.mod12( cl.co5Value - key.toCo5() ) == 0 )\r
1040                                         cl.setBold(true);\r
1041                         }\r
1042                 }\r
1043                 this.capoKey = (this.key = key).clone().transpose(capoSelecter.getCapo());\r
1044                 for( ChordLabel cl : chordLabels ) cl.keyChanged();\r
1045                 fireKeySignatureChanged();\r
1046         }\r
1047         private int capo = 0;\r
1048         /**\r
1049          * カポ位置の変更処理\r
1050          * @param newCapo 新しいカポ位置\r
1051          */\r
1052         protected void capoChanged(int newCapo) {\r
1053                 if( this.capo == newCapo )\r
1054                         return;\r
1055                 (this.capoKey = this.key.clone()).transpose(this.capo = newCapo);\r
1056                 selectedChordCapo = (\r
1057                         selectedChord == null ? null : selectedChord.clone().transpose(newCapo)\r
1058                 );\r
1059                 for( ChordLabel cl : chordLabels ) cl.keyChanged();\r
1060                 fireKeySignatureChanged();\r
1061         }\r
1062 \r
1063         /**\r
1064          * コードサフィックスのヘルプ\r
1065          */\r
1066         public ChordGuide chordGuide = new ChordGuide(this);\r
1067 \r
1068         /**\r
1069          * ドラッグ先コードボタン\r
1070          */\r
1071         private ChordLabel      destinationChordLabel = null;\r
1072         /**\r
1073          * ドラッグされたかどうか調べます。\r
1074          * @return ドラッグ先コードボタンがあればtrue\r
1075          */\r
1076         public boolean isDragged() {\r
1077                 return destinationChordLabel != null ;\r
1078         }\r
1079 \r
1080         private boolean isDark = false;\r
1081         public void setDarkMode(boolean is_dark) {\r
1082                 this.isDark = is_dark;\r
1083                 currentColorset = (is_dark ? darkModeColorset : normalModeColorset);\r
1084                 setBackground( currentColorset.focus[0] );\r
1085                 Key prev_key = key;\r
1086                 key = null;\r
1087                 setKeySignature(prev_key);\r
1088                 for( int i=0; i < keysigLabels.length; i++ ) keysigLabels[i].setSelection();\r
1089                 for( int i=0; i <  chordLabels.length; i++ ) chordLabels[i].setSelection();\r
1090                 chordGuide.setDarkMode( is_dark );\r
1091                 chordDisplay.setDarkMode( is_dark );\r
1092                 Color col = is_dark ? Color.black : null;\r
1093                 capoSelecter.setBackground( col );\r
1094                 capoSelecter.valueSelecter.setBackground( col );\r
1095         }\r
1096 \r
1097         private boolean isPlaying = false;\r
1098         public boolean isPlaying() { return isPlaying; }\r
1099         public void setPlaying(boolean is_playing) {\r
1100                 this.isPlaying = is_playing;\r
1101                 repaint();\r
1102         }\r
1103 \r
1104         private byte currentBeat = 0;\r
1105         private byte timesigUpper = 4;\r
1106         public void setBeat(SequenceTickIndex sequenceTickIndex) {\r
1107                 byte beat = (byte)(sequenceTickIndex.lastBeat);\r
1108                 byte tsu = sequenceTickIndex.timesigUpper;\r
1109                 if( currentBeat == beat && timesigUpper == tsu )\r
1110                         return;\r
1111                 timesigUpper = tsu;\r
1112                 currentBeat = beat;\r
1113                 keysigLabels[ key.toCo5() + 12 ].repaint();\r
1114         }\r
1115 \r
1116         private ChordLabel      selectedChordLabel = null;\r
1117         public JComponent getSelectedButton() {\r
1118                 return selectedChordLabel;\r
1119         }\r
1120         private Chord   selectedChord = null;\r
1121         public Chord getSelectedChord() {\r
1122                 return selectedChord;\r
1123         }\r
1124         private Chord   selectedChordCapo = null;\r
1125         public Chord getSelectedChordCapo() {\r
1126                 return selectedChordCapo;\r
1127         }\r
1128         public void setSelectedChordCapo( Chord chord ) {\r
1129                 setNoteIndex(-1); // Cancel arpeggio mode\r
1130                 selectedChord = (chord == null ? null : chord.clone().transpose(-capo,capoKey));\r
1131                 selectedChordCapo = chord;\r
1132                 fireChordChanged();\r
1133         }\r
1134         public void setSelectedChord( Chord chord ) {\r
1135                 setNoteIndex(-1); // Cancel arpeggio mode\r
1136                 selectedChord = chord;\r
1137                 selectedChordCapo = (chord == null ? null : chord.clone().transpose(capo,key));\r
1138                 fireChordChanged();\r
1139         }\r
1140         /**\r
1141          * コードを文字列で設定します。\r
1142          * @param chordSymbol コード名\r
1143          */\r
1144         public void setSelectedChord(String chordSymbol) throws IllegalArgumentException {\r
1145                 Chord chord = null;\r
1146                 if( chordSymbol != null && ! chordSymbol.isEmpty() ) {\r
1147                         try {\r
1148                                 chord = new Chord(chordSymbol);\r
1149                         } catch( IllegalArgumentException e ) {\r
1150                                 JOptionPane.showMessageDialog(\r
1151                                         null, e.getMessage(), "Input error",\r
1152                                         JOptionPane.ERROR_MESSAGE\r
1153                                 );\r
1154                                 return;\r
1155                         }\r
1156                 }\r
1157                 setSelectedChord(chord);\r
1158         }\r
1159 \r
1160         private int selectedNoteIndex = -1;\r
1161         public int getNoteIndex() {\r
1162                 return selectedChord == null || selectedNoteIndex < 0 ? -1 : selectedNoteIndex;\r
1163         }\r
1164         public void setNoteIndex(int noteIndex) {\r
1165                 selectedNoteIndex = noteIndex;\r
1166         }\r
1167         public void clear() {\r
1168                 if( selectedChordLabel != null ) {\r
1169                         selectedChordLabel.setSelection(false);\r
1170                         selectedChordLabel = null;\r
1171                 }\r
1172                 selectedChord = null; selectedNoteIndex = -1;\r
1173         }\r
1174 \r
1175 }\r