diff --git a/data/themes/classic/cursor_knife.png b/data/themes/classic/cursor_knife.png new file mode 100644 index 00000000000..23dd3331be5 Binary files /dev/null and b/data/themes/classic/cursor_knife.png differ diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index d4c3671e692..e471d3a90b2 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -598,10 +598,10 @@ FxLine { color: #e0e0e0; qproperty-backgroundActive: qlineargradient(spread:reflect, x1:0, y1:0, x2:1, y2:0, stop:0 #7b838d, stop:1 #6b7581 ); - qproperty-strokeOuterActive: rgb( 0, 0, 0 ); - qproperty-strokeOuterInactive: rgba( 0, 0, 0, 50 ); - qproperty-strokeInnerActive: rgba( 255, 255, 255, 100 ); - qproperty-strokeInnerInactive: rgba( 255, 255, 255, 50 ); + qproperty-strokeOuterActive: rgb( 0, 0, 0 ); + qproperty-strokeOuterInactive: rgba( 0, 0, 0, 50 ); + qproperty-strokeInnerActive: rgba( 255, 255, 255, 100 ); + qproperty-strokeInnerInactive: rgba( 255, 255, 255, 50 ); } /* persistent peak markers for fx peak meters */ @@ -658,7 +658,8 @@ TrackContentObjectView { qproperty-gradient: true; /* boolean property, set true to have a gradient */ /* finger tip offset of cursor */ qproperty-mouseHotspotHand: 3px 3px; - + qproperty-mouseHotspotKnife: 0px 0px; + font-size: 11px; } diff --git a/data/themes/default/cursor_knife.png b/data/themes/default/cursor_knife.png new file mode 100644 index 00000000000..23dd3331be5 Binary files /dev/null and b/data/themes/default/cursor_knife.png differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 334322da024..a406aafa09f 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -704,7 +704,8 @@ TrackContentObjectView { qproperty-gradient: false; /* boolean property, set true to have a gradient */ /* finger tip offset of cursor */ qproperty-mouseHotspotHand: 7px 2px; - + qproperty-mouseHotspotKnife: 0px 0px; + font-size: 11px; } diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 0cdc213181a..039aa59369a 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -93,7 +93,6 @@ public slots: BoolModel m_recordModel; bool m_isPlaying; - friend class SampleTCOView; @@ -132,6 +131,7 @@ public slots: private: SampleTCO * m_tco; QPixmap m_paintPixmap; + bool splitTCO( const TimePos pos ) override; } ; diff --git a/include/SongEditor.h b/include/SongEditor.h index 8d279c594a5..9c59e3328c4 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -55,6 +55,7 @@ class SongEditor : public TrackContainerView enum EditMode { DrawMode, + KnifeMode, SelectMode }; @@ -77,6 +78,7 @@ public slots: void setEditMode( EditMode mode ); void setEditModeDraw(); + void setEditModeKnife(); void setEditModeSelect(); void toggleProportionalSnap(); @@ -112,6 +114,7 @@ private slots: void wheelEvent( QWheelEvent * we ) override; bool allowRubberband() const override; + bool knifeMode() const override; int trackIndexFromSelectionPoint(int yPos); int indexOfTrackView(const TrackView* tv); @@ -173,6 +176,7 @@ class SongEditorWindow : public Editor QSize sizeHint() const override; SongEditor* m_editor; + void syncEditMode(); protected: void resizeEvent( QResizeEvent * event ) override; @@ -194,9 +198,6 @@ protected slots: void resized(); private: - void keyPressEvent( QKeyEvent * ke ) override; - void keyReleaseEvent( QKeyEvent * ke ) override; - QAction* m_addBBTrackAction; QAction* m_addSampleTrackAction; QAction* m_addAutomationTrackAction; @@ -204,6 +205,7 @@ protected slots: ActionGroup * m_editModeGroup; QAction* m_drawModeAction; + QAction* m_knifeModeAction; QAction* m_selectModeAction; QAction* m_crtlAction; diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 0edd63865f6..ebc4e42a6ea 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -80,6 +80,7 @@ class TrackContainerView : public QWidget, public ModelView, const TrackView * trackViewAt( const int _y ) const; virtual bool allowRubberband() const; + virtual bool knifeMode() const; inline bool rubberBandActive() const { diff --git a/include/TrackContentObjectView.h b/include/TrackContentObjectView.h index e9461c5dd50..5f54d39f333 100644 --- a/include/TrackContentObjectView.h +++ b/include/TrackContentObjectView.h @@ -58,6 +58,7 @@ class TrackContentObjectView : public selectableObject, public ModelView // We have to use a QSize here because using QPoint isn't supported. // width -> x, height -> y Q_PROPERTY( QSize mouseHotspotHand WRITE setMouseHotspotHand ) + Q_PROPERTY( QSize mouseHotspotKnife WRITE setMouseHotspotKnife ) public: TrackContentObjectView( TrackContentObject * tco, TrackView * tv ); @@ -93,6 +94,7 @@ class TrackContentObjectView : public selectableObject, public ModelView void setBBPatternBackground( const QColor & c ); void setGradient( const bool & b ); void setMouseHotspotHand(const QSize & s); + void setMouseHotspotKnife(const QSize & s); // access needsUpdate member variable bool needsUpdate(); @@ -118,6 +120,9 @@ class TrackContentObjectView : public selectableObject, public ModelView QColor getColorForDisplay( QColor ); + void inline setMarkerPos(int x) { m_markerPos = x; } + void inline setMarkerEnabled(bool e) { m_marker = e; } + public slots: virtual bool close(); void remove(); @@ -137,6 +142,13 @@ public slots: Merge }; + TrackView * m_trackView; + TimePos m_initialTCOPos; + TimePos m_initialTCOEnd; + + bool m_marker = false; + int m_markerPos = 0; + virtual void constructContextMenu( QMenu * ) { } @@ -145,7 +157,6 @@ public slots: void contextMenuAction( ContextMenuAction action ); void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; - void leaveEvent( QEvent * e ) override; void mousePressEvent( QMouseEvent * me ) override; void mouseMoveEvent( QMouseEvent * me ) override; void mouseReleaseEvent( QMouseEvent * me ) override; @@ -155,6 +166,9 @@ public slots: selectableObject::resizeEvent( re ); } + bool unquantizedModHeld( QMouseEvent * me ); + TimePos quantizeSplitPos( TimePos, bool shiftMode ); + float pixelsPerBar(); @@ -176,6 +190,7 @@ protected slots: MoveSelection, Resize, ResizeLeft, + Split, CopySelection, ToggleSelected } ; @@ -183,12 +198,9 @@ protected slots: static TextFloat * s_textFloat; TrackContentObject * m_tco; - TrackView * m_trackView; Actions m_action; QPoint m_initialMousePos; QPoint m_initialMouseGlobalPos; - TimePos m_initialTCOPos; - TimePos m_initialTCOEnd; QVector m_initialOffsets; TextFloat * m_hint; @@ -202,7 +214,10 @@ protected slots: QColor m_textShadowColor; QColor m_BBPatternBackground; bool m_gradient; - QSize m_mouseHotspotHand; // QSize must be used because QPoint isn't supported by property system + QSize m_mouseHotspotHand; // QSize must be used because QPoint + QSize m_mouseHotspotKnife; // isn't supported by property system + QCursor m_cursorHand; + QCursor m_cursorKnife; bool m_cursorSetYet; bool m_needsUpdate; @@ -217,6 +232,10 @@ protected slots: bool mouseMovedDistance( QMouseEvent * me, int distance ); TimePos draggedTCOPos( QMouseEvent * me ); + int knifeMarkerPos( QMouseEvent * me ); + //! Return true iff TCO could be split. Currently only implemented for samples + virtual bool splitTCO( const TimePos pos ){ return false; }; + void updateCursor(QMouseEvent * me); } ; diff --git a/include/TrackContentWidget.h b/include/TrackContentWidget.h index c2764bfc6b4..37362319b76 100644 --- a/include/TrackContentWidget.h +++ b/include/TrackContentWidget.h @@ -99,6 +99,7 @@ public slots: void dragEnterEvent( QDragEnterEvent * dee ) override; void dropEvent( QDropEvent * de ) override; void mousePressEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; void paintEvent( QPaintEvent * pe ) override; void resizeEvent( QResizeEvent * re ) override; diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 2ee33586812..ffd9d550270 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -308,6 +308,14 @@ bool TrackContainerView::allowRubberband() const +bool TrackContainerView::knifeMode() const +{ + return false; +} + + + + void TrackContainerView::setPixelsPerBar( int ppb ) { m_ppb = ppb; @@ -374,7 +382,7 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) //it->toggledInstrumentTrackButton( true ); _de->accept(); } - else if( type == "samplefile" || type == "pluginpresetfile" + else if( type == "samplefile" || type == "pluginpresetfile" || type == "soundfontfile" || type == "vstpluginfile" || type == "patchfile" ) { diff --git a/src/gui/TrackContentObjectView.cpp b/src/gui/TrackContentObjectView.cpp index 6aa78a1dd6e..49f539b72e2 100644 --- a/src/gui/TrackContentObjectView.cpp +++ b/src/gui/TrackContentObjectView.cpp @@ -77,13 +77,13 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, TrackView * tv ) : selectableObject( tv->getTrackContentWidget() ), ModelView( NULL, this ), - m_tco( tco ), m_trackView( tv ), + m_initialTCOPos( TimePos(0) ), + m_initialTCOEnd( TimePos(0) ), + m_tco( tco ), m_action( NoAction ), m_initialMousePos( QPoint( 0, 0 ) ), m_initialMouseGlobalPos( QPoint( 0, 0 ) ), - m_initialTCOPos( TimePos(0) ), - m_initialTCOEnd( TimePos(0) ), m_initialOffsets( QVector() ), m_hint( NULL ), m_mutedColor( 0, 0, 0 ), @@ -94,6 +94,9 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, m_BBPatternBackground( 0, 0, 0 ), m_gradient( true ), m_mouseHotspotHand( 0, 0 ), + m_mouseHotspotKnife( 0, 0 ), + m_cursorHand( QCursor( embed::getIconPixmap( "hand" ) ) ), + m_cursorKnife( QCursor( embed::getIconPixmap( "cursor_knife" ) ) ), m_cursorSetYet( false ), m_needsUpdate( true ) { @@ -106,7 +109,7 @@ TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco, setAttribute( Qt::WA_OpaquePaintEvent, true ); setAttribute( Qt::WA_DeleteOnClose, true ); setFocusPolicy( Qt::StrongFocus ); - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + setCursor( m_cursorHand ); move( 0, 0 ); show(); @@ -159,7 +162,9 @@ void TrackContentObjectView::update() { if( !m_cursorSetYet ) { - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + m_cursorHand = QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ); + m_cursorKnife = QCursor( embed::getIconPixmap( "cursor_knife" ), m_mouseHotspotKnife.width(), m_mouseHotspotKnife.height() ); + setCursor( m_cursorHand ); m_cursorSetYet = true; } @@ -248,6 +253,11 @@ void TrackContentObjectView::setMouseHotspotHand(const QSize & s) m_mouseHotspotHand = s; } +void TrackContentObjectView::setMouseHotspotKnife(const QSize & s) +{ + m_mouseHotspotKnife = s; +} + // access needsUpdate member variable bool TrackContentObjectView::needsUpdate() { return m_needsUpdate; } @@ -439,22 +449,32 @@ void TrackContentObjectView::dropEvent( QDropEvent * de ) -/*! \brief Handle a dragged selection leaving our 'airspace'. +/* @brief Chooses the correct cursor to be displayed on the widget * - * \param e The QEvent to watch. + * @param me The QMouseEvent that is triggering the cursor change */ -void TrackContentObjectView::leaveEvent( QEvent * e ) +void TrackContentObjectView::updateCursor(QMouseEvent * me) { - if( cursor().shape() != Qt::BitmapCursor ) + SampleTCO * sTco = dynamic_cast(m_tco); + + // If we are at the edges, use the resize cursor + if ((me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize()) + || (me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize())) { - setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) ); + setCursor(Qt::SizeHorCursor); } - if( e != NULL ) + // If we are in the middle on knife mode, use the knife cursor + else if (sTco && m_trackView->trackContainerView()->knifeMode()) { - QWidget::leaveEvent( e ); + setCursor(m_cursorKnife); } + // If we are in the middle in any other mode, use the hand cursor + else { setCursor(m_cursorHand); } } + + + /*! \brief Create a DataFile suitable for copying multiple trackContentObjects. * * trackContentObjects in the vector are written to the "tcos" node in the @@ -568,7 +588,10 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) setInitialOffsets(); if( !fixedTCOs() && me->button() == Qt::LeftButton ) { - if( me->modifiers() & Qt::ControlModifier ) + SampleTCO * sTco = dynamic_cast( m_tco ); + const bool knifeMode = m_trackView->trackContainerView()->knifeMode(); + + if ( me->modifiers() & Qt::ControlModifier && !(sTco && knifeMode) ) { if( isSelected() ) { @@ -579,9 +602,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) m_action = ToggleSelected; } } - else if( !me->modifiers() - || (me->modifiers() & Qt::AltModifier) - || (me->modifiers() & Qt::ShiftModifier) ) + else { if( isSelected() ) { @@ -592,28 +613,43 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) gui->songEditor()->m_editor->selectAllTcos( false ); m_tco->addJournalCheckPoint(); - // move or resize - m_tco->setJournalling( false ); + // Move, Resize and ResizeLeft + // Split action doesn't disable TCO journalling + if (m_action == Move || m_action == Resize || m_action == ResizeLeft) + { + m_tco->setJournalling(false); + } setInitialPos( me->pos() ); setInitialOffsets(); - SampleTCO * sTco = dynamic_cast( m_tco ); - if( me->x() < RESIZE_GRIP_WIDTH && sTco - && !m_tco->getAutoResize() ) + if( m_tco->getAutoResize() ) + { // Always move clips that can't be manually resized + m_action = Move; + setCursor( Qt::SizeAllCursor ); + } + else if( me->x() >= width() - RESIZE_GRIP_WIDTH ) + { + m_action = Resize; + setCursor( Qt::SizeHorCursor ); + } + else if( me->x() < RESIZE_GRIP_WIDTH && sTco ) { m_action = ResizeLeft; setCursor( Qt::SizeHorCursor ); } - else if( m_tco->getAutoResize() || me->x() < width() - RESIZE_GRIP_WIDTH ) + else if( sTco && knifeMode ) { - m_action = Move; - setCursor( Qt::SizeAllCursor ); + m_action = Split; + setCursor( m_cursorKnife ); + setMarkerPos( knifeMarkerPos( me ) ); + setMarkerEnabled( true ); + update(); } else { - m_action = Resize; - setCursor( Qt::SizeHorCursor ); + m_action = Move; + setCursor( Qt::SizeAllCursor ); } if( m_action == Move ) @@ -641,7 +677,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) // s_textFloat->reparent( this ); // setup text-float as if TCO was already moved/resized s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); - s_textFloat->show(); + if ( m_action != Split) { s_textFloat->show(); } } delete m_hint; @@ -662,6 +698,16 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { remove( active ); } + if (m_action == Split) + { + m_action = NoAction; + SampleTCO * sTco = dynamic_cast( m_tco ); + if (sTco) + { + setMarkerEnabled( false ); + update(); + } + } } else if( me->button() == Qt::MidButton ) { @@ -790,8 +836,6 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) } else if( m_action == Resize || m_action == ResizeLeft ) { - // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize - const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier); const float snapSize = gui->songEditor()->m_editor->getSnapSize(); // Length in ticks of one snap increment const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) ); @@ -801,7 +845,8 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) // The clip's new length TimePos l = static_cast( me->x() * TimePos::ticksPerBar() / ppb ); - if ( unquantized ) + // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize + if ( unquantizedModHeld(me) ) { // We want to preserve this adjusted offset, // even if the user switches to snapping later setInitialPos( m_initialMousePos ); @@ -837,7 +882,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) m_trackView->trackContainerView()->currentPosition() + static_cast( x * TimePos::ticksPerBar() / ppb ) ); - if( unquantized ) + if( unquantizedModHeld(me) ) { // We want to preserve this adjusted offset, // even if the user switches to snapping later setInitialPos( m_initialMousePos ); @@ -882,19 +927,17 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) TimePos::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); } - else + else if( m_action == Split ) { SampleTCO * sTco = dynamic_cast( m_tco ); - if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() ) - || ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize() ) ) - { - setCursor( Qt::SizeHorCursor ); - } - else - { - leaveEvent( NULL ); + if (sTco) { + setCursor( m_cursorKnife ); + setMarkerPos( knifeMarkerPos( me ) ); } + update(); } + // None of the actions above, we will just handle the cursor + else { updateCursor(me); } } @@ -918,17 +961,26 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) { setSelected( !isSelected() ); } - - if( m_action == Move || m_action == Resize || m_action == ResizeLeft ) + else if( m_action == Move || m_action == Resize || m_action == ResizeLeft ) { // TODO: Fix m_tco->setJournalling() consistency m_tco->setJournalling( true ); } + else if( m_action == Split ) + { + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + const TimePos relPos = me->pos().x() * TimePos::ticksPerBar() / ppb; + splitTCO(unquantizedModHeld(me) ? + relPos : + quantizeSplitPos(relPos, me->modifiers() & Qt::ShiftModifier) + ); + } + m_action = NoAction; delete m_hint; m_hint = NULL; s_textFloat->hide(); - leaveEvent( NULL ); + updateCursor(me); selectableObject::mouseReleaseEvent( me ); } @@ -1271,6 +1323,15 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance + +bool TrackContentObjectView::unquantizedModHeld( QMouseEvent * me ) +{ + return me->modifiers() & Qt::ControlModifier || me->modifiers() & Qt::AltModifier; +} + + + + /*! \brief Calculate the new position of a dragged TCO from a mouse event * * @@ -1285,12 +1346,8 @@ TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) TimePos newPos = m_initialTCOPos + mouseOff * TimePos::ticksPerBar() / ppb; TimePos offset = newPos - m_initialTCOPos; // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize - if ( me->button() != Qt::NoButton - || (me->modifiers() & Qt::ControlModifier) - || (me->modifiers() & Qt::AltModifier) ) - { - // We want to preserve this adjusted offset, - // even if the user switches to snapping + if ( me->button() != Qt::NoButton || unquantizedModHeld(me) ) + { // We want to preserve this adjusted offset, even if the user switches to snapping setInitialPos( m_initialMousePos ); } else if ( me->modifiers() & Qt::ShiftModifier ) @@ -1313,6 +1370,50 @@ TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) } +int TrackContentObjectView::knifeMarkerPos( QMouseEvent * me ) +{ + //Position relative to start of clip + const int markerPos = me->pos().x(); + + //In unquantized mode, we don't have to mess with the position at all + if ( unquantizedModHeld(me) ) { return markerPos; } + else + { //Otherwise we... + //1: Convert the position to a TimePos + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); + TimePos midiPos = markerPos * TimePos::ticksPerBar() / ppb; + //2: Snap to the correct position, based on modifier keys + midiPos = quantizeSplitPos( midiPos, me->modifiers() & Qt::ShiftModifier ); + //3: Convert back to a pixel position + return midiPos * ppb / TimePos::ticksPerBar(); + } +} + + + + +TimePos TrackContentObjectView::quantizeSplitPos( TimePos midiPos, bool shiftMode ) +{ + const float snapSize = gui->songEditor()->m_editor->getSnapSize(); + if ( shiftMode ) + { //If shift is held we quantize the length of the new left clip... + const TimePos leftPos = midiPos.quantize( snapSize ); + //...or right clip... + const TimePos rightOff = m_tco->length() - midiPos; + const TimePos rightPos = m_tco->length() - rightOff.quantize( snapSize ); + //...whichever gives a position closer to the cursor + if ( abs(leftPos - midiPos) < abs(rightPos - midiPos) ) { return leftPos; } + else { return rightPos; } + } + else + { + return TimePos(midiPos + m_initialTCOPos).quantize( snapSize ) - m_initialTCOPos; + } +} + + + + // Return the color that the TCO's background should be QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor ) { @@ -1355,4 +1456,3 @@ QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor ) // Return color to caller return c; } - diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index c20ac7a3b7f..5a148ff8a37 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -449,6 +449,11 @@ void SongEditor::setEditModeDraw() setEditMode(DrawMode); } +void SongEditor::setEditModeKnife() +{ + setEditMode(KnifeMode); +} + void SongEditor::setEditModeSelect() { setEditMode(SelectMode); @@ -860,6 +865,14 @@ bool SongEditor::allowRubberband() const +bool SongEditor::knifeMode() const +{ + return m_mode == KnifeMode; +} + + + + int SongEditor::trackIndexFromSelectionPoint(int yPos) { const TrackView * tv = trackViewAt(yPos - m_timeLine->height()); @@ -944,13 +957,16 @@ SongEditorWindow::SongEditorWindow(Song* song) : m_editModeGroup = new ActionGroup(this); m_drawModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_draw"), tr("Draw mode")); + m_knifeModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_knife"), tr("Knife mode (split sample clips)")); m_selectModeAction = m_editModeGroup->addAction(embed::getIconPixmap("edit_select"), tr("Edit mode (select and move)")); m_drawModeAction->setChecked(true); connect(m_drawModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeDraw())); + connect(m_knifeModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeKnife())); connect(m_selectModeAction, SIGNAL(triggered()), m_editor, SLOT(setEditModeSelect())); editActionsToolBar->addAction( m_drawModeAction ); + editActionsToolBar->addAction( m_knifeModeAction ); editActionsToolBar->addAction( m_selectModeAction ); DropToolBar *timeLineToolBar = addDropToolBarToTop(tr("Timeline controls")); @@ -1031,6 +1047,13 @@ void SongEditorWindow::updateSnapLabel(){ +void SongEditorWindow::syncEditMode(){ + m_editModeGroup->checkedAction()->trigger(); +} + + + + void SongEditorWindow::resizeEvent(QResizeEvent *event) { emit resized(); @@ -1108,31 +1131,3 @@ void SongEditorWindow::adjustUiAfterProjectLoad() connect( qobject_cast( parentWidget() ), SIGNAL( focusLost() ), this, SLOT( lostFocus() ) ); m_editor->scrolled(0); } - - - - -void SongEditorWindow::keyPressEvent( QKeyEvent *ke ) -{ - if( ke->key() == Qt::Key_Control ) - { - m_crtlAction = m_editModeGroup->checkedAction(); - m_selectModeAction->setChecked( true ); - m_selectModeAction->trigger(); - } -} - - - - -void SongEditorWindow::keyReleaseEvent( QKeyEvent *ke ) -{ - if( ke->key() == Qt::Key_Control ) - { - if( m_crtlAction ) - { - m_crtlAction->setChecked( true ); - m_crtlAction->trigger(); - } - } -} diff --git a/src/gui/widgets/TrackContentWidget.cpp b/src/gui/widgets/TrackContentWidget.cpp index 11ac3a60f0b..d7dee1a1ba1 100644 --- a/src/gui/widgets/TrackContentWidget.cpp +++ b/src/gui/widgets/TrackContentWidget.cpp @@ -547,14 +547,22 @@ void TrackContentWidget::dropEvent( QDropEvent * de ) */ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) { + // Enable box select if control is held when clicking an empty space + // (If we had clicked a TCO it would have intercepted the mouse event) + if( me->modifiers() & Qt::ControlModifier ){ + gui->songEditor()->m_editor->setEditMode(SongEditor::EditMode::SelectMode); + } + // Forward event to allow box select if the editor supports it and is in that mode if( m_trackView->trackContainerView()->allowRubberband() == true ) { QWidget::mousePressEvent( me ); } + // Forward shift clicks so tracks can be resized else if( me->modifiers() & Qt::ShiftModifier ) { QWidget::mousePressEvent( me ); } + // For an unmodified click, create a new TCO else if( me->button() == Qt::LeftButton && !m_trackView->trackContainerView()->fixedTCOs() ) { @@ -573,6 +581,15 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) +void TrackContentWidget::mouseReleaseEvent( QMouseEvent * me ) +{ + gui->songEditor()->syncEditMode(); + QWidget::mouseReleaseEvent(me); +} + + + + /*! \brief Repaint the trackContentWidget on command * * \param pe the Paint Event to respond to @@ -707,4 +724,3 @@ void TrackContentWidget::setGridColor( const QBrush & c ) //! \brief CSS theming qproperty access method void TrackContentWidget::setEmbossColor( const QBrush & c ) { m_embossColor = c; } - diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index fd48217f633..8a5ecc66837 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -106,9 +106,6 @@ SampleTCO::SampleTCO( Track * _track ) : updateTrackTcos(); } - - - SampleTCO::SampleTCO(const SampleTCO& orig) : SampleTCO(orig.getTrack()) { @@ -634,6 +631,10 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) embed::getIconPixmap( "muted", size, size ) ); } + if ( m_marker ) + { + p.drawLine(m_markerPos, rect().bottom(), m_markerPos, rect().top()); + } // recording sample tracks is not possible at the moment /* if( m_tco->isRecord() ) @@ -667,6 +668,39 @@ void SampleTCOView::reverseSample() +//! Split this TCO. +/*! \param pos the position of the split, relative to the start of the clip */ +bool SampleTCOView::splitTCO( const TimePos pos ) +{ + setMarkerEnabled( false ); + + const TimePos splitPos = m_initialTCOPos + pos; + + //Don't split if we slid off the TCO or if we're on the clip's start/end + //Cutting at exactly the start/end position would create a zero length + //clip (bad), and a clip the same length as the original one (pointless). + if ( splitPos > m_initialTCOPos && splitPos < m_initialTCOEnd ) + { + m_tco->getTrack()->addJournalCheckPoint(); + m_tco->getTrack()->saveJournallingState( false ); + + SampleTCO * rightTCO = new SampleTCO ( *m_tco ); + + m_tco->changeLength( splitPos - m_initialTCOPos ); + + rightTCO->movePosition( splitPos ); + rightTCO->changeLength( m_initialTCOEnd - splitPos ); + rightTCO->setStartTimeOffset( m_tco->startTimeOffset() - m_tco->length() ); + + m_tco->getTrack()->restoreJournallingState(); + return true; + } + else { return false; } +} + + + + SampleTrack::SampleTrack(TrackContainer* tc) :