]> git.sesse.net Git - kdenlive/blob - src/renderwidget.cpp
Open website upload page after rendering from a website profile if user wants it
[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 #include <QItemDelegate>
23 #include <QTreeWidgetItem>
24 #include <QHeaderView>
25
26 #include <KStandardDirs>
27 #include <KDebug>
28 #include <KMessageBox>
29 #include <KComboBox>
30 #include <KRun>
31
32 #include "kdenlivesettings.h"
33 #include "renderwidget.h"
34 #include "ui_saveprofile_ui.h"
35
36 const int GroupRole = Qt::UserRole;
37 const int ExtensionRole = GroupRole + 1;
38 const int StandardRole = GroupRole + 2;
39 const int RenderRole = GroupRole + 3;
40 const int ParamsRole = GroupRole + 4;
41 const int EditableRole = GroupRole + 5;
42 const int MetaGroupRole = GroupRole + 6;
43 const int ExtraRole = GroupRole + 7;
44
45 RenderWidget::RenderWidget(QWidget * parent): QDialog(parent) {
46     m_view.setupUi(this);
47     setWindowTitle(i18n("Rendering"));
48     m_view.buttonDelete->setIcon(KIcon("trash-empty"));
49     m_view.buttonDelete->setToolTip(i18n("Delete profile"));
50     m_view.buttonDelete->setEnabled(false);
51
52     m_view.buttonEdit->setIcon(KIcon("document-properties"));
53     m_view.buttonEdit->setToolTip(i18n("Edit profile"));
54     m_view.buttonEdit->setEnabled(false);
55
56     m_view.buttonSave->setIcon(KIcon("document-new"));
57     m_view.buttonSave->setToolTip(i18n("Create new profile"));
58
59     m_view.buttonInfo->setIcon(KIcon("help-about"));
60
61     if (KdenliveSettings::showrenderparams()) {
62         m_view.buttonInfo->setDown(true);
63     } else m_view.advanced_params->hide();
64
65     parseProfiles();
66
67     connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
68
69     connect(m_view.buttonSave, SIGNAL(clicked()), this, SLOT(slotSaveProfile()));
70     connect(m_view.buttonEdit, SIGNAL(clicked()), this, SLOT(slotEditProfile()));
71     connect(m_view.buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
72     connect(m_view.buttonStart, SIGNAL(clicked()), this, SLOT(slotExport()));
73     connect(m_view.abort_job, SIGNAL(clicked()), this, SLOT(slotAbortCurrentJob()));
74     connect(m_view.buttonClose, SIGNAL(clicked()), this, SLOT(hide()));
75     connect(m_view.buttonClose2, SIGNAL(clicked()), this, SLOT(hide()));
76     connect(m_view.rescale, SIGNAL(toggled(bool)), m_view.rescale_size, SLOT(setEnabled(bool)));
77     connect(m_view.destination_list, SIGNAL(activated(int)), this, SLOT(refreshView()));
78     connect(m_view.out_file, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButtons()));
79     connect(m_view.out_file, SIGNAL(urlSelected(const KUrl &)), this, SLOT(slotUpdateButtons(const KUrl &)));
80     connect(m_view.format_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshView()));
81     connect(m_view.size_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshParams()));
82
83     connect(m_view.render_guide, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
84     connect(m_view.render_zone, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
85     connect(m_view.render_full, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
86
87     connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
88     connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));
89
90     connect(m_view.format_selection, SIGNAL(activated(int)), this, SLOT(refreshView()));
91
92     m_view.buttonStart->setEnabled(false);
93     m_view.rescale_size->setEnabled(false);
94     m_view.guides_box->setVisible(false);
95     m_view.open_dvd->setVisible(false);
96     m_view.open_browser->setVisible(false);
97     m_view.error_box->setVisible(false);
98
99     m_view.splitter->setStretchFactor(1, 5);
100     m_view.splitter->setStretchFactor(0, 2);
101
102     m_view.out_file->setMode(KFile::File);
103
104     m_view.running_jobs->setItemDelegate(new RenderViewDelegate(this));
105     QHeaderView *header = m_view.running_jobs->header();
106     QFontMetrics fm = fontMetrics();
107     //header->resizeSection(0, fm.width("typical-name-for-a-torrent.torrent"));
108     header->setResizeMode(0, QHeaderView::Interactive);
109     header->resizeSection(0, fm.width("typical-name-for-a-file.torrent"));
110     header->setResizeMode(1, QHeaderView::Fixed);
111     header->resizeSection(0, width() * 2 / 3);
112     header->setResizeMode(1, QHeaderView::Interactive);
113     //header->setResizeMode(1, QHeaderView::Fixed);
114
115     focusFirstVisibleItem();
116 }
117
118 void RenderWidget::showInfoPanel() {
119     if (m_view.advanced_params->isVisible()) {
120         m_view.advanced_params->setVisible(false);
121         m_view.buttonInfo->setDown(false);
122         KdenliveSettings::setShowrenderparams(false);
123     } else {
124         m_view.advanced_params->setVisible(true);
125         m_view.buttonInfo->setDown(true);
126         KdenliveSettings::setShowrenderparams(true);
127     }
128 }
129
130 void RenderWidget::slotUpdateGuideBox() {
131     m_view.guides_box->setVisible(m_view.render_guide->isChecked());
132 }
133
134 void RenderWidget::slotCheckStartGuidePosition() {
135     if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex())
136         m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
137 }
138
139 void RenderWidget::slotCheckEndGuidePosition() {
140     if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex())
141         m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
142 }
143
144 void RenderWidget::setGuides(QDomElement guidesxml, double duration) {
145     m_view.guide_start->clear();
146     m_view.guide_end->clear();
147     QDomNodeList nodes = guidesxml.elementsByTagName("guide");
148     if (nodes.count() > 0) {
149         m_view.guide_start->addItem(i18n("Render"), "0");
150         m_view.render_guide->setEnabled(true);
151     } else m_view.render_guide->setEnabled(false);
152     for (int i = 0; i < nodes.count(); i++) {
153         QDomElement e = nodes.item(i).toElement();
154         if (!e.isNull()) {
155             m_view.guide_start->addItem(e.attribute("comment"), e.attribute("time").toDouble());
156             m_view.guide_end->addItem(e.attribute("comment"), e.attribute("time").toDouble());
157         }
158     }
159     if (nodes.count() > 0)
160         m_view.guide_end->addItem(i18n("End"), QString::number(duration));
161 }
162
163 // Will be called when the user selects an output file via the file dialog.
164 // File extension will be added automatically.
165 void RenderWidget::slotUpdateButtons(KUrl url) {
166     if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
167     else m_view.buttonStart->setEnabled(true);
168     if (url != 0) {
169         QListWidgetItem *item = m_view.size_list->currentItem();
170         QString extension = item->data(ExtensionRole).toString();
171         url = filenameWithExtension(url, extension);
172         m_view.out_file->setUrl(url);
173     }
174 }
175
176 // Will be called when the user changes the output file path in the text line.
177 // File extension must NOT be added, would make editing impossible!
178 void RenderWidget::slotUpdateButtons() {
179     if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
180     else m_view.buttonStart->setEnabled(true);
181 }
182
183 void RenderWidget::slotSaveProfile() {
184     Ui::SaveProfile_UI ui;
185     QDialog *d = new QDialog(this);
186     ui.setupUi(d);
187     QString customGroup = i18n("Custom");
188     QStringList groupNames;
189     for (int i = 0; i < m_view.format_list->count(); i++)
190         groupNames.append(m_view.format_list->item(i)->text());
191     if (!groupNames.contains(customGroup)) groupNames.prepend(customGroup);
192     ui.group_name->addItems(groupNames);
193     int pos = ui.group_name->findText(customGroup);
194     ui.group_name->setCurrentIndex(pos);
195
196     ui.parameters->setText(m_view.advanced_params->toPlainText());
197     ui.extension->setText(m_view.size_list->currentItem()->data(ExtensionRole).toString());
198     ui.profile_name->setFocus();
199     if (d->exec() == QDialog::Accepted && !ui.profile_name->text().simplified().isEmpty()) {
200         QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
201         QDomDocument doc;
202         QFile file(exportFile);
203         doc.setContent(&file, false);
204         file.close();
205
206         QDomElement documentElement;
207         bool groupExists = false;
208         QString groupName;
209         QString newProfileName = ui.profile_name->text().simplified();
210         QString newGroupName = ui.group_name->currentText();
211         QDomNodeList groups = doc.elementsByTagName("group");
212         int i = 0;
213         if (groups.count() == 0) {
214             QDomElement profiles = doc.createElement("profiles");
215             doc.appendChild(profiles);
216         } else while (!groups.item(i).isNull()) {
217                 documentElement = groups.item(i).toElement();
218                 groupName = documentElement.attribute("name");
219                 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
220                 if (groupName == newGroupName) {
221                     groupExists = true;
222                     break;
223                 }
224                 i++;
225             }
226         if (!groupExists) {
227             documentElement = doc.createElement("group");
228             documentElement.setAttribute("name", ui.group_name->currentText());
229             documentElement.setAttribute("renderer", "avformat");
230             doc.documentElement().appendChild(documentElement);
231         }
232         QDomElement profileElement = doc.createElement("profile");
233         profileElement.setAttribute("name", newProfileName);
234         profileElement.setAttribute("extension", ui.extension->text().simplified());
235         profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
236         documentElement.appendChild(profileElement);
237
238         //QCString save = doc.toString().utf8();
239
240         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
241             KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
242             delete d;
243             return;
244         }
245         QTextStream out(&file);
246         out << doc.toString();
247         file.close();
248         parseProfiles(newGroupName, newProfileName);
249     }
250     delete d;
251 }
252
253 void RenderWidget::slotEditProfile() {
254     QListWidgetItem *item = m_view.size_list->currentItem();
255     if (!item) return;
256     QString currentGroup = m_view.format_list->currentItem()->text();
257
258     QString params = item->data(ParamsRole).toString();
259     QString extension = item->data(ExtensionRole).toString();
260     QString currentProfile = item->text();
261
262     Ui::SaveProfile_UI ui;
263     QDialog *d = new QDialog(this);
264     ui.setupUi(d);
265     QStringList groupNames;
266     for (int i = 0; i < m_view.format_list->count(); i++)
267         groupNames.append(m_view.format_list->item(i)->text());
268     if (!groupNames.contains(currentGroup)) groupNames.prepend(currentGroup);
269     ui.group_name->addItems(groupNames);
270     int pos = ui.group_name->findText(currentGroup);
271     ui.group_name->setCurrentIndex(pos);
272     ui.profile_name->setText(currentProfile);
273     ui.extension->setText(extension);
274     ui.parameters->setText(params);
275     ui.profile_name->setFocus();
276
277     if (d->exec() == QDialog::Accepted) {
278         slotDeleteProfile();
279         QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
280         QDomDocument doc;
281         QFile file(exportFile);
282         doc.setContent(&file, false);
283         file.close();
284
285         QDomElement documentElement;
286         bool groupExists = false;
287         QString groupName;
288         QString newProfileName = ui.profile_name->text();
289         QString newGroupName = ui.group_name->currentText();
290         QDomNodeList groups = doc.elementsByTagName("group");
291         int i = 0;
292         if (groups.count() == 0) {
293             QDomElement profiles = doc.createElement("profiles");
294             doc.appendChild(profiles);
295         } else while (!groups.item(i).isNull()) {
296                 documentElement = groups.item(i).toElement();
297                 groupName = documentElement.attribute("name");
298                 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
299                 if (groupName == newGroupName) {
300                     groupExists = true;
301                     break;
302                 }
303                 i++;
304             }
305         if (!groupExists) {
306             documentElement = doc.createElement("group");
307             documentElement.setAttribute("name", ui.group_name->currentText());
308             documentElement.setAttribute("renderer", "avformat");
309             doc.documentElement().appendChild(documentElement);
310         }
311         QDomElement profileElement = doc.createElement("profile");
312         profileElement.setAttribute("name", newProfileName);
313         profileElement.setAttribute("extension", ui.extension->text().simplified());
314         profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
315         documentElement.appendChild(profileElement);
316
317         //QCString save = doc.toString().utf8();
318
319         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
320             KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
321             delete d;
322             return;
323         }
324         QTextStream out(&file);
325         out << doc.toString();
326         file.close();
327         parseProfiles(newGroupName, newProfileName);
328     }
329     delete d;
330 }
331
332 void RenderWidget::slotDeleteProfile() {
333     QString currentGroup = m_view.format_list->currentItem()->text();
334     QString currentProfile = m_view.size_list->currentItem()->text();
335
336     QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
337     QDomDocument doc;
338     QFile file(exportFile);
339     doc.setContent(&file, false);
340     file.close();
341
342     QDomElement documentElement;
343     bool groupExists = false;
344     QString groupName;
345     QDomNodeList groups = doc.elementsByTagName("group");
346     int i = 0;
347
348     while (!groups.item(i).isNull()) {
349         documentElement = groups.item(i).toElement();
350         groupName = documentElement.attribute("name");
351         if (groupName == currentGroup) {
352             QDomNodeList children = documentElement.childNodes();
353             for (int j = 0; j < children.count(); j++) {
354                 QDomElement pro = children.at(j).toElement();
355                 if (pro.attribute("name") == currentProfile) {
356                     groups.item(i).removeChild(children.at(j));
357                     if (groups.item(i).childNodes().isEmpty())
358                         doc.documentElement().removeChild(groups.item(i));
359                     break;
360                 }
361             }
362             break;
363         }
364         i++;
365     }
366
367     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
368         KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
369         return;
370     }
371     QTextStream out(&file);
372     out << doc.toString();
373     file.close();
374     parseProfiles(currentGroup);
375     focusFirstVisibleItem();
376 }
377
378 void RenderWidget::updateButtons() {
379     if (!m_view.size_list->currentItem() || m_view.size_list->currentItem()->isHidden()) {
380         m_view.buttonSave->setEnabled(false);
381         m_view.buttonDelete->setEnabled(false);
382         m_view.buttonEdit->setEnabled(false);
383     } else {
384         m_view.buttonSave->setEnabled(true);
385         if (m_view.size_list->currentItem()->data(EditableRole).toString().isEmpty()) {
386             m_view.buttonDelete->setEnabled(false);
387             m_view.buttonEdit->setEnabled(false);
388         } else {
389             m_view.buttonDelete->setEnabled(true);
390             m_view.buttonEdit->setEnabled(true);
391         }
392     }
393 }
394
395
396 void RenderWidget::focusFirstVisibleItem() {
397     if (m_view.size_list->currentItem() && !m_view.size_list->currentItem()->isHidden()) {
398         updateButtons();
399         return;
400     }
401     for (uint ix = 0; ix < m_view.size_list->count(); ix++) {
402         QListWidgetItem *item = m_view.size_list->item(ix);
403         if (item && !item->isHidden()) {
404             m_view.size_list->setCurrentRow(ix);
405             break;
406         }
407     }
408     if (!m_view.size_list->currentItem()) m_view.size_list->setCurrentRow(0);
409     updateButtons();
410 }
411
412 void RenderWidget::slotExport() {
413     QListWidgetItem *item = m_view.size_list->currentItem();
414     if (!item) return;
415     const QString dest = m_view.out_file->url().path();
416     if (dest.isEmpty()) return;
417     QFile f(dest);
418     if (f.exists()) {
419         if (KMessageBox::warningYesNo(this, i18n("File already exists. Do you want to overwrite it ?")) != KMessageBox::Yes)
420             return;
421     }
422     QStringList overlayargs;
423     if (m_view.tc_overlay->isChecked()) {
424         QString filterFile = KStandardDirs::locate("appdata", "metadata.properties");
425         overlayargs << "meta.attr.timecode=1" << "meta.attr.timecode.markup=#timecode";
426         overlayargs << "-attach" << "data_feed:attr_check" << "-attach";
427         overlayargs << "data_show:" + filterFile << "_fezzik=1" << "dynamic=1";
428     }
429     double startPos = -1;
430     double endPos = -1;
431     if (m_view.render_guide->isChecked()) {
432         startPos = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
433         endPos = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
434     }
435     QString renderArgs = m_view.advanced_params->toPlainText();
436
437     // Adjust frame scale
438     int width;
439     int height;
440     if (m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
441         width = m_view.rescale_size->text().section('x', 0, 0).toInt();
442         height = m_view.rescale_size->text().section('x', 1, 1).toInt();
443     } else {
444         width = m_profile.width;
445         height = m_profile.height;
446     }
447     renderArgs.replace("%dar", "@" + QString::number(m_profile.display_aspect_num) + "/" + QString::number(m_profile.display_aspect_den));
448
449     // Adjust scanning
450     if (m_view.scanning_list->currentIndex() == 1) renderArgs.append(" progressive=1");
451     else if (m_view.scanning_list->currentIndex() == 2) renderArgs.append(" progressive=0");
452
453     // disable audio if requested
454     if (!m_view.export_audio->isChecked())
455         renderArgs.append(" an=1 ");
456
457     // Check if the rendering profile is different from project profile,
458     // in which case we need to use the producer_comsumer from MLT
459     bool resizeProfile = false;
460
461     QString std = renderArgs;
462     QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
463     if (std.contains(" s=")) {
464         QString subsize = std.section(" s=", 1, 1);
465         subsize = subsize.section(' ', 0, 0).toLower();
466         const QString currentSize = QString::number(width) + 'x' + QString::number(height);
467         if (subsize != currentSize) resizeProfile = true;
468     } else if (destination != "audioonly") {
469         // Add current site parametrer
470         renderArgs.append(QString(" s=%1x%2").arg(width).arg(height));
471     }
472
473     // insert item in running jobs list
474     QTreeWidgetItem *renderItem;
475     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly);
476     if (!existing.isEmpty()) renderItem = existing.at(0);
477     else renderItem = new QTreeWidgetItem(m_view.running_jobs, QStringList() << dest << QString());
478     // Set rendering type
479     QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString();
480     if (group == "dvd" && m_view.open_dvd->isChecked()) {
481         renderItem->setData(0, Qt::UserRole, group);
482         if (renderArgs.contains("profile=")) {
483             // rendering profile contains an MLT profile, so pass it to the running jog item, usefull for dvd
484             QString prof = renderArgs.section("profile=", 1, 1);
485             prof = prof.section(' ', 0, 0);
486             kDebug() << "// render profile: " << prof;
487             renderItem->setData(0, Qt::UserRole + 1, prof);
488         }
489     } else if (group == "websites" && m_view.open_browser->isChecked()) {
490         renderItem->setData(0, Qt::UserRole, group);
491         // pass the url
492         QString url = m_view.size_list->currentItem()->data(ExtraRole).toString();
493         renderItem->setData(0, Qt::UserRole + 1, url);
494     }
495
496     emit doRender(dest, item->data(RenderRole).toString(), overlayargs, renderArgs.simplified().split(' '), m_view.render_zone->isChecked(), m_view.play_after->isChecked(), startPos, endPos, resizeProfile);
497     m_view.tabWidget->setCurrentIndex(1);
498 }
499
500 void RenderWidget::setProfile(MltVideoProfile profile) {
501     m_profile = profile;
502     //WARNING: this way to tell the video standard is a bit hackish...
503     if (m_profile.description.contains("pal", Qt::CaseInsensitive) || m_profile.description.contains("25", Qt::CaseInsensitive) || m_profile.description.contains("50", Qt::CaseInsensitive)) m_view.format_selection->setCurrentIndex(0);
504     else m_view.format_selection->setCurrentIndex(1);
505     m_view.scanning_list->setCurrentIndex(0);
506     refreshView();
507 }
508
509 void RenderWidget::refreshView() {
510     m_view.size_list->blockSignals(true);
511     QListWidgetItem *sizeItem;
512
513     QString destination;
514     if (m_view.destination_list->currentIndex() > 0)
515         destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
516
517     if (destination == "dvd") m_view.open_dvd->setVisible(true);
518     else m_view.open_dvd->setVisible(false);
519     if (destination == "websites") m_view.open_browser->setVisible(true);
520     else m_view.open_browser->setVisible(false);
521     if (!destination.isEmpty() && QString("dvd websites audioonly").contains(destination))
522         m_view.rescale->setEnabled(false);
523     else m_view.rescale->setEnabled(true);
524     // hide groups that are not in the correct destination
525     for (int i = 0; i < m_view.format_list->count(); i++) {
526         sizeItem = m_view.format_list->item(i);
527         if (sizeItem->data(MetaGroupRole).toString() == destination)
528             sizeItem->setHidden(false);
529         else sizeItem->setHidden(true);
530     }
531
532     // activate first visible item
533     QListWidgetItem * item = m_view.format_list->currentItem();
534     if (!item || item->isHidden()) {
535         for (int i = 0; i < m_view.format_list->count(); i++) {
536             if (!m_view.format_list->item(i)->isHidden()) {
537                 m_view.format_list->setCurrentRow(i);
538                 break;
539             }
540         }
541         item = m_view.format_list->currentItem();
542     }
543     if (!item) return;
544     int count = 0;
545     for (int i = 0; i < m_view.format_list->count() && count < 2; i++) {
546         if (!m_view.format_list->isRowHidden(i)) count++;
547     }
548     if (count > 1) m_view.format_list->setVisible(true);
549     else m_view.format_list->setVisible(false);
550     QString std;
551     QString group = item->text();
552     bool firstSelected = false;
553     const QStringList formatsList = KdenliveSettings::supportedformats();
554     const QStringList vcodecsList = KdenliveSettings::videocodecs();
555     const QStringList acodecsList = KdenliveSettings::audiocodecs();
556
557     for (int i = 0; i < m_view.size_list->count(); i++) {
558         sizeItem = m_view.size_list->item(i);
559         if (sizeItem->data(GroupRole) == group) {
560             std = sizeItem->data(StandardRole).toString();
561             if (!std.isEmpty()) {
562                 if (std.contains("PAL", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 0);
563                 else if (std.contains("NTSC", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 1);
564             } else {
565                 sizeItem->setHidden(false);
566                 if (!firstSelected) m_view.size_list->setCurrentItem(sizeItem);
567                 firstSelected = true;
568             }
569
570             if (!sizeItem->isHidden()) {
571                 // Make sure the selected profile uses an installed avformat codec / format
572                 std = sizeItem->data(ParamsRole).toString();
573
574                 if (!formatsList.isEmpty()) {
575                     QString format;
576                     if (std.startsWith("f=")) format = std.section("f=", 1, 1);
577                     else if (std.contains(" f=")) format = std.section(" f=", 1, 1);
578                     if (!format.isEmpty()) {
579                         format = format.section(' ', 0, 0).toLower();
580                         if (!formatsList.contains(format)) {
581                             kDebug() << "*****  UNSUPPORTED F: " << format;
582                             sizeItem->setHidden(true);
583                         }
584                     }
585                 }
586                 if (!acodecsList.isEmpty() && !sizeItem->isHidden()) {
587                     QString format;
588                     if (std.startsWith("acodec=")) format = std.section("acodec=", 1, 1);
589                     else if (std.contains(" acodec=")) format = std.section(" acodec=", 1, 1);
590                     if (!format.isEmpty()) {
591                         format = format.section(' ', 0, 0).toLower();
592                         if (!acodecsList.contains(format)) {
593                             kDebug() << "*****  UNSUPPORTED ACODEC: " << format;
594                             sizeItem->setHidden(true);
595                         }
596                     }
597                 }
598                 if (!vcodecsList.isEmpty() && !sizeItem->isHidden()) {
599                     QString format;
600                     if (std.startsWith("vcodec=")) format = std.section("vcodec=", 1, 1);
601                     else if (std.contains(" vcodec=")) format = std.section(" vcodec=", 1, 1);
602                     if (!format.isEmpty()) {
603                         format = format.section(' ', 0, 0).toLower();
604                         if (!vcodecsList.contains(format)) {
605                             kDebug() << "*****  UNSUPPORTED VCODEC: " << format;
606                             sizeItem->setHidden(true);
607                         }
608                     }
609                 }
610             }
611         } else sizeItem->setHidden(true);
612     }
613     focusFirstVisibleItem();
614     m_view.size_list->blockSignals(false);
615     refreshParams();
616 }
617
618 KUrl RenderWidget::filenameWithExtension(KUrl url, QString extension) {
619     QString path;
620     if (!url.isEmpty()) {
621         path = url.path();
622         int pos = path.lastIndexOf('.') + 1;
623         if (pos == 0) path.append('.' + extension);
624         else path = path.left(pos) + extension;
625
626     } else {
627         path = QDir::homePath() + "/untitled." + extension;
628     }
629     return KUrl(path);
630 }
631
632
633 void RenderWidget::refreshParams() {
634     QListWidgetItem *item = m_view.size_list->currentItem();
635     if (!item || item->isHidden()) {
636         m_view.advanced_params->clear();
637         m_view.buttonStart->setEnabled(false);
638         return;
639     }
640     QString params = item->data(ParamsRole).toString();
641     QString extension = item->data(ExtensionRole).toString();
642     m_view.advanced_params->setPlainText(params);
643     QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
644     if (params.contains(" s=") || destination == "audioonly") {
645         // profile has a fixed size, do not allow resize
646         m_view.rescale->setEnabled(false);
647         m_view.rescale_size->setEnabled(false);
648     } else {
649         m_view.rescale->setEnabled(true);
650         m_view.rescale_size->setEnabled(true);
651     }
652     KUrl url = filenameWithExtension(m_view.out_file->url(), extension);
653     m_view.out_file->setUrl(url);
654 //     if (!url.isEmpty()) {
655 //         QString path = url.path();
656 //         int pos = path.lastIndexOf('.') + 1;
657 //  if (pos == 0) path.append('.' + extension);
658 //         else path = path.left(pos) + extension;
659 //         m_view.out_file->setUrl(KUrl(path));
660 //     } else {
661 //         m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
662 //     }
663     m_view.out_file->setFilter("*." + extension);
664
665     if (item->data(EditableRole).toString().isEmpty()) {
666         m_view.buttonDelete->setEnabled(false);
667         m_view.buttonEdit->setEnabled(false);
668     } else {
669         m_view.buttonDelete->setEnabled(true);
670         m_view.buttonEdit->setEnabled(true);
671     }
672     m_view.buttonStart->setEnabled(true);
673 }
674
675 void RenderWidget::parseProfiles(QString group, QString profile) {
676     m_view.size_list->clear();
677     m_view.format_list->clear();
678     m_view.destination_list->clear();
679     m_view.destination_list->addItem(KIcon("video-x-generic"), i18n("File rendering"));
680     QString exportFile = KStandardDirs::locate("appdata", "export/profiles.xml");
681     parseFile(exportFile, false);
682     exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
683     if (QFile::exists(exportFile)) parseFile(exportFile, true);
684     refreshView();
685     QList<QListWidgetItem *> child;
686     child = m_view.format_list->findItems(group, Qt::MatchExactly);
687     if (!child.isEmpty()) m_view.format_list->setCurrentItem(child.at(0));
688     child = m_view.size_list->findItems(profile, Qt::MatchExactly);
689     if (!child.isEmpty()) m_view.size_list->setCurrentItem(child.at(0));
690 }
691
692 void RenderWidget::parseFile(QString exportFile, bool editable) {
693     QDomDocument doc;
694     QFile file(exportFile);
695     doc.setContent(&file, false);
696     file.close();
697     QDomElement documentElement;
698     QDomElement profileElement;
699     QDomNodeList groups = doc.elementsByTagName("group");
700
701     if (groups.count() == 0) {
702         kDebug() << "// Export file: " << exportFile << " IS BROKEN";
703         return;
704     }
705
706     int i = 0;
707     QString groupName;
708     QString profileName;
709     QString extension;
710     QString prof_extension;
711     QString renderer;
712     QString params;
713     QString standard;
714     KIcon icon;
715     QListWidgetItem *item;
716     while (!groups.item(i).isNull()) {
717         documentElement = groups.item(i).toElement();
718         QDomNode gname = documentElement.elementsByTagName("groupname").at(0);
719         QString metagroupName;
720         QString metagroupId;
721         if (!gname.isNull()) {
722             metagroupName = gname.firstChild().nodeValue();
723             metagroupId = gname.toElement().attribute("id");
724             if (!metagroupName.isEmpty() && !m_view.destination_list->contains(metagroupName)) {
725                 if (metagroupId == "dvd") icon = KIcon("media-optical");
726                 else if (metagroupId == "audioonly") icon = KIcon("audio-x-generic");
727                 else if (metagroupId == "websites") icon = KIcon("applications-internet");
728                 else if (metagroupId == "mediaplayers") icon = KIcon("applications-multimedia");
729                 else if (metagroupId == "lossless") icon = KIcon("drive-harddisk");
730                 m_view.destination_list->addItem(icon, i18n(metagroupName.toUtf8().data()), metagroupId);
731             }
732         }
733         groupName = documentElement.attribute("name", QString::null);
734         extension = documentElement.attribute("extension", QString::null);
735         renderer = documentElement.attribute("renderer", QString::null);
736         if (m_view.format_list->findItems(groupName, Qt::MatchExactly).isEmpty()) {
737             item = new QListWidgetItem(groupName, m_view.format_list);
738             item->setData(MetaGroupRole, metagroupId);
739         }
740
741         QDomNode n = groups.item(i).firstChild();
742         while (!n.isNull()) {
743             if (n.toElement().tagName() != "profile") {
744                 n = n.nextSibling();
745                 continue;
746             }
747             profileElement = n.toElement();
748             profileName = profileElement.attribute("name");
749             standard = profileElement.attribute("standard");
750             params = profileElement.attribute("args");
751             prof_extension = profileElement.attribute("extension");
752             if (!prof_extension.isEmpty()) extension = prof_extension;
753             item = new QListWidgetItem(profileName, m_view.size_list);
754             item->setData(GroupRole, groupName);
755             item->setData(MetaGroupRole, metagroupId);
756             item->setData(ExtensionRole, extension);
757             item->setData(RenderRole, renderer);
758             item->setData(StandardRole, standard);
759             item->setData(ParamsRole, params);
760             if (profileElement.hasAttribute("url")) item->setData(ExtraRole, profileElement.attribute("url"));
761             if (editable) item->setData(EditableRole, "true");
762             n = n.nextSibling();
763         }
764
765         i++;
766     }
767 }
768
769 void RenderWidget::setRenderJob(const QString &dest, int progress) {
770     QTreeWidgetItem *item;
771     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly);
772     if (!existing.isEmpty()) item = existing.at(0);
773     else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << dest << QString());
774     item->setData(1, Qt::UserRole, progress);
775     if (progress == 0) item->setIcon(0, KIcon("system-run"));
776 }
777
778 void RenderWidget::setRenderStatus(const QString &dest, int status, const QString &error) {
779     QTreeWidgetItem *item;
780     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly);
781     if (!existing.isEmpty()) item = existing.at(0);
782     else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << dest << QString());
783     if (status == -1) {
784         // Job finished successfully
785         item->setIcon(0, KIcon("dialog-ok"));
786         item->setData(1, Qt::UserRole, 100);
787         QString itemGroup = item->data(0, Qt::UserRole).toString();
788         if (itemGroup == "dvd") {
789             emit openDvdWizard(item->text(0), item->data(0, Qt::UserRole + 1).toString());
790         } else if (itemGroup == "websites") {
791             QString url = item->data(0, Qt::UserRole + 1).toString();
792             if (!url.isEmpty()) KRun *openBrowser = new KRun(url, this);
793         }
794     } else if (status == -2) {
795         // Rendering crashed
796         item->setIcon(0, KIcon("dialog-close"));
797         item->setData(1, Qt::UserRole, 0);
798         m_view.error_log->append(i18n("<strong>Rendering of %1 crashed</strong><br />", dest));
799         m_view.error_log->append(error);
800         m_view.error_log->append("<hr />");
801         m_view.error_box->setVisible(true);
802     } else if (status == -3) {
803         // User aborted job
804         item->setIcon(0, KIcon("dialog-cancel"));
805         item->setData(1, Qt::UserRole, 100);
806         item->setData(1, Qt::UserRole + 1, i18n("Aborted by user"));
807     }
808 }
809
810 void RenderWidget::slotAbortCurrentJob() {
811     QTreeWidgetItem *current = m_view.running_jobs->currentItem();
812     if (current) emit abortProcess(current->text(0));
813 }
814
815 #include "renderwidget.moc"