]> git.sesse.net Git - kdenlive/blobdiff - src/stopmotion/stopmotion.cpp
Basic webcam (video4linux) autodetection
[kdenlive] / src / stopmotion / stopmotion.cpp
index c11ddc56ffd69208f664ee8fa17de51a84d2cec8..e4e89f05470d37dbe1da8c86ccee7f92f2ab2449 100644 (file)
 #include <kdeversion.h>
 #include <KNotification>
 
+#ifdef QIMAGEBLITZ
+#include <qimageblitz/qimageblitz.h>
+#endif
+
 #include <QtConcurrentRun>
 #include <QInputDialog>
 #include <QComboBox>
@@ -38,6 +42,7 @@
 #include <QPainter>
 #include <QAction>
 #include <QWheelEvent>
+#include <QMenu>
 
 MyLabel::MyLabel(QWidget *parent) :
     QLabel(parent)
@@ -56,6 +61,12 @@ void MyLabel::wheelEvent(QWheelEvent * event)
     else emit seek(false);
 }
 
+//virtual
+void MyLabel::mousePressEvent(QMouseEvent *)
+{
+    emit switchToLive();
+}
+
 //virtual
 void MyLabel::paintEvent(QPaintEvent * event)
 {
@@ -78,7 +89,7 @@ void MyLabel::paintEvent(QPaintEvent * event)
 }
 
 
-StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
+StopmotionWidget::StopmotionWidget(KUrl projectFolder, const QList< QAction * > actions, QWidget *parent) :
     QDialog(parent)
     , Ui::Stopmotion_UI()
     , m_projectFolder(projectFolder)
@@ -86,24 +97,92 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     , m_sequenceFrame(0)
     , m_animatedIndex(-1)
 {
+    addActions(actions);
     setupUi(this);
     setWindowTitle(i18n("Stop Motion Capture"));
     setFont(KGlobalSettings::toolBarFont());
 
     live_button->setIcon(KIcon("camera-photo"));
-    frameoverlay_button->setIcon(KIcon("edit-paste"));
-    m_captureAction = new QAction(KIcon("media-record"), i18n("Capture frame"), this);
-    m_captureAction->setShortcut(QKeySequence(Qt::Key_Space));
+
+    m_captureAction = actions.at(0);
     connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
     capture_button->setDefaultAction(m_captureAction);
 
+    connect(actions.at(1), SIGNAL(triggered()), this, SLOT(slotSwitchLive()));
+
     preview_button->setIcon(KIcon("media-playback-start"));
-    removelast_button->setIcon(KIcon("edit-delete"));
-    frameoverlay_button->setEnabled(false);
-    removelast_button->setEnabled(false);
     capture_button->setEnabled(false);
 
+    // Build config menu
+    QMenu *confMenu = new QMenu;
+    m_showOverlay = actions.at(2);
+    connect(m_showOverlay, SIGNAL(triggered(bool)), this, SLOT(slotShowOverlay(bool)));
+    confMenu->addAction(m_showOverlay);
+
+#ifdef QIMAGEBLITZ
+    m_effectIndex = KdenliveSettings::blitzeffect();
+    QMenu *effectsMenu = new QMenu(i18n("Overlay effect"));
+    QActionGroup *effectGroup = new QActionGroup(this);
+    QAction *noEffect = new QAction(i18n("No Effect"), effectGroup);
+    noEffect->setData(1);
+    QAction *contrastEffect = new QAction(i18n("Contrast"), effectGroup);
+    contrastEffect->setData(2);
+    QAction *edgeEffect = new QAction(i18n("Edge detect"), effectGroup);
+    edgeEffect->setData(3);
+    QAction *brightEffect = new QAction(i18n("Brighten"), effectGroup);
+    brightEffect->setData(4);
+    QAction *invertEffect = new QAction(i18n("Invert"), effectGroup);
+    invertEffect->setData(5);
+    QAction *thresEffect = new QAction(i18n("Threshold"), effectGroup);
+    thresEffect->setData(6);
+
+    effectsMenu->addAction(noEffect);
+    effectsMenu->addAction(contrastEffect);
+    effectsMenu->addAction(edgeEffect);
+    effectsMenu->addAction(brightEffect);
+    effectsMenu->addAction(invertEffect);
+    effectsMenu->addAction(thresEffect);
+    QList <QAction *> list = effectsMenu->actions();
+    for (int i = 0; i < list.count(); i++) {
+        list.at(i)->setCheckable(true);
+        if (list.at(i)->data().toInt() == m_effectIndex) {
+            list.at(i)->setChecked(true);
+        }
+    }
+    connect(effectsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotUpdateOverlayEffect(QAction*)));
+    confMenu->addMenu(effectsMenu);
+#endif
+
+    QAction *showThumbs = new QAction(KIcon("image-x-generic"), i18n("Show sequence thumbnails"), this);
+    showThumbs->setCheckable(true);
+    showThumbs->setChecked(KdenliveSettings::showstopmotionthumbs());
+    connect(showThumbs, SIGNAL(triggered(bool)), this, SLOT(slotShowThumbs(bool)));
+
+    QAction *removeCurrent = new QAction(KIcon("edit-delete"), i18n("Delete current frame"), this);
+    removeCurrent->setShortcut(Qt::Key_Delete);
+    connect(removeCurrent, SIGNAL(triggered()), this, SLOT(slotRemoveFrame()));
+
+    QAction *capInterval = new QAction(KIcon(), i18n("Set capture interval"), this);
+    connect(capInterval, SIGNAL(triggered()), this, SLOT(slotSetCaptureInterval()));
+
+    confMenu->addAction(showThumbs);
+    confMenu->addAction(capInterval);
+    confMenu->addAction(removeCurrent);
+    config_button->setIcon(KIcon("configure"));
+    config_button->setMenu(confMenu);
+
+    // Build capture menu
+    QMenu *capMenu = new QMenu;
+    m_intervalCapture = new QAction(KIcon("edit-redo"), i18n("Interval capture"), this);
+    m_intervalCapture->setCheckable(true);
+    m_intervalCapture->setChecked(false);
+    connect(m_intervalCapture, SIGNAL(triggered(bool)), this, SLOT(slotIntervalCapture(bool)));
+    capMenu->addAction(m_intervalCapture);
+    capture_button->setMenu(capMenu);
+
     connect(sequence_name, SIGNAL(textChanged(const QString &)), this, SLOT(sequenceNameChanged(const QString &)));
+    connect(sequence_name, SIGNAL(currentIndexChanged(int)), live_button, SLOT(setFocus()));
+
     m_layout = new QVBoxLayout;
     if (BMInterface::getBlackMagicDeviceList(capture_device, NULL)) {
         // Found a BlackMagic device
@@ -112,25 +191,29 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     }
     if (QFile::exists(KdenliveSettings::video4vdevice())) {
         if (m_bmCapture == NULL) m_bmCapture = new V4lCaptureHandler(m_layout);
-        capture_device->addItem(KdenliveSettings::video4vdevice(), "v4l");
+        capture_device->addItem(m_bmCapture->getDeviceName(KdenliveSettings::video4vdevice(), 0, 0), "v4l");
     }
+
+    connect(m_bmCapture, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString)));
     connect(capture_device, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateHandler()));
     m_frame_preview = new MyLabel(this);
     connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool)));
+    connect(m_frame_preview, SIGNAL(switchToLive()), this, SLOT(slotSwitchLive()));
     m_layout->addWidget(m_frame_preview);
     m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     video_preview->setLayout(m_layout);
     live_button->setChecked(false);
-    frameoverlay_button->setChecked(false);
     button_addsequence->setEnabled(false);
     connect(live_button, SIGNAL(clicked(bool)), this, SLOT(slotLive(bool)));
-    connect(frameoverlay_button, SIGNAL(clicked(bool)), this, SLOT(slotShowOverlay(bool)));
-    connect(frame_number, SIGNAL(valueChanged(int)), this, SLOT(slotShowFrame(int)));
     connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
-    connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview()));
+    connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview(bool)));
     connect(frame_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotShowSelectedFrame()));
+    connect(frame_list, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(slotShowSelectedFrame()));
     connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
 
+    frame_list->addAction(removeCurrent);
+    frame_list->setContextMenuPolicy(Qt::ActionsContextMenu);
+    frame_list->setHidden(!KdenliveSettings::showstopmotionthumbs());
     parseExistingSequences();
 }
 
@@ -139,6 +222,47 @@ StopmotionWidget::~StopmotionWidget()
     m_bmCapture->stopPreview();
 }
 
+void StopmotionWidget::slotUpdateOverlayEffect(QAction *act)
+{
+#ifdef QIMAGEBLITZ
+    if (act) m_effectIndex = act->data().toInt();
+    KdenliveSettings::setBlitzeffect(m_effectIndex);
+    if (m_showOverlay->isChecked()) slotUpdateOverlay();
+#endif
+}
+
+void StopmotionWidget::closeEvent(QCloseEvent *e)
+{
+    slotLive(false);
+    QDialog::closeEvent(e);
+}
+
+void StopmotionWidget::slotSetCaptureInterval()
+{
+    int interval = QInputDialog::getInteger(this, i18n("Set Capture Interval"), i18n("Interval (in seconds)"), KdenliveSettings::captureinterval(), 1);
+    if (interval > 0 && interval != KdenliveSettings::captureinterval())
+        KdenliveSettings::setCaptureinterval(interval);
+}
+
+void StopmotionWidget::slotShowThumbs(bool show)
+{
+    KdenliveSettings::setShowstopmotionthumbs(show);
+    if (show) {
+        frame_list->clear();
+        sequenceNameChanged(sequence_name->currentText());
+    } else {
+        m_filesList.clear();
+        frame_list->clear();
+    }
+    frame_list->setHidden(!show);
+}
+
+void StopmotionWidget::slotIntervalCapture(bool capture)
+{
+    if (capture) slotCaptureFrame();
+}
+
+
 void StopmotionWidget::slotUpdateHandler()
 {
     QString data = capture_device->itemData(capture_device->currentIndex()).toString();
@@ -174,16 +298,31 @@ void StopmotionWidget::parseExistingSequences()
     }
 }
 
+void StopmotionWidget::slotSwitchLive()
+{
+    setUpdatesEnabled(false);
+    if (m_frame_preview->isHidden()) {
+        m_bmCapture->hidePreview(true);
+        m_frame_preview->setHidden(false);
+    } else {
+        m_frame_preview->setHidden(true);
+        m_bmCapture->hidePreview(false);
+    }
+    setUpdatesEnabled(true);
+}
+
 void StopmotionWidget::slotLive(bool isOn)
 {
     if (isOn) {
-        m_frame_preview->setImage(QImage());
+        //m_frame_preview->setImage(QImage());
         m_frame_preview->setHidden(true);
-        m_bmCapture->startPreview(KdenliveSettings::hdmi_capturedevice(), KdenliveSettings::hdmi_capturemode());
+        m_bmCapture->startPreview(KdenliveSettings::hdmi_capturedevice(), KdenliveSettings::hdmi_capturemode(), false);
         capture_button->setEnabled(true);
     } else {
         m_bmCapture->stopPreview();
+        m_frame_preview->setHidden(false);
         capture_button->setEnabled(false);
+        live_button->setChecked(false);
     }
 }
 
@@ -207,6 +346,31 @@ void StopmotionWidget::slotUpdateOverlay()
         QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
         return;
     }
+
+#ifdef QIMAGEBLITZ
+    //img = Blitz::convolveEdge(img, 0, Blitz::Low);
+    switch (m_effectIndex) {
+    case 2:
+        img = Blitz::contrast(img, true, 6);
+        break;
+    case 3:
+        img = Blitz::edge(img);
+        break;
+    case 4:
+        img = Blitz::intensity(img, 0.5);
+        break;
+    case 5:
+        Blitz::invert(img);
+        break;
+    case 6:
+        img = Blitz::threshold(img, 120, Blitz::Grayscale, qRgba(0, 0, 0, 0), qRgba(255, 0, 0, 255));
+        //img = Blitz::flatten(img, QColor(255, 0, 0, 255), QColor(0, 0, 0, 0));
+        break;
+    default:
+        break;
+
+    }
+#endif
     m_bmCapture->showOverlay(img);
 }
 
@@ -219,31 +383,21 @@ void StopmotionWidget::sequenceNameChanged(const QString &name)
     frame_list->clear();
     if (name.isEmpty()) {
         button_addsequence->setEnabled(false);
-        frame_number->blockSignals(true);
-        frame_number->setValue(m_sequenceFrame);
-        frame_number->blockSignals(false);
-        frameoverlay_button->setEnabled(false);
-        removelast_button->setEnabled(false);
     } else {
         // Check if we are editing an existing sequence
         QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_filesList);
-        m_sequenceFrame = m_filesList.size();
-        frame_number->blockSignals(true);
-        frame_number->setValue(0);
-        frame_number->blockSignals(false);
-        m_currentIndex = 0;
+        m_sequenceFrame = m_filesList.isEmpty() ? 0 : SlideshowClip::getFrameNumberFromPath(m_filesList.last()) + 1;
         if (!m_filesList.isEmpty()) {
             m_sequenceName = sequence_name->currentText();
             connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
             m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
             button_addsequence->setEnabled(true);
-            frameoverlay_button->setEnabled(true);
         } else {
+            // new sequence
+            connect(this, SIGNAL(doCreateThumbs(QImage, int)), this, SLOT(slotCreateThumbs(QImage, int)));
             button_addsequence->setEnabled(false);
-            frameoverlay_button->setEnabled(false);
         }
-        frame_number->setRange(0, m_sequenceFrame);
-        capture_button->setEnabled(true);
+        capture_button->setEnabled(live_button->isChecked());
     }
 }
 
@@ -260,18 +414,22 @@ void StopmotionWidget::slotCaptureFrame()
         m_sequenceName = sequence_name->currentText();
         m_sequenceFrame = 0;
     }
-    capture_button->setEnabled(false);
-    m_bmCapture->captureFrame(getPathForFrame(m_sequenceFrame));
+    //capture_button->setEnabled(false);
+    QString currentPath = getPathForFrame(m_sequenceFrame);
+    m_bmCapture->captureFrame(currentPath);
     KNotification::event("FrameCaptured");
-    frameoverlay_button->setEnabled(true);
     m_sequenceFrame++;
-    frame_number->setRange(0, m_sequenceFrame);
-    frame_number->blockSignals(true);
-    frame_number->setValue(m_sequenceFrame);
-    frame_number->blockSignals(false);
     button_addsequence->setEnabled(true);
-    //if (frameoverlay_button->isChecked()) QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
-    QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
+    if (m_intervalCapture->isChecked()) QTimer::singleShot(KdenliveSettings::captureinterval() * 1000, this, SLOT(slotCaptureFrame()));
+}
+
+
+void StopmotionWidget::slotNewThumb(const QString path)
+{
+    if (!KdenliveSettings::showstopmotionthumbs()) return;
+    m_filesList.append(path);
+    if (m_showOverlay->isChecked()) slotUpdateOverlay();
+    if (!m_future.isRunning()) m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
 }
 
 void StopmotionWidget::slotPrepareThumbs()
@@ -295,7 +453,7 @@ void StopmotionWidget::slotCreateThumbs(QImage img, int ix)
     QString nb = QString::number(ix);
     QPainter p(&pix);
     QFontInfo finfo(font());
-    p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(0, 0, 0, 150));
+    p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(80, 80, 80, 150));
     p.setPen(Qt::white);
     p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
     p.end();
@@ -303,41 +461,10 @@ void StopmotionWidget::slotCreateThumbs(QImage img, int ix)
     QListWidgetItem *item = new QListWidgetItem(icon, QString(), frame_list);
     item->setToolTip(getPathForFrame(ix, sequence_name->currentText()));
     item->setData(Qt::UserRole, ix);
-    m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
-}
-
-void StopmotionWidget::slotUpdateFrameList(int ix)
-{
-    kDebug() << "// GET FRAME: " << ix;
-    if (ix == -1) ix = m_sequenceFrame - 1;
-    QString path = getPathForFrame(ix);
-    if (!QFile::exists(path)) {
-        capture_button->setEnabled(true);
-        return;
-    }
-    QImage img(path);
-    if (img.isNull()) {
-        if (ix == m_sequenceFrame - 1) QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
-        return;
-    }
-    int height = 90;
-    int width = height * img.width() / img.height();
-    frame_list->setIconSize(QSize(width, height));
-    QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
-    QString nb = QString::number(ix);
-    QPainter p(&pix);
-    QFontInfo finfo(font());
-    p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(0, 0, 0, 150));
-    p.setPen(Qt::white);
-    p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
-    p.end();
-    QIcon icon(pix);
-    QListWidgetItem *item = new QListWidgetItem(icon, QString(), frame_list);
-    item->setData(Qt::UserRole, ix);
     frame_list->blockSignals(true);
     frame_list->setCurrentItem(item);
-    frame_list->blockSignals(true);
-    capture_button->setEnabled(true);
+    frame_list->blockSignals(false);
+    m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
 }
 
 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
@@ -346,49 +473,25 @@ QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
     return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
 }
 
-void StopmotionWidget::slotShowFrame(int ix)
+void StopmotionWidget::slotShowFrame(const QString &path)
 {
-    if (m_sequenceFrame == 0) {
-        //there are no images in sequence
-        return;
-    }
-    frameoverlay_button->blockSignals(true);
-    frameoverlay_button->setChecked(false);
-    frameoverlay_button->blockSignals(false);
-    if (ix < m_sequenceFrame) {
-        // Show previous frame
-        slotLive(false);
-        live_button->setChecked(false);
-        QImage img(getPathForFrame(ix));
-        capture_button->setEnabled(false);
-        if (!img.isNull()) {
-            //m_bmCapture->showOverlay(img, false);
-            m_bmCapture->hidePreview(true);
-            m_frame_preview->setImage(img);
-            m_frame_preview->setHidden(false);
-            m_frame_preview->update();
-            selectFrame(ix);
-        }
+    //slotLive(false);
+    QImage img(path);
+    capture_button->setEnabled(false);
+    if (!img.isNull()) {
+        m_bmCapture->hidePreview(true);
+        m_frame_preview->setImage(img);
+        m_frame_preview->setHidden(false);
+        m_frame_preview->update();
     }
-    /*else {
-    slotLive(true);
-    m_frame_preview->setImage(QImage());
-    m_frame_preview->setHidden(true);
-    m_bmCapture->hideOverlay();
-    m_bmCapture->hidePreview(false);
-    capture_button->setEnabled(true);
-    }*/
 }
 
 void StopmotionWidget::slotShowSelectedFrame()
 {
     QListWidgetItem *item = frame_list->currentItem();
     if (item) {
-        int ix = item->data(Qt::UserRole).toInt();
-        //frame_number->blockSignals(true);
-        frame_number->setValue(ix);
-        //frame_number->blockSignals(false);
-        //slotShowFrame(ix);
+        //int ix = item->data(Qt::UserRole).toInt();;
+        slotShowFrame(item->toolTip());
     }
 }
 
@@ -397,26 +500,36 @@ void StopmotionWidget::slotAddSequence()
     emit addOrUpdateSequence(getPathForFrame(0));
 }
 
-void StopmotionWidget::slotPlayPreview()
+void StopmotionWidget::slotPlayPreview(bool animate)
 {
-    if (m_animatedIndex != -1) {
+    if (!animate) {
         // stop animation
-        m_animatedIndex = -1;
+        m_animationList.clear();
         return;
     }
-    QListWidgetItem *item = frame_list->currentItem();
-    if (item) {
-        m_animatedIndex = item->data(Qt::UserRole).toInt();
+    if (KdenliveSettings::showstopmotionthumbs()) {
+        frame_list->setCurrentRow(0);
+        QTimer::singleShot(200, this, SLOT(slotAnimate()));
+    } else {
+        SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &m_animationList);
+        slotAnimate();
     }
-    QTimer::singleShot(200, this, SLOT(slotAnimate()));
 }
 
 void StopmotionWidget::slotAnimate()
 {
-    slotShowFrame(m_animatedIndex);
-    m_animatedIndex++;
-    if (m_animatedIndex < m_sequenceFrame) QTimer::singleShot(200, this, SLOT(slotAnimate()));
-    else m_animatedIndex = -1;
+    //slotShowFrame(m_animatedIndex);
+    if (KdenliveSettings::showstopmotionthumbs()) {
+        //TODO: loop
+        if (frame_list->currentRow() < (frame_list->count() - 1)) {
+            frame_list->setCurrentRow(frame_list->currentRow() + 1);
+            QTimer::singleShot(100, this, SLOT(slotAnimate()));
+        } else preview_button->setChecked(false);
+    } else if (!m_animationList.isEmpty()) {
+        slotShowFrame(m_animationList.takeFirst());
+        QTimer::singleShot(100, this, SLOT(slotAnimate()));
+    } else preview_button->setChecked(false);
+
 }
 
 QListWidgetItem *StopmotionWidget::getFrameFromIndex(int ix)
@@ -469,4 +582,14 @@ void StopmotionWidget::slotSeekFrame(bool forward)
     } else if (ix > 0) frame_list->setCurrentRow(ix - 1);
 }
 
-
+void StopmotionWidget::slotRemoveFrame()
+{
+    if (frame_list->currentItem() == NULL) return;
+    QString path = frame_list->currentItem()->toolTip();
+    if (KMessageBox::questionYesNo(this, i18n("Delete frame %1 from disk?", path), i18n("Delete Frame")) != KMessageBox::Yes) return;
+    QFile f(path);
+    if (f.remove()) {
+        QListWidgetItem *item = frame_list->takeItem(frame_list->currentRow());
+        delete item;
+    }
+}