hydrogen 1.2.3
PatternEditorInstrumentList.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
24
26#include <core/EventQueue.h>
27#include <core/Hydrogen.h>
30#include <core/Basics/Note.h>
31#include <core/Basics/Pattern.h>
33#include <core/Basics/Song.h>
34using namespace H2Core;
35
36#include "CommonStrings.h"
37#include "UndoActions.h"
38#include "PatternEditorPanel.h"
40#include "DrumPatternEditor.h"
41#include "../HydrogenApp.h"
42#include "../MainForm.h"
43#include "../Widgets/Button.h"
44#include "../Skin.h"
45
46#include <QtGui>
47#include <QtWidgets>
48#include <QClipboard>
49
50#include <cassert>
51#include <algorithm> // for std::min
52
53
55 : PixmapWidget(pParent)
57 , m_bIsSelected( false )
58 , m_bEntered( false )
59{
60
62 auto pCommonStrings = HydrogenApp::get_instance()->getCommonStrings();
63
64 int h = pPref->getPatternEditorGridHeight();
65 setFixedSize(181, h);
66
67 QFont nameFont( pPref->getLevel2FontFamily(), getPointSize( pPref->getFontSize() ) );
68
69 m_pNameLbl = new QLabel(this);
70 m_pNameLbl->resize( 145, h );
71 m_pNameLbl->move( 10, 1 );
72 m_pNameLbl->setFont(nameFont);
73
74 /*: Text displayed on the button for muting an instrument. Its
75 size is designed for a single character.*/
76 m_pMuteBtn = new Button( this, QSize( InstrumentLine::m_nButtonWidth, height() - 1 ),
78 pCommonStrings->getSmallMuteButton(),
79 true, QSize(), tr("Mute instrument"),
80 false, true );
81 m_pMuteBtn->move( 145, 0 );
82 m_pMuteBtn->setChecked(false);
83 m_pMuteBtn->setObjectName( "InstrumentLineMuteButton" );
84 connect(m_pMuteBtn, SIGNAL( clicked() ), this, SLOT( muteClicked() ));
85
86 /*: Text displayed on the button for soloing an instrument. Its
87 size is designed for a single character.*/
88 m_pSoloBtn = new Button( this, QSize( InstrumentLine::m_nButtonWidth, height() - 1 ),
90 pCommonStrings->getSmallSoloButton(),
91 false, QSize(), tr("Solo"),
92 false, true );
93 m_pSoloBtn->move( 163, 0 );
94 m_pSoloBtn->setChecked(false);
95 m_pSoloBtn->setObjectName( "InstrumentLineSoloButton" );
96 connect(m_pSoloBtn, SIGNAL( clicked() ), this, SLOT(soloClicked()));
97
98 m_pSampleWarning = new Button( this, QSize( 15, 13 ), Button::Type::Icon,
99 "warning.svg", "", false, QSize(),
100 tr( "Some samples for this instrument failed to load." ),
101 true );
102 m_pSampleWarning->move( 128, 5 );
103 m_pSampleWarning->hide();
104 connect(m_pSampleWarning, SIGNAL( clicked() ), this, SLOT( sampleWarningClicked() ));
105
106
107 // Popup menu
108 m_pFunctionPopup = new QMenu( this );
109 m_pFunctionPopup->addAction( tr( "Delete notes" ), this, SLOT( functionClearNotes() ) );
110
111 m_pFunctionPopupSub = new QMenu( tr( "Fill notes ..." ), m_pFunctionPopup );
112 m_pFunctionPopupSub->addAction( tr( "Fill all notes" ), this, SLOT( functionFillAllNotes() ) );
113 m_pFunctionPopupSub->addAction( tr( "Fill 1/2 notes" ), this, SLOT( functionFillEveryTwoNotes() ) );
114 m_pFunctionPopupSub->addAction( tr( "Fill 1/3 notes" ), this, SLOT( functionFillEveryThreeNotes() ) );
115 m_pFunctionPopupSub->addAction( tr( "Fill 1/4 notes" ), this, SLOT( functionFillEveryFourNotes() ) );
116 m_pFunctionPopupSub->addAction( tr( "Fill 1/6 notes" ), this, SLOT( functionFillEverySixNotes() ) );
117 m_pFunctionPopupSub->addAction( tr( "Fill 1/8 notes" ), this, SLOT( functionFillEveryEightNotes() ) );
118 m_pFunctionPopupSub->addAction( tr( "Fill 1/12 notes" ), this, SLOT( functionFillEveryTwelveNotes() ) );
119 m_pFunctionPopupSub->addAction( tr( "Fill 1/16 notes" ), this, SLOT( functionFillEverySixteenNotes() ) );
121
122 m_pFunctionPopup->addAction( tr( "Randomize velocity" ), this, SLOT( functionRandomizeVelocity() ) );
123 auto selectNotesAction = m_pFunctionPopup->addAction( tr( "Select notes" ) );
124 connect( selectNotesAction, &QAction::triggered, this,
126
127 m_pFunctionPopup->addSection( tr( "Edit all patterns" ) );
128 m_pFunctionPopup->addAction( tr( "Cut notes"), this, SLOT( functionCutNotesAllPatterns() ) );
129 m_pFunctionPopup->addAction( tr( "Copy notes"), this, SLOT( functionCopyAllInstrumentPatterns() ) );
130 m_pFunctionPopup->addAction( tr( "Paste notes" ), this, SLOT( functionPasteAllInstrumentPatterns() ) );
131 m_pFunctionPopup->addAction( tr( "Delete notes" ), this, SLOT( functionDeleteNotesAllPatterns() ) );
132
133 m_pFunctionPopup->addSection( tr( "Instrument" ) );
134 m_pFunctionPopup->addAction( tr( "Rename instrument" ), this, SLOT( functionRenameInstrument() ) );
135 auto deleteAction = m_pFunctionPopup->addAction( tr( "Delete instrument" ) );
136 connect( deleteAction, &QAction::triggered, this, [=](){
138 functionDeleteInstrument( m_nInstrumentNumber );} );
139 m_pFunctionPopup->setObjectName( "PatternEditorFunctionPopup" );
140
141 // Reset the clicked row once the popup is closed by clicking at
142 // any position other than at an action of the popup.
143 connect( m_pFunctionPopup, &QMenu::aboutToHide, [=](){
146 }
147 });
148
150}
151
152
154 if ( m_rowSelection != rowSelection ) {
155 m_rowSelection = rowSelection;
156 update();
157 }
158}
159
160
161void InstrumentLine::setName(const QString& sName)
162{
163 if ( m_pNameLbl->text() != sName ){
164 m_pNameLbl->setText(sName);
165 }
166}
167
168
169
170void InstrumentLine::setSelected( bool bSelected )
171{
172 if ( bSelected == m_bIsSelected ) {
173 return;
174 }
175
176 m_bIsSelected = bSelected;
177
179 update();
180}
181
183
185
186 QColor textColor;
187 if ( m_bIsSelected ) {
188 textColor = pPref->getColorTheme()->m_patternEditor_selectedRowTextColor;
189 } else {
190 textColor = pPref->getColorTheme()->m_patternEditor_textColor;
191 }
192
193 m_pNameLbl->setStyleSheet( QString( "\
194QLabel {\
195 color: %1;\
196 font-weight: bold;\
197 }" ).arg( textColor.name() ) );
198}
199
200void InstrumentLine::enterEvent( QEvent* ev ) {
201 UNUSED( ev );
202 m_bEntered = true;
203 update();
204}
205
206void InstrumentLine::leaveEvent( QEvent* ev ) {
207 UNUSED( ev );
208 m_bEntered = false;
209 update();
210}
211
212void InstrumentLine::paintEvent( QPaintEvent* ev ) {
213 auto pPref = Preferences::get_instance();
214 auto pHydrogenApp = HydrogenApp::get_instance();
215
216 QPainter painter(this);
217
218 QColor backgroundColor;
219 if ( m_bIsSelected ) {
220 backgroundColor = pPref->getColorTheme()->m_patternEditor_selectedRowColor.darker( 114 );
221 } else {
222 if ( m_nInstrumentNumber == 0 ||
223 m_nInstrumentNumber % 2 == 0 ) {
224 backgroundColor = pPref->getColorTheme()->m_patternEditor_backgroundColor.darker( 120 );
225 } else {
226 backgroundColor = pPref->getColorTheme()->m_patternEditor_alternateRowColor.darker( 132 );
227 }
228 }
229
230 // Make the background slightly lighter when hovered.
231 bool bHovered = false;
233 bHovered = true;
234 }
235
236 Skin::drawListBackground( &painter, QRect( 0, 0, width(), height() ),
237 backgroundColor, bHovered );
238
239 // Draw border indicating cursor position
240 if ( ( m_bIsSelected && pHydrogenApp->getPatternEditorPanel() != nullptr &&
241 pHydrogenApp->getPatternEditorPanel()->getDrumPatternEditor()->hasFocus() &&
242 ! pHydrogenApp->hideKeyboardCursor() ) ||
244
245 QPen pen;
246
248 // In case a row was right-clicked, highlight it using a border.
249 pen.setColor( pPref->getColorTheme()->m_highlightColor);
250 } else {
251 pen.setColor( pPref->getColorTheme()->m_cursorColor );
252 }
253
254 pen.setWidth( 2 );
255 painter.setPen( pen );
256 painter.setRenderHint( QPainter::Antialiasing );
257 painter.drawRoundedRect( QRect( 1, 1, width() - 2 * InstrumentLine::m_nButtonWidth - 1,
258 height() - 2 ), 4, 4 );
259 }
260}
261
262
264{
265 if ( m_nInstrumentNumber != nIndex ) {
266 m_nInstrumentNumber = nIndex;
267 update();
268 }
269}
270
271
272
273void InstrumentLine::setMuted(bool isMuted)
274{
275 if ( ! m_pMuteBtn->isDown() &&
276 m_pMuteBtn->isChecked() != isMuted ) {
277 m_pMuteBtn->setChecked(isMuted);
278 }
279}
280
281
282void InstrumentLine::setSoloed( bool soloed )
283{
284 if ( ! m_pSoloBtn->isDown() &&
285 m_pSoloBtn->isChecked() != soloed ) {
286 m_pSoloBtn->setChecked( soloed );
287 }
288}
289
290
291void InstrumentLine::setSamplesMissing( bool bSamplesMissing )
292{
293 if ( bSamplesMissing ) {
294 m_pSampleWarning->show();
295 } else {
296 m_pSampleWarning->hide();
297 }
298}
299
300
301
303{
304 Hydrogen *pHydrogen = Hydrogen::get_instance();
305 std::shared_ptr<Song> pSong = pHydrogen->getSong();
306 if ( pSong == nullptr ) {
307 ERRORLOG( "No song set yet" );
308 return;
309 }
310
311 auto pInstrList = pSong->getInstrumentList();
312 auto pInstr = pInstrList->get( m_nInstrumentNumber );
313 if ( pInstr == nullptr ) {
314 ERRORLOG( QString( "Unable to retrieve instrument [%1]" )
315 .arg( m_nInstrumentNumber ) );
316 return;
317 }
318
320
321 CoreActionController* pCoreActionController = pHydrogen->getCoreActionController();
322 pCoreActionController->setStripIsMuted( m_nInstrumentNumber, !pInstr->is_muted() );
323}
324
325
326
328{
329 Hydrogen *pHydrogen = Hydrogen::get_instance();
330 std::shared_ptr<Song> pSong = pHydrogen->getSong();
331 if ( pSong == nullptr ) {
332 ERRORLOG( "No song set yet" );
333 return;
334 }
335
336 auto pInstrList = pSong->getInstrumentList();
337 auto pInstr = pInstrList->get( m_nInstrumentNumber );
338 if ( pInstr == nullptr ) {
339 ERRORLOG( QString( "Unable to retrieve instrument [%1]" )
340 .arg( m_nInstrumentNumber ) );
341 return;
342 }
343
345
346 CoreActionController* pCoreActionController = pHydrogen->getCoreActionController();
347 pCoreActionController->setStripIsSoloed( m_nInstrumentNumber, !pInstr->is_soloed() );
348}
349
351{
352 QMessageBox::information( this, "Hydrogen",
353 tr( "One or more samples for this instrument failed to load. This may be because the"
354 " songfile uses an older default drumkit. This might be fixed by opening a new "
355 "drumkit." ) );
356}
357
362
364{
367
368 if ( ev->button() == Qt::LeftButton ) {
369
370 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
371 if ( pSong == nullptr ) {
372 ERRORLOG( "No song set yet" );
373 return;
374 }
375 auto pInstr = pSong->getInstrumentList()->get( m_nInstrumentNumber );
376 if ( pInstr != nullptr && pInstr->hasSamples() ) {
377
378 const int nWidth = m_pMuteBtn->x() - 5; // clickable field width
379 const float fVelocity = std::min((float)ev->x()/(float)nWidth, 1.0f);
380 Note *pNote = new Note( pInstr, 0, fVelocity);
382 }
383
384 } else if (ev->button() == Qt::RightButton ) {
385
387 // There is still a dialog window opened from the last
388 // time. It needs to be closed before the popup will
389 // be shown again.
390 ERRORLOG( "A dialog is still opened. It needs to be closed first." );
391 return;
392 }
393
395
396 m_pFunctionPopup->popup( QPoint( ev->globalX(), ev->globalY() ) );
397 }
398
399 // propago l'evento al parent: serve per il drag&drop
400 PixmapWidget::mousePressEvent(ev);
401}
402
406
408{
409 Hydrogen *pHydrogen = Hydrogen::get_instance();
410 PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
411 assert( pPatternList != nullptr );
412
413 int nSelectedPatternNumber = pHydrogen->getSelectedPatternNumber();
414 if ( nSelectedPatternNumber != -1 &&
415 nSelectedPatternNumber < pPatternList->size() ) {
416 Pattern* pCurrentPattern = pPatternList->get( nSelectedPatternNumber );
417 return pCurrentPattern;
418 }
419 return nullptr;
420}
421
422
423
424
426{
427 Hydrogen * pHydrogen = Hydrogen::get_instance();
428 int selectedPatternNr = pHydrogen->getSelectedPatternNumber();
429 Pattern *pPattern = getCurrentPattern();
430 auto pSelectedInstrument = pHydrogen->getSong()->getInstrumentList()->get( m_nInstrumentNumber );
431 if ( pSelectedInstrument == nullptr ) {
432 ERRORLOG( "No instrument selected" );
433 return;
434 }
435
436 if ( selectedPatternNr == -1 ) {
437 // No pattern selected. Nothing to be clear.
438 return;
439 }
440
441 std::list< Note* > noteList;
442 const Pattern::notes_t* notes = pPattern->get_notes();
444 Note *pNote = it->second;
445 assert( pNote );
446 if ( pNote->get_instrument() == pSelectedInstrument ) {
447 noteList.push_back( pNote );
448 }
449 }
450 if( noteList.size() > 0 ){
452 HydrogenApp::get_instance()->m_pUndoStack->push( action );
453 }
454}
455
456
458{
459 Hydrogen* pHydrogen = Hydrogen::get_instance();
460 std::shared_ptr<Song> pSong = pHydrogen->getSong();
461 if ( pSong == nullptr ) {
462 assert( pSong );
463 ERRORLOG( "No song present" );
464 return;
465 }
466
467 // Serialize & put to clipboard
468 QString sSerialized = pSong->copyInstrumentLineToString( m_nInstrumentNumber );
469 if ( sSerialized.isEmpty() ) {
470 ERRORLOG( QString( "Unable to serialize instrument line [%1]" )
471 .arg( m_nInstrumentNumber ) );
472 return;
473 }
474
475 QClipboard *clipboard = QApplication::clipboard();
476 clipboard->setText( sSerialized );
477}
478
479
481{
482 Hydrogen* pHydrogen = Hydrogen::get_instance();
483 std::shared_ptr<Song> pSong = pHydrogen->getSong();
484 if ( pSong == nullptr ) {
485 assert( pSong );
486 ERRORLOG( "No song present" );
487 return;
488 }
489
490 // This is a note list for pasted notes collection
491 std::list<Pattern*> patternList;
492
493 // Get from clipboard & deserialize
494 QClipboard *clipboard = QApplication::clipboard();
495 QString sSerialized = clipboard->text();
496 if ( ! pSong->pasteInstrumentLineFromString( sSerialized,
498 patternList ) ) {
499 return;
500 }
501
502 // Ignore empty result
503 if (patternList.size() <= 0) {
504 return;
505 }
506
507 // Create action
510}
511
513{
514 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
515 PatternList *pPatternList = pSong->getPatternList();
516 auto pSelectedInstrument = pSong->getInstrumentList()->get( m_nInstrumentNumber );
517 if ( pSelectedInstrument == nullptr ) {
518 ERRORLOG( "No instrument selected" );
519 return;
520 }
521 QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
522
523 pUndo->beginMacro( tr( "Delete all notes on %1" ).arg( pSelectedInstrument->get_name() ) );
524 for ( int nPattern = 0; nPattern < pPatternList->size(); nPattern++ ) {
525 std::list< Note* > noteList;
526 Pattern *pPattern = pPatternList->get( nPattern );
527 const Pattern::notes_t* notes = pPattern->get_notes();
529 if ( it->second->get_instrument() == pSelectedInstrument ) {
530 noteList.push_back( it->second );
531 }
532 }
533 if ( noteList.size() > 0 ) {
534 pUndo->push( new SE_clearNotesPatternEditorAction( noteList, m_nInstrumentNumber, nPattern ) );
535 }
536 }
537 pUndo->endMacro();
538}
539
545
546
555
557{
558 Hydrogen *pHydrogen = Hydrogen::get_instance();
559 if ( pHydrogen->getSelectedPatternNumber() == -1 ) {
560 // No pattern selected. Nothing to be filled.
561 return;
562 }
563
565 DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
566 int nBase;
567 if ( pPatternEditor->isUsingTriplets() ) {
568 nBase = 3;
569 }
570 else {
571 nBase = 4;
572 }
573 int nResolution = 4 * MAX_NOTES * every / ( nBase * pPatternEditor->getResolution() );
574
575
576 std::shared_ptr<Song> pSong = pHydrogen->getSong();
577
578 QStringList notePositions;
579
580 Pattern* pCurrentPattern = getCurrentPattern();
581 if (pCurrentPattern != nullptr) {
582 int nPatternSize = pCurrentPattern->get_length();
583 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
584 if ( pSelectedInstrument == nullptr ) {
585 ERRORLOG( "No instrument selected" );
586 return;
587 }
588 int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
589
590 for (int i = 0; i < nPatternSize; i += nResolution) {
591 bool noteAlreadyPresent = false;
592 const Pattern::notes_t* notes = pCurrentPattern->get_notes();
593 FOREACH_NOTE_CST_IT_BOUND_LENGTH(notes,it,i,pCurrentPattern) {
594 Note *pNote = it->second;
595 if ( pNote->get_instrument() == pSelectedInstrument ) {
596 // note already exists
597 noteAlreadyPresent = true;
598 break;
599 }
600 }
601
602 if ( noteAlreadyPresent == false ) {
603 notePositions << QString("%1").arg(i);
604 }
605 }
606 SE_fillNotesRightClickAction *action = new SE_fillNotesRightClickAction( notePositions, nSelectedInstrument, pHydrogen->getSelectedPatternNumber() );
607 HydrogenApp::get_instance()->m_pUndoStack->push( action );
608 }
609
610}
611
612
613
615{
616 Hydrogen *pHydrogen = Hydrogen::get_instance();
617
618 if ( pHydrogen->getSelectedPatternNumber() == -1 ) {
619 // No pattern selected. Nothing to be randomized.
620 return;
621 }
622
624 DrumPatternEditor *pPatternEditor = pPatternEditorPanel->getDrumPatternEditor();
625
626
627 int nBase;
628 if ( pPatternEditor->isUsingTriplets() ) {
629 nBase = 3;
630 }
631 else {
632 nBase = 4;
633 }
634 int nResolution = 4 * MAX_NOTES / ( nBase * pPatternEditor->getResolution() );
635
636 std::shared_ptr<Song> pSong = pHydrogen->getSong();
637
638 QStringList noteVeloValue;
639 QStringList oldNoteVeloValue;
640
641 Pattern* pCurrentPattern = getCurrentPattern();
642 if (pCurrentPattern != nullptr) {
643 int nPatternSize = pCurrentPattern->get_length();
644 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
645 if ( pSelectedInstrument == nullptr ) {
646 ERRORLOG( "No instrument selected" );
647 return;
648 }
649 int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
650
651 for (int i = 0; i < nPatternSize; i += nResolution) {
652 const Pattern::notes_t* notes = pCurrentPattern->get_notes();
653 FOREACH_NOTE_CST_IT_BOUND_LENGTH(notes,it,i,pCurrentPattern) {
654 Note *pNote = it->second;
655 if ( pNote->get_instrument() == pSelectedInstrument ) {
656 float fVal = ( rand() % 100 ) / 100.0;
657 oldNoteVeloValue << QString("%1").arg( pNote->get_velocity() );
658 fVal = pNote->get_velocity() + ( ( fVal - 0.50 ) / 2 );
659 if ( fVal < 0 ) {
660 fVal = 0;
661 }
662 if ( fVal > 1 ) {
663 fVal = 1;
664 }
665 noteVeloValue << QString("%1").arg(fVal);
666 }
667 }
668 }
669 SE_randomVelocityRightClickAction *action = new SE_randomVelocityRightClickAction( noteVeloValue, oldNoteVeloValue, nSelectedInstrument, pHydrogen->getSelectedPatternNumber() );
670 HydrogenApp::get_instance()->m_pUndoStack->push( action );
671 }
672}
673
674
675
677{
679 // This code is pretty much a duplicate of void InstrumentEditor::labelClicked
680 // in InstrumentEditor.cpp
681 Hydrogen * pHydrogen = Hydrogen::get_instance();
682 auto pSelectedInstrument = pHydrogen->getSong()->getInstrumentList()->get( m_nInstrumentNumber );
683 if ( pSelectedInstrument == nullptr ) {
684 ERRORLOG( "No instrument selected" );
685 return;
686 }
687
688 QString sOldName = pSelectedInstrument->get_name();
689 bool bIsOkPressed;
690 QString sNewName = QInputDialog::getText( this, "Hydrogen", tr( "New instrument name" ), QLineEdit::Normal, sOldName, &bIsOkPressed );
691 if ( bIsOkPressed ) {
692 pSelectedInstrument->set_name( sNewName );
693
694 if ( pHydrogen->hasJackAudioDriver() ) {
695 pHydrogen->getAudioEngine()->lock( RIGHT_HERE );
696 pHydrogen->renameJackPorts( pHydrogen->getSong() );
697 pHydrogen->getAudioEngine()->unlock();
698 }
699
700 // this will force an update...
702
703 }
704 else
705 {
706 // user entered nothing or pressed Cancel
707 }
708
710}
711
714
715 if ( changes & H2Core::Preferences::Changes::Font ) {
716
717 m_pNameLbl->setFont( QFont( pPref->getLevel2FontFamily(), getPointSize( pPref->getFontSize() ) ) );
718 }
719
720 if ( changes & H2Core::Preferences::Changes::Colors ) {
722 update();
723 }
724}
725
726
728
730 : QWidget( parent )
731 {
732
734
735 //INFOLOG("INIT");
736 m_pPattern = nullptr;
737 m_pPatternEditorPanel = pPatternEditorPanel;
738
740
741 m_nEditorWidth = 181;
743
745
746 setAcceptDrops(true);
747
748 for ( int i = 0; i < MAX_INSTRUMENTS; ++i) {
749 m_pInstrumentLine[i] = nullptr;
750 }
751
752
754
755 m_pUpdateTimer = new QTimer( this );
756 connect( m_pUpdateTimer, SIGNAL( timeout() ), this, SLOT( updateInstrumentLines() ) );
757 m_pUpdateTimer->start(50);
758
759 QScrollArea *pScrollArea = dynamic_cast< QScrollArea *>( parentWidget()->parentWidget() );
760 assert( pScrollArea );
761 m_pDragScroller = new DragScroller( pScrollArea );
762}
763
764
765
767{
768 //INFOLOG( "DESTROY" );
769 m_pUpdateTimer->stop();
770 delete m_pDragScroller;
771}
772
773
774
775
786
788 if ( nEvent == 0 || nEvent == 1 ) {
790 }
791}
792
796
798 auto pHydrogen = Hydrogen::get_instance();
799 auto pSong = pHydrogen->getSong();
800 auto pInstrList = pSong->getInstrumentList();
801
802 unsigned nInstruments = pInstrList->size();
803 for ( unsigned nInstr = 0; nInstr < MAX_INSTRUMENTS; ++nInstr ) {
804 if ( nInstr < nInstruments &&
805 m_pInstrumentLine[ nInstr ] != nullptr ) {
806 m_pInstrumentLine[ nInstr ]->update();
807 }
808 }
809}
810
812
813 auto pHydrogen = Hydrogen::get_instance();
814 auto pSong = pHydrogen->getSong();
815 auto pInstrList = pSong->getInstrumentList();
816
817 unsigned nSelectedInstr = pHydrogen->getSelectedInstrumentNumber();
818
819 unsigned nInstruments = pInstrList->size();
820 for ( unsigned nInstr = 0; nInstr < MAX_INSTRUMENTS; ++nInstr ) {
821 if ( nInstr < nInstruments &&
822 m_pInstrumentLine[ nInstr ] != nullptr ) {
823
824 InstrumentLine *pLine = m_pInstrumentLine[ nInstr ];
825 pLine->setSelected( nInstr == nSelectedInstr );
826 }
827 }
828}
829
834{
835 Hydrogen *pHydrogen = Hydrogen::get_instance();
836 std::shared_ptr<Song> pSong = pHydrogen->getSong();
837 auto pInstrList = pSong->getInstrumentList();
838
839 unsigned nSelectedInstr = pHydrogen->getSelectedInstrumentNumber();
840
841 unsigned nInstruments = pInstrList->size();
842 for ( unsigned nInstr = 0; nInstr < MAX_INSTRUMENTS; ++nInstr ) {
843 if ( nInstr >= nInstruments ) { // unused instrument! let's hide and destroy the mixerline!
844 if ( m_pInstrumentLine[ nInstr ] ) {
845 delete m_pInstrumentLine[ nInstr ];
846 m_pInstrumentLine[ nInstr ] = nullptr;
847
848 int newHeight = m_nGridHeight * nInstruments + 1;
849 resize( width(), newHeight );
850 }
851 continue;
852 }
853 else {
854 if ( m_pInstrumentLine[ nInstr ] == nullptr ) {
855 // the instrument line doesn't exists..I'll create a new one!
857 m_pInstrumentLine[nInstr]->move( 0, m_nGridHeight * nInstr + 1 );
858 m_pInstrumentLine[nInstr]->show();
859
860 int newHeight = m_nGridHeight * nInstruments;
861 resize( width(), newHeight );
862 }
863 InstrumentLine *pLine = m_pInstrumentLine[ nInstr ];
864 auto pInstr = pInstrList->get(nInstr);
865 assert(pInstr);
866
867 pLine->setNumber(nInstr);
868 pLine->setName( pInstr->get_name() );
869 pLine->setSelected( nInstr == nSelectedInstr );
870 pLine->setMuted( pInstr->is_muted() );
871 pLine->setSoloed( pInstr->is_soloed() );
872
873 pLine->setSamplesMissing( pInstr->has_missing_samples() );
874 }
875 }
876
877}
878
880{
881 event->acceptProposedAction();
882}
883
885{
886 //WARNINGLOG("Drop!");
887 if ( ! event->mimeData()->hasFormat("text/plain") ) {
888 event->ignore();
889 return;
890 }
891
892 auto pHydrogen = Hydrogen::get_instance();
893 std::shared_ptr<Song> pSong = pHydrogen->getSong();
894 auto pInstrumentList = pSong->getInstrumentList();
895 int nInstruments = pInstrumentList->size();
896 if ( nInstruments >= MAX_INSTRUMENTS ) {
897 event->ignore();
898 QMessageBox::critical( this, "Hydrogen", tr( "Unable to insert further instruments. Maximum possible number" ) +
899 QString( ": %1" ).arg( MAX_INSTRUMENTS ) );
900 return;
901 }
902
903 QString sText = event->mimeData()->text();
904
905
906 if ( sText.startsWith("Songs:") ||
907 sText.startsWith("Patterns:") ||
908 sText.startsWith("move pattern:") ||
909 sText.startsWith("drag pattern:") ) {
910 return;
911 }
912
913 if (sText.startsWith("move instrument:")) {
914
915 int nSourceInstrument = pHydrogen->getSelectedInstrumentNumber();
916
917 // Starting point for instument list is 50 lower than
918 // on the drum pattern editor
919
920 int pos_y = ( event->pos().x() >= m_nEditorWidth ) ? event->pos().y() - 50 : event->pos().y();
921
922 int nTargetInstrument = pos_y / m_nGridHeight;
923
924 if( nTargetInstrument >= pInstrumentList->size() ){
925 nTargetInstrument = pInstrumentList->size() - 1;
926 }
927
928 if ( nSourceInstrument == nTargetInstrument ) {
929 event->acceptProposedAction();
930 return;
931 }
932
933 SE_moveInstrumentAction *action = new SE_moveInstrumentAction( nSourceInstrument, nTargetInstrument );
934 HydrogenApp::get_instance()->m_pUndoStack->push( action );
935
936 event->acceptProposedAction();
937 }
938 if( sText.startsWith("importInstrument:") ) {
939 //an instrument was dragged from the soundlibrary browser to the patterneditor
940
941 sText = sText.remove(0,QString("importInstrument:").length());
942
943 QStringList tokens = sText.split( "::" );
944 QString sDrumkitPath = tokens.at( 0 );
945 QString sInstrumentName = tokens.at( 1 );
946
947 int nTargetInstrument = event->pos().y() / m_nGridHeight;
948
949 /*
950 "X > 181": border between the instrument names on the left and the grid
951 Because the right part of the grid starts above the name column, we have to subtract the difference
952 */
953 if ( event->pos().x() > 181 ) {
954 nTargetInstrument = ( event->pos().y() - 90 ) / m_nGridHeight ;
955 }
956
957 if( nTargetInstrument > pInstrumentList->size() ){
958 nTargetInstrument = pInstrumentList->size();
959 }
960
961 auto pCommonString = HydrogenApp::get_instance()->getCommonStrings();
962
963 if ( sDrumkitPath.isEmpty() ) {
964 QMessageBox::critical( this, "Hydrogen", pCommonString->getInstrumentLoadError() );
965 return;
966 }
967
968 SE_dragInstrumentAction *action = new SE_dragInstrumentAction( sDrumkitPath, sInstrumentName, nTargetInstrument );
969 HydrogenApp::get_instance()->m_pUndoStack->push( action );
970
971 event->acceptProposedAction();
972 }
973}
974
975
976
978{
979 if (event->button() == Qt::LeftButton) {
980 __drag_start_position = event->pos();
981 }
982
983}
984
985
986
988{
989 if (!(event->buttons() & Qt::LeftButton)) {
990 return;
991 }
992 if ( abs(event->pos().y() - __drag_start_position.y()) < (int)m_nGridHeight) {
993 return;
994 }
995
996 Hydrogen *pHydrogen = Hydrogen::get_instance();
997 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
998 if ( pSelectedInstrument == nullptr ) {
999 ERRORLOG( "No instrument selected" );
1000 return;
1001 }
1002
1003 QString sText = QString("move instrument:%1").arg( pSelectedInstrument->get_name() );
1004
1005 QDrag *pDrag = new QDrag(this);
1006 QMimeData *pMimeData = new QMimeData;
1007
1008 pMimeData->setText( sText );
1009 pDrag->setMimeData( pMimeData);
1010
1012 pDrag->exec( Qt::CopyAction | Qt::MoveAction );
1014
1015 // propago l'evento
1016 QWidget::mouseMoveEvent(event);
1017}
1018
1019
1021 auto pInstrumentList = Hydrogen::get_instance()->getSong()->getInstrumentList();
1022
1023 if ( nInstrumentNumber == -1 ) {
1024 // Update all lines.
1025 for ( int ii = 0; ii < MAX_INSTRUMENTS; ++ii ) {
1026 auto pInstrumentLine = m_pInstrumentLine[ ii ];
1027 if ( pInstrumentLine != nullptr ) {
1028 auto pInstrument = pInstrumentList->get( ii );
1029 if ( pInstrument == nullptr ) {
1030 ERRORLOG( QString( "Instrument [%1] associated to InstrumentLine [%1] not found" )
1031 .arg( ii ) );
1032 return;
1033 }
1034
1035 pInstrumentLine->setName( pInstrument->get_name() );
1036 pInstrumentLine->setMuted( pInstrument->is_muted() );
1037 pInstrumentLine->setSoloed( pInstrument->is_soloed() );
1038 }
1039 }
1040 }
1041 else {
1042 // Update a specific line
1043 auto pInstrument = pInstrumentList->get( nInstrumentNumber );
1044 if ( pInstrument == nullptr ) {
1045 ERRORLOG( QString( "Instrument [%1] not found" )
1046 .arg( nInstrumentNumber ) );
1047 return;
1048 }
1049
1050 auto pInstrumentLine = m_pInstrumentLine[ nInstrumentNumber ];
1051 if ( pInstrumentLine == nullptr ) {
1052 ERRORLOG( QString( "No InstrumentLine for instrument [%1] created yet" )
1053 .arg( nInstrumentNumber ) );
1054 return;
1055 }
1056
1057 pInstrumentLine->setName( pInstrument->get_name() );
1058 pInstrumentLine->setMuted( pInstrument->is_muted() );
1059 pInstrumentLine->setSoloed( pInstrument->is_soloed() );
1060 }
1061}
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:59
#define ERRORLOG(x)
Definition Object.h:239
#define FOREACH_NOTE_CST_IT_BOUND_LENGTH(_notes, _it, _bound, _pattern)
Iterate over all notes in column _bound in an immutable way if it is contained in _pattern.
Definition Pattern.h:290
#define FOREACH_NOTE_CST_IT_BEGIN_END(_notes, _it)
Iterate over all provided notes in an immutable way.
Definition Pattern.h:268
Generic Button with SVG icons or text.
Definition Button.h:60
@ Icon
Button is both flat and has a transparent background.
@ Toggle
Button is set checkable.
Drag scroller object.
Definition Selection.h:127
void startDrag()
Definition Selection.h:145
void endDrag()
Definition Selection.h:154
Drum pattern editor.
virtual void updateEditor(bool bPatternOnly=false) override
void unlock()
Mutex unlocking of the AudioEngine.
void lock(const char *file, unsigned int line, const char *function)
Mutex locking of the AudioEngine.
Sampler * getSampler() const
bool setStripIsMuted(int nStrip, bool isMuted)
bool setStripIsSoloed(int nStrip, bool isSoloed)
static EventQueue * get_instance()
Returns a pointer to the current EventQueue singleton stored in __instance.
Definition EventQueue.h:224
void push_event(const EventType type, const int nValue)
Queues the next event into the EventQueue.
Hydrogen Audio Engine.
Definition Hydrogen.h:54
bool hasJackAudioDriver() const
void renameJackPorts(std::shared_ptr< Song > pSong)
Calls audioEngine_renameJackPorts() if Preferences::m_bJackTrackOuts is set to true.
Definition Hydrogen.cpp:931
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
int getSelectedInstrumentNumber() const
Definition Hydrogen.h:664
int getSelectedPatternNumber() const
Definition Hydrogen.h:660
void setSelectedInstrumentNumber(int nInstrument, bool bTriggerEvent=true)
Definition Hydrogen.cpp:918
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:649
std::shared_ptr< Instrument > getSelectedInstrument() const
CoreActionController * getCoreActionController() const
Definition Hydrogen.h:639
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
float get_velocity() const
__velocity accessor
Definition Note.h:540
PatternList is a collection of patterns.
Definition PatternList.h:43
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
Definition Pattern.h:46
int get_length() const
set the denominator of the pattern
Definition Pattern.h:340
const notes_t * get_notes() const
get the virtual pattern set
Definition Pattern.h:355
std::multimap< int, Note * > notes_t
< multimap note type
Definition Pattern.h:50
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
unsigned getPatternEditorGridHeight()
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ Font
Either the font size or font family have changed.
@ Colors
At least one of the colors has changed.
void noteOn(Note *pNote)
Start playing a note.
Definition Sampler.cpp:190
MainForm * getMainForm()
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
std::shared_ptr< CommonStrings > getCommonStrings()
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
void setRowSelection(RowSelection rowSelection)
virtual void enterEvent(QEvent *ev)
InstrumentLine(QWidget *pParent)
virtual void mousePressEvent(QMouseEvent *ev) override
virtual void mouseDoubleClickEvent(QMouseEvent *ev) override
virtual void leaveEvent(QEvent *ev)
void onPreferencesChanged(H2Core::Preferences::Changes changes)
void setSelected(bool isSelected)
void setSamplesMissing(bool bSamplesMissing)
H2Core::Pattern * getCurrentPattern()
void setName(const QString &sName)
static constexpr int m_nButtonWidth
virtual void paintEvent(QPaintEvent *ev) override
int m_nInstrumentNumber
The related instrument number.
bool m_bEntered
Whether the cursor entered the boundary of the widget.
virtual void mouseMoveEvent(QMouseEvent *event) override
void updateInstrumentLines()
Update every InstrumentLine, create or destroy lines if necessary.
InstrumentLine * createInstrumentLine()
Create a new InstrumentLine.
virtual void mousePressEvent(QMouseEvent *event) override
PatternEditorInstrumentList(QWidget *parent, PatternEditorPanel *pPatternEditorPanel)
virtual void instrumentParametersChangedEvent(int) override
virtual void dragEnterEvent(QDragEnterEvent *event) override
virtual void selectedInstrumentChangedEvent() override
virtual void updateSongEvent(int nEvent) override
virtual void dropEvent(QDropEvent *event) override
InstrumentLine * m_pInstrumentLine[MAX_INSTRUMENTS]
Pattern Editor Panel.
void selectInstrumentNotes(int nInstrument)
DrumPatternEditor * getDrumPatternEditor()
bool isUsingTriplets() const
uint getResolution() const
static void drawListBackground(QPainter *p, QRect rect, QColor background, bool bHovered)
Draws the background of a row in both the pattern list of the SongEditor and the instrument list in t...
Definition Skin.cpp:144
Widget has a list of items associated with a popup which in turn can open dialog windows.
RowSelection m_rowSelection
Determines the highlighting of the row associated with m_nRowClicked.
RowSelection
Specifies whether the row corresponding to m_nRowClicked should be highlighted and determines the lif...
@ Popup
The m_nRowClicked row was right-clicked and a popup dialog did open and is still shown.
@ None
No highlighting will be drawn for the row last clicked.
@ Dialog
The popup dialog is already closed but the user clicked an associated action and its dialog is still ...
constexpr int getPointSize(H2Core::FontTheme::FontSize fontSize) const
#define MAX_INSTRUMENTS
Maximum number of instruments allowed in Hydrogen.
Definition config.dox:70
#define MAX_NOTES
Maximum number of notes.
Definition config.dox:79
#define UNUSED(v)
Definition Globals.h:42
@ EVENT_SELECTED_INSTRUMENT_CHANGED
Definition EventQueue.h:77