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