mainwindow.cpp 193 KB
Newer Older
1
/*
2
3
    SPDX-FileCopyrightText: 2007 Jean-Baptiste Mardelle <jb@kdenlive.org>

Camille Moulin's avatar
Camille Moulin committed
4
SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5
*/
6

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
7
#include "mainwindow.h"
8
#include "assets/assetpanel.hpp"
9
#include "audiomixer/mixermanager.hpp"
10
#include "bin/clipcreator.hpp"
11
#include "bin/generators/generators.h"
12
#include "bin/model/subtitlemodel.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
13
#include "bin/projectclip.h"
14
15
#include "bin/projectfolder.h"
#include "bin/projectitemmodel.h"
Nicolas Carion's avatar
Nicolas Carion committed
16
#include "core.h"
17
#include "dialogs/clipcreationdialog.h"
Nicolas Carion's avatar
Nicolas Carion committed
18
19
#include "dialogs/kdenlivesettingsdialog.h"
#include "dialogs/renderwidget.h"
20
#include "dialogs/subtitleedit.h"
21
#include "dialogs/wizard.h"
22
#include "doc/docundostack.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
23
#include "doc/kdenlivedoc.h"
24
#include "effects/effectbasket.h"
25
#include "effects/effectlist/view/effectlistwidget.hpp"
26
#include "docktitlebarmanager.h"
27
#include "jobs/audiolevelstask.h"
28
#include "jobs/scenesplittask.h"
29
#include "jobs/speedtask.h"
30
31
#include "jobs/stabilizetask.h"
#include "jobs/transcodetask.h"
Nicolas Carion's avatar
Nicolas Carion committed
32
33
34
#include "kdenlivesettings.h"
#include "layoutmanagement.h"
#include "library/librarywidget.h"
35
#ifdef NODBUS
36
#include "render/renderserver.h"
37
#else
Nicolas Carion's avatar
Nicolas Carion committed
38
#include "mainwindowadaptor.h"
39
#endif
40
41
42
#include "dialogs/textbasededit.h"
#include "dialogs/timeremap.h"
#include "lib/localeHandling.h"
Nicolas Carion's avatar
Nicolas Carion committed
43
44
45
46
47
#include "mltconnection.h"
#include "mltcontroller/clipcontroller.h"
#include "monitor/monitor.h"
#include "monitor/monitormanager.h"
#include "monitor/scopes/audiographspectrum.h"
48
#include "onlineresources/resourcewidget.hpp"
Nicolas Carion's avatar
linting    
Nicolas Carion committed
49
#include "profiles/profilemodel.hpp"
50
#include "profiles/profilerepository.hpp"
Vincent PINON's avatar
Vincent PINON committed
51
#include "project/cliptranscode.h"
Vincent PINON's avatar
Vincent PINON committed
52
#include "project/dialogs/archivewidget.h"
Nicolas Carion's avatar
Nicolas Carion committed
53
#include "project/dialogs/projectsettings.h"
54
#include "project/dialogs/temporarydata.h"
Nicolas Carion's avatar
Nicolas Carion committed
55
#include "project/projectcommands.h"
Till Theato's avatar
Till Theato committed
56
#include "project/projectmanager.h"
Nicolas Carion's avatar
Nicolas Carion committed
57
#include "scopes/scopemanager.h"
Nicolas Carion's avatar
linting    
Nicolas Carion committed
58
#include "timeline2/view/timelinecontroller.h"
59
#include "timeline2/view/timelinetabs.hpp"
60
#include "timeline2/view/timelinewidget.h"
Nicolas Carion's avatar
Nicolas Carion committed
61
62
63
#include "titler/titlewidget.h"
#include "transitions/transitionlist/view/transitionlistwidget.hpp"
#include "transitions/transitionsrepository.hpp"
Julius Künzel's avatar
Julius Künzel committed
64
#include "pythoninterfaces/otioconvertions.h"
65
#include "utils/thememanager.h"
66
67
#include "widgets/progressbutton.h"
#include <config-kdenlive.h>
68

69
70
71
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
72

73
#include <KAboutData>
74
#include <KActionCategory>
Nicolas Carion's avatar
Nicolas Carion committed
75
#include <KActionCollection>
76
#include <KActionMenu>
Nicolas Carion's avatar
Nicolas Carion committed
77
78
#include <KColorScheme>
#include <KColorSchemeManager>
79
#include <KConfigDialog>
80
#include <KCoreAddons>
Nicolas Carion's avatar
Nicolas Carion committed
81
82
#include <KDualAction>
#include <KEditToolBar>
83
#include <KIconTheme>
Nicolas Carion's avatar
Nicolas Carion committed
84
85
#include <KMessageBox>
#include <KNotifyConfigWidget>
86
#include <KRecentDirs>
Nicolas Carion's avatar
Nicolas Carion committed
87
88
89
90
91
#include <KShortcutsDialog>
#include <KStandardAction>
#include <KToolBar>
#include <KXMLGUIFactory>
#include <klocalizedstring.h>
92
#include <knewstuff_version.h>
93
// TODO The NewStuff QML Dialog doesn't work on windows for some reasons, use the old one until we found out why
94
#if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5,78,0)
95
#include <kns3/downloaddialog.h>
96
97
98
#else
#include <kns3/qtquickdialogwrapper.h>
#endif
99
#include <kcoreaddons_version.h>
100
#include <kns3/knewstuffaction.h>
Nicolas Carion's avatar
Nicolas Carion committed
101
#include <ktogglefullscreenaction.h>
Vincent Pinon's avatar
Vincent Pinon committed
102
#include <kwidgetsaddons_version.h>
103

Laurent Montel's avatar
Laurent Montel committed
104
#include "kdenlive_debug.h"
105
#include <KConfigGroup>
Nicolas Carion's avatar
Nicolas Carion committed
106
#include <QAction>
107
#include <QDialogButtonBox>
Nicolas Carion's avatar
Nicolas Carion committed
108
109
110
#include <QFileDialog>
#include <QMenu>
#include <QMenuBar>
Vincent PINON's avatar
Vincent PINON committed
111
#include <QPushButton>
112
#include <QScreen>
Nicolas Carion's avatar
Nicolas Carion committed
113
#include <QStandardPaths>
114
115
116
#include <QStatusBar>
#include <QStyleFactory>
#include <QUndoGroup>
Nicolas Carion's avatar
Nicolas Carion committed
117
#include <QVBoxLayout>
118

119
static const char version[] = KDENLIVE_VERSION;
Nicolas Carion's avatar
Nicolas Carion committed
120
namespace Mlt {
121
class Producer;
Laurent Montel's avatar
Laurent Montel committed
122
}
123

Laurent Montel's avatar
Laurent Montel committed
124
125
QMap<QString, QImage> MainWindow::m_lumacache;
QMap<QString, QStringList> MainWindow::m_lumaFiles;
126

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

Yuri Chornoivan's avatar
Yuri Chornoivan committed
132
// determine the default KDE style as defined BY THE USER
133
// (as opposed to whatever style KDE considers default)
Laurent Montel's avatar
Laurent Montel committed
134
static QString defaultStyle(const char *fallback = nullptr)
135
136
137
138
139
140
{
    KSharedConfigPtr kdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals);
    KConfigGroup cg(kdeGlobals, "KDE");
    return cg.readEntry("widgetStyle", fallback);
}

Nicolas Carion's avatar
Nicolas Carion committed
141
MainWindow::MainWindow(QWidget *parent)
142
    : KXmlGuiWindow(parent)
143
    , m_activeTool(ToolType::SelectTool)
144
    , m_mousePosition(0)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
145
    , m_effectBasket(nullptr)
146
{
147
148
149
150
151
152
153
154
155
156
157
    // Init all action categories that are used by other parts of the software
    // before we call MainWindow::init and therefore can't be initilized there
    KActionCategory *category = new KActionCategory(i18n("Monitor"), actionCollection());
    kdenliveCategoryMap.insert(QStringLiteral("monitor"), category);
    category = new KActionCategory(i18n("Add Clip"), actionCollection());
    kdenliveCategoryMap.insert(QStringLiteral("addclip"), category);
    category = new KActionCategory(i18n("Navigation and Playback"), actionCollection());
    kdenliveCategoryMap.insert(QStringLiteral("navandplayback"), category);
    category = new KActionCategory(i18n("Bin Tags"), actionCollection());
    kdenliveCategoryMap.insert(QStringLiteral("bintags"), category);

158
159
}

160
void MainWindow::init(const QString &mltPath)
161
{
162
    QString desktopStyle = QApplication::style()->objectName();
163
164
165
166
    // Load themes
    auto themeManager = new ThemeManager(actionCollection());
    actionCollection()->addAction(QStringLiteral("themes_menu"), themeManager);
    connect(themeManager, &ThemeManager::themeChanged, this, &MainWindow::slotThemeChanged);
167
    emit pCore->updatePalette();
168

169
170
171
172
    if (!KdenliveSettings::widgetstyle().isEmpty() && QString::compare(desktopStyle, KdenliveSettings::widgetstyle(), Qt::CaseInsensitive) != 0) {
        // User wants a custom widget style, init
        doChangeStyle();
    }
Nicolas Carion's avatar
Nicolas Carion committed
173

174
    // Widget themes for non KDE users
Laurent Montel's avatar
Laurent Montel committed
175
    KActionMenu *stylesAction = new KActionMenu(i18n("Style"), this);
Nicolas Carion's avatar
Nicolas Carion committed
176
    auto *stylesGroup = new QActionGroup(stylesAction);
177

178
179
    // GTK theme does not work well with Kdenlive, and does not support color theming, so avoid it
    QStringList availableStyles = QStyleFactory::keys();
180
181
    if (KdenliveSettings::widgetstyle().isEmpty()) {
        // First run
182
        QStringList incompatibleStyles = {QStringLiteral("GTK+"), QStringLiteral("windowsvista"), QStringLiteral("Windows"), QStringLiteral("macintosh")};
183

184
        if (incompatibleStyles.contains(desktopStyle, Qt::CaseInsensitive)) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
185
            if (availableStyles.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) {
186
187
                // Auto switch to Breeze theme
                KdenliveSettings::setWidgetstyle(QStringLiteral("Breeze"));
188
		QApplication::setStyle(QStyleFactory::create(QStringLiteral("Breeze")));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
189
            } else if (availableStyles.contains(QStringLiteral("fusion"), Qt::CaseInsensitive)) {
190
                KdenliveSettings::setWidgetstyle(QStringLiteral("Fusion"));
191
		QApplication::setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
192
            }
193
194
        } else {
            KdenliveSettings::setWidgetstyle(QStringLiteral("Default"));
195
196
197
        }
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
198
199
    // Add default style action
    QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
200
    defaultStyle->setData(QStringLiteral("Default"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
201
202
    defaultStyle->setCheckable(true);
    stylesAction->addAction(defaultStyle);
203
    if (KdenliveSettings::widgetstyle() == QLatin1String("Default") || KdenliveSettings::widgetstyle().isEmpty()) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
204
205
206
        defaultStyle->setChecked(true);
    }

Vincent Pinon's avatar
Vincent Pinon committed
207
    for (const QString &style : qAsConst(availableStyles)) {
Nicolas Carion's avatar
Nicolas Carion committed
208
        auto *a = new QAction(style, stylesGroup);
209
210
        a->setCheckable(true);
        a->setData(style);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
211
212
213
        if (KdenliveSettings::widgetstyle() == style) {
            a->setChecked(true);
        }
214
215
216
        stylesAction->addAction(a);
    }
    connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);
Nicolas Carion's avatar
Nicolas Carion committed
217
    // QIcon::setThemeSearchPaths(QStringList() <<QStringLiteral(":/icons/"));
218
219
220
#ifdef NODBUS
    new RenderServer(this);
#else
221
    new RenderingAdaptor(this);
222
#endif
223
    QString defaultProfile = KdenliveSettings::default_profile();
224
225
226
    
    // Initialise MLT connection
    MltConnection::construct(mltPath);
227
    pCore->setCurrentProfile(defaultProfile.isEmpty() ? ProjectManager::getDefaultProjectFormat() : defaultProfile);
228
    m_commandStack = new QUndoGroup();
229
230

    // If using a custom profile, make sure the file exists or fallback to default
231
232
    QString currentProfilePath = pCore->getCurrentProfile()->path();
    if (currentProfilePath.startsWith(QLatin1Char('/')) && !QFile::exists(currentProfilePath)) {
233
        KMessageBox::sorry(this, i18n("Cannot find your default profile, switching to ATSC 1080p 25"));
234
        pCore->setCurrentProfile(QStringLiteral("atsc_1080p_25"));
Laurent Montel's avatar
Laurent Montel committed
235
        KdenliveSettings::setDefault_profile(QStringLiteral("atsc_1080p_25"));
236
    }
237
    m_gpuAllowed = EffectsRepository::get()->hasInternalEffect(QStringLiteral("glsl.manager"));
238

239
    m_shortcutRemoveFocus = new QShortcut(QKeySequence(QStringLiteral("Esc")), this);
Laurent Montel's avatar
Laurent Montel committed
240
    connect(m_shortcutRemoveFocus, &QShortcut::activated, this, &MainWindow::slotRemoveFocus);
Simon Eugster's avatar
Simon Eugster committed
241

242
    /// Add Widgets
243
    setDockOptions(dockOptions() | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
244
    setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
Vincent Pinon's avatar
Vincent Pinon committed
245
    setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition(KdenliveSettings::tabposition()));
246
    m_timelineToolBar = toolBar(QStringLiteral("timelineToolBar"));
247
    m_timelineToolBarContainer = new TimelineContainer(this);
Nicolas Carion's avatar
Nicolas Carion committed
248
    auto *ctnLay = new QVBoxLayout;
249
250
    ctnLay->setSpacing(0);
    ctnLay->setContentsMargins(0, 0, 0, 0);
251
    m_timelineToolBarContainer->setLayout(ctnLay);
252
253
254
255
    QFrame *topFrame = new QFrame(this);
    topFrame->setFrameShape(QFrame::HLine);
    topFrame->setFixedHeight(1);
    topFrame->setLineWidth(1);
Vincent Pinon's avatar
Vincent Pinon committed
256
    connect(this, &MainWindow::focusTimeline, this, [topFrame](bool focus, bool highlight) {
257
258
259
260
261
262
263
264
265
266
267
268
269
270
        if (focus) {
            KColorScheme scheme(QApplication::palette().currentColorGroup(), KColorScheme::Tooltip);
            if (highlight) {
                QColor col = scheme.decoration(KColorScheme::HoverColor).color();
                topFrame->setStyleSheet(QString("QFrame {border: 1px solid rgba(%1,%2,%3,70)}").arg(col.red()).arg(col.green()).arg(col.blue()));
            } else {
                QColor col = scheme.decoration(KColorScheme::FocusColor).color();
                topFrame->setStyleSheet(QString("QFrame {border: 1px solid rgba(%1,%2,%3,100)}").arg(col.red()).arg(col.green()).arg(col.blue()));
            }
        } else {
            topFrame->setStyleSheet(QString());
        }
    });
    ctnLay->addWidget(topFrame);
271
    ctnLay->addWidget(m_timelineToolBar);
272
273
274
275
    KSharedConfigPtr config = KSharedConfig::openConfig();
    KConfigGroup mainConfig(config, QStringLiteral("MainWindow"));
    KConfigGroup tbGroup(&mainConfig, QStringLiteral("Toolbar timelineToolBar"));
    m_timelineToolBar->applySettings(tbGroup);
276
277
278
279
280
    QFrame *fr = new QFrame(this);
    fr->setFrameShape(QFrame::HLine);
    fr->setMaximumHeight(1);
    fr->setLineWidth(1);
    ctnLay->addWidget(fr);
281
    setupActions();
282
    auto *layoutManager = new LayoutManagement(this);
283
    pCore->bin()->setupMenu();
284

Laurent Montel's avatar
Laurent Montel committed
285
    QDockWidget *libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
286
    QDockWidget *subtitlesDock = addDock(i18n("Subtitles"), QStringLiteral("Subtitles"), pCore->subtitleWidget());
287
    QDockWidget *textEditingDock = addDock(i18n("Speech Editor"), QStringLiteral("textedit"), pCore->textEditWidget());
288
    QDockWidget *timeRemapDock = addDock(i18n("Time Remapping"), QStringLiteral("timeremap"), pCore->timeRemapWidget());
289
290
291
292
293
294
295
    connect(pCore.get(), &Core::remapClip, this, [&, timeRemapDock] (int id) {
        if (id > -1) {
            timeRemapDock->show();
            timeRemapDock->raise();
        }
        pCore->timeRemapWidget()->selectedClip(id);
    });
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
296
    m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
297
    pCore->bin()->setMonitor(m_clipMonitor);
298
299
    connect(m_clipMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
    connect(m_clipMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
300
301
    connect(m_clipMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
302

Nicolas Carion's avatar
Nicolas Carion committed
303
    // TODO deprecated, replace with Bin methods if necessary
304
    /*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
305
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
Laurent Montel's avatar
Laurent Montel committed
306
    connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
307
    connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
308
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
309

Laurent Montel's avatar
Laurent Montel committed
310
    connect(m_clipMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
311

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
312
    m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
Laurent Montel's avatar
Laurent Montel committed
313
    connect(m_projectMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
314
    connect(m_projectMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
315
    connect(m_projectMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteGuide);
316
317
    connect(m_projectMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_projectMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
Vincent Pinon's avatar
Vincent Pinon committed
318
    connect(m_loopClip, &QAction::triggered, this, [&]() {
319
320
321
        QPoint inOut = getMainTimeline()->controller()->selectionInOut();
        m_projectMonitor->slotLoopClip(inOut);
    });
322
    installEventFilter(this);
323
    pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor);
324

325
326
    m_timelineTabs = new TimelineTabs(this);
    ctnLay->addWidget(m_timelineTabs);
327
    setCentralWidget(m_timelineToolBarContainer);
328

329
330
    // Screen grab widget
    QWidget *grabWidget = new QWidget(this);
331
    auto *grabLayout = new QVBoxLayout;
332
    grabWidget->setLayout(grabLayout);
333
    auto *recToolbar = new QToolBar(grabWidget);
334
335
    grabLayout->addWidget(recToolbar);
    grabLayout->addStretch(10);
336
    // Check number of monitors for FFmpeg screen capture
Vincent Pinon's avatar
Vincent Pinon committed
337
    int screens = QApplication::screens().count();
338
    if (screens > 1) {
339
        auto *screenCombo = new QComboBox(recToolbar);
340
341
342
343
344
345
346
347
        for (int ix = 0; ix < screens; ix++) {
            screenCombo->addItem(i18n("Monitor %1", ix));
        }
        connect(screenCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_clipMonitor, &Monitor::slotSetScreen);
        recToolbar->addWidget(screenCombo);
        // Update screen grab monitor choice in case we changed from fullscreen
        screenCombo->setEnabled(KdenliveSettings::grab_capture_type() == 0);
    }
348
349
350
351
352
353
    QAction *recAction = m_clipMonitor->recAction();
    addAction(QStringLiteral("screengrab_record"), recAction);
    recToolbar->addAction(recAction);
    QAction *recConfig = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Recording"), this);
    recToolbar->addAction(recConfig);
    connect(recConfig, &QAction::triggered, [&]() {
Vincent Pinon's avatar
Vincent Pinon committed
354
        emit pCore->showConfigDialog(4, 0);
355
356
    });
    QDockWidget *screenGrabDock = addDock(i18n("Screen Grab"), QStringLiteral("screengrab"), grabWidget);
357

358
359
    // Audio spectrum scope
    m_audioSpectrum = new AudioGraphSpectrum(pCore->monitorManager());
Laurent Montel's avatar
Laurent Montel committed
360
    QDockWidget *spectrumDock = addDock(i18n("Audio Spectrum"), QStringLiteral("audiospectrum"), m_audioSpectrum);
Vincent Pinon's avatar
Vincent Pinon committed
361
    connect(spectrumDock, &QDockWidget::visibilityChanged, this, [&](bool visible) {
362
363
        m_audioSpectrum->dockVisible(visible);
    });
364
365
366
367
368
369
370
371
    
    // Project bin
    m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
    
    // Media browser widget
    QDockWidget* clipDockWidget = addDock(i18n("Media Browser"), QStringLiteral("bin_clip"), pCore->bin()->getWidget());
    pCore->bin()->dockWidgetInit(clipDockWidget);

372
    // Online resources widget
373
    auto *onlineResources = new ResourceWidget(this);
374
    m_onlineResourcesDock = addDock(i18n("Online Resources"), QStringLiteral("onlineresources"), onlineResources);
375
    connect(onlineResources, &ResourceWidget::previewClip, this, [&](const QString &path, const QString &title) {
376
        m_clipMonitor->slotPreviewResource(path, title);
377
378
379
        m_clipMonitorDock->show();
        m_clipMonitorDock->raise();
    });
380

381
    connect(onlineResources, &ResourceWidget::addClip, this, &MainWindow::slotAddProjectClip);
Julius Künzel's avatar
Julius Künzel committed
382
    connect(onlineResources, &ResourceWidget::addLicenseInfo, this, &MainWindow::slotAddTextNote);
383

384
    // Close library and audiospectrum and others on first run
385
    screenGrabDock->close();
386
    libraryDock->close();
387
    subtitlesDock->close();
388
    textEditingDock->close();
389
    timeRemapDock->close();
390
    spectrumDock->close();
391
    clipDockWidget->close();
392
    m_onlineResourcesDock->close();
393

394
    m_effectStackDock = addDock(i18n("Effect/Composition Stack"), QStringLiteral("effect_stack"), m_assetPanel);
395
396
    connect(m_assetPanel, &AssetPanel::doSplitEffect, m_projectMonitor, &Monitor::slotSwitchCompare);
    connect(m_assetPanel, &AssetPanel::doSplitBinEffect, m_clipMonitor, &Monitor::slotSwitchCompare);
Vincent Pinon's avatar
Vincent Pinon committed
397
    connect(m_assetPanel, &AssetPanel::switchCurrentComposition, this, [&](int cid, const QString &compositionId) {
398
399
400
        getMainTimeline()->controller()->getModel()->switchComposition(cid, compositionId);
    });

401
    connect(m_timelineTabs, &TimelineTabs::showMixModel, m_assetPanel, &AssetPanel::showMix);
402
    connect(m_timelineTabs, &TimelineTabs::showTransitionModel, m_assetPanel, &AssetPanel::showTransition);
Vincent Pinon's avatar
Vincent Pinon committed
403
    connect(m_timelineTabs, &TimelineTabs::showTransitionModel, this, [&] () {
404
405
        m_effectStackDock->raise();
    });
406
    connect(m_timelineTabs, &TimelineTabs::showItemEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
Vincent Pinon's avatar
Vincent Pinon committed
407
    connect(m_timelineTabs, &TimelineTabs::showItemEffectStack, this, [&] () {
408
409
        m_effectStackDock->raise();
    });
410
411
412
    
    connect(m_timelineTabs, &TimelineTabs::updateAssetPosition, m_assetPanel, &AssetPanel::updateAssetPosition);
    
413

414
415
416
417
418
419
420
421
    connect(m_timelineTabs, &TimelineTabs::showSubtitle, this, [&, subtitlesDock] (int id) {
        if (id > -1) {
            subtitlesDock->show();
            subtitlesDock->raise();
        }
        pCore->subtitleWidget()->setActiveSubtitle(id);
    });

422
    connect(m_timelineTabs, &TimelineTabs::updateZoom, this, &MainWindow::updateZoomSlider);
423
    connect(pCore->bin(), &Bin::requestShowEffectStack, [&] () {
424
425
        // Don't raise effect stack on clip bin in case it is docked with bin or clip monitor
        // m_effectStackDock->raise();
426
    });
427
    connect(this, &MainWindow::clearAssetPanel, m_assetPanel, &AssetPanel::clearAssetPanel, Qt::DirectConnection);
428
    connect(this, &MainWindow::assetPanelWarning, m_assetPanel, &AssetPanel::assetPanelWarning);
Vincent Pinon's avatar
Vincent Pinon committed
429
    connect(m_assetPanel, &AssetPanel::seekToPos, this, [this](int pos) {
430
431
        ObjectId oId = m_assetPanel->effectStackOwner();
        switch (oId.first) {
Nicolas Carion's avatar
Nicolas Carion committed
432
433
434
        case ObjectType::TimelineTrack:
        case ObjectType::TimelineClip:
        case ObjectType::TimelineComposition:
435
        case ObjectType::Master:
436
        case ObjectType::TimelineMix:
437
            m_projectMonitor->requestSeek(pos);
Nicolas Carion's avatar
Nicolas Carion committed
438
439
440
441
442
443
444
            break;
        case ObjectType::BinClip:
            m_clipMonitor->requestSeek(pos);
            break;
        default:
            qDebug() << "ERROR unhandled object type";
            break;
445
446
        }
    });
447

448
449
    m_effectList2 = new EffectListWidget(this);
    connect(m_effectList2, &EffectListWidget::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
450
    connect(m_assetPanel, &AssetPanel::reloadEffect, m_effectList2, &EffectListWidget::reloadCustomEffect);
451
    m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList2);
Simon Eugster's avatar
Simon Eugster committed
452

453
454
    m_compositionList = new TransitionListWidget(this);
    m_compositionListDock = addDock(i18n("Compositions"), QStringLiteral("transition_list"), m_compositionList);
455

Simon Eugster's avatar
Simon Eugster committed
456
    // Add monitors here to keep them at the right of the window
457
458
    m_clipMonitorDock = addDock(i18n("Clip Monitor"), QStringLiteral("clip_monitor"), m_clipMonitor);
    m_projectMonitorDock = addDock(i18n("Project Monitor"), QStringLiteral("project_monitor"), m_projectMonitor);
Simon Eugster's avatar
Simon Eugster committed
459

460
    m_undoView = new QUndoView();
461
    m_undoView->setCleanIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
462
463
    m_undoView->setEmptyLabel(i18n("Clean"));
    m_undoView->setGroup(m_commandStack);
464
    m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
465

466
    // Color and icon theme stuff
Laurent Montel's avatar
Laurent Montel committed
467
    connect(m_commandStack, &QUndoGroup::cleanChanged, m_saveAction, &QAction::setDisabled);
468
    addAction(QStringLiteral("styles_menu"), stylesAction);
Simon Eugster's avatar
Simon Eugster committed
469

470
471
472
473
474
    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);
475

476
    m_mixerDock = addDock(i18n("Audio Mixer"), QStringLiteral("mixer"), pCore->mixer());
477
    QAction *showMixer = new QAction(QIcon::fromTheme(QStringLiteral("view-media-equalizer")), i18n("Audio Mixer"), this);
478
479
    showMixer->setCheckable(true);
    addAction(QStringLiteral("audiomixer_button"), showMixer);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
480
    connect(m_mixerDock, &QDockWidget::visibilityChanged, this, [&, showMixer](bool visible) {
481
        pCore->mixer()->connectMixer(visible);
482
483
        pCore->audioMixerVisible = visible;
        m_projectMonitor->displayAudioMonitor(m_projectMonitor->isActive());
484
485
        showMixer->setChecked(visible);
    });
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
486
    connect(showMixer, &QAction::triggered, this, [&]() {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
487
488
        if (m_mixerDock->isVisible() && !m_mixerDock->visibleRegion().isEmpty()) {
            m_mixerDock->close();
489
        } else {
490
491
            m_mixerDock->show();
            m_mixerDock->raise();
492
493
494
        }
    });

Simon Eugster's avatar
Simon Eugster committed
495
496
497
    // Close non-general docks for the initial layout
    // only show important ones
    m_undoViewDock->close();
498
    m_mixerDock->close();
Simon Eugster's avatar
Simon Eugster committed
499

500
    // Tabify Widgets
501
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
502
    tabifyDockWidget(m_compositionListDock, m_effectListDock);
503
    tabifyDockWidget(m_effectStackDock, pCore->bin()->clipPropertiesDock());
504
    bool firstRun = readOptions();
Simon Eugster's avatar
Simon Eugster committed
505

506
    // Build effects menu
507
    m_effectsMenu = new QMenu(i18n("Add Effect"), this);
508
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
509
    m_effectList2->reloadEffectMenu(m_effectsMenu, m_effectActions);
510

511
512
513
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
    m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());

Nicolas Carion's avatar
Nicolas Carion committed
514
    auto *scmanager = new ScopeManager(this);
515

516
    auto *titleBars = new DockTitleBarManager(this);
517
    connect(layoutManager, &LayoutManagement::updateTitleBars, titleBars, [&] () { titleBars->slotUpdateTitleBars(); });
518
    connect(layoutManager, &LayoutManagement::connectDocks, titleBars, &DockTitleBarManager::connectDocks);
519
520
    m_extraFactory = new KXMLGUIClient(this);
    buildDynamicActions();
521

522
    // Create Effect Basket (dropdown list of favorites)
523
524
    m_effectBasket = new EffectBasket(this);
    connect(m_effectBasket, &EffectBasket::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
525
    connect(m_effectList2, &EffectListWidget::reloadFavorites, m_effectBasket, &EffectBasket::slotReloadBasket);
Nicolas Carion's avatar
Nicolas Carion committed
526
    auto *widgetlist = new QWidgetAction(this);
527
    widgetlist->setDefaultWidget(m_effectBasket);
Nicolas Carion's avatar
Nicolas Carion committed
528
    // widgetlist->setText(i18n("Favorite Effects"));
529
    widgetlist->setToolTip(i18n("Favorite Effects"));
530
    widgetlist->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
Nicolas Carion's avatar
Nicolas Carion committed
531
    auto *menu = new QMenu(this);
532
533
    menu->addAction(widgetlist);

Nicolas Carion's avatar
Nicolas Carion committed
534
    auto *basketButton = new QToolButton(this);
535
    basketButton->setMenu(menu);
536
    basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
537
538
    basketButton->setDefaultAction(widgetlist);
    basketButton->setPopupMode(QToolButton::InstantPopup);
Nicolas Carion's avatar
Nicolas Carion committed
539
    // basketButton->setText(i18n("Favorite Effects"));
540
    basketButton->setToolTip(i18n("Favorite Effects"));
541
    basketButton->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
542

Nicolas Carion's avatar
Nicolas Carion committed
543
    auto *toolButtonAction = new QWidgetAction(this);
544
    toolButtonAction->setText(i18n("Favorite Effects"));
545
    toolButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
546
    toolButtonAction->setDefaultWidget(basketButton);
547
    addAction(QStringLiteral("favorite_effects"), toolButtonAction);
Laurent Montel's avatar
Laurent Montel committed
548
    connect(toolButtonAction, &QAction::triggered, basketButton, &QToolButton::showMenu);
549
    connect(m_effectBasket, &EffectBasket::activateAsset, menu, &QMenu::close);
550

551
    // Render button
552
    ProgressButton *timelineRender = new ProgressButton(i18n("Render…"), 100, this);
Nicolas Carion's avatar
Nicolas Carion committed
553
    auto *tlrMenu = new QMenu(this);
554
555
    timelineRender->setMenu(tlrMenu);
    connect(this, &MainWindow::setRenderProgress, timelineRender, &ProgressButton::setProgress);
Nicolas Carion's avatar
Nicolas Carion committed
556
    auto *renderButtonAction = new QWidgetAction(this);
557
    renderButtonAction->setText(i18n("Render Button"));
558
    renderButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("media-record")));
559
560
561
    renderButtonAction->setDefaultWidget(timelineRender);
    addAction(QStringLiteral("project_render_button"), renderButtonAction);

562
    // Timeline preview button
563
    ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
Nicolas Carion's avatar
Nicolas Carion committed
564
    auto *tlMenu = new QMenu(this);
565
    timelinePreview->setMenu(tlMenu);
566
    connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
Nicolas Carion's avatar
Nicolas Carion committed
567
    auto *previewButtonAction = new QWidgetAction(this);
568
    previewButtonAction->setText(i18n("Timeline Preview"));
569
    previewButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("preview-render-on")));
570
    previewButtonAction->setDefaultWidget(timelinePreview);
571
    addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
572

573
    setupGUI(KXmlGuiWindow::ToolBar | KXmlGuiWindow::StatusBar | KXmlGuiWindow::Save | KXmlGuiWindow::Create);
574
    LocaleHandling::resetLocale();
575
    if (firstRun) {
576
        if (QScreen *current = QApplication::primaryScreen()) {
577
578
            int screenHeight = current->availableSize().height();
            if (screenHeight < 1000) {
579
                resize(current->availableSize());
580
            } else if (screenHeight < 2000) {
581
                resize(current->availableSize() / 1.2);
582
            } else {
583
                resize(current->availableSize() / 1.6);
584
585
586
            }
        }
    }
587

588
589
    m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
    m_timelineToolBar->setProperty("otherToolbar", true);
590
591
    timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
    connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
592

593
    timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
594
595
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
596

597
    loadGenerators();
598
    loadDockActions();
599
    loadClipActions();
600

601
    // Timeline clip menu
602
    auto *timelineClipMenu = new QMenu(this);
603
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("edit_copy")));
604
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("paste_effects")));
605
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_effects")));
606
607
608
609
610
611
612
613
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("group_clip")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("ungroup_clip")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("edit_item_duration")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_split")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_switch")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("extract_clip")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("save_to_bin")));
614
615

    QMenu *markerMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("marker_menu"), this));
616
    timelineClipMenu->addMenu(markerMenu);
617

618
619
620
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("set_audio_align_ref")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("align_audio")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("edit_item_speed")));
621
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("edit_item_remap")));
622
623
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_in_project_tree")));
    timelineClipMenu->addAction(actionCollection()->action(QStringLiteral("cut_timeline_clip")));
624

625
    // Timeline composition menu
626
    auto *compositionMenu = new QMenu(this);
627
628
629
630
    compositionMenu->addAction(actionCollection()->action(QStringLiteral("edit_item_duration")));
    compositionMenu->addAction(actionCollection()->action(QStringLiteral("edit_copy")));
    compositionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));

631
    // Timeline main menu
632
    auto *timelineMenu = new QMenu(this);
633
634
635
636
637
638
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("edit_paste")));
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("delete_space_all_tracks")));
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("add_guide")));
    timelineMenu->addAction(actionCollection()->action(QStringLiteral("edit_guide")));
639
    QMenu *guideMenu = new QMenu(i18n("Go to Guide…"), this);
640
641
    timelineMenu->addMenu(guideMenu);

642
    // Timeline ruler menu
643
    auto *timelineRulerMenu = new QMenu(this);
644
645
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_guide")));
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("edit_guide")));
646
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("lock_guides")));
647
    timelineRulerMenu->addMenu(guideMenu);
648
649
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("mark_in")));
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("mark_out")));
650
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_project_note")));
651
    timelineRulerMenu->addAction(actionCollection()->action(QStringLiteral("add_subtitle")));
652

Sashmita Raghav's avatar
Sashmita Raghav committed
653
    //Timeline subtitle menu
654
    auto *timelineSubtitleMenu = new QMenu(this);
655
    timelineSubtitleMenu->addAction(actionCollection()->action(QStringLiteral("edit_copy")));
Sashmita Raghav's avatar
Sashmita Raghav committed
656
657
    timelineSubtitleMenu->addAction(actionCollection()->action(QStringLiteral("delete_subtitle_clip")));

658
    // Timeline headers menu
659
    auto *timelineHeadersMenu = new QMenu(this);
660
661
    timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("insert_track")));
    timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("delete_track")));
662
    timelineHeadersMenu->addAction(actionCollection()->action(QStringLiteral("show_track_record")));
663

664
665
666
    QAction *separate_channels = new QAction(QIcon(), i18n("Separate Channels"), this);
    separate_channels->setCheckable(true);
    separate_channels->setChecked(KdenliveSettings::displayallchannels());
667
    separate_channels->setData("separate_channels");
668
669
    connect(separate_channels, &QAction::triggered, this, &MainWindow::slotSeparateAudioChannel);
    timelineHeadersMenu->addAction(separate_channels);
670
671
672
673
674
675
676
    
    QAction *normalize_channels = new QAction(QIcon(), i18n("Normalize Audio Thumbnails"), this);
    normalize_channels->setCheckable(true);
    normalize_channels->setChecked(KdenliveSettings::normalizechannels());
    normalize_channels->setData("normalize_channels");
    connect(normalize_channels, &QAction::triggered, this, &MainWindow::slotNormalizeAudioChannel);
    timelineHeadersMenu->addAction(normalize_channels);
677

678
    QMenu *thumbsMenu = new QMenu(i18n("Thumbnails"), this);
679
    auto *thumbGroup = new QActionGroup(this);
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
    QAction *inFrame = new QAction(i18n("In Frame"), thumbGroup);
    inFrame->setData(QStringLiteral("2"));
    inFrame->setCheckable(true);
    thumbsMenu->addAction(inFrame);
    QAction *inOutFrame = new QAction(i18n("In/Out Frames"), thumbGroup);
    inOutFrame->setData(QStringLiteral("0"));
    inOutFrame->setCheckable(true);
    thumbsMenu->addAction(inOutFrame);
    QAction *allFrame = new QAction(i18n("All Frames"), thumbGroup);
    allFrame->setData(QStringLiteral("1"));
    allFrame->setCheckable(true);
    thumbsMenu->addAction(allFrame);
    QAction *noFrame = new QAction(i18n("No Thumbnails"), thumbGroup);
    noFrame->setData(QStringLiteral("3"));
    noFrame->setCheckable(true);
    thumbsMenu->addAction(noFrame);
696

697
698
699
700
701
702
703
704
705
706
707
    QMenu *openGLMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("qt_opengl"), this