Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/Clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ class LMMS_EXPORT Clip : public Model, public JournallingObject

bool manuallyResizable() const;

// Returns whether the clip can be looped
virtual bool loopable() const
{
return false;
}

/*! \brief Set whether a clip has been resized yet by the user or the knife tool.
*
* If a clip has been resized previously, it will not automatically
Expand All @@ -116,6 +122,25 @@ class LMMS_EXPORT Clip : public Model, public JournallingObject
return m_autoResize;
}

int loopCount() const
{
return m_loopCount;
}

// Increase/decrease loop count by one
// Note : does not create / close the corresponding view
virtual void increaseLoopCount()
{
++m_loopCount;
}
virtual void decreaseLoopCount()
{
if (m_loopCount > 0)
{
--m_loopCount;
}
}

auto color() const -> const std::optional<QColor>& { return m_color; }
void setColor(const std::optional<QColor>& color);

Expand Down Expand Up @@ -170,6 +195,7 @@ public slots:
TimePos m_startPosition;
TimePos m_length;
TimePos m_startTimeOffset;
int m_loopCount;

BoolModel m_mutedModel;
BoolModel m_soloModel;
Expand Down
28 changes: 26 additions & 2 deletions include/ClipView.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ClipView : public selectableObject, public ModelView
public:
const static int BORDER_WIDTH = 2;

ClipView( Clip * clip, TrackView * tv );
ClipView(Clip * clip, TrackView * tv, int offset = 0);
~ClipView() override;

bool fixedClips();
Expand All @@ -81,6 +81,11 @@ class ClipView : public selectableObject, public ModelView
return m_trackView;
}

inline int offset() const
{
return m_offset;
}

// qproperty access func
QColor mutedColor() const;
QColor mutedBackgroundColor() const;
Expand Down Expand Up @@ -135,14 +140,19 @@ public slots:
void randomizeColor();
void resetColor();

signals:
void closing();
void extandLoop();

protected:
enum class ContextMenuAction
{
Remove,
Cut,
Copy,
Paste,
Mute
Mute,
Loop
};

TrackView * m_trackView;
Expand Down Expand Up @@ -181,9 +191,22 @@ public slots:

auto hasCustomColor() const -> bool;

inline bool lastLoopView()
{
return m_offset == m_clip->loopCount();
}

protected slots:
void updateLength();
void updatePosition();
void closeLoopViews();

/**
* Create a new loop view
*/
virtual void loop()
{
};


private:
Expand All @@ -202,6 +225,7 @@ protected slots:
static TextFloat * s_textFloat;

Clip * m_clip;
int m_offset; // Offset of the View from the Clip, in Clip's lengths (offset != 0 => loop view)
Action m_action;
QPoint m_initialMousePos;
QPoint m_initialMouseGlobalPos;
Expand Down
2 changes: 2 additions & 0 deletions include/MidiClip.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class LMMS_EXPORT MidiClip : public Clip
return m_clipType;
}

bool loopable() const override;


// next/previous track based on position in the containing track
MidiClip * previousMidiClip() const;
Expand Down
4 changes: 3 additions & 1 deletion include/MidiClipView.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MidiClipView : public ClipView
Q_OBJECT

public:
MidiClipView( MidiClip* clip, TrackView* parent );
MidiClipView(MidiClip* clip, TrackView* parent, int offset = 0);
~MidiClipView() override = default;

Q_PROPERTY(QColor noteFillColor READ getNoteFillColor WRITE setNoteFillColor)
Expand Down Expand Up @@ -83,6 +83,8 @@ protected slots:
void transposeSelection();
void clearNotesOutOfBounds();

virtual void loop();


protected:
void constructContextMenu( QMenu * ) override;
Expand Down
2 changes: 2 additions & 0 deletions src/core/Clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Clip::Clip( Track * track ) :
m_track( track ),
m_startPosition(),
m_length(),
m_loopCount(0),
m_mutedModel( false, this, tr( "Mute" ) ),
m_selectViewOnCreate{false}
{
Expand Down Expand Up @@ -76,6 +77,7 @@ Clip::Clip(const Clip& other):
m_startPosition(other.m_startPosition),
m_length(other.m_length),
m_startTimeOffset(other.m_startTimeOffset),
m_loopCount(0),
m_mutedModel(other.m_mutedModel.value(), this, tr( "Mute" )),
m_autoResize(other.m_autoResize),
m_selectViewOnCreate{other.m_selectViewOnCreate},
Expand Down
2 changes: 1 addition & 1 deletion src/core/Track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ void Track::getClipsInRange( clipVector & clipV, const TimePos & start,
for( Clip* clip : m_clips )
{
int s = clip->startPosition();
int e = clip->endPosition();
int e = clip->endPosition() + (clip->loopCount() * clip->length());
if( ( s <= end ) && ( e >= start ) )
{
// Clip is within given range
Expand Down
70 changes: 60 additions & 10 deletions src/gui/clips/ClipView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,14 @@ TextFloat * ClipView::s_textFloat = nullptr;
* \param _tv The track view that will contain the new object
*/
ClipView::ClipView( Clip * clip,
TrackView * tv ) :
TrackView * tv , int offset) :
selectableObject( tv->getTrackContentWidget() ),
ModelView( nullptr, this ),
m_trackView( tv ),
m_initialClipPos( TimePos(0) ),
m_initialClipEnd( TimePos(0) ),
m_clip( clip ),
m_offset( offset ),
m_action( Action::None ),
m_initialMousePos( QPoint( 0, 0 ) ),
m_initialMouseGlobalPos( QPoint( 0, 0 ) ),
Expand Down Expand Up @@ -131,6 +132,11 @@ ClipView::ClipView( Clip * clip,
if (!m_clip->color().has_value()) { update(); }
});

if (offset != 0)
{
clip->increaseLoopCount();
}

m_trackView->getTrackContentWidget()->addClipView( this );
updateLength();
updatePosition();
Expand Down Expand Up @@ -264,6 +270,10 @@ void ClipView::setNeedsUpdate( bool b )
*/
bool ClipView::close()
{
if (m_offset != 0)
{
m_clip->decreaseLoopCount();
}
m_trackView->getTrackContentWidget()->removeClipView( this );
return QWidget::close();
}
Expand Down Expand Up @@ -343,6 +353,15 @@ void ClipView::updatePosition()
m_trackView->trackContainerView()->update();
}

void ClipView::closeLoopViews()
{
if (m_offset != 0)
{
closing();
close();
}
}

void ClipView::selectColor()
{
// Get a color from the user
Expand Down Expand Up @@ -486,16 +505,23 @@ void ClipView::updateCursor(QMouseEvent * me)
{
const auto posX = position(me).x();

// If we are at the edges, use the resize cursor
if (!me->buttons() && m_clip->manuallyResizable() && !isSelected()
// If we are at the edges, use the resize cursor (loop views are not allowed to be resized directly)
if (!me->buttons() && m_clip->manuallyResizable() && !isSelected() && !m_offset
&& ((posX > width() - RESIZE_GRIP_WIDTH) || (posX < RESIZE_GRIP_WIDTH)))
{
setCursor(Qt::SizeHorCursor);
}
// If we are in the middle on knife mode, use the knife cursor
else if (m_trackView->trackContainerView()->knifeMode() && !isSelected())
{
setCursor(Qt::SplitHCursor);
if (m_offset == 0)
{
setCursor(Qt::SplitHCursor);
}
else // Knife mode have no effect on loop views, we use the Forbidden cursor
{
setCursor(Qt::ForbiddenCursor);
}
}
// If we are in the middle in any other mode, use the hand cursor
else { setCursor(Qt::PointingHandCursor); }
Expand Down Expand Up @@ -719,7 +745,7 @@ void ClipView::mousePressEvent( QMouseEvent * me )
{
hint = tr("Press <%1> and drag to make a copy.");
}
else if (m_action == Action::Split)
else if (m_action == Action::Split && m_offset == 0)
{
hint = dynamic_cast<MidiClipView*>(this)
? tr("Press <%1> or <Alt> for unquantized splitting.\nPress <Shift> for destructive splitting.")
Expand All @@ -743,10 +769,12 @@ void ClipView::mousePressEvent( QMouseEvent * me )
{
remove( active );
}
if (m_action == Action::Split)
if (m_action == Action::Split && m_offset == 0)
{
m_action = Action::None;
setMarkerEnabled(false);
// Destroy the loop
closing();
update();
}
}
Expand All @@ -758,7 +786,15 @@ void ClipView::mousePressEvent( QMouseEvent * me )
}
else if( !fixedClips() )
{
remove( active );
closing();
if (m_offset)
{
close();
}
else
{
remove( active );
}
}
}
}
Expand Down Expand Up @@ -873,7 +909,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me )
( *it )->movePosition( newPos + m_initialOffsets[index] );
}
}
else if( m_action == Action::Resize || m_action == Action::ResizeLeft )
else if( ( m_action == Action::Resize || m_action == Action::ResizeLeft ) && !m_offset ) // Loop views can't be resized directly
{
const float snapSize = getGUI()->songEditor()->m_editor->getSnapSize();
// Length in ticks of one snap increment
Expand Down Expand Up @@ -987,8 +1023,9 @@ void ClipView::mouseMoveEvent( QMouseEvent * me )
arg( m_clip->endPosition().getTicks() %
TimePos::ticksPerBar() ) );
s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) );
updatePosition();
}
else if( m_action == Action::Split )
else if( m_action == Action::Split && m_offset == 0 )
{
setCursor(Qt::SplitHCursor);
setMarkerPos(knifeMarkerPos(me));
Expand Down Expand Up @@ -1024,7 +1061,7 @@ void ClipView::mouseReleaseEvent( QMouseEvent * me )
// TODO: Fix m_clip->setJournalling() consistency
m_clip->setJournalling( true );
}
else if( m_action == Action::Split )
else if( m_action == Action::Split && m_offset == 0 )
{
const float ppb = m_trackView->trackContainerView()->pixelsPerBar();
const TimePos relPos = position(me).x() * TimePos::ticksPerBar() / ppb;
Expand All @@ -1037,6 +1074,8 @@ void ClipView::mouseReleaseEvent( QMouseEvent * me )
splitClip(unquantizedModHeld(me) ? relPos : quantizeSplitPos(relPos));
}
setMarkerEnabled(false);
// Destroy loop
closing();
}

m_action = Action::None;
Expand Down Expand Up @@ -1103,6 +1142,14 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme )
tr( "Paste" ),
[this](){ contextMenuAction( ContextMenuAction::Paste ); } );

if (m_clip->loopable())
{
contextMenu.addAction(
embed::getIconPixmap( "loop_points_on" ),
tr( "Loop" ),
[this](){ contextMenuAction( ContextMenuAction::Loop ); } );
}

contextMenu.addSeparator();

contextMenu.addAction(
Expand Down Expand Up @@ -1155,6 +1202,9 @@ void ClipView::contextMenuAction( ContextMenuAction action )
case ContextMenuAction::Mute:
toggleMute( active );
break;
case ContextMenuAction::Loop:
loop();
break;
}
}

Expand Down
Loading
Loading