1 /***************************************************************************
2 stopmotion.cpp - description
5 copyright : (C) 2010 by Jean-Baptiste Mardelle
6 email : jb@kdenlive.org
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
18 #include "stopmotion.h"
19 #include "../blackmagic/devices.h"
20 #include "../slideshowclip.h"
21 #include "kdenlivesettings.h"
25 #include <KGlobalSettings>
26 #include <KFileDialog>
27 #include <KStandardDirs>
28 #include <KMessageBox>
29 #include <kdeversion.h>
30 #include <KNotification>
32 #include <QInputDialog>
34 #include <QVBoxLayout>
38 #include <QWheelEvent>
40 MyLabel::MyLabel(QWidget *parent)
44 void MyLabel::setImage(QImage img)
50 void MyLabel::wheelEvent(QWheelEvent * event)
52 if (event->delta() > 0) emit seek(true);
53 else emit seek(false);
57 void MyLabel::paintEvent( QPaintEvent * event)
59 QRect r(0, 0, width(), height());
61 p.fillRect(r, QColor(KdenliveSettings::window_background()));
62 double aspect_ratio = (double) m_img.width() / m_img.height();
63 int pictureHeight = height();
64 int pictureWidth = width();
65 int calculatedWidth = aspect_ratio * height();
66 if (calculatedWidth > width()) pictureHeight = width() / aspect_ratio;
68 int calculatedHeight = width() / aspect_ratio;
69 if (calculatedHeight > height()) pictureWidth = height() * aspect_ratio;
71 p.drawImage(QRect((width() - pictureWidth) / 2, (height() - pictureHeight) / 2, pictureWidth, pictureHeight), m_img, QRect(0, 0, m_img.width(), m_img.height()));
76 StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
79 , m_projectFolder(projectFolder)
84 setWindowTitle(i18n("Stop Motion Capture"));
85 setFont(KGlobalSettings::toolBarFont());
87 live_button->setIcon(KIcon("camera-photo"));
88 frameoverlay_button->setIcon(KIcon("edit-paste"));
89 m_captureAction = new QAction(KIcon("media-record"), i18n("Capture frame"), this);
90 m_captureAction->setShortcut(QKeySequence(Qt::Key_Space));
91 connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
92 capture_button->setDefaultAction(m_captureAction);
94 preview_button->setIcon(KIcon("media-playback-start"));
95 removelast_button->setIcon(KIcon("edit-delete"));
96 frameoverlay_button->setEnabled(false);
97 removelast_button->setEnabled(false);
98 capture_button->setEnabled(false);
100 connect(sequence_name, SIGNAL(textChanged(const QString &)), this, SLOT(sequenceNameChanged(const QString &)));
101 BMInterface::getBlackMagicDeviceList(capture_device, NULL);
102 QVBoxLayout *lay = new QVBoxLayout;
103 m_bmCapture = new CaptureHandler(lay);
104 m_frame_preview = new MyLabel(this);
105 connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool)));
106 lay->addWidget(m_frame_preview);
107 m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
108 video_preview->setLayout(lay);
109 live_button->setChecked(false);
110 frameoverlay_button->setChecked(false);
111 button_addsequence->setEnabled(false);
112 connect(live_button, SIGNAL(clicked(bool)), this, SLOT(slotLive(bool)));
113 connect(frameoverlay_button, SIGNAL(clicked(bool)), this, SLOT(slotShowOverlay(bool)));
114 connect(frame_number, SIGNAL(valueChanged(int)), this, SLOT(slotShowFrame(int)));
115 connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
116 connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview()));
117 connect(frame_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotShowSelectedFrame()));
120 parseExistingSequences();
123 StopmotionWidget::~StopmotionWidget()
125 m_bmCapture->stopPreview();
128 void StopmotionWidget::parseExistingSequences()
130 sequence_name->clear();
131 sequence_name->addItem(QString());
132 QDir dir(m_projectFolder.path());
134 filters << "*_0000.png";
135 //dir.setNameFilters(filters);
136 QStringList sequences = dir.entryList(filters, QDir::Files, QDir::Name);
137 //kDebug()<<"PF: "<<<<", sm: "<<sequences;
138 foreach(QString sequencename, sequences) {
139 sequence_name->addItem(sequencename.section("_", 0, -2));
143 void StopmotionWidget::slotLive(bool isOn)
146 m_frame_preview->setImage(QImage());
147 m_frame_preview->setHidden(true);
148 m_bmCapture->startPreview(KdenliveSettings::hdmicapturedevice(), KdenliveSettings::hdmicapturemode());
149 capture_button->setEnabled(true);
152 m_bmCapture->stopPreview();
153 capture_button->setEnabled(false);
157 void StopmotionWidget::slotShowOverlay(bool isOn)
160 if (live_button->isChecked() && m_sequenceFrame > 0) {
165 m_bmCapture->hideOverlay();
169 void StopmotionWidget::slotUpdateOverlay()
171 QString path = getPathForFrame(m_sequenceFrame - 1);
172 if (!QFile::exists(path)) return;
175 QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
178 m_bmCapture->showOverlay(img);
181 void StopmotionWidget::sequenceNameChanged(const QString &name)
184 if (name.isEmpty()) {
185 button_addsequence->setEnabled(false);
186 frame_number->blockSignals(true);
187 frame_number->setValue(m_sequenceFrame);
188 frame_number->blockSignals(false);
189 frameoverlay_button->setEnabled(false);
190 removelast_button->setEnabled(false);
193 // Check if we are editing an existing sequence
195 QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &count);
196 m_sequenceFrame = count;
198 m_sequenceName = sequence_name->currentText();
199 //TODO: Do the thumbnail stuff in a thread
200 for (int i = 0; i < count; i++) {
201 slotUpdateFrameList(i);
203 button_addsequence->setEnabled(true);
204 frameoverlay_button->setEnabled(true);
207 button_addsequence->setEnabled(false);
208 frameoverlay_button->setEnabled(false);
210 frame_number->setRange(0, m_sequenceFrame);
211 frame_number->blockSignals(true);
212 frame_number->setValue(m_sequenceFrame);
213 frame_number->blockSignals(false);
214 capture_button->setEnabled(true);
218 void StopmotionWidget::slotCaptureFrame()
220 if (sequence_name->currentText().isEmpty()) {
221 QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name"));
222 if (seqName.isEmpty()) return;
223 sequence_name->blockSignals(true);
224 sequence_name->setItemText(sequence_name->currentIndex(), seqName);
225 sequence_name->blockSignals(false);
227 if (m_sequenceName != sequence_name->currentText()) {
228 m_sequenceName = sequence_name->currentText();
231 capture_button->setEnabled(false);
232 m_bmCapture->captureFrame(getPathForFrame(m_sequenceFrame));
233 KNotification::event("FrameCaptured");
234 frameoverlay_button->setEnabled(true);
236 frame_number->setRange(0, m_sequenceFrame);
237 frame_number->blockSignals(true);
238 frame_number->setValue(m_sequenceFrame);
239 frame_number->blockSignals(false);
240 button_addsequence->setEnabled(true);
241 //if (frameoverlay_button->isChecked()) QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
242 QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
245 void StopmotionWidget::slotUpdateFrameList(int ix)
247 kDebug()<< "// GET FRAME: "<<ix;
248 if (ix == -1) ix = m_sequenceFrame - 1;
249 QString path = getPathForFrame(ix);
250 if (!QFile::exists(path)) {
251 capture_button->setEnabled(true);
256 if (ix == m_sequenceFrame - 1) QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
260 int width = height * img.width() / img.height();
261 frame_list->setIconSize(QSize(width, height));
262 QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
263 QString nb = QString::number(ix);
265 QFontInfo finfo(font());
266 p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(0, 0, 0, 150));
268 p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
271 QListWidgetItem *item = new QListWidgetItem(icon, QString(), frame_list);
272 item->setData(Qt::UserRole, path);
273 item->setData(Qt::UserRole + 1, ix);
274 item->setToolTip(path);
275 capture_button->setEnabled(true);
278 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
280 if (seqName.isEmpty()) seqName = m_sequenceName;
281 return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
284 void StopmotionWidget::slotShowFrame(int ix)
286 if (m_sequenceFrame == 0) {
287 //there are no images in sequence
290 frameoverlay_button->blockSignals(true);
291 frameoverlay_button->setChecked(false);
292 frameoverlay_button->blockSignals(false);
293 if (ix < m_sequenceFrame) {
294 // Show previous frame
296 live_button->setChecked(false);
297 QImage img(getPathForFrame(ix));
298 capture_button->setEnabled(false);
300 //m_bmCapture->showOverlay(img, false);
301 m_bmCapture->hidePreview(true);
302 m_frame_preview->setImage(img);
303 m_frame_preview->setHidden(false);
304 m_frame_preview->update();
310 m_frame_preview->setImage(QImage());
311 m_frame_preview->setHidden(true);
312 m_bmCapture->hideOverlay();
313 m_bmCapture->hidePreview(false);
314 capture_button->setEnabled(true);
318 void StopmotionWidget::slotShowSelectedFrame()
320 QListWidgetItem *item = frame_list->currentItem();
322 int ix = item->data(Qt::UserRole + 1).toInt();
323 //frame_number->blockSignals(true);
324 frame_number->setValue(ix);
325 //frame_number->blockSignals(false);
330 void StopmotionWidget::slotAddSequence()
332 emit addOrUpdateSequence(getPathForFrame(0));
335 void StopmotionWidget::slotPlayPreview()
337 if (m_animatedIndex != -1) {
339 m_animatedIndex = -1;
342 QListWidgetItem *item = frame_list->currentItem();
344 m_animatedIndex = item->data(Qt::UserRole + 1).toInt();
346 QTimer::singleShot(200, this, SLOT(slotAnimate()));
349 void StopmotionWidget::slotAnimate()
351 slotShowFrame(m_animatedIndex);
353 if (m_animatedIndex < m_sequenceFrame) QTimer::singleShot(200, this, SLOT(slotAnimate()));
354 else m_animatedIndex = -1;
357 void StopmotionWidget::selectFrame(int ix)
359 frame_list->blockSignals(true);
360 QListWidgetItem *item = frame_list->item(ix);
361 int current = item->data(Qt::UserRole + 1).toInt();
363 frame_list->setCurrentItem(item);
365 else if (current < ix) {
366 for (int i = ix; i < frame_list->count(); i++) {
367 item = frame_list->item(i);
368 current = item->data(Qt::UserRole + 1).toInt();
370 frame_list->setCurrentItem(item);
376 for (int i = ix; i >= 0; i--) {
377 item = frame_list->item(i);
378 current = item->data(Qt::UserRole + 1).toInt();
380 frame_list->setCurrentItem(item);
385 frame_list->blockSignals(false);
388 void StopmotionWidget::slotSeekFrame(bool forward)
390 int ix = frame_list->currentRow();
392 if (ix < frame_list->count() - 1) frame_list->setCurrentRow(ix + 1);
394 else if (ix > 0) frame_list->setCurrentRow(ix - 1);