mainwindow.cpp 202 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 "kdenlivesettings.h"
24
#include "kdenlivesettingsdialog.h"
25
#include "initeffects.h"
26 27
#include "profilesdialog.h"
#include "projectsettings.h"
28
#include "events.h"
29 30 31 32 33 34 35 36 37
#include "clipmanager.h"
#include "projectlist.h"
#include "monitor.h"
#include "recmonitor.h"
#include "monitormanager.h"
#include "kdenlivedoc.h"
#include "trackview.h"
#include "customtrackview.h"
#include "effectslistview.h"
38
#include "effectstack/effectstackview2.h"
39 40
#include "transitionsettings.h"
#include "renderwidget.h"
41
#include "renderer.h"
42
#ifdef USE_JOGSHUTTLE
43
#include "jogshuttle.h"
44 45
#include "jogaction.h"
#include "jogshuttleconfig.h"
46
#endif
47
#include "clipproperties.h"
48
#include "wizard.h"
49
#include "commands/editclipcommand.h"
50
#include "titlewidget.h"
51 52
#include "markerdialog.h"
#include "clipitem.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
53
#include "interfaces.h"
54
#include "config-kdenlive.h"
55
#include "cliptranscode.h"
56
#include "ui_templateclip_ui.h"
57 58 59 60 61 62 63 64
#include "scopes/scopemanager.h"
#include "scopes/colorscopes/vectorscope.h"
#include "scopes/colorscopes/waveform.h"
#include "scopes/colorscopes/rgbparade.h"
#include "scopes/colorscopes/histogram.h"
#include "scopes/audioscopes/audiosignal.h"
#include "scopes/audioscopes/audiospectrum.h"
#include "scopes/audioscopes/spectrogram.h"
65
#include "archivewidget.h"
66
#include "databackup/backupwidget.h"
67
#include "utils/resourcewidget.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
68

69

70 71 72 73 74
#include <KApplication>
#include <KAction>
#include <KLocale>
#include <KGlobal>
#include <KActionCollection>
75
#include <KActionCategory>
76
#include <KStandardAction>
77
#include <KShortcutsDialog>
78 79 80 81 82 83 84 85 86 87 88 89
#include <KFileDialog>
#include <KMessageBox>
#include <KDebug>
#include <KIO/NetAccess>
#include <KSaveFile>
#include <KRuler>
#include <KConfigDialog>
#include <KXMLGUIFactory>
#include <KStatusBar>
#include <kstandarddirs.h>
#include <KUrlRequesterDialog>
#include <KTemporaryFile>
90
#include <KProcess>
91 92 93 94 95 96
#include <KActionMenu>
#include <KMenu>
#include <ktogglefullscreenaction.h>
#include <KFileItem>
#include <KNotification>
#include <KNotifyConfigWidget>
97
#if KDE_IS_VERSION(4,3,80)
Alberto Villa's avatar
Alberto Villa committed
98 99
#include <knewstuff3/downloaddialog.h>
#include <knewstuff3/knewstuffaction.h>
100 101 102 103 104
#else
#include <knewstuff2/engine.h>
#include <knewstuff2/ui/knewstuffaction.h>
#define KNS3 KNS
#endif
105
#include <KToolBar>
106
#include <KColorScheme>
107
#include <KProgressDialog>
108 109 110 111 112

#include <QTextStream>
#include <QTimer>
#include <QAction>
#include <QKeyEvent>
113
#include <QInputDialog>
114
#include <QDesktopWidget>
115
#include <QBitmap>
116 117

#include <stdlib.h>
118
#include <locale.h>
119

120
// Uncomment for deeper debugging
121
//#define DEBUG_MAINW
122

123 124 125 126
#ifdef DEBUG_MAINW
#include <QDebug>
#endif

127 128
static const char version[] = VERSION;

129 130
namespace Mlt
{
131 132 133
class Producer;
};

134 135
Q_DECLARE_METATYPE(QVector<int16_t>)

136

Marco Gittler's avatar
Marco Gittler committed
137 138 139 140 141
EffectsList MainWindow::videoEffects;
EffectsList MainWindow::audioEffects;
EffectsList MainWindow::customEffects;
EffectsList MainWindow::transitions;

142 143
QMap <QString,QImage> MainWindow::m_lumacache;

144 145 146 147 148
static bool sortByNames(const QPair<QString, KAction*> &a, const QPair<QString, KAction*> &b)
{
    return a.first < b.first;
}

149
MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, const QString & clipsToLoad, QWidget *parent) :
150 151 152
    KXmlGuiWindow(parent),
    m_activeDocument(NULL),
    m_activeTimeline(NULL),
153 154 155
    m_projectList(NULL),
    m_effectList(NULL),
    m_effectStack(NULL),
156 157
    m_clipMonitor(NULL),
    m_projectMonitor(NULL),
158
    m_recMonitor(NULL),
159
    m_renderWidget(NULL),
160
#ifdef USE_JOGSHUTTLE
161
    m_jogProcess(NULL),
162
    m_jogShuttle(NULL),
163
#endif
164
    m_findActivated(false),
165 166
    m_stopmotion(NULL),
    m_mainClip(NULL)
167
{
168
    qRegisterMetaType<QVector<int16_t> > ();
169
    qRegisterMetaType<stringMap> ("stringMap");
170 171
    qRegisterMetaType<audioByteArray> ("audioByteArray");

172 173
    // Init locale
    QLocale systemLocale = QLocale();
174 175 176 177
    setlocale(LC_NUMERIC, NULL);
    char *separator = localeconv()->decimal_point;
    if (separator != systemLocale.decimalPoint()) {
        kDebug()<<"------\n!!! system locale is not similar to Qt's locale... be prepared for bugs!!!\n------";
178 179 180 181 182
        // HACK: There is a locale conflict, so set locale to C
	// Make sure to override exported values or it won't work
	setenv("LANG", "C", 1);
	setlocale(LC_NUMERIC, "C");
        systemLocale = QLocale::c();
183
    }
184

185 186 187
    systemLocale.setNumberOptions(QLocale::OmitGroupSeparator);
    QLocale::setDefault(systemLocale);

188 189 190 191
    // Create DBus interface
    new MainWindowAdaptor(this);
    QDBusConnection dbus = QDBusConnection::sessionBus();
    dbus.registerObject("/MainWindow", this);
192

193
    if (!KdenliveSettings::colortheme().isEmpty()) slotChangePalette(NULL, KdenliveSettings::colortheme());
194
    setFont(KGlobalSettings::toolBarFont());
195
    parseProfiles(MltPath);
196
    KdenliveSettings::setCurrent_profile(KdenliveSettings::default_profile());
197
    m_commandStack = new QUndoGroup;
198
    setDockNestingEnabled(true);
199 200
    m_timelineArea = new KTabWidget(this);
    m_timelineArea->setTabReorderingEnabled(true);
201
    m_timelineArea->setTabBarHidden(true);
Simon Eugster's avatar
Simon Eugster committed
202 203
    m_timelineArea->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
    m_timelineArea->setMinimumHeight(200);
204 205

    QToolButton *closeTabButton = new QToolButton;
206
    connect(closeTabButton, SIGNAL(clicked()), this, SLOT(closeCurrentDocument()));
207 208 209 210
    closeTabButton->setIcon(KIcon("tab-close"));
    closeTabButton->adjustSize();
    closeTabButton->setToolTip(i18n("Close the current tab"));
    m_timelineArea->setCornerWidget(closeTabButton);
211
    //connect(m_timelineArea, SIGNAL(currentChanged(int)), this, SLOT(activateDocument()));
212

213 214 215
    connect(&m_findTimer, SIGNAL(timeout()), this, SLOT(findTimeout()));
    m_findTimer.setSingleShot(true);

Ray Lehtiniemi's avatar
Ray Lehtiniemi committed
216
    // FIXME: the next call returns a newly allocated object, which leaks
Marco Gittler's avatar
Marco Gittler committed
217
    initEffects::parseEffectFiles();
218
    //initEffects::parseCustomEffectsFile();
219
    
220 221
    m_monitorManager = new MonitorManager();

Simon Eugster's avatar
Simon Eugster committed
222 223 224 225 226 227
    m_shortcutRemoveFocus = new QShortcut(QKeySequence("Esc"), this);
    connect(m_shortcutRemoveFocus, SIGNAL(activated()), this, SLOT(slotRemoveFocus()));


    /// Add Widgets ///

228 229
    m_projectListDock = new QDockWidget(i18n("Project Tree"), this);
    m_projectListDock->setObjectName("project_tree");
230
    m_projectList = new ProjectList();
231 232
    m_projectListDock->setWidget(m_projectList);
    addDockWidget(Qt::TopDockWidgetArea, m_projectListDock);
233

234 235
    m_clipMonitorDock = new QDockWidget(i18n("Clip Monitor"), this);
    m_clipMonitorDock->setObjectName("clip_monitor");
236
    m_clipMonitor = new Monitor(Kdenlive::clipMonitor, m_monitorManager, QString(), m_timelineArea);
237
    m_clipMonitorDock->setWidget(m_clipMonitor);
238

239
    // Connect the project list
Laurent Montel's avatar
Laurent Montel committed
240
    connect(m_projectList, SIGNAL(clipSelected(DocClipBase*,QPoint,bool)), m_clipMonitor, SLOT(slotSetClipProducer(DocClipBase*,QPoint,bool)));
241
    connect(m_projectList, SIGNAL(raiseClipMonitor(bool)), m_clipMonitor, SLOT(slotActivateMonitor(bool)));
242
    connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
Laurent Montel's avatar
Laurent Montel committed
243
    connect(m_projectList, SIGNAL(displayMessage(QString,int,MessageType)), this, SLOT(slotGotProgressInfo(QString,int,MessageType)));
244
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
Laurent Montel's avatar
Laurent Montel committed
245 246 247 248
    connect(m_projectList, SIGNAL(clipNeedsReload(QString)),this, SLOT(slotUpdateClip(QString)));
    connect(m_projectList, SIGNAL(updateProfile(QString)), this, SLOT(slotUpdateProjectProfile(QString)));
    connect(m_projectList, SIGNAL(refreshClip(QString,bool)), m_monitorManager, SLOT(slotRefreshCurrentMonitor(QString)));
    connect(m_projectList, SIGNAL(findInTimeline(QString)), this, SLOT(slotClipInTimeline(QString)));
249
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));
Laurent Montel's avatar
Laurent Montel committed
250
    connect(m_clipMonitor, SIGNAL(extractZone(QString,QPoint)), m_projectList, SLOT(slotCutClipJob(QString,QPoint)));
251

252 253
    m_projectMonitorDock = new QDockWidget(i18n("Project Monitor"), this);
    m_projectMonitorDock->setObjectName("project_monitor");
254
    m_projectMonitor = new Monitor(Kdenlive::projectMonitor, m_monitorManager, QString());
255
    m_projectMonitorDock->setWidget(m_projectMonitor);
256

257
#ifndef Q_WS_MAC
258 259
    m_recMonitorDock = new QDockWidget(i18n("Record Monitor"), this);
    m_recMonitorDock->setObjectName("record_monitor");
260
    m_recMonitor = new RecMonitor(Kdenlive::recordMonitor, m_monitorManager);
261
    m_recMonitorDock->setWidget(m_recMonitor);
262
    connect(m_recMonitor, SIGNAL(addProjectClip(KUrl)), this, SLOT(slotAddProjectClip(KUrl)));
263
    connect(m_recMonitor, SIGNAL(addProjectClipList(KUrl::List)), this, SLOT(slotAddProjectClipList(KUrl::List)));
Laurent Montel's avatar
Laurent Montel committed
264
    connect(m_recMonitor, SIGNAL(showConfigDialog(int,int)), this, SLOT(slotPreferences(int,int)));
265

266
#endif /* ! Q_WS_MAC */
267
    m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
268

269 270
    m_notesDock = new QDockWidget(i18n("Project Notes"), this);
    m_notesDock->setObjectName("notes_widget");
271 272 273
    m_notesWidget = new NotesWidget();
    connect(m_notesWidget, SIGNAL(insertNotesTimecode()), this, SLOT(slotInsertNotesTimecode()));
    connect(m_notesWidget, SIGNAL(seekProject(int)), m_projectMonitor->render, SLOT(seekToFrame(int)));
274

275
    m_notesWidget->setTabChangesFocus(true);
276
#if KDE_IS_VERSION(4,4,0)
277
    m_notesWidget->setClickMessage(i18n("Enter your project notes here ..."));
278
#endif
279 280 281
    m_notesDock->setWidget(m_notesWidget);
    addDockWidget(Qt::TopDockWidgetArea, m_notesDock);

282 283
    m_effectStackDock = new QDockWidget(i18n("Effect Stack"), this);
    m_effectStackDock->setObjectName("effect_stack");
284
    m_effectStack = new EffectStackView2(m_projectMonitor);
285 286
    m_effectStackDock->setWidget(m_effectStack);
    addDockWidget(Qt::TopDockWidgetArea, m_effectStackDock);
Laurent Montel's avatar
Laurent Montel committed
287
    connect(m_effectStack, SIGNAL(startFilterJob(ItemInfo,QString,QString,QString,QString,QString,QMap<QString,QString>)), m_projectList, SLOT(slotStartFilterJob(ItemInfo,QString,QString,QString,QString,QString,QMap<QString,QString>)));
288 289 290

    m_transitionConfigDock = new QDockWidget(i18n("Transition"), this);
    m_transitionConfigDock->setObjectName("transition");
291
    m_transitionConfig = new TransitionSettings(m_projectMonitor);
292 293 294
    m_transitionConfigDock->setWidget(m_transitionConfig);
    addDockWidget(Qt::TopDockWidgetArea, m_transitionConfigDock);

Simon Eugster's avatar
Simon Eugster committed
295 296 297 298 299 300
    m_effectListDock = new QDockWidget(i18n("Effect List"), this);
    m_effectListDock->setObjectName("effect_list");
    m_effectList = new EffectsListView();
    m_effectListDock->setWidget(m_effectList);
    addDockWidget(Qt::TopDockWidgetArea, m_effectListDock);

301 302
    m_scopeManager = new ScopeManager(m_monitorManager);
    m_vectorscope = new Vectorscope();
Simon Eugster's avatar
Simon Eugster committed
303
    m_vectorscopeDock = new QDockWidget(i18n("Vectorscope"), this);
Simon Eugster's avatar
Simon Eugster committed
304
    m_vectorscopeDock->setObjectName(m_vectorscope->widgetName());
Simon Eugster's avatar
Simon Eugster committed
305 306
    m_vectorscopeDock->setWidget(m_vectorscope);
    addDockWidget(Qt::TopDockWidgetArea, m_vectorscopeDock);
307
    m_scopeManager->addScope(m_vectorscope, m_vectorscopeDock);
Simon Eugster's avatar
Simon Eugster committed
308

309
    m_waveform = new Waveform();
Simon Eugster's avatar
Simon Eugster committed
310
    m_waveformDock = new QDockWidget(i18n("Waveform"), this);
Simon Eugster's avatar
Simon Eugster committed
311
    m_waveformDock->setObjectName(m_waveform->widgetName());
Simon Eugster's avatar
Simon Eugster committed
312 313
    m_waveformDock->setWidget(m_waveform);
    addDockWidget(Qt::TopDockWidgetArea, m_waveformDock);
314
    m_scopeManager->addScope(m_waveform, m_waveformDock);
Simon Eugster's avatar
Simon Eugster committed
315

316
    m_RGBParade = new RGBParade();
317
    m_RGBParadeDock = new QDockWidget(i18n("RGB Parade"), this);
Simon Eugster's avatar
Simon Eugster committed
318 319 320
    m_RGBParadeDock->setObjectName(m_RGBParade->widgetName());
    m_RGBParadeDock->setWidget(m_RGBParade);
    addDockWidget(Qt::TopDockWidgetArea, m_RGBParadeDock);
321
    m_scopeManager->addScope(m_RGBParade, m_RGBParadeDock);
322

323
    m_histogram = new Histogram();
324 325 326 327
    m_histogramDock = new QDockWidget(i18n("Histogram"), this);
    m_histogramDock->setObjectName(m_histogram->widgetName());
    m_histogramDock->setWidget(m_histogram);
    addDockWidget(Qt::TopDockWidgetArea, m_histogramDock);
328
    m_scopeManager->addScope(m_histogram, m_histogramDock);
Simon Eugster's avatar
Simon Eugster committed
329

330 331
    m_audiosignal = new AudioSignal;
    m_audiosignalDock = new QDockWidget(i18n("Audio Signal"), this);
332 333
    m_audiosignalDock->setObjectName("audiosignal");
    m_audiosignalDock->setWidget(m_audiosignal);
334
    addDockWidget(Qt::TopDockWidgetArea, m_audiosignalDock);
335
    m_scopeManager->addScope(m_audiosignal, m_audiosignalDock);
336

337
    m_audioSpectrum = new AudioSpectrum();
338 339 340 341
    m_audioSpectrumDock = new QDockWidget(i18n("AudioSpectrum"), this);
    m_audioSpectrumDock->setObjectName(m_audioSpectrum->widgetName());
    m_audioSpectrumDock->setWidget(m_audioSpectrum);
    addDockWidget(Qt::TopDockWidgetArea, m_audioSpectrumDock);
342
    m_scopeManager->addScope(m_audioSpectrum, m_audioSpectrumDock);
Simon Eugster's avatar
Simon Eugster committed
343

Simon Eugster's avatar
Simon Eugster committed
344 345 346 347 348
    m_spectrogram = new Spectrogram();
    m_spectrogramDock = new QDockWidget(i18n("Spectrogram"), this);
    m_spectrogramDock->setObjectName(m_spectrogram->widgetName());
    m_spectrogramDock->setWidget(m_spectrogram);
    addDockWidget(Qt::TopDockWidgetArea, m_spectrogramDock);
349
    m_scopeManager->addScope(m_spectrogram, m_spectrogramDock);
Simon Eugster's avatar
Simon Eugster committed
350 351 352 353 354 355 356 357

    // Add monitors here to keep them at the right of the window
    addDockWidget(Qt::TopDockWidgetArea, m_clipMonitorDock);
    addDockWidget(Qt::TopDockWidgetArea, m_projectMonitorDock);
#ifndef Q_WS_MAC
    addDockWidget(Qt::TopDockWidgetArea, m_recMonitorDock);
#endif

358 359
    m_undoViewDock = new QDockWidget(i18n("Undo History"), this);
    m_undoViewDock->setObjectName("undo_history");
360
    m_undoView = new QUndoView();
361 362
    m_undoView->setCleanIcon(KIcon("edit-clear"));
    m_undoView->setEmptyLabel(i18n("Clean"));
363
    m_undoViewDock->setWidget(m_undoView);
364
    m_undoView->setGroup(m_commandStack);
365
    addDockWidget(Qt::TopDockWidgetArea, m_undoViewDock);
366

Simon Eugster's avatar
Simon Eugster committed
367

368
    setupActions();
369
    connect(m_commandStack, SIGNAL(cleanChanged(bool)), m_saveAction, SLOT(setDisabled(bool)));
Simon Eugster's avatar
Simon Eugster committed
370

Simon Eugster's avatar
Simon Eugster committed
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

    // Close non-general docks for the initial layout
    // only show important ones
    m_histogramDock->close();
    m_RGBParadeDock->close();
    m_waveformDock->close();
    m_vectorscopeDock->close();

    m_audioSpectrumDock->close();
    m_spectrogramDock->close();
    m_audiosignalDock->close();

    m_undoViewDock->close();



Simon Eugster's avatar
Simon Eugster committed
387
    /// Tabify Widgets ///
Simon Eugster's avatar
Simon Eugster committed
388 389
    tabifyDockWidget(m_effectListDock, m_effectStackDock);
    tabifyDockWidget(m_effectListDock, m_transitionConfigDock);
390
    tabifyDockWidget(m_projectListDock, m_notesDock);
391

392
    tabifyDockWidget(m_clipMonitorDock, m_projectMonitorDock);
393
#ifndef Q_WS_MAC
394
    tabifyDockWidget(m_clipMonitorDock, m_recMonitorDock);
395
#endif
396
    setCentralWidget(m_timelineArea);
Simon Eugster's avatar
Simon Eugster committed
397

Laurent Montel's avatar
Laurent Montel committed
398
    m_fileOpenRecent = KStandardAction::openRecent(this, SLOT(openFile(KUrl)), actionCollection());
Simon Eugster's avatar
Simon Eugster committed
399 400 401 402
    readOptions();
    m_fileRevert = KStandardAction::revert(this, SLOT(slotRevert()), actionCollection());
    m_fileRevert->setEnabled(false);

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
403 404
    // Prepare layout actions
    KActionCategory *layoutActions = new KActionCategory(i18n("Layouts"), actionCollection());
405
    m_loadLayout = new KSelectAction(i18n("Load Layout"), actionCollection());
406
    for (int i = 1; i < 5; ++i) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
407
        KAction *load = new KAction(KIcon(), i18n("Layout %1", i), this);
408
        load->setData('_' + QString::number(i));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
409
	layoutActions->addAction("load_layout" + QString::number(i), load);
410
        m_loadLayout->addAction(load);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
411
        KAction *save = new KAction(KIcon(), i18n("Save As Layout %1", i), this);
412
        save->setData('_' + QString::number(i));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
413 414
        layoutActions->addAction("save_layout" + QString::number(i), save);
    }
415
    // Required to enable user to add the load layout action to toolbar
416 417
    layoutActions->addAction("load_layouts", m_loadLayout);
    connect(m_loadLayout, SIGNAL(triggered(QAction*)), this, SLOT(slotLoadLayout(QAction*)));
Simon Eugster's avatar
Simon Eugster committed
418

419 420 421 422 423 424 425 426 427 428 429 430 431
    KAction *action;
    // 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());
    action = new KAction(KIcon("media-record"), i18n("Capture frame"), this);
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    m_stopmotion_actions->addAction("stopmotion_capture", action);
    action = new KAction(i18n("Switch live / captured frame"), this);
    //action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    m_stopmotion_actions->addAction("stopmotion_switch", action);
    action = new KAction(KIcon("edit-paste"), i18n("Show last frame over video"), this);
    action->setCheckable(true);
    action->setChecked(false);
    m_stopmotion_actions->addAction("stopmotion_overlay", action);
432 433

    // Build effects menu
434
    m_effectsMenu = new QMenu(i18n("Add Effect"));
435 436 437
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
    m_effectsActionCollection->readSettings();
438

439 440 441
       // Populate View menu with show / hide actions for dock widgets
    KActionCategory *guiActions = new KActionCategory(i18n("Interface"), actionCollection());

442
    setupGUI();
443

444 445
    // Find QDockWidget tab bars and show / hide widget title bars on right click
    QList <QTabBar *> tabs = findChildren<QTabBar *>();
446
    for (int i = 0; i < tabs.count(); ++i) {
447
        tabs.at(i)->setContextMenuPolicy(Qt::CustomContextMenu);
Laurent Montel's avatar
Laurent Montel committed
448
        connect(tabs.at(i), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotSwitchTitles()));
449 450
    }

451 452
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
453 454 455
    QMenu *trackMenu = (QMenu*)(factory()->container("track_menu", this));
    if (trackMenu) trackMenu->addActions(m_tracksActionCollection->actions());

456

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
457
    QMenu *saveLayout = (QMenu*)(factory()->container("layout_save_as", this));
458
    if (saveLayout)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
459 460 461 462 463
        connect(saveLayout, SIGNAL(triggered(QAction*)), this, SLOT(slotSaveLayout(QAction*)));


    // Load layout names from config file
    loadLayouts();
464

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
465
    loadPlugins();
466
    loadTranscoders();
467
    loadClipActions();
468

469
    m_projectMonitor->setupMenu(static_cast<QMenu*>(factory()->container("monitor_go", this)), m_playZone, m_loopZone, NULL, m_loopClip);
470
    m_clipMonitor->setupMenu(static_cast<QMenu*>(factory()->container("monitor_go", this)), m_playZone, m_loopZone, static_cast<QMenu*>(factory()->container("marker_menu", this)));
471

Till Theato's avatar
Till Theato committed
472 473
    QMenu *clipInTimeline = static_cast<QMenu*>(factory()->container("clip_in_timeline", this));
    clipInTimeline->setIcon(KIcon("go-jump"));
Jean-Baptiste Mardelle's avatar
Cleanup  
Jean-Baptiste Mardelle committed
474 475 476 477 478 479
    QHash<QString,QMenu*> menus;
    menus.insert("addMenu",static_cast<QMenu*>(factory()->container("generators", this)));
    menus.insert("extractAudioMenu",static_cast<QMenu*>(factory()->container("extract_audio", this)));
    menus.insert("transcodeMenu",static_cast<QMenu*>(factory()->container("transcoders", this)));
    menus.insert("clipActionsMenu",static_cast<QMenu*>(factory()->container("clip_actions", this)));
    menus.insert("inTimelineMenu",clipInTimeline);
480
    m_projectList->setupGeneratorMenu(menus);
Marco Gittler's avatar
Marco Gittler committed
481

482 483
    // build themes menus
    QMenu *themesMenu = static_cast<QMenu*>(factory()->container("themes_menu", this));
484 485
    QActionGroup *themegroup = new QActionGroup(this);
    themegroup->setExclusive(true);
486
    action = new KAction(i18n("Default"), this);
487 488
    action->setCheckable(true);
    themegroup->addAction(action);
489
    if (KdenliveSettings::colortheme().isEmpty()) action->setChecked(true);
490 491 492

    const QStringList schemeFiles = KGlobal::dirs()->findAllResources("data", "color-schemes/*.colors", KStandardDirs::NoDuplicates);

493
    for (int i = 0; i < schemeFiles.size(); ++i) {
494 495 496 497 498 499 500 501 502
        // get the file name
        const QString filename = schemeFiles.at(i);
        const QFileInfo info(filename);

        // add the entry
        KSharedConfigPtr config = KSharedConfig::openConfig(filename);
        QIcon icon = createSchemePreviewIcon(config);
        KConfigGroup group(config, "General");
        const QString name = group.readEntry("Name", info.baseName());
503
        action = new KAction(name, this);
504 505 506 507
        action->setData(filename);
        action->setIcon(icon);
        action->setCheckable(true);
        themegroup->addAction(action);
508
        if (KdenliveSettings::colortheme() == filename) action->setChecked(true);
509 510 511
    }

    /*KGlobal::dirs()->addResourceDir("themes", KStandardDirs::installPath("data") + QString("kdenlive/themes"));
512
    QStringList themes = KGlobal::dirs()->findAllResources("themes", QString(), KStandardDirs::Recursive | KStandardDirs::NoDuplicates);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
513 514
    for (QStringList::const_iterator it = themes.constBegin(); it != themes.constEnd(); ++it)
    {
515
    QFileInfo fi(*it);
516 517
        action = new QAction(fi.fileName(), this);
        action->setData(*it);
518 519 520 521
    action->setCheckable(true);
    themegroup->addAction(action);
    if (KdenliveSettings::colortheme() == *it) action->setChecked(true);
    }*/
522
    themesMenu->addActions(themegroup->actions());
Laurent Montel's avatar
Laurent Montel committed
523
    connect(themesMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotChangePalette(QAction*)));
524

525
    // Setup and fill effects and transitions menus.
526 527 528


    QMenu *m = static_cast<QMenu*>(factory()->container("video_effects_menu", this));
529
    m->addActions(m_effectsMenu->actions());
530

531

532
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
533
    for (int i = 0; i < transitions.count(); ++i)
534 535
        m_transitionsMenu->addAction(m_transitions[i]);

Laurent Montel's avatar
Laurent Montel committed
536 537 538
    connect(m, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
    connect(m_effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddVideoEffect(QAction*)));
    connect(m_transitionsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotAddTransition(QAction*)));    
539

540 541 542 543
    m_timelineContextMenu = new QMenu(this);
    m_timelineContextClipMenu = new QMenu(this);
    m_timelineContextTransitionMenu = new QMenu(this);

544
    m_timelineContextMenu->addAction(actionCollection()->action("insert_space"));
545
    m_timelineContextMenu->addAction(actionCollection()->action("delete_space"));
546 547
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

548
    m_timelineContextClipMenu->addAction(actionCollection()->action("clip_in_project_tree"));
549
    //m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
550
    m_timelineContextClipMenu->addAction(actionCollection()->action("delete_item"));
551
    m_timelineContextClipMenu->addSeparator();
552 553
    m_timelineContextClipMenu->addAction(actionCollection()->action("group_clip"));
    m_timelineContextClipMenu->addAction(actionCollection()->action("ungroup_clip"));
554
    m_timelineContextClipMenu->addAction(actionCollection()->action("split_audio"));
555 556
    m_timelineContextClipMenu->addAction(actionCollection()->action("set_audio_align_ref"));
    m_timelineContextClipMenu->addAction(actionCollection()->action("align_audio"));
557
    m_timelineContextClipMenu->addSeparator();
558
    m_timelineContextClipMenu->addAction(actionCollection()->action("cut_timeline_clip"));
559
    m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
560
    m_timelineContextClipMenu->addAction(actionCollection()->action("paste_effects"));
561
    m_timelineContextClipMenu->addSeparator();
562

563 564
    QMenu *markersMenu = (QMenu*)(factory()->container("marker_menu", this));
    m_timelineContextClipMenu->addMenu(markersMenu);
565
    m_timelineContextClipMenu->addSeparator();
566
    m_timelineContextClipMenu->addMenu(m_transitionsMenu);
567
    m_timelineContextClipMenu->addMenu(m_effectsMenu);
Marco Gittler's avatar
Marco Gittler committed
568

569
    m_timelineContextTransitionMenu->addAction(actionCollection()->action("delete_item"));
570
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
571

572 573
    m_timelineContextTransitionMenu->addAction(actionCollection()->action("auto_transition"));

574 575
    connect(m_projectMonitorDock, SIGNAL(visibilityChanged(bool)), m_projectMonitor, SLOT(refreshMonitor(bool)));
    connect(m_clipMonitorDock, SIGNAL(visibilityChanged(bool)), m_clipMonitor, SLOT(refreshMonitor(bool)));
Laurent Montel's avatar
Laurent Montel committed
576
    connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
577 578
    connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));

579
    slotConnectMonitors();
580

581
#ifdef USE_JOGSHUTTLE
582
    activateShuttleDevice();
583
#endif
584
    m_projectListDock->raise();
585 586 587

    actionCollection()->addAssociatedWidget(m_clipMonitor->container());
    actionCollection()->addAssociatedWidget(m_projectMonitor->container());
588

589 590
    QList<QPair<QString, KAction *> > viewActions;
    QPair <QString, KAction *> pair;
591 592 593 594
    KAction *showTimeline = new KAction(i18n("Timeline"), this);
    showTimeline->setCheckable(true);
    showTimeline->setChecked(true);
    connect(showTimeline, SIGNAL(triggered(bool)), this, SLOT(slotShowTimeline(bool)));
595 596 597 598 599
    
    KMenu *viewMenu = static_cast<KMenu*>(factory()->container("dockwindows", this));
    pair.first = showTimeline->text();
    pair.second = showTimeline;
    viewActions.append(pair);
600 601
    
    QList <QDockWidget *> docks = findChildren<QDockWidget *>();
602
    for (int i = 0; i < docks.count(); ++i) {
603
        QDockWidget* dock = docks.at(i);
604 605
	QAction * a = dock->toggleViewAction();
	if (!a) continue;
606
	KAction* dockInformations = new KAction(this);
607
	dockInformations->setText(a->text());
608 609
	dockInformations->setCheckable(true);
	dockInformations->setChecked(!dock->isHidden());
610 611 612 613 614 615
	// HACK: since QActions cannot be used in KActionCategory to allow shortcut, we create a duplicate KAction of the dock QAction and link them
	connect(a,SIGNAL(toggled(bool)), dockInformations, SLOT(setChecked(bool)));
	connect(dockInformations,SIGNAL(triggered(bool)), a, SLOT(trigger()));
	pair.first = dockInformations->text();
	pair.second = dockInformations;
	viewActions.append(pair);
616 617
    }
    
618 619 620
    // Sort dock view action by name
    qSort(viewActions.begin(), viewActions.end(), sortByNames);
    // Populate view menu
621
    for (int i = 0; i < viewActions.count(); ++i)
622 623
	viewMenu->addAction(guiActions->addAction(viewActions.at(i).first, viewActions.at(i).second));
    
624
    // Populate encoding profiles
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
625
    KConfig conf("encodingprofiles.rc", KConfig::CascadeConfig, "appdata");
626
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
627 628 629 630 631 632 633 634 635 636
        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));
        }
    }
637
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
638 639 640 641 642 643 644 645 646 647
        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));
        }
    }
648 649 650 651 652 653 654 655 656 657 658
    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));
        }
    }
659
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
660 661 662 663 664 665 666 667 668 669
        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));
        }
    }
670 671
    
    connect (KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotChangePalette()));
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694

    // Open or create a file.  Command line argument passed in Url has
    // precedence, then "openlastproject", then just a plain empty file.
    // If opening Url fails, openlastproject will _not_ be used.
    if (!Url.isEmpty()) {
        // delay loading so that the window shows up
        m_startUrl = Url;
        QTimer::singleShot(500, this, SLOT(openFile()));
    } else if (KdenliveSettings::openlastproject()) {
        QTimer::singleShot(500, this, SLOT(openLastFile()));
    } else { //if (m_timelineArea->count() == 0) {
        newFile(false);
    }

    if (!clipsToLoad.isEmpty() && m_activeDocument) {
        QStringList list = clipsToLoad.split(',');
        QList <QUrl> urls;
        foreach(const QString &path, list) {
            kDebug() << QDir::current().absoluteFilePath(path);
            urls << QUrl::fromLocalFile(QDir::current().absoluteFilePath(path));
        }
        m_projectList->slotAddClip(urls);
    }
695
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
696

697
MainWindow::~MainWindow()
698
{
Laurent Montel's avatar
Laurent Montel committed
699
    delete m_stopmotion;
700 701

#ifdef USE_JOGSHUTTLE
Laurent Montel's avatar
Laurent Montel committed
702
    delete m_jogProcess;
703 704
#endif

705
    m_effectStack->slotClipItemSelected(NULL);
706 707
    m_transitionConfig->slotTransitionItemSelected(NULL, 0, QPoint(), false);

708 709
    if (m_projectMonitor) m_projectMonitor->stop();
    if (m_clipMonitor) m_clipMonitor->stop();
710

711
    delete m_activeTimeline;
712 713
    delete m_effectStack;
    delete m_transitionConfig;
714
    delete m_activeDocument;
715 716 717
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
Simon Eugster's avatar
Simon Eugster committed
718
    delete[] m_transitions;
719 720
    delete m_monitorManager;
    delete m_scopeManager;
721 722 723
    Mlt::Factory::close();
}

724
//virtual
725 726
bool MainWindow::queryClose()
{
727
    if (m_renderWidget) {
728
        int waitingJobs = m_renderWidget->waitingJobsCount();
729 730
        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")))) {
731 732
            case KMessageBox::Yes :
                // create script with waiting jobs and start it
733
                if (m_renderWidget->startWaitingRenderJobs() == false) return false;
734 735 736 737 738 739 740 741 742
                break;
            case KMessageBox::No :
                // Don't do anything, jobs will be deleted
                break;
            default:
                return false;
            }
        }
    }
743
    saveOptions();
744
    if (m_monitorManager) m_monitorManager->stopActiveMonitor();
745
    // warn the user to save if document is modified and we have clips in our project list
746
    if (m_activeDocument && m_activeDocument->isModified() &&
747 748
            ((m_projectList->documentClipList().isEmpty() && !m_activeDocument->url().isEmpty()) ||
             !m_projectList->documentClipList().isEmpty())) {
749 750
        raise();
        activateWindow();
751
        QString message;
752
        if (m_activeDocument->url().fileName().isEmpty())
753 754
            message = i18n("Save changes to document?");
        else
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
755
            message = i18n("The project <b>\"%1\"</b> has been changed.\nDo you want to save your changes?", m_activeDocument->url().fileName());
756
        switch (KMessageBox::warningYesNoCancel(this, message)) {
757 758
        case KMessageBox::Yes :
            // save document here. If saving fails, return false;
759
            return saveFile();