diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index acc5d1d41..664299cb7 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -25,6 +25,7 @@ #include "core.h" #include "panel/footageviewer/footageviewer.h" +#include "panel/timeline/timeline.h" #include "panel/panelmanager.h" #include "project/item/sequence/sequence.h" #include "widget/menu/menushared.h" @@ -113,6 +114,24 @@ void ProjectPanel::DeselectAll() explorer_->DeselectAll(); } +void ProjectPanel::Insert() +{ + TimelinePanel* timeline = PanelManager::instance()->MostRecentlyFocused(); + + if (timeline) { + timeline->InsertFootageAtPlayhead(GetSelectedFootage()); + } +} + +void ProjectPanel::Overwrite() +{ + TimelinePanel* timeline = PanelManager::instance()->MostRecentlyFocused(); + + if (timeline) { + timeline->OverwriteFootageAtPlayhead(GetSelectedFootage()); + } +} + void ProjectPanel::Edit(Item* item) { explorer_->Edit(item); @@ -156,3 +175,17 @@ void ProjectPanel::ProjectNameChanged() SetSubtitle(project()->name()); } } + +QList ProjectPanel::GetSelectedFootage() +{ + QList items = SelectedItems(); + QList footage; + + foreach (Item* i, items) { + if (i->type() == Item::kFootage) { + footage.append(static_cast(i)); + } + } + + return footage; +} diff --git a/app/panel/project/project.h b/app/panel/project/project.h index f6d6ed0fb..34bcbb669 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -46,6 +46,9 @@ public: virtual void SelectAll() override; virtual void DeselectAll() override; + virtual void Insert() override; + virtual void Overwrite() override; + public slots: void Edit(Item *item); @@ -60,6 +63,9 @@ private slots: void ShowNewMenu(); void ProjectNameChanged(); + + QList GetSelectedFootage(); + }; #endif // PROJECT_PANEL_H diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index f995a9fcc..2a66b85fb 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -89,6 +89,16 @@ void TimelinePanel::DecreaseTrackHeight() static_cast(GetTimeBasedWidget())->DecreaseTrackHeight(); } +void TimelinePanel::InsertFootageAtPlayhead(const QList &footage) +{ + static_cast(GetTimeBasedWidget())->InsertFootageAtPlayhead(footage); +} + +void TimelinePanel::OverwriteFootageAtPlayhead(const QList &footage) +{ + static_cast(GetTimeBasedWidget())->OverwriteFootageAtPlayhead(footage); +} + void TimelinePanel::Retranslate() { TimeBasedPanel::Retranslate(); diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index 2cfcec671..e04a69593 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -55,6 +55,10 @@ public: virtual void DecreaseTrackHeight() override; + void InsertFootageAtPlayhead(const QList &footage); + + void OverwriteFootageAtPlayhead(const QList &footage); + protected: virtual void Retranslate() override; diff --git a/app/widget/panel/panel.h b/app/widget/panel/panel.h index aca9a1f39..e324c149f 100644 --- a/app/widget/panel/panel.h +++ b/app/widget/panel/panel.h @@ -108,6 +108,12 @@ public: virtual void DeleteSelected(){} + virtual void RippleDelete(){} + + virtual void Insert(){} + + virtual void Overwrite(){} + virtual void IncreaseTrackHeight(){} virtual void DecreaseTrackHeight(){} diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index c6ef2566c..c0c5564cd 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -418,6 +418,11 @@ void TimelineWidget::DeleteSelected() Core::instance()->undo_stack()->pushIfHasChildren(command); } +void TimelineWidget::RippleDelete() +{ + +} + void TimelineWidget::IncreaseTrackHeight() { if (!GetConnectedNode()) { @@ -446,6 +451,16 @@ void TimelineWidget::DecreaseTrackHeight() } } +void TimelineWidget::InsertFootageAtPlayhead(const QList& footage) +{ + import_tool_->PlaceAt(footage, GetTime(), true); +} + +void TimelineWidget::OverwriteFootageAtPlayhead(const QList &footage) +{ + import_tool_->PlaceAt(footage, GetTime(), false); +} + QList TimelineWidget::GetSelectedBlocks() { QList list; diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index ac4e0208b..b9d4a389e 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -49,10 +49,16 @@ public: void DeleteSelected(); + void RippleDelete(); + void IncreaseTrackHeight(); void DecreaseTrackHeight(); + void InsertFootageAtPlayhead(const QList &footage); + + void OverwriteFootageAtPlayhead(const QList &footage); + QList GetSelectedBlocks(); signals: @@ -217,9 +223,15 @@ private: virtual void DragLeave(QDragLeaveEvent *event) override; virtual void DragDrop(TimelineViewMouseEvent *event) override; + 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 PrepGhosts(const rational &frame, const int &track_index); + + void DropGhosts(bool insert); + QList dragged_footage_; int import_pre_buffer_; @@ -355,7 +367,7 @@ private: QVector tools_; - Tool* import_tool_; + ImportTool* import_tool_; Tool* active_tool_; diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index d50873501..8445c3ebd 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -99,12 +99,8 @@ void TimelineWidget::ImportTool::DragEnter(TimelineViewMouseEvent *event) } } - if (parent()->GetConnectedNode()) { - FootageToGhosts(drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_), - dragged_footage_, - parent()->timebase(), - drag_start_.GetTrack().index()); - } + PrepGhosts(drag_start_.GetFrame() - parent()->SceneToTime(import_pre_buffer_), + drag_start_.GetTrack().index()); event->accept(); } else { @@ -179,175 +175,7 @@ void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event) { if (!dragged_footage_.isEmpty()) { - QUndoCommand* command = new QUndoCommand(); - - NodeGraph* dst_graph = nullptr; - ViewerOutput* viewer_node = nullptr; - Sequence* open_sequence = nullptr; - - if (parent()->GetConnectedNode()) { - viewer_node = parent()->GetConnectedNode(); - dst_graph = static_cast(parent()->GetConnectedNode()->parent()); - } else { - // There's no active timeline here, ask the user what to do - - DropWithoutSequenceBehavior behavior = static_cast(Config::Current()["DropWithoutSequenceBehavior"].toInt()); - - if (behavior == kDWSAsk) { - QCheckBox* dont_ask_again_box = new QCheckBox(tr("Don't ask me again")); - - QMessageBox mbox(parent()); - - mbox.setIcon(QMessageBox::Question); - mbox.setWindowTitle(tr("No Active Sequence")); - mbox.setText(tr("No sequence is currently open. Would you like to create one?")); - mbox.setCheckBox(dont_ask_again_box); - - QPushButton* auto_params_btn = mbox.addButton(tr("Automatically Detect Parameters From Footage"), QMessageBox::YesRole); - QPushButton* manual_params_btn = mbox.addButton(tr("Set Parameters Manually"), QMessageBox::NoRole); - mbox.addButton(QMessageBox::Cancel); - - mbox.exec(); - - if (mbox.clickedButton() == auto_params_btn) { - behavior = kDWSAuto; - } else if (mbox.clickedButton() == manual_params_btn) { - behavior = kDWSManual; - } else { - behavior = kDWSDisable; - } - - if (behavior != kDWSDisable && dont_ask_again_box->isChecked()) { - Config::Current()["DropWithoutSequenceBehavior"] = behavior; - } - } - - if (behavior != kDWSDisable) { - Project* active_project = Core::instance()->GetActiveProject(); - - if (active_project) { - SequencePtr new_sequence = Core::instance()->CreateNewSequenceForProject(active_project); - - new_sequence->set_default_parameters(); - - bool sequence_is_valid = true; - - if (behavior == kDWSAuto) { - - new_sequence->set_parameters_from_footage(dragged_footage_); - - } else { - - SequenceDialog sd(new_sequence.get(), SequenceDialog::kNew, parent()); - sd.SetUndoable(false); - - if (sd.exec() != QDialog::Accepted) { - sequence_is_valid = false; - } - - } - - if (sequence_is_valid) { - new_sequence->add_default_nodes(); - - new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(), - Core::instance()->GetSelectedFolderInActiveProject(), - new_sequence, - command); - - FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0); - - dst_graph = new_sequence.get(); - viewer_node = new_sequence->viewer_output(); - - // Set this as the sequence to open - open_sequence = new_sequence.get(); - } - } - } - } - - if (dst_graph) { - - QVector block_items(parent()->ghost_items_.size()); - - // Check if we're inserting - if (event->GetModifiers() & Qt::ControlModifier) { - InsertGapsAtGhostDestination(parent()->ghost_items_, command); - } - - for (int i=0;ighost_items_.size();i++) { - TimelineViewGhostItem* ghost = parent()->ghost_items_.at(i); - - StreamPtr footage_stream = ghost->data(TimelineViewGhostItem::kAttachedFootage).value(); - - ClipBlock* clip = new ClipBlock(); - clip->set_length_and_media_out(ghost->Length()); - clip->set_block_name(footage_stream->footage()->name()); - new NodeAddCommand(dst_graph, clip, command); - - switch (footage_stream->type()) { - case Stream::kVideo: - case Stream::kImage: - { - VideoInput* video_input = new VideoInput(); - video_input->SetFootage(footage_stream); - new NodeAddCommand(dst_graph, video_input, command); - new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command); - - TransformDistort* transform = new TransformDistort(); - new NodeAddCommand(dst_graph, transform, command); - new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command); - - //OpacityNode* opacity = new OpacityNode(); - //NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); - //NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); - break; - } - case Stream::kAudio: - { - AudioInput* audio_input = new AudioInput(); - audio_input->SetFootage(footage_stream); - new NodeAddCommand(dst_graph, audio_input, command); - - VolumeNode* volume_node = new VolumeNode(); - new NodeAddCommand(dst_graph, volume_node, command); - - new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command); - new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command); - break; - } - default: - break; - } - - new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()), - ghost->GetAdjustedTrack().index(), - clip, - ghost->GetAdjustedIn(), - command); - - block_items.replace(i, clip); - - // Link any clips so far that share the same Footage with this one - for (int j=0;jghost_items_.at(j)->data(TimelineViewGhostItem::kAttachedFootage).value(); - - if (footage_compare->footage() == footage_stream->footage()) { - Block::Link(block_items.at(j), clip); - } - } - } - } - - Core::instance()->undo_stack()->pushIfHasChildren(command); - - parent()->ClearGhosts(); - dragged_footage_.clear(); - - if (open_sequence) { - Sequence::Open(open_sequence); - } + DropGhosts(event->GetModifiers() & Qt::ControlModifier); event->accept(); } else { @@ -355,6 +183,13 @@ void TimelineWidget::ImportTool::DragDrop(TimelineViewMouseEvent *event) } } +void TimelineWidget::ImportTool::PlaceAt(const QList &footage, const rational &start, bool insert) +{ + dragged_footage_ = footage; + PrepGhosts(start, 0); + DropGhosts(insert); +} + void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QList &footage_list, const rational& dest_tb, const int& track_start) { foreach (Footage* footage, footage_list) { @@ -413,3 +248,186 @@ void TimelineWidget::ImportTool::FootageToGhosts(rational ghost_start, const QLi } } + +void TimelineWidget::ImportTool::PrepGhosts(const rational& frame, const int& track_index) +{ + if (parent()->GetConnectedNode()) { + FootageToGhosts(frame, + dragged_footage_, + parent()->timebase(), + track_index); + } +} + +void TimelineWidget::ImportTool::DropGhosts(bool insert) +{ + QUndoCommand* command = new QUndoCommand(); + + NodeGraph* dst_graph = nullptr; + ViewerOutput* viewer_node = nullptr; + Sequence* open_sequence = nullptr; + + if (parent()->GetConnectedNode()) { + viewer_node = parent()->GetConnectedNode(); + dst_graph = static_cast(parent()->GetConnectedNode()->parent()); + } else { + // There's no active timeline here, ask the user what to do + + DropWithoutSequenceBehavior behavior = static_cast(Config::Current()["DropWithoutSequenceBehavior"].toInt()); + + if (behavior == kDWSAsk) { + QCheckBox* dont_ask_again_box = new QCheckBox(tr("Don't ask me again")); + + QMessageBox mbox(parent()); + + mbox.setIcon(QMessageBox::Question); + mbox.setWindowTitle(tr("No Active Sequence")); + mbox.setText(tr("No sequence is currently open. Would you like to create one?")); + mbox.setCheckBox(dont_ask_again_box); + + QPushButton* auto_params_btn = mbox.addButton(tr("Automatically Detect Parameters From Footage"), QMessageBox::YesRole); + QPushButton* manual_params_btn = mbox.addButton(tr("Set Parameters Manually"), QMessageBox::NoRole); + mbox.addButton(QMessageBox::Cancel); + + mbox.exec(); + + if (mbox.clickedButton() == auto_params_btn) { + behavior = kDWSAuto; + } else if (mbox.clickedButton() == manual_params_btn) { + behavior = kDWSManual; + } else { + behavior = kDWSDisable; + } + + if (behavior != kDWSDisable && dont_ask_again_box->isChecked()) { + Config::Current()["DropWithoutSequenceBehavior"] = behavior; + } + } + + if (behavior != kDWSDisable) { + Project* active_project = Core::instance()->GetActiveProject(); + + if (active_project) { + SequencePtr new_sequence = Core::instance()->CreateNewSequenceForProject(active_project); + + new_sequence->set_default_parameters(); + + bool sequence_is_valid = true; + + if (behavior == kDWSAuto) { + + new_sequence->set_parameters_from_footage(dragged_footage_); + + } else { + + SequenceDialog sd(new_sequence.get(), SequenceDialog::kNew, parent()); + sd.SetUndoable(false); + + if (sd.exec() != QDialog::Accepted) { + sequence_is_valid = false; + } + + } + + if (sequence_is_valid) { + new_sequence->add_default_nodes(); + + new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(), + Core::instance()->GetSelectedFolderInActiveProject(), + new_sequence, + command); + + FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0); + + dst_graph = new_sequence.get(); + viewer_node = new_sequence->viewer_output(); + + // Set this as the sequence to open + open_sequence = new_sequence.get(); + } + } + } + } + + if (dst_graph) { + + QVector block_items(parent()->ghost_items_.size()); + + // Check if we're inserting + if (insert) { + InsertGapsAtGhostDestination(parent()->ghost_items_, command); + } + + for (int i=0;ighost_items_.size();i++) { + TimelineViewGhostItem* ghost = parent()->ghost_items_.at(i); + + StreamPtr footage_stream = ghost->data(TimelineViewGhostItem::kAttachedFootage).value(); + + ClipBlock* clip = new ClipBlock(); + clip->set_length_and_media_out(ghost->Length()); + clip->set_block_name(footage_stream->footage()->name()); + new NodeAddCommand(dst_graph, clip, command); + + switch (footage_stream->type()) { + case Stream::kVideo: + case Stream::kImage: + { + VideoInput* video_input = new VideoInput(); + video_input->SetFootage(footage_stream); + new NodeAddCommand(dst_graph, video_input, command); + new NodeEdgeAddCommand(video_input->output(), clip->texture_input(), command); + + TransformDistort* transform = new TransformDistort(); + new NodeAddCommand(dst_graph, transform, command); + new NodeEdgeAddCommand(transform->output(), video_input->matrix_input(), command); + + //OpacityNode* opacity = new OpacityNode(); + //NodeParam::ConnectEdge(opacity->texture_output(), clip->texture_input()); + //NodeParam::ConnectEdge(media->texture_output(), opacity->texture_input()); + break; + } + case Stream::kAudio: + { + AudioInput* audio_input = new AudioInput(); + audio_input->SetFootage(footage_stream); + new NodeAddCommand(dst_graph, audio_input, command); + + VolumeNode* volume_node = new VolumeNode(); + new NodeAddCommand(dst_graph, volume_node, command); + + new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command); + new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command); + break; + } + default: + break; + } + + new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()), + ghost->GetAdjustedTrack().index(), + clip, + ghost->GetAdjustedIn(), + command); + + block_items.replace(i, clip); + + // Link any clips so far that share the same Footage with this one + for (int j=0;jghost_items_.at(j)->data(TimelineViewGhostItem::kAttachedFootage).value(); + + if (footage_compare->footage() == footage_stream->footage()) { + Block::Link(block_items.at(j), clip); + } + } + } + } + + Core::instance()->undo_stack()->pushIfHasChildren(command); + + parent()->ClearGhosts(); + dragged_footage_.clear(); + + if (open_sequence) { + Sequence::Open(open_sequence); + } +} diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 41892358f..429a63282 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -75,6 +75,9 @@ MainMenu::MainMenu(QMainWindow *parent) : edit_menu_->addSeparator(); MenuShared::instance()->AddItemsForClipEditMenu(edit_menu_); edit_menu_->addSeparator(); + edit_insert_item_ = edit_menu_->AddItem("insert", this, SLOT(InsertTriggered()), ","); + edit_overwrite_item_ = edit_menu_->AddItem("overwrite", this, SLOT(OverwriteTriggered()), "."); + edit_menu_->addSeparator(); edit_ripple_to_in_item_ = edit_menu_->AddItem("rippletoin", this, SLOT(RippleToInTriggered()), "Q"); edit_ripple_to_out_item_ = edit_menu_->AddItem("rippletoout", this, SLOT(RippleToOutTriggered()), "W"); edit_edit_to_in_item_ = edit_menu_->AddItem("edittoin", this, SLOT(EditToInTriggered()), "Ctrl+Alt+Q"); @@ -442,6 +445,16 @@ void MainMenu::DeselectAllTriggered() PanelManager::instance()->CurrentlyFocused()->DeselectAll(); } +void MainMenu::InsertTriggered() +{ + PanelManager::instance()->CurrentlyFocused()->Insert(); +} + +void MainMenu::OverwriteTriggered() +{ + PanelManager::instance()->CurrentlyFocused()->Overwrite(); +} + void MainMenu::RippleToInTriggered() { PanelManager::instance()->CurrentlyFocused()->RippleToIn(); @@ -516,6 +529,8 @@ void MainMenu::Retranslate() edit_menu_->setTitle(tr("&Edit")); //edit_undo_item_->setText(tr("&Undo")); FIXME: Does Qt translate these automatically? //edit_redo_item_->setText(tr("Redo")); + edit_insert_item_->setText(tr("Insert")); + edit_overwrite_item_->setText(tr("Overwrite")); edit_select_all_item_->setText(tr("Select &All")); edit_deselect_all_item_->setText(tr("Deselect All")); edit_ripple_to_in_item_->setText(tr("Ripple to In Point")); diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index 9444dfad5..81476ab2d 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -123,6 +123,9 @@ private slots: void SelectAllTriggered(); void DeselectAllTriggered(); + void InsertTriggered(); + void OverwriteTriggered(); + void RippleToInTriggered(); void RippleToOutTriggered(); void EditToInTriggered(); @@ -160,6 +163,8 @@ private: QAction* edit_redo_item_; QAction* edit_select_all_item_; QAction* edit_deselect_all_item_; + QAction* edit_insert_item_; + QAction* edit_overwrite_item_; QAction* edit_ripple_to_in_item_; QAction* edit_ripple_to_out_item_; QAction* edit_edit_to_in_item_;