cleanup and included closing footage viewer if footage is in use

This commit is contained in:
itsmattkc
2020-10-05 01:04:45 +11:00
parent fa8e7ab5ee
commit d552e71845
7 changed files with 206 additions and 200 deletions
+96 -122
View File
@@ -32,6 +32,7 @@
#include "core.h"
#include "dialog/footageproperties/footageproperties.h"
#include "dialog/sequence/sequence.h"
#include "projectexplorerundo.h"
#include "task/precache/precachetask.h"
#include "task/taskmanager.h"
#include "widget/menu/menu.h"
@@ -539,104 +540,28 @@ void ProjectExplorer::DeselectAll()
CurrentView()->selectionModel()->clearSelection();
}
QMap<Node*, StreamPtr> ProjectExplorer::GetFootageNodes(Item* item)
QList<MediaInput *> ProjectExplorer::GetMediaNodesUsingFootage(Footage *item)
{
// Output list list
QMap<Node*, StreamPtr> footage_nodes;
QList<MediaInput *> list;
// Get all sequences.
QList<ItemPtr> sequences = model_.project()->get_items_of_type(Item::kSequence);
// Get item pointer.
ItemPtr item_ptr = item->get_shared_ptr();
if (item_ptr.get()->type() == Item::kFootage) {
// If no sequences exist we don't need to do anything clever here
if (!sequences.isEmpty()) {
// Footage can contain multiple streams, all of which need to be dealt with
foreach (StreamPtr stream, static_cast<Footage*>(item_ptr.get())->streams()) {
// Check each sequence to see if it contains the footage in question
foreach (ItemPtr seq, sequences) {
Sequence* s = static_cast<Sequence*>(seq.get());
// Footage can contain multiple streams, all of which need to be dealt with
foreach (ItemPtr s, sequences) {
const QList<Node*>& nodes = static_cast<Sequence*>(s.get())->nodes();
foreach (Node* n, nodes) {
if (n->IsMedia()) {
MediaInput* media_node = static_cast<MediaInput*>(n);
// Loop through nodes to find our Footage node
foreach (Node* node, s->nodes()) {
// Check if node is of the right type
if (node->IsMedia()){
// Check the streams are the same
if (static_cast<MediaInput*>(node)->footage() == stream) {
footage_nodes.insert(node, stream);
}
}
}
if (media_node->footage()->footage() == item) {
list.append(media_node);
}
}
}
}
return footage_nodes;
}
QList<Block*> ProjectExplorer::GetFootageBlocks(QList<Node*> nodes)
{
// Get all sequences.
QList<ItemPtr> sequences = model_.project()->get_items_of_type(Item::kSequence);
QList<Block*> blocks;
foreach (ItemPtr seq, sequences) {
Sequence* s = static_cast<Sequence*>(seq.get());
// Loop through nodes in sequence
foreach (Node* node, s->nodes()) {
// For each Block see if it solely depends on one of our input nodes and if so add to the block list
if (node->IsBlock()) {
int footage_deps = 0;
QList<Node*> dependancies = node->GetDependencies();
QSet<Node*> intersection = QSet<Node*>(dependancies.begin(), dependancies.end())
.intersect(QSet<Node*>(nodes.begin(), nodes.end()));
if (!intersection.isEmpty()) {
// Count how many Media inputs this block depends on
foreach (Node* dep, dependancies) {
if (dep->IsMedia()) {
footage_deps++;
}
}
// If it only depends on one input we can safely delete it
if (footage_deps == 1) {
blocks.append(static_cast<Block*>(node));
}
}
}
}
}
return blocks;
}
ProjectExplorer::FootageDeleteResponse ProjectExplorer::DeleteWarningMessage(Item* item)
{
ItemPtr item_ptr = item->get_shared_ptr();
if (item_ptr->type() == Item::kFootage) {
QString clip_name = static_cast<Footage*>(item_ptr.get())->filename().split("/").last();
QMessageBox msgBox;
msgBox.setWindowTitle(clip_name);
msgBox.setText(clip_name + tr(" is in use."));
QPushButton* offline = msgBox.addButton(tr("Offline Footage"), QMessageBox::ApplyRole);
QPushButton* deleteClips = msgBox.addButton(tr("Delete Clips"), QMessageBox::ApplyRole);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
if (msgBox.clickedButton() == offline) {
return kOffline;
}
if (msgBox.clickedButton() == deleteClips) {
return kDelete;
}
}
return kCancel;
return list;
}
void ProjectExplorer::DeleteSelected()
@@ -650,60 +575,109 @@ void ProjectExplorer::DeleteSelected()
QUndoCommand* command = new QUndoCommand();
foreach (Item* item, selected) {
ItemPtr item_ptr = item->get_shared_ptr();
// If this is a sequence, close it
if (item_ptr->type() == Item::kSequence) {
Sequence* s = static_cast<Sequence*>(item_ptr.get());
// Verify whether this item is in use anywhere
switch (item->type()) {
case Item::kSequence:
{
// If this is a sequence, check if it's open and close it if necessary
Sequence* s = static_cast<Sequence*>(item);
if (Core::instance()->main_window()->IsSequenceOpen(s)) {
Core::instance()->main_window()->CloseSequence(s);
}
new ProjectViewModel::RemoveItemCommand(&model_, item_ptr, command);
break;
}
case Item::kFootage:
{
// If this is footage, check if it's used anywhere in any sequence
Footage* footage = static_cast<Footage*>(item);
// If this is a footage item, clean up if necessary
if (item_ptr->type() == Item::kFootage) {
// Check if nodes exists
QMap<Node*, StreamPtr> footage_nodes = GetFootageNodes(item);
if (!footage_nodes.isEmpty()){
// Warn user and ask them what to do
FootageDeleteResponse response = DeleteWarningMessage(item);
if (response == kOffline) {
new ProjectViewModel::OfflineFootageCommand(&model_, item_ptr, footage_nodes, command);
QList<MediaInput*> footage_nodes = GetMediaNodesUsingFootage(footage);
if (!footage_nodes.isEmpty()) {
// Footage is in use, show messagebox asking what to do about it
QList<Sequence*> used_in_sequences;
// Compile list of sequences to assist the user in making this decision
foreach (MediaInput* i, footage_nodes) {
Sequence* media_parent = static_cast<Sequence*>(i->parent());
if (!used_in_sequences.contains(media_parent)) {
used_in_sequences.append(media_parent);
}
}
if (response == kDelete) {
QUndoCommand* deleteCommand = new QUndoCommand(command);
// Delete any non-composite blocks
TimelineWidget::ReplaceBlocksWithGaps(GetFootageBlocks(footage_nodes.keys()), true, deleteCommand);
new ProjectViewModel::RemoveItemCommand(&model_, item_ptr, command);
// Catch any input nodes we missed due to composites etc.
QString sequence_list_str;
foreach (Sequence* s, used_in_sequences) {
sequence_list_str.append(QStringLiteral("%1\n").arg(s->name()));
}
QList<ItemPtr> sequences = model_.project()->get_items_of_type(Item::kSequence);
QMessageBox msgbox(this);
msgbox.setWindowTitle(tr("Confirm Footage Deletion"));
msgbox.setText(tr("The footage \"%1\" is currently used in the following sequence(s):\n\n"
"%2\nWhat would you like to do with these clips?")
.arg(footage->filename(), sequence_list_str));
msgbox.setIcon(QMessageBox::Warning);
QList<Node*> nodes_to_delete;
foreach (ItemPtr seq, sequences) {
Sequence* s = static_cast<Sequence*>(seq.get());
foreach (Node* node, s->nodes()) {
if (node->IsMedia()) {
if (footage_nodes.contains(node)) {
nodes_to_delete.append(node);
// Set up buttons
QPushButton* offline_btn = msgbox.addButton(tr("Offline Footage"), QMessageBox::YesRole);
QPushButton* delete_clip_btn = msgbox.addButton(tr("Delete Clips"), QMessageBox::NoRole);
msgbox.addButton(QMessageBox::Cancel);
// Run messagebox
msgbox.exec();
if (msgbox.clickedButton() == offline_btn || msgbox.clickedButton() == delete_clip_btn) {
// For safety, even if we're deleting clips, we'll offline the footage nodes too
new OfflineFootageCommand(footage_nodes, command);
}
if (msgbox.clickedButton() == delete_clip_btn) {
// Delete any blocks that use this footage
QList<Block*> blocks_to_remove;
foreach (Sequence* s, used_in_sequences) {
foreach (TrackOutput* track, s->viewer_output()->GetTracks()) {
foreach (Block* b, track->Blocks()) {
QList<Node*> deps = b->GetDependencies();
foreach (MediaInput* i, footage_nodes) {
if (deps.contains(i)) {
blocks_to_remove.append(b);
break;
}
}
}
}
QUndoCommand* deleteNodesCommand = new QUndoCommand(deleteCommand);
new NodeRemoveCommand(static_cast<NodeGraph*>(s), nodes_to_delete, deleteNodesCommand);
}
}
if (response == kCancel) {
TimelineWidget::ReplaceBlocksWithGaps(blocks_to_remove, true, command);
} else if (msgbox.clickedButton() != offline_btn) {
// Must have cancelled
delete command;
return;
}
}
// Close footage if currently open in footage panel
FootageViewerPanel* footage_panel = PanelManager::instance()->GetPanelsOfType<FootageViewerPanel>().first();
if (footage_panel->GetSelectedFootage().contains(footage)) {
footage_panel->SetFootage(nullptr);
}
break;
}
case Item::kFolder:
// Do nothing
break;
}
new ProjectViewModel::RemoveItemCommand(&model_, item->get_shared_ptr(), command);
}
Core::instance()->undo_stack()->pushIfHasChildren(command);