hydrogen 1.2.3
NotePropertiesRuler.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
23#include <core/Hydrogen.h>
26#include <core/Basics/Pattern.h>
28using namespace H2Core;
29
30#include <cassert>
31
32#include "../HydrogenApp.h"
33
34#include "UndoActions.h"
35#include "NotePropertiesRuler.h"
36#include "PatternEditorPanel.h"
37#include "PatternEditorRuler.h"
38#include "DrumPatternEditor.h"
39#include "PianoRollEditor.h"
40#include "../Skin.h"
41
43 : PatternEditor( parent, pPatternEditorPanel )
44 , m_bEntered( false )
45{
46
48 m_mode = mode;
49
50 m_fGridWidth = (Preferences::get_instance())->getPatternEditorGridWidth();
52
53 m_fLastSetValue = 0.0;
54 m_bValueHasBeenSet = false;
55
57 m_nEditorHeight = 210;
58 }
59 else {
60 m_nEditorHeight = 100;
61 }
62
64 setMinimumHeight( m_nEditorHeight );
65
67 show();
68
71
72 setFocusPolicy( Qt::StrongFocus );
73
74 // Generic pattern editor menu contains some operations that don't apply here, and we will want to add
75 // menu options specific to this later.
76 delete m_pPopupMenu;
77 m_pPopupMenu = new QMenu( this );
78 m_pPopupMenu->addAction( tr( "Select &all" ), this, SLOT( selectAll() ) );
79 m_pPopupMenu->addAction( tr( "Clear selection" ), this, SLOT( selectNone() ) );
80
81 setMouseTracking( true );
82}
83
84
85
86
90
91
95void NotePropertiesRuler::wheelEvent(QWheelEvent *ev )
96{
97 Hydrogen *pHydrogen = Hydrogen::get_instance();
98 if ( m_pPattern == nullptr ) {
99 return;
100 }
101
102#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 )
103 prepareUndoAction( ev->position().x() ); //get all old values
104#else
105 prepareUndoAction( ev->x() ); //get all old values
106#endif
107
108 float fDelta;
109 if ( ev->modifiers() == Qt::ControlModifier || ev->modifiers() == Qt::AltModifier ) {
110 fDelta = 0.01; // fine control
111 } else {
112 fDelta = 0.05; // coarse control
113 }
114 if ( ev->angleDelta().y() < 0 ) {
115 fDelta = fDelta * -1.0;
116 }
117
118#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 )
119 int nColumn = getColumn( ev->position().x() );
120#else
121 int nColumn = getColumn( ev->x() );
122#endif
123
125
126 auto pHydrogenApp = HydrogenApp::get_instance();
127 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
128 pHydrogenApp->setHideKeyboardCursor( true );
129
130 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
131 if ( pSelectedInstrument == nullptr ) {
132 ERRORLOG( "No instrument selected" );
133 return;
134 }
135
136 // Gather notes to act on: selected or under the mouse cursor
137 std::list< Note *> notes;
138 if ( m_selection.begin() != m_selection.end() ) {
139 for ( Note *pNote : m_selection ) {
140 notes.push_back( pNote );
141 }
142 } else {
144 notes.push_back( it->second );
145 }
146 }
147
148 bool bValueChanged = false;
149 for ( Note *pNote : notes ) {
150 assert( pNote );
151 if ( pNote->get_instrument() != pSelectedInstrument && !m_selection.isSelected( pNote ) ) {
152 continue;
153 }
154 bValueChanged = true;
155 adjustNotePropertyDelta( pNote, fDelta, /* bMessage=*/ true );
156 }
157
158 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
159 // Immediate update to prevent visual delay.
161 if ( ! bValueChanged ) {
162 update();
163 }
164 }
165
166 if ( bValueChanged ) {
169 update();
170 }
171}
172
173
175 if ( ev->button() == Qt::RightButton ) {
176 m_pPopupMenu->popup( ev->globalPos() );
177
178 } else {
179 // Treat single click as an instantaneous drag
180 propertyDragStart( ev );
181 propertyDragUpdate( ev );
183 }
184}
185
187 if ( ev->x() > m_nActiveWidth ) {
188 return;
189 }
190
192
193 auto pHydrogenApp = HydrogenApp::get_instance();
194
195 // Hide cursor in case this behavior was selected in the
196 // Preferences.
197 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
198 pHydrogenApp->setHideKeyboardCursor( true );
199
200 // Cursor just got hidden.
201 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
202 // Immediate update to prevent visual delay.
204 update();
205 }
206
207 // Update cursor position
208 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
209 int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
210 if ( ( m_pPattern != nullptr &&
211 nColumn >= (int)m_pPattern->get_length() ) ||
212 nColumn >= MAX_INSTRUMENTS ) {
213 return;
214 }
215
217
218 update();
220 }
221}
222
224 if ( m_selection.isMoving() ) {
225 prepareUndoAction( ev->x() );
227 } else {
228 propertyDragStart( ev );
229 propertyDragUpdate( ev );
230 }
231}
232
234 propertyDragUpdate( ev );
235}
236
239}
240
241
243 if ( m_pPattern == nullptr ) {
244 return;
245 }
246 Hydrogen *pHydrogen = Hydrogen::get_instance();
247
248 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
249 if ( pSelectedInstrument == nullptr ) {
250 ERRORLOG( "No instrument selected" );
251 return;
252 }
253
254 float fDelta;
255
256 QPoint movingOffset = m_selection.movingOffset();
258 fDelta = (float)-movingOffset.y() / 10;
259 } else {
260 fDelta = (float)-movingOffset.y() / height();
261 }
262
263 // Only send a status message for the update in case a single note
264 // was selected.
265 bool bSendStatusMsg = false;
266 int nNotes = 0;
267 for ( Note *pNote : m_selection ) {
268 ++nNotes;
269 }
270 if ( nNotes == 1 ) {
271 bSendStatusMsg = true;
272 }
273
274 bool bValueChanged = false;
275 for ( Note *pNote : m_selection ) {
276 if ( pNote->get_instrument() == pSelectedInstrument || m_selection.isSelected( pNote ) ) {
277
278 // Record original note if not already recorded
279 if ( m_oldNotes.find( pNote ) == m_oldNotes.end() ) {
280 m_oldNotes[ pNote ] = new Note( pNote );
281 }
282
283 adjustNotePropertyDelta( pNote, fDelta, bSendStatusMsg );
284 bValueChanged = true;
285 }
286 }
287
288 if ( bValueChanged ) {
290 update();
291 }
292}
293
298 update();
299}
300
302 for ( auto it : m_oldNotes ) {
303 delete it.second;
304 }
305 m_oldNotes.clear();
306}
307
310 for ( auto it : m_oldNotes ) {
311 Note *pNote = it.first, *pOldNote = it.second;
312 switch ( m_mode ) {
314 pNote->set_velocity( pOldNote->get_velocity() );
315 break;
317 pNote->setPan( pOldNote->getPan() );
318 break;
320 pNote->set_lead_lag( pOldNote->get_lead_lag() );
321 break;
323 pNote->set_key_octave( pOldNote->get_key(), pOldNote->get_octave() );
324 break;
326 pNote->set_probability( pOldNote->get_probability() );
327 break;
328 default:
329 break;
330 }
331 }
332
333 if ( m_oldNotes.size() == 0 ) {
334 for ( const auto& it : m_oldNotes ){
336 }
337 }
338
340}
341
342
344{
345 if ( m_pPattern == nullptr ) {
346 return;
347 }
348
349 if ( ev->buttons() == Qt::NoButton ) {
350 int nColumn = getColumn( ev->x() );
351 bool bFound = false;
353 bFound = true;
354 break;
355 }
356 if ( bFound ) {
357 setCursor( Qt::PointingHandCursor );
358 } else {
359 unsetCursor();
360 }
361
362 } else {
364 }
365}
366
367
369{
370 setCursor( Qt::CrossCursor );
371 prepareUndoAction( ev->x() );
373 update();
374}
375
376
379{
380 if ( m_pPattern == nullptr ) {
381 return;
382 }
383 Hydrogen *pHydrogen = Hydrogen::get_instance();
384
386
387 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
388 if ( pSelectedInstrument == nullptr ) {
389 ERRORLOG( "No instrument selected" );
390 return;
391 }
392
393 if ( m_selection.begin() != m_selection.end() ) {
394 // If there is a selection, preserve the initial state of all the selected notes.
395 for ( Note *pNote : m_selection ) {
396 if ( pNote->get_instrument() == pSelectedInstrument || m_selection.isSelected( pNote ) ) {
397 m_oldNotes[ pNote ] = new Note( pNote );
398 }
399 }
400
401 } else {
402 // No notes are selected. The target notes to adjust are all those at column given by 'x', so we preserve these.
403 int nColumn = getColumn( x );
405 nColumn, m_pPattern ) {
406 Note *pNote = it->second;
407 if ( pNote->get_instrument() == pSelectedInstrument ) {
408 m_oldNotes[ pNote ] = new Note( pNote );
409 }
410 }
411 }
412}
413
418{
419 if (m_pPattern == nullptr) {
420 return;
421 }
422
423 int nColumn = getColumn( ev->x() );
424
426
427 auto pHydrogenApp = HydrogenApp::get_instance();
428 auto pHydrogen = Hydrogen::get_instance();
429
430 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
431 pHydrogenApp->setHideKeyboardCursor( true );
432
433 if ( m_nDragPreviousColumn != nColumn ) {
434 // Complete current undo action, and start a new one.
436 prepareUndoAction( ev->x() );
437 }
438
439 float val = height() - ev->y();
440 if (val > height()) {
441 val = height();
442 }
443 else if (val < 0.0) {
444 val = 0.0;
445 }
446 int keyval = val;
447 val = val / height(); // val is normalized, in [0;1]
448 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
449 if ( pSelectedInstrument == nullptr ) {
450 ERRORLOG( "No instrument selected" );
451 return;
452 }
453
454 bool bValueSet = false;
455
457 Note *pNote = it->second;
458
459 if ( pNote->get_instrument() != pSelectedInstrument &&
460 !m_selection.isSelected( pNote ) ) {
461 continue;
462 }
463 if ( m_mode == PatternEditor::Mode::Velocity && !pNote->get_note_off() ) {
464 pNote->set_velocity( val );
465 m_fLastSetValue = val;
466 bValueSet = true;
467 }
468 else if ( m_mode == PatternEditor::Mode::Pan && !pNote->get_note_off() ){
469 if ( (ev->button() == Qt::MiddleButton)
470 || (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
471 val = 0.5; // central pan
472 }
473 pNote->setPanWithRangeFrom0To1( val ); // checks the boundaries
475 bValueSet = true;
476
477 }
479 if ( (ev->button() == Qt::MiddleButton) ||
480 (ev->modifiers() == Qt::ControlModifier &&
481 ev->button() == Qt::LeftButton) ) {
482 pNote->set_lead_lag(0.0);
483 m_fLastSetValue = 0.0;
484 bValueSet = true;
485 }
486 else {
487 m_fLastSetValue = val * -2.0 + 1.0;
488 bValueSet = true;
490 }
491 }
493 if ( ev->button() != Qt::MiddleButton &&
494 ! ( ev->modifiers() == Qt::ControlModifier &&
495 ev->button() == Qt::LeftButton ) ) {
496 //set the note height
497 int k = 666;
498 int o = 666;
499 if( keyval >= 6 && keyval <= 125 ) {
500 k = ( keyval - 6 ) / 10;
501 }
502 else if( keyval >= 135 && keyval <= 205 ) {
503 o = ( keyval - 166 ) / 10;
504 if ( o == -4 ) {
505 o = -3; // 135
506 }
507 }
508 m_fLastSetValue = o * 12 + k;
509 bValueSet = true;
510 pNote->set_key_octave((Note::Key)k,(Note::Octave)o); // won't set wrong values see Note::set_key_octave
511 }
512 }
513 else if ( m_mode == PatternEditor::Mode::Probability && !pNote->get_note_off() ) {
514 m_fLastSetValue = val;
515 bValueSet = true;
516 pNote->set_probability( val );
517 }
518
519 if ( bValueSet ) {
521 m_bValueHasBeenSet = true;
523 }
524 }
525
526 // Cursor just got hidden.
527 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
528 // Immediate update to prevent visual delay.
530 }
531
532 m_nDragPreviousColumn = nColumn;
534 update();
535
538}
539
541{
543 unsetCursor();
545 update();
546}
547
550void NotePropertiesRuler::adjustNotePropertyDelta( Note *pNote, float fDelta, bool bMessage )
551{
552 Note *pOldNote = m_oldNotes[ pNote ];
553 assert( pOldNote );
554
555 bool bValueSet = false;
556
557 switch (m_mode) {
559 if ( !pNote->get_note_off() ) {
560 float fVelocity = qBound( VELOCITY_MIN, (pOldNote->get_velocity() + fDelta), VELOCITY_MAX );
561 pNote->set_velocity( fVelocity );
562 m_fLastSetValue = fVelocity;
563 bValueSet = true;
564 }
565 break;
567 if ( !pNote->get_note_off() ) {
568 float fVal = pOldNote->getPanWithRangeFrom0To1() + fDelta; // value in [0,1] or slight out of boundaries
569 pNote->setPanWithRangeFrom0To1( fVal ); // checks the boundaries as well
571 bValueSet = true;
572 }
573 break;
575 {
576 float fLeadLag = qBound( LEAD_LAG_MIN, pOldNote->get_lead_lag() - fDelta, LEAD_LAG_MAX );
577 pNote->set_lead_lag( fLeadLag );
578 m_fLastSetValue = fLeadLag;
579 bValueSet = true;
580 }
581 break;
583 if ( !pNote->get_note_off() ) {
584 float fProbability = qBound( 0.0f, pOldNote->get_probability() + fDelta, 1.0f );
585 pNote->set_probability( fProbability );
586 m_fLastSetValue = fProbability;
587 bValueSet = true;
588 }
589 break;
591 int nPitch = qBound( 12 * OCTAVE_MIN, (int)( pOldNote->get_notekey_pitch() + fDelta ),
592 12 * OCTAVE_MAX + KEY_MAX );
593 Note::Octave octave;
594 if ( nPitch >= 0 ) {
595 octave = (Note::Octave)( nPitch / 12 );
596 } else {
597 octave = (Note::Octave)( (nPitch-11) / 12 );
598 }
599 Note::Key key = (Note::Key)( nPitch - 12 * (int)octave );
600
601 pNote->set_key_octave( key, octave );
602 m_fLastSetValue = 12 * octave + key;
603
604 bValueSet = true;
605 break;
606 }
607
608 if ( bValueSet ) {
610 m_bValueHasBeenSet = true;
611 if ( bMessage ) {
613 }
614 }
615}
616
618{
619 if ( m_pPattern == nullptr ) {
620 return;
621 }
622
623 auto pHydrogenApp = HydrogenApp::get_instance();
624 auto pHydrogen = Hydrogen::get_instance();
625 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
626
627 const int nWordSize = 5;
628 bool bIsSelectionKey = m_selection.keyPressEvent( ev );
629 bool bUnhideCursor = true;
630
631 bool bValueChanged = false;
632
633 if ( bIsSelectionKey ) {
634 // Key was claimed by selection
635 } else if ( ev->matches( QKeySequence::MoveToNextChar ) || ev->matches( QKeySequence::SelectNextChar ) ) {
636 // ->
638
639 } else if ( ev->matches( QKeySequence::MoveToNextWord ) || ev->matches( QKeySequence::SelectNextWord ) ) {
640 // ->
642
643 } else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ev->matches( QKeySequence::SelectEndOfLine ) ) {
644 // -->|
646
647 } else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ev->matches( QKeySequence::SelectPreviousChar ) ) {
648 // <-
650
651 } else if ( ev->matches( QKeySequence::MoveToPreviousWord ) || ev->matches( QKeySequence::SelectPreviousWord ) ) {
652 // <-
654
655 } else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ev->matches( QKeySequence::SelectStartOfLine ) ) {
656 // |<--
658
659 } else {
660
661 // Value adjustments
662 float fDelta = 0.0;
663 bool bRepeatLastValue = false;
664
665 if ( ev->matches( QKeySequence::MoveToPreviousLine ) ) {
666 // Key: Up: increase note parameter value
667 fDelta = 0.1;
668
669 } else if ( ev->key() == Qt::Key_Up && ev->modifiers() & Qt::AltModifier ) {
670 // Key: Alt+Up: increase parameter slightly
671 fDelta = 0.01;
672
673 } else if ( ev->matches( QKeySequence::MoveToNextLine ) ) {
674 // Key: Down: decrease note parameter value
675 fDelta = -0.1;
676
677 } else if ( ev->key() == Qt::Key_Down && ev->modifiers() & Qt::AltModifier ) {
678 // Key: Alt+Up: decrease parameter slightly
679 fDelta = -0.01;
680
681 } else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) ) {
682 // Key: MoveToStartOfDocument: increase parameter to maximum value
683 fDelta = 1.0;
684
685 } else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) ) {
686 // Key: MoveEndOfDocument: decrease parameter to minimum value
687 fDelta = -1.0;
688
689 } else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
690 // Key: Enter/Return: repeat last parameter value set.
691 if ( m_bValueHasBeenSet ) {
692 bRepeatLastValue = true;
693 }
694
695 } else if ( ev->matches( QKeySequence::SelectAll ) ) {
696 // Key: Ctrl + A: Select all
697 bUnhideCursor = false;
698 selectAll();
699
700 } else if ( ev->matches( QKeySequence::Deselect ) ) {
701 // Key: Shift + Ctrl + A: clear selection
702 bUnhideCursor = false;
703 selectNone();
704
705 }
706
707 if ( fDelta != 0.0 || bRepeatLastValue ) {
709
710 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
711 if ( pSelectedInstrument == nullptr ) {
712 ERRORLOG( "No instrument selected" );
713 return;
714 }
715
716 int nNotes = 0;
717
718 // Collect notes to apply the change to
719 std::list< Note *> notes;
720 if ( m_selection.begin() != m_selection.end() ) {
721 for ( Note *pNote : m_selection ) {
722 nNotes++;
723 notes.push_back( pNote );
724 }
725 } else {
727 Note *pNote = it->second;
728 assert( pNote );
729 assert( pNote->get_position() == column );
730 if ( pNote->get_instrument() == pSelectedInstrument ) {
731 nNotes++;
732 notes.push_back( pNote );
733 }
734 }
735 }
736
737 // For the NoteKeyEditor, adjust the pitch by a whole semitone
739 if ( fDelta > 0.0 ) {
740 fDelta = 1;
741 } else if ( fDelta < 0.0 ) {
742 fDelta = -1;
743 }
744 }
745
747
748 for ( Note *pNote : notes ) {
749 bValueChanged = true;
750
751 if ( !bRepeatLastValue ) {
752
753 // Apply delta to the property
754 adjustNotePropertyDelta( pNote, fDelta, nNotes == 1 );
755
756 } else {
757
758 bool bValueSet = false;
759
760 // Repeating last value
761 switch (m_mode) {
763 if ( !pNote->get_note_off() ) {
764 pNote->set_velocity( m_fLastSetValue );
765 bValueSet = true;
766 }
767 break;
769 if ( !pNote->get_note_off() ) {
770 if ( m_fLastSetValue > 1. ) { // TODO whats this for? is it ever reached?
771 printf( "reached m_fLastSetValue > 1 in NotePropertiesRuler.cpp\n" );
772 pNote->setPanWithRangeFrom0To1( m_fLastSetValue );
773 }
774 bValueSet = true;
775 }
776 break;
778 pNote->set_lead_lag( m_fLastSetValue );
779 bValueSet = true;
780 break;
782 if ( !pNote->get_note_off() ) {
783 pNote->set_probability( m_fLastSetValue );
784 bValueSet = true;
785 }
786 break;
788 pNote->set_key_octave( (Note::Key)( (int)m_fLastSetValue % 12 ),
789 (Note::Octave)( (int)m_fLastSetValue / 12 ) );
790 bValueSet = true;
791 break;
792 }
793
794 if ( bValueSet ) {
795 if ( nNotes == 1 ) {
797 }
799 }
800 }
801 }
803 } else {
804 pHydrogenApp->setHideKeyboardCursor( true );
805 ev->ignore();
806
807 // Cursor either just got hidden.
808 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
809 // Immediate update to prevent visual delay.
811 update();
812 }
813 return;
814 }
815 }
816 if ( bUnhideCursor ) {
817 pHydrogenApp->setHideKeyboardCursor( false );
818 }
819
820 // Cursor either just got hidden or was moved.
821 if ( ! HydrogenApp::get_instance()->hideKeyboardCursor() ||
822 bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
823 // Immediate update to prevent visual delay.
825 }
826
828
829 if ( bValueChanged ) {
831 }
832 update();
833
834 ev->accept();
835
836}
837
839{
840 if ( m_nSelectedPatternNumber == -1 ) {
841 // No pattern selected.
842 return;
843 }
844
845 auto pInstrumentList = Hydrogen::get_instance()->getSong()->getInstrumentList();
846 int nSize = m_oldNotes.size();
847 if ( nSize != 0 ) {
848 QUndoStack *pUndoStack = HydrogenApp::get_instance()->m_pUndoStack;
849
850 if ( nSize != 1 ) {
851 pUndoStack->beginMacro( QString( tr( "Edit [%1] property of [%2] notes" ) )
853 .arg( nSize ) );
854 }
855 for ( auto it : m_oldNotes ) {
856 Note *pNewNote = it.first, *pOldNote = it.second;
857 pUndoStack->push( new SE_editNotePropertiesVolumeAction( pNewNote->get_position(),
858 m_mode,
860 pInstrumentList->index( pNewNote->get_instrument() ),
861 pNewNote->get_velocity(),
862 pOldNote->get_velocity(),
863 pNewNote->getPan(),
864 pOldNote->getPan(),
865 pNewNote->get_lead_lag(),
866 pOldNote->get_lead_lag(),
867 pNewNote->get_probability(),
868 pOldNote->get_probability(),
869 pNewNote->get_key(),
870 pOldNote->get_key(),
871 pNewNote->get_octave(),
872 pOldNote->get_octave() ) );
873 }
874 if ( nSize != 1 ) {
875 pUndoStack->endMacro();
876 }
877 }
879}
880
882{
883 if (!isVisible()) {
884 return;
885 }
886
887 auto pPref = Preferences::get_instance();
888
889 qreal pixelRatio = devicePixelRatio();
890 if ( pixelRatio != m_pBackgroundPixmap->devicePixelRatio() ||
893 }
894
895 QPainter painter(this);
896 painter.drawPixmap( ev->rect(), *m_pBackgroundPixmap,
897 QRectF( pixelRatio * ev->rect().x(),
898 pixelRatio * ev->rect().y(),
899 pixelRatio * ev->rect().width(),
900 pixelRatio * ev->rect().height() ) );
901
902 // Draw playhead
903 if ( m_nTick != -1 ) {
904
905 int nOffset = Skin::getPlayheadShaftOffset();
906 int nX = static_cast<int>(static_cast<float>(PatternEditor::nMargin) +
907 static_cast<float>(m_nTick) *
908 m_fGridWidth );
909 Skin::setPlayheadPen( &painter, false );
910 painter.drawLine( nX, 0, nX, height() );
911 }
912
913 drawFocus( painter );
914
915 m_selection.paintSelection( &painter );
916
917 // cursor
918 if ( hasFocus() && ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
920
921 QPen pen( pPref->getColorTheme()->m_cursorColor );
922 pen.setWidth( 2 );
923 painter.setPen( pen );
924 painter.setBrush( Qt::NoBrush );
925 painter.setRenderHint( QPainter::Antialiasing );
926 painter.drawRoundedRect( QRect( x-m_fGridWidth*3, 0 + 3, m_fGridWidth*6, height() - 6 ), 4, 4 );
927 }
928}
929
930void NotePropertiesRuler::drawFocus( QPainter& painter ) {
931
932 if ( ! m_bEntered && ! hasFocus() ) {
933 return;
934 }
935
937
938 QColor color = pPref->getColorTheme()->m_highlightColor;
939
940 // If the mouse is placed on the widget but the user hasn't
941 // clicked it yet, the highlight will be done more transparent to
942 // indicate that keyboard inputs are not accepted yet.
943 if ( ! hasFocus() ) {
944 color.setAlpha( 125 );
945 }
946
947 const QScrollArea* pScrollArea;
948
949 switch ( m_mode ) {
952 break;
955 break;
958 break;
961 break;
964 break;
965 default:
966 return;
967 }
968 int nStartY = pScrollArea->verticalScrollBar()->value();
969 int nStartX = pScrollArea->horizontalScrollBar()->value();
970 int nEndY = nStartY + pScrollArea->viewport()->size().height();
971 // In order to match the width used in the DrumPatternEditor.
972 int nEndX = std::min( nStartX + pScrollArea->viewport()->size().width(),
973 static_cast<int>( m_nEditorWidth ) );
974
975 int nMargin;
976 if ( nEndX == static_cast<int>( m_nEditorWidth ) ) {
977 nEndX = nEndX - 2;
978 nMargin = 1;
979 } else {
980 nMargin = 0;
981 }
982
983 QPen pen( color );
984 pen.setWidth( 4 );
985 painter.setPen( pen );
986 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nEndX, nStartY ) );
987 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nStartX, nEndY ) );
988 painter.drawLine( QPoint( nEndX, nEndY ), QPoint( nStartX, nEndY ) );
989
990 if ( nMargin != 0 ) {
991 // Since for all other lines we are drawing at a border with just
992 // half of the line being painted in the visual viewport, there
993 // has to be some tweaking since the NotePropertiesRuler is
994 // paintable to the right.
995 pen.setWidth( 2 );
996 painter.setPen( pen );
997 }
998 painter.drawLine( QPoint( nEndX + nMargin, nStartY ), QPoint( nEndX + nMargin, nEndY ) );
999
1000}
1001
1003 UNUSED( nValue );
1004 update();
1005}
1006
1008 UNUSED( ev );
1009 m_bEntered = true;
1010 update();
1011}
1012
1014 UNUSED( ev );
1015 m_bEntered = false;
1016 update();
1017}
1018
1019void NotePropertiesRuler::drawDefaultBackground( QPainter& painter, int nHeight, int nIncrement ) {
1020
1021 auto pPref = H2Core::Preferences::get_instance();
1022
1023 const QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
1024 const QColor lineColor( pPref->getColorTheme()->m_patternEditor_line5Color );
1025 const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
1026 const QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
1027 const QColor backgroundInactiveColor( pPref->getColorTheme()->m_windowColor );
1028
1029 if ( nHeight == 0 ) {
1030 nHeight = height();
1031 }
1032 if ( nIncrement == 0 ) {
1033 nIncrement = nHeight / 10;
1034 }
1035
1036 painter.fillRect( 0, 0, m_nActiveWidth, height(), backgroundColor );
1037 painter.fillRect( m_nActiveWidth, 0, m_nEditorWidth - m_nActiveWidth,
1038 height(), backgroundInactiveColor );
1039
1040 drawGridLines( painter, Qt::DotLine );
1041
1042 painter.setPen( lineColor );
1043 for (unsigned y = 0; y < nHeight; y += nIncrement ) {
1044 painter.drawLine( PatternEditor::nMargin, y, m_nActiveWidth, y );
1045 }
1046
1047 painter.setPen( borderColor );
1048 painter.drawLine( 0, 0, m_nActiveWidth, 0 );
1049 painter.drawLine( 0, m_nEditorHeight - 1, m_nActiveWidth, m_nEditorHeight - 1 );
1050
1051 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1052 painter.setPen( lineInactiveColor );
1053 for (unsigned y = 0; y < nHeight; y += nIncrement ) {
1054 painter.drawLine( m_nActiveWidth, y, m_nEditorWidth, y );
1055 }
1056
1057 painter.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
1058 painter.drawLine( m_nActiveWidth, m_nEditorHeight - 1,
1060 }
1061}
1062
1064{
1065 auto pPref = H2Core::Preferences::get_instance();
1066 auto pHydrogen = Hydrogen::get_instance();
1067
1068 QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
1069 const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
1070 QPainter p( pixmap );
1071
1073
1074 // draw velocity lines
1075 if ( m_pPattern != nullptr ) {
1076 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
1077 if ( pSelectedInstrument == nullptr ) {
1078 ERRORLOG( "No instrument selected" );
1079 return;
1080 }
1081
1082 QPen selectedPen( selectedNoteColor() );
1083 selectedPen.setWidth( 2 );
1084
1085 const Pattern::notes_t* notes = m_pPattern->get_notes();
1087 Note *pposNote = it->second;
1088 assert( pposNote );
1089 uint pos = pposNote->get_position();
1090 int xoffset = 0;
1092 Note *pNote = coit->second;
1093 assert( pNote );
1094 if ( pNote->get_instrument() != pSelectedInstrument
1095 && !m_selection.isSelected( pNote ) ) {
1096 continue;
1097 }
1098 uint x_pos = PatternEditor::nMargin + pos * m_fGridWidth;
1099 uint line_end = height();
1100
1101
1102 uint value = 0;
1104 value = (uint)(pNote->get_velocity() * height());
1105 }
1107 value = (uint)(pNote->get_probability() * height());
1108 }
1109 uint line_start = line_end - value;
1110 QColor noteColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
1111 int nLineWidth = 3;
1112
1113 p.fillRect( x_pos - 1 + xoffset, line_start,
1114 nLineWidth, line_end - line_start,
1115 noteColor );
1116 p.setPen( QPen( Qt::black, 1 ) );
1117 p.setRenderHint( QPainter::Antialiasing );
1118 p.drawRoundedRect( x_pos - 1 - 1 + xoffset, line_start - 1,
1119 nLineWidth + 2, line_end - line_start + 2, 2, 2 );
1120
1121 if ( m_selection.isSelected( pNote ) ) {
1122 p.setPen( selectedPen );
1123 p.setRenderHint( QPainter::Antialiasing );
1124 p.drawRoundedRect( x_pos - 1 -2 + xoffset, line_start - 2,
1125 nLineWidth + 4, line_end - line_start + 4 ,
1126 4, 4 );
1127 }
1128 xoffset++;
1129 }
1130 }
1131 }
1132
1133 p.setPen( borderColor );
1134 p.setRenderHint( QPainter::Antialiasing );
1135 p.drawLine( 0, 0, m_nEditorWidth, 0 );
1136 p.setPen( QPen( borderColor, 2 ) );
1138
1139 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1140 p.setPen( lineInactiveColor );
1141 p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
1142 p.setPen( QPen( lineInactiveColor, 2 ) );
1143 p.drawLine( m_nActiveWidth, m_nEditorHeight,
1145 }
1146}
1147
1149{
1150 auto pPref = H2Core::Preferences::get_instance();
1151 auto pHydrogen = Hydrogen::get_instance();
1152
1153 QColor baseLineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
1154 QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
1155 const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
1156
1157 QPainter p( pixmap );
1158
1160
1161 // central line
1162 p.setPen( baseLineColor );
1163 p.drawLine(0, height() / 2.0, m_nActiveWidth, height() / 2.0);
1164 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1165 p.setPen( lineInactiveColor );
1166 p.drawLine( m_nActiveWidth, height() / 2.0,
1167 m_nEditorWidth, height() / 2.0);
1168 }
1169
1170 if ( m_pPattern != nullptr ) {
1171 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
1172 if ( pSelectedInstrument == nullptr ) {
1173 ERRORLOG( "No instrument selected" );
1174 return;
1175 }
1176
1177 QPen selectedPen( selectedNoteColor() );
1178 selectedPen.setWidth( 2 );
1179
1180 const Pattern::notes_t* notes = m_pPattern->get_notes();
1182 Note *pposNote = it->second;
1183 assert( pposNote );
1184 uint pos = pposNote->get_position();
1185 int xoffset = 0;
1187 Note *pNote = coit->second;
1188 assert( pNote );
1189 if ( pNote->get_note_off() || (pNote->get_instrument()
1190 != pSelectedInstrument
1191 && !m_selection.isSelected( pNote ) ) ) {
1192 continue;
1193 }
1194 uint x_pos = PatternEditor::nMargin + pNote->get_position() * m_fGridWidth;
1195 QColor noteColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
1196
1197 p.setPen( Qt::NoPen );
1198
1199 float fValue = 0;
1201 fValue = pNote->getPan();
1202 } else if ( m_mode == PatternEditor::Mode::LeadLag ) {
1203 fValue = -1 * pNote->get_lead_lag();
1204 }
1205
1206 // Rounding in order to not miss the center due to
1207 // rounding errors introduced in the Note class
1208 // internals.
1209 fValue *= 100;
1210 fValue = std::round( fValue );
1211 fValue /= 100;
1212
1213 int nLineWidth = 3;
1214 p.setPen( QPen( Qt::black, 1 ) );
1215 p.setRenderHint( QPainter::Antialiasing );
1216 if ( fValue == 0.f ) {
1217 // value is centered - draw circle
1218 int y_pos = (int)( height() * 0.5 );
1219 p.setBrush(QColor( noteColor ));
1220 p.drawEllipse( x_pos-4 + xoffset, y_pos-4, 8, 8);
1221 p.setBrush( Qt::NoBrush );
1222
1223 if ( m_selection.isSelected( pNote ) ) {
1224 p.setPen( selectedPen );
1225 p.setRenderHint( QPainter::Antialiasing );
1226 p.drawEllipse( x_pos - 6 + xoffset, y_pos - 6,
1227 12, 12);
1228 }
1229 }
1230 else {
1231 // value was altered - draw a rectangle
1232 int nHeight = 0.5 * height() * std::abs( fValue ) + 5;
1233 int nStartY = height() * 0.5 - 2;
1234 if ( fValue >= 0 ) {
1235 nStartY = nStartY - nHeight + 5;
1236 }
1237
1238 p.fillRect( x_pos - 1 + xoffset, nStartY,
1239 nLineWidth, nHeight, QColor( noteColor ) );
1240 p.drawRoundedRect( x_pos - 1 + xoffset - 1, nStartY - 1,
1241 nLineWidth + 2, nHeight + 2, 2, 2 );
1242
1243 if ( m_selection.isSelected( pNote ) ) {
1244 p.setPen( selectedPen );
1245 p.drawRoundedRect( x_pos - 1 - 2 + xoffset, nStartY - 2,
1246 nLineWidth + 4, nHeight + 4,
1247 4, 4 );
1248 }
1249 }
1250 xoffset++;
1251 }
1252 }
1253 }
1254
1255
1256 p.setPen( borderColor );
1257 p.setRenderHint( QPainter::Antialiasing );
1258 p.drawLine( 0, 0, m_nEditorWidth, 0 );
1259 p.setPen( QPen( borderColor, 2 ) );
1261
1262 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1263 p.setPen( lineInactiveColor );
1264 p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
1265 p.setPen( QPen( lineInactiveColor, 2 ) );
1266 p.drawLine( m_nActiveWidth, m_nEditorHeight,
1268 }
1269}
1270
1272{
1273 auto pPref = H2Core::Preferences::get_instance();
1274 QColor backgroundColor = pPref->getColorTheme()->m_patternEditor_backgroundColor;
1275 const QColor backgroundInactiveColor( pPref->getColorTheme()->m_windowColor );
1276 QColor alternateRowColor = pPref->getColorTheme()->m_patternEditor_alternateRowColor;
1277 QColor octaveColor = pPref->getColorTheme()->m_patternEditor_octaveRowColor;
1278 QColor lineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
1279 const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
1280 QColor textColor( pPref->getColorTheme()->m_patternEditor_textColor );
1281
1282 QPainter p( pixmap );
1283 p.fillRect( 0, 0, m_nEditorWidth, m_nEditorHeight, backgroundInactiveColor );
1284 drawDefaultBackground( p, 80, 10 );
1285
1286 // fill the background of the key region;
1287 for ( unsigned y = 90; y < 210; y = y + 10 ) {
1288
1289 if ( y == 100 || y == 120 || y == 140 || y == 170 || y == 190) {
1290 p.setPen( QPen( alternateRowColor,
1291 9, Qt::SolidLine, Qt::FlatCap ) );
1292 }
1293 else {
1294 p.setPen( QPen( octaveColor, 9, Qt::SolidLine, Qt::FlatCap) );
1295 }
1296
1297 p.drawLine( PatternEditor::nMargin, y, m_nActiveWidth, y );
1298 }
1299
1300 // Annotate with note class names
1301 static QString noteNames[] = { tr( "B" ), tr( "A#" ), tr( "A" ), tr( "G#" ), tr( "G" ), tr( "F#" ),
1302 tr( "F" ), tr( "E" ), tr( "D#" ), tr( "D" ), tr( "C#" ), tr( "C" ) };
1303
1304 QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
1305
1306 p.setFont( font );
1307 p.setPen( textColor );
1308 for ( int n = 0; n < 12; n++ ) {
1309 p.drawText( 3, 90 + 10 * n +3, noteNames[n] );
1310 }
1311
1312 // Horizontal grid lines in the key region
1313 p.setPen( QPen( lineColor, 1, Qt::SolidLine));
1314 for (unsigned y = 90; y <= 210; y = y + 10 ) {
1315 p.drawLine( PatternEditor::nMargin, y - 5, m_nActiveWidth, y-5);
1316 }
1317
1318 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1319 p.setPen( lineInactiveColor );
1320 for (unsigned y = 90; y <= 210; y = y + 10 ) {
1321 p.drawLine( m_nActiveWidth, y - 5, m_nEditorWidth, y-5);
1322 }
1323 }
1324
1325 if ( m_pPattern != nullptr ) {
1326 auto pSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrument();
1327 if ( pSelectedInstrument == nullptr ) {
1328 DEBUGLOG( "No instrument selected" );
1329 return;
1330 }
1331 QPen selectedPen( selectedNoteColor() );
1332 selectedPen.setWidth( 2 );
1333
1334 const Pattern::notes_t* notes = m_pPattern->get_notes();
1336 Note *pNote = it->second;
1337 assert( pNote );
1338 if ( pNote->get_instrument() != pSelectedInstrument
1339 && !m_selection.isSelected( pNote ) ) {
1340 continue;
1341 }
1342 if ( !pNote->get_note_off() ) {
1343 //paint the octave
1344 uint x_pos = 17 + pNote->get_position() * m_fGridWidth;
1345 uint y_pos = (4-pNote->get_octave())*10-3;
1346 p.setBrush( DrumPatternEditor::computeNoteColor( pNote->get_velocity() ) );
1347 p.drawEllipse( x_pos, y_pos, 6, 6);
1348
1349 //paint note
1350 int d = 8;
1351 int k = pNote->get_key();
1352 x_pos = 16 + pNote->get_position() * m_fGridWidth;
1353 y_pos = 200-(k*10)-4;
1354
1355 x_pos -= 1;
1356 y_pos -= 1;
1357 d += 2;
1358 p.setPen( QPen( Qt::black, 1 ) );
1359 p.setBrush( DrumPatternEditor::computeNoteColor( pNote->get_velocity() ) );
1360 p.drawEllipse( x_pos, y_pos, d, d);
1361
1362 // Paint selection outlines
1363 int nLineWidth = 3;
1364 if ( m_selection.isSelected( pNote ) ) {
1365 p.setPen( selectedPen );
1366 p.setBrush( Qt::NoBrush );
1367 p.setRenderHint( QPainter::Antialiasing );
1368 p.drawRoundedRect( x_pos - 1 -2 +3, 2,
1369 nLineWidth + 4 + 4, height() - 4,
1370 4, 4 );
1371 }
1372 }
1373 }
1374 }
1375
1376 p.setPen( lineColor );
1377 p.setRenderHint( QPainter::Antialiasing );
1378 p.drawLine( 0, 0, m_nEditorWidth, 0 );
1379 p.setPen( QPen( lineColor, 2 ) );
1381
1382 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
1383 p.setPen( lineInactiveColor );
1384 p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
1385 p.setPen( QPen( lineInactiveColor, 2 ) );
1386 p.drawLine( m_nActiveWidth, m_nEditorHeight,
1388 }
1389}
1390
1391
1393{
1394 Hydrogen *pHydrogen = Hydrogen::get_instance();
1395 PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
1396 int nSelectedPatternNumber = pHydrogen->getSelectedPatternNumber();
1397 if ( (nSelectedPatternNumber != -1) && ( (uint)nSelectedPatternNumber < pPatternList->size() ) ) {
1398 m_pPattern = pPatternList->get( nSelectedPatternNumber );
1399 }
1400 else {
1401 m_pPattern = nullptr;
1402 }
1403 m_nSelectedPatternNumber = nSelectedPatternNumber;
1404
1405 updateWidth();
1406 resize( m_nEditorWidth, height() );
1407
1409 update();
1410}
1411
1413{
1414 qreal pixelRatio = devicePixelRatio();
1415 if ( m_pBackgroundPixmap->width() != m_nEditorWidth ||
1416 m_pBackgroundPixmap->height() != m_nEditorHeight ||
1417 m_pBackgroundPixmap->devicePixelRatio() != pixelRatio ) {
1418 delete m_pBackgroundPixmap;
1419 m_pBackgroundPixmap = new QPixmap( m_nEditorWidth * pixelRatio ,
1420 m_nEditorHeight * pixelRatio );
1421 m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
1422 }
1423
1427 }
1428 else if ( m_mode == PatternEditor::Mode::Pan ||
1431 }
1432 else if ( m_mode == PatternEditor::Mode::NoteKey ) {
1434 }
1435
1436 m_bBackgroundInvalid = false;
1437}
1438
1439
1444
1449
1453
1454std::vector<NotePropertiesRuler::SelectionIndex> NotePropertiesRuler::elementsIntersecting( QRect r ) {
1455 std::vector<SelectionIndex> result;
1456 if ( m_pPattern == nullptr ) {
1457 return std::move( result );
1458 }
1459
1460 auto pHydrogen = Hydrogen::get_instance();
1461
1462 const Pattern::notes_t* notes = m_pPattern->get_notes();
1463 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
1464 if ( pSelectedInstrument == nullptr ) {
1465 ERRORLOG( "No instrument selected" );
1466 return std::move( result );
1467 }
1468
1469 // Account for the notional active area of the slider. We allow a
1470 // width of 8 as this is the size of the circle used for the zero
1471 // position on the lead/lag editor.
1472 r = r.normalized();
1473 if ( r.top() == r.bottom() && r.left() == r.right() ) {
1474 r += QMargins( 2, 2, 2, 2 );
1475 }
1476 r += QMargins( 4, 4, 4, 4 );
1477
1479 if ( it->second->get_instrument() != pSelectedInstrument
1480 && !m_selection.isSelected( it->second ) ) {
1481 continue;
1482 }
1483
1484 int pos = it->first;
1485 uint x_pos = PatternEditor::nMargin + pos * m_fGridWidth;
1486 if ( r.intersects( QRect( x_pos, 0, 1, height() ) ) ) {
1487 result.push_back( it->second );
1488 }
1489 }
1490
1491 // Updating selection, we may need to repaint the whole widget.
1493 update();
1494
1495 return std::move(result);
1496}
1497
1502{
1503 uint x = PatternEditor::nMargin +
1505 return QRect( x-m_fGridWidth*3, 3, m_fGridWidth*6, height()-6 );
1506}
1507
1509{
1510 selectInstrumentNotes( Hydrogen::get_instance()->getSelectedInstrumentNumber() );
1511}
1512
#define VELOCITY_MAX
Definition Note.h:44
#define LEAD_LAG_MIN
Definition Note.h:47
#define KEY_MAX
Definition Note.h:33
#define OCTAVE_MIN
Definition Note.h:34
#define OCTAVE_MAX
Definition Note.h:35
#define VELOCITY_MIN
Definition Note.h:43
#define LEAD_LAG_MAX
Definition Note.h:48
#define ERRORLOG(x)
Definition Object.h:239
#define DEBUGLOG(x)
Definition Object.h:236
#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_LENGTH(_notes, _it, _pattern)
Iterate over all accessible notes between position 0 and length of _pattern in an immutable way.
Definition Pattern.h:285
virtual void updateEditor(bool bPatternOnly=false) override
Hydrogen Audio Engine.
Definition Hydrogen.h:54
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
int getSelectedPatternNumber() const
Definition Hydrogen.h:660
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
std::shared_ptr< Instrument > getSelectedInstrument() const
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
int get_position() const
__position accessor
Definition Note.h:535
float getPanWithRangeFrom0To1() const
get pan of the note, scaling and translating the range from [-1;1] to [0;1]
Definition Note.h:197
void set_lead_lag(float value)
__lead_lag setter
Definition Note.cpp:148
void set_key_octave(const QString &str)
parse str and set __key and __octave
Definition Note.cpp:202
Octave get_octave()
__octave accessor
Definition Note.h:677
void set_probability(float value)
Definition Note.h:615
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
float get_lead_lag() const
__lead_lag accessor
Definition Note.h:550
float get_notekey_pitch() const
note key pitch accessor
Definition Note.h:695
float get_velocity() const
__velocity accessor
Definition Note.h:540
void setPanWithRangeFrom0To1(float fVal)
set pan of the note, assuming the input range in [0;1]
Definition Note.h:191
Key
possible keys
Definition Note.h:106
bool get_note_off() const
__note_off accessor
Definition Note.h:580
void setPan(float val)
set pan of the note.
Definition Note.cpp:153
void set_velocity(float value)
__velocity setter
Definition Note.cpp:143
Key get_key()
__key accessor
Definition Note.h:672
float get_probability() const
Definition Note.h:610
Octave
possible octaves
Definition Note.h:110
float getPan() const
get pan of the note.
Definition Note.h:545
PatternList is a collection of patterns.
Definition PatternList.h:43
Pattern * get(int idx)
get a pattern from the list
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.
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 addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
bool hideKeyboardCursor()
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
virtual void mouseDragUpdateEvent(QMouseEvent *ev) override
std::map< H2Core::Note *, H2Core::Note * > m_oldNotes
Map of notes currently in the pattern -> old notes with their properties.
virtual void mouseMoveEvent(QMouseEvent *ev) override
virtual void updateEditor(bool bPatternOnly=false) override
virtual void mousePressEvent(QMouseEvent *ev) override
Raw Qt mouse events are passed to the Selection.
virtual void selectionMoveEndEvent(QInputEvent *ev) override
virtual QRect getKeyboardCursorRect() override
The screen area occupied by the keyboard cursor.
void prepareUndoAction(int x)
Preserve current note properties at position x (or in selection, if any) for use in later UndoAction.
void adjustNotePropertyDelta(H2Core::Note *pNote, float fDelta, bool bMessage=false)
Adjust a note's property by applying a delta to the current value, and clipping to the appropriate ra...
virtual void selectionMoveUpdateEvent(QMouseEvent *ev) override
virtual void selectAll() override
void onPreferencesChanged(H2Core::Preferences::Changes changes)
virtual void songModeActivationEvent() override
void leaveEvent(QEvent *ev) override
void wheelEvent(QWheelEvent *ev) override
Scroll wheel gestures will adjust the property of notes under the mouse cursor (or selected notes,...
void createNoteKeyBackground(QPixmap *pixmap)
void keyPressEvent(QKeyEvent *ev) override
void createNormalizedBackground(QPixmap *pixmap)
virtual void mouseDragStartEvent(QMouseEvent *ev) override
void propertyDragUpdate(QMouseEvent *ev)
Update notes for a property adjust drag, in response to the mouse moving.
void createBackground() override
Updates m_pBackgroundPixmap to show the latest content.
virtual void selectedInstrumentChangedEvent() override
void drawDefaultBackground(QPainter &painter, int nHeight=0, int nIncrement=0)
virtual void mouseDragEndEvent(QMouseEvent *ev) override
virtual void mouseClickEvent(QMouseEvent *ev) override
void paintEvent(QPaintEvent *ev) override
void createCenteredBackground(QPixmap *pixmap)
virtual void selectionMoveCancelEvent() override
Move of selection is cancelled. Revert notes to preserved state.
virtual void selectedPatternChangedEvent() override
void drawFocus(QPainter &painter)
void propertyDragStart(QMouseEvent *ev)
void enterEvent(QEvent *ev) override
virtual std::vector< SelectionIndex > elementsIntersecting(QRect r) override
Find list of elements which intersect a rectangular area.
Pattern Editor Panel.
void setCursorPosition(int nCursorPosition)
const QScrollArea * getNotePanScrollArea() const
PianoRollEditor * getPianoRollEditor()
const QScrollArea * getNoteLeadLagScrollArea() const
const QScrollArea * getNoteNoteKeyScrollArea() const
PatternEditorRuler * getPatternEditorRuler()
const QScrollArea * getNoteVelocityScrollArea() const
const QScrollArea * getNoteProbabilityScrollArea() const
DrumPatternEditor * getDrumPatternEditor()
Pattern Editor.
static QString modeToQString(Mode mode)
virtual void selectInstrumentNotes(int nInstrument)
virtual void mouseMoveEvent(QMouseEvent *ev) override
static QColor computeNoteColor(float velocity)
Calculate colour to use for note representation based on note velocity.
virtual void mousePressEvent(QMouseEvent *ev) override
Raw Qt mouse events are passed to the Selection.
virtual void selectNone()
QColor selectedNoteColor() const
Colour to use for outlining selected notes.
static void triggerStatusMessage(H2Core::Note *pNote, Mode mode)
static constexpr int nMargin
PatternEditorPanel * m_pPatternEditorPanel
QMenu * m_pPopupMenu
QPixmap * m_pBackgroundPixmap
int getColumn(int x, bool bUseFineGrained=false) const
H2Core::Pattern * m_pPattern
void invalidateBackground()
void updateWidth()
Adjusts m_nActiveWidth and m_nEditorWidth to the current state of the editor.
Selection< SelectionIndex > m_selection
The Selection object.
bool m_bBackgroundInvalid
void drawGridLines(QPainter &p, Qt::PenStyle style=Qt::SolidLine) const
Draw lines for note grid.
virtual void updateEditor(bool bPatternOnly=false) override
void paintSelection(QPainter *painter)
Paint selection-related elements (ie lasso)
Definition Selection.h:487
bool keyPressEvent(QKeyEvent *ev)
Key press event filter.
Definition Selection.h:659
void updateKeyboardCursorPosition(QRect cursor)
Update the keyboard cursor.
Definition Selection.h:756
bool isSelected(Elem e) const
Is an element in the set of currently selected elements?
Definition Selection.h:335
bool isMoving() const
Is there an ongoing (and incomplete) selection movement gesture?
Definition Selection.h:314
QPoint movingOffset() const
During a selection "move" gesture, return the current movement position relative to the start positio...
Definition Selection.h:325
iterator end()
Definition Selection.h:371
iterator begin()
Definition Selection.h:370
static void setPlayheadPen(QPainter *p, bool bHovered=false)
Definition Skin.cpp:190
static int getPlayheadShaftOffset()
Definition Skin.h:79
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