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