diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 312a9eff4..9e89f85fb 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -351,13 +351,31 @@ void ExportDialog::on_pushButton_clicked() case FORMAT_GIF: ext = "gif"; break; - case FORMAT_IMG: - // ext_name = ""; - // ext = ""; - // ext is determined from codec, but we prevent the switch from going to 'default' here - qDebug() << "[INFO] Image export not implemented yet"; - QMessageBox::information(this, "Image sequence support coming soon", "Sorry, image sequence exporting is currently unsupported. Check back soon (or hound GitHub issue #83).", QMessageBox::Ok); - return; + case FORMAT_IMG: + switch (format_vcodecs.at(ui->vcodecCombobox->currentIndex())) { + case AV_CODEC_ID_BMP: + ext = "bmp"; + break; + case AV_CODEC_ID_MJPEG: + ext = "jpg"; + break; + case AV_CODEC_ID_JPEG2000: + ext = "jp2"; + break; + case AV_CODEC_ID_PSD: + ext = "psd"; + break; + case AV_CODEC_ID_PNG: + ext = "png"; + break; + case AV_CODEC_ID_TIFF: + ext = "tif"; + break; + default: + qDebug() << "[ERROR] Invalid codec selection for an image sequence"; + QMessageBox::critical(this, "Invalid codec", "Couldn't determine output parameters for the selected codec. This is a bug, please contact the developers.", QMessageBox::Ok); + return; + } break; case FORMAT_MP3: ext = "mp3"; diff --git a/mainwindow.cpp b/mainwindow.cpp index 87530ebf2..7e15330de 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -94,12 +94,14 @@ MainWindow::MainWindow(QWidget *parent) : 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())); connect(ui->menuEdit, SIGNAL(aboutToShow()), this, SLOT(editMenu_About_To_Be_Shown())); + connect(ui->menu_File, SIGNAL(aboutToShow()), this, SLOT(fileMenu_About_To_Be_Shown())); QString data_dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if (!data_dir.isEmpty()) { QDir dir(data_dir); dir.mkpath("."); if (dir.exists()) { + // detect auto-recovery file autorecovery_filename = data_dir + "/autorecovery.ove"; if (QFile::exists(autorecovery_filename)) { if (QMessageBox::question(NULL, "Auto-recovery", "Olive didn't close properly and an autorecovery file was detected. Would you like to open it?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { @@ -110,6 +112,22 @@ MainWindow::MainWindow(QWidget *parent) : autorecovery_timer.setInterval(60000); QObject::connect(&autorecovery_timer, SIGNAL(timeout()), this, SLOT(autorecover_interval())); autorecovery_timer.start(); + + // search for open recents list + recent_proj_file = data_dir + "/recents"; + QFile f(recent_proj_file); + if (f.exists() && f.open(QFile::ReadOnly | QFile::Text)) { + QTextStream text_stream(&f); + while (true) { + QString line = text_stream.readLine(); + if (line.isNull()) { + break; + } else { + recent_projects.append(line); + } + } + f.close(); + } } } } @@ -556,3 +574,27 @@ void MainWindow::on_actionFolder_triggered() { panel_project->new_folder(); } + +void MainWindow::fileMenu_About_To_Be_Shown() { + if (recent_projects.size() > 0) { + ui->menuOpen_Recent->clear(); + ui->menuOpen_Recent->setEnabled(true); + for (int i=0;imenuOpen_Recent->addAction(recent_projects.at(i)); + action->setData(i); + connect(action, SIGNAL(triggered()), this, SLOT(load_recent_project())); + } + ui->menuOpen_Recent->addSeparator(); + QAction* clear_action = ui->menuOpen_Recent->addAction("Clear Recent List"); + connect(clear_action, SIGNAL(triggered()), panel_project, SLOT(clear_recent_projects())); + } else { + ui->menuOpen_Recent->setEnabled(false); + } +} + +void MainWindow::load_recent_project() { + if (can_close_project()) { + project_url = recent_projects.at(static_cast(sender())->data().toInt()); + panel_project->load_project(); + } +} diff --git a/mainwindow.h b/mainwindow.h index 55d8ba275..5df50b416 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -18,7 +18,7 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); + ~MainWindow(); protected: void closeEvent(QCloseEvent *); @@ -146,6 +146,10 @@ private slots: void editMenu_About_To_Be_Shown(); + void fileMenu_About_To_Be_Shown(); + + void load_recent_project(); + private: Ui::MainWindow *ui; void setup_layout(); diff --git a/mainwindow.ui b/mainwindow.ui index 04f5837e3..57503bb82 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -40,8 +40,14 @@ + + + Open Recent + + + @@ -589,6 +595,11 @@ Folder + + + Clear Recent List + + diff --git a/panels/project.cpp b/panels/project.cpp index 5c9ca4e25..d1f5864ae 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -12,6 +12,7 @@ #include "project/effect.h" #include "effects/transition.h" #include "io/previewgenerator.h" +#include "mainwindow.h" #include #include @@ -30,8 +31,12 @@ extern "C" { #include } +#define MAXIMUM_RECENT_PROJECTS 10 + bool project_changed = false; QString project_url = ""; +QStringList recent_projects; +QString recent_proj_file; Project::Project(QWidget *parent) : QDockWidget(parent), @@ -647,190 +652,6 @@ void Project::load_project() { cont = load_worker(file, stream, MEDIA_TYPE_SEQUENCE); } - /*int state = LOAD_STATE_IDLE; - while (!error && !stream.atEnd()) { - stream.readNext(); - switch (state) { - case LOAD_STATE_IDLE: - if (stream.isStartElement()) { - if (stream.name() == "footage") { - for (int j=0;jsave_id = temp_media_id; - m->name = temp_name; - temp_media_list.append(m); - } - state = LOAD_STATE_IDLE; - } else if (stream.isStartElement()) { - if (stream.name() == "name") { - stream.readNext(); - temp_name = stream.text().toString(); - } else if (stream.name() == "url") { - stream.readNext(); - temp_url = stream.text().toString(); - } - } - break; - case LOAD_STATE_SEQUENCE: - if (stream.isEndElement() && stream.name() == "sequence") { - // correct IDs - for (int i=0;iclip_count();i++) { - Clip* clip = temp_seq->get_clip(i); - - // correct IDs in linked clips - for (int j=0;jlinked.size();j++) { - bool found = false; - for (int k=0;kclip_count();k++) { - if (temp_seq->get_clip(k)->load_id == clip->linked.at(j)) { - clip->linked[j] = k; - found = true; - break; - } - } - if (!found) { - if (QMessageBox::warning(this, "Corrupt clip link", "Project contains a non-existent clip link. It may be corrupt. Would you like to load it anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - clip->linked.removeAt(j); - } else { - error = true; - } - } - } - } - - temp_seq->reset_undo(); - new_sequence(temp_seq, false); - state = LOAD_STATE_IDLE; - } else if (stream.isStartElement()) { - if (stream.name() == "name") { - stream.readNext(); - temp_seq->name = stream.text().toString(); - } else if (stream.name() == "width") { - stream.readNext(); - temp_seq->width = stream.text().toInt(); - } else if (stream.name() == "height") { - stream.readNext(); - temp_seq->height = stream.text().toInt(); - } else if (stream.name() == "framerate") { - stream.readNext(); - temp_seq->frame_rate = stream.text().toFloat(); - } else if (stream.name() == "afreq") { - stream.readNext(); - temp_seq->audio_frequency = stream.text().toInt(); - } else if (stream.name() == "alayout") { - stream.readNext(); - temp_seq->audio_layout = stream.text().toInt(); - } else if (stream.name() == "clip") { - temp_clip = new Clip(); - temp_clip->sequence = temp_seq; - state = LOAD_STATE_CLIP; - } - } - break; - case LOAD_STATE_CLIP: - if (stream.isEndElement() && stream.name() == "clip") { - temp_seq->add_clip(temp_clip); - state = LOAD_STATE_SEQUENCE; - } else if (stream.isStartElement()) { - if (stream.name() == "name") { - stream.readNext(); - temp_clip->name = stream.text().toString(); - } else if (stream.name() == "id") { - stream.readNext(); - temp_clip->load_id = stream.text().toInt(); - } else if (stream.name() == "clipin") { - stream.readNext(); - temp_clip->clip_in = stream.text().toInt(); - } else if (stream.name() == "in") { - stream.readNext(); - temp_clip->timeline_in = stream.text().toInt(); - } else if (stream.name() == "out") { - stream.readNext(); - temp_clip->timeline_out = stream.text().toInt(); - } else if (stream.name() == "track") { - stream.readNext(); - temp_clip->track = stream.text().toInt(); - } else if (stream.name() == "opening") { - stream.readNext(); - temp_clip->opening_transition = create_transition(stream.text().toInt(), temp_clip); - } else if (stream.name() == "closing") { - stream.readNext(); - temp_clip->closing_transition = create_transition(stream.text().toInt(), temp_clip); - } else if (stream.name() == "color") { - for (int j=0;jcolor_r = attr.value().toInt(); - } else if (attr.name().toString() == QLatin1String("g")) { - temp_clip->color_g = attr.value().toInt(); - } else if (attr.name().toString() == QLatin1String("b")) { - temp_clip->color_b = attr.value().toInt(); - } - } - } else if (stream.name() == "media") { - stream.readNext(); - for (int i=0;isave_id == stream.text().toInt()) { - temp_clip->media = m; - break; - } - } - } else if (stream.name() == "stream") { - stream.readNext(); - int stream_index = stream.text().toInt(); - if (temp_clip->media != NULL) { - temp_clip->media_stream = temp_clip->media->get_stream_from_file_index(stream_index); - if (temp_clip->media_stream == NULL) { - if (QMessageBox::warning(this, "Source changed", "Source file '" + temp_clip->media->url + "' appears to have changed since last saving the project. This is unsupported and may lead to crashes. Do you wish to continue?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { - error = true; - } - } - } - } else if (stream.name() == "linked") { - state = LOAD_STATE_CLIP_LINKS; - } else if (stream.name() == "effect") { - int effect_id = -1; - for (int j=0;jload(&stream); - temp_clip->effects.append(e); - } - } - } - break; - case LOAD_STATE_CLIP_LINKS: - if (stream.name() == "linked" && stream.isEndElement()) { - state = LOAD_STATE_CLIP; - } else if (stream.isStartElement() && stream.name() == "link") { - stream.readNext(); - temp_clip->linked.append(stream.text().toInt()); - } - break; - } - }*/ if (!cont) { QMessageBox::critical(this, "Project Load Error", "Error loading project: " + error_str, QMessageBox::Ok); } else if (stream.hasError()) { @@ -845,6 +666,10 @@ void Project::load_project() { } else { new_project(); } + + file.close(); + + add_recent_project(project_url); } void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type) { @@ -970,5 +795,47 @@ void Project::save_project() { file.close(); + add_recent_project(project_url); + project_changed = false; } + +void Project::save_recent_projects() { + // save to file + QFile f(recent_proj_file); + if (f.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) { + QTextStream out(&f); + for (int i=0;i 0) { + out << "\n"; + } + out << recent_projects.at(i); + } + f.close(); + } else { + qDebug() << "[WARNING] Could not save recent projects"; + } +} + +void Project::clear_recent_projects() { + recent_projects.clear(); + save_recent_projects(); +} + +void Project::add_recent_project(QString url) { + bool found = false; + for (int i=0;i MAXIMUM_RECENT_PROJECTS) { + recent_projects.removeLast(); + } + } + save_recent_projects(); +} diff --git a/panels/project.h b/panels/project.h index 2b31e1684..0bc4c4cc4 100644 --- a/panels/project.h +++ b/panels/project.h @@ -29,6 +29,8 @@ class Project; extern bool project_changed; extern QString project_url; +extern QStringList recent_projects; +extern QString recent_proj_file; class Project : public QDockWidget { @@ -73,8 +75,11 @@ private: QVector loaded_folders; QVector loaded_media; QTreeWidgetItem* find_loaded_folder_by_id(int id); + void add_recent_project(QString url); + void save_recent_projects(); private slots: void rename_media(QTreeWidgetItem* item, int column); + void clear_recent_projects(); }; #endif // PROJECT_H diff --git a/playback/playback.cpp b/playback/playback.cpp index ff75ff098..67df049b2 100644 --- a/playback/playback.cpp +++ b/playback/playback.cpp @@ -267,4 +267,5 @@ void set_sequence(Sequence* s) { sequence = s; panel_timeline->update_sequence(); panel_viewer->update_sequence(); + panel_timeline->setFocus(); } diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index 6c125173a..ba28406b8 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -41,6 +41,7 @@ void SourceTable::mousePressEvent(QMouseEvent* event) { } void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) { + stop_rename_timer(); if (selectedItems().count() == 0) { panel_project->import_dialog(); } else if (selectedItems().count() == 1) { diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index d3573dcd8..630ff39ff 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -985,7 +985,6 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { for (int i=0;idrag_frame_start, panel_timeline->split_tracks[i]); if (clip_index > -1) { - Clip* c = sequence->get_clip(clip_index); QVector tracks = panel_timeline->get_tracks_of_linked_clips(clip_index); for (int j=0;j