1 package camidion.chordhelper.chordmatrix;
4 import java.awt.Component;
5 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.Graphics2D;
9 import java.awt.GridLayout;
10 import java.awt.event.ActionEvent;
11 import java.awt.event.ActionListener;
12 import java.awt.event.FocusEvent;
13 import java.awt.event.FocusListener;
14 import java.awt.event.InputEvent;
15 import java.awt.event.ItemEvent;
16 import java.awt.event.ItemListener;
17 import java.awt.event.KeyEvent;
18 import java.awt.event.KeyListener;
19 import java.awt.event.MouseEvent;
20 import java.awt.event.MouseListener;
21 import java.awt.event.MouseMotionListener;
22 import java.awt.event.MouseWheelEvent;
23 import java.awt.event.MouseWheelListener;
24 import java.util.ArrayList;
26 import javax.swing.JComponent;
27 import javax.swing.JLabel;
28 import javax.swing.JOptionPane;
29 import javax.swing.JPanel;
31 import camidion.chordhelper.ButtonIcon;
32 import camidion.chordhelper.ChordDisplayLabel;
33 import camidion.chordhelper.chorddiagram.CapoComboBoxModel;
34 import camidion.chordhelper.chorddiagram.CapoSelecterView;
35 import camidion.chordhelper.midieditor.SequenceTickIndex;
36 import camidion.chordhelper.music.Chord;
37 import camidion.chordhelper.music.Key;
38 import camidion.chordhelper.music.Music;
39 import camidion.chordhelper.music.NoteSymbol;
40 import camidion.chordhelper.music.NoteSymbolLanguage;
43 * MIDI Chord Helper 用のコードボタンマトリクス
46 * Copyright (C) 2004-2016 Akiyoshi Kamide
47 * http://www.yk.rim.or.jp/~kamide/music/chordhelper/
49 public class ChordMatrix extends JPanel
50 implements MouseListener, KeyListener, MouseMotionListener, MouseWheelListener
55 public static final int N_COLUMNS = Music.SEMITONES_PER_OCTAVE * 2 + 1;
59 public static final int CHORD_BUTTON_ROWS = 3;
63 public Co5Label keysigLabels[] = new Co5Label[ N_COLUMNS ];
67 public ChordLabel chordLabels[] = new ChordLabel[N_COLUMNS * CHORD_BUTTON_ROWS];
71 public ChordDisplayLabel chordDisplay = new ChordDisplayLabel("Chord Pad", this, null);
73 private static class ChordLabelSelection {
74 ChordLabel chordLabel;
77 public ChordLabelSelection(ChordLabel chordLabel, int bitIndex) {
78 this.chordLabel = chordLabel;
79 this.bitIndex = bitIndex;
80 this.isSus4 = chordLabel.isSus4;
82 public void setCheckBit(boolean isOn) {
83 chordLabel.setCheckBit(isOn, bitIndex);
85 public boolean setBassCheckBit(boolean isOn) {
86 if( bitIndex == 0 && ! isSus4 ) {
87 chordLabel.setCheckBit(isOn, 6);
93 private static class ChordLabelSelections {
96 boolean is_active = false;
97 boolean is_bass_active = false;
98 private ChordLabelSelection acls[];
99 public ChordLabelSelections(ArrayList<ChordLabelSelection> al) {
100 acls = al.toArray(new ChordLabelSelection[al.size()]);
102 void addWeight(int weight_diff) {
103 if( (weight += weight_diff) < 0 ) weight = 0;
104 if( (weight > 0) != is_active ) {
105 is_active = !is_active;
106 for( ChordLabelSelection cls : acls ) {
107 cls.setCheckBit(is_active);
111 void addBassWeight(int weight_diff) {
112 if( (bass_weight += weight_diff) < 0 ) bass_weight = 0;
113 if( (bass_weight > 0) != is_bass_active ) {
114 is_bass_active = !is_bass_active;
115 for( ChordLabelSelection cls : acls ) {
116 if( ! cls.setBassCheckBit(is_bass_active) ) {
117 // No more root major/minor
122 addWeight(weight_diff);
125 weight = bass_weight = 0;
126 is_active = is_bass_active = false;
127 for( ChordLabelSelection cls : acls ) {
128 cls.setCheckBit(false);
129 cls.setBassCheckBit(false);
133 private ChordLabelSelections chordLabelSelections[] =
134 new ChordLabelSelections[Music.SEMITONES_PER_OCTAVE];
138 public void clearIndicators() {
139 for( int i=0; i<chordLabelSelections.length; i++ ) {
140 chordLabelSelections[i].clearWeight();
145 * MIDIのノートイベント(ON/OFF)を受け取ります。
146 * @param isNoteOn ONのときtrue
147 * @param noteNumber ノート番号
149 public void note(boolean isNoteOn, int noteNumber) {
150 int weightDiff = (isNoteOn ? 1 : -1);
151 ChordLabelSelections cls = chordLabelSelections[Music.mod12(noteNumber)];
152 if( noteNumber < 49 )
153 cls.addBassWeight(weightDiff);
155 cls.addWeight(weightDiff);
161 private class Co5Label extends JLabel {
162 public boolean isSelected = false;
163 public int co5Value = 0;
164 private Color indicatorColor;
165 public Co5Label(int v) {
166 Key key = new Key(co5Value = v);
168 setBackground(false);
169 setForeground( currentColorset.foregrounds[0] );
170 setHorizontalAlignment( JLabel.CENTER );
171 String tip = "Key signature: ";
172 if( v != key.toCo5() ) {
173 tip += "out of range" ;
176 tip += key.signatureDescription() + " " +
177 key.toStringIn(NoteSymbolLanguage.IN_JAPANESE);
179 setIcon(new ButtonIcon(ButtonIcon.NATURAL_ICON));
182 setFont( getFont().deriveFont(Font.PLAIN) );
183 setText( key.signature() );
188 public void paint(Graphics g) {
190 Dimension d = getSize();
191 if( ChordMatrix.this.isFocusOwner() && isSelected ) {
192 g.setColor( currentColorset.focus[1] );
193 g.drawRect( 0, 0, d.width-1, d.height-1 );
195 if( !isSelected || !isPlaying || currentBeat+1 == timesigUpper ) {
198 if( currentBeat == 0 ) {
199 g.setColor( indicatorColor );
200 g.drawRect( 2, 2, d.width-5, d.height-5 );
201 g.setColor( isDark ? indicatorColor.darker() : indicatorColor.brighter() );
202 g.drawRect( 0, 0, d.width-1, d.height-1 );
205 Color color = currentColorset.indicators[0];
207 if( currentBeat == 1 ) {
210 g.drawLine( 2, d.height-3, d.width-3, d.height-3 );
211 g.drawLine( d.width-3, d.height*3/4, d.width-3, d.height-3 );
212 g.drawLine( 2, 2, 2, d.height-3 );
213 g.setColor( isDark ? color.darker() : color.brighter() );
214 g.drawLine( 0, d.height-1, d.width-1, d.height-1 );
215 g.drawLine( d.width-1, d.height*3/4, d.width-1, d.height-1 );
216 g.drawLine( 0, 0, 0, d.height-1 );
222 int vertical_top = (d.height-1) * (currentBeat-1) / (timesigUpper-2) ;
223 g.drawLine( 2, vertical_top == 0 ? 2 : vertical_top, 2, d.height-3 );
224 g.setColor( isDark ? color.darker() : color.brighter() );
225 g.drawLine( 0, vertical_top, 0, d.height-1 );
228 public void setBackground(boolean isActive) {
229 super.setBackground(currentColorset.backgrounds[isActive?2:0]);
233 public void setSelection(boolean isSelected) {
234 this.isSelected = isSelected;
237 public void setSelection() {
238 setForeground(currentColorset.foregrounds[isSelected?1:0]);
240 public void setIndicatorColor() {
242 indicatorColor = currentColorset.indicators[2];
244 else if( co5Value > 0 ) {
245 indicatorColor = currentColorset.indicators[1];
248 indicatorColor = currentColorset.foregrounds[1];
255 private class ChordLabel extends JLabel {
256 public byte checkBits = 0;
258 public boolean isMinor;
259 public boolean isSus4;
260 public boolean isSelected = false;
263 private boolean inActiveZone = true;
264 private Font boldFont;
265 private Font plainFont;
266 private int indicatorColorIndices[] = new int[5];
267 private byte indicatorBits = 0;
269 public ChordLabel(Chord chord) {
271 isMinor = chord.isSet(Chord.Interval.MINOR);
272 isSus4 = chord.isSet(Chord.Interval.SUS4);
273 co5Value = chord.rootNoteSymbol().toCo5();
274 if( isMinor ) co5Value -= 3;
275 String labelText = ( isSus4 ? chord.symbolSuffix() : chord.toString() );
276 if( isMinor && labelText.length() > 3 ) {
277 float small_point_size = getFont().getSize2D() - 2;
278 boldFont = getFont().deriveFont(Font.BOLD, small_point_size);
279 plainFont = getFont().deriveFont(Font.PLAIN, small_point_size);
282 boldFont = getFont().deriveFont(Font.BOLD);
283 plainFont = getFont().deriveFont(Font.PLAIN);
287 setForeground( currentColorset.foregrounds[0] );
289 setHorizontalAlignment( JLabel.CENTER );
291 setToolTipText( "Chord: " + chord.toName() );
293 public void paint(Graphics g) {
295 Dimension d = getSize();
296 Graphics2D g2 = (Graphics2D) g;
299 if( ! inActiveZone ) {
300 g2.setColor( Color.gray );
303 if( (indicatorBits & 32) != 0 ) {
305 // Draw square [] with 3rd/sus4th note color
308 color = currentColorset.indicators[indicatorColorIndices[1]];
309 g2.setColor( color );
311 g2.drawRect( 0, 0, d.width-1, d.height-1 );
312 g2.drawRect( 2, 2, d.width-5, d.height-5 );
314 if( (indicatorBits & 1) != 0 ) {
316 // Draw ||__ with root note color
319 color = currentColorset.indicators[indicatorColorIndices[0]];
320 g2.setColor( color );
322 g2.drawLine( 0, 0, 0, d.height-1 );
323 g2.drawLine( 2, 2, 2, d.height-3 );
325 if( (indicatorBits & 64) != 0 ) {
326 // Draw bass mark with root note color
329 color = currentColorset.indicators[indicatorColorIndices[0]];
330 g2.setColor( color );
332 g2.fillRect( 6, d.height-7, d.width-12, 2 );
334 if( (indicatorBits & 4) != 0 ) {
336 // Draw short __ii with parfect 5th color
339 color = currentColorset.indicators[indicatorColorIndices[2]];
340 g2.setColor( color );
342 g2.drawLine( d.width-1, d.height*3/4, d.width-1, d.height-1 );
343 g2.drawLine( d.width-3, d.height*3/4, d.width-3, d.height-3 );
345 if( (indicatorBits & 2) != 0 ) {
347 // Draw __ with 3rd note color
350 color = currentColorset.indicators[indicatorColorIndices[1]];
351 g2.setColor( color );
353 g2.drawLine( 0, d.height-1, d.width-1, d.height-1 );
354 g2.drawLine( 2, d.height-3, d.width-3, d.height-3 );
356 if( (indicatorBits & 8) != 0 ) {
358 // Draw circle with diminished 5th color
361 g2.setColor( currentColorset.indicators[indicatorColorIndices[3]] );
363 g2.drawOval( 1, 1, d.width-2, d.height-2 );
365 if( (indicatorBits & 16) != 0 ) {
367 // Draw + with augument 5th color
370 g2.setColor( currentColorset.indicators[indicatorColorIndices[4]] );
372 g2.drawLine( 1, 3, d.width-3, 3 );
373 g2.drawLine( 1, 4, d.width-3, 4 );
374 g2.drawLine( d.width/2-1, 0, d.width/2-1, 7 );
375 g2.drawLine( d.width/2, 0, d.width/2, 7 );
378 public void setCheckBit( boolean is_on, int bit_index ) {
380 // Check bits: x6x43210
382 // 4:Augumented5th, 3:Diminished5th, 2:Parfect5th,
383 // 1:Major3rd/minor3rd/sus4th, 0:Root
385 byte mask = ((byte)(1<<bit_index));
386 byte old_check_bits = checkBits;
393 if( old_check_bits == checkBits ) {
397 // Indicator bits: x6543210 6:Bass||_ 5:[] 4:+ 3:O 2:_ii 1:__ 0:||_
399 byte indicator_bits = 0;
400 if( (checkBits & 1) != 0 ) {
401 if( (checkBits & 7) == 7 ) { // All triad notes appared
404 indicator_bits |= 0x20;
406 // Draw different-colored vertical lines
407 if( indicatorColorIndices[0] != indicatorColorIndices[1] ) {
410 if( indicatorColorIndices[2] != indicatorColorIndices[1] ) {
416 // Draw vertical lines || ii
419 if( (checkBits & 2) != 0 && (!isMinor || (checkBits & 0x18) != 0) ) {
421 // Draw horizontal bottom lines __
426 if( isMinor || (checkBits & 2) != 0 ) {
427 indicator_bits |= (byte)(checkBits & 0x18); // Copy bit 3 and bit 4
429 if( (checkBits & 0x40) != 0 ) {
430 indicator_bits |= 0x40; // Bass
434 if( this.indicatorBits == indicator_bits ) {
438 this.indicatorBits = indicator_bits;
441 public void setBackground(int i) {
447 super.setBackground(currentColorset.backgrounds[i]);
453 public void setSelection(boolean is_selected) {
454 this.isSelected = is_selected;
457 public void setSelection() {
458 setForeground(currentColorset.foregrounds[this.isSelected?1:0]);
460 public void setBold(boolean is_bold) {
461 setFont( is_bold ? boldFont : plainFont );
463 public void keyChanged() {
464 int co5_key = capoKey.toCo5();
465 int co5_offset = co5Value - co5_key;
466 inActiveZone = (co5_offset <= 6 && co5_offset >= -6) ;
467 int root_note = chord.rootNoteSymbol().toNoteNumber();
469 // Reconstruct color index
472 indicatorColorIndices[0] = Music.isOnScale(
474 ) ? 0 : co5_offset > 0 ? 1 : 2;
477 indicatorColorIndices[1] = Music.isOnScale(
478 root_note+(isMinor?3:isSus4?5:4), co5_key
479 ) ? 0 : co5_offset > 0 ? 1 : 2;
482 indicatorColorIndices[2] = Music.isOnScale(
484 ) ? 0 : co5_offset > 0 ? 1 : 2;
487 indicatorColorIndices[3] = Music.isOnScale(
489 ) ? 0 : co5_offset > 4 ? 1 : 2;
492 indicatorColorIndices[4] = Music.isOnScale(
494 ) ? 0 : co5_offset > -3 ? 1 : 2;
501 public class ColorSet {
502 Color[] focus = new Color[2]; // 0:lost 1:gained
503 Color[] foregrounds = new Color[2]; // 0:unselected 1:selected
504 public Color[] backgrounds = new Color[4]; // 0:remote 1:left 2:local 3:right
505 Color[] indicators = new Color[3]; // 0:natural 1:sharp 2:flat
507 public ColorSet normalModeColorset = new ColorSet() {
509 foregrounds[0] = null;
510 foregrounds[1] = new Color(0xFF,0x3F,0x3F);
511 backgrounds[0] = new Color(0xCF,0xFF,0xCF);
512 backgrounds[1] = new Color(0x9F,0xFF,0xFF);
513 backgrounds[2] = new Color(0xFF,0xCF,0xCF);
514 backgrounds[3] = new Color(0xFF,0xFF,0x9F);
515 indicators[0] = new Color(0xFF,0x3F,0x3F);
516 indicators[1] = new Color(0xCF,0x6F,0x00);
517 indicators[2] = new Color(0x3F,0x3F,0xFF);
519 focus[1] = getBackground().darker();
522 public ColorSet darkModeColorset = new ColorSet() {
524 foregrounds[0] = Color.gray.darker();
525 foregrounds[1] = Color.pink.brighter();
526 backgrounds[0] = Color.black;
527 backgrounds[1] = new Color(0x00,0x18,0x18);
528 backgrounds[2] = new Color(0x20,0x00,0x00);
529 backgrounds[3] = new Color(0x18,0x18,0x00);
530 indicators[0] = Color.pink;
531 indicators[1] = Color.yellow;
532 indicators[2] = Color.cyan;
533 focus[0] = Color.black;
534 focus[1] = getForeground().brighter();
537 private ColorSet currentColorset = normalModeColorset;
540 * カポ値選択コンボボックス(コードボタン側ビュー)
542 public CapoSelecterView capoSelecter;
546 * @param capoValueModel カポ選択値モデル
548 public ChordMatrix(CapoComboBoxModel capoValueModel) {
549 capoSelecter = new CapoSelecterView(capoValueModel) {{
550 checkbox.addItemListener(new ItemListener() {
551 public void itemStateChanged(ItemEvent e) {capoChanged(getCapo());}
553 valueSelecter.addActionListener(new ActionListener() {
554 public void actionPerformed(ActionEvent e) {capoChanged(getCapo());}
558 Dimension buttonSize = new Dimension(28,26);
560 // Make key-signature labels and chord labels
562 for (i=0, v= -Music.SEMITONES_PER_OCTAVE; i<N_COLUMNS; i++, v++) {
564 l.addMouseListener(this);
565 l.addMouseMotionListener(this);
566 add( keysigLabels[i] = l );
567 l.setPreferredSize(buttonSize);
570 for (i=0; i < N_COLUMNS * CHORD_BUTTON_ROWS; i++) {
572 v = i - (N_COLUMNS * row) - 12;
573 Chord chord = new Chord(
574 new NoteSymbol(row==2 ? v+3 : v)
576 if( row==0 ) chord.set(Chord.Interval.SUS4);
577 else if( row==2 ) chord.set(Chord.Interval.MINOR);
578 ChordLabel cl = new ChordLabel(chord);
579 cl.addMouseListener(this);
580 cl.addMouseMotionListener(this);
581 cl.addMouseWheelListener(this);
582 add(chordLabels[i] = cl);
583 cl.setPreferredSize(buttonSize);
587 addKeyListener(this);
588 addFocusListener(new FocusListener() {
589 public void focusGained(FocusEvent e) {
592 public void focusLost(FocusEvent e) {
593 selectedChord = selectedChordCapo = null;
598 setLayout(new GridLayout( 4, N_COLUMNS, 2, 2 ));
599 setKeySignature( new Key() );
601 // Make chord label selections index
604 ArrayList<ChordLabelSelection> al;
606 for( int note_no=0; note_no<chordLabelSelections.length; note_no++ ) {
607 al = new ArrayList<ChordLabelSelection>();
609 // Root major/minor chords
610 for( ChordLabel cl : chordLabels ) {
611 if( ! cl.isSus4 && cl.chord.indexOf(note_no) == 0 ) {
612 al.add(new ChordLabelSelection( cl, 0 )); // Root
616 for( ChordLabel cl : chordLabels ) {
617 if( cl.isSus4 && cl.chord.indexOf(note_no) == 0 ) {
618 al.add(new ChordLabelSelection( cl, 0 )); // Root
621 // 3rd,sus4th,5th included chords
622 for( ChordLabel cl : chordLabels ) {
623 noteIndex = cl.chord.indexOf(note_no);
624 if( noteIndex == 1 || noteIndex == 2 ) {
625 al.add(new ChordLabelSelection( cl, noteIndex )); // 3rd,sus4,P5
628 // Diminished chords (major/minor chord button only)
629 for( ChordLabel cl : chordLabels ) {
630 if( cl.isSus4 ) continue;
631 (chord = cl.chord.clone()).set(Chord.Interval.FLAT5);
632 if( chord.indexOf(note_no) == 2 ) {
633 al.add(new ChordLabelSelection( cl, 3 ));
636 // Augumented chords (major chord button only)
637 for( ChordLabel cl : chordLabels ) {
638 if( cl.isSus4 || cl.isMinor ) continue;
639 (chord = cl.chord.clone()).set(Chord.Interval.SHARP5);
640 if( chord.indexOf(note_no) == 2 ) {
641 al.add(new ChordLabelSelection( cl, 4 ));
644 chordLabelSelections[note_no] = new ChordLabelSelections(al);
649 public void mousePressed(MouseEvent e) {
650 Component obj = e.getComponent();
651 if( obj instanceof ChordLabel ) {
652 ChordLabel cl = (ChordLabel)obj;
653 Chord chord = cl.chord.clone();
654 if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
655 if( e.isShiftDown() )
656 chord.set(Chord.Interval.MAJOR_SEVENTH);
658 chord.set(Chord.Interval.SEVENTH);
660 else if( e.isShiftDown() )
661 chord.set(Chord.Interval.SIXTH);
662 if( e.isControlDown() )
663 chord.set(Chord.Interval.NINTH);
665 chord.clear(Chord.OffsetIndex.NINTH);
667 if( e.isAltDown() ) {
669 chord.set(Chord.Interval.MAJOR); // To cancel sus4
670 chord.set(Chord.Interval.SHARP5);
672 else chord.set(Chord.Interval.FLAT5);
674 if( selectedChordLabel != null ) {
675 selectedChordLabel.setSelection(false);
677 (selectedChordLabel = cl).setSelection(true);
678 setSelectedChord(chord);
680 else if( obj instanceof Co5Label ) {
681 int v = ((Co5Label)obj).co5Value;
682 if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
683 setKeySignature( new Key(Music.oppositeCo5(v)) );
685 else if ( v == key.toCo5() ) {
687 // Cancel selected chord
689 setSelectedChord( (Chord)null );
693 setKeySignature( new Key(v) );
696 requestFocusInWindow();
699 public void mouseReleased(MouseEvent e) { destinationChordLabel = null; }
700 public void mouseEntered(MouseEvent e) { }
701 public void mouseExited(MouseEvent e) { }
702 public void mouseClicked(MouseEvent e) { }
703 public void mouseDragged(MouseEvent e) {
704 Component obj = e.getComponent();
705 if( obj instanceof ChordLabel ) {
706 ChordLabel l_src = (ChordLabel)obj;
707 Component obj2 = this.getComponentAt(
708 l_src.getX() + e.getX(),
709 l_src.getY() + e.getY()
713 // Entered gap between chord buttons - do nothing
718 ( (obj2 instanceof ChordLabel ) ? (ChordLabel)obj2 : null );
719 if( l_dst == l_src ) {
721 // Returned to original chord button
723 destinationChordLabel = null;
726 if( destinationChordLabel != null ) {
728 // Already touched another chord button
732 Chord chord = l_src.chord.clone();
733 if( l_src.isMinor ) {
734 if( l_dst == null ) { // Out of chord buttons
736 chord.set(Chord.Interval.MAJOR_SEVENTH);
738 else if( l_src.co5Value < l_dst.co5Value ) { // Right
740 chord.set(Chord.Interval.SIXTH);
742 else { // Left or up from minor to major
744 chord.set(Chord.Interval.SEVENTH);
747 else if( l_src.isSus4 ) {
748 if( l_dst == null ) { // Out of chord buttons
751 else if( ! l_dst.isSus4 ) { // Down from sus4 to major
752 chord.set(Chord.Interval.MAJOR);
754 else if( l_src.co5Value < l_dst.co5Value ) { // Right
755 chord.set(Chord.Interval.NINTH);
759 chord.set(Chord.Interval.SEVENTH);
763 if( l_dst == null ) { // Out of chord buttons
766 else if( l_dst.isSus4 ) { // Up from major to sus4
767 chord.set(Chord.Interval.NINTH);
769 else if( l_src.co5Value < l_dst.co5Value ) { // Right
771 chord.set(Chord.Interval.MAJOR_SEVENTH);
773 else if( l_dst.isMinor ) { // Down from major to minor
775 chord.set(Chord.Interval.SIXTH);
779 chord.set(Chord.Interval.SEVENTH);
782 if( chord.isSet(Chord.OffsetIndex.NINTH) || (l_src.isSus4 && (l_dst == null || ! l_dst.isSus4) ) ) {
783 if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
784 if( e.isShiftDown() ) {
785 chord.set(Chord.Interval.MAJOR_SEVENTH);
788 chord.set(Chord.Interval.SEVENTH);
791 else if( e.isShiftDown() ) {
792 chord.set(Chord.Interval.SIXTH);
796 if( e.isControlDown() )
797 chord.set(Chord.Interval.NINTH);
799 chord.clear(Chord.OffsetIndex.NINTH);
801 if( e.isAltDown() ) {
803 chord.set(Chord.Interval.MAJOR);
804 chord.set(Chord.Interval.SHARP5);
807 chord.set(Chord.Interval.FLAT5);
810 setSelectedChord(chord);
811 destinationChordLabel = (l_dst == null ? l_src : l_dst ) ;
813 else if( obj instanceof Co5Label ) {
814 Co5Label l_src = (Co5Label)obj;
815 Component obj2 = this.getComponentAt(
816 l_src.getX() + e.getX(),
817 l_src.getY() + e.getY()
819 if( !(obj2 instanceof Co5Label) ) {
822 Co5Label l_dst = (Co5Label)obj2;
823 int v = l_dst.co5Value;
824 if( (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0 ) {
825 setKeySignature( new Key(Music.oppositeCo5(v)) );
828 setKeySignature( new Key(v) );
833 public void mouseMoved(MouseEvent e) { }
834 public void mouseWheelMoved(MouseWheelEvent e) {
835 if( selectedChord != null ) {
836 if( e.getWheelRotation() > 0 ) { // Wheel moved down
837 if( --selectedNoteIndex < 0 ) {
838 selectedNoteIndex = selectedChord.numberOfNotes() - 1;
841 else { // Wheel moved up
842 if( ++selectedNoteIndex >= selectedChord.numberOfNotes() ) {
843 selectedNoteIndex = 0;
849 private Chord.Interval pcKeyNextShift7;
850 public void keyPressed(KeyEvent e) {
851 int i = -1, i_col = -1, i_row = 1;
852 boolean shiftPressed = false; // True if Shift-key pressed or CapsLocked
853 char keyChar = e.getKeyChar();
854 int keyCode = e.getKeyCode();
855 ChordLabel cl = null;
857 int key_co5 = key.toCo5();
858 // System.out.println( keyChar + " Pressed on chord matrix" );
860 if( (i = "6 ".indexOf(keyChar)) >= 0 ) {
861 selectedChord = selectedChordCapo = null;
863 pcKeyNextShift7 = null;
866 else if( (i = "asdfghjkl;:]".indexOf(keyChar)) >= 0 ) {
867 i_col = i + key_co5 + 7;
869 else if( (i = "ASDFGHJKL+*}".indexOf(keyChar)) >= 0 ) {
870 i_col = i + key_co5 + 7;
873 else if( (i = "zxcvbnm,./\\".indexOf(keyChar)) >=0 ) {
874 i_col = i + key_co5 + 7;
877 else if( (i = "ZXCVBNM<>?_".indexOf(keyChar)) >=0 ) {
878 i_col = i + key_co5 + 7;
882 else if( (i = "qwertyuiop@[".indexOf(keyChar)) >= 0 ) {
883 i_col = i + key_co5 + 7;
886 else if( (i = "QWERTYUIOP`{".indexOf(keyChar)) >= 0 ) {
887 i_col = i + key_co5 + 7;
891 else if( keyChar == '5' ) {
892 pcKeyNextShift7 = Chord.Interval.MAJOR_SEVENTH; return;
894 else if( keyChar == '7' ) {
895 pcKeyNextShift7 = Chord.Interval.SEVENTH; return;
897 // Shift current key-signature
898 else if( keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_KP_LEFT ) {
900 setKeySignature( new Key(key_co5-1) );
903 else if( keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_KP_RIGHT ) {
905 setKeySignature( new Key(key_co5+1) );
908 else if( keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_KP_DOWN ) {
910 Key key = new Key(key_co5);
912 setKeySignature(key);
915 else if( keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_KP_UP ) {
917 Key key = new Key(key_co5);
919 setKeySignature(key);
922 if( i < 0 ) // No key char found
924 if( i_col < 0 ) i_col += 12; else if( i_col > N_COLUMNS ) i_col -= 12;
925 cl = chordLabels[i_col + N_COLUMNS * i_row];
926 chord = cl.chord.clone();
928 chord.set(Chord.Interval.SEVENTH);
930 // specify by previous key
931 else if( pcKeyNextShift7 == null ) {
932 chord.clear(Chord.OffsetIndex.SEVENTH);
935 chord.set(pcKeyNextShift7);
937 if( e.isAltDown() ) {
939 chord.set(Chord.Interval.MAJOR); // To cancel sus4
940 chord.set(Chord.Interval.SHARP5);
943 chord.set(Chord.Interval.FLAT5);
946 if( e.isControlDown() ) { // Cannot use for ninth ?
947 chord.set(Chord.Interval.NINTH);
949 if( selectedChordLabel != null ) clear();
950 (selectedChordLabel = cl).setSelection(true);
951 setSelectedChord(chord);
952 pcKeyNextShift7 = null;
955 public void keyReleased(KeyEvent e) { }
956 public void keyTyped(KeyEvent e) { }
958 public void addChordMatrixListener(ChordMatrixListener l) {
959 listenerList.add(ChordMatrixListener.class, l);
961 public void removeChordMatrixListener(ChordMatrixListener l) {
962 listenerList.remove(ChordMatrixListener.class, l);
964 protected void fireChordChanged() {
965 Object[] listeners = listenerList.getListenerList();
966 for (int i = listeners.length-2; i>=0; i-=2) {
967 if (listeners[i]==ChordMatrixListener.class) {
968 ((ChordMatrixListener)listeners[i+1]).chordChanged();
971 if( selectedChord == null ) clearIndicators();
973 public void fireKeySignatureChanged() {
974 Object[] listeners = listenerList.getListenerList();
975 for (int i = listeners.length-2; i>=0; i-=2) {
976 if (listeners[i]==ChordMatrixListener.class) {
977 ((ChordMatrixListener)listeners[i+1]).keySignatureChanged();
981 private Key key = null;
982 private Key capoKey = null;
983 public Key getKeySignature() { return key; }
984 public Key getKeySignatureCapo() { return capoKey; }
985 public void setKeySignature( Key key ) {
986 if( key == null || this.key != null && key.equals(this.key) )
990 if( this.key == null ) {
991 for( i = 0; i < keysigLabels.length; i++ ) {
992 keysigLabels[i].setBackground(false);
996 keysigLabels[this.key.toCo5() + 12].setSelection(false);
997 for( i = Music.mod12(this.key.toCo5()); i < N_COLUMNS; i+=12 ) {
998 keysigLabels[i].setBackground(false);
1002 keysigLabels[i = key.toCo5() + 12].setSelection(true);
1003 for( i = Music.mod12(key.toCo5()); i < N_COLUMNS; i+=12 ) {
1004 keysigLabels[i].setBackground(true);
1006 // Change chord-label's color & font
1007 int i_color, old_i_color;
1008 for( ChordLabel cl : chordLabels ) {
1009 i_color = ((cl.co5Value - key.toCo5() + 31)/3) & 3;
1010 if( this.key != null ) {
1011 old_i_color = ((cl.co5Value - this.key.toCo5() + 31)/3) & 3;
1012 if( i_color != old_i_color ) {
1013 cl.setBackground(i_color);
1016 else cl.setBackground(i_color);
1017 if( !(cl.isSus4) ) {
1018 if( this.key != null && Music.mod12(cl.co5Value - this.key.toCo5()) == 0)
1020 if( Music.mod12( cl.co5Value - key.toCo5() ) == 0 )
1024 this.capoKey = (this.key = key).clone().transpose(capoSelecter.getCapo());
1025 for( ChordLabel cl : chordLabels ) cl.keyChanged();
1026 fireKeySignatureChanged();
1028 private int capo = 0;
1031 * @param newCapo 新しいカポ位置
1033 protected void capoChanged(int newCapo) {
1034 if( this.capo == newCapo )
1036 (this.capoKey = this.key.clone()).transpose(this.capo = newCapo);
1037 selectedChordCapo = (
1038 selectedChord == null ? null : selectedChord.clone().transpose(newCapo)
1040 for( ChordLabel cl : chordLabels ) cl.keyChanged();
1041 fireKeySignatureChanged();
1047 public ChordGuide chordGuide = new ChordGuide(this);
1052 private ChordLabel destinationChordLabel = null;
1055 * @return ドラッグ先コードボタンがあればtrue
1057 public boolean isDragged() {
1058 return destinationChordLabel != null ;
1061 private boolean isDark = false;
1062 public void setDarkMode(boolean is_dark) {
1063 this.isDark = is_dark;
1064 currentColorset = (is_dark ? darkModeColorset : normalModeColorset);
1065 setBackground( currentColorset.focus[0] );
1068 setKeySignature(prev_key);
1069 for( int i=0; i < keysigLabels.length; i++ ) keysigLabels[i].setSelection();
1070 for( int i=0; i < chordLabels.length; i++ ) chordLabels[i].setSelection();
1071 chordGuide.setDarkMode( is_dark );
1072 chordDisplay.setDarkMode( is_dark );
1073 Color col = is_dark ? Color.black : null;
1074 capoSelecter.setBackground( col );
1075 capoSelecter.valueSelecter.setBackground( col );
1078 private boolean isPlaying = false;
1079 public boolean isPlaying() { return isPlaying; }
1080 public void setPlaying(boolean is_playing) {
1081 this.isPlaying = is_playing;
1085 private byte currentBeat = 0;
1086 private byte timesigUpper = 4;
1087 public void setBeat(SequenceTickIndex sequenceTickIndex) {
1088 byte beat = (byte)(sequenceTickIndex.lastBeat);
1089 byte tsu = sequenceTickIndex.timesigUpper;
1090 if( currentBeat == beat && timesigUpper == tsu )
1094 keysigLabels[ key.toCo5() + 12 ].repaint();
1097 private ChordLabel selectedChordLabel = null;
1098 public JComponent getSelectedButton() {
1099 return selectedChordLabel;
1101 private Chord selectedChord = null;
1102 public Chord getSelectedChord() {
1103 return selectedChord;
1105 private Chord selectedChordCapo = null;
1106 public Chord getSelectedChordCapo() {
1107 return selectedChordCapo;
1109 public void setSelectedChordCapo( Chord chord ) {
1110 setNoteIndex(-1); // Cancel arpeggio mode
1111 selectedChord = (chord == null ? null : chord.clone().transpose(-capo,capoKey));
1112 selectedChordCapo = chord;
1115 public void setSelectedChord( Chord chord ) {
1116 setNoteIndex(-1); // Cancel arpeggio mode
1117 selectedChord = chord;
1118 selectedChordCapo = (chord == null ? null : chord.clone().transpose(capo,key));
1123 * @param chordSymbol コード名
1125 public void setSelectedChord(String chordSymbol) throws IllegalArgumentException {
1127 if( chordSymbol != null && ! chordSymbol.isEmpty() ) {
1129 chord = new Chord(chordSymbol);
1130 } catch( IllegalArgumentException e ) {
1131 JOptionPane.showMessageDialog(
1132 null, e.getMessage(), "Input error",
1133 JOptionPane.ERROR_MESSAGE
1138 setSelectedChord(chord);
1141 private int selectedNoteIndex = -1;
1142 public int getNoteIndex() {
1143 return selectedChord == null || selectedNoteIndex < 0 ? -1 : selectedNoteIndex;
1145 public void setNoteIndex(int noteIndex) {
1146 selectedNoteIndex = noteIndex;
1148 public void clear() {
1149 if( selectedChordLabel != null ) {
1150 selectedChordLabel.setSelection(false);
1151 selectedChordLabel = null;
1153 selectedChord = null; selectedNoteIndex = -1;