Add replace clip feature

parent e544b872
Pipeline #14620 passed with stage
in 19 minutes and 12 seconds
......@@ -54,13 +54,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ui_qtextclip_ui.h"
#include "undohelper.hpp"
#include "xml/xml.hpp"
#include <utils/thumbnailcache.hpp>
#include <profiles/profilemodel.hpp>
#include <KColorScheme>
#include <KRatingPainter>
#include <KMessageBox>
#include <KXMLGUIFactory>
#include <QToolBar>
#include <QToolBar>
#include <QCryptographicHash>
#include <QDesktopServices>
#include <QDrag>
......@@ -73,7 +75,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QUrl>
#include <QVBoxLayout>
#include <utility>
#include <utils/thumbnailcache.hpp>
/**
* @class BinItemDelegate
......@@ -1416,6 +1417,56 @@ void Bin::slotReloadClip()
}
}
void Bin::slotReplaceClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
for (const QModelIndex &ix : indexes) {
if (!ix.isValid() || ix.column() != 0) {
continue;
}
std::shared_ptr<AbstractProjectItem> item = m_itemModel->getBinItemByIndex(m_proxyModel->mapToSource(ix));
std::shared_ptr<ProjectClip> currentItem = nullptr;
if (item->itemType() == AbstractProjectItem::ClipItem) {
currentItem = std::static_pointer_cast<ProjectClip>(item);
} else if (item->itemType() == AbstractProjectItem::SubClipItem) {
currentItem = std::static_pointer_cast<ProjectSubClip>(item)->getMasterClip();
}
if (currentItem) {
emit openClip(std::shared_ptr<ProjectClip>());
QString fileName = QFileDialog::getOpenFileName(this, i18n("Open replacement file"),
QFileInfo(currentItem->url()).absolutePath(),
ClipCreationDialog::getExtensionsFilter());
if (!fileName.isEmpty()) {
QMap <QString, QString> sourceProps;
QMap <QString, QString> newProps;
sourceProps.insert(QStringLiteral("resource"), currentItem->url());
sourceProps.insert(QStringLiteral("kdenlive:clipname"), currentItem->clipName());
newProps.insert(QStringLiteral("resource"), fileName);
newProps.insert(QStringLiteral("kdenlive:clipname"), QFileInfo(fileName).fileName());
// Check if replacement clip is long enough
if (currentItem->hasLimitedDuration() && currentItem->isIncludedInTimeline()) {
// Clip is used in timeline, make sure lentgh is similar
std::unique_ptr<Mlt::Producer> replacementProd(new Mlt::Producer(pCore->getCurrentProfile()->profile(), fileName.toUtf8().constData()));
int currentDuration = (int)currentItem->frameDuration();
if (replacementProd->is_valid()) {
int replacementDuration = replacementProd->get_length();
if (replacementDuration < currentDuration) {
if (KMessageBox::warningContinueCancel(this, i18n("You are replacing a clip with a shorter one, this might cause issues in timeline.\nReplacement is %1 frames shorter.", (currentDuration - replacementDuration))) != KMessageBox::Continue) {
continue;
}
}
} else {
KMessageBox::sorry(this, i18n("The selected file %1 is invalid.", fileName));
continue;
}
}
slotEditClipCommand(currentItem->clipId(), sourceProps, newProps);
}
}
}
}
void Bin::slotLocateClip()
{
const QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
......@@ -1738,6 +1789,7 @@ void Bin::selectProxyModel(const QModelIndex &id)
setCurrent(currentItem);
if (currentItem->itemType() == AbstractProjectItem::ClipItem) {
m_reloadAction->setEnabled(true);
m_replaceAction->setEnabled(true);
m_locateAction->setEnabled(true);
m_duplicateAction->setEnabled(true);
std::shared_ptr<ProjectClip> clip = std::static_pointer_cast<ProjectClip>(currentItem);
......@@ -1752,6 +1804,7 @@ void Bin::selectProxyModel(const QModelIndex &id)
m_tagsWidget->setTagData();
m_openAction->setEnabled(false);
m_reloadAction->setEnabled(false);
m_replaceAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_deleteAction->setText(i18n("Delete Folder"));
......@@ -1761,6 +1814,7 @@ void Bin::selectProxyModel(const QModelIndex &id)
showClipProperties(std::static_pointer_cast<ProjectClip>(currentItem->parent()), false);
m_openAction->setEnabled(false);
m_reloadAction->setEnabled(false);
m_replaceAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_deleteAction->setText(i18n("Delete Clip"));
......@@ -1770,6 +1824,7 @@ void Bin::selectProxyModel(const QModelIndex &id)
m_renameAction->setEnabled(true);
} else {
m_reloadAction->setEnabled(false);
m_replaceAction->setEnabled(false);
m_locateAction->setEnabled(false);
m_duplicateAction->setEnabled(false);
m_openAction->setEnabled(false);
......@@ -2068,6 +2123,7 @@ void Bin::contextMenuEvent(QContextMenuEvent *event)
m_proxyAction->setEnabled((m_doc->getDocumentProperty(QStringLiteral("enableproxy")).toInt() != 0) && enableClipActions);
m_openAction->setEnabled(type == ClipType::Image || type == ClipType::Audio || type == ClipType::TextTemplate || type == ClipType::Text);
m_reloadAction->setEnabled(enableClipActions);
m_replaceAction->setEnabled(enableClipActions);
m_locateAction->setEnabled(enableClipActions);
m_duplicateAction->setEnabled(enableClipActions);
......@@ -2076,6 +2132,7 @@ void Bin::contextMenuEvent(QContextMenuEvent *event)
m_extractAudioAction->setEnabled(enableClipActions);
m_openAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_reloadAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_replaceAction->setVisible(itemType == AbstractProjectItem::ClipItem);
m_duplicateAction->setVisible(itemType != AbstractProjectItem::FolderItem);
m_inTimelineAction->setVisible(itemType == AbstractProjectItem::ClipItem);
......@@ -2431,6 +2488,9 @@ void Bin::setupGeneratorMenu()
if (m_reloadAction) {
m_menu->addAction(m_reloadAction);
}
if (m_replaceAction) {
m_menu->addAction(m_replaceAction);
}
if (m_duplicateAction) {
m_menu->addAction(m_duplicateAction);
}
......@@ -2481,6 +2541,12 @@ void Bin::setupMenu()
m_reloadAction->setEnabled(false);
connect(m_reloadAction, &QAction::triggered, this, &Bin::slotReloadClip);
m_replaceAction =
addAction(QStringLiteral("replace_clip"), i18n("Replace Clip"), QIcon::fromTheme(QStringLiteral("edit-find-replace")));
m_replaceAction->setData("replace_clip");
m_replaceAction->setEnabled(false);
connect(m_replaceAction, &QAction::triggered, this, &Bin::slotReplaceClip);
m_duplicateAction =
addAction(QStringLiteral("duplicate_clip"), i18n("Duplicate Clip"), QIcon::fromTheme(QStringLiteral("edit-copy")));
m_duplicateAction->setData("duplicate_clip");
......
......@@ -276,7 +276,10 @@ public:
private slots:
void slotAddClip();
/** @brief Reload clip from disk */
void slotReloadClip();
/** @brief Replace clip with another file */
void slotReplaceClip();
/** @brief Set sorting column */
void slotSetSorting();
/** @brief Show/hide date column */
......@@ -421,6 +424,7 @@ private:
QAction *m_openAction;
QAction *m_editAction;
QAction *m_reloadAction;
QAction *m_replaceAction;
QAction *m_duplicateAction;
QAction *m_locateAction;
QAction *m_proxyAction;
......
......@@ -1018,14 +1018,19 @@ void ProjectClip::setProperties(const QMap<QString, QString> &properties, bool r
const QList<QString> propKeys = properties.keys();
for (const QString &k : propKeys) {
if (forceReloadProperties.contains(k)) {
if (m_clipType != ClipType::Color) {
reload = true;
refreshOnly = false;
} else {
// Clip resource changed, update thumbnail
reload = true;
refreshPanel = true;
refreshPanel = true;
reload = true;
if (m_clipType == ClipType::Color) {
refreshOnly = true;
updateRoles << TimelineModel::ResourceRole;
} else {
// Clip resource changed, update thumbnail, name, clear hash
refreshOnly = false;
if (propKeys.contains(QStringLiteral("resource"))) {
resetProducerProperty(QStringLiteral("kdenlive:file_hash"));
setProducerProperty(QStringLiteral("kdenlive:originalurl"), url());
updateRoles << TimelineModel::ResourceRole << TimelineModel::MaxDurationRole << TimelineModel::NameRole;
}
}
break;
}
......@@ -1061,7 +1066,9 @@ void ProjectClip::setProperties(const QMap<QString, QString> &properties, bool r
AbstractProjectItem::DataName);
}
// update timeline clips
updateTimelineClips(QVector<int>() << TimelineModel::NameRole);
if (!reload) {
updateTimelineClips(QVector<int>() << TimelineModel::NameRole);
}
}
if (refreshPanel) {
// Some of the clip properties have changed through a command, update properties panel
......
......@@ -117,7 +117,6 @@ QString ClipCreationDialog::getExtensionsFilter(const QStringList& additionalFil
filter += ";;";
filter.append(additionalFilters.join(";;"));
}
return filter;
}
......
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="kdenlive" version="173" translationDomain="kdenlive">
<kpartgui name="kdenlive" version="174" translationDomain="kdenlive">
<MenuBar>
<Menu name="file" >
<Action name="dvd_wizard" />
......@@ -63,6 +63,7 @@
<Separator />
<Action name="locate_clip" />
<Action name="reload_clip" />
<Action name="replace_clip" />
<Action name="duplicate_clip" />
<Action name="proxy_clip" />
<Menu name="clip_in_timeline"><text>Clip in Timeline</text>
......
......@@ -835,3 +835,7 @@ int ClipModel::getMaxDuration() const
return m_producer->get_length();
}
const QString ClipModel::clipName()
{
return pCore->projectItemModel()->getClipByBinID(m_binClipId)->clipName();
}
......@@ -67,11 +67,14 @@ public:
static int construct(const std::shared_ptr<TimelineModel> &parent, const QString &binClipId, const std::shared_ptr<Mlt::Producer> &producer,
PlaylistState::ClipState state);
/* @brief returns a property of the clip, or from it's parent if it's a cut
/** @brief returns a property of the clip, or from it's parent if it's a cut
*/
const QString getProperty(const QString &name) const override;
int getIntProperty(const QString &name) const;
double getDoubleProperty(const QString &name) const;
/** @brief returns the bin clip name
*/
const QString clipName();
QSize getFrameSize() const;
Q_INVOKABLE bool showKeyframes() const;
Q_INVOKABLE void setShowKeyframes(bool show);
......
......@@ -260,19 +260,7 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
// TODO
case NameRole:
case Qt::DisplayRole: {
QString result = clip->getProperty("kdenlive:clipname");
if (result.isEmpty()) {
result = clip->getProperty("kdenlive:originalurl");
if (result.isEmpty()) {
result = clip->getProperty("resource");
}
if (!result.isEmpty()) {
result = QFileInfo(result).fileName();
} else {
result = clip->getProperty("mlt_service");
}
}
return result;
return clip->clipName();
}
case ResourceRole: {
QString result = clip->getProperty("resource");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment