]> git.sesse.net Git - kdenlive/blob - src/monitor.cpp
Implement deinterlacer and rescale options for MLT consumer
[kdenlive] / src / monitor.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 #include "monitor.h"
22 #include "smallruler.h"
23 #include "docclipbase.h"
24 #include "abstractclipitem.h"
25 #include "monitorscene.h"
26 #include "monitoreditwidget.h"
27 #include "kdenlivesettings.h"
28
29 #include <KDebug>
30 #include <KLocale>
31 #include <KFileDialog>
32 #include <KApplication>
33 #include <KMessageBox>
34 #include <KSelectAction>
35
36 #include <QMouseEvent>
37 #include <QStylePainter>
38 #include <QMenu>
39 #include <QToolButton>
40 #include <QToolBar>
41 #include <QDesktopWidget>
42 #include <QLabel>
43 #include <QIntValidator>
44 #include <QVBoxLayout>
45
46
47 #define SEEK_INACTIVE (-1)
48
49
50 Monitor::Monitor(Kdenlive::MONITORID id, MonitorManager *manager, QString profile, QWidget *parent) :
51     AbstractMonitor(id, manager, parent)
52     , render(NULL)
53     , m_currentClip(NULL)
54     , m_overlay(NULL)
55     , m_scale(1)
56     , m_length(2)
57     , m_dragStarted(false)
58     , m_loopClipAction(NULL)
59     , m_contextMenu(NULL)
60     , m_effectWidget(NULL)
61     , m_selectedClip(NULL)
62     , m_loopClipTransition(true)
63 #ifdef USE_OPENGL
64     , m_glWidget(NULL)
65 #endif
66     , m_editMarker(NULL)
67 {
68     QVBoxLayout *layout = new QVBoxLayout;
69     layout->setContentsMargins(0, 0, 0, 0);
70     layout->setSpacing(0);
71
72     // Video widget holder
73     layout->addWidget(videoBox, 10);
74     layout->addStretch();
75
76     // Get base size for icons
77     int s = style()->pixelMetric(QStyle::PM_SmallIconSize);
78
79
80     // Tool bar buttons
81     m_toolbar = new QToolBar(this);
82     m_toolbar->setIconSize(QSize(s, s));
83
84     m_playIcon = KIcon("media-playback-start");
85     m_pauseIcon = KIcon("media-playback-pause");
86
87
88     if (id != Kdenlive::dvdMonitor) {
89         m_toolbar->addAction(KIcon("kdenlive-zone-start"), i18n("Set zone start"), this, SLOT(slotSetZoneStart()));
90         m_toolbar->addAction(KIcon("kdenlive-zone-end"), i18n("Set zone end"), this, SLOT(slotSetZoneEnd()));
91     }
92
93     m_toolbar->addAction(KIcon("media-seek-backward"), i18n("Rewind"), this, SLOT(slotRewind()));
94     //m_toolbar->addAction(KIcon("media-skip-backward"), i18n("Rewind 1 frame"), this, SLOT(slotRewindOneFrame()));
95
96     QToolButton *playButton = new QToolButton(m_toolbar);
97     m_playMenu = new QMenu(i18n("Play..."), this);
98     m_playAction = m_playMenu->addAction(m_playIcon, i18n("Play"));
99     //m_playAction->setCheckable(true);
100     connect(m_playAction, SIGNAL(triggered()), this, SLOT(slotPlay()));
101
102     playButton->setMenu(m_playMenu);
103     playButton->setPopupMode(QToolButton::MenuButtonPopup);
104     m_toolbar->addWidget(playButton);
105
106     //m_toolbar->addAction(KIcon("media-skip-forward"), i18n("Forward 1 frame"), this, SLOT(slotForwardOneFrame()));
107     m_toolbar->addAction(KIcon("media-seek-forward"), i18n("Forward"), this, SLOT(slotForward()));
108
109     playButton->setDefaultAction(m_playAction);
110
111     if (id != Kdenlive::dvdMonitor) {
112         QToolButton *configButton = new QToolButton(m_toolbar);
113         m_configMenu = new QMenu(i18n("Misc..."), this);
114         configButton->setIcon(KIcon("system-run"));
115         configButton->setMenu(m_configMenu);
116         configButton->setPopupMode(QToolButton::QToolButton::InstantPopup);
117         m_toolbar->addWidget(configButton);
118
119         if (id == Kdenlive::clipMonitor) {
120             m_markerMenu = new QMenu(i18n("Go to marker..."), this);
121             m_markerMenu->setEnabled(false);
122             m_configMenu->addMenu(m_markerMenu);
123             connect(m_markerMenu, SIGNAL(triggered(QAction *)), this, SLOT(slotGoToMarker(QAction *)));
124         }
125         m_configMenu->addAction(KIcon("transform-scale"), i18n("Resize (100%)"), this, SLOT(slotSetSizeOneToOne()));
126         m_configMenu->addAction(KIcon("transform-scale"), i18n("Resize (50%)"), this, SLOT(slotSetSizeOneToTwo()));
127     }
128
129     // Create Volume slider popup
130     m_volumePopup = new QFrame(this, Qt::Popup);
131     QVBoxLayout *poplayout = new QVBoxLayout;
132     poplayout->setContentsMargins(0, 0, 0, 0);
133     m_audioSlider = new QSlider(Qt::Vertical);
134     m_audioSlider->setRange(0, 100);
135     poplayout->addWidget(m_audioSlider);
136     m_volumePopup->setLayout(poplayout);
137     KIcon icon;
138     if (KdenliveSettings::volume() == 0) icon = KIcon("audio-volume-muted");
139     else icon = KIcon("audio-volume-medium");
140
141     m_volumeWidget = m_toolbar->widgetForAction(m_toolbar->addAction(icon, i18n("Audio volume"), this, SLOT(slotShowVolume())));
142
143     // we need to show / hide the popup once so that it's geometry can be calculated in slotShowVolume
144     m_volumePopup->show();
145     m_volumePopup->hide();
146
147     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
148     setLayout(layout);
149     setMinimumHeight(200);
150
151     if (profile.isEmpty()) profile = KdenliveSettings::current_profile();
152
153     bool monitorCreated = false;
154 #ifdef Q_WS_MAC
155     createOpenGlWidget(videoBox, profile);
156     monitorCreated = true;
157     //m_glWidget->setFixedSize(width, height);
158 #elif defined(USE_OPENGL)
159     if (KdenliveSettings::openglmonitors()) {
160         monitorCreated = createOpenGlWidget(videoBox, profile);
161     }
162 #endif
163     if (!monitorCreated) {
164         createVideoSurface();
165         render = new Render(m_id, (int) videoSurface->winId(), profile, this);
166         connect(videoSurface, SIGNAL(refreshMonitor()), render, SLOT(doRefresh()));
167     }
168 #ifdef USE_OPENGL
169     else if (m_glWidget) {
170         QVBoxLayout *lay = new QVBoxLayout;
171         lay->setContentsMargins(0, 0, 0, 0);
172         lay->addWidget(m_glWidget);
173         videoBox->setLayout(lay);
174     }
175 #endif
176
177     // Monitor ruler
178     m_ruler = new SmallRuler(this, render);
179     if (id == Kdenlive::dvdMonitor) m_ruler->setZone(-3, -2);
180     layout->addWidget(m_ruler);
181     
182     connect(m_audioSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSetVolume(int)));
183     connect(render, SIGNAL(durationChanged(int)), this, SLOT(adjustRulerSize(int)));
184     connect(render, SIGNAL(rendererStopped(int)), this, SLOT(rendererStopped(int)));
185     connect(render, SIGNAL(rendererPosition(int)), this, SLOT(seekCursor(int)));
186
187     if (id != Kdenlive::clipMonitor) {
188         connect(render, SIGNAL(rendererPosition(int)), this, SIGNAL(renderPosition(int)));
189         connect(render, SIGNAL(durationChanged(int)), this, SIGNAL(durationChanged(int)));
190         connect(m_ruler, SIGNAL(zoneChanged(QPoint)), this, SIGNAL(zoneUpdated(QPoint)));
191     } else {
192         connect(m_ruler, SIGNAL(zoneChanged(QPoint)), this, SLOT(setClipZone(QPoint)));
193     }
194
195     if (videoSurface) videoSurface->show();
196
197     if (id == Kdenlive::projectMonitor) {
198         m_effectWidget = new MonitorEditWidget(render, videoBox);
199         connect(m_effectWidget, SIGNAL(showEdit(bool, bool)), this, SLOT(slotShowEffectScene(bool, bool)));
200         m_toolbar->addAction(m_effectWidget->getVisibilityAction());
201         videoBox->layout()->addWidget(m_effectWidget);
202         m_effectWidget->hide();
203     }
204
205     QWidget *spacer = new QWidget(this);
206     spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
207     m_toolbar->addWidget(spacer);
208     m_timePos = new TimecodeDisplay(m_monitorManager->timecode(), this);
209     m_toolbar->addWidget(m_timePos);
210     connect(m_timePos, SIGNAL(timeCodeEditingFinished()), this, SLOT(slotSeek()));
211     m_toolbar->setMaximumHeight(m_timePos->height());
212     layout->addWidget(m_toolbar);
213 }
214
215 Monitor::~Monitor()
216 {
217     delete m_ruler;
218     delete m_timePos;
219     delete m_overlay;
220     if (m_effectWidget)
221         delete m_effectWidget;
222     delete render;
223 }
224
225 QWidget *Monitor::container()
226 {
227     return videoBox;
228 }
229
230 #ifdef USE_OPENGL
231 bool Monitor::createOpenGlWidget(QWidget *parent, const QString profile)
232 {
233     render = new Render(id(), 0, profile, this);
234     m_glWidget = new VideoGLWidget(parent);
235     if (m_glWidget == NULL) {
236         // Creation failed, we are in trouble...
237         return false;
238     }
239     m_glWidget->setImageAspectRatio(render->dar());
240     m_glWidget->setBackgroundColor(KdenliveSettings::window_background());
241     connect(render, SIGNAL(showImageSignal(QImage)), m_glWidget, SLOT(showImage(QImage)));
242     return true;
243 }
244 #endif
245
246 void Monitor::setupMenu(QMenu *goMenu, QAction *playZone, QAction *loopZone, QMenu *markerMenu, QAction *loopClip)
247 {
248     m_contextMenu = new QMenu(this);
249     m_contextMenu->addMenu(m_playMenu);
250     if (goMenu)
251         m_contextMenu->addMenu(goMenu);
252     if (markerMenu) {
253         m_contextMenu->addMenu(markerMenu);
254         QList <QAction *>list = markerMenu->actions();
255         for (int i = 0; i < list.count(); i++) {
256             if (list.at(i)->data().toString() == "edit_marker") {
257                 m_editMarker = list.at(i);
258                 break;
259             }
260         }
261     }
262
263     m_playMenu->addAction(playZone);
264     m_playMenu->addAction(loopZone);
265     if (loopClip) {
266         m_loopClipAction = loopClip;
267         m_playMenu->addAction(loopClip);
268     }
269
270     //TODO: add save zone to timeline monitor when fixed
271     if (m_id == Kdenlive::clipMonitor) {
272         m_contextMenu->addMenu(m_markerMenu);
273         m_contextMenu->addAction(KIcon("document-save"), i18n("Save zone"), this, SLOT(slotSaveZone()));
274         QAction *extractZone = m_configMenu->addAction(KIcon("document-new"), i18n("Extract Zone"), this, SLOT(slotExtractCurrentZone()));
275         m_contextMenu->addAction(extractZone);
276     }
277     QAction *extractFrame = m_configMenu->addAction(KIcon("document-new"), i18n("Extract frame"), this, SLOT(slotExtractCurrentFrame()));
278     m_contextMenu->addAction(extractFrame);
279
280     if (m_id != Kdenlive::clipMonitor) {
281         QAction *splitView = m_contextMenu->addAction(KIcon("view-split-left-right"), i18n("Split view"), render, SLOT(slotSplitView(bool)));
282         splitView->setCheckable(true);
283         m_configMenu->addAction(splitView);
284     } else {
285         QAction *setThumbFrame = m_contextMenu->addAction(KIcon("document-new"), i18n("Set current image as thumbnail"), this, SLOT(slotSetThumbFrame()));
286         m_configMenu->addAction(setThumbFrame);
287     }
288
289     QAction *showTips = m_contextMenu->addAction(KIcon("help-hint"), i18n("Monitor overlay infos"));
290     showTips->setCheckable(true);
291     connect(showTips, SIGNAL(toggled(bool)), this, SLOT(slotSwitchMonitorInfo(bool)));
292     showTips->setChecked(KdenliveSettings::displayMonitorInfo());
293     
294     KSelectAction *interlace = new KSelectAction(i18n("Deinterlacer"), this);
295     interlace->addAction(i18n("One Field (fast)"));
296     interlace->addAction(i18n("Linear Blend (fast)"));
297     interlace->addAction(i18n("YADIF - temporal only (good)"));
298     interlace->addAction(i18n("YADIF - temporal + spacial (best)"));
299     if (KdenliveSettings::mltdeinterlacer() == "linearblend") interlace->setCurrentItem(1);
300     else if (KdenliveSettings::mltdeinterlacer() == "yadif-temporal") interlace->setCurrentItem(2);
301     else if (KdenliveSettings::mltdeinterlacer() == "yadif") interlace->setCurrentItem(3);
302     else interlace->setCurrentItem(0);
303     connect(interlace, SIGNAL(triggered(int)), this, SLOT(slotSetDeinterlacer(int)));
304     
305     KSelectAction *interpol = new KSelectAction(i18n("Interpolation"), this);
306     interpol->addAction(i18n("Nearest Neighbor (fast)"));
307     interpol->addAction(i18n("Bilinear (good)"));
308     interpol->addAction(i18n("Bicubic (better)"));
309     interpol->addAction(i18n("Hyper/Lanczos (best)"));
310     if (KdenliveSettings::mltinterpolation() == "bilinear") interpol->setCurrentItem(1);
311     else if (KdenliveSettings::mltinterpolation() == "bicubic") interpol->setCurrentItem(2);
312     else if (KdenliveSettings::mltinterpolation() == "hyper") interpol->setCurrentItem(3);
313     else interpol->setCurrentItem(0);
314     connect(interpol, SIGNAL(triggered(int)), this, SLOT(slotSetInterpolation(int)));
315
316     QAction *dropFrames = m_contextMenu->addAction(KIcon(), i18n("Real time (drop frames)"));
317     dropFrames->setCheckable(true);
318     dropFrames->setChecked(true);
319     connect(dropFrames, SIGNAL(toggled(bool)), this, SLOT(slotSwitchDropFrames(bool)));
320     
321     m_configMenu->addAction(showTips);
322     m_configMenu->addAction(dropFrames);
323     m_configMenu->addAction(interlace);
324     m_configMenu->addAction(interpol);
325
326 }
327
328 void Monitor::slotGoToMarker(QAction *action)
329 {
330     int pos = action->data().toInt();
331     slotSeek(pos);
332 }
333
334 void Monitor::slotSetSizeOneToOne()
335 {
336     QRect r = QApplication::desktop()->screenGeometry();
337     const int maxWidth = r.width() - 20;
338     const int maxHeight = r.height() - 20;
339     int width = render->renderWidth();
340     int height = render->renderHeight();
341     kDebug() << "// render info: " << width << "x" << height;
342     while (width >= maxWidth || height >= maxHeight) {
343         width = width * 0.8;
344         height = height * 0.8;
345     }
346     kDebug() << "// MONITOR; set SIZE: " << width << ", " << height;
347     videoBox->setFixedSize(width, height);
348     updateGeometry();
349     adjustSize();
350     //m_ui.video_frame->setMinimumSize(0, 0);
351     emit adjustMonitorSize();
352 }
353
354 void Monitor::slotSetSizeOneToTwo()
355 {
356     QRect r = QApplication::desktop()->screenGeometry();
357     const int maxWidth = r.width() - 20;
358     const int maxHeight = r.height() - 20;
359     int width = render->renderWidth() / 2;
360     int height = render->renderHeight() / 2;
361     kDebug() << "// render info: " << width << "x" << height;
362     while (width >= maxWidth || height >= maxHeight) {
363         width = width * 0.8;
364         height = height * 0.8;
365     }
366     kDebug() << "// MONITOR; set SIZE: " << width << ", " << height;
367     videoBox->setFixedSize(width, height);
368     updateGeometry();
369     adjustSize();
370     //m_ui.video_frame->setMinimumSize(0, 0);
371     emit adjustMonitorSize();
372 }
373
374 void Monitor::resetSize()
375 {
376     videoBox->setMinimumSize(0, 0);
377 }
378
379 QString Monitor::getTimecodeFromFrames(int pos)
380 {
381     return m_monitorManager->timecode().getTimecodeFromFrames(pos);
382 }
383
384 double Monitor::fps() const
385 {
386     return m_monitorManager->timecode().fps();
387 }
388
389 DocClipBase *Monitor::activeClip()
390 {
391     return m_currentClip;
392 }
393
394 void Monitor::updateMarkers(DocClipBase *source)
395 {
396     if (source == m_currentClip && source != NULL) {
397         m_markerMenu->clear();
398         QList <CommentedTime> markers = m_currentClip->commentedSnapMarkers();
399         if (!markers.isEmpty()) {
400             QList <int> marks;
401             for (int i = 0; i < markers.count(); i++) {
402                 int pos = (int) markers.at(i).time().frames(m_monitorManager->timecode().fps());
403                 marks.append(pos);
404                 QString position = m_monitorManager->timecode().getTimecode(markers.at(i).time()) + ' ' + markers.at(i).comment();
405                 QAction *go = m_markerMenu->addAction(position);
406                 go->setData(pos);
407             }
408         }
409         m_ruler->setMarkers(markers);
410         m_markerMenu->setEnabled(!m_markerMenu->isEmpty());
411     }
412 }
413
414 void Monitor::setMarkers(QList <CommentedTime> markers)
415 {
416     m_ruler->setMarkers(markers);
417 }
418
419 void Monitor::slotSeekToPreviousSnap()
420 {
421     if (m_currentClip) slotSeek(getSnapForPos(true).frames(m_monitorManager->timecode().fps()));
422 }
423
424 void Monitor::slotSeekToNextSnap()
425 {
426     if (m_currentClip) slotSeek(getSnapForPos(false).frames(m_monitorManager->timecode().fps()));
427 }
428
429 GenTime Monitor::position()
430 {
431     return render->seekPosition();
432 }
433
434 GenTime Monitor::getSnapForPos(bool previous)
435 {
436     QList <GenTime> snaps;
437     QList < GenTime > markers = m_currentClip->snapMarkers();
438     for (int i = 0; i < markers.size(); ++i) {
439         GenTime t = markers.at(i);
440         snaps.append(t);
441     }
442     QPoint zone = m_ruler->zone();
443     snaps.append(GenTime(zone.x(), m_monitorManager->timecode().fps()));
444     snaps.append(GenTime(zone.y(), m_monitorManager->timecode().fps()));
445     snaps.append(GenTime());
446     snaps.append(m_currentClip->duration());
447     qSort(snaps);
448
449     const GenTime pos = render->seekPosition();
450     for (int i = 0; i < snaps.size(); ++i) {
451         if (previous && snaps.at(i) >= pos) {
452             if (i == 0) i = 1;
453             return snaps.at(i - 1);
454         } else if (!previous && snaps.at(i) > pos) {
455             return snaps.at(i);
456         }
457     }
458     return GenTime();
459 }
460
461 void Monitor::slotZoneMoved(int start, int end)
462 {
463     m_ruler->setZone(start, end);
464     setClipZone(m_ruler->zone());
465     checkOverlay();
466 }
467
468 void Monitor::slotSetZoneStart()
469 {
470     m_ruler->setZoneStart();
471     emit zoneUpdated(m_ruler->zone());
472     setClipZone(m_ruler->zone());
473     checkOverlay();
474 }
475
476 void Monitor::slotSetZoneEnd()
477 {
478     m_ruler->setZoneEnd();
479     emit zoneUpdated(m_ruler->zone());
480     setClipZone(m_ruler->zone());
481     checkOverlay();
482 }
483
484 // virtual
485 void Monitor::mousePressEvent(QMouseEvent * event)
486 {
487     if (render) render->setActiveMonitor();
488     if (event->button() != Qt::RightButton) {
489         if (videoBox->geometry().contains(event->pos()) && (!m_overlay || !m_overlay->underMouse())) {
490             m_dragStarted = true;
491             m_DragStartPosition = event->pos();
492         }
493     } else if (m_contextMenu && (!m_effectWidget || !m_effectWidget->isVisible())) {
494         m_contextMenu->popup(event->globalPos());
495     }
496 }
497
498 void Monitor::resizeEvent(QResizeEvent *event)
499 {
500     Q_UNUSED(event);
501     if (render && isVisible() && isActive()) render->doRefresh();
502 }
503
504 void Monitor::slotSwitchFullScreen()
505 {
506     videoBox->switchFullScreen();
507 }
508
509 // virtual
510 void Monitor::mouseReleaseEvent(QMouseEvent * event)
511 {
512     if (m_dragStarted && event->button() != Qt::RightButton) {
513         if (videoBox->geometry().contains(event->pos()) && (!m_effectWidget || !m_effectWidget->isVisible())) {
514             if (isActive()) slotPlay();
515             else slotActivateMonitor();
516         } //else event->ignore(); //QWidget::mouseReleaseEvent(event);
517     }
518     m_dragStarted = false;
519     event->accept();
520 }
521
522 // virtual
523 void Monitor::mouseMoveEvent(QMouseEvent *event)
524 {
525     if (!m_dragStarted || m_currentClip == NULL) return;
526
527     if ((event->pos() - m_DragStartPosition).manhattanLength()
528             < QApplication::startDragDistance())
529         return;
530
531     {
532         QDrag *drag = new QDrag(this);
533         QMimeData *mimeData = new QMimeData;
534
535         QStringList list;
536         list.append(m_currentClip->getId());
537         QPoint p = m_ruler->zone();
538         list.append(QString::number(p.x()));
539         list.append(QString::number(p.y()));
540         QByteArray data;
541         data.append(list.join(";").toUtf8());
542         mimeData->setData("kdenlive/clip", data);
543         drag->setMimeData(mimeData);
544         /*QPixmap pix = m_currentClip->thumbnail();
545         drag->setPixmap(pix);
546         drag->setHotSpot(QPoint(0, 50));*/
547         drag->start(Qt::MoveAction);
548         /*Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
549         Qt::DropAction dropAction;
550         dropAction = drag->start(Qt::CopyAction | Qt::MoveAction);*/
551
552         //Qt::DropAction dropAction = drag->exec();
553
554     }
555     //event->accept();
556 }
557
558
559 /*void Monitor::dragMoveEvent(QDragMoveEvent * event) {
560     event->setDropAction(Qt::IgnoreAction);
561     event->setDropAction(Qt::MoveAction);
562     if (event->mimeData()->hasText()) {
563         event->acceptProposedAction();
564     }
565 }
566
567 Qt::DropActions Monitor::supportedDropActions() const {
568     // returns what actions are supported when dropping
569     return Qt::MoveAction;
570 }*/
571
572 QStringList Monitor::mimeTypes() const
573 {
574     QStringList qstrList;
575     // list of accepted mime types for drop
576     qstrList.append("kdenlive/clip");
577     return qstrList;
578 }
579
580 // virtual
581 void Monitor::wheelEvent(QWheelEvent * event)
582 {
583     slotMouseSeek(event->delta(), event->modifiers() == Qt::ControlModifier);
584     event->accept();
585 }
586
587 void Monitor::mouseDoubleClickEvent(QMouseEvent * event)
588 {
589     if (!KdenliveSettings::openglmonitors()) {
590         videoBox->switchFullScreen();
591         event->accept();
592     }
593 }
594
595 void Monitor::slotMouseSeek(int eventDelta, bool fast)
596 {
597     if (fast) {
598         int delta = m_monitorManager->timecode().fps();
599         if (eventDelta > 0) delta = 0 - delta;
600         if (render->requestedSeekPosition != SEEK_INACTIVE)
601             slotSeek(render->requestedSeekPosition - delta);
602         else slotSeek(render->seekFramePosition() - delta);
603     } else {
604         if (eventDelta >= 0) slotForwardOneFrame();
605         else slotRewindOneFrame();
606     }
607 }
608
609 void Monitor::slotSetThumbFrame()
610 {
611     if (m_currentClip == NULL) {
612         return;
613     }
614     m_currentClip->setClipThumbFrame((uint) render->seekFramePosition());
615     emit refreshClipThumbnail(m_currentClip->getId(), true);
616 }
617
618 void Monitor::slotExtractCurrentZone()
619 {
620     if (m_currentClip == NULL) return;
621     emit extractZone(m_currentClip->getId(), m_ruler->zone());
622 }
623
624 void Monitor::slotExtractCurrentFrame()
625 {
626     QImage frame;
627     // check if we are using a proxy
628     if (m_currentClip && !m_currentClip->getProperty("proxy").isEmpty() && m_currentClip->getProperty("proxy") != "-") {
629         // using proxy, use original clip url to get frame
630         frame = render->extractFrame(render->seekFramePosition(), m_currentClip->fileURL().path());
631     }
632     else frame = render->extractFrame(render->seekFramePosition());
633     QPointer<KFileDialog> fs = new KFileDialog(KUrl(), "image/png", this);
634     fs->setOperationMode(KFileDialog::Saving);
635     fs->setMode(KFile::File);
636     fs->setConfirmOverwrite(true);
637     fs->setKeepLocation(true);
638     fs->exec();
639     QString path;
640     if (fs) path = fs->selectedFile();
641     delete fs;
642     if (!path.isEmpty()) {
643         frame.save(path);
644     }
645 }
646
647 void Monitor::setTimePos(const QString &pos)
648 {
649     m_timePos->setValue(pos);
650     slotSeek();
651 }
652
653 void Monitor::slotSeek()
654 {
655     slotSeek(m_timePos->getValue());
656 }
657
658 void Monitor::slotSeek(int pos)
659 {
660     if (render == NULL) return;
661     slotActivateMonitor();
662     render->seekToFrame(pos);
663     m_ruler->update();
664 }
665
666 void Monitor::checkOverlay()
667 {
668     if (m_overlay == NULL) return;
669     QString overlayText;
670     int pos = m_timePos->getValue();//render->seekFramePosition();
671     QPoint zone = m_ruler->zone();
672     if (pos == zone.x())
673         overlayText = i18n("In Point");
674     else if (pos == zone.y())
675         overlayText = i18n("Out Point");
676     else {
677         if (m_currentClip) {
678             overlayText = m_currentClip->markerComment(GenTime(pos, m_monitorManager->timecode().fps()));
679             if (!overlayText.isEmpty()) {
680                 m_overlay->setOverlayText(overlayText, false);
681                 return;
682             }
683         }
684     }
685     if (m_overlay->isVisible() && overlayText.isEmpty()) m_overlay->setOverlayText(QString(), false);
686     else m_overlay->setOverlayText(overlayText);
687 }
688
689 void Monitor::slotStart()
690 {
691     slotActivateMonitor();
692     render->play(0);
693     render->seekToFrame(0);
694 }
695
696 void Monitor::slotEnd()
697 {
698     slotActivateMonitor();
699     render->play(0);
700     render->seekToFrame(render->getLength());
701 }
702
703 void Monitor::slotZoneStart()
704 {
705     slotActivateMonitor();
706     render->play(0);
707     render->seekToFrame(m_ruler->zone().x());
708 }
709
710 void Monitor::slotZoneEnd()
711 {
712     slotActivateMonitor();
713     render->play(0);
714     render->seekToFrame(m_ruler->zone().y());
715 }
716
717 void Monitor::slotRewind(double speed)
718 {
719     slotActivateMonitor();
720     if (speed == 0) {
721         double currentspeed = render->playSpeed();
722         if (currentspeed >= 0) render->play(-1);
723         else switch((int) currentspeed) {
724             case -1:
725                 render->play(-2);
726                 break;
727             case -2:
728                 render->play(-3);
729                 break;
730             case -3:
731                 render->play(-5);
732                 break;
733             default:
734                 render->play(-8);
735         }
736     } else render->play(speed);
737     //m_playAction->setChecked(true);
738     m_playAction->setIcon(m_pauseIcon);
739 }
740
741 void Monitor::slotForward(double speed)
742 {
743     slotActivateMonitor();
744     if (speed == 0) {
745         double currentspeed = render->playSpeed();
746         if (currentspeed <= 0) render->play(1);
747         else switch((int) currentspeed) {
748             case 1:
749                 render->play(2);
750                 break;
751             case 2:
752                 render->play(3);
753                 break;
754             case 3:
755                 render->play(5);
756                 break;
757             default:
758                 render->play(8);
759         }
760     } else render->play(speed);
761     //m_playAction->setChecked(true);
762     m_playAction->setIcon(m_pauseIcon);
763 }
764
765 void Monitor::slotRewindOneFrame(int diff)
766 {
767     slotActivateMonitor();
768     render->play(0);
769     render->seekToFrameDiff(-diff);
770     m_ruler->update();
771 }
772
773 void Monitor::slotForwardOneFrame(int diff)
774 {
775     slotActivateMonitor();
776     render->play(0);
777     render->seekToFrameDiff(diff);
778     m_ruler->update();
779 }
780
781 void Monitor::seekCursor(int pos)
782 {
783     if (m_ruler->slotNewValue(pos)) {
784         m_timePos->setValue(pos);
785         checkOverlay();
786     }
787 }
788
789 void Monitor::rendererStopped(int pos)
790 {
791     if (m_ruler->slotNewValue(pos)) {
792         m_timePos->setValue(pos);
793         checkOverlay();
794     }
795     m_playAction->setIcon(m_playIcon);
796 }
797
798 void Monitor::adjustRulerSize(int length)
799 {
800     if (length > 0) m_length = length;
801     m_ruler->adjustScale(m_length);
802     if (m_currentClip != NULL) {
803         QPoint zone = m_currentClip->zone();
804         m_ruler->setZone(zone.x(), zone.y());
805     }
806 }
807
808 void Monitor::stop()
809 {
810     if (render) render->stop();
811 }
812
813 void Monitor::start()
814 {
815     if (!isVisible() || !isActive()) return;
816 #ifdef USE_OPENGL    
817     if (m_glWidget) m_glWidget->activateMonitor();
818 #endif
819     if (render) render->startConsumer();
820 }
821
822 void Monitor::refreshMonitor(bool visible)
823 {
824     if (visible && render) {
825         if (!slotActivateMonitor()) {
826             // the monitor was already active, simply refreshClipThumbnail
827             render->doRefresh();
828         }
829     }
830 }
831
832 void Monitor::refreshMonitor()
833 {
834     if (isActive()) {
835         render->doRefresh();
836     }
837 }
838
839 void Monitor::pause()
840 {
841     if (render == NULL) return;
842     slotActivateMonitor();
843     render->pause();
844     //m_playAction->setChecked(true);
845     m_playAction->setIcon(m_playIcon);
846 }
847
848 void Monitor::unpause()
849 {
850 }
851
852 void Monitor::slotPlay()
853 {
854     if (render == NULL) return;
855     slotActivateMonitor();
856     if (render->isPlaying()) {
857         m_playAction->setIcon(m_playIcon);
858         render->switchPlay(false);
859     }
860     else {
861         m_playAction->setIcon(m_pauseIcon);
862         render->switchPlay(true);
863     }
864     m_ruler->refreshRuler();
865 }
866
867 void Monitor::slotPlayZone()
868 {
869     if (render == NULL) return;
870     slotActivateMonitor();
871     QPoint p = m_ruler->zone();
872     render->playZone(GenTime(p.x(), m_monitorManager->timecode().fps()), GenTime(p.y(), m_monitorManager->timecode().fps()));
873     //m_playAction->setChecked(true);
874     m_playAction->setIcon(m_pauseIcon);
875 }
876
877 void Monitor::slotLoopZone()
878 {
879     if (render == NULL) return;
880     slotActivateMonitor();
881     QPoint p = m_ruler->zone();
882     render->loopZone(GenTime(p.x(), m_monitorManager->timecode().fps()), GenTime(p.y(), m_monitorManager->timecode().fps()));
883     //m_playAction->setChecked(true);
884     m_playAction->setIcon(m_pauseIcon);
885 }
886
887 void Monitor::slotLoopClip()
888 {
889     if (render == NULL || m_selectedClip == NULL)
890         return;
891     slotActivateMonitor();
892     render->loopZone(m_selectedClip->startPos(), m_selectedClip->endPos());
893     //m_playAction->setChecked(true);
894     m_playAction->setIcon(m_pauseIcon);
895 }
896
897 void Monitor::updateClipProducer(Mlt::Producer *prod)
898 {
899     if (render == NULL) return;
900     render->setProducer(prod, render->seekFramePosition());
901 }
902
903 void Monitor::slotSetClipProducer(DocClipBase *clip, QPoint zone, bool forceUpdate, int position)
904 {
905     if (render == NULL) return;
906     if (clip == NULL && m_currentClip != NULL) {
907         m_currentClip->lastSeekPosition = render->seekFramePosition();
908         m_currentClip = NULL;
909         m_length = -1;
910         render->setProducer(NULL, -1);
911         return;
912     }
913
914     if (clip != m_currentClip || forceUpdate) {
915         if (m_currentClip) m_currentClip->lastSeekPosition = render->seekFramePosition();
916         m_currentClip = clip;
917         if (position == -1) position = clip->lastSeekPosition;
918         updateMarkers(clip);
919         Mlt::Producer *prod = NULL;
920         if (clip) prod = clip->getCloneProducer();
921         if (render->setProducer(prod, position) == -1) {
922             // MLT CONSUMER is broken
923             kDebug(QtWarningMsg) << "ERROR, Cannot start monitor";
924         } else start();
925     } else {
926         if (m_currentClip) {
927             slotActivateMonitor();
928             if (position == -1) position = render->seekFramePosition();
929             render->seek(position);
930             if (zone.isNull()) {
931                 zone = m_currentClip->zone();
932                 m_ruler->setZone(zone.x(), zone.y());
933                 return;
934             }
935         }
936     }
937     if (!zone.isNull()) {
938         m_ruler->setZone(zone.x(), zone.y());
939         render->seek(zone.x());
940     }
941 }
942
943 void Monitor::slotOpenFile(const QString &file)
944 {
945     if (render == NULL) return;
946     slotActivateMonitor();
947     render->loadUrl(file);
948 }
949
950 void Monitor::slotSaveZone()
951 {
952     if (render == NULL) return;
953     emit saveZone(render, m_ruler->zone(), m_currentClip);
954
955     //render->setSceneList(doc, 0);
956 }
957
958 void Monitor::setCustomProfile(const QString &profile, Timecode tc)
959 {
960     m_timePos->updateTimeCode(tc);
961     if (render == NULL) return;
962     if (!render->hasProfile(profile)) {
963         slotActivateMonitor();
964         render->resetProfile(profile);
965 #ifdef USE_OPENGL    
966         if (m_glWidget) m_glWidget->setImageAspectRatio(render->dar());
967 #endif
968     }
969 }
970
971 void Monitor::resetProfile(const QString &profile)
972 {
973     m_timePos->updateTimeCode(m_monitorManager->timecode());
974     if (render == NULL) return;
975     if (!render->hasProfile(profile)) {
976         slotActivateMonitor();
977         render->resetProfile(profile);
978 #ifdef USE_OPENGL
979         if (m_glWidget) m_glWidget->setImageAspectRatio(render->dar());
980 #endif
981     }
982     if (m_effectWidget)
983         m_effectWidget->resetProfile(render);
984 }
985
986 void Monitor::saveSceneList(QString path, QDomElement info)
987 {
988     if (render == NULL) return;
989     render->saveSceneList(path, info);
990 }
991
992 const QString Monitor::sceneList()
993 {
994     if (render == NULL) return QString();
995     return render->sceneList();
996 }
997
998 void Monitor::setClipZone(QPoint pos)
999 {
1000     if (m_currentClip == NULL) return;
1001     m_currentClip->setZone(pos);
1002 }
1003
1004 void Monitor::slotSwitchDropFrames(bool show)
1005 {
1006     render->setDropFrames(show);
1007 }
1008
1009 void Monitor::slotSetDeinterlacer(int ix)
1010 {
1011     QString value;
1012     switch (ix) {
1013       
1014       case 1:
1015         value = "linearblend";
1016         break;
1017       case 2:
1018         value = "yadif-nospatial";
1019         break;
1020       case 3:
1021         value = "yadif";
1022         break;
1023       default:
1024         value = "onefield";
1025     }
1026     KdenliveSettings::setMltdeinterlacer(value);
1027     m_monitorManager->setConsumerProperty("deinterlace_method", value);
1028 }
1029
1030 void Monitor::slotSetInterpolation(int ix)
1031 {
1032     QString value;
1033     switch (ix) {
1034       case 1:
1035         value = "bilinear";
1036         break;
1037       case 2:
1038         value = "bicubic";
1039         break;
1040       case 3:
1041         value = "hyper";
1042         break;
1043       default:
1044         value = "nearest";
1045     }
1046     KdenliveSettings::setMltinterpolation(value);
1047     m_monitorManager->setConsumerProperty("rescale", value);
1048 }
1049
1050
1051 void Monitor::slotSwitchMonitorInfo(bool show)
1052 {
1053     KdenliveSettings::setDisplayMonitorInfo(show);
1054     if (show) {
1055         if (m_overlay) return;
1056         if (videoSurface == NULL) {
1057             // Using OpenGL display
1058 #ifdef USE_OPENGL
1059             if (m_glWidget->layout()) delete m_glWidget->layout();
1060             m_overlay = new Overlay();
1061             connect(m_overlay, SIGNAL(editMarker()), this, SLOT(slotEditMarker()));
1062             QVBoxLayout *layout = new QVBoxLayout;
1063             layout->addStretch(10);
1064             layout->addWidget(m_overlay);
1065             m_glWidget->setLayout(layout);
1066 #endif
1067         } else {
1068             if (videoSurface->layout()) delete videoSurface->layout();
1069             m_overlay = new Overlay();
1070             connect(m_overlay, SIGNAL(editMarker()), this, SLOT(slotEditMarker()));
1071             QVBoxLayout *layout = new QVBoxLayout;
1072             layout->addStretch(10);
1073             layout->addWidget(m_overlay);
1074             videoSurface->setLayout(layout);
1075             m_overlay->raise();
1076             m_overlay->setHidden(true);
1077         }
1078         checkOverlay();
1079     } else {
1080         delete m_overlay;
1081         m_overlay = NULL;
1082     }
1083 }
1084
1085 void Monitor::slotEditMarker()
1086 {
1087     if (m_editMarker) m_editMarker->trigger();
1088 }
1089
1090 void Monitor::updateTimecodeFormat()
1091 {
1092     m_timePos->slotUpdateTimeCodeFormat();
1093 }
1094
1095 QStringList Monitor::getZoneInfo() const
1096 {
1097     QStringList result;
1098     if (m_currentClip == NULL) return result;
1099     result << m_currentClip->getId();
1100     QPoint zone = m_ruler->zone();
1101     result << QString::number(zone.x()) << QString::number(zone.y());
1102     return result;
1103 }
1104
1105 void Monitor::slotSetSelectedClip(AbstractClipItem* item)
1106 {
1107     if (item) {
1108         if (m_loopClipAction) m_loopClipAction->setEnabled(true);
1109         m_selectedClip = item;
1110     } else {
1111         if (m_loopClipAction) m_loopClipAction->setEnabled(false);
1112     }
1113 }
1114
1115 void Monitor::slotSetSelectedClip(ClipItem* item)
1116 {
1117     if (item || (!item && !m_loopClipTransition)) {
1118         m_loopClipTransition = false;
1119         slotSetSelectedClip((AbstractClipItem*)item);
1120     }
1121 }
1122
1123 void Monitor::slotSetSelectedClip(Transition* item)
1124 {
1125     if (item || (!item && m_loopClipTransition)) {
1126         m_loopClipTransition = true;
1127         slotSetSelectedClip((AbstractClipItem*)item);
1128     }
1129 }
1130
1131
1132 void Monitor::slotShowEffectScene(bool show, bool manuallyTriggered)
1133 {
1134     if (m_id == Kdenlive::projectMonitor) {
1135         if (!m_effectWidget->getVisibilityAction()->isChecked())
1136             show = false;
1137         if (m_effectWidget->isVisible() == show)
1138             return;
1139         setUpdatesEnabled(false);
1140         if (show) {
1141             if (videoSurface) {
1142                 videoSurface->setVisible(false);
1143                 // Preview is handeled internally through the Render::showFrame method
1144                 render->disablePreview(true);
1145 #ifdef USE_OPENGL
1146             } else {
1147                 m_glWidget->setVisible(false);
1148 #endif
1149             }
1150             m_effectWidget->setVisible(true);
1151             m_effectWidget->getScene()->slotZoomFit();
1152             emit requestFrameForAnalysis(true);
1153         } else {    
1154             m_effectWidget->setVisible(false);
1155             emit requestFrameForAnalysis(false);
1156             if (videoSurface) {
1157                 videoSurface->setVisible(true);
1158                 // Preview is handeled internally through the Render::showFrame method
1159                 render->disablePreview(false);
1160             
1161 #ifdef USE_OPENGL
1162             } else {
1163                 m_glWidget->setVisible(true);
1164 #endif
1165             }
1166         }
1167         if (!manuallyTriggered)
1168             m_effectWidget->showVisibilityButton(show);
1169         setUpdatesEnabled(true);
1170         videoBox->setEnabled(show);
1171         //render->doRefresh();
1172     }
1173 }
1174
1175 MonitorEditWidget* Monitor::getEffectEdit()
1176 {
1177     return m_effectWidget;
1178 }
1179
1180 bool Monitor::effectSceneDisplayed()
1181 {
1182     return m_effectWidget->isVisible();
1183 }
1184
1185 void Monitor::slotSetVolume(int volume)
1186 {
1187     KdenliveSettings::setVolume(volume);
1188     KIcon icon;
1189     if (volume == 0) icon = KIcon("audio-volume-muted");
1190     else icon = KIcon("audio-volume-medium");
1191     static_cast <QToolButton *>(m_volumeWidget)->setIcon(icon);
1192     render->slotSetVolume(volume);
1193 }
1194
1195 void Monitor::slotShowVolume()
1196 {
1197     m_volumePopup->move(mapToGlobal(m_toolbar->geometry().topLeft()) + QPoint(mapToParent(m_volumeWidget->geometry().bottomLeft()).x(), -m_volumePopup->height()));
1198     int vol = render->volume();
1199     // Disable widget if we cannot get the volume
1200     m_volumePopup->setEnabled(vol != -1);
1201     m_audioSlider->blockSignals(true);
1202     m_audioSlider->setValue(vol);
1203     m_audioSlider->blockSignals(false);
1204     m_volumePopup->show();
1205 }
1206
1207 AbstractRender *Monitor::abstractRender()
1208 {
1209     return render;
1210 }
1211
1212 void Monitor::reloadProducer(const QString &id)
1213 {
1214     if (!m_currentClip) return;
1215     if (m_currentClip->getId() == id)
1216         slotSetClipProducer(m_currentClip, m_currentClip->zone(), true);
1217 }
1218
1219 QString Monitor::getMarkerThumb(GenTime pos)
1220 {
1221     if (!m_currentClip) return QString();
1222     if (!m_currentClip->getClipHash().isEmpty()) {
1223         QString url = m_monitorManager->getProjectFolder() + "thumbs/" + m_currentClip->getClipHash() + '#' + QString::number((int) pos.frames(m_monitorManager->timecode().fps())) + ".png";
1224         if (QFile::exists(url)) return url;
1225     }
1226     return QString();
1227 }
1228
1229 void Monitor::setPalette ( const QPalette & p)
1230 {
1231     QWidget::setPalette(p);
1232     if (m_ruler) m_ruler->updatePalette();
1233     
1234 }
1235
1236 Overlay::Overlay(QWidget* parent) :
1237     QLabel(parent)
1238 {
1239     //setAttribute(Qt::WA_TransparentForMouseEvents);
1240     setAutoFillBackground(true);
1241     setBackgroundRole(QPalette::Base);
1242     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
1243     setCursor(Qt::PointingHandCursor);
1244
1245 }
1246
1247 // virtual
1248 void Overlay::mouseReleaseEvent ( QMouseEvent * event )
1249 {
1250     event->ignore();
1251 }
1252
1253 // virtual
1254 void Overlay::mousePressEvent( QMouseEvent * event )
1255 {
1256     event->ignore();
1257 }
1258
1259 // virtual
1260 void Overlay::mouseDoubleClickEvent ( QMouseEvent * event )
1261 {
1262     emit editMarker();
1263     event->ignore();
1264 }
1265
1266 void Overlay::setOverlayText(const QString &text, bool isZone)
1267 {
1268     if (text.isEmpty()) {
1269         /*QPalette p;
1270         p.setColor(QPalette::Base, KdenliveSettings::window_background());
1271         setPalette(p);
1272         setText(QString());
1273         repaint();*/
1274         setHidden(true);
1275         return;
1276     }
1277     setHidden(true);
1278     QPalette p;
1279     p.setColor(QPalette::Text, Qt::white);
1280     if (isZone) p.setColor(QPalette::Base, QColor(200, 0, 0));
1281     else p.setColor(QPalette::Base, QColor(0, 0, 200));
1282     setPalette(p);
1283     setText(' ' + text + ' ');
1284     setHidden(false);
1285 }
1286
1287
1288 #include "monitor.moc"