OSDN Git Service

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