From 9aa997e5740fb6f5442ee2de5b2789530836a142 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 16:38:16 +0100 Subject: [PATCH 01/35] Allow TabWidget to be variable sized --- include/TabWidget.h | 8 +++++-- src/gui/widgets/TabWidget.cpp | 43 ++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/include/TabWidget.h b/include/TabWidget.h index dacd2648b9c..11e4da40a02 100644 --- a/include/TabWidget.h +++ b/include/TabWidget.h @@ -36,7 +36,10 @@ class TabWidget : public QWidget { Q_OBJECT public: - TabWidget( const QString & _caption, QWidget * _parent, bool usePixmap = false ); + //! @param resizable If true, the widget resizes to fit the size of all tabs + //! If false, all child widget will be cut down to the TabWidget's size + TabWidget( const QString & _caption, QWidget * _parent, + bool usePixmap = false, bool resizable = false ); virtual ~TabWidget() = default; void addTab( QWidget * w, const QString & name, const char *pixmap = NULL, int idx = -1 ); @@ -74,7 +77,7 @@ class TabWidget : public QWidget virtual void paintEvent( QPaintEvent * _pe ); virtual void resizeEvent( QResizeEvent * _re ); virtual void wheelEvent( QWheelEvent * _we ); - + virtual QSize minimumSizeHint() const; private: struct widgetDesc @@ -88,6 +91,7 @@ class TabWidget : public QWidget widgetStack m_widgets; + bool m_resizable; int m_activeTab; QString m_caption; // Tab caption, used as the tooltip text on icon tabs quint8 m_tabbarHeight; // The height of the tab bar diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index f06710098e3..49898a3c725 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -34,8 +34,10 @@ #include "gui_templates.h" #include "embed.h" -TabWidget::TabWidget( const QString & caption, QWidget * parent, bool usePixmap ) : +TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap, + bool resizable) : QWidget( parent ), + m_resizable( resizable ), m_activeTab( 0 ), m_caption( caption ), m_usePixmap( usePixmap ), @@ -81,7 +83,9 @@ void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, i m_widgets[idx] = d; // Position tab's window - w->setFixedSize( width() - 4, height() - m_tabbarHeight ); + if(!m_resizable) { + w->setFixedSize( width() - 4, height() - m_tabbarHeight ); + } w->move( 2, m_tabbarHeight - 1 ); w->hide(); @@ -189,17 +193,19 @@ void TabWidget::mousePressEvent( QMouseEvent * me ) void TabWidget::resizeEvent( QResizeEvent * ) { - for( widgetStack::iterator it = m_widgets.begin(); - it != m_widgets.end(); ++it ) + if(!m_resizable) { - ( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight ); + for( widgetStack::iterator it = m_widgets.begin(); + it != m_widgets.end(); ++it ) + { + ( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight ); + } } } - void TabWidget::paintEvent( QPaintEvent * pe ) { QPainter p( this ); @@ -300,6 +306,31 @@ void TabWidget::wheelEvent( QWheelEvent * we ) setActiveTab( tab ); } + + + +// Let parent widgets know how much space this tab widget needs +QSize TabWidget::minimumSizeHint() const +{ + if(m_resizable) + { + int maxWidth = 0, maxHeight = 0; + for( widgetStack::const_iterator it = m_widgets.begin(); + it != m_widgets.end(); ++it ) + { + maxWidth = std::max(maxWidth, it->w->width()); + maxHeight = std::max(maxHeight, it->w->height()); + } + return QSize(maxWidth + 4, maxHeight + m_tabbarHeight); + } + else { + return QWidget::minimumSizeHint(); + } +} + + + + // Return the color to be used to draw a TabWidget's title text (if any) QColor TabWidget::tabTitleText() const { From dd43127514a423cc76ba0a5135c355f3af14220c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 19:46:21 +0100 Subject: [PATCH 02/35] Allow instrument views to grow with contents Make InstrumentTrackWindow as large as the InstrumentView requires --- src/gui/InstrumentView.cpp | 1 - src/tracks/InstrumentTrack.cpp | 16 +++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/gui/InstrumentView.cpp b/src/gui/InstrumentView.cpp index 9e8fc58c132..d2a7483e47c 100644 --- a/src/gui/InstrumentView.cpp +++ b/src/gui/InstrumentView.cpp @@ -34,7 +34,6 @@ InstrumentView::InstrumentView( Instrument * _Instrument, QWidget * _parent ) : PluginView( _Instrument, _parent ) { setModel( _Instrument ); - setFixedSize( 250, 250 ); setAttribute( Qt::WA_DeleteOnClose, true ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 298430b030e..6f4ad2b8e3f 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1326,7 +1326,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : this, SLOT( textChanged( const QString & ) ) ); m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); - nameAndChangeTrackLayout->addWidget(m_nameLineEdit); + nameAndChangeTrackLayout->addWidget(m_nameLineEdit, 1); // set up left/right arrows for changing instrument @@ -1438,8 +1438,8 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : generalSettingsLayout->addLayout( basicControlsLayout ); - m_tabWidget = new TabWidget( "", this, true ); - m_tabWidget->setFixedHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 ); + m_tabWidget = new TabWidget( "", this, true, true ); + m_tabWidget->setMinimumHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 ); // create tab-widgets @@ -1474,18 +1474,16 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // setup piano-widget m_pianoView = new PianoView( this ); - m_pianoView->setFixedSize( INSTRUMENT_WIDTH, PIANO_HEIGHT ); + m_pianoView->setMinimumHeight( PIANO_HEIGHT ); + m_pianoView->setMaximumHeight( PIANO_HEIGHT ); vlayout->addWidget( generalSettingsWidget ); - vlayout->addWidget( m_tabWidget ); + vlayout->addWidget( m_tabWidget, 1 ); vlayout->addWidget( m_pianoView ); - - setModel( _itv->model() ); updateInstrumentView(); - setFixedWidth( INSTRUMENT_WIDTH ); resize( sizeHint() ); QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); @@ -1501,7 +1499,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : systemMenu->actions().at( 4 )->setVisible( false ); // Maximize subWin->setWindowIcon( embed::getIconPixmap( "instrument_track" ) ); - subWin->setFixedSize( subWin->size() ); + subWin->setMinimumSize( subWin->size() ); subWin->hide(); } From 5101aa021440c9e6c183fd197af09154df5d619a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 20:19:27 +0100 Subject: [PATCH 03/35] Make Model class visitable --- include/AutomatableModel.h | 45 ++++++++++++++++++++++++++++++++ include/ComboBoxModel.h | 1 + include/ModelVisitor.h | 53 ++++++++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/ModelVisitor.cpp | 28 ++++++++++++++++++++ 5 files changed, 128 insertions(+) create mode 100644 include/ModelVisitor.h create mode 100644 src/core/ModelVisitor.cpp diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 78f4882b423..f5215676e3b 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -33,6 +33,7 @@ #include "MidiTime.h" #include "ValueBuffer.h" #include "MemoryManager.h" +#include "ModelVisitor.h" // simple way to map a property of a view to a model #define mapPropertyFromModelPtr(type,getfunc,setfunc,modelname) \ @@ -68,6 +69,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject Q_OBJECT MM_OPERATORS public: + typedef QVector AutoModelVector; enum ScaleType @@ -80,6 +82,32 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject virtual ~AutomatableModel(); + // Implement those by using the MODEL_IS_VISITABLE macro + virtual void accept(ModelVisitor& v) = 0; + virtual void accept(ConstModelVisitor& v) const = 0; + // use this to make subclasses visitable +#define MODEL_IS_VISITABLE \ + void accept(ModelVisitor& v) override { v.visit(*this); } \ + void accept(ConstModelVisitor& v) const override { v.visit(*this); } + +public: + //! Return this class casted to Target, or nullptr if impossible + template + Target* dcast(bool doThrow = false) + { + DCastVisitor vis; accept(vis); + if(doThrow && !vis.result) Q_ASSERT(false); + return vis.result; + } + + //! Return this class casted to const Target, or nullptr if impossible + template + const Target* dcast(bool doThrow = false) const + { + ConstDCastVisitor vis; accept(vis); + if(doThrow && !vis.result) Q_ASSERT(false); + return vis.result; + } bool isAutomated() const; bool isAutomatedOrControlled() const @@ -283,6 +311,20 @@ public slots: private: + template + struct DCastVisitor : public ModelVisitor + { + Target* result = nullptr; + void visit(Target& tar) { result = &tar; } + }; + + template + struct ConstDCastVisitor : public ConstModelVisitor + { + const Target* result = nullptr; + void visit(const Target& tar) { result = &tar; } + }; + static bool mustQuoteName(const QString &name); virtual void saveSettings( QDomDocument& doc, QDomElement& element ) @@ -382,6 +424,7 @@ template class LMMS_EXPORT TypedAutomatableModel : public Automatab class LMMS_EXPORT FloatModel : public TypedAutomatableModel { Q_OBJECT + MODEL_IS_VISITABLE public: FloatModel( float val = 0, float min = 0, float max = 0, float step = 0, Model * parent = NULL, @@ -399,6 +442,7 @@ class LMMS_EXPORT FloatModel : public TypedAutomatableModel class LMMS_EXPORT IntModel : public TypedAutomatableModel { Q_OBJECT + MODEL_IS_VISITABLE public: IntModel( int val = 0, int min = 0, int max = 0, Model* parent = NULL, @@ -414,6 +458,7 @@ class LMMS_EXPORT IntModel : public TypedAutomatableModel class LMMS_EXPORT BoolModel : public TypedAutomatableModel { Q_OBJECT + MODEL_IS_VISITABLE public: BoolModel( const bool val = false, Model* parent = NULL, diff --git a/include/ComboBoxModel.h b/include/ComboBoxModel.h index ad3603759ad..82c01e69e94 100644 --- a/include/ComboBoxModel.h +++ b/include/ComboBoxModel.h @@ -36,6 +36,7 @@ class LMMS_EXPORT ComboBoxModel : public IntModel { Q_OBJECT + MODEL_IS_VISITABLE public: ComboBoxModel( Model* parent = NULL, const QString& displayName = QString(), diff --git a/include/ModelVisitor.h b/include/ModelVisitor.h new file mode 100644 index 00000000000..59d1df0c62a --- /dev/null +++ b/include/ModelVisitor.h @@ -0,0 +1,53 @@ +/* + * ModelVisitor.h - visitors for automatable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 MODELVISITOR_H +#define MODELVISITOR_H + +class BoolModel; +class IntModel; +class FloatModel; +class ComboBoxModel; + +class ModelVisitor +{ +public: + virtual void visit(BoolModel& ) {} + virtual void visit(IntModel& ) {} + virtual void visit(FloatModel& ) {} + virtual void visit(ComboBoxModel& ) {} + virtual ~ModelVisitor(); +}; + +class ConstModelVisitor +{ +public: + virtual void visit(const BoolModel& ) {} + virtual void visit(const IntModel& ) {} + virtual void visit(const FloatModel& ) {} + virtual void visit(const ComboBoxModel& ) {} + virtual ~ConstModelVisitor(); +}; + +#endif // MODELVISITOR_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 85a00780b10..b573b93b0bd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -40,6 +40,7 @@ set(LMMS_SRCS core/MixerWorkerThread.cpp core/MixHelpers.cpp core/Model.cpp + core/ModelVisitor.cpp core/Note.cpp core/NotePlayHandle.cpp core/Oscillator.cpp diff --git a/src/core/ModelVisitor.cpp b/src/core/ModelVisitor.cpp new file mode 100644 index 00000000000..11a8fc1b11a --- /dev/null +++ b/src/core/ModelVisitor.cpp @@ -0,0 +1,28 @@ +/* + * ModelVisitor.cpp - visitors for automatable models + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "ModelVisitor.h" + +ModelVisitor::~ModelVisitor() {} +ConstModelVisitor::~ConstModelVisitor() {} From 7e7141f38816a0b7b293585858c69866c972d2f1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 17 Mar 2019 10:43:38 +0100 Subject: [PATCH 04/35] Fix too small instrument tabs Previously, they had been resized by the fixed size parent tab widget. We need to do this manually now. --- src/tracks/InstrumentTrack.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 6f4ad2b8e3f..fb9261ff739 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1472,6 +1472,17 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget->addTab( m_midiView, tr( "MIDI" ), "midi_tab", 4 ); m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 ); + //! adjust size of any child widget of the main tab + //! required to keep the old look when using a variable sized tab widget + auto adjustSize = [](QWidget *w) { + w->setMinimumSize(INSTRUMENT_WIDTH, INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT); + }; + adjustSize(m_ssView); + adjustSize(instrumentFunctions); + adjustSize(m_effectView); + adjustSize(m_midiView); + adjustSize(m_miscView); + // setup piano-widget m_pianoView = new PianoView( this ); m_pianoView->setMinimumHeight( PIANO_HEIGHT ); From d52c220a5cd40af37a0c167c1425f89edca991cf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 28 Mar 2019 18:57:19 +0100 Subject: [PATCH 05/35] Fix instrument window tab sizes - Fix the instrument window tabs minimum width and height formulae - Also set minimum height and width for instrument tab --- include/InstrumentTrack.h | 3 +++ src/tracks/InstrumentTrack.cpp | 26 ++++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index fb12e825a41..0627e9f4ec2 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -427,6 +427,9 @@ protected slots: private: virtual void modelChanged(); void viewInstrumentInDirection(int d); + //! adjust size of any child widget of the main tab + //! required to keep the old look when using a variable sized tab widget + void adjustTabSize(QWidget *w); InstrumentTrack * m_track; InstrumentTrackView * m_itv; diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index fb9261ff739..69924c8be44 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1471,17 +1471,11 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget->addTab( m_effectView, tr( "Effects" ), "fx_tab", 3 ); m_tabWidget->addTab( m_midiView, tr( "MIDI" ), "midi_tab", 4 ); m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 ); - - //! adjust size of any child widget of the main tab - //! required to keep the old look when using a variable sized tab widget - auto adjustSize = [](QWidget *w) { - w->setMinimumSize(INSTRUMENT_WIDTH, INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT); - }; - adjustSize(m_ssView); - adjustSize(instrumentFunctions); - adjustSize(m_effectView); - adjustSize(m_midiView); - adjustSize(m_miscView); + adjustTabSize(m_ssView); + adjustTabSize(instrumentFunctions); + adjustTabSize(m_effectView); + adjustTabSize(m_midiView); + adjustTabSize(m_miscView); // setup piano-widget m_pianoView = new PianoView( this ); @@ -1497,7 +1491,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : resize( sizeHint() ); - QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this ); + QMdiSubWindow* subWin = gui->mainWindow()->addWindowedWidget( this ); Qt::WindowFlags flags = subWin->windowFlags(); flags |= Qt::MSWindowsFixedSizeDialogHint; flags &= ~Qt::WindowMaximizeButtonHint; @@ -1659,6 +1653,8 @@ void InstrumentTrackWindow::updateInstrumentView() modelChanged(); // Get the instrument window to refresh m_track->dataChanged(); // Get the text on the trackButton to change + + adjustTabSize(m_instrumentView); } } @@ -1846,6 +1842,7 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d) // scroll the SongEditor/BB-editor to make sure the new trackview label is visible bringToFront->trackContainerView()->scrollToTrackView(bringToFront); } + Q_ASSERT(bringToFront); bringToFront->getInstrumentTrackWindow()->setFocus(); } @@ -1858,4 +1855,9 @@ void InstrumentTrackWindow::viewPrevInstrument() viewInstrumentInDirection(-1); } +void InstrumentTrackWindow::adjustTabSize(QWidget *w) +{ + w->setMinimumSize(INSTRUMENT_WIDTH - 4, INSTRUMENT_HEIGHT - 4); +} + #include "InstrumentTrack.moc" From 82e3ba75c62a9db174e8844893da5901b76e1838 Mon Sep 17 00:00:00 2001 From: Gingka Akiyama <33764485+GingkathFox@users.noreply.github.com> Date: Sat, 6 Apr 2019 09:19:32 -0700 Subject: [PATCH 06/35] [Equalizer] Bright analyzer colors, opacity increased (#4772) * [Equalizer] Bright analyzer colors, opacity incr Brightened spectrum analyzer colors and increased opacity a tad to make more visible * Fixed RGB Value * Update EqControlsDialog.cpp * Fixed color change * Changed colors again * Fixed colors, now brighter and bluer * Ok, its actually bright now lol --- plugins/Eq/EqControlsDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Eq/EqControlsDialog.cpp b/plugins/Eq/EqControlsDialog.cpp index 826b5e73faf..b43faa8a10a 100644 --- a/plugins/Eq/EqControlsDialog.cpp +++ b/plugins/Eq/EqControlsDialog.cpp @@ -55,10 +55,10 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : EqSpectrumView * inSpec = new EqSpectrumView( &controls->m_inFftBands, this ); inSpec->move( 26, 17 ); - inSpec->setColor( QColor( 54, 45, 142, 150 ) ); + inSpec->setColor( QColor( 77, 101, 242, 150 ) ); EqSpectrumView * outSpec = new EqSpectrumView( &controls->m_outFftBands, this ); - outSpec->setColor( QColor( 9, 166, 156, 150 ) ); + outSpec->setColor( QColor( 0, 255, 239, 150 ) ); outSpec->move( 26, 17 ); m_parameterWidget = new EqParameterWidget( this , controls ); From 91f9f1a890f529779931501d7f571de9cec54226 Mon Sep 17 00:00:00 2001 From: Noah Brecht Date: Sun, 14 Apr 2019 06:26:49 -0400 Subject: [PATCH 07/35] Don't try to connect to nonexistent controllers (#4939) Fixes crash on loading presets with controllers. --- src/core/ControllerConnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index 45e36e12fc0..4d43a4366c3 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -205,7 +205,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this ) else { m_controllerId = _this.attribute( "id", "-1" ).toInt(); - if( m_controllerId < 0 ) + if( m_controllerId < 0 || m_controllerId >= Engine::getSong()->controllers().size() ) { qWarning( "controller index invalid\n" ); m_controllerId = -1; From 5a56969af2dbc8729070729ecbd3d522c2344c5f Mon Sep 17 00:00:00 2001 From: Spekular Date: Mon, 15 Apr 2019 17:54:18 +0200 Subject: [PATCH 08/35] Allow sample track TCOs to resize smaller than one bar (#4933) Other changes: * Update TCO position more exact when a drag leaves a TCO and enters `TrackContentWidget` (required to detect that the cursor has really moved when leaving a TCO with length < 1 to the right) * Use exact length when samples are loaded, don't round it up * Reset size when reloading same file --- src/core/Track.cpp | 22 ++++++++++---------- src/tracks/SampleTrack.cpp | 41 ++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index cd1d23b1625..4cec5a4eebb 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -310,8 +310,8 @@ TrackContentObjectView::~TrackContentObjectView() /*! \brief Update a TrackContentObjectView * - * TCO's get drawn only when needed, - * and when a TCO is updated, + * TCO's get drawn only when needed, + * and when a TCO is updated, * it needs to be redrawn. * */ @@ -678,7 +678,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) } } } - else if( me->button() == Qt::LeftButton && + else if( me->button() == Qt::LeftButton && me->modifiers() & Qt::ControlModifier ) { // start drag-action @@ -1123,7 +1123,7 @@ void TrackContentWidget::updateBackground() // draw lines // vertical lines - pmp.setPen( QPen( gridColor(), 1 ) ); + pmp.setPen( QPen( gridColor(), 1 ) ); for( float x = 0; x < w * 2; x += ppt ) { pmp.drawLine( QLineF( x, 0.0, x, h ) ); @@ -1134,9 +1134,9 @@ void TrackContentWidget::updateBackground() { pmp.drawLine( QLineF( x, 0.0, x, h ) ); } - + // horizontal line - pmp.setPen( QPen( gridColor(), 1 ) ); + pmp.setPen( QPen( gridColor(), 1 ) ); pmp.drawLine( 0, h-1, w*2, h-1 ); pmp.end(); @@ -1319,7 +1319,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) */ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) { - MidiTime tcoPos = MidiTime( getPosition( dee->pos().x() ).getTact(), 0 ); + MidiTime tcoPos = getPosition( dee->pos().x() ); if( canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); @@ -1862,7 +1862,7 @@ void TrackOperationsWidget::updateMenu() toMenu->addAction( embed::getIconPixmap( "cancel", 16, 16 ), tr( "Remove this track" ), this, SLOT( removeTrack() ) ); - + if( ! m_trackView->trackContainerView()->fixedTCOs() ) { toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) ); @@ -2787,12 +2787,12 @@ void TrackView::mouseMoveEvent( QMouseEvent * me ) else if( m_action == MoveTrack ) { // look which track-widget the mouse-cursor is over - const int yPos = + const int yPos = m_trackContainerView->contentWidget()->mapFromGlobal( me->globalPos() ).y(); const TrackView * trackAtY = m_trackContainerView->trackViewAt( yPos ); -// debug code -// qDebug( "y position %d", yPos ); + // debug code + // qDebug( "y position %d", yPos ); // a track-widget not equal to ourself? if( trackAtY != NULL && trackAtY != this ) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index fd9a92a234e..0a5fabd1adf 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -106,7 +106,7 @@ SampleTCO::SampleTCO( Track * _track ) : SampleTCO::~SampleTCO() { SampleTrack * sampletrack = dynamic_cast( getTrack() ); - if( sampletrack) + if ( sampletrack ) { sampletrack->updateTcos(); } @@ -118,10 +118,7 @@ SampleTCO::~SampleTCO() void SampleTCO::changeLength( const MidiTime & _length ) { - float nom = Engine::getSong()->getTimeSigModel().getNumerator(); - float den = Engine::getSong()->getTimeSigModel().getDenominator(); - int ticksPerTact = DefaultTicksPerTact * ( nom / den ); - TrackContentObject::changeLength( qMax( static_cast( _length ), ticksPerTact ) ); + TrackContentObject::changeLength( qMax( static_cast( _length ), 1 ) ); } @@ -147,8 +144,19 @@ void SampleTCO::setSampleBuffer( SampleBuffer* sb ) void SampleTCO::setSampleFile( const QString & _sf ) { - m_sampleBuffer->setAudioFile( _sf ); - changeLength( (int) ( m_sampleBuffer->frames() / Engine::framesPerTick() ) ); + int length; + if ( _sf.isEmpty() ) + { //When creating an empty sample pattern make it a bar long + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + length = DefaultTicksPerTact * ( nom / den ); + } + else + { //Otherwise set it to the sample's length + m_sampleBuffer->setAudioFile( _sf ); + length = sampleLength(); + } + changeLength(length); emit sampleChanged(); emit playbackPositionChanged(); @@ -440,8 +448,15 @@ void SampleTCOView::mouseReleaseEvent(QMouseEvent *_me) void SampleTCOView::mouseDoubleClickEvent( QMouseEvent * ) { QString af = m_tco->m_sampleBuffer->openAudioFile(); - if( af != "" && af != m_tco->m_sampleBuffer->audioFile() ) - { + + if ( af.isEmpty() ) {} //Don't do anything if no file is loaded + else if ( af == m_tco->m_sampleBuffer->audioFile() ) + { //Instead of reloading the existing file, just reset the size + int length = (int) ( m_tco->m_sampleBuffer->frames() / Engine::framesPerTick() ); + m_tco->changeLength(length); + } + else + { //Otherwise load the new file as ususal m_tco->setSampleFile( af ); Engine::getSong()->setModified(); } @@ -462,7 +477,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) setNeedsUpdate( false ); - m_paintPixmap = m_paintPixmap.isNull() == true || m_paintPixmap.size() != size() + m_paintPixmap = m_paintPixmap.isNull() == true || m_paintPixmap.size() != size() ? QPixmap( size() ) : m_paintPixmap; QPainter p( &m_paintPixmap ); @@ -472,7 +487,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); // state: selected, muted, normal - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() + c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() : painter.background().color() ); lingrad.setColorAt( 1, c.darker( 300 ) ); @@ -511,7 +526,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) // inner border p.setPen( c.lighter( 160 ) ); - p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, + p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, rect().bottom() - TCO_BORDER_WIDTH ); // outer border @@ -527,7 +542,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) embed::getIconPixmap( "muted", size, size ) ); } - // recording sample tracks is not possible at the moment + // recording sample tracks is not possible at the moment /* if( m_tco->isRecord() ) { From 5784dd6dc9483535c2290407d4e417a4b5ad8bd6 Mon Sep 17 00:00:00 2001 From: "https://gitlab.com/users/CYBERDEViLNL" <1148379+CYBERDEViLNL@users.noreply.github.com> Date: Mon, 15 Apr 2019 23:37:20 +0200 Subject: [PATCH 09/35] Use local cursor for TrackContentObjectView (#4918) Fixes crash on cloning patterns on Qt >= 5.12. --- src/core/Track.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 4cec5a4eebb..63893c6a4e2 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -570,9 +570,9 @@ void TrackContentObjectView::dropEvent( QDropEvent * de ) */ void TrackContentObjectView::leaveEvent( QEvent * e ) { - while( QApplication::overrideCursor() != NULL ) + if( cursor().shape() != Qt::BitmapCursor ) { - QApplication::restoreOverrideCursor(); + setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); } if( e != NULL ) { @@ -707,8 +707,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) if( me->x() < width() - RESIZE_GRIP_WIDTH ) { m_action = Move; - QCursor c( Qt::SizeAllCursor ); - QApplication::setOverrideCursor( c ); + setCursor( Qt::SizeAllCursor ); delete m_hint; m_hint = TextFloat::displayMessage( tr( "Hint" ), tr( "Press <%1> and drag to make " @@ -729,8 +728,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) else if( !m_tco->getAutoResize() ) { m_action = Resize; - QCursor c( Qt::SizeHorCursor ); - QApplication::setOverrideCursor( c ); + setCursor( Qt::SizeHorCursor ); delete m_hint; m_hint = TextFloat::displayMessage( tr( "Hint" ), tr( "Press <%1> for free " @@ -932,17 +930,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) { if( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() ) { - if( QApplication::overrideCursor() != NULL && - QApplication::overrideCursor()->shape() != - Qt::SizeHorCursor ) - { - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } - } - QCursor c( Qt::SizeHorCursor ); - QApplication::setOverrideCursor( c ); + setCursor( Qt::SizeHorCursor ); } else { From 96cc5e0e5e9ae1277108622020b54ec1d63dd356 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Wed, 17 Apr 2019 19:08:59 +0200 Subject: [PATCH 10/35] Add PluginIssue class (#4901) --- include/PluginIssue.h | 66 ++++++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/PluginIssue.cpp | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 include/PluginIssue.h create mode 100644 src/core/PluginIssue.cpp diff --git a/include/PluginIssue.h b/include/PluginIssue.h new file mode 100644 index 00000000000..c009458056e --- /dev/null +++ b/include/PluginIssue.h @@ -0,0 +1,66 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 PLUGINISSUE_H +#define PLUGINISSUE_H + +#include +#include + +//! Types of issues that can cause LMMS to not load a plugin +//! LMMS Plugins should use this to indicate errors +enum PluginIssueType +{ + unknownPortFlow, + unknownPortType, + tooManyInputChannels, + tooManyOutputChannels, + noOutputChannel, + portHasNoDef, + portHasNoMin, + portHasNoMax, + featureNotSupported, //!< plugin requires functionality LMMS can't offer + badPortType, //!< port type not supported + noIssue +}; + +//! Issue type bundled with informational string +class PluginIssue +{ + static const char* msgFor(const PluginIssueType& it); + + PluginIssueType m_issueType; + std::string m_info; + +public: + PluginIssue(PluginIssueType it, std::string msg = std::string()) + : m_issueType(it), m_info(msg) + { + } + friend QDebug operator<<(QDebug stream, const PluginIssue& iss); +}; + +QDebug operator<<(QDebug stream, const PluginIssue& iss); + +#endif // PLUGINISSUE_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7870415f971..cc8bfd34c5a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -48,6 +48,7 @@ set(LMMS_SRCS core/Piano.cpp core/PlayHandle.cpp core/Plugin.cpp + core/PluginIssue.cpp core/PluginFactory.cpp core/PresetPreviewPlayHandle.cpp core/ProjectJournal.cpp diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp new file mode 100644 index 00000000000..4a8b2ee5b4e --- /dev/null +++ b/src/core/PluginIssue.cpp @@ -0,0 +1,72 @@ +/* + * PluginIssue.h - PluginIssue class + * + * Copyright (c) 2019 Johannes Lorenz + * + * 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 + +#include "PluginIssue.h" + +const char *PluginIssue::msgFor(const PluginIssueType &it) +{ + switch (it) + { + case unknownPortFlow: + return "unknown port flow for mandatory port"; + case unknownPortType: + return "unknown port type for mandatory port"; + case tooManyInputChannels: + return "too many audio input channels"; + case tooManyOutputChannels: + return "too many audio output channels"; + case noOutputChannel: + return "no audio output channel"; + case portHasNoDef: + return "port is missing default value"; + case portHasNoMin: + return "port is missing min value"; + case portHasNoMax: + return "port is missing max value"; + case featureNotSupported: + return "required feature not supported"; + case badPortType: + return "unsupported port type"; + case noIssue: + return nullptr; + } + return nullptr; +} + + + + +QDebug operator<<(QDebug stream, const PluginIssue &iss) +{ + stream << PluginIssue::msgFor(iss.m_issueType); + if (iss.m_info.length()) + { + stream.nospace() << ": " << iss.m_info.c_str(); + } + return stream; +} + + From d06f5088a152f3bb228050f8fe25673332ce66fd Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 20 Apr 2019 13:09:49 +0200 Subject: [PATCH 11/35] Move macro definition out of class --- include/AutomatableModel.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index f5215676e3b..a92bd51913d 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -60,6 +60,11 @@ modelname.setValue( (float) val ); \ } +// use this to make subclasses visitable +#define MODEL_IS_VISITABLE \ + void accept(ModelVisitor& v) override { v.visit(*this); } \ + void accept(ConstModelVisitor& v) const override { v.visit(*this); } + class ControllerConnection; @@ -85,10 +90,6 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject // Implement those by using the MODEL_IS_VISITABLE macro virtual void accept(ModelVisitor& v) = 0; virtual void accept(ConstModelVisitor& v) const = 0; - // use this to make subclasses visitable -#define MODEL_IS_VISITABLE \ - void accept(ModelVisitor& v) override { v.visit(*this); } \ - void accept(ConstModelVisitor& v) const override { v.visit(*this); } public: //! Return this class casted to Target, or nullptr if impossible From 91099e28d56bdb0ddf4c37da6a23582f92b52db1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 20 Apr 2019 13:23:42 +0200 Subject: [PATCH 12/35] Fix -1 offset in plugin tab In the instrument plugin tab, there was an orange stripe for TripleOscillator. This was because internally, TabWidget moves up the widget by 1 (TabWidget.cpp, line 89). The size of the whole window is: ``` widget->height() + m_tabbarHeight - 1 ``` So this code adds an offset of "-1" to the necessary computations. --- src/gui/widgets/TabWidget.cpp | 5 ++++- src/tracks/InstrumentTrack.cpp | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 49898a3c725..6049ce6cb34 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -321,7 +321,10 @@ QSize TabWidget::minimumSizeHint() const maxWidth = std::max(maxWidth, it->w->width()); maxHeight = std::max(maxHeight, it->w->height()); } - return QSize(maxWidth + 4, maxHeight + m_tabbarHeight); + // "-1" : + // in "addTab", under "Position tab's window", the widget is + // moved up by 1 pixel + return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1); } else { return QWidget::minimumSizeHint(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 69924c8be44..5466dd80632 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1439,7 +1439,10 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget = new TabWidget( "", this, true, true ); - m_tabWidget->setMinimumHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 ); + // "-1" : + // in "TabWidget::addTab", under "Position tab's window", the widget is + // moved up by 1 pixel + m_tabWidget->setMinimumHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 - 1 ); // create tab-widgets @@ -1857,7 +1860,10 @@ void InstrumentTrackWindow::viewPrevInstrument() void InstrumentTrackWindow::adjustTabSize(QWidget *w) { - w->setMinimumSize(INSTRUMENT_WIDTH - 4, INSTRUMENT_HEIGHT - 4); + // "-1" : + // in "TabWidget::addTab", under "Position tab's window", the widget is + // moved up by 1 pixel + w->setMinimumSize(INSTRUMENT_WIDTH - 4, INSTRUMENT_HEIGHT - 4 - 1); } #include "InstrumentTrack.moc" From 53942a146a50c7378d99737749bb88879416b16d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 20 Apr 2019 18:32:17 +0200 Subject: [PATCH 13/35] Fix bad identation in old code --- src/gui/widgets/TabWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 6049ce6cb34..10e139ce4be 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -290,7 +290,7 @@ void TabWidget::wheelEvent( QWheelEvent * we ) if( we->y() > m_tabheight ) { return; - } + } we->accept(); int dir = ( we->delta() < 0 ) ? 1 : -1; From e1df16c45b8ac2923dec8e475b6cf44f23752512 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 20 Apr 2019 22:12:32 +0200 Subject: [PATCH 14/35] Coding conventions --- src/gui/widgets/TabWidget.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 10e139ce4be..9bdbec2e0be 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -83,7 +83,8 @@ void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, i m_widgets[idx] = d; // Position tab's window - if(!m_resizable) { + if (!m_resizable) + { w->setFixedSize( width() - 4, height() - m_tabbarHeight ); } w->move( 2, m_tabbarHeight - 1 ); @@ -193,9 +194,9 @@ void TabWidget::mousePressEvent( QMouseEvent * me ) void TabWidget::resizeEvent( QResizeEvent * ) { - if(!m_resizable) + if (!m_resizable) { - for( widgetStack::iterator it = m_widgets.begin(); + for ( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { ( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight ); @@ -312,10 +313,10 @@ void TabWidget::wheelEvent( QWheelEvent * we ) // Let parent widgets know how much space this tab widget needs QSize TabWidget::minimumSizeHint() const { - if(m_resizable) + if (m_resizable) { int maxWidth = 0, maxHeight = 0; - for( widgetStack::const_iterator it = m_widgets.begin(); + for ( widgetStack::const_iterator it = m_widgets.begin(); it != m_widgets.end(); ++it ) { maxWidth = std::max(maxWidth, it->w->width()); @@ -326,9 +327,7 @@ QSize TabWidget::minimumSizeHint() const // moved up by 1 pixel return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1); } - else { - return QWidget::minimumSizeHint(); - } + else { return QWidget::minimumSizeHint(); } } From 407444ea1d28cf2c0412f87ab8b5efbd2a059ea0 Mon Sep 17 00:00:00 2001 From: CYBERDEViLNL Date: Wed, 3 Apr 2019 19:54:01 +0200 Subject: [PATCH 15/35] FileBrowser: Backup expanded directories and restore that state when the tree is reloaded. --- include/FileBrowser.h | 6 +++++- src/gui/FileBrowser.cpp | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 7e0482ce36f..4b92dd5496d 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -55,7 +55,7 @@ class FileBrowser : public SideBarWidget private slots: void reloadTree( void ); - void expandItems( QTreeWidgetItem * item=NULL ); + void expandItems( QTreeWidgetItem * item=NULL, QList expandedDirs = QList() ); // call with item=NULL to filter the entire tree bool filterItems( const QString & filter, QTreeWidgetItem * item=NULL ); void giveFocusToFilter(); @@ -87,6 +87,10 @@ class FileBrowserTreeWidget : public QTreeWidget FileBrowserTreeWidget( QWidget * parent ); virtual ~FileBrowserTreeWidget() = default; + //! This method returns a QList with paths (QString's) of all directories + //! that are expanded in the tree. + QList expandedDirs( QTreeWidgetItem * item = nullptr ) const; + protected: virtual void contextMenuEvent( QContextMenuEvent * e ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 4311e4e053b..7eeb87ed29c 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -163,6 +163,7 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item ) void FileBrowser::reloadTree( void ) { + QList expandedDirs = m_fileBrowserTreeWidget->expandedDirs(); const QString text = m_filterEdit->text(); m_filterEdit->clear(); m_fileBrowserTreeWidget->clear(); @@ -171,17 +172,17 @@ void FileBrowser::reloadTree( void ) { addItems( *it ); } - expandItems(); + expandItems(NULL, expandedDirs); m_filterEdit->setText( text ); filterItems( text ); } -void FileBrowser::expandItems( QTreeWidgetItem * item ) +void FileBrowser::expandItems( QTreeWidgetItem * item, QList expandedDirs ) { int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); - for( int i = 0; i < numChildren; ++i ) + for (int i = 0; i < numChildren; ++i) { QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i); if ( m_recurse ) @@ -189,14 +190,15 @@ void FileBrowser::expandItems( QTreeWidgetItem * item ) it->setExpanded( true ); } Directory *d = dynamic_cast ( it ); - if( d ) + if (d) { d->update(); - d->setExpanded( false ); + bool expand = expandedDirs.contains( d->fullName() ); + d->setExpanded( expand ); } - if( m_recurse && it->childCount() ) + if (m_recurse && it->childCount()) { - expandItems(it); + expandItems(it, expandedDirs); } } } @@ -326,6 +328,30 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : } +QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) const +{ + int numChildren = item ? item->childCount() : topLevelItemCount(); + QList dirs; + for (int i = 0; i < numChildren; ++i) + { + QTreeWidgetItem * it = item ? item->child(i) : topLevelItem(i); + + // Add expanded top level directories. + if (it->isExpanded() && (it->type() == TypeDirectoryItem)) + { + Directory *d = static_cast ( it ); + dirs.append( d->fullName() ); + } + + // Add expanded child directories (recurse). + if (it->childCount()) + { + dirs.append( expandedDirs( it ) ); + } + } + return dirs; +} + void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) { FileItem * f = dynamic_cast( itemAt( e->pos() ) ); From a685049627483cd71a5286a355861e0a00affacc Mon Sep 17 00:00:00 2001 From: makepost Date: Fri, 19 Apr 2019 15:12:29 +0000 Subject: [PATCH 16/35] Allow build for Wayland w/o X11Extras, if VST off @lukas-w reminds in 134dae8 comments that X11Extras help Linux users of VST effects #3786. Now LMMS builds and runs on Weston without X11 dependencies, though only if WANT_VST is off. --- CMakeLists.txt | 2 +- src/3rdparty/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2611d70867f..e186fa61cbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ IF(WANT_QT5) Qt5::Xml ) - IF(LMMS_BUILD_LINUX) + IF(LMMS_BUILD_LINUX AND WANT_VST) FIND_PACKAGE(Qt5X11Extras REQUIRED) LIST(APPEND QT_LIBRARIES Qt5::X11Extras) ENDIF() diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 5dec2890c45..d546fd06e87 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -1,7 +1,7 @@ set(CMAKE_C_FLAGS "") set(CMAKE_CXX_FLAGS "") -IF(QT5 AND LMMS_BUILD_LINUX) +IF(QT5 AND LMMS_BUILD_LINUX AND WANT_VST) set(BUILD_SHARED_LIBS OFF) add_subdirectory(qt5-x11embed) ENDIF() From d537561ffc60e5bed978bf8ac839f5a7c646f783 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 09:27:01 +0200 Subject: [PATCH 17/35] Document Effect::checkGate --- include/Effect.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/Effect.h b/include/Effect.h index 3d765fdc03c..4dc50e8a478 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -161,6 +161,13 @@ class LMMS_EXPORT Effect : public Plugin protected: + /** + Effects should call this at the end of audio processing + + If the setting "Keep effects running even without input" is disabled, + after "decay" ms of a signal below "gate", the effect is turned off + and won't be processed again until it receives new audio input + */ void checkGate( double _out_sum ); virtual PluginView * instantiateView( QWidget * ); From 7ae4e85ffe27753f87c6cf2bd8b7617ee39f8996 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 11:05:59 +0200 Subject: [PATCH 18/35] Document graph widget --- include/Graph.h | 38 ++++++++++++++++++++++++++++++++------ src/gui/widgets/Graph.cpp | 5 +++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/include/Graph.h b/include/Graph.h index 4827bda4026..1bee05c4118 100644 --- a/include/Graph.h +++ b/include/Graph.h @@ -44,13 +44,18 @@ class LMMS_EXPORT Graph : public QWidget, public ModelView public: enum graphStyle { - NearestStyle, - LinearStyle, - LinearNonCyclicStyle, - BarStyle, + NearestStyle, //!< draw as stairs + LinearStyle, //!< connect each 2 samples with a line, with wrapping + LinearNonCyclicStyle, //!< LinearStyle without wrapping + BarStyle, //!< draw thick bars NumGraphStyles }; + /** + * @brief Constructor + * @param _width Pixel width of widget + * @param _height Pixel height of widget + */ Graph( QWidget * _parent, graphStyle _style = Graph::LinearStyle, int _width = 132, int _height = 104 @@ -111,10 +116,24 @@ protected slots: } ; +/** + @brief 2 dimensional function plot + + Function plot graph with discrete x scale and continous y scale + This makes it possible to display "#x" samples +*/ class LMMS_EXPORT graphModel : public Model { Q_OBJECT public: + /** + * @brief Constructor + * @param _min Minimum y value to display + * @param _max Maximum y value to display + * @param _size Number of samples (e.g. x value) + * @param _step Step size on y axis where values snap to, or 0.0f + * for "no snapping" + */ graphModel( float _min, float _max, int _size, @@ -146,14 +165,21 @@ class LMMS_EXPORT graphModel : public Model return( m_samples.data() ); } - void convolve(const float *convolution, const int convolutionLength, const int centerOffset); + //! Make cyclic convolution + //! @param convolution Samples to convolve with + //! @param convolutionLength Number of samples to take for each sum + //! @param centerOffset Offset for resulting values + void convolve(const float *convolution, + const int convolutionLength, const int centerOffset); public slots: + //! Set range of y values void setRange( float _min, float _max ); void setLength( int _size ); - + //! Update one sample void setSampleAt( int x, float val ); + //! Update samples array void setSamples( const float * _value ); void setWaveToSine(); diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index f93ed523c98..4710089dd1a 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -635,13 +635,14 @@ void graphModel::smoothNonCyclic() emit samplesChanged(0, length()-1); } -//makes a cyclic convolution. -void graphModel::convolve(const float *convolution, const int convolutionLength, const int centerOffset) +void graphModel::convolve(const float *convolution, + const int convolutionLength, const int centerOffset) { // store values in temporary array QVector temp = m_samples; const int graphLength = length(); float sum; + // make a cyclic convolution for ( int i = 0; i < graphLength; i++ ) { sum = 0; From b5e408d25a7a1805e0442bebd4c834bbb9835d62 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 22 Apr 2019 20:03:57 +0200 Subject: [PATCH 19/35] Update wiki submodule --- doc/wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/wiki b/doc/wiki index 42193f98f37..9c161315589 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 42193f98f37d6b69f47edbdfd50a20090193e70a +Subproject commit 9c1613155898f9a70273101b09907a87501b82ed From c80f0bf5e716f8fa6e847a1ef20646b7b165e680 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 24 Apr 2019 13:58:37 +0900 Subject: [PATCH 20/35] Fix wiki submodule --- doc/wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/wiki b/doc/wiki index 9c161315589..19179c6f6af 160000 --- a/doc/wiki +++ b/doc/wiki @@ -1 +1 @@ -Subproject commit 9c1613155898f9a70273101b09907a87501b82ed +Subproject commit 19179c6f6afb422cf8376ed3b4a498a6396fc12f From 461faccaa0d9399e137e471d5bfa0f2b43434d9b Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Wed, 24 Apr 2019 12:50:07 +0100 Subject: [PATCH 21/35] Fix notes getting stuck under high CPU conditions (#4908) --- include/NotePlayHandle.h | 1 + src/core/NotePlayHandle.cpp | 31 +++++++++++++++++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 768a74aa276..023c3f7c491 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -302,6 +302,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public Note NotePlayHandleList m_subNotes; // used for chords and arpeggios volatile bool m_released; // indicates whether note is released bool m_releaseStarted; + bool m_hasMidiNote; bool m_hasParent; // indicates whether note has parent NotePlayHandle * m_parent; // parent note bool m_hadChildren; diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 596e17a7177..84d83bb0a78 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -63,6 +63,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, m_subNotes(), m_released( false ), m_releaseStarted( false ), + m_hasMidiNote( false ), m_hasParent( parent != NULL ), m_parent( parent ), m_hadChildren( false ), @@ -106,17 +107,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, m_instrumentTrack->midiNoteOn( *this ); } - if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) - { - const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity(); - - // send MidiNoteOn event - m_instrumentTrack->processOutEvent( - MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ), - MidiTime::fromFrames( offset(), Engine::framesPerTick() ), - offset() ); - } - if( m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed ) { setUsesBuffer( false ); @@ -208,6 +198,21 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) } lock(); + + if( m_totalFramesPlayed == 0 && !m_hasMidiNote + && ( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) ) + { + m_hasMidiNote = true; + + const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity(); + + // send MidiNoteOn event + m_instrumentTrack->processOutEvent( + MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ), + MidiTime::fromFrames( offset(), Engine::framesPerTick() ), + offset() ); + } + if( m_frequencyNeedsUpdate ) { updateFrequency(); @@ -360,8 +365,10 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) m_framesBeforeRelease = _s; m_releaseFramesToDo = qMax( 0, actualReleaseFramesToDo() ); - if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) + if( m_hasMidiNote ) { + m_hasMidiNote = false; + // send MidiNoteOff event m_instrumentTrack->processOutEvent( MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ), From 160d306f481a90e1ddb37666200934d70902ce5c Mon Sep 17 00:00:00 2001 From: Alexandra Dutton Date: Wed, 24 Apr 2019 13:18:53 -0400 Subject: [PATCH 22/35] Record chords (#4938) * Added check for chord to notes recorded from keyboard --- src/gui/editors/PianoRoll.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index fef788885db..7d9f95257a6 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3522,6 +3522,22 @@ void PianoRoll::finishRecordNote(const Note & n ) it->key(), it->getVolume(), it->getPanning() ); n1.quantizeLength( quantization() ); + + //Get selected chord + const InstrumentFunctionNoteStacking::Chord & chord = InstrumentFunctionNoteStacking::ChordTable::getInstance() + .getChordByName( m_chordModel.currentText() ); + + if( !chord.isEmpty() ) + { + for( int i = 1; i < chord.size(); i++ ) + { + Note new_note( n.length(), it->pos(), it->key() + chord[i] ); + new_note.setPanning( it->getPanning() ); + new_note.setVolume( it->getVolume() ); + m_pattern->addNote( new_note ); + } + } + m_pattern->addNote( n1 ); update(); m_recordingNotes.erase( it ); From ca9a9564cd11eb298f598af33d6032e4b17bac10 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Fri, 26 Apr 2019 12:59:53 +0100 Subject: [PATCH 23/35] Make more connections direct for automation (#4942) --- plugins/LadspaEffect/LadspaControls.cpp | 6 ++++-- plugins/VstEffect/VstEffect.cpp | 3 --- plugins/vst_base/RemoteVstPlugin.cpp | 3 ++- plugins/vst_base/VstPlugin.cpp | 2 +- src/core/EnvelopeAndLfoParameters.cpp | 26 ++++++++++++------------- src/core/LadspaControl.cpp | 3 ++- src/core/LfoController.cpp | 6 +++--- src/core/MeterModel.cpp | 4 ++-- src/core/PeakController.cpp | 6 ++++-- src/core/Song.cpp | 8 ++++---- src/core/TempoSyncKnobModel.cpp | 6 ++++-- src/core/Track.cpp | 2 +- src/core/midi/MidiAlsaSeq.cpp | 2 +- src/core/midi/MidiPort.cpp | 9 ++++++--- src/gui/FxMixerView.cpp | 2 +- src/tracks/SampleTrack.cpp | 2 +- 16 files changed, 49 insertions(+), 41 deletions(-) diff --git a/plugins/LadspaEffect/LadspaControls.cpp b/plugins/LadspaEffect/LadspaControls.cpp index 1a2f26a352b..b7f5d2f5771 100644 --- a/plugins/LadspaEffect/LadspaControls.cpp +++ b/plugins/LadspaEffect/LadspaControls.cpp @@ -36,7 +36,8 @@ LadspaControls::LadspaControls( LadspaEffect * _eff ) : { connect( &m_stereoLinkModel, SIGNAL( dataChanged() ), - this, SLOT( updateLinkStatesFromGlobal() ) ); + this, SLOT( updateLinkStatesFromGlobal() ), + Qt::DirectConnection ); multi_proc_t controls = m_effect->getPortControls(); m_controlCount = controls.count(); @@ -59,7 +60,8 @@ LadspaControls::LadspaControls( LadspaEffect * _eff ) : if( linked_control ) { connect( (*it)->control, SIGNAL( linkChanged( int, bool ) ), - this, SLOT( linkPort( int, bool ) ) ); + this, SLOT( linkPort( int, bool ) ), + Qt::DirectConnection ); } } } diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index 10005d74492..ec2ab78e70e 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -145,9 +145,6 @@ void VstEffect::openPlugin( const QString & _plugin ) return; } - VstPlugin::connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), m_plugin.data(), SLOT( setTempo( bpm_t ) ) ); - m_plugin->setTempo( Engine::getSong()->getTempo() ); - delete tf; m_key.attributes["file"] = _plugin; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 8dd25c11f83..4a147f7c9b4 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1953,7 +1953,8 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { if( m.id == IdStartProcessing || m.id == IdMidiEvent - || m.id == IdVstSetParameter ) + || m.id == IdVstSetParameter + || m.id == IdVstSetTempo ) { _this->processMessage( m ); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index e0e1347fe53..c707e3fc3b8 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -92,7 +92,7 @@ VstPlugin::VstPlugin( const QString & _plugin ) : setTempo( Engine::getSong()->getTempo() ); connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( setTempo( bpm_t ) ) ); + this, SLOT( setTempo( bpm_t ) ), Qt::DirectConnection ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index d1fbaeb0382..27766709d2e 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -129,32 +129,32 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters( instances()->add( this ); connect( &m_predelayModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_attackModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_holdModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_decayModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_sustainModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_releaseModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_amountModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_lfoPredelayModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_lfoAttackModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_lfoSpeedModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_lfoAmountModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_lfoWaveModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( &m_x100Model, SIGNAL( dataChanged() ), - this, SLOT( updateSampleVars() ) ); + this, SLOT( updateSampleVars() ), Qt::DirectConnection ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleVars() ) ); diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp index d2d6e147a61..73a32dbb434 100644 --- a/src/core/LadspaControl.cpp +++ b/src/core/LadspaControl.cpp @@ -42,7 +42,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, if( m_link ) { connect( &m_linkEnabledModel, SIGNAL( dataChanged() ), - this, SLOT( linkStateChanged() ) ); + this, SLOT( linkStateChanged() ), + Qt::DirectConnection ); } switch( m_port->data_type ) diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index c1c81119f64..2b2db2f1471 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -49,12 +49,12 @@ LfoController::LfoController( Model * _parent ) : { setSampleExact( true ); connect( &m_waveModel, SIGNAL( dataChanged() ), - this, SLOT( updateSampleFunction() ) ); + this, SLOT( updateSampleFunction() ), Qt::DirectConnection ); connect( &m_speedModel, SIGNAL( dataChanged() ), - this, SLOT( updateDuration() ) ); + this, SLOT( updateDuration() ), Qt::DirectConnection ); connect( &m_multiplierModel, SIGNAL( dataChanged() ), - this, SLOT( updateDuration() ) ); + this, SLOT( updateDuration() ), Qt::DirectConnection ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateDuration() ) ); diff --git a/src/core/MeterModel.cpp b/src/core/MeterModel.cpp index 87b1b950afb..30db26d9b71 100644 --- a/src/core/MeterModel.cpp +++ b/src/core/MeterModel.cpp @@ -33,9 +33,9 @@ MeterModel::MeterModel( ::Model * _parent ) : m_denominatorModel( 4, 1, 32, this, tr( "Denominator" ) ) { connect( &m_numeratorModel, SIGNAL( dataChanged() ), - this, SIGNAL( dataChanged() ) ); + this, SIGNAL( dataChanged() ), Qt::DirectConnection ); connect( &m_denominatorModel, SIGNAL( dataChanged() ), - this, SIGNAL( dataChanged() ) ); + this, SIGNAL( dataChanged() ), Qt::DirectConnection ); } diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index 9e5e654a660..b2e3bc92132 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -53,8 +53,10 @@ PeakController::PeakController( Model * _parent, this, SLOT( handleDestroyedEffect( ) ) ); } connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateCoeffs() ) ); - connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) ); - connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) ); + connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ), + this, SLOT( updateCoeffs() ), Qt::DirectConnection ); + connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ), + this, SLOT( updateCoeffs() ), Qt::DirectConnection ); m_coeffNeedsUpdate = true; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ba2659a6c3a..943524e6d0d 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -96,18 +96,18 @@ Song::Song() : m_elapsedTacts( 0 ) { connect( &m_tempoModel, SIGNAL( dataChanged() ), - this, SLOT( setTempo() ) ); + this, SLOT( setTempo() ), Qt::DirectConnection ); connect( &m_tempoModel, SIGNAL( dataUnchanged() ), - this, SLOT( setTempo() ) ); + this, SLOT( setTempo() ), Qt::DirectConnection ); connect( &m_timeSigModel, SIGNAL( dataChanged() ), - this, SLOT( setTimeSignature() ) ); + this, SLOT( setTimeSignature() ), Qt::DirectConnection ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFramesPerTick() ) ); connect( &m_masterVolumeModel, SIGNAL( dataChanged() ), - this, SLOT( masterVolumeChanged() ) ); + this, SLOT( masterVolumeChanged() ), Qt::DirectConnection ); /* connect( &m_masterPitchModel, SIGNAL( dataChanged() ), this, SLOT( masterPitchChanged() ) );*/ diff --git a/src/core/TempoSyncKnobModel.cpp b/src/core/TempoSyncKnobModel.cpp index e94c6e42446..a85ca2e9ff8 100644 --- a/src/core/TempoSyncKnobModel.cpp +++ b/src/core/TempoSyncKnobModel.cpp @@ -42,7 +42,8 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min, m_custom( _parent ) { connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( calculateTempoSyncTime( bpm_t ) ) ); + this, SLOT( calculateTempoSyncTime( bpm_t ) ), + Qt::DirectConnection ); } @@ -154,7 +155,8 @@ void TempoSyncKnobModel::setSyncMode( TempoSyncMode _new_mode ) if( _new_mode == SyncCustom ) { connect( &m_custom, SIGNAL( dataChanged() ), - this, SLOT( updateCustom() ) ); + this, SLOT( updateCustom() ), + Qt::DirectConnection ); } } calculateTempoSyncTime( Engine::getSong()->getTempo() ); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 63893c6a4e2..7a04ded954d 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -2548,7 +2548,7 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : &m_trackContentWidget, SLOT( update() ) ); connect( &m_track->m_soloModel, SIGNAL( dataChanged() ), - m_track, SLOT( toggleSolo() ) ); + m_track, SLOT( toggleSolo() ), Qt::DirectConnection ); // create views for already existing TCOs for( Track::tcoVector::iterator it = m_track->m_trackContentObjects.begin(); diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index be1e623de95..e420ebc084c 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -100,7 +100,7 @@ MidiAlsaSeq::MidiAlsaSeq() : snd_seq_start_queue( m_seqHandle, m_queueID, NULL ); changeQueueTempo( Engine::getSong()->getTempo() ); connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( changeQueueTempo( bpm_t ) ) ); + this, SLOT( changeQueueTempo( bpm_t ) ), Qt::DirectConnection ); // initial list-update updatePortList(); diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 9e3cdb13d83..52e0a522362 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -63,9 +63,12 @@ MidiPort::MidiPort( const QString& name, m_readableModel.setValue( m_mode == Input || m_mode == Duplex ); m_writableModel.setValue( m_mode == Output || m_mode == Duplex ); - connect( &m_readableModel, SIGNAL( dataChanged() ), this, SLOT( updateMidiPortMode() ) ); - connect( &m_writableModel, SIGNAL( dataChanged() ), this, SLOT( updateMidiPortMode() ) ); - connect( &m_outputProgramModel, SIGNAL( dataChanged() ), this, SLOT( updateOutputProgram() ) ); + connect( &m_readableModel, SIGNAL( dataChanged() ), + this, SLOT( updateMidiPortMode() ), Qt::DirectConnection ); + connect( &m_writableModel, SIGNAL( dataChanged() ), + this, SLOT( updateMidiPortMode() ), Qt::DirectConnection ); + connect( &m_outputProgramModel, SIGNAL( dataChanged() ), + this, SLOT( updateOutputProgram() ), Qt::DirectConnection ); // when using with non-raw-clients we can provide buttons showing diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 5a26372f9d0..8f1b7e791ec 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -306,7 +306,7 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv, m_soloBtn->setCheckable( true ); m_soloBtn->move( 9, m_fader->y()-21); connect(&fxChannel->m_soloModel, SIGNAL( dataChanged() ), - _mv, SLOT ( toggledSolo() ) ); + _mv, SLOT ( toggledSolo() ), Qt::DirectConnection ); ToolTip::add( m_soloBtn, tr( "Solo FX channel" ) ); // Create EffectRack for the channel diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 0a5fabd1adf..faf066e8d72 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -61,7 +61,7 @@ SampleTCO::SampleTCO( Track * _track ) : // we need to receive bpm-change-events, because then we have to // change length of this TCO connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), - this, SLOT( updateLength() ) ); + this, SLOT( updateLength() ), Qt::DirectConnection ); connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ), this, SLOT( updateLength() ) ); From 0fd5693e12cf2e5cae0967763854382e0ad376be Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 00:29:49 +0200 Subject: [PATCH 24/35] Improve dcast * document `dcast` * make `dcast` not only cast exact, but also upwards * add `dcast` test * rename `dcast` -> `dynamicCast` --- include/AutomatableModel.h | 17 ++++++-- include/ModelVisitor.h | 24 +++++++---- src/core/ModelVisitor.cpp | 13 ++++++ tests/CMakeLists.txt | 1 + tests/src/core/AutomatableModelTest.cpp | 53 +++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 tests/src/core/AutomatableModelTest.cpp diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index a92bd51913d..93550e33f65 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -92,18 +92,25 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject virtual void accept(ConstModelVisitor& v) const = 0; public: - //! Return this class casted to Target, or nullptr if impossible + /** + @brief Return this class casted to Target + @test AutomatableModelTest.cpp + @param doThrow throw an assertion if the cast fails, instead of + returning a nullptr + @return the casted class if Target is the exact or a base class of + *this, nullptr otherwise + */ template - Target* dcast(bool doThrow = false) + Target* dynamicCast(bool doThrow = false) { DCastVisitor vis; accept(vis); if(doThrow && !vis.result) Q_ASSERT(false); return vis.result; } - //! Return this class casted to const Target, or nullptr if impossible + //! const overload, see overloaded function template - const Target* dcast(bool doThrow = false) const + const Target* dynamicCast(bool doThrow = false) const { ConstDCastVisitor vis; accept(vis); if(doThrow && !vis.result) Q_ASSERT(false); @@ -312,6 +319,7 @@ public slots: private: + // dynamicCast implementation template struct DCastVisitor : public ModelVisitor { @@ -319,6 +327,7 @@ public slots: void visit(Target& tar) { result = &tar; } }; + // dynamicCast implementation template struct ConstDCastVisitor : public ConstModelVisitor { diff --git a/include/ModelVisitor.h b/include/ModelVisitor.h index 59d1df0c62a..6411d070236 100644 --- a/include/ModelVisitor.h +++ b/include/ModelVisitor.h @@ -25,6 +25,7 @@ #ifndef MODELVISITOR_H #define MODELVISITOR_H +class AutomatableModel; class BoolModel; class IntModel; class FloatModel; @@ -32,21 +33,28 @@ class ComboBoxModel; class ModelVisitor { + template + void up(ModelType& m) { visit(static_cast(m)); } public: - virtual void visit(BoolModel& ) {} - virtual void visit(IntModel& ) {} - virtual void visit(FloatModel& ) {} - virtual void visit(ComboBoxModel& ) {} + virtual void visit(AutomatableModel& ) {} + virtual void visit(BoolModel& m); + virtual void visit(IntModel& ); + virtual void visit(FloatModel& ); + virtual void visit(ComboBoxModel& ); virtual ~ModelVisitor(); }; class ConstModelVisitor { + template + void up(const ModelType& m) { + visit(static_cast(m)); } public: - virtual void visit(const BoolModel& ) {} - virtual void visit(const IntModel& ) {} - virtual void visit(const FloatModel& ) {} - virtual void visit(const ComboBoxModel& ) {} + virtual void visit(const AutomatableModel& ) {} + virtual void visit(const BoolModel& m); + virtual void visit(const IntModel& m); + virtual void visit(const FloatModel& m); + virtual void visit(const ComboBoxModel& m); virtual ~ConstModelVisitor(); }; diff --git a/src/core/ModelVisitor.cpp b/src/core/ModelVisitor.cpp index 11a8fc1b11a..48065c57eea 100644 --- a/src/core/ModelVisitor.cpp +++ b/src/core/ModelVisitor.cpp @@ -24,5 +24,18 @@ #include "ModelVisitor.h" +#include "AutomatableModel.h" +#include "ComboBoxModel.h" + +void ModelVisitor::visit(BoolModel &m) { up(m); } +void ModelVisitor::visit(IntModel &m) { up(m); } +void ModelVisitor::visit(FloatModel &m) { up(m); } +void ModelVisitor::visit(ComboBoxModel &m) { up(m); } + +void ConstModelVisitor::visit(const BoolModel &m) { up(m); } +void ConstModelVisitor::visit(const IntModel &m) { up(m); } +void ConstModelVisitor::visit(const FloatModel &m) { up(m); } +void ConstModelVisitor::visit(const ComboBoxModel &m) { up(m); } + ModelVisitor::~ModelVisitor() {} ConstModelVisitor::~ConstModelVisitor() {} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c39f8e56ed9..ddebe116c6e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ ADD_EXECUTABLE(tests QTestSuite $ + src/core/AutomatableModelTest.cpp src/core/ProjectVersionTest.cpp src/core/RelativePathsTest.cpp diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp new file mode 100644 index 00000000000..9bc19d7e863 --- /dev/null +++ b/tests/src/core/AutomatableModelTest.cpp @@ -0,0 +1,53 @@ +/* + * AutomatableModelTest.cpp + * + * Copyright (c) 2019-2019 Johannes Lorenz + * + * 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 "QTestSuite.h" + +#include "AutomatableModel.h" +#include "ComboBoxModel.h" + +class AutomatableModelTest : QTestSuite +{ + Q_OBJECT + +private slots: + void CastTests() + { + ComboBoxModel comboModel; + AutomatableModel* amPtr = &comboModel; + QCOMPARE(nullptr, amPtr->dynamicCast()); + QVERIFY(nullptr != amPtr->dynamicCast()); + QVERIFY(nullptr != amPtr->dynamicCast()); + QVERIFY(nullptr != amPtr->dynamicCast()); + + IntModel intModel; + IntModel* imPtr = &intModel; + QCOMPARE(nullptr, imPtr->dynamicCast()); + QVERIFY(nullptr != imPtr->dynamicCast()); + QVERIFY(nullptr != imPtr->dynamicCast()); + QCOMPARE(nullptr, imPtr->dynamicCast()); + } +} AutomatableModelTests; + +#include "AutomatableModelTest.moc" From 8d005e7565b80f161a51bebb26cf62c700b39dd6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 10:48:37 +0200 Subject: [PATCH 25/35] AutomatableModelTest: Improve tests Check whether returned pointers from the cast are equal to the original pointers, rather than just checking wether they are not `nullptr`. --- tests/src/core/AutomatableModelTest.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp index 9bc19d7e863..6717da12d50 100644 --- a/tests/src/core/AutomatableModelTest.cpp +++ b/tests/src/core/AutomatableModelTest.cpp @@ -32,21 +32,23 @@ class AutomatableModelTest : QTestSuite Q_OBJECT private slots: + //! Test that upcast and exact casts work, + //! but no downcast or any other casts void CastTests() { ComboBoxModel comboModel; AutomatableModel* amPtr = &comboModel; - QCOMPARE(nullptr, amPtr->dynamicCast()); - QVERIFY(nullptr != amPtr->dynamicCast()); - QVERIFY(nullptr != amPtr->dynamicCast()); - QVERIFY(nullptr != amPtr->dynamicCast()); + QCOMPARE(nullptr, amPtr->dynamicCast()); // not a parent class + QCOMPARE(&comboModel, amPtr->dynamicCast()); // parent class + QCOMPARE(&comboModel, amPtr->dynamicCast()); // parent class + QCOMPARE(&comboModel, amPtr->dynamicCast()); // same class IntModel intModel; IntModel* imPtr = &intModel; - QCOMPARE(nullptr, imPtr->dynamicCast()); - QVERIFY(nullptr != imPtr->dynamicCast()); - QVERIFY(nullptr != imPtr->dynamicCast()); - QCOMPARE(nullptr, imPtr->dynamicCast()); + QCOMPARE(nullptr, imPtr->dynamicCast()); // not a parent class + QCOMPARE(&intModel, imPtr->dynamicCast()); // parent class + QCOMPARE(&intModel, imPtr->dynamicCast()); // same class + QCOMPARE(nullptr, imPtr->dynamicCast()); // child class } } AutomatableModelTests; From 777da5e391a81ed561767708c2f86a6b7b36af09 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 11:11:48 +0200 Subject: [PATCH 26/35] Fix CI on windows --- tests/src/core/AutomatableModelTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp index 6717da12d50..116f95e60a0 100644 --- a/tests/src/core/AutomatableModelTest.cpp +++ b/tests/src/core/AutomatableModelTest.cpp @@ -38,17 +38,17 @@ private slots: { ComboBoxModel comboModel; AutomatableModel* amPtr = &comboModel; - QCOMPARE(nullptr, amPtr->dynamicCast()); // not a parent class + QVERIFY(nullptr == amPtr->dynamicCast()); // not a parent class QCOMPARE(&comboModel, amPtr->dynamicCast()); // parent class QCOMPARE(&comboModel, amPtr->dynamicCast()); // parent class QCOMPARE(&comboModel, amPtr->dynamicCast()); // same class IntModel intModel; IntModel* imPtr = &intModel; - QCOMPARE(nullptr, imPtr->dynamicCast()); // not a parent class + QVERIFY(nullptr == imPtr->dynamicCast()); // not a parent class QCOMPARE(&intModel, imPtr->dynamicCast()); // parent class QCOMPARE(&intModel, imPtr->dynamicCast()); // same class - QCOMPARE(nullptr, imPtr->dynamicCast()); // child class + QVERIFY(nullptr == imPtr->dynamicCast()); // child class } } AutomatableModelTests; From 2c134d65fed33930801581e8c906ed1eb0048112 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 27 Apr 2019 15:26:17 +0200 Subject: [PATCH 27/35] Code style + Extend for TempoSyncKnob --- include/AutomatableModel.h | 4 ++-- include/ModelVisitor.h | 9 ++++++--- include/TempoSyncKnobModel.h | 1 + src/core/ModelVisitor.cpp | 3 +++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 93550e33f65..3e0b6143da3 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -104,7 +104,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject Target* dynamicCast(bool doThrow = false) { DCastVisitor vis; accept(vis); - if(doThrow && !vis.result) Q_ASSERT(false); + if (doThrow && !vis.result) { Q_ASSERT(false); } return vis.result; } @@ -113,7 +113,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject const Target* dynamicCast(bool doThrow = false) const { ConstDCastVisitor vis; accept(vis); - if(doThrow && !vis.result) Q_ASSERT(false); + if (doThrow && !vis.result) { Q_ASSERT(false); } return vis.result; } diff --git a/include/ModelVisitor.h b/include/ModelVisitor.h index 6411d070236..f9d156e30f4 100644 --- a/include/ModelVisitor.h +++ b/include/ModelVisitor.h @@ -30,6 +30,7 @@ class BoolModel; class IntModel; class FloatModel; class ComboBoxModel; +class TempoSyncKnobModel; class ModelVisitor { @@ -38,9 +39,10 @@ class ModelVisitor public: virtual void visit(AutomatableModel& ) {} virtual void visit(BoolModel& m); - virtual void visit(IntModel& ); - virtual void visit(FloatModel& ); - virtual void visit(ComboBoxModel& ); + virtual void visit(IntModel& m); + virtual void visit(FloatModel& m); + virtual void visit(ComboBoxModel& m); + virtual void visit(TempoSyncKnobModel& m); virtual ~ModelVisitor(); }; @@ -55,6 +57,7 @@ class ConstModelVisitor virtual void visit(const IntModel& m); virtual void visit(const FloatModel& m); virtual void visit(const ComboBoxModel& m); + virtual void visit(const TempoSyncKnobModel& m); virtual ~ConstModelVisitor(); }; diff --git a/include/TempoSyncKnobModel.h b/include/TempoSyncKnobModel.h index 9a8ad619c11..52fab30bbab 100644 --- a/include/TempoSyncKnobModel.h +++ b/include/TempoSyncKnobModel.h @@ -33,6 +33,7 @@ class QAction; class LMMS_EXPORT TempoSyncKnobModel : public FloatModel { Q_OBJECT + MODEL_IS_VISITABLE public: enum TempoSyncMode { diff --git a/src/core/ModelVisitor.cpp b/src/core/ModelVisitor.cpp index 48065c57eea..4036f56e0a3 100644 --- a/src/core/ModelVisitor.cpp +++ b/src/core/ModelVisitor.cpp @@ -26,16 +26,19 @@ #include "AutomatableModel.h" #include "ComboBoxModel.h" +#include "TempoSyncKnobModel.h" void ModelVisitor::visit(BoolModel &m) { up(m); } void ModelVisitor::visit(IntModel &m) { up(m); } void ModelVisitor::visit(FloatModel &m) { up(m); } void ModelVisitor::visit(ComboBoxModel &m) { up(m); } +void ModelVisitor::visit(TempoSyncKnobModel &m) { up(m); } void ConstModelVisitor::visit(const BoolModel &m) { up(m); } void ConstModelVisitor::visit(const IntModel &m) { up(m); } void ConstModelVisitor::visit(const FloatModel &m) { up(m); } void ConstModelVisitor::visit(const ComboBoxModel &m) { up(m); } +void ConstModelVisitor::visit(const TempoSyncKnobModel &m) { up(m); } ModelVisitor::~ModelVisitor() {} ConstModelVisitor::~ConstModelVisitor() {} From 8bcdf06c6c632d612eb39e6a82d62022f9dc2ddd Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 29 Apr 2019 16:34:35 +0900 Subject: [PATCH 28/35] Travis: fix a debootstrap error from missing keyrings Uses 18.04's debian-archive-keyring to fix the missing keyrings. --- .travis/debian_pkgs.sha256 | 1 + .travis/linux.debian-sid.install.sh | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis/debian_pkgs.sha256 b/.travis/debian_pkgs.sha256 index 96a4bd8aa99..ed4e1173789 100644 --- a/.travis/debian_pkgs.sha256 +++ b/.travis/debian_pkgs.sha256 @@ -1,2 +1,3 @@ 314ef4af137903dfb13e8c3ef1e6ea56cfdb23808d52ec4f5f50e288c73610c5 pbuilder_0.229.1_all.deb fa82aa8ed3055c6f6330104deedf080b26778295e589426d4c4dd0f2c2a5defa debootstrap_1.0.95_all.deb +2ef4c09f7841b72f93412803ddd142f72658536dbfabe00e449eb548f432f3f8 debian-archive-keyring_2017.7ubuntu1_all.deb diff --git a/.travis/linux.debian-sid.install.sh b/.travis/linux.debian-sid.install.sh index ecdcf6d91fc..ef836882232 100755 --- a/.travis/linux.debian-sid.install.sh +++ b/.travis/linux.debian-sid.install.sh @@ -2,15 +2,16 @@ set -e sudo apt-get install -y \ - debian-archive-keyring \ dpkg \ pbuilder # work around a pbuilder bug which breaks ccache # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666525 +# and also missing signing keys in Trusty's debian-archive-keyring cd /tmp wget http://archive.ubuntu.com/ubuntu/pool/main/p/pbuilder/pbuilder_0.229.1_all.deb wget http://archive.ubuntu.com/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.95_all.deb +wget http://archive.ubuntu.com/ubuntu/pool/universe/d/debian-archive-keyring/debian-archive-keyring_2017.7ubuntu1_all.deb sha256sum -c "$TRAVIS_BUILD_DIR/.travis/debian_pkgs.sha256" -sudo dpkg -i pbuilder_0.229.1_all.deb debootstrap_1.0.95_all.deb +sudo dpkg -i pbuilder_0.229.1_all.deb debootstrap_1.0.95_all.deb debian-archive-keyring_2017.7ubuntu1_all.deb cd "$OLDPWD" From 32df2d7fbad48c97322fdd1ebdffd0b5fee8fff8 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 5 May 2019 12:39:36 +0900 Subject: [PATCH 29/35] Don't draw note detuning info over the volume/panning area (#4965) --- src/gui/editors/PianoRoll.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 7d9f95257a6..18995bfde21 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -883,6 +883,9 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, { int middle_y = _y + KEY_LINE_HEIGHT / 2; _p.setPen( noteColor() ); + _p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, + width() - WHITE_KEY_WIDTH, + keyAreaBottom() - PR_TOP_MARGIN); int old_x = 0; int old_y = 0; @@ -3077,6 +3080,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } } + p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, + width() - WHITE_KEY_WIDTH, + height() - PR_TOP_MARGIN - PR_TOP_MARGIN); + p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) ); p.drawPoints( editHandles ); From cb6b4ec44b0df002ea7f38163207678511d1cc19 Mon Sep 17 00:00:00 2001 From: "https://gitlab.com/users/CYBERDEViLNL" <1148379+CYBERDEViLNL@users.noreply.github.com> Date: Sun, 5 May 2019 15:57:16 +0200 Subject: [PATCH 30/35] Show/Focus BBEditor on TrackLabelButton click #4946 (#4959) It will be shown even if the parent is hidden. --- src/tracks/BBTrack.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index c37c1466fcf..205a22087f8 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -635,5 +635,6 @@ bool BBTrackView::close() void BBTrackView::clickedTrackLabel() { Engine::getBBTrackContainer()->setCurrentBB( m_bbTrack->index() ); - gui->getBBEditor()->show(); + gui->getBBEditor()->parentWidget()->show(); + gui->getBBEditor()->setFocus( Qt::ActiveWindowFocusReason ); } From 8f4757ee3b8f1efe321a82d8b10f6bcf990233a9 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 6 May 2019 12:13:22 +0900 Subject: [PATCH 31/35] Use extracted linuxdeployqt directly As of https://github.com/probonopd/linuxdeployqt/pull/370/, the AppRun of linuxdeployqt unsets LD_LIBRARY_PATH. This behavior isn't suitable for our cases, so we use the extracted binary directly as a workaround. --- cmake/linux/package_linux.sh.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index b02a4c68855..7c0e2593b01 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -70,6 +70,9 @@ elif ! find "$LINUXDEPLOYQT" -mtime -$DAYSOLD 2>/dev/null|grep -q "." > /dev/nul touch "$LINUXDEPLOYQT" success "Downloaded $LINUXDEPLOYQT" "$LINUXDEPLOYQT" --appimage-extract > /dev/null 2>&1 + # We need to set LD_LIBRARY_PATH, but linuxdepoyqt's AppRun unsets it + # See https://github.com/probonopd/linuxdeployqt/pull/370/ + LINUXDEPLOYQT="squashfs-root/usr/bin/linuxdeployqt" success "Extracted $APPIMAGETOOL" else skipped "$LINUXDEPLOYQT is less than $DAYSOLD days old" From 9ff882d09d169e16e0b58b32d279d919968a7329 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 6 May 2019 15:36:38 +0900 Subject: [PATCH 32/35] Fix invisible note editing handles when a note has detuning info Fixes a regression in 32df2d7fbad48c97322fdd1ebdffd0b5fee8fff8, the clipping area was restored in a wrong place. Also, a wrong value was used while restoring. --- src/gui/editors/PianoRoll.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 18995bfde21..5b306a783f8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3077,12 +3077,12 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) drawDetuningInfo( p, note, x + WHITE_KEY_WIDTH, y_base - key * KEY_LINE_HEIGHT ); + p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, + width() - WHITE_KEY_WIDTH, + height() - PR_TOP_MARGIN); } } - p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, - width() - WHITE_KEY_WIDTH, - height() - PR_TOP_MARGIN - PR_TOP_MARGIN); p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) ); p.drawPoints( editHandles ); From d54c79d33be63367415258a214791f4b9d84878b Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 6 May 2019 16:43:01 +0900 Subject: [PATCH 33/35] Fix the wrong merge --- cmake/linux/package_linux.sh.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index a517ee53e07..0dec715f480 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -76,6 +76,7 @@ else # to support systems without fuse # Also, we need to set LD_LIBRARY_PATH, but linuxdepoyqt's AppRun unsets it # See https://github.com/probonopd/linuxdeployqt/pull/370/ + "$LINUXDEPLOYQT" --appimage-extract > /dev/null 2>&1 LINUXDEPLOYQT="squashfs-root/usr/bin/linuxdeployqt" success "Extracted $APPIMAGETOOL" fi From 28143e61edc53124e6df0081517e17fb964d13d4 Mon Sep 17 00:00:00 2001 From: sharpblade4 Date: Fri, 24 May 2019 19:37:14 +0300 Subject: [PATCH 34/35] playing/recording pianoRoll's chord notes (#4963) --- include/PianoRoll.h | 2 + src/gui/editors/PianoRoll.cpp | 84 +++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index b4115b054d6..4451a07c5c4 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -296,6 +296,8 @@ protected slots: void testPlayNote( Note * n ); void testPlayKey( int _key, int _vol, int _pan ); void pauseTestNotes(bool pause = true ); + void playChordNotes(int key, int velocity=-1); + void pauseChordNotes(int key); QList getAllOctavesForKey( int keyToMirror ) const; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 03d7c14c7cc..a3ff9c5fdb2 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1191,9 +1191,11 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) { const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave; - if(! ke->isAutoRepeat() && key_num > -1) + if (!ke->isAutoRepeat() && key_num > -1) { - m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key_num ); + m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num); + // if a chord is set, play all chord notes (simulate click on all): + playChordNotes(key_num); ke->accept(); } } @@ -1391,10 +1393,11 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) if( hasValidPattern() && ke->modifiers() == Qt::NoModifier ) { const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave; - - if( ! ke->isAutoRepeat() && key_num > -1 ) + if (!ke->isAutoRepeat() && key_num > -1) { - m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( key_num ); + m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease(key_num); + // if a chord is set, simulate click release on all chord notes + pauseChordNotes(key_num); ke->accept(); } } @@ -1839,7 +1842,9 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { // left click - play the note int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity; - m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key_num, v ); + m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num, v); + // if a chord is set, play the chords notes as well: + playChordNotes(key_num, v); } } else @@ -1942,7 +1947,10 @@ void PianoRoll::testPlayNote( Note * n ) const int baseVelocity = m_pattern->instrumentTrack()->midiPort()->baseVelocity(); - m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( n->key(), n->midiVelocity( baseVelocity ) ); + m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(n->key(), n->midiVelocity(baseVelocity)); + + // if a chord is set, play the chords notes as well: + playChordNotes(n->key(), n->midiVelocity(baseVelocity)); MidiEvent event( MidiMetaEvent, -1, n->key(), panningToMidi( n->getPanning() ) ); @@ -1965,6 +1973,9 @@ void PianoRoll::pauseTestNotes( bool pause ) { // stop note m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( note->key() ); + + // if a chord was set, stop the chords notes as well: + pauseChordNotes(note->key()); } else { @@ -1976,19 +1987,56 @@ void PianoRoll::pauseTestNotes( bool pause ) } } +void PianoRoll::playChordNotes(int key, int velocity) +{ + // if a chord is set, play the chords notes beside the base note. + Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel(); + const InstrumentFunctionNoteStacking::Chord & chord = + InstrumentFunctionNoteStacking::ChordTable::getInstance().getChordByName( + m_chordModel.currentText()); + if (!chord.isEmpty()) + { + for (int i = 1; i < chord.size(); ++i) + { + pianoModel->handleKeyPress(key + chord[i], velocity); + } + } +} + +void PianoRoll::pauseChordNotes(int key) +{ + // if a chord was set, stop the chords notes beside the base note. + Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel(); + const InstrumentFunctionNoteStacking::Chord & chord = + InstrumentFunctionNoteStacking::ChordTable::getInstance().getChordByName( + m_chordModel.currentText()); + if (!chord.isEmpty()) + { + for (int i = 1; i < chord.size(); ++i) + { + pianoModel->handleKeyRelease(key + chord[i]); + } + } +} + void PianoRoll::testPlayKey( int key, int velocity, int pan ) { + Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel(); // turn off old key - m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( m_lastKey ); + pianoModel->handleKeyRelease( m_lastKey ); + // if a chord was set, stop the chords notes as well + pauseChordNotes(m_lastKey); // remember which one we're playing m_lastKey = key; // play new key - m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key, velocity ); + pianoModel->handleKeyPress( key, velocity ); + // and if a chord is set, play chord notes: + playChordNotes(key, velocity); } @@ -2118,6 +2166,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) { m_pattern->instrumentTrack()->pianoModel()-> handleKeyRelease( note->key() ); + pauseChordNotes(note->key()); note->setIsPlaying( false ); } } @@ -2125,6 +2174,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) // stop playing keys that we let go of m_pattern->instrumentTrack()->pianoModel()-> handleKeyRelease( m_lastKey ); + pauseChordNotes(m_lastKey); } m_currentNote = NULL; @@ -2312,6 +2362,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) { // mouse not over this note, stop playing it. m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( n->key() ); + pauseChordNotes(n->key()); n->setIsPlaying( false ); } @@ -3789,21 +3840,6 @@ void PianoRoll::finishRecordNote(const Note & n ) it->key(), it->getVolume(), it->getPanning() ); n1.quantizeLength( quantization() ); - - //Get selected chord - const InstrumentFunctionNoteStacking::Chord & chord = InstrumentFunctionNoteStacking::ChordTable::getInstance() - .getChordByName( m_chordModel.currentText() ); - - if( !chord.isEmpty() ) - { - for( int i = 1; i < chord.size(); i++ ) - { - Note new_note( n.length(), it->pos(), it->key() + chord[i] ); - new_note.setPanning( it->getPanning() ); - new_note.setVolume( it->getVolume() ); - m_pattern->addNote( new_note ); - } - } m_pattern->addNote( n1 ); update(); m_recordingNotes.erase( it ); From 46c74d0a8149c741c0a1e1eef4dc8f2563a97735 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 27 May 2019 23:02:17 +0100 Subject: [PATCH 35/35] Making clearer the hierarchy. (#4967) Add `override` keyword --- include/TempoSyncKnobModel.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/TempoSyncKnobModel.h b/include/TempoSyncKnobModel.h index 52fab30bbab..9aaf48fea8b 100644 --- a/include/TempoSyncKnobModel.h +++ b/include/TempoSyncKnobModel.h @@ -52,10 +52,10 @@ class LMMS_EXPORT TempoSyncKnobModel : public FloatModel const float _max, const float _step, const float _scale, Model * _parent, const QString & _display_name = QString() ); - virtual ~TempoSyncKnobModel(); + virtual ~TempoSyncKnobModel() override; - void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name ); - void loadSettings( const QDomElement & _this, const QString& name ); + void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name ) override; + void loadSettings( const QDomElement & _this, const QString& name ) override; TempoSyncMode syncMode() const {