]> git.sesse.net Git - kdenlive/blob - src/mainwindow.cpp
8004e65b6584cb73d8b2e961394eb86911624d10
[kdenlive] / src / mainwindow.cpp
1 /***************************************************************************
2  *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21
22 #include <QTextStream>
23 #include <QTimer>
24 #include <QAction>
25 #include <QtTest>
26 #include <QtCore>
27
28 #include <KApplication>
29 #include <KAction>
30 #include <KLocale>
31 #include <KActionCollection>
32 #include <KStandardAction>
33 #include <KFileDialog>
34 #include <KMessageBox>
35 #include <KDebug>
36 #include <KIO/NetAccess>
37 #include <KSaveFile>
38 #include <KRuler>
39 #include <KConfigDialog>
40 #include <KXMLGUIFactory>
41 #include <KStatusBar>
42 #include <kstandarddirs.h>
43 #include <KUrlRequesterDialog>
44
45 #include <mlt++/Mlt.h>
46
47 #include "mainwindow.h"
48 #include "kdenlivesettings.h"
49 #include "kdenlivesettingsdialog.h"
50 #include "initeffects.h"
51 #include "profilesdialog.h"
52 #include "projectsettings.h"
53 #include "events.h"
54
55 #define ID_STATUS_MSG 1
56 #define ID_EDITMODE_MSG 2
57 #define ID_TIMELINE_MSG 3
58 #define ID_TIMELINE_POS 4
59 #define ID_TIMELINE_FORMAT 5
60  
61 MainWindow::MainWindow(QWidget *parent)
62     : KXmlGuiWindow(parent),
63       fileName(QString()), m_activeDocument(NULL), m_commandStack(NULL)
64 {
65   parseProfiles();
66   m_timelineArea = new KTabWidget(this);
67   m_timelineArea->setHoverCloseButton(true);
68   m_timelineArea->setTabReorderingEnabled(true);
69   connect(m_timelineArea, SIGNAL(currentChanged (int)), this, SLOT(activateDocument()));
70
71   initEffects::parseEffectFiles( &m_audioEffects, &m_videoEffects );
72   m_monitorManager = new MonitorManager();
73
74   projectListDock = new QDockWidget(i18n("Project Tree"), this);
75   projectListDock->setObjectName("project_tree");
76   m_projectList = new ProjectList(this);
77   projectListDock->setWidget(m_projectList);
78   addDockWidget(Qt::TopDockWidgetArea, projectListDock);
79
80   effectListDock = new QDockWidget(i18n("Effect List"), this);
81   effectListDock->setObjectName("effect_list");
82   m_effectList = new EffectsListView(&m_audioEffects, &m_videoEffects, &m_customEffects);
83
84   //m_effectList = new KListWidget(this);
85   effectListDock->setWidget(m_effectList);
86   addDockWidget(Qt::TopDockWidgetArea, effectListDock);
87   
88   effectStackDock = new QDockWidget(i18n("Effect Stack"), this);
89   effectStackDock->setObjectName("effect_stack");
90   effectStack = new EffectStackView(&m_audioEffects, &m_videoEffects, &m_customEffects,this);
91   effectStackDock->setWidget(effectStack);
92   addDockWidget(Qt::TopDockWidgetArea, effectStackDock);
93   
94   transitionConfigDock = new QDockWidget(i18n("Transition"), this);
95   transitionConfigDock->setObjectName("transition");
96   transitionConfig = new KListWidget(this);
97   transitionConfigDock->setWidget(transitionConfig);
98   addDockWidget(Qt::TopDockWidgetArea, transitionConfigDock);
99
100
101   clipMonitorDock = new QDockWidget(i18n("Clip Monitor"), this);
102   clipMonitorDock->setObjectName("clip_monitor");
103   m_clipMonitor = new Monitor("clip", m_monitorManager, this);
104   clipMonitorDock->setWidget(m_clipMonitor);
105   addDockWidget(Qt::TopDockWidgetArea, clipMonitorDock);
106   //m_clipMonitor->stop();
107
108   projectMonitorDock = new QDockWidget(i18n("Project Monitor"), this);
109   projectMonitorDock->setObjectName("project_monitor");
110   m_projectMonitor = new Monitor("project", m_monitorManager, this);
111   projectMonitorDock->setWidget(m_projectMonitor);
112   addDockWidget(Qt::TopDockWidgetArea, projectMonitorDock);
113
114   undoViewDock = new QDockWidget(i18n("Undo History"), this);
115   undoViewDock->setObjectName("undo_history");
116   m_undoView = new QUndoView(this);
117   undoViewDock->setWidget(m_undoView);
118   m_undoView->setStack(m_commandStack);
119   addDockWidget(Qt::TopDockWidgetArea, undoViewDock);
120
121   overviewDock = new QDockWidget(i18n("Project Overview"), this);
122   overviewDock->setObjectName("project_overview");
123   m_overView = new CustomTrackView(NULL, NULL, this);
124   overviewDock->setWidget(m_overView);
125   addDockWidget(Qt::TopDockWidgetArea, overviewDock);
126
127   setupActions();
128   tabifyDockWidget (projectListDock, effectListDock);
129   tabifyDockWidget (projectListDock, effectStackDock);
130   tabifyDockWidget (projectListDock, transitionConfigDock);
131   tabifyDockWidget (projectListDock, undoViewDock);
132   projectListDock->raise();
133
134   tabifyDockWidget (clipMonitorDock, projectMonitorDock);
135   setCentralWidget(m_timelineArea);
136
137   m_timecodeFormat = new KComboBox(this);
138   m_timecodeFormat->addItem(i18n("hh:mm:ss::ff"));
139   m_timecodeFormat->addItem(i18n("Frames"));
140
141   statusProgressBar=new QProgressBar(this);
142   statusProgressBar->setMinimum(0);
143   statusProgressBar->setMaximum(100);
144   statusProgressBar->setMaximumWidth(150);
145   statusProgressBar->setVisible(false);
146   statusLabel=new QLabel(this); 
147
148   statusBar()->insertPermanentWidget(0,statusProgressBar,1);
149   statusBar()->insertPermanentWidget(1,statusLabel,1);
150   statusBar()->insertPermanentFixedItem("00:00:00:00", ID_TIMELINE_POS);
151   statusBar()->insertPermanentWidget(ID_TIMELINE_FORMAT, m_timecodeFormat); 
152
153   setupGUI(Default, "kdenliveui.rc");
154
155   connect(projectMonitorDock, SIGNAL(visibilityChanged (bool)), m_projectMonitor, SLOT(refreshMonitor(bool)));
156   connect(clipMonitorDock, SIGNAL(visibilityChanged (bool)), m_clipMonitor, SLOT(refreshMonitor(bool)));
157   connect(m_monitorManager, SIGNAL(connectMonitors ()), this, SLOT(slotConnectMonitors()));
158   connect(m_monitorManager, SIGNAL(raiseClipMonitor (bool)), this, SLOT(slotRaiseMonitor(bool)));
159   connect(m_effectList, SIGNAL(addEffect(QDomElement)), this, SLOT(slotAddEffect(QDomElement)));
160   m_monitorManager->initMonitors(m_clipMonitor, m_projectMonitor);
161         
162   setAutoSaveSettings();
163   newFile();
164 }
165
166 //virtual
167 bool MainWindow::queryClose() 
168 {
169   saveOptions();
170   switch ( KMessageBox::warningYesNoCancel( this, i18n("Save changes to document ?")) ) {
171        case KMessageBox::Yes :
172          // save document here. If saving fails, return false;
173          return true;
174        case KMessageBox::No :
175          return true;
176        default: // cancel
177          return false;
178   }
179 }
180
181 void MainWindow::slotAddEffect(QDomElement effect, GenTime pos, int track)
182 {
183   if (!m_activeDocument) return;
184   /*QMap <QString, QString> filter;
185   if (effectType == 0)
186     filter = m_videoEffects.effect(effectName);
187   else if (effectType == 1)
188     filter = m_audioEffects.effect(effectName);
189   else 
190     filter = m_customEffects.effect(effectName);*/
191   if (effect.isNull()) {
192     kDebug()<<"--- ERROR, TRYING TO APPEND NULL EFFECT";
193     return;
194   }
195   TrackView *currentTimeLine = (TrackView *) m_timelineArea->currentWidget();
196   currentTimeLine->projectView()->slotAddEffect(effect, pos, track);
197 }
198
199 void MainWindow::slotRaiseMonitor(bool clipMonitor)
200 {
201   if (clipMonitor) clipMonitorDock->raise();
202   else projectMonitorDock->raise();
203 }
204
205 void MainWindow::slotSetClipDuration(int id, int duration)
206 {
207   if (!m_activeDocument) return;
208   m_activeDocument->setProducerDuration(id, duration);
209 }
210
211 void MainWindow::slotConnectMonitors()
212 {
213
214   m_projectList->setRenderer(m_clipMonitor->render);
215
216   connect(m_projectList, SIGNAL(clipSelected(const QDomElement &)), m_clipMonitor, SLOT(slotSetXml(const QDomElement &)));
217
218   connect(m_projectList, SIGNAL(receivedClipDuration(int, int)), this, SLOT(slotSetClipDuration(int, int)));
219
220   connect(m_projectList, SIGNAL(getFileProperties(const QDomElement &, int)), m_clipMonitor->render, SLOT(getFileProperties(const QDomElement &, int)));
221
222   connect(m_clipMonitor->render, SIGNAL(replyGetImage(int, int, const QPixmap &, int, int)), m_projectList, SLOT(slotReplyGetImage(int, int, const QPixmap &, int, int)));
223
224   connect(m_clipMonitor->render, SIGNAL(replyGetFileProperties(int, const QMap < QString, QString > &, const QMap < QString, QString > &)), m_projectList, SLOT(slotReplyGetFileProperties(int, const QMap < QString, QString > &, const QMap < QString, QString > &)));
225
226 }
227
228 void MainWindow::setupActions()
229 {
230   KAction* clearAction = new KAction(this);
231   clearAction->setText(i18n("Clear"));
232   clearAction->setIcon(KIcon("document-new"));
233   clearAction->setShortcut(Qt::CTRL + Qt::Key_W);
234   actionCollection()->addAction("clear", clearAction);
235   /*connect(clearAction, SIGNAL(triggered(bool)),
236           textArea, SLOT(clear()));*/
237
238   KAction* profilesAction = new KAction(this);
239   profilesAction->setText(i18n("Manage Profiles"));
240   profilesAction->setIcon(KIcon("document-new"));
241   actionCollection()->addAction("manage_profiles", profilesAction);
242   connect(profilesAction, SIGNAL(triggered(bool)), this, SLOT(slotEditProfiles()));
243
244   KAction* projectAction = new KAction(this);
245   projectAction->setText(i18n("Project Settings"));
246   projectAction->setIcon(KIcon("document-new"));
247   actionCollection()->addAction("project_settings", projectAction);
248   connect(projectAction, SIGNAL(triggered(bool)), this, SLOT(slotEditProjectSettings()));
249
250
251  
252   KStandardAction::quit(kapp, SLOT(quit()),
253                         actionCollection());
254  
255   KStandardAction::open(this, SLOT(openFile()),
256                         actionCollection());
257
258   m_fileOpenRecent = KStandardAction::openRecent(this, SLOT(openFile(const KUrl &)),
259                         actionCollection());
260
261   KStandardAction::save(this, SLOT(saveFile()),
262                         actionCollection());
263  
264   KStandardAction::saveAs(this, SLOT(saveFileAs()),
265                         actionCollection());
266  
267   KStandardAction::openNew(this, SLOT(newFile()),
268                         actionCollection());
269
270   KStandardAction::preferences(this, SLOT(slotPreferences()),
271                         actionCollection());
272
273   /*KStandardAction::undo(this, SLOT(undo()),
274                         actionCollection());
275
276   KStandardAction::redo(this, SLOT(redo()),
277                         actionCollection());*/
278
279   connect(actionCollection(), SIGNAL( actionHighlighted( QAction* ) ), 
280     this, SLOT( slotDisplayActionMessage( QAction* ) ) );
281   //connect(actionCollection(), SIGNAL( clearStatusText() ),
282     //statusBar(), SLOT( clear() ) );
283
284   readOptions();  
285
286   /*m_redo = m_commandStack->createRedoAction(actionCollection());
287   m_undo = m_commandStack->createUndoAction(actionCollection());*/
288 }
289
290 void MainWindow::slotDisplayActionMessage( QAction *a)
291 {
292   statusBar()->showMessage(a->data().toString(), 3000);
293 }
294
295 void MainWindow::saveOptions()
296 {
297   KSharedConfigPtr config = KGlobal::config ();
298   m_fileOpenRecent->saveEntries(KConfigGroup (config, "Recent Files"));
299   config->sync(); 
300 }
301
302 void MainWindow::readOptions()
303 {
304   KSharedConfigPtr config = KGlobal::config ();
305   m_fileOpenRecent->loadEntries(KConfigGroup (config, "Recent Files"));
306 }
307  
308 void MainWindow::newFile()
309 {
310   KdenliveDoc *doc = new KdenliveDoc(KUrl(), 25, 720, 576);
311   TrackView *trackView = new TrackView(doc);
312   m_timelineArea->addTab(trackView, "New Project");
313   if (m_timelineArea->count() == 1)
314     connectDocument(trackView, doc);
315 }
316
317 void MainWindow::activateDocument()
318 {
319   TrackView *currentTab = (TrackView *) m_timelineArea->currentWidget();
320   KdenliveDoc *currentDoc = currentTab->document();
321   connectDocument(currentTab, currentDoc);
322 }
323  
324 void MainWindow::saveFileAs(const QString &outputFileName)
325 {
326   KSaveFile file(outputFileName);
327   file.open();
328   
329   QByteArray outputByteArray;
330   //outputByteArray.append(textArea->toPlainText());
331   file.write(outputByteArray);
332   file.finalize();
333   file.close();
334   
335   fileName = outputFileName;
336 }
337
338 void MainWindow::saveFileAs()
339 {
340   saveFileAs(KFileDialog::getSaveFileName());
341 }
342  
343 void MainWindow::saveFile()
344 {
345   if(!fileName.isEmpty())
346   {
347     saveFileAs(fileName);
348   }
349   else
350   {
351     saveFileAs();
352   }
353 }
354  
355 void MainWindow::openFile() //changed
356 {
357     KUrl url = KFileDialog::getOpenUrl(KUrl(), "application/vnd.kde.kdenlive;*.kdenlive");
358     if (url.isEmpty()) return;
359     m_fileOpenRecent->addUrl (url);
360     openFile(url);
361 }
362  
363 void MainWindow::openFile(const KUrl &url) //new
364 {
365   KdenliveDoc *doc = new KdenliveDoc(url, 25, 720, 576);
366   TrackView *trackView = new TrackView(doc);
367   m_timelineArea->setCurrentIndex(m_timelineArea->addTab(trackView, QIcon(), doc->documentName()));
368   m_timelineArea->setTabToolTip(m_timelineArea->currentIndex(), doc->url().path());
369   //connectDocument(trackView, doc);
370 }
371
372
373 void MainWindow::parseProfiles()
374 {
375         //kdDebug()<<" + + YOUR MLT INSTALL WAS FOUND IN: "<< MLT_PREFIX <<endl;
376         if (KdenliveSettings::mltpath().isEmpty()) {
377             KdenliveSettings::setMltpath(QString(MLT_PREFIX) + QString("/share/mlt/profiles/"));
378         }
379         if (KdenliveSettings::rendererpath().isEmpty())
380         {
381             KdenliveSettings::setRendererpath(KStandardDirs::findExe("inigo"));
382         }
383         QStringList profilesFilter;
384         profilesFilter<<"*";
385         QStringList profilesList = QDir(KdenliveSettings::mltpath()).entryList(profilesFilter, QDir::Files);
386
387         if (profilesList.isEmpty()) {
388             // Cannot find MLT path, try finding inigo
389             QString profilePath = KdenliveSettings::rendererpath();
390             if (!profilePath.isEmpty()) {
391                 profilePath = profilePath.section('/', 0, -3);
392                 KdenliveSettings::setMltpath(profilePath + "/share/mlt/profiles/");
393                 QStringList profilesList = QDir(KdenliveSettings::mltpath()).entryList(profilesFilter, QDir::Files);
394             }
395
396             if (profilesList.isEmpty()) {
397                 // Cannot find the MLT profiles, ask for location
398                 KUrlRequesterDialog *getUrl = new KUrlRequesterDialog(KdenliveSettings::mltpath(), i18n("Cannot find your Mlt profiles, please give the path"), this);
399                 getUrl->fileDialog()->setMode(KFile::Directory);
400                 getUrl->exec();
401                 KUrl mltPath = getUrl->selectedUrl ();
402                 delete getUrl;
403                 if (mltPath.isEmpty()) exit(1);
404                 KdenliveSettings::setMltpath(mltPath.path());
405                 QStringList profilesList = QDir(KdenliveSettings::mltpath()).entryList(profilesFilter, QDir::Files);
406             }
407         }
408
409         if (KdenliveSettings::rendererpath().isEmpty()) {
410                 // Cannot find the MLT inigo renderer, ask for location
411                 KUrlRequesterDialog *getUrl = new KUrlRequesterDialog(KdenliveSettings::mltpath(), i18n("Cannot find the inigo program required for rendering (part of Mlt)"), this);
412                 getUrl->exec();
413                 KUrl rendererPath = getUrl->selectedUrl();
414                 delete getUrl;
415                 if (rendererPath.isEmpty()) exit(1);
416                 KdenliveSettings::setRendererpath(rendererPath.path());
417         }
418
419         kDebug()<<"RESULTING MLT PATH: "<<KdenliveSettings::mltpath();
420
421         // Parse MLT profiles to build a list of available video formats
422         if (profilesList.isEmpty()) parseProfiles();
423     }
424
425
426 void MainWindow::slotEditProfiles()
427 {
428   ProfilesDialog *w = new ProfilesDialog;
429   w->exec();
430   delete w;
431 }
432
433 void MainWindow::slotEditProjectSettings()
434 {
435   ProjectSettings *w = new ProjectSettings;
436   w->exec();
437   delete w;
438 }
439
440
441 void MainWindow::slotUpdateMousePosition(int pos)
442 {
443   if (m_activeDocument)
444     switch(m_timecodeFormat->currentIndex()) {
445       case 0:
446         statusBar()->changeItem(m_activeDocument->timecode().getTimecodeFromFrames(pos), ID_TIMELINE_POS);
447         break;
448     default:
449       statusBar()->changeItem(QString::number(pos), ID_TIMELINE_POS);
450     }
451 }
452
453 void MainWindow::connectDocument(TrackView *trackView, KdenliveDoc *doc) //changed
454 {
455   //m_projectMonitor->stop();
456   kDebug()<<"///////////////////   CONNECTING DOC TO PROJECT VIEW ////////////////";
457   if (m_activeDocument) {
458     if (m_activeDocument == doc) return;
459     m_activeDocument->setProducers(m_projectList->producersList());
460     m_activeDocument->setRenderer(NULL);
461   }
462   connect(trackView, SIGNAL(cursorMoved()), m_projectMonitor, SLOT(activateMonitor()));
463   connect(trackView, SIGNAL(mousePosition(int)), this, SLOT(slotUpdateMousePosition(int)));
464   connect(m_projectMonitor, SIGNAL(renderPosition(int)), trackView, SLOT(moveCursorPos(int)));
465   connect(m_projectMonitor, SIGNAL(durationChanged(int)), trackView->projectView(), SLOT(setDuration(int)));
466   connect(doc, SIGNAL(addProjectClip(DocClipBase *)), m_projectList, SLOT(slotAddClip(DocClipBase *)));
467   connect(doc, SIGNAL(signalDeleteProjectClip(int)), m_projectList, SLOT(slotDeleteClip(int)));
468   connect(doc, SIGNAL(updateClipDisplay(int)), m_projectList, SLOT(slotUpdateClip(int)));
469   connect(doc, SIGNAL(deletTimelineClip(int)), trackView, SLOT(slotDeleteClip(int)));
470   connect(doc, SIGNAL(thumbsProgress(KUrl, int)), this, SLOT(slotGotProgressInfo(KUrl, int)));
471
472   connect(trackView, SIGNAL(clipItemSelected(ClipItem*)), effectStack, SLOT(slotClipItemSelected(ClipItem*)));
473   connect(effectStack, SIGNAL(updateClipEffect(ClipItem*, QDomElement, QDomElement)), trackView->projectView(), SLOT(slotUpdateClipEffect(ClipItem*, QDomElement, QDomElement)));
474   connect(effectStack, SIGNAL(removeEffect(ClipItem*, QDomElement)), trackView->projectView(), SLOT(slotDeleteEffect(ClipItem*, QDomElement)));
475   connect(effectStack, SIGNAL(changeEffectState(ClipItem*, QDomElement, bool)), trackView->projectView(), SLOT(slotChangeEffectState(ClipItem*, QDomElement, bool)));
476   connect(effectStack, SIGNAL(refreshEffectStack(ClipItem*)), trackView->projectView(), SLOT(slotRefreshEffects(ClipItem*)));
477
478   m_projectList->setDocument(doc);
479   m_monitorManager->setTimecode(doc->timecode());
480   doc->setRenderer(m_projectMonitor->render);
481   //m_undoView->setStack(0);
482   m_commandStack = doc->commandStack();
483
484   m_overView->setScene(trackView->projectScene());
485   m_overView->scale(m_overView->width() / trackView->duration(), m_overView->height() / (50 * trackView->tracksNumber()));
486   //m_overView->fitInView(m_overView->itemAt(0, 50), Qt::KeepAspectRatio);
487   QAction *redo = m_commandStack->createRedoAction(actionCollection());
488   QAction *undo = m_commandStack->createUndoAction(actionCollection());
489
490   QWidget* w = factory()->container("mainToolBar", this);
491   if(w) {
492     if (actionCollection()->action("undo"))
493       delete actionCollection()->action("undo");
494     if(actionCollection()->action("redo"))
495       delete actionCollection()->action("redo");
496
497     actionCollection()->addAction("undo", undo);
498     actionCollection()->addAction("redo", redo);
499     w->addAction(undo);
500     w->addAction(redo);
501   }
502   m_undoView->setStack(doc->commandStack());
503   m_activeDocument = doc;
504 }
505
506 void MainWindow::slotPreferences()
507 {
508   //An instance of your dialog could be already created and could be
509   // cached, in which case you want to display the cached dialog
510   // instead of creating another one
511   if ( KConfigDialog::showDialog( "settings" ) )
512     return;
513
514   // KConfigDialog didn't find an instance of this dialog, so lets
515   // create it :
516   KdenliveSettingsDialog* dialog = new KdenliveSettingsDialog(this);
517   //connect( dialog, SIGNAL(settingsChanged()), this, SLOT(updateConfiguration()) );
518   dialog->show();
519 }
520
521 void MainWindow::slotGotProgressInfo( KUrl url, int progress) {
522   statusProgressBar->setValue(progress);
523   if (progress>0) {
524     statusLabel->setText(tr("Creating Audio Thumbs"));
525     statusProgressBar->setVisible(true);
526   }
527   else {
528     statusLabel->setText("");
529     statusProgressBar->setVisible(false);
530   }
531 }
532
533 #include "mainwindow.moc"