diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 6ae93b49856..3e64c9da5a9 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -118,7 +118,7 @@ QMenu::indicator:selected { background-color: #747474; } -positionLine { +PositionLine { qproperty-tailGradient: false; qproperty-lineColor: rgb(255, 255, 255); } @@ -138,6 +138,17 @@ PianoRoll { qproperty-ghostNoteBorders: true; qproperty-barColor: #4afd85; qproperty-markedSemitoneColor: rgba( 0, 255, 200, 60 ); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: rgb( 128, 128, 128); + qproperty-whiteKeyInactiveTextShadow: rgb( 240, 240, 240 ); + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: rgba( 128, 128, 128, 80 ); qproperty-beatLineColor: rgba( 128, 128, 128, 160 ); diff --git a/data/themes/default/style.css b/data/themes/default/style.css index fc6495921a9..4dee86788ec 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -150,7 +150,7 @@ QMenu::indicator:selected { background-color: #101213; } -positionLine { +PositionLine { qproperty-tailGradient: true; qproperty-lineColor: rgb(255, 255, 255); } @@ -170,6 +170,17 @@ PianoRoll { qproperty-ghostNoteBorders: false; qproperty-barColor: #078f3a; qproperty-markedSemitoneColor: rgba(255, 255, 255, 30); + /* Piano keys */ + qproperty-whiteKeyWidth: 64; + qproperty-whiteKeyActiveTextColor: #000; + qproperty-whiteKeyActiveTextShadow: #fff; + qproperty-whiteKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-whiteKeyInactiveTextColor: #000; + qproperty-whiteKeyInactiveTextShadow: #fff; + qproperty-whiteKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #eeeeee, stop:1 #ffffff); + qproperty-blackKeyWidth: 48; + qproperty-blackKeyActiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #43e97b, stop:1 #3bcd6c); + qproperty-blackKeyInactiveBackground: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #333, stop:1 #000); /* Grid colors */ qproperty-lineColor: #292929; qproperty-beatLineColor: #2d6b45; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 86a65050c1d..7a99e7b1f80 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -40,6 +40,7 @@ #include "ToolTip.h" #include "StepRecorder.h" #include "StepRecorderWidget.h" +#include "PositionLine.h" class QPainter; class QPixmap; @@ -55,25 +56,38 @@ class TimeLineWidget; class PianoRoll : public QWidget { Q_OBJECT - Q_PROPERTY( QColor barLineColor READ barLineColor WRITE setBarLineColor ) - Q_PROPERTY( QColor beatLineColor READ beatLineColor WRITE setBeatLineColor ) - 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 ) - Q_PROPERTY( QColor textColorLight READ textColorLight WRITE setTextColorLight ) - Q_PROPERTY( QColor textShadow READ textShadow WRITE setTextShadow ) - 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 ) + Q_PROPERTY(QColor barLineColor MEMBER m_barLineColor) + Q_PROPERTY(QColor beatLineColor MEMBER m_beatLineColor) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) + Q_PROPERTY(QColor noteModeColor MEMBER m_noteModeColor) + Q_PROPERTY(QColor noteColor MEMBER m_noteColor) + Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor) + Q_PROPERTY(QColor noteTextColor MEMBER m_noteTextColor) + Q_PROPERTY(QColor ghostNoteTextColor MEMBER m_ghostNoteTextColor) + Q_PROPERTY(QColor barColor MEMBER m_barColor) + Q_PROPERTY(QColor selectedNoteColor MEMBER m_selectedNoteColor) + Q_PROPERTY(QColor textColor MEMBER m_textColor) + Q_PROPERTY(QColor textColorLight MEMBER m_textColorLight) + Q_PROPERTY(QColor textShadow MEMBER m_textShadow) + Q_PROPERTY(QColor markedSemitoneColor MEMBER m_markedSemitoneColor) + Q_PROPERTY(int noteOpacity MEMBER m_noteOpacity) + Q_PROPERTY(bool noteBorders MEMBER m_noteBorders) + Q_PROPERTY(int ghostNoteOpacity MEMBER m_ghostNoteOpacity) + Q_PROPERTY(bool ghostNoteBorders MEMBER m_ghostNoteBorders) + Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade) + + /* white key properties */ + Q_PROPERTY(int whiteKeyWidth MEMBER m_whiteKeyWidth) + Q_PROPERTY(QColor whiteKeyInactiveTextColor MEMBER m_whiteKeyInactiveTextColor) + Q_PROPERTY(QColor whiteKeyInactiveTextShadow MEMBER m_whiteKeyInactiveTextShadow) + Q_PROPERTY(QBrush whiteKeyInactiveBackground MEMBER m_whiteKeyInactiveBackground) + Q_PROPERTY(QColor whiteKeyActiveTextColor MEMBER m_whiteKeyActiveTextColor) + Q_PROPERTY(QColor whiteKeyActiveTextShadow MEMBER m_whiteKeyActiveTextShadow) + Q_PROPERTY(QBrush whiteKeyActiveBackground MEMBER m_whiteKeyActiveBackground) + /* black key properties */ + Q_PROPERTY(int blackKeyWidth MEMBER m_blackKeyWidth) + Q_PROPERTY(QBrush blackKeyInactiveBackground MEMBER m_blackKeyInactiveBackground) + Q_PROPERTY(QBrush blackKeyActiveBackground MEMBER m_blackKeyActiveBackground) public: enum EditModes { @@ -125,47 +139,6 @@ class PianoRoll : public QWidget int quantization() const; - // qproperty access functions - QColor barLineColor() const; - void setBarLineColor( const QColor & c ); - QColor beatLineColor() const; - void setBeatLineColor( const QColor & c ); - QColor lineColor() const; - void setLineColor( const QColor & c ); - QColor noteModeColor() const; - void setNoteModeColor( const QColor & c ); - QColor noteColor() const; - void setNoteColor( const QColor & c ); - QColor noteTextColor() const; - void setNoteTextColor( const QColor & c ); - QColor barColor() const; - void setBarColor( const QColor & c ); - QColor selectedNoteColor() const; - void setSelectedNoteColor( const QColor & c ); - QColor textColor() const; - void setTextColor( const QColor & c ); - QColor textColorLight() const; - void setTextColorLight( const QColor & c ); - QColor textShadow() const; - void setTextShadow( const QColor & c ); - QColor markedSemitoneColor() const; - void setMarkedSemitoneColor( const QColor & c ); - int noteOpacity() const; - 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 ); - - protected: void keyPressEvent( QKeyEvent * ke ) override; void keyReleaseEvent( QKeyEvent * ke ) override; @@ -188,7 +161,6 @@ class PianoRoll : public QWidget void selectAll(); NoteVector getSelectedNotes() const; void selectNotesOnKey(); - int xCoordOfTick( int tick ); // for entering values with dblclick in the vol/pan bars void enterValue( NoteVector* nv ); @@ -279,6 +251,8 @@ protected slots: PR_BLACK_KEY }; + PositionLine * m_positionLine; + QVector m_nemStr; // gui names of each edit mode QMenu * m_noteEditMenu; // when you right click below the key area @@ -306,6 +280,9 @@ protected slots: void playChordNotes(int key, int velocity=-1); void pauseChordNotes(int key); + void updateScrollbars(); + void updatePositionLineHeight(); + QList getAllOctavesForKey( int keyToMirror ) const; int noteEditTop() const; @@ -320,12 +297,6 @@ protected slots: static const int cm_scrollAmtHoriz = 10; static const int cm_scrollAmtVert = 1; - static QPixmap * s_whiteKeyBigPm; - static QPixmap * s_whiteKeyBigPressedPm; - static QPixmap * s_whiteKeySmallPm; - static QPixmap * s_whiteKeySmallPressedPm; - static QPixmap * s_blackKeyPm; - static QPixmap * s_blackKeyPressedPm; static QPixmap * s_toolDraw; static QPixmap * s_toolErase; static QPixmap * s_toolSelect; @@ -389,10 +360,11 @@ protected slots: int m_moveStartX; int m_moveStartY; - int m_oldNotesEditHeight; int m_notesEditHeight; + int m_userSetNotesEditHeight; int m_ppb; // pixels per bar int m_totalKeysToScroll; + int m_pianoKeysVisible; int m_keyLineHeight; int m_octaveHeight; @@ -458,6 +430,18 @@ protected slots: bool m_noteBorders; bool m_ghostNoteBorders; QColor m_backgroundShade; + /* white key properties */ + int m_whiteKeyWidth; + QColor m_whiteKeyActiveTextColor; + QColor m_whiteKeyActiveTextShadow; + QBrush m_whiteKeyActiveBackground; + QColor m_whiteKeyInactiveTextColor; + QColor m_whiteKeyInactiveTextShadow; + QBrush m_whiteKeyInactiveBackground; + /* black key properties */ + int m_blackKeyWidth; + QBrush m_blackKeyActiveBackground; + QBrush m_blackKeyInactiveBackground; signals: void positionChanged( const MidiTime & ); @@ -501,6 +485,7 @@ class PianoRollWindow : public Editor, SerializingObject } QSize sizeHint() const override; + bool hasFocus() const; signals: void currentPatternChanged(); diff --git a/include/PositionLine.h b/include/PositionLine.h new file mode 100644 index 00000000000..d48fd3df1cb --- /dev/null +++ b/include/PositionLine.h @@ -0,0 +1,49 @@ +/* + * PositionLine.h - declaration of class PositionLine, a simple widget that + * draws a line, mainly works with TimeLineWidget + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef POSITION_LINE_H +#define POSITION_LINE_H + +#include + +class PositionLine : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool tailGradient MEMBER m_hasTailGradient) + Q_PROPERTY(QColor lineColor MEMBER m_lineColor) +public: + PositionLine(QWidget* parent); + +public slots: + void zoomChange(double zoom); + +private: + void paintEvent(QPaintEvent* pe) override; + + bool m_hasTailGradient; + QColor m_lineColor; +}; + +#endif \ No newline at end of file diff --git a/include/SongEditor.h b/include/SongEditor.h index c4c25d7d0c9..7e0fe986a8c 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -33,6 +33,7 @@ #include "ActionGroup.h" #include "Editor.h" #include "TrackContainerView.h" +#include "PositionLine.h" class QLabel; class QScrollBar; @@ -46,31 +47,6 @@ class Song; class TextFloat; class TimeLineWidget; -class positionLine : public QWidget -{ - Q_OBJECT - Q_PROPERTY ( bool tailGradient READ hasTailGradient WRITE setHasTailGradient ) - Q_PROPERTY ( QColor lineColor READ lineColor WRITE setLineColor ) -public: - positionLine ( QWidget* parent ); - - // qproperty access functions - bool hasTailGradient () const; - void setHasTailGradient ( const bool g ); - QColor lineColor () const; - void setLineColor ( const QColor & c ); - -public slots: - void zoomChange (double zoom); - -private: - void paintEvent( QPaintEvent* pe ) override; - - bool m_hasTailGradient; - QColor m_lineColor; - -}; - class SongEditor : public TrackContainerView { @@ -156,7 +132,7 @@ private slots: TextFloat * m_mvsStatus; TextFloat * m_mpsStatus; - positionLine * m_positionLine; + PositionLine * m_positionLine; ComboBoxModel* m_zoomingModel; ComboBoxModel* m_snappingModel; diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index 14cfc2eedce..67f2a4b6547 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -45,7 +45,9 @@ class StepRecorderWidget : public QWidget //API used by PianoRoll void setPixelsPerBar(int ppb); void setCurrentPosition(MidiTime currentPosition); + void setMargins(const QMargins &qm); void setBottomMargin(const int marginBottom); + QMargins margins(); //API used by StepRecorder void setStepsLength(MidiTime stepsLength); diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 5b33bd98987..8a9dc5044ea 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -150,6 +150,8 @@ class TimeLineWidget : public QWidget, public JournallingObject update(); } + void setXOffset(const int x); + void addToolButtons(QToolBar* _tool_bar ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 81e588c6659..3c6529e0b78 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -81,6 +81,7 @@ SET(LMMS_SRCS gui/widgets/NStateButton.cpp gui/widgets/Oscilloscope.cpp gui/widgets/PixmapButton.cpp + gui/widgets/PositionLine.cpp gui/widgets/ProjectNotes.cpp gui/widgets/RenameDialog.cpp gui/widgets/Rubberband.cpp diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index 8e79410b853..070ac0a40d1 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -109,6 +109,14 @@ TimeLineWidget::~TimeLineWidget() +void TimeLineWidget::setXOffset(const int x) +{ + m_xOffset = x; + if (s_posMarkerPixmap != nullptr) { m_xOffset -= s_posMarkerPixmap->width() / 2; } +} + + + void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index b519fb9e5e1..3d35c9c5133 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -72,7 +73,7 @@ typedef AutomationPattern::timeMap timeMap; // some constants... const int INITIAL_PIANOROLL_WIDTH = 860; -const int INITIAL_PIANOROLL_HEIGHT = 480; +const int INITIAL_PIANOROLL_HEIGHT = 485; const int SCROLLBAR_SIZE = 12; const int PIANO_X = 0; @@ -86,9 +87,9 @@ const int DEFAULT_CELL_WIDTH = 12; const int NOTE_EDIT_RESIZE_BAR = 6; const int NOTE_EDIT_MIN_HEIGHT = 50; -const int KEY_AREA_MIN_HEIGHT = 100; +const int KEY_AREA_MIN_HEIGHT = DEFAULT_KEY_LINE_HEIGHT * 10; const int PR_BOTTOM_MARGIN = SCROLLBAR_SIZE; -const int PR_TOP_MARGIN = 16; +const int PR_TOP_MARGIN = 18; const int PR_RIGHT_MARGIN = SCROLLBAR_SIZE; @@ -107,12 +108,6 @@ const int NUM_TRIPLET_LENGTHS = 5; -QPixmap * PianoRoll::s_whiteKeySmallPm = NULL; -QPixmap * PianoRoll::s_whiteKeySmallPressedPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPm = NULL; -QPixmap * PianoRoll::s_whiteKeyBigPressedPm = NULL; -QPixmap * PianoRoll::s_blackKeyPm = NULL; -QPixmap * PianoRoll::s_blackKeyPressedPm = NULL; QPixmap * PianoRoll::s_toolDraw = NULL; QPixmap * PianoRoll::s_toolErase = NULL; QPixmap * PianoRoll::s_toolSelect = NULL; @@ -170,14 +165,14 @@ PianoRoll::PianoRoll() : m_mouseDownTick( 0 ), m_lastMouseX( 0 ), m_lastMouseY( 0 ), - m_oldNotesEditHeight( 100 ), m_notesEditHeight( 100 ), + m_userSetNotesEditHeight(100), m_ppb( DEFAULT_PR_PPB ), m_keyLineHeight(DEFAULT_KEY_LINE_HEIGHT), m_octaveHeight(m_keyLineHeight * KeysPerOctave), - m_whiteKeySmallHeight(round(m_keyLineHeight * 1.5)), + m_whiteKeySmallHeight(qFloor(m_keyLineHeight * 1.5)), m_whiteKeyBigHeight(m_keyLineHeight * 2), - m_blackKeyHeight(round(m_keyLineHeight * 1.3333)), + m_blackKeyHeight(m_keyLineHeight), m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), @@ -207,7 +202,9 @@ PianoRoll::PianoRoll() : m_ghostNoteOpacity( 255 ), m_noteBorders( true ), m_ghostNoteBorders( true ), - m_backgroundShade( 0, 0, 0 ) + m_backgroundShade( 0, 0, 0 ), + m_whiteKeyWidth(WHITE_KEY_WIDTH), + m_blackKeyWidth(BLACK_KEY_WIDTH) { // gui names of edit modes m_nemStr.push_back( tr( "Note Velocity" ) ); @@ -252,36 +249,6 @@ PianoRoll::PianoRoll() : m_semiToneMarkerMenu->addAction( copyAllNotesAction ); // init pixmaps - if( s_whiteKeySmallPm == NULL ) - { - s_whiteKeySmallPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small" ) ); - } - if( s_whiteKeySmallPressedPm == NULL ) - { - s_whiteKeySmallPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_small_pressed" ) ); - } - if( s_whiteKeyBigPm == NULL ) - { - s_whiteKeyBigPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big" ) ); - } - if( s_whiteKeyBigPressedPm == NULL ) - { - s_whiteKeyBigPressedPm = new QPixmap( embed::getIconPixmap( - "pr_white_key_big_pressed" ) ); - } - if( s_blackKeyPm == NULL ) - { - s_blackKeyPm = new QPixmap( embed::getIconPixmap( - "pr_black_key" ) ); - } - if( s_blackKeyPressedPm == NULL ) - { - s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( - "pr_black_key_pressed" ) ); - } if( s_toolDraw == NULL ) { s_toolDraw = new QPixmap( embed::getIconPixmap( "edit_draw" ) ); @@ -312,7 +279,7 @@ PianoRoll::PianoRoll() : setAttribute( Qt::WA_OpaquePaintEvent, true ); // add time-line - m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppb, + m_timeLine = new TimeLineWidget(m_whiteKeyWidth, 0, m_ppb, Engine::getSong()->getPlayPos( Song::Mode_PlayPattern ), m_currentPosition, @@ -322,6 +289,9 @@ PianoRoll::PianoRoll() : connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePosition( const MidiTime & ) ) ); + // white position line follows timeline marker + m_positionLine = new PositionLine(this); + //update timeline when in step-recording mode connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const MidiTime & ) ), this, SLOT( updatePositionStepRecording( const MidiTime & ) ) ); @@ -768,8 +738,8 @@ void PianoRoll::hidePattern( Pattern* pattern ) void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) { - xStart -= WHITE_KEY_WIDTH; - xEnd -= WHITE_KEY_WIDTH; + xStart -= m_whiteKeyWidth; + xEnd -= m_whiteKeyWidth; // select an area of notes int posTicks = xStart * MidiTime::ticksPerBar() / m_ppb + @@ -804,127 +774,6 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) - - -/** \brief qproperty access implementation */ - -QColor PianoRoll::barLineColor() const -{ return m_barLineColor; } - -void PianoRoll::setBarLineColor( const QColor & c ) -{ m_barLineColor = c; } - -QColor PianoRoll::beatLineColor() const -{ return m_beatLineColor; } - -void PianoRoll::setBeatLineColor( const QColor & c ) -{ m_beatLineColor = c; } - -QColor PianoRoll::lineColor() const -{ return m_lineColor; } - -void PianoRoll::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -QColor PianoRoll::noteModeColor() const -{ return m_noteModeColor; } - -void PianoRoll::setNoteModeColor( const QColor & c ) -{ m_noteModeColor = c; } - -QColor PianoRoll::noteColor() const -{ return m_noteColor; } - -void PianoRoll::setNoteColor( const QColor & c ) -{ m_noteColor = c; } - -QColor PianoRoll::noteTextColor() const -{ return m_noteTextColor; } - -void PianoRoll::setNoteTextColor( const QColor & c ) -{ m_noteTextColor = c; } - -QColor PianoRoll::barColor() const -{ return m_barColor; } - -void PianoRoll::setBarColor( const QColor & c ) -{ m_barColor = c; } - -QColor PianoRoll::selectedNoteColor() const -{ return m_selectedNoteColor; } - -void PianoRoll::setSelectedNoteColor( const QColor & c ) -{ m_selectedNoteColor = c; } - -QColor PianoRoll::textColor() const -{ return m_textColor; } - -void PianoRoll::setTextColor( const QColor & c ) -{ m_textColor = c; } - -QColor PianoRoll::textColorLight() const -{ return m_textColorLight; } - -void PianoRoll::setTextColorLight( const QColor & c ) -{ m_textColorLight = c; } - -QColor PianoRoll::textShadow() const -{ return m_textShadow; } - -void PianoRoll::setTextShadow( const QColor & c ) -{ m_textShadow = c; } - -QColor PianoRoll::markedSemitoneColor() const -{ return m_markedSemitoneColor; } - -void PianoRoll::setMarkedSemitoneColor( const QColor & c ) -{ m_markedSemitoneColor = c; } - -int PianoRoll::noteOpacity() const -{ return m_noteOpacity; } - -void PianoRoll::setNoteOpacity( const int i ) -{ m_noteOpacity = i; } - -bool PianoRoll::noteBorders() const -{ return m_noteBorders; } - -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; } - -void PianoRoll::setBackgroundShade( const QColor & c ) -{ m_backgroundShade = 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 ) @@ -1039,9 +888,11 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, int _y ) const { int middle_y = _y + m_keyLineHeight / 2; - _p.setPen( noteColor() ); - _p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + _p.setPen(m_noteColor); + _p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, keyAreaBottom() - PR_TOP_MARGIN); int old_x = 0; @@ -1449,8 +1300,7 @@ void PianoRoll::leaveEvent(QEvent * e ) int PianoRoll::noteEditTop() const { - return height() - PR_BOTTOM_MARGIN - - m_notesEditHeight + NOTE_EDIT_RESIZE_BAR; + return keyAreaBottom() + NOTE_EDIT_RESIZE_BAR; } @@ -1474,7 +1324,7 @@ int PianoRoll::noteEditRight() const int PianoRoll::noteEditLeft() const { - return WHITE_KEY_WIDTH; + return m_whiteKeyWidth; } @@ -1539,11 +1389,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_moveStartY = me->y(); } - if( me->y() > keyAreaBottom() && me->y() < noteEditTop() ) + if(me->button() == Qt::LeftButton && + me->y() > keyAreaBottom() && me->y() < noteEditTop()) { // resizing the note edit area m_action = ActionResizeNoteEditArea; - m_oldNotesEditHeight = m_notesEditHeight; return; } @@ -1556,11 +1406,11 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) int x = me->x(); - if( x > WHITE_KEY_WIDTH ) + if (x > m_whiteKeyWidth) { // set, move or resize note - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; // get tick in which the user clicked int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + @@ -1827,7 +1677,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) else if( me->buttons() == Qt::LeftButton ) { // left click - play the note - int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity; + int v = ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity; m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num, v); // if a chord is set, play the chords notes as well: playChordNotes(key_num, v); @@ -1870,7 +1720,7 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) { // get values for going through notes int pixel_range = 4; - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; const int ticks_start = ( x-pixel_range/2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; const int ticks_end = ( x+pixel_range/2 ) * @@ -2203,14 +2053,23 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } else if( m_action == ActionResizeNoteEditArea ) { + // Don't try to show more keys than the full keyboard, bail if trying to + if (m_pianoKeysVisible == NumKeys && me->y() > m_moveStartY) + { + return; + } + int newHeight = height() - me->y(); + if (me->y() < KEY_AREA_MIN_HEIGHT) + { + newHeight = height() - KEY_AREA_MIN_HEIGHT - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; // - NOTE_EDIT_RESIZE_BAR + } // change m_notesEditHeight and then repaint - m_notesEditHeight = qBound( - NOTE_EDIT_MIN_HEIGHT, - m_oldNotesEditHeight - ( me->y() - m_moveStartY ), - height() - PR_TOP_MARGIN - NOTE_EDIT_RESIZE_BAR - - PR_BOTTOM_MARGIN - KEY_AREA_MIN_HEIGHT ); - + m_notesEditHeight = qMax(NOTE_EDIT_MIN_HEIGHT, newHeight); + m_userSetNotesEditHeight = m_notesEditHeight; m_stepRecorderWidget.setBottomMargin(PR_BOTTOM_MARGIN + m_notesEditHeight); + updateScrollbars(); + updatePositionLineHeight(); repaint(); return; } @@ -2225,17 +2084,17 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) int x = me->x(); // see if they clicked on the keyboard on the left - if( x < WHITE_KEY_WIDTH && m_action == ActionNone + if (x < m_whiteKeyWidth && m_action == ActionNone && ! edit_note && key_num != m_lastKey && me->buttons() & Qt::LeftButton ) { // clicked on a key, play the note - testPlayKey( key_num, ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity, 0 ); + testPlayKey(key_num, ((float) x) / ((float) m_whiteKeyWidth) * MidiDefaultVelocity, 0); update(); return; } - x -= WHITE_KEY_WIDTH; + x -= m_whiteKeyWidth; if( me->buttons() & Qt::LeftButton && m_editMode == ModeDraw @@ -2508,12 +2367,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_action == ActionSelectNotes ) { - int x = me->x() - WHITE_KEY_WIDTH; + int x = me->x() - m_whiteKeyWidth; if( x < 0 && m_currentPosition > 0 ) { x = 0; QCursor::setPos( mapToGlobal( QPoint( - WHITE_KEY_WIDTH, + m_whiteKeyWidth, me->y() ) ) ); if( m_currentPosition >= 4 ) { @@ -2525,9 +2384,9 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_leftRightScroll->setValue( 0 ); } } - else if( x > width() - WHITE_KEY_WIDTH ) + else if (x > width() - m_whiteKeyWidth) { - x = width() - WHITE_KEY_WIDTH; + x = width() - m_whiteKeyWidth; QCursor::setPos( mapToGlobal( QPoint( width(), me->y() ) ) ); m_leftRightScroll->setValue( m_currentPosition + @@ -2774,11 +2633,8 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) Engine::getSong()->setModified(); } -int PianoRoll::xCoordOfTick( int tick ) -{ - return WHITE_KEY_WIDTH + ( ( tick - m_currentPosition ) - * m_ppb / MidiTime::ticksPerBar() ); -} + + void PianoRoll::paintEvent(QPaintEvent * pe ) { @@ -2794,353 +2650,337 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // fill with bg color p.fillRect( 0, 0, width(), height(), bgColor ); - // set font-size to 8 - p.setFont( pointSize<8>( p.font() ) ); + // set font-size to 80% of key line height + QFont f = p.font(); + f.setPixelSize(m_keyLineHeight * 0.8); + p.setFont(f); // font size doesn't change without this for some reason QFontMetrics fontMetrics(p.font()); - QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); - // This is two times of the y coordinate of the center of the bounding rectangle - // (-(top+bottom)=-2(center)) but labelHeight is more intuitive/describing name - int const labelHeight = - boundingRect.top() - boundingRect.bottom(); + // G4 is one of the widest + QRect const boundingRect = fontMetrics.boundingRect(QString("G4")); + + // Order of drawing + // - vertical quantization lines + // - piano roll + horizontal key lines + // - alternating bar colors + // - vertical beat lines + // - vertical bar lines + // - marked semitones + // - note editing + // - notes + // - selection frame + // - highlight hovered note + // - note edit area resize bar + // - cursor mode icon - // y_offset is used to align the piano-keys on the key-lines - int y_offset = 0; - - // calculate y_offset according to first key - switch( prKeyOrder[m_startKey % KeysPerOctave] ) + if (hasValidPattern()) { - case PR_BLACK_KEY: y_offset = m_keyLineHeight / 4; break; - case PR_WHITE_KEY_BIG: y_offset = m_keyLineHeight / 2; break; - case PR_WHITE_KEY_SMALL: - if( prKeyOrder[( ( m_startKey + 1 ) % - KeysPerOctave)] != PR_BLACK_KEY ) + int pianoAreaHeight, partialKeyVisible, topKey, topNote; + pianoAreaHeight = keyAreaBottom() - keyAreaTop(); + m_pianoKeysVisible = pianoAreaHeight / m_keyLineHeight; + partialKeyVisible = pianoAreaHeight % m_keyLineHeight; + // check if we're below the minimum key area size + if (m_pianoKeysVisible * m_keyLineHeight < KEY_AREA_MIN_HEIGHT) + { + m_pianoKeysVisible = KEY_AREA_MIN_HEIGHT / m_keyLineHeight; + partialKeyVisible = KEY_AREA_MIN_HEIGHT % m_keyLineHeight; + // if we have a partial key, just show it + if (partialKeyVisible > 0) { - y_offset = m_keyLineHeight / 2; + m_pianoKeysVisible += 1; + partialKeyVisible = 0; } - break; - } - // start drawing at the bottom - int key_line_y = qMin(keyAreaBottom() - 1, m_keyLineHeight * NumKeys); - // we need to set m_notesEditHeight here because it needs to fill in the - // rest of the window if key_line_y is bound to m_keyLineHeight * NumKeys - if (key_line_y == m_keyLineHeight * NumKeys) { - m_notesEditHeight = height() - (PR_TOP_MARGIN + m_keyLineHeight * NumKeys); - } - // used for aligning black-keys later - int first_white_key_height = m_whiteKeySmallHeight; - // key-counter - only needed for finding out whether the processed - // key is the first one - int keys_processed = 0; - - int key = m_startKey; - - // draw all white keys... - for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; - key_line_y -= m_keyLineHeight, ++keys_processed ) - { - // check for white key that is only half visible on the - // bottom of piano-roll - if( keys_processed == 0 && - prKeyOrder[m_startKey % KeysPerOctave] == - PR_BLACK_KEY ) + // have to modifiy the notes edit area height instead + m_notesEditHeight = height() - (m_pianoKeysVisible * m_keyLineHeight) + - PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + } + // check if we're trying to show more keys than available + else if (m_pianoKeysVisible >= NumKeys) { - // draw it! - p.drawPixmap( PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm ); - // update y-pos - y -= m_whiteKeySmallHeight / 2; - // move first black key down (we didn't draw whole - // white key so black key needs to be lifted down) - // (default for first_white_key_height = - // m_whiteKeySmallHeight, so m_whiteKeySmallHeight/2 - // is smaller) - first_white_key_height = m_whiteKeySmallHeight / 2; + m_pianoKeysVisible = NumKeys; + // have to modify the notes edit area height instead + m_notesEditHeight = height() - (NumKeys * m_keyLineHeight) - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN; + partialKeyVisible = 0; } - // check whether to draw a big or a small white key - if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_SMALL ) + topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + topNote = topKey % KeysPerOctave; + // if not resizing the note edit area, we can change m_notesEditHeight + if (m_action != ActionResizeNoteEditArea && partialKeyVisible != 0) { - // draw a small one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + // calculate the height change adding and subtracting the partial key + int noteAreaPlus = (m_notesEditHeight + partialKeyVisible) - m_userSetNotesEditHeight; + int noteAreaMinus = m_userSetNotesEditHeight - (m_notesEditHeight - partialKeyVisible); + // if adding the partial key to height is more distant from the set height + // we want to subtract the partial key + if (noteAreaPlus > noteAreaMinus) { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPressedPm); + m_notesEditHeight -= partialKeyVisible; + // since we're adding a partial key, we add one to the number visible + m_pianoKeysVisible += 1; } - else - { - p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, - *s_whiteKeySmallPm); - } - // update y-pos - y -= m_whiteKeySmallHeight; + // otherwise we add height + else { m_notesEditHeight += partialKeyVisible; } + } + updatePositionLineHeight(); + int x, q = quantization(), tick; + // draw vertical quantization lines + // If we're over 100% zoom, we allow all quantization level grids + if (m_zoomingModel.value() <= 3) + { + // we're under 100% zoom + // allow quantization grid up to 1/24 for triplets + if (q % 3 != 0 && q < 8) { q = 8; } + // allow quantization grid up to 1/32 for normal notes + else if (q < 6) { q = 6; } } - else if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_BIG ) + auto xCoordOfTick = [=](int tick) { + return m_whiteKeyWidth + ( + (tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar() + ); + }; + p.setPen(m_lineColor); + for (tick = m_currentPosition - m_currentPosition % q, + x = xCoordOfTick(tick); + x <= width(); + tick += q, x = xCoordOfTick(tick)) { - // draw a big one while checking if it is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) - { - p.drawPixmap(PIANO_X, y - m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPressedPm); - } - else - { - p.drawPixmap(PIANO_X, y-m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, - *s_whiteKeyBigPm); - } - // if a big white key has been the first key, - // black keys needs to be lifted up - if( keys_processed == 0 ) - { - first_white_key_height = m_whiteKeyBigHeight; - } - // update y-pos - y -= m_whiteKeyBigHeight; + p.drawLine(x, keyAreaTop(), x, noteEditBottom()); } - // Compute the corrections for the note names - int yCorrectionForNoteLabels = 0; + // draw horizontal grid lines and piano notes + p.setClipRect(0, keyAreaTop(), width(), keyAreaBottom() - keyAreaTop()); + // the first grid line from the top Y position + int grid_line_y = keyAreaTop() + m_keyLineHeight - 1; - int keyCode = key % KeysPerOctave; - switch (keyCode) + // lambda function for returning the height of a key + auto keyHeight = [&]( + const int key + ) -> int { - case 0: // C - case 5: // F - yCorrectionForNoteLabels = (m_whiteKeySmallHeight - labelHeight + 1) / -2; - break; - case 2: // D - case 7: // G - case 9: // A - yCorrectionForNoteLabels = (m_whiteKeyBigHeight / 2 - labelHeight + 1) / -2; - break; - case 4: // E - case 11: // B - // calculate center point of key and move half of text - yCorrectionForNoteLabels = -(((m_whiteKeySmallHeight - (m_whiteKeySmallHeight * 2 + 3) / 6) / 4) - - labelHeight / 2); - break; - } - - if( Piano::isWhiteKey( key ) ) + switch (prKeyOrder[key % KeysPerOctave]) + { + case PR_WHITE_KEY_BIG: + return m_whiteKeyBigHeight; + case PR_WHITE_KEY_SMALL: + return m_whiteKeySmallHeight; + case PR_BLACK_KEY: + return m_blackKeyHeight; + } + return 0; // should never happen + }; + // lambda function for returning the distance to the top of a key + auto gridCorrection = [&]( + const int key + ) -> int { - // Draw note names if activated in the preferences, C notes are always drawn - if ( (key % 12 == 0 || drawNoteNames) && m_keyLineHeight > 10 ) + const int keyCode = key % KeysPerOctave; + switch (prKeyOrder[keyCode]) { - QString noteString = getNoteString( key ); - - QPoint textStart( WHITE_KEY_WIDTH - 18, key_line_y ); - textStart += QPoint( 0, yCorrectionForNoteLabels ); - - p.setPen( textShadow() ); - p.drawText( textStart + QPoint( 1, 1 ), noteString ); - // The C key is painted darker than the other ones - if ( key % 12 == 0 ) + case PR_WHITE_KEY_BIG: + return m_whiteKeySmallHeight; + case PR_WHITE_KEY_SMALL: + // These two keys need to adjust up small height instead of only key line height + if (keyCode == Key_C || keyCode == Key_F) { - p.setPen( textColor() ); + return m_whiteKeySmallHeight; } - else - { - p.setPen( textColorLight() ); - } - p.drawText( textStart, noteString ); + case PR_BLACK_KEY: + return m_blackKeyHeight; } - } - ++key; - } - - // reset all values, because now we're going to draw all black keys - key = m_startKey; - keys_processed = 0; - int white_cnt = 0; - key_line_y = qMin(keyAreaBottom(), m_keyLineHeight * NumKeys); - - // and go! - for( int y = key_line_y + y_offset; - y > PR_TOP_MARGIN; ++keys_processed ) - { - // check for black key that is only half visible on the bottom - // of piano-roll - if( keys_processed == 0 - // current key may not be a black one - && prKeyOrder[key % KeysPerOctave] != PR_BLACK_KEY - // but the previous one must be black (we must check this - // because there might be two white keys (E-F) - && prKeyOrder[( key - 1 ) % KeysPerOctave] == - PR_BLACK_KEY ) + return 0; // should never happen + }; + auto keyWidth = [&]( + const int key + ) -> int { - // draw the black key! - p.drawPixmap( PIANO_X, y - m_blackKeyHeight / 2, BLACK_KEY_WIDTH, m_blackKeyHeight, - *s_blackKeyPm ); - // is the one after the start-note a black key?? - if( prKeyOrder[( key + 1 ) % KeysPerOctave] != - PR_BLACK_KEY ) + switch (prKeyOrder[key % KeysPerOctave]) { - // no, then move it up! - y -= m_keyLineHeight / 2; + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + return m_whiteKeyWidth; + case PR_BLACK_KEY: + return m_blackKeyWidth; } - } - // current key black? - if( prKeyOrder[key % KeysPerOctave] == PR_BLACK_KEY) + return 0; // should never happen + }; + // lambda function to draw a key + auto drawKey = [&]( + const int key, + const int yb) { - // then draw it (calculation of y very complicated, - // but that's the only working solution, sorry...) - // check if the key is pressed or not - if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) + const bool pressed = m_pattern->instrumentTrack()->pianoModel()->isKeyPressed(key); + const int keyCode = key % KeysPerOctave; + const int yt = yb - gridCorrection(key); + const int kh = keyHeight(key); + const int kw = keyWidth(key); + // set key colors + p.setPen(QColor(0, 0, 0)); + switch (prKeyOrder[keyCode]) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPressedPm ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + p.setBrush(pressed ? m_whiteKeyActiveBackground : m_whiteKeyInactiveBackground); + break; + case PR_BLACK_KEY: + p.setBrush(pressed ? m_blackKeyActiveBackground : m_blackKeyInactiveBackground); } - else + // draw key + p.drawRect(PIANO_X, yt, kw, kh); + // draw note name + if (keyCode == Key_C || (drawNoteNames && Piano::isWhiteKey(key))) { - p.drawPixmap( PIANO_X, y - ( first_white_key_height - - m_whiteKeySmallHeight ) - - m_whiteKeySmallHeight/2 - 1 - - m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPm ); + // small font sizes have 1 pixel offset instead of 2 + auto zoomOffset = m_zoomYLevels[m_zoomingYModel.value()] > 1.0f ? 2 : 1; + QString noteString = getNoteString(key); + QRect textRect( + m_whiteKeyWidth - boundingRect.width() - 2, + yb - m_keyLineHeight + zoomOffset, + boundingRect.width(), + boundingRect.height() + ); + p.setPen(pressed ? m_whiteKeyActiveTextShadow : m_whiteKeyInactiveTextShadow); + p.drawText(textRect.adjusted(0, 1, 1, 0), Qt::AlignRight | Qt::AlignHCenter, noteString); + p.setPen(pressed ? m_whiteKeyActiveTextColor : m_whiteKeyInactiveTextColor); + // if (keyCode == Key_C) { p.setPen(textColor()); } + // else { p.setPen(textColorLight()); } + p.drawText(textRect, Qt::AlignRight | Qt::AlignHCenter, noteString); } - // update y-pos - y -= m_whiteKeyBigHeight; - // reset white-counter - white_cnt = 0; - } - else + }; + // lambda for drawing the horizontal grid line + auto drawHorizontalLine = [&]( + const int key, + const int y + ) { - // simple workaround for increasing x if there were - // two white keys (e.g. between E and F) - ++white_cnt; - if( white_cnt > 1 ) - { - y -= m_whiteKeyBigHeight/2; - } - } - - ++key; - } - - - // erase the area below the piano, because there might be keys that - // should be only half-visible - p.fillRect( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), bgColor ); - - // display note editing info - QFont f = p.font(); - f.setBold( false ); - p.setFont( pointSize<10>( f ) ); - p.setPen( noteModeColor() ); - p.drawText( QRect( 0, key_line_y, - WHITE_KEY_WIDTH, noteEditBottom() - key_line_y ), - Qt::AlignCenter | Qt::TextWordWrap, - m_nemStr.at( m_noteEditMode ) + ":" ); - - // set clipping area, because we are not allowed to paint over - // keyboard... - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN ); - - // draw the grid - if( hasValidPattern() ) - { - int q, x, tick; - - if( m_zoomingModel.value() > 3 ) + if (key % KeysPerOctave == Key_C) { p.setPen(m_beatLineColor); } + else { p.setPen(m_lineColor); } + p.drawLine(m_whiteKeyWidth, y, width(), y); + }; + // correct y offset of the top key + switch (prKeyOrder[topNote]) { - // If we're over 100% zoom, we allow all quantization level grids - q = quantization(); - } - else if( quantization() % 3 != 0 ) - { - // If we're under 100% zoom, we allow quantization grid up to 1/24 for triplets - // to ensure a dense doesn't fill out the background - q = quantization() < 8 ? 8 : quantization(); - } - else { - // If we're under 100% zoom, we allow quantization grid up to 1/32 for normal notes - q = quantization() < 6 ? 6 : quantization(); - } - - // First we draw the vertical quantization lines - for( tick = m_currentPosition - m_currentPosition % q, x = xCoordOfTick( tick ); - x <= width(); tick += q, x = xCoordOfTick( tick ) ) - { - p.setPen( lineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + case PR_WHITE_KEY_SMALL: + case PR_WHITE_KEY_BIG: + break; + case PR_BLACK_KEY: + // draw extra white key + drawKey(topKey + 1, grid_line_y - m_keyLineHeight); } - - // Draw horizontal lines - key = m_startKey; - for( int y = key_line_y - 1; y > PR_TOP_MARGIN; - y -= m_keyLineHeight ) + // loop through visible keys + const int lastKey = qMax(0, topKey - m_pianoKeysVisible); + for (int key = topKey; key > lastKey; --key) { - if( static_cast( key % KeysPerOctave ) == Key_C ) + bool whiteKey = Piano::isWhiteKey(key); + if (whiteKey) { - // C note gets accented - p.setPen( beatLineColor() ); + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + grid_line_y += m_keyLineHeight; } else { - p.setPen( lineColor() ); + // draw next white key + drawKey(key - 1, grid_line_y + m_keyLineHeight); + drawHorizontalLine(key - 1, grid_line_y + m_keyLineHeight); + // draw black key over previous and next white key + drawKey(key, grid_line_y); + drawHorizontalLine(key, grid_line_y); + // drew two grid keys so skip ahead properly + grid_line_y += m_keyLineHeight + m_keyLineHeight; + // capture double key draw + --key; } - p.drawLine( WHITE_KEY_WIDTH, y, width(), y ); - ++key; } + // don't draw over keys + p.setClipRect(m_whiteKeyWidth, keyAreaTop(), width(), noteEditBottom() - keyAreaTop()); - // Draw alternating shades on bars - float timeSignature = static_cast( Engine::getSong()->getTimeSigModel().getNumerator() ) - / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); + // draw alternating shading on bars + float timeSignature = + static_cast(Engine::getSong()->getTimeSigModel().getNumerator()) / + static_cast(Engine::getSong()->getTimeSigModel().getDenominator()); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; //the bars which disappears at the left side by scrolling int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); - //iterates the visible bars and draw the shading on uneven bars - for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) + for (int x = m_whiteKeyWidth, barCount = leftBars; + x < width() + m_currentPosition * zoomFactor / timeSignature; + x += m_ppb, ++barCount) { - if( ( barCount + leftBars ) % 2 != 0 ) + if ((barCount + leftBars) % 2 != 0) { - p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppb, - height() - ( PR_BOTTOM_MARGIN + PR_TOP_MARGIN ), backgroundShade() ); + p.fillRect(x - m_currentPosition * zoomFactor / timeSignature, + PR_TOP_MARGIN, + m_ppb, + height() - (PR_BOTTOM_MARGIN + PR_TOP_MARGIN), + m_backgroundShade); } } - // Draw the vertical beat lines + // draw vertical beat lines int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); - - for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, - x = xCoordOfTick( tick ); x <= width(); - tick += ticksPerBeat, x = xCoordOfTick( tick ) ) + p.setPen(m_beatLineColor); + for(tick = m_currentPosition - m_currentPosition % ticksPerBeat, + x = xCoordOfTick( tick ); + x <= width(); + tick += ticksPerBeat, x = xCoordOfTick(tick)) { - p.setPen( beatLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } - // Draw the vertical bar lines - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), - x = xCoordOfTick( tick ); x <= width(); - tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) + // draw vertical bar lines + p.setPen(m_barLineColor); + for(tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), + x = xCoordOfTick( tick ); + x <= width(); + tick += MidiTime::ticksPerBar(), x = xCoordOfTick(tick)) { - p.setPen( barLineColor() ); - p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); + p.drawLine(x, PR_TOP_MARGIN, x, noteEditBottom()); } // draw marked semitones after the grid - for( int i = 0; i < m_markedSemiTones.size(); i++ ) + for(x = 0; x < m_markedSemiTones.size(); ++x) { - const int key_num = m_markedSemiTones.at( i ); - const int y = key_line_y + 5 - - m_keyLineHeight * ( key_num - m_startKey + 1 ); - - if( y > key_line_y ) - { - break; - } - - p.fillRect( WHITE_KEY_WIDTH + 1, y - m_keyLineHeight / 2, width() - 10, m_keyLineHeight + 1, - markedSemitoneColor() ); + const int key_num = m_markedSemiTones.at(x); + const int y = keyAreaBottom() + 5 - m_keyLineHeight * + (key_num - m_startKey + 1); + if(y > keyAreaBottom()) { break; } + p.fillRect(m_whiteKeyWidth + 1, + y - m_keyLineHeight / 2, + width() - 10, + m_keyLineHeight + 1, + m_markedSemitoneColor); } } + // reset clip + p.setClipRect(0, 0, width(), height()); + + // erase the area below the piano, because there might be keys that + // should be only half-visible + p.fillRect( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), bgColor); + + // display note editing info + //QFont f = p.font(); + f.setBold( false ); + p.setFont( pointSize<10>( f ) ); + p.setPen(m_noteModeColor); + p.drawText( QRect( 0, keyAreaBottom(), + m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), + Qt::AlignCenter | Qt::TextWordWrap, + m_nemStr.at( m_noteEditMode ) + ":" ); + + // set clipping area, because we are not allowed to paint over + // keyboard... + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN); + // following code draws all notes in visible area // and the note editing stuff (volume, panning, etc) @@ -3159,15 +2999,17 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) qSwap( sel_key_start, sel_key_end ); } - int y_base = key_line_y - 1; + int y_base = keyAreaBottom() - 1; if( hasValidPattern() ) { - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN); - const int visible_keys = ( key_line_y-keyAreaTop() ) / - m_keyLineHeight + 2; + const int topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); + const int bottomKey = topKey - m_pianoKeysVisible; QPolygonF editHandles; @@ -3194,21 +3036,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(), - ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_ghostNoteColor, m_ghostNoteTextColor, m_selectedNoteColor, + m_ghostNoteOpacity, m_ghostNoteBorders, drawNoteNames); } } @@ -3236,31 +3077,30 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { - // we've done and checked all, let's draw the - // note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, noteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + // we've done and checked all, let's draw the note + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_noteColor, m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } // draw note editing stuff int editHandleTop = 0; if( m_noteEditMode == NoteEditVolume ) { - QColor color = barColor().lighter( 30 + ( note->getVolume() * 90 / MaxVolume ) ); + QColor color = m_barColor.lighter(30 + (note->getVolume() * 90 / MaxVolume)); if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3275,10 +3115,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } else if( m_noteEditMode == NoteEditPanning ) { - QColor color = noteColor(); + QColor color = m_noteColor; if( note->selected() ) { - color = selectedNoteColor(); + color = m_selectedNoteColor; } p.setPen( QPen( color, NOTE_EDIT_LINE_WIDTH ) ); @@ -3297,11 +3137,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if( note->hasDetuningInfo() ) { - drawDetuningInfo( p, note, - x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight ); - p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, + drawDetuningInfo(p, note, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, height() - PR_TOP_MARGIN); } } @@ -3324,24 +3164,24 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int x = ( pos_ticks - m_currentPosition ) * m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all - if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) + if (!(x + note_width >= 0 && x <= width() - m_whiteKeyWidth)) { continue; } // is the note in visible area? - if( key > 0 && key <= visible_keys ) + if (note->key() > bottomKey && note->key() <= topKey) { // we've done and checked all, let's draw the note - drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * m_keyLineHeight, - note_width, note, m_stepRecorder.curStepNoteColor(), noteTextColor(), selectedNoteColor(), - noteOpacity(), noteBorders(), drawNoteNames ); + drawNoteRect( + p, x + m_whiteKeyWidth, y_base - key * m_keyLineHeight, note_width, + note, m_stepRecorder.curStepNoteColor(), m_noteTextColor, m_selectedNoteColor, + m_noteOpacity, m_noteBorders, drawNoteNames); } } - p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) ); + p.setPen(QPen(m_noteColor, NOTE_EDIT_LINE_WIDTH + 2)); p.drawPoints( editHandles ); } @@ -3352,14 +3192,16 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setFont( pointSize<14>( f ) ); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::BrightText ) ); - p.drawText( WHITE_KEY_WIDTH + 20, PR_TOP_MARGIN + 40, + p.drawText(m_whiteKeyWidth + 20, PR_TOP_MARGIN + 40, tr( "Please open a pattern by double-clicking " "on it!" ) ); } - p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - - WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN - - m_notesEditHeight - PR_BOTTOM_MARGIN ); + p.setClipRect( + m_whiteKeyWidth, + PR_TOP_MARGIN, + width() - m_whiteKeyWidth, + height() - PR_TOP_MARGIN - m_notesEditHeight - PR_BOTTOM_MARGIN); // now draw selection-frame int x = ( ( sel_pos_start - m_currentPosition ) * m_ppb ) / @@ -3368,9 +3210,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) MidiTime::ticksPerBar() ) - x; int y = (int) y_base - sel_key_start * m_keyLineHeight; int h = (int) y_base - sel_key_end * m_keyLineHeight - y; - p.setPen( selectedNoteColor() ); + p.setPen(m_selectedNoteColor); p.setBrush( Qt::NoBrush ); - p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); + p.drawRect(x + m_whiteKeyWidth, y, w, h); // TODO: Get this out of paint event int l = ( hasValidPattern() )? (int) m_pattern->length() : 0; @@ -3383,70 +3225,92 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // set line colors - QColor editAreaCol = QColor( lineColor() ); - QColor currentKeyCol = QColor( beatLineColor() ); + QColor editAreaCol = QColor(m_lineColor); + QColor currentKeyCol = QColor(m_beatLineColor); editAreaCol.setAlpha( 64 ); currentKeyCol.setAlpha( 64 ); // horizontal line for the key under the cursor - if( hasValidPattern() ) + if(hasValidPattern() && gui->pianoRoll()->hasFocus()) { int key_num = getKey( mapFromGlobal( QCursor::pos() ).y() ); - p.fillRect( 10, key_line_y + 3 - m_keyLineHeight * + p.fillRect( 10, keyAreaBottom() + 3 - m_keyLineHeight * ( key_num - m_startKey + 1 ), width() - 10, m_keyLineHeight - 7, currentKeyCol ); } // bar to resize note edit area p.setClipRect( 0, 0, width(), height() ); - p.fillRect( QRect( 0, key_line_y, + p.fillRect( QRect( 0, keyAreaBottom(), width()-PR_RIGHT_MARGIN, NOTE_EDIT_RESIZE_BAR ), editAreaCol ); - const QPixmap * cursor = NULL; - // draw current edit-mode-icon below the cursor - switch( m_editMode ) - { - case ModeDraw: - if( m_mouseDownRight ) - { - cursor = s_toolErase; - } - else if( m_action == ActionMoveNote ) - { - cursor = s_toolMove; - } - else - { - cursor = s_toolDraw; - } - break; - case ModeErase: cursor = s_toolErase; break; - case ModeSelect: cursor = s_toolSelect; break; - case ModeEditDetuning: cursor = s_toolOpen; break; - } - QPoint mousePosition = mapFromGlobal( QCursor::pos() ); - if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + if (gui->pianoRoll()->hasFocus()) { - p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + const QPixmap * cursor = NULL; + // draw current edit-mode-icon below the cursor + switch( m_editMode ) + { + case ModeDraw: + if( m_mouseDownRight ) + { + cursor = s_toolErase; + } + else if( m_action == ActionMoveNote ) + { + cursor = s_toolMove; + } + else + { + cursor = s_toolDraw; + } + break; + case ModeErase: cursor = s_toolErase; break; + case ModeSelect: cursor = s_toolSelect; break; + case ModeEditDetuning: cursor = s_toolOpen; break; + } + QPoint mousePosition = mapFromGlobal( QCursor::pos() ); + if( cursor != NULL && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) + { + p.drawPixmap( mousePosition + QPoint( 8, 8 ), *cursor ); + } } } +void PianoRoll::updateScrollbars() +{ + m_leftRightScroll->setGeometry( + m_whiteKeyWidth, + height() - SCROLLBAR_SIZE, + width() - m_whiteKeyWidth, + SCROLLBAR_SIZE + ); + m_topBottomScroll->setGeometry( + width() - SCROLLBAR_SIZE, + PR_TOP_MARGIN, + SCROLLBAR_SIZE, + height() - PR_TOP_MARGIN - SCROLLBAR_SIZE + ); + int pianoAreaHeight = keyAreaBottom() - PR_TOP_MARGIN; + int numKeysVisible = pianoAreaHeight / m_keyLineHeight; + m_totalKeysToScroll = qMax(0, NumKeys - numKeysVisible); + m_topBottomScroll->setRange(0, m_totalKeysToScroll); + if (m_startKey > m_totalKeysToScroll) + { + m_startKey = qMax(0, m_totalKeysToScroll); + } + m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); +} + // responsible for moving/resizing scrollbars after window-resizing void PianoRoll::resizeEvent(QResizeEvent * re) { - m_leftRightScroll->setGeometry( WHITE_KEY_WIDTH, - height() - - SCROLLBAR_SIZE, - width()-WHITE_KEY_WIDTH, - SCROLLBAR_SIZE ); - updateYScroll(); - - Engine::getSong()->getPlayPos( Song::Mode_PlayPattern - ).m_timeLine->setFixedWidth( width() ); - + updatePositionLineHeight(); + updateScrollbars(); + Engine::getSong()->getPlayPos(Song::Mode_PlayPattern) + .m_timeLine->setFixedWidth(width()); update(); } @@ -3463,7 +3327,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if (!hasValidPattern()) {return;} // get values for going through notes int pixel_range = 8; - int x = we->x() - WHITE_KEY_WIDTH; + int x = we->x() - m_whiteKeyWidth; int ticks_start = ( x - pixel_range / 2 ) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * @@ -3568,7 +3432,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - WHITE_KEY_WIDTH)* MidiTime::ticksPerBar(); + int x = (we->x() - m_whiteKeyWidth)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x @@ -3619,24 +3483,16 @@ void PianoRoll::focusInEvent( QFocusEvent * ) -int PianoRoll::getKey(int y ) const +int PianoRoll::getKey(int y) const { - int key_line_y = keyAreaBottom() - 1; - // pressed key on piano - int key_num = ( key_line_y - y ) / m_keyLineHeight; - key_num += m_startKey; - - // some range-checking-stuff - if( key_num < 0 ) - { - key_num = 0; - } - - if( key_num >= KeysPerOctave * NumOctaves ) - { - key_num = KeysPerOctave * NumOctaves - 1; - } - + // handle case that very top pixel maps to next key above + if (y - keyAreaTop() <= 1) { y = keyAreaTop() + 2; } + int key_num = qBound( + 0, + // add + 1 to stay within the grid lines + ((keyAreaBottom() - y + 1) / m_keyLineHeight) + m_startKey, + NumKeys - 1 + ); return key_num; } @@ -3854,7 +3710,7 @@ void PianoRoll::horScrolled(int new_pos ) void PianoRoll::verScrolled( int new_pos ) { // revert value - m_startKey = m_totalKeysToScroll - new_pos; + m_startKey = qMax(0, m_totalKeysToScroll - new_pos); update(); } @@ -4008,13 +3864,13 @@ void PianoRoll::updateYScroll() int total_pixels = m_octaveHeight * NumOctaves - (height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN - m_notesEditHeight); - m_totalKeysToScroll = total_pixels * KeysPerOctave / m_octaveHeight; + m_totalKeysToScroll = qMax(0, total_pixels * KeysPerOctave / m_octaveHeight); m_topBottomScroll->setRange(0, m_totalKeysToScroll); if(m_startKey > m_totalKeysToScroll) { - m_startKey = m_totalKeysToScroll; + m_startKey = qMax(0, m_totalKeysToScroll); } m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); } @@ -4161,7 +4017,7 @@ bool PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const MidiTime & t ) { - const int w = width() - WHITE_KEY_WIDTH; + const int w = width() - m_whiteKeyWidth; if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) { m_leftRightScroll->setValue( t.getBar() * MidiTime::ticksPerBar() ); @@ -4187,6 +4043,22 @@ void PianoRoll::updatePosition( const MidiTime & t ) { autoScroll( t ); } + const int pos = m_timeLine->pos() * m_ppb / MidiTime::ticksPerBar(); + if (pos >= m_currentPosition && pos <= m_currentPosition + width() - m_whiteKeyWidth) + { + m_positionLine->show(); + m_positionLine->move(pos - (m_positionLine->width() - 1) - m_currentPosition + m_whiteKeyWidth, keyAreaTop()); + } + else + { + m_positionLine->hide(); + } +} + + +void PianoRoll::updatePositionLineHeight() +{ + m_positionLine->setFixedHeight(keyAreaBottom() - keyAreaTop()); } @@ -4230,6 +4102,7 @@ void PianoRoll::zoomingChanged() m_timeLine->setPixelsPerBar( m_ppb ); m_stepRecorderWidget.setPixelsPerBar( m_ppb ); + m_positionLine->zoomChange(m_zoomLevels[m_zoomingModel.value()]); update(); } @@ -4239,9 +4112,9 @@ void PianoRoll::zoomingYChanged() { m_keyLineHeight = m_zoomYLevels[m_zoomingYModel.value()] * DEFAULT_KEY_LINE_HEIGHT; m_octaveHeight = m_keyLineHeight * KeysPerOctave; - m_whiteKeySmallHeight = round(m_keyLineHeight * 1.5); + m_whiteKeySmallHeight = qFloor(m_keyLineHeight * 1.5); m_whiteKeyBigHeight = m_keyLineHeight * 2; - m_blackKeyHeight = round(m_keyLineHeight * 1.3333); + m_blackKeyHeight = m_keyLineHeight; //round(m_keyLineHeight * 1.3333); updateYScroll(); update(); @@ -4360,7 +4233,7 @@ Note * PianoRoll::noteUnderMouse() { QPoint pos = mapFromGlobal( QCursor::pos() ); - if( pos.x() <= WHITE_KEY_WIDTH + if (pos.x() <= m_whiteKeyWidth || pos.x() > width() - SCROLLBAR_SIZE || pos.y() < PR_TOP_MARGIN || pos.y() > keyAreaBottom() ) @@ -4369,7 +4242,7 @@ Note * PianoRoll::noteUnderMouse() } int key_num = getKey( pos.y() ); - int pos_ticks = ( pos.x() - WHITE_KEY_WIDTH ) * + int pos_ticks = (pos.x() - m_whiteKeyWidth) * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // loop through whole note-vector... @@ -4721,6 +4594,14 @@ void PianoRollWindow::loadSettings( const QDomElement & de ) m_editor->loadMarkedSemiTones(de.firstChildElement("markedSemiTones")); MainWindow::restoreWidgetState( this, de ); + + // update margins here because we're later in the startup process + // We can't earlier because everything is still starting with the + // WHITE_KEY_WIDTH default + QMargins qm = m_editor->m_stepRecorderWidget.margins(); + qm.setLeft(m_editor->m_whiteKeyWidth); + m_editor->m_stepRecorderWidget.setMargins(qm); + m_editor->m_timeLine->setXOffset(m_editor->m_whiteKeyWidth); } @@ -4733,6 +4614,13 @@ QSize PianoRollWindow::sizeHint() const +bool PianoRollWindow::hasFocus() const +{ + return m_editor->hasFocus(); +} + + + void PianoRollWindow::updateAfterPatternChange() { patternRenamed(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index b6647d44a3b..d60b986d898 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -52,86 +52,6 @@ #include "PianoRoll.h" #include "Track.h" -positionLine::positionLine( QWidget* parent ) : - QWidget( parent ), - m_hasTailGradient ( false ), - m_lineColor (0, 0, 0, 0) -{ - resize( 8, height() ); - - setAttribute( Qt::WA_NoSystemBackground, true ); - setAttribute( Qt::WA_TransparentForMouseEvents ); -} - -void positionLine::paintEvent( QPaintEvent* pe ) -{ - QPainter p( this ); - - // If width is 1, we don't need a gradient - if (width() == 1) - { - p.fillRect( rect(), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - } - - // If width > 1, we need the gradient - else - { - // Create the gradient trail behind the line - QLinearGradient gradient( rect().bottomLeft(), rect().bottomRight() ); - - // If gradient is enabled, we're in focus and we're playing, enable gradient - if (Engine::getSong()->isPlaying() && m_hasTailGradient && - Engine::getSong()->playMode() == Song::Mode_PlaySong) - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 60) ); - } - else - { - gradient.setColorAt(( ( width() - 1.0 )/width() ), - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - } - - // Fill in the remaining parts - gradient.setColorAt(0, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 0) ); - gradient.setColorAt(1, - QColor( m_lineColor.red(), m_lineColor.green(), m_lineColor.blue(), 153) ); - - // Fill line - p.fillRect( rect(), gradient ); - } -} - -// QProperty handles -bool positionLine::hasTailGradient() const -{ return m_hasTailGradient; } - -void positionLine::setHasTailGradient( const bool g ) -{ m_hasTailGradient = g; } - -QColor positionLine::lineColor() const -{ return m_lineColor; } - -void positionLine::setLineColor( const QColor & c ) -{ m_lineColor = c; } - -// NOTE: the move() implementation fixes a bug where the position line would appear -// in an unexpected location when positioned at the start of the track -void positionLine::zoomChange( double zoom ) -{ - int playHeadPos = x() + width() - 1; - - resize( 8.0 * zoom, height() ); - move( playHeadPos - width() + 1, y() ); - - update(); -} - - - - const QVector SongEditor::m_zoomLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; @@ -172,7 +92,7 @@ SongEditor::SongEditor( Song * song ) : connect( m_timeLine, SIGNAL( selectionFinished() ), this, SLOT( stopRubberBand() ) ); - m_positionLine = new positionLine( this ); + m_positionLine = new PositionLine(this); static_cast( layout() )->insertWidget( 1, m_timeLine ); connect( m_song, SIGNAL( playbackStateChanged() ), diff --git a/src/gui/widgets/PositionLine.cpp b/src/gui/widgets/PositionLine.cpp new file mode 100644 index 00000000000..ef003c5ebad --- /dev/null +++ b/src/gui/widgets/PositionLine.cpp @@ -0,0 +1,98 @@ +/* + * PositionLine.cpp + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "PositionLine.h" + +#include + +#include "GuiApplication.h" +#include "Song.h" + + +PositionLine::PositionLine(QWidget* parent) : + QWidget(parent), + m_hasTailGradient(false), + m_lineColor(0, 0, 0, 0) +{ + resize(8, height()); + + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void PositionLine::paintEvent(QPaintEvent* pe) +{ + QPainter p(this); + QColor c = QColor(m_lineColor); + + // If width is 1, we don't need a gradient + if (width() == 1) + { + c.setAlpha(153); + p.fillRect(rect(), c); + } + // If width > 1, we need the gradient + else + { + // Create the gradient trail behind the line + QLinearGradient gradient(rect().bottomLeft(), rect().bottomRight()); + qreal w = (width() - 1.0) / width(); + + // If gradient is enabled, we're in focus and we're playing, enable gradient + if (m_hasTailGradient && + Engine::getSong()->isPlaying() && + (Engine::getSong()->playMode() == Song::Mode_PlaySong || + Engine::getSong()->playMode() == Song::Mode_PlayPattern)) + { + c.setAlpha(60); + gradient.setColorAt(w, c); + } + else + { + c.setAlpha(0); + gradient.setColorAt(w, c); + } + + // Fill in the remaining parts + c.setAlpha(0); + gradient.setColorAt(0, c); + c.setAlpha(153); + gradient.setColorAt(1, c); + + // Fill line + p.fillRect(rect(), gradient); + } +} + +// NOTE: the move() implementation fixes a bug where the position line would appear +// in an unexpected location when positioned at the start of the track +void PositionLine::zoomChange(double zoom) +{ + int playHeadPos = x() + width() - 1; + + resize(8.0 * zoom, height()); + move(playHeadPos - width() + 1, y()); + + update(); +} \ No newline at end of file diff --git a/src/gui/widgets/StepRecorderWidget.cpp b/src/gui/widgets/StepRecorderWidget.cpp index a546c2a2cdc..d2157ef66bc 100644 --- a/src/gui/widgets/StepRecorderWidget.cpp +++ b/src/gui/widgets/StepRecorderWidget.cpp @@ -58,6 +58,19 @@ void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition) m_currentPosition = currentPosition; } +void StepRecorderWidget::setMargins(const QMargins &qm) +{ + m_left = qm.left(); + m_right = qm.right(); + m_top = qm.top(); + m_bottom = qm.bottom(); +} + +QMargins StepRecorderWidget::margins() +{ + return QMargins(m_left, m_top, m_right, m_bottom); +} + void StepRecorderWidget::setBottomMargin(const int marginBottom) { m_marginBottom = marginBottom;