mainwindow.cpp 162 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/***************************************************************************
 *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

20

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
21
#include "mainwindow.h"
22
#include "mainwindowadaptor.h"
23
#include "core.h"
24
#include "bin/projectclip.h"
25
#include "bin/generators/generators.h"
26
#include "library/librarywidget.h"
27
#include "monitor/scopes/audiographspectrum.h"
28
#include "mltcontroller/clipcontroller.h"
29
#include "kdenlivesettings.h"
Vincent Pinon's avatar
Vincent Pinon committed
30
#include "dialogs/kdenlivesettingsdialog.h"
31
#include "dialogs/clipcreationdialog.h"
Vincent Pinon's avatar
Vincent Pinon committed
32
#include "effectslist/initeffects.h"
Vincent Pinon's avatar
Vincent Pinon committed
33
#include "project/dialogs/projectsettings.h"
Vincent Pinon's avatar
Vincent Pinon committed
34
#include "project/clipmanager.h"
Till Theato's avatar
Till Theato committed
35 36 37
#include "monitor/monitor.h"
#include "monitor/recmonitor.h"
#include "monitor/monitormanager.h"
38
#include "doc/kdenlivedoc.h"
Vincent Pinon's avatar
Vincent Pinon committed
39
#include "timeline/timeline.h"
Ed Rogalsky's avatar
Ed Rogalsky committed
40
#include "timeline/track.h"
41
#include "timeline/customtrackview.h"
Vincent Pinon's avatar
Vincent Pinon committed
42
#include "effectslist/effectslistview.h"
43
#include "effectslist/effectbasket.h"
44
#include "effectstack/effectstackview2.h"
45
#include "project/transitionsettings.h"
46
#include "mltcontroller/bincontroller.h"
47
#include "mltcontroller/producerqueue.h"
Vincent Pinon's avatar
Vincent Pinon committed
48
#include "dialogs/renderwidget.h"
49
#include "renderer.h"
Vincent Pinon's avatar
Vincent Pinon committed
50
#include "dialogs/wizard.h"
51
#include "project/projectcommands.h"
Vincent Pinon's avatar
Vincent Pinon committed
52
#include "titler/titlewidget.h"
Vincent Pinon's avatar
Vincent Pinon committed
53
#include "timeline/markerdialog.h"
54
#include "timeline/clipitem.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
55
#include "interfaces.h"
Vincent Pinon's avatar
Vincent Pinon committed
56
#include "project/cliptranscode.h"
57
#include "scopes/scopemanager.h"
Vincent Pinon's avatar
Vincent Pinon committed
58
#include "project/dialogs/archivewidget.h"
59
#include "utils/resourcewidget.h"
60
#include "layoutmanagement.h"
61
#include "hidetitlebars.h"
62
#include "mltconnection.h"
Till Theato's avatar
Till Theato committed
63
#include "project/projectmanager.h"
64
#include "timeline/timelinesearch.h"
65
#include <config-kdenlive.h>
66
#include "utils/thememanager.h"
67
#include "utils/progressbutton.h"
68
#include "utils/KoIconUtils.h"
69
#include "project/dialogs/temporarydata.h"
70 71 72
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
73

74
#include <KActionCollection>
75
#include <KActionCategory>
76
#include <KActionMenu>
77
#include <KStandardAction>
78
#include <KShortcutsDialog>
79 80 81
#include <KMessageBox>
#include <KConfigDialog>
#include <KXMLGUIFactory>
82
#include <KIconTheme>
83
#include <KColorSchemeManager>
84
#include <KRecentDirs>
85 86 87
#include <KUrlRequesterDialog>
#include <ktogglefullscreenaction.h>
#include <KNotifyConfigWidget>
88 89
#include <kns3/downloaddialog.h>
#include <kns3/knewstuffaction.h>
90
#include <KToolBar>
91
#include <KColorScheme>
92
#include <KEditToolBar>
93
#include <KDualAction>
94
#include <klocalizedstring.h>
95 96

#include <QAction>
97 98 99 100
#include <QDebug>
#include <QStatusBar>
#include <QTemporaryFile>
#include <QMenu>
101
#include <QDesktopWidget>
102
#include <QBitmap>
103
#include <QUndoGroup>
104
#include <QFileDialog>
105
#include <QStyleFactory>
106 107

#include <stdlib.h>
108
#include <QStandardPaths>
Vincent Pinon's avatar
Vincent Pinon committed
109 110 111 112
#include <KConfigGroup>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
113
#include <QtGlobal>
114

115

116
static const char version[] = KDENLIVE_VERSION;
117 118
namespace Mlt
{
119 120 121
class Producer;
};

Marco Gittler's avatar
Marco Gittler committed
122 123 124 125 126
EffectsList MainWindow::videoEffects;
EffectsList MainWindow::audioEffects;
EffectsList MainWindow::customEffects;
EffectsList MainWindow::transitions;

127
QMap <QString,QImage> MainWindow::m_lumacache;
128
QMap <QString,QStringList> MainWindow::m_lumaFiles;
129

130
/*static bool sortByNames(const QPair<QString, QAction *> &a, const QPair<QString, QAction*> &b)
131 132
{
    return a.first < b.first;
133
}*/
134

135 136 137 138 139 140 141 142 143
// determine the the default KDE style as defined BY THE USER
// (as opposed to whatever style KDE considers default)
static QString defaultStyle(const char *fallback=Q_NULLPTR)
{
    KSharedConfigPtr kdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
    KConfigGroup cg(kdeGlobals, "KDE");
    return cg.readEntry("widgetStyle", fallback);
}

144
MainWindow::MainWindow(const QString &MltPath, const QUrl &Url, const QString & clipsToLoad, QWidget *parent) :
145
    KXmlGuiWindow(parent),
Vincent Pinon's avatar
Vincent Pinon committed
146
    m_timelineArea(NULL),
Vincent Pinon's avatar
Vincent Pinon committed
147
    m_stopmotion(NULL),
148
    m_effectStack(NULL),
Vincent Pinon's avatar
Vincent Pinon committed
149
    m_exitCode(EXIT_SUCCESS),
Vincent Pinon's avatar
Vincent Pinon committed
150
    m_effectList(NULL),
151
    m_transitionList(NULL),
152 153
    m_clipMonitor(NULL),
    m_projectMonitor(NULL),
154
    m_recMonitor(NULL),
155
    m_renderWidget(NULL),
156
    m_messageLabel(NULL),
157 158
    m_themeInitialized(false),
    m_isDarkTheme(false)
159
{
Vincent Pinon's avatar
Vincent Pinon committed
160
    qRegisterMetaType<audioShortVector> ("audioShortVector");
161
    qRegisterMetaType< QVector<double> > ("QVector<double>");
Vincent Pinon's avatar
Vincent Pinon committed
162
    qRegisterMetaType<MessageType> ("MessageType");
163
    qRegisterMetaType<stringMap> ("stringMap");
164
    qRegisterMetaType<audioByteArray> ("audioByteArray");
165
    qRegisterMetaType< QVector <int> > ();
166
    qRegisterMetaType<QDomElement> ("QDomElement");
167
    qRegisterMetaType<requestClipInfo> ("requestClipInfo");
168
    qRegisterMetaType<MltVideoProfile> ("MltVideoProfile");
169

170
    Core::build(this);
171 172 173 174 175

    // Widget themes for non KDE users
    KActionMenu *stylesAction= new KActionMenu(i18n("Style"), this);
    QActionGroup *stylesGroup = new QActionGroup(stylesAction);

176 177 178 179 180 181 182 183 184 185 186 187 188

    // GTK theme does not work well with Kdenlive, and does not support color theming, so avoid it
    QStringList availableStyles = QStyleFactory::keys();
    QString desktopStyle = QApplication::style()->objectName();
    if (QString::compare(desktopStyle, QLatin1String("GTK+"), Qt::CaseInsensitive) == 0 && KdenliveSettings::widgetstyle().isEmpty()) {
        if (availableStyles.contains(QLatin1String("breeze"), Qt::CaseInsensitive)) {
            // Auto switch to Breeze theme
            KdenliveSettings::setWidgetstyle(QStringLiteral("Breeze"));
        } else if (availableStyles.contains(QLatin1String("fusion"), Qt::CaseInsensitive)) {
            KdenliveSettings::setWidgetstyle(QStringLiteral("Fusion"));
        }
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
189 190 191 192 193 194 195 196
    // Add default style action
    QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
    defaultStyle->setCheckable(true);
    stylesAction->addAction(defaultStyle);
    if (KdenliveSettings::widgetstyle().isEmpty()) {
        defaultStyle->setChecked(true);
    }

197 198 199 200
    foreach(const QString &style, availableStyles) {
        QAction *a = new QAction(style, stylesGroup);
        a->setCheckable(true);
        a->setData(style);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
201 202 203
        if (KdenliveSettings::widgetstyle() == style) {
            a->setChecked(true);
        }
204 205 206 207 208
        stylesAction->addAction(a);
    }
    connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);

    // Color schemes
209 210 211
    KActionMenu *themeAction= new KActionMenu(i18n("Theme"), this);
    ThemeManager::instance()->setThemeMenuAction(themeAction);
    ThemeManager::instance()->setCurrentTheme(KdenliveSettings::colortheme());
212
    connect(ThemeManager::instance(), SIGNAL(signalThemeChanged(const QString &)), this, SLOT(slotThemeChanged(const QString &)), Qt::DirectConnection);
213 214

    if (!KdenliveSettings::widgetstyle().isEmpty() && QString::compare(desktopStyle, KdenliveSettings::widgetstyle(), Qt::CaseInsensitive) != 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
215 216 217 218
        // User wants a custom widget style, init
        doChangeStyle();
    }
    else ThemeManager::instance()->slotChangePalette();
219 220 221 222

    new RenderingAdaptor(this);
    pCore->initialize();
    MltConnection::locateMeltAndProfilesPath(MltPath);
223

224
    KdenliveSettings::setCurrent_profile(KdenliveSettings::default_profile());
225 226 227 228 229 230 231

    // If using a custom profile, make sure the file exists or fallback to default
    if (KdenliveSettings::current_profile().startsWith(QStringLiteral("/")) && !QFile::exists(KdenliveSettings::current_profile())) {
        KMessageBox::sorry(this, i18n("Cannot find your default profile, switching to ATSC 1080p 25"));
        KdenliveSettings::setCurrent_profile("atsc_1080p_25");
        KdenliveSettings::setDefault_profile("atsc_1080p_25");
    }
232
    m_commandStack = new QUndoGroup;
233 234
    m_timelineArea = new QTabWidget(this);
    //m_timelineArea->setTabReorderingEnabled(true);
235
    m_timelineArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
Simon Eugster's avatar
Simon Eugster committed
236
    m_timelineArea->setMinimumHeight(200);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
237 238 239
    // Hide tabbar
    QTabBar *bar = m_timelineArea->findChild<QTabBar *>();
    bar->setHidden(true);
240

241
    m_gpuAllowed = initEffects::parseEffectFiles(pCore->binController()->mltRepository());
242
    //initEffects::parseCustomEffectsFile();
243

244
    m_shortcutRemoveFocus = new QShortcut(QKeySequence(QStringLiteral("Esc")), this);
Simon Eugster's avatar
Simon Eugster committed
245 246
    connect(m_shortcutRemoveFocus, SIGNAL(activated()), this, SLOT(slotRemoveFocus()));

247
    /// Add Widgets
248
    setDockOptions(QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
249
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
250 251
    setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
#endif
252
    setTabPosition(Qt::AllDockWidgetAreas, (QTabWidget::TabPosition) KdenliveSettings::tabposition());
253 254
    m_timelineToolBar = toolBar("timelineToolBar"); //KToolBar("timelineToolBar", this);
    m_timelineToolBarContainer = new QWidget(this);
255
    QVBoxLayout *ctnLay = new QVBoxLayout;
256 257
    ctnLay->setSpacing(0);
    ctnLay->setContentsMargins(0, 0, 0, 0);
258 259
    m_timelineToolBarContainer->setLayout(ctnLay);
    ctnLay->addWidget(m_timelineToolBar);
260 261 262 263 264
    QFrame *fr = new QFrame(this);
    fr->setFrameShape(QFrame::HLine);
    fr->setMaximumHeight(1);
    fr->setLineWidth(1);
    ctnLay->addWidget(fr);
265
    ctnLay->addWidget(m_timelineArea);
266
    setCentralWidget(m_timelineToolBarContainer);
267
    setupActions();
268

269
    m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
270
    QDockWidget * libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
Simon Eugster's avatar
Simon Eugster committed
271

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
272
    m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
273
    pCore->bin()->setMonitor(m_clipMonitor);
274
    connect(m_clipMonitor, &Monitor::showConfigDialog, this, &MainWindow::slotPreferences);
275 276
    connect(m_clipMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
    connect(m_clipMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
277 278
    connect(m_clipMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
279

280
    connect(pCore->bin(), SIGNAL(clipNeedsReload(QString,bool)),this, SLOT(slotUpdateClip(QString,bool)));
281
    connect(pCore->bin(), SIGNAL(findInTimeline(QString)), this, SLOT(slotClipInTimeline(QString)));
282

283 284
    //TODO deprecated, replace with Bin methods if necessary
    /*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
285
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
Laurent Montel's avatar
Laurent Montel committed
286
    connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
287
    connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
288
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
289
    connect(m_clipMonitor, SIGNAL(extractZone(QString)), pCore->bin(), SLOT(slotStartCutJob(QString)));
290
    connect(m_clipMonitor, SIGNAL(passKeyPress(QKeyEvent*)), this, SLOT(triggerKey(QKeyEvent*)));
291

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
292
    m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
293
    connect(m_projectMonitor, SIGNAL(passKeyPress(QKeyEvent*)), this, SLOT(triggerKey(QKeyEvent*)));
294 295 296 297
    connect(m_projectMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
    connect(m_projectMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
    connect(m_projectMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_projectMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
298
    connect(m_loopClip, &QAction::triggered, m_projectMonitor, &Monitor::slotLoopClip);
299
    connect(m_projectMonitor, SIGNAL(updateGuide(int, QString)), this, SLOT(slotEditGuide(int, QString)));
300

301 302
/*
    //TODO disabled until ported to qml
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
303
    m_recMonitor = new RecMonitor(Kdenlive::RecordMonitor, pCore->monitorManager(), this);
304 305
    connect(m_recMonitor, SIGNAL(addProjectClip(QUrl)), this, SLOT(slotAddProjectClip(QUrl)));
    connect(m_recMonitor, SIGNAL(addProjectClipList(QList<QUrl>)), this, SLOT(slotAddProjectClipList(QList<QUrl>)));
306

307
*/
308
    pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
309
    connect(m_clipMonitor, SIGNAL(addMasterEffect(QString,QDomElement)), pCore->bin(), SLOT(slotEffectDropped(QString,QDomElement)));
310

311 312 313 314 315 316 317 318
    // Audio spectrum scope
    m_audioSpectrum = new AudioGraphSpectrum(pCore->monitorManager());
    QDockWidget * spectrumDock = addDock(i18n("Audio Spectrum"), QStringLiteral("audiospectrum"), m_audioSpectrum);
    connect(this, &MainWindow::reloadTheme, m_audioSpectrum, &AudioGraphSpectrum::refreshPixmap);
    // Close library and audiospectrum on first run
    libraryDock->close();
    spectrumDock->close();

319
    m_effectStack = new EffectStackView2(m_projectMonitor, this);
320
    connect(m_effectStack, SIGNAL(startFilterJob(const ItemInfo&,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)), pCore->bin(), SLOT(slotStartFilterJob(const ItemInfo &,const QString&,QMap<QString,QString>&,QMap<QString,QString>&,QMap<QString,QString>&)));
321
    connect(pCore->bin(), SIGNAL(masterClipSelected(ClipController *, Monitor *)), m_effectStack, SLOT(slotMasterClipItemSelected(ClipController *, Monitor *)));
322
    connect(pCore->bin(), SIGNAL(masterClipUpdated(ClipController *, Monitor *)), m_effectStack, SLOT(slotRefreshMasterClipEffects(ClipController *, Monitor *)));
323 324
    connect(m_effectStack, SIGNAL(addMasterEffect(QString,QDomElement)), pCore->bin(), SLOT(slotEffectDropped(QString,QDomElement)));
    connect(m_effectStack, SIGNAL(removeMasterEffect(QString,QDomElement)), pCore->bin(), SLOT(slotDeleteEffect(QString,QDomElement)));
325
    connect(m_effectStack, SIGNAL(changeEffectPosition(QString,const QList <int>,int)), pCore->bin(), SLOT(slotMoveEffect(QString,const QList <int>,int)));
326
    connect(m_effectStack, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
327
    connect(m_effectStack, SIGNAL(displayMessage(QString,int)), m_messageLabel, SLOT(setProgressMessage(QString,int)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
328
    m_effectStackDock = addDock(i18n("Properties"), QStringLiteral("effect_stack"), m_effectStack);
329

Simon Eugster's avatar
Simon Eugster committed
330
    m_effectList = new EffectsListView();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
331
    m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList);
Simon Eugster's avatar
Simon Eugster committed
332

333
    m_transitionList = new EffectsListView(EffectsListView::TransitionMode);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
334
    m_transitionListDock = addDock(i18n("Transitions"), QStringLiteral("transition_list"), m_transitionList);
335

Simon Eugster's avatar
Simon Eugster committed
336
    // Add monitors here to keep them at the right of the window
337 338
    m_clipMonitorDock = addDock(i18n("Clip Monitor"), QStringLiteral("clip_monitor"), m_clipMonitor);
    m_projectMonitorDock = addDock(i18n("Project Monitor"), QStringLiteral("project_monitor"), m_projectMonitor);
339
    if (m_recMonitor) {
340
        m_recMonitorDock = addDock(i18n("Record Monitor"), QStringLiteral("record_monitor"), m_recMonitor);
341
    }
Simon Eugster's avatar
Simon Eugster committed
342

343
    m_undoView = new QUndoView();
344
    m_undoView->setCleanIcon(KoIconUtils::themedIcon(QStringLiteral("edit-clear")));
345 346
    m_undoView->setEmptyLabel(i18n("Clean"));
    m_undoView->setGroup(m_commandStack);
347
    m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
348

349
    // Color and icon theme stuff
350
    addAction(QStringLiteral("themes_menu"), themeAction);
351
    connect(m_commandStack, SIGNAL(cleanChanged(bool)), m_saveAction, SLOT(setDisabled(bool)));
352
    addAction(QStringLiteral("styles_menu"), stylesAction);
Simon Eugster's avatar
Simon Eugster committed
353

Simon Eugster's avatar
Simon Eugster committed
354 355 356 357 358 359
    // Close non-general docks for the initial layout
    // only show important ones
    m_undoViewDock->close();



Simon Eugster's avatar
Simon Eugster committed
360
    /// Tabify Widgets ///
361
    tabifyDockWidget(m_transitionListDock, m_effectListDock);
362 363
    tabifyDockWidget(pCore->bin()->clipPropertiesDock(), m_effectStackDock);
    //tabifyDockWidget(m_effectListDock, m_effectStackDock);
364

365
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
366 367 368
    if (m_recMonitor) {
        tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
    }
Simon Eugster's avatar
Simon Eugster committed
369 370 371

    readOptions();

372
    QAction *action;
373 374
    // Stop motion actions. Beware of the order, we MUST use the same order in stopmotion/stopmotion.cpp
    m_stopmotion_actions = new KActionCategory(i18n("Stop Motion"), actionCollection());
375
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("media-record")), i18n("Capture frame"), this);
376
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
377
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_capture"), action);
378
    action = new QAction(i18n("Switch live / captured frame"), this);
379
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
380 381
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_switch"), action);
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("edit-paste")), i18n("Show last frame over video"), this);
382 383
    action->setCheckable(true);
    action->setChecked(false);
384
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_overlay"), action);
385

386
    // Monitor Record action
387
    addAction(QStringLiteral("switch_monitor_rec"), m_clipMonitor->recAction());
388

389
    // Build effects menu
390
    m_effectsMenu = new QMenu(i18n("Add Effect"), this);
391 392
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
393

394 395 396 397
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
    m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
    m_transitionList->reloadEffectList(m_transitionsMenu, m_transitionActions);

398
    ScopeManager *scmanager = new ScopeManager(this);
399

Till Theato's avatar
Till Theato committed
400
    new LayoutManagement(this);
401
    new HideTitleBars(this);
402
    new TimelineSearch(this);
403 404
    m_extraFactory = new KXMLGUIClient(this);
    buildDynamicActions();
405

406 407 408 409 410 411 412 413 414 415 416 417 418 419
    // Add shortcut to action tooltips
    QList< KActionCollection * > collections = KActionCollection::allCollections();
    for (int i = 0; i < collections.count(); ++i) {
        KActionCollection *coll = collections.at(i);
        foreach( QAction* tempAction, coll->actions()) {
            // find the shortcut pattern and delete (note the preceding space in the RegEx)
            QString strippedTooltip = tempAction->toolTip().remove(QRegExp("\\s\\(.*\\)"));
            // append shortcut if it exists for action
            if (tempAction->shortcut() == QKeySequence(0))
                tempAction->setToolTip( strippedTooltip);
            else
                tempAction->setToolTip( strippedTooltip + " (" + tempAction->shortcut().toString() + ")");
        }
    }
420

421 422
    // Create Effect Basket (dropdown list of favorites)
    m_effectBasket = new EffectBasket(m_effectList);
423
    connect(m_effectBasket, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
424 425
    QWidgetAction *widgetlist = new QWidgetAction(this);
    widgetlist->setDefaultWidget(m_effectBasket);
426
    //widgetlist->setText(i18n("Favorite Effects"));
427 428
    widgetlist->setToolTip(i18n("Favorite Effects"));
    widgetlist->setIcon(KoIconUtils::themedIcon("favorite"));
429 430 431 432 433
    QMenu *menu = new QMenu(this);
    menu->addAction(widgetlist);

    QToolButton *basketButton = new QToolButton(this);
    basketButton->setMenu(menu);
434
    basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
435 436
    basketButton->setDefaultAction(widgetlist);
    basketButton->setPopupMode(QToolButton::InstantPopup);
437
    //basketButton->setText(i18n("Favorite Effects"));
438
    basketButton->setToolTip(i18n("Favorite Effects"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
439
    basketButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
440

441
    QWidgetAction* toolButtonAction = new QWidgetAction(this);
442
    toolButtonAction->setText(i18n("Favorite Effects"));
443 444
    toolButtonAction->setIcon(KoIconUtils::themedIcon("favorite"));
    toolButtonAction->setDefaultWidget(basketButton);
445
    addAction(QStringLiteral("favorite_effects"), toolButtonAction);
446
    connect(toolButtonAction, SIGNAL(triggered(bool)), basketButton, SLOT(showMenu()));
447

448 449 450 451 452 453 454 455 456 457 458
    // Render button
    ProgressButton *timelineRender = new ProgressButton(i18n("Render"), 100, this);
    QMenu *tlrMenu = new QMenu(this);
    timelineRender->setMenu(tlrMenu);
    connect(this, &MainWindow::setRenderProgress, timelineRender, &ProgressButton::setProgress);
    QWidgetAction* renderButtonAction = new QWidgetAction(this);
    renderButtonAction->setText(i18n("Render Button"));
    renderButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("media-record")));
    renderButtonAction->setDefaultWidget(timelineRender);
    addAction(QStringLiteral("project_render_button"), renderButtonAction);

459
    // Timeline preview button
460
    ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
461 462
    QMenu *tlMenu = new QMenu(this);
    timelinePreview->setMenu(tlMenu);
463
    connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
464 465
    QWidgetAction* previewButtonAction = new QWidgetAction(this);
    previewButtonAction->setText(i18n("Timeline Preview"));
466
    previewButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("preview-render-on")));
467
    previewButtonAction->setDefaultWidget(timelinePreview);
468
    addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
469

470
    setupGUI();
471 472
    timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
    connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
473

474 475 476
    timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
    /*connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);*/

477 478
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
479

480
    loadGenerators();
481
    loadDockActions();
482
    loadClipActions();
483 484

    // Connect monitor overlay info menu.
485 486
    QMenu *monitorOverlay = static_cast<QMenu*>(factory()->container(QStringLiteral("monitor_config_overlay"), this));
    connect(monitorOverlay, SIGNAL(triggered(QAction*)), this, SLOT(slotSwitchMonitorOverlay(QAction*)));
487

488 489
    m_projectMonitor->setupMenu(static_cast<QMenu*>(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone, NULL, m_loopClip);
    m_clipMonitor->setupMenu(static_cast<QMenu*>(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone, static_cast<QMenu*>(factory()->container(QStringLiteral("marker_menu"), this)));
490

491 492
    QMenu *clipInTimeline = static_cast<QMenu*>(factory()->container(QStringLiteral("clip_in_timeline"), this));
    clipInTimeline->setIcon(KoIconUtils::themedIcon(QStringLiteral("go-jump")));
493
    pCore->bin()->setupGeneratorMenu();
Marco Gittler's avatar
Marco Gittler committed
494

495 496
    connect(pCore->monitorManager(), SIGNAL(updateOverlayInfos(int,int)), this, SLOT(slotUpdateMonitorOverlays(int,int)));

497
    // Setup and fill effects and transitions menus.
498
    QMenu *m = static_cast<QMenu*>(factory()->container(QStringLiteral("video_effects_menu"), this));
Laurent Montel's avatar
Laurent Montel committed
499 500
    connect(m, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
    connect(m_effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
501
    connect(m_transitionsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddTransition(QAction*)));
502

503 504 505 506
    m_timelineContextMenu = new QMenu(this);
    m_timelineContextClipMenu = new QMenu(this);
    m_timelineContextTransitionMenu = new QMenu(this);

507 508
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
509 510
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

511
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_in_project_tree")));
512
    //m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
513
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
514
    m_timelineContextClipMenu->addSeparator();
515 516 517 518 519
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("group_clip")));
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("ungroup_clip")));
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("split_audio")));
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("set_audio_align_ref")));
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("align_audio")));
520
    m_timelineContextClipMenu->addSeparator();
521
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("cut_timeline_clip")));
522
    m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
523
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("paste_effects")));
524
    m_timelineContextClipMenu->addSeparator();
525

526
    QMenu *markersMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("marker_menu"), this));
527
    m_timelineContextClipMenu->addMenu(markersMenu);
528
    m_timelineContextClipMenu->addSeparator();
529
    m_timelineContextClipMenu->addMenu(m_transitionsMenu);
530
    m_timelineContextClipMenu->addMenu(m_effectsMenu);
Marco Gittler's avatar
Marco Gittler committed
531

532
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
533
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
534

535
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("auto_transition")));
536

Laurent Montel's avatar
Laurent Montel committed
537
    connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
538 539
    connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));

540
    slotConnectMonitors();
541

542
    m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
543 544
    // TODO: let user select timeline toolbar toolbutton style
    //connect(toolBar(), &QToolBar::toolButtonStyleChanged, m_timelineToolBar, &QToolBar::setToolButtonStyle);
545 546
    m_timelineToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_timelineToolBar, &QWidget::customContextMenuRequested, this, &MainWindow::showTimelineToolbarMenu);
547 548

    QAction *prevRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
549
    tlMenu->addAction(actionCollection()->action(QStringLiteral("stop_prerender_timeline")));
550 551
    tlMenu->addAction(actionCollection()->action(QStringLiteral("set_render_timeline_zone")));
    tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));
552 553 554 555 556 557 558 559
    tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));

    // Automatic timeline preview action
    QAction *autoRender = new QAction(KoIconUtils::themedIcon(QStringLiteral("view-refresh")), i18n("Automatic Preview"), this);
    autoRender->setCheckable(true);
    autoRender->setChecked(KdenliveSettings::autopreview());
    connect(autoRender, &QAction::triggered, this, &MainWindow::slotToggleAutoPreview);
    tlMenu->addAction(autoRender);
560
    tlMenu->addSeparator();
561
    tlMenu->addAction(actionCollection()->action(QStringLiteral("disable_preview")));
562
    tlMenu->addAction(actionCollection()->action(QStringLiteral("manage_cache")));
563
    timelinePreview->defineDefaultAction(prevRender, false);
564
    timelinePreview->setAutoRaise(true);
565 566 567 568 569 570

    tlrMenu->addAction(actionCollection()->action(QStringLiteral("project_render")));
    tlrMenu->addAction(actionCollection()->action(QStringLiteral("stop_project_render")));
    timelineRender->defineDefaultAction(actionCollection()->action(QStringLiteral("project_render")));
    timelineRender->setAutoRaise(true);

571

572
    //m_timelineToolBar->addAction(toolButtonAction);
573

574
    /*QWidget *sep = new QWidget(this);
575
    sep->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
576 577 578 579 580 581 582 583 584 585 586
    m_timelineToolBar->addWidget(sep);
    m_timelineToolBar->addAction(m_buttonAutomaticSplitAudio);
    m_timelineToolBar->addAction(m_buttonVideoThumbs);
    m_timelineToolBar->addAction(m_buttonAudioThumbs);
    m_timelineToolBar->addAction(m_buttonShowMarkers);
    m_timelineToolBar->addAction(m_buttonSnap);
    m_timelineToolBar->addSeparator();
    m_timelineToolBar->addAction(m_buttonFitZoom);
    m_timelineToolBar->addAction(m_zoomOut);
    m_timelineToolBar->addWidget(m_zoomSlider);
    m_timelineToolBar->addAction(m_zoomIn);*/
587

588
    // Populate encoding profiles
589
    KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::DataLocation);
590
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
591 592 593 594 595 596 597 598 599 600
        KConfigGroup group(&conf, "proxy");
        QMap< QString, QString > values = group.entryMap();
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
            QString data = i.value();
            KdenliveSettings::setProxyparams(data.section(';', 0, 0));
            KdenliveSettings::setProxyextension(data.section(';', 1, 1));
        }
    }
601
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
602 603 604 605 606 607 608 609 610 611
        KConfigGroup group(&conf, "video4linux");
        QMap< QString, QString > values = group.entryMap();
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
            QString data = i.value();
            KdenliveSettings::setV4l_parameters(data.section(';', 0, 0));
            KdenliveSettings::setV4l_extension(data.section(';', 1, 1));
        }
    }
612 613 614 615 616 617 618 619 620 621 622
    if (KdenliveSettings::grab_parameters().isEmpty() || KdenliveSettings::grab_extension().isEmpty()) {
        KConfigGroup group(&conf, "screengrab");
        QMap< QString, QString > values = group.entryMap();
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
            QString data = i.value();
            KdenliveSettings::setGrab_parameters(data.section(';', 0, 0));
            KdenliveSettings::setGrab_extension(data.section(';', 1, 1));
        }
    }
623
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
624 625 626 627 628 629 630 631 632 633
        KConfigGroup group(&conf, "decklink");
        QMap< QString, QString > values = group.entryMap();
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
            QString data = i.value();
            KdenliveSettings::setDecklink_parameters(data.section(';', 0, 0));
            KdenliveSettings::setDecklink_extension(data.section(';', 1, 1));
        }
    }
634
    statusBar()->show();
635
    emit GUISetupDone();
Till Theato's avatar
Till Theato committed
636
    pCore->projectManager()->init(Url, clipsToLoad);
637
    QTimer::singleShot(0, pCore->projectManager(), SLOT(slotLoadOnOpen()));
638
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
639

640 641 642
#ifdef USE_JOGSHUTTLE
    new JogManager(this);
#endif
643
    scmanager->slotCheckActiveScopes();
644
    //TODO: remove for release
645
    m_messageLabel->setMessage("This is an untested development version. Always backup your data", MltError);
646
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
647

648 649
void MainWindow::slotThemeChanged(const QString &theme)
{
650
    disconnect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()));
651 652 653 654
    KSharedConfigPtr config = KSharedConfig::openConfig(theme);
    setPalette(KColorScheme::createApplicationPalette(config));
    qApp->setPalette(palette());
    QPalette plt = palette();
655 656

    KdenliveSettings::setColortheme(theme);
657 658 659 660
    if (m_effectStack) {
        m_effectStack->updatePalette();
        m_effectStack->transitionConfig()->updatePalette();
    }
661
    if (m_effectList) m_effectList->updatePalette();
662
    if (m_transitionList) m_transitionList->updatePalette();
663 664
    if (m_clipMonitor) m_clipMonitor->setPalette(plt);
    if (m_projectMonitor) m_projectMonitor->setPalette(plt);
665 666
    if (m_messageLabel)
        m_messageLabel->updatePalette();
667
    if (pCore->projectManager() && pCore->projectManager()->currentTimeline()) {
668
        pCore->projectManager()->currentTimeline()->updatePalette();
669
    }
670 671 672
    if (m_timelineArea) {
        m_timelineArea->setPalette(plt);
    }
673

674 675
#if KXMLGUI_VERSION_MINOR < 23 && KXMLGUI_VERSION_MAJOR == 5
    // Not required anymore with auto colored icons since KF5 5.23
676 677 678 679 680 681 682 683 684
    QColor background = plt.window().color();
    bool useDarkIcons = background.value() < 100;
    if (m_themeInitialized && useDarkIcons != m_isDarkTheme) {
        if (pCore->bin()) {
            pCore->bin()->refreshIcons();
        }
        if (m_clipMonitor) m_clipMonitor->refreshIcons();
        if (m_projectMonitor) m_projectMonitor->refreshIcons();
        if (pCore->monitorManager()) pCore->monitorManager()->refreshIcons();
Vincent Pinon's avatar
Vincent Pinon committed
685
        if (m_effectStack && m_effectStack->transitionConfig()) m_effectStack->transitionConfig()->refreshIcons();
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        if (m_effectList) m_effectList->refreshIcons();
        if (m_effectStack) m_effectStack->refreshIcons();
        if (pCore->projectManager() && pCore->projectManager()->currentTimeline()) {
            pCore->projectManager()->currentTimeline()->refreshIcons();
        }

        foreach(QAction* action, actionCollection()->actions()) {
            QIcon icon = action->icon();
            if (icon.isNull()) continue;
            QString iconName = icon.name();
            QIcon newIcon = KoIconUtils::themedIcon(iconName);
            if (newIcon.isNull()) continue;
            action->setIcon(newIcon);
        }
    }
    m_themeInitialized = true;
    m_isDarkTheme = useDarkIcons;
703
#endif
704
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
705 706 707 708 709
}

bool MainWindow::event(QEvent *e) {
    switch (e->type()) {
        case QEvent::ApplicationPaletteChange:
710
            emit reloadTheme();
711 712 713 714 715 716 717 718
            e->accept();
            break;
        default:
            break;
    }
    return KXmlGuiWindow::event(e);
}

719 720 721 722 723
void MainWindow::slotReloadTheme()
{
    ThemeManager::instance()->slotSettingsChanged();
}

724
MainWindow::~MainWindow()
725
{
Laurent Montel's avatar
Laurent Montel committed
726
    delete m_stopmotion;
727
    delete m_audioSpectrum;
728
    m_effectStack->slotClipItemSelected(NULL, m_projectMonitor);
729
    m_effectStack->slotTransitionItemSelected(NULL, 0, QPoint(), false);
730 731
    if (m_projectMonitor) m_projectMonitor->stop();
    if (m_clipMonitor) m_clipMonitor->stop();
732
    delete pCore;
733 734 735 736
    delete m_effectStack;
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
737
    qDeleteAll(m_transitions);
738 739 740
    Mlt::Factory::close();
}

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
//virtual
bool MainWindow::queryClose()
{
    if (m_renderWidget) {
        int waitingJobs = m_renderWidget->waitingJobsCount();
        if (waitingJobs > 0) {
            switch (KMessageBox::warningYesNoCancel(this, i18np("You have 1 rendering job waiting in the queue.\nWhat do you want to do with this job?", "You have %1 rendering jobs waiting in the queue.\nWhat do you want to do with these jobs?", waitingJobs), QString(), KGuiItem(i18n("Start them now")), KGuiItem(i18n("Delete them")))) {
            case KMessageBox::Yes :
                // create script with waiting jobs and start it
                if (m_renderWidget->startWaitingRenderJobs() == false) return false;
                break;
            case KMessageBox::No :
                // Don't do anything, jobs will be deleted
                break;
           default:
                return false;
            }
        }
    }
    saveOptions();

    // WARNING: According to KMainWindow::queryClose documentation we are not supposed to close the document here?
    return pCore->projectManager()->closeCurrentDocument(true, true);
764 765
}

766 767 768 769 770
void MainWindow::loadGenerators()
{
    QMenu *addMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("generators"), this));
    Generators::getGenerators(KdenliveSettings::producerslist(), addMenu);
    connect(addMenu, SIGNAL(triggered(QAction *)), this, SLOT(buildGenerator(QAction *)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
771 772
}

773
void MainWindow::buildGenerator(QAction *action)
774
{
Jean-Baptiste Mardelle's avatar