diff --git a/README.md b/README.md
index 306c93ee9..4f3fee830 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,6 @@ Olive is a free non-linear video editor for Windows, macOS, and Linux.
Discover more and download binaries at: https://www.olivevideoeditor.org/
-For compilation information, check the [wiki](https://github.com/olive-editor/olive/wiki). Currently the `master` branch serves more or less as the stable branch and the majority of development happens in the `alpha` branch. Before filing a feature request or bug report, please try the `alpha` branch first to ensure it hasn't already been addressed.
+Compiling instructions for Windows, macOS, and Linux are [here](https://olivevideoeditor.org/compile.php).
If you like Olive, please consider helping keep it alive by supporting it on [Patreon](https://www.patreon.com/olivevideoeditor).
diff --git a/icons/dirup-disabled.png b/icons/dirup-disabled.png
new file mode 100644
index 000000000..f23fa3f68
Binary files /dev/null and b/icons/dirup-disabled.png differ
diff --git a/icons/dirup.png b/icons/dirup.png
new file mode 100644
index 000000000..f67b44672
Binary files /dev/null and b/icons/dirup.png differ
diff --git a/icons/icons.qrc b/icons/icons.qrc
index 711fdcdc0..ac37d6339 100644
--- a/icons/icons.qrc
+++ b/icons/icons.qrc
@@ -49,5 +49,7 @@
transition-tool.png
transition-tool-disabled.png
error.png
+ dirup.png
+ dirup-disabled.png
diff --git a/io/config.cpp b/io/config.cpp
index bd0882f68..2ecb744ba 100644
--- a/io/config.cpp
+++ b/io/config.cpp
@@ -32,7 +32,8 @@ Config::Config()
autoscroll(AUTOSCROLL_PAGE_SCROLL),
audio_rate(48000),
fast_seeking(false),
- hover_focus(false)
+ hover_focus(false),
+ project_view_type(PROJECT_VIEW_TREE)
{}
void Config::load(QString path) {
@@ -115,6 +116,9 @@ void Config::load(QString path) {
} else if (stream.name() == "HoverFocus") {
stream.readNext();
hover_focus = (stream.text() == "1");
+ } else if (stream.name() == "ProjectViewType") {
+ stream.readNext();
+ project_view_type = stream.text().toInt();
}
}
}
@@ -163,6 +167,7 @@ void Config::save(QString path) {
stream.writeTextElement("AudioRate", QString::number(audio_rate));
stream.writeTextElement("FastSeeking", QString::number(fast_seeking));
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
+ stream.writeTextElement("ProjectViewType", QString::number(project_view_type));
stream.writeEndElement(); // configuration
stream.writeEndDocument(); // doc
diff --git a/io/config.h b/io/config.h
index 4092204ff..4972c9dd3 100644
--- a/io/config.h
+++ b/io/config.h
@@ -18,6 +18,9 @@
#define AUTOSCROLL_PAGE_SCROLL 1
#define AUTOSCROLL_SMOOTH_SCROLL 2
+#define PROJECT_VIEW_TREE 0
+#define PROJECT_VIEW_ICON 1
+
struct Config {
Config();
bool saved_layout;
@@ -44,6 +47,7 @@ struct Config {
int audio_rate;
bool fast_seeking;
bool hover_focus;
+ int project_view_type;
void load(QString path);
void save(QString path);
diff --git a/io/exportthread.cpp b/io/exportthread.cpp
index 52e6e6999..5715ce9f0 100644
--- a/io/exportthread.cpp
+++ b/io/exportthread.cpp
@@ -97,8 +97,8 @@ bool ExportThread::setupVideo() {
}
// allocate context
- vcodec_ctx = video_stream->codec;
-// vcodec_ctx = avcodec_alloc_context3(vcodec);
+// vcodec_ctx = video_stream->codec;
+ vcodec_ctx = avcodec_alloc_context3(vcodec);
if (!vcodec_ctx) {
dout << "[ERROR] Could not allocate video encoding context";
ed->export_error = "could not allocate video encoding context";
@@ -125,7 +125,7 @@ bool ExportThread::setupVideo() {
itoa(vcodec_ctx, buffer, 10);*/
//av_opt_set(vcodec_ctx->priv_data, "preset", "fast", AV_OPT_SEARCH_CHILDREN);
- av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN);
+ //av_opt_set(vcodec_ctx->priv_data, "x264opts", "opencl", AV_OPT_SEARCH_CHILDREN);
switch (video_compression_type) {
case COMPRESSION_TYPE_CFR:
@@ -203,8 +203,8 @@ bool ExportThread::setupAudio() {
}
// allocate context
- acodec_ctx = audio_stream->codec;
-// acodec_ctx = avcodec_alloc_context3(acodec);
+// acodec_ctx = audio_stream->codec;
+ acodec_ctx = avcodec_alloc_context3(acodec);
if (!acodec_ctx) {
dout << "[ERROR] Could not find allocate audio encoding context";
ed->export_error = "could not allocate audio encoding context";
@@ -448,14 +448,14 @@ void ExportThread::run() {
avcodec_close(vcodec_ctx);
av_packet_unref(&video_pkt);
av_frame_free(&video_frame);
-// avcodec_free_context(&vcodec_ctx);
+ avcodec_free_context(&vcodec_ctx);
}
if (audio_enabled) {
avcodec_close(acodec_ctx);
av_packet_unref(&audio_pkt);
av_frame_free(&audio_frame);
-// avcodec_free_context(&acodec_ctx);
+ avcodec_free_context(&acodec_ctx);
}
avformat_free_context(fmt_ctx);
diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp
index 696c7987a..bb24f3cbd 100644
--- a/io/previewgenerator.cpp
+++ b/io/previewgenerator.cpp
@@ -128,6 +128,7 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
QFile f(thumb_path);
if (f.exists() && ms->video_preview.load(thumb_path)) {
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
+ ms->make_square_thumb();
ms->preview_done = true;
} else {
found = false;
@@ -255,7 +256,7 @@ void PreviewGenerator::generate_waveform() {
if (!s->preview_done) {
int dstH = 120;
int dstW = dstH * ((float)temp_frame->width/(float)temp_frame->height);
- uint8_t* data = new uint8_t[dstW*dstH*3];
+ uint8_t* data = new uint8_t[dstW*dstH*4];
sws_ctx = sws_getContext(
temp_frame->width,
@@ -263,7 +264,7 @@ void PreviewGenerator::generate_waveform() {
static_cast(temp_frame->format),
dstW,
dstH,
- static_cast(AV_PIX_FMT_RGB24),
+ static_cast(AV_PIX_FMT_RGBA),
SWS_FAST_BILINEAR,
NULL,
NULL,
@@ -271,10 +272,11 @@ void PreviewGenerator::generate_waveform() {
);
int linesize[AV_NUM_DATA_POINTERS];
- linesize[0] = dstW*3;
+ linesize[0] = dstW*4;
sws_scale(sws_ctx, temp_frame->data, temp_frame->linesize, 0, temp_frame->height, &data, linesize);
- s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGB888);
+ s->video_preview = QImage(data, dstW, dstH, linesize[0], QImage::Format_RGBA8888);
+ s->make_square_thumb();
// is video interlaced?
s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE;
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 404a6dc9b..adc24850e 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -130,15 +130,6 @@ MainWindow::MainWindow(QWidget *parent) :
layout()->invalidate();
- // TODO maybe replace these with non-pointers later on?
- panel_sequence_viewer = new Viewer(this);
- panel_footage_viewer = new Viewer(this);
- panel_project = new Project(this);
- panel_effect_controls = new EffectControls(this);
- panel_timeline = new Timeline(this);
-
- setup_layout(false);
-
connect(ui->menuWindow, SIGNAL(aboutToShow()), this, SLOT(windowMenu_About_To_Be_Shown()));
connect(ui->menu_View, SIGNAL(aboutToShow()), this, SLOT(viewMenu_About_To_Be_Shown()));
connect(ui->menu_Tools, SIGNAL(aboutToShow()), this, SLOT(toolMenu_About_To_Be_Shown()));
@@ -215,6 +206,15 @@ MainWindow::MainWindow(QWidget *parent) :
}
}
+ // TODO maybe replace these with non-pointers later on?
+ panel_sequence_viewer = new Viewer(this);
+ panel_footage_viewer = new Viewer(this);
+ panel_project = new Project(this);
+ panel_effect_controls = new EffectControls(this);
+ panel_timeline = new Timeline(this);
+
+ setup_layout(false);
+
init_audio();
connect(ui->action_Undo, SIGNAL(triggered(bool)), this, SLOT(undo()));
@@ -439,7 +439,7 @@ void MainWindow::new_project() {
panel_project->new_project();
updateTitle("");
update_ui(false);
- panel_project->source_table->update();
+ panel_project->tree_view->update();
}
}
diff --git a/mainwindow.ui b/mainwindow.ui
index 61cc110c8..6533a2453 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -33,7 +33,7 @@
0
0
653
- 25
+ 16
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 260
- 20
-
-
-
-
- -
-
-
-
-
- Fit
-
-
- -
-
- 10%
-
-
- -
-
- 25%
-
-
- -
-
- 50%
-
-
- -
-
- 75%
-
-
- -
-
- 100%
-
-
- -
-
- 150%
-
-
- -
-
- 200%
-
-
- -
-
- 400%
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 259
- 20
-
-
-
-
-
-
-
-
diff --git a/project/clip.cpp b/project/clip.cpp
index df0927544..65fbdb50a 100644
--- a/project/clip.cpp
+++ b/project/clip.cpp
@@ -109,7 +109,12 @@ void Clip::refresh() {
// validates media if it was replaced
if (replaced && media != NULL && media->get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* m = media->to_footage();
- media_stream = (track < 0) ? m->video_tracks.at(0)->file_index : m->audio_tracks.at(0)->file_index;
+
+ if (track < 0 && m->video_tracks.size() > 0) {
+ media_stream = m->video_tracks.at(0)->file_index;
+ } else if (track >= 0 && m->audio_tracks.size() > 0) {
+ media_stream = m->audio_tracks.at(0)->file_index;
+ }
}
replaced = false;
diff --git a/project/footage.cpp b/project/footage.cpp
index 6dc841eb7..b54f5a4a3 100644
--- a/project/footage.cpp
+++ b/project/footage.cpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include "io/previewgenerator.h"
extern "C" {
@@ -55,3 +56,16 @@ FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
}
return NULL;
}
+
+void FootageStream::make_square_thumb() {
+ // generate square version for QListView?
+ int square_size = qMax(video_preview.width(), video_preview.height());
+ QPixmap pixmap(square_size, square_size);
+ pixmap.fill(Qt::transparent);
+ QPainter p(&pixmap);
+ int diff = (video_preview.width() - video_preview.height())>>1;
+ int sqx = (diff < 0) ? -diff : 0;
+ int sqy = (diff > 0) ? diff : 0;
+ p.drawImage(sqx, sqy, video_preview);
+ video_preview_square = QIcon(pixmap);
+}
diff --git a/project/footage.h b/project/footage.h
index 55442e3ed..015253173 100644
--- a/project/footage.h
+++ b/project/footage.h
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
#define VIDEO_PROGRESSIVE 0
#define VIDEO_TOP_FIELD_FIRST 1
@@ -32,7 +33,9 @@ struct FootageStream {
// preview thumbnail/waveform
bool preview_done;
QImage video_preview;
+ QIcon video_preview_square;
QVector audio_preview;
+ void make_square_thumb();
};
struct Footage {
diff --git a/project/media.cpp b/project/media.cpp
index bb01dc72e..cec9c7f8f 100644
--- a/project/media.cpp
+++ b/project/media.cpp
@@ -8,6 +8,8 @@
#include "panels/project.h"
#include "projectmodel.h"
+#include
+
#include "debug.h"
extern "C" {
@@ -253,6 +255,14 @@ QVariant Media::data(int column, int role) {
switch (role) {
case Qt::DecorationRole:
if (column == 0) {
+ if (get_type() == MEDIA_TYPE_FOOTAGE) {
+ Footage* f = to_footage();
+ if (f->video_tracks.size() > 0
+ && f->video_tracks.at(0)->preview_done) {
+ return f->video_tracks.at(0)->video_preview_square;
+ }
+ }
+
return icon;
}
break;
diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp
new file mode 100644
index 000000000..dd1bd3da2
--- /dev/null
+++ b/project/sourcescommon.cpp
@@ -0,0 +1,280 @@
+#include "sourcescommon.h"
+
+#include "panels/panels.h"
+#include "project/media.h"
+#include "project/undo.h"
+#include "panels/timeline.h"
+#include "panels/project.h"
+#include "project/footage.h"
+#include "panels/viewer.h"
+#include "io/config.h"
+#include "mainwindow.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+SourcesCommon::SourcesCommon(Project* parent) :
+ project_parent(parent),
+ editing_item(NULL)
+{
+ rename_timer.setInterval(1000);
+ connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
+}
+
+void SourcesCommon::create_seq_from_selected() {
+ if (!selected_items.isEmpty()) {
+ QVector media_list;
+ for (int i=0;iitem_to_media(selected_items.at(i)));
+ }
+
+ ComboAction* ca = new ComboAction();
+ Sequence* s = create_sequence_from_media(media_list);
+
+ // add clips to it
+ panel_timeline->create_ghosts_from_media(s, 0, media_list);
+ panel_timeline->add_clips_from_ghosts(ca, s);
+
+ project_parent->new_sequence(ca, s, true, NULL);
+ undo_stack.push(ca);
+ }
+}
+
+void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& items) {
+ QMenu menu(parent);
+
+ selected_items = items;
+
+ QAction* import_action = menu.addAction("Import...");
+ QObject::connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog()));
+
+ QAction* new_folder_action = menu.addAction("New Folder...");
+ QObject::connect(new_folder_action, SIGNAL(triggered(bool)), mainWindow, SLOT(on_actionFolder_triggered()));
+
+ if (items.size() > 0) {
+ Media* m = project_parent->item_to_media(items.at(0));
+
+ if (items.size() == 1) {
+ // replace footage
+ int type = m->get_type();
+ if (type == MEDIA_TYPE_FOOTAGE) {
+ QAction* replace_action = menu.addAction("Replace/Relink Media");
+ QObject::connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
+
+#if defined(Q_OS_WIN)
+ QAction* reveal_in_explorer = menu.addAction("Reveal in Explorer");
+#elif defined(Q_OS_MAC)
+ QAction* reveal_in_explorer = menu.addAction("Reveal in Finder");
+#else
+ QAction* reveal_in_explorer = menu.addAction("Reveal in File Manager");
+#endif
+ QObject::connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
+ }
+ if (type != MEDIA_TYPE_FOLDER) {
+ QAction* replace_clip_media = menu.addAction("Replace Clips Using This Media");
+ QObject::connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
+ }
+ }
+
+ // duplicate item
+ bool all_sequences = true;
+ bool all_footage = true;
+ for (int i=0;iget_type() != MEDIA_TYPE_SEQUENCE) {
+ all_sequences = false;
+ }
+ if (m->get_type() != MEDIA_TYPE_FOOTAGE) {
+ all_footage = false;
+ }
+ }
+
+ // create sequence from
+ QAction* create_seq_from = menu.addAction("Create Sequence With This Media");
+ QObject::connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
+
+ // ONLY sequences are selected
+ if (all_sequences) {
+ // ONLY sequences are selected
+ QAction* duplicate_action = menu.addAction("Duplicate");
+ QObject::connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, SLOT(duplicate_selected()));
+ }
+
+ // ONLY footage is selected
+ if (all_footage) {
+ QAction* delete_footage_from_sequences = menu.addAction("Delete All Clips Using This Media");
+ QObject::connect(delete_footage_from_sequences, SIGNAL(triggered(bool)), project_parent, SLOT(delete_clips_using_selected_media()));
+ }
+
+ // delete media
+ QAction* delete_action = menu.addAction("Delete");
+ QObject::connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
+
+ if (items.size() == 1) {
+ QAction* properties_action = menu.addAction("Properties...");
+ QObject::connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
+ }
+ }
+
+ menu.addSeparator();
+
+ QAction* tree_view_action = menu.addAction("Tree View");
+ connect(tree_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_tree_view()));
+
+ QAction* icon_view_action = menu.addAction("Icon View");
+ connect(icon_view_action, SIGNAL(triggered(bool)), project_parent, SLOT(set_icon_view()));
+
+ menu.exec(QCursor::pos());
+}
+
+void SourcesCommon::mousePressEvent(QMouseEvent *) {
+ stop_rename_timer();
+}
+
+void SourcesCommon::item_click(Media *m, const QModelIndex& index) {
+ if (editing_item == m) {
+ rename_timer.start();
+ } else {
+ editing_item = m;
+ editing_index = index;
+ }
+}
+
+void SourcesCommon::mouseDoubleClickEvent(QMouseEvent *e, const QModelIndexList& selected_items) {
+ stop_rename_timer();
+ if (selected_items.size() == 0) {
+ project_parent->import_dialog();
+ } else if (selected_items.size() == 1) {
+ Media* item = project_parent->item_to_media(selected_items.at(0));
+ switch (item->get_type()) {
+ case MEDIA_TYPE_FOOTAGE:
+ panel_footage_viewer->set_media(item);
+ panel_footage_viewer->setFocus();
+ break;
+ case MEDIA_TYPE_SEQUENCE:
+ undo_stack.push(new ChangeSequenceAction(item->to_sequence()));
+ break;
+ }
+ }
+}
+
+void SourcesCommon::dropEvent(QWidget* parent, QDropEvent *event, const QModelIndex& drop_item, const QModelIndexList& items) {
+ const QMimeData* mimeData = event->mimeData();
+ Media* m = project_parent->item_to_media(drop_item);
+ if (mimeData->hasUrls()) {
+ // drag files in from outside
+ QList urls = mimeData->urls();
+ if (!urls.isEmpty()) {
+ QStringList paths;
+ for (int i=0;iget_type() == MEDIA_TYPE_FOOTAGE
+ && !QFileInfo(paths.at(0)).isDir()
+ && config.drop_on_media_to_replace
+ && QMessageBox::question(parent, "Replace Media", "You dropped a file onto '" + m->get_name() + "'. Would you like to replace it with the dropped file?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
+ replace = true;
+ project_parent->replace_media(m, paths.at(0));
+ }
+ if (!replace) {
+ QModelIndex parent;
+ if (drop_item.isValid()) {
+ if (m->get_type() == MEDIA_TYPE_FOLDER) {
+ parent = drop_item;
+ } else {
+ parent = drop_item.parent();
+ }
+ }
+ project_parent->process_file_list(paths, false, NULL, panel_project->item_to_media(parent));
+ }
+ }
+ event->acceptProposedAction();
+ } else {
+ event->ignore();
+
+ // dragging files within project
+ // if we dragged to the root OR dragged to a folder
+ if (!drop_item.isValid() || (drop_item.isValid() && m->get_type() == MEDIA_TYPE_FOLDER)) {
+ QVector move_items;
+ for (int i=0;iitem_to_media(item);
+ if (parent != drop_item && item != drop_item) {
+ bool ignore = false;
+ if (parent.isValid()) {
+ // if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
+ QModelIndex par = parent;
+ while (par.isValid() && !ignore) {
+ for (int j=0;j 0) {
+ MediaMove* mm = new MediaMove();
+ mm->to = m;
+ mm->items = move_items;
+ undo_stack.push(mm);
+ }
+ }
+ }
+}
+
+void SourcesCommon::reveal_in_browser() {
+ Media* media = project_parent->item_to_media(selected_items.at(0));
+ Footage* m = media->to_footage();
+
+#if defined(Q_OS_WIN)
+ QStringList args;
+ args << "/select," << QDir::toNativeSeparators(m->url);
+ QProcess::startDetached("explorer", args);
+#elif defined(Q_OS_MAC)
+ QStringList args;
+ args << "-e";
+ args << "tell application \"Finder\"";
+ args << "-e";
+ args << "activate";
+ args << "-e";
+ args << "select POSIX file \""+m->url+"\"";
+ args << "-e";
+ args << "end tell";
+ QProcess::startDetached("osascript", args);
+#else
+ QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
+#endif
+}
+
+void SourcesCommon::stop_rename_timer() {
+ rename_timer.stop();
+}
+
+void SourcesCommon::rename_interval() {
+ stop_rename_timer();
+ if (view->hasFocus() && editing_item != NULL) {
+ view->edit(editing_index);
+ }
+}
+
+void SourcesCommon::item_renamed(Media* item) {
+ if (editing_item == item) {
+ MediaRename* mr = new MediaRename(item, "idk");
+ undo_stack.push(mr);
+ editing_item = NULL;
+ }
+}
diff --git a/project/sourcescommon.h b/project/sourcescommon.h
new file mode 100644
index 000000000..4522efdfa
--- /dev/null
+++ b/project/sourcescommon.h
@@ -0,0 +1,39 @@
+#ifndef SOURCESCOMMON_H
+#define SOURCESCOMMON_H
+
+#include
+#include
+
+class Project;
+class QMouseEvent;
+class Media;
+class QAbstractItemView;
+class QDropEvent;
+
+class SourcesCommon : public QObject {
+ Q_OBJECT
+public:
+ SourcesCommon(Project *parent);
+ QAbstractItemView* view;
+ void show_context_menu(QWidget* parent, const QModelIndexList &items);
+
+ void mousePressEvent(QMouseEvent* e);
+ void mouseDoubleClickEvent(QMouseEvent* e, const QModelIndexList& selected_items);
+ void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
+
+ void item_click(Media* m, const QModelIndex &index);
+private slots:
+ void create_seq_from_selected();
+ void reveal_in_browser();
+ void rename_interval();
+ void item_renamed(Media *item);
+private:
+ Media* editing_item;
+ QModelIndex editing_index;
+ QModelIndexList selected_items;
+ Project* project_parent;
+ void stop_rename_timer();
+ QTimer rename_timer;
+};
+
+#endif // SOURCESCOMMON_H
diff --git a/project/undo.cpp b/project/undo.cpp
index 17fe258d7..8e9132d82 100644
--- a/project/undo.cpp
+++ b/project/undo.cpp
@@ -592,6 +592,7 @@ void ReplaceMediaCommand::replace(QString& filename) {
// replace media
QStringList files;
files.append(filename);
+ item->to_footage()->ready_lock.lock();
panel_project->process_file_list(files, false, item, NULL);
}
@@ -694,7 +695,7 @@ void EffectDeleteCommand::redo() {
mainWindow->setWindowModified(true);
}
-MediaMove::MediaMove(SourceTable *s) : table(s), old_project_changed(mainWindow->isWindowModified()) {}
+MediaMove::MediaMove() : old_project_changed(mainWindow->isWindowModified()) {}
void MediaMove::undo() {
for (int i=0;i items;
Media* to;
void undo();
void redo();
private:
QVector froms;
- SourceTable* table;
bool old_project_changed;
};
diff --git a/ui/otreeview.cpp b/ui/otreeview.cpp
new file mode 100644
index 000000000..9ea03a828
--- /dev/null
+++ b/ui/otreeview.cpp
@@ -0,0 +1,9 @@
+#include "otreeview.h"
+
+OTreeView::OTreeView(QWidget *parent) : QTreeView(parent) {
+ setSortingEnabled(true);
+ sortByColumn(0, Qt::AscendingOrder);
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
+ connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
+}
diff --git a/ui/otreeview.h b/ui/otreeview.h
new file mode 100644
index 000000000..810f8b5ec
--- /dev/null
+++ b/ui/otreeview.h
@@ -0,0 +1,16 @@
+#ifndef OTREEVIEW_H
+#define OTREEVIEW_H
+
+#include
+
+#include "ui/sourcetable.h"
+
+class OTreeView : public QTreeView {
+ Q_OBJECT
+public:
+ OTreeView(QWidget* parent = 0);
+private:
+
+};
+
+#endif // OTREEVIEW_H
diff --git a/ui/sourceiconview.cpp b/ui/sourceiconview.cpp
new file mode 100644
index 000000000..9617adc24
--- /dev/null
+++ b/ui/sourceiconview.cpp
@@ -0,0 +1,67 @@
+#include "sourceiconview.h"
+
+#include
+
+#include "panels/project.h"
+#include "project/media.h"
+#include "project/sourcescommon.h"
+#include "debug.h"
+
+SourceIconView::SourceIconView(QWidget *parent) : QListView(parent) {
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
+}
+
+void SourceIconView::show_context_menu() {
+ project_parent->sources_common->show_context_menu(this, selectedIndexes());
+}
+
+void SourceIconView::item_click(const QModelIndex& index) {
+ if (selectionModel()->selectedRows().size() == 1 && index.column() == 0) {
+ project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
+ }
+}
+
+void SourceIconView::mousePressEvent(QMouseEvent* event) {
+ project_parent->sources_common->mousePressEvent(event);
+ if (!indexAt(event->pos()).isValid()) selectionModel()->clear();
+ QListView::mousePressEvent(event);
+}
+
+void SourceIconView::dragEnterEvent(QDragEnterEvent *event) {
+ if (event->mimeData()->hasUrls()) {
+ event->acceptProposedAction();
+ } else {
+ QListView::dragEnterEvent(event);
+ }
+}
+
+void SourceIconView::dragMoveEvent(QDragMoveEvent *event) {
+ if (event->mimeData()->hasUrls()) {
+ event->acceptProposedAction();
+ } else {
+ QListView::dragMoveEvent(event);
+ }
+}
+
+void SourceIconView::dropEvent(QDropEvent* event) {
+ QModelIndex drop_item = indexAt(event->pos());
+ if (!drop_item.isValid()) drop_item = rootIndex();
+ project_parent->sources_common->dropEvent(this, event, drop_item, selectedIndexes());
+}
+
+void SourceIconView::mouseDoubleClickEvent(QMouseEvent *event) {
+ bool default_behavior = true;
+ if (selectedIndexes().size() == 1) {
+ Media* m = project_parent->item_to_media(selectedIndexes().at(0));
+ if (m->get_type() == MEDIA_TYPE_FOLDER) {
+ default_behavior = false;
+ setRootIndex(selectedIndexes().at(0));
+ emit changed_root();
+ }
+ }
+ if (default_behavior) {
+ project_parent->sources_common->mouseDoubleClickEvent(event, selectedIndexes());
+ }
+}
diff --git a/ui/sourceiconview.h b/ui/sourceiconview.h
new file mode 100644
index 000000000..9320a1628
--- /dev/null
+++ b/ui/sourceiconview.h
@@ -0,0 +1,26 @@
+#ifndef SOURCEICONVIEW_H
+#define SOURCEICONVIEW_H
+
+#include
+
+class Project;
+
+class SourceIconView : public QListView {
+ Q_OBJECT
+public:
+ SourceIconView(QWidget* parent = 0);
+ Project* project_parent;
+
+ void mousePressEvent(QMouseEvent* event);
+ void mouseDoubleClickEvent(QMouseEvent *event);
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dropEvent(QDropEvent* event);
+signals:
+ void changed_root();
+private slots:
+ void show_context_menu();
+ void item_click(const QModelIndex& index);
+};
+
+#endif // SOURCEICONVIEW_H
diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp
index aa835f55d..75586e8b4 100644
--- a/ui/sourcetable.cpp
+++ b/ui/sourcetable.cpp
@@ -11,6 +11,7 @@
#include "mainwindow.h"
#include "io/config.h"
#include "project/media.h"
+#include "project/sourcescommon.h"
#include "debug.h"
#include
@@ -25,193 +26,30 @@
#include
SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
- editing_item = NULL;
setSortingEnabled(true);
sortByColumn(0, Qt::AscendingOrder);
- rename_timer.setInterval(1000);
setContextMenuPolicy(Qt::CustomContextMenu);
- connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
}
void SourceTable::show_context_menu() {
- QMenu menu(this);
-
- QAction* import_action = menu.addAction("Import...");
- connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog()));
-
- QAction* new_folder_action = menu.addAction("New Folder...");
- connect(new_folder_action, SIGNAL(triggered(bool)), mainWindow, SLOT(on_actionFolder_triggered()));
-
- QModelIndexList selected_items = selectionModel()->selectedRows();
-
- if (selected_items.size() > 0) {
- Media* m = project_parent->item_to_media(selected_items.at(0));
-
- if (selected_items.size() == 1) {
- // replace footage
- int type = m->get_type();
- if (type == MEDIA_TYPE_FOOTAGE) {
- QAction* replace_action = menu.addAction("Replace/Relink Media");
- connect(replace_action, SIGNAL(triggered(bool)), project_parent, SLOT(replace_selected_file()));
-
-#if defined(Q_OS_WIN)
- QAction* reveal_in_explorer = menu.addAction("Reveal in Explorer");
-#elif defined(Q_OS_MAC)
- QAction* reveal_in_explorer = menu.addAction("Reveal in Finder");
-#else
- QAction* reveal_in_explorer = menu.addAction("Reveal in File Manager");
-#endif
- connect(reveal_in_explorer, SIGNAL(triggered(bool)), this, SLOT(reveal_in_browser()));
- }
- if (type != MEDIA_TYPE_FOLDER) {
- QAction* replace_clip_media = menu.addAction("Replace Clips Using This Media");
- connect(replace_clip_media, SIGNAL(triggered(bool)), project_parent, SLOT(replace_clip_media()));
- }
- }
-
- // duplicate item
- bool all_sequences = true;
- bool all_footage = true;
- for (int i=0;iget_type() != MEDIA_TYPE_SEQUENCE) {
- all_sequences = false;
- }
- if (m->get_type() != MEDIA_TYPE_FOOTAGE) {
- all_footage = false;
- }
- }
-
- // create sequence from
- QAction* create_seq_from = menu.addAction("Create Sequence With This Media");
- connect(create_seq_from, SIGNAL(triggered(bool)), this, SLOT(create_seq_from_selected()));
-
- // ONLY sequences are selected
- if (all_sequences) {
- // ONLY sequences are selected
- QAction* duplicate_action = menu.addAction("Duplicate");
- connect(duplicate_action, SIGNAL(triggered(bool)), project_parent, 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)), project_parent, SLOT(delete_clips_using_selected_media()));
- }
-
- // delete media
- QAction* delete_action = menu.addAction("Delete");
- connect(delete_action, SIGNAL(triggered(bool)), project_parent, SLOT(delete_selected_media()));
-
- if (selected_items.size() == 1) {
- QAction* properties_action = menu.addAction("Properties...");
- connect(properties_action, SIGNAL(triggered(bool)), project_parent, SLOT(open_properties()));
- }
- }
-
- menu.exec(QCursor::pos());
+ project_parent->sources_common->show_context_menu(this, selectionModel()->selectedRows());
}
-void SourceTable::create_seq_from_selected() {
- QModelIndexList selected_items = selectionModel()->selectedRows();
-
- if (!selected_items.isEmpty()) {
- QVector media_list;
- for (int i=0;iitem_to_media(selected_items.at(i)));
- }
-
- ComboAction* ca = new ComboAction();
- Sequence* s = create_sequence_from_media(media_list);
-
- // add clips to it
- panel_timeline->create_ghosts_from_media(s, 0, media_list);
- panel_timeline->add_clips_from_ghosts(ca, s);
-
- project_parent->new_sequence(ca, s, true, NULL);
- undo_stack.push(ca);
- }
-}
-
-void SourceTable::reveal_in_browser() {
- QModelIndexList selected_items = selectionModel()->selectedRows();
- Media* media = project_parent->item_to_media(selected_items.at(0));
- Footage* m = media->to_footage();
-
-#if defined(Q_OS_WIN)
- QStringList args;
- args << "/select," << QDir::toNativeSeparators(m->url);
- QProcess::startDetached("explorer", args);
-#elif defined(Q_OS_MAC)
- QStringList args;
- args << "-e";
- args << "tell application \"Finder\"";
- args << "-e";
- args << "activate";
- args << "-e";
- args << "select POSIX file \""+m->url+"\"";
- args << "-e";
- args << "end tell";
- QProcess::startDetached("osascript", args);
-#else
- QDesktopServices::openUrl(QUrl::fromLocalFile(m->url.left(m->url.lastIndexOf('/'))));
-#endif
-}
-
-void SourceTable::item_renamed(Media* item) {
- if (editing_item == item) {
- MediaRename* mr = new MediaRename(item, "idk");
- undo_stack.push(mr);
- editing_item = NULL;
- }
-}
-
-void SourceTable::stop_rename_timer() {
- rename_timer.stop();
-}
-
-void SourceTable::rename_interval() {
- stop_rename_timer();
- if (hasFocus() && editing_item != NULL) {
- edit(editing_index);
- //editItem(editing_item, 0);
- }
-}
void SourceTable::item_click(const QModelIndex& index) {
if (selectionModel()->selectedRows().size() == 1 && index.column() == 0) {
- Media* m = project_parent->item_to_media(index);
- if (editing_item == m) {
- rename_timer.start();
- } else {
- editing_item = m;
- editing_index = index;
- }
+ project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
}
}
void SourceTable::mousePressEvent(QMouseEvent* event) {
- stop_rename_timer();
+ project_parent->sources_common->mousePressEvent(event);
QTreeView::mousePressEvent(event);
}
-void SourceTable::mouseDoubleClickEvent(QMouseEvent* e) {
- stop_rename_timer();
- QModelIndexList selected_items = selectionModel()->selectedRows();
- if (selected_items.size() == 0) {
- project_parent->import_dialog();
- } else if (selected_items.size() == 1) {
- Media* item = project_parent->item_to_media(selected_items.at(0));
- switch (item->get_type()) {
- case MEDIA_TYPE_FOOTAGE:
- panel_footage_viewer->set_media(item);
- panel_footage_viewer->setFocus();
- break;
- case MEDIA_TYPE_SEQUENCE:
- undo_stack.push(new ChangeSequenceAction(item->to_sequence()));
- break;
- }
- }
+void SourceTable::mouseDoubleClickEvent(QMouseEvent* event) {
+ project_parent->sources_common->mouseDoubleClickEvent(event, selectionModel()->selectedRows());
}
void SourceTable::dragEnterEvent(QDragEnterEvent *event) {
@@ -231,79 +69,5 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
}
void SourceTable::dropEvent(QDropEvent* event) {
- const QMimeData* mimeData = event->mimeData();
- const QModelIndex& drop_item = indexAt(event->pos());
- Media* m = project_parent->item_to_media(drop_item);
- if (mimeData->hasUrls()) {
- // drag files in from outside
- QList urls = mimeData->urls();
- if (!urls.isEmpty()) {
- QStringList paths;
- for (int i=0;iget_type() == MEDIA_TYPE_FOOTAGE
- && !QFileInfo(paths.at(0)).isDir()
- && config.drop_on_media_to_replace
- && QMessageBox::question(this, "Replace Media", "You dropped a file onto '" + m->get_name() + "'. Would you like to replace it with the dropped file?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
- replace = true;
- project_parent->replace_media(m, paths.at(0));
- }
- if (!replace) {
- QModelIndex parent;
- if (drop_item.isValid()) {
- if (m->get_type() == MEDIA_TYPE_FOLDER) {
- parent = drop_item;
- } else {
- parent = drop_item.parent();
- }
- }
- if (parent.isValid()) setExpanded(parent, true);
- project_parent->process_file_list(paths, false, NULL, panel_project->item_to_media(parent));
- }
- }
- event->acceptProposedAction();
- } else {
- event->ignore();
-
- // dragging files within project
- // if we dragged to the root OR dragged to a folder
- if (!drop_item.isValid() || (drop_item.isValid() && m->get_type() == MEDIA_TYPE_FOLDER)) {
- QVector move_items;
- QModelIndexList selected_items = selectionModel()->selectedRows();
- for (int i=0;iitem_to_media(item);
- if (parent != drop_item && item != drop_item) {
- bool ignore = false;
- if (parent.isValid()) {
- // if child belongs to a selected parent, assume the user is just moving the parent and ignore the child
- QModelIndex par = parent;
- while (par.isValid() && !ignore) {
- for (int j=0;j 0) {
- MediaMove* mm = new MediaMove(this);
- mm->to = m;
- mm->items = move_items;
- undo_stack.push(mm);
- }
- }
- }
+ project_parent->sources_common->dropEvent(this, event, indexAt(event->pos()), selectionModel()->selectedRows());
}
diff --git a/ui/sourcetable.h b/ui/sourcetable.h
index c2a2637a7..aa19a5449 100644
--- a/ui/sourcetable.h
+++ b/ui/sourcetable.h
@@ -12,26 +12,17 @@ class SourceTable : public QTreeView
{
Q_OBJECT
public:
- SourceTable(QWidget* parent = 0);
+ SourceTable(QWidget* parent = 0);
Project* project_parent;
protected:
void mousePressEvent(QMouseEvent*);
- void mouseDoubleClickEvent(QMouseEvent *event);
+ void mouseDoubleClickEvent(QMouseEvent *);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
-private:
- QTimer rename_timer;
- Media* editing_item;
- QModelIndex editing_index;
private slots:
- void rename_interval();
void item_click(const QModelIndex& index);
- void stop_rename_timer();
- void item_renamed(Media *item);
- void show_context_menu();
- void create_seq_from_selected();
- void reveal_in_browser();
+ void show_context_menu();
};
#endif // SOURCETABLE_H
diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp
index ba4d42fcb..0506995af 100644
--- a/ui/timelinewidget.cpp
+++ b/ui/timelinewidget.cpp
@@ -10,6 +10,7 @@
#include "panels/timeline.h"
#include "project/footage.h"
#include "ui/sourcetable.h"
+#include "ui/sourceiconview.h"
#include "panels/effectcontrols.h"
#include "panels/viewer.h"
#include "project/undo.h"
@@ -265,8 +266,10 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
QVector media_list;
panel_timeline->importing_files = false;
- if (event->source() == panel_project->source_table) {
- QModelIndexList items = panel_project->source_table->selectionModel()->selectedRows();
+ if (event->source() == panel_project->tree_view || event->source() == panel_project->icon_view) {
+ QModelIndexList items = (event->source() == panel_project->tree_view)
+ ? panel_project->tree_view->selectionModel()->selectedRows()
+ : panel_project->icon_view->selectionModel()->selectedIndexes();
media_list.resize(items.size());
for (int i=0;iitem_to_media(items.at(i));
diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h
index 2ba01cab4..20e4bfea9 100644
--- a/ui/timelinewidget.h
+++ b/ui/timelinewidget.h
@@ -19,6 +19,7 @@ class TimelineAction;
class QScrollBar;
class SetSelectionsCommand;
class QPainter;
+class Media;
bool same_sign(int a, int b);
void draw_waveform(Clip* clip, FootageStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
@@ -65,7 +66,7 @@ private:
// used for "right click ripple"
long rc_ripple_min;
long rc_ripple_max;
- void* rc_reveal_media;
+ Media* rc_reveal_media;
QTimer tooltip_timer;
int tooltip_clip;
diff --git a/ui/viewercontainer.cpp b/ui/viewercontainer.cpp
index be24c84bf..5c109b077 100644
--- a/ui/viewercontainer.cpp
+++ b/ui/viewercontainer.cpp
@@ -75,6 +75,8 @@ void ViewerContainer::adjust() {
child->move(widget_x, widget_y);
child->resize(widget_width, widget_height);
+
+ zoom = double(widget_width) / double(viewer->seq->width);
} else {
int zoomed_width = double(viewer->seq->width)*zoom;
int zoomed_height = double(viewer->seq->height)*zoom;
diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp
index 0c80622cd..05156f77f 100644
--- a/ui/viewerwidget.cpp
+++ b/ui/viewerwidget.cpp
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
extern "C" {
#include
@@ -91,6 +92,22 @@ void ViewerWidget::show_context_menu() {
QAction* show_fullscreen_action = menu.addAction("Show Fullscreen");
connect(show_fullscreen_action, SIGNAL(triggered()), this, SLOT(show_fullscreen()));
+ QMenu zoom_menu("Zoom");
+ QAction* fit_zoom = zoom_menu.addAction("Fit");
+ connect(fit_zoom, SIGNAL(triggered(bool)), this, SLOT(set_fit_zoom()));
+ zoom_menu.addAction("10%")->setData(0.1);
+ zoom_menu.addAction("25%")->setData(0.25);
+ zoom_menu.addAction("50%")->setData(0.5);
+ zoom_menu.addAction("75%")->setData(0.75);
+ zoom_menu.addAction("100%")->setData(1.0);
+ zoom_menu.addAction("150%")->setData(1.5);
+ zoom_menu.addAction("200%")->setData(2.0);
+ zoom_menu.addAction("400%")->setData(4.0);
+ QAction* custom_zoom = zoom_menu.addAction("Custom");
+ connect(custom_zoom, SIGNAL(triggered(bool)), this, SLOT(set_custom_zoom()));
+ connect(&zoom_menu, SIGNAL(triggered(QAction*)), this, SLOT(set_menu_zoom(QAction*)));
+ menu.addMenu(&zoom_menu);
+
menu.exec(QCursor::pos());
}
@@ -130,6 +147,30 @@ void ViewerWidget::show_fullscreen() {
showFullScreen();
}
+void ViewerWidget::set_fit_zoom() {
+ container->fit = true;
+ container->adjust();
+}
+
+void ViewerWidget::set_custom_zoom() {
+ bool ok;
+ double d = QInputDialog::getDouble(this, "Viewer Zoom", "Set Custom Zoom Value:", container->zoom*100, 0, 2147483647, 2, &ok);
+ if (ok) {
+ container->fit = false;
+ container->zoom = d*0.01;
+ container->adjust();
+ }
+}
+
+void ViewerWidget::set_menu_zoom(QAction* action) {
+ const QVariant& data = action->data();
+ if (!data.isNull()) {
+ container->fit = false;
+ container->zoom = data.toDouble();
+ container->adjust();
+ }
+}
+
void ViewerWidget::retry() {
update();
}
@@ -640,12 +681,8 @@ GLuint ViewerWidget::compose_sequence(QVector& nests, bool render_audio)
int widget_width = width();
int widget_height = height();
- QString qt_scale_factor = QString(qgetenv("QT_SCALE_FACTOR"));
- if (!qt_scale_factor.isEmpty()) {
- double scale = qt_scale_factor.toDouble();
- widget_width *= scale;
- widget_height *= scale;
- }
+ widget_width *= QApplication::desktop()->devicePixelRatio();
+ widget_height *= QApplication::desktop()->devicePixelRatio();
glViewport(0, 0, widget_width, widget_height);
}
diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h
index 8d9899151..fa51aa383 100644
--- a/ui/viewerwidget.h
+++ b/ui/viewerwidget.h
@@ -70,6 +70,10 @@ private slots:
void show_context_menu();
void save_frame();
void show_fullscreen();
+
+ void set_fit_zoom();
+ void set_custom_zoom();
+ void set_menu_zoom(QAction *action);
};
#endif // VIEWERWIDGET_H