]> git.sesse.net Git - kdenlive/blob - src/slideshowclip.cpp
Fix typo in time code format display.
[kdenlive] / src / slideshowclip.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20 #include "slideshowclip.h"
21 #include "kdenlivesettings.h"
22
23 #include <KStandardDirs>
24 #include <KDebug>
25 #include <KFileItem>
26
27 #include <QDir>
28
29
30 SlideshowClip::SlideshowClip(Timecode tc, QWidget * parent) :
31     QDialog(parent),
32     m_count(0),
33     m_timecode(tc),
34     m_thumbJob(NULL)
35 {
36     setFont(KGlobalSettings::toolBarFont());
37     setWindowTitle(i18n("Add Slideshow Clip"));
38     m_view.setupUi(this);
39     m_view.clip_name->setText(i18n("Slideshow Clip"));
40     m_view.folder_url->setMode(KFile::Directory);
41     m_view.icon_list->setIconSize(QSize(50, 50));
42     m_view.show_thumbs->setChecked(KdenliveSettings::showslideshowthumbs());
43
44     connect(m_view.folder_url, SIGNAL(textChanged(const QString &)), this, SLOT(parseFolder()));
45     connect(m_view.image_type, SIGNAL(currentIndexChanged(int)), this, SLOT(parseFolder()));
46     connect(m_view.pattern_url, SIGNAL(textChanged(const QString &)), this, SLOT(parseFolder()));
47
48     connect(m_view.show_thumbs, SIGNAL(stateChanged(int)), this, SLOT(slotEnableThumbs(int)));
49     connect(m_view.slide_fade, SIGNAL(stateChanged(int)), this, SLOT(slotEnableLuma(int)));
50     connect(m_view.luma_fade, SIGNAL(stateChanged(int)), this, SLOT(slotEnableLumaFile(int)));
51
52     //WARNING: keep in sync with clipproperties.cpp
53     m_view.image_type->addItem("JPG (*.jpg)", "jpg");
54     m_view.image_type->addItem("JPEG (*.jpeg)", "jpeg");
55     m_view.image_type->addItem("PNG (*.png)", "png");
56     m_view.image_type->addItem("BMP (*.bmp)", "bmp");
57     m_view.image_type->addItem("GIF (*.gif)", "gif");
58     m_view.image_type->addItem("TGA (*.tga)", "tga");
59     m_view.image_type->addItem("TIF (*.tif)", "tif");
60     m_view.image_type->addItem("TIFF (*.tiff)", "tiff");
61     m_view.image_type->addItem("Open EXR (*.exr)", "exr");
62     m_view.animation->addItem(i18n("None"), QString());
63     m_view.animation->addItem(i18n("Pan"), "Pan");
64     m_view.animation->addItem(i18n("Pan, low-pass"), "Pan, low-pass");
65     m_view.animation->addItem(i18n("Pan and zoom"), "Pan and zoom");
66     m_view.animation->addItem(i18n("Pan and zoom, low-pass"), "Pan and zoom, low-pass");
67     m_view.animation->addItem(i18n("Zoom"), "Zoom");
68     m_view.animation->addItem(i18n("Zoom, low-pass"), "Zoom, low-pass");
69
70     m_view.clip_duration->setInputMask("");
71     m_view.clip_duration->setValidator(m_timecode.validator());
72     m_view.luma_duration->setInputMask("");
73     m_view.luma_duration->setValidator(m_timecode.validator());
74     m_view.luma_duration->setText(m_timecode.getTimecodeFromFrames(int(ceil(m_timecode.fps()))));
75     m_view.folder_url->setUrl(QDir::homePath());
76
77     m_view.clip_duration_format->addItem(i18n("hh:mm:ss:ff"));
78     m_view.clip_duration_format->addItem(i18n("Frames"));
79     connect(m_view.clip_duration_format, SIGNAL(activated(int)), this, SLOT(slotUpdateDurationFormat(int)));
80     m_view.clip_duration_frames->setHidden(true);
81     m_view.luma_duration_frames->setHidden(true);
82     m_view.method_mime->setChecked(KdenliveSettings::slideshowbymime());
83     connect(m_view.method_mime, SIGNAL(toggled(bool)), this, SLOT(slotMethodChanged(bool)));
84     slotMethodChanged(m_view.method_mime->isChecked());
85
86
87     // Check for Kdenlive installed luma files
88     QStringList filters;
89     filters << "*.pgm" << "*.png";
90
91     QStringList customLumas = KGlobal::dirs()->findDirs("appdata", "lumas");
92     foreach(const QString & folder, customLumas) {
93         QStringList filesnames = QDir(folder).entryList(filters, QDir::Files);
94         foreach(const QString & fname, filesnames) {
95             QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
96             m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
97         }
98     }
99
100     // Check for MLT lumas
101     QString profilePath = KdenliveSettings::mltpath();
102     QString folder = profilePath.section('/', 0, -3);
103     folder.append("/lumas/PAL"); // TODO: cleanup the PAL / NTSC mess in luma files
104     QDir lumafolder(folder);
105     QStringList filesnames = lumafolder.entryList(filters, QDir::Files);
106     foreach(const QString & fname, filesnames) {
107         QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
108         m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
109     }
110
111     //adjustSize();
112 }
113
114 SlideshowClip::~SlideshowClip()
115 {
116     if (m_thumbJob) {
117         delete m_thumbJob;
118     }
119 }
120
121 void SlideshowClip::slotEnableLuma(int state)
122 {
123     bool enable = false;
124     if (state == Qt::Checked) enable = true;
125     m_view.luma_duration->setEnabled(enable);
126     m_view.luma_duration_frames->setEnabled(enable);
127     m_view.luma_fade->setEnabled(enable);
128     if (enable) {
129         m_view.luma_file->setEnabled(m_view.luma_fade->isChecked());
130     } else m_view.luma_file->setEnabled(false);
131     m_view.label_softness->setEnabled(m_view.luma_fade->isChecked() && enable);
132     m_view.luma_softness->setEnabled(m_view.label_softness->isEnabled());
133 }
134
135 void SlideshowClip::slotEnableThumbs(int state)
136 {
137     if (state == Qt::Checked) {
138         KdenliveSettings::setShowslideshowthumbs(true);
139         slotGenerateThumbs();
140     } else {
141         KdenliveSettings::setShowslideshowthumbs(false);
142         if (m_thumbJob) {
143             disconnect(m_thumbJob, SIGNAL(gotPreview(const KFileItem &, const QPixmap &)), this, SLOT(slotSetPixmap(const KFileItem &, const QPixmap &)));
144             m_thumbJob->kill();
145             m_thumbJob = NULL;
146         }
147     }
148
149 }
150
151 void SlideshowClip::slotEnableLumaFile(int state)
152 {
153     bool enable = false;
154     if (state == Qt::Checked) enable = true;
155     m_view.luma_file->setEnabled(enable);
156     m_view.luma_softness->setEnabled(enable);
157     m_view.label_softness->setEnabled(enable);
158 }
159
160 // static
161 int SlideshowClip::sequenceCount(KUrl file)
162 {
163     // find pattern
164     int count = 0;
165     QString filter = file.fileName();
166     QString ext = filter.section('.', -1);
167     filter = filter.section('.', 0, -2);
168     int fullSize = filter.size();
169     bool hasDigit = false;
170     while (filter.at(filter.size() - 1).isDigit()) {
171         hasDigit = true;
172         filter.remove(filter.size() - 1, 1);
173     }
174     if (!hasDigit) return 0;
175
176
177     // Find number of digits in sequence
178     int precision = fullSize - filter.size();
179     QString folder = file.directory(KUrl::AppendTrailingSlash);
180     // Check how many files we have
181     QDir dir(folder);
182     QString path;
183     int gap = 0;
184     for (int i = 0; gap < 100; i++) {
185         path = filter + QString::number(i).rightJustified(precision, '0', false) + ext;
186         if (dir.exists(path)) {
187             count ++;
188             gap = 0;
189         } else {
190             gap++;
191         }
192     }
193     return count;
194 }
195
196 void SlideshowClip::parseFolder()
197 {
198     m_view.icon_list->clear();
199     bool isMime = m_view.method_mime->isChecked();
200     QString path = isMime ? m_view.folder_url->url().path() : m_view.pattern_url->url().directory();
201     QDir dir(path);
202     if (path.isEmpty() || !dir.exists()) return;
203
204     KIcon unknownicon("unknown");
205     QStringList result;
206     QStringList filters;
207     QString filter;
208     if (isMime) {
209         // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
210         filter = m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
211         filters << "*." + filter;
212         dir.setNameFilters(filters);
213         result = dir.entryList(QDir::Files);
214     } else {
215         // find pattern
216         filter = m_view.pattern_url->url().fileName();
217         QString ext = filter.section('.', -1);
218         filter = filter.section('.', 0, -2);
219         int fullSize = filter.size();
220         while (filter.at(filter.size() - 1).isDigit()) {
221             filter.remove(filter.size() - 1, 1);
222         }
223         int precision = fullSize - filter.size();
224         QString path;
225         int gap = 0;
226         for (int i = 0; gap < 100; i++) {
227             path = filter + QString::number(i).rightJustified(precision, '0', false) + ext;
228             if (dir.exists(path)) {
229                 result.append(path);
230                 gap = 0;
231             } else {
232                 gap++;
233             }
234         }
235     }
236     QListWidgetItem *item;
237     foreach(const QString & path, result) {
238         item = new QListWidgetItem(unknownicon, KUrl(path).fileName());
239         item->setData(Qt::UserRole, dir.filePath(path));
240         m_view.icon_list->addItem(item);
241     }
242     m_count = result.count();
243     if (m_count == 0) m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
244     else m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
245     m_view.label_info->setText(i18np("1 image found", "%1 images found", m_count));
246     if (m_view.show_thumbs->isChecked()) slotGenerateThumbs();
247     m_view.icon_list->setCurrentRow(0);
248 }
249
250 void SlideshowClip::slotGenerateThumbs()
251 {
252     if (m_thumbJob) {
253         delete m_thumbJob;
254     };
255     KFileItemList fileList;
256     for (int i = 0; i < m_view.icon_list->count(); i++) {
257         QListWidgetItem* item = m_view.icon_list->item(i);
258         if (item) {
259             QString path = item->data(Qt::UserRole).toString();
260             if (!path.isEmpty()) {
261                 fileList.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, KUrl(path)));
262             }
263         }
264     }
265     m_thumbJob = new KIO::PreviewJob(fileList, 50, 0, 0, 0, true, true, 0);
266     m_thumbJob->setAutoDelete(false);
267     connect(m_thumbJob, SIGNAL(gotPreview(const KFileItem &, const QPixmap &)), this, SLOT(slotSetPixmap(const KFileItem &, const QPixmap &)));
268     m_thumbJob->start();
269 }
270
271 void SlideshowClip::slotSetPixmap(const KFileItem &fileItem, const QPixmap &pix)
272 {
273     for (int i = 0; i < m_view.icon_list->count(); i++) {
274         QListWidgetItem* item = m_view.icon_list->item(i);
275         if (item) {
276             QString path = item->data(Qt::UserRole).toString();
277             if (path == fileItem.url().path()) {
278                 item->setIcon(KIcon(pix));
279                 item->setData(Qt::UserRole, QString());
280                 break;
281             }
282         }
283     }
284 }
285
286
287 QString SlideshowClip::selectedPath()
288 {
289     QStringList list;
290     QString path = selectedPath(m_view.folder_url->url(), m_view.method_mime->isChecked(), ".all." + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString(), &list);
291     m_count = list.count();
292     return path;
293
294
295 }
296
297 // static
298 int SlideshowClip::getFrameNumberFromPath(KUrl path)
299 {
300     QString filter = path.fileName();
301     filter = filter.section('.', 0, -2);
302     int ix = filter.size() - 1;
303     while (filter.at(ix).isDigit()) {
304         ix--;
305     }
306     return filter.remove(0, ix + 1).toInt();
307 }
308
309 // static
310 QString SlideshowClip::selectedPath(KUrl url, bool isMime, QString extension, QStringList *list)
311 {
312     QString folder;
313     if (isMime) {
314         folder = url.path(KUrl::AddTrailingSlash);
315     } else {
316         folder = url.directory(KUrl::AppendTrailingSlash);
317         QString filter = url.fileName();
318         QString ext = '.' + filter.section('.', -1);
319         filter = filter.section('.', 0, -2);
320         int fullSize = filter.size();
321
322         while (filter.at(filter.size() - 1).isDigit()) {
323             filter.chop(1);
324         }
325
326         // Find number of digits in sequence
327         int precision = fullSize - filter.size();
328
329         // Check how many files we have
330         QDir dir(folder);
331         QString path;
332         int gap = 0;
333         for (int i = 0; gap < 100; i++) {
334             path = filter + QString::number(i).rightJustified(precision, '0', false) + ext;
335             if (dir.exists(path)) {
336                 (*list).append(folder + path);
337                 gap = 0;
338             } else {
339                 gap++;
340             }
341         }
342         extension = filter + "%." + QString::number(precision) + "d" + ext;
343     }
344     kDebug() << "// FOUND " << (*list).count() << " items for " << url.path();
345     return  folder + extension;
346 }
347
348
349 QString SlideshowClip::clipName() const
350 {
351     return m_view.clip_name->text();
352 }
353
354 QString SlideshowClip::clipDuration() const
355 {
356     if (m_view.clip_duration_format->currentIndex() == 1) {
357         // we are in frames mode
358         return m_timecode.getTimecodeFromFrames(m_view.clip_duration_frames->value());
359     }
360     return m_view.clip_duration->text();
361 }
362
363 int SlideshowClip::imageCount() const
364 {
365     return m_count;
366 }
367
368 int SlideshowClip::softness() const
369 {
370     return m_view.luma_softness->value();
371 }
372
373 bool SlideshowClip::loop() const
374 {
375     return m_view.slide_loop->isChecked();
376 }
377
378 bool SlideshowClip::crop() const
379 {
380     return m_view.slide_crop->isChecked();
381 }
382
383 bool SlideshowClip::fade() const
384 {
385     return m_view.slide_fade->isChecked();
386 }
387
388 QString SlideshowClip::lumaDuration() const
389 {
390     if (m_view.clip_duration_format->currentIndex() == 1) {
391         // we are in frames mode
392         return m_timecode.getTimecodeFromFrames(m_view.luma_duration_frames->value());
393     }
394     return m_view.luma_duration->text();
395 }
396
397 QString SlideshowClip::lumaFile() const
398 {
399     if (!m_view.luma_fade->isChecked() || !m_view.luma_file->isEnabled()) return QString();
400     return m_view.luma_file->itemData(m_view.luma_file->currentIndex()).toString();
401 }
402
403 QString SlideshowClip::animation() const
404 {
405     if (m_view.animation->itemData(m_view.animation->currentIndex()).isNull()) return QString();
406     return m_view.animation->itemData(m_view.animation->currentIndex()).toString();
407 }
408
409 void SlideshowClip::slotUpdateDurationFormat(int ix)
410 {
411     bool framesFormat = ix == 1;
412     if (framesFormat) {
413         // switching to frames count, update widget
414         m_view.clip_duration_frames->setValue(m_timecode.getFrameCount(m_view.clip_duration->text()));
415         m_view.luma_duration_frames->setValue(m_timecode.getFrameCount(m_view.luma_duration->text()));
416     } else {
417         // switching to timecode format
418         m_view.clip_duration->setInputMask("");
419         m_view.clip_duration->setValidator(m_timecode.validator());
420         m_view.clip_duration->setText(m_timecode.getTimecodeFromFrames(m_view.clip_duration_frames->value()));
421         m_view.luma_duration->setInputMask("");
422         m_view.luma_duration->setValidator(m_timecode.validator());
423         m_view.luma_duration->setText(m_timecode.getTimecodeFromFrames(m_view.luma_duration_frames->value()));
424     }
425     m_view.clip_duration_frames->setHidden(!framesFormat);
426     m_view.clip_duration->setHidden(framesFormat);
427     m_view.luma_duration_frames->setHidden(!framesFormat);
428     m_view.luma_duration->setHidden(framesFormat);
429 }
430
431 void SlideshowClip::slotMethodChanged(bool active)
432 {
433     if (active) {
434         // User wants mimetype image sequence
435         if (m_view.clip_duration->text().isEmpty()) {
436             m_view.clip_duration->setText(m_timecode.reformatSeparators(KdenliveSettings::image_duration()));
437         }
438         m_view.stackedWidget->setCurrentIndex(0);
439         KdenliveSettings::setSlideshowbymime(true);
440     } else {
441         // User wants pattern image sequence
442         if (m_view.clip_duration->text().isEmpty()) {
443             m_view.clip_duration->setText(m_timecode.reformatSeparators(KdenliveSettings::sequence_duration()));
444         }
445         m_view.stackedWidget->setCurrentIndex(1);
446         KdenliveSettings::setSlideshowbymime(false);
447     }
448     parseFolder();
449 }
450
451 // static
452 QString SlideshowClip::animationToGeometry(const QString &animation, int &ttl)
453 {
454     QString geometry;
455     if (animation.startsWith("Pan and zoom")) {
456         geometry = QString().sprintf("0=0,0:100%%x100%%;%d=-14%%,-14%%:120%%x120%%;%d=-5%%,-5%%:110%%x110%%;%d=0,0:110%%x110%%;%d=0,-5%%:110%%x110%%;%d=-5%%,0:110%%x110%%",
457                                      ttl - 1, ttl, ttl * 2 - 1, ttl * 2, ttl * 3 - 1);
458         ttl *= 3;
459     } else if (animation.startsWith("Pan")) {
460         geometry = QString().sprintf("0=-5%%,-5%%:110%%x110%%;%d=0,0:110%%x110%%;%d=0,0:110%%x110%%;%d=0,-5%%:110%%x110%%;%d=0,-5%%:110%%x110%%;%d=-5%%,-5%%:110%%x110%%;%d=0,-5%%:110%%x110%%;%d=-5%%,0:110%%x110%%",
461                                      ttl - 1, ttl, ttl * 2 - 1, ttl * 2, ttl * 3 - 1, ttl * 3, ttl * 4 - 1);
462         ttl *= 4;
463     } else if (animation.startsWith("Zoom")) {
464         geometry = QString().sprintf("0=0,0:100%%x100%%;%d=-14%%,-14%%:120%%x120%%", ttl - 1);
465     }
466     return geometry;
467 }
468
469 #include "slideshowclip.moc"
470
471