diff --git a/data/themes/classic/clear_ghost_note.png b/data/themes/classic/clear_ghost_note.png new file mode 100644 index 00000000000..c9f85a2b4ab Binary files /dev/null and b/data/themes/classic/clear_ghost_note.png differ diff --git a/data/themes/classic/ghost_note.png b/data/themes/classic/ghost_note.png new file mode 100644 index 00000000000..532245e11f5 Binary files /dev/null and b/data/themes/classic/ghost_note.png differ diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index be17ee58fdb..12d90981d56 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -127,6 +127,10 @@ PianoRoll { qproperty-noteOpacity: 128; qproperty-noteBorders: true; /* boolean property, set false to have borderless notes */ qproperty-selectedNoteColor: rgb( 0, 125, 255 ); + qproperty-ghostNoteColor: #000000; + qproperty-ghostNoteTextColor: #ffffff; + qproperty-ghostNoteOpacity: 50; + qproperty-ghostNoteBorders: true; qproperty-barColor: #4afd85; qproperty-markedSemitoneColor: rgba( 0, 255, 200, 60 ); /* Grid colors */ diff --git a/data/themes/default/clear_ghost_note.png b/data/themes/default/clear_ghost_note.png new file mode 100644 index 00000000000..c9f85a2b4ab Binary files /dev/null and b/data/themes/default/clear_ghost_note.png differ diff --git a/data/themes/default/ghost_note.png b/data/themes/default/ghost_note.png new file mode 100644 index 00000000000..07344265978 Binary files /dev/null and b/data/themes/default/ghost_note.png differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 625d8657aa1..5d889295cae 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -146,6 +146,10 @@ PianoRoll { qproperty-noteOpacity: 165; qproperty-noteBorders: false; /* boolean property, set false to have borderless notes */ qproperty-selectedNoteColor: #064d79; + qproperty-ghostNoteColor: #000000; + qproperty-ghostNoteTextColor: #ffffff; + qproperty-ghostNoteOpacity: 50; + qproperty-ghostNoteBorders: false; qproperty-barColor: #078f3a; qproperty-markedSemitoneColor: rgba(255, 255, 255, 30); /* Grid colors */ diff --git a/include/Pattern.h b/include/Pattern.h index eddbed313f1..3a1cc941c3a 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -187,6 +187,7 @@ public slots: protected slots: void openInPianoRoll(); + void setGhostInPianoRoll(); void resetName(); void changeName(); diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 8b0f1babfba..168036245ad 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -59,7 +59,9 @@ class PianoRoll : public QWidget Q_PROPERTY( QColor lineColor READ lineColor WRITE setLineColor ) Q_PROPERTY( QColor noteModeColor READ noteModeColor WRITE setNoteModeColor ) Q_PROPERTY( QColor noteColor READ noteColor WRITE setNoteColor ) + Q_PROPERTY( QColor ghostNoteColor READ ghostNoteColor WRITE setGhostNoteColor ) Q_PROPERTY( QColor noteTextColor READ noteTextColor WRITE setNoteTextColor ) + Q_PROPERTY( QColor ghostNoteTextColor READ ghostNoteTextColor WRITE setGhostNoteTextColor ) Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) Q_PROPERTY( QColor selectedNoteColor READ selectedNoteColor WRITE setSelectedNoteColor ) Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor ) @@ -68,6 +70,8 @@ class PianoRoll : public QWidget Q_PROPERTY( QColor markedSemitoneColor READ markedSemitoneColor WRITE setMarkedSemitoneColor ) Q_PROPERTY( int noteOpacity READ noteOpacity WRITE setNoteOpacity ) Q_PROPERTY( bool noteBorders READ noteBorders WRITE setNoteBorders ) + Q_PROPERTY( int ghostNoteOpacity READ ghostNoteOpacity WRITE setGhostNoteOpacity ) + Q_PROPERTY( bool ghostNoteBorders READ ghostNoteBorders WRITE setGhostNoteBorders ) Q_PROPERTY( QColor backgroundShade READ backgroundShade WRITE setBackgroundShade ) public: enum EditModes @@ -87,6 +91,7 @@ class PianoRoll : public QWidget void showPanTextFloat(panning_t pan, const QPoint &pos, int timeout=-1); void setCurrentPattern( Pattern* newPattern ); + void setGhostPattern( Pattern* newPattern ); inline void stopRecording() { @@ -141,6 +146,14 @@ class PianoRoll : public QWidget void setNoteOpacity( const int i ); bool noteBorders() const; void setNoteBorders( const bool b ); + QColor ghostNoteColor() const; + void setGhostNoteColor( const QColor & c ); + QColor ghostNoteTextColor() const; + void setGhostNoteTextColor( const QColor & c ); + int ghostNoteOpacity() const; + void setGhostNoteOpacity( const int i ); + bool ghostNoteBorders() const; + void setGhostNoteBorders( const bool b ); QColor backgroundShade() const; void setBackgroundShade( const QColor & c ); @@ -206,9 +219,12 @@ protected slots: void selectRegionFromPixels( int xStart, int xEnd ); + void clearGhostPattern(); + signals: void currentPatternChanged(); + void ghostPatternSet(bool); void semiToneMarkerMenuScaleSetEnabled(bool); void semiToneMarkerMenuChordSetEnabled(bool); @@ -309,6 +325,7 @@ protected slots: static const QVector m_zoomLevels; Pattern* m_pattern; + Pattern* m_ghostPattern; QScrollBar * m_leftRightScroll; QScrollBar * m_topBottomScroll; @@ -388,6 +405,8 @@ protected slots: QColor m_noteModeColor; QColor m_noteColor; QColor m_noteTextColor; + QColor m_ghostNoteColor; + QColor m_ghostNoteTextColor; QColor m_barColor; QColor m_selectedNoteColor; QColor m_textColor; @@ -395,7 +414,9 @@ protected slots: QColor m_textShadow; QColor m_markedSemitoneColor; int m_noteOpacity; + int m_ghostNoteOpacity; bool m_noteBorders; + bool m_ghostNoteBorders; QColor m_backgroundShade; signals: @@ -412,7 +433,8 @@ class PianoRollWindow : public Editor, SerializingObject PianoRollWindow(); const Pattern* currentPattern() const; - void setCurrentPattern(Pattern* pattern); + void setCurrentPattern( Pattern* pattern ); + void setGhostPattern( Pattern* pattern ); int quantization() const; @@ -445,6 +467,7 @@ class PianoRollWindow : public Editor, SerializingObject private slots: void patternRenamed(); + void ghostPatternSet( bool state ); private: void focusInEvent(QFocusEvent * event); @@ -456,6 +479,7 @@ private slots: ComboBox * m_noteLenComboBox; ComboBox * m_scaleComboBox; ComboBox * m_chordComboBox; + QPushButton * m_clearGhostButton; }; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index e5f6458b55e..116159a51e0 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -182,6 +182,8 @@ PianoRoll::PianoRoll() : m_lineColor( 0, 0, 0 ), m_noteModeColor( 0, 0, 0 ), m_noteColor( 0, 0, 0 ), + m_ghostNoteColor( 0, 0, 0 ), + m_ghostNoteTextColor( 0, 0, 0 ), m_barColor( 0, 0, 0 ), m_selectedNoteColor( 0, 0, 0 ), m_textColor( 0, 0, 0 ), @@ -189,7 +191,9 @@ PianoRoll::PianoRoll() : m_textShadow( 0, 0, 0 ), m_markedSemitoneColor( 0, 0, 0 ), m_noteOpacity( 255 ), + m_ghostNoteOpacity( 255 ), m_noteBorders( true ), + m_ghostNoteBorders( true ), m_backgroundShade( 0, 0, 0 ) { // gui names of edit modes @@ -599,6 +603,26 @@ PianoRoll::~PianoRoll() } +void PianoRoll::setGhostPattern( Pattern* newPattern ) +{ + m_ghostPattern = newPattern; + if( newPattern != nullptr ) + { + // make sure to always get informed about the pattern being destroyed + connect( m_ghostPattern, SIGNAL( destroyedPattern( Pattern* ) ), this, SLOT( clearGhostPattern() ) ); + emit ghostPatternSet( true ); + } +} + + +void PianoRoll::clearGhostPattern() +{ + setGhostPattern( nullptr ); + emit ghostPatternSet( false ); + update(); +} + + void PianoRoll::setCurrentPattern( Pattern* newPattern ) { if( hasValidPattern() ) @@ -801,6 +825,30 @@ bool PianoRoll::noteBorders() const void PianoRoll::setNoteBorders( const bool b ) { m_noteBorders = b; } +QColor PianoRoll::ghostNoteColor() const +{ return m_ghostNoteColor; } + +void PianoRoll::setGhostNoteColor( const QColor & c ) +{ m_ghostNoteColor = c; } + +QColor PianoRoll::ghostNoteTextColor() const +{ return m_ghostNoteTextColor; } + +void PianoRoll::setGhostNoteTextColor( const QColor & c ) +{ m_ghostNoteTextColor = c; } + +int PianoRoll::ghostNoteOpacity() const +{ return m_ghostNoteOpacity; } + +void PianoRoll::setGhostNoteOpacity( const int i ) +{ m_ghostNoteOpacity = i; } + +bool PianoRoll::ghostNoteBorders() const +{ return m_ghostNoteBorders; } + +void PianoRoll::setGhostNoteBorders( const bool b ) +{ m_ghostNoteBorders = b; } + QColor PianoRoll::backgroundShade() const { return m_backgroundShade; } @@ -810,7 +858,6 @@ void PianoRoll::setBackgroundShade( const QColor & c ) - void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor, const QColor & selCol, const int noteOpc, const bool borders, bool drawNoteName ) @@ -3024,6 +3071,50 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) QPolygonF editHandles; + // -- Begin ghost pattern + if( m_ghostPattern != nullptr && m_ghostPattern != m_pattern ) + { + for( const Note *note : m_ghostPattern->notes() ) + { + int len_ticks = note->length(); + + if( len_ticks == 0 ) + { + continue; + } + else if( len_ticks < 0 ) + { + len_ticks = 4; + } + const int key = note->key() - m_startKey + 1; + + int pos_ticks = note->pos(); + + int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact(); + const int x = ( pos_ticks - m_currentPosition ) * + m_ppt / MidiTime::ticksPerTact(); + // skip this note if not in visible area at all + if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + { + continue; + } + + // is the note in visible area? + if( key > 0 && key <= visible_keys ) + { + + // we've done and checked all, let's draw the + // note + drawNoteRect( p, x + WHITE_KEY_WIDTH, + y_base - key * KEY_LINE_HEIGHT, + note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(), + ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames ); + } + + } + } + // -- End ghost pattern + for( const Note *note : m_pattern->notes() ) { int len_ticks = note->length(); @@ -4221,8 +4312,15 @@ PianoRollWindow::PianoRollWindow() : m_chordComboBox = new ComboBox( m_toolBar ); m_chordComboBox->setModel( &m_editor->m_chordModel ); m_chordComboBox->setFixedSize( 105, 22 ); - m_chordComboBox->setToolTip( tr( "Chord") ); + m_chordComboBox->setToolTip( tr( "Chord" ) ); + // -- Clear ghost pattern button + m_clearGhostButton = new QPushButton( m_toolBar ); + m_clearGhostButton->setIcon( embed::getIconPixmap( "clear_ghost_note" ) ); + m_clearGhostButton->setToolTip( tr( "Clear ghost notes" ) ); + m_clearGhostButton->setEnabled( false ); + connect( m_clearGhostButton, SIGNAL( clicked() ), m_editor, SLOT( clearGhostPattern() ) ); + connect( m_editor, SIGNAL( ghostPatternSet( bool ) ), this, SLOT( ghostPatternSet( bool ) ) ); zoomAndNotesToolBar->addWidget( zoom_lbl ); zoomAndNotesToolBar->addWidget( m_zoomingComboBox ); @@ -4243,6 +4341,9 @@ PianoRollWindow::PianoRollWindow() : zoomAndNotesToolBar->addWidget( chord_lbl ); zoomAndNotesToolBar->addWidget( m_chordComboBox ); + zoomAndNotesToolBar->addSeparator(); + zoomAndNotesToolBar->addWidget( m_clearGhostButton ); + // setup our actual window setFocusPolicy( Qt::StrongFocus ); setFocus(); @@ -4265,6 +4366,14 @@ const Pattern* PianoRollWindow::currentPattern() const +void PianoRollWindow::setGhostPattern( Pattern* pattern ) +{ + m_editor->setGhostPattern( pattern ); +} + + + + void PianoRollWindow::setCurrentPattern( Pattern* pattern ) { m_editor->setCurrentPattern( pattern ); @@ -4387,6 +4496,14 @@ void PianoRollWindow::patternRenamed() +void PianoRollWindow::ghostPatternSet( bool state ) +{ + m_clearGhostButton->setEnabled( state ); +} + + + + void PianoRollWindow::focusInEvent( QFocusEvent * event ) { // when the window is given focus, also give focus to the actual piano roll diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 8218d16f631..13f0bad9f93 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -637,6 +637,18 @@ void PatternView::openInPianoRoll() + +void PatternView::setGhostInPianoRoll() +{ + gui->pianoRoll()->setGhostPattern( m_pat ); + gui->pianoRoll()->parentWidget()->show(); + gui->pianoRoll()->show(); + gui->pianoRoll()->setFocus(); +} + + + + void PatternView::resetName() { m_pat->setName( m_pat->m_instrumentTrack->name() ); @@ -663,7 +675,22 @@ void PatternView::constructContextMenu( QMenu * _cm ) _cm->insertAction( _cm->actions()[0], a ); connect( a, SIGNAL( triggered( bool ) ), this, SLOT( openInPianoRoll() ) ); - _cm->insertSeparator( _cm->actions()[1] ); + + if( gui->pianoRoll()->currentPattern() && + gui->pianoRoll()->currentPattern() != m_pat && + not m_pat->empty() ) + { + QAction * b = new QAction( embed::getIconPixmap( "ghost_note" ), + tr( "Set as ghost in piano-roll" ), _cm ); + _cm->insertAction( _cm->actions()[1], b ); + connect( b, SIGNAL( triggered( bool ) ), + this, SLOT( setGhostInPianoRoll() ) ); + _cm->insertSeparator( _cm->actions()[2] ); + } + else + { + _cm->insertSeparator( _cm->actions()[1] ); + } _cm->addSeparator();