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          *
 ***************************************************************************/

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
20
#include "mainwindow.h"
21
#include "assets/assetpanel.hpp"
22
#include "bin/clipcreator.hpp"
23
#include "bin/generators/generators.h"
Nicolas Carion's avatar
Nicolas Carion committed
24
#include "bin/projectclip.h"
25
26
#include "bin/projectfolder.h"
#include "bin/projectitemmodel.h"
Nicolas Carion's avatar
Nicolas Carion committed
27
#include "core.h"
28
#include "dialogs/clipcreationdialog.h"
Nicolas Carion's avatar
Nicolas Carion committed
29
30
31
#include "dialogs/kdenlivesettingsdialog.h"
#include "dialogs/renderwidget.h"
#include "dialogs/wizard.h"
32
#include "doc/docundostack.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
33
#include "doc/kdenlivedoc.h"
34
#include "effects/effectlist/view/effectlistwidget.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
35
36
#include "effectslist/effectbasket.h"
#include "hidetitlebars.h"
37
38
#include "jobs/jobmanager.h"
#include "jobs/scenesplitjob.hpp"
39
#include "jobs/speedjob.hpp"
Nicolas Carion's avatar
Nicolas Carion committed
40
#include "jobs/stabilizejob.hpp"
41
#include "jobs/transcodeclipjob.h"
Nicolas Carion's avatar
Nicolas Carion committed
42
43
44
45
46
47
48
49
50
#include "kdenlivesettings.h"
#include "layoutmanagement.h"
#include "library/librarywidget.h"
#include "mainwindowadaptor.h"
#include "mltconnection.h"
#include "mltcontroller/clipcontroller.h"
#include "monitor/monitor.h"
#include "monitor/monitormanager.h"
#include "monitor/scopes/audiographspectrum.h"
Nicolas Carion's avatar
linting    
Nicolas Carion committed
51
#include "profiles/profilemodel.hpp"
Vincent Pinon's avatar
Vincent Pinon committed
52
#include "project/cliptranscode.h"
Vincent Pinon's avatar
Vincent Pinon committed
53
#include "project/dialogs/archivewidget.h"
Nicolas Carion's avatar
Nicolas Carion committed
54
55
#include "project/dialogs/projectsettings.h"
#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
64
65
#include "titler/titlewidget.h"
#include "transitions/transitionlist/view/transitionlistwidget.hpp"
#include "transitions/transitionsrepository.hpp"
#include "utils/resourcewidget.h"
#include "utils/thememanager.h"
66

67
#include "profiles/profilerepository.hpp"
68
69
#include "widgets/progressbutton.h"
#include <config-kdenlive.h>
70

71
#include "project/dialogs/temporarydata.h"
72

73
74
75
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
76

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

Laurent Montel's avatar
Laurent Montel committed
99
#include "kdenlive_debug.h"
Nicolas Carion's avatar
Nicolas Carion committed
100
101
102
103
#include <QAction>
#include <QFileDialog>
#include <QMenu>
#include <QMenuBar>
104
#include <QStatusBar>
Nicolas Carion's avatar
Nicolas Carion committed
105
#include <QStyleFactory>
106
#include <QTemporaryFile>
107
#include <QUndoGroup>
108

Vincent Pinon's avatar
Vincent Pinon committed
109
110
111
#include <KConfigGroup>
#include <QDialogButtonBox>
#include <QPushButton>
112
#include <QScreen>
Nicolas Carion's avatar
Nicolas Carion committed
113
114
#include <QStandardPaths>
#include <QVBoxLayout>
115

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

Laurent Montel's avatar
Laurent Montel committed
121
122
QMap<QString, QImage> MainWindow::m_lumacache;
QMap<QString, QStringList> MainWindow::m_lumaFiles;
123

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

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

Nicolas Carion's avatar
Nicolas Carion committed
138
MainWindow::MainWindow(QWidget *parent)
139
    : KXmlGuiWindow(parent)
Nicolas Carion's avatar
Nicolas Carion committed
140

141
{
142
143
}

144
void MainWindow::init()
145
{
146
    QString desktopStyle = QApplication::style()->objectName();
147
148
149
150
151
    // Load themes
    auto themeManager = new ThemeManager(actionCollection());
    actionCollection()->addAction(QStringLiteral("themes_menu"), themeManager);
    connect(themeManager, &ThemeManager::themeChanged, this, &MainWindow::slotThemeChanged);

152
153
154
155
    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
156

157
    // Widget themes for non KDE users
Laurent Montel's avatar
Laurent Montel committed
158
    KActionMenu *stylesAction = new KActionMenu(i18n("Style"), this);
Nicolas Carion's avatar
Nicolas Carion committed
159
    auto *stylesGroup = new QActionGroup(stylesAction);
160

161
162
    // GTK theme does not work well with Kdenlive, and does not support color theming, so avoid it
    QStringList availableStyles = QStyleFactory::keys();
163
164
    if (KdenliveSettings::widgetstyle().isEmpty()) {
        // First run
165
        QStringList incompatibleStyles = {QStringLiteral("GTK+"), QStringLiteral("windowsvista"), QStringLiteral("Windows")};
166

167
        if (incompatibleStyles.contains(desktopStyle, Qt::CaseInsensitive)) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
168
            if (availableStyles.contains(QStringLiteral("breeze"), Qt::CaseInsensitive)) {
169
170
                // Auto switch to Breeze theme
                KdenliveSettings::setWidgetstyle(QStringLiteral("Breeze"));
171
		QApplication::setStyle(QStyleFactory::create(QStringLiteral("Breeze")));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
172
            } else if (availableStyles.contains(QStringLiteral("fusion"), Qt::CaseInsensitive)) {
173
                KdenliveSettings::setWidgetstyle(QStringLiteral("Fusion"));
174
		QApplication::setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
175
            }
176
177
        } else {
            KdenliveSettings::setWidgetstyle(QStringLiteral("Default"));
178
179
180
        }
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
181
182
    // Add default style action
    QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
183
    defaultStyle->setData(QStringLiteral("Default"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
184
185
    defaultStyle->setCheckable(true);
    stylesAction->addAction(defaultStyle);
186
    if (KdenliveSettings::widgetstyle() == QLatin1String("Default") || KdenliveSettings::widgetstyle().isEmpty()) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
187
188
189
        defaultStyle->setChecked(true);
    }

Nicolas Carion's avatar
Nicolas Carion committed
190
    for (const QString &style : availableStyles) {
Nicolas Carion's avatar
Nicolas Carion committed
191
        auto *a = new QAction(style, stylesGroup);
192
193
        a->setCheckable(true);
        a->setData(style);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
194
195
196
        if (KdenliveSettings::widgetstyle() == style) {
            a->setChecked(true);
        }
197
198
199
        stylesAction->addAction(a);
    }
    connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);
Nicolas Carion's avatar
Nicolas Carion committed
200
    // QIcon::setThemeSearchPaths(QStringList() <<QStringLiteral(":/icons/"));
201
202

    new RenderingAdaptor(this);
203
    QString defaultProfile = KdenliveSettings::default_profile();
204
    pCore->setCurrentProfile(defaultProfile.isEmpty() ? ProjectManager::getDefaultProjectFormat() : defaultProfile);
205
    m_commandStack = new QUndoGroup();
206
207

    // If using a custom profile, make sure the file exists or fallback to default
208
209
    QString currentProfilePath = pCore->getCurrentProfile()->path();
    if (currentProfilePath.startsWith(QLatin1Char('/')) && !QFile::exists(currentProfilePath)) {
210
        KMessageBox::sorry(this, i18n("Cannot find your default profile, switching to ATSC 1080p 25"));
211
        pCore->setCurrentProfile(QStringLiteral("atsc_1080p_25"));
Laurent Montel's avatar
Laurent Montel committed
212
        KdenliveSettings::setDefault_profile(QStringLiteral("atsc_1080p_25"));
213
    }
214

215
    m_gpuAllowed = EffectsRepository::get()->hasInternalEffect(QStringLiteral("glsl.manager"));
216

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

220
    /// Add Widgets
221
    setDockOptions(dockOptions() | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
222
    setDockOptions(dockOptions() | QMainWindow::GroupedDragging);
Nicolas Carion's avatar
Nicolas Carion committed
223
    setTabPosition(Qt::AllDockWidgetAreas, (QTabWidget::TabPosition)KdenliveSettings::tabposition());
224
    m_timelineToolBar = toolBar(QStringLiteral("timelineToolBar"));
225
    m_timelineToolBarContainer = new QWidget(this);
Nicolas Carion's avatar
Nicolas Carion committed
226
    auto *ctnLay = new QVBoxLayout;
227
228
    ctnLay->setSpacing(0);
    ctnLay->setContentsMargins(0, 0, 0, 0);
229
230
    m_timelineToolBarContainer->setLayout(ctnLay);
    ctnLay->addWidget(m_timelineToolBar);
231
232
233
234
    KSharedConfigPtr config = KSharedConfig::openConfig();
    KConfigGroup mainConfig(config, QStringLiteral("MainWindow"));
    KConfigGroup tbGroup(&mainConfig, QStringLiteral("Toolbar timelineToolBar"));
    m_timelineToolBar->applySettings(tbGroup);
235
236
237
238
239
    QFrame *fr = new QFrame(this);
    fr->setFrameShape(QFrame::HLine);
    fr->setMaximumHeight(1);
    fr->setLineWidth(1);
    ctnLay->addWidget(fr);
240
    setCentralWidget(m_timelineToolBarContainer);
241
    setupActions();
242

Laurent Montel's avatar
Laurent Montel committed
243
    QDockWidget *libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
Simon Eugster's avatar
Simon Eugster committed
244

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
245
    m_clipMonitor = new Monitor(Kdenlive::ClipMonitor, pCore->monitorManager(), this);
246
    pCore->bin()->setMonitor(m_clipMonitor);
247
248
    connect(m_clipMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
    connect(m_clipMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteClipMarker);
249
250
    connect(m_clipMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_clipMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
251

Laurent Montel's avatar
Laurent Montel committed
252
    connect(pCore->bin(), &Bin::findInTimeline, this, &MainWindow::slotClipInTimeline);
253

Nicolas Carion's avatar
Nicolas Carion committed
254
    // TODO deprecated, replace with Bin methods if necessary
255
    /*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
256
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
Laurent Montel's avatar
Laurent Montel committed
257
    connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
258
    connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
259
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
260
261
262
263

    // TODO refac : reimplement ?
    // connect(m_clipMonitor, &Monitor::extractZone, pCore->bin(), &Bin::slotStartCutJob);

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

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
266
    m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
Laurent Montel's avatar
Laurent Montel committed
267
    connect(m_projectMonitor, &Monitor::passKeyPress, this, &MainWindow::triggerKey);
268
    connect(m_projectMonitor, &Monitor::addMarker, this, &MainWindow::slotAddMarkerGuideQuickly);
269
    connect(m_projectMonitor, &Monitor::deleteMarker, this, &MainWindow::slotDeleteGuide);
270
271
    connect(m_projectMonitor, &Monitor::seekToPreviousSnap, this, &MainWindow::slotSnapRewind);
    connect(m_projectMonitor, &Monitor::seekToNextSnap, this, &MainWindow::slotSnapForward);
272
    connect(m_loopClip, &QAction::triggered, m_projectMonitor, &Monitor::slotLoopClip);
273

274
    pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor);
275
    connect(m_clipMonitor, &Monitor::addMasterEffect, pCore->bin(), &Bin::slotAddEffect);
276

277
278
    m_timelineTabs = new TimelineTabs(this);
    ctnLay->addWidget(m_timelineTabs);
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    
    // Screen grab widget
    QWidget *grabWidget = new QWidget(this);
    QVBoxLayout *grabLayout = new QVBoxLayout;
    grabWidget->setLayout(grabLayout);
    QToolBar *recToolbar = new QToolBar(grabWidget);
    grabLayout->addWidget(recToolbar);
    grabLayout->addStretch(10);
    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, [&]() {
        pCore->showConfigDialog(4, 0);
    });
    QDockWidget *screenGrabDock = addDock(i18n("Screen Grab"), QStringLiteral("screengrab"), grabWidget);
296

297
298
    // Audio spectrum scope
    m_audioSpectrum = new AudioGraphSpectrum(pCore->monitorManager());
Laurent Montel's avatar
Laurent Montel committed
299
    QDockWidget *spectrumDock = addDock(i18n("Audio Spectrum"), QStringLiteral("audiospectrum"), m_audioSpectrum);
300
    // Close library and audiospectrum on first run
301
    screenGrabDock->close();
302
303
304
    libraryDock->close();
    spectrumDock->close();

305
    m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
306

307
    m_assetPanel = new AssetPanel(this);
308
    m_effectStackDock = addDock(i18n("Properties"), QStringLiteral("effect_stack"), m_assetPanel);
309
310
    connect(m_assetPanel, &AssetPanel::doSplitEffect, m_projectMonitor, &Monitor::slotSwitchCompare);
    connect(m_assetPanel, &AssetPanel::doSplitBinEffect, m_clipMonitor, &Monitor::slotSwitchCompare);
311
    connect(m_timelineTabs, &TimelineTabs::showTransitionModel, m_assetPanel, &AssetPanel::showTransition);
312
313
314
    connect(m_timelineTabs, &TimelineTabs::showTransitionModel, [&] () {
        m_effectStackDock->raise();
    });
315
    connect(m_timelineTabs, &TimelineTabs::showItemEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
316
317
318
319
    connect(m_timelineTabs, &TimelineTabs::showItemEffectStack, [&] () {
        m_effectStackDock->raise();
    });

320
    connect(m_timelineTabs, &TimelineTabs::updateZoom, this, &MainWindow::updateZoomSlider);
321
    connect(pCore->bin(), &Bin::requestShowEffectStack, m_assetPanel, &AssetPanel::showEffectStack);
322
    connect(pCore->bin(), &Bin::requestShowEffectStack, [&] () {
323
324
        // Don't raise effect stack on clip bin in case it is docked with bin or clip monitor
        // m_effectStackDock->raise();
325
    });
326
    connect(this, &MainWindow::clearAssetPanel, m_assetPanel, &AssetPanel::clearAssetPanel);
327
328
329
    connect(m_assetPanel, &AssetPanel::seekToPos, [this](int pos) {
        ObjectId oId = m_assetPanel->effectStackOwner();
        switch (oId.first) {
Nicolas Carion's avatar
Nicolas Carion committed
330
331
332
333
334
335
336
337
338
339
340
        case ObjectType::TimelineTrack:
        case ObjectType::TimelineClip:
        case ObjectType::TimelineComposition:
            getCurrentTimeline()->controller()->setPosition(pos);
            break;
        case ObjectType::BinClip:
            m_clipMonitor->requestSeek(pos);
            break;
        default:
            qDebug() << "ERROR unhandled object type";
            break;
341
342
        }
    });
343

344
345
    m_effectList2 = new EffectListWidget(this);
    connect(m_effectList2, &EffectListWidget::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
346
    connect(m_assetPanel, &AssetPanel::reloadEffect, m_effectList2, &EffectListWidget::reloadCustomEffect);
347
    m_effectListDock = addDock(i18n("Effects"), QStringLiteral("effect_list"), m_effectList2);
Simon Eugster's avatar
Simon Eugster committed
348

349
    m_transitionList2 = new TransitionListWidget(this);
350
    m_transitionListDock = addDock(i18n("Compositions"), QStringLiteral("transition_list"), m_transitionList2);
351

Simon Eugster's avatar
Simon Eugster committed
352
    // Add monitors here to keep them at the right of the window
353
354
    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
355

356
    m_undoView = new QUndoView();
357
    m_undoView->setCleanIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
358
359
    m_undoView->setEmptyLabel(i18n("Clean"));
    m_undoView->setGroup(m_commandStack);
360
    m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
361

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

366
367
368
369
370
371
    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
372
373
374
375
    // Close non-general docks for the initial layout
    // only show important ones
    m_undoViewDock->close();

376
    /// Tabify Widgets
377
    tabifyDockWidget(m_transitionListDock, m_effectListDock);
378
    tabifyDockWidget(m_effectStackDock, pCore->bin()->clipPropertiesDock());
Nicolas Carion's avatar
Nicolas Carion committed
379
    // tabifyDockWidget(m_effectListDock, m_effectStackDock);
380

381
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
382
    bool firstRun = readOptions();
Simon Eugster's avatar
Simon Eugster committed
383

384
    // Build effects menu
385
    m_effectsMenu = new QMenu(i18n("Add Effect"), this);
386
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
387
    m_effectList2->reloadEffectMenu(m_effectsMenu, m_effectActions);
388

389
390
391
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
    m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());

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

Till Theato's avatar
Till Theato committed
394
    new LayoutManagement(this);
395
    new HideTitleBars(this);
396
397
    m_extraFactory = new KXMLGUIClient(this);
    buildDynamicActions();
398

399
    // Create Effect Basket (dropdown list of favorites)
400
401
    m_effectBasket = new EffectBasket(this);
    connect(m_effectBasket, &EffectBasket::activateAsset, pCore->projectManager(), &ProjectManager::activateAsset);
402
    connect(m_effectList2, &EffectListWidget::reloadFavorites, m_effectBasket, &EffectBasket::slotReloadBasket);
Nicolas Carion's avatar
Nicolas Carion committed
403
    auto *widgetlist = new QWidgetAction(this);
404
    widgetlist->setDefaultWidget(m_effectBasket);
Nicolas Carion's avatar
Nicolas Carion committed
405
    // widgetlist->setText(i18n("Favorite Effects"));
406
    widgetlist->setToolTip(i18n("Favorite Effects"));
407
    widgetlist->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
Nicolas Carion's avatar
Nicolas Carion committed
408
    auto *menu = new QMenu(this);
409
410
    menu->addAction(widgetlist);

Nicolas Carion's avatar
Nicolas Carion committed
411
    auto *basketButton = new QToolButton(this);
412
    basketButton->setMenu(menu);
413
    basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
414
415
    basketButton->setDefaultAction(widgetlist);
    basketButton->setPopupMode(QToolButton::InstantPopup);
Nicolas Carion's avatar
Nicolas Carion committed
416
    // basketButton->setText(i18n("Favorite Effects"));
417
    basketButton->setToolTip(i18n("Favorite Effects"));
418
    basketButton->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
419

Nicolas Carion's avatar
Nicolas Carion committed
420
    auto *toolButtonAction = new QWidgetAction(this);
421
    toolButtonAction->setText(i18n("Favorite Effects"));
422
    toolButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("favorite")));
423
    toolButtonAction->setDefaultWidget(basketButton);
424
    addAction(QStringLiteral("favorite_effects"), toolButtonAction);
Laurent Montel's avatar
Laurent Montel committed
425
    connect(toolButtonAction, &QAction::triggered, basketButton, &QToolButton::showMenu);
426

427
428
    // Render button
    ProgressButton *timelineRender = new ProgressButton(i18n("Render"), 100, this);
Nicolas Carion's avatar
Nicolas Carion committed
429
    auto *tlrMenu = new QMenu(this);
430
431
    timelineRender->setMenu(tlrMenu);
    connect(this, &MainWindow::setRenderProgress, timelineRender, &ProgressButton::setProgress);
Nicolas Carion's avatar
Nicolas Carion committed
432
    auto *renderButtonAction = new QWidgetAction(this);
433
    renderButtonAction->setText(i18n("Render Button"));
434
    renderButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("media-record")));
435
436
437
    renderButtonAction->setDefaultWidget(timelineRender);
    addAction(QStringLiteral("project_render_button"), renderButtonAction);

438
    // Timeline preview button
439
    ProgressButton *timelinePreview = new ProgressButton(i18n("Rendering preview"), 1000, this);
Nicolas Carion's avatar
Nicolas Carion committed
440
    auto *tlMenu = new QMenu(this);
441
    timelinePreview->setMenu(tlMenu);
442
    connect(this, &MainWindow::setPreviewProgress, timelinePreview, &ProgressButton::setProgress);
Nicolas Carion's avatar
Nicolas Carion committed
443
    auto *previewButtonAction = new QWidgetAction(this);
444
    previewButtonAction->setText(i18n("Timeline Preview"));
445
    previewButtonAction->setIcon(QIcon::fromTheme(QStringLiteral("preview-render-on")));
446
    previewButtonAction->setDefaultWidget(timelinePreview);
447
    addAction(QStringLiteral("timeline_preview_button"), previewButtonAction);
448

449
    setupGUI(KXmlGuiWindow::ToolBar | KXmlGuiWindow::StatusBar | KXmlGuiWindow::Save | KXmlGuiWindow::Create);
450
    if (firstRun) {
451
        if (QScreen *current = QApplication::primaryScreen()) {
452
453
454
455
456
457
458
            if (current->availableSize().height() < 1000) {
                resize(current->availableSize());
            } else {
                resize(current->availableSize() / 1.5);
            }
        }
    }
459
460
461
    updateActionsToolTip();
    m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonFollowStyle);
    m_timelineToolBar->setProperty("otherToolbar", true);
462
463
    timelinePreview->setToolButtonStyle(m_timelineToolBar->toolButtonStyle());
    connect(m_timelineToolBar, &QToolBar::toolButtonStyleChanged, timelinePreview, &ProgressButton::setToolButtonStyle);
464

465
    timelineRender->setToolButtonStyle(toolBar()->toolButtonStyle());
466
467
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
468

469
    loadGenerators();
470
    loadDockActions();
471
    loadClipActions();
472
473

    // Connect monitor overlay info menu.
Laurent Montel's avatar
Laurent Montel committed
474
    QMenu *monitorOverlay = static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_config_overlay"), this));
Laurent Montel's avatar
Laurent Montel committed
475
    connect(monitorOverlay, &QMenu::triggered, this, &MainWindow::slotSwitchMonitorOverlay);
476

Nicolas Carion's avatar
Nicolas Carion committed
477
478
479
480
    m_projectMonitor->setupMenu(static_cast<QMenu *>(factory()->container(QStringLiteral("monitor_go"), this)), monitorOverlay, m_playZone, m_loopZone, nullptr,
                                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)));
481

Laurent Montel's avatar
Laurent Montel committed
482
    QMenu *clipInTimeline = static_cast<QMenu *>(factory()->container(QStringLiteral("clip_in_timeline"), this));
483
    clipInTimeline->setIcon(QIcon::fromTheme(QStringLiteral("go-jump")));
484
    pCore->bin()->setupGeneratorMenu();
Marco Gittler's avatar
Marco Gittler committed
485

Laurent Montel's avatar
Laurent Montel committed
486
    connect(pCore->monitorManager(), &MonitorManager::updateOverlayInfos, this, &MainWindow::slotUpdateMonitorOverlays);
487

488
    // Setup and fill effects and transitions menus.
Laurent Montel's avatar
Laurent Montel committed
489
    QMenu *m = static_cast<QMenu *>(factory()->container(QStringLiteral("video_effects_menu"), this));
490
491
    connect(m, &QMenu::triggered, this, &MainWindow::slotAddEffect);
    connect(m_effectsMenu, &QMenu::triggered, this, &MainWindow::slotAddEffect);
Laurent Montel's avatar
Laurent Montel committed
492
    connect(m_transitionsMenu, &QMenu::triggered, this, &MainWindow::slotAddTransition);
493

494
495
    m_timelineContextMenu = new QMenu(this);

496
497
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
498
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space_all_tracks")));
499
500
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

501
    // QMenu *markersMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("marker_menu"), this));
502
503
504
505
506

    /*m_timelineClipActions->addMenu(markersMenu);
    m_timelineClipActions->addSeparator();
    m_timelineClipActions->addMenu(m_transitionsMenu);
    m_timelineClipActions->addMenu(m_effectsMenu);*/
Marco Gittler's avatar
Marco Gittler committed
507

508
    slotConnectMonitors();
509

510
    m_timelineToolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
511
    // TODO: let user select timeline toolbar toolbutton style
Nicolas Carion's avatar
Nicolas Carion committed
512
    // connect(toolBar(), &QToolBar::iconSizeChanged, m_timelineToolBar, &QToolBar::setToolButtonStyle);
513
514
    m_timelineToolBar->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_timelineToolBar, &QWidget::customContextMenuRequested, this, &MainWindow::showTimelineToolbarMenu);
515
516

    QAction *prevRender = actionCollection()->action(QStringLiteral("prerender_timeline_zone"));
517
518
    QAction *stopPrevRender = actionCollection()->action(QStringLiteral("stop_prerender_timeline"));
    tlMenu->addAction(stopPrevRender);
519
520
    tlMenu->addAction(actionCollection()->action(QStringLiteral("set_render_timeline_zone")));
    tlMenu->addAction(actionCollection()->action(QStringLiteral("unset_render_timeline_zone")));
521
    tlMenu->addAction(actionCollection()->action(QStringLiteral("clear_render_timeline_zone")));
522
523

    // Automatic timeline preview action
524
    QAction *autoRender = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18n("Automatic Preview"), this);
525
526
527
528
    autoRender->setCheckable(true);
    autoRender->setChecked(KdenliveSettings::autopreview());
    connect(autoRender, &QAction::triggered, this, &MainWindow::slotToggleAutoPreview);
    tlMenu->addAction(autoRender);
529
    tlMenu->addSeparator();
530
    tlMenu->addAction(actionCollection()->action(QStringLiteral("disable_preview")));
531
    tlMenu->addAction(actionCollection()->action(QStringLiteral("manage_cache")));
532
    timelinePreview->defineDefaultAction(prevRender, stopPrevRender);
533
    timelinePreview->setAutoRaise(true);
534

535
536
    QAction *showRender = actionCollection()->action(QStringLiteral("project_render"));
    tlrMenu->addAction(showRender);
537
    tlrMenu->addAction(actionCollection()->action(QStringLiteral("stop_project_render")));
538
    timelineRender->defineDefaultAction(showRender, showRender);
539
540
    timelineRender->setAutoRaise(true);

541
    // Populate encoding profiles
542
    KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
543
    /*KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
544
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
545
        KConfigGroup group(&conf, "proxy");
Nicolas Carion's avatar
Nicolas Carion committed
546
        QMap<QString, QString> values = group.entryMap();
547
548
549
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
550
551
552
            QString proxystring = i.value();
            KdenliveSettings::setProxyparams(proxystring.section(QLatin1Char(';'), 0, 0));
            KdenliveSettings::setProxyextension(proxystring.section(QLatin1Char(';'), 1, 1));
553
        }
554
    }*/
555
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
556
        KConfigGroup group(&conf, "video4linux");
Nicolas Carion's avatar
Nicolas Carion committed
557
        QMap<QString, QString> values = group.entryMap();
558
559
560
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
561
562
563
            QString v4lstring = i.value();
            KdenliveSettings::setV4l_parameters(v4lstring.section(QLatin1Char(';'), 0, 0));
            KdenliveSettings::setV4l_extension(v4lstring.section(QLatin1Char(';'), 1, 1));
564
565
        }
    }
566
567
    if (KdenliveSettings::grab_parameters().isEmpty() || KdenliveSettings::grab_extension().isEmpty()) {
        KConfigGroup group(&conf, "screengrab");
Nicolas Carion's avatar
Nicolas Carion committed
568
        QMap<QString, QString> values = group.entryMap();
569
570
571
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
572
573
574
            QString grabstring = i.value();
            KdenliveSettings::setGrab_parameters(grabstring.section(QLatin1Char(';'), 0, 0));
            KdenliveSettings::setGrab_extension(grabstring.section(QLatin1Char(';'), 1, 1));
575
576
        }
    }
577
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
578
        KConfigGroup group(&conf, "decklink");
Nicolas Carion's avatar
Nicolas Carion committed
579
        QMap<QString, QString> values = group.entryMap();
580
581
582
        QMapIterator<QString, QString> i(values);
        if (i.hasNext()) {
            i.next();
583
584
585
            QString decklinkstring = i.value();
            KdenliveSettings::setDecklink_parameters(decklinkstring.section(QLatin1Char(';'), 0, 0));
            KdenliveSettings::setDecklink_extension(decklinkstring.section(QLatin1Char(';'), 1, 1));
586
587
        }
    }
Vincent Pinon's avatar
Vincent Pinon committed
588
589
590
    if (!QDir(KdenliveSettings::currenttmpfolder()).isReadable())
        KdenliveSettings::setCurrenttmpfolder(QStandardPaths::writableLocation(QStandardPaths::TempLocation));

Laurent Montel's avatar
Laurent Montel committed
591
    QTimer::singleShot(0, this, &MainWindow::GUISetupDone);
592

593
594
595
#ifdef USE_JOGSHUTTLE
    new JogManager(this);
#endif
596
    scmanager->slotCheckActiveScopes();
Nicolas Carion's avatar
format    
Nicolas Carion committed
597
    // m_messageLabel->setMessage(QStringLiteral("This is a beta version. Always backup your data"), MltError);
598
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
599

600
void MainWindow::slotThemeChanged(const QString &name)
601
{
602
    KSharedConfigPtr config = KSharedConfig::openConfig(name);
603
    QPalette plt = KColorScheme::createApplicationPalette(config);
604
    // qApp->setPalette(plt);
605
    // Required for qml palette change
606
    QGuiApplication::setPalette(plt);
607
608
609
610

    QColor background = plt.window().color();
    bool useDarkIcons = background.value() < 100;

611
612
    if (m_assetPanel) {
        m_assetPanel->updatePalette();
613
    }
614
615
616
617
618
619
620
621
    if (m_effectList2) {
        // Trigger a repaint to have icons adapted
        m_effectList2->reset();
    }
    if (m_transitionList2) {
        // Trigger a repaint to have icons adapted
        m_transitionList2->reset();
    }
Laurent Montel's avatar
Laurent Montel committed
622
623
624
625
626
627
    if (m_clipMonitor) {
        m_clipMonitor->setPalette(plt);
    }
    if (m_projectMonitor) {
        m_projectMonitor->setPalette(plt);
    }
628
629
    if (m_timelineTabs) {
        m_timelineTabs->setPalette(plt);
630
        getMainTimeline()->controller()->resetView();
631
632
633
    }
    if (m_audioSpectrum) {
        m_audioSpectrum->refreshPixmap();
634
    }
635

636
637
638
639
640
641
642
643
    KSharedConfigPtr kconfig = KSharedConfig::openConfig();
    KConfigGroup initialGroup(kconfig, "version");
    if (initialGroup.exists() && KdenliveSettings::force_breeze() && useDarkIcons != KdenliveSettings::use_dark_breeze()) {
        // We need to reload icon theme
        QIcon::setThemeName(useDarkIcons ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
        KdenliveSettings::setUse_dark_breeze(useDarkIcons);
    }

Vincent Pinon's avatar
Vincent Pinon committed
644
#if (KXMLGUI_VERSION < QT_VERSION_CHECK(5, 23, 0)) || defined(Q_OS_WIN)
645
    // Not required anymore with auto colored icons since KF5 5.23
646
    if (m_themeInitialized && useDarkIcons != m_isDarkTheme) {
Vincent Pinon's avatar
Vincent Pinon committed
647
        QIcon::setThemeName(useDarkIcons ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"));
648
649
650
        if (pCore->bin()) {
            pCore->bin()->refreshIcons();
        }
Laurent Montel's avatar
Laurent Montel committed
651
652
653
654
655
656
657
658
659
        if (m_clipMonitor) {
            m_clipMonitor->refreshIcons();
        }
        if (m_projectMonitor) {
            m_projectMonitor->refreshIcons();
        }
        if (pCore->monitorManager()) {
            pCore->monitorManager()->refreshIcons();
        }
660

Nicolas Carion's avatar
Nicolas Carion committed
661
        for (QAction *action : actionCollection()->actions()) {
662
            QIcon icon = action->icon();
Laurent Montel's avatar
Laurent Montel committed
663
664
665
            if (icon.isNull()) {
                continue;
            }
666
            QString iconName = icon.name();
667
            QIcon newIcon = QIcon::fromTheme(iconName);
Laurent Montel's avatar
Laurent Montel committed
668
669
670
            if (newIcon.isNull()) {
                continue;
            }
671
672
673
674
675
            action->setIcon(newIcon);
        }
    }
    m_themeInitialized = true;
    m_isDarkTheme = useDarkIcons;
676
#endif
677
678
}

679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
void MainWindow::updateActionsToolTip()
{
    // Add shortcut to action tooltips
    QList<KActionCollection *> collections = KActionCollection::allCollections();
    for (int i = 0; i < collections.count(); ++i) {
        KActionCollection *coll = collections.at(i);
        for (QAction *tempAction : coll->actions()) {
            // find the shortcut pattern and delete (note the preceding space in the RegEx)
            QString strippedTooltip = tempAction->toolTip().remove(QRegExp(QStringLiteral("\\s\\(.*\\)")));
            // append shortcut if it exists for action
            if (tempAction->shortcut() == QKeySequence(0)) {
                tempAction->setToolTip(strippedTooltip);
            } else {
                tempAction->setToolTip(strippedTooltip + QStringLiteral(" (") + tempAction->shortcut().toString() + QLatin1Char(')'));
            }
            connect(tempAction, &QAction::changed, this, &MainWindow::updateAction);
        }
    }
}

void MainWindow::updateAction()
{
Nicolas Carion's avatar
Nicolas Carion committed
701
    auto *action = qobject_cast<QAction *>(sender());
702
703
704
705
706
    QString toolTip = KLocalizedString::removeAcceleratorMarker(action->toolTip());
    QString strippedTooltip = toolTip.remove(QRegExp(QStringLiteral("\\s\\(.*\\)")));
    action->setToolTip(i18nc("@info:tooltip Tooltip of toolbar button", "%1 (%2)", strippedTooltip, action->shortcut().toString()));
}

707
MainWindow::~MainWindow()
708
{
709
    pCore->prepareShutdown();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
710
    m_timelineTabs->disconnectTimeline(getMainTimeline());
711
    delete m_audioSpectrum;
Laurent Montel's avatar
Laurent Montel committed
712
713
714
715
716
717
    if (m_projectMonitor) {
        m_projectMonitor->stop();
    }
    if (m_clipMonitor) {
        m_clipMonitor->stop();
    }
718
    ClipController::mediaUnavailable.reset();
719
720
721
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
722
723
    delete m_effectList2;
    delete m_transitionList2;
724
    qDeleteAll(m_transitions);
Nicolas Carion's avatar
linting    
Nicolas Carion committed
725
    // Mlt::Factory::close();
726
727
}

Nicolas Carion's avatar
Nicolas Carion committed
728
// virtual
729
730
731
732
733
bool MainWindow::queryClose()
{
    if (m_renderWidget) {
        int waitingJobs = m_renderWidget->waitingJobsCount();
        if (waitingJobs > 0) {
Nicolas Carion's avatar
Nicolas Carion committed
734
735
736
737
738
            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")))) {
Nicolas Carion's avatar
Nicolas Carion committed
739
            case KMessageBox::Yes:
740
                // create script with waiting jobs and start it
Nicolas Carion's avatar
Nicolas Carion committed
741
                if (!m_renderWidget->startWaitingRenderJobs()) {
Laurent Montel's avatar
Laurent Montel committed
742
743
                    return false;
                }
744
                break;
Nicolas Carion's avatar
Nicolas Carion committed
745
            case KMessageBox::No:
746
747
                // Don't do anything, jobs will be deleted
                break;
Laurent Montel's avatar
Laurent Montel committed
748
            default:
749
750
751
752
753
754
755
756
                return false;
            }
        }
    }
    saveOptions();

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

759
760
void MainWindow::loadGenerators()
{
Laurent Montel's avatar
Laurent Montel committed
761
    QMenu *addMenu = static_cast<QMenu *>(factory()->container(QStringLiteral("generators"), this));
762
    Generators::getGenerators(KdenliveSettings::producerslist(), addMenu);
Laurent Montel's avatar
Laurent Montel committed
763
    connect(addMenu, &QMenu::triggered, this, &MainWindow::buildGenerator);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
764
765
}

766
void MainWindow::buildGenerator(QAction *action)
767
{
768
769
770
771
    Generators gen(m_clipMonitor, action->data().toString(), this);
    if (gen.exec() == QDialog::Accepted) {
        pCore->bin()->slotAddClipToProject(gen.getSavedClip());
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
772
773
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
774
void MainWindow::saveProperties(KConfigGroup &config)
775
{
776
    // save properties here
777
    KXmlGuiWindow::saveProperties(config);
Nicolas Carion's avatar
Nicolas Carion committed
778
    // TODO: fix session management
779
    if (qApp->isSavingSession() && pCore->projectManager()) {
780
781
        if (pCore->currentDoc() && !pCore->currentDoc()->url().isEmpty()) {
            config.writeEntry("kdenlive_lastUrl", pCore->currentDoc()->url().toLocalFile());
Laurent Montel's avatar
Laurent Montel committed
782
        }
783
    }
784
785
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
786
void MainWindow::readProperties(const KConfigGroup &config)
787
{
788
    // read properties here
789
    KXmlGuiWindow::readProperties(config);