]> git.sesse.net Git - kdenlive/blob - src/stopmotion/stopmotion.cpp
Fix some compilation warnings
[kdenlive] / src / stopmotion / stopmotion.cpp
1 /***************************************************************************
2                           stopmotion.cpp  -  description
3                              -------------------
4     begin                : Feb 28 2008
5     copyright            : (C) 2010 by Jean-Baptiste Mardelle
6     email                : jb@kdenlive.org
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
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.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include "stopmotion.h"
19 #include "../blackmagic/devices.h"
20 #include "../slideshowclip.h"
21 #include "kdenlivesettings.h"
22
23
24 #include <KDebug>
25 #include <KGlobalSettings>
26 #include <KFileDialog>
27 #include <KStandardDirs>
28 #include <KMessageBox>
29 #include <kdeversion.h>
30 #include <KNotification>
31
32 #include <QInputDialog>
33 #include <QComboBox>
34 #include <QVBoxLayout>
35 #include <QTimer>
36 #include <QPainter>
37 #include <QAction>
38 #include <QWheelEvent>
39
40 MyLabel::MyLabel(QWidget *parent) :
41         QLabel(parent)
42 {
43 }
44
45 void MyLabel::setImage(QImage img)
46 {
47     m_img = img;
48 }
49
50 //virtual
51 void MyLabel::wheelEvent(QWheelEvent * event)
52 {
53     if (event->delta() > 0) emit seek(true);
54     else emit seek(false);
55 }
56
57 //virtual
58 void MyLabel::paintEvent( QPaintEvent * event)
59 {
60     Q_UNUSED(event);
61
62     QRect r(0, 0, width(), height());
63     QPainter p(this);
64     p.fillRect(r, QColor(KdenliveSettings::window_background()));
65     double aspect_ratio = (double) m_img.width() / m_img.height();
66     int pictureHeight = height();
67     int pictureWidth = width();
68     int calculatedWidth = aspect_ratio * height();
69     if (calculatedWidth > width()) pictureHeight = width() / aspect_ratio;
70     else {
71         int calculatedHeight = width() / aspect_ratio;
72         if (calculatedHeight > height()) pictureWidth = height() * aspect_ratio;
73     }
74     p.drawImage(QRect((width() - pictureWidth) / 2, (height() - pictureHeight) / 2, pictureWidth, pictureHeight), m_img, QRect(0, 0, m_img.width(), m_img.height()));
75     p.end();
76 }
77
78
79 StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
80         QDialog(parent)
81         , Ui::Stopmotion_UI()
82         , m_projectFolder(projectFolder)
83         , m_sequenceFrame(0)
84         , m_animatedIndex(-1)
85 {
86     setupUi(this);
87     setWindowTitle(i18n("Stop Motion Capture"));
88     setFont(KGlobalSettings::toolBarFont());
89
90     live_button->setIcon(KIcon("camera-photo"));
91     frameoverlay_button->setIcon(KIcon("edit-paste"));
92     m_captureAction = new QAction(KIcon("media-record"), i18n("Capture frame"), this);
93     m_captureAction->setShortcut(QKeySequence(Qt::Key_Space));
94     connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
95     capture_button->setDefaultAction(m_captureAction);
96     
97     preview_button->setIcon(KIcon("media-playback-start"));
98     removelast_button->setIcon(KIcon("edit-delete"));
99     frameoverlay_button->setEnabled(false);
100     removelast_button->setEnabled(false);
101     capture_button->setEnabled(false);
102
103     connect(sequence_name, SIGNAL(textChanged(const QString &)), this, SLOT(sequenceNameChanged(const QString &)));
104     BMInterface::getBlackMagicDeviceList(capture_device, NULL);
105     QVBoxLayout *lay = new QVBoxLayout;
106     m_bmCapture = new CaptureHandler(lay);
107     m_frame_preview = new MyLabel(this);
108     connect(m_frame_preview, SIGNAL(seek(bool)), this, SLOT(slotSeekFrame(bool)));
109     lay->addWidget(m_frame_preview);
110     m_frame_preview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
111     video_preview->setLayout(lay);
112     live_button->setChecked(false);
113     frameoverlay_button->setChecked(false);
114     button_addsequence->setEnabled(false);
115     connect(live_button, SIGNAL(clicked(bool)), this, SLOT(slotLive(bool)));
116     connect(frameoverlay_button, SIGNAL(clicked(bool)), this, SLOT(slotShowOverlay(bool)));
117     connect(frame_number, SIGNAL(valueChanged(int)), this, SLOT(slotShowFrame(int)));
118     connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
119     connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview()));
120     connect(frame_list, SIGNAL(currentRowChanged(int)), this, SLOT(slotShowSelectedFrame()));
121
122     
123     parseExistingSequences();
124 }
125
126 StopmotionWidget::~StopmotionWidget()
127 {
128     m_bmCapture->stopPreview();
129 }
130
131 void StopmotionWidget::parseExistingSequences()
132 {
133     sequence_name->clear();
134     sequence_name->addItem(QString());
135     QDir dir(m_projectFolder.path());
136     QStringList filters;
137     filters << "*_0000.png";
138     //dir.setNameFilters(filters);
139     QStringList sequences = dir.entryList(filters, QDir::Files, QDir::Name);
140     //kDebug()<<"PF: "<<<<", sm: "<<sequences;
141     foreach(QString sequencename, sequences) {
142         sequence_name->addItem(sequencename.section("_", 0, -2));
143     }
144 }
145
146 void StopmotionWidget::slotLive(bool isOn)
147 {
148     if (isOn) {
149         m_frame_preview->setImage(QImage());
150         m_frame_preview->setHidden(true);
151         m_bmCapture->startPreview(KdenliveSettings::hdmicapturedevice(), KdenliveSettings::hdmicapturemode());
152         capture_button->setEnabled(true);
153     }
154     else {
155         m_bmCapture->stopPreview();
156         capture_button->setEnabled(false);
157     }
158 }
159
160 void StopmotionWidget::slotShowOverlay(bool isOn)
161 {
162     if (isOn) {
163         if (live_button->isChecked() && m_sequenceFrame > 0) {
164             slotUpdateOverlay();
165         }
166     }
167     else {
168         m_bmCapture->hideOverlay();
169     }
170 }
171
172 void StopmotionWidget::slotUpdateOverlay()
173 {
174     QString path = getPathForFrame(m_sequenceFrame - 1);
175     if (!QFile::exists(path)) return;
176     QImage img(path);
177     if (img.isNull()) {
178         QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
179         return;
180     }
181     m_bmCapture->showOverlay(img);
182 }
183
184 void StopmotionWidget::sequenceNameChanged(const QString &name)
185 {
186     frame_list->clear();
187     if (name.isEmpty()) {
188         button_addsequence->setEnabled(false);
189         frame_number->blockSignals(true);
190         frame_number->setValue(m_sequenceFrame);
191         frame_number->blockSignals(false);
192         frameoverlay_button->setEnabled(false);
193         removelast_button->setEnabled(false);
194     }
195     else {
196         // Check if we are editing an existing sequence
197         int count = 0;
198         QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->currentText()), false, QString(), &count);
199         m_sequenceFrame = count;
200         if (count > 0) {
201             m_sequenceName = sequence_name->currentText();
202             //TODO: Do the thumbnail stuff in a thread
203             for (int i = 0; i < count; i++) {
204                 slotUpdateFrameList(i);
205             }
206             button_addsequence->setEnabled(true);
207             frameoverlay_button->setEnabled(true);
208         }
209         else {
210             button_addsequence->setEnabled(false);
211             frameoverlay_button->setEnabled(false);
212         }
213         frame_number->setRange(0, m_sequenceFrame);
214         frame_number->blockSignals(true);
215         frame_number->setValue(m_sequenceFrame);
216         frame_number->blockSignals(false);
217         capture_button->setEnabled(true);
218     }
219 }
220
221 void StopmotionWidget::slotCaptureFrame()
222 {
223     if (sequence_name->currentText().isEmpty()) {
224         QString seqName = QInputDialog::getText(this, i18n("Create New Sequence"), i18n("Enter sequence name"));
225         if (seqName.isEmpty()) return;
226         sequence_name->blockSignals(true);
227         sequence_name->setItemText(sequence_name->currentIndex(), seqName);
228         sequence_name->blockSignals(false);
229     }
230     if (m_sequenceName != sequence_name->currentText()) {
231         m_sequenceName = sequence_name->currentText();
232         m_sequenceFrame = 0;
233     }
234     capture_button->setEnabled(false);
235     m_bmCapture->captureFrame(getPathForFrame(m_sequenceFrame));
236     KNotification::event("FrameCaptured");
237     frameoverlay_button->setEnabled(true);
238     m_sequenceFrame++;
239     frame_number->setRange(0, m_sequenceFrame);
240     frame_number->blockSignals(true);
241     frame_number->setValue(m_sequenceFrame);
242     frame_number->blockSignals(false);
243     button_addsequence->setEnabled(true);
244     //if (frameoverlay_button->isChecked()) QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
245     QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
246 }
247
248 void StopmotionWidget::slotUpdateFrameList(int ix)
249 {
250     kDebug()<< "// GET FRAME: "<<ix;
251     if (ix == -1) ix = m_sequenceFrame - 1;
252     QString path = getPathForFrame(ix);
253     if (!QFile::exists(path)) {
254         capture_button->setEnabled(true);
255         return;
256     }
257     QImage img(path);
258     if (img.isNull()) {
259         if (ix == m_sequenceFrame - 1) QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
260         return;
261     }
262     int height = 90;
263     int width = height * img.width() / img.height();
264     frame_list->setIconSize(QSize(width, height));
265     QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
266     QString nb = QString::number(ix);
267     QPainter p(&pix);
268     QFontInfo finfo(font());
269     p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(0, 0, 0, 150));
270     p.setPen(Qt::white);
271     p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
272     p.end();
273     QIcon icon(pix);
274     QListWidgetItem *item = new QListWidgetItem(icon, QString(), frame_list);
275     item->setData(Qt::UserRole, path);
276     item->setData(Qt::UserRole + 1, ix);
277     item->setToolTip(path);
278     capture_button->setEnabled(true);
279 }
280
281 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
282 {
283     if (seqName.isEmpty()) seqName = m_sequenceName;
284     return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
285 }
286
287 void StopmotionWidget::slotShowFrame(int ix)
288 {
289     if (m_sequenceFrame == 0) {
290         //there are no images in sequence
291         return;
292     }
293     frameoverlay_button->blockSignals(true);
294     frameoverlay_button->setChecked(false);
295     frameoverlay_button->blockSignals(false);
296     if (ix < m_sequenceFrame) {
297         // Show previous frame
298         slotLive(false);
299         live_button->setChecked(false);
300         QImage img(getPathForFrame(ix));
301         capture_button->setEnabled(false);
302         if (!img.isNull()) {
303             //m_bmCapture->showOverlay(img, false);
304             m_bmCapture->hidePreview(true);
305             m_frame_preview->setImage(img);
306             m_frame_preview->setHidden(false);
307             m_frame_preview->update();
308             selectFrame(ix);
309         }
310     }
311     else {
312         slotLive(true);
313         m_frame_preview->setImage(QImage());
314         m_frame_preview->setHidden(true);
315         m_bmCapture->hideOverlay();
316         m_bmCapture->hidePreview(false);
317         capture_button->setEnabled(true);
318     }    
319 }
320
321 void StopmotionWidget::slotShowSelectedFrame()
322 {
323     QListWidgetItem *item = frame_list->currentItem();
324     if (item) {
325         int ix = item->data(Qt::UserRole + 1).toInt();
326         //frame_number->blockSignals(true);
327         frame_number->setValue(ix);
328         //frame_number->blockSignals(false);
329         //slotShowFrame(ix);
330     }
331 }
332
333 void StopmotionWidget::slotAddSequence()
334 {
335     emit addOrUpdateSequence(getPathForFrame(0));
336 }
337
338 void StopmotionWidget::slotPlayPreview()
339 {
340     if (m_animatedIndex != -1) {
341         // stop animation
342         m_animatedIndex = -1;
343         return;
344     }
345     QListWidgetItem *item = frame_list->currentItem();
346     if (item) {
347         m_animatedIndex = item->data(Qt::UserRole + 1).toInt();
348     }
349     QTimer::singleShot(200, this, SLOT(slotAnimate()));
350 }
351
352 void StopmotionWidget::slotAnimate()
353 {
354     slotShowFrame(m_animatedIndex);
355     m_animatedIndex++;
356     if (m_animatedIndex < m_sequenceFrame) QTimer::singleShot(200, this, SLOT(slotAnimate()));
357     else m_animatedIndex = -1;
358 }
359
360 void StopmotionWidget::selectFrame(int ix)
361 {
362     frame_list->blockSignals(true);
363     QListWidgetItem *item = frame_list->item(ix);
364     int current = item->data(Qt::UserRole + 1).toInt();
365     if (current == ix) {
366         frame_list->setCurrentItem(item);
367     }
368     else if (current < ix) {
369         for (int i = ix; i < frame_list->count(); i++) {
370             item = frame_list->item(i);
371             current = item->data(Qt::UserRole + 1).toInt();
372             if (current == ix) {
373                 frame_list->setCurrentItem(item);
374                 break;
375             }
376         }
377     }
378     else {
379         for (int i = ix; i >= 0; i--) {
380             item = frame_list->item(i);
381             current = item->data(Qt::UserRole + 1).toInt();
382             if (current == ix) {
383                 frame_list->setCurrentItem(item);
384                 break;
385             }
386         }
387     }
388     frame_list->blockSignals(false);
389 }
390
391 void StopmotionWidget::slotSeekFrame(bool forward)
392 {
393     int ix = frame_list->currentRow();
394     if (forward) {
395         if (ix < frame_list->count() - 1) frame_list->setCurrentRow(ix + 1);
396     }
397     else if (ix > 0) frame_list->setCurrentRow(ix - 1);
398 }
399
400