]> git.sesse.net Git - kdenlive/blob - src/slideshowclip.cpp
aa8e67ba633f89557f464302c841f52d7abab7a0
[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("TIFF (*.tiff)", "tiff");
60     m_view.image_type->addItem("Open EXR (*.exr)", "exr");
61
62     m_view.clip_duration->setInputMask("");
63     m_view.clip_duration->setValidator(m_timecode.validator());
64     m_view.clip_duration->setText(m_timecode.reformatSeparators(KdenliveSettings::sequence_duration()));
65     m_view.luma_duration->setInputMask("");
66     m_view.luma_duration->setValidator(m_timecode.validator());
67     m_view.luma_duration->setText(m_timecode.getTimecodeFromFrames(int(ceil(m_timecode.fps()))));
68     m_view.folder_url->setUrl(QDir::homePath());
69
70     m_view.clip_duration_format->addItem(i18n("hh:mm:ss::ff"));
71     m_view.clip_duration_format->addItem(i18n("Frames"));
72     connect(m_view.clip_duration_format, SIGNAL(activated(int)), this, SLOT(slotUpdateDurationFormat(int)));
73     m_view.clip_duration_frames->setHidden(true);
74     m_view.luma_duration_frames->setHidden(true);
75     m_view.method_mime->setChecked(KdenliveSettings::slideshowbymime());
76     connect(m_view.method_mime, SIGNAL(toggled(bool)), this, SLOT(slotMethodChanged(bool)));
77     slotMethodChanged(m_view.method_mime->isChecked());
78
79     // Check for Kdenlive installed luma files
80     QStringList filters;
81     filters << "*.pgm" << "*.png";
82
83     QStringList customLumas = KGlobal::dirs()->findDirs("appdata", "lumas");
84     foreach(const QString &folder, customLumas) {
85         QStringList filesnames = QDir(folder).entryList(filters, QDir::Files);
86         foreach(const QString &fname, filesnames) {
87             QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
88             m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
89         }
90     }
91
92     // Check for MLT lumas
93     QString profilePath = KdenliveSettings::mltpath();
94     QString folder = profilePath.section('/', 0, -3);
95     folder.append("/lumas/PAL"); // TODO: cleanup the PAL / NTSC mess in luma files
96     QDir lumafolder(folder);
97     QStringList filesnames = lumafolder.entryList(filters, QDir::Files);
98     foreach(const QString &fname, filesnames) {
99         QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
100         m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
101     }
102
103     //adjustSize();
104 }
105
106 SlideshowClip::~SlideshowClip()
107 {
108     if (m_thumbJob) {
109         delete m_thumbJob;
110     }
111 }
112
113 void SlideshowClip::slotEnableLuma(int state)
114 {
115     bool enable = false;
116     if (state == Qt::Checked) enable = true;
117     m_view.luma_duration->setEnabled(enable);
118     m_view.luma_duration_frames->setEnabled(enable);
119     m_view.luma_fade->setEnabled(enable);
120     if (enable) {
121         m_view.luma_file->setEnabled(m_view.luma_fade->isChecked());
122     } else m_view.luma_file->setEnabled(false);
123     m_view.label_softness->setEnabled(m_view.luma_fade->isChecked() && enable);
124     m_view.luma_softness->setEnabled(m_view.label_softness->isEnabled());
125 }
126
127 void SlideshowClip::slotEnableThumbs(int state)
128 {
129     if (state == Qt::Checked) {
130         KdenliveSettings::setShowslideshowthumbs(true);
131         slotGenerateThumbs();
132     } else {
133         KdenliveSettings::setShowslideshowthumbs(false);
134         if (m_thumbJob) {
135             disconnect(m_thumbJob, SIGNAL(gotPreview(const KFileItem &, const QPixmap &)), this, SLOT(slotSetPixmap(const KFileItem &, const QPixmap &)));
136             m_thumbJob->kill();
137             m_thumbJob = NULL;
138         }
139     }
140
141 }
142
143 void SlideshowClip::slotEnableLumaFile(int state)
144 {
145     bool enable = false;
146     if (state == Qt::Checked) enable = true;
147     m_view.luma_file->setEnabled(enable);
148     m_view.luma_softness->setEnabled(enable);
149     m_view.label_softness->setEnabled(enable);
150 }
151
152 // static
153 int SlideshowClip::sequenceCount(KUrl file)
154 {
155     // find pattern
156     QString filter = file.fileName();
157     QString ext = filter.section('.', -1);
158     filter = filter.section('.', 0, -2);
159     bool hasDigit = false;
160     while (filter.at(filter.size() - 1).isDigit()) {
161         hasDigit = true;
162         filter.remove(filter.size() - 1, 1);
163     }
164     if (!hasDigit) return 0;
165
166     QString regexp = "^" + filter + "\\d+\\." + ext + "$";
167     QRegExp rx(regexp);
168
169     QDir dir(file.directory());
170     QStringList result = dir.entryList(QDir::Files);
171
172     int count = 0;
173     foreach(const QString &path, result) {
174         if (rx.exactMatch(path)) count ++;
175     }
176     return count;
177 }
178
179 void SlideshowClip::parseFolder()
180 {
181     m_view.icon_list->clear();
182     bool isMime = m_view.method_mime->isChecked();
183     QString path = isMime ? m_view.folder_url->url().path() : m_view.pattern_url->url().directory();
184     QDir dir(path);
185     if (path.isEmpty() || !dir.exists()) return;
186
187     QStringList filters;
188     QString filter;
189     if (isMime) {
190         // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
191         filter = m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
192         filters << "*." + filter;
193         dir.setNameFilters(filters);
194     }
195
196     QStringList result = dir.entryList(QDir::Files);
197
198     if (!isMime) {
199         // find pattern
200         filter = m_view.pattern_url->url().fileName();
201         QString ext = filter.section('.', -1);
202         filter = filter.section('.', 0, -2);
203
204         while (filter.at(filter.size() - 1).isDigit()) {
205             filter.remove(filter.size() - 1, 1);
206         }
207         QString regexp = "^" + filter + "\\d+\\." + ext + "$";
208         QRegExp rx(regexp);
209         QStringList entries;
210         foreach(const QString &path, result) {
211             if (rx.exactMatch(path)) entries << path;
212         }
213         result = entries;
214     }
215
216     m_count = result.count();
217     if (m_count == 0) m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
218     else m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
219     m_view.label_info->setText(i18np("1 image found", "%1 images found", m_count));
220     QListWidgetItem *item;
221     int i = 0;
222     KIcon unknownicon("unknown");
223     foreach(const QString &path, result) {
224         i++;
225         item = new QListWidgetItem(unknownicon, KUrl(path).fileName());
226         item->setData(Qt::UserRole, dir.filePath(path));
227         m_view.icon_list->addItem(item);
228     }
229     if (m_view.show_thumbs->isChecked()) slotGenerateThumbs();
230     m_view.icon_list->setCurrentRow(0);
231 }
232
233 void SlideshowClip::slotGenerateThumbs()
234 {
235     if (m_thumbJob) {
236         delete m_thumbJob;
237     };
238     KFileItemList fileList;
239     for (int i = 0; i < m_view.icon_list->count(); i++) {
240         QListWidgetItem* item = m_view.icon_list->item(i);
241         if (item) {
242             QString path = item->data(Qt::UserRole).toString();
243             if (!path.isEmpty()) {
244                 fileList.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, KUrl(path)));
245             }
246         }
247     }
248     m_thumbJob = new KIO::PreviewJob(fileList, 50, 0, 0, 0, true, true, 0);
249     m_thumbJob->setAutoDelete(false);
250     connect(m_thumbJob, SIGNAL(gotPreview(const KFileItem &, const QPixmap &)), this, SLOT(slotSetPixmap(const KFileItem &, const QPixmap &)));
251     m_thumbJob->start();
252 }
253
254 void SlideshowClip::slotSetPixmap(const KFileItem &fileItem, const QPixmap &pix)
255 {
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 == fileItem.url().path()) {
261                 item->setIcon(KIcon(pix));
262                 item->setData(Qt::UserRole, QString());
263                 break;
264             }
265         }
266     }
267 }
268
269
270 QString SlideshowClip::selectedPath()
271 {
272     return selectedPath(m_view.folder_url->url(), m_view.method_mime->isChecked(), ".all." + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString(), &m_count);
273
274
275 }
276 // static
277 QString SlideshowClip::selectedPath(KUrl url, bool isMime, QString extension, int *count)
278 {
279     QString folder;
280
281     if (isMime) {
282         folder = url.path(KUrl::AddTrailingSlash);
283     } else {
284         folder = url.directory(KUrl::AppendTrailingSlash);
285         QString filter = url.fileName();
286         QString ext = '.' + filter.section('.', -1);
287         filter = filter.section('.', 0, -2);
288
289         while (filter.at(filter.size() - 1).isDigit()) {
290             filter.chop(1);
291         }
292         // Check that the first image exists and which format it has (image1.jpg or image001.jpg, ...)
293
294         // Find first image in sequence
295         QString regexp = "^" + filter + "\\d+" + ext + "$";
296         QRegExp rx(regexp);
297         QStringList entries;
298
299         QDir dir(folder);
300         QStringList result = dir.entryList(QDir::Files);
301         int precision = 1;
302         QString pathValue;
303         QMap <int, QString> sortedList;
304         foreach(const QString &path, result) {
305             if (rx.exactMatch(path)) {
306                 pathValue = path.section('.', 0, -2);
307                 pathValue.remove(0, filter.size());
308                 sortedList.insert(pathValue.toInt(), path);
309             }
310         }
311         *count = sortedList.size();
312         if (*count == 0) kDebug() << "No IMAGE FOUND!!!!!!!";
313         else  {
314             QMapIterator<int, QString> i(sortedList);
315             i.next();
316             QString result = i.value();
317             result.remove(0, filter.size());
318             result = result.section('.', 0, -2);
319             precision = result.size();
320         }
321         extension = filter + "%." + QString::number(precision) + "d" + ext;
322     }
323     return  folder + extension;
324 }
325
326
327 QString SlideshowClip::clipName() const
328 {
329     return m_view.clip_name->text();
330 }
331
332 QString SlideshowClip::clipDuration() const
333 {
334     if (m_view.clip_duration_format->currentIndex() == 1) {
335         // we are in frames mode
336         return m_timecode.getTimecodeFromFrames(m_view.clip_duration_frames->value());
337     }
338     return m_view.clip_duration->text();
339 }
340
341 int SlideshowClip::imageCount() const
342 {
343     return m_count;
344 }
345
346 int SlideshowClip::softness() const
347 {
348     return m_view.luma_softness->value();
349 }
350
351 bool SlideshowClip::loop() const
352 {
353     return m_view.slide_loop->isChecked();
354 }
355
356 bool SlideshowClip::crop() const
357 {
358     return m_view.slide_crop->isChecked();
359 }
360
361 bool SlideshowClip::fade() const
362 {
363     return m_view.slide_fade->isChecked();
364 }
365
366 QString SlideshowClip::lumaDuration() const
367 {
368     if (m_view.clip_duration_format->currentIndex() == 1) {
369         // we are in frames mode
370         return m_timecode.getTimecodeFromFrames(m_view.luma_duration_frames->value());
371     }
372     return m_view.luma_duration->text();
373 }
374
375 QString SlideshowClip::lumaFile() const
376 {
377     if (!m_view.luma_fade->isChecked() || !m_view.luma_file->isEnabled()) return QString();
378     return m_view.luma_file->itemData(m_view.luma_file->currentIndex()).toString();
379 }
380
381 void SlideshowClip::slotUpdateDurationFormat(int ix)
382 {
383     bool framesFormat = ix == 1;
384     if (framesFormat) {
385         // switching to frames count, update widget
386         m_view.clip_duration_frames->setValue(m_timecode.getFrameCount(m_view.clip_duration->text()));
387         m_view.luma_duration_frames->setValue(m_timecode.getFrameCount(m_view.luma_duration->text()));
388     } else {
389         // switching to timecode format
390         m_view.clip_duration->setInputMask("");
391         m_view.clip_duration->setValidator(m_timecode.validator());
392         m_view.clip_duration->setText(m_timecode.getTimecodeFromFrames(m_view.clip_duration_frames->value()));
393         m_view.luma_duration->setInputMask("");
394         m_view.luma_duration->setValidator(m_timecode.validator());
395         m_view.luma_duration->setText(m_timecode.getTimecodeFromFrames(m_view.luma_duration_frames->value()));
396     }
397     m_view.clip_duration_frames->setHidden(!framesFormat);
398     m_view.clip_duration->setHidden(framesFormat);
399     m_view.luma_duration_frames->setHidden(!framesFormat);
400     m_view.luma_duration->setHidden(framesFormat);
401 }
402
403 void SlideshowClip::slotMethodChanged(bool active)
404 {
405     if (active) {
406         // User wants mimetype image sequence
407         m_view.stackedWidget->setCurrentIndex(0);
408         KdenliveSettings::setSlideshowbymime(true);
409     } else {
410         // User wants pattern image sequence
411         m_view.stackedWidget->setCurrentIndex(1);
412         KdenliveSettings::setSlideshowbymime(false);
413     }
414     parseFolder();
415 }
416
417
418
419 #include "slideshowclip.moc"
420
421