]> git.sesse.net Git - kdenlive/blobdiff - src/stopmotion/stopmotion.cpp
Stopmotion widget: Improved webcam support, taken from the fswebcam project
[kdenlive] / src / stopmotion / stopmotion.cpp
index 40372969d94991eec4348dbe7c7491428d5aae45..3a2dbd2b84abf401b97882bfb57f3d6518896713 100644 (file)
@@ -61,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)
 {
@@ -83,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)
@@ -91,24 +97,26 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     , m_sequenceFrame(0)
     , m_animatedIndex(-1)
 {
+    //setAttribute(Qt::WA_DeleteOnClose);
+    addActions(actions);
     setupUi(this);
     setWindowTitle(i18n("Stop Motion Capture"));
     setFont(KGlobalSettings::toolBarFont());
 
     live_button->setIcon(KIcon("camera-photo"));
-    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"));
     capture_button->setEnabled(false);
 
     // Build config menu
     QMenu *confMenu = new QMenu;
-    m_showOverlay = new QAction(KIcon("edit-paste"), i18n("Show last frame over video"), this);
-    m_showOverlay->setCheckable(true);
-    m_showOverlay->setChecked(false);
+    m_showOverlay = actions.at(2);
     connect(m_showOverlay, SIGNAL(triggered(bool)), this, SLOT(slotShowOverlay(bool)));
     confMenu->addAction(m_showOverlay);
 
@@ -152,8 +160,8 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     connect(showThumbs, SIGNAL(triggered(bool)), this, SLOT(slotShowThumbs(bool)));
 
     QAction *removeCurrent = new QAction(KIcon("edit-delete"), i18n("Delete current frame"), this);
-    //TODO: implement frame deletion
-    //connect(removeCurrent, SIGNAL(triggered()), this, SLOT(slotRemoveFrame()));
+    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()));
@@ -174,6 +182,8 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     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
@@ -181,14 +191,40 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
         connect(m_bmCapture, SIGNAL(gotMessage(const QString &)), this, SLOT(slotGotHDMIMessage(const QString &)));
     }
     if (QFile::exists(KdenliveSettings::video4vdevice())) {
-        if (m_bmCapture == NULL) m_bmCapture = new V4lCaptureHandler(m_layout);
-        capture_device->addItem(m_bmCapture->getDeviceName(KdenliveSettings::video4vdevice()), "v4l");
+#ifndef Q_WS_MAC
+        V4lCaptureHandler v4l(NULL);
+        // Video 4 Linux device detection
+        for (int i = 0; i < 10; i++) {
+            QString path = "/dev/video" + QString::number(i);
+            if (QFile::exists(path)) {
+                QStringList deviceInfo = v4l.getDeviceName(path);
+                if (!deviceInfo.isEmpty()) {
+                    capture_device->addItem(deviceInfo.at(0), "v4l");
+                    capture_device->setItemData(capture_device->count() - 1, path, Qt::UserRole + 1);
+                    capture_device->setItemData(capture_device->count() - 1, deviceInfo.at(1), Qt::UserRole + 2);
+                    if (path == KdenliveSettings::video4vdevice()) capture_device->setCurrentIndex(capture_device->count() - 1);
+                }
+            }
+        }
+
+        /*V4lCaptureHandler v4lhandler(NULL);
+        QStringList deviceInfo = v4lhandler.getDeviceName(KdenliveSettings::video4vdevice());
+            capture_device->addItem(deviceInfo.at(0), "v4l");
+        capture_device->setItemData(capture_device->count() - 1, deviceInfo.at(3), Qt::UserRole + 1);*/
+        if (m_bmCapture == NULL) {
+            m_bmCapture = new V4lCaptureHandler(m_layout);
+            m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString());
+        }
+#endif
     }
 
-    connect(m_bmCapture, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString)));
     connect(capture_device, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateHandler()));
+    if (m_bmCapture) {
+        connect(m_bmCapture, SIGNAL(frameSaved(const QString)), this, SLOT(slotNewThumb(const QString)));
+    } else live_button->setEnabled(false);
     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);
@@ -198,15 +234,19 @@ StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
     connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
     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();
 }
 
 StopmotionWidget::~StopmotionWidget()
 {
-    m_bmCapture->stopPreview();
+    if (m_bmCapture)
+        m_bmCapture->stopPreview();
 }
 
 void StopmotionWidget::slotUpdateOverlayEffect(QAction *act)
@@ -253,15 +293,21 @@ void StopmotionWidget::slotIntervalCapture(bool capture)
 void StopmotionWidget::slotUpdateHandler()
 {
     QString data = capture_device->itemData(capture_device->currentIndex()).toString();
-    m_bmCapture->stopPreview();
-    delete m_bmCapture;
+    slotLive(false);
+    if (m_bmCapture) {
+        delete m_bmCapture;
+    }
     m_layout->removeWidget(m_frame_preview);
     if (data == "v4l") {
+#ifndef Q_WS_MAC
         m_bmCapture = new V4lCaptureHandler(m_layout);
+        m_bmCapture->setDevice(capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 1).toString(), capture_device->itemData(capture_device->currentIndex(), Qt::UserRole + 2).toString());
+#endif
     } else {
         m_bmCapture = new BmdCaptureHandler(m_layout);
-        connect(m_bmCapture, SIGNAL(gotMessage(const QString &)), this, SLOT(slotGotHDMIMessage(const QString &)));
+        if (m_bmCapture) connect(m_bmCapture, SIGNAL(gotMessage(const QString &)), this, SLOT(slotGotHDMIMessage(const QString &)));
     }
+    live_button->setEnabled(m_bmCapture != NULL);
     m_layout->addWidget(m_frame_preview);
 }
 
@@ -285,15 +331,29 @@ void StopmotionWidget::parseExistingSequences()
     }
 }
 
+void StopmotionWidget::slotSwitchLive()
+{
+    setUpdatesEnabled(false);
+    if (m_frame_preview->isHidden()) {
+        if (m_bmCapture) m_bmCapture->hidePreview(true);
+        m_frame_preview->setHidden(false);
+    } else {
+        m_frame_preview->setHidden(true);
+        if (m_bmCapture) m_bmCapture->hidePreview(false);
+    }
+    setUpdatesEnabled(true);
+}
+
 void StopmotionWidget::slotLive(bool isOn)
 {
-    if (isOn) {
-        m_frame_preview->setImage(QImage());
+    if (isOn && m_bmCapture) {
+        //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();
+        if (m_bmCapture) m_bmCapture->stopPreview();
+        m_frame_preview->setHidden(false);
         capture_button->setEnabled(false);
         live_button->setChecked(false);
     }
@@ -305,13 +365,14 @@ void StopmotionWidget::slotShowOverlay(bool isOn)
         if (live_button->isChecked() && m_sequenceFrame > 0) {
             slotUpdateOverlay();
         }
-    } else {
+    } else if (m_bmCapture) {
         m_bmCapture->hideOverlay();
     }
 }
 
 void StopmotionWidget::slotUpdateOverlay()
 {
+    if (m_bmCapture == NULL) return;
     QString path = getPathForFrame(m_sequenceFrame - 1);
     if (!QFile::exists(path)) return;
     QImage img(path);
@@ -376,6 +437,7 @@ void StopmotionWidget::sequenceNameChanged(const QString &name)
 
 void StopmotionWidget::slotCaptureFrame()
 {
+    if (m_bmCapture == NULL) return;
     if (sequence_name->currentText().isEmpty()) {
         QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name"));
         if (seqName.isEmpty()) return;
@@ -389,7 +451,6 @@ void StopmotionWidget::slotCaptureFrame()
     }
     //capture_button->setEnabled(false);
     QString currentPath = getPathForFrame(m_sequenceFrame);
-    kDebug() << "Capture FRame NB: " << m_sequenceFrame;
     m_bmCapture->captureFrame(currentPath);
     KNotification::event("FrameCaptured");
     m_sequenceFrame++;
@@ -435,6 +496,9 @@ 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);
+    frame_list->blockSignals(true);
+    frame_list->setCurrentItem(item);
+    frame_list->blockSignals(false);
     m_future = QtConcurrent::run(this, &StopmotionWidget::slotPrepareThumbs);
 }
 
@@ -446,11 +510,11 @@ QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
 
 void StopmotionWidget::slotShowFrame(const QString &path)
 {
-    slotLive(false);
+    //slotLive(false);
     QImage img(path);
     capture_button->setEnabled(false);
     if (!img.isNull()) {
-        m_bmCapture->hidePreview(true);
+        if (m_bmCapture) m_bmCapture->hidePreview(true);
         m_frame_preview->setImage(img);
         m_frame_preview->setHidden(false);
         m_frame_preview->update();
@@ -494,11 +558,11 @@ void StopmotionWidget::slotAnimate()
         //TODO: loop
         if (frame_list->currentRow() < (frame_list->count() - 1)) {
             frame_list->setCurrentRow(frame_list->currentRow() + 1);
-            QTimer::singleShot(200, this, SLOT(slotAnimate()));
+            QTimer::singleShot(100, this, SLOT(slotAnimate()));
         } else preview_button->setChecked(false);
     } else if (!m_animationList.isEmpty()) {
         slotShowFrame(m_animationList.takeFirst());
-        QTimer::singleShot(200, this, SLOT(slotAnimate()));
+        QTimer::singleShot(100, this, SLOT(slotAnimate()));
     } else preview_button->setChecked(false);
 
 }
@@ -553,4 +617,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;
+    }
+}