1 /*
2  * Hydrogen
3  * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4  *
5  * http://www.hydrogen-music.org
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY, without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 
23 #include "PatternEditorInstrumentList.h"
24 
25 #include <hydrogen/audio_engine.h>
26 #include <hydrogen/event_queue.h>
27 #include <hydrogen/hydrogen.h>
28 #include <hydrogen/basics/instrument.h>
29 #include <hydrogen/basics/instrument_list.h>
30 #include <hydrogen/basics/note.h>
31 #include <hydrogen/basics/pattern.h>
32 #include <hydrogen/basics/pattern_list.h>
33 #include <hydrogen/Preferences.h>
34 #include <hydrogen/basics/song.h>
35 #include <hydrogen/LocalFileMng.h>
36 using namespace H2Core;
37 
38 #include "UndoActions.h"
39 #include "PatternEditorPanel.h"
40 #include "InstrumentEditor/InstrumentEditorPanel.h"
41 #include "DrumPatternEditor.h"
42 #include "../HydrogenApp.h"
43 #include "../Mixer/Mixer.h"
44 #include "../Widgets/Button.h"
45 
46 #include <QtGui>
47 #if QT_VERSION >= 0x050000
48 #  include <QtWidgets>
49 #endif
50 #include <QClipboard>
51 #include <cassert>
52 #include <algorithm> // for std::min
53 
54 using namespace std;
55 
56 const char* InstrumentLine::__class_name = "InstrumentLine";
57 
InstrumentLine(QWidget * pParent)58 InstrumentLine::InstrumentLine(QWidget* pParent)
59 	: PixmapWidget(pParent, __class_name)
60 	, m_bIsSelected(false)
61 {
62 	int h = Preferences::get_instance()->getPatternEditorGridHeight();
63 	setFixedSize(181, h);
64 
65 	m_pNameLbl = new QLabel(this);
66 	m_pNameLbl->resize( 145, h );
67 	m_pNameLbl->move( 10, 1 );
68 	QFont nameFont;
69 	nameFont.setPointSize( 10 );
70 	nameFont.setBold( true );
71 	m_pNameLbl->setFont(nameFont);
72 
73 	m_pMuteBtn = new ToggleButton(
74 			this,
75 			"/mixerPanel/btn_mute_on.png",
76 			"/mixerPanel/btn_mute_off.png",
77 			"/mixerPanel/btn_mute_off.png",
78 			QSize( 18, 13 )
79 	);
80 	m_pMuteBtn->move( 145, 5 );
81 	m_pMuteBtn->setPressed(false);
82 	m_pMuteBtn->setToolTip( tr("Mute instrument") );
83 	connect(m_pMuteBtn, SIGNAL(clicked(Button*)), this, SLOT(muteClicked()));
84 
85 	m_pSoloBtn = new ToggleButton(
86 			this,
87 			"/mixerPanel/btn_solo_on.png",
88 			"/mixerPanel/btn_solo_off.png",
89 			"/mixerPanel/btn_solo_off.png",
90 			QSize( 18, 13 )
91 	);
92 	m_pSoloBtn->move( 163, 5 );
93 	m_pSoloBtn->setPressed(false);
94 	m_pSoloBtn->setToolTip( tr("Solo") );
95 	connect(m_pSoloBtn, SIGNAL(clicked(Button*)), this, SLOT(soloClicked()));
96 
97 
98 
99 	// Popup menu
100 	m_pFunctionPopup = new QMenu( this );
101 	m_pFunctionPopup->addAction( tr( "Clear notes" ), this, SLOT( functionClearNotes() ) );
102 
103 	m_pFunctionPopupSub = new QMenu( tr( "Fill notes ..." ), m_pFunctionPopup );
104 	m_pFunctionPopupSub->addAction( tr( "Fill all notes" ), this, SLOT( functionFillAllNotes() ) );
105 	m_pFunctionPopupSub->addAction( tr( "Fill 1/2 notes" ), this, SLOT( functionFillEveryTwoNotes() ) );
106 	m_pFunctionPopupSub->addAction( tr( "Fill 1/3 notes" ), this, SLOT( functionFillEveryThreeNotes() ) );
107 	m_pFunctionPopupSub->addAction( tr( "Fill 1/4 notes" ), this, SLOT( functionFillEveryFourNotes() ) );
108 	m_pFunctionPopupSub->addAction( tr( "Fill 1/6 notes" ), this, SLOT( functionFillEverySixNotes() ) );
109 	m_pFunctionPopupSub->addAction( tr( "Fill 1/8 notes" ), this, SLOT( functionFillEveryEightNotes() ) );
110 	m_pFunctionPopupSub->addAction( tr( "Fill 1/12 notes" ), this, SLOT( functionFillEveryTwelveNotes() ) );
111 	m_pFunctionPopupSub->addAction( tr( "Fill 1/16 notes" ), this, SLOT( functionFillEverySixteenNotes() ) );
112 	m_pFunctionPopup->addMenu( m_pFunctionPopupSub );
113 
114 	m_pFunctionPopup->addAction( tr( "Randomize velocity" ), this, SLOT( functionRandomizeVelocity() ) );
115 	m_pFunctionPopup->addSeparator();
116 
117 	m_pCopyPopupSub = new QMenu( tr( "Copy notes ..." ), m_pFunctionPopup );
118 	m_pCopyPopupSub->addAction( tr( "Only for this pattern" ), this, SLOT( functionCopyInstrumentPattern() ) );
119 	m_pCopyPopupSub->addAction( tr( "For all patterns" ), this, SLOT( functionCopyAllInstrumentPatterns() ) );
120 	m_pFunctionPopup->addMenu( m_pCopyPopupSub );
121 
122 	m_pPastePopupSub = new QMenu( tr( "Paste notes ..." ), m_pFunctionPopup );
123 	m_pPastePopupSub->addAction( tr( "Only for this pattern" ), this, SLOT( functionPasteInstrumentPattern() ) );
124 	m_pPastePopupSub->addAction( tr( "For all patterns" ), this, SLOT( functionPasteAllInstrumentPatterns() ) );
125 	m_pFunctionPopup->addMenu( m_pPastePopupSub );
126 
127 	m_pFunctionPopup->addSeparator();
128 	m_pFunctionPopup->addAction( tr( "Rename instrument" ), this, SLOT( functionRenameInstrument() ) );
129 	m_pFunctionPopup->addAction( tr( "Delete instrument" ), this, SLOT( functionDeleteInstrument() ) );
130 
131 	m_bIsSelected = true;
132 	setSelected(false);
133 }
134 
135 
136 
setName(const QString & sName)137 void InstrumentLine::setName(const QString& sName)
138 {
139 	m_pNameLbl->setText(sName);
140 }
141 
142 
143 
setSelected(bool bSelected)144 void InstrumentLine::setSelected(bool bSelected)
145 {
146 	if (bSelected == m_bIsSelected) {
147 		return;
148 	}
149 	m_bIsSelected = bSelected;
150 	if (m_bIsSelected) {
151 		setPixmap( "/patternEditor/instrument_line_selected.png");
152 	}
153 	else {
154 		setPixmap( "/patternEditor/instrument_line.png");
155 	}
156 }
157 
158 
159 
setNumber(int nIndex)160 void InstrumentLine::setNumber(int nIndex)
161 {
162 	m_nInstrumentNumber = nIndex;
163 }
164 
165 
166 
setMuted(bool isMuted)167 void InstrumentLine::setMuted(bool isMuted)
168 {
169 	m_pMuteBtn->setPressed(isMuted);
170 }
171 
172 
setSoloed(bool soloed)173 void InstrumentLine::setSoloed( bool soloed )
174 {
175 	m_pSoloBtn->setPressed( soloed );
176 }
177 
178 
179 
muteClicked()180 void InstrumentLine::muteClicked()
181 {
182 	Hydrogen *pEngine = Hydrogen::get_instance();
183 	Song *pSong = pEngine->getSong();
184 	InstrumentList *pInstrList = pSong->get_instrument_list();
185 	Instrument *pInstr = pInstrList->get( m_nInstrumentNumber );
186 
187 	CoreActionController* pCoreActionController = pEngine->getCoreActionController();
188 	pCoreActionController->setStripIsMuted( m_nInstrumentNumber, !pInstr->is_muted() );
189 }
190 
191 
192 
soloClicked()193 void InstrumentLine::soloClicked()
194 {
195 	HydrogenApp::get_instance()->getMixer()->soloClicked( m_nInstrumentNumber );
196 }
197 
198 
199 
mousePressEvent(QMouseEvent * ev)200 void InstrumentLine::mousePressEvent(QMouseEvent *ev)
201 {
202 	Hydrogen::get_instance()->setSelectedInstrumentNumber( m_nInstrumentNumber );
203 	HydrogenApp::get_instance()->getPatternEditorPanel()->updatePianorollEditor();
204 
205 	if ( ev->button() == Qt::LeftButton ) {
206 		const int width = m_pMuteBtn->x() - 5; // clickable field width
207 		const float velocity = std::min((float)ev->x()/(float)width, 1.0f);
208 		const float pan_L = 0.5f;
209 		const float pan_R = 0.5f;
210 		const int nLength = -1;
211 		const float fPitch = 0.0f;
212 		Song *pSong = Hydrogen::get_instance()->getSong();
213 
214 		Instrument *pInstr = pSong->get_instrument_list()->get( m_nInstrumentNumber );
215 
216 		Note *pNote = new Note( pInstr, 0, velocity, pan_L, pan_R, nLength, fPitch);
217 		AudioEngine::get_instance()->get_sampler()->note_on(pNote);
218 	}
219 	else if (ev->button() == Qt::RightButton ) {
220 		m_pFunctionPopup->popup( QPoint( ev->globalX(), ev->globalY() ) );
221 	}
222 
223 	// propago l'evento al parent: serve per il drag&drop
224 	PixmapWidget::mousePressEvent(ev);
225 }
226 
227 
228 
getCurrentPattern()229 H2Core::Pattern* InstrumentLine::getCurrentPattern()
230 {
231 	Hydrogen *pEngine = Hydrogen::get_instance();
232 	PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
233 	assert( pPatternList != nullptr );
234 
235 	int nSelectedPatternNumber = pEngine->getSelectedPatternNumber();
236 	if ( nSelectedPatternNumber != -1 ) {
237 		Pattern* pCurrentPattern = pPatternList->get( nSelectedPatternNumber );
238 		return pCurrentPattern;
239 	}
240 	return nullptr;
241 }
242 
243 
244 
245 
functionClearNotes()246 void InstrumentLine::functionClearNotes()
247 {
248 	Hydrogen * pEngine = Hydrogen::get_instance();
249 	int selectedPatternNr = pEngine->getSelectedPatternNumber();
250 	Pattern *pPattern = getCurrentPattern();
251 	Instrument *pSelectedInstrument = pEngine->getSong()->get_instrument_list()->get( m_nInstrumentNumber );
252 
253 	std::list< Note* > noteList;
254 	const Pattern::notes_t* notes = pPattern->get_notes();
255 	FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
256 		Note *pNote = it->second;
257 		assert( pNote );
258 		if ( pNote->get_instrument() == pSelectedInstrument ) {
259 			noteList.push_back( pNote );
260 		}
261 	}
262 	if( noteList.size() > 0 ){
263 		SE_clearNotesPatternEditorAction *action = new SE_clearNotesPatternEditorAction( noteList, m_nInstrumentNumber,selectedPatternNr);
264 		HydrogenApp::get_instance()->m_pUndoStack->push( action );
265 	}
266 }
267 
functionCopyInstrumentPattern()268 void InstrumentLine::functionCopyInstrumentPattern()
269 {
270 	Hydrogen * pEngine = Hydrogen::get_instance();
271 	int selectedPatternNr = pEngine->getSelectedPatternNumber();
272 	Song *song = pEngine->getSong();
273 	assert(song);
274 
275 	// Serialize & put to clipboard
276 	QString serialized = song->copyInstrumentLineToString( selectedPatternNr, m_nInstrumentNumber );
277 	QClipboard *clipboard = QApplication::clipboard();
278 	clipboard->setText(serialized);
279 }
280 
functionCopyAllInstrumentPatterns()281 void InstrumentLine::functionCopyAllInstrumentPatterns()
282 {
283 	Hydrogen * pEngine = Hydrogen::get_instance();
284 	Song *song = pEngine->getSong();
285 	assert(song);
286 
287 	// Serialize & put to clipboard
288 	QString serialized = song->copyInstrumentLineToString( -1, m_nInstrumentNumber );
289 	QClipboard *clipboard = QApplication::clipboard();
290 	clipboard->setText(serialized);
291 }
292 
functionPasteInstrumentPattern()293 void InstrumentLine::functionPasteInstrumentPattern()
294 {
295 	Hydrogen * pEngine = Hydrogen::get_instance();
296 	int selectedPatternNr = pEngine->getSelectedPatternNumber();
297 
298 	functionPasteInstrumentPatternExec(selectedPatternNr);
299 }
300 
functionPasteAllInstrumentPatterns()301 void InstrumentLine::functionPasteAllInstrumentPatterns()
302 {
303 	functionPasteInstrumentPatternExec(-1);
304 }
305 
functionPasteInstrumentPatternExec(int patternID)306 void InstrumentLine::functionPasteInstrumentPatternExec(int patternID)
307 {
308 	Hydrogen * pEngine = Hydrogen::get_instance();
309 	Song *song = pEngine->getSong();
310 	assert(song);
311 
312 	// This is a note list for pasted notes collection
313 	std::list< Pattern* > patternList;
314 
315 	// Get from clipboard & deserialize
316 	QClipboard *clipboard = QApplication::clipboard();
317 	QString serialized = clipboard->text();
318 	if ( !song->pasteInstrumentLineFromString( serialized, patternID, m_nInstrumentNumber, patternList ) )
319 		return;
320 
321 	// Ignore empty result
322 	if (patternList.size() <= 0)
323 		return;
324 
325 	// Create action
326 	SE_pasteNotesPatternEditorAction *action = new SE_pasteNotesPatternEditorAction(patternList);
327 	HydrogenApp::get_instance()->m_pUndoStack->push(action);
328 }
329 
330 
functionFillAllNotes()331 void InstrumentLine::functionFillAllNotes(){ functionFillNotes(1); }
functionFillEveryTwoNotes()332 void InstrumentLine::functionFillEveryTwoNotes(){ functionFillNotes(2); }
functionFillEveryThreeNotes()333 void InstrumentLine::functionFillEveryThreeNotes(){ functionFillNotes(3); }
functionFillEveryFourNotes()334 void InstrumentLine::functionFillEveryFourNotes(){ functionFillNotes(4); }
functionFillEverySixNotes()335 void InstrumentLine::functionFillEverySixNotes(){ functionFillNotes(6); }
functionFillEveryEightNotes()336 void InstrumentLine::functionFillEveryEightNotes(){ functionFillNotes(8); }
functionFillEveryTwelveNotes()337 void InstrumentLine::functionFillEveryTwelveNotes(){ functionFillNotes(12); }
functionFillEverySixteenNotes()338 void InstrumentLine::functionFillEverySixteenNotes(){ functionFillNotes(16); }
339 
functionFillNotes(int every)340 void InstrumentLine::functionFillNotes( int every )
341 {
342 	Hydrogen *pEngine = Hydrogen::get_instance();
343 
344 	PatternEditorPanel *pPatternEditorPanel = HydrogenApp::get_instance()->getPatternEditorPanel();
345 	DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
346 	int nBase;
347 	if ( pPatternEditor->isUsingTriplets() ) {
348 		nBase = 3;
349 	}
350 	else {
351 		nBase = 4;
352 	}
353 	int nResolution = 4 * MAX_NOTES * every / ( nBase * pPatternEditor->getResolution() );
354 
355 
356 	Song *pSong = pEngine->getSong();
357 
358 	QStringList notePositions;
359 
360 	Pattern* pCurrentPattern = getCurrentPattern();
361 	if (pCurrentPattern != nullptr) {
362 		int nPatternSize = pCurrentPattern->get_length();
363 		int nSelectedInstrument = pEngine->getSelectedInstrumentNumber();
364 
365 		if (nSelectedInstrument != -1) {
366 			Instrument *instrRef = (pSong->get_instrument_list())->get( nSelectedInstrument );
367 
368 			for (int i = 0; i < nPatternSize; i += nResolution) {
369 				bool noteAlreadyPresent = false;
370 				const Pattern::notes_t* notes = pCurrentPattern->get_notes();
371 				FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
372 					Note *pNote = it->second;
373 					if ( pNote->get_instrument() == instrRef ) {
374 						// note already exists
375 						noteAlreadyPresent = true;
376 						break;
377 					}
378 				}
379 
380 				if ( noteAlreadyPresent == false ) {
381 					notePositions << QString("%1").arg(i);
382 				}
383 			}
384 			SE_fillNotesRightClickAction *action = new SE_fillNotesRightClickAction( notePositions, nSelectedInstrument, pEngine->getSelectedPatternNumber() );
385 			HydrogenApp::get_instance()->m_pUndoStack->push( action );
386 		}
387 	}
388 
389 }
390 
391 
392 
functionRandomizeVelocity()393 void InstrumentLine::functionRandomizeVelocity()
394 {
395 	Hydrogen *pEngine = Hydrogen::get_instance();
396 
397 	PatternEditorPanel *pPatternEditorPanel = HydrogenApp::get_instance()->getPatternEditorPanel();
398 	DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
399 
400 
401 	int nBase;
402 	if ( pPatternEditor->isUsingTriplets() ) {
403 		nBase = 3;
404 	}
405 	else {
406 		nBase = 4;
407 	}
408 	int nResolution = 4 * MAX_NOTES / ( nBase * pPatternEditor->getResolution() );
409 
410 	Song *pSong = pEngine->getSong();
411 
412 	QStringList noteVeloValue;
413 	QStringList oldNoteVeloValue;
414 
415 	Pattern* pCurrentPattern = getCurrentPattern();
416 	if (pCurrentPattern != nullptr) {
417 		int nPatternSize = pCurrentPattern->get_length();
418 		int nSelectedInstrument = pEngine->getSelectedInstrumentNumber();
419 
420 		if (nSelectedInstrument != -1) {
421 			Instrument *instrRef = (pSong->get_instrument_list())->get( nSelectedInstrument );
422 
423 			for (int i = 0; i < nPatternSize; i += nResolution) {
424 				const Pattern::notes_t* notes = pCurrentPattern->get_notes();
425 				FOREACH_NOTE_CST_IT_BOUND(notes,it,i) {
426 					Note *pNote = it->second;
427 					if ( pNote->get_instrument() == instrRef ) {
428 						float fVal = ( rand() % 100 ) / 100.0;
429 						oldNoteVeloValue <<  QString("%1").arg( pNote->get_velocity() );
430 						fVal = pNote->get_velocity() + ( ( fVal - 0.50 ) / 2 );
431 						if ( fVal < 0  ) {
432 							fVal = 0;
433 						}
434 						if ( fVal > 1 ) {
435 							fVal = 1;
436 						}
437 						noteVeloValue << QString("%1").arg(fVal);
438 					}
439 				}
440 			}
441 			SE_randomVelocityRightClickAction *action = new SE_randomVelocityRightClickAction( noteVeloValue, oldNoteVeloValue, nSelectedInstrument, pEngine->getSelectedPatternNumber() );
442 			HydrogenApp::get_instance()->m_pUndoStack->push( action );
443 		}
444 	}
445 }
446 
447 
448 
functionRenameInstrument()449 void InstrumentLine::functionRenameInstrument()
450 {
451 	// This code is pretty much a duplicate of void InstrumentEditor::labelClicked
452 	// in InstrumentEditor.cpp
453 	Hydrogen * pEngine = Hydrogen::get_instance();
454 	Instrument *pSelectedInstrument = pEngine->getSong()->get_instrument_list()->get( m_nInstrumentNumber );
455 
456 	QString sOldName = pSelectedInstrument->get_name();
457 	bool bIsOkPressed;
458 	QString sNewName = QInputDialog::getText( this, "Hydrogen", tr( "New instrument name" ), QLineEdit::Normal, sOldName, &bIsOkPressed );
459 	if ( bIsOkPressed  ) {
460 		pSelectedInstrument->set_name( sNewName );
461 
462 #ifdef H2CORE_HAVE_JACK
463 		AudioEngine::get_instance()->lock( RIGHT_HERE );
464 		Hydrogen *engine = Hydrogen::get_instance();
465 		engine->renameJackPorts(engine->getSong());
466 		AudioEngine::get_instance()->unlock();
467 #endif
468 
469 		// this will force an update...
470 		EventQueue::get_instance()->push_event( EVENT_SELECTED_INSTRUMENT_CHANGED, -1 );
471 
472 	}
473 	else
474 	{
475 		// user entered nothing or pressed Cancel
476 	}
477 
478 }
479 
functionDeleteInstrument()480 void InstrumentLine::functionDeleteInstrument()
481 {
482 	Hydrogen * pEngine = Hydrogen::get_instance();
483 	Instrument *pSelectedInstrument = pEngine->getSong()->get_instrument_list()->get( m_nInstrumentNumber );
484 
485 	std::list< Note* > noteList;
486 	Song* song = pEngine->getSong();
487 	PatternList *patList = song->get_pattern_list();
488 
489 	QString instrumentName =  pSelectedInstrument->get_name();
490 	QString drumkitName = pEngine->getCurrentDrumkitname();
491 
492 	for ( int i = 0; i < patList->size(); i++ ) {
493 		H2Core::Pattern *pPattern = song->get_pattern_list()->get(i);
494 		const Pattern::notes_t* notes = pPattern->get_notes();
495 		FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
496 			Note *pNote = it->second;
497 			assert( pNote );
498 			if ( pNote->get_instrument() == pSelectedInstrument ) {
499 				pNote->set_pattern_idx( i );
500 				noteList.push_back( pNote );
501 			}
502 		}
503 	}
504 	SE_deleteInstrumentAction *action = new SE_deleteInstrumentAction( noteList, drumkitName, instrumentName, m_nInstrumentNumber );
505 	HydrogenApp::get_instance()->m_pUndoStack->push( action );
506 }
507 
508 
509 
510 //////
511 
512 const char* PatternEditorInstrumentList::__class_name = "PatternEditorInstrumentList";
513 
PatternEditorInstrumentList(QWidget * parent,PatternEditorPanel * pPatternEditorPanel)514 PatternEditorInstrumentList::PatternEditorInstrumentList( QWidget *parent, PatternEditorPanel *pPatternEditorPanel )
515  : QWidget( parent )
516  , Object( __class_name )
517 {
518 	//INFOLOG("INIT");
519 	m_pPattern = nullptr;
520 	m_pPatternEditorPanel = pPatternEditorPanel;
521 
522 	m_nGridHeight = Preferences::get_instance()->getPatternEditorGridHeight();
523 
524 	m_nEditorWidth = 181;
525 	m_nEditorHeight = m_nGridHeight * MAX_INSTRUMENTS;
526 
527 	resize( m_nEditorWidth, m_nEditorHeight );
528 
529 
530 	setAcceptDrops(true);
531 
532 	for ( int i = 0; i < MAX_INSTRUMENTS; ++i) {
533 		m_pInstrumentLine[i] = nullptr;
534 	}
535 
536 
537 	updateInstrumentLines();
538 
539 	m_pUpdateTimer = new QTimer( this );
540 	connect( m_pUpdateTimer, SIGNAL( timeout() ), this, SLOT( updateInstrumentLines() ) );
541 	m_pUpdateTimer->start(50);
542 
543 }
544 
545 
546 
~PatternEditorInstrumentList()547 PatternEditorInstrumentList::~PatternEditorInstrumentList()
548 {
549 	//INFOLOG( "DESTROY" );
550 	m_pUpdateTimer->stop();
551 }
552 
553 
554 
555 
556 ///
557 /// Create a new InstrumentLine
558 ///
createInstrumentLine()559 InstrumentLine* PatternEditorInstrumentList::createInstrumentLine()
560 {
561 	InstrumentLine *pLine = new InstrumentLine(this);
562 	return pLine;
563 }
564 
565 
566 
567 ///
568 /// Update every InstrumentLine, create or destroy lines if necessary.
569 ///
updateInstrumentLines()570 void PatternEditorInstrumentList::updateInstrumentLines()
571 {
572 	//INFOLOG( "Update lines" );
573 
574 	Hydrogen *pEngine = Hydrogen::get_instance();
575 	Song *pSong = pEngine->getSong();
576 	InstrumentList *pInstrList = pSong->get_instrument_list();
577 	Mixer * mixer = HydrogenApp::get_instance()->getMixer();
578 
579 	unsigned nSelectedInstr = pEngine->getSelectedInstrumentNumber();
580 
581 	unsigned nInstruments = pInstrList->size();
582 	for ( unsigned nInstr = 0; nInstr < MAX_INSTRUMENTS; ++nInstr ) {
583 		if ( nInstr >= nInstruments ) {	// unused instrument! let's hide and destroy the mixerline!
584 			if ( m_pInstrumentLine[ nInstr ] ) {
585 				delete m_pInstrumentLine[ nInstr ];
586 				m_pInstrumentLine[ nInstr ] = nullptr;
587 
588 				int newHeight = m_nGridHeight * nInstruments;
589 				resize( width(), newHeight );
590 
591 			}
592 			continue;
593 		}
594 		else {
595 			if ( m_pInstrumentLine[ nInstr ] == nullptr ) {
596 				// the instrument line doesn't exists..I'll create a new one!
597 				m_pInstrumentLine[ nInstr ] = createInstrumentLine();
598 				m_pInstrumentLine[nInstr]->move( 0, m_nGridHeight * nInstr );
599 				m_pInstrumentLine[nInstr]->show();
600 
601 				int newHeight = m_nGridHeight * nInstruments;
602 				resize( width(), newHeight );
603 			}
604 			InstrumentLine *pLine = m_pInstrumentLine[ nInstr ];
605 			Instrument* pInstr = pInstrList->get(nInstr);
606 			assert(pInstr);
607 
608 			pLine->setNumber(nInstr);
609 			pLine->setName( pInstr->get_name() );
610 			pLine->setSelected( nInstr == nSelectedInstr );
611 			pLine->setMuted( pInstr->is_muted() );
612 			if ( mixer ) {
613 				pLine->setSoloed( mixer->isSoloClicked( nInstr ) );
614 			}
615 
616 		}
617 	}
618 
619 }
620 
621 
622 
623 
dragEnterEvent(QDragEnterEvent * event)624 void PatternEditorInstrumentList::dragEnterEvent(QDragEnterEvent *event)
625 {
626 	INFOLOG( "[dragEnterEvent]" );
627 	if ( event->mimeData()->hasFormat("text/plain") ) {
628 		Song *song = (Hydrogen::get_instance())->getSong();
629 		int nInstruments = song->get_instrument_list()->size();
630 		if ( nInstruments < MAX_INSTRUMENTS ) {
631 			event->acceptProposedAction();
632 		}
633 	}
634 }
635 
636 
dropEvent(QDropEvent * event)637 void PatternEditorInstrumentList::dropEvent(QDropEvent *event)
638 {
639 	//WARNINGLOG("Drop!");
640 	QString sText = event->mimeData()->text();
641 
642 
643 	if(sText.startsWith("Songs:") || sText.startsWith("Patterns:") || sText.startsWith("move pattern:") || sText.startsWith("drag pattern:")) return;
644 
645 	if (sText.startsWith("move instrument:")) {
646 
647 		Hydrogen *engine = Hydrogen::get_instance();
648 		int nSourceInstrument = engine->getSelectedInstrumentNumber();
649 
650 		// Starting point for instument list is 50 lower than
651 		// on the drum pattern editor
652 
653 		int pos_y = ( event->pos().x() >= m_nEditorWidth ) ? event->pos().y() - 50 : event->pos().y();
654 
655 		int nTargetInstrument = pos_y / m_nGridHeight;
656 
657 		if( nTargetInstrument >= engine->getSong()->get_instrument_list()->size() ){
658 			nTargetInstrument = engine->getSong()->get_instrument_list()->size() - 1;
659 		}
660 
661 		if ( nSourceInstrument == nTargetInstrument ) {
662 			event->acceptProposedAction();
663 			return;
664 		}
665 
666 		SE_moveInstrumentAction *action = new SE_moveInstrumentAction( nSourceInstrument, nTargetInstrument );
667 		HydrogenApp::get_instance()->m_pUndoStack->push( action );
668 
669 		event->acceptProposedAction();
670 	}
671 	if( sText.startsWith("importInstrument:") ) {
672 		//an instrument was dragged from the soundlibrary browser to the patterneditor
673 
674 		sText = sText.remove(0,QString("importInstrument:").length());
675 
676 		QStringList tokens = sText.split( "::" );
677 		QString sDrumkitName = tokens.at( 0 );
678 		QString sInstrumentName = tokens.at( 1 );
679 
680 		int nTargetInstrument = event->pos().y() / m_nGridHeight;
681 
682 		/*
683 				"X > 181": border between the instrument names on the left and the grid
684 				Because the right part of the grid starts above the name column, we have to subtract the difference
685 		*/
686 		if (  event->pos().x() > 181 ) nTargetInstrument = ( event->pos().y() - 90 )  / m_nGridHeight ;
687 
688 		Hydrogen *engine = Hydrogen::get_instance();
689 		if( nTargetInstrument > engine->getSong()->get_instrument_list()->size() ){
690 			nTargetInstrument = engine->getSong()->get_instrument_list()->size();
691 		}
692 
693 		SE_dragInstrumentAction *action = new SE_dragInstrumentAction( sDrumkitName, sInstrumentName, nTargetInstrument);
694 		HydrogenApp::get_instance()->m_pUndoStack->push( action );
695 
696 		event->acceptProposedAction();
697 	}
698 }
699 
700 
701 
mousePressEvent(QMouseEvent * event)702 void PatternEditorInstrumentList::mousePressEvent(QMouseEvent *event)
703 {
704 	if (event->button() == Qt::LeftButton) {
705 		__drag_start_position = event->pos();
706 	}
707 
708 }
709 
710 
711 
mouseMoveEvent(QMouseEvent * event)712 void PatternEditorInstrumentList::mouseMoveEvent(QMouseEvent *event)
713 {
714 	if (!(event->buttons() & Qt::LeftButton)) {
715 		return;
716 	}
717 	if ( abs(event->pos().y() - __drag_start_position.y()) < (int)m_nGridHeight) {
718 		return;
719 	}
720 
721 	Hydrogen *pEngine = Hydrogen::get_instance();
722 	int nSelectedInstr = pEngine->getSelectedInstrumentNumber();
723 	Instrument *pInstr = pEngine->getSong()->get_instrument_list()->get(nSelectedInstr);
724 
725 	QString sText = QString("move instrument:%1").arg( pInstr->get_name() );
726 
727 	QDrag *pDrag = new QDrag(this);
728 	QMimeData *pMimeData = new QMimeData;
729 
730 	pMimeData->setText( sText );
731 	pDrag->setMimeData( pMimeData);
732 
733 	pDrag->exec( Qt::CopyAction | Qt::MoveAction );
734 
735 	// propago l'evento
736 	QWidget::mouseMoveEvent(event);
737 }
738