mainwindow.cpp 166 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
#include <QMenuBar>
107 108

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

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

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

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

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

136 137 138 139 140 141 142 143 144
// 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);
}

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

    new RenderingAdaptor(this);
    pCore->initialize();
    MltConnection::locateMeltAndProfilesPath(MltPath);
224 225
    QString defaultProfile = KdenliveSettings::default_profile();
    KdenliveSettings::setCurrent_profile(defaultProfile.isEmpty() ? ProjectManager::getDefaultProjectFormat() : defaultProfile);
226 227 228 229 230 231 232

    // 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");
    }
233
    m_commandStack = new QUndoGroup;
234 235
    m_timelineArea = new QTabWidget(this);
    //m_timelineArea->setTabReorderingEnabled(true);
236
    m_timelineArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
Simon Eugster's avatar
Simon Eugster committed
237
    m_timelineArea->setMinimumHeight(200);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
238 239 240
    // Hide tabbar
    QTabBar *bar = m_timelineArea->findChild<QTabBar *>();
    bar->setHidden(true);
241

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

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

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

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_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
320
    m_effectStack = new EffectStackView2(m_projectMonitor, this);
321
    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>&)));
322
    connect(pCore->bin(), SIGNAL(masterClipSelected(ClipController *, Monitor *)), m_effectStack, SLOT(slotMasterClipItemSelected(ClipController *, Monitor *)));
323
    connect(pCore->bin(), SIGNAL(masterClipUpdated(ClipController *, Monitor *)), m_effectStack, SLOT(slotRefreshMasterClipEffects(ClipController *, Monitor *)));
324 325
    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)));
326
    connect(m_effectStack, SIGNAL(changeEffectPosition(QString,const QList <int>,int)), pCore->bin(), SLOT(slotMoveEffect(QString,const QList <int>,int)));
327
    connect(m_effectStack, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
328
    connect(m_effectStack, SIGNAL(displayMessage(QString,int)), m_messageLabel, SLOT(setProgressMessage(QString,int)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
329
    m_effectStackDock = addDock(i18n("Properties"), QStringLiteral("effect_stack"), m_effectStack);
330

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

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

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

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

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

355 356 357 358 359 360
    QAction *iconAction = new QAction(i18n("Force Breeze Icon Theme"), this);
    iconAction->setCheckable(true);
    iconAction->setChecked(KdenliveSettings::force_breeze());
    addAction(QStringLiteral("force_icon_theme"), iconAction);
    connect(iconAction, &QAction::triggered, this, &MainWindow::forceIconSet);

Simon Eugster's avatar
Simon Eugster committed
361 362 363 364 365 366
    // Close non-general docks for the initial layout
    // only show important ones
    m_undoViewDock->close();



367
    /// Tabify Widgets
368
    tabifyDockWidget(m_transitionListDock, m_effectListDock);
369
    tabifyDockWidget(m_effectStackDock, pCore->bin()->clipPropertiesDock());
370
    //tabifyDockWidget(m_effectListDock, m_effectStackDock);
371

372
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
373 374 375
    if (m_recMonitor) {
        tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
    }
376
    bool firstRun = readOptions();
Simon Eugster's avatar
Simon Eugster committed
377

378
    QAction *action;
379 380
    // 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());
381
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("media-record")), i18n("Capture frame"), this);
382
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
383
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_capture"), action);
384
    action = new QAction(i18n("Switch live / captured frame"), this);
385
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
386 387
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_switch"), action);
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("edit-paste")), i18n("Show last frame over video"), this);
388 389
    action->setCheckable(true);
    action->setChecked(false);
390
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_overlay"), action);
391

392
    // Monitor Record action
393
    addAction(QStringLiteral("switch_monitor_rec"), m_clipMonitor->recAction());
394

395
    // Build effects menu
396
    m_effectsMenu = new QMenu(i18n("Add Effect"), this);
397 398
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
399

400 401 402 403
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
    m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
    m_transitionList->reloadEffectList(m_transitionsMenu, m_transitionActions);

404
    ScopeManager *scmanager = new ScopeManager(this);
405

Till Theato's avatar
Till Theato committed
406
    new LayoutManagement(this);
407
    new HideTitleBars(this);
408
    new TimelineSearch(this);
409 410
    m_extraFactory = new KXMLGUIClient(this);
    buildDynamicActions();
411

412 413 414 415 416 417 418 419 420 421 422 423 424 425
    // 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() + ")");
        }
    }
426

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

    QToolButton *basketButton = new QToolButton(this);
    basketButton->setMenu(menu);
440
    basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
441 442
    basketButton->setDefaultAction(widgetlist);
    basketButton->setPopupMode(QToolButton::InstantPopup);
443
    //basketButton->setText(i18n("Favorite Effects"));
444
    basketButton->setToolTip(i18n("Favorite Effects"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
445
    basketButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
446

447
    QWidgetAction* toolButtonAction = new QWidgetAction(this);
448
    toolButtonAction->setText(i18n("Favorite Effects"));
449 450
    toolButtonAction->setIcon(KoIconUtils::themedIcon("favorite"));
    toolButtonAction->setDefaultWidget(basketButton);
451
    addAction(QStringLiteral("favorite_effects"), toolButtonAction);
452
    connect(toolButtonAction, SIGNAL(triggered(bool)), basketButton, SLOT(showMenu()));
453

454 455 456 457 458 459 460 461 462 463 464
    // 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);

465
    // Timeline preview button
466
    ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
467 468
    QMenu *tlMenu = new QMenu(this);
    timelinePreview->setMenu(tlMenu);
469
    connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
470 471
    QWidgetAction* previewButtonAction = new QWidgetAction(this);
    previewButtonAction->setText(i18n("Timeline Preview"));
472
    previewButtonAction->setIcon(KoIconUtils::themedIcon(QStringLiteral("preview-render-on")));
473
    previewButtonAction->setDefaultWidget(timelinePreview);
474
    addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
475

476 477 478 479 480 481 482 483 484 485 486
    setupGUI(QSize(1800,1000));
    if (firstRun) {
        QScreen *current = QApplication::primaryScreen();
        if (current) {
            if (current->availableSize().height() < 1000) {
                resize(current->availableSize());
            } else {
                resize(current->availableSize() / 1.5);
            }
        }
    }
487 488
    timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
    connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
489

490 491 492
    timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
    /*connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);*/

493 494
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
495

496
    loadGenerators();
497
    loadDockActions();
498
    loadClipActions();
499 500

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

504 505
    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)));
506

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

511 512
    connect(pCore->monitorManager(), SIGNAL(updateOverlayInfos(int,int)), this, SLOT(slotUpdateMonitorOverlays(int,int)));

513
    // Setup and fill effects and transitions menus.
514
    QMenu *m = static_cast<QMenu*>(factory()->container(QStringLiteral("video_effects_menu"), this));
Laurent Montel's avatar
Laurent Montel committed
515 516
    connect(m, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
    connect(m_effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
517
    connect(m_transitionsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddTransition(QAction*)));
518

519 520 521 522
    m_timelineContextMenu = new QMenu(this);
    m_timelineContextClipMenu = new QMenu(this);
    m_timelineContextTransitionMenu = new QMenu(this);

523 524
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
525 526
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

527
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_in_project_tree")));
528
    //m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
529
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
530
    m_timelineContextClipMenu->addSeparator();
531 532 533 534 535
    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")));
536
    m_timelineContextClipMenu->addSeparator();
537
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("cut_timeline_clip")));
538
    m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
539
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("paste_effects")));
540
    m_timelineContextClipMenu->addSeparator();
541

542
    QMenu *markersMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("marker_menu"), this));
543
    m_timelineContextClipMenu->addMenu(markersMenu);
544
    m_timelineContextClipMenu->addSeparator();
545
    m_timelineContextClipMenu->addMenu(m_transitionsMenu);
546
    m_timelineContextClipMenu->addMenu(m_effectsMenu);
Marco Gittler's avatar
Marco Gittler committed
547

548
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
549
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
550

551
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("auto_transition")));
552

Laurent Montel's avatar
Laurent Montel committed
553
    connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
554 555
    connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));

556
    slotConnectMonitors();
557

558
    m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
559 560
    // TODO: let user select timeline toolbar toolbutton style
    //connect(toolBar(), &QToolBar::toolButtonStyleChanged, m_timelineToolBar, &QToolBar::setToolButtonStyle);
561 562
    m_timelineToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_timelineToolBar, &QWidget::customContextMenuRequested, this, &MainWindow::showTimelineToolbarMenu);
563 564

    QAction *prevRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
565
    tlMenu->addAction(actionCollection()->action(QStringLiteral("stop_prerender_timeline")));
566 567
    tlMenu->addAction(actionCollection()->action(QStringLiteral("set_render_timeline_zone")));
    tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));
568 569 570 571 572 573 574 575
    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);
576
    tlMenu->addSeparator();
577
    tlMenu->addAction(actionCollection()->action(QStringLiteral("disable_preview")));
578
    tlMenu->addAction(actionCollection()->action(QStringLiteral("manage_cache")));
579
    timelinePreview->defineDefaultAction(prevRender, false);
580
    timelinePreview->setAutoRaise(true);
581 582 583 584 585 586

    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);

587

588
    //m_timelineToolBar->addAction(toolButtonAction);
589

590
    /*QWidget *sep = new QWidget(this);
591
    sep->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
592 593 594 595 596 597 598 599 600 601 602
    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);*/
603

604
    // Populate encoding profiles
605
    KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::DataLocation);
606
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
607 608 609 610 611 612 613 614 615 616
        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));
        }
    }
617
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
618 619 620 621 622 623 624 625 626 627
        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));
        }
    }
628 629 630 631 632 633 634 635 636 637 638
    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));
        }
    }
639
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
640 641 642 643 644 645 646 647 648 649
        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));
        }
    }
650
    emit GUISetupDone();
Till Theato's avatar
Till Theato committed
651
    pCore->projectManager()->init(Url, clipsToLoad);
652
    QTimer::singleShot(0, pCore->projectManager(), SLOT(slotLoadOnOpen()));
653
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
654

655 656 657
#ifdef USE_JOGSHUTTLE
    new JogManager(this);
#endif
658
    scmanager->slotCheckActiveScopes();
659
    //TODO: remove for release
660
    m_messageLabel->setMessage("This is an untested development version. Always backup your data", MltError);
661
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
662

663 664
void MainWindow::slotThemeChanged(const QString &theme)
{
665
    disconnect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()));
666 667 668 669
    KSharedConfigPtr config = KSharedConfig::openConfig(theme);
    setPalette(KColorScheme::createApplicationPalette(config));
    qApp->setPalette(palette());
    QPalette plt = palette();
670 671

    KdenliveSettings::setColortheme(theme);
672 673 674 675
    if (m_effectStack) {
        m_effectStack->updatePalette();
        m_effectStack->transitionConfig()->updatePalette();
    }
676
    if (m_effectList) m_effectList->updatePalette();
677
    if (m_transitionList) m_transitionList->updatePalette();
678 679
    if (m_clipMonitor) m_clipMonitor->setPalette(plt);
    if (m_projectMonitor) m_projectMonitor->setPalette(plt);
680 681
    if (m_messageLabel)
        m_messageLabel->updatePalette();
682
    if (pCore->projectManager() && pCore->projectManager()->currentTimeline()) {
683
        pCore->projectManager()->currentTimeline()->updatePalette();
684
    }
685 686 687
    if (m_timelineArea) {
        m_timelineArea->setPalette(plt);
    }
688

689 690
#if KXMLGUI_VERSION_MINOR < 23 && KXMLGUI_VERSION_MAJOR == 5
    // Not required anymore with auto colored icons since KF5 5.23
691 692 693 694 695 696 697 698 699
    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
700
        if (m_effectStack && m_effectStack->transitionConfig()) m_effectStack->transitionConfig()->refreshIcons();
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
        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;
718
#endif
719
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
720 721 722 723 724
}

bool MainWindow::event(QEvent *e) {
    switch (e->type()) {
        case QEvent::ApplicationPaletteChange:
725
            emit reloadTheme();
726 727 728 729 730 731 732 733
            e->accept();
            break;
        default:
            break;
    }
    return KXmlGuiWindow::event(e);
}

734 735 736 737 738
void MainWindow::slotReloadTheme()
{
    ThemeManager::instance()->slotSettingsChanged();
}

739
MainWindow::~MainWindow()
740
{
Laurent Montel's avatar
Laurent Montel committed
741
    delete m_stopmotion;
742
    delete m_audioSpectrum;
743
    m_effectStack->slotClipItemSelected(NULL, m_projectMonitor);
744
    m_effectStack->slotTransitionItemSelected(NULL, 0, QPoint(), false);
745 746
    if (m_projectMonitor) m_projectMonitor->stop();
    if (m_clipMonitor) m_clipMonitor->stop();
747
    delete pCore;
748 749 750 751
    delete m_effectStack;
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
752
    qDeleteAll(m_transitions);
753 754 755
    Mlt::Factory::close();
}

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
//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