diff --git a/include/AutomationClip.h b/include/AutomationClip.h index f59917eaffc..ae511f08401 100644 --- a/include/AutomationClip.h +++ b/include/AutomationClip.h @@ -103,6 +103,11 @@ class LMMS_EXPORT AutomationClip : public Clip const bool ignoreSurroundingPoints = true ); + bool isEmpty() override + { + return m_timeMap.empty(); + } + void removeNode(const TimePos & time); void removeNodes(const int tick0, const int tick1); diff --git a/include/Clip.h b/include/Clip.h index e3c29138254..27b191f7c74 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -49,6 +49,7 @@ class TrackView; class LMMS_EXPORT Clip : public Model, public JournallingObject { Q_OBJECT + mapPropertyFromModel(bool,isHidden,setHidden,m_hiddenModel); mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); public: @@ -98,6 +99,11 @@ class LMMS_EXPORT Clip : public Model, public JournallingObject bool isInPattern() const; + virtual bool isEmpty() + { + return false; + } + bool manuallyResizable() const; /*! \brief Set whether a clip has been resized yet by the user or the knife tool. @@ -171,6 +177,7 @@ public slots: TimePos m_length; TimePos m_startTimeOffset; + BoolModel m_hiddenModel; BoolModel m_mutedModel; BoolModel m_soloModel; bool m_autoResize = true; diff --git a/include/MidiClip.h b/include/MidiClip.h index d8553e87eab..8d232e32429 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -93,6 +93,11 @@ class LMMS_EXPORT MidiClip : public Clip return m_clipType; } + bool isEmpty() override + { + return !m_notes.size(); + } + // next/previous track based on position in the containing track MidiClip * previousMidiClip() const; diff --git a/include/PatternEditor.h b/include/PatternEditor.h index 0ea0061203a..526601973a5 100644 --- a/include/PatternEditor.h +++ b/include/PatternEditor.h @@ -68,6 +68,8 @@ public slots: protected slots: void dropEvent(QDropEvent * de ) override; void resizeEvent(QResizeEvent* de) override; + void showAllTracks(); + void hideEmptyTracks(); void updatePosition(); void updatePixelsPerBar(); diff --git a/include/SampleClip.h b/include/SampleClip.h index ea7853386e6..64f3f57c79f 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -79,6 +79,11 @@ class SampleClip : public Clip void setIsPlaying(bool isPlaying); void setSampleBuffer(std::shared_ptr sb); + bool isEmpty() override + { + return !m_sample.sampleSize(); + } + SampleClip* clone() override { return new SampleClip(*this); diff --git a/include/Track.h b/include/Track.h index 92cb38f26e6..972a12d0c5f 100644 --- a/include/Track.h +++ b/include/Track.h @@ -138,6 +138,9 @@ class LMMS_EXPORT Track : public Model, public JournallingObject void createClipsForPattern(int pattern); + bool hiddenForPattern(size_t pattern); + void setVisibilityForPattern(bool hidden, size_t pattern); + void hideForPatternIfEmpty(size_t pattern); void insertBar( const TimePos & pos ); void removeBar( const TimePos & pos ); @@ -238,6 +241,7 @@ public slots: void nameChanged(); void clipAdded( lmms::Clip * ); void colorChanged(); + void visibilityChanged(); } ; diff --git a/include/TrackOperationsWidget.h b/include/TrackOperationsWidget.h index ed3b72c81b2..ed9500102a4 100644 --- a/include/TrackOperationsWidget.h +++ b/include/TrackOperationsWidget.h @@ -63,6 +63,7 @@ private slots: void recordingOn(); void recordingOff(); void clearTrack(); + void hideTrack(); private: TrackView * m_trackView; diff --git a/include/TrackView.h b/include/TrackView.h index 4954071927a..2113bd13f1e 100644 --- a/include/TrackView.h +++ b/include/TrackView.h @@ -104,9 +104,13 @@ class TrackView : public QWidget, public ModelView, public JournallingObject // Currently instrument track and sample track supports it virtual QMenu * createMixerMenu(QString title, QString newMixerLabel); + void setVisibleForThisPattern(bool hidden); + void hideIfEmpty(); + public slots: virtual bool close(); + void visibilityChanged(); protected: diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index 32739482b74..22b1cb920da 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -785,6 +785,7 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "name", name() ); _this.setAttribute( "prog", QString::number( static_cast(progressionType()) ) ); _this.setAttribute( "tens", QString::number( getTension() ) ); + _this.setAttribute( "hidden", QString::number( isHidden() ) ); _this.setAttribute( "mute", QString::number( isMuted() ) ); _this.setAttribute("off", startTimeOffset()); _this.setAttribute("autoresize", QString::number(getAutoResize())); @@ -837,6 +838,7 @@ void AutomationClip::loadSettings( const QDomElement & _this ) setProgressionType( static_cast( _this.attribute( "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); + setHidden(_this.attribute( "hidden", QString::number( false ) ).toInt() ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); setAutoResize(_this.attribute("autoresize", "1").toInt()); setStartTimeOffset(_this.attribute("off").toInt()); diff --git a/src/core/Clip.cpp b/src/core/Clip.cpp index 3e769f4ea13..ed2324809f3 100644 --- a/src/core/Clip.cpp +++ b/src/core/Clip.cpp @@ -55,6 +55,8 @@ Clip::Clip( Track * track ) : if( getTrack() ) { getTrack()->addClip( this ); + connect(&m_hiddenModel, SIGNAL(dataChanged()), + getTrack(), SIGNAL(visibilityChanged())); } setJournalling( false ); movePosition( 0 ); @@ -84,6 +86,8 @@ Clip::Clip(const Clip& other): if (getTrack()) { getTrack()->addClip(this); + connect(&m_hiddenModel, SIGNAL(dataChanged()), + getTrack(), SIGNAL(visibilityChanged())); } } diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index e279f76236e..b7dcaf8b573 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -274,6 +274,7 @@ void SampleClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "pos", startPosition() ); } _this.setAttribute( "len", length() ); + _this.setAttribute( "hidden", isHidden() ); _this.setAttribute( "muted", isMuted() ); _this.setAttribute( "src", sampleFile() ); _this.setAttribute( "off", startTimeOffset() ); @@ -324,6 +325,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) m_sample = Sample(std::move(buffer)); } changeLength( _this.attribute( "len" ).toInt() ); + setHidden( _this.attribute( "hidden" ).toInt() ); setMuted( _this.attribute( "muted" ).toInt() ); setStartTimeOffset( _this.attribute( "off" ).toInt() ); setAutoResize(_this.attribute("autoresize", "1").toInt()); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index e44475d9374..3ae38b4172c 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -505,6 +505,33 @@ void Track::createClipsForPattern(int pattern) } +bool Track::hiddenForPattern(size_t pattern) +{ + if (pattern >= m_clips.size()) + return false; + return m_clips[pattern]->isHidden(); +} + +void Track::setVisibilityForPattern(bool hidden, size_t pattern) +{ + if (pattern < m_clips.size()) + { + m_clips[pattern]->setHidden(hidden); + } +} + +void Track::hideForPatternIfEmpty(size_t pattern) +{ + if (pattern < m_clips.size()) + { + if (m_clips[pattern]->isEmpty()) + { + m_clips[pattern]->setHidden(true); + } + } +} + + /*! \brief Move all the clips after a certain time later by one bar. diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 6c3b66e29b5..24f51065f14 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -191,6 +191,23 @@ void PatternEditor::resizeEvent(QResizeEvent* re) } +void PatternEditor::showAllTracks() +{ + for (const auto& trackView : trackViews()) + { + trackView->setVisibleForThisPattern(false); + } +} + +void PatternEditor::hideEmptyTracks() +{ + for (const auto& trackView : trackViews()) + { + trackView->hideIfEmpty(); + } +} + + void PatternEditor::updatePosition() { //realignTracks(); @@ -322,6 +339,10 @@ PatternEditorWindow::PatternEditorWindow(PatternStore* ps) : m_editor, SLOT(addSampleTrack())); trackAndStepActionsToolBar->addAction(embed::getIconPixmap("add_automation"), tr("Add automation-track"), m_editor, SLOT(addAutomationTrack())); + trackAndStepActionsToolBar->addAction(embed::getIconPixmap(""), tr("Show all"), + m_editor, SLOT(showAllTracks())); + trackAndStepActionsToolBar->addAction(embed::getIconPixmap(""), tr("Hide empty"), + m_editor, SLOT(hideEmptyTracks())); auto stretch = new QWidget(m_toolBar); stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index 16c0ca3e424..adad435b79c 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -43,6 +43,7 @@ #include "InstrumentTrackView.h" #include "lmms_math.h" #include "KeyboardShortcuts.h" +#include "PatternStore.h" #include "Song.h" #include "StringPairDrag.h" #include "Track.h" @@ -236,6 +237,12 @@ void TrackOperationsWidget::clearTrack() } +void TrackOperationsWidget::hideTrack() +{ + m_trackView->setVisibleForThisPattern(true); +} + + /*! \brief Remove this track from the track list * */ @@ -313,6 +320,10 @@ void TrackOperationsWidget::updateMenu() { toMenu->addAction( tr( "Clear this track" ), this, SLOT(clearTrack())); } + if (m_trackView->getTrack()->trackContainer() == Engine::patternStore()) + { + toMenu->addAction(tr( "Hide this track for current pattern" ), this, SLOT(hideTrack())); + } if (QMenu *mixerMenu = m_trackView->createMixerMenu(tr("Channel %1: %2"), tr("Assign to new Mixer Channel"))) { toMenu->addMenu(mixerMenu); diff --git a/src/gui/tracks/TrackView.cpp b/src/gui/tracks/TrackView.cpp index 1d282338f6f..34e68204e00 100644 --- a/src/gui/tracks/TrackView.cpp +++ b/src/gui/tracks/TrackView.cpp @@ -39,6 +39,7 @@ #include "DeprecationHelper.h" #include "Engine.h" #include "FadeButton.h" +#include "PatternStore.h" #include "StringPairDrag.h" #include "Track.h" #include "TrackGrip.h" @@ -95,6 +96,8 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : this, SLOT(createClipView(lmms::Clip*)), Qt::QueuedConnection ); + connect(m_track, SIGNAL(visibilityChanged()), this, SLOT(visibilityChanged())); + connect( &m_track->m_mutedModel, SIGNAL(dataChanged()), &m_trackContentWidget, SLOT(update())); @@ -115,6 +118,7 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : } m_trackContainerView->addTrackView( this ); + visibilityChanged(); } @@ -157,7 +161,7 @@ void TrackView::update() { m_trackContentWidget.changePosition(); } - QWidget::update(); + visibilityChanged(); } @@ -435,6 +439,16 @@ void TrackView::createClipView( Clip * clip ) +void TrackView::setVisibleForThisPattern(bool hidden) +{ + m_track->setVisibilityForPattern(hidden, Engine::patternStore()->currentPattern()); +} +void TrackView::hideIfEmpty() +{ + m_track->hideForPatternIfEmpty(Engine::patternStore()->currentPattern()); +} + + void TrackView::muteChanged() { @@ -443,6 +457,17 @@ void TrackView::muteChanged() } +void TrackView::visibilityChanged() +{ + PatternStore* ps = Engine::patternStore(); + if (m_track->trackContainer() == ps) + { + setVisible(!m_track->hiddenForPattern(ps->currentPattern())); + } + QWidget::update(); +} + + void TrackView::onTrackGripGrabbed() { m_action = Action::Move; diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index f1909f04b2d..dc392ff7b79 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -451,6 +451,7 @@ void MidiClip::exportToXML(QDomDocument& doc, QDomElement& midiClipElement, bool { midiClipElement.setAttribute("pos", startPosition()); } + midiClipElement.setAttribute("hidden", isHidden()); midiClipElement.setAttribute("muted", isMuted()); midiClipElement.setAttribute("steps", m_steps); midiClipElement.setAttribute("len", length()); @@ -489,6 +490,8 @@ void MidiClip::loadSettings( const QDomElement & _this ) { movePosition( _this.attribute( "pos" ).toInt() ); } + setHidden(static_cast(_this.attribute("hidden").toInt())); + if (static_cast(_this.attribute("muted").toInt()) != isMuted()) { toggleMute();