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();
65 m_view.rescale_size->setInputMask("0099\\x0099");
66 m_view.rescale_size->setText("320x240");
70 connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
72 connect(m_view.buttonSave, SIGNAL(clicked()), this, SLOT(slotSaveProfile()));
73 connect(m_view.buttonEdit, SIGNAL(clicked()), this, SLOT(slotEditProfile()));
74 connect(m_view.buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
75 connect(m_view.buttonStart, SIGNAL(clicked()), this, SLOT(slotExport()));
76 connect(m_view.abort_job, SIGNAL(clicked()), this, SLOT(slotAbortCurrentJob()));
77 connect(m_view.buttonClose, SIGNAL(clicked()), this, SLOT(hide()));
78 connect(m_view.buttonClose2, SIGNAL(clicked()), this, SLOT(hide()));
79 connect(m_view.rescale, SIGNAL(toggled(bool)), m_view.rescale_size, SLOT(setEnabled(bool)));
80 connect(m_view.destination_list, SIGNAL(activated(int)), this, SLOT(refreshView()));
81 connect(m_view.out_file, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButtons()));
82 connect(m_view.out_file, SIGNAL(urlSelected(const KUrl &)), this, SLOT(slotUpdateButtons(const KUrl &)));
83 connect(m_view.format_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshView()));
84 connect(m_view.size_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshParams()));
86 connect(m_view.render_guide, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
87 connect(m_view.render_zone, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
88 connect(m_view.render_full, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
90 connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
91 connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));
93 connect(m_view.format_selection, SIGNAL(activated(int)), this, SLOT(refreshView()));
95 m_view.buttonStart->setEnabled(false);
96 m_view.rescale_size->setEnabled(false);
97 m_view.guides_box->setVisible(false);
98 m_view.open_dvd->setVisible(false);
99 m_view.open_browser->setVisible(false);
100 m_view.error_box->setVisible(false);
102 m_view.splitter->setStretchFactor(1, 5);
103 m_view.splitter->setStretchFactor(0, 2);
105 m_view.out_file->setMode(KFile::File);
107 m_view.running_jobs->setHeaderLabels(QStringList() << QString() << i18n("File") << i18n("Progress"));
108 m_view.running_jobs->setItemDelegate(new RenderViewDelegate(this));
109 QHeaderView *header = m_view.running_jobs->header();
110 QFontMetrics fm = fontMetrics();
111 //header->resizeSection(0, fm.width("typical-name-for-a-torrent.torrent"));
112 header->setResizeMode(0, QHeaderView::Fixed);
113 header->resizeSection(0, 30);
114 header->setResizeMode(1, QHeaderView::Interactive);
115 header->resizeSection(1, fm.width("typical-name-for-a-file.torrent"));
116 header->setResizeMode(2, QHeaderView::Fixed);
117 header->resizeSection(1, width() * 2 / 3);
118 header->setResizeMode(2, QHeaderView::Interactive);
119 //header->setResizeMode(1, QHeaderView::Fixed);
121 focusFirstVisibleItem();
124 void RenderWidget::showInfoPanel() {
125 if (m_view.advanced_params->isVisible()) {
126 m_view.advanced_params->setVisible(false);
127 m_view.buttonInfo->setDown(false);
128 KdenliveSettings::setShowrenderparams(false);
130 m_view.advanced_params->setVisible(true);
131 m_view.buttonInfo->setDown(true);
132 KdenliveSettings::setShowrenderparams(true);
136 void RenderWidget::slotUpdateGuideBox() {
137 m_view.guides_box->setVisible(m_view.render_guide->isChecked());
140 void RenderWidget::slotCheckStartGuidePosition() {
141 if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex())
142 m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
145 void RenderWidget::slotCheckEndGuidePosition() {
146 if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex())
147 m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
150 void RenderWidget::setGuides(QDomElement guidesxml, double duration) {
151 m_view.guide_start->clear();
152 m_view.guide_end->clear();
153 QDomNodeList nodes = guidesxml.elementsByTagName("guide");
154 if (nodes.count() > 0) {
155 m_view.guide_start->addItem(i18n("Render"), "0");
156 m_view.render_guide->setEnabled(true);
157 } else m_view.render_guide->setEnabled(false);
158 for (int i = 0; i < nodes.count(); i++) {
159 QDomElement e = nodes.item(i).toElement();
161 m_view.guide_start->addItem(e.attribute("comment"), e.attribute("time").toDouble());
162 m_view.guide_end->addItem(e.attribute("comment"), e.attribute("time").toDouble());
165 if (nodes.count() > 0)
166 m_view.guide_end->addItem(i18n("End"), QString::number(duration));
169 // Will be called when the user selects an output file via the file dialog.
170 // File extension will be added automatically.
171 void RenderWidget::slotUpdateButtons(KUrl url) {
172 if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
173 else m_view.buttonStart->setEnabled(true);
175 QListWidgetItem *item = m_view.size_list->currentItem();
176 QString extension = item->data(ExtensionRole).toString();
177 url = filenameWithExtension(url, extension);
178 m_view.out_file->setUrl(url);
182 // Will be called when the user changes the output file path in the text line.
183 // File extension must NOT be added, would make editing impossible!
184 void RenderWidget::slotUpdateButtons() {
185 if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
186 else m_view.buttonStart->setEnabled(true);
189 void RenderWidget::slotSaveProfile() {
190 Ui::SaveProfile_UI ui;
191 QDialog *d = new QDialog(this);
193 QString customGroup = i18n("Custom");
194 QStringList groupNames;
195 for (int i = 0; i < m_view.format_list->count(); i++)
196 groupNames.append(m_view.format_list->item(i)->text());
197 if (!groupNames.contains(customGroup)) groupNames.prepend(customGroup);
198 ui.group_name->addItems(groupNames);
199 int pos = ui.group_name->findText(customGroup);
200 ui.group_name->setCurrentIndex(pos);
202 ui.parameters->setText(m_view.advanced_params->toPlainText());
203 ui.extension->setText(m_view.size_list->currentItem()->data(ExtensionRole).toString());
204 ui.profile_name->setFocus();
205 if (d->exec() == QDialog::Accepted && !ui.profile_name->text().simplified().isEmpty()) {
206 QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
208 QFile file(exportFile);
209 doc.setContent(&file, false);
212 QDomElement documentElement;
213 bool groupExists = false;
215 QString newProfileName = ui.profile_name->text().simplified();
216 QString newGroupName = ui.group_name->currentText();
217 QDomNodeList groups = doc.elementsByTagName("group");
219 if (groups.count() == 0) {
220 QDomElement profiles = doc.createElement("profiles");
221 doc.appendChild(profiles);
222 } else while (!groups.item(i).isNull()) {
223 documentElement = groups.item(i).toElement();
224 groupName = documentElement.attribute("name");
225 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
226 if (groupName == newGroupName) {
233 documentElement = doc.createElement("group");
234 documentElement.setAttribute("name", ui.group_name->currentText());
235 documentElement.setAttribute("renderer", "avformat");
236 doc.documentElement().appendChild(documentElement);
238 QDomElement profileElement = doc.createElement("profile");
239 profileElement.setAttribute("name", newProfileName);
240 profileElement.setAttribute("extension", ui.extension->text().simplified());
241 profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
242 documentElement.appendChild(profileElement);
244 //QCString save = doc.toString().utf8();
246 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
247 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
251 QTextStream out(&file);
252 out << doc.toString();
254 parseProfiles(newGroupName, newProfileName);
259 void RenderWidget::slotEditProfile() {
260 QListWidgetItem *item = m_view.size_list->currentItem();
262 QString currentGroup = m_view.format_list->currentItem()->text();
264 QString params = item->data(ParamsRole).toString();
265 QString extension = item->data(ExtensionRole).toString();
266 QString currentProfile = item->text();
268 Ui::SaveProfile_UI ui;
269 QDialog *d = new QDialog(this);
271 QStringList groupNames;
272 for (int i = 0; i < m_view.format_list->count(); i++)
273 groupNames.append(m_view.format_list->item(i)->text());
274 if (!groupNames.contains(currentGroup)) groupNames.prepend(currentGroup);
275 ui.group_name->addItems(groupNames);
276 int pos = ui.group_name->findText(currentGroup);
277 ui.group_name->setCurrentIndex(pos);
278 ui.profile_name->setText(currentProfile);
279 ui.extension->setText(extension);
280 ui.parameters->setText(params);
281 ui.profile_name->setFocus();
283 if (d->exec() == QDialog::Accepted) {
285 QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
287 QFile file(exportFile);
288 doc.setContent(&file, false);
291 QDomElement documentElement;
292 bool groupExists = false;
294 QString newProfileName = ui.profile_name->text();
295 QString newGroupName = ui.group_name->currentText();
296 QDomNodeList groups = doc.elementsByTagName("group");
298 if (groups.count() == 0) {
299 QDomElement profiles = doc.createElement("profiles");
300 doc.appendChild(profiles);
301 } else while (!groups.item(i).isNull()) {
302 documentElement = groups.item(i).toElement();
303 groupName = documentElement.attribute("name");
304 kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
305 if (groupName == newGroupName) {
312 documentElement = doc.createElement("group");
313 documentElement.setAttribute("name", ui.group_name->currentText());
314 documentElement.setAttribute("renderer", "avformat");
315 doc.documentElement().appendChild(documentElement);
317 QDomElement profileElement = doc.createElement("profile");
318 profileElement.setAttribute("name", newProfileName);
319 profileElement.setAttribute("extension", ui.extension->text().simplified());
320 profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
321 documentElement.appendChild(profileElement);
323 //QCString save = doc.toString().utf8();
325 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
326 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
330 QTextStream out(&file);
331 out << doc.toString();
333 parseProfiles(newGroupName, newProfileName);
338 void RenderWidget::slotDeleteProfile() {
339 QString currentGroup = m_view.format_list->currentItem()->text();
340 QString currentProfile = m_view.size_list->currentItem()->text();
342 QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
344 QFile file(exportFile);
345 doc.setContent(&file, false);
348 QDomElement documentElement;
349 bool groupExists = false;
351 QDomNodeList groups = doc.elementsByTagName("group");
354 while (!groups.item(i).isNull()) {
355 documentElement = groups.item(i).toElement();
356 groupName = documentElement.attribute("name");
357 if (groupName == currentGroup) {
358 QDomNodeList children = documentElement.childNodes();
359 for (int j = 0; j < children.count(); j++) {
360 QDomElement pro = children.at(j).toElement();
361 if (pro.attribute("name") == currentProfile) {
362 groups.item(i).removeChild(children.at(j));
363 if (groups.item(i).childNodes().isEmpty())
364 doc.documentElement().removeChild(groups.item(i));
373 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
374 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
377 QTextStream out(&file);
378 out << doc.toString();
380 parseProfiles(currentGroup);
381 focusFirstVisibleItem();
384 void RenderWidget::updateButtons() {
385 if (!m_view.size_list->currentItem() || m_view.size_list->currentItem()->isHidden()) {
386 m_view.buttonSave->setEnabled(false);
387 m_view.buttonDelete->setEnabled(false);
388 m_view.buttonEdit->setEnabled(false);
390 m_view.buttonSave->setEnabled(true);
391 if (m_view.size_list->currentItem()->data(EditableRole).toString().isEmpty()) {
392 m_view.buttonDelete->setEnabled(false);
393 m_view.buttonEdit->setEnabled(false);
395 m_view.buttonDelete->setEnabled(true);
396 m_view.buttonEdit->setEnabled(true);
402 void RenderWidget::focusFirstVisibleItem() {
403 if (m_view.size_list->currentItem() && !m_view.size_list->currentItem()->isHidden()) {
407 for (uint ix = 0; ix < m_view.size_list->count(); ix++) {
408 QListWidgetItem *item = m_view.size_list->item(ix);
409 if (item && !item->isHidden()) {
410 m_view.size_list->setCurrentRow(ix);
414 if (!m_view.size_list->currentItem()) m_view.size_list->setCurrentRow(0);
418 void RenderWidget::slotExport() {
419 QListWidgetItem *item = m_view.size_list->currentItem();
421 const QString dest = m_view.out_file->url().path();
422 if (dest.isEmpty()) return;
425 if (KMessageBox::warningYesNo(this, i18n("File already exists. Do you want to overwrite it ?")) != KMessageBox::Yes)
428 QStringList overlayargs;
429 if (m_view.tc_overlay->isChecked()) {
430 QString filterFile = KStandardDirs::locate("appdata", "metadata.properties");
431 overlayargs << "meta.attr.timecode=1" << "meta.attr.timecode.markup=#timecode";
432 overlayargs << "-attach" << "data_feed:attr_check" << "-attach";
433 overlayargs << "data_show:" + filterFile << "_fezzik=1" << "dynamic=1";
435 double startPos = -1;
437 if (m_view.render_guide->isChecked()) {
438 startPos = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
439 endPos = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
441 QString renderArgs = m_view.advanced_params->toPlainText();
443 // Adjust frame scale
446 if (m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
447 width = m_view.rescale_size->text().section('x', 0, 0).toInt();
448 height = m_view.rescale_size->text().section('x', 1, 1).toInt();
450 width = m_profile.width;
451 height = m_profile.height;
453 renderArgs.replace("%dar", "@" + QString::number(m_profile.display_aspect_num) + "/" + QString::number(m_profile.display_aspect_den));
456 if (m_view.scanning_list->currentIndex() == 1) renderArgs.append(" progressive=1");
457 else if (m_view.scanning_list->currentIndex() == 2) renderArgs.append(" progressive=0");
459 // disable audio if requested
460 if (!m_view.export_audio->isChecked())
461 renderArgs.append(" an=1 ");
463 // Check if the rendering profile is different from project profile,
464 // in which case we need to use the producer_comsumer from MLT
465 bool resizeProfile = false;
467 QString std = renderArgs;
468 QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
469 if (std.contains(" s=")) {
470 QString subsize = std.section(" s=", 1, 1);
471 subsize = subsize.section(' ', 0, 0).toLower();
472 const QString currentSize = QString::number(width) + 'x' + QString::number(height);
473 if (subsize != currentSize) resizeProfile = true;
474 } else if (destination != "audioonly") {
475 // Add current size parametrer
476 renderArgs.append(QString(" s=%1x%2").arg(width).arg(height));
479 // insert item in running jobs list
480 QTreeWidgetItem *renderItem;
481 QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
482 if (!existing.isEmpty()) renderItem = existing.at(0);
483 else renderItem = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
484 renderItem->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
485 renderItem->setData(1, Qt::UserRole + 1, QTime::currentTime());
487 // Set rendering type
488 QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString();
489 if (group == "dvd" && m_view.open_dvd->isChecked()) {
490 renderItem->setData(0, Qt::UserRole, group);
491 if (renderArgs.contains("profile=")) {
492 // rendering profile contains an MLT profile, so pass it to the running jog item, usefull for dvd
493 QString prof = renderArgs.section("profile=", 1, 1);
494 prof = prof.section(' ', 0, 0);
495 kDebug() << "// render profile: " << prof;
496 renderItem->setData(0, Qt::UserRole + 1, prof);
498 } else if (group == "websites" && m_view.open_browser->isChecked()) {
499 renderItem->setData(0, Qt::UserRole, group);
501 QString url = m_view.size_list->currentItem()->data(ExtraRole).toString();
502 renderItem->setData(0, Qt::UserRole + 1, url);
505 emit doRender(dest, item->data(RenderRole).toString(), overlayargs, renderArgs.simplified().split(' '), m_view.render_zone->isChecked(), m_view.play_after->isChecked(), startPos, endPos, resizeProfile);
506 m_view.tabWidget->setCurrentIndex(1);
509 void RenderWidget::setProfile(MltVideoProfile profile) {
511 //WARNING: this way to tell the video standard is a bit hackish...
512 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);
513 else m_view.format_selection->setCurrentIndex(1);
514 m_view.scanning_list->setCurrentIndex(0);
518 void RenderWidget::refreshView() {
519 m_view.size_list->blockSignals(true);
520 QListWidgetItem *sizeItem;
523 if (m_view.destination_list->currentIndex() > 0)
524 destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
526 if (destination == "dvd") m_view.open_dvd->setVisible(true);
527 else m_view.open_dvd->setVisible(false);
528 if (destination == "websites") m_view.open_browser->setVisible(true);
529 else m_view.open_browser->setVisible(false);
530 if (!destination.isEmpty() && QString("dvd websites audioonly").contains(destination))
531 m_view.rescale->setEnabled(false);
532 else m_view.rescale->setEnabled(true);
533 // hide groups that are not in the correct destination
534 for (int i = 0; i < m_view.format_list->count(); i++) {
535 sizeItem = m_view.format_list->item(i);
536 if (sizeItem->data(MetaGroupRole).toString() == destination)
537 sizeItem->setHidden(false);
538 else sizeItem->setHidden(true);
541 // activate first visible item
542 QListWidgetItem * item = m_view.format_list->currentItem();
543 if (!item || item->isHidden()) {
544 for (int i = 0; i < m_view.format_list->count(); i++) {
545 if (!m_view.format_list->item(i)->isHidden()) {
546 m_view.format_list->setCurrentRow(i);
550 item = m_view.format_list->currentItem();
554 for (int i = 0; i < m_view.format_list->count() && count < 2; i++) {
555 if (!m_view.format_list->isRowHidden(i)) count++;
557 if (count > 1) m_view.format_list->setVisible(true);
558 else m_view.format_list->setVisible(false);
560 QString group = item->text();
561 bool firstSelected = false;
562 const QStringList formatsList = KdenliveSettings::supportedformats();
563 const QStringList vcodecsList = KdenliveSettings::videocodecs();
564 const QStringList acodecsList = KdenliveSettings::audiocodecs();
566 for (int i = 0; i < m_view.size_list->count(); i++) {
567 sizeItem = m_view.size_list->item(i);
568 if (sizeItem->data(GroupRole) == group) {
569 std = sizeItem->data(StandardRole).toString();
570 if (!std.isEmpty()) {
571 if (std.contains("PAL", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 0);
572 else if (std.contains("NTSC", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 1);
574 sizeItem->setHidden(false);
575 if (!firstSelected) m_view.size_list->setCurrentItem(sizeItem);
576 firstSelected = true;
579 if (!sizeItem->isHidden()) {
580 // Make sure the selected profile uses an installed avformat codec / format
581 std = sizeItem->data(ParamsRole).toString();
583 if (!formatsList.isEmpty()) {
585 if (std.startsWith("f=")) format = std.section("f=", 1, 1);
586 else if (std.contains(" f=")) format = std.section(" f=", 1, 1);
587 if (!format.isEmpty()) {
588 format = format.section(' ', 0, 0).toLower();
589 if (!formatsList.contains(format)) {
590 kDebug() << "***** UNSUPPORTED F: " << format;
591 sizeItem->setHidden(true);
595 if (!acodecsList.isEmpty() && !sizeItem->isHidden()) {
597 if (std.startsWith("acodec=")) format = std.section("acodec=", 1, 1);
598 else if (std.contains(" acodec=")) format = std.section(" acodec=", 1, 1);
599 if (!format.isEmpty()) {
600 format = format.section(' ', 0, 0).toLower();
601 if (!acodecsList.contains(format)) {
602 kDebug() << "***** UNSUPPORTED ACODEC: " << format;
603 sizeItem->setHidden(true);
607 if (!vcodecsList.isEmpty() && !sizeItem->isHidden()) {
609 if (std.startsWith("vcodec=")) format = std.section("vcodec=", 1, 1);
610 else if (std.contains(" vcodec=")) format = std.section(" vcodec=", 1, 1);
611 if (!format.isEmpty()) {
612 format = format.section(' ', 0, 0).toLower();
613 if (!vcodecsList.contains(format)) {
614 kDebug() << "***** UNSUPPORTED VCODEC: " << format;
615 sizeItem->setHidden(true);
620 } else sizeItem->setHidden(true);
622 focusFirstVisibleItem();
623 m_view.size_list->blockSignals(false);
627 KUrl RenderWidget::filenameWithExtension(KUrl url, QString extension) {
629 if (!url.isEmpty()) {
631 int pos = path.lastIndexOf('.') + 1;
632 if (pos == 0) path.append('.' + extension);
633 else path = path.left(pos) + extension;
636 path = QDir::homePath() + "/untitled." + extension;
642 void RenderWidget::refreshParams() {
643 QListWidgetItem *item = m_view.size_list->currentItem();
644 if (!item || item->isHidden()) {
645 m_view.advanced_params->clear();
646 m_view.buttonStart->setEnabled(false);
649 QString params = item->data(ParamsRole).toString();
650 QString extension = item->data(ExtensionRole).toString();
651 m_view.advanced_params->setPlainText(params);
652 QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
653 if (params.contains(" s=") || destination == "audioonly") {
654 // profile has a fixed size, do not allow resize
655 m_view.rescale->setEnabled(false);
656 m_view.rescale_size->setEnabled(false);
658 m_view.rescale->setEnabled(true);
659 m_view.rescale_size->setEnabled(true);
661 KUrl url = filenameWithExtension(m_view.out_file->url(), extension);
662 m_view.out_file->setUrl(url);
663 // if (!url.isEmpty()) {
664 // QString path = url.path();
665 // int pos = path.lastIndexOf('.') + 1;
666 // if (pos == 0) path.append('.' + extension);
667 // else path = path.left(pos) + extension;
668 // m_view.out_file->setUrl(KUrl(path));
670 // m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
672 m_view.out_file->setFilter("*." + extension);
674 if (item->data(EditableRole).toString().isEmpty()) {
675 m_view.buttonDelete->setEnabled(false);
676 m_view.buttonEdit->setEnabled(false);
678 m_view.buttonDelete->setEnabled(true);
679 m_view.buttonEdit->setEnabled(true);
681 m_view.buttonStart->setEnabled(true);
684 void RenderWidget::parseProfiles(QString group, QString profile) {
685 m_view.size_list->clear();
686 m_view.format_list->clear();
687 m_view.destination_list->clear();
688 m_view.destination_list->addItem(KIcon("video-x-generic"), i18n("File rendering"));
689 QString exportFile = KStandardDirs::locate("appdata", "export/profiles.xml");
690 parseFile(exportFile, false);
691 exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
692 if (QFile::exists(exportFile)) parseFile(exportFile, true);
694 QList<QListWidgetItem *> child;
695 child = m_view.format_list->findItems(group, Qt::MatchExactly);
696 if (!child.isEmpty()) m_view.format_list->setCurrentItem(child.at(0));
697 child = m_view.size_list->findItems(profile, Qt::MatchExactly);
698 if (!child.isEmpty()) m_view.size_list->setCurrentItem(child.at(0));
701 void RenderWidget::parseFile(QString exportFile, bool editable) {
703 QFile file(exportFile);
704 doc.setContent(&file, false);
706 QDomElement documentElement;
707 QDomElement profileElement;
708 QDomNodeList groups = doc.elementsByTagName("group");
710 if (groups.count() == 0) {
711 kDebug() << "// Export file: " << exportFile << " IS BROKEN";
719 QString prof_extension;
724 QListWidgetItem *item;
725 while (!groups.item(i).isNull()) {
726 documentElement = groups.item(i).toElement();
727 QDomNode gname = documentElement.elementsByTagName("groupname").at(0);
728 QString metagroupName;
730 if (!gname.isNull()) {
731 metagroupName = gname.firstChild().nodeValue();
732 metagroupId = gname.toElement().attribute("id");
733 if (!metagroupName.isEmpty() && !m_view.destination_list->contains(metagroupName)) {
734 if (metagroupId == "dvd") icon = KIcon("media-optical");
735 else if (metagroupId == "audioonly") icon = KIcon("audio-x-generic");
736 else if (metagroupId == "websites") icon = KIcon("applications-internet");
737 else if (metagroupId == "mediaplayers") icon = KIcon("applications-multimedia");
738 else if (metagroupId == "lossless") icon = KIcon("drive-harddisk");
739 m_view.destination_list->addItem(icon, i18n(metagroupName.toUtf8().data()), metagroupId);
742 groupName = documentElement.attribute("name", QString::null);
743 extension = documentElement.attribute("extension", QString::null);
744 renderer = documentElement.attribute("renderer", QString::null);
745 if (m_view.format_list->findItems(groupName, Qt::MatchExactly).isEmpty()) {
746 item = new QListWidgetItem(groupName, m_view.format_list);
747 item->setData(MetaGroupRole, metagroupId);
750 QDomNode n = groups.item(i).firstChild();
751 while (!n.isNull()) {
752 if (n.toElement().tagName() != "profile") {
756 profileElement = n.toElement();
757 profileName = profileElement.attribute("name");
758 standard = profileElement.attribute("standard");
759 params = profileElement.attribute("args");
760 prof_extension = profileElement.attribute("extension");
761 if (!prof_extension.isEmpty()) extension = prof_extension;
762 item = new QListWidgetItem(profileName, m_view.size_list);
763 item->setData(GroupRole, groupName);
764 item->setData(MetaGroupRole, metagroupId);
765 item->setData(ExtensionRole, extension);
766 item->setData(RenderRole, renderer);
767 item->setData(StandardRole, standard);
768 item->setData(ParamsRole, params);
769 if (profileElement.hasAttribute("url")) item->setData(ExtraRole, profileElement.attribute("url"));
770 if (editable) item->setData(EditableRole, "true");
778 void RenderWidget::setRenderJob(const QString &dest, int progress) {
779 QTreeWidgetItem *item;
780 QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
781 if (!existing.isEmpty()) item = existing.at(0);
782 else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
783 item->setData(2, Qt::UserRole, progress);
785 item->setIcon(0, KIcon("system-run"));
786 item->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
787 item->setData(1, Qt::UserRole + 1, QTime::currentTime());
789 QTime startTime = item->data(1, Qt::UserRole + 1).toTime();
790 int seconds = startTime.secsTo(QTime::currentTime());;
791 const QString t = i18n("Estimated time %1", QTime().addSecs(seconds * (100 - progress) / progress).toString("hh:mm:ss"));
792 item->setData(1, Qt::UserRole, t);
796 void RenderWidget::setRenderStatus(const QString &dest, int status, const QString &error) {
797 QTreeWidgetItem *item;
798 QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
799 if (!existing.isEmpty()) item = existing.at(0);
800 else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
802 // Job finished successfully
803 item->setIcon(0, KIcon("dialog-ok"));
804 item->setData(2, Qt::UserRole, 100);
805 QTime startTime = item->data(1, Qt::UserRole + 1).toTime();
806 int seconds = startTime.secsTo(QTime::currentTime());
807 const QTime tm = QTime().addSecs(seconds);
808 const QString t = i18n("Rendering finished in %1", tm.toString("hh:mm:ss"));
809 item->setData(1, Qt::UserRole, t);
810 QString itemGroup = item->data(0, Qt::UserRole).toString();
811 if (itemGroup == "dvd") {
812 emit openDvdWizard(item->text(1), item->data(0, Qt::UserRole + 1).toString());
813 } else if (itemGroup == "websites") {
814 QString url = item->data(0, Qt::UserRole + 1).toString();
815 if (!url.isEmpty()) KRun *openBrowser = new KRun(url, this);
817 } else if (status == -2) {
819 item->setData(1, Qt::UserRole, i18n("Rendering crashed"));
820 item->setIcon(0, KIcon("dialog-close"));
821 item->setData(2, Qt::UserRole, 0);
822 m_view.error_log->append(i18n("<strong>Rendering of %1 crashed</strong><br />", dest));
823 m_view.error_log->append(error);
824 m_view.error_log->append("<hr />");
825 m_view.error_box->setVisible(true);
826 } else if (status == -3) {
828 item->setData(1, Qt::UserRole, i18n("Rendering aborted"));
829 item->setIcon(0, KIcon("dialog-cancel"));
830 item->setData(2, Qt::UserRole, 100);
834 void RenderWidget::slotAbortCurrentJob() {
835 QTreeWidgetItem *current = m_view.running_jobs->currentItem();
836 if (current) emit abortProcess(current->text(1));
839 #include "renderwidget.moc"