1 /***************************************************************************
2 * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
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. *
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. *
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 ***************************************************************************/
21 #include <QDomDocument>
22 #include <QItemDelegate>
23 #include <QTreeWidgetItem>
24 #include <QHeaderView>
26 #include <KStandardDirs>
28 #include <KMessageBox>
32 #include "kdenlivesettings.h"
33 #include "renderwidget.h"
34 #include "ui_saveprofile_ui.h"
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;
45 RenderWidget::RenderWidget(QWidget * parent): QDialog(parent) {
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);
52 m_view.buttonEdit->setIcon(KIcon("document-properties"));
53 m_view.buttonEdit->setToolTip(i18n("Edit profile"));
54 m_view.buttonEdit->setEnabled(false);
56 m_view.buttonSave->setIcon(KIcon("document-new"));
57 m_view.buttonSave->setToolTip(i18n("Create new profile"));
59 m_view.buttonInfo->setIcon(KIcon("help-about"));
61 if (KdenliveSettings::showrenderparams()) {
62 m_view.buttonInfo->setDown(true);
63 } else m_view.advanced_params->hide();
67 connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
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()));
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()));
87 connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
88 connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));
90 connect(m_view.format_selection, SIGNAL(activated(int)), this, SLOT(refreshView()));
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);
99 m_view.splitter->setStretchFactor(1, 5);
100 m_view.splitter->setStretchFactor(0, 2);
102 m_view.out_file->setMode(KFile::File);
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);
115 focusFirstVisibleItem();
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);
124 m_view.advanced_params->setVisible(true);
125 m_view.buttonInfo->setDown(true);
126 KdenliveSettings::setShowrenderparams(true);
130 void RenderWidget::slotUpdateGuideBox() {
131 m_view.guides_box->setVisible(m_view.render_guide->isChecked());
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());
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());
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();
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());
159 if (nodes.count() > 0)
160 m_view.guide_end->addItem(i18n("End"), QString::number(duration));
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);
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);
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);
183 void RenderWidget::slotSaveProfile() {
184 Ui::SaveProfile_UI ui;
185 QDialog *d = new QDialog(this);
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);
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");
202 QFile file(exportFile);
203 doc.setContent(&file, false);
206 QDomElement documentElement;
207 bool groupExists = false;
209 QString newProfileName = ui.profile_name->text().simplified();
210 QString newGroupName = ui.group_name->currentText();
211 QDomNodeList groups = doc.elementsByTagName("group");
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) {
227 documentElement = doc.createElement("group");
228 documentElement.setAttribute("name", ui.group_name->currentText());
229 documentElement.setAttribute("renderer", "avformat");
230 doc.documentElement().appendChild(documentElement);
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);
238 //QCString save = doc.toString().utf8();
240 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
241 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
245 QTextStream out(&file);
246 out << doc.toString();
248 parseProfiles(newGroupName, newProfileName);
253 void RenderWidget::slotEditProfile() {
254 QListWidgetItem *item = m_view.size_list->currentItem();
256 QString currentGroup = m_view.format_list->currentItem()->text();
258 QString params = item->data(ParamsRole).toString();
259 QString extension = item->data(ExtensionRole).toString();
260 QString currentProfile = item->text();
262 Ui::SaveProfile_UI ui;
263 QDialog *d = new QDialog(this);
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();
277 if (d->exec() == QDialog::Accepted) {
279 QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
281 QFile file(exportFile);
282 doc.setContent(&file, false);
285 QDomElement documentElement;
286 bool groupExists = false;
288 QString newProfileName = ui.profile_name->text();
289 QString newGroupName = ui.group_name->currentText();
290 QDomNodeList groups = doc.elementsByTagName("group");
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) {
306 documentElement = doc.createElement("group");
307 documentElement.setAttribute("name", ui.group_name->currentText());
308 documentElement.setAttribute("renderer", "avformat");
309 doc.documentElement().appendChild(documentElement);
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);
317 //QCString save = doc.toString().utf8();
319 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
320 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
324 QTextStream out(&file);
325 out << doc.toString();
327 parseProfiles(newGroupName, newProfileName);
332 void RenderWidget::slotDeleteProfile() {
333 QString currentGroup = m_view.format_list->currentItem()->text();
334 QString currentProfile = m_view.size_list->currentItem()->text();
336 QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
338 QFile file(exportFile);
339 doc.setContent(&file, false);
342 QDomElement documentElement;
343 bool groupExists = false;
345 QDomNodeList groups = doc.elementsByTagName("group");
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));
367 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
368 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
371 QTextStream out(&file);
372 out << doc.toString();
374 parseProfiles(currentGroup);
375 focusFirstVisibleItem();
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);
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);
389 m_view.buttonDelete->setEnabled(true);
390 m_view.buttonEdit->setEnabled(true);
396 void RenderWidget::focusFirstVisibleItem() {
397 if (m_view.size_list->currentItem() && !m_view.size_list->currentItem()->isHidden()) {
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);
408 if (!m_view.size_list->currentItem()) m_view.size_list->setCurrentRow(0);
412 void RenderWidget::slotExport() {
413 QListWidgetItem *item = m_view.size_list->currentItem();
415 const QString dest = m_view.out_file->url().path();
416 if (dest.isEmpty()) return;
419 if (KMessageBox::warningYesNo(this, i18n("File already exists. Do you want to overwrite it ?")) != KMessageBox::Yes)
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";
429 double startPos = -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();
435 QString renderArgs = m_view.advanced_params->toPlainText();
437 // Adjust frame scale
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();
444 width = m_profile.width;
445 height = m_profile.height;
447 renderArgs.replace("%dar", "@" + QString::number(m_profile.display_aspect_num) + "/" + QString::number(m_profile.display_aspect_den));
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");
453 // disable audio if requested
454 if (!m_view.export_audio->isChecked())
455 renderArgs.append(" an=1 ");
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;
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));
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);
489 } else if (group == "websites" && m_view.open_browser->isChecked()) {
490 renderItem->setData(0, Qt::UserRole, group);
492 QString url = m_view.size_list->currentItem()->data(ExtraRole).toString();
493 renderItem->setData(0, Qt::UserRole + 1, url);
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);
500 void RenderWidget::setProfile(MltVideoProfile 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);
509 void RenderWidget::refreshView() {
510 m_view.size_list->blockSignals(true);
511 QListWidgetItem *sizeItem;
514 if (m_view.destination_list->currentIndex() > 0)
515 destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
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);
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);
541 item = m_view.format_list->currentItem();
545 for (int i = 0; i < m_view.format_list->count() && count < 2; i++) {
546 if (!m_view.format_list->isRowHidden(i)) count++;
548 if (count > 1) m_view.format_list->setVisible(true);
549 else m_view.format_list->setVisible(false);
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();
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);
565 sizeItem->setHidden(false);
566 if (!firstSelected) m_view.size_list->setCurrentItem(sizeItem);
567 firstSelected = true;
570 if (!sizeItem->isHidden()) {
571 // Make sure the selected profile uses an installed avformat codec / format
572 std = sizeItem->data(ParamsRole).toString();
574 if (!formatsList.isEmpty()) {
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);
586 if (!acodecsList.isEmpty() && !sizeItem->isHidden()) {
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);
598 if (!vcodecsList.isEmpty() && !sizeItem->isHidden()) {
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);
611 } else sizeItem->setHidden(true);
613 focusFirstVisibleItem();
614 m_view.size_list->blockSignals(false);
618 KUrl RenderWidget::filenameWithExtension(KUrl url, QString extension) {
620 if (!url.isEmpty()) {
622 int pos = path.lastIndexOf('.') + 1;
623 if (pos == 0) path.append('.' + extension);
624 else path = path.left(pos) + extension;
627 path = QDir::homePath() + "/untitled." + extension;
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);
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);
649 m_view.rescale->setEnabled(true);
650 m_view.rescale_size->setEnabled(true);
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));
661 // m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
663 m_view.out_file->setFilter("*." + extension);
665 if (item->data(EditableRole).toString().isEmpty()) {
666 m_view.buttonDelete->setEnabled(false);
667 m_view.buttonEdit->setEnabled(false);
669 m_view.buttonDelete->setEnabled(true);
670 m_view.buttonEdit->setEnabled(true);
672 m_view.buttonStart->setEnabled(true);
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);
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));
692 void RenderWidget::parseFile(QString exportFile, bool editable) {
694 QFile file(exportFile);
695 doc.setContent(&file, false);
697 QDomElement documentElement;
698 QDomElement profileElement;
699 QDomNodeList groups = doc.elementsByTagName("group");
701 if (groups.count() == 0) {
702 kDebug() << "// Export file: " << exportFile << " IS BROKEN";
710 QString prof_extension;
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;
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);
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);
741 QDomNode n = groups.item(i).firstChild();
742 while (!n.isNull()) {
743 if (n.toElement().tagName() != "profile") {
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");
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"));
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());
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);
794 } else if (status == -2) {
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) {
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"));
810 void RenderWidget::slotAbortCurrentJob() {
811 QTreeWidgetItem *current = m_view.running_jobs->currentItem();
812 if (current) emit abortProcess(current->text(0));
815 #include "renderwidget.moc"