]> git.sesse.net Git - kdenlive/blob - src/renderwidget.cpp
Allow user to select between PAL and NTSC profiles for export, fixes:
[kdenlive] / src / renderwidget.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 <QDomDocument>
22
23 #include <KStandardDirs>
24 #include <KDebug>
25 #include <KMessageBox>
26 #include <KComboBox>
27
28 #include "kdenlivesettings.h"
29 #include "renderwidget.h"
30 #include "ui_saveprofile_ui.h"
31
32 const int GroupRole = Qt::UserRole;
33 const int ExtensionRole = GroupRole + 1;
34 const int StandardRole = GroupRole + 2;
35 const int RenderRole = GroupRole + 3;
36 const int ParamsRole = GroupRole + 4;
37 const int EditableRole = GroupRole + 5;
38
39 RenderWidget::RenderWidget(QWidget * parent): QDialog(parent) {
40     m_view.setupUi(this);
41     m_view.buttonDelete->setIcon(KIcon("trash-empty"));
42     m_view.buttonDelete->setToolTip(i18n("Delete profile"));
43     m_view.buttonDelete->setEnabled(false);
44
45     m_view.buttonEdit->setIcon(KIcon("document-properties"));
46     m_view.buttonEdit->setToolTip(i18n("Edit profile"));
47     m_view.buttonEdit->setEnabled(false);
48
49     m_view.buttonSave->setIcon(KIcon("document-new"));
50     m_view.buttonSave->setToolTip(i18n("Create new profile"));
51
52     m_view.buttonInfo->setIcon(KIcon("help-about"));
53
54     if (KdenliveSettings::showrenderparams()) {
55         m_view.buttonInfo->setDown(true);
56     } else m_view.advanced_params->hide();
57
58     connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
59
60     connect(m_view.buttonSave, SIGNAL(clicked()), this, SLOT(slotSaveProfile()));
61     connect(m_view.buttonEdit, SIGNAL(clicked()), this, SLOT(slotEditProfile()));
62     connect(m_view.buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
63     connect(m_view.buttonStart, SIGNAL(clicked()), this, SLOT(slotExport()));
64     connect(m_view.out_file, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButtons()));
65     connect(m_view.format_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshView()));
66     connect(m_view.size_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshParams()));
67
68     connect(m_view.render_guide, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
69     connect(m_view.render_zone, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
70     connect(m_view.render_full, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
71
72     connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
73     connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));
74
75     connect(m_view.format_selection, SIGNAL(activated(int)), this, SLOT(refreshView()));
76
77     m_view.buttonStart->setEnabled(false);
78     m_view.guides_box->setVisible(false);
79     parseProfiles();
80     m_view.splitter->setStretchFactor(1, 5);
81     m_view.splitter->setStretchFactor(0, 2);
82 }
83
84 void RenderWidget::showInfoPanel() {
85     if (m_view.advanced_params->isVisible()) {
86         m_view.advanced_params->setVisible(false);
87         m_view.buttonInfo->setDown(false);
88         KdenliveSettings::setShowrenderparams(false);
89     } else {
90         m_view.advanced_params->setVisible(true);
91         m_view.buttonInfo->setDown(true);
92         KdenliveSettings::setShowrenderparams(true);
93     }
94 }
95
96 void RenderWidget::slotUpdateGuideBox() {
97     m_view.guides_box->setVisible(m_view.render_guide->isChecked());
98 }
99
100 void RenderWidget::slotCheckStartGuidePosition() {
101     if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex())
102         m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
103 }
104
105 void RenderWidget::slotCheckEndGuidePosition() {
106     if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex())
107         m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
108 }
109
110 void RenderWidget::setGuides(QDomElement guidesxml, double duration) {
111     m_view.guide_start->clear();
112     m_view.guide_end->clear();
113     QDomNodeList nodes = guidesxml.elementsByTagName("guide");
114     if (nodes.count() > 0) {
115         m_view.guide_start->addItem(i18n("Render"), "0");
116         m_view.render_guide->setEnabled(true);
117     } else m_view.render_guide->setEnabled(false);
118     for (int i = 0; i < nodes.count(); i++) {
119         QDomElement e = nodes.item(i).toElement();
120         if (!e.isNull()) {
121             m_view.guide_start->addItem(e.attribute("comment"), e.attribute("time").toDouble());
122             m_view.guide_end->addItem(e.attribute("comment"), e.attribute("time").toDouble());
123         }
124     }
125     if (nodes.count() > 0)
126         m_view.guide_end->addItem(i18n("End"), QString::number(duration));
127 }
128
129 void RenderWidget::slotUpdateButtons() {
130     if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
131     else m_view.buttonStart->setEnabled(true);
132 }
133
134 void RenderWidget::slotSaveProfile() {
135     Ui::SaveProfile_UI ui;
136     QDialog *d = new QDialog(this);
137     ui.setupUi(d);
138     QString customGroup = i18n("Custom");
139     QStringList groupNames;
140     for (int i = 0; i < m_view.format_list->count(); i++)
141         groupNames.append(m_view.format_list->item(i)->text());
142     if (!groupNames.contains(customGroup)) groupNames.prepend(customGroup);
143     ui.group_name->addItems(groupNames);
144     int pos = ui.group_name->findText(customGroup);
145     ui.group_name->setCurrentIndex(pos);
146
147     ui.parameters->setText(m_view.advanced_params->toPlainText());
148     ui.extension->setText(m_view.size_list->currentItem()->data(ExtensionRole).toString());
149     ui.profile_name->setFocus();
150     if (d->exec() == QDialog::Accepted) {
151         QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
152         QDomDocument doc;
153         QFile file(exportFile);
154         doc.setContent(&file, false);
155         file.close();
156
157         QDomElement documentElement;
158         bool groupExists = false;
159         QString groupName;
160         QString newProfileName = ui.profile_name->text();
161         QString newGroupName = ui.group_name->currentText();
162         QDomNodeList groups = doc.elementsByTagName("group");
163         int i = 0;
164         if (groups.count() == 0) {
165             QDomElement profiles = doc.createElement("profiles");
166             doc.appendChild(profiles);
167         } else while (!groups.item(i).isNull()) {
168                 documentElement = groups.item(i).toElement();
169                 groupName = documentElement.attribute("name");
170                 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
171                 if (groupName == newGroupName) {
172                     groupExists = true;
173                     break;
174                 }
175                 i++;
176             }
177         if (!groupExists) {
178             documentElement = doc.createElement("group");
179             documentElement.setAttribute("name", ui.group_name->currentText());
180             documentElement.setAttribute("renderer", "avformat");
181             doc.documentElement().appendChild(documentElement);
182         }
183         QDomElement profileElement = doc.createElement("profile");
184         profileElement.setAttribute("name", newProfileName);
185         profileElement.setAttribute("extension", ui.extension->text().simplified());
186         profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
187         documentElement.appendChild(profileElement);
188
189         //QCString save = doc.toString().utf8();
190
191         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
192             KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
193             delete d;
194             return;
195         }
196         QTextStream out(&file);
197         out << doc.toString();
198         file.close();
199         parseProfiles(newGroupName, newProfileName);
200     }
201     delete d;
202 }
203
204 void RenderWidget::slotEditProfile() {
205     QListWidgetItem *item = m_view.size_list->currentItem();
206     if (!item) return;
207     QString currentGroup = m_view.format_list->currentItem()->text();
208
209     QString params = item->data(ParamsRole).toString();
210     QString extension = item->data(ExtensionRole).toString();
211     QString currentProfile = item->text();
212
213     Ui::SaveProfile_UI ui;
214     QDialog *d = new QDialog(this);
215     ui.setupUi(d);
216     QStringList groupNames;
217     for (int i = 0; i < m_view.format_list->count(); i++)
218         groupNames.append(m_view.format_list->item(i)->text());
219     if (!groupNames.contains(currentGroup)) groupNames.prepend(currentGroup);
220     ui.group_name->addItems(groupNames);
221     int pos = ui.group_name->findText(currentGroup);
222     ui.group_name->setCurrentIndex(pos);
223     ui.profile_name->setText(currentProfile);
224     ui.extension->setText(extension);
225     ui.parameters->setText(params);
226     ui.profile_name->setFocus();
227
228     if (d->exec() == QDialog::Accepted) {
229         slotDeleteProfile();
230         QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
231         QDomDocument doc;
232         QFile file(exportFile);
233         doc.setContent(&file, false);
234         file.close();
235
236         QDomElement documentElement;
237         bool groupExists = false;
238         QString groupName;
239         QString newProfileName = ui.profile_name->text();
240         QString newGroupName = ui.group_name->currentText();
241         QDomNodeList groups = doc.elementsByTagName("group");
242         int i = 0;
243         if (groups.count() == 0) {
244             QDomElement profiles = doc.createElement("profiles");
245             doc.appendChild(profiles);
246         } else while (!groups.item(i).isNull()) {
247                 documentElement = groups.item(i).toElement();
248                 groupName = documentElement.attribute("name");
249                 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
250                 if (groupName == newGroupName) {
251                     groupExists = true;
252                     break;
253                 }
254                 i++;
255             }
256         if (!groupExists) {
257             documentElement = doc.createElement("group");
258             documentElement.setAttribute("name", ui.group_name->currentText());
259             documentElement.setAttribute("renderer", "avformat");
260             doc.documentElement().appendChild(documentElement);
261         }
262         QDomElement profileElement = doc.createElement("profile");
263         profileElement.setAttribute("name", newProfileName);
264         profileElement.setAttribute("extension", ui.extension->text().simplified());
265         profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
266         documentElement.appendChild(profileElement);
267
268         //QCString save = doc.toString().utf8();
269
270         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
271             KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
272             delete d;
273             return;
274         }
275         QTextStream out(&file);
276         out << doc.toString();
277         file.close();
278         parseProfiles(newGroupName, newProfileName);
279     }
280     delete d;
281 }
282
283 void RenderWidget::slotDeleteProfile() {
284     QString currentGroup = m_view.format_list->currentItem()->text();
285     QString currentProfile = m_view.size_list->currentItem()->text();
286
287     QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
288     QDomDocument doc;
289     QFile file(exportFile);
290     doc.setContent(&file, false);
291     file.close();
292
293     QDomElement documentElement;
294     bool groupExists = false;
295     QString groupName;
296     QDomNodeList groups = doc.elementsByTagName("group");
297     int i = 0;
298
299     while (!groups.item(i).isNull()) {
300         documentElement = groups.item(i).toElement();
301         groupName = documentElement.attribute("name");
302         if (groupName == currentGroup) {
303             QDomNodeList children = documentElement.childNodes();
304             for (int j = 0; j < children.count(); j++) {
305                 QDomElement pro = children.at(j).toElement();
306                 if (pro.attribute("name") == currentProfile) {
307                     groups.item(i).removeChild(children.at(j));
308                     if (groups.item(i).childNodes().isEmpty())
309                         doc.documentElement().removeChild(groups.item(i));
310                     break;
311                 }
312             }
313             break;
314         }
315         i++;
316     }
317
318     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
319         KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
320         return;
321     }
322     QTextStream out(&file);
323     out << doc.toString();
324     file.close();
325     parseProfiles(currentGroup);
326 }
327
328 void RenderWidget::slotExport() {
329     QListWidgetItem *item = m_view.size_list->currentItem();
330     if (!item) return;
331     QFile f(m_view.out_file->url().path());
332     if (f.exists()) {
333         if (KMessageBox::warningYesNo(this, i18n("File already exists. Do you want to overwrite it ?")) != KMessageBox::Yes)
334             return;
335     }
336     QStringList overlayargs;
337     if (m_view.tc_overlay->isChecked()) {
338         QString filterFile = KStandardDirs::locate("appdata", "metadata.properties");
339         overlayargs << "meta.attr.timecode=1" << "meta.attr.timecode.markup=#timecode";
340         overlayargs << "-attach" << "data_feed:attr_check" << "-attach";
341         overlayargs << "data_show:" + filterFile << "_fezzik=1" << "dynamic=1";
342     }
343     double startPos = -1;
344     double endPos = -1;
345     if (m_view.render_guide->isChecked()) {
346         startPos = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
347         endPos = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
348     }
349     emit doRender(m_view.out_file->url().path(), item->data(RenderRole).toString(), overlayargs, m_view.advanced_params->toPlainText().split(' '), m_view.render_zone->isChecked(), m_view.play_after->isChecked(), startPos, endPos);
350 }
351
352 void RenderWidget::setDocumentStandard(QString std) {
353     if (std == "PAL") m_view.format_selection->setCurrentIndex(0);
354     else m_view.format_selection->setCurrentIndex(1);
355
356     refreshView();
357 }
358
359 void RenderWidget::refreshView() {
360     QListWidgetItem *item = m_view.format_list->currentItem();
361     if (!item) {
362         m_view.format_list->setCurrentRow(0);
363         item = m_view.format_list->currentItem();
364     }
365     if (!item) return;
366     QString std;
367     QString group = item->text();
368     QListWidgetItem *sizeItem;
369     bool firstSelected = false;
370     for (int i = 0; i < m_view.size_list->count(); i++) {
371         sizeItem = m_view.size_list->item(i);
372         std = sizeItem->data(StandardRole).toString();
373         if (sizeItem->data(GroupRole) == group) {
374             if (!std.isEmpty()) {
375                 if (std.contains("PAL", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 0);
376                 else if (std.contains("NTSC", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 1);
377             } else {
378                 sizeItem->setHidden(false);
379                 if (!firstSelected) m_view.size_list->setCurrentItem(sizeItem);
380                 firstSelected = true;
381             }
382         } else sizeItem->setHidden(true);
383     }
384
385 }
386
387 void RenderWidget::refreshParams() {
388     QListWidgetItem *item = m_view.size_list->currentItem();
389     if (!item) return;
390     QString params = item->data(ParamsRole).toString();
391     QString extension = item->data(ExtensionRole).toString();
392     m_view.advanced_params->setPlainText(params);
393     m_view.advanced_params->setToolTip(params);
394     KUrl url = m_view.out_file->url();
395     if (!url.isEmpty()) {
396         QString path = url.path();
397         int pos = path.lastIndexOf('.') + 1;
398         if (pos == 0) path.append('.') + extension;
399         else path = path.left(pos) + extension;
400         m_view.out_file->setUrl(KUrl(path));
401     } else {
402         m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
403     }
404
405     if (item->data(EditableRole).toString().isEmpty()) {
406         m_view.buttonDelete->setEnabled(false);
407         m_view.buttonEdit->setEnabled(false);
408     } else {
409         m_view.buttonDelete->setEnabled(true);
410         m_view.buttonEdit->setEnabled(true);
411     }
412 }
413
414 void RenderWidget::parseProfiles(QString group, QString profile) {
415     m_view.size_list->clear();
416     m_view.format_list->clear();
417     QString exportFile = KStandardDirs::locate("appdata", "export/profiles.xml");
418     parseFile(exportFile, false);
419     exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
420     if (QFile::exists(exportFile)) parseFile(exportFile, true);
421     refreshView();
422     QList<QListWidgetItem *> child;
423     child = m_view.format_list->findItems(group, Qt::MatchExactly);
424     if (!child.isEmpty()) m_view.format_list->setCurrentItem(child.at(0));
425     child = m_view.size_list->findItems(profile, Qt::MatchExactly);
426     if (!child.isEmpty()) m_view.size_list->setCurrentItem(child.at(0));
427 }
428
429 void RenderWidget::parseFile(QString exportFile, bool editable) {
430     QDomDocument doc;
431     QFile file(exportFile);
432     doc.setContent(&file, false);
433     file.close();
434     QDomElement documentElement;
435     QDomElement profileElement;
436     QDomNodeList groups = doc.elementsByTagName("group");
437
438     if (groups.count() == 0) {
439         kDebug() << "// Export file: " << exportFile << " IS BROKEN";
440         return;
441     }
442
443     int i = 0;
444     QString groupName;
445     QString profileName;
446     QString extension;
447     QString prof_extension;
448     QString renderer;
449     QString params;
450     QString standard;
451     QListWidgetItem *item;
452     while (!groups.item(i).isNull()) {
453         documentElement = groups.item(i).toElement();
454         groupName = documentElement.attribute("name", QString::null);
455         extension = documentElement.attribute("extension", QString::null);
456         renderer = documentElement.attribute("renderer", QString::null);
457         if (m_view.format_list->findItems(groupName, Qt::MatchExactly).isEmpty())
458             new QListWidgetItem(groupName, m_view.format_list);
459
460         QDomNode n = groups.item(i).firstChild();
461         while (!n.isNull()) {
462             profileElement = n.toElement();
463             profileName = profileElement.attribute("name");
464             standard = profileElement.attribute("standard");
465             params = profileElement.attribute("args");
466             prof_extension = profileElement.attribute("extension");
467             if (!prof_extension.isEmpty()) extension = prof_extension;
468             item = new QListWidgetItem(profileName, m_view.size_list);
469             item->setData(GroupRole, groupName);
470             item->setData(ExtensionRole, extension);
471             item->setData(RenderRole, renderer);
472             item->setData(StandardRole, standard);
473             item->setData(ParamsRole, params);
474             if (editable) item->setData(EditableRole, "true");
475             n = n.nextSibling();
476         }
477
478         i++;
479     }
480 }
481
482
483
484 #include "renderwidget.moc"
485
486