replace clip media implemented
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#include "replaceclipmediadialog.h"
|
||||
|
||||
#include "ui/sourcetable.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/sequence.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/cacher.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QTreeWidget>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
|
||||
ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, SourceTable *table, QTreeWidgetItem *old_media) :
|
||||
QDialog(parent),
|
||||
source_table(table),
|
||||
media(old_media)
|
||||
{
|
||||
resize(300, 400);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
|
||||
layout->addWidget(new QLabel("Select which media you want to replace this media's clips with:"));
|
||||
|
||||
tree = new QTreeWidget();
|
||||
tree->setHeaderHidden(true);
|
||||
|
||||
layout->addWidget(tree);
|
||||
|
||||
use_same_media_in_points = new QCheckBox("Keep the same media in points");
|
||||
use_same_media_in_points->setChecked(true);
|
||||
layout->addWidget(use_same_media_in_points);
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout();
|
||||
|
||||
buttons->addStretch();
|
||||
|
||||
QPushButton* replace_button = new QPushButton("Replace");
|
||||
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(replace()));
|
||||
buttons->addWidget(replace_button);
|
||||
|
||||
QPushButton* cancel_button = new QPushButton("Cancel");
|
||||
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(close()));
|
||||
buttons->addWidget(cancel_button);
|
||||
|
||||
buttons->addStretch();
|
||||
|
||||
layout->addLayout(buttons);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
copy_tree(NULL, NULL);
|
||||
}
|
||||
|
||||
void ReplaceClipMediaDialog::replace() {
|
||||
if (tree->selectedItems().size() != 1) {
|
||||
QMessageBox::critical(this, "No media selected", "Please select a media to replace with or click 'Cancel'.", QMessageBox::Ok);
|
||||
} else {
|
||||
QTreeWidgetItem* selected_item = tree->selectedItems().at(0);
|
||||
QTreeWidgetItem* new_item = reinterpret_cast<QTreeWidgetItem*>(selected_item->data(0, Qt::UserRole + 1).value<quintptr>());
|
||||
if (media == new_item) {
|
||||
QMessageBox::critical(this, "Same media selected", "You selected the same media that you're replacing. Please select a different one or click 'Cancel'.", QMessageBox::Ok);
|
||||
} else if (get_type_from_tree(new_item) == MEDIA_TYPE_FOLDER) {
|
||||
QMessageBox::critical(this, "Folder selected", "You cannot replace footage with a folder.", QMessageBox::Ok);
|
||||
} else {
|
||||
if (get_type_from_tree(new_item) == MEDIA_TYPE_SEQUENCE && sequence == get_sequence_from_tree(new_item)) {
|
||||
QMessageBox::critical(this, "Active sequence selected", "You cannot insert a sequence into itself.", QMessageBox::Ok);
|
||||
} else {
|
||||
void* old_media = get_media_from_tree(media);
|
||||
void* new_media = get_media_from_tree(new_item);
|
||||
|
||||
// TODO not undoable yet
|
||||
bool changed = false;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (c != NULL && c->media == old_media) {
|
||||
if (c->open) {
|
||||
close_clip(c);
|
||||
c->cacher->wait();
|
||||
}
|
||||
|
||||
if (!use_same_media_in_points->isChecked()) {
|
||||
c->clip_in = 0;
|
||||
}
|
||||
|
||||
c->media = new_media;
|
||||
c->refresh();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
panel_timeline->redraw_all_clips(true);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceClipMediaDialog::copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem* target) {
|
||||
QVector<QTreeWidgetItem*> items;
|
||||
|
||||
if (parent == NULL) {
|
||||
for (int i=0;i<source_table->topLevelItemCount();i++) {
|
||||
items.append(source_table->topLevelItem(i));
|
||||
}
|
||||
} else {
|
||||
for (int i=0;i<parent->childCount();i++) {
|
||||
items.append(parent->child(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* original = items.at(i);
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setText(0, original->text(0));
|
||||
item->setData(0, Qt::UserRole + 1, reinterpret_cast<quintptr>(original));
|
||||
|
||||
if (target == NULL) {
|
||||
tree->addTopLevelItem(item);
|
||||
} else {
|
||||
target->addChild(item);
|
||||
}
|
||||
|
||||
if (original->childCount() > 0) {
|
||||
copy_tree(original, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef REPLACECLIPMEDIADIALOG_H
|
||||
#define REPLACECLIPMEDIADIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class SourceTable;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QCheckBox;
|
||||
|
||||
class ReplaceClipMediaDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ReplaceClipMediaDialog(QWidget* parent, SourceTable* table, QTreeWidgetItem *old_media);
|
||||
private slots:
|
||||
void replace();
|
||||
private:
|
||||
SourceTable* source_table;
|
||||
QTreeWidget* tree;
|
||||
QTreeWidgetItem* media;
|
||||
QCheckBox* use_same_media_in_points;
|
||||
void copy_tree(QTreeWidgetItem* parent, QTreeWidgetItem *target);
|
||||
};
|
||||
|
||||
#endif // REPLACECLIPMEDIADIALOG_H
|
||||
+1
-1
@@ -28,7 +28,7 @@ struct MediaStream {
|
||||
|
||||
struct Media {
|
||||
Media();
|
||||
~Media();
|
||||
~Media();
|
||||
|
||||
QString url;
|
||||
QString name;
|
||||
|
||||
+6
-3
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "project/sequence.h"
|
||||
|
||||
#include "ui/sourcetable.h"
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "panels/project.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
@@ -590,9 +592,10 @@ void MainWindow::on_actionSlide_Tool_triggered()
|
||||
if (panel_timeline->focused()) panel_timeline->ui->toolSlideButton->click();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFolder_triggered()
|
||||
{
|
||||
panel_project->new_folder();
|
||||
void MainWindow::on_actionFolder_triggered() {
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
ta->add_media(panel_project->new_folder(0), panel_project->get_selected_folder());
|
||||
undo_stack.push(ta);
|
||||
}
|
||||
|
||||
void MainWindow::fileMenu_About_To_Be_Shown() {
|
||||
|
||||
+4
-2
@@ -24,7 +24,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>653</width>
|
||||
<height>17</height>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_File">
|
||||
@@ -151,7 +151,6 @@
|
||||
<addaction name="actionScroll_Wheel_Zooms"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCrash"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
@@ -420,6 +419,9 @@
|
||||
<property name="text">
|
||||
<string>Crash</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPointer_Tool">
|
||||
<property name="text">
|
||||
|
||||
@@ -1,135 +1,137 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2018-05-11T10:31:59
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui multimedia opengl
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = Olive
|
||||
TEMPLATE = app
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
panels/project.cpp \
|
||||
panels/effectcontrols.cpp \
|
||||
panels/viewer.cpp \
|
||||
panels/timeline.cpp \
|
||||
ui/sourcetable.cpp \
|
||||
dialogs/aboutdialog.cpp \
|
||||
ui/timelinewidget.cpp \
|
||||
io/media.cpp \
|
||||
project/sequence.cpp \
|
||||
project/clip.cpp \
|
||||
playback/playback.cpp \
|
||||
playback/audio.cpp \
|
||||
io/config.cpp \
|
||||
dialogs/newsequencedialog.cpp \
|
||||
ui/viewerwidget.cpp \
|
||||
ui/viewercontainer.cpp \
|
||||
dialogs/exportdialog.cpp \
|
||||
ui/collapsiblewidget.cpp \
|
||||
effects/transformeffect.cpp \
|
||||
panels/panels.cpp \
|
||||
effects/volumeeffect.cpp \
|
||||
effects/paneffect.cpp \
|
||||
project/effect.cpp \
|
||||
effects/effects.cpp \
|
||||
playback/cacher.cpp \
|
||||
io/exportthread.cpp \
|
||||
ui/timelineheader.cpp \
|
||||
io/previewgenerator.cpp \
|
||||
ui/labelslider.cpp \
|
||||
dialogs/preferencesdialog.cpp \
|
||||
effects/transition.cpp \
|
||||
effects/crossdissolvetransition.cpp \
|
||||
ui/audiomonitor.cpp \
|
||||
project/undo.cpp \
|
||||
ui/scrollarea.cpp \
|
||||
effects/shakeeffect.cpp \
|
||||
effects/texteffect.cpp \
|
||||
ui/comboboxex.cpp \
|
||||
ui/colorbutton.cpp \
|
||||
effects/solideffect.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
panels/project.h \
|
||||
panels/effectcontrols.h \
|
||||
panels/viewer.h \
|
||||
panels/timeline.h \
|
||||
ui/sourcetable.h \
|
||||
dialogs/aboutdialog.h \
|
||||
ui/timelinewidget.h \
|
||||
io/media.h \
|
||||
project/sequence.h \
|
||||
project/clip.h \
|
||||
playback/playback.h \
|
||||
playback/audio.h \
|
||||
effects/effects.h \
|
||||
io/config.h \
|
||||
dialogs/newsequencedialog.h \
|
||||
ui/viewerwidget.h \
|
||||
ui/viewercontainer.h \
|
||||
dialogs/exportdialog.h \
|
||||
ui/collapsiblewidget.h \
|
||||
project/effect.h \
|
||||
panels/panels.h \
|
||||
playback/cacher.h \
|
||||
io/exportthread.h \
|
||||
ui/timelinetools.h \
|
||||
ui/timelineheader.h \
|
||||
io/previewgenerator.h \
|
||||
ui/labelslider.h \
|
||||
dialogs/preferencesdialog.h \
|
||||
effects/transition.h \
|
||||
ui/audiomonitor.h \
|
||||
project/undo.h \
|
||||
ui/scrollarea.h \
|
||||
ui/comboboxex.h \
|
||||
ui/colorbutton.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
panels/project.ui \
|
||||
panels/effectcontrols.ui \
|
||||
panels/viewer.ui \
|
||||
panels/timeline.ui \
|
||||
dialogs/aboutdialog.ui \
|
||||
dialogs/newsequencedialog.ui \
|
||||
dialogs/exportdialog.ui \
|
||||
dialogs/preferencesdialog.ui
|
||||
|
||||
win32 {
|
||||
LIBS += -L../ffmpeg/lib -lopengl32
|
||||
INCLUDEPATH = ../ffmpeg/include
|
||||
RC_FILE = icons/win.rc
|
||||
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
}
|
||||
|
||||
mac {
|
||||
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
INCLUDEPATH = /usr/local/include
|
||||
}
|
||||
|
||||
linux {
|
||||
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
icons/icons.qrc
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2018-05-11T10:31:59
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui multimedia opengl
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = Olive
|
||||
TEMPLATE = app
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if you use deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
panels/project.cpp \
|
||||
panels/effectcontrols.cpp \
|
||||
panels/viewer.cpp \
|
||||
panels/timeline.cpp \
|
||||
ui/sourcetable.cpp \
|
||||
dialogs/aboutdialog.cpp \
|
||||
ui/timelinewidget.cpp \
|
||||
io/media.cpp \
|
||||
project/sequence.cpp \
|
||||
project/clip.cpp \
|
||||
playback/playback.cpp \
|
||||
playback/audio.cpp \
|
||||
io/config.cpp \
|
||||
dialogs/newsequencedialog.cpp \
|
||||
ui/viewerwidget.cpp \
|
||||
ui/viewercontainer.cpp \
|
||||
dialogs/exportdialog.cpp \
|
||||
ui/collapsiblewidget.cpp \
|
||||
effects/transformeffect.cpp \
|
||||
panels/panels.cpp \
|
||||
effects/volumeeffect.cpp \
|
||||
effects/paneffect.cpp \
|
||||
project/effect.cpp \
|
||||
effects/effects.cpp \
|
||||
playback/cacher.cpp \
|
||||
io/exportthread.cpp \
|
||||
ui/timelineheader.cpp \
|
||||
io/previewgenerator.cpp \
|
||||
ui/labelslider.cpp \
|
||||
dialogs/preferencesdialog.cpp \
|
||||
effects/transition.cpp \
|
||||
effects/crossdissolvetransition.cpp \
|
||||
ui/audiomonitor.cpp \
|
||||
project/undo.cpp \
|
||||
ui/scrollarea.cpp \
|
||||
effects/shakeeffect.cpp \
|
||||
effects/texteffect.cpp \
|
||||
ui/comboboxex.cpp \
|
||||
ui/colorbutton.cpp \
|
||||
effects/solideffect.cpp \
|
||||
dialogs/replaceclipmediadialog.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
panels/project.h \
|
||||
panels/effectcontrols.h \
|
||||
panels/viewer.h \
|
||||
panels/timeline.h \
|
||||
ui/sourcetable.h \
|
||||
dialogs/aboutdialog.h \
|
||||
ui/timelinewidget.h \
|
||||
io/media.h \
|
||||
project/sequence.h \
|
||||
project/clip.h \
|
||||
playback/playback.h \
|
||||
playback/audio.h \
|
||||
effects/effects.h \
|
||||
io/config.h \
|
||||
dialogs/newsequencedialog.h \
|
||||
ui/viewerwidget.h \
|
||||
ui/viewercontainer.h \
|
||||
dialogs/exportdialog.h \
|
||||
ui/collapsiblewidget.h \
|
||||
project/effect.h \
|
||||
panels/panels.h \
|
||||
playback/cacher.h \
|
||||
io/exportthread.h \
|
||||
ui/timelinetools.h \
|
||||
ui/timelineheader.h \
|
||||
io/previewgenerator.h \
|
||||
ui/labelslider.h \
|
||||
dialogs/preferencesdialog.h \
|
||||
effects/transition.h \
|
||||
ui/audiomonitor.h \
|
||||
project/undo.h \
|
||||
ui/scrollarea.h \
|
||||
ui/comboboxex.h \
|
||||
ui/colorbutton.h \
|
||||
dialogs/replaceclipmediadialog.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
panels/project.ui \
|
||||
panels/effectcontrols.ui \
|
||||
panels/viewer.ui \
|
||||
panels/timeline.ui \
|
||||
dialogs/aboutdialog.ui \
|
||||
dialogs/newsequencedialog.ui \
|
||||
dialogs/exportdialog.ui \
|
||||
dialogs/preferencesdialog.ui
|
||||
|
||||
win32 {
|
||||
LIBS += -L../ffmpeg/lib -lopengl32
|
||||
INCLUDEPATH = ../ffmpeg/include
|
||||
RC_FILE = icons/win.rc
|
||||
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
}
|
||||
|
||||
mac {
|
||||
LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
INCLUDEPATH = /usr/local/include
|
||||
}
|
||||
|
||||
linux {
|
||||
LIBS += -lavutil -lavformat -lavcodec -lswscale -lswresample
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
icons/icons.qrc
|
||||
|
||||
+269
-115
@@ -15,6 +15,8 @@
|
||||
#include "project/undo.h"
|
||||
#include "mainwindow.h"
|
||||
#include "io/config.h"
|
||||
#include "playback/cacher.h"
|
||||
#include "dialogs/replaceclipmediadialog.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QString>
|
||||
@@ -81,7 +83,7 @@ void Project::rename_media(QTreeWidgetItem* item, int column) {
|
||||
int type = get_type_from_tree(item);
|
||||
QString n = item->text(column);
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: get_media_from_tree(item)->name = n; break;
|
||||
case MEDIA_TYPE_FOOTAGE: get_footage_from_tree(item)->name = n; break;
|
||||
case MEDIA_TYPE_SEQUENCE: get_sequence_from_tree(item)->name = n; break;
|
||||
}
|
||||
}
|
||||
@@ -104,6 +106,71 @@ void Project::duplicate_selected() {
|
||||
}
|
||||
}
|
||||
|
||||
void Project::replace_selected_file() {
|
||||
QList<QTreeWidgetItem*> selected_items = ui->treeWidget->selectedItems();
|
||||
if (selected_items.size() == 1) {
|
||||
QTreeWidgetItem* item = selected_items.at(0);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_FOOTAGE) {
|
||||
replace_media(item, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Project::replace_media(QTreeWidgetItem* item, QString filename) {
|
||||
Media* m = get_footage_from_tree(item);
|
||||
|
||||
QStringList files;
|
||||
if (filename.isEmpty()) {
|
||||
QString browse_file = QFileDialog::getOpenFileName(this, "Replace '" + item->text(0) + "'", "", "All Files (*)");
|
||||
if (!browse_file.isEmpty()) files.append(browse_file);
|
||||
} else {
|
||||
files.append(filename);
|
||||
}
|
||||
|
||||
if (files.size() > 0) {
|
||||
// close any clips currently using this media
|
||||
QVector<Sequence*> all_sequences = list_all_project_sequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i);
|
||||
for (int j=0;j<s->clip_count();j++) {
|
||||
Clip* c = s->get_clip(j);
|
||||
if (c != NULL && c->media == m && c->open) {
|
||||
close_clip(c);
|
||||
c->cacher->wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process_file_list(NULL, files, NULL, item);
|
||||
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i);
|
||||
for (int j=0;j<s->clip_count();j++) {
|
||||
Clip* c = s->get_clip(j);
|
||||
if (c != NULL && c->media == m) {
|
||||
c->refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
panel_timeline->redraw_all_clips(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Project::replace_clip_media() {
|
||||
if (sequence == NULL) {
|
||||
QMessageBox::critical(this, "No active sequence", "No sequence is active, please open the sequence you want to replace clips from.", QMessageBox::Ok);
|
||||
} else if (ui->treeWidget->selectedItems().size() == 1) {
|
||||
QTreeWidgetItem* item = ui->treeWidget->selectedItems().at(0);
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE && sequence == get_sequence_from_tree(item)) {
|
||||
QMessageBox::critical(this, "Active sequence selected", "You cannot insert a sequence into itself, so no clips of this media would be in this sequence.", QMessageBox::Ok);
|
||||
} else {
|
||||
ReplaceClipMediaDialog dialog(this, ui->treeWidget, item);
|
||||
dialog.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Project::new_sequence(TimelineAction *ta, Sequence *s, bool open, QTreeWidgetItem* parent) {
|
||||
QTreeWidgetItem* item = new_item();
|
||||
item->setText(0, s->name);
|
||||
@@ -134,17 +201,16 @@ void Project::start_preview_generator(QTreeWidgetItem* item, Media* media) {
|
||||
pg->start(QThread::LowPriority);
|
||||
}
|
||||
|
||||
QTreeWidgetItem* Project::import_file(QString file, QString imported_filename) {
|
||||
QTreeWidgetItem* item = new_item();
|
||||
Media* m = new Media();
|
||||
QString Project::get_file_name_from_path(const QString& path) {
|
||||
return path.mid(path.lastIndexOf('/')+1);
|
||||
}
|
||||
|
||||
void Project::import_file(QTreeWidgetItem* item, Media* m, QString file, QString imported_filename) {
|
||||
m->url = file;
|
||||
m->name = imported_filename.mid(file.lastIndexOf('/')+1);
|
||||
set_media_of_tree(item, m);
|
||||
m->name = get_file_name_from_path(imported_filename);
|
||||
|
||||
// generate waveform/thumbnail in another thread
|
||||
start_preview_generator(item, m);
|
||||
|
||||
return item;
|
||||
start_preview_generator(item, m);
|
||||
}
|
||||
|
||||
QTreeWidgetItem* Project::new_item() {
|
||||
@@ -157,17 +223,15 @@ bool Project::is_focused() {
|
||||
return ui->treeWidget->hasFocus();
|
||||
}
|
||||
|
||||
QTreeWidgetItem* Project::new_folder() {
|
||||
QTreeWidgetItem* Project::new_folder(QString name) {
|
||||
QTreeWidgetItem* item = new_item();
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
item->setText(0, "New Folder");
|
||||
item->setText(0, (name.isEmpty()) ? "New Folder" : name);
|
||||
set_item_to_folder(item);
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
project_changed = true;
|
||||
return item;
|
||||
}
|
||||
|
||||
void Project::get_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& list, int search_type) {
|
||||
void Project::get_all_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& list, int search_type) {
|
||||
for (int i=0;i<items.size();i++) {
|
||||
QTreeWidgetItem* item = items.at(i);
|
||||
int type = get_type_from_tree(item);
|
||||
@@ -176,7 +240,7 @@ void Project::get_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWid
|
||||
for (int j=0;j<item->childCount();j++) {
|
||||
children.append(item->child(j));
|
||||
}
|
||||
get_media_from_table(children, list, search_type);
|
||||
get_all_media_from_table(children, list, search_type);
|
||||
} else if (search_type == type) {
|
||||
list.append(item);
|
||||
}
|
||||
@@ -196,13 +260,13 @@ void Project::delete_selected_media() {
|
||||
for (int i=0;i<ui->treeWidget->topLevelItemCount();i++) {
|
||||
all_top_level_items.append(ui->treeWidget->topLevelItem(i));
|
||||
}
|
||||
get_media_from_table(all_top_level_items, sequence_items, MEDIA_TYPE_SEQUENCE); // find all sequences in project
|
||||
get_all_media_from_table(all_top_level_items, sequence_items, MEDIA_TYPE_SEQUENCE); // find all sequences in project
|
||||
if (sequence_items.size() > 0) {
|
||||
QList<QTreeWidgetItem*> media_items;
|
||||
get_media_from_table(items, media_items, MEDIA_TYPE_FOOTAGE);
|
||||
get_all_media_from_table(items, media_items, MEDIA_TYPE_FOOTAGE);
|
||||
for (int i=0;i<media_items.size();i++) {
|
||||
QTreeWidgetItem* item = media_items.at(i);
|
||||
Media* media = get_media_from_tree(item);
|
||||
Media* media = get_footage_from_tree(item);
|
||||
bool confirm_delete = false;
|
||||
for (int j=0;j<sequence_items.size();j++) {
|
||||
Sequence* s = get_sequence_from_tree(sequence_items.at(j));
|
||||
@@ -299,114 +363,186 @@ void Project::delete_selected_media() {
|
||||
}
|
||||
}
|
||||
|
||||
void Project::process_file_list(QStringList& files) {
|
||||
void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetItem* parent, QTreeWidgetItem* replace) {
|
||||
bool imported = false;
|
||||
|
||||
QVector<QString> image_sequence_urls;
|
||||
QVector<bool> image_sequence_importassequence;
|
||||
QStringList image_sequence_formats = config.img_seq_formats.split("|");
|
||||
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
TimelineAction* ta;
|
||||
if (!recursive) ta = new TimelineAction();
|
||||
|
||||
for (int i=0;i<files.count();i++) {
|
||||
QString file(files.at(i));
|
||||
bool skip = false;
|
||||
if (QFileInfo(files.at(i)).isDir()) {
|
||||
QString folder_name = get_file_name_from_path(files.at(i));
|
||||
QTreeWidgetItem* folder = new_folder(folder_name);
|
||||
|
||||
/* Heuristic to determine whether file is part of an image sequence */
|
||||
QDir directory(files.at(i));
|
||||
directory.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);
|
||||
|
||||
// check file extension (assume it's not a
|
||||
int lastcharindex = file.lastIndexOf(".");
|
||||
bool found = true;
|
||||
if (lastcharindex != -1 && lastcharindex > file.lastIndexOf('/')) {
|
||||
// image_sequence_formats
|
||||
found = false;
|
||||
QString ext = file.mid(lastcharindex+1);
|
||||
for (int j=0;j<image_sequence_formats.size();j++) {
|
||||
if (ext == image_sequence_formats.at(j)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lastcharindex = file.length();
|
||||
}
|
||||
QFileInfoList subdir_files = directory.entryInfoList();
|
||||
QStringList subdir_filenames;
|
||||
|
||||
if (found && file[lastcharindex-1].isDigit()) {
|
||||
bool is_img_sequence = false;
|
||||
for (int j=0;j<subdir_files.size();j++) {
|
||||
subdir_filenames.append(subdir_files.at(j).filePath());
|
||||
}
|
||||
|
||||
// how many digits are in the filename?
|
||||
int digit_count = 0;
|
||||
int digit_test = lastcharindex-1;
|
||||
while (file[digit_test].isDigit()) {
|
||||
digit_count++;
|
||||
digit_test--;
|
||||
}
|
||||
process_file_list(true, subdir_filenames, folder, NULL);
|
||||
|
||||
// retrieve number from filename
|
||||
digit_test++;
|
||||
int file_number = file.mid(digit_test, digit_count).toInt();
|
||||
if (!recursive) {
|
||||
ta->add_media(folder, parent);
|
||||
} else {
|
||||
parent->addChild(folder);
|
||||
}
|
||||
|
||||
// Check if there are files with the same filename but just different numbers
|
||||
if (QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number-1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))
|
||||
|| QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number+1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))) {
|
||||
is_img_sequence = true;
|
||||
}
|
||||
imported = true;
|
||||
} else {
|
||||
QString file(files.at(i));
|
||||
bool skip = false;
|
||||
|
||||
if (is_img_sequence) {
|
||||
// get the URL that we would pass to FFmpeg to force it to read the image as a sequence
|
||||
QString new_filename = file.left(digit_test) + "%" + QString("%1").arg(digit_count, 2, 10, QChar('0')) + "d" + file.mid(lastcharindex);
|
||||
/* Heuristic to determine whether file is part of an image sequence */
|
||||
|
||||
// add image sequence url to a vector in case the user imported several files that
|
||||
// we're interpreting as a possible sequence
|
||||
found = false;
|
||||
for (int i=0;i<image_sequence_urls.size();i++) {
|
||||
if (image_sequence_urls.at(i) == new_filename) {
|
||||
// either SKIP if we're importing as a sequence, or leave it if we aren't
|
||||
if (image_sequence_importassequence.at(i)) {
|
||||
skip = true;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
image_sequence_urls.append(new_filename);
|
||||
if (QMessageBox::question(this, "Image sequence detected", "The file '" + file + "' appears to be part of an image sequence. Would you like to import it as such?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
|
||||
file = new_filename;
|
||||
image_sequence_importassequence.append(true);
|
||||
} else {
|
||||
image_sequence_importassequence.append(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check file extension (assume it's not a
|
||||
int lastcharindex = file.lastIndexOf(".");
|
||||
bool found = true;
|
||||
if (lastcharindex != -1 && lastcharindex > file.lastIndexOf('/')) {
|
||||
// image_sequence_formats
|
||||
found = false;
|
||||
QString ext = file.mid(lastcharindex+1);
|
||||
for (int j=0;j<image_sequence_formats.size();j++) {
|
||||
if (ext == image_sequence_formats.at(j)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lastcharindex = file.length();
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
ta->add_media(import_file(file, files.at(i)));
|
||||
imported = true;
|
||||
project_changed = true;
|
||||
}
|
||||
}
|
||||
if (imported) {
|
||||
undo_stack.push(ta);
|
||||
} else {
|
||||
delete ta;
|
||||
if (found && file[lastcharindex-1].isDigit()) {
|
||||
bool is_img_sequence = false;
|
||||
|
||||
// how many digits are in the filename?
|
||||
int digit_count = 0;
|
||||
int digit_test = lastcharindex-1;
|
||||
while (file[digit_test].isDigit()) {
|
||||
digit_count++;
|
||||
digit_test--;
|
||||
}
|
||||
|
||||
// retrieve number from filename
|
||||
digit_test++;
|
||||
int file_number = file.mid(digit_test, digit_count).toInt();
|
||||
|
||||
// Check if there are files with the same filename but just different numbers
|
||||
if (QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number-1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))
|
||||
|| QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number+1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))) {
|
||||
is_img_sequence = true;
|
||||
}
|
||||
|
||||
if (is_img_sequence) {
|
||||
// get the URL that we would pass to FFmpeg to force it to read the image as a sequence
|
||||
QString new_filename = file.left(digit_test) + "%" + QString("%1").arg(digit_count, 2, 10, QChar('0')) + "d" + file.mid(lastcharindex);
|
||||
|
||||
// add image sequence url to a vector in case the user imported several files that
|
||||
// we're interpreting as a possible sequence
|
||||
found = false;
|
||||
for (int i=0;i<image_sequence_urls.size();i++) {
|
||||
if (image_sequence_urls.at(i) == new_filename) {
|
||||
// either SKIP if we're importing as a sequence, or leave it if we aren't
|
||||
if (image_sequence_importassequence.at(i)) {
|
||||
skip = true;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
image_sequence_urls.append(new_filename);
|
||||
if (QMessageBox::question(this, "Image sequence detected", "The file '" + file + "' appears to be part of an image sequence. Would you like to import it as such?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
|
||||
file = new_filename;
|
||||
image_sequence_importassequence.append(true);
|
||||
} else {
|
||||
image_sequence_importassequence.append(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip) {
|
||||
QTreeWidgetItem* item;
|
||||
Media* m;
|
||||
|
||||
if (replace != NULL) {
|
||||
item = replace;
|
||||
m = get_footage_from_tree(replace);
|
||||
m->reset();
|
||||
} else {
|
||||
item = new_item();
|
||||
m = new Media();
|
||||
}
|
||||
|
||||
import_file(item, m, file, files.at(i));
|
||||
set_footage_of_tree(item, m);
|
||||
|
||||
if (recursive) {
|
||||
parent->addChild(item);
|
||||
} else {
|
||||
ta->add_media(item, parent);
|
||||
}
|
||||
|
||||
imported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!recursive) {
|
||||
if (imported) {
|
||||
undo_stack.push(ta);
|
||||
} else {
|
||||
delete ta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem* Project::get_selected_folder() {
|
||||
// if one item is selected and it's a folder, return it
|
||||
QList<QTreeWidgetItem*> selected_items = panel_project->source_table->selectedItems();
|
||||
if (selected_items.size() == 1 && get_type_from_tree(selected_items.at(0)) == MEDIA_TYPE_FOLDER) {
|
||||
return selected_items.at(0);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Project::import_dialog() {
|
||||
QStringList files = QFileDialog::getOpenFileNames(this, "Import media...", "", "All Files (*)");
|
||||
process_file_list(files);
|
||||
QFileDialog fd(this, "Import media...", "", "All Files (*)");
|
||||
fd.setFileMode(QFileDialog::ExistingFiles);
|
||||
|
||||
if (fd.exec()) {
|
||||
QStringList files = fd.selectedFiles();
|
||||
process_file_list(NULL, files, get_selected_folder(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void set_item_to_folder(QTreeWidgetItem* item) {
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOLDER);
|
||||
}
|
||||
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item) {
|
||||
void* get_media_from_tree(QTreeWidgetItem* item) {
|
||||
int type = get_type_from_tree(item);
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE: return get_footage_from_tree(item);
|
||||
case MEDIA_TYPE_SEQUENCE: return get_sequence_from_tree(item);
|
||||
default: qDebug() << "[ERROR] Invalid media type when retrieving media";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Media* get_footage_from_tree(QTreeWidgetItem* item) {
|
||||
return reinterpret_cast<Media*>(item->data(0, Qt::UserRole + 2).value<quintptr>());
|
||||
}
|
||||
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media) {
|
||||
void set_footage_of_tree(QTreeWidgetItem* item, Media* media) {
|
||||
item->setText(0, media->name);
|
||||
item->setData(0, Qt::UserRole + 1, MEDIA_TYPE_FOOTAGE);
|
||||
item->setData(0, Qt::UserRole + 2, QVariant::fromValue(reinterpret_cast<quintptr>(media)));
|
||||
@@ -426,18 +562,35 @@ int get_type_from_tree(QTreeWidgetItem* item) {
|
||||
}
|
||||
|
||||
void Project::delete_media(QTreeWidgetItem* item) {
|
||||
int type = get_type_from_tree(item);
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
delete get_media_from_tree(item);
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
Sequence* s = get_sequence_from_tree(item);
|
||||
delete s;
|
||||
break;
|
||||
}
|
||||
delete get_media_from_tree(item);
|
||||
}
|
||||
|
||||
project_changed = true;
|
||||
void Project::delete_clips_using_selected_media() {
|
||||
if (sequence == NULL) {
|
||||
QMessageBox::critical(this, "No active sequence", "No sequence is active, please open the sequence you want to delete clips from.", QMessageBox::Ok);
|
||||
} else {
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
bool deleted = false;
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* c = sequence->get_clip(i);
|
||||
if (c != NULL) {
|
||||
QList<QTreeWidgetItem*> items = source_table->selectedItems();
|
||||
for (int j=0;j<items.size();j++) {
|
||||
Media* m = get_footage_from_tree(items.at(j));
|
||||
if (c->media == m) {
|
||||
ta->delete_clip(sequence, i);
|
||||
deleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleted) {
|
||||
undo_stack.push(ta);
|
||||
panel_timeline->redraw_all_clips(true);
|
||||
} else {
|
||||
delete ta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Project::clear() {
|
||||
@@ -516,7 +669,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
switch (type) {
|
||||
case MEDIA_TYPE_FOLDER:
|
||||
{
|
||||
QTreeWidgetItem* folder = new_folder();
|
||||
QTreeWidgetItem* folder = new_folder(0);
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
if (attr.name() == "id") {
|
||||
@@ -534,7 +687,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
{
|
||||
int folder = 0;
|
||||
|
||||
QTreeWidgetItem* item = new_item();//import_file(url);
|
||||
QTreeWidgetItem* item = new_item();
|
||||
Media* m = new Media();
|
||||
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
@@ -591,7 +744,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
}
|
||||
}*/
|
||||
|
||||
set_media_of_tree(item, m);
|
||||
set_footage_of_tree(item, m);
|
||||
|
||||
if (folder == 0) {
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
@@ -823,10 +976,11 @@ void Project::load_project() {
|
||||
for (int i=0;i<loaded_folders.size();i++) {
|
||||
QTreeWidgetItem* folder = loaded_folders.at(i);
|
||||
int parent = folder->data(0, Qt::UserRole + 4).toInt();
|
||||
if (parent > 0) {
|
||||
ui->treeWidget->takeTopLevelItem(ui->treeWidget->indexOfTopLevelItem(folder));
|
||||
if (parent > 0) {
|
||||
find_loaded_folder_by_id(parent)->addChild(folder);
|
||||
}
|
||||
} else {
|
||||
ui->treeWidget->addTopLevelItem(folder);
|
||||
}
|
||||
}
|
||||
|
||||
cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE);
|
||||
@@ -897,7 +1051,7 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
} else {
|
||||
int folder = root ? 0 : parent->data(0, Qt::UserRole + 3).toInt();
|
||||
if (type == MEDIA_TYPE_FOOTAGE) {
|
||||
Media* m = get_media_from_tree(item);
|
||||
Media* m = get_footage_from_tree(item);
|
||||
m->save_id = media_id;
|
||||
stream.writeStartElement("footage");
|
||||
stream.writeAttribute("id", QString::number(media_id));
|
||||
|
||||
+14
-7
@@ -36,8 +36,9 @@ extern QStringList recent_projects;
|
||||
extern QString recent_proj_file;
|
||||
|
||||
int get_type_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_media_from_tree(QTreeWidgetItem* item);
|
||||
void set_media_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
void* get_media_from_tree(QTreeWidgetItem* item);
|
||||
Media* get_footage_from_tree(QTreeWidgetItem* item);
|
||||
void set_footage_of_tree(QTreeWidgetItem* item, Media* media);
|
||||
Sequence* get_sequence_from_tree(QTreeWidgetItem* item);
|
||||
void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence);
|
||||
void set_item_to_folder(QTreeWidgetItem* item);
|
||||
@@ -51,17 +52,19 @@ public:
|
||||
~Project();
|
||||
bool is_focused();
|
||||
void clear();
|
||||
QTreeWidgetItem* import_file(QString url, QString imported_filename);
|
||||
void import_file(QTreeWidgetItem* item, Media* m, QString url, QString imported_filename);
|
||||
void new_sequence(TimelineAction* ta, Sequence* s, bool open, QTreeWidgetItem* parent);
|
||||
QString get_next_sequence_name();
|
||||
void delete_media(QTreeWidgetItem* item);
|
||||
void process_file_list(QStringList& files);
|
||||
void process_file_list(bool recursive, QStringList& files, QTreeWidgetItem *parent, QTreeWidgetItem* replace);
|
||||
void replace_media(QTreeWidgetItem* item, QString filename);
|
||||
QTreeWidgetItem* get_selected_folder();
|
||||
|
||||
void new_project();
|
||||
void load_project();
|
||||
void save_project(bool autorecovery);
|
||||
|
||||
QTreeWidgetItem* new_folder();
|
||||
QTreeWidgetItem* new_folder(QString name);
|
||||
|
||||
void save_recent_projects();
|
||||
|
||||
@@ -69,9 +72,12 @@ public:
|
||||
|
||||
SourceTable* source_table;
|
||||
public slots:
|
||||
void import_dialog();
|
||||
void import_dialog();
|
||||
void delete_selected_media();
|
||||
void duplicate_selected();
|
||||
void delete_clips_using_selected_media();
|
||||
void replace_selected_file();
|
||||
void replace_clip_media();
|
||||
private:
|
||||
Ui::Project *ui;
|
||||
QTreeWidgetItem* new_item();
|
||||
@@ -89,9 +95,10 @@ private:
|
||||
QVector<Sequence*> loaded_sequences;
|
||||
QTreeWidgetItem* find_loaded_folder_by_id(int id);
|
||||
void add_recent_project(QString url);
|
||||
void get_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& list, int type);
|
||||
void get_all_media_from_table(QList<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& list, int type);
|
||||
void start_preview_generator(QTreeWidgetItem* item, Media* media);
|
||||
void list_all_sequences_worker(QVector<Sequence*>* list, QTreeWidgetItem* parent);
|
||||
QString get_file_name_from_path(const QString &path);
|
||||
private slots:
|
||||
void rename_media(QTreeWidgetItem* item, int column);
|
||||
void clear_recent_projects();
|
||||
|
||||
+1
-2
@@ -264,8 +264,7 @@ void Timeline::repaint_timeline() {
|
||||
}
|
||||
|
||||
void Timeline::redraw_all_clips(bool changed) {
|
||||
if (changed) {
|
||||
project_changed = true;
|
||||
if (changed) {
|
||||
if (!playing) reset_all_audio();
|
||||
panel_viewer->viewer_widget->update();
|
||||
}
|
||||
|
||||
@@ -182,7 +182,6 @@ void reset_cache(Clip* c, long target_frame) {
|
||||
// seeks to nearest keyframe (target_frame represents internal clip frame)
|
||||
|
||||
av_seek_frame(c->formatCtx, ms->file_index, (int64_t) qFloor(clip_frame_to_seconds(c, target_frame) / timebase), AVSEEK_FLAG_BACKWARD);
|
||||
qDebug() << target_frame;
|
||||
|
||||
// play up to the frame we actually want
|
||||
long retrieved_frame = 0;
|
||||
|
||||
@@ -246,7 +246,7 @@ void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
|
||||
}
|
||||
|
||||
bool is_clip_active(Clip* c, long playhead) {
|
||||
return c->timeline_in < playhead + ceil(c->sequence->frame_rate) && c->timeline_out > playhead && c->enabled;
|
||||
return c->timeline_in < playhead + ceil(c->sequence->frame_rate) && c->timeline_out > playhead && c->enabled;
|
||||
}
|
||||
|
||||
void set_sequence(Sequence* s) {
|
||||
|
||||
+44
-38
@@ -31,7 +31,8 @@ TimelineAction::TimelineAction() :
|
||||
done(false),
|
||||
change_seq(false),
|
||||
ripple_enabled(false),
|
||||
change_in_out(false)
|
||||
change_in_out(false),
|
||||
old_project_changed(project_changed)
|
||||
{}
|
||||
|
||||
TimelineAction::~TimelineAction() {
|
||||
@@ -129,8 +130,9 @@ void TimelineAction::delete_clip(Sequence* s, int clip) {
|
||||
new_action(s, TA_DELETE, clip, 0, 0);
|
||||
}
|
||||
|
||||
void TimelineAction::add_media(QTreeWidgetItem* item) {
|
||||
void TimelineAction::add_media(QTreeWidgetItem* item, QTreeWidgetItem *parent) {
|
||||
media_to_add.append(item);
|
||||
media_to_add_parents.append(parent);
|
||||
}
|
||||
|
||||
void TimelineAction::delete_media(QTreeWidgetItem* item) {
|
||||
@@ -242,20 +244,20 @@ void TimelineAction::undo() {
|
||||
}
|
||||
|
||||
// delete added clips
|
||||
if (clips_to_add.size() > 0) {
|
||||
int delete_count = 0;
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
sequence_to_add_clips_to.at(i)->destroy_clip(added_indexes.at(i)-delete_count, true);
|
||||
delete_count++;
|
||||
}
|
||||
}
|
||||
int delete_count = 0;
|
||||
for (int i=0;i<clips_to_add.size();i++) {
|
||||
sequence_to_add_clips_to.at(i)->destroy_clip(added_indexes.at(i)-delete_count, true);
|
||||
delete_count++;
|
||||
}
|
||||
|
||||
// remove any added media
|
||||
if (media_to_add.size() > 0) {
|
||||
for (int i=0;i<media_to_add.size();i++) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(media_to_add.at(i)));
|
||||
}
|
||||
}
|
||||
for (int i=0;i<media_to_add.size();i++) {
|
||||
if (media_to_add_parents.at(i) == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(media_to_add.at(i)));
|
||||
} else {
|
||||
media_to_add_parents.at(i)->removeChild(media_to_add.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// un-change workarea
|
||||
if (change_in_out) {
|
||||
@@ -269,6 +271,8 @@ void TimelineAction::undo() {
|
||||
set_sequence(old_seq);
|
||||
}
|
||||
|
||||
project_changed = old_project_changed;
|
||||
|
||||
done = false;
|
||||
}
|
||||
|
||||
@@ -411,34 +415,34 @@ void TimelineAction::redo() {
|
||||
}
|
||||
|
||||
// delete media
|
||||
if (deleted_media.size() > 0) {
|
||||
for (int i=0;i<deleted_media.size();i++) {
|
||||
QTreeWidgetItem* item = deleted_media.at(i);
|
||||
deleted_media_parents.append(item->parent());
|
||||
for (int i=0;i<deleted_media.size();i++) {
|
||||
QTreeWidgetItem* item = deleted_media.at(i);
|
||||
deleted_media_parents.append(item->parent());
|
||||
|
||||
// if we're deleting the open sequence, close it
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE &&
|
||||
get_sequence_from_tree(item) == sequence &&
|
||||
!change_seq) {
|
||||
old_seq = sequence;
|
||||
new_seq = NULL;
|
||||
change_seq = true;
|
||||
}
|
||||
// if we're deleting the open sequence, close it
|
||||
if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE &&
|
||||
get_sequence_from_tree(item) == sequence &&
|
||||
!change_seq) {
|
||||
old_seq = sequence;
|
||||
new_seq = NULL;
|
||||
change_seq = true;
|
||||
}
|
||||
|
||||
if (item->parent() == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item));
|
||||
} else {
|
||||
item->parent()->removeChild(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item->parent() == NULL) {
|
||||
panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item));
|
||||
} else {
|
||||
item->parent()->removeChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
// add any new media
|
||||
if (media_to_add.size() > 0) {
|
||||
for (int i=0;i<media_to_add.size();i++) {
|
||||
panel_project->source_table->addTopLevelItem(media_to_add.at(i));
|
||||
}
|
||||
}
|
||||
for (int i=0;i<media_to_add.size();i++) {
|
||||
if (media_to_add_parents.at(i) == NULL) {
|
||||
panel_project->source_table->addTopLevelItem(media_to_add.at(i));
|
||||
} else {
|
||||
media_to_add_parents.at(i)->addChild(media_to_add.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// change sequence workarea
|
||||
if (change_in_out) {
|
||||
@@ -453,6 +457,8 @@ void TimelineAction::redo() {
|
||||
set_sequence(new_seq);
|
||||
}
|
||||
|
||||
project_changed = true;
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -28,7 +28,7 @@ public:
|
||||
void set_track(Sequence* s, int clip, int value);
|
||||
void increase_track(Sequence* s, int clip, int value);
|
||||
void delete_clip(Sequence* s, int clip);
|
||||
void add_media(QTreeWidgetItem* item);
|
||||
void add_media(QTreeWidgetItem* item, QTreeWidgetItem* parent);
|
||||
void delete_media(QTreeWidgetItem* item);
|
||||
void ripple(Sequence* s, long point, long length);
|
||||
void ripple(Sequence* s, long point, long length, QVector<int> &ignore);
|
||||
@@ -68,6 +68,7 @@ private:
|
||||
QVector<QTreeWidgetItem*> deleted_media_parents;
|
||||
|
||||
QVector<QTreeWidgetItem*> media_to_add;
|
||||
QVector<QTreeWidgetItem*> media_to_add_parents;
|
||||
|
||||
Sequence* ripple_sequence;
|
||||
bool ripple_enabled;
|
||||
@@ -87,6 +88,8 @@ private:
|
||||
|
||||
void new_action(Sequence* s, int action, int clip, long old_val, long new_val);
|
||||
void offset_links(QVector<Clip*>& clips, int offset);
|
||||
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
class LinkCommand : public QUndoCommand {
|
||||
|
||||
+44
-11
@@ -12,6 +12,8 @@
|
||||
#include <QMimeData>
|
||||
#include <QHeaderView>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
|
||||
SourceTable::SourceTable(QWidget* parent) : QTreeWidget(parent) {
|
||||
@@ -34,24 +36,41 @@ void SourceTable::show_context_menu(const QPoint& pos) {
|
||||
} else {
|
||||
if (selectedItems().size() == 1) {
|
||||
// replace footage
|
||||
if (get_type_from_tree(selectedItems().at(0)) == MEDIA_TYPE_FOOTAGE) {
|
||||
menu.addAction("Replace/Relink Media");
|
||||
int type = get_type_from_tree(selectedItems().at(0));
|
||||
if (type == MEDIA_TYPE_FOOTAGE) {
|
||||
QAction* replace_action = menu.addAction("Replace/Relink Media");
|
||||
connect(replace_action, SIGNAL(triggered(bool)), panel_project, SLOT(replace_selected_file()));
|
||||
}
|
||||
menu.addAction("Replace Clips Using This Media");
|
||||
if (type != MEDIA_TYPE_FOLDER) {
|
||||
QAction* replace_clip_media = menu.addAction("Replace Clips Using This Media");
|
||||
connect(replace_clip_media, SIGNAL(triggered(bool)), panel_project, SLOT(replace_clip_media()));
|
||||
}
|
||||
}
|
||||
|
||||
// duplicate item
|
||||
bool all_sequences = true;
|
||||
bool all_footage = true;
|
||||
for (int i=0;i<selectedItems().size();i++) {
|
||||
if (get_type_from_tree(selectedItems().at(i)) != MEDIA_TYPE_SEQUENCE) {
|
||||
all_sequences = false;
|
||||
break;
|
||||
all_sequences = false;
|
||||
}
|
||||
if (get_type_from_tree(selectedItems().at(i)) != MEDIA_TYPE_FOOTAGE) {
|
||||
all_footage = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ONLY sequences are selected
|
||||
if (all_sequences) {
|
||||
// ONLY sequences are selected
|
||||
QAction* duplicate_action = menu.addAction("Duplicate");
|
||||
connect(duplicate_action, SIGNAL(triggered(bool)), panel_project, SLOT(duplicate_selected()));
|
||||
}
|
||||
}
|
||||
|
||||
// ONLY footage is selected
|
||||
if (all_footage) {
|
||||
QAction* delete_footage_from_sequences = menu.addAction("Delete All Clips Using This Media");
|
||||
connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), panel_project, SLOT(delete_clips_using_selected_media()));
|
||||
}
|
||||
|
||||
// delete media
|
||||
QAction* delete_action = menu.addAction("Delete");
|
||||
@@ -127,22 +146,36 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
|
||||
|
||||
void SourceTable::dropEvent(QDropEvent* event) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
QTreeWidgetItem* drop_item = itemAt(event->pos());
|
||||
if (mimeData->hasUrls()) {
|
||||
// drag files in from outside
|
||||
QList<QUrl> urls = mimeData->urls();
|
||||
if (!urls.isEmpty()) {
|
||||
if (!urls.isEmpty()) {
|
||||
QStringList paths;
|
||||
for (int i=0;i<urls.size();i++) {
|
||||
qDebug() << urls.at(i).toLocalFile();
|
||||
for (int i=0;i<urls.size();i++) {
|
||||
paths.append(urls.at(i).toLocalFile());
|
||||
}
|
||||
panel_project->process_file_list(paths);
|
||||
bool replace = false;
|
||||
if (urls.size() == 1
|
||||
&& drop_item != NULL
|
||||
&& get_type_from_tree(drop_item) == MEDIA_TYPE_FOOTAGE
|
||||
&& !QFileInfo(paths.at(0)).isDir()
|
||||
&& QMessageBox::question(this, "Replace Media", "You dropped a file onto '" + drop_item->text(0) + "'. Would you like to replace it with the dropped file?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
||||
replace = true;
|
||||
panel_project->replace_media(drop_item, paths.at(0));
|
||||
}
|
||||
if (!replace) {
|
||||
QTreeWidgetItem* parent = NULL;
|
||||
if (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER) {
|
||||
parent = drop_item;
|
||||
}
|
||||
panel_project->process_file_list(NULL, paths, parent, NULL);
|
||||
}
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
} else {
|
||||
// dragging files within project
|
||||
QVector<QTreeWidgetItem*> move_items;
|
||||
QTreeWidgetItem* drop_item = itemAt(event->pos());
|
||||
// if we dragged to the root OR dragged to a folder
|
||||
if (drop_item == NULL || (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER)) {
|
||||
QList<QTreeWidgetItem*> selected_items = selectedItems();
|
||||
|
||||
+48
-42
@@ -79,7 +79,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
switch (get_type_from_tree(item)) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Media* m = get_media_from_tree(item);
|
||||
Media* m = get_footage_from_tree(item);
|
||||
if (m->ready) {
|
||||
if (!got_video_values) {
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
@@ -140,7 +140,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
|
||||
switch (item_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
m = get_media_from_tree(item);
|
||||
m = get_footage_from_tree(item);
|
||||
media = m;
|
||||
can_import = m->ready;
|
||||
break;
|
||||
@@ -815,12 +815,12 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
validator = g.ghost_length + frame_diff;
|
||||
if (validator < 1) frame_diff += (1 - validator);
|
||||
|
||||
// prevent clip length exceeding media length
|
||||
if (c->media_type == MEDIA_TYPE_SEQUENCE
|
||||
// prevent clip length exceeding media length
|
||||
if (c->media_type == MEDIA_TYPE_SEQUENCE
|
||||
|| (c->media_type == MEDIA_TYPE_FOOTAGE && !static_cast<Media*>(c->media)->get_stream_from_file_index(c->media_stream)->infinite_length)) {
|
||||
validator = g.old_clip_in + g.ghost_length + frame_diff;
|
||||
if (validator > g.media_length) frame_diff -= validator - g.media_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ripple ops
|
||||
@@ -1433,30 +1433,22 @@ void TimelineWidget::redraw_clips() {
|
||||
}
|
||||
|
||||
if (clip->media_type == MEDIA_TYPE_FOOTAGE) {
|
||||
bool draw_checkerboard = false;
|
||||
QRect checkerboard_rect = clip_rect;
|
||||
Media* m = static_cast<Media*>(clip->media);
|
||||
MediaStream* ms = m->get_stream_from_file_index(clip->media_stream);
|
||||
if (ms == NULL) {
|
||||
// draw "error lines" if media stream is missing
|
||||
clip_painter.setPen(QPen(QColor(64, 64, 64), 2));
|
||||
int limit = clip_rect.width();
|
||||
int clip_height = clip_rect.bottom()-clip_rect.top();
|
||||
for (int j=-clip_height;j<limit;j+=15) {
|
||||
int lines_start_x = clip_rect.left()+j;
|
||||
int lines_start_y = clip_rect.bottom();
|
||||
int lines_end_x = lines_start_x + clip_height;
|
||||
int lines_end_y = clip_rect.top();
|
||||
if (lines_start_x < clip_rect.left()) {
|
||||
lines_start_y -= (clip_rect.left() - lines_start_x);
|
||||
lines_start_x = clip_rect.left();
|
||||
}
|
||||
if (lines_end_x > clip_rect.right()) {
|
||||
lines_end_y -= (clip_rect.right() - lines_end_x);
|
||||
lines_end_x = clip_rect.right();
|
||||
}
|
||||
clip_painter.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y);
|
||||
}
|
||||
draw_checkerboard = true;
|
||||
} else if (ms->preview_done) {
|
||||
// draw thumbnail/waveform
|
||||
long media_length = m->get_length_in_frames(clip->sequence->frame_rate);
|
||||
int waveform_limit = qMin(clip_rect.width(), panel_timeline->getScreenPointFromFrame(media_length - clip->clip_in));
|
||||
|
||||
if (waveform_limit < clip_rect.width()) {
|
||||
draw_checkerboard = true;
|
||||
checkerboard_rect.setLeft(clip_rect.left() + waveform_limit);
|
||||
}
|
||||
|
||||
if (clip->track < 0) {
|
||||
int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING;
|
||||
int thumb_height = clip_rect.height()-thumb_y;
|
||||
@@ -1465,33 +1457,47 @@ void TimelineWidget::redraw_clips() {
|
||||
QRect thumb_rect(thumb_x, clip_rect.y()+thumb_y, thumb_width, thumb_height);
|
||||
clip_painter.drawImage(thumb_rect, ms->video_preview);
|
||||
}
|
||||
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
|
||||
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
|
||||
int divider = ms->audio_channels*2;
|
||||
|
||||
clip_painter.setPen(QColor(80, 80, 80));
|
||||
int channel_height = clip_rect.height()/ms->audio_channels;
|
||||
long media_length = m->get_length_in_frames(clip->sequence->frame_rate);
|
||||
for (int i=0;i<clip_rect.width();i++) {
|
||||
|
||||
for (int i=0;i<waveform_limit;i++) {
|
||||
int waveform_index = qFloor((((clip->clip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
|
||||
|
||||
for (int j=0;j<ms->audio_channels;j++) {
|
||||
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
for (int j=0;j<ms->audio_channels;j++) {
|
||||
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
|
||||
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
|
||||
/*if (offset >= 0 && offset < ms->audio_preview.size()) {
|
||||
|
||||
} else {
|
||||
QMessageBox::critical(this, "AAAAAAAAAAAAA", "Here's that terrible, no-good bug again!!! I've stepped around it but plz report the following to Matt:\n\n" + QString::number(offset) + " vs " + QString::number(clip->media_stream->audio_preview.size()), QMessageBox::Ok);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
clip_painter.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (draw_checkerboard) {
|
||||
// draw "error lines" if media stream is missing
|
||||
clip_painter.setPen(QPen(QColor(64, 64, 64), 2));
|
||||
int limit = checkerboard_rect.width();
|
||||
int clip_height = checkerboard_rect.bottom()-checkerboard_rect.top();
|
||||
for (int j=-clip_height;j<limit;j+=15) {
|
||||
int lines_start_x = checkerboard_rect.left()+j;
|
||||
int lines_start_y = checkerboard_rect.bottom();
|
||||
int lines_end_x = lines_start_x + clip_height;
|
||||
int lines_end_y = checkerboard_rect.top();
|
||||
if (lines_start_x < checkerboard_rect.left()) {
|
||||
lines_start_y -= (checkerboard_rect.left() - lines_start_x);
|
||||
lines_start_x = checkerboard_rect.left();
|
||||
}
|
||||
if (lines_end_x > checkerboard_rect.right()) {
|
||||
lines_end_y -= (checkerboard_rect.right() - lines_end_x);
|
||||
lines_end_x = checkerboard_rect.right();
|
||||
}
|
||||
clip_painter.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// top left bevel
|
||||
|
||||
+15
-12
@@ -110,19 +110,22 @@ void ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Media* m = static_cast<Media*>(c->media);
|
||||
if (m->ready
|
||||
&& m->get_stream_from_file_index(c->media_stream) != NULL
|
||||
&& is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, multithreaded);
|
||||
}
|
||||
if (m->ready) {
|
||||
if (m->get_stream_from_file_index(c->media_stream) != NULL
|
||||
&& is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, multithreaded);
|
||||
}
|
||||
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
} else {
|
||||
texture_failed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
|
||||
Reference in New Issue
Block a user