mainwindow.cpp 153 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/KoIconUtils.h"
68 69 70
#ifdef USE_JOGSHUTTLE
#include "jogshuttle/jogmanager.h"
#endif
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
71

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

#include <QAction>
93 94 95 96
#include <QDebug>
#include <QStatusBar>
#include <QTemporaryFile>
#include <QMenu>
97
#include <QDesktopWidget>
98
#include <QBitmap>
99
#include <QUndoGroup>
100
#include <QFileDialog>
101
#include <QStyleFactory>
102 103

#include <stdlib.h>
104
#include <QStandardPaths>
Vincent Pinon's avatar
Vincent Pinon committed
105 106 107 108
#include <KConfigGroup>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
109
#include <QtGlobal>
110

111
static const char version[] = KDENLIVE_VERSION;
112 113
namespace Mlt
{
114 115 116
class Producer;
};

Marco Gittler's avatar
Marco Gittler committed
117 118 119 120 121
EffectsList MainWindow::videoEffects;
EffectsList MainWindow::audioEffects;
EffectsList MainWindow::customEffects;
EffectsList MainWindow::transitions;

122
QMap <QString,QImage> MainWindow::m_lumacache;
123
QMap <QString,QStringList> MainWindow::m_lumaFiles;
124

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

130 131 132 133 134 135 136 137 138
// 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);
}

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

164
    Core::build(this);
165 166 167 168 169

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

170 171 172 173 174 175 176 177 178 179 180 181 182

    // 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
183 184 185 186 187 188 189 190
    // Add default style action
    QAction *defaultStyle = new QAction(i18n("Default"), stylesGroup);
    defaultStyle->setCheckable(true);
    stylesAction->addAction(defaultStyle);
    if (KdenliveSettings::widgetstyle().isEmpty()) {
        defaultStyle->setChecked(true);
    }

191 192 193 194
    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
195 196 197
        if (KdenliveSettings::widgetstyle() == style) {
            a->setChecked(true);
        }
198 199 200 201 202
        stylesAction->addAction(a);
    }
    connect(stylesGroup, &QActionGroup::triggered, this, &MainWindow::slotChangeStyle);

    // Color schemes
203 204 205
    KActionMenu *themeAction= new KActionMenu(i18n("Theme"), this);
    ThemeManager::instance()->setThemeMenuAction(themeAction);
    ThemeManager::instance()->setCurrentTheme(KdenliveSettings::colortheme());
206
    connect(ThemeManager::instance(), SIGNAL(signalThemeChanged(const QString &)), this, SLOT(slotThemeChanged(const QString &)), Qt::DirectConnection);
207 208

    if (!KdenliveSettings::widgetstyle().isEmpty() && QString::compare(desktopStyle, KdenliveSettings::widgetstyle(), Qt::CaseInsensitive) != 0) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
209 210 211 212
        // User wants a custom widget style, init
        doChangeStyle();
    }
    else ThemeManager::instance()->slotChangePalette();
213 214 215 216

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

218
    KdenliveSettings::setCurrent_profile(KdenliveSettings::default_profile());
219 220 221 222 223 224 225

    // 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");
    }
226
    m_commandStack = new QUndoGroup;
227 228
    m_timelineArea = new QTabWidget(this);
    //m_timelineArea->setTabReorderingEnabled(true);
229
    m_timelineArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
Simon Eugster's avatar
Simon Eugster committed
230
    m_timelineArea->setMinimumHeight(200);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
231 232 233
    // Hide tabbar
    QTabBar *bar = m_timelineArea->findChild<QTabBar *>();
    bar->setHidden(true);
234

235
    m_gpuAllowed = initEffects::parseEffectFiles(pCore->binController()->mltRepository());
236
    //initEffects::parseCustomEffectsFile();
237

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

241 242 243
    /// Add Widgets
    setDockNestingEnabled(true);
    setCentralWidget(m_timelineArea);
244
    m_projectBinDock = addDock(i18n("Project Bin"), QStringLiteral("project_bin"), pCore->bin());
245
    QDockWidget * libraryDock = addDock(i18n("Library"), QStringLiteral("library"), pCore->library());
Simon Eugster's avatar
Simon Eugster committed
246

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

255
    connect(pCore->bin(), SIGNAL(clipNeedsReload(QString,bool)),this, SLOT(slotUpdateClip(QString,bool)));
256
    connect(pCore->bin(), SIGNAL(findInTimeline(QString)), this, SLOT(slotClipInTimeline(QString)));
257

258 259
    //TODO deprecated, replace with Bin methods if necessary
    /*connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
Laurent Montel's avatar
Laurent Montel committed
260
    connect(m_projectList, SIGNAL(displayMessage(QString,int,MessageType)), this, SLOT(slotGotProgressInfo(QString,int,MessageType)));
261
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
Laurent Montel's avatar
Laurent Montel committed
262
    connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
263
    connect(m_projectList, SIGNAL(refreshClip(QString,bool)), pCore->monitorManager(), SLOT(slotRefreshCurrentMonitor(QString)));
264
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));*/
265
    connect(m_clipMonitor, SIGNAL(extractZone(QString)), pCore->bin(), SLOT(slotStartCutJob(QString)));
266
    connect(m_clipMonitor, SIGNAL(passKeyPress(QKeyEvent*)), this, SLOT(triggerKey(QKeyEvent*)));
267

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
268
    m_projectMonitor = new Monitor(Kdenlive::ProjectMonitor, pCore->monitorManager(), this);
269
    connect(m_projectMonitor, SIGNAL(passKeyPress(QKeyEvent*)), this, SLOT(triggerKey(QKeyEvent*)));
270 271 272 273 274
    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);
    
275
    connect(m_projectMonitor, SIGNAL(updateGuide(int, QString)), this, SLOT(slotEditGuide(int, QString)));
276

277 278
/*
    //TODO disabled until ported to qml
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
279
    m_recMonitor = new RecMonitor(Kdenlive::RecordMonitor, pCore->monitorManager(), this);
280 281
    connect(m_recMonitor, SIGNAL(addProjectClip(QUrl)), this, SLOT(slotAddProjectClip(QUrl)));
    connect(m_recMonitor, SIGNAL(addProjectClipList(QList<QUrl>)), this, SLOT(slotAddProjectClipList(QList<QUrl>)));
282

283
*/
284
    pCore->monitorManager()->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
285
    connect(m_clipMonitor, SIGNAL(addMasterEffect(QString,QDomElement)), pCore->bin(), SLOT(slotEffectDropped(QString,QDomElement)));
286

287 288 289 290 291 292 293 294
    // 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();

295
    m_effectStack = new EffectStackView2(m_projectMonitor, this);
296
    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>&)));
297
    connect(pCore->bin(), SIGNAL(masterClipSelected(ClipController *, Monitor *)), m_effectStack, SLOT(slotMasterClipItemSelected(ClipController *, Monitor *)));
298
    connect(pCore->bin(), SIGNAL(masterClipUpdated(ClipController *, Monitor *)), m_effectStack, SLOT(slotRefreshMasterClipEffects(ClipController *, Monitor *)));
299 300 301 302 303
    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)));
    connect(m_effectStack, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
    connect(m_effectStack, SIGNAL(displayMessage(QString,int)), this, SLOT(slotGotProgressInfo(QString,int)));
    
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
304
    m_effectStackDock = addDock(i18n("Properties"), QStringLiteral("effect_stack"), m_effectStack);
305

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

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

312 313
    setupActions();

Simon Eugster's avatar
Simon Eugster committed
314
    // Add monitors here to keep them at the right of the window
315 316
    m_clipMonitorDock = addDock(i18n("Clip Monitor"), QStringLiteral("clip_monitor"), m_clipMonitor);
    m_projectMonitorDock = addDock(i18n("Project Monitor"), QStringLiteral("project_monitor"), m_projectMonitor);
317
    if (m_recMonitor) {
318
        m_recMonitorDock = addDock(i18n("Record Monitor"), QStringLiteral("record_monitor"), m_recMonitor);
319
    }
Simon Eugster's avatar
Simon Eugster committed
320

321
    m_undoView = new QUndoView();
322
    m_undoView->setCleanIcon(KoIconUtils::themedIcon(QStringLiteral("edit-clear")));
323 324
    m_undoView->setEmptyLabel(i18n("Clean"));
    m_undoView->setGroup(m_commandStack);
325
    m_undoViewDock = addDock(i18n("Undo History"), QStringLiteral("undo_history"), m_undoView);
326

327
    // Color and icon theme stuff
328
    addAction(QStringLiteral("themes_menu"), themeAction);
329
    connect(m_commandStack, SIGNAL(cleanChanged(bool)), m_saveAction, SLOT(setDisabled(bool)));
330
    addAction(QStringLiteral("styles_menu"), stylesAction);
Simon Eugster's avatar
Simon Eugster committed
331

Simon Eugster's avatar
Simon Eugster committed
332 333 334 335 336 337
    // Close non-general docks for the initial layout
    // only show important ones
    m_undoViewDock->close();



Simon Eugster's avatar
Simon Eugster committed
338
    /// Tabify Widgets ///
339
    tabifyDockWidget(m_transitionListDock, m_effectListDock);
340 341
    tabifyDockWidget(pCore->bin()->clipPropertiesDock(), m_effectStackDock);
    //tabifyDockWidget(m_effectListDock, m_effectStackDock);
342

343
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
344 345 346
    if (m_recMonitor) {
        tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
    }
Simon Eugster's avatar
Simon Eugster committed
347 348 349

    readOptions();

350
    QAction *action;
351 352
    // 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());
353
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("media-record")), i18n("Capture frame"), this);
354
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
355
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_capture"), action);
356
    action = new QAction(i18n("Switch live / captured frame"), this);
357
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
358 359
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_switch"), action);
    action = new QAction(KoIconUtils::themedIcon(QStringLiteral("edit-paste")), i18n("Show last frame over video"), this);
360 361
    action->setCheckable(true);
    action->setChecked(false);
362
    m_stopmotion_actions->addAction(QStringLiteral("stopmotion_overlay"), action);
363

364
    // Monitor Record action
365
    addAction(QStringLiteral("switch_monitor_rec"), m_clipMonitor->recAction());
366

367
    // Build effects menu
368
    m_effectsMenu = new QMenu(i18n("Add Effect"), this);
369 370
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
371

372 373 374 375
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
    m_transitionActions = new KActionCategory(i18n("Transitions"), actionCollection());
    m_transitionList->reloadEffectList(m_transitionsMenu, m_transitionActions);

376
    ScopeManager *scmanager = new ScopeManager(this);
377

Till Theato's avatar
Till Theato committed
378
    new LayoutManagement(this);
379
    new HideTitleBars(this);
380
    new TimelineSearch(this);
381 382
    m_extraFactory = new KXMLGUIClient(this);
    buildDynamicActions();
383

384 385 386 387 388 389 390 391 392 393 394 395 396 397
    // 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() + ")");
        }
    }
398

399 400
    // Create Effect Basket (dropdown list of favorites)
    m_effectBasket = new EffectBasket(m_effectList);
401
    connect(m_effectBasket, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
402 403
    QWidgetAction *widgetlist = new QWidgetAction(this);
    widgetlist->setDefaultWidget(m_effectBasket);
404 405 406
    widgetlist->setText(i18n("Favorite Effects"));
    widgetlist->setToolTip(i18n("Favorite Effects"));
    widgetlist->setIcon(KoIconUtils::themedIcon("favorite"));
407 408 409 410 411
    QMenu *menu = new QMenu(this);
    menu->addAction(widgetlist);

    QToolButton *basketButton = new QToolButton(this);
    basketButton->setMenu(menu);
412
    basketButton->setToolButtonStyle(toolBar()->toolButtonStyle());
413 414 415
    basketButton->setDefaultAction(widgetlist);
    basketButton->setPopupMode(QToolButton::InstantPopup);
    basketButton->setText(i18n("Favorite Effects"));
416
    basketButton->setToolTip(i18n("Favorite Effects"));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
417
    basketButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
418

419 420 421 422
    QWidgetAction* toolButtonAction = new QWidgetAction(this);
    toolButtonAction->setText(i18n("Favorite Effects"));
    toolButtonAction->setIcon(KoIconUtils::themedIcon("favorite"));
    toolButtonAction->setDefaultWidget(basketButton);
423

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
424
    addAction(QStringLiteral("favorite_effects"), toolButtonAction);
425 426
    connect(toolButtonAction, SIGNAL(triggered(bool)), basketButton, SLOT(showMenu()));
    setupGUI();
427

428 429
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
430

431
    loadGenerators();
432
    loadDockActions();
433
    loadClipActions();
434 435

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

439 440
    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)));
441

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

446 447
    connect(pCore->monitorManager(), SIGNAL(updateOverlayInfos(int,int)), this, SLOT(slotUpdateMonitorOverlays(int,int)));

448
    // Setup and fill effects and transitions menus.
449
    QMenu *m = static_cast<QMenu*>(factory()->container(QStringLiteral("video_effects_menu"), this));
Laurent Montel's avatar
Laurent Montel committed
450 451
    connect(m, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
    connect(m_effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
452
    connect(m_transitionsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddTransition(QAction*)));
453

454 455 456 457
    m_timelineContextMenu = new QMenu(this);
    m_timelineContextClipMenu = new QMenu(this);
    m_timelineContextTransitionMenu = new QMenu(this);

458 459
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("insert_space")));
    m_timelineContextMenu->addAction(actionCollection()->action(QStringLiteral("delete_space")));
460 461
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

462
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("clip_in_project_tree")));
463
    //m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
464
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
465
    m_timelineContextClipMenu->addSeparator();
466 467 468 469 470
    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")));
471
    m_timelineContextClipMenu->addSeparator();
472
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("cut_timeline_clip")));
473
    m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
474
    m_timelineContextClipMenu->addAction(actionCollection()->action(QStringLiteral("paste_effects")));
475
    m_timelineContextClipMenu->addSeparator();
476

477
    QMenu *markersMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("marker_menu"), this));
478
    m_timelineContextClipMenu->addMenu(markersMenu);
479
    m_timelineContextClipMenu->addSeparator();
480
    m_timelineContextClipMenu->addMenu(m_transitionsMenu);
481
    m_timelineContextClipMenu->addMenu(m_effectsMenu);
Marco Gittler's avatar
Marco Gittler committed
482

483
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("delete_timeline_clip")));
484
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
485

486
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(QStringLiteral("auto_transition")));
487

Laurent Montel's avatar
Laurent Montel committed
488
    connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
489 490
    connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));

491
    slotConnectMonitors();
492

493
    // Populate encoding profiles
494
    KConfig conf(QStringLiteral("encodingprofiles.rc"), KConfig::CascadeConfig, QStandardPaths::DataLocation);
495
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
496 497 498 499 500 501 502 503 504 505
        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));
        }
    }
506
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
507 508 509 510 511 512 513 514 515 516
        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));
        }
    }
517 518 519 520 521 522 523 524 525 526 527
    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));
        }
    }
528
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
529 530 531 532 533 534 535 536 537 538
        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));
        }
    }
539
    emit GUISetupDone();
Till Theato's avatar
Till Theato committed
540
    pCore->projectManager()->init(Url, clipsToLoad);
541
    QTimer::singleShot(0, pCore->projectManager(), SLOT(slotLoadOnOpen()));
542
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
543

544 545 546
#ifdef USE_JOGSHUTTLE
    new JogManager(this);
#endif
547
    scmanager->slotCheckActiveScopes();
548
    //KMessageBox::information(this, "Warning, development version for testing only. we are currently working on core functionnalities,\ndo not save any project or your project files might be corrupted.");
549
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
550

551 552
void MainWindow::slotThemeChanged(const QString &theme)
{
553
    disconnect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()));
554 555 556 557
    KSharedConfigPtr config = KSharedConfig::openConfig(theme);
    setPalette(KColorScheme::createApplicationPalette(config));
    qApp->setPalette(palette());
    QPalette plt = palette();
558 559

    KdenliveSettings::setColortheme(theme);
560 561 562 563
    if (m_effectStack) {
        m_effectStack->updatePalette();
        m_effectStack->transitionConfig()->updatePalette();
    }
564
    if (m_effectList) m_effectList->updatePalette();
565
    if (m_transitionList) m_transitionList->updatePalette();
566 567 568
    if (m_clipMonitor) m_clipMonitor->setPalette(plt);
    if (m_projectMonitor) m_projectMonitor->setPalette(plt);
    setStatusBarStyleSheet(plt);
569
    if (pCore->projectManager() && pCore->projectManager()->currentTimeline()) {
570
        pCore->projectManager()->currentTimeline()->updatePalette();
571
    }
572 573 574 575 576 577 578 579 580 581 582 583 584
    if (m_timelineArea) {
        m_timelineArea->setPalette(plt);
    }
    if (statusBar()) {
        const QObjectList children = statusBar()->children();
        foreach(QObject * child, children) {
            if (child->isWidgetType())
                ((QWidget*)child)->setPalette(plt);
            const QObjectList subchildren = child->children();
            foreach(QObject * subchild, subchildren) {
                if (subchild->isWidgetType())
                    ((QWidget*)subchild)->setPalette(plt);
            }
585 586
        }
    }
587 588 589 590 591 592 593 594 595 596

    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
597
        if (m_effectStack && m_effectStack->transitionConfig()) m_effectStack->transitionConfig()->refreshIcons();
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
        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;
615
    connect(this, SIGNAL(reloadTheme()), this, SLOT(slotReloadTheme()), Qt::UniqueConnection);
616 617 618 619 620
}

bool MainWindow::event(QEvent *e) {
    switch (e->type()) {
        case QEvent::ApplicationPaletteChange:
621
            emit reloadTheme();
622 623 624 625 626 627 628 629
            e->accept();
            break;
        default:
            break;
    }
    return KXmlGuiWindow::event(e);
}

630 631 632 633 634
void MainWindow::slotReloadTheme()
{
    ThemeManager::instance()->slotSettingsChanged();
}

635
MainWindow::~MainWindow()
636
{
Laurent Montel's avatar
Laurent Montel committed
637
    delete m_stopmotion;
638
    delete m_audioSpectrum;
639
    m_effectStack->slotClipItemSelected(NULL, m_projectMonitor);
640
    m_effectStack->slotTransitionItemSelected(NULL, 0, QPoint(), false);
641 642
    if (m_projectMonitor) m_projectMonitor->stop();
    if (m_clipMonitor) m_clipMonitor->stop();
643
    delete pCore;
644 645 646 647
    delete m_effectStack;
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
648
    qDeleteAll(m_transitions);
649 650 651
    Mlt::Factory::close();
}

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
//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);
675 676
}

677 678 679 680 681
void MainWindow::loadGenerators()
{
    QMenu *addMenu = static_cast<QMenu*>(factory()->container(QStringLiteral("generators"), this));
    Generators::getGenerators(KdenliveSettings::producerslist(), addMenu);
    connect(addMenu, SIGNAL(triggered(QAction *)), this, SLOT(buildGenerator(QAction *)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
682 683
}

684
void MainWindow::buildGenerator(QAction *action)
685
{
686 687 688 689
    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
690 691
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
692
void MainWindow::saveProperties(KConfigGroup &config)
693
{
694
    // save properties here
695
    KXmlGuiWindow::saveProperties(config);
696 697 698 699 700 701
    //TODO: fix session management
    if (qApp->isSavingSession()) {
	if (pCore->projectManager()->current() && !pCore->projectManager()->current()->url().isEmpty()) {
	    config.writeEntry("kdenlive_lastUrl", pCore->projectManager()->current()->url().path());
	}
    }
702 703
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
704
void MainWindow::readProperties(const KConfigGroup &config)
705
{
706
    // read properties here
707
    KXmlGuiWindow::readProperties(config);
708 709 710 711
    //TODO: fix session management
    /*if (qApp->isSessionRestored()) {
	pCore->projectManager()->openFile(QUrl::fromLocalFile(config.readEntry("kdenlive_lastUrl", QString())));
    }*/
712 713
}

714 715 716 717 718 719 720 721 722 723
void MainWindow::saveNewToolbarConfig()
{
    KXmlGuiWindow::saveNewToolbarConfig();
    //TODO for some reason all dynamically inserted actions are removed by the save toolbar
    // So we currently re-add them manually....
    loadDockActions();
    loadClipActions();
    pCore->bin()->rebuildMenu();
}

724 725
void MainWindow::slotReloadEffects()
{
726
    initEffects::parseCustomEffectsFile();
727
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
728 729
}

730 731
void MainWindow::configureNotifications()
{
732 733 734
    KNotifyConfigWidget::configure(this);
}

735 736
void MainWindow::slotFullScreen()
{
737
    KToggleFullScreenAction::setFullScreen(this, actionCollection()->action(QStringLiteral("fullscreen"))->isChecked());
738 739
}

740
void MainWindow::slotAddEffect(const QDomElement &effect)
741
{
742
    if (effect.isNull()) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
743
        //qDebug() << "--- ERROR, TRYING TO APPEND NULL EFFECT";
744 745
        return;
    }
746
    QDomElement effectToAdd = effect.cloneNode().toElement();
747
    EFFECTMODE status = m_effectStack->effectStatus();
748 749 750 751
    if (status == TIMELINE_TRACK) {
        pCore->projectManager()->currentTimeline()->projectView()->slotAddTrackEffect(effectToAdd, pCore->projectManager()->currentTimeline()->tracksCount() - m_effectStack->trackIndex());
    }
    else if (status == TIMELINE_CLIP) {
752
        pCore->projectManager()->currentTimeline()->projectView()->slotAddEffectToCurrentItem(effectToAdd);
753 754
    }
    else if (status == MASTER_CLIP) {
755
        pCore->bin()->slotEffectDropped(QString(), effectToAdd);
756
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
757 758
}

759
void MainWindow::slotUpdateClip(const QString &id, bool reload)
760
{
761
    ProjectClip *clip = pCore->bin()->getBinClip(id);
762 763 764
    if (!clip) {
        return;
    }
765
    pCore->projectManager()->currentTimeline()->projectView()->slotUpdateClip(id, reload);
766 767
}

768 769
void MainWindow::slotConnectMonitors()
{
770
    //connect(m_projectList, SIGNAL(deleteProjectClips(QStringList,QMap<QString,QString>)), this, SLOT(slotDeleteProjectClips(QStringList,QMap<QString,QString>)));
771
    connect(m_projectMonitor->render, SIGNAL(gotFileProperties(requestClipInfo,ClipController *)), pCore->bin(), SLOT(slotProducerReady(requestClipInfo,ClipController *)), Qt::DirectConnection);
772

773
    connect(m_clipMonitor, SIGNAL(refreshClipThumbnail(QString)), pCore->bin(), SLOT(slotRefreshClipThumbnail(QString)));
774
    connect(m_projectMonitor, SIGNAL(requestFrameForAnalysis(bool)), this, SLOT(slotMonitorRequestRenderFrame(bool)));
775 776
    connect(m_projectMonitor, &Monitor::createSplitOverlay, this, &MainWindow::createSplitOverlay);
    connect(m_projectMonitor, &Monitor::removeSplitOverlay, this, &MainWindow::removeSplitOverlay);
777 778
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
void MainWindow::createSplitOverlay(Mlt::Filter *filter)
{
    if (pCore->projectManager()->currentTimeline()) {
        if (pCore->projectManager()->currentTimeline()->projectView()->createSplitOverlay(filter)) {
            m_projectMonitor->activateSplit();
        }
    }
}

void MainWindow::removeSplitOverlay()
{
    if (pCore->projectManager()->currentTimeline()) {
        pCore->projectManager()->currentTimeline()->projectView()->removeSplitOverlay();
    }
}
794

795 796 797 798
void MainWindow::addAction(const QString &name, QAction *action)
{
    m_actionNames.append(name);
    actionCollection()->addAction(name, action);
799
    actionCollection()->setDefaultShortcut(action, action->shortcut()); //Fix warning about setDefaultShortcut
800
}
801

802 803
QAction *MainWindow::addAction(const QString &name, const QString &text, const QObject *receiver,
                           const char *member, const QIcon &icon, const QKeySequence &shortcut)