hydrogen 1.2.3
PianoRollEditor.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 "PianoRollEditor.h"
24#include "PatternEditorPanel.h"
25#include "PatternEditorRuler.h"
27#include "UndoActions.h"
28#include <cassert>
29
30#include <core/Hydrogen.h>
33#include <core/Basics/Note.h>
34#include <core/Basics/Pattern.h>
37#include <core/Helpers/Xml.h>
38using namespace H2Core;
39
40#include "../HydrogenApp.h"
41#include "../Skin.h"
42
43
45 QScrollArea *pScrollView)
46 : PatternEditor( pParent, panel )
47 , m_pScrollView( pScrollView )
48{
50
51 m_nGridHeight = 10;
52 m_nOctaves = 7;
53
54 setAttribute(Qt::WA_OpaquePaintEvent);
55
57
58 m_pTemp = new QPixmap( m_nEditorWidth, m_nEditorHeight );
59
61
63
65
67
68 m_bNeedsUpdate = true;
70 m_bSelectNewNotes = false;
71}
72
73
74
76{
77 INFOLOG( "DESTROY" );
78 delete m_pTemp;
79}
80
81
82void PianoRollEditor::updateEditor( bool bPatternOnly )
83{
84 // Ensure that m_pPattern is up to date.
87
88 if ( !bPatternOnly ) {
90 }
91 if ( !m_bNeedsUpdate ) {
92 m_bNeedsUpdate = true;
93 update();
94 }
95}
96
98{
99 assert( m_bNeedsUpdate );
100 resize( m_nEditorWidth, height() );
101
102 // Ensure that m_pPattern is up to date.
104
107 } else {
108 drawPattern();
109 }
110
111 // ERRORLOG(QString("update editor %1").arg(m_nEditorWidth));
112 m_bNeedsUpdate = false;
114}
115
117{
118 // Update pattern only
119 updateEditor( true );
120}
121
126
130
131void PianoRollEditor::paintEvent(QPaintEvent *ev)
132{
133 if (!isVisible()) {
134 return;
135 }
136
137 auto pPref = Preferences::get_instance();
138
139 qreal pixelRatio = devicePixelRatio();
140 if ( pixelRatio != m_pBackgroundPixmap->devicePixelRatio() || m_bBackgroundInvalid ) {
142 }
143
144 QPainter painter( this );
145 if ( m_bNeedsUpdate ) {
147 }
148 painter.drawPixmap( ev->rect(), *m_pTemp,
149 QRectF( pixelRatio * ev->rect().x(),
150 pixelRatio * ev->rect().y(),
151 pixelRatio * ev->rect().width(),
152 pixelRatio * ev->rect().height() ) );
153
154 // Draw playhead
155 if ( m_nTick != -1 ) {
156
157 int nOffset = Skin::getPlayheadShaftOffset();
158 int nX = static_cast<int>(static_cast<float>(PatternEditor::nMargin) +
159 static_cast<float>(m_nTick) *
160 m_fGridWidth );
161 Skin::setPlayheadPen( &painter, false );
162 painter.drawLine( nX, 2, nX, height() - 2 );
163 }
164
165 drawFocus( painter );
166
167 m_selection.paintSelection( &painter );
168
169 // Draw cursor
170 if ( hasFocus() && !HydrogenApp::get_instance()->hideKeyboardCursor() ) {
171 QPoint pos = cursorPosition();
172
173 QPen pen( pPref->getColorTheme()->m_cursorColor );
174 pen.setWidth( 2 );
175 painter.setPen( pen );
176 painter.setBrush( Qt::NoBrush );
177 painter.setRenderHint( QPainter::Antialiasing );
178 painter.drawRoundedRect( QRect( pos.x() - m_fGridWidth*3, pos.y()-2,
179 m_fGridWidth*6, m_nGridHeight+3 ), 4, 4 );
180 }
181}
182
183void PianoRollEditor::drawFocus( QPainter& painter ) {
184
186
187 if ( ! m_bEntered && ! hasFocus() ) {
188 return;
189 }
190
191 QColor color = pPref->getColorTheme()->m_highlightColor;
192
193 // If the mouse is placed on the widget but the user hasn't
194 // clicked it yet, the highlight will be done more transparent to
195 // indicate that keyboard inputs are not accepted yet.
196 if ( ! hasFocus() ) {
197 color.setAlpha( 125 );
198 }
199
200 int nStartY = HydrogenApp::get_instance()->getPatternEditorPanel()->getPianoRollEditorScrollArea()->verticalScrollBar()->value();
201 int nStartX = HydrogenApp::get_instance()->getPatternEditorPanel()->getPianoRollEditorScrollArea()->horizontalScrollBar()->value();
202 int nEndY = nStartY + HydrogenApp::get_instance()->getPatternEditorPanel()->getPianoRollEditorScrollArea()->viewport()->size().height();
203 int nEndX = std::min( nStartX + HydrogenApp::get_instance()->getPatternEditorPanel()->getPianoRollEditorScrollArea()->viewport()->size().width(), width() );
204
205 QPen pen( color );
206 pen.setWidth( 4 );
207 painter.setPen( pen );
208 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nEndX, nStartY ) );
209 painter.drawLine( QPoint( nStartX, nStartY ), QPoint( nStartX, nEndY ) );
210 painter.drawLine( QPoint( nEndX, nStartY ), QPoint( nEndX, nEndY ) );
211 painter.drawLine( QPoint( nEndX, nEndY ), QPoint( nStartX, nEndY ) );
212}
213
215{
217
218 const QColor backgroundColor = pPref->getColorTheme()->m_patternEditor_backgroundColor;
219 const QColor backgroundInactiveColor = pPref->getColorTheme()->m_windowColor;
220 const QColor alternateRowColor = pPref->getColorTheme()->m_patternEditor_alternateRowColor;
221 const QColor octaveColor = pPref->getColorTheme()->m_patternEditor_octaveRowColor;
222 // The line corresponding to the default pitch set to new notes
223 // will be highlighted.
224 const QColor baseNoteColor = octaveColor.lighter( 119 );
225 const QColor lineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
226 const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
227
228 unsigned start_x = 0;
229 unsigned end_x = m_nActiveWidth;
230
231 // Resize pixmap if pixel ratio has changed
232 qreal pixelRatio = devicePixelRatio();
233 if ( m_pBackgroundPixmap->width() != m_nEditorWidth ||
235 m_pBackgroundPixmap->devicePixelRatio() != pixelRatio ) {
236 delete m_pBackgroundPixmap;
237 m_pBackgroundPixmap = new QPixmap( width() * pixelRatio , height() * pixelRatio );
238 m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
239 delete m_pTemp;
240 m_pTemp = new QPixmap( width() * pixelRatio , height() * pixelRatio );
241 m_pTemp->setDevicePixelRatio( pixelRatio );
242 }
243
244 m_pBackgroundPixmap->fill( backgroundInactiveColor );
245
246 QPainter p( m_pBackgroundPixmap );
247
248 for ( uint ooctave = 0; ooctave < m_nOctaves; ++ooctave ) {
249 unsigned start_y = ooctave * 12 * m_nGridHeight;
250
251 for ( int ii = 0; ii < 12; ++ii ) {
252 if ( ii == 0 || ii == 2 || ii == 4 || ii == 6 || ii == 7 ||
253 ii == 9 || ii == 11 ) {
254 if ( ooctave % 2 != 0 ) {
255 p.fillRect( start_x, start_y + ii * m_nGridHeight,
256 end_x - start_x, start_y + ( ii + 1 ) * m_nGridHeight,
257 octaveColor );
258 } else {
259 p.fillRect( start_x, start_y + ii * m_nGridHeight,
260 end_x - start_x, start_y + ( ii + 1 ) * m_nGridHeight,
261 backgroundColor );
262 }
263 } else {
264 p.fillRect( start_x, start_y + ii * m_nGridHeight,
265 end_x - start_x, start_y + ( ii + 1 ) * m_nGridHeight,
266 alternateRowColor );
267 }
268 }
269
270 // Highlight base note pitch
271 if ( ooctave == 3 ) {
272 p.fillRect( start_x, start_y + 11 * m_nGridHeight,
273 end_x - start_x, start_y + 12 * m_nGridHeight,
274 baseNoteColor );
275 }
276 }
277
278
279 // horiz lines
280 p.setPen( lineColor );
281 for ( uint row = 0; row < ( 12 * m_nOctaves ); ++row ) {
282 unsigned y = row * m_nGridHeight;
283 p.drawLine( start_x, y, end_x, y );
284 }
285
286 if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
287 p.setPen( lineInactiveColor );
288 for ( uint row = 0; row < ( 12 * m_nOctaves ); ++row ) {
289 unsigned y = row * m_nGridHeight;
290 p.drawLine( m_nActiveWidth, y, m_nEditorWidth, y );
291 }
292 }
293
294 //draw text
295 QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
296 p.setFont( font );
297 p.setPen( pPref->getColorTheme()->m_patternEditor_textColor );
298
299 int offset = 0;
300 int insertx = 3;
301 for ( int oct = 0; oct < (int)m_nOctaves; oct++ ){
302 if( oct > 3 ){
303 p.drawText( insertx, m_nGridHeight + offset, "B" );
304 p.drawText( insertx, 10 + m_nGridHeight + offset, "A#" );
305 p.drawText( insertx, 20 + m_nGridHeight + offset, "A" );
306 p.drawText( insertx, 30 + m_nGridHeight + offset, "G#" );
307 p.drawText( insertx, 40 + m_nGridHeight + offset, "G" );
308 p.drawText( insertx, 50 + m_nGridHeight + offset, "F#" );
309 p.drawText( insertx, 60 + m_nGridHeight + offset, "F" );
310 p.drawText( insertx, 70 + m_nGridHeight + offset, "E" );
311 p.drawText( insertx, 80 + m_nGridHeight + offset, "D#" );
312 p.drawText( insertx, 90 + m_nGridHeight + offset, "D" );
313 p.drawText( insertx, 100 + m_nGridHeight + offset, "C#" );
314 p.drawText( insertx, 110 + m_nGridHeight + offset, "C" );
315 offset += 12 * m_nGridHeight;
316 }else
317 {
318 p.drawText( insertx, m_nGridHeight + offset, "b" );
319 p.drawText( insertx, 10 + m_nGridHeight + offset, "a#" );
320 p.drawText( insertx, 20 + m_nGridHeight + offset, "a" );
321 p.drawText( insertx, 30 + m_nGridHeight + offset, "g#" );
322 p.drawText( insertx, 40 + m_nGridHeight + offset, "g" );
323 p.drawText( insertx, 50 + m_nGridHeight + offset, "f#" );
324 p.drawText( insertx, 60 + m_nGridHeight + offset, "f" );
325 p.drawText( insertx, 70 + m_nGridHeight + offset, "e" );
326 p.drawText( insertx, 80 + m_nGridHeight + offset, "d#" );
327 p.drawText( insertx, 90 + m_nGridHeight + offset, "d" );
328 p.drawText( insertx, 100 + m_nGridHeight + offset, "c#" );
329 p.drawText( insertx, 110 + m_nGridHeight + offset, "c" );
330 offset += 12 * m_nGridHeight;
331 }
332 }
333
334 drawGridLines( p, Qt::DashLine );
335 drawPattern();
336
337 p.setPen( QPen( lineColor, 2, Qt::SolidLine ) );
339
340 m_bBackgroundInvalid = false;
341}
342
343
345{
346 //INFOLOG( "draw pattern" );
347
349
350 qreal pixelRatio = devicePixelRatio();
351
352 QPainter p( m_pTemp );
353 // copy the background image
354 p.drawPixmap( rect(), *m_pBackgroundPixmap,
355 QRectF( pixelRatio * rect().x(),
356 pixelRatio * rect().y(),
357 pixelRatio * rect().width(),
358 pixelRatio * rect().height() ) );
359
360 // for each note...
361 for ( Pattern *pPattern : getPatternsToShow() ) {
362 bool bIsForeground = ( pPattern == m_pPattern );
363 const Pattern::notes_t* notes = pPattern->get_notes();
364 FOREACH_NOTE_CST_IT_BEGIN_LENGTH( notes, it, pPattern ) {
365 Note *note = it->second;
366 assert( note );
367 drawNote( note, &p, bIsForeground );
368 }
369 }
370
371}
372
373
374void PianoRollEditor::drawNote( Note *pNote, QPainter *pPainter, bool bIsForeground )
375{
376 Hydrogen *pHydrogen = Hydrogen::get_instance();
377 if ( pNote->get_instrument() == pHydrogen->getSelectedInstrument() ) {
378 QPoint pos ( PatternEditor::nMargin + pNote->get_position() * m_fGridWidth,
380 drawNoteSymbol( *pPainter, pos, pNote, bIsForeground );
381 }
382}
383
384
385void PianoRollEditor::addOrRemoveNote( int nColumn, int nRealColumn, int nLine,
386 int nNotekey, int nOctave,
387 bool bDoAdd, bool bDoDelete )
388{
389 if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
390 // No pattern selected.
391 return;
392 }
393
394 Note::Octave octave = (Note::Octave)nOctave;
395 Note::Key notekey = (Note::Key)nNotekey;
396 Hydrogen *pHydrogen = Hydrogen::get_instance();
397 int nSelectedInstrumentnumber = pHydrogen->getSelectedInstrumentNumber();
398 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
399 if ( pSelectedInstrument == nullptr ) {
400 DEBUGLOG( "No instrument selected" );
401 return;
402 }
403
404 Note* pOldNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument,
405 notekey, octave );
406
407 int nLength = -1;
408 float fVelocity = 0.8f;
409 float fPan = 0.0f;
410 float fLeadLag = 0.0f;
411 float fProbability = 1.0f;
412
413 if ( pOldNote && !bDoDelete ) {
414 // Found an old note, but we don't want to delete, so just return.
415 return;
416 } else if ( !pOldNote && !bDoAdd ) {
417 // No note there, but we don't want to add a new one, so return.
418 return;
419 }
420
421 if ( pOldNote ) {
422 nLength = pOldNote->get_length();
423 fVelocity = pOldNote->get_velocity();
424 fPan = pOldNote->getPan();
425 fLeadLag = pOldNote->get_lead_lag();
426 notekey = pOldNote->get_key();
427 octave = pOldNote->get_octave();
428 fProbability = pOldNote->get_probability();
429 }
430
431 if ( pOldNote == nullptr ) {
432 // hear note
434 if ( pref->getHearNewNotes() && pSelectedInstrument->hasSamples() ) {
435 Note *pNote2 = new Note( pSelectedInstrument );
436 pNote2->set_key_octave( notekey, octave );
437 m_pAudioEngine->getSampler()->noteOn( pNote2 );
438 }
439 }
440
442 nLine,
444 nSelectedInstrumentnumber,
445 nLength,
446 fVelocity,
447 fPan,
448 fLeadLag,
449 notekey,
450 octave,
451 fProbability,
452 pOldNote != nullptr );
453 HydrogenApp::get_instance()->m_pUndoStack->push( action );
454
455}
456
457
458void PianoRollEditor::mouseClickEvent( QMouseEvent *ev ) {
459
460 if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
461 return;
462 }
463
464 auto pHydrogenApp = HydrogenApp::get_instance();
465 std::shared_ptr<Song> pSong = Hydrogen::get_instance()->getSong();
466
467 int nPressedLine = ((int) ev->y()) / ((int) m_nGridHeight);
468 if ( nPressedLine >= (int) m_nOctaves * 12 ) {
469 return;
470 }
471
472 int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
473
474 if ( nColumn >= (int)m_pPattern->get_length() ) {
475 update( 0, 0, width(), height() );
476 return;
477 }
479
480 auto pSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrument();
481 int nSelectedInstrumentnumber = Hydrogen::get_instance()->getSelectedInstrumentNumber();
482
483 if ( pSelectedInstrument == nullptr ) {
484 ERRORLOG( "No instrument selected" );
485 return;
486 }
487
488 int nPitch = lineToPitch( nPressedLine );
489 Note::Octave pressedoctave = Note::pitchToOctave( nPitch );
490 Note::Key pressednotekey = Note::pitchToKey( nPitch );
491 m_nCursorPitch = nPitch;
492
493 if (ev->button() == Qt::LeftButton ) {
494
495 unsigned nRealColumn = 0;
496 if( ev->x() > PatternEditor::nMargin ) {
497 nRealColumn = (ev->x() - PatternEditor::nMargin) / static_cast<float>(m_fGridWidth);
498 }
499
500 if ( ev->modifiers() & Qt::ShiftModifier ) {
501 H2Core::Note *pNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, pressednotekey, pressedoctave );
502 if ( pNote != nullptr ) {
504 nPressedLine,
506 nSelectedInstrumentnumber,
507 pNote->get_length(),
508 pNote->get_velocity(),
509 pNote->getPan(),
510 pNote->get_lead_lag(),
511 pNote->get_key(),
512 pNote->get_octave(),
513 pNote->get_probability(),
514 pNote != nullptr );
515 pHydrogenApp->m_pUndoStack->push( action );
516 } else {
517 SE_addPianoRollNoteOffAction *action = new SE_addPianoRollNoteOffAction( nColumn, nPressedLine, m_nSelectedPatternNumber, nSelectedInstrumentnumber );
518 pHydrogenApp->m_pUndoStack->push( action );
519 }
520 return;
521 }
522
523 addOrRemoveNote( nColumn, nRealColumn, nPressedLine, pressednotekey, pressedoctave );
524
525 } else if ( ev->button() == Qt::RightButton ) {
526 // Show context menu
527 m_pPopupMenu->popup( ev->globalPos() );
528
529 }
530
531}
532
533void PianoRollEditor::mousePressEvent( QMouseEvent* ev ) {
534 if ( ev->x() > m_nActiveWidth ) {
535 return;
536 }
537
539
540 auto pHydrogenApp = HydrogenApp::get_instance();
541
542 // Hide cursor in case this behavior was selected in the
543 // Preferences.
544 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
545 pHydrogenApp->setHideKeyboardCursor( true );
546
547 // Cursor just got hidden.
548 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
549 // Immediate update to prevent visual delay.
551 update();
552 }
553
554 // Update cursor position
555 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
556 int nPressedLine = ((int) ev->y()) / ((int) m_nGridHeight);
557 if ( nPressedLine >= (int) m_nOctaves * 12 ) {
558 return;
559 }
560 m_nCursorPitch = lineToPitch( nPressedLine );
561
562 int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
563 if ( ( m_pPattern != nullptr &&
564 nColumn >= (int)m_pPattern->get_length() ) ||
565 nColumn >= MAX_INSTRUMENTS ) {
566 return;
567 }
568
570
571 update();
573 }
574}
575
577{
578 if ( m_pPattern == nullptr ) {
579 return;
580 }
581
582 // Handles cursor repositioning and hiding and stores general
583 // properties.
585
586 m_pDraggedNote = nullptr;
587 Hydrogen *pHydrogen = Hydrogen::get_instance();
588 int nColumn = getColumn( ev->x() );
589 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
590 if ( pSelectedInstrument == nullptr ) {
591 DEBUGLOG( "No instrument selected" );
592 return;
593 }
594
595 int nRow = std::floor(static_cast<float>(ev->y()) /
596 static_cast<float>(m_nGridHeight));
597
598 Note::Octave pressedOctave = Note::pitchToOctave( lineToPitch( nRow ) );
599 Note::Key pressedNoteKey = Note::pitchToKey( lineToPitch( nRow ) );
600 m_nCursorPitch = lineToPitch( nRow );
601
602 if (ev->button() == Qt::RightButton ) {
603
604 int nRealColumn = 0;
605 if( ev->x() > PatternEditor::nMargin ) {
606 nRealColumn =
607 static_cast<int>(std::floor(
608 static_cast<float>((ev->x() - PatternEditor::nMargin)) /
609 m_fGridWidth));
610 }
611
612 m_pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn,
613 pSelectedInstrument,
614 pressedNoteKey,
615 pressedOctave, false );
616
617 // Store note-specific properties.
619
620 m_nRow = nRow;
621 }
622}
623
625{
626 int nRow = std::floor(static_cast<float>(ev->y()) /
627 static_cast<float>(m_nGridHeight));
628 if ( nRow >= (int) m_nOctaves * 12 ) {
629 return;
630 }
631
633}
634
636 int pressedLine,
637 int selectedPatternNumber,
638 int selectedinstrument,
639 int oldLength,
640 float oldVelocity,
641 float fOldPan,
642 float oldLeadLag,
643 int oldNoteKeyVal,
644 int oldOctaveKeyVal,
645 float fProbability,
646 bool noteOff,
647 bool isDelete )
648{
649 if ( m_pPattern == nullptr ) {
650 return;
651 }
652
653 Hydrogen *pHydrogen = Hydrogen::get_instance();
654 std::shared_ptr<Song> pSong = pHydrogen->getSong();
655 PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
656
657 auto pSelectedInstrument = pSong->getInstrumentList()->get( selectedinstrument );
658 if ( pSelectedInstrument == nullptr ) {
659 ERRORLOG( QString( "Instrument [%1] could not be found" )
660 .arg( selectedinstrument ) );
661 return;
662 }
663
664 Pattern *pPattern = nullptr;
665 if ( ( selectedPatternNumber != -1 ) && ( (uint)selectedPatternNumber < pPatternList->size() ) ) {
666 pPattern = pPatternList->get( selectedPatternNumber );
667 }
668
669 Note::Octave pressedoctave = Note::pitchToOctave( lineToPitch( pressedLine ) );
670 Note::Key pressednotekey = Note::pitchToKey( lineToPitch( pressedLine ) );
671
672 m_pAudioEngine->lock( RIGHT_HERE ); // lock the audio engine
673
674 if ( isDelete ) {
675 Note* note = m_pPattern->find_note( nColumn, -1, pSelectedInstrument, pressednotekey, pressedoctave );
676 if ( note ) {
677 // the note exists...remove it!
678 m_pPattern->remove_note( note );
679 delete note;
680 } else {
681 ERRORLOG( "Could not find note to delete" );
682 }
683 } else {
684 // create the new note
685 unsigned nPosition = nColumn;
686 float fVelocity = oldVelocity;
687 float fPan = fOldPan;
688 int nLength = oldLength;
689
690 if ( noteOff ) {
691 fVelocity = 0.0f;
692 fPan = 0.f;
693 nLength = 1;
694 }
695
696 if ( pPattern != nullptr ) {
697 Note *pNote = new Note( pSelectedInstrument, nPosition, fVelocity, fPan, nLength );
698 pNote->set_note_off( noteOff );
699 if( ! noteOff ) {
700 pNote->set_lead_lag( oldLeadLag );
701 }
702 pNote->set_key_octave( pressednotekey, pressedoctave );
703 pNote->set_probability( fProbability );
704 pPattern->insert_note( pNote );
705 if ( m_bSelectNewNotes ) {
707 }
708 }
709 }
710 pHydrogen->setIsModified( true );
711 m_pAudioEngine->unlock(); // unlock the audio engine
712
714}
715
716
717// Find a note that matches pNote, and move it from (nColumn, nRow) to (nNewColumn, nNewRow)
719 Note::Octave octave,
720 Note::Key key,
721 int nPattern,
722 int nNewColumn,
723 Note::Octave newOctave,
724 Note::Key newKey,
725 Note *pNote)
726{
727 Hydrogen *pHydrogen = Hydrogen::get_instance();
728 std::shared_ptr<Song> pSong = pHydrogen->getSong();
729
731 PatternList *pPatternList = pSong->getPatternList();
732 Note *pFoundNote = nullptr;
733
734 if ( nPattern < 0 || nPattern > pPatternList->size() ) {
735 ERRORLOG( "Invalid pattern number" );
737 return;
738 }
739
740 Pattern *pPattern = pPatternList->get( nPattern );
741
742 FOREACH_NOTE_IT_BOUND_END((Pattern::notes_t *)pPattern->get_notes(), it, nColumn) {
743 Note *pCandidateNote = it->second;
744 if ( pCandidateNote->get_instrument() == pNote->get_instrument()
745 && pCandidateNote->get_octave() == octave
746 && pCandidateNote->get_key() == key
747 && pCandidateNote->get_velocity() == pNote->get_velocity()
748 && pCandidateNote->get_lead_lag() == pNote->get_lead_lag()
749 && pCandidateNote->getPan() == pNote->getPan()
750 && pCandidateNote->get_note_off() == pNote->get_note_off() ) {
751 pFoundNote = pCandidateNote;
752 if ( m_selection.isSelected( pFoundNote ) ) {
753 // If a candidate note is in the selection, this will be the one to move.
754 break;
755 }
756 }
757 }
758 if ( pFoundNote == nullptr ) {
759 ERRORLOG( "Couldn't find note to move" );
761 return;
762 }
763
764 // Remove and insert at new position
765 pPattern->remove_note( pFoundNote );
766 pFoundNote->set_position( nNewColumn );
767 pPattern->insert_note( pFoundNote );
768 pFoundNote->set_key_octave( newKey, newOctave );
769
770 pHydrogen->setIsModified( true );
772
774}
775
776
783
785{
786 selectInstrumentNotes( Hydrogen::get_instance()->getSelectedInstrumentNumber() );
787}
788
789
791{
792 if ( m_nSelectedPatternNumber == -1 ) {
793 // No pattern selected.
794 return;
795 }
796
797 if ( m_selection.begin() != m_selection.end() ) {
798 // Delete a selection.
799 Hydrogen *pHydrogen = Hydrogen::get_instance();
800 int nSelectedInstrumentNumber = pHydrogen->getSelectedInstrumentNumber();
801 auto pSelectedInstrument = pHydrogen->getSelectedInstrument();
802 if ( pSelectedInstrument == nullptr ) {
803 DEBUGLOG( "No instrument selected" );
804 return;
805 }
806 QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
808 std::list< QUndoCommand * > actions;
809 for ( Note *pNote : m_selection ) {
810 if ( m_selection.isSelected( pNote ) ) {
811 if ( pNote->get_instrument() == pSelectedInstrument ) {
812 int nLine = pitchToLine( pNote->get_notekey_pitch() );
813 actions.push_back( new SE_addOrDeleteNotePianoRollAction( pNote->get_position(),
814 nLine,
816 nSelectedInstrumentNumber,
817 pNote->get_length(),
818 pNote->get_velocity(),
819 pNote->getPan(),
820 pNote->get_lead_lag(),
821 pNote->get_key(),
822 pNote->get_octave(),
823 pNote->get_probability(),
824 true ) );
825 }
826 }
827 }
829
830 pUndo->beginMacro("delete notes");
831 for ( QUndoCommand * pAction : actions ) {
832 pUndo->push( pAction );
833 }
834 pUndo->endMacro();
835 }
836}
837
838
845{
846 if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
847 // No pattern selected.
848 return;
849 }
850
851 QClipboard *clipboard = QApplication::clipboard();
852 QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
853 auto pInstrList = Hydrogen::get_instance()->getSong()->getInstrumentList();
855 XMLNode noteList;
856 int nDeltaPos = 0, nDeltaPitch = 0;
857
858
859 XMLDoc doc;
860 if ( ! doc.setContent( clipboard->text() ) ) {
861 // Pasted something that's not valid XML.
862 return;
863 }
864
865 XMLNode selection = doc.firstChildElement( "noteSelection" );
866 if ( ! selection.isNull() ) {
867
868 // Got a noteSelection.
869 // <noteSelection>
870 // <noteList>
871 // <note> ...
872 noteList = selection.firstChildElement( "noteList" );
873 if ( noteList.isNull() ) {
874 return;
875 }
876
877 XMLNode positionNode = selection.firstChildElement( "sourcePosition" );
878
879 // If position information is supplied in the selection, use
880 // it to adjust the location relative to the current keyboard
881 // input cursor.
882 if ( !positionNode.isNull() ) {
883 int nCurrentPos = m_pPatternEditorPanel->getCursorPosition();
884
885 nDeltaPos = nCurrentPos - positionNode.read_int( "position", nCurrentPos );
886 nDeltaPitch = m_nCursorPitch - positionNode.read_int( "pitch", m_nCursorPitch );
887 }
888 } else {
889
890 XMLNode instrumentLine = doc.firstChildElement( "instrument_line" );
891 if ( ! instrumentLine.isNull() ) {
892 // Found 'instrument_line', structure is:
893 // <instrument_line>
894 // <patternList>
895 // <pattern>
896 // <noteList>
897 // <note> ...
898 XMLNode patternList = instrumentLine.firstChildElement( "patternList" );
899 if ( patternList.isNull() ) {
900 return;
901 }
902 XMLNode pattern = patternList.firstChildElement( "pattern" );
903 if ( pattern.isNull() ) {
904 return;
905 }
906 // Don't attempt to paste multiple patterns
907 if ( ! pattern.nextSiblingElement( "pattern" ).isNull() ) {
908 QMessageBox::information( this, "Hydrogen", tr( "Cannot paste multi-pattern selection" ) );
909 return;
910 }
911 noteList = pattern.firstChildElement( "noteList" );
912 if ( noteList.isNull() ) {
913 return;
914 }
915 }
916 }
917
919 m_bSelectNewNotes = true;
920
921 if ( noteList.hasChildNodes() ) {
922
923 pUndo->beginMacro( "paste notes" );
924 for ( XMLNode n = noteList.firstChildElement( "note" ); ! n.isNull(); n = n.nextSiblingElement() ) {
925 Note *pNote = Note::load_from( &n, pInstrList );
926 int nPos = pNote->get_position() + nDeltaPos;
927 int nPitch = pNote->get_notekey_pitch() + nDeltaPitch;
928
929 if ( nPos >= 0 && nPos < m_pPattern->get_length() && nPitch >= 12 * OCTAVE_MIN && nPitch < 12 * (OCTAVE_MAX+1) ) {
930 int nLine = pitchToLine( nPitch );
931 pUndo->push( new SE_addOrDeleteNotePianoRollAction( nPos,
932 nLine,
934 nInstrument,
935 pNote->get_length(),
936 pNote->get_velocity(),
937 pNote->getPan(),
938 pNote->get_lead_lag(),
939 0,
940 0,
941 pNote->get_probability(),
942 false ) );
943 }
944 delete pNote;
945 }
946 pUndo->endMacro();
947 }
948
949 m_bSelectNewNotes = false;
950}
951
952
953void PianoRollEditor::keyPressEvent( QKeyEvent * ev )
954{
955 if ( m_pPattern == nullptr ) {
956 return;
957 }
958
959 auto pHydrogenApp = HydrogenApp::get_instance();
960 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
961
962 const int nBlockSize = 5, nWordSize = 5;
963 bool bIsSelectionKey = m_selection.keyPressEvent( ev );
964 bool bUnhideCursor = true;
965 updateModifiers( ev );
966
967 if ( bIsSelectionKey ) {
968 // Selection key, nothing more to do (other than update editor)
969 } else if ( ev->matches( QKeySequence::MoveToNextChar ) || ev->matches( QKeySequence::SelectNextChar ) ) {
970 // ->
972
973 } else if ( ev->matches( QKeySequence::MoveToNextWord ) || ev->matches( QKeySequence::SelectNextWord ) ) {
974 // ->
976
977 } else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ev->matches( QKeySequence::SelectEndOfLine ) ) {
978 // -->|
980
981 } else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ev->matches( QKeySequence::SelectPreviousChar ) ) {
982 // <-
984
985 } else if ( ev->matches( QKeySequence::MoveToPreviousWord ) || ev->matches( QKeySequence::SelectPreviousWord ) ) {
986 // <-
988
989 } else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ev->matches( QKeySequence::SelectStartOfLine ) ) {
990 // |<--
992
993 } else if ( ev->matches( QKeySequence::MoveToNextLine ) || ev->matches( QKeySequence::SelectNextLine ) ) {
996 }
997
998 } else if ( ev->matches( QKeySequence::MoveToEndOfBlock ) || ev->matches( QKeySequence::SelectEndOfBlock ) ) {
1000 m_nCursorPitch - nBlockSize );
1001
1002 } else if ( ev->matches( QKeySequence::MoveToNextPage ) || ev->matches( QKeySequence::SelectNextPage ) ) {
1003 // Page down -- move down by a whole octave
1005 m_nCursorPitch -= 12;
1006 if ( m_nCursorPitch < nMinPitch ) {
1007 m_nCursorPitch = nMinPitch;
1008 }
1009
1010 } else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) || ev->matches( QKeySequence::SelectEndOfDocument ) ) {
1012
1013 } else if ( ev->matches( QKeySequence::MoveToPreviousLine ) || ev->matches( QKeySequence::SelectPreviousLine ) ) {
1015 m_nCursorPitch ++;
1016 }
1017
1018 } else if ( ev->matches( QKeySequence::MoveToStartOfBlock ) || ev->matches( QKeySequence::SelectStartOfBlock ) ) {
1020 m_nCursorPitch + nBlockSize );
1021
1022 } else if ( ev->matches( QKeySequence::MoveToPreviousPage ) || ev->matches( QKeySequence::SelectPreviousPage ) ) {
1024 m_nCursorPitch += 12;
1025 if ( m_nCursorPitch >= nMaxPitch ) {
1026 m_nCursorPitch = nMaxPitch;
1027 }
1028
1029 } else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) || ev->matches( QKeySequence::SelectStartOfDocument ) ) {
1031
1032 } else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
1033 // Key: Enter/Return : Place or remove note at current position
1034 int pressedline = pitchToLine( m_nCursorPitch );
1035 int nPitch = lineToPitch( pressedline );
1037 Note::pitchToKey( nPitch ), Note::pitchToOctave( nPitch ) );
1038
1039 } else if ( ev->matches( QKeySequence::SelectAll ) ) {
1040 // Key: Ctrl + A: Select all
1041 bUnhideCursor = false;
1042 selectAll();
1043
1044 } else if ( ev->matches( QKeySequence::Deselect ) ) {
1045 // Key: Shift + Ctrl + A: clear selection
1046 bUnhideCursor = false;
1047 selectNone();
1048
1049 } else if ( ev->key() == Qt::Key_Delete ) {
1050 // Key: Delete: delete selection or note at keyboard cursor
1051 bUnhideCursor = false;
1052 if ( m_selection.begin() != m_selection.end() ) {
1054 } else {
1055 // Delete a note under the keyboard cursor
1056 int pressedline = pitchToLine( m_nCursorPitch );
1057 int nPitch = lineToPitch( pressedline );
1059 Note::pitchToKey( nPitch ), Note::pitchToOctave( nPitch ),
1060 /*bDoAdd=*/false, /*bDoDelete=*/true );
1061 }
1062
1063 } else if ( ev->matches( QKeySequence::Copy ) ) {
1064 bUnhideCursor = false;
1065 copy();
1066
1067 } else if ( ev->matches( QKeySequence::Paste ) ) {
1068 bUnhideCursor = false;
1069 paste();
1070
1071 } else if ( ev->matches( QKeySequence::Cut ) ) {
1072 bUnhideCursor = false;
1073 cut();
1074
1075 } else {
1076 ev->ignore();
1077 pHydrogenApp->setHideKeyboardCursor( true );
1078
1079 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
1081 update();
1082 }
1083 return;
1084 }
1085
1086 // Update editor
1087 QPoint pos = cursorPosition();
1088 if ( bUnhideCursor ) {
1090 }
1091 m_pScrollView->ensureVisible( pos.x(), pos.y() );
1093
1094 if ( ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
1095 // Immediate update to prevent visual delay.
1097 }
1098
1099 updateEditor( true );
1100 ev->accept();
1101}
1102
1103// Selection manager interface
1105{
1106 updateModifiers( ev );
1107
1108 QPoint offset = movingGridOffset();
1109 if ( offset.x() == 0 && offset.y() == 0 ) {
1110 // Move with no effect.
1111 return;
1112 }
1113
1115
1116 Hydrogen *pHydrogen = Hydrogen::get_instance();
1117 int nSelectedPatternNumber = pHydrogen->getSelectedPatternNumber();
1118 int nSelectedInstrumentNumber = pHydrogen->getSelectedInstrumentNumber();
1119
1120 if ( m_pPattern == nullptr || nSelectedPatternNumber == -1 ) {
1121 // No pattern selected. Nothing to be selected.
1122 return;
1123 }
1124
1125 QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
1126
1127 if (m_bCopyNotMove) {
1128 pUndo->beginMacro( "copy notes" );
1129 } else {
1130 pUndo->beginMacro( "move notes" );
1131 }
1132 std::list< Note * > selectedNotes;
1133 for ( auto pNote : m_selection ) {
1134 selectedNotes.push_back( pNote );
1135 }
1136
1137 if ( m_bCopyNotMove ) {
1138 // Clear selection so the new notes can be selection instead
1139 // of the originals.
1141 }
1142 m_bSelectNewNotes = true;
1143
1144 for ( auto pNote : selectedNotes ) {
1145 int nPosition = pNote->get_position();
1146 int nNewPosition = nPosition + offset.x();
1147
1148 Note::Octave octave = pNote->get_octave();
1149 Note::Key key = pNote->get_key();
1150 // Transpose note
1151 int nNewPitch = pNote->get_notekey_pitch() - offset.y();
1152 int nLine = pitchToLine( nNewPitch );
1153 Note::Octave newOctave = Note::pitchToOctave( nNewPitch );
1154 Note::Key newKey = Note::pitchToKey( nNewPitch );
1155 bool bNoteInRange = ( newOctave >= OCTAVE_MIN && newOctave <= OCTAVE_MAX && nNewPosition >= 0
1156 && nNewPosition < m_pPattern->get_length() );
1157
1158 if ( m_bCopyNotMove ) {
1159 if ( bNoteInRange ) {
1160 pUndo->push( new SE_addOrDeleteNotePianoRollAction( nNewPosition,
1161 nLine,
1162 nSelectedPatternNumber,
1163 nSelectedInstrumentNumber,
1164 pNote->get_length(),
1165 pNote->get_velocity(),
1166 pNote->getPan(),
1167 pNote->get_lead_lag(),
1168 newKey,
1169 newOctave,
1170 pNote->get_probability(),
1171 false ) );
1172 }
1173 } else {
1174 if ( bNoteInRange ) {
1175 pUndo->push( new SE_moveNotePianoRollAction( nPosition, octave, key, nSelectedPatternNumber, nNewPosition, newOctave, newKey, pNote ) );
1176 } else {
1177 pUndo->push( new SE_addOrDeleteNotePianoRollAction( pNote->get_position(),
1178 nLine - offset.y(),
1179 nSelectedPatternNumber,
1180 nSelectedInstrumentNumber,
1181 pNote->get_length(),
1182 pNote->get_velocity(),
1183 pNote->getPan(),
1184 pNote->get_lead_lag(),
1185 key,
1186 octave,
1187 pNote->get_probability(),
1188 true ) );
1189 }
1190 }
1191 }
1192
1193 m_bSelectNewNotes = false;
1194 pUndo->endMacro();
1195}
1196
1197std::vector<PianoRollEditor::SelectionIndex> PianoRollEditor::elementsIntersecting( QRect r )
1198{
1199 std::vector<SelectionIndex> result;
1200 if ( m_pPattern == nullptr ) {
1201 return std::move( result );
1202 }
1203
1204 int w = 8;
1205 int h = m_nGridHeight - 2;
1206 auto pSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrument();
1207 if ( pSelectedInstrument == nullptr ) {
1208 DEBUGLOG( "No instrument selected" );
1209 return std::move( result );
1210 }
1211
1212 r = r.normalized();
1213 if ( r.top() == r.bottom() && r.left() == r.right() ) {
1214 r += QMargins( 2, 2, 2, 2 );
1215 }
1216
1217 // Calculate the first and last position values that this rect will intersect with
1218 int x_min = (r.left() - w - PatternEditor::nMargin) / m_fGridWidth;
1219 int x_max = (r.right() + w - PatternEditor::nMargin) / m_fGridWidth;
1220
1221 const Pattern::notes_t* pNotes = m_pPattern->get_notes();
1222
1223 for ( auto it = pNotes->lower_bound( x_min ); it != pNotes->end() && it->first <= x_max; ++it ) {
1224 Note *pNote = it->second;
1225 if ( pNote->get_instrument() == pSelectedInstrument ) {
1226 uint start_x = PatternEditor::nMargin + pNote->get_position() * m_fGridWidth;
1227 uint start_y = m_nGridHeight * pitchToLine( pNote->get_notekey_pitch() ) + 1;
1228
1229 if ( r.intersects( QRect( start_x -4 , start_y, w, h ) ) ) {
1230 result.push_back( pNote );
1231 }
1232 }
1233 }
1234 updateEditor( true );
1235 return std::move( result );
1236}
1237
1242{
1243 QPoint pos = cursorPosition();
1244 return QRect( pos.x() - m_fGridWidth*3, pos.y()-2,
1246}
1247
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
Definition AudioEngine.h:59
#define KEY_MAX
Definition Note.h:33
#define OCTAVE_MIN
Definition Note.h:34
#define OCTAVE_MAX
Definition Note.h:35
#define KEY_MIN
Definition Note.h:32
#define INFOLOG(x)
Definition Object.h:237
#define ERRORLOG(x)
Definition Object.h:239
#define DEBUGLOG(x)
Definition Object.h:236
#define FOREACH_NOTE_IT_BOUND_END(_notes, _it, _bound)
Iterate over all notes in column _bound in a mutable way.
Definition Pattern.h:280
#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
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
Hydrogen Audio Engine.
Definition Hydrogen.h:54
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
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
static int octaveKeyToPitch(Octave octave, Key key)
Definition Note.h:403
static Key pitchToKey(int nPitch)
Definition Note.h:400
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
static Octave pitchToOctave(int nPitch)
Definition Note.h:393
static Note * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a note from an XMLNode
Definition Note.cpp:502
Octave get_octave()
__octave accessor
Definition Note.h:677
void set_probability(float value)
Definition Note.h:615
void set_position(int value)
__position setter
Definition Note.h:530
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
float get_lead_lag() const
__lead_lag accessor
Definition Note.h:550
int get_length() const
__length accessor
Definition Note.h:560
float get_notekey_pitch() const
note key pitch accessor
Definition Note.h:695
float get_velocity() const
__velocity accessor
Definition Note.h:540
Key
possible keys
Definition Note.h:106
bool get_note_off() const
__note_off accessor
Definition Note.h:580
void set_note_off(bool value)
__note_off setter
Definition Note.h:575
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
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
void remove_note(Note *note)
removes a given note from __notes, it's not deleted
Definition Pattern.cpp:244
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
Note * find_note(int idx_a, int idx_b, std::shared_ptr< Instrument > instrument, bool strict=true) const
search for a note at a given index within __notes which correspond to the given arguments
Definition Pattern.cpp:217
void insert_note(Note *note)
insert a new note within __notes
Definition Pattern.h:370
Manager for User Preferences File (singleton)
Definition Preferences.h:78
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 noteOn(Note *pNote)
Start playing a note.
Definition Sampler.cpp:190
XMLDoc is a subclass of QDomDocument with read and write methods.
Definition Xml.h:182
XMLNode is a subclass of QDomNode with read and write values methods.
Definition Xml.h:39
int read_int(const QString &node, int default_value, bool inexistent_ok=true, bool empty_ok=true, bool bSilent=false)
reads an integer stored into a child node
Definition Xml.cpp:170
void setHideKeyboardCursor(bool bHidden)
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
Pattern Editor Panel.
void setCursorPosition(int nCursorPosition)
void updateEditors(bool bPatternOnly=false)
PatternEditorRuler * getPatternEditorRuler()
const QScrollArea * getPianoRollEditorScrollArea() const
Pattern Editor.
virtual void selectInstrumentNotes(int nInstrument)
virtual void mouseDragUpdateEvent(QMouseEvent *ev) override
int lineToPitch(int nLine)
virtual void validateSelection() override
Ensure that the Selection contains only valid elements.
int m_nSelectedPatternNumber
virtual void mousePressEvent(QMouseEvent *ev) override
Raw Qt mouse events are passed to the Selection.
virtual void updateModifiers(QInputEvent *ev)
Update the status of modifier keys in response to input events.
virtual void selectNone()
int pitchToLine(int nPitch)
void storeNoteProperties(const H2Core::Note *pNote)
Stores the properties of pNote in member variables.
void drawNoteSymbol(QPainter &p, QPoint pos, H2Core::Note *pNote, bool bIsForeground=true) const
Draw a note.
static constexpr int nMargin
PatternEditorPanel * m_pPatternEditorPanel
H2Core::AudioEngine * m_pAudioEngine
H2Core::Note * m_pDraggedNote
QMenu * m_pPopupMenu
void updatePatternInfo()
Update current pattern information.
virtual void mouseDragStartEvent(QMouseEvent *ev) override
unsigned m_nGridHeight
virtual void copy()
Copy selection to clipboard in XML.
std::vector< H2Core::Pattern * > getPatternsToShow(void)
Get notes to show in pattern editor.
QPixmap * m_pBackgroundPixmap
virtual void cut()
unsigned m_nOctaves
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.
QPoint movingGridOffset() const
bool m_bEntered
Indicates whether the mouse pointer entered the widget.
bool m_bBackgroundInvalid
void drawGridLines(QPainter &p, Qt::PenStyle style=Qt::SolidLine) const
Draw lines for note grid.
virtual std::vector< SelectionIndex > elementsIntersecting(QRect r) override
Selections are indexed by Note pointers.
virtual void mouseDragUpdateEvent(QMouseEvent *ev) override
void moveNoteAction(int nColumn, H2Core::Note::Octave octave, H2Core::Note::Key key, int nPattern, int nNewColumn, H2Core::Note::Octave newOctave, H2Core::Note::Key newKey, H2Core::Note *pNote)
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
Position of keyboard input cursor on screen.
void addOrDeleteNoteAction(int nColumn, int pressedLine, int selectedPatternNumber, int selectedinstrument, int oldLength, float oldVelocity, float fOldPan, float oldLeadLag, int oldNoteKeyVal, int oldOctaveKeyVal, float fProbability, bool noteOff, bool isDelete)
void addOrRemoveNote(int nColumn, int nRealColumn, int nLine, int nNotekey, int nOctave, bool bDoAdd=true, bool bDoDelete=true)
virtual void selectAll() override
void onPreferencesChanged(H2Core::Preferences::Changes changes)
virtual void songModeActivationEvent() override
virtual void keyPressEvent(QKeyEvent *ev) override
PianoRollEditor(QWidget *pParent, PatternEditorPanel *panel, QScrollArea *pScrollView)
QScrollArea * m_pScrollView
virtual void paste() override
Paste selection.
virtual void mouseDragStartEvent(QMouseEvent *ev) override
void createBackground() override
Updates m_pBackgroundPixmap to show the latest content.
virtual void selectedInstrumentChangedEvent() override
void drawNote(H2Core::Note *pNote, QPainter *pPainter, bool bIsForeground)
Draw a note.
virtual void mouseClickEvent(QMouseEvent *ev) override
virtual void paintEvent(QPaintEvent *ev) override
virtual void deleteSelection() override
virtual void selectedPatternChangedEvent() override
void drawFocus(QPainter &painter)
void paintSelection(QPainter *painter)
Paint selection-related elements (ie lasso)
Definition Selection.h:487
void addToSelection(Elem e)
Definition Selection.h:346
void clearSelection(bool bCheck=true)
Definition Selection.h:350
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
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