monitor.cpp 69.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***************************************************************************
 *   Copyright (C) 2007 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
20

Vincent Pinon's avatar
Vincent Pinon committed
21
#include "monitor.h"
22
#include "glwidget.h"
23
#include "qmlmanager.h"
24
#include "recmanager.h"
Vincent Pinon's avatar
Vincent Pinon committed
25
#include "smallruler.h"
Jean-Baptiste Mardelle's avatar
cleanup    
Jean-Baptiste Mardelle committed
26
#include "mltcontroller/clipcontroller.h"
27
#include "mltcontroller/bincontroller.h"
28
#include "scopes/monitoraudiolevel.h"
29
#include "lib/audio/audioStreamInfo.h"
Vincent Pinon's avatar
Vincent Pinon committed
30
#include "kdenlivesettings.h"
31
#include "timeline/abstractclipitem.h"
32
#include "timeline/clip.h"
33
#include "dialogs/profilesdialog.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
34
#include "doc/kthumb.h"
35
#include "utils/KoIconUtils.h"
36
#include "timeline/transitionhandler.h"
37
38
39
40
41
#include "core.h"
#include "bin/bin.h"
#include "project/projectmanager.h"
#include "doc/kdenlivedoc.h"
#include "mainwindow.h"
42

43
#include "klocalizedstring.h"
44
#include <KRecentDirs>
45
#include <KDualAction>
46
47
#include <KSelectAction>
#include <KMessageWidget>
48
#include <KMessageBox>
49

50
#include <QDebug>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
51
#include <QMouseEvent>
52
53
#include <QMenu>
#include <QToolButton>
54
#include <QToolBar>
55
#include <QDesktopWidget>
56
#include <QLabel>
57
#include <QVBoxLayout>
58
#include <QSlider>
59
#include <QDrag>
60
#include <QFileDialog>
61
#include <QMimeData>
62
#include <QQuickItem>
63
#include <QScrollBar>
64
#include <QWidgetAction>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
65

66
67
#define SEEK_INACTIVE (-1)

68
69


70
71
72
73
74
75
QuickEventEater::QuickEventEater(QObject *parent) : QObject(parent)
{
}

bool QuickEventEater::eventFilter(QObject *obj, QEvent *event)
{
76
77
78
79
80
81
82
83
84
    switch (event->type()) {
        case QEvent::DragEnter:
        {
            QDragEnterEvent *ev = reinterpret_cast< QDragEnterEvent* >(event);
            if (ev->mimeData()->hasFormat(QStringLiteral("kdenlive/effectslist"))) {
                ev->acceptProposedAction();
                return true;
            }
            break;
85
        }
86
87
88
89
90
91
92
93
        case QEvent::DragMove:
        {
            QDragEnterEvent *ev = reinterpret_cast< QDragEnterEvent* >(event);
            if (ev->mimeData()->hasFormat(QStringLiteral("kdenlive/effectslist"))) {
                ev->acceptProposedAction();
                return true;
            }
            break;
94
        }
95
96
97
98
99
100
101
102
103
104
105
106
        case QEvent::Drop:
        {
            QDropEvent *ev = static_cast< QDropEvent* >(event);
            if (ev) {
                const QString effects = QString::fromUtf8(ev->mimeData()->data(QStringLiteral("kdenlive/effectslist")));
                QDomDocument doc;
                doc.setContent(effects, true);
               emit addEffect(doc.documentElement());
               ev->accept();
               return true;
            }
            break;
107
        }
108
109
        default:
            break;
110
111
112
113
    }
    return QObject::eventFilter(obj, event);
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
QuickMonitorEventEater::QuickMonitorEventEater(QWidget *parent) : QObject(parent)
{
}

bool QuickMonitorEventEater::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *ev = static_cast< QKeyEvent* >(event);
        if (ev) {
            emit doKeyPressEvent(ev);
            return true;
        }
    }
    return QObject::eventFilter(obj, event);
}
130

131

132
Monitor::Monitor(Kdenlive::MonitorId id, MonitorManager *manager, QWidget *parent) :
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
133
134
    AbstractMonitor(id, manager, parent)
    , render(NULL)
Jean-Baptiste Mardelle's avatar
cleanup    
Jean-Baptiste Mardelle committed
135
    , m_controller(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
136
137
138
    , m_glMonitor(NULL)
    , m_splitEffect(NULL)
    , m_splitProducer(NULL)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
139
140
    , m_length(2)
    , m_dragStarted(false)
Vincent Pinon's avatar
Vincent Pinon committed
141
    , m_recManager(NULL)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
142
    , m_loopClipAction(NULL)
143
    , m_sceneVisibilityAction(NULL)
144
    , m_multitrackView(NULL)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
145
146
147
148
    , m_contextMenu(NULL)
    , m_selectedClip(NULL)
    , m_loopClipTransition(true)
    , m_editMarker(NULL)
149
    , m_forceSizeFactor(0)
150
    , m_lastMonitorSceneType(MonitorSceneDefault)
151
{
152
    QVBoxLayout *layout = new QVBoxLayout;
153
    layout->setContentsMargins(0, 0, 0, 0);
154
    layout->setSpacing(0);
155

156
157
158
159
160
161
    // Create container widget
    m_glWidget = new QWidget;
    QGridLayout* glayout = new QGridLayout(m_glWidget);
    glayout->setSpacing(0);
    glayout->setContentsMargins(0, 0, 0, 0);
    // Create QML OpenGL widget
162
    m_glMonitor = new GLWidget((int) id);
163
    connect(m_glMonitor, SIGNAL(passKeyEvent(QKeyEvent*)), this, SLOT(doKeyPressEvent(QKeyEvent*)));
164
    connect(m_glMonitor, &GLWidget::panView, this, &Monitor::panView);
165
    m_videoWidget = QWidget::createWindowContainer(qobject_cast<QWindow*>(m_glMonitor));
166
167
168
169
170
    m_videoWidget->setAcceptDrops(true);
    QuickEventEater *leventEater = new QuickEventEater(this);
    m_videoWidget->installEventFilter(leventEater);
    connect(leventEater, &QuickEventEater::addEffect, this, &Monitor::slotAddEffect);

171
172
173
174
    m_qmlManager = new QmlManager(m_glMonitor);
    connect(m_qmlManager, &QmlManager::effectChanged, this, &Monitor::effectChanged);
    connect(m_qmlManager, &QmlManager::effectPointsChanged, this, &Monitor::effectPointsChanged);

175
    QuickMonitorEventEater *monitorEventEater = new QuickMonitorEventEater(this);
176
177
178
    m_glWidget->installEventFilter(monitorEventEater);
    connect(monitorEventEater, SIGNAL(doKeyPressEvent(QKeyEvent*)), this, SLOT(doKeyPressEvent(QKeyEvent*)));

179
    glayout->addWidget(m_videoWidget, 0, 0);
180
181
182
183
184
185
    m_verticalScroll = new QScrollBar(Qt::Vertical);
    glayout->addWidget(m_verticalScroll, 0, 1);
    m_verticalScroll->hide();
    m_horizontalScroll = new QScrollBar(Qt::Horizontal);
    glayout->addWidget(m_horizontalScroll, 1, 0);
    m_horizontalScroll->hide();
186
187
    connect(m_horizontalScroll, &QScrollBar::valueChanged, this, &Monitor::setOffsetX);
    connect(m_verticalScroll, &QScrollBar::valueChanged, this, &Monitor::setOffsetY);
188
    connect(m_glMonitor, SIGNAL(frameDisplayed(const SharedFrame&)), this, SLOT(onFrameDisplayed(const SharedFrame&)));
189
    connect(m_glMonitor, SIGNAL(mouseSeek(int,int)), this, SLOT(slotMouseSeek(int,int)));
190
191
    connect(m_glMonitor, SIGNAL(monitorPlay()), this, SLOT(slotPlay()));
    connect(m_glMonitor, SIGNAL(startDrag()), this, SLOT(slotStartDrag()));
192
    connect(m_glMonitor, SIGNAL(switchFullScreen(bool)), this, SLOT(slotSwitchFullScreen(bool)));
193
    connect(m_glMonitor, SIGNAL(zoomChanged()), this, SLOT(setZoom()));
194
    connect(m_glMonitor, SIGNAL(lockMonitor(bool)), this, SLOT(slotLockMonitor(bool)), Qt::DirectConnection);
195
    connect(m_glMonitor, SIGNAL(showContextMenu(QPoint)), this, SLOT(slotShowMenu(QPoint)));
196
    connect(m_glMonitor, &GLWidget::gpuNotSupported, this, &Monitor::gpuError);
197

198
199
    m_glWidget->setMinimumSize(QSize(320, 180));
    layout->addWidget(m_glWidget, 10);
200
201
202
    layout->addStretch();

    // Tool bar buttons
203
    m_toolbar = new QToolBar(this);
204
205
206
    QWidget *sp1 = new QWidget(this);
    sp1->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
    m_toolbar->addWidget(sp1);
207
208
    if (id == Kdenlive::ClipMonitor) {
        // Add options for recording
209
        m_recManager = new RecManager(this);
210
        connect(m_recManager, SIGNAL(warningMessage(QString,int,QList <QAction*>)), this, SLOT(warningMessage(QString,int,QList <QAction*>)));
211
        connect(m_recManager, &RecManager::addClipToProject, this, &Monitor::addClipToProject);
212
213
    }

214
    if (id != Kdenlive::DvdMonitor) {
215
216
        m_toolbar->addAction(manager->getAction(QStringLiteral("mark_in")));
        m_toolbar->addAction(manager->getAction(QStringLiteral("mark_out")));
217
    }
218
    m_toolbar->addAction(manager->getAction(QStringLiteral("monitor_seek_backward")));
219

220
    QToolButton *playButton = new QToolButton(m_toolbar);
221
    m_playMenu = new QMenu(i18n("Play..."), this);
222
    QAction *originalPlayAction = static_cast<KDualAction*> (manager->getAction(QStringLiteral("monitor_play")));
223
    m_playAction = new KDualAction(i18n("Play"), i18n("Pause"), this);
224
225
    m_playAction->setInactiveIcon(KoIconUtils::themedIcon(QStringLiteral("media-playback-start")));
    m_playAction->setActiveIcon(KoIconUtils::themedIcon(QStringLiteral("media-playback-pause")));
226
227
228
229
230
231
232
233
234

    QString strippedTooltip = m_playAction->toolTip().remove(QRegExp("\\s\\(.*\\)"));
    // append shortcut if it exists for action
    if (originalPlayAction->shortcut() == QKeySequence(0)) {
        m_playAction->setToolTip( strippedTooltip);
    }
    else {
        m_playAction->setToolTip( strippedTooltip + " (" + originalPlayAction->shortcut().toString() + ")");
    }
235
    m_playMenu->addAction(m_playAction);
236
    connect(m_playAction, &QAction::triggered, this, &Monitor::slotSwitchPlay);
237
238
239

    playButton->setMenu(m_playMenu);
    playButton->setPopupMode(QToolButton::MenuButtonPopup);
240
    m_toolbar->addWidget(playButton);
241
    m_toolbar->addAction(manager->getAction(QStringLiteral("monitor_seek_forward")));
242
243

    playButton->setDefaultAction(m_playAction);
244
    m_configMenu = new QMenu(i18n("Misc..."), this);
245

246
247
    if (id != Kdenlive::DvdMonitor) {
        if (id == Kdenlive::ClipMonitor) {
248
            m_markerMenu = new QMenu(i18n("Go to marker..."), this);
249
250
        } else {
            m_markerMenu = new QMenu(i18n("Go to guide..."), this);
251
        }
252
253
254
        m_markerMenu->setEnabled(false);
        m_configMenu->addMenu(m_markerMenu);
        connect(m_markerMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotGoToMarker(QAction*)));
255
        m_forceSize = new KSelectAction(KoIconUtils::themedIcon(QStringLiteral("transform-scale")), i18n("Force Monitor Size"), this);
256
257
258
259
260
261
262
263
264
        QAction *fullAction = m_forceSize->addAction(QIcon(), i18n("Force 100%"));
        fullAction->setData(100);
        QAction *halfAction = m_forceSize->addAction(QIcon(), i18n("Force 50%"));
        halfAction->setData(50);
        QAction *freeAction = m_forceSize->addAction(QIcon(), i18n("Free Resize"));
        freeAction->setData(0);
        m_configMenu->addAction(m_forceSize);
        m_forceSize->setCurrentAction(freeAction);
        connect(m_forceSize, SIGNAL(triggered(QAction*)), this, SLOT(slotForceSize(QAction*)));
265
    }
266

267
268
269
    // Create Volume slider popup
    m_audioSlider = new QSlider(Qt::Vertical);
    m_audioSlider->setRange(0, 100);
270
271
272
273
274
275
276
277
278
279
    m_audioSlider->setValue(100);
    connect(m_audioSlider, &QSlider::valueChanged, this, &Monitor::slotSetVolume);
    QWidgetAction *widgetslider = new QWidgetAction(this);
    widgetslider->setText(i18n("Audio volume"));
    widgetslider->setDefaultWidget(m_audioSlider);
    QMenu *menu = new QMenu(this);
    menu->addAction(widgetslider);

    m_audioButton = new QToolButton(this);
    m_audioButton->setMenu(menu);
280
    m_audioButton->setToolTip(i18n("Volume"));
281
    m_audioButton->setPopupMode(QToolButton::InstantPopup);
282
    QIcon icon;
283
284
    if (KdenliveSettings::volume() == 0) icon = KoIconUtils::themedIcon(QStringLiteral("audio-volume-muted"));
    else icon = KoIconUtils::themedIcon(QStringLiteral("audio-volume-medium"));
285
286
287
    m_audioButton->setIcon(icon);
    m_toolbar->addWidget(m_audioButton);

288
289
290
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setLayout(layout);
    setMinimumHeight(200);
291

292
    render = new Render(m_id, m_monitorManager->binController(), m_glMonitor, this);
293

294
    // Monitor ruler
295
    m_ruler = new SmallRuler(this, render);
296
    if (id == Kdenlive::DvdMonitor) m_ruler->setZone(-3, -2);
297
    layout->addWidget(m_ruler);
298

299
    connect(render, SIGNAL(durationChanged(int,int)), this, SLOT(adjustRulerSize(int,int)));
300
    connect(render, SIGNAL(rendererStopped(int)), this, SLOT(rendererStopped(int)));
301
302
    connect(render, &AbstractRender::scopesClear, m_glMonitor, &GLWidget::releaseAnalyse, Qt::DirectConnection);
    connect(m_glMonitor, SIGNAL(analyseFrame(QImage)), render, SIGNAL(frameUpdated(QImage)));
303
    connect(m_glMonitor, SIGNAL(audioSamplesSignal(const audioShortVector&,int,int,int)), render, SIGNAL(audioSamplesSignal(const audioShortVector&,int,int,int)));
304

305
    if (id != Kdenlive::ClipMonitor) {
306
        connect(render, SIGNAL(durationChanged(int)), this, SIGNAL(durationChanged(int)));
307
308
309
        connect(m_ruler, SIGNAL(zoneChanged(QPoint)), this, SIGNAL(zoneUpdated(QPoint)));
    } else {
        connect(m_ruler, SIGNAL(zoneChanged(QPoint)), this, SLOT(setClipZone(QPoint)));
310
    }
311

312
313
314
315
316
    m_sceneVisibilityAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("transform-crop")), i18n("Show/Hide edit mode"), this);
    m_sceneVisibilityAction->setCheckable(true);
    m_sceneVisibilityAction->setChecked(KdenliveSettings::showOnMonitorScene());
    connect(m_sceneVisibilityAction, SIGNAL(triggered(bool)), this, SLOT(slotEnableEffectScene(bool)));
    m_toolbar->addAction(m_sceneVisibilityAction);
317

318
319
320
    m_zoomVisibilityAction = new QAction(KoIconUtils::themedIcon(QStringLiteral("zoom-in")), i18n("Zoom"), this);
    m_zoomVisibilityAction->setCheckable(true);
    connect(m_zoomVisibilityAction, SIGNAL(triggered(bool)), this, SLOT(slotEnableSceneZoom(bool)));
321

322
323
324
325
326
    m_toolbar->addSeparator();
    m_timePos = new TimecodeDisplay(m_monitorManager->timecode(), this);
    m_toolbar->addWidget(m_timePos);

    QToolButton *configButton = new QToolButton(m_toolbar);
327
328
    configButton->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-menu")));
    configButton->setToolTip(i18n("Options"));
329
    configButton->setMenu(m_configMenu);
330
    configButton->setPopupMode(QToolButton::InstantPopup);
331
332
    m_toolbar->addWidget(configButton);
    if (m_recManager) m_toolbar->addAction(m_recManager->switchAction());
333
    /*QWidget *spacer = new QWidget(this);
334
    spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
335
336
337
    m_toolbar->addWidget(spacer);*/

    m_toolbar->addSeparator();
338
339
340
341
    int tm = 0;
    int bm = 0;
    m_toolbar->getContentsMargins(0, &tm, 0, &bm);
    m_audioMeterWidget = new MonitorAudioLevel(m_glMonitor->profile(), m_toolbar->height() - tm - bm, this);
342
    m_toolbar->addWidget(m_audioMeterWidget);
343
    if (!m_audioMeterWidget->isValid) {
344
345
346
347
        KdenliveSettings::setMonitoraudio(0x01);
        m_audioMeterWidget->setVisibility(false);
    } else {
        m_audioMeterWidget->setVisibility((KdenliveSettings::monitoraudio() & m_id) != 0);
348
    }
349

350
    connect(m_timePos, SIGNAL(timeCodeEditingFinished()), this, SLOT(slotSeek()));
351
    layout->addWidget(m_toolbar);
352
    if (m_recManager) layout->addWidget(m_recManager->toolbar());
353
354

    // Load monitor overlay qml
355
    loadQmlScene(MonitorSceneDefault);
356

357
358
359
360
    // Info message widget
    m_infoMessage = new KMessageWidget(this);
    layout->addWidget(m_infoMessage);
    m_infoMessage->hide();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
361
362
}

363
364
Monitor::~Monitor()
{
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
365
    render->stop();
366
    delete m_audioMeterWidget;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
367
    delete m_glMonitor;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
368
369
    delete m_videoWidget;
    delete m_glWidget;
370
371
    delete m_ruler;
    delete m_timePos;
372
    delete render;
373
374
}

375
376
377
378
379
380
381
382
383
384
void Monitor::setOffsetX(int x)
{
    m_glMonitor->setOffsetX(x, m_horizontalScroll->maximum());
}

void Monitor::setOffsetY(int y)
{
    m_glMonitor->setOffsetY(y, m_verticalScroll->maximum());
}

385
void Monitor::slotGetCurrentImage(bool request)
386
{
387
388
389
390
391
392
393
394
395
    m_glMonitor->sendFrameForAnalysis = request;
    if (request) {
        m_monitorManager->activateMonitor(m_id, true);
        refreshMonitorIfActive();
        // Update analysis state
        QTimer::singleShot(500, m_monitorManager, SIGNAL(checkScopes()));
    } else {
        m_glMonitor->releaseAnalyse();
    }
396
397
}

398
399
400
void Monitor::slotAddEffect(QDomElement effect)
{
    if (m_id == Kdenlive::ClipMonitor) {
401
        if (m_controller) emit addMasterEffect(m_controller->clipId(), effect);
402
403
404
405
    }
    else emit addEffect(effect);
}

406
407
408
409
410
411
void Monitor::refreshIcons()
{
    QList<QAction *> allMenus = this->findChildren<QAction *>();
    for (int i = 0; i < allMenus.count(); i++) {
        QAction *m = allMenus.at(i);
        QIcon ic = m->icon();
412
        if (ic.isNull() || ic.name().isEmpty()) continue;
413
414
415
416
417
418
419
        QIcon newIcon = KoIconUtils::themedIcon(ic.name());
        m->setIcon(newIcon);
    }
    QList<KDualAction *> allButtons = this->findChildren<KDualAction *>();
    for (int i = 0; i < allButtons.count(); i++) {
        KDualAction *m = allButtons.at(i);
        QIcon ic = m->activeIcon();
420
        if (ic.isNull() || ic.name().isEmpty()) continue;
421
422
423
        QIcon newIcon = KoIconUtils::themedIcon(ic.name());
        m->setActiveIcon(newIcon);
        ic = m->inactiveIcon();
424
        if (ic.isNull() || ic.name().isEmpty()) continue;
425
426
427
428
429
        newIcon = KoIconUtils::themedIcon(ic.name());
        m->setInactiveIcon(newIcon);
    }
}

430
QAction *Monitor::recAction()
431
{
432
433
434
435
    if (m_recManager) return m_recManager->switchAction();
    return NULL;
}

436
437
438
439
440
void Monitor::slotLockMonitor(bool lock)
{
    m_monitorManager->lockMonitor(m_id, lock);
}

441
void Monitor::setupMenu(QMenu *goMenu, QMenu *overlayMenu, QAction *playZone, QAction *loopZone, QMenu *markerMenu, QAction *loopClip)
442
{
443
444
    if (m_contextMenu)
        delete m_contextMenu;
445
    m_contextMenu = new QMenu(this);
446
    m_contextMenu->addMenu(m_playMenu);
447
448
    if (goMenu)
        m_contextMenu->addMenu(goMenu);
449

450
    if (markerMenu) {
451
        m_contextMenu->addMenu(markerMenu);
452
        QList <QAction *>list = markerMenu->actions();
453
        for (int i = 0; i < list.count(); ++i) {
454
            if (list.at(i)->data().toString() == QLatin1String("edit_marker")) {
455
456
457
458
459
                m_editMarker = list.at(i);
                break;
            }
        }
    }
460

461
462
    m_playMenu->addAction(playZone);
    m_playMenu->addAction(loopZone);
463
464
465
466
    if (loopClip) {
        m_loopClipAction = loopClip;
        m_playMenu->addAction(loopClip);
    }
467

468
    //TODO: add save zone to timeline monitor when fixed
469
    m_contextMenu->addMenu(m_markerMenu);
470
    if (m_id == Kdenlive::ClipMonitor) {
471
472
	m_contextMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("document-save")), i18n("Save zone"), this, SLOT(slotSaveZone()));
        QAction *extractZone = m_configMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Extract Zone"), this, SLOT(slotExtractCurrentZone()));
473
        m_contextMenu->addAction(extractZone);
474
    }
475
    QAction *extractFrame = m_configMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Extract frame..."), this, SLOT(slotExtractCurrentFrame()));
476
477
    m_contextMenu->addAction(extractFrame);

478
479
480
    QAction *extractFrameToProject = m_configMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Extract frame to project..."), this, SLOT(slotExtractCurrentFrameToProject()));
    m_contextMenu->addAction (extractFrameToProject);

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
481
    if (m_id == Kdenlive::ProjectMonitor) {
482
483
484
        m_multitrackView = m_contextMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("view-split-left-right")), i18n("Multitrack view"), this, SIGNAL(multitrackView(bool)));
        m_multitrackView->setCheckable(true);
        m_configMenu->addAction(m_multitrackView);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
485
    } else if (m_id == Kdenlive::ClipMonitor) {
486
        QAction *setThumbFrame = m_contextMenu->addAction(KoIconUtils::themedIcon(QStringLiteral("document-new")), i18n("Set current image as thumbnail"), this, SLOT(slotSetThumbFrame()));
487
488
489
        m_configMenu->addAction(setThumbFrame);
    }

490
491
492
    if (overlayMenu)
        m_contextMenu->addMenu(overlayMenu);

493
494
495
496
    QAction *overlayAudio = m_contextMenu->addAction(QIcon(), i18n("Overlay audio waveform"));
    overlayAudio->setCheckable(true);
    connect(overlayAudio, SIGNAL(toggled(bool)), m_glMonitor, SLOT(slotSwitchAudioOverlay(bool)));
    overlayAudio->setChecked(KdenliveSettings::displayAudioOverlay());
497

498
499
500
    QAction *switchAudioMonitor = m_configMenu->addAction(i18n("Show Audio Levels"), this, SLOT(slotSwitchAudioMonitor()));
    switchAudioMonitor->setCheckable(true);
    switchAudioMonitor->setChecked(KdenliveSettings::monitoraudio() & m_id);
501
    m_configMenu->addAction(overlayAudio);
502
    m_configMenu->addAction(m_zoomVisibilityAction);
503
    m_contextMenu->addAction(m_zoomVisibilityAction);
504
505
506
    // For some reason, the frame in QAbstracSpinBox (base class of TimeCodeDisplay) needs to be displayed once, then hidden
    // or it will never appear (supposed to appear on hover).
    m_timePos->setFrame(false);
507
508
}

509
510
511
512
513
514
void Monitor::slotGoToMarker(QAction *action)
{
    int pos = action->data().toInt();
    slotSeek(pos);
}

515
void Monitor::slotForceSize(QAction *a)
516
{
517
518
519
520
521
522
523
524
525
526
527
528
    int resizeType = a->data().toInt();
    int profileWidth = 320;
    int profileHeight = 200;
    if (resizeType > 0) {
        // calculate size
        QRect r = QApplication::desktop()->screenGeometry();
        profileWidth = m_glMonitor->profileSize().width() * resizeType / 100;
        profileHeight = m_glMonitor->profileSize().height() * resizeType / 100;
        if (profileWidth > r.width() * 0.8 || profileHeight > r.height() * 0.7) {
            // reset action to free resize
            QList< QAction * > list = m_forceSize->actions ();
            foreach(QAction *ac, list) {
529
                if (ac->data().toInt() == m_forceSizeFactor) {
530
531
532
533
                    m_forceSize->setCurrentAction(ac);
                    break;
                }
            }
534
            warningMessage(i18n("Your screen resolution is not sufficient for this action"));
535
536
            return;
        }
537
    }
538
539
540
541
542
543
544
545
546
547
548
549
    switch (resizeType) {
      case 100:
      case 50:
          // resize full size
          setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
          m_videoWidget->setMinimumSize(profileWidth, profileHeight);
          m_videoWidget->setMaximumSize(profileWidth, profileHeight);
          setMinimumSize(QSize(profileWidth, profileHeight + m_toolbar->height() + m_ruler->height()));
          break;
      default:
        // Free resize
        m_videoWidget->setMinimumSize(profileWidth, profileHeight);
550
        m_videoWidget->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
551
552
553
        setMinimumSize(QSize(profileWidth, profileHeight + m_toolbar->height() + m_ruler->height()));
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        break;
554
    }
555
    m_forceSizeFactor = resizeType;
556
557
558
    updateGeometry();
}

559
560
561
562
563
564
565
566
567
568
QString Monitor::getTimecodeFromFrames(int pos)
{
    return m_monitorManager->timecode().getTimecodeFromFrames(pos);
}

double Monitor::fps() const
{
    return m_monitorManager->timecode().fps();
}

569
570
571
572
573
Timecode Monitor::timecode() const
{
    return m_monitorManager->timecode();
}

574
void Monitor::updateMarkers()
575
{
576
    if (m_controller) {
577
        m_markerMenu->clear();
578
        QList <CommentedTime> markers = m_controller->commentedSnapMarkers();
579
        if (!markers.isEmpty()) {
580
            for (int i = 0; i < markers.count(); ++i) {
581
                int pos = (int) markers.at(i).time().frames(m_monitorManager->timecode().fps());
582
583
584
585
                QString position = m_monitorManager->timecode().getTimecode(markers.at(i).time()) + ' ' + markers.at(i).comment();
                QAction *go = m_markerMenu->addAction(position);
                go->setData(pos);
            }
586
587
        }
	m_ruler->setMarkers(markers);
588
        m_markerMenu->setEnabled(!m_markerMenu->isEmpty());
589
        checkOverlay();
590
591
592
    }
}

593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
void Monitor::setGuides(QMap <double, QString> guides)
{
    m_markerMenu->clear();
    QMapIterator<double, QString> i(guides);
    QList <CommentedTime> guidesList;
    while (i.hasNext()) {
        i.next();
        CommentedTime timeGuide(GenTime(i.key()), i.value());
        guidesList << timeGuide;
        int pos = (int) timeGuide.time().frames(m_monitorManager->timecode().fps());
        QString position = m_monitorManager->timecode().getTimecode(timeGuide.time()) + ' ' + timeGuide.comment();
        QAction *go = m_markerMenu->addAction(position);
        go->setData(pos);
    }
    m_ruler->setMarkers(guidesList);
    m_markerMenu->setEnabled(!m_markerMenu->isEmpty());
    checkOverlay();
}

Laurent Montel's avatar
Laurent Montel committed
612
void Monitor::setMarkers(const QList<CommentedTime> &markers)
613
614
615
616
{
    m_ruler->setMarkers(markers);
}

617
618
void Monitor::slotSeekToPreviousSnap()
{
619
    if (m_controller) slotSeek(getSnapForPos(true).frames(m_monitorManager->timecode().fps()));
620
621
}

622
623
void Monitor::slotSeekToNextSnap()
{
624
    if (m_controller) slotSeek(getSnapForPos(false).frames(m_monitorManager->timecode().fps()));
625
626
}

627
628
GenTime Monitor::position()
{
629
    return render->seekPosition();
630
631
}

632
633
GenTime Monitor::getSnapForPos(bool previous)
{
634
    QList <GenTime> snaps;
635
    QList < GenTime > markers = m_controller->snapMarkers();
636
637
638
639
640
641
642
643
    for (int i = 0; i < markers.size(); ++i) {
        GenTime t = markers.at(i);
        snaps.append(t);
    }
    QPoint zone = m_ruler->zone();
    snaps.append(GenTime(zone.x(), m_monitorManager->timecode().fps()));
    snaps.append(GenTime(zone.y(), m_monitorManager->timecode().fps()));
    snaps.append(GenTime());
644
    snaps.append(m_controller->getPlaytime());
645
646
    qSort(snaps);

647
    const GenTime pos = render->seekPosition();
648
649
650
651
652
653
654
655
656
657
658
    for (int i = 0; i < snaps.size(); ++i) {
        if (previous && snaps.at(i) >= pos) {
            if (i == 0) i = 1;
            return snaps.at(i - 1);
        } else if (!previous && snaps.at(i) > pos) {
            return snaps.at(i);
        }
    }
    return GenTime();
}

659
660
void Monitor::slotZoneMoved(int start, int end)
{
661
    m_ruler->setZone(start, end);
662
    setClipZone(m_ruler->zone());
663
    checkOverlay();
664
665
}

666
667
void Monitor::slotSetZoneStart()
{
668
    m_ruler->setZoneStart();
669
    emit zoneUpdated(m_ruler->zone());
670
    setClipZone(m_ruler->zone());
671
    checkOverlay();
672
673
}

674
void Monitor::slotSetZoneEnd(bool discardLastFrame)
675
{
676
    m_ruler->setZoneEnd(discardLastFrame);
677
    emit zoneUpdated(m_ruler->zone());
678
    setClipZone(m_ruler->zone());
679
    checkOverlay();
680
681
}

682
// virtual
683
684
void Monitor::mousePressEvent(QMouseEvent * event)
{
685
    m_monitorManager->activateMonitor(m_id);
686
    if (!(event->button() & Qt::RightButton)) {
687
        if (m_glWidget->geometry().contains(event->pos())) {
688
            m_DragStartPosition = event->pos();
689
            event->accept();
690
        }
691
    } else if (m_contextMenu) {
692
        slotActivateMonitor();
693
        m_contextMenu->popup(event->globalPos());
694
        event->accept();
695
    }
696
    QWidget::mousePressEvent(event);
697
698
}

699
700
void Monitor::slotShowMenu(const QPoint pos)
{
701
    slotActivateMonitor();
702
    if (m_contextMenu) m_contextMenu->popup(pos);
703
704
}

705
706
void Monitor::resizeEvent(QResizeEvent *event)
{
Vincent Pinon's avatar
Vincent Pinon committed
707
    Q_UNUSED(event)
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
    if (m_glMonitor->zoom() > 0.0f) {
        float horizontal = float(m_horizontalScroll->value()) / m_horizontalScroll->maximum();
        float vertical = float(m_verticalScroll->value()) / m_verticalScroll->maximum();
        adjustScrollBars(horizontal, vertical);
    } else {
        m_horizontalScroll->hide();
        m_verticalScroll->hide();
    }
}

void Monitor::adjustScrollBars(float horizontal, float vertical)
{
    if (m_glMonitor->zoom() > 1.0f) {
        m_horizontalScroll->setPageStep(m_glWidget->width());
        m_horizontalScroll->setMaximum(m_glMonitor->profileSize().width() * m_glMonitor->zoom()
                                       - m_horizontalScroll->pageStep());
        m_horizontalScroll->setValue(qRound(horizontal * m_horizontalScroll->maximum()));
        emit m_horizontalScroll->valueChanged(m_horizontalScroll->value());
        m_horizontalScroll->show();
    } else {
        int max = m_glMonitor->profileSize().width() * m_glMonitor->zoom() - m_glWidget->width();
        emit m_horizontalScroll->valueChanged(qRound(0.5 * max));
        m_horizontalScroll->hide();
    }

    if (m_glMonitor->zoom() > 1.0f) {
        m_verticalScroll->setPageStep(m_glWidget->height());
        m_verticalScroll->setMaximum(m_glMonitor->profileSize().height() * m_glMonitor->zoom()
                                     - m_verticalScroll->pageStep());
        m_verticalScroll->setValue(qRound(vertical * m_verticalScroll->maximum()));
        emit m_verticalScroll->valueChanged(m_verticalScroll->value());
        m_verticalScroll->show();
    } else {
        int max = m_glMonitor->profileSize().height() * m_glMonitor->zoom() - m_glWidget->height();
        emit m_verticalScroll->valueChanged(qRound(0.5 * max));
        m_verticalScroll->hide();
    }
}

void Monitor::setZoom()
{
    if (m_glMonitor->zoom() == 1.0f) {
        m_horizontalScroll->hide();
        m_verticalScroll->hide();
752
753
        m_glMonitor->setOffsetX(m_horizontalScroll->value(), m_horizontalScroll->maximum());
        m_glMonitor->setOffsetY(m_verticalScroll->value(), m_verticalScroll->maximum());
754
755
756
    } else {
        adjustScrollBars(0.5f, 0.5f);
    }
757
758
}

759
void Monitor::slotSwitchFullScreen(bool minimizeOnly)
760
{
761
762
763
764
765
766
767
768
769
770
    // TODO: disable screensaver?
    if (!m_glWidget->isFullScreen() && !minimizeOnly) {
        // Check if we have a multiple monitor setup
        int monitors = QApplication::desktop()->screenCount();
        int screen = -1;
        if (monitors > 1) {
            QRect screenres;
            // Move monitor widget to the second screen (one screen for Kdenlive, the other one for the Monitor widget
            //int currentScreen = QApplication::desktop()->screenNumber(this);
            for (int i = 0; screen == -1 && i < QApplication::desktop()->screenCount(); i++) {
771
            if (i != QApplication::desktop()->screenNumber(this->parentWidget()->parentWidget()))
772
773
774
                screen = i;
            }
        }
775
        m_qmlManager->enableAudioThumbs(false);
776
777
778
779
780
        m_glWidget->setParent(QApplication::desktop()->screen(screen));
        m_glWidget->move(QApplication::desktop()->screenGeometry(screen).bottomLeft());
        m_glWidget->showFullScreen();
    } else {
        m_glWidget->showNormal();
781
        m_qmlManager->enableAudioThumbs(true);
782
783
784
        QVBoxLayout *lay = (QVBoxLayout *) layout();
        lay->insertWidget(0, m_glWidget, 10);
    }
785
786
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
787
788
789
790
791
792
793
794
795
796
void Monitor::reparent()
{
    m_glWidget->setParent(NULL);
    m_glWidget->showMinimized();
    m_glWidget->showNormal();
    QVBoxLayout *lay = (QVBoxLayout *) layout();
    lay->insertWidget(0, m_glWidget, 10);
}


797
// virtual
798
799
void Monitor::mouseReleaseEvent(QMouseEvent * event)
{
800
801
802
803
804
    if (m_dragStarted) {
        event->ignore();
        return;
    }
    if (event->button() != Qt::RightButton) {
805
        if (m_glMonitor->geometry().contains(event->pos())) {
806
            if (isActive()) slotPlay();
807
808
809
            else {
              slotActivateMonitor();
            }
810
        } //else event->ignore(); //QWidget::mouseReleaseEvent(event);
811
    }
812
813
    m_dragStarted = false;
    event->accept();
814
    QWidget::mouseReleaseEvent(event);
815
816
}

817
818
819

void Monitor::slotStartDrag()
{
820
    if (m_id == Kdenlive::ProjectMonitor || m_controller == NULL) {
821
822
823
824
825
        // dragging is only allowed for clip monitor
        return;
    }
    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;
826

827
828
829
830
    QStringList list;
    list.append(m_controller->clipId());
    QPoint p = m_ruler->zone();
    list.append(QString::number(p.x()));
831
    list.append(QString::number(p.y()));
832
    QByteArray data;
833
834
    data.append(list.join(QStringLiteral(";")).toUtf8());
    mimeData->setData(QStringLiteral("kdenlive/clip"), data);
835
836
837
838
839
    drag->setMimeData(mimeData);
    /*QPixmap pix = m_currentClip->thumbnail();
    drag->setPixmap(pix);
    drag->setHotSpot(QPoint(0, 50));*/
    drag->start(Qt::MoveAction);
840
841
}

842
843
void Monitor::enterEvent(QEvent * event)
{
844
    m_qmlManager->enableAudioThumbs(true);
845
846
847
848
849
    QWidget::enterEvent(event);
}

void Monitor::leaveEvent(QEvent * event)
{
850
    m_qmlManager->enableAudioThumbs(false);
851
852
853
    QWidget::leaveEvent(event);
}

854
// virtual
855
856
void Monitor::mouseMoveEvent(QMouseEvent *event)
{
857
    if (m_dragStarted || m_controller == NULL) return;
858
859
860
861
862
863
864
865

    if ((event->pos() - m_DragStartPosition).manhattanLength()
            < QApplication::startDragDistance())
        return;

    {
        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;
866
        m_dragStarted = true;
867
        QStringList list;
868
        list.append(m_controller->clipId());
869
870
871
872
        QPoint p = m_ruler->zone();
        list.append(QString::number(p.x()));
        list.append(QString::number(p.y()));
        QByteArray data;
873
874
        data.append(list.join(QStringLiteral(";")).toUtf8());
        mimeData->setData(QStringLiteral("kdenlive/clip"), data);
875
876
877
        drag->setMimeData(mimeData);
        drag->start(Qt::MoveAction);
    }
878
    event->accept();
879
880
}

881

882
883
884
885
886
887
888
889
890
891
892
893
894
/*void Monitor::dragMoveEvent(QDragMoveEvent * event) {
    event->setDropAction(Qt::IgnoreAction);
    event->setDropAction(Qt::MoveAction);
    if (event->mimeData()->hasText()) {
        event->acceptProposedAction();
    }
}

Qt::DropActions Monitor::supportedDropActions() const {
    // returns what actions are supported when dropping
    return Qt::MoveAction;
}*/

895
896
QStringList Monitor::mimeTypes() const
{
897
898
    QStringList qstrList;
    // list of accepted mime types for drop
899
    qstrList.append(QStringLiteral("kdenlive/clip"));
900
901
902
    return qstrList;
}

903
// virtual
904
905
void Monitor::wheelEvent(QWheelEvent * event)
{
906
    slotMouseSeek(event->delta(), (int) event->modifiers());
907
908
909
    event->accept();
}

910
911
void Monitor::mouseDoubleClickEvent(QMouseEvent * event)
{
912
    slotSwitchFullScreen();
913
    event->accept();
914
915
}

916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
void Monitor::keyPressEvent(QKeyEvent* event)
{
    if (event->key()==Qt::Key_Escape) {
        slotSwitchFullScreen();
        event->accept();
        return;
    }
    if (m_glWidget->isFullScreen()) {
        event->ignore();
        emit passKeyPress(event);
        return;
    }
    QWidget::keyPressEvent(event);
}