diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 4434eb531..6bd7b9c2a 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -272,6 +272,22 @@ QString Footage::rate() return QString(); } +quint64 Footage::get_enabled_stream_flags() const +{ + quint64 enabled_streams = 0; + quint64 stream_enabler = 1; + + foreach (StreamPtr s, streams_) { + if (s->enabled()) { + enabled_streams |= stream_enabler; + } + + stream_enabler <<= 1; + } + + return enabled_streams; +} + void Footage::ClearStreams() { if (streams_.empty()) { diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index 0e5f31ee9..27d1206d0 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -203,6 +203,8 @@ public: virtual QString rate() override; + quint64 get_enabled_stream_flags() const; + private: /** * @brief Internal function to delete all Stream children and empty the array diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index 6742f83a0..fa92470cf 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -272,7 +272,7 @@ QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const // Check if we've dragged this item before if (!dragged_items.contains(index.internalPointer())) { // If not, add it to the stream (and also keep track of it in the vector) - stream << index.row() << reinterpret_cast(index.internalPointer()); + stream << static_cast(index.internalPointer())->get_enabled_stream_flags() << index.row() << reinterpret_cast(index.internalPointer()); dragged_items.append(index.internalPointer()); } } @@ -316,6 +316,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action // Variables to deserialize into quintptr item_ptr; int r; + quint64 enabled_streams; // Loop through all data QUndoCommand* move_command = new QUndoCommand(); @@ -323,7 +324,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action move_command->setText(tr("Move Items")); while (!stream.atEnd()) { - stream >> r >> item_ptr; + stream >> enabled_streams >> r >> item_ptr; Item* item = reinterpret_cast(item_ptr); diff --git a/app/widget/playbackcontrols/CMakeLists.txt b/app/widget/playbackcontrols/CMakeLists.txt index 3506c7f6d..68f3a0799 100644 --- a/app/widget/playbackcontrols/CMakeLists.txt +++ b/app/widget/playbackcontrols/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + widget/playbackcontrols/dragbutton.h + widget/playbackcontrols/dragbutton.cpp widget/playbackcontrols/playbackcontrols.h widget/playbackcontrols/playbackcontrols.cpp PARENT_SCOPE diff --git a/app/widget/playbackcontrols/dragbutton.cpp b/app/widget/playbackcontrols/dragbutton.cpp new file mode 100644 index 000000000..aec32cc4e --- /dev/null +++ b/app/widget/playbackcontrols/dragbutton.cpp @@ -0,0 +1,12 @@ +#include "dragbutton.h" + +DragButton::DragButton(QWidget *parent) : + QPushButton(parent) +{ +} + +void DragButton::mousePressEvent(QMouseEvent *event) { + QPushButton::mousePressEvent(event); + + emit MousePressed(); +} diff --git a/app/widget/playbackcontrols/dragbutton.h b/app/widget/playbackcontrols/dragbutton.h new file mode 100644 index 000000000..2fc5fd629 --- /dev/null +++ b/app/widget/playbackcontrols/dragbutton.h @@ -0,0 +1,20 @@ +#ifndef DRAGBUTTON_H +#define DRAGBUTTON_H + +#include + +class DragButton : public QPushButton +{ + Q_OBJECT +public: + DragButton(QWidget* parent = nullptr); + +signals: + void MousePressed(); + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + +}; + +#endif // DRAGBUTTON_H diff --git a/app/widget/playbackcontrols/playbackcontrols.cpp b/app/widget/playbackcontrols/playbackcontrols.cpp index 400fdedba..6131327d9 100644 --- a/app/widget/playbackcontrols/playbackcontrols.cpp +++ b/app/widget/playbackcontrols/playbackcontrols.cpp @@ -37,12 +37,14 @@ PlaybackControls::PlaybackControls(QWidget *parent) : lower_control_layout->setSpacing(0); lower_control_layout->setMargin(0); - QSizePolicy lower_container_size_policy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + QSizePolicy lower_container_size_policy(QSizePolicy::Minimum, QSizePolicy::Expanding); + lower_container_size_policy.setHorizontalStretch(1); // In the lower-left, we create a current timecode label wrapped in a QWidget for fixed sizing lower_left_container_ = new QWidget(); lower_left_container_->setVisible(false); lower_left_container_->setSizePolicy(lower_container_size_policy); + lower_control_layout->addWidget(lower_left_container_); QHBoxLayout* lower_left_layout = new QHBoxLayout(lower_left_container_); @@ -54,6 +56,12 @@ PlaybackControls::PlaybackControls(QWidget *parent) : lower_left_layout->addWidget(cur_tc_lbl_); lower_left_layout->addStretch(); + // This is only here + QWidget* blank_widget = new QWidget(); + //new QHBoxLayout(blank_widget); + blank_widget->setSizePolicy(lower_container_size_policy); + lower_control_layout->addWidget(blank_widget); + // In the lower-middle, we create playback control buttons QWidget* lower_middle_container = new QWidget(); lower_middle_container->setSizePolicy(lower_container_size_policy); @@ -63,8 +71,6 @@ PlaybackControls::PlaybackControls(QWidget *parent) : lower_middle_layout->setSpacing(0); lower_middle_layout->setMargin(0); - lower_middle_layout->addStretch(); - // Go To Start Button go_to_start_btn_ = new QPushButton(); lower_middle_layout->addWidget(go_to_start_btn_); @@ -101,7 +107,20 @@ PlaybackControls::PlaybackControls(QWidget *parent) : lower_middle_layout->addWidget(go_to_end_btn_); connect(go_to_end_btn_, &QPushButton::clicked, this, &PlaybackControls::EndClicked); - lower_middle_layout->addStretch(); + QWidget* av_btn_widget = new QWidget(); + av_btn_widget->setSizePolicy(lower_container_size_policy); + QHBoxLayout* av_btn_layout = new QHBoxLayout(av_btn_widget); + av_btn_layout->setSpacing(0); + av_btn_layout->setMargin(0); + video_drag_btn_ = new DragButton(); + connect(video_drag_btn_, &QPushButton::clicked, this, &PlaybackControls::VideoClicked); + connect(video_drag_btn_, &DragButton::MousePressed, this, &PlaybackControls::VideoPressed); + av_btn_layout->addWidget(video_drag_btn_); + audio_drag_btn_ = new DragButton(); + connect(audio_drag_btn_, &QPushButton::clicked, this, &PlaybackControls::AudioClicked); + connect(audio_drag_btn_, &DragButton::MousePressed, this, &PlaybackControls::AudioPressed); + av_btn_layout->addWidget(audio_drag_btn_); + lower_control_layout->addWidget(av_btn_widget); // The lower-right, we create another timecode label, this time to show the end timecode lower_right_container_ = new QWidget(); @@ -121,6 +140,8 @@ PlaybackControls::PlaybackControls(QWidget *parent) : SetTimebase(0); + SetAudioVideoDragButtonsVisible(false); + connect(Core::instance(), &Core::TimecodeDisplayChanged, this, &PlaybackControls::TimecodeChanged); } @@ -138,6 +159,12 @@ void PlaybackControls::SetTimebase(const rational &r) cur_tc_lbl_->setEnabled(!r.isNull()); } +void PlaybackControls::SetAudioVideoDragButtonsVisible(bool e) +{ + video_drag_btn_->setVisible(e); + audio_drag_btn_->setVisible(e); +} + void PlaybackControls::SetTime(const int64_t &r) { cur_tc_lbl_->SetValue(r); @@ -184,6 +211,8 @@ void PlaybackControls::UpdateIcons() pause_btn_->setIcon(icon::Pause); next_frame_btn_->setIcon(icon::NextFrame); go_to_end_btn_->setIcon(icon::GoToEnd); + video_drag_btn_->setIcon(icon::Video); + audio_drag_btn_->setIcon(icon::Audio); } void PlaybackControls::TimecodeChanged() diff --git a/app/widget/playbackcontrols/playbackcontrols.h b/app/widget/playbackcontrols/playbackcontrols.h index 9ad983f0b..4e7562146 100644 --- a/app/widget/playbackcontrols/playbackcontrols.h +++ b/app/widget/playbackcontrols/playbackcontrols.h @@ -27,6 +27,7 @@ #include #include "common/rational.h" +#include "dragbutton.h" #include "widget/slider/timeslider.h" /** @@ -47,6 +48,8 @@ public: void SetTimebase(const rational& r); + void SetAudioVideoDragButtonsVisible(bool e); + public slots: void SetTime(const int64_t &r); @@ -87,6 +90,14 @@ signals: */ void EndClicked(); + void AudioClicked(); + + void VideoClicked(); + + void AudioPressed(); + + void VideoPressed(); + void TimeChanged(const int64_t& t); protected: @@ -111,6 +122,8 @@ private: QPushButton* pause_btn_; QPushButton* next_frame_btn_; QPushButton* go_to_end_btn_; + DragButton* video_drag_btn_; + DragButton* audio_drag_btn_; QStackedWidget* playpause_stack_; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index d25221fa9..456f0fe44 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -254,6 +254,22 @@ void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n) } } +TimelineWidget::DraggedFootage TimelineWidget::FootageToDraggedFootage(Footage *f) +{ + return DraggedFootage(f, f->get_enabled_stream_flags()); +} + +QList TimelineWidget::FootageToDraggedFootage(QList footage) +{ + QList df; + + foreach (Footage* f, footage) { + df.append(FootageToDraggedFootage(f)); + } + + return df; +} + void TimelineWidget::SelectAll() { foreach (TimelineAndTrackView* view, views_) { diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 3c011cb42..cca1a6a9e 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -79,6 +79,32 @@ protected: virtual void DisconnectNodeInternal(ViewerOutput* n) override; private: + class DraggedFootage { + public: + DraggedFootage(Footage* f, quint64 streams) : + footage_(f), + streams_(streams) + { + } + + Footage* footage() const { + return footage_; + } + + const quint64& streams() const { + return streams_; + } + + private: + Footage* footage_; + + quint64 streams_; + + }; + + static DraggedFootage FootageToDraggedFootage(Footage* f); + static QList FootageToDraggedFootage(QList footage); + class Tool { public: @@ -227,16 +253,17 @@ private: virtual void DragLeave(QDragLeaveEvent *event) override; virtual void DragDrop(TimelineViewMouseEvent *event) override; - void PlaceAt(const QList& footage, const rational& start, bool insert); + void PlaceAt(const QList &footage, const rational& start, bool insert); + void PlaceAt(const QList &footage, const rational& start, bool insert); private: - void FootageToGhosts(rational ghost_start, const QList& footage, const rational &dest_tb, const int &track_start); + void FootageToGhosts(rational ghost_start, const QList& footage, const rational &dest_tb, const int &track_start); void PrepGhosts(const rational &frame, const int &track_index); void DropGhosts(bool insert); - QList dragged_footage_; + QList dragged_footage_; int import_pre_buffer_; diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index c4a30de08..da94c4df7 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -79,6 +79,7 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event) // Variables to deserialize into quintptr item_ptr; int r; + quint64 enabled_streams; // Set drag start position drag_start_ = event->GetCoordinates(); @@ -86,7 +87,7 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event) snap_points_.clear(); while (!stream.atEnd()) { - stream >> r >> item_ptr; + stream >> enabled_streams >> r >> item_ptr; // Get Item object Item* item = reinterpret_cast(item_ptr); @@ -95,7 +96,7 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event) if (item->type() == Item::kFootage) { // If the Item is Footage, we can create a Ghost from it - dragged_footage_.append(static_cast(item)); + dragged_footage_.append(DraggedFootage(static_cast(item), enabled_streams)); } } @@ -175,7 +176,6 @@ void TimelineWidget::ImportTool::DragLeave(QDragLeaveEvent* event) void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event) { if (!dragged_footage_.isEmpty()) { - DropGhosts(event->GetModifiers() & Qt::ControlModifier); event->accept(); @@ -185,6 +185,11 @@ void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event) } void TimelineWidget::ImportTool::PlaceAt(const QList &footage, const rational &start, bool insert) +{ + PlaceAt(FootageToDraggedFootage(footage), start, insert); +} + +void TimelineWidget::ImportTool::PlaceAt(const QList &footage, const rational &start, bool insert) { dragged_footage_ = footage; @@ -196,9 +201,9 @@ void TimelineWidget::ImportTool::PlaceAt(const QList &footage, const DropGhosts(insert); } -void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QList &footage_list, const rational& dest_tb, const int& track_start) +void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QList &footage_list, const rational& dest_tb, const int& track_start) { - foreach (Footage* footage, footage_list) { + foreach (const DraggedFootage& footage, footage_list) { // Each stream is offset by one track per track "type", we keep track of them in this vector QVector track_offsets(Timeline::kTrackTypeCount); @@ -206,12 +211,18 @@ void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QLi rational footage_duration; + quint64 enabled_streams = footage.streams(); + // Loop through all streams in footage - foreach (StreamPtr stream, footage->streams()) { + foreach (StreamPtr stream, footage.footage()->streams()) { Timeline::TrackType track_type = TrackTypeFromStreamType(stream->type()); + quint64 cached_enabled_streams = enabled_streams; + enabled_streams >>= 1; + // Check if this stream has a compatible TrackList - if (track_type == Timeline::kTrackTypeNone || !stream->enabled()) { + if (track_type == Timeline::kTrackTypeNone + || !(cached_enabled_streams & 0x1)) { continue; } @@ -322,7 +333,13 @@ void TimelineWidget::ImportTool::DropGhosts(bool insert) if (behavior == kDWSAuto) { - new_sequence->set_parameters_from_footage(dragged_footage_); + QList footage_only; + + foreach (const DraggedFootage& df, dragged_footage_) { + footage_only.append(df.footage()); + } + + new_sequence->set_parameters_from_footage(footage_only); } else { diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 6045e5534..706e63dc5 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -14,6 +14,10 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : viewer_node_ = new ViewerOutput(); connect(gl_widget_, &ViewerGLWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); + + controls_->SetAudioVideoDragButtonsVisible(true); + connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag); + connect(controls_, &PlaybackControls::AudioPressed, this, &FootageViewerWidget::StartAudioDrag); } Footage *FootageViewerWidget::GetFootage() const @@ -69,10 +73,10 @@ void FootageViewerWidget::SetFootage(Footage *footage) TimelinePoints *FootageViewerWidget::ConnectTimelinePoints() { - return footage_ ? footage_ : nullptr; + return footage_; } -void FootageViewerWidget::StartFootageDrag() +void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enable_audio) { if (!GetFootage()) { return; @@ -84,10 +88,41 @@ void FootageViewerWidget::StartFootageDrag() QByteArray encoded_data; QDataStream stream(&encoded_data, QIODevice::WriteOnly); - stream << -1 << reinterpret_cast(GetFootage()); + quint64 enabled_stream_flags = GetFootage()->get_enabled_stream_flags(); + + // Disable streams that have been disabled + if (!enable_video || !enable_audio) { + quint64 stream_disabler = 0x1; + + foreach (StreamPtr s, GetFootage()->streams()) { + if ((s->type() == Stream::kVideo && !enable_video) + || (s->type() == Stream::kAudio && !enable_audio)) { + enabled_stream_flags &= ~stream_disabler; + } + + stream_disabler <<= 1; + } + } + + stream << enabled_stream_flags << -1 << reinterpret_cast(GetFootage()); mimedata->setData("application/x-oliveprojectitemdata", encoded_data); drag->setMimeData(mimedata); drag->exec(); } + +void FootageViewerWidget::StartFootageDrag() +{ + StartFootageDragInternal(true, true); +} + +void FootageViewerWidget::StartVideoDrag() +{ + StartFootageDragInternal(true, false); +} + +void FootageViewerWidget::StartAudioDrag() +{ + StartFootageDragInternal(false, true); +} diff --git a/app/widget/viewer/footageviewer.h b/app/widget/viewer/footageviewer.h index dc1634291..39358b841 100644 --- a/app/widget/viewer/footageviewer.h +++ b/app/widget/viewer/footageviewer.h @@ -19,6 +19,8 @@ protected: virtual TimelinePoints* ConnectTimelinePoints() override; private: + void StartFootageDragInternal(bool enable_video, bool enable_audio); + Footage* footage_; VideoInput* video_node_; @@ -30,6 +32,10 @@ private: private slots: void StartFootageDrag(); + void StartVideoDrag(); + + void StartAudioDrag(); + }; #endif // FOOTAGEVIEWERWIDGET_H diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 4c3c642dd..272a93645 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -123,6 +123,8 @@ protected: ViewerGLWidget* gl_widget_; + PlaybackControls* controls_; + private: void UpdateTimeInternal(int64_t i); @@ -140,8 +142,6 @@ private: ViewerSizer* sizer_; - PlaybackControls* controls_; - qint64 start_msec_; int64_t start_timestamp_;