]> git.sesse.net Git - kdenlive/blob - src/stopmotion/stopmotion.cpp
Improve stopmotion widget (show thumbs of previous frames)
[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 <QComboBox>
33 #include <QVBoxLayout>
34 #include <QTimer>
35 #include <QPainter>
36 #include <QAction>
37
38 StopmotionWidget::StopmotionWidget(KUrl projectFolder, QWidget *parent) :
39         m_projectFolder(projectFolder)
40         , QDialog(parent)
41         , Ui::Stopmotion_UI()
42         , m_sequenceFrame(0)
43         , m_animatedIndex(-1)
44 {
45     setupUi(this);
46     setWindowTitle(i18n("Stop Motion Capture"));
47     setFont(KGlobalSettings::toolBarFont());
48
49     live_button->setIcon(KIcon("camera-photo"));
50     frameoverlay_button->setIcon(KIcon("edit-paste"));
51     m_captureAction = new QAction(KIcon("media-record"), i18n("Capture frame"), this);
52     m_captureAction->setShortcut(QKeySequence(Qt::Key_Space));
53     connect(m_captureAction, SIGNAL(triggered()), this, SLOT(slotCaptureFrame()));
54     capture_button->setDefaultAction(m_captureAction);
55     
56     preview_button->setIcon(KIcon("media-playback-start"));
57     removelast_button->setIcon(KIcon("edit-delete"));
58     capture_button->setEnabled(false);
59     frameoverlay_button->setEnabled(false);
60     removelast_button->setEnabled(false);
61
62 #if KDE_IS_VERSION(4,4,0)
63     sequence_name->setClickMessage(i18n("Enter sequence name..."));
64 #endif
65
66     connect(sequence_name, SIGNAL(textChanged(const QString &)), this, SLOT(sequenceNameChanged(const QString &)));
67     BMInterface::getBlackMagicDeviceList(capture_device, NULL);
68     QVBoxLayout *lay = new QVBoxLayout;
69     m_bmCapture = new CaptureHandler(lay);
70     video_preview->setLayout(lay);
71     live_button->setChecked(false);
72     frameoverlay_button->setChecked(false);
73     button_addsequence->setEnabled(false);
74     connect(live_button, SIGNAL(clicked(bool)), this, SLOT(slotLive(bool)));
75     connect(frameoverlay_button, SIGNAL(clicked(bool)), this, SLOT(slotShowOverlay(bool)));
76     connect(frame_number, SIGNAL(valueChanged(int)), this, SLOT(slotShowFrame(int)));
77     connect(button_addsequence, SIGNAL(clicked(bool)), this, SLOT(slotAddSequence()));
78     connect(preview_button, SIGNAL(clicked(bool)), this, SLOT(slotPlayPreview()));
79     connect(frame_list, SIGNAL(activated(const QModelIndex &)), this, SLOT(slotShowSelectedFrame()));
80 }
81
82 StopmotionWidget::~StopmotionWidget()
83 {
84     m_bmCapture->stopPreview();
85 }
86
87 void StopmotionWidget::slotLive(bool isOn)
88 {
89     if (isOn) {
90         m_bmCapture->startPreview(KdenliveSettings::hdmicapturedevice(), KdenliveSettings::hdmicapturemode());
91         capture_button->setEnabled(sequence_name->text().isEmpty() == false);
92     }
93     else {
94         m_bmCapture->stopPreview();
95         capture_button->setEnabled(false);
96     }
97 }
98
99 void StopmotionWidget::slotShowOverlay(bool isOn)
100 {
101     if (isOn) {
102         if (live_button->isChecked() && m_sequenceFrame > 0) {
103             slotUpdateOverlay();
104         }
105     }
106     else {
107         m_bmCapture->hideOverlay();
108     }
109 }
110
111 void StopmotionWidget::slotUpdateOverlay()
112 {
113     QString path = getPathForFrame(m_sequenceFrame - 1);
114     if (!QFile::exists(path)) return;
115     QImage img(path);
116     if (img.isNull()) {
117         QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
118         return;
119     }
120     m_bmCapture->showOverlay(img);
121 }
122
123 void StopmotionWidget::sequenceNameChanged(const QString &name)
124 {
125     frame_list->clear();
126     if (name.isEmpty()) {
127         button_addsequence->setEnabled(false);
128         capture_button->setEnabled(false);
129         frame_number->blockSignals(true);
130         frame_number->setValue(m_sequenceFrame);
131         frame_number->blockSignals(false);
132         frameoverlay_button->setEnabled(false);
133         removelast_button->setEnabled(false);
134     }
135     else {
136         // Check if we are editing an existing sequence
137         int count = 0;
138         QString pattern = SlideshowClip::selectedPath(getPathForFrame(0, sequence_name->text()), false, QString(), &count);
139         m_sequenceFrame = count;
140         if (count > 0) {
141             m_sequenceName = sequence_name->text();
142             //TODO: Do the thumbnail stuff in a thread
143             for (int i = 0; i < count; i++) {
144                 slotUpdateFrameList(i);
145             }
146             button_addsequence->setEnabled(true);
147             frameoverlay_button->setEnabled(true);
148         }
149         else {
150             button_addsequence->setEnabled(false);
151             frameoverlay_button->setEnabled(false);
152         }
153         frame_number->setRange(0, m_sequenceFrame);
154         frame_number->blockSignals(true);
155         frame_number->setValue(m_sequenceFrame);
156         frame_number->blockSignals(false);
157         capture_button->setEnabled(true);
158     }
159 }
160
161 void StopmotionWidget::slotCaptureFrame()
162 {
163     if (m_sequenceName != sequence_name->text()) {
164         m_sequenceName = sequence_name->text();
165         m_sequenceFrame = 0;
166     }
167     capture_button->setEnabled(false);
168     m_bmCapture->captureFrame(getPathForFrame(m_sequenceFrame));
169     KNotification::event("FrameCaptured");
170     frameoverlay_button->setEnabled(true);
171     m_sequenceFrame++;
172     frame_number->setRange(0, m_sequenceFrame);
173     frame_number->blockSignals(true);
174     frame_number->setValue(m_sequenceFrame);
175     frame_number->blockSignals(false);
176     button_addsequence->setEnabled(true);
177     //if (frameoverlay_button->isChecked()) QTimer::singleShot(1000, this, SLOT(slotUpdateOverlay()));
178     QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
179 }
180
181 void StopmotionWidget::slotUpdateFrameList(int ix)
182 {
183     kDebug()<< "// GET FRAME: "<<ix;
184     if (ix == -1) ix = m_sequenceFrame - 1;
185     QString path = getPathForFrame(ix);
186     if (!QFile::exists(path)) {
187         capture_button->setEnabled(true);
188         return;
189     }
190     QImage img(path);
191     if (img.isNull()) {
192         if (ix == m_sequenceFrame - 1) QTimer::singleShot(1000, this, SLOT(slotUpdateFrameList()));
193         return;
194     }
195     int height = 90;
196     int width = height * img.width() / img.height();
197     frame_list->setIconSize(QSize(width, height));
198     QPixmap pix = QPixmap::fromImage(img).scaled(width, height);
199     QString nb = QString::number(ix);
200     QPainter p(&pix);
201     QFontInfo finfo(font());
202     p.fillRect(0, 0, finfo.pixelSize() * nb.count() + 6, finfo.pixelSize() + 6, QColor(0, 0, 0, 150));
203     p.setPen(Qt::white);
204     p.drawText(QPoint(3, finfo.pixelSize() + 3), nb);
205     p.end();
206     QIcon icon(pix);
207     QListWidgetItem *item = new QListWidgetItem(icon, QString(), frame_list);
208     item->setData(Qt::UserRole, path);
209     item->setData(Qt::UserRole + 1, ix);
210     item->setToolTip(path);
211     capture_button->setEnabled(true);
212 }
213
214 QString StopmotionWidget::getPathForFrame(int ix, QString seqName)
215 {
216     if (seqName.isEmpty()) seqName = m_sequenceName;
217     return m_projectFolder.path(KUrl::AddTrailingSlash) + seqName + "_" + QString::number(ix).rightJustified(4, '0', false) + ".png";
218 }
219
220 void StopmotionWidget::slotShowFrame(int ix)
221 {
222     if (m_sequenceFrame == 0) {
223         //there are no images in sequence
224         return;
225     }
226     frameoverlay_button->blockSignals(true);
227     frameoverlay_button->setChecked(false);
228     frameoverlay_button->blockSignals(false);
229     if (ix < m_sequenceFrame) {
230         // Show previous frame
231         QImage img(getPathForFrame(ix));
232         capture_button->setEnabled(false);
233         if (!img.isNull()) m_bmCapture->showOverlay(img, false);
234     }
235     else {
236         m_bmCapture->hideOverlay();
237         capture_button->setEnabled(true);
238     }    
239 }
240
241 void StopmotionWidget::slotShowSelectedFrame()
242 {
243     QListWidgetItem *item = frame_list->currentItem();
244     if (item) {
245         int ix = item->data(Qt::UserRole + 1).toInt();
246         //frame_number->blockSignals(true);
247         frame_number->setValue(ix);
248         //frame_number->blockSignals(false);
249         //slotShowFrame(ix);
250     }
251 }
252
253 void StopmotionWidget::slotAddSequence()
254 {
255     emit addOrUpdateSequence(getPathForFrame(0));
256 }
257
258 void StopmotionWidget::slotPlayPreview()
259 {
260     if (m_animatedIndex != -1) {
261         // stop animation
262         m_animatedIndex = -1;
263         return;
264     }
265     QListWidgetItem *item = frame_list->currentItem();
266     if (item) {
267         m_animatedIndex = item->data(Qt::UserRole + 1).toInt();
268     }
269     QTimer::singleShot(200, this, SLOT(slotAnimate()));
270 }
271
272 void StopmotionWidget::slotAnimate()
273 {
274     slotShowFrame(m_animatedIndex);
275     m_animatedIndex++;
276     if (m_animatedIndex < m_sequenceFrame) QTimer::singleShot(200, this, SLOT(slotAnimate()));
277     else m_animatedIndex = -1;
278 }