1 /* 2 * Created on 28-dic-2005 3 * 4 * TODO To change the template for this generated file go to 5 * Window - Preferences - Java - Code Style - Code Templates 6 */ 7 package org.herac.tuxguitar.gui.editors.chord; 8 9 import java.util.ArrayList; 10 import java.util.Iterator; 11 import java.util.List; 12 13 import org.eclipse.swt.SWT; 14 import org.eclipse.swt.events.MouseAdapter; 15 import org.eclipse.swt.events.PaintEvent; 16 import org.eclipse.swt.events.PaintListener; 17 import org.eclipse.swt.events.SelectionAdapter; 18 import org.eclipse.swt.events.SelectionEvent; 19 import org.eclipse.swt.graphics.Point; 20 import org.eclipse.swt.layout.GridData; 21 import org.eclipse.swt.layout.GridLayout; 22 import org.eclipse.swt.widgets.Composite; 23 import org.eclipse.swt.widgets.Label; 24 import org.eclipse.swt.widgets.Text; 25 import org.herac.tuxguitar.gui.TuxGuitar; 26 import org.herac.tuxguitar.gui.editors.TGPainter; 27 import org.herac.tuxguitar.gui.editors.tab.TGChordImpl; 28 import org.herac.tuxguitar.song.models.TGChannel; 29 import org.herac.tuxguitar.song.models.TGChord; 30 import org.herac.tuxguitar.song.models.TGTrack; 31 import org.herac.tuxguitar.song.models.TGVelocities; 32 /** 33 * @author julian 34 * @author Nikola Kolarovic 35 */ 36 public class ChordEditor extends Composite { 37 38 public static final int STRING_SPACING = 30; 39 public static final int FRET_SPACING = 30; 40 public static final short MIN_FRET = 1; 41 public static final short MAX_FRET = 24; 42 43 private ChordDialog dialog; 44 private Composite composite; 45 private Text chordName; 46 private List points; 47 private boolean[] firstFrets; 48 private int[] strings; 49 private int[] frets; 50 private short fret; 51 private short maxStrings; 52 private int width; 53 private int height; 54 private TGTrack currentTrack = null; 55 ChordEditor(Composite parent, int style)56 public ChordEditor(Composite parent, int style) { 57 super(parent, style); 58 } 59 ChordEditor(ChordDialog dialog, Composite parent,int style, short maxStrings)60 public ChordEditor(ChordDialog dialog, Composite parent,int style, short maxStrings) { 61 this(parent, style); 62 this.dialog = dialog; 63 this.setLayout(dialog.gridLayout(1, false, 0, 0)); 64 this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 65 this.init(maxStrings); 66 } 67 init(short maxStrings)68 public void init(short maxStrings) { 69 this.fret = MIN_FRET; 70 this.maxStrings = maxStrings; 71 this.firstFrets = new boolean[this.maxStrings]; 72 this.strings = new int[this.maxStrings]; 73 this.frets = new int[TGChordImpl.MAX_FRETS]; 74 this.width = ((STRING_SPACING * this.maxStrings) - STRING_SPACING); 75 this.height = ((FRET_SPACING * TGChordImpl.MAX_FRETS) - FRET_SPACING); 76 this.points = new ArrayList(); 77 78 for (int i = 0; i < this.firstFrets.length; i++) { 79 this.firstFrets[i] = false; 80 } 81 82 for (int i = 0; i < this.strings.length; i++) { 83 this.strings[i] = ((i + 1) * STRING_SPACING); 84 } 85 86 for (int i = 0; i < this.frets.length; i++) { 87 this.frets[i] = ((i + 1) * FRET_SPACING); 88 } 89 90 Composite composite = new Composite(this, SWT.NONE); 91 composite.setLayout(new GridLayout()); 92 composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 93 94 this.composite = new Composite(composite, SWT.BORDER | SWT.V_SCROLL | SWT.DOUBLE_BUFFERED); 95 96 Composite nameComposite = new Composite(composite, SWT.NONE); 97 nameComposite.setLayout(this.dialog.gridLayout(1, true, 0, 0)); 98 nameComposite.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true,true)); 99 100 Label formulaLabel = new Label(nameComposite, SWT.SEPARATOR | SWT.HORIZONTAL); 101 formulaLabel.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true,true)); 102 103 Label chordNameLabel = new Label(nameComposite, SWT.LEFT); 104 chordNameLabel.setText(TuxGuitar.getProperty("chord.name")); 105 chordNameLabel.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true,false)); 106 107 this.chordName = new Text(nameComposite, SWT.SINGLE | SWT.BORDER); 108 this.chordName.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true,false)); 109 110 this.composite.setBackground(this.getDisplay().getSystemColor(SWT.COLOR_WHITE)); 111 this.composite.addPaintListener(new PaintListener() { 112 public void paintControl(PaintEvent e) { 113 TGPainter painter = new TGPainter(e.gc); 114 paintEditor(painter); 115 } 116 }); 117 118 this.composite.addMouseListener(new MouseAdapter() { 119 public void mouseUp(org.eclipse.swt.events.MouseEvent e) { 120 getComposite().setFocus(); 121 checkPoint(e.x, e.y); 122 redraw(); 123 } 124 }); 125 126 this.composite.getVerticalBar().setIncrement(1); 127 this.composite.getVerticalBar().setMaximum( ((MAX_FRET + MIN_FRET) - (TGChordImpl.MAX_FRETS - 1) + 1)); 128 this.composite.getVerticalBar().setMinimum(MIN_FRET); 129 this.composite.getVerticalBar().setThumb(1); 130 this.composite.getVerticalBar().addSelectionListener(new SelectionAdapter() { 131 public void widgetSelected(SelectionEvent e) { 132 setFret((short) getComposite().getVerticalBar().getSelection(), false, true); 133 redraw(); 134 } 135 }); 136 137 this.composite.setLayoutData(makeCompositeData()); 138 } 139 makeCompositeData()140 private GridData makeCompositeData() { 141 GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); 142 data.minimumWidth = (getWidth() + (STRING_SPACING * 2) + this.composite.getVerticalBar().getSize().x); 143 data.minimumHeight = (getHeight() + (FRET_SPACING * 2)); 144 return data; 145 } 146 paintEditor(TGPainter painter)147 protected void paintEditor(TGPainter painter) { 148 int noteSize = (FRET_SPACING / 2); 149 150 painter.setForeground(this.getDisplay().getSystemColor(SWT.COLOR_BLACK)); 151 152 // dibujo el puente 153 painter.initPath(); 154 painter.setAntialias(false); 155 painter.moveTo((STRING_SPACING - 10), (FRET_SPACING - 10)); 156 painter.lineTo(STRING_SPACING + (this.width + 10), (FRET_SPACING - 10)); 157 painter.closePath(); 158 159 painter.drawString(Integer.toString(getFret()), FRET_SPACING - 25,STRING_SPACING); 160 161 // dibujo las cuerdas 162 painter.initPath(); 163 painter.setAntialias(false); 164 for (int i = 0; i < this.strings.length; i++) { 165 painter.moveTo(this.strings[i], FRET_SPACING); 166 painter.lineTo(this.strings[i], FRET_SPACING + this.height); 167 } 168 painter.closePath(); 169 170 // dibujo las cegillas 171 painter.initPath(); 172 painter.setAntialias(false); 173 for (int i = 0; i < this.frets.length; i++) { 174 painter.moveTo(STRING_SPACING, this.frets[i]); 175 painter.lineTo(STRING_SPACING + this.width, this.frets[i]); 176 } 177 painter.closePath(); 178 179 // dibujo las notas 180 painter.setBackground(this.getDisplay().getSystemColor(SWT.COLOR_BLACK)); 181 Iterator it = this.points.iterator(); 182 while (it.hasNext()) { 183 Point point = (Point) it.next(); 184 painter.initPath(TGPainter.PATH_FILL); 185 painter.addOval(point.x - (noteSize / 2), point.y + (noteSize / 2),noteSize, noteSize); 186 painter.closePath(); 187 } 188 189 // dibujo las notas al aire 190 for (int i = 0; i < this.firstFrets.length; i++) { 191 if (!hasPoints(i)) { 192 painter.initPath(); 193 if (this.firstFrets[i]) { 194 int x = this.strings[i] - (noteSize / 2); 195 int y = (FRET_SPACING - noteSize) - 11; 196 painter.addOval(x, y, (noteSize - 1), (noteSize - 1)); 197 } else { 198 int x = this.strings[i]; 199 int y = (FRET_SPACING - noteSize) - 4; 200 painter.moveTo(x - ((noteSize / 2) - 1), y + ((noteSize / 2) - 1)); 201 painter.lineTo(x + ((noteSize / 2) - 1), y - ((noteSize / 2) - 1)); 202 painter.moveTo(x - ((noteSize / 2) - 1), y - ((noteSize / 2) - 1)); 203 painter.lineTo(x + ((noteSize / 2) - 1), y + ((noteSize / 2) - 1)); 204 } 205 painter.closePath(); 206 } 207 } 208 } 209 checkPoint(int x, int y)210 protected void checkPoint(int x, int y) { 211 int stringIndex = getStringIndex(x); 212 int fretIndex = getFretIndex(y); 213 214 if (y < FRET_SPACING) { 215 this.firstFrets[stringIndex] = !this.firstFrets[stringIndex]; 216 this.removePointsAtStringLine(this.strings[stringIndex]); 217 } else if (y < (FRET_SPACING * TGChordImpl.MAX_FRETS)) { 218 Point point = new Point(this.strings[stringIndex],this.frets[fretIndex]); 219 if (!this.removePoint(point)) { 220 this.firstFrets[stringIndex] = false; 221 this.removePointsAtStringLine(this.strings[stringIndex]); 222 this.addPoint(point); 223 this.orderPoints(); 224 } 225 } 226 else{ 227 return; // don't recognize it otherwise 228 } 229 230 // after changing a chord, recognize it 231 this.dialog.getRecognizer().recognize(getChord(),true,false); 232 } 233 removePoint(Point point)234 private boolean removePoint(Point point) { 235 Iterator it = this.points.iterator(); 236 while (it.hasNext()) { 237 Point currPoint = (Point) it.next(); 238 if (currPoint.x == point.x && currPoint.y == point.y) { 239 this.points.remove(point); 240 return true; 241 } 242 } 243 return false; 244 } 245 orderPoints()246 private void orderPoints() { 247 for (int i = 0; i < this.points.size(); i++) { 248 Point minPoint = null; 249 for (int noteIdx = i; noteIdx < this.points.size(); noteIdx++) { 250 Point point = (Point) this.points.get(noteIdx); 251 if (minPoint == null || point.x < minPoint.x) { 252 minPoint = point; 253 } 254 } 255 this.points.remove(minPoint); 256 this.points.add(i, minPoint); 257 } 258 } 259 removePointsAtStringLine(int x)260 private void removePointsAtStringLine(int x) { 261 Iterator it = this.points.iterator(); 262 while (it.hasNext()) { 263 Point point = (Point) it.next(); 264 if (point.x == x) { 265 this.points.remove(point); 266 break; 267 } 268 } 269 } 270 addPoint(Point point)271 private void addPoint(Point point) { 272 this.points.add(point); 273 } 274 getStringIndex(int x)275 private int getStringIndex(int x) { 276 int index = -1; 277 for (int i = 0; i < this.strings.length; i++) { 278 if (index < 0) { 279 index = i; 280 } else { 281 int distanceX = Math.abs(x - this.strings[index]); 282 int currDistanceX = Math.abs(x - this.strings[i]); 283 if (currDistanceX < distanceX) { 284 index = i; 285 } 286 } 287 } 288 return index; 289 } 290 getFretIndex(int y)291 private int getFretIndex(int y) { 292 int index = -1; 293 for (int i = 0; i < this.frets.length; i++) { 294 if (index < 0) { 295 index = i; 296 } else { 297 int distanceX = Math.abs(y - (this.frets[index] + (FRET_SPACING / 2))); 298 int currDistanceX = Math.abs(y - (this.frets[i] + (FRET_SPACING / 2))); 299 if (currDistanceX < distanceX) { 300 index = i; 301 } 302 } 303 } 304 return index; 305 306 } 307 hasPoints(int stringIndex)308 private boolean hasPoints(int stringIndex) { 309 Iterator it = this.points.iterator(); 310 while (it.hasNext()) { 311 Point point = (Point) it.next(); 312 if (point.x == this.strings[stringIndex]) { 313 return true; 314 } 315 } 316 return false; 317 } 318 isEmpty()319 public boolean isEmpty() { 320 return this.points.isEmpty(); 321 } 322 getValue(int string)323 public int getValue(int string) { 324 int value = -1; 325 if (this.firstFrets[this.maxStrings - string]) { 326 value = 0; 327 } 328 329 if (value < 0) { 330 Iterator it = this.points.iterator(); 331 while (it.hasNext()) { 332 Point point = (Point) it.next(); 333 if (string == (this.maxStrings - getStringIndex(point.x))) { 334 value = (getFretIndex(point.y + (FRET_SPACING / 2)) + 1); 335 value += (getFret() - 1); 336 } 337 } 338 } 339 return value; 340 } 341 addValue(int value, int string )342 public void addValue(int value, int string/*, boolean redecorate*/) { 343 int realValue = value; 344 if (string >= 1 && string <= this.maxStrings) { 345 this.firstFrets[this.maxStrings - string] = false; 346 this.removePointsAtStringLine(this.strings[this.maxStrings - string]); 347 if (realValue == 0) { 348 this.firstFrets[this.maxStrings - string] = true; 349 } else if (realValue >= 0) { 350 realValue -= (getFret() - 1); 351 if (realValue > 0 && realValue <= TGChordImpl.MAX_FRETS) { 352 this.addPoint(new Point(this.strings[this.maxStrings - string], this.frets[realValue - 1])); 353 } 354 } 355 //INNECESARY CODE 356 //this method is called allways from "setChord(c)" 357 //but it is called some times, as Strings has the chord. 358 //So i moved it to "setChord" to call "recognize" only one time. 359 360 // after adding a value, recognize the current chord 361 //this.chordName.setText(this.dialog.getRecognizer().recognize(getChord(), redecorate)); 362 } 363 } 364 getFret()365 public short getFret() { 366 return this.fret; 367 } 368 setFret(short fret)369 public void setFret(short fret) { 370 setFret(fret, true, false); 371 } 372 setFret(short fret, boolean updateScroll, boolean recognize)373 protected void setFret(short fret, boolean updateScroll, boolean recognize) { 374 if (fret >= MIN_FRET && fret <= MAX_FRET) { 375 this.fret = fret; 376 } 377 378 if (updateScroll) { 379 this.composite.getVerticalBar().setSelection(this.fret); 380 } 381 382 if(recognize){ 383 this.dialog.getRecognizer().recognize(getChord(), true,false); 384 } 385 } 386 getChord()387 public TGChord getChord() { 388 TGChord chord = TuxGuitar.instance().getSongManager().getFactory().newChord(this.strings.length); 389 chord.setName(this.chordName.getText()); 390 chord.setFirstFret(this.fret); 391 for (int i = 0; i < chord.getStrings().length; i++) { 392 chord.addFretValue(i, getValue(i + 1)); 393 //chord.setName(this.chordName.getText()); 394 } 395 return chord; 396 } 397 setChord(TGChord chord)398 public void setChord(TGChord chord) { 399 if (chord != null) { 400 this.setFret((short)chord.getFirstFret()); 401 for (int i = 0; i < chord.getStrings().length; i++) { 402 int fretValue = chord.getFretValue(i); 403 this.addValue(fretValue, i + 1/*, false*/); 404 } 405 406 //SEE Comment on addValue. 407 //this.getChordName().setText(chord.getName() != null ? chord.getName() : this.dialog.getRecognizer().recognize(getChord(),true) ); 408 409 String name = chord.getName(); 410 411 this.dialog.getRecognizer().recognize(getChord(), (name == null), (name == null) ); 412 413 this.previewChord(chord); 414 415 if(name != null){ 416 this.setChordName( name ); 417 } 418 419 this.redraw(); 420 } 421 } 422 getMaxStrings()423 public short getMaxStrings() { 424 return this.maxStrings; 425 } 426 setMaxStrings(short maxStrings)427 public void setMaxStrings(short maxStrings) { 428 this.maxStrings = maxStrings; 429 } 430 getWidth()431 public int getWidth() { 432 return this.width; 433 } 434 getHeight()435 public int getHeight() { 436 return this.height; 437 } 438 getComposite()439 protected Composite getComposite(){ 440 return this.composite; 441 } 442 getChordName()443 public Text getChordName() { 444 return this.chordName; 445 } 446 setChordName(String chordName)447 public void setChordName(String chordName) { 448 this.chordName.setText(chordName); 449 } 450 redraw()451 public void redraw() { 452 super.redraw(); 453 this.composite.redraw(); 454 } 455 setCurrentTrack(TGTrack track)456 public void setCurrentTrack(TGTrack track) { 457 this.currentTrack = track; 458 } 459 getCurrentTrack()460 public TGTrack getCurrentTrack() { 461 return this.currentTrack; 462 } 463 previewChord(final TGChord chord)464 public void previewChord(final TGChord chord) { 465 466 new Thread(new Runnable() { 467 public void run() { 468 int playedStrings = 0; 469 int stringCount = Math.min( getMaxStrings(), chord.countStrings() ); 470 for (int i = 0; i < stringCount; i++) { 471 if (chord.getFretValue( i ) != -1) { 472 playedStrings ++; 473 } 474 } 475 int next = 0; 476 int[][] beat = new int[playedStrings][2]; 477 for (int i = 0; i < stringCount; i++) { 478 int string = (stringCount - i); 479 int value = chord.getFretValue(string - 1); 480 if (value != -1) { 481 beat[next][0] = getCurrentTrack().getOffset() + getCurrentTrack().getString(string).getValue() + value; 482 beat[next][1] = TGVelocities.DEFAULT; 483 next ++; 484 } 485 } 486 487 TGChannel ch = getCurrentTrack().getChannel(); 488 TuxGuitar.instance().getPlayer().playBeat(ch.getChannel(), 489 ch.getInstrument(), 490 ch.getVolume(), 491 ch.getBalance(), 492 ch.getChorus(), 493 ch.getReverb(), 494 ch.getPhaser(), 495 ch.getTremolo(), 496 beat, 497 200, 498 200 ); 499 } 500 }).start(); 501 } 502 } 503