45 QScrollArea *pScrollView)
47 , m_pScrollView( pScrollView )
54 setAttribute(Qt::WA_OpaquePaintEvent);
88 if ( !bPatternOnly ) {
139 qreal pixelRatio = devicePixelRatio();
144 QPainter painter(
this );
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() ) );
162 painter.drawLine( nX, 2, nX, height() - 2 );
173 QPen pen( pPref->getColorTheme()->m_cursorColor );
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,
191 QColor color = pPref->getColorTheme()->m_highlightColor;
196 if ( ! hasFocus() ) {
197 color.setAlpha( 125 );
203 int nEndX = std::min( nStartX +
HydrogenApp::get_instance()->getPatternEditorPanel()->getPianoRollEditorScrollArea()->viewport()->size().width(), width() );
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 ) );
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;
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 ) );
228 unsigned start_x = 0;
232 qreal pixelRatio = devicePixelRatio();
240 m_pTemp =
new QPixmap( width() * pixelRatio , height() * pixelRatio );
241 m_pTemp->setDevicePixelRatio( pixelRatio );
248 for ( uint ooctave = 0; ooctave <
m_nOctaves; ++ooctave ) {
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 ) {
271 if ( ooctave == 3 ) {
280 p.setPen( lineColor );
281 for ( uint row = 0; row < ( 12 *
m_nOctaves ); ++row ) {
283 p.drawLine( start_x, y, end_x, y );
287 p.setPen( lineInactiveColor );
288 for ( uint row = 0; row < ( 12 *
m_nOctaves ); ++row ) {
295 QFont font( pPref->getApplicationFontFamily(),
getPointSize( pPref->getFontSize() ) );
297 p.setPen( pPref->getColorTheme()->m_patternEditor_textColor );
301 for (
int oct = 0; oct < (int)
m_nOctaves; oct++ ){
337 p.setPen( QPen( lineColor, 2, Qt::SolidLine ) );
350 qreal pixelRatio = devicePixelRatio();
355 QRectF( pixelRatio * rect().x(),
356 pixelRatio * rect().y(),
357 pixelRatio * rect().width(),
358 pixelRatio * rect().height() ) );
362 bool bIsForeground = ( pPattern ==
m_pPattern );
365 Note *note = it->second;
367 drawNote( note, &p, bIsForeground );
386 int nNotekey,
int nOctave,
387 bool bDoAdd,
bool bDoDelete )
399 if ( pSelectedInstrument ==
nullptr ) {
400 DEBUGLOG(
"No instrument selected" );
408 float fVelocity = 0.8f;
410 float fLeadLag = 0.0f;
411 float fProbability = 1.0f;
413 if ( pOldNote && !bDoDelete ) {
416 }
else if ( !pOldNote && !bDoAdd ) {
424 fPan = pOldNote->
getPan();
431 if ( pOldNote ==
nullptr ) {
435 Note *pNote2 =
new Note( pSelectedInstrument );
444 nSelectedInstrumentnumber,
452 pOldNote !=
nullptr );
468 if ( nPressedLine >= (
int)
m_nOctaves * 12 ) {
472 int nColumn =
getColumn( ev->x(),
true );
475 update( 0, 0, width(), height() );
483 if ( pSelectedInstrument ==
nullptr ) {
484 ERRORLOG(
"No instrument selected" );
493 if (ev->button() == Qt::LeftButton ) {
495 unsigned nRealColumn = 0;
500 if ( ev->modifiers() & Qt::ShiftModifier ) {
502 if ( pNote !=
nullptr ) {
506 nSelectedInstrumentnumber,
515 pHydrogenApp->m_pUndoStack->push( action );
518 pHydrogenApp->m_pUndoStack->push( action );
523 addOrRemoveNote( nColumn, nRealColumn, nPressedLine, pressednotekey, pressedoctave );
525 }
else if ( ev->button() == Qt::RightButton ) {
544 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
545 pHydrogenApp->setHideKeyboardCursor(
true );
548 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
555 if ( ! pHydrogenApp->hideKeyboardCursor() ) {
557 if ( nPressedLine >= (
int)
m_nOctaves * 12 ) {
562 int nColumn =
getColumn( ev->x(),
true );
590 if ( pSelectedInstrument ==
nullptr ) {
591 DEBUGLOG(
"No instrument selected" );
595 int nRow = std::floor(
static_cast<float>(ev->y()) /
602 if (ev->button() == Qt::RightButton ) {
607 static_cast<int>(std::floor(
615 pressedOctave,
false );
626 int nRow = std::floor(
static_cast<float>(ev->y()) /
637 int selectedPatternNumber,
638 int selectedinstrument,
654 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
657 auto pSelectedInstrument = pSong->getInstrumentList()->get( selectedinstrument );
658 if ( pSelectedInstrument ==
nullptr ) {
659 ERRORLOG( QString(
"Instrument [%1] could not be found" )
660 .arg( selectedinstrument ) );
665 if ( ( selectedPatternNumber != -1 ) && ( (uint)selectedPatternNumber < pPatternList->size() ) ) {
666 pPattern = pPatternList->
get( selectedPatternNumber );
681 ERRORLOG(
"Could not find note to delete" );
685 unsigned nPosition = nColumn;
686 float fVelocity = oldVelocity;
687 float fPan = fOldPan;
688 int nLength = oldLength;
696 if ( pPattern !=
nullptr ) {
697 Note *pNote =
new Note( pSelectedInstrument, nPosition, fVelocity, fPan, nLength );
728 std::shared_ptr<Song> pSong = pHydrogen->
getSong();
731 PatternList *pPatternList = pSong->getPatternList();
732 Note *pFoundNote =
nullptr;
734 if ( nPattern < 0 || nPattern > pPatternList->
size() ) {
735 ERRORLOG(
"Invalid pattern number" );
740 Pattern *pPattern = pPatternList->
get( nPattern );
743 Note *pCandidateNote = it->second;
746 && pCandidateNote->
get_key() == key
751 pFoundNote = pCandidateNote;
758 if ( pFoundNote ==
nullptr ) {
759 ERRORLOG(
"Couldn't find note to move" );
802 if ( pSelectedInstrument ==
nullptr ) {
803 DEBUGLOG(
"No instrument selected" );
808 std::list< QUndoCommand * > actions;
811 if ( pNote->get_instrument() == pSelectedInstrument ) {
812 int nLine =
pitchToLine( pNote->get_notekey_pitch() );
816 nSelectedInstrumentNumber,
818 pNote->get_velocity(),
820 pNote->get_lead_lag(),
823 pNote->get_probability(),
830 pUndo->beginMacro(
"delete notes");
831 for ( QUndoCommand * pAction : actions ) {
832 pUndo->push( pAction );
851 QClipboard *clipboard = QApplication::clipboard();
856 int nDeltaPos = 0, nDeltaPitch = 0;
860 if ( ! doc.setContent( clipboard->text() ) ) {
865 XMLNode selection = doc.firstChildElement(
"noteSelection" );
866 if ( ! selection.isNull() ) {
872 noteList = selection.firstChildElement(
"noteList" );
873 if ( noteList.isNull() ) {
877 XMLNode positionNode = selection.firstChildElement(
"sourcePosition" );
882 if ( !positionNode.isNull() ) {
885 nDeltaPos = nCurrentPos - positionNode.
read_int(
"position", nCurrentPos );
890 XMLNode instrumentLine = doc.firstChildElement(
"instrument_line" );
891 if ( ! instrumentLine.isNull() ) {
898 XMLNode patternList = instrumentLine.firstChildElement(
"patternList" );
899 if ( patternList.isNull() ) {
902 XMLNode pattern = patternList.firstChildElement(
"pattern" );
903 if ( pattern.isNull() ) {
907 if ( ! pattern.nextSiblingElement(
"pattern" ).isNull() ) {
908 QMessageBox::information(
this,
"Hydrogen", tr(
"Cannot paste multi-pattern selection" ) );
911 noteList = pattern.firstChildElement(
"noteList" );
912 if ( noteList.isNull() ) {
921 if ( noteList.hasChildNodes() ) {
923 pUndo->beginMacro(
"paste notes" );
924 for (
XMLNode n = noteList.firstChildElement(
"note" ); ! n.isNull(); n = n.nextSiblingElement() ) {
929 if ( nPos >= 0 && nPos < m_pPattern->get_length() && nPitch >= 12 *
OCTAVE_MIN && nPitch < 12 * (
OCTAVE_MAX+1) ) {
960 bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
962 const int nBlockSize = 5, nWordSize = 5;
964 bool bUnhideCursor =
true;
967 if ( bIsSelectionKey ) {
969 }
else if ( ev->matches( QKeySequence::MoveToNextChar ) || ev->matches( QKeySequence::SelectNextChar ) ) {
973 }
else if ( ev->matches( QKeySequence::MoveToNextWord ) || ev->matches( QKeySequence::SelectNextWord ) ) {
977 }
else if ( ev->matches( QKeySequence::MoveToEndOfLine ) || ev->matches( QKeySequence::SelectEndOfLine ) ) {
981 }
else if ( ev->matches( QKeySequence::MoveToPreviousChar ) || ev->matches( QKeySequence::SelectPreviousChar ) ) {
985 }
else if ( ev->matches( QKeySequence::MoveToPreviousWord ) || ev->matches( QKeySequence::SelectPreviousWord ) ) {
989 }
else if ( ev->matches( QKeySequence::MoveToStartOfLine ) || ev->matches( QKeySequence::SelectStartOfLine ) ) {
993 }
else if ( ev->matches( QKeySequence::MoveToNextLine ) || ev->matches( QKeySequence::SelectNextLine ) ) {
998 }
else if ( ev->matches( QKeySequence::MoveToEndOfBlock ) || ev->matches( QKeySequence::SelectEndOfBlock ) ) {
1002 }
else if ( ev->matches( QKeySequence::MoveToNextPage ) || ev->matches( QKeySequence::SelectNextPage ) ) {
1010 }
else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) || ev->matches( QKeySequence::SelectEndOfDocument ) ) {
1013 }
else if ( ev->matches( QKeySequence::MoveToPreviousLine ) || ev->matches( QKeySequence::SelectPreviousLine ) ) {
1018 }
else if ( ev->matches( QKeySequence::MoveToStartOfBlock ) || ev->matches( QKeySequence::SelectStartOfBlock ) ) {
1022 }
else if ( ev->matches( QKeySequence::MoveToPreviousPage ) || ev->matches( QKeySequence::SelectPreviousPage ) ) {
1029 }
else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) || ev->matches( QKeySequence::SelectStartOfDocument ) ) {
1032 }
else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
1039 }
else if ( ev->matches( QKeySequence::SelectAll ) ) {
1041 bUnhideCursor =
false;
1044 }
else if ( ev->matches( QKeySequence::Deselect ) ) {
1046 bUnhideCursor =
false;
1049 }
else if ( ev->key() == Qt::Key_Delete ) {
1051 bUnhideCursor =
false;
1063 }
else if ( ev->matches( QKeySequence::Copy ) ) {
1064 bUnhideCursor =
false;
1067 }
else if ( ev->matches( QKeySequence::Paste ) ) {
1068 bUnhideCursor =
false;
1071 }
else if ( ev->matches( QKeySequence::Cut ) ) {
1072 bUnhideCursor =
false;
1077 pHydrogenApp->setHideKeyboardCursor(
true );
1079 if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
1088 if ( bUnhideCursor ) {
1109 if ( offset.x() == 0 && offset.y() == 0 ) {
1120 if (
m_pPattern ==
nullptr || nSelectedPatternNumber == -1 ) {
1128 pUndo->beginMacro(
"copy notes" );
1130 pUndo->beginMacro(
"move notes" );
1132 std::list< Note * > selectedNotes;
1134 selectedNotes.push_back( pNote );
1144 for (
auto pNote : selectedNotes ) {
1145 int nPosition = pNote->get_position();
1146 int nNewPosition = nPosition + offset.x();
1151 int nNewPitch = pNote->get_notekey_pitch() - offset.y();
1155 bool bNoteInRange = ( newOctave >=
OCTAVE_MIN && newOctave <= OCTAVE_MAX && nNewPosition >= 0
1159 if ( bNoteInRange ) {
1162 nSelectedPatternNumber,
1163 nSelectedInstrumentNumber,
1164 pNote->get_length(),
1165 pNote->get_velocity(),
1167 pNote->get_lead_lag(),
1170 pNote->get_probability(),
1174 if ( bNoteInRange ) {
1175 pUndo->push(
new SE_moveNotePianoRollAction( nPosition, octave, key, nSelectedPatternNumber, nNewPosition, newOctave, newKey, pNote ) );
1179 nSelectedPatternNumber,
1180 nSelectedInstrumentNumber,
1181 pNote->get_length(),
1182 pNote->get_velocity(),
1184 pNote->get_lead_lag(),
1187 pNote->get_probability(),
1199 std::vector<SelectionIndex> result;
1201 return std::move( result );
1207 if ( pSelectedInstrument ==
nullptr ) {
1208 DEBUGLOG(
"No instrument selected" );
1209 return std::move( result );
1213 if ( r.top() == r.bottom() && r.left() == r.right() ) {
1214 r += QMargins( 2, 2, 2, 2 );
1223 for (
auto it = pNotes->lower_bound( x_min ); it != pNotes->end() && it->first <= x_max; ++it ) {
1224 Note *pNote = it->second;
1229 if ( r.intersects( QRect( start_x -4 , start_y, w, h ) ) ) {
1230 result.push_back( pNote );
1235 return std::move( result );
#define RIGHT_HERE
Macro intended to be used for the logging of the locking of the H2Core::AudioEngine.
#define FOREACH_NOTE_IT_BOUND_END(_notes, _it, _bound)
Iterate over all notes in column _bound in a mutable way.
#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.
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
std::shared_ptr< Song > getSong() const
Get the current song.
int getSelectedInstrumentNumber() const
int getSelectedPatternNumber() const
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
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.
int get_position() const
__position accessor
static int octaveKeyToPitch(Octave octave, Key key)
static Key pitchToKey(int nPitch)
void set_lead_lag(float value)
__lead_lag setter
void set_key_octave(const QString &str)
parse str and set __key and __octave
static Octave pitchToOctave(int nPitch)
static Note * load_from(XMLNode *node, std::shared_ptr< InstrumentList > instruments, bool bSilent=false)
load a note from an XMLNode
Octave get_octave()
__octave accessor
void set_probability(float value)
void set_position(int value)
__position setter
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
float get_lead_lag() const
__lead_lag accessor
int get_length() const
__length accessor
float get_notekey_pitch() const
note key pitch accessor
float get_velocity() const
__velocity accessor
bool get_note_off() const
__note_off accessor
void set_note_off(bool value)
__note_off setter
Key get_key()
__key accessor
float get_probability() const
float getPan() const
get pan of the note.
PatternList is a collection of patterns.
int size() const
returns the numbers of patterns
Pattern * get(int idx)
get a pattern from the list
Pattern class is a Note container.
void remove_note(Note *note)
removes a given note from __notes, it's not deleted
int get_length() const
set the denominator of the pattern
const notes_t * get_notes() const
get the virtual pattern set
std::multimap< int, Note * > notes_t
< multimap note type
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
void insert_note(Note *note)
insert a new note within __notes
Manager for User Preferences File (singleton)
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.
XMLDoc is a subclass of QDomDocument with read and write methods.
XMLNode is a subclass of QDomNode with read and write values methods.
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
void setHideKeyboardCursor(bool bHidden)
void addEventListener(EventListener *pListener)
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
QUndoStack * m_pUndoStack
PatternEditorPanel * getPatternEditorPanel()
void setCursorPosition(int nCursorPosition)
void updateEditors(bool bPatternOnly=false)
int moveCursorLeft(int n=1)
PatternEditorRuler * getPatternEditorRuler()
const QScrollArea * getPianoRollEditorScrollArea() const
int moveCursorRight(int n=1)
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
void updatePatternInfo()
Update current pattern information.
virtual void mouseDragStartEvent(QMouseEvent *ev) override
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
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
void finishUpdateEditor()
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.
bool m_bNeedsBackgroundUpdate
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)
void addToSelection(Elem e)
void clearSelection(bool bCheck=true)
bool keyPressEvent(QKeyEvent *ev)
Key press event filter.
void updateKeyboardCursorPosition(QRect cursor)
Update the keyboard cursor.
bool isSelected(Elem e) const
Is an element in the set of currently selected elements?
static void setPlayheadPen(QPainter *p, bool bHovered=false)
static int getPlayheadShaftOffset()
#define MAX_INSTRUMENTS
Maximum number of instruments allowed in Hydrogen.