]> git.sesse.net Git - kdenlive/blob - src/clipproperties.cpp
a01875fd90e3bfd20e13085e3d8d55f1eaf922dc
[kdenlive] / src / clipproperties.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
21 #include "clipproperties.h"
22 #include "kdenlivesettings.h"
23 #include "kthumb.h"
24 #include "markerdialog.h"
25 #include "profilesdialog.h"
26
27 #include <KStandardDirs>
28 #include <KDebug>
29 #include <KFileItem>
30
31 #include <QDir>
32
33 static const int VIDEOTAB = 0;
34 static const int AUDIOTAB = 1;
35 static const int COLORTAB = 2;
36 static const int SLIDETAB = 3;
37 static const int IMAGETAB = 4;
38 static const int MARKERTAB = 5;
39 static const int METATAB = 6;
40 static const int ADVANCEDTAB = 7;
41
42
43 ClipProperties::ClipProperties(DocClipBase *clip, Timecode tc, double fps, QWidget * parent) :
44     QDialog(parent),
45     m_clip(clip),
46     m_tc(tc),
47     m_fps(fps),
48     m_count(0),
49     m_clipNeedsRefresh(false),
50     m_clipNeedsReLoad(false),
51     m_propsDelegate(NULL)
52 {
53     setAttribute(Qt::WA_DeleteOnClose, true);
54     setFont(KGlobalSettings::toolBarFont());
55     m_view.setupUi(this);
56     KUrl url = m_clip->fileURL();
57     m_view.clip_path->setText(url.path());
58     m_view.clip_description->setText(m_clip->description());
59     connect(m_view.clip_description, SIGNAL(textChanged(QString)), this, SLOT(slotModified()));
60
61     QMap <QString, QString> props = m_clip->properties();
62     m_view.clip_force_out->setHidden(true);
63     m_view.clip_out->setHidden(true);
64
65     // New display aspect ratio support
66     if (props.contains("force_aspect_num") && props.value("force_aspect_num").toInt() > 0 &&
67         props.contains("force_aspect_den") && props.value("force_aspect_den").toInt() > 0) {
68         m_view.clip_force_ar->setChecked(true);
69         m_view.clip_ar_num->setEnabled(true);
70         m_view.clip_ar_den->setEnabled(true);
71         m_view.clip_ar_num->setValue(props.value("force_aspect_num").toInt());
72         m_view.clip_ar_den->setValue(props.value("force_aspect_den").toInt());
73     }
74     // Legacy support for pixel aspect ratio
75     else if (props.contains("force_aspect_ratio") && props.value("force_aspect_ratio").toDouble() > 0) {
76         m_view.clip_force_ar->setChecked(true);
77         m_view.clip_ar_num->setEnabled(true);
78         m_view.clip_ar_den->setEnabled(true);
79         if (props.contains("frame_size")) {
80             int width = props.value("force_aspect_ratio").toDouble() * props.value("frame_size").section('x', 0, 0).toInt();
81             int height = props.value("frame_size").section('x', 1, 1).toInt();
82             if (width > 0 && height > 0) {
83                 if ((width / height * 100) == 133) {
84                     width = 4;
85                     height = 3;
86                 }
87                 else if (int(width / height * 100) == 177) {
88                     width = 16;
89                     height = 9;
90                 }
91                 m_view.clip_ar_num->setValue(width);
92                 m_view.clip_ar_den->setValue(height);
93             }
94         }
95     }
96     connect(m_view.clip_force_ar, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
97     connect(m_view.clip_ar_num, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
98     connect(m_view.clip_ar_den, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
99
100     if (props.contains("force_fps") && props.value("force_fps").toDouble() > 0) {
101         m_view.clip_force_framerate->setChecked(true);
102         m_view.clip_framerate->setEnabled(true);
103         m_view.clip_framerate->setValue(props.value("force_fps").toDouble());
104     }
105     connect(m_view.clip_force_framerate, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
106     connect(m_view.clip_framerate, SIGNAL(valueChanged(double)), this, SLOT(slotModified()));
107
108     if (props.contains("force_progressive")) {
109         m_view.clip_force_progressive->setChecked(true);
110         m_view.clip_progressive->setEnabled(true);
111         m_view.clip_progressive->setValue(props.value("force_progressive").toInt());
112     }
113     connect(m_view.clip_force_progressive, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
114     connect(m_view.clip_progressive, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
115
116     if (props.contains("threads") && props.value("threads").toInt() != 1) {
117         m_view.clip_force_threads->setChecked(true);
118         m_view.clip_threads->setEnabled(true);
119         m_view.clip_threads->setValue(props.value("threads").toInt());
120     }
121     connect(m_view.clip_force_threads, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
122     connect(m_view.clip_threads, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
123
124     if (props.contains("video_index") && props.value("video_index").toInt() != 0) {
125         m_view.clip_force_vindex->setChecked(true);
126         m_view.clip_vindex->setEnabled(true);
127         m_view.clip_vindex->setValue(props.value("video_index").toInt());
128     }
129     connect(m_view.clip_force_vindex, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
130     connect(m_view.clip_vindex, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
131
132     if (props.contains("audio_index") && props.value("audio_index").toInt() != 0) {
133         m_view.clip_force_aindex->setChecked(true);
134         m_view.clip_aindex->setEnabled(true);
135         m_view.clip_aindex->setValue(props.value("audio_index").toInt());
136     }
137     connect(m_view.clip_force_aindex, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
138     connect(m_view.clip_aindex, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
139
140     if (props.contains("audio_max")) {
141         m_view.clip_aindex->setMaximum(props.value("audio_max").toInt());
142     }
143
144     if (props.contains("video_max")) {
145         m_view.clip_vindex->setMaximum(props.value("video_max").toInt());
146     }
147     
148     m_view.clip_colorspace->addItem(ProfilesDialog::getColorspaceDescription(601), 601);
149     m_view.clip_colorspace->addItem(ProfilesDialog::getColorspaceDescription(709), 709);
150     m_view.clip_colorspace->addItem(ProfilesDialog::getColorspaceDescription(240), 240);
151     if (props.contains("force_colorspace")) {
152         m_view.clip_force_colorspace->setChecked(true);
153         m_view.clip_colorspace->setEnabled(true);
154         m_view.clip_colorspace->setCurrentIndex(m_view.clip_colorspace->findData(props.value("force_colorspace").toInt()));
155     } else if (props.contains("colorspace")) {
156         m_view.clip_colorspace->setCurrentIndex(m_view.clip_colorspace->findData(props.value("colorspace").toInt()));
157     }
158     connect(m_view.clip_force_colorspace, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
159     connect(m_view.clip_colorspace, SIGNAL(currentIndexChanged(int)), this, SLOT(slotModified()));
160     
161     if (props.contains("full_luma")) {
162         m_view.clip_full_luma->setChecked(true);
163     }
164     connect(m_view.clip_full_luma, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
165
166     // Check for Metadata
167     QMap<QString, QString> meta = m_clip->metadata();
168     QMap<QString, QString>::const_iterator i = meta.constBegin();
169     while (i != meta.constEnd()) {
170         QTreeWidgetItem *metaitem = new QTreeWidgetItem(m_view.metadata_list);
171         metaitem->setText(0, i.key()); //i18n(i.key().section('.', 2, 3).toUtf8().data()));
172         metaitem->setText(1, i.value());
173         ++i;
174     }
175
176     connect(m_view.clip_force_ar, SIGNAL(toggled(bool)), m_view.clip_ar_num, SLOT(setEnabled(bool)));
177     connect(m_view.clip_force_ar, SIGNAL(toggled(bool)), m_view.clip_ar_den, SLOT(setEnabled(bool)));
178     connect(m_view.clip_force_framerate, SIGNAL(toggled(bool)), m_view.clip_framerate, SLOT(setEnabled(bool)));
179     connect(m_view.clip_force_progressive, SIGNAL(toggled(bool)), m_view.clip_progressive, SLOT(setEnabled(bool)));
180     connect(m_view.clip_force_threads, SIGNAL(toggled(bool)), m_view.clip_threads, SLOT(setEnabled(bool)));
181     connect(m_view.clip_force_vindex, SIGNAL(toggled(bool)), m_view.clip_vindex, SLOT(setEnabled(bool)));
182     connect(m_view.clip_force_aindex, SIGNAL(toggled(bool)), m_view.clip_aindex, SLOT(setEnabled(bool)));
183     connect(m_view.clip_force_colorspace, SIGNAL(toggled(bool)), m_view.clip_colorspace, SLOT(setEnabled(bool)));
184
185     if (props.contains("audiocodec"))
186         QTreeWidgetItem *item = new QTreeWidgetItem(m_view.clip_aproperties, QStringList() << i18n("Audio codec") << props.value("audiocodec"));
187
188     if (props.contains("channels"))
189         QTreeWidgetItem *item = new QTreeWidgetItem(m_view.clip_aproperties, QStringList() << i18n("Channels") << props.value("channels"));
190
191     if (props.contains("frequency"))
192         QTreeWidgetItem *item = new QTreeWidgetItem(m_view.clip_aproperties, QStringList() << i18n("Frequency") << props.value("frequency"));
193
194     CLIPTYPE t = m_clip->clipType();
195     if (t != AUDIO && t != AV) {
196         m_view.clip_force_aindex->setEnabled(false);
197     }
198
199     if (t != VIDEO && t != AV) {
200         m_view.clip_force_vindex->setEnabled(false);
201     }
202
203     if (t == IMAGE) {
204         m_view.tabWidget->removeTab(SLIDETAB);
205         m_view.tabWidget->removeTab(COLORTAB);
206         m_view.tabWidget->removeTab(AUDIOTAB);
207         m_view.tabWidget->removeTab(VIDEOTAB);
208         if (props.contains("frame_size"))
209             m_view.image_size->setText(props.value("frame_size"));
210         if (props.contains("transparency"))
211             m_view.image_transparency->setChecked(props.value("transparency").toInt());
212         connect(m_view.image_transparency, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
213         int width = 180.0 * KdenliveSettings::project_display_ratio();
214         if (width % 2 == 1) width++;
215         m_view.clip_thumb->setPixmap(QPixmap(url.path()).scaled(QSize(width, 180), Qt::KeepAspectRatio));
216     } else if (t == COLOR) {
217         m_view.clip_path->setEnabled(false);
218         m_view.tabWidget->removeTab(METATAB);
219         m_view.tabWidget->removeTab(IMAGETAB);
220         m_view.tabWidget->removeTab(SLIDETAB);
221         m_view.tabWidget->removeTab(AUDIOTAB);
222         m_view.tabWidget->removeTab(VIDEOTAB);
223         m_view.clip_thumb->setHidden(true);
224         m_view.clip_color->setColor(QColor('#' + props.value("colour").right(8).left(6)));
225         connect(m_view.clip_color, SIGNAL(changed(QColor)), this, SLOT(slotModified()));
226     } else if (t == SLIDESHOW) {
227         bool isMime = true;
228         if (url.fileName().startsWith(".all.")) {
229             // the image sequence is defined by mimetype
230             m_view.clip_path->setText(url.directory());
231         } else {
232             // the image sequence is defined by pattern
233             m_view.slide_type_label->setHidden(true);
234             m_view.image_type->setHidden(true);
235             m_view.clip_path->setText(url.path());
236             isMime = false;
237         }
238
239         m_view.tabWidget->removeTab(METATAB);
240         m_view.tabWidget->removeTab(IMAGETAB);
241         m_view.tabWidget->removeTab(COLORTAB);
242         m_view.tabWidget->removeTab(AUDIOTAB);
243         m_view.tabWidget->removeTab(VIDEOTAB);
244
245         //WARNING: Keep in sync with slideshowclip.cpp
246         m_view.image_type->addItem("JPG (*.jpg)", "jpg");
247         m_view.image_type->addItem("JPEG (*.jpeg)", "jpeg");
248         m_view.image_type->addItem("PNG (*.png)", "png");
249         m_view.image_type->addItem("BMP (*.bmp)", "bmp");
250         m_view.image_type->addItem("GIF (*.gif)", "gif");
251         m_view.image_type->addItem("TGA (*.tga)", "tga");
252         m_view.image_type->addItem("TIFF (*.tiff)", "tiff");
253         m_view.image_type->addItem("Open EXR (*.exr)", "exr");
254         m_view.animation->addItem(i18n("None"), QString());
255         m_view.animation->addItem(i18n("Pan"), "Pan");
256         m_view.animation->addItem(i18n("Pan, low-pass"), "Pan, low-pass");
257         m_view.animation->addItem(i18n("Pan and zoom"), "Pan and zoom");
258         m_view.animation->addItem(i18n("Pan and zoom, low-pass"), "Pan and zoom, low-pass");
259         m_view.animation->addItem(i18n("Zoom"), "Zoom");
260         m_view.animation->addItem(i18n("Zoom, low-pass"), "Zoom, low-pass");
261
262         m_view.slide_loop->setChecked(props.value("loop").toInt());
263         m_view.slide_crop->setChecked(props.value("crop").toInt());
264         m_view.slide_fade->setChecked(props.value("fade").toInt());
265         m_view.luma_softness->setValue(props.value("softness").toInt());
266         if (!props.value("animation").isEmpty())
267             m_view.animation->setCurrentItem(props.value("animation"));
268         else
269             m_view.animation->setCurrentIndex(0);
270         QString path = props.value("resource");
271         QString ext = path.section('.', -1);
272         for (int i = 0; i < m_view.image_type->count(); i++) {
273             if (m_view.image_type->itemData(i).toString() == ext) {
274                 m_view.image_type->setCurrentIndex(i);
275                 break;
276             }
277         }
278         m_view.slide_duration->setText(tc.getTimecodeFromFrames(props.value("ttl").toInt()));
279
280         m_view.slide_duration_format->addItem(i18n("hh:mm:ss::ff"));
281         m_view.slide_duration_format->addItem(i18n("Frames"));
282         connect(m_view.slide_duration_format, SIGNAL(activated(int)), this, SLOT(slotUpdateDurationFormat(int)));
283         m_view.slide_duration_frames->setHidden(true);
284         m_view.luma_duration_frames->setHidden(true);
285
286         parseFolder();
287
288         m_view.luma_duration->setText(tc.getTimecodeFromFrames(props.value("luma_duration").toInt()));
289         QString lumaFile = props.value("luma_file");
290
291         // Check for Kdenlive installed luma files
292         QStringList filters;
293         filters << "*.pgm" << "*.png";
294
295         QStringList customLumas = KGlobal::dirs()->findDirs("appdata", "lumas");
296         foreach(const QString & folder, customLumas) {
297             QStringList filesnames = QDir(folder).entryList(filters, QDir::Files);
298             foreach(const QString & fname, filesnames) {
299                 QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
300                 m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
301             }
302         }
303
304         // Check for MLT lumas
305         QString profilePath = KdenliveSettings::mltpath();
306         QString folder = profilePath.section('/', 0, -3);
307         folder.append("/lumas/PAL"); // TODO: cleanup the PAL / NTSC mess in luma files
308         QDir lumafolder(folder);
309         QStringList filesnames = lumafolder.entryList(filters, QDir::Files);
310         foreach(const QString & fname, filesnames) {
311             QString filePath = KUrl(folder).path(KUrl::AddTrailingSlash) + fname;
312             m_view.luma_file->addItem(KIcon(filePath), fname, filePath);
313         }
314
315         if (!lumaFile.isEmpty()) {
316             m_view.slide_luma->setChecked(true);
317             m_view.luma_file->setCurrentIndex(m_view.luma_file->findData(lumaFile));
318         } else m_view.luma_file->setEnabled(false);
319         slotEnableLuma(m_view.slide_fade->checkState());
320         slotEnableLumaFile(m_view.slide_luma->checkState());
321
322         connect(m_view.slide_fade, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
323         connect(m_view.slide_luma, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
324         connect(m_view.slide_loop, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
325         connect(m_view.slide_crop, SIGNAL(toggled(bool)), this, SLOT(slotModified()));
326         connect(m_view.slide_duration, SIGNAL(textChanged(QString)), this, SLOT(slotModified()));
327         connect(m_view.slide_duration_frames, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
328         connect(m_view.luma_duration, SIGNAL(textChanged(QString)), this, SLOT(slotModified()));
329         connect(m_view.luma_softness, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
330         connect(m_view.luma_file, SIGNAL(currentIndexChanged(int)), this, SLOT(slotModified()));
331         connect(m_view.animation, SIGNAL(currentIndexChanged(int)), this, SLOT(slotModified()));
332
333
334         connect(m_view.slide_fade, SIGNAL(stateChanged(int)), this, SLOT(slotEnableLuma(int)));
335         connect(m_view.slide_luma, SIGNAL(stateChanged(int)), this, SLOT(slotEnableLumaFile(int)));
336         connect(m_view.image_type, SIGNAL(currentIndexChanged(int)), this, SLOT(parseFolder()));
337
338     } else if (t != AUDIO) {
339         m_view.tabWidget->removeTab(IMAGETAB);
340         m_view.tabWidget->removeTab(SLIDETAB);
341         m_view.tabWidget->removeTab(COLORTAB);
342
343
344
345         m_propsDelegate = new PropertiesViewDelegate(this);
346         m_view.clip_vproperties->setItemDelegate(m_propsDelegate);
347         m_view.clip_aproperties->setItemDelegate(m_propsDelegate);
348         m_view.clip_aproperties->setStyleSheet(QString("QTreeWidget { background-color: transparent;}"));
349         m_view.clip_vproperties->setStyleSheet(QString("QTreeWidget { background-color: transparent;}"));
350
351         if (props.contains("videocodec"))
352             QTreeWidgetItem *item = new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Video codec") << props.value("videocodec"));
353
354         if (props.contains("frame_size"))
355             new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Frame size") << props.value("frame_size"));
356
357         if (props.contains("fps")) {
358             new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Frame rate") << props.value("fps"));
359             if (!m_view.clip_framerate->isEnabled()) m_view.clip_framerate->setValue(props.value("fps").toDouble());
360         }
361         if (props.contains("aspect_ratio"))
362             new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Pixel aspect ratio") << props.value("aspect_ratio"));
363
364         if (props.contains("pix_fmt"))
365             new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Pixel format") << props.value("pix_fmt"));
366
367         if (props.contains("colorspace"))
368             new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Colorspace") << ProfilesDialog::getColorspaceDescription(props.value("colorspace").toInt()));
369
370         int width = 180.0 * KdenliveSettings::project_display_ratio();
371         if (width % 2 == 1) width++;
372         QPixmap pix = m_clip->thumbProducer()->getImage(url, m_clip->getClipThumbFrame(), width, 180);
373         m_view.clip_thumb->setPixmap(pix);
374         if (t == IMAGE || t == VIDEO) m_view.tabWidget->removeTab(AUDIOTAB);
375     } else {
376         m_view.tabWidget->removeTab(IMAGETAB);
377         m_view.tabWidget->removeTab(SLIDETAB);
378         m_view.tabWidget->removeTab(COLORTAB);
379         m_view.tabWidget->removeTab(VIDEOTAB);
380         m_view.clip_thumb->setHidden(true);
381     }
382
383     if (t != SLIDESHOW && t != COLOR) {
384         KFileItem f(KFileItem::Unknown, KFileItem::Unknown, url, true);
385         m_view.clip_filesize->setText(KIO::convertSize(f.size()));
386     } else {
387         m_view.clip_filesize->setHidden(true);
388         m_view.label_size->setHidden(true);
389     }
390     m_view.clip_duration->setInputMask("");
391     m_view.clip_duration->setValidator(tc.validator());
392     m_view.clip_duration->setText(tc.getTimecode(m_clip->duration()));
393     if (t != IMAGE && t != COLOR && t != TEXT) m_view.clip_duration->setReadOnly(true);
394     else connect(m_view.clip_duration, SIGNAL(editingFinished()), this, SLOT(slotCheckMaxLength()));
395
396     // markers
397     m_view.marker_new->setIcon(KIcon("document-new"));
398     m_view.marker_new->setToolTip(i18n("Add marker"));
399     m_view.marker_edit->setIcon(KIcon("document-properties"));
400     m_view.marker_edit->setToolTip(i18n("Edit marker"));
401     m_view.marker_delete->setIcon(KIcon("trash-empty"));
402     m_view.marker_delete->setToolTip(i18n("Delete marker"));
403
404     slotFillMarkersList();
405     connect(m_view.marker_new, SIGNAL(clicked()), this, SLOT(slotAddMarker()));
406     connect(m_view.marker_edit, SIGNAL(clicked()), this, SLOT(slotEditMarker()));
407     connect(m_view.marker_delete, SIGNAL(clicked()), this, SLOT(slotDeleteMarker()));
408     connect(m_view.markers_list, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(slotEditMarker()));
409
410     connect(this, SIGNAL(accepted()), this, SLOT(slotApplyProperties()));
411     connect(m_view.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotApplyProperties()));
412     m_view.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
413     //adjustSize();
414 }
415
416
417 // Used for multiple clips editing
418 ClipProperties::ClipProperties(QList <DocClipBase *>cliplist, Timecode tc, QMap <QString, QString> commonproperties, QWidget * parent) :
419     QDialog(parent),
420     m_clip(NULL),
421     m_tc(tc),
422     m_fps(0),
423     m_count(0),
424     m_clipNeedsRefresh(false),
425     m_clipNeedsReLoad(false)
426 {
427     setFont(KGlobalSettings::toolBarFont());
428     m_view.setupUi(this);
429     QMap <QString, QString> props = cliplist.at(0)->properties();
430     m_old_props = commonproperties;
431
432     if (commonproperties.contains("force_aspect_num") && !commonproperties.value("force_aspect_num").isEmpty() && commonproperties.value("force_aspect_den").toInt() > 0) {
433         m_view.clip_force_ar->setChecked(true);
434         m_view.clip_ar_num->setEnabled(true);
435         m_view.clip_ar_den->setEnabled(true);
436         m_view.clip_ar_num->setValue(commonproperties.value("force_aspect_num").toInt());
437         m_view.clip_ar_den->setValue(commonproperties.value("force_aspect_den").toInt());
438     }
439
440     if (commonproperties.contains("force_fps") && !commonproperties.value("force_fps").isEmpty() && commonproperties.value("force_fps").toDouble() > 0) {
441         m_view.clip_force_framerate->setChecked(true);
442         m_view.clip_framerate->setEnabled(true);
443         m_view.clip_framerate->setValue(commonproperties.value("force_fps").toDouble());
444     }
445
446     if (commonproperties.contains("force_progressive") && !commonproperties.value("force_progressive").isEmpty()) {
447         m_view.clip_force_progressive->setChecked(true);
448         m_view.clip_progressive->setEnabled(true);
449         m_view.clip_progressive->setValue(commonproperties.value("force_progressive").toInt());
450     }
451
452     if (commonproperties.contains("threads") && !commonproperties.value("threads").isEmpty() && commonproperties.value("threads").toInt() != 1) {
453         m_view.clip_force_threads->setChecked(true);
454         m_view.clip_threads->setEnabled(true);
455         m_view.clip_threads->setValue(commonproperties.value("threads").toInt());
456     }
457
458     if (commonproperties.contains("video_index") && !commonproperties.value("video_index").isEmpty() && commonproperties.value("video_index").toInt() != 0) {
459         m_view.clip_force_vindex->setChecked(true);
460         m_view.clip_vindex->setEnabled(true);
461         m_view.clip_vindex->setValue(commonproperties.value("video_index").toInt());
462     }
463
464     if (commonproperties.contains("audio_index") && !commonproperties.value("audio_index").isEmpty() && commonproperties.value("audio_index").toInt() != 0) {
465         m_view.clip_force_aindex->setChecked(true);
466         m_view.clip_aindex->setEnabled(true);
467         m_view.clip_aindex->setValue(commonproperties.value("audio_index").toInt());
468     }
469
470     if (props.contains("audio_max")) {
471         m_view.clip_aindex->setMaximum(props.value("audio_max").toInt());
472     }
473
474     if (props.contains("video_max")) {
475         m_view.clip_vindex->setMaximum(props.value("video_max").toInt());
476     }
477     
478     if (commonproperties.contains("force_colorspace") && !commonproperties.value("force_colorspace").isEmpty() && commonproperties.value("force_colorspace").toInt() != 0) {
479         m_view.clip_force_colorspace->setChecked(true);
480         m_view.clip_colorspace->setEnabled(true);
481         m_view.clip_colorspace->setCurrentIndex(m_view.clip_colorspace->findData(commonproperties.value("force_colorspace").toInt()));
482     }
483     
484     if (commonproperties.contains("full_luma") && !commonproperties.value("full_luma").isEmpty()) {
485         m_view.clip_full_luma->setChecked(true);
486     }
487
488     connect(m_view.clip_force_ar, SIGNAL(toggled(bool)), m_view.clip_ar_num, SLOT(setEnabled(bool)));
489     connect(m_view.clip_force_ar, SIGNAL(toggled(bool)), m_view.clip_ar_den, SLOT(setEnabled(bool)));
490     connect(m_view.clip_force_progressive, SIGNAL(toggled(bool)), m_view.clip_progressive, SLOT(setEnabled(bool)));
491     connect(m_view.clip_force_threads, SIGNAL(toggled(bool)), m_view.clip_threads, SLOT(setEnabled(bool)));
492     connect(m_view.clip_force_vindex, SIGNAL(toggled(bool)), m_view.clip_vindex, SLOT(setEnabled(bool)));
493     connect(m_view.clip_force_aindex, SIGNAL(toggled(bool)), m_view.clip_aindex, SLOT(setEnabled(bool)));
494     connect(m_view.clip_force_out, SIGNAL(toggled(bool)), m_view.clip_out, SLOT(setEnabled(bool)));
495     connect(m_view.clip_force_colorspace, SIGNAL(toggled(bool)), m_view.clip_colorspace, SLOT(setEnabled(bool)));
496
497     m_view.tabWidget->removeTab(METATAB);
498     m_view.tabWidget->removeTab(MARKERTAB);
499     m_view.tabWidget->removeTab(IMAGETAB);
500     m_view.tabWidget->removeTab(SLIDETAB);
501     m_view.tabWidget->removeTab(COLORTAB);
502     m_view.tabWidget->removeTab(AUDIOTAB);
503     m_view.tabWidget->removeTab(VIDEOTAB);
504
505     m_view.clip_path->setHidden(true);
506     m_view.label_path->setHidden(true);
507     m_view.label_description->setHidden(true);
508     m_view.label_size->setHidden(true);
509     m_view.clip_filesize->setHidden(true);
510     m_view.clip_filesize->setHidden(true);
511     m_view.clip_path->setHidden(true);
512     m_view.clip_description->setHidden(true);
513     m_view.clip_thumb->setHidden(true);
514     m_view.label_duration->setHidden(true);
515     m_view.clip_duration->setHidden(true);
516
517     if (commonproperties.contains("out")) {
518         if (commonproperties.value("out").toInt() > 0) {
519             m_view.clip_force_out->setChecked(true);
520             m_view.clip_out->setText(m_tc.getTimecodeFromFrames(commonproperties.value("out").toInt()));
521         } else m_view.clip_out->setText(KdenliveSettings::image_duration());
522     } else {
523         m_view.clip_force_out->setHidden(true);
524         m_view.clip_out->setHidden(true);
525     }
526 }
527
528 ClipProperties::~ClipProperties()
529 {
530     if (m_propsDelegate) delete m_propsDelegate;
531 }
532
533 void ClipProperties::slotApplyProperties()
534 {
535     if (m_clip != NULL)
536         emit applyNewClipProperties(m_clip->getId(), m_clip->properties(), properties(), needsTimelineRefresh(), needsTimelineReload());
537     m_view.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
538 }
539
540 void ClipProperties::disableClipId(const QString &id)
541 {
542     if (m_clip && m_view.buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) {
543         if (m_clip->getId() == id) {
544             // clip was removed from project, close this properties dialog
545             close();
546         }
547     }
548 }
549
550 void ClipProperties::slotModified()
551 {
552     m_view.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
553 }
554
555
556 void ClipProperties::slotEnableLuma(int state)
557 {
558     bool enable = false;
559     if (state == Qt::Checked) enable = true;
560     m_view.luma_duration->setEnabled(enable);
561     m_view.luma_duration_frames->setEnabled(enable);
562     m_view.slide_luma->setEnabled(enable);
563     if (enable) {
564         m_view.luma_file->setEnabled(m_view.slide_luma->isChecked());
565     } else m_view.luma_file->setEnabled(false);
566     m_view.label_softness->setEnabled(m_view.slide_luma->isChecked() && enable);
567     m_view.luma_softness->setEnabled(m_view.label_softness->isEnabled());
568 }
569
570 void ClipProperties::slotEnableLumaFile(int state)
571 {
572     bool enable = false;
573     if (state == Qt::Checked) enable = true;
574     m_view.luma_file->setEnabled(enable);
575     m_view.luma_softness->setEnabled(enable);
576     m_view.label_softness->setEnabled(enable);
577 }
578
579 void ClipProperties::slotFillMarkersList()
580 {
581     m_view.markers_list->clear();
582     QList < CommentedTime > marks = m_clip->commentedSnapMarkers();
583     for (int count = 0; count < marks.count(); ++count) {
584         QString time = m_tc.getTimecode(marks[count].time());
585         QStringList itemtext;
586         itemtext << time << marks[count].comment();
587         (void) new QTreeWidgetItem(m_view.markers_list, itemtext);
588     }
589 }
590
591 void ClipProperties::slotAddMarker()
592 {
593     CommentedTime marker(GenTime(), i18n("Marker"));
594     MarkerDialog d(m_clip, marker, m_tc, i18n("Add Marker"), this);
595     if (d.exec() == QDialog::Accepted) {
596         emit addMarker(m_clip->getId(), d.newMarker().time(), d.newMarker().comment());
597     }
598     QTimer::singleShot(500, this, SLOT(slotFillMarkersList()));
599 }
600
601 void ClipProperties::slotEditMarker()
602 {
603     QList < CommentedTime > marks = m_clip->commentedSnapMarkers();
604     int pos = m_view.markers_list->currentIndex().row();
605     if (pos < 0 || pos > marks.count() - 1) return;
606     MarkerDialog d(m_clip, marks.at(pos), m_tc, i18n("Edit Marker"), this);
607     if (d.exec() == QDialog::Accepted) {
608         emit addMarker(m_clip->getId(), d.newMarker().time(), d.newMarker().comment());
609     }
610     QTimer::singleShot(500, this, SLOT(slotFillMarkersList()));
611 }
612
613 void ClipProperties::slotDeleteMarker()
614 {
615     QList < CommentedTime > marks = m_clip->commentedSnapMarkers();
616     int pos = m_view.markers_list->currentIndex().row();
617     if (pos < 0 || pos > marks.count() - 1) return;
618     emit addMarker(m_clip->getId(), marks.at(pos).time(), QString());
619
620     QTimer::singleShot(500, this, SLOT(slotFillMarkersList()));
621 }
622
623 const QString &ClipProperties::clipId() const
624 {
625     return m_clip->getId();
626 }
627
628
629 QMap <QString, QString> ClipProperties::properties()
630 {
631     QMap <QString, QString> props;
632     CLIPTYPE t = UNKNOWN;
633     if (m_clip != NULL) {
634         t = m_clip->clipType();
635         m_old_props = m_clip->properties();
636     }
637
638     int aspectNumerator = m_view.clip_ar_num->value();
639     int aspectDenominator = m_view.clip_ar_den->value();
640     if (m_view.clip_force_ar->isChecked()) {
641         if (aspectNumerator != m_old_props.value("force_aspect_num").toInt() ||
642             aspectDenominator != m_old_props.value("force_aspect_den").toInt()) {
643             props["force_aspect_num"] = QString::number(aspectNumerator);
644             props["force_aspect_den"] = QString::number(aspectDenominator);
645             props["force_aspect_ratio"].clear();
646             m_clipNeedsRefresh = true;
647         }
648     } else {
649         if (m_old_props.contains("force_aspect_num")) {
650             props["force_aspect_num"].clear();
651             m_clipNeedsRefresh = true;
652         }
653         if (m_old_props.contains("force_aspect_den")) {
654             props["force_aspect_den"].clear();
655             m_clipNeedsRefresh = true;
656         }
657     }
658
659     double fps = m_view.clip_framerate->value();
660     if (m_view.clip_force_framerate->isChecked()) {
661         if (fps != m_old_props.value("force_fps").toDouble()) {
662             props["force_fps"] = QString::number(fps);
663             m_clipNeedsRefresh = true;
664         }
665     } else if (m_old_props.contains("force_fps")) {
666         props["force_fps"].clear();
667         m_clipNeedsRefresh = true;
668     }
669
670     int progressive = m_view.clip_progressive->value();
671     if (m_view.clip_force_progressive->isChecked()) {
672         if (progressive != m_old_props.value("force_progressive").toInt()) {
673             props["force_progressive"] = QString::number(progressive);
674         }
675     } else if (m_old_props.contains("force_progressive")) {
676         props["force_progressive"].clear();
677     }
678
679     int threads = m_view.clip_threads->value();
680     if (m_view.clip_force_threads->isChecked()) {
681         if (threads != m_old_props.value("threads").toInt()) {
682             props["threads"] = QString::number(threads);
683         }
684     } else if (m_old_props.contains("threads")) {
685         props["threads"].clear();
686     }
687
688     int vindex = m_view.clip_vindex->value();
689     if (m_view.clip_force_vindex->isChecked()) {
690         if (vindex != m_old_props.value("video_index").toInt()) {
691             props["video_index"] = QString::number(vindex);
692         }
693     } else if (m_old_props.contains("video_index")) {
694         props["video_index"].clear();
695     }
696
697     int aindex = m_view.clip_aindex->value();
698     if (m_view.clip_force_aindex->isChecked()) {
699         if (aindex != m_old_props.value("audio_index").toInt()) {
700             props["audio_index"] = QString::number(aindex);
701         }
702     } else if (m_old_props.contains("audio_index")) {
703         props["audio_index"].clear();
704     }
705     
706     int colorspace = m_view.clip_colorspace->itemData(m_view.clip_colorspace->currentIndex()).toInt();
707     if (m_view.clip_force_colorspace->isChecked()) {
708         if (colorspace != m_old_props.value("force_colorspace").toInt()) {
709             props["force_colorspace"] = QString::number(colorspace);
710             m_clipNeedsRefresh = true;
711         }
712     } else if (m_old_props.contains("force_colorspace")) {
713         props["force_colorspace"].clear();
714         m_clipNeedsRefresh = true;
715     }
716
717     if (m_view.clip_full_luma->isChecked()) {
718         props["full_luma"] = QString::number(1);
719         m_clipNeedsRefresh = true;
720     } else if (m_old_props.contains("full_luma")) {
721         props["full_luma"].clear();
722         m_clipNeedsRefresh = true;
723     }
724
725     // If we adjust several clips, return now
726     if (m_clip == NULL) {
727         if (m_view.clip_out->isEnabled()) {
728             int duration = m_tc.getFrameCount(m_view.clip_out->text());
729             if (duration != m_old_props.value("out").toInt()) {
730                 props["out"] = QString::number(duration - 1);
731             }
732         }
733         return props;
734     }
735
736     if (m_old_props.value("description") != m_view.clip_description->text())
737         props["description"] = m_view.clip_description->text();
738
739     if (t == COLOR) {
740         QString new_color = m_view.clip_color->color().name();
741         if (new_color != QString('#' + m_old_props.value("colour").right(8).left(6))) {
742             m_clipNeedsRefresh = true;
743             props["colour"] = "0x" + new_color.right(6) + "ff";
744         }
745         int duration = m_tc.getFrameCount(m_view.clip_duration->text());
746         if (duration != m_clip->duration().frames(m_fps)) {
747             props["out"] = QString::number(duration - 1);
748         }
749     } else if (t == IMAGE) {
750         if ((int) m_view.image_transparency->isChecked() != m_old_props.value("transparency").toInt()) {
751             props["transparency"] = QString::number((int)m_view.image_transparency->isChecked());
752             //m_clipNeedsRefresh = true;
753         }
754         int duration = m_tc.getFrameCount(m_view.clip_duration->text());
755         if (duration != m_clip->duration().frames(m_fps)) {
756             props["out"] = QString::number(duration - 1);
757         }
758     } else if (t == SLIDESHOW) {
759         QString value = QString::number((int) m_view.slide_loop->isChecked());
760         if (m_old_props.value("loop") != value) props["loop"] = value;
761         value = QString::number((int) m_view.slide_crop->isChecked());
762         if (m_old_props.value("crop") != value) props["crop"] = value;
763         value = QString::number((int) m_view.slide_fade->isChecked());
764         if (m_old_props.value("fade") != value) props["fade"] = value;
765         value = QString::number((int) m_view.luma_softness->value());
766         if (m_old_props.value("softness") != value) props["softness"] = value;
767
768         bool isMime = !(m_view.clip_path->text().contains('%'));
769         if (isMime) {
770             QString extension = "/.all." + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
771             QString new_path = m_view.clip_path->text() + extension;
772             if (new_path != m_old_props.value("resource")) {
773                 m_clipNeedsReLoad = true;
774                 props["resource"] = new_path;
775                 kDebug() << "////  SLIDE EDIT, NEW:" << new_path << ", OLD; " << m_old_props.value("resource");
776             }
777         }
778         int duration;
779         if (m_view.slide_duration_format->currentIndex() == 1) {
780             // we are in frames mode
781             duration = m_view.slide_duration_frames->value();
782         } else duration = m_tc.getFrameCount(m_view.slide_duration->text());
783         if (duration != m_old_props.value("ttl").toInt()) {
784             m_clipNeedsRefresh = true;
785             props["ttl"] = QString::number(duration);
786             props["out"] = QString::number(duration * m_count - 1);
787         }
788
789         if (duration * m_count - 1 != m_old_props.value("out").toInt()) {
790             m_clipNeedsRefresh = true;
791             props["out"] = QString::number(duration * m_count - 1);
792         }
793         if (m_view.slide_fade->isChecked()) {
794             int luma_duration;
795             if (m_view.slide_duration_format->currentIndex() == 1) {
796                 // we are in frames mode
797                 luma_duration = m_view.luma_duration_frames->value();
798             } else luma_duration = m_tc.getFrameCount(m_view.luma_duration->text());
799             if (luma_duration != m_old_props.value("luma_duration").toInt()) {
800                 m_clipNeedsRefresh = true;
801                 props["luma_duration"] = QString::number(luma_duration);
802             }
803             QString lumaFile;
804             if (m_view.slide_luma->isChecked())
805                 lumaFile = m_view.luma_file->itemData(m_view.luma_file->currentIndex()).toString();
806             if (lumaFile != m_old_props.value("luma_file")) {
807                 m_clipNeedsRefresh = true;
808                 props["luma_file"] = lumaFile;
809             }
810         } else {
811             if (!m_old_props.value("luma_file").isEmpty()) {
812                 props["luma_file"].clear();
813             }
814         }
815
816         QString animation = m_view.animation->itemData(m_view.animation->currentIndex()).toString();
817         if (animation != m_old_props.value("animation")) {
818             if (animation.isEmpty()) {
819                 props["animation"].clear();
820             } else {
821                 props["animation"] = animation;
822             }
823             m_clipNeedsRefresh = true;
824         }
825     }
826     return props;
827 }
828
829 bool ClipProperties::needsTimelineRefresh() const
830 {
831     return m_clipNeedsRefresh;
832 }
833
834 bool ClipProperties::needsTimelineReload() const
835 {
836     return m_clipNeedsReLoad;
837 }
838
839
840 void ClipProperties::parseFolder()
841 {
842     QString path = m_view.clip_path->text();
843     bool isMime = !(path.contains('%'));
844     if (!isMime) path = KUrl(path).directory();
845     QDir dir(path);
846
847     QStringList filters;
848     QString extension;
849
850     if (isMime) {
851         // TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
852         filters << "*." + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
853         extension = "/.all." + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
854         dir.setNameFilters(filters);
855     }
856
857     QStringList result = dir.entryList(QDir::Files);
858
859     if (!isMime) {
860         // find pattern
861         QString filter = KUrl(m_view.clip_path->text()).fileName();
862         QString ext = filter.section('.', -1);
863         filter = filter.section('%', 0, -2);
864         QString regexp = "^" + filter + "\\d+\\." + ext + "$";
865         QRegExp rx(regexp);
866         QStringList entries;
867         foreach(const QString & path, result) {
868             if (rx.exactMatch(path)) entries << path;
869         }
870         result = entries;
871     }
872
873     m_count = result.count();
874     m_view.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(m_count > 0);
875     if (m_count == 0) {
876         // no images, do not accept that
877         m_view.slide_info->setText(i18n("No image found"));
878         m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
879         return;
880     }
881     m_view.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
882     m_view.slide_info->setText(i18np("1 image found", "%1 images found", m_count));
883     QDomElement xml = m_clip->toXML();
884     xml.setAttribute("resource", m_view.clip_path->text() + extension);
885     int width = 180.0 * KdenliveSettings::project_display_ratio();
886     if (width % 2 == 1) width++;
887     QString filePath = m_view.clip_path->text();
888     if (isMime) filePath.append(extension);
889     QPixmap pix = m_clip->thumbProducer()->getImage(KUrl(filePath), 1, width, 180);
890     QMap <QString, QString> props = m_clip->properties();
891     m_view.clip_duration->setText(m_tc.getTimecodeFromFrames(props.value("ttl").toInt() * m_count));
892     m_view.clip_thumb->setPixmap(pix);
893 }
894
895 void ClipProperties::slotCheckMaxLength()
896 {
897     if (m_clip->maxDuration() == GenTime()) return;
898     int duration = m_tc.getFrameCount(m_view.clip_duration->text());
899     if (duration > m_clip->maxDuration().frames(m_fps)) {
900         m_view.clip_duration->setText(m_tc.getTimecode(m_clip->maxDuration()));
901     }
902 }
903
904 void ClipProperties::slotUpdateDurationFormat(int ix)
905 {
906     bool framesFormat = ix == 1;
907     if (framesFormat) {
908         // switching to frames count, update widget
909         m_view.slide_duration_frames->setValue(m_tc.getFrameCount(m_view.slide_duration->text()));
910         m_view.luma_duration_frames->setValue(m_tc.getFrameCount(m_view.luma_duration->text()));
911         m_view.slide_duration->setHidden(true);
912         m_view.luma_duration->setHidden(true);
913         m_view.slide_duration_frames->setHidden(false);
914         m_view.luma_duration_frames->setHidden(false);
915     } else {
916         // switching to timecode format
917         m_view.slide_duration->setText(m_tc.getTimecodeFromFrames(m_view.slide_duration_frames->value()));
918         m_view.luma_duration->setText(m_tc.getTimecodeFromFrames(m_view.luma_duration_frames->value()));
919         m_view.slide_duration_frames->setHidden(true);
920         m_view.luma_duration_frames->setHidden(true);
921         m_view.slide_duration->setHidden(false);
922         m_view.luma_duration->setHidden(false);
923     }
924 }
925
926 #include "clipproperties.moc"
927
928