1 package org.herac.tuxguitar.gui.editors.matrix;
2 
3 import java.util.Iterator;
4 import java.util.List;
5 
6 import org.eclipse.swt.SWT;
7 import org.eclipse.swt.events.DisposeEvent;
8 import org.eclipse.swt.events.DisposeListener;
9 import org.eclipse.swt.events.MouseEvent;
10 import org.eclipse.swt.events.MouseListener;
11 import org.eclipse.swt.events.MouseMoveListener;
12 import org.eclipse.swt.events.MouseTrackListener;
13 import org.eclipse.swt.events.PaintEvent;
14 import org.eclipse.swt.events.PaintListener;
15 import org.eclipse.swt.events.SelectionAdapter;
16 import org.eclipse.swt.events.SelectionEvent;
17 import org.eclipse.swt.graphics.GC;
18 import org.eclipse.swt.graphics.Image;
19 import org.eclipse.swt.graphics.Point;
20 import org.eclipse.swt.graphics.Rectangle;
21 import org.eclipse.swt.graphics.Resource;
22 import org.eclipse.swt.layout.FillLayout;
23 import org.eclipse.swt.layout.GridData;
24 import org.eclipse.swt.layout.GridLayout;
25 import org.eclipse.swt.widgets.Button;
26 import org.eclipse.swt.widgets.Combo;
27 import org.eclipse.swt.widgets.Composite;
28 import org.eclipse.swt.widgets.Event;
29 import org.eclipse.swt.widgets.Label;
30 import org.eclipse.swt.widgets.Listener;
31 import org.eclipse.swt.widgets.ScrollBar;
32 import org.eclipse.swt.widgets.Shell;
33 import org.herac.tuxguitar.gui.TuxGuitar;
34 import org.herac.tuxguitar.gui.actions.ActionLock;
35 import org.herac.tuxguitar.gui.actions.caret.GoLeftAction;
36 import org.herac.tuxguitar.gui.actions.caret.GoRightAction;
37 import org.herac.tuxguitar.gui.actions.duration.DecrementDurationAction;
38 import org.herac.tuxguitar.gui.actions.duration.IncrementDurationAction;
39 import org.herac.tuxguitar.gui.editors.TGPainter;
40 import org.herac.tuxguitar.gui.editors.TGRedrawListener;
41 import org.herac.tuxguitar.gui.editors.tab.Caret;
42 import org.herac.tuxguitar.gui.editors.tab.TGNoteImpl;
43 import org.herac.tuxguitar.gui.system.config.TGConfigKeys;
44 import org.herac.tuxguitar.gui.system.icons.IconLoader;
45 import org.herac.tuxguitar.gui.system.language.LanguageLoader;
46 import org.herac.tuxguitar.gui.undo.undoables.measure.UndoableMeasureGeneric;
47 import org.herac.tuxguitar.gui.util.DialogUtils;
48 import org.herac.tuxguitar.gui.util.TGMusicKeyUtils;
49 import org.herac.tuxguitar.player.base.MidiPercussion;
50 import org.herac.tuxguitar.song.managers.TGSongManager;
51 import org.herac.tuxguitar.song.models.TGBeat;
52 import org.herac.tuxguitar.song.models.TGChannel;
53 import org.herac.tuxguitar.song.models.TGDuration;
54 import org.herac.tuxguitar.song.models.TGMeasure;
55 import org.herac.tuxguitar.song.models.TGNote;
56 import org.herac.tuxguitar.song.models.TGString;
57 import org.herac.tuxguitar.song.models.TGTrack;
58 import org.herac.tuxguitar.song.models.TGVelocities;
59 import org.herac.tuxguitar.song.models.TGVoice;
60 
61 public class MatrixEditor implements TGRedrawListener,IconLoader,LanguageLoader{
62 
63 	private static final int BORDER_HEIGHT = 20;
64 	private static final int SCROLL_INCREMENT = 50;
65 	private static final String[] NOTE_NAMES = TGMusicKeyUtils.getSharpKeyNames(TGMusicKeyUtils.PREFIX_MATRIX);
66 	private static final MidiPercussion[] PERCUSSIONS = TuxGuitar.instance().getPlayer().getPercussions();
67 	protected static final int[] DIVISIONS = new int[] {1,2,3,4,6,8,16};
68 
69 	private MatrixConfig config;
70 	private MatrixListener listener;
71 	private Shell dialog;
72 	private Composite composite;
73 	private Composite toolbar;
74 	private Composite editor;
75 	private Rectangle clientArea;
76 	private Image buffer;
77 	private BufferDisposer bufferDisposer;
78 	private Label durationLabel;
79 	private Label gridsLabel;
80 	private Button settings;
81 	private float width;
82 	private float height;
83 	private float bufferWidth;
84 	private float bufferHeight;
85 	private float timeWidth;
86 	private float lineHeight;
87 	private int leftSpacing;
88 	private int minNote;
89 	private int maxNote;
90 	private int duration;
91 	private int selection;
92 	private int grids;
93 	private int playedTrack;
94 	private int playedMeasure;
95 	private TGBeat playedBeat;
96 
97 	private Image selectionBackBuffer;
98 	private int selectionX;
99 	private int selectionY;
100 
101 	private boolean selectionPaintDisabled;
102 
MatrixEditor()103 	public MatrixEditor(){
104 		this.grids = this.loadGrids();
105 		this.listener = new MatrixListener();
106 	}
107 
show()108 	public void show(){
109 		this.config = new MatrixConfig();
110 		this.config.load();
111 
112 		this.dialog = DialogUtils.newDialog(TuxGuitar.instance().getShell(),SWT.DIALOG_TRIM | SWT.RESIZE);
113 		this.dialog.setText(TuxGuitar.getProperty("matrix.editor"));
114 		this.dialog.setImage(TuxGuitar.instance().getIconManager().getAppIcon());
115 		this.dialog.setLayout(new GridLayout());
116 		this.dialog.addDisposeListener(new DisposeListenerImpl());
117 		this.bufferDisposer = new BufferDisposer();
118 
119 		this.composite = new Composite(this.dialog,SWT.NONE);
120 		this.composite.setLayout(new GridLayout());
121 		this.composite.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
122 
123 		this.initToolBar();
124 		this.initEditor();
125 		this.loadIcons();
126 
127 		this.addListeners();
128 		this.dialog.addDisposeListener(new DisposeListener() {
129 			public void widgetDisposed(DisposeEvent e) {
130 				removeListeners();
131 				TuxGuitar.instance().updateCache(true);
132 			}
133 		});
134 		DialogUtils.openDialog(this.dialog,DialogUtils.OPEN_STYLE_CENTER);
135 	}
136 
addListeners()137 	public void addListeners(){
138 		TuxGuitar.instance().getkeyBindingManager().appendListenersTo(this.toolbar);
139 		TuxGuitar.instance().getkeyBindingManager().appendListenersTo(this.editor);
140 		TuxGuitar.instance().getIconManager().addLoader(this);
141 		TuxGuitar.instance().getLanguageManager().addLoader(this);
142 		TuxGuitar.instance().getEditorManager().addRedrawListener( this );
143 	}
144 
removeListeners()145 	public void removeListeners(){
146 		TuxGuitar.instance().getIconManager().removeLoader(this);
147 		TuxGuitar.instance().getLanguageManager().removeLoader(this);
148 		TuxGuitar.instance().getEditorManager().removeRedrawListener( this );
149 	}
150 
initToolBar()151 	private void initToolBar() {
152 		GridLayout layout = new GridLayout();
153 		layout.makeColumnsEqualWidth = false;
154 		layout.numColumns = 0;
155 		layout.marginWidth = 0;
156 		layout.marginHeight = 0;
157 
158 		this.toolbar = new Composite(this.composite, SWT.NONE);
159 
160 		// position
161 		layout.numColumns ++;
162 		Button goLeft = new Button(this.toolbar, SWT.ARROW | SWT.LEFT);
163 		goLeft.addSelectionListener(TuxGuitar.instance().getAction(GoLeftAction.NAME));
164 
165 		layout.numColumns ++;
166 		Button goRight = new Button(this.toolbar, SWT.ARROW | SWT.RIGHT);
167 		goRight.addSelectionListener(TuxGuitar.instance().getAction(GoRightAction.NAME));
168 
169 		// separator
170 		layout.numColumns ++;
171 		makeToolSeparator(this.toolbar);
172 
173 		// duration
174 		layout.numColumns ++;
175 		Button decrement = new Button(this.toolbar, SWT.ARROW | SWT.MIN);
176 		decrement.addSelectionListener(TuxGuitar.instance().getAction(DecrementDurationAction.NAME));
177 
178 		layout.numColumns ++;
179 		this.durationLabel = new Label(this.toolbar, SWT.BORDER);
180 
181 		layout.numColumns ++;
182 		Button increment = new Button(this.toolbar, SWT.ARROW | SWT.MAX);
183 		increment.addSelectionListener(TuxGuitar.instance().getAction(IncrementDurationAction.NAME));
184 
185 		// separator
186 		layout.numColumns ++;
187 		makeToolSeparator(this.toolbar);
188 
189 		// grids
190 		layout.numColumns ++;
191 		this.gridsLabel = new Label(this.toolbar,SWT.NONE);
192 		this.gridsLabel.setText(TuxGuitar.getProperty("matrix.grids"));
193 
194 		layout.numColumns ++;
195 		final Combo divisionsCombo = new Combo(this.toolbar, SWT.DROP_DOWN | SWT.READ_ONLY);
196 		divisionsCombo.setLayoutData(new GridData(SWT.FILL,SWT.CENTER,false, true));
197 		for(int i = 0; i < DIVISIONS.length; i ++){
198 			divisionsCombo.add(Integer.toString(DIVISIONS[i]));
199 			if(this.grids == DIVISIONS[i]){
200 				divisionsCombo.select(i);
201 			}
202 		}
203 		if(this.grids == 0){
204 			divisionsCombo.select(0);
205 		}
206 		divisionsCombo.addSelectionListener(new SelectionAdapter() {
207 			public void widgetSelected(SelectionEvent e) {
208 				int index = divisionsCombo.getSelectionIndex();
209 				if(index >= 0 && index < DIVISIONS.length){
210 					setGrids(DIVISIONS[index]);
211 				}
212 			}
213 		});
214 
215 		// settings
216 		layout.numColumns ++;
217 		this.settings = new Button(this.toolbar, SWT.PUSH);
218 		this.settings.setImage(TuxGuitar.instance().getIconManager().getSettings());
219 		this.settings.setToolTipText(TuxGuitar.getProperty("settings"));
220 		this.settings.setLayoutData(new GridData(SWT.RIGHT,SWT.FILL,true,true));
221 		this.settings.addSelectionListener(new SelectionAdapter() {
222 			public void widgetSelected(SelectionEvent e) {
223 				configure();
224 			}
225 		});
226 
227 		this.toolbar.setLayout(layout);
228 		this.toolbar.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
229 	}
230 
makeToolSeparator(Composite parent)231 	private void makeToolSeparator(Composite parent){
232 		Label separator = new Label(parent,SWT.SEPARATOR);
233 		separator.setLayoutData(new GridData(20,20));
234 	}
235 
loadDurationImage(boolean force)236 	private void loadDurationImage(boolean force) {
237 		int duration = TuxGuitar.instance().getTablatureEditor().getTablature().getCaret().getDuration().getValue();
238 		if(force || this.duration != duration){
239 			this.duration = duration;
240 			this.durationLabel.setImage(TuxGuitar.instance().getIconManager().getDuration(this.duration));
241 		}
242 	}
243 
initEditor()244 	public void initEditor(){
245 		this.selection = -1;
246 		this.editor = new Composite(this.composite,SWT.DOUBLE_BUFFERED | SWT.BORDER  | SWT.H_SCROLL | SWT.V_SCROLL);
247 		this.editor.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
248 		this.editor.setLayout(new FillLayout());
249 		this.editor.setFocus();
250 		this.editor.addPaintListener(this.listener);
251 		this.editor.addMouseListener(this.listener);
252 		this.editor.addMouseMoveListener(this.listener);
253 		this.editor.addMouseTrackListener(this.listener);
254 		this.editor.getHorizontalBar().setIncrement(SCROLL_INCREMENT);
255 		this.editor.getHorizontalBar().addListener(SWT.Selection, new Listener() {
256 			public void handleEvent(Event event) {
257 				redrawLocked();
258 			}
259 		});
260 		this.editor.getVerticalBar().setIncrement(SCROLL_INCREMENT);
261 		this.editor.getVerticalBar().addListener(SWT.Selection, new Listener() {
262 			public void handleEvent(Event event) {
263 				redrawLocked();
264 			}
265 		});
266 	}
267 
updateScroll()268 	protected void updateScroll(){
269 		if( this.clientArea != null ){
270 			int borderWidth = this.editor.getBorderWidth();
271 			ScrollBar vBar = this.editor.getVerticalBar();
272 			ScrollBar hBar = this.editor.getHorizontalBar();
273 			vBar.setMaximum(Math.round(this.height + (borderWidth * 2)));
274 			vBar.setThumb(Math.round(Math.min(this.height + (borderWidth * 2), this.clientArea.height)));
275 			hBar.setMaximum(Math.round(this.width + (borderWidth * 2)));
276 			hBar.setThumb(Math.round(Math.min(this.width + (borderWidth * 2), this.clientArea.width)));
277 		}
278 	}
279 
getValueAt(float y)280 	protected int getValueAt(float y){
281 		if(this.clientArea == null || (y - BORDER_HEIGHT) < 0 || y + BORDER_HEIGHT > this.clientArea.height){
282 			return -1;
283 		}
284 		int scroll = this.editor.getVerticalBar().getSelection();
285 		int value = (this.maxNote -  ((int)(  (y + scroll - BORDER_HEIGHT)  / this.lineHeight)) );
286 		return value;
287 	}
288 
getStartAt(float x)289 	protected long getStartAt(float x){
290 		TGMeasure measure = getMeasure();
291 		float posX = (x + this.editor.getHorizontalBar().getSelection());
292 		long start =(long) (measure.getStart() + (((posX - this.leftSpacing) * measure.getLength()) / (this.timeWidth * measure.getTimeSignature().getNumerator())));
293 		return start;
294 	}
295 
paintEditor(TGPainter painter)296 	protected void paintEditor(TGPainter painter){
297 		if(!TuxGuitar.instance().getPlayer().isRunning()){
298 			this.resetPlayed();
299 		}
300 
301 		this.disposeSelectionBuffer();
302 		this.clientArea = this.editor.getClientArea();
303 
304 		if( this.clientArea != null ){
305 			Image buffer = getBuffer();
306 
307 			this.width = this.bufferWidth;
308 			this.height = (this.bufferHeight + (BORDER_HEIGHT *2));
309 
310 			this.updateScroll();
311 			int scrollX = this.editor.getHorizontalBar().getSelection();
312 			int scrollY = this.editor.getVerticalBar().getSelection();
313 
314 			painter.drawImage(buffer,-scrollX,(BORDER_HEIGHT - scrollY));
315 			this.paintMeasure(painter,(-scrollX), (BORDER_HEIGHT - scrollY) );
316 			this.paintBorders(painter,(-scrollX),0);
317 			this.paintPosition(painter,(-scrollX),0);
318 
319 			this.paintSelection(painter, (-scrollX), (BORDER_HEIGHT - scrollY) );
320 		}
321 	}
322 
getBuffer()323 	protected Image getBuffer(){
324 		if( this.clientArea != null ){
325 			this.bufferDisposer.update(this.clientArea.width, this.clientArea.height);
326 			if(this.buffer == null || this.buffer.isDisposed()){
327 				String[] names = null;
328 				TGMeasure measure = getMeasure();
329 				boolean percussion = measure.getTrack().isPercussionTrack();
330 				this.maxNote = 0;
331 				this.minNote = 127;
332 				if(percussion){
333 					names = new String[PERCUSSIONS.length];
334 					for(int i = 0; i < names.length;i ++){
335 						this.minNote = Math.min(this.minNote,PERCUSSIONS[i].getValue());
336 						this.maxNote = Math.max(this.maxNote,PERCUSSIONS[i].getValue());
337 						names[i] = PERCUSSIONS[names.length - i -1].getName();
338 					}
339 				}else{
340 					for(int sNumber = 1; sNumber <= measure.getTrack().stringCount();sNumber ++){
341 						TGString string = measure.getTrack().getString(sNumber);
342 						this.minNote = Math.min(this.minNote,string.getValue());
343 						this.maxNote = Math.max(this.maxNote,(string.getValue() + 20));
344 					}
345 					names = new String[this.maxNote - this.minNote + 1];
346 					for(int i = 0; i < names.length;i ++){
347 						names[i] = (NOTE_NAMES[ (this.maxNote - i) % 12] + ((this.maxNote - i) / 12 ) );
348 					}
349 				}
350 
351 				int minimumNameWidth = 110;
352 				int minimumNameHeight = 0;
353 				TGPainter painter = new TGPainter(new GC(this.dialog.getDisplay()));
354 				painter.setFont(this.config.getFont());
355 				for(int i = 0; i < names.length;i ++){
356 					Point size = painter.getStringExtent(names[i]);
357 					if( size.x > minimumNameWidth ){
358 						minimumNameWidth = size.x;
359 					}
360 					if( size.y  > minimumNameHeight ){
361 						minimumNameHeight = size.y ;
362 					}
363 				}
364 				painter.dispose();
365 
366 				int cols = measure.getTimeSignature().getNumerator();
367 				int rows = (this.maxNote - this.minNote);
368 
369 				this.leftSpacing = minimumNameWidth + 10;
370 				this.lineHeight = Math.max(minimumNameHeight,( (this.clientArea.height - (BORDER_HEIGHT * 2.0f))/ (rows + 1.0f)));
371 				this.timeWidth = Math.max((10 * (TGDuration.SIXTY_FOURTH / measure.getTimeSignature().getDenominator().getValue())),( (this.clientArea.width-this.leftSpacing) / cols)  );
372 				this.bufferWidth = this.leftSpacing + (this.timeWidth * cols);
373 				this.bufferHeight = (this.lineHeight * (rows + 1));
374 				this.buffer = new Image(this.editor.getDisplay(),Math.round( this.bufferWidth),Math.round(this.bufferHeight));
375 
376 				painter = new TGPainter(new GC(this.buffer));
377 				painter.setFont(this.config.getFont());
378 				painter.setForeground(this.config.getColorForeground());
379 				for(int i = 0; i <= rows; i++){
380 					painter.setBackground(this.config.getColorLine( i % 2 ) );
381 					painter.initPath(TGPainter.PATH_FILL);
382 					painter.setAntialias(false);
383 					painter.addRectangle(0 ,(i * this.lineHeight),this.bufferWidth ,this.lineHeight);
384 					painter.closePath();
385 					painter.drawString(names[i],5,( Math.round( (i * this.lineHeight) ) +  Math.round(  (this.lineHeight - minimumNameHeight) / 2 )  ) );
386 				}
387 				for(int i = 0; i < cols; i ++){
388 					float colX = this.leftSpacing + (i * this.timeWidth);
389 					float divisionWidth = ( this.timeWidth / this.grids );
390 					for( int j = 0; j < this.grids; j ++ ){
391 						painter.setLineStyle( j == 0 ? SWT.LINE_SOLID : SWT.LINE_DOT);
392 						painter.initPath();
393 						painter.setAntialias(false);
394 						painter.moveTo(Math.round( colX + (j * divisionWidth) ),0);
395 						painter.lineTo(Math.round( colX + (j * divisionWidth) ),this.bufferHeight);
396 						painter.closePath();
397 					}
398 				}
399 				painter.dispose();
400 			}
401 		}
402 		return this.buffer;
403 	}
404 
paintMeasure(TGPainter painter,float fromX, float fromY)405 	protected void paintMeasure(TGPainter painter,float fromX, float fromY){
406 		if( this.clientArea != null ){
407 			TGMeasure measure = getMeasure();
408 			if(measure != null){
409 				Iterator it = measure.getBeats().iterator();
410 				while(it.hasNext()){
411 					TGBeat beat = (TGBeat)it.next();
412 					paintBeat(painter, measure, beat, fromX, fromY);
413 				}
414 			}
415 		}
416 	}
417 
paintBeat(TGPainter painter,TGMeasure measure,TGBeat beat,float fromX, float fromY)418 	protected void paintBeat(TGPainter painter,TGMeasure measure,TGBeat beat,float fromX, float fromY){
419 		if( this.clientArea != null ){
420 			int minimumY = BORDER_HEIGHT;
421 			int maximumY = (this.clientArea.height - BORDER_HEIGHT);
422 
423 			for( int v = 0; v < beat.countVoices(); v ++ ){
424 				TGVoice voice = beat.getVoice(v);
425 				for( int i = 0 ; i < voice.countNotes() ; i ++){
426 					TGNoteImpl note = (TGNoteImpl)voice.getNote(i);
427 					float x1 = (fromX + this.leftSpacing + (((beat.getStart() - measure.getStart()) * (this.timeWidth * measure.getTimeSignature().getNumerator())) / measure.getLength()) + 1);
428 					float y1 = (fromY + (((this.maxNote - this.minNote) - (note.getRealValue() - this.minNote)) * this.lineHeight) + 1 );
429 					float x2 = (x1 + ((voice.getDuration().getTime() * this.timeWidth) / measure.getTimeSignature().getDenominator().getTime()) - 2 );
430 					float y2 = (y1 + this.lineHeight - 2 );
431 
432 					if( y1 >= maximumY || y2 <= minimumY){
433 						continue;
434 					}
435 
436 					y1 = ( y1 < minimumY ? minimumY : y1 );
437 					y2 = ( y2 > maximumY ? maximumY : y2 );
438 
439 					if((x2 - x1) > 0 && (y2 - y1) > 0){
440 						painter.setBackground( (note.getBeatImpl().isPlaying(TuxGuitar.instance().getTablatureEditor().getTablature().getViewLayout()) ? this.config.getColorPlay():this.config.getColorNote() ) );
441 						painter.initPath(TGPainter.PATH_FILL);
442 						painter.setAntialias(false);
443 						painter.addRectangle(x1,y1, (x2 - x1), (y2 - y1));
444 						painter.closePath();
445 					}
446 				}
447 			}
448 		}
449 	}
450 
paintBorders(TGPainter painter,float fromX, float fromY)451 	protected void paintBorders(TGPainter painter,float fromX, float fromY){
452 		if( this.clientArea != null ){
453 			painter.setBackground(this.config.getColorBorder());
454 			painter.initPath(TGPainter.PATH_FILL);
455 			painter.setAntialias(false);
456 			painter.addRectangle(fromX,fromY,this.bufferWidth ,BORDER_HEIGHT);
457 			painter.addRectangle(fromX,fromY + (this.clientArea.height - BORDER_HEIGHT),this.bufferWidth ,BORDER_HEIGHT);
458 			painter.closePath();
459 
460 			painter.initPath();
461 			painter.setAntialias(false);
462 			painter.addRectangle(fromX,fromY,this.width,this.clientArea.height);
463 			painter.closePath();
464 		}
465 	}
466 
paintPosition(TGPainter painter,float fromX, float fromY)467 	protected void paintPosition(TGPainter painter,float fromX, float fromY){
468 		if( this.clientArea != null && !TuxGuitar.instance().getPlayer().isRunning()){
469 			Caret caret = getCaret();
470 			TGMeasure measure = getMeasure();
471 			TGBeat beat = caret.getSelectedBeat();
472 			if(beat != null){
473 				float x = (((beat.getStart() - measure.getStart()) * (this.timeWidth * measure.getTimeSignature().getNumerator())) / measure.getLength());
474 				float width = ((beat.getVoice(caret.getVoice()).getDuration().getTime() * this.timeWidth) / measure.getTimeSignature().getDenominator().getTime());
475 				painter.setBackground(this.config.getColorPosition());
476 				painter.initPath(TGPainter.PATH_FILL);
477 				painter.setAntialias(false);
478 				painter.addRectangle(fromX + (this.leftSpacing + x),fromY , width,BORDER_HEIGHT);
479 				painter.closePath();
480 
481 				painter.initPath(TGPainter.PATH_FILL);
482 				painter.setAntialias(false);
483 				painter.addRectangle(fromX + (this.leftSpacing + x),fromY + (this.clientArea.height - BORDER_HEIGHT), width,BORDER_HEIGHT);
484 				painter.closePath();
485 			}
486 		}
487 	}
488 
paintSelection(TGPainter painter, float fromX, float fromY)489 	protected void paintSelection(TGPainter painter, float fromX, float fromY){
490 		if( !this.selectionPaintDisabled && this.clientArea != null && !TuxGuitar.instance().getPlayer().isRunning()){
491 			selectionFinish();
492 			if(this.selection >= 0){
493 				this.selectionPaintDisabled = true;
494 
495 				int x = Math.round( fromX );
496 				int y = Math.round( fromY + ((this.maxNote - this.selection) * this.lineHeight)  );
497 				int width = Math.round( this.bufferWidth );
498 				int height = Math.round( this.lineHeight );
499 
500 				Image selectionArea = new Image(this.editor.getDisplay(),width,height);
501 				painter.copyArea(selectionArea,x,y);
502 				painter.setAlpha(100);
503 				painter.setBackground(this.config.getColorLine(2));
504 				painter.initPath(TGPainter.PATH_FILL);
505 				painter.setAntialias(false);
506 				painter.addRectangle(x,y,width,height);
507 				painter.closePath();
508 
509 				this.selectionX = x;
510 				this.selectionY = y;
511 				this.selectionBackBuffer = selectionArea;
512 				this.selectionPaintDisabled = false;
513 			}
514 		}
515 	}
516 
updateSelection(float y)517 	protected void updateSelection(float y){
518 		if(!TuxGuitar.instance().getPlayer().isRunning()){
519 			int selection = getValueAt(y);
520 
521 			if(this.selection != selection){
522 				this.selection = selection;
523 
524 				int scrollX = this.editor.getHorizontalBar().getSelection();
525 				int scrollY = this.editor.getVerticalBar().getSelection();
526 
527 				TGPainter painter = new TGPainter(new GC(this.editor));
528 				this.paintSelection(painter, (-scrollX), (BORDER_HEIGHT - scrollY) );
529 				painter.dispose();
530 			}
531 		}
532 	}
533 
selectionFinish()534 	public void selectionFinish(){
535 		if(this.selectionBackBuffer != null && !this.selectionBackBuffer.isDisposed()){
536 			TGPainter painter = new TGPainter(new GC(this.editor));
537 			painter.drawImage(this.selectionBackBuffer,this.selectionX, this.selectionY);
538 			painter.dispose();
539 		}
540 		disposeSelectionBuffer();
541 	}
542 
disposeSelectionBuffer()543 	protected void disposeSelectionBuffer(){
544 		if(this.selectionBackBuffer != null && !this.selectionBackBuffer.isDisposed()){
545 			this.selectionBackBuffer.dispose();
546 			this.selectionBackBuffer = null;
547 		}
548 	}
549 
hit(float x, float y)550 	protected void hit(float x, float y){
551 		if(!TuxGuitar.instance().getPlayer().isRunning()){
552 			TGMeasure measure = getMeasure();
553 			Caret caret = getCaret();
554 			int value = getValueAt(y);
555 			long start = getStartAt(x);
556 
557 			if(start >= measure.getStart() && start < (measure.getStart() + measure.getLength())){
558 				caret.update(caret.getTrack().getNumber(),start,caret.getStringNumber());
559 				TuxGuitar.instance().updateCache(true);
560 			}
561 			if(value >= this.minNote || value <= this.maxNote){
562 				if(start >= measure.getStart()){
563 					TGVoice voice = TuxGuitar.instance().getSongManager().getMeasureManager().getVoiceIn(measure, start, caret.getVoice());
564 					if( voice != null ){
565 						if(!removeNote(voice.getBeat(), value)){
566 							addNote(voice.getBeat(), start, value);
567 						}
568 					}
569 				}else{
570 					play(value);
571 				}
572 			}
573 		}
574 	}
575 
removeNote(TGBeat beat,int value)576 	private boolean removeNote(TGBeat beat,int value) {
577 		Caret caret = TuxGuitar.instance().getTablatureEditor().getTablature().getCaret();
578 		TGMeasure measure = getMeasure();
579 
580 		for(int v = 0; v < beat.countVoices(); v ++){
581 			TGVoice voice = beat.getVoice( v );
582 			Iterator it = voice.getNotes().iterator();
583 			while (it.hasNext()) {
584 				TGNoteImpl note = (TGNoteImpl) it.next();
585 				if (note.getRealValue() == value) {
586 					caret.update(measure.getTrack().getNumber(),beat.getStart(),note.getString());
587 
588 					//comienza el undoable
589 					UndoableMeasureGeneric undoable = UndoableMeasureGeneric.startUndo();
590 
591 					TGSongManager manager = TuxGuitar.instance().getSongManager();
592 					manager.getMeasureManager().removeNote(note);
593 
594 					//termia el undoable
595 					TuxGuitar.instance().getUndoableManager().addEdit(undoable.endUndo());
596 					TuxGuitar.instance().getFileHistory().setUnsavedFile();
597 
598 					this.afterAction();
599 
600 					return true;
601 				}
602 			}
603 		}
604 		return false;
605 	}
606 
addNote(TGBeat beat,long start, int value)607 	private boolean addNote(TGBeat beat,long start, int value) {
608 		if(beat != null){
609 			TGMeasure measure = getMeasure();
610 			Caret caret = TuxGuitar.instance().getTablatureEditor().getTablature().getCaret();
611 
612 			List strings = measure.getTrack().getStrings();
613 			for(int i = 0;i < strings.size();i ++){
614 				TGString string = (TGString)strings.get(i);
615 				if(value >= string.getValue()){
616 					boolean emptyString = true;
617 
618 					for(int v = 0; v < beat.countVoices(); v ++){
619 						TGVoice voice = beat.getVoice( v );
620 						Iterator it = voice.getNotes().iterator();
621 						while (it.hasNext()) {
622 							TGNoteImpl note = (TGNoteImpl) it.next();
623 							if (note.getString() == string.getNumber()) {
624 								emptyString = false;
625 								break;
626 							}
627 						}
628 					}
629 					if(emptyString){
630 						TGSongManager manager = TuxGuitar.instance().getSongManager();
631 
632 						//comienza el undoable
633 						UndoableMeasureGeneric undoable = UndoableMeasureGeneric.startUndo();
634 
635 						TGNote note = manager.getFactory().newNote();
636 						note.setValue((value - string.getValue()));
637 						note.setVelocity(caret.getVelocity());
638 						note.setString(string.getNumber());
639 
640 						TGDuration duration = manager.getFactory().newDuration();
641 						caret.getDuration().copy(duration);
642 
643 						manager.getMeasureManager().addNote(beat,note,duration,start,caret.getVoice());
644 
645 						caret.moveTo(caret.getTrack(),caret.getMeasure(),note.getVoice().getBeat(),note.getString());
646 
647 						//termia el undoable
648 						TuxGuitar.instance().getUndoableManager().addEdit(undoable.endUndo());
649 						TuxGuitar.instance().getFileHistory().setUnsavedFile();
650 
651 						//reprodusco las notas en el pulso
652 						caret.getSelectedBeat().play();
653 
654 						this.afterAction();
655 
656 						return true;
657 					}
658 				}
659 			}
660 		}
661 		return false;
662 	}
663 
afterAction()664 	protected void afterAction() {
665 		TuxGuitar.instance().getTablatureEditor().getTablature().getViewLayout().fireUpdate(getMeasure().getNumber());
666 		TuxGuitar.instance().updateCache(true);
667 		this.editor.redraw();
668 	}
669 
play(final int value)670 	protected void play(final int value){
671 		new Thread(new Runnable() {
672 			public void run() {
673 				TGTrack track = getMeasure().getTrack();
674 				int volume = TGChannel.DEFAULT_VOLUME;
675 				int balance = TGChannel.DEFAULT_BALANCE;
676 				int chorus = track.getChannel().getChorus();
677 				int reverb = track.getChannel().getReverb();
678 				int phaser = track.getChannel().getPhaser();
679 				int tremolo = track.getChannel().getTremolo();
680 				int channel = track.getChannel().getChannel();
681 				int program = track.getChannel().getInstrument();
682 				int[][] beat = new int[][]{ new int[]{ (track.getOffset() + value) , TGVelocities.DEFAULT } };
683 				TuxGuitar.instance().getPlayer().playBeat(channel,program, volume, balance,chorus,reverb,phaser,tremolo,beat);
684 			}
685 		}).start();
686 	}
687 
loadGrids()688 	protected int loadGrids(){
689 		int grids = TuxGuitar.instance().getConfig().getIntConfigValue(TGConfigKeys.MATRIX_GRIDS);
690 		// check if is valid value
691 		for(int i = 0 ; i < DIVISIONS.length ; i ++ ){
692 			if(grids == DIVISIONS[i]){
693 				return grids;
694 			}
695 		}
696 		return DIVISIONS[1];
697 	}
698 
setGrids(int grids)699 	protected void setGrids(int grids){
700 		this.grids = grids;
701 		this.disposeBuffer();
702 		this.redrawLocked();
703 	}
704 
getGrids()705 	public int getGrids(){
706 		return this.grids;
707 	}
708 
getMeasure()709 	protected TGMeasure getMeasure(){
710 		if(TuxGuitar.instance().getPlayer().isRunning()){
711 			TGMeasure measure = TuxGuitar.instance().getEditorCache().getPlayMeasure();
712 			if(measure != null){
713 				return measure;
714 			}
715 		}
716 		return TuxGuitar.instance().getTablatureEditor().getTablature().getCaret().getMeasure();
717 	}
718 
getCaret()719 	protected Caret getCaret(){
720 		return TuxGuitar.instance().getTablatureEditor().getTablature().getCaret();
721 	}
722 
isDisposed()723 	public boolean isDisposed(){
724 		return (this.dialog == null || this.dialog.isDisposed());
725 	}
726 
resetPlayed()727 	protected void resetPlayed(){
728 		this.playedBeat = null;
729 		this.playedMeasure = -1;
730 		this.playedTrack = -1;
731 	}
732 
redrawLocked()733 	public void redrawLocked(){
734 		if(!TuxGuitar.instance().isLocked()){
735 			TuxGuitar.instance().lock();
736 			this.redraw();
737 			TuxGuitar.instance().unlock();
738 		}
739 	}
740 
redraw()741 	public void redraw(){
742 		if(!isDisposed() && !TuxGuitar.instance().isLocked()){
743 			//TuxGuitar.instance().lock();
744 
745 			this.editor.redraw();
746 			this.loadDurationImage(false);
747 
748 			//TuxGuitar.instance().unlock();
749 		}
750 	}
751 
redrawPlayingMode()752 	public void redrawPlayingMode(){
753 		if(!isDisposed() && !TuxGuitar.instance().isLocked() && TuxGuitar.instance().getPlayer().isRunning()){
754 			//TuxGuitar.instance().lock();
755 
756 			TGMeasure measure = TuxGuitar.instance().getEditorCache().getPlayMeasure();
757 			TGBeat beat = TuxGuitar.instance().getEditorCache().getPlayBeat();
758 			if(measure != null && beat != null){
759 				int currentMeasure = measure.getNumber();
760 				int currentTrack = measure.getTrack().getNumber();
761 				boolean changed = (currentMeasure != this.playedMeasure || currentTrack != this.playedTrack);
762 				if(changed){
763 					this.resetPlayed();
764 					this.editor.redraw();
765 				}
766 				else{
767 					TGPainter painter = new TGPainter(new GC(this.editor));
768 					int scrollX = this.editor.getHorizontalBar().getSelection();
769 					int scrollY = this.editor.getVerticalBar().getSelection();
770 					if(this.playedBeat != null){
771 						this.paintBeat(painter,measure,this.playedBeat,(-scrollX), (BORDER_HEIGHT - scrollY));
772 					}
773 					this.paintBeat(painter,measure,beat,(-scrollX), (BORDER_HEIGHT - scrollY));
774 					painter.dispose();
775 				}
776 				this.playedMeasure = currentMeasure;
777 				this.playedTrack = currentTrack;
778 				this.playedBeat = beat;
779 			}
780 			//TuxGuitar.instance().unlock();
781 		}
782 	}
783 
configure()784 	protected void configure(){
785 		this.config.configure(this.dialog);
786 		this.disposeBuffer();
787 		this.redrawLocked();
788 	}
789 
layout()790 	private void layout(){
791 		if( !isDisposed() ){
792 			this.toolbar.layout();
793 			this.editor.layout();
794 			this.composite.layout(true,true);
795 		}
796 	}
797 
loadIcons()798 	public void loadIcons(){
799 		if( !isDisposed() ){
800 			this.dialog.setImage(TuxGuitar.instance().getIconManager().getAppIcon());
801 			this.settings.setImage(TuxGuitar.instance().getIconManager().getSettings());
802 			this.loadDurationImage(true);
803 			this.layout();
804 			this.redraw();
805 		}
806 	}
807 
loadProperties()808 	public void loadProperties() {
809 		if( !isDisposed() ){
810 			this.dialog.setText(TuxGuitar.getProperty("matrix.editor"));
811 			this.gridsLabel.setText(TuxGuitar.getProperty("matrix.grids"));
812 			this.settings.setToolTipText(TuxGuitar.getProperty("settings"));
813 			this.disposeBuffer();
814 			this.layout();
815 			this.redraw();
816 		}
817 	}
818 
dispose()819 	public void dispose(){
820 		if(!isDisposed()){
821 			this.dialog.dispose();
822 		}
823 	}
824 
disposeBuffer()825 	protected void disposeBuffer(){
826 		if(this.buffer != null && !this.buffer.isDisposed()){
827 			this.buffer.dispose();
828 			this.buffer = null;
829 		}
830 	}
831 
dispose(Resource[] resources)832 	protected void dispose(Resource[] resources){
833 		if(resources != null){
834 			for(int i = 0; i < resources.length; i ++){
835 				dispose(resources[i]);
836 			}
837 		}
838 	}
839 
dispose(Resource resource)840 	protected void dispose(Resource resource){
841 		if(resource != null){
842 			resource.dispose();
843 		}
844 	}
845 
disposeAll()846 	protected void disposeAll(){
847 		this.disposeBuffer();
848 		this.disposeSelectionBuffer();
849 		this.config.dispose();
850 	}
851 
getEditor()852 	protected Composite getEditor(){
853 		return this.editor;
854 	}
855 
856 	protected class BufferDisposer{
857 		private int numerator;
858 		private int denominator;
859 		private int track;
860 		private boolean percussion;
861 
862 		private int width;
863 		private int height;
864 
update(int width, int height)865 		public void update(int width, int height){
866 			TGMeasure measure = getMeasure();
867 			int track = measure.getTrack().getNumber();
868 			int numerator = measure.getTimeSignature().getNumerator();
869 			int denominator = measure.getTimeSignature().getDenominator().getValue();
870 			boolean percussion = measure.getTrack().isPercussionTrack();
871 			if(width != this.width || height != this.height || this.track != track || this.numerator != numerator || this.denominator != denominator || this.percussion != percussion){
872 				disposeBuffer();
873 			}
874 			this.track = track;
875 			this.numerator = numerator;
876 			this.denominator = denominator;
877 			this.percussion = percussion;
878 			this.width = width;
879 			this.height = height;
880 		}
881 	}
882 
883 	protected class DisposeListenerImpl implements DisposeListener{
widgetDisposed(DisposeEvent e)884 		public void widgetDisposed(DisposeEvent e) {
885 			disposeAll();
886 		}
887 	}
888 
889 	protected class MatrixListener implements PaintListener,MouseListener,MouseMoveListener,MouseTrackListener {
890 
MatrixListener()891 		public MatrixListener(){
892 			super();
893 		}
894 
paintControl(PaintEvent e)895 		public void paintControl(PaintEvent e) {
896 			if(!TuxGuitar.instance().isLocked()){
897 				TuxGuitar.instance().lock();
898 				TGPainter painter = new TGPainter(e.gc);
899 				paintEditor(painter);
900 				TuxGuitar.instance().unlock();
901 			}
902 		}
903 
mouseUp(MouseEvent e)904 		public void mouseUp(MouseEvent e) {
905 			getEditor().setFocus();
906 			if(e.button == 1){
907 				if(!TuxGuitar.instance().isLocked() && !ActionLock.isLocked()){
908 					ActionLock.lock();
909 					hit(e.x,e.y);
910 					ActionLock.unlock();
911 				}
912 			}
913 		}
914 
mouseMove(MouseEvent e)915 		public void mouseMove(MouseEvent e) {
916 			if(!TuxGuitar.instance().isLocked() && !ActionLock.isLocked()){
917 				updateSelection(e.y);
918 			}
919 		}
920 
mouseExit(MouseEvent e)921 		public void mouseExit(MouseEvent e) {
922 			if(!TuxGuitar.instance().isLocked() && !ActionLock.isLocked()){
923 				updateSelection(-1);
924 			}
925 		}
926 
mouseEnter(MouseEvent e)927 		public void mouseEnter(MouseEvent e) {
928 			if(!TuxGuitar.instance().isLocked() && !ActionLock.isLocked()){
929 				redrawLocked();
930 			}
931 		}
932 
mouseDoubleClick(MouseEvent e)933 		public void mouseDoubleClick(MouseEvent e) {
934 			// TODO Auto-generated method stub
935 		}
936 
mouseDown(MouseEvent e)937 		public void mouseDown(MouseEvent e) {
938 			// TODO Auto-generated method stub
939 		}
940 
mouseHover(MouseEvent e)941 		public void mouseHover(MouseEvent e) {
942 			// TODO Auto-generated method stub
943 		}
944 	}
945 
doRedraw(int type)946 	public void doRedraw(int type) {
947 		if( type == TGRedrawListener.NORMAL ){
948 			this.redraw();
949 		}else if( type == TGRedrawListener.PLAYING_NEW_BEAT ){
950 			this.redrawPlayingMode();
951 		}
952 	}
953 }
954