projectmanager.cpp 27.7 KB
Newer Older
Till Theato's avatar
Till Theato committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
Copyright (C) 2014  Till Theato <root@ttill.de>
This file is part of kdenlive. See www.kdenlive.org.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/

#include "projectmanager.h"
#include "core.h"
13
#include "bin/bin.h"
14
#include "mltcontroller/bincontroller.h"
15
#include "mltcontroller/producerqueue.h"
Till Theato's avatar
Till Theato committed
16 17 18 19
#include "mainwindow.h"
#include "kdenlivesettings.h"
#include "monitor/monitormanager.h"
#include "doc/kdenlivedoc.h"
Vincent Pinon's avatar
Vincent Pinon committed
20
#include "timeline/timeline.h"
Till Theato's avatar
Till Theato committed
21 22 23 24 25
#include "project/dialogs/projectsettings.h"
#include "timeline/customtrackview.h"
#include "transitionsettings.h"
#include "project/dialogs/archivewidget.h"
#include "effectstack/effectstackview2.h"
26
#include "project/dialogs/backupwidget.h"
27
#include "project/notesplugin.h"
28 29
#include "utils/KoIconUtils.h"

Till Theato's avatar
Till Theato committed
30
#include <KActionCollection>
31
#include <QAction>
Till Theato's avatar
Till Theato committed
32
#include <KMessageBox>
33
#include <klocalizedstring.h>
34

35
#include <QProgressDialog>
Till Theato's avatar
Till Theato committed
36
#include <QCryptographicHash>
37
#include <QFileDialog>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
38
#include <QDebug>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
39 40
#include <QMimeDatabase>
#include <QMimeType>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
41
#include <KConfigGroup>
Till Theato's avatar
Till Theato committed
42 43 44

ProjectManager::ProjectManager(QObject* parent) :
    QObject(parent),
45
    m_project(0),
46 47
    m_trackView(0),
    m_progressDialog(NULL)
Till Theato's avatar
Till Theato committed
48 49
{
    m_fileRevert = KStandardAction::revert(this, SLOT(slotRevert()), pCore->window()->actionCollection());
50
    m_fileRevert->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-revert")));
Till Theato's avatar
Till Theato committed
51 52
    m_fileRevert->setEnabled(false);

53
    QAction *a = KStandardAction::open(this,                   SLOT(openFile()),               pCore->window()->actionCollection());
54
    a->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-open")));
55
    a = KStandardAction::saveAs(this,                 SLOT(saveFileAs()),             pCore->window()->actionCollection());
56
    a->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-save-as")));
57
    a = KStandardAction::openNew(this,                SLOT(newFile()),                pCore->window()->actionCollection());
58
    a->setIcon(KoIconUtils::themedIcon(QStringLiteral("document-new")));
59
    m_recentFilesAction = KStandardAction::openRecent(this, SLOT(openFile(QUrl)), pCore->window()->actionCollection());
60

61 62
    QAction * backupAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("edit-undo")), i18n("Open Backup File"), this);
    pCore->window()->addAction(QStringLiteral("open_backup"), backupAction);
63
    connect(backupAction, SIGNAL(triggered(bool)), SLOT(slotOpenBackup()));
64 65

    m_notesPlugin = new NotesPlugin(this);
66 67 68

    m_autoSaveTimer.setSingleShot(true);
    connect(&m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
Till Theato's avatar
Till Theato committed
69 70 71 72
}

ProjectManager::~ProjectManager()
{
73 74
    delete m_notesPlugin;

75 76 77
    if (m_trackView) {
        delete m_trackView;
    }
78 79 80
    if (m_project) {
	delete m_project;
    }
Till Theato's avatar
Till Theato committed
81 82
}

83
void ProjectManager::slotLoadOnOpen()
Till Theato's avatar
Till Theato committed
84
{
85 86 87 88 89
    if (m_startUrl.isValid()) {
        openFile();
    }
    else if (KdenliveSettings::openlastproject()) {
        openLastFile();
Till Theato's avatar
Till Theato committed
90
    }
91
    else newFile(false);
Till Theato's avatar
Till Theato committed
92

93 94
    if (!m_loadClipsOnOpen.isEmpty() && m_project) {
        QStringList list = m_loadClipsOnOpen.split(',');
Till Theato's avatar
Till Theato committed
95 96
        QList <QUrl> urls;
        foreach(const QString &path, list) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
97
            //qDebug() << QDir::current().absoluteFilePath(path);
Till Theato's avatar
Till Theato committed
98 99
            urls << QUrl::fromLocalFile(QDir::current().absoluteFilePath(path));
        }
100
        pCore->bin()->droppedUrls(urls);
Till Theato's avatar
Till Theato committed
101
    }
102 103 104 105 106 107 108
    m_loadClipsOnOpen.clear();
}

void ProjectManager::init(const QUrl& projectUrl, const QString& clipList)
{
    m_startUrl = projectUrl;
    m_loadClipsOnOpen = clipList;
Till Theato's avatar
Till Theato committed
109 110 111 112 113 114 115
}

void ProjectManager::newFile(bool showProjectSettings, bool force)
{
    if (!pCore->window()->m_timelineArea->isEnabled() && !force) {
        return;
    }
116
    // fix mantis#3160
117
    QUrl startFile = QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder() + "/_untitled.kdenlive");
118 119 120
    if (checkForBackupFile(startFile)) {
        return;
    }
Till Theato's avatar
Till Theato committed
121 122
    m_fileRevert->setEnabled(false);
    QString profileName = KdenliveSettings::default_profile();
123
    QUrl projectFolder = QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder());
Till Theato's avatar
Till Theato committed
124 125 126
    QMap <QString, QString> documentProperties;
    QMap <QString, QString> documentMetadata;
    QPoint projectTracks(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks());
127
    pCore->monitorManager()->resetDisplay();
Till Theato's avatar
Till Theato committed
128
    if (!showProjectSettings) {
Till Theato's avatar
Till Theato committed
129 130
        if (!closeCurrentDocument()) {
            return;
Till Theato's avatar
Till Theato committed
131 132 133
        }
    } else {
        QPointer<ProjectSettings> w = new ProjectSettings(NULL, QMap <QString, QString> (), QStringList(), projectTracks.x(), projectTracks.y(), KdenliveSettings::defaultprojectfolder(), false, true, pCore->window());
134
        connect(w, SIGNAL(refreshProfiles()), pCore->window(), SLOT(slotRefreshProfiles()));
Till Theato's avatar
Till Theato committed
135 136 137 138
        if (w->exec() != QDialog::Accepted) {
            delete w;
            return;
        }
Till Theato's avatar
Till Theato committed
139 140 141
        if (!closeCurrentDocument()) {
            delete w;
            return;
Till Theato's avatar
Till Theato committed
142 143 144 145 146 147 148 149 150 151
        }
        if (KdenliveSettings::videothumbnails() != w->enableVideoThumbs()) {
            pCore->window()->slotSwitchVideoThumbs();
        }
        if (KdenliveSettings::audiothumbnails() != w->enableAudioThumbs()) {
            pCore->window()->slotSwitchAudioThumbs();
        }
        profileName = w->selectedProfile();
        projectFolder = w->selectedFolder();
        projectTracks = w->tracks();
152 153 154 155 156 157
        documentProperties.insert(QStringLiteral("enableproxy"), QString::number((int) w->useProxy()));
        documentProperties.insert(QStringLiteral("generateproxy"), QString::number((int) w->generateProxy()));
        documentProperties.insert(QStringLiteral("proxyminsize"), QString::number(w->proxyMinSize()));
        documentProperties.insert(QStringLiteral("proxyparams"), w->proxyParams());
        documentProperties.insert(QStringLiteral("proxyextension"), w->proxyExtension());
        documentProperties.insert(QStringLiteral("generateimageproxy"), QString::number((int) w->generateImageProxy()));
158 159 160 161 162
        QString preview = w->selectedPreview();
        if (!preview.isEmpty()) {
            documentProperties.insert(QStringLiteral("previewparameters"), preview.section(";", 0, 0));
            documentProperties.insert(QStringLiteral("previewextension"), preview.section(";", 1, 1));
        }
163
        documentProperties.insert(QStringLiteral("proxyimageminsize"), QString::number(w->proxyImageMinSize()));
Till Theato's avatar
Till Theato committed
164 165 166 167 168
        documentMetadata = w->metadata();
        delete w;
    }
    pCore->window()->m_timelineArea->setEnabled(true);
    bool openBackup;
169
    m_notesPlugin->clear();
170
    KdenliveDoc *doc = new KdenliveDoc(QUrl(), projectFolder, pCore->window()->m_commandStack, profileName, documentProperties, documentMetadata, projectTracks, pCore->monitorManager()->projectMonitor()->render, m_notesPlugin, &openBackup, pCore->window());
171
    doc->m_autosave = new KAutoSaveFile(startFile, doc);
Till Theato's avatar
Till Theato committed
172
    bool ok;
173
    pCore->bin()->setDocument(doc);
174 175 176 177
    QList <QAction*> rulerActions;
    rulerActions << pCore->window()->actionCollection()->action(QStringLiteral("set_render_timeline_zone"));
    rulerActions << pCore->window()->actionCollection()->action(QStringLiteral("unset_render_timeline_zone"));
    m_trackView = new Timeline(doc, pCore->window()->kdenliveCategoryMap.value(QStringLiteral("timeline"))->actions(), rulerActions, &ok, pCore->window());
178 179 180
    // Set default target tracks to upper audio / lower video tracks
    m_trackView->audioTarget = projectTracks.y() > 0 ? projectTracks.y() : -1;
    m_trackView->videoTarget = projectTracks.x() > 0 ? projectTracks.y() + 1 : -1;
181
    connect(m_trackView->projectView(), SIGNAL(importPlaylistClips(ItemInfo, QUrl, QUndoCommand*)), pCore->bin(), SLOT(slotExpandUrl(ItemInfo, QUrl, QUndoCommand*)), Qt::DirectConnection);
182

183
    m_trackView->loadTimeline();
184
    pCore->window()->m_timelineArea->addTab(m_trackView, QIcon::fromTheme(QStringLiteral("kdenlive")), doc->description());
185
    m_project = doc;
Till Theato's avatar
Till Theato committed
186 187 188 189 190 191 192
    if (!ok) {
        // MLT is broken
        //pCore->window()->m_timelineArea->setEnabled(false);
        //pCore->window()->m_projectList->setEnabled(false);
        pCore->window()->slotPreferences(6);
        return;
    }
193
    pCore->window()->connectDocument();
194 195
    bool disabled = m_project->getDocumentProperty(QStringLiteral("disabletimelineeffects")) == QLatin1String("1");
    QAction *disableEffects = pCore->window()->actionCollection()->action(QStringLiteral("disable_timeline_effects"));
196 197 198 199 200 201 202
    if (disableEffects) {
        if (disabled != disableEffects->isChecked()) {
            disableEffects->blockSignals(true);
            disableEffects->setChecked(disabled);
            disableEffects->blockSignals(false);
        }
    }
203
    emit docOpened(m_project);
204
    //pCore->monitorManager()->activateMonitor(Kdenlive::ClipMonitor);
205
    m_trackView->projectView()->setFocus();
206
    m_lastSave.start();
Till Theato's avatar
Till Theato committed
207 208
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
209
bool ProjectManager::closeCurrentDocument(bool saveChanges, bool quit)
Till Theato's avatar
Till Theato committed
210
{
Till Theato's avatar
Till Theato committed
211
    if (m_project && m_project->isModified() && saveChanges) {
Till Theato's avatar
Till Theato committed
212 213 214 215 216 217 218 219 220 221
        QString message;
        if (m_project->url().fileName().isEmpty()) {
            message = i18n("Save changes to document?");
        } else {
            message = i18n("The project <b>\"%1\"</b> has been changed.\nDo you want to save your changes?", m_project->url().fileName());
        }

        switch (KMessageBox::warningYesNoCancel(pCore->window(), message)) {
        case KMessageBox::Yes :
            // save document here. If saving fails, return false;
Till Theato's avatar
Till Theato committed
222 223 224
            if (saveFile() == false) {
                return false;
            }
Till Theato's avatar
Till Theato committed
225 226 227 228 229 230 231 232
            break;
        case KMessageBox::Cancel :
            return false;
            break;
        default:
            break;
        }
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
233
    if (!quit && !qApp->isSavingSession()) {
234
	m_autoSaveTimer.stop();
235
        if (m_project) {
236
            pCore->producerQueue()->abortOperations();
237
            pCore->bin()->abortAudioThumbs();
238 239 240
            pCore->window()->slotTimelineClipSelected(NULL, false);
            pCore->monitorManager()->clipMonitor()->slotOpenClip(NULL);
            pCore->window()->m_effectStack->clear();
241
            pCore->window()->m_effectStack->transitionConfig()->slotTransitionItemSelected(NULL, 0, QPoint(), false);
242 243
            delete m_trackView;
            m_trackView = NULL;
244 245
            delete m_project;
            m_project = NULL;
246
        }
247 248
	pCore->monitorManager()->setDocument(m_project);
    }
Till Theato's avatar
Till Theato committed
249 250 251 252 253
    return true;
}

bool ProjectManager::saveFileAs(const QString &outputFileName)
{
254
    pCore->monitorManager()->pauseActiveMonitor();
255 256
    // Sync document properties
    prepareSave();
257

258 259
    QString scene = projectSceneList();
    if (m_project->saveSceneList(outputFileName, scene) == false) {
Till Theato's avatar
Till Theato committed
260 261
        return false;
    }
262

Till Theato's avatar
Till Theato committed
263
    // Save timeline thumbnails
264
    m_trackView->projectView()->saveThumbnails();
265 266
    m_project->setUrl(QUrl::fromLocalFile(outputFileName));
    // setting up autosave file in ~/.kde/data/stalefiles/kdenlive/
267
    // saved under file name
268 269
    // actual saving by KdenliveDoc::slotAutoSave() called by a timer 3 seconds after the document has been edited
    // This timer is set by KdenliveDoc::setModified()
Till Theato's avatar
Till Theato committed
270
    if (m_project->m_autosave == NULL) {
271 272
        // The temporary file is not opened or created until actually needed.
        // The file filename does not have to exist for KAutoSaveFile to be constructed (if it exists, it will not be touched).
273
        m_project->m_autosave = new KAutoSaveFile(QUrl::fromLocalFile(outputFileName), this);
Till Theato's avatar
Till Theato committed
274
    } else {
275
        m_project->m_autosave->setManagedFile(QUrl::fromLocalFile(outputFileName));
Till Theato's avatar
Till Theato committed
276 277
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
278
    pCore->window()->setWindowTitle(m_project->description());
Till Theato's avatar
Till Theato committed
279
    m_project->setModified(false);
280
    m_recentFilesAction->addUrl(QUrl::fromLocalFile(outputFileName));
281
    saveRecentFiles();
Till Theato's avatar
Till Theato committed
282
    m_fileRevert->setEnabled(true);
283
    pCore->window()->m_undoView->stack()->setClean();
Till Theato's avatar
Till Theato committed
284 285 286 287

    return true;
}

288 289 290 291 292 293 294
void ProjectManager::saveRecentFiles()
{
    KSharedConfigPtr config = KSharedConfig::openConfig();
    m_recentFilesAction->saveEntries(KConfigGroup(config, "Recent Files"));
    config->sync();
}

295
void ProjectManager::slotSaveSelection(QString path)
296
{
297
    m_trackView->projectView()->exportTimelineSelection(path);
298 299
}

300 301 302 303 304 305
bool ProjectManager::hasSelection() const
{
    return m_trackView->projectView()->hasSelection();
}


Till Theato's avatar
Till Theato committed
306 307
bool ProjectManager::saveFileAs()
{
308
    QFileDialog fd(pCore->window());
309
    fd.setDirectory(m_project->url().isValid() ? m_project->url().adjusted(QUrl::RemoveFilename).path() : m_project->projectFolder().path());
310
    fd.setMimeTypeFilters(QStringList()<<QStringLiteral("application/x-kdenlive"));
311
    fd.setAcceptMode(QFileDialog::AcceptSave);
312
    fd.setFileMode(QFileDialog::AnyFile);
313
    fd.setDefaultSuffix(QStringLiteral("kdenlive"));
314 315
    if (fd.exec() != QDialog::Accepted) {
        return false;
316
    }
317
    if (fd.selectedFiles().isEmpty()) {
Till Theato's avatar
Till Theato committed
318 319
        return false;
    }
320
    QString outputFile = fd.selectedFiles().at(0);
Till Theato's avatar
Till Theato committed
321 322 323 324 325 326 327

    if (QFile::exists(outputFile)) {
        // Show the file dialog again if the user does not want to overwrite the file
        if (KMessageBox::questionYesNo(pCore->window(), i18n("File %1 already exists.\nDo you want to overwrite it?", outputFile)) == KMessageBox::No) {
            return saveFileAs();
        }
    }
328 329 330 331 332 333 334
    bool ok = false;
    QDir cacheDir = m_project->getCacheDir(CacheBase, &ok);
    if (ok) {
        QFile file(cacheDir.absoluteFilePath(QUrl::toPercentEncoding("." + outputFile)));
        file.open(QIODevice::ReadWrite | QIODevice::Text);
        file.close();
    }
Till Theato's avatar
Till Theato committed
335 336 337 338 339
    return saveFileAs(outputFile);
}

bool ProjectManager::saveFile()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
340 341 342 343 344
    if (!m_project) {
        // Calling saveFile before a project was created, something is wrong
        qDebug()<<"SaveFile called without project";
        return false;
    }
Till Theato's avatar
Till Theato committed
345 346 347 348 349 350 351 352 353 354 355
    if (m_project->url().isEmpty()) {
        return saveFileAs();
    } else {
        bool result = saveFileAs(m_project->url().path());
        m_project->m_autosave->resize(0);
        return result;
    }
}

void ProjectManager::openFile()
{
356
    if (m_startUrl.isValid()) {
Till Theato's avatar
Till Theato committed
357
        openFile(m_startUrl);
358
        m_startUrl.clear();
Till Theato's avatar
Till Theato committed
359 360
        return;
    }
361
    //TODO KF5 set default location to project folder
362
    QUrl url = QFileDialog::getOpenFileUrl(pCore->window(), QString(), QUrl(), getMimeType());
363
    if (!url.isValid()) {
Till Theato's avatar
Till Theato committed
364 365
        return;
    }
366
    m_recentFilesAction->addUrl(url);
367
    saveRecentFiles();
Till Theato's avatar
Till Theato committed
368 369 370 371 372
    openFile(url);
}

void ProjectManager::openLastFile()
{
373
    if (m_recentFilesAction->selectableActionGroup()->actions().isEmpty()) {
Till Theato's avatar
Till Theato committed
374 375 376 377 378
        // No files in history
        newFile(false);
        return;
    }

379
    QAction *firstUrlAction = m_recentFilesAction->selectableActionGroup()->actions().last();
Till Theato's avatar
Till Theato committed
380 381 382 383 384 385 386
    if (firstUrlAction) {
        firstUrlAction->trigger();
    } else {
        newFile(false);
    }
}

387
// fix mantis#3160 separate check from openFile() so we can call it from newFile()
388
// to find autosaved files (in ~/.local/share/stalefiles/kdenlive) and recover it
389 390
bool ProjectManager::checkForBackupFile(const QUrl &url)
{
391
    // Check for autosave file that belong to the url we passed in.
392
    QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::staleFiles(url);
393 394 395
    KAutoSaveFile *orphanedFile = NULL;
    // Check if we can have a lock on one of the file, 
    // meaning it is not handled by any Kdenlive instancce
396
    if (!staleFiles.isEmpty()) {
397 398 399 400 401 402 403
        foreach(KAutoSaveFile * stale, staleFiles) {
            if (stale->open(QIODevice::QIODevice::ReadWrite)) {
                  // Found orphaned autosave file
                  orphanedFile = stale;
                  break;
            } else {
              // Another Kdenlive instance is probably handling this autosave file
404
              staleFiles.removeAll(stale);
405 406 407 408 409 410 411
              delete stale;
              continue;
            }
        }
    }

    if (orphanedFile) {
412
        if (KMessageBox::questionYesNo(0,
413 414 415
                                       i18n("Auto-saved files exist. Do you want to recover them now?"),
                                       i18n("File Recovery"),
                                       KGuiItem(i18n("Recover")), KGuiItem(i18n("Don't recover"))) == KMessageBox::Yes) {
416
            doOpenFile(url, orphanedFile);
417 418 419 420 421 422 423 424 425 426 427 428 429
            return true;
        } else {
            // remove the stale files
            foreach(KAutoSaveFile * stale, staleFiles) {
                stale->open(QIODevice::ReadWrite);
                delete stale;
            }
        }
        return false;
    }
    return false;
}

430
void ProjectManager::openFile(const QUrl &url)
Till Theato's avatar
Till Theato committed
431
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
432
    QMimeDatabase db;
Till Theato's avatar
Till Theato committed
433
    // Make sure the url is a Kdenlive project file
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
434
    QMimeType mime = db.mimeTypeForUrl(url);
435
    if (mime.inherits(QStringLiteral("application/x-compressed-tar"))) {
Till Theato's avatar
Till Theato committed
436
        // Opening a compressed project file, we need to process it
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
437
        //qDebug()<<"Opening archive, processing";
Till Theato's avatar
Till Theato committed
438 439
        QPointer<ArchiveWidget> ar = new ArchiveWidget(url);
        if (ar->exec() == QDialog::Accepted) {
440
            openFile(QUrl::fromLocalFile(ar->extractedProjectFile()));
441
        } else if (m_startUrl.isValid()) {
Till Theato's avatar
Till Theato committed
442 443 444 445 446 447 448
            // we tried to open an invalid file from command line, init new project
            newFile(false);
        }
        delete ar;
        return;
    }

449
    /*if (!url.fileName().endsWith(".kdenlive")) {
Till Theato's avatar
Till Theato committed
450 451
        // This is not a Kdenlive project file, abort loading
        KMessageBox::sorry(pCore->window(), i18n("File %1 is not a Kdenlive project file", url.path()));
452
        if (m_startUrl.isValid()) {
Till Theato's avatar
Till Theato committed
453 454 455 456
            // we tried to open an invalid file from command line, init new project
            newFile(false);
        }
        return;
457
    }*/
Till Theato's avatar
Till Theato committed
458

Till Theato's avatar
Till Theato committed
459
    if (m_project && m_project->url() == url) {
Till Theato's avatar
Till Theato committed
460 461 462
        return;
    }

Till Theato's avatar
Till Theato committed
463 464
    if (!closeCurrentDocument()) {
        return;
Till Theato's avatar
Till Theato committed
465 466
    }

467 468
    if (checkForBackupFile(url)) {
        return;
Till Theato's avatar
Till Theato committed
469
    }
470
    pCore->window()->slotGotProgressInfo(i18n("Opening file %1", url.path()), 100, InformationMessage);
Till Theato's avatar
Till Theato committed
471 472 473
    doOpenFile(url, NULL);
}

474
void ProjectManager::doOpenFile(const QUrl &url, KAutoSaveFile *stale)
Till Theato's avatar
Till Theato committed
475
{
Till Theato's avatar
Till Theato committed
476
    Q_ASSERT(m_project == NULL);
Till Theato's avatar
Till Theato committed
477 478 479 480 481 482 483 484
    if (!pCore->window()->m_timelineArea->isEnabled()) return;
    m_fileRevert->setEnabled(true);

    // Recreate stopmotion widget on document change
    if (pCore->window()->m_stopmotion) {
        delete pCore->window()->m_stopmotion;
        pCore->window()->m_stopmotion = NULL;
    }
485 486 487
    if (m_progressDialog) {
        delete m_progressDialog;
    }
488
    pCore->monitorManager()->resetDisplay();
489 490 491 492 493 494
    m_progressDialog = new QProgressDialog(pCore->window());
    m_progressDialog->setWindowTitle(i18n("Loading project"));
    m_progressDialog->setCancelButton(0);
    m_progressDialog->setLabelText(i18n("Loading playlist"));
    m_progressDialog->setMaximum(0);
    m_progressDialog->show();
Till Theato's avatar
Till Theato committed
495
    bool openBackup;
496
    m_notesPlugin->clear();
497
    KdenliveDoc *doc = new KdenliveDoc(stale ? QUrl::fromLocalFile(stale->fileName()) : url, QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder()), pCore->window()->m_commandStack, KdenliveSettings::default_profile(), QMap <QString, QString> (), QMap <QString, QString> (), QPoint(KdenliveSettings::videotracks(), KdenliveSettings::audiotracks()), pCore->monitorManager()->projectMonitor()->render, m_notesPlugin, &openBackup, pCore->window());
Till Theato's avatar
Till Theato committed
498
    if (stale == NULL) {
499
        stale = new KAutoSaveFile(url, doc);
Till Theato's avatar
Till Theato committed
500 501 502
        doc->m_autosave = stale;
    } else {
        doc->m_autosave = stale;
503
        stale->setParent(doc);
504
        // if loading from an autosave of unnamed file then keep unnamed
505
        if (url.fileName().contains(QStringLiteral("_untitled.kdenlive")))
506 507 508
            doc->setUrl(QUrl());
        else
            doc->setUrl(url);
Till Theato's avatar
Till Theato committed
509 510 511
        doc->setModified(true);
        stale->setParent(doc);
    }
512
    m_progressDialog->setLabelText(i18n("Loading clips"));
513
    pCore->bin()->setDocument(doc);
Till Theato's avatar
Till Theato committed
514

515 516 517
    QList <QAction*> rulerActions;
    rulerActions << pCore->window()->actionCollection()->action(QStringLiteral("set_render_timeline_zone"));
    rulerActions << pCore->window()->actionCollection()->action(QStringLiteral("unset_render_timeline_zone"));
518
    bool ok;
519
    m_trackView = new Timeline(doc, pCore->window()->kdenliveCategoryMap.value(QStringLiteral("timeline"))->actions(), rulerActions, &ok, pCore->window());
520
    connect(m_trackView, &Timeline::startLoadingBin, m_progressDialog, &QProgressDialog::setMaximum, Qt::DirectConnection);
521
    connect(m_trackView, &Timeline::resetUsageCount, pCore->bin(), &Bin::resetUsageCount, Qt::DirectConnection);
522
    connect(m_trackView, &Timeline::loadingBin, m_progressDialog, &QProgressDialog::setValue, Qt::DirectConnection);
523 524

    // Set default target tracks to upper audio / lower video tracks
525
    m_project = doc;
526 527
    m_trackView->audioTarget = doc->getDocumentProperty(QStringLiteral("audiotargettrack"), QStringLiteral("-1")).toInt();
    m_trackView->videoTarget = doc->getDocumentProperty(QStringLiteral("videotargettrack"), QStringLiteral("-1")).toInt();
528
    m_trackView->loadTimeline();
529
    m_trackView->loadGuides(pCore->binController()->takeGuidesData());
530
    connect(m_trackView->projectView(), SIGNAL(importPlaylistClips(ItemInfo, QUrl, QUndoCommand*)), pCore->bin(), SLOT(slotExpandUrl(ItemInfo, QUrl, QUndoCommand*)), Qt::DirectConnection);
531
    pCore->window()->connectDocument();
532 533
    bool disabled = m_project->getDocumentProperty(QStringLiteral("disabletimelineeffects")) == QLatin1String("1");
    QAction *disableEffects = pCore->window()->actionCollection()->action(QStringLiteral("disable_timeline_effects"));
534 535 536 537 538 539 540
    if (disableEffects) {
        if (disabled != disableEffects->isChecked()) {
            disableEffects->blockSignals(true);
            disableEffects->setChecked(disabled);
            disableEffects->blockSignals(false);
        }
    }
541
    emit docOpened(m_project);
542

543
    pCore->window()->m_timelineArea->setCurrentIndex(pCore->window()->m_timelineArea->addTab(m_trackView, QIcon::fromTheme(QStringLiteral("kdenlive")), m_project->description()));
Till Theato's avatar
Till Theato committed
544 545 546 547 548 549 550
    if (!ok) {
        pCore->window()->m_timelineArea->setEnabled(false);
        KMessageBox::sorry(pCore->window(), i18n("Cannot open file %1.\nProject is corrupted.", url.path()));
        pCore->window()->slotGotProgressInfo(QString(), -1);
        newFile(false, true);
        return;
    }
551
    m_trackView->setDuration(m_trackView->duration());
Till Theato's avatar
Till Theato committed
552 553

    pCore->window()->slotGotProgressInfo(QString(), -1);
554
    pCore->monitorManager()->projectMonitor()->adjustRulerSize(m_trackView->duration() - 1);
555
    pCore->monitorManager()->projectMonitor()->slotZoneMoved(m_trackView->inPoint(), m_trackView->outPoint());
Till Theato's avatar
Till Theato committed
556
    if (openBackup) {
557
        slotOpenBackup(url);
Till Theato's avatar
Till Theato committed
558
    }
559
    m_lastSave.start();
560 561
    delete m_progressDialog;
    m_progressDialog = NULL;
Till Theato's avatar
Till Theato committed
562 563 564 565
}

void ProjectManager::slotRevert()
{
Till Theato's avatar
Till Theato committed
566 567 568
    if (KMessageBox::warningContinueCancel(pCore->window(), i18n("This will delete all changes made since you last saved your project. Are you sure you want to continue?"), i18n("Revert to last saved version")) == KMessageBox::Cancel){
        return;
    }
569
    QUrl url = m_project->url();
Till Theato's avatar
Till Theato committed
570 571 572 573 574 575
    if (closeCurrentDocument(false))
        doOpenFile(url, NULL);
}

QString ProjectManager::getMimeType(bool open)
{
576 577
    QString mimetype = i18n("Kdenlive project (*.kdenlive)");
    if (open) mimetype.append(";;" + i18n("Archived project (*.tar.gz)"));
Till Theato's avatar
Till Theato committed
578 579 580 581 582 583 584 585 586
    return mimetype;
}


KdenliveDoc* ProjectManager::current()
{
    return m_project;
}

587
void ProjectManager::slotOpenBackup(const QUrl& url)
588
{
589 590
    QUrl projectFile;
    QUrl projectFolder;
591
    QString projectId;
592
    if (url.isValid()) {
593
        // we could not open the project file, guess where the backups are
594
        projectFolder = QUrl::fromLocalFile(KdenliveSettings::defaultprojectfolder());
595 596 597 598
        projectFile = url;
    } else {
        projectFolder = m_project->projectFolder();
        projectFile = m_project->url();
599
        projectId = m_project->getDocumentProperty(QStringLiteral("documentid"));
600 601 602 603 604 605 606
    }

    QPointer<BackupWidget> dia = new BackupWidget(projectFile, projectFolder, projectId, pCore->window());
    if (dia->exec() == QDialog::Accepted) {
        QString requestedBackup = dia->selectedFile();
        m_project->backupLastSavedVersion(projectFile.path());
        closeCurrentDocument(false);
607
        doOpenFile(QUrl::fromLocalFile(requestedBackup), NULL);
Vincent Pinon's avatar
Vincent Pinon committed
608 609 610 611 612
        if (m_project) {
            m_project->setUrl(projectFile);
            m_project->setModified(true);
            pCore->window()->setWindowTitle(m_project->description());
        }
613 614 615 616
    }
    delete dia;
}

Vincent Pinon's avatar
Vincent Pinon committed
617
Timeline* ProjectManager::currentTimeline()
618 619 620 621
{
    return m_trackView;
}

622

623 624 625 626 627
KRecentFilesAction* ProjectManager::recentFilesAction()
{
    return m_recentFilesAction;
}

628

629 630
void ProjectManager::slotStartAutoSave()
{
631 632 633 634 635 636 637 638
    if (m_lastSave.elapsed() > 300000) {
        // If the project was not saved in the last 5 minute, force save
        m_autoSaveTimer.stop();
        slotAutoSave();
    }
    else {
        m_autoSaveTimer.start(3000); // will trigger slotAutoSave() in 3 seconds
    }
639 640 641 642
}

void ProjectManager::slotAutoSave()
{
643
    prepareSave();
644 645 646 647 648
    bool multitrackEnabled = m_trackView->multitrackView;
    if (multitrackEnabled) {
        // Multitrack view was enabled, disable for auto save
        m_trackView->slotMultitrackView(false);
    }
649
    m_trackView->connectOverlayTrack(false);
650
    m_project->slotAutoSave();
651
    m_trackView->connectOverlayTrack(true);
652 653 654 655
    if (multitrackEnabled) {
        // Multitrack view was enabled, re-enable for auto save
        m_trackView->slotMultitrackView(true);
    }
656
    m_lastSave.start();
657 658
}

659 660 661 662 663 664 665
QString ProjectManager::projectSceneList()
{
    bool multitrackEnabled = m_trackView->multitrackView;
    if (multitrackEnabled) {
        // Multitrack view was enabled, disable for auto save
        m_trackView->slotMultitrackView(false);
    }
666
    m_trackView->connectOverlayTrack(false);
667
    QString scene = pCore->monitorManager()->projectMonitor()->sceneList();
668
    m_trackView->connectOverlayTrack(true);
669 670 671 672 673 674 675
    if (multitrackEnabled) {
        // Multitrack view was enabled, re-enable for auto save
        m_trackView->slotMultitrackView(true);
    }
    return scene;
}

676 677
void ProjectManager::prepareSave()
{
678
    pCore->binController()->saveDocumentProperties(m_trackView->documentProperties(), m_project->metadata(), m_trackView->projectView()->guidesData());
679
    QString projectNotes = m_project->documentNotes();
680
    pCore->binController()->saveProperty(QStringLiteral("kdenlive:documentnotes"), projectNotes);
681 682 683
    QString groupsXml = m_project->groupsXml();
    if (!groupsXml.isEmpty())
	pCore->binController()->saveProperty(QStringLiteral("kdenlive:clipgroups"), groupsXml);
684 685
}

686

687 688 689 690 691 692
void ProjectManager::slotResetProfiles()
{
    m_project->resetProfile();
    pCore->monitorManager()->resetProfiles(m_project->mltProfile(), m_project->timecode());
    pCore->monitorManager()->updateScopeSource();
}
693 694 695 696 697 698

void ProjectManager::slotExpandClip()
{
    m_trackView->projectView()->expandActiveClip();
}

699
void ProjectManager::disableBinEffects(bool disable)
700
{
701 702
    if (m_project) {
        if (disable) {
703
            m_project->setDocumentProperty(QStringLiteral("disablebineffects"), QString::number((int) true));
704
        } else {
705
            m_project->setDocumentProperty(QStringLiteral("disablebineffects"), QString());
706
        }
707 708
    }
    pCore->window()->m_effectStack->disableBinEffects(disable);
709 710
}

711 712 713
void ProjectManager::slotDisableTimelineEffects(bool disable)
{
    if (disable) {
714
        m_project->setDocumentProperty(QStringLiteral("disabletimelineeffects"), QString::number((int) true));
715
    } else {
716
        m_project->setDocumentProperty(QStringLiteral("disabletimelineeffects"), QString());
717 718 719
    }
    m_trackView->disableTimelineEffects(disable);
    pCore->window()->m_effectStack->disableTimelineEffects(disable);
720
    pCore->monitorManager()->projectMonitor()->refreshMonitorIfActive();
721
}
722

723 724 725 726 727
void ProjectManager::slotSwitchTrackLock()
{
    m_trackView->projectView()->switchTrackLock();
}

728 729 730 731 732
void ProjectManager::slotSwitchAllTrackLock()
{
    m_trackView->projectView()->switchAllTrackLock();
}

733 734 735 736 737
void ProjectManager::slotSwitchTrackTarget()
{
    m_trackView->switchTrackTarget();
}

738