mainwindow.cpp 193 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
static const int ID_TIMELINE_POS = 0;
130 131
namespace Mlt
{
132 133 134
class Producer;
};

135 136
Q_DECLARE_METATYPE(QVector<int16_t>)

137

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

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

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

167 168
    // Init locale
    QLocale systemLocale = QLocale();
169 170 171 172
    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------";
173 174 175 176 177
        // 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();
178
    }
179

180 181 182
    systemLocale.setNumberOptions(QLocale::OmitGroupSeparator);
    QLocale::setDefault(systemLocale);

183 184 185 186
    // Create DBus interface
    new MainWindowAdaptor(this);
    QDBusConnection dbus = QDBusConnection::sessionBus();
    dbus.registerObject("/MainWindow", this);
187

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

    QToolButton *closeTabButton = new QToolButton;
201
    connect(closeTabButton, SIGNAL(clicked()), this, SLOT(closeCurrentDocument()));
202 203 204 205
    closeTabButton->setIcon(KIcon("tab-close"));
    closeTabButton->adjustSize();
    closeTabButton->setToolTip(i18n("Close the current tab"));
    m_timelineArea->setCornerWidget(closeTabButton);
206
    //connect(m_timelineArea, SIGNAL(currentChanged(int)), this, SLOT(activateDocument()));
207

208 209 210
    connect(&m_findTimer, SIGNAL(timeout()), this, SLOT(findTimeout()));
    m_findTimer.setSingleShot(true);

Ray Lehtiniemi's avatar
Ray Lehtiniemi committed
211
    // FIXME: the next call returns a newly allocated object, which leaks
Marco Gittler's avatar
Marco Gittler committed
212
    initEffects::parseEffectFiles();
213
    //initEffects::parseCustomEffectsFile();
214
    
215 216
    m_monitorManager = new MonitorManager();

Simon Eugster's avatar
Simon Eugster committed
217 218 219 220 221 222
    m_shortcutRemoveFocus = new QShortcut(QKeySequence("Esc"), this);
    connect(m_shortcutRemoveFocus, SIGNAL(activated()), this, SLOT(slotRemoveFocus()));


    /// Add Widgets ///

223 224
    m_projectListDock = new QDockWidget(i18n("Project Tree"), this);
    m_projectListDock->setObjectName("project_tree");
225
    m_projectList = new ProjectList();
226 227
    m_projectListDock->setWidget(m_projectList);
    addDockWidget(Qt::TopDockWidgetArea, m_projectListDock);
228

229 230
    m_clipMonitorDock = new QDockWidget(i18n("Clip Monitor"), this);
    m_clipMonitorDock->setObjectName("clip_monitor");
231
    m_clipMonitor = new Monitor(Kdenlive::clipMonitor, m_monitorManager, QString(), m_timelineArea);
232
    m_clipMonitorDock->setWidget(m_clipMonitor);
233

234
    // Connect the project list
235
    connect(m_projectList, SIGNAL(clipSelected(DocClipBase *, QPoint, bool)), m_clipMonitor, SLOT(slotSetClipProducer(DocClipBase *, QPoint, bool)));
236
    connect(m_projectList, SIGNAL(raiseClipMonitor()), m_clipMonitor, SLOT(slotActivateMonitor()));
237 238 239 240 241
    connect(m_projectList, SIGNAL(loadingIsOver()), this, SLOT(slotElapsedTime()));
    connect(m_projectList, SIGNAL(displayMessage(const QString&, int)), this, SLOT(slotGotProgressInfo(const QString&, int)));
    connect(m_projectList, SIGNAL(updateRenderStatus()), this, SLOT(slotCheckRenderStatus()));
    connect(m_projectList, SIGNAL(clipNeedsReload(const QString&)),this, SLOT(slotUpdateClip(const QString &)));
    connect(m_projectList, SIGNAL(updateProfile(const QString &)), this, SLOT(slotUpdateProjectProfile(const QString &)));
242
    connect(m_projectList, SIGNAL(refreshClip(const QString &, bool)), m_monitorManager, SLOT(slotRefreshCurrentMonitor(const QString &)));
243 244
    connect(m_projectList, SIGNAL(findInTimeline(const QString&)), this, SLOT(slotClipInTimeline(const QString&)));
    connect(m_clipMonitor, SIGNAL(zoneUpdated(QPoint)), m_projectList, SLOT(slotUpdateClipCut(QPoint)));
245
    connect(m_clipMonitor, SIGNAL(extractZone(const QString &, QPoint)), m_projectList, SLOT(slotCutClipJob(const QString &, QPoint)));
246

247 248
    m_projectMonitorDock = new QDockWidget(i18n("Project Monitor"), this);
    m_projectMonitorDock->setObjectName("project_monitor");
249
    m_projectMonitor = new Monitor(Kdenlive::projectMonitor, m_monitorManager, QString());
250
    m_projectMonitorDock->setWidget(m_projectMonitor);
251

252
#ifndef Q_WS_MAC
253 254
    m_recMonitorDock = new QDockWidget(i18n("Record Monitor"), this);
    m_recMonitorDock->setObjectName("record_monitor");
255
    m_recMonitor = new RecMonitor(Kdenlive::recordMonitor, m_monitorManager);
256
    m_recMonitorDock->setWidget(m_recMonitor);
257
    connect(m_recMonitor, SIGNAL(addProjectClip(KUrl)), this, SLOT(slotAddProjectClip(KUrl)));
258
    connect(m_recMonitor, SIGNAL(addProjectClipList(KUrl::List)), this, SLOT(slotAddProjectClipList(KUrl::List)));
259
    connect(m_recMonitor, SIGNAL(showConfigDialog(int, int)), this, SLOT(slotPreferences(int, int)));
260

261
#endif /* ! Q_WS_MAC */
262
    m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor, m_recMonitor);
263

264 265
    m_notesDock = new QDockWidget(i18n("Project Notes"), this);
    m_notesDock->setObjectName("notes_widget");
266 267 268
    m_notesWidget = new NotesWidget();
    connect(m_notesWidget, SIGNAL(insertNotesTimecode()), this, SLOT(slotInsertNotesTimecode()));
    connect(m_notesWidget, SIGNAL(seekProject(int)), m_projectMonitor->render, SLOT(seekToFrame(int)));
269

270
    m_notesWidget->setTabChangesFocus(true);
271
#if KDE_IS_VERSION(4,4,0)
272
    m_notesWidget->setClickMessage(i18n("Enter your project notes here ..."));
273
#endif
274 275 276
    m_notesDock->setWidget(m_notesWidget);
    addDockWidget(Qt::TopDockWidgetArea, m_notesDock);

277 278
    m_effectStackDock = new QDockWidget(i18n("Effect Stack"), this);
    m_effectStackDock->setObjectName("effect_stack");
279
    m_effectStack = new EffectStackView2(m_projectMonitor);
280 281
    m_effectStackDock->setWidget(m_effectStack);
    addDockWidget(Qt::TopDockWidgetArea, m_effectStackDock);
282
    connect(m_effectStack, SIGNAL(startFilterJob(ItemInfo, const QString&,const QString&,const QString&,const QString&,const QString&,const QString&,const QString&)), m_projectList, SLOT(slotStartFilterJob(ItemInfo, const QString&,const QString&,const QString&,const QString&,const QString&,const QString&,const QString&)));
283 284 285

    m_transitionConfigDock = new QDockWidget(i18n("Transition"), this);
    m_transitionConfigDock->setObjectName("transition");
286
    m_transitionConfig = new TransitionSettings(m_projectMonitor);
287 288 289
    m_transitionConfigDock->setWidget(m_transitionConfig);
    addDockWidget(Qt::TopDockWidgetArea, m_transitionConfigDock);

Simon Eugster's avatar
Simon Eugster committed
290 291 292 293 294 295
    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);

296 297
    m_scopeManager = new ScopeManager(m_monitorManager);
    m_vectorscope = new Vectorscope();
Simon Eugster's avatar
Simon Eugster committed
298
    m_vectorscopeDock = new QDockWidget(i18n("Vectorscope"), this);
Simon Eugster's avatar
Simon Eugster committed
299
    m_vectorscopeDock->setObjectName(m_vectorscope->widgetName());
Simon Eugster's avatar
Simon Eugster committed
300 301
    m_vectorscopeDock->setWidget(m_vectorscope);
    addDockWidget(Qt::TopDockWidgetArea, m_vectorscopeDock);
302
    m_scopeManager->addScope(m_vectorscope, m_vectorscopeDock);
Simon Eugster's avatar
Simon Eugster committed
303

304
    m_waveform = new Waveform();
Simon Eugster's avatar
Simon Eugster committed
305
    m_waveformDock = new QDockWidget(i18n("Waveform"), this);
Simon Eugster's avatar
Simon Eugster committed
306
    m_waveformDock->setObjectName(m_waveform->widgetName());
Simon Eugster's avatar
Simon Eugster committed
307 308
    m_waveformDock->setWidget(m_waveform);
    addDockWidget(Qt::TopDockWidgetArea, m_waveformDock);
309
    m_scopeManager->addScope(m_waveform, m_waveformDock);
Simon Eugster's avatar
Simon Eugster committed
310

311
    m_RGBParade = new RGBParade();
312
    m_RGBParadeDock = new QDockWidget(i18n("RGB Parade"), this);
Simon Eugster's avatar
Simon Eugster committed
313 314 315
    m_RGBParadeDock->setObjectName(m_RGBParade->widgetName());
    m_RGBParadeDock->setWidget(m_RGBParade);
    addDockWidget(Qt::TopDockWidgetArea, m_RGBParadeDock);
316
    m_scopeManager->addScope(m_RGBParade, m_RGBParadeDock);
317

318
    m_histogram = new Histogram();
319 320 321 322
    m_histogramDock = new QDockWidget(i18n("Histogram"), this);
    m_histogramDock->setObjectName(m_histogram->widgetName());
    m_histogramDock->setWidget(m_histogram);
    addDockWidget(Qt::TopDockWidgetArea, m_histogramDock);
323
    m_scopeManager->addScope(m_histogram, m_histogramDock);
Simon Eugster's avatar
Simon Eugster committed
324

325 326
    m_audiosignal = new AudioSignal;
    m_audiosignalDock = new QDockWidget(i18n("Audio Signal"), this);
327 328
    m_audiosignalDock->setObjectName("audiosignal");
    m_audiosignalDock->setWidget(m_audiosignal);
329
    addDockWidget(Qt::TopDockWidgetArea, m_audiosignalDock);
330
    m_scopeManager->addScope(m_audiosignal, m_audiosignalDock);
331

332
    m_audioSpectrum = new AudioSpectrum();
333 334 335 336
    m_audioSpectrumDock = new QDockWidget(i18n("AudioSpectrum"), this);
    m_audioSpectrumDock->setObjectName(m_audioSpectrum->widgetName());
    m_audioSpectrumDock->setWidget(m_audioSpectrum);
    addDockWidget(Qt::TopDockWidgetArea, m_audioSpectrumDock);
337
    m_scopeManager->addScope(m_audioSpectrum, m_audioSpectrumDock);
Simon Eugster's avatar
Simon Eugster committed
338

Simon Eugster's avatar
Simon Eugster committed
339 340 341 342 343
    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);
344
    m_scopeManager->addScope(m_spectrogram, m_spectrogramDock);
Simon Eugster's avatar
Simon Eugster committed
345 346 347 348 349 350 351 352

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

353 354
    m_undoViewDock = new QDockWidget(i18n("Undo History"), this);
    m_undoViewDock->setObjectName("undo_history");
355
    m_undoView = new QUndoView();
356 357
    m_undoView->setCleanIcon(KIcon("edit-clear"));
    m_undoView->setEmptyLabel(i18n("Clean"));
358
    m_undoViewDock->setWidget(m_undoView);
359
    m_undoView->setGroup(m_commandStack);
360
    addDockWidget(Qt::TopDockWidgetArea, m_undoViewDock);
361

Simon Eugster's avatar
Simon Eugster committed
362

363
    setupActions();
364
    connect(m_commandStack, SIGNAL(cleanChanged(bool)), m_saveAction, SLOT(setDisabled(bool)));
Simon Eugster's avatar
Simon Eugster committed
365

Simon Eugster's avatar
Simon Eugster committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381

    // 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
382
    /// Tabify Widgets ///
Simon Eugster's avatar
Simon Eugster committed
383 384
    tabifyDockWidget(m_effectListDock, m_effectStackDock);
    tabifyDockWidget(m_effectListDock, m_transitionConfigDock);
385
    tabifyDockWidget(m_projectListDock, m_notesDock);
386

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

    m_fileOpenRecent = KStandardAction::openRecent(this, SLOT(openFile(const KUrl &)), actionCollection());
    readOptions();
    m_fileRevert = KStandardAction::revert(this, SLOT(slotRevert()), actionCollection());
    m_fileRevert->setEnabled(false);

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

414 415 416 417 418 419 420 421 422 423 424 425 426
    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);
427 428

    // Build effects menu
429
    m_effectsMenu = new QMenu(i18n("Add Effect"));
430 431 432
    m_effectActions = new KActionCategory(i18n("Effects"), actionCollection());
    m_effectList->reloadEffectList(m_effectsMenu, m_effectActions);
    m_effectsActionCollection->readSettings();
433

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

437
    setupGUI();
438

439 440
    // Find QDockWidget tab bars and show / hide widget title bars on right click
    QList <QTabBar *> tabs = findChildren<QTabBar *>();
441
    for (int i = 0; i < tabs.count(); i++) {
442 443 444 445
        tabs.at(i)->setContextMenuPolicy(Qt::CustomContextMenu);
        connect(tabs.at(i), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotSwitchTitles()));
    }

446 447
    /*ScriptingPart* sp = new ScriptingPart(this, QStringList());
    guiFactory()->addClient(sp);*/
448 449 450
    QMenu *trackMenu = (QMenu*)(factory()->container("track_menu", this));
    if (trackMenu) trackMenu->addActions(m_tracksActionCollection->actions());

451

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
452
    QMenu *saveLayout = (QMenu*)(factory()->container("layout_save_as", this));
453
    if (saveLayout)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
454 455 456 457 458
        connect(saveLayout, SIGNAL(triggered(QAction*)), this, SLOT(slotSaveLayout(QAction*)));


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

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
460
    loadPlugins();
461
    loadTranscoders();
462
    loadStabilize();
463

464
    m_projectMonitor->setupMenu(static_cast<QMenu*>(factory()->container("monitor_go", this)), m_playZone, m_loopZone, NULL, m_loopClip);
465
    m_clipMonitor->setupMenu(static_cast<QMenu*>(factory()->container("monitor_go", this)), m_playZone, m_loopZone, static_cast<QMenu*>(factory()->container("marker_menu", this)));
466

Till Theato's avatar
Till Theato committed
467 468
    QMenu *clipInTimeline = static_cast<QMenu*>(factory()->container("clip_in_timeline", this));
    clipInTimeline->setIcon(KIcon("go-jump"));
469 470
	QHash<QString,QMenu*> menus;
	menus.insert("addMenu",static_cast<QMenu*>(factory()->container("generators", this)));
471
        menus.insert("extractAudioMenu",static_cast<QMenu*>(factory()->container("extract_audio", this)));
472 473 474 475
	menus.insert("transcodeMenu",static_cast<QMenu*>(factory()->container("transcoders", this)));
	menus.insert("stabilizeMenu",static_cast<QMenu*>(factory()->container("stabilize", this)));
	menus.insert("inTimelineMenu",clipInTimeline);
    m_projectList->setupGeneratorMenu(menus);
Marco Gittler's avatar
Marco Gittler committed
476

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

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

488
    for (int i = 0; i < schemeFiles.size(); ++i) {
489 490 491 492 493 494 495 496 497
        // 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());
498
        action = new KAction(name, this);
499 500 501 502
        action->setData(filename);
        action->setIcon(icon);
        action->setCheckable(true);
        themegroup->addAction(action);
503
        if (KdenliveSettings::colortheme() == filename) action->setChecked(true);
504 505 506
    }

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

520
    // Setup and fill effects and transitions menus.
521 522 523


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

526

527
    m_transitionsMenu = new QMenu(i18n("Add Transition"), this);
528
    for (int i = 0; i < transitions.count(); ++i)
529 530
        m_transitionsMenu->addAction(m_transitions[i]);

531
    connect(m, SIGNAL(triggered(QAction *)), this, SLOT(slotAddVideoEffect(QAction *)));
532
    connect(m_effectsMenu, SIGNAL(triggered(QAction *)), this, SLOT(slotAddVideoEffect(QAction *)));
533
    connect(m_transitionsMenu, SIGNAL(triggered(QAction *)), this, SLOT(slotAddTransition(QAction *)));    
534

535 536 537 538
    m_timelineContextMenu = new QMenu(this);
    m_timelineContextClipMenu = new QMenu(this);
    m_timelineContextTransitionMenu = new QMenu(this);

539
    m_timelineContextMenu->addAction(actionCollection()->action("insert_space"));
540
    m_timelineContextMenu->addAction(actionCollection()->action("delete_space"));
541 542
    m_timelineContextMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Paste)));

543
    m_timelineContextClipMenu->addAction(actionCollection()->action("clip_in_project_tree"));
544
    //m_timelineContextClipMenu->addAction(actionCollection()->action("clip_to_project_tree"));
545
    m_timelineContextClipMenu->addAction(actionCollection()->action("delete_item"));
546
    m_timelineContextClipMenu->addSeparator();
547 548
    m_timelineContextClipMenu->addAction(actionCollection()->action("group_clip"));
    m_timelineContextClipMenu->addAction(actionCollection()->action("ungroup_clip"));
549
    m_timelineContextClipMenu->addAction(actionCollection()->action("split_audio"));
550 551
    m_timelineContextClipMenu->addAction(actionCollection()->action("set_audio_align_ref"));
    m_timelineContextClipMenu->addAction(actionCollection()->action("align_audio"));
552
    m_timelineContextClipMenu->addSeparator();
553
    m_timelineContextClipMenu->addAction(actionCollection()->action("cut_timeline_clip"));
554
    m_timelineContextClipMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
555
    m_timelineContextClipMenu->addAction(actionCollection()->action("paste_effects"));
556
    m_timelineContextClipMenu->addSeparator();
557

558 559
    QMenu *markersMenu = (QMenu*)(factory()->container("marker_menu", this));
    m_timelineContextClipMenu->addMenu(markersMenu);
560
    m_timelineContextClipMenu->addSeparator();
561
    m_timelineContextClipMenu->addMenu(m_transitionsMenu);
562
    m_timelineContextClipMenu->addMenu(m_effectsMenu);
Marco Gittler's avatar
Marco Gittler committed
563

564
    m_timelineContextTransitionMenu->addAction(actionCollection()->action("delete_item"));
565
    m_timelineContextTransitionMenu->addAction(actionCollection()->action(KStandardAction::name(KStandardAction::Copy)));
566

567 568
    m_timelineContextTransitionMenu->addAction(actionCollection()->action("auto_transition"));

569 570
    connect(m_projectMonitorDock, SIGNAL(visibilityChanged(bool)), m_projectMonitor, SLOT(refreshMonitor(bool)));
    connect(m_clipMonitorDock, SIGNAL(visibilityChanged(bool)), m_clipMonitor, SLOT(refreshMonitor(bool)));
571
    connect(m_effectList, SIGNAL(addEffect(const QDomElement)), this, SLOT(slotAddEffect(const QDomElement)));
572 573
    connect(m_effectList, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));

574
    slotConnectMonitors();
575

576 577 578
    // 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.
579
    if (!Url.isEmpty()) {
580 581 582
        // delay loading so that the window shows up
        m_startUrl = Url;
        QTimer::singleShot(500, this, SLOT(openFile()));
583
    } else if (KdenliveSettings::openlastproject()) {
584
        QTimer::singleShot(500, this, SLOT(openLastFile()));
585
    } else { //if (m_timelineArea->count() == 0) {
586
        newFile(false);
587
    }
588

589
    if (!clipsToLoad.isEmpty() && m_activeDocument) {
590 591 592 593 594 595 596 597 598
        QStringList list = clipsToLoad.split(',');
        QList <QUrl> urls;
        foreach(QString path, list) {
            kDebug() << QDir::current().absoluteFilePath(path);
            urls << QUrl::fromLocalFile(QDir::current().absoluteFilePath(path));
        }
        m_projectList->slotAddClip(urls);
    }

599
#ifdef USE_JOGSHUTTLE
600
    activateShuttleDevice();
601
#endif
602
    m_projectListDock->raise();
603 604 605

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

607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
    QMap <QString, KAction *> viewActions;
    KAction *showTimeline = new KAction(i18n("Timeline"), this);
    showTimeline->setCheckable(true);
    showTimeline->setChecked(true);
    connect(showTimeline, SIGNAL(triggered(bool)), this, SLOT(slotShowTimeline(bool)));
    viewActions.insert(showTimeline->text(), showTimeline);
    
    QList <QDockWidget *> docks = findChildren<QDockWidget *>();
    for (int i = 0; i < docks.count(); i++) {
        QDockWidget* dock = docks.at(i);
	KAction* dockInformations = new KAction(this);
	dockInformations->setText(dock->windowTitle());
	dockInformations->setCheckable(true);
	dockInformations->setChecked(!dock->isHidden());
	connect(dockInformations,SIGNAL(toggled(bool)), dock, SLOT(setVisible(bool)));
	viewActions.insert(dockInformations->text(), dockInformations);
    }


    KMenu *viewMenu = static_cast<KMenu*>(factory()->container("dockwindows", this));
    //const QList<QAction *> viewActions = createPopupMenu()->actions();
    QMap<QString, KAction *>::const_iterator i = viewActions.constBegin();
    while (i != viewActions.constEnd()) {
	viewMenu->addAction(guiActions->addAction(i.key(), i.value()));
	++i;
    }
    
634 635
    // Populate encoding profiles
    KConfig conf("encodingprofiles.rc", KConfig::FullConfig, "appdata");
636
    if (KdenliveSettings::proxyparams().isEmpty() || KdenliveSettings::proxyextension().isEmpty()) {
637 638 639 640 641 642 643 644 645 646
        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));
        }
    }
647
    if (KdenliveSettings::v4l_parameters().isEmpty() || KdenliveSettings::v4l_extension().isEmpty()) {
648 649 650 651 652 653 654 655 656 657
        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));
        }
    }
658
    if (KdenliveSettings::decklink_parameters().isEmpty() || KdenliveSettings::decklink_extension().isEmpty()) {
659 660 661 662 663 664 665 666 667 668
        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));
        }
    }
669 670
    
    connect (KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotChangePalette()));
671
}
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
672

673
MainWindow::~MainWindow()
674
{
675 676 677
    if (m_stopmotion) {
        delete m_stopmotion;
    }
678
    m_effectStack->slotClipItemSelected(NULL);
679 680
    m_transitionConfig->slotTransitionItemSelected(NULL, 0, QPoint(), false);

681 682
    if (m_projectMonitor) m_projectMonitor->stop();
    if (m_clipMonitor) m_clipMonitor->stop();
683

684
    delete m_activeTimeline;
685 686
    delete m_effectStack;
    delete m_transitionConfig;
687
    delete m_activeDocument;
688 689 690
    delete m_projectMonitor;
    delete m_clipMonitor;
    delete m_shortcutRemoveFocus;
Simon Eugster's avatar
Simon Eugster committed
691
    delete[] m_transitions;
692 693
    delete m_monitorManager;
    delete m_scopeManager;
694 695 696
    Mlt::Factory::close();
}

697
//virtual
698 699
bool MainWindow::queryClose()
{
700
    if (m_renderWidget) {
701
        int waitingJobs = m_renderWidget->waitingJobsCount();
702 703
        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")))) {
704 705
            case KMessageBox::Yes :
                // create script with waiting jobs and start it
706
                if (m_renderWidget->startWaitingRenderJobs() == false) return false;
707 708 709 710 711 712 713 714 715
                break;
            case KMessageBox::No :
                // Don't do anything, jobs will be deleted
                break;
            default:
                return false;
            }
        }
    }
716
    saveOptions();
717
    if (m_monitorManager) m_monitorManager->stopActiveMonitor();
718
    // warn the user to save if document is modified and we have clips in our project list
719
    if (m_activeDocument && m_activeDocument->isModified() &&
720 721
            ((m_projectList->documentClipList().isEmpty() && !m_activeDocument->url().isEmpty()) ||
             !m_projectList->documentClipList().isEmpty())) {
722 723
        raise();
        activateWindow();
724
        QString message;
725
        if (m_activeDocument->url().fileName().isEmpty())
726 727
            message = i18n("Save changes to document?");
        else
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
728
            message = i18n("The project <b>\"%1\"</b> has been changed.\nDo you want to save your changes?", m_activeDocument->url().fileName());
729
        switch (KMessageBox::warningYesNoCancel(this, message)) {
730 731
        case KMessageBox::Yes :
            // save document here. If saving fails, return false;
732
            return saveFile();
733
        case KMessageBox::No :
734 735
            // User does not want to save the changes, clear recovery files
            m_activeDocument->m_autosave->resize(0);
736 737 738 739
            return true;
        default: // cancel
            return false;
        }
740
    }
741
    return true;
742 743
}

744 745
void MainWindow::loadPlugins()
{
746
    foreach(QObject * plugin, QPluginLoader::staticInstances()) {
747 748
        populateMenus(plugin);
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
749 750 751 752

    QStringList directories = KGlobal::dirs()->findDirs("module", QString());
    QStringList filters;
    filters << "libkdenlive*";
753
    foreach(const QString & folder, directories) {
754
        kDebug() << "Parsing plugin folder: " << folder;
755
        QDir pluginsDir(folder);
756
        foreach(const QString & fileName,
757 758 759 760 761
                pluginsDir.entryList(filters, QDir::Files)) {
            /*
             * Avoid loading the same plugin twice when there is more than one
             * installation.
             */
762
            if (!m_pluginFileNames.contains(fileName)) {
763 764 765
                kDebug() << "Found plugin: " << fileName;
                QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
                QObject *plugin = loader.instance();
766
                if (plugin) {
767 768 769 770 771
                    populateMenus(plugin);
                    m_pluginFileNames += fileName;
                } else
                    kDebug() << "Error loading plugin: " << fileName << ", " << loader.errorString();
            }
772
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
773 774 775
    }
}

776 777
void MainWindow::populateMenus(QObject *plugin)
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
778 779
    QMenu *addMenu = static_cast<QMenu*>(factory()->container("generators", this));
    ClipGenerator *iGenerator = qobject_cast<ClipGenerator *>(plugin);
780
    if (iGenerator)
781
        addToMenu(plugin, iGenerator->generators(KdenliveSettings::producerslist()), addMenu, SLOT(generateClip()),
782
                  NULL);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
783 784 785
}

void MainWindow::addToMenu(QObject *plugin, const QStringList &texts,
786
                           QMenu *menu, const char *member,
787 788
                           QActionGroup *actionGroup)
{