]> git.sesse.net Git - kdenlive/blob - src/renderwidget.cpp
Minor optimization
[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 "renderwidget.h"
22 #include "kdenlivesettings.h"
23 #include "ui_saveprofile_ui.h"
24 #include "timecode.h"
25 #include "profilesdialog.h"
26
27 #include <KStandardDirs>
28 #include <KDebug>
29 #include <KMessageBox>
30 #include <KComboBox>
31 #include <KRun>
32 #include <KIO/NetAccess>
33 #include <KColorScheme>
34 #include <KNotification>
35 #include <KStartupInfo>
36
37 #include <QDomDocument>
38 #include <QItemDelegate>
39 #include <QTreeWidgetItem>
40 #include <QListWidgetItem>
41 #include <QHeaderView>
42 #include <QMenu>
43 #include <QInputDialog>
44 #include <QProcess>
45 #include <QDBusConnectionInterface>
46 #include <QDBusInterface>
47 #include <QThread>
48 #include <QScriptEngine>
49
50 #include "locale.h"
51
52
53 // Render profiles roles
54 const int GroupRole = Qt::UserRole;
55 const int ExtensionRole = GroupRole + 1;
56 const int StandardRole = GroupRole + 2;
57 const int RenderRole = GroupRole + 3;
58 const int ParamsRole = GroupRole + 4;
59 const int EditableRole = GroupRole + 5;
60 const int MetaGroupRole = GroupRole + 6;
61 const int ExtraRole = GroupRole + 7;
62 const int BitratesRole = GroupRole + 8;
63 const int DefaultBitrateRole = GroupRole + 9;
64 const int AudioBitratesRole = GroupRole + 10;
65 const int DefaultAudioBitrateRole = GroupRole + 11;
66
67 // Render job roles
68 const int ParametersRole = Qt::UserRole + 1;
69 const int TimeRole = Qt::UserRole + 2;
70 const int ProgressRole = Qt::UserRole + 3;
71 const int ExtraInfoRole = Qt::UserRole + 5;
72
73 const int DirectRenderType = QTreeWidgetItem::Type;
74 const int ScriptRenderType = QTreeWidgetItem::UserType;
75
76
77 // Running job status
78 const int WAITINGJOB = 0;
79 const int RUNNINGJOB = 1;
80 const int FINISHEDJOB = 2;
81 const int FAILEDJOB = 3;
82 const int ABORTEDJOB = 4;
83
84
85 RenderJobItem::RenderJobItem(QTreeWidget * parent, const QStringList & strings, int type)
86     : QTreeWidgetItem(parent, strings, type),
87     m_status(-1)
88 {
89     setSizeHint(1, QSize(parent->columnWidth(1), parent->fontMetrics().height() * 3));
90     setStatus(WAITINGJOB);
91 }
92
93 void RenderJobItem::setStatus(int status)
94 {
95     if (m_status == status)
96         return;
97     m_status = status;
98     switch (status) {
99         case WAITINGJOB:
100             setIcon(0, KIcon("media-playback-pause"));
101             setData(1, Qt::UserRole, i18n("Waiting..."));
102             break;
103         case FINISHEDJOB:
104             setData(1, Qt::UserRole, i18n("Rendering finished"));
105             setIcon(0, KIcon("dialog-ok"));
106             setData(1, ProgressRole, 100);
107             break;
108         case FAILEDJOB:
109             setData(1, Qt::UserRole, i18n("Rendering crashed"));
110             setIcon(0, KIcon("dialog-close"));
111             setData(1, ProgressRole, 100);
112             break;
113         case ABORTEDJOB:
114             setData(1, Qt::UserRole, i18n("Rendering aborted"));
115             setIcon(0, KIcon("dialog-cancel"));
116             setData(1, ProgressRole, 100);
117         
118         default:
119             break;
120     }
121 }
122
123 int RenderJobItem::status() const
124 {
125     return m_status;
126 }
127
128 void RenderJobItem::setMetadata(const QString &data)
129 {
130     m_data = data;
131 }
132
133 const QString RenderJobItem::metadata() const
134 {
135     return m_data;
136 }
137
138
139 RenderWidget::RenderWidget(const QString &projectfolder, bool enableProxy, const MltVideoProfile &profile, QWidget * parent) :
140         QDialog(parent),
141         m_projectFolder(projectfolder),
142         m_profile(profile),
143         m_blockProcessing(false)
144 {
145     m_view.setupUi(this);
146     int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
147     QSize iconSize(size, size);
148     
149     setWindowTitle(i18n("Rendering"));
150     m_view.buttonDelete->setIconSize(iconSize);
151     m_view.buttonEdit->setIconSize(iconSize);
152     m_view.buttonSave->setIconSize(iconSize);
153     m_view.buttonInfo->setIconSize(iconSize);
154     m_view.buttonFavorite->setIconSize(iconSize);
155     
156     m_view.buttonDelete->setIcon(KIcon("trash-empty"));
157     m_view.buttonDelete->setToolTip(i18n("Delete profile"));
158     m_view.buttonDelete->setEnabled(false);
159
160     m_view.buttonEdit->setIcon(KIcon("document-properties"));
161     m_view.buttonEdit->setToolTip(i18n("Edit profile"));
162     m_view.buttonEdit->setEnabled(false);
163
164     m_view.buttonSave->setIcon(KIcon("document-new"));
165     m_view.buttonSave->setToolTip(i18n("Create new profile"));
166
167     m_view.buttonInfo->setIcon(KIcon("help-about"));
168     m_view.hide_log->setIcon(KIcon("go-down"));
169
170     m_view.buttonFavorite->setIcon(KIcon("favorites"));
171     m_view.buttonFavorite->setToolTip(i18n("Copy profile to favorites"));
172     
173     m_view.show_all_profiles->setToolTip(i18n("Show profiles with different framerate"));
174     
175     m_view.advanced_params->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5);
176     
177     m_view.buttonRender->setEnabled(false);
178     m_view.buttonGenerateScript->setEnabled(false);
179     setRescaleEnabled(false);
180     m_view.guides_box->setVisible(false);
181     m_view.open_dvd->setVisible(false);
182     m_view.create_chapter->setVisible(false);
183     m_view.open_browser->setVisible(false);
184     m_view.error_box->setVisible(false);
185     m_view.tc_type->setEnabled(false);
186     m_view.checkTwoPass->setEnabled(false);
187     
188     if (KdenliveSettings::showrenderparams()) {
189         m_view.buttonInfo->setDown(true);
190     } else {
191         m_view.advanced_params->hide();
192     }
193     
194     m_view.proxy_render->setHidden(!enableProxy);
195
196     KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
197     QColor bg = scheme.background(KColorScheme::NegativeBackground).color();
198     m_view.errorBox->setStyleSheet(QString("QGroupBox { background-color: rgb(%1, %2, %3); border-radius: 5px;}; ").arg(bg.red()).arg(bg.green()).arg(bg.blue()));
199     int height = QFontInfo(font()).pixelSize();
200     m_view.errorIcon->setPixmap(KIcon("dialog-warning").pixmap(height, height));
201     m_view.errorBox->setHidden(true);
202
203 #if KDE_IS_VERSION(4,7,0)
204     m_infoMessage = new KMessageWidget;
205     QGridLayout *s =  static_cast <QGridLayout*> (m_view.tab->layout());
206     s->addWidget(m_infoMessage, 16, 0, 1, -1);
207     m_infoMessage->setCloseButtonVisible(false);
208     m_infoMessage->hide();
209 #endif
210
211     m_view.encoder_threads->setMaximum(QThread::idealThreadCount());
212     m_view.encoder_threads->setValue(KdenliveSettings::encodethreads());
213     connect(m_view.encoder_threads, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateEncodeThreads(int)));
214
215     m_view.rescale_keep->setChecked(KdenliveSettings::rescalekeepratio());
216     connect(m_view.rescale_width, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateRescaleWidth(int)));
217     connect(m_view.rescale_height, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateRescaleHeight(int)));
218     m_view.rescale_keep->setIcon(KIcon("insert-link"));
219     m_view.rescale_keep->setToolTip(i18n("Preserve aspect ratio"));
220     connect(m_view.rescale_keep, SIGNAL(clicked()), this, SLOT(slotSwitchAspectRatio()));
221
222     connect(m_view.buttonRender, SIGNAL(clicked()), this, SLOT(slotPrepareExport()));
223     connect(m_view.buttonGenerateScript, SIGNAL(clicked()), this, SLOT(slotGenerateScript()));
224
225     m_view.abort_job->setEnabled(false);
226     m_view.start_script->setEnabled(false);
227     m_view.delete_script->setEnabled(false);
228
229     m_view.format_list->setAlternatingRowColors(true);
230     m_view.size_list->setAlternatingRowColors(true);
231
232     connect(m_view.export_audio, SIGNAL(stateChanged(int)), this, SLOT(slotUpdateAudioLabel(int)));
233     m_view.export_audio->setCheckState(Qt::PartiallyChecked);
234
235     parseProfiles();
236     parseScriptFiles();
237     m_view.running_jobs->setUniformRowHeights(false);
238     m_view.scripts_list->setUniformRowHeights(false);
239     connect(m_view.start_script, SIGNAL(clicked()), this, SLOT(slotStartScript()));
240     connect(m_view.delete_script, SIGNAL(clicked()), this, SLOT(slotDeleteScript()));
241     connect(m_view.scripts_list, SIGNAL(itemSelectionChanged()), this, SLOT(slotCheckScript()));
242     connect(m_view.running_jobs, SIGNAL(itemSelectionChanged()), this, SLOT(slotCheckJob()));
243     connect(m_view.running_jobs, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotPlayRendering(QTreeWidgetItem*,int)));
244
245     connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));
246
247     connect(m_view.buttonSave, SIGNAL(clicked()), this, SLOT(slotSaveProfile()));
248     connect(m_view.buttonEdit, SIGNAL(clicked()), this, SLOT(slotEditProfile()));
249     connect(m_view.buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
250     connect(m_view.buttonFavorite, SIGNAL(clicked()), this, SLOT(slotCopyToFavorites()));
251
252     connect(m_view.abort_job, SIGNAL(clicked()), this, SLOT(slotAbortCurrentJob()));
253     connect(m_view.start_job, SIGNAL(clicked()), this, SLOT(slotStartCurrentJob()));
254     connect(m_view.clean_up, SIGNAL(clicked()), this, SLOT(slotCLeanUpJobs()));
255     connect(m_view.hide_log, SIGNAL(clicked()), this, SLOT(slotHideLog()));
256
257     connect(m_view.buttonClose, SIGNAL(clicked()), this, SLOT(hide()));
258     connect(m_view.buttonClose2, SIGNAL(clicked()), this, SLOT(hide()));
259     connect(m_view.buttonClose3, SIGNAL(clicked()), this, SLOT(hide()));
260     connect(m_view.rescale, SIGNAL(toggled(bool)), this, SLOT(setRescaleEnabled(bool)));
261     connect(m_view.destination_list, SIGNAL(activated(int)), this, SLOT(refreshCategory()));
262     connect(m_view.out_file, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateButtons()));
263     connect(m_view.out_file, SIGNAL(urlSelected(KUrl)), this, SLOT(slotUpdateButtons(KUrl)));
264     connect(m_view.format_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshView()));
265     connect(m_view.size_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshParams()));
266     connect(m_view.show_all_profiles, SIGNAL(stateChanged(int)), this, SLOT(refreshView()));
267
268     connect(m_view.size_list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotEditItem(QListWidgetItem*)));
269
270     connect(m_view.render_guide, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
271     connect(m_view.render_zone, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
272     connect(m_view.render_full, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
273
274     connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
275     connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));
276
277     connect(m_view.tc_overlay, SIGNAL(toggled(bool)), m_view.tc_type, SLOT(setEnabled(bool)));
278
279     m_view.splitter->setStretchFactor(1, 5);
280     m_view.splitter->setStretchFactor(0, 2);
281
282     m_view.out_file->setMode(KFile::File);
283     m_view.out_file->setFocusPolicy(Qt::ClickFocus);
284
285     m_view.running_jobs->setHeaderLabels(QStringList() << QString() << i18n("File"));
286     m_jobsDelegate = new RenderViewDelegate(this);
287     m_view.running_jobs->setItemDelegate(m_jobsDelegate);
288
289     QHeaderView *header = m_view.running_jobs->header();
290     header->setResizeMode(0, QHeaderView::Fixed);
291     header->resizeSection(0, 30);
292     header->setResizeMode(1, QHeaderView::Interactive);
293
294     m_view.scripts_list->setHeaderLabels(QStringList() << QString() << i18n("Script Files"));
295     m_scriptsDelegate = new RenderViewDelegate(this);
296     m_view.scripts_list->setItemDelegate(m_scriptsDelegate);
297     header = m_view.scripts_list->header();
298     header->setResizeMode(0, QHeaderView::Fixed);
299     header->resizeSection(0, 30);
300
301     // Find path for Kdenlive renderer
302     m_renderer = QCoreApplication::applicationDirPath() + QString("/kdenlive_render");
303     if (!QFile::exists(m_renderer)) {
304         m_renderer = KStandardDirs::findExe("kdenlive_render");
305         if (m_renderer.isEmpty())
306             m_renderer = KStandardDirs::locate("exe", "kdenlive_render");
307         if (m_renderer.isEmpty())
308             m_renderer = "kdenlive_render";
309     }
310
311     QDBusConnectionInterface* interface = QDBusConnection::sessionBus().interface();
312     if (!interface || (!interface->isServiceRegistered("org.kde.ksmserver") && !interface->isServiceRegistered("org.gnome.SessionManager")))
313         m_view.shutdown->setEnabled(false);
314
315     focusFirstVisibleItem();
316     adjustSize();
317 }
318
319 QSize RenderWidget::sizeHint() const
320 {
321     // Make sure the widget has minimum size on opening
322     return QSize(200, 200);
323 }
324
325 RenderWidget::~RenderWidget()
326 {
327     m_view.running_jobs->blockSignals(true);
328     m_view.scripts_list->blockSignals(true);
329     m_view.running_jobs->clear();
330     m_view.scripts_list->clear();
331     delete m_jobsDelegate;
332     delete m_scriptsDelegate;
333 #if KDE_IS_VERSION(4,7,0)
334     delete m_infoMessage;
335 #endif
336 }
337
338 void RenderWidget::slotEditItem(QListWidgetItem *item)
339 {
340     const QString edit = item->data(EditableRole).toString();
341     if (edit.isEmpty() || !edit.endsWith(QLatin1String("customprofiles.xml")))
342         slotSaveProfile();
343     else slotEditProfile();
344 }
345
346 void RenderWidget::showInfoPanel()
347 {
348     if (m_view.advanced_params->isVisible()) {
349         m_view.advanced_params->setVisible(false);
350         m_view.buttonInfo->setDown(false);
351         KdenliveSettings::setShowrenderparams(false);
352     } else {
353         m_view.advanced_params->setVisible(true);
354         m_view.buttonInfo->setDown(true);
355         KdenliveSettings::setShowrenderparams(true);
356     }
357 }
358
359 void RenderWidget::setDocumentPath(const QString &path)
360 {
361     if (m_view.out_file->url().directory() == KUrl(m_projectFolder).directory()) {
362         const QString fileName = m_view.out_file->url().fileName();
363         m_view.out_file->setUrl(KUrl(path + fileName));
364     }
365     m_projectFolder = path;
366     parseScriptFiles();
367
368 }
369
370 void RenderWidget::slotUpdateGuideBox()
371 {
372     m_view.guides_box->setVisible(m_view.render_guide->isChecked());
373 }
374
375 void RenderWidget::slotCheckStartGuidePosition()
376 {
377     if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex())
378         m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
379 }
380
381 void RenderWidget::slotCheckEndGuidePosition()
382 {
383     if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex())
384         m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
385 }
386
387 void RenderWidget::setGuides(QDomElement guidesxml, double duration)
388 {
389     m_view.guide_start->clear();
390     m_view.guide_end->clear();
391     QDomNodeList nodes = guidesxml.elementsByTagName("guide");
392     if (!nodes.isEmpty()) {
393         m_view.guide_start->addItem(i18n("Beginning"), "0");
394         m_view.render_guide->setEnabled(true);
395         m_view.create_chapter->setEnabled(true);
396     } else {
397         m_view.render_guide->setEnabled(false);
398         m_view.create_chapter->setEnabled(false);
399     }
400     double fps = (double) m_profile.frame_rate_num / m_profile.frame_rate_den;
401     for (int i = 0; i < nodes.count(); ++i) {
402         QDomElement e = nodes.item(i).toElement();
403         if (!e.isNull()) {
404             GenTime pos = GenTime(e.attribute("time").toDouble());
405             const QString guidePos = Timecode::getStringTimecode(pos.frames(fps), fps);
406             m_view.guide_start->addItem(e.attribute("comment") + '/' + guidePos, e.attribute("time").toDouble());
407             m_view.guide_end->addItem(e.attribute("comment") + '/' + guidePos, e.attribute("time").toDouble());
408         }
409     }
410     if (!nodes.isEmpty())
411         m_view.guide_end->addItem(i18n("End"), QString::number(duration));
412 }
413
414 /**
415  * Will be called when the user selects an output file via the file dialog.
416  * File extension will be added automatically.
417  */
418 void RenderWidget::slotUpdateButtons(const KUrl &url)
419 {
420     if (m_view.out_file->url().isEmpty()) {
421         m_view.buttonGenerateScript->setEnabled(false);
422         m_view.buttonRender->setEnabled(false);
423     } else {
424         updateButtons(); // This also checks whether the selected format is available
425     }
426     if (url != 0) {
427         QListWidgetItem *item = m_view.size_list->currentItem();
428         if (!item) {
429             m_view.buttonRender->setEnabled(false);
430             m_view.buttonGenerateScript->setEnabled(false);
431             return;
432         }
433         const QString extension = item->data(ExtensionRole).toString();
434         m_view.out_file->setUrl(filenameWithExtension(url, extension));
435     }
436 }
437
438 /**
439  * Will be called when the user changes the output file path in the text line.
440  * File extension must NOT be added, would make editing impossible!
441  */
442 void RenderWidget::slotUpdateButtons()
443 {
444     if (m_view.out_file->url().isEmpty()) {
445         m_view.buttonRender->setEnabled(false);
446         m_view.buttonGenerateScript->setEnabled(false);
447     } else {
448         updateButtons(); // This also checks whether the selected format is available
449     }
450 }
451
452 void RenderWidget::slotSaveProfile()
453 {
454     //TODO: update to correctly use metagroups
455     Ui::SaveProfile_UI ui;
456     QDialog *d = new QDialog(this);
457     ui.setupUi(d);
458
459     for (int i = 0; i < m_view.destination_list->count(); ++i)
460         ui.destination_list->addItem(m_view.destination_list->itemIcon(i), m_view.destination_list->itemText(i), m_view.destination_list->itemData(i, Qt::UserRole));
461
462     ui.destination_list->setCurrentIndex(m_view.destination_list->currentIndex());
463     QString dest = ui.destination_list->itemData(ui.destination_list->currentIndex(), Qt::UserRole).toString();
464
465     QString customGroup = m_view.format_list->currentItem()->text();
466     if (customGroup.isEmpty()) customGroup = i18nc("Group Name", "Custom");
467     ui.group_name->setText(customGroup);
468
469     QStringList arguments = m_view.advanced_params->toPlainText().split(' ', QString::SkipEmptyParts);
470     ui.parameters->setText(arguments.join(" "));
471     ui.extension->setText(m_view.size_list->currentItem()->data(ExtensionRole).toString());
472     ui.profile_name->setFocus();
473     QListWidgetItem *item = m_view.size_list->currentItem();
474     if (ui.parameters->toPlainText().contains("%bitrate")) {
475         if ( item && item->data(BitratesRole).canConvert(QVariant::StringList) && item->data(BitratesRole).toStringList().count()) {
476             QStringList bitrates = item->data(BitratesRole).toStringList();
477             ui.vbitrates_list->setText(bitrates.join(","));
478             if (item->data(DefaultBitrateRole).canConvert(QVariant::String))
479                 ui.default_vbitrate->setValue(item->data(DefaultBitrateRole).toInt());
480         }
481     }
482     else ui.vbitrates->setHidden(true);
483     if (ui.parameters->toPlainText().contains("%audiobitrate")) {
484       if ( item && item->data(AudioBitratesRole).canConvert(QVariant::StringList) && item->data(AudioBitratesRole).toStringList().count()) {
485             QStringList bitrates = item->data(AudioBitratesRole).toStringList();
486             ui.abitrates_list->setText(bitrates.join(","));
487             if (item->data(DefaultAudioBitrateRole).canConvert(QVariant::String))
488                 ui.default_abitrate->setValue(item->data(DefaultAudioBitrateRole).toInt());
489         }
490     }
491     else ui.abitrates->setHidden(true);
492
493     if (d->exec() == QDialog::Accepted && !ui.profile_name->text().simplified().isEmpty()) {
494         QString newProfileName = ui.profile_name->text().simplified();
495         QString newGroupName = ui.group_name->text().simplified();
496         if (newGroupName.isEmpty()) newGroupName = i18nc("Group Name", "Custom");
497         QString newMetaGroupId = ui.destination_list->itemData(ui.destination_list->currentIndex(), Qt::UserRole).toString();
498
499         QDomDocument doc;
500         QDomElement profileElement = doc.createElement("profile");
501         profileElement.setAttribute("name", newProfileName);
502         profileElement.setAttribute("category", newGroupName);
503         profileElement.setAttribute("destinationid", newMetaGroupId);
504         profileElement.setAttribute("extension", ui.extension->text().simplified());
505         QString args = ui.parameters->toPlainText().simplified();
506         profileElement.setAttribute("args", args);
507         if (args.contains("%bitrate")) {
508             // profile has a variable bitrate
509             profileElement.setAttribute("defaultbitrate", QString::number(ui.default_vbitrate->value()));
510             profileElement.setAttribute("bitrates", ui.vbitrates_list->text());
511         }
512         if (args.contains("%audiobitrate")) {
513             // profile has a variable bitrate
514             profileElement.setAttribute("defaultaudiobitrate", QString::number(ui.default_abitrate->value()));
515             profileElement.setAttribute("audiobitrates", ui.abitrates_list->text());
516         }
517         doc.appendChild(profileElement);
518         saveProfile(doc.documentElement());
519
520         parseProfiles(newMetaGroupId, newGroupName, newProfileName);
521     }
522     delete d;
523 }
524
525
526 void RenderWidget::saveProfile(const QDomElement &newprofile)
527 {
528     QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
529     QDomDocument doc;
530     QFile file(exportFile);
531     doc.setContent(&file, false);
532     file.close();
533     QDomElement documentElement;
534     QDomElement profiles = doc.documentElement();
535     if (profiles.isNull() || profiles.tagName() != "profiles") {
536         doc.clear();
537         profiles = doc.createElement("profiles");
538         profiles.setAttribute("version", 1);
539         doc.appendChild(profiles);
540     }
541     int version = profiles.attribute("version", 0).toInt();
542     if (version < 1) {
543         kDebug() << "// OLD profile version";
544         doc.clear();
545         profiles = doc.createElement("profiles");
546         profiles.setAttribute("version", 1);
547         doc.appendChild(profiles);
548     }
549
550
551     QDomNodeList profilelist = doc.elementsByTagName("profile");
552     int i = 0;
553     while (!profilelist.item(i).isNull()) {
554         // make sure a profile with same name doesn't exist
555         documentElement = profilelist.item(i).toElement();
556         QString profileName = documentElement.attribute("name");
557         if (profileName == newprofile.attribute("name")) {
558             // a profile with that same name already exists
559             bool ok;
560             QString newProfileName = QInputDialog::getText(this, i18n("Profile already exists"), i18n("This profile name already exists. Change the name if you don't want to overwrite it."), QLineEdit::Normal, profileName, &ok);
561             if (!ok) return;
562             if (profileName == newProfileName) {
563                 profiles.removeChild(profilelist.item(i));
564                 break;
565             }
566         }
567         ++i;
568     }
569
570     profiles.appendChild(newprofile);
571
572     //QCString save = doc.toString().utf8();
573
574     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
575         KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
576         return;
577     }
578     QTextStream out(&file);
579     out << doc.toString();
580     if (file.error() != QFile::NoError) {
581         KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
582         file.close();
583         return;
584     }
585     file.close();
586 }
587
588 void RenderWidget::slotCopyToFavorites()
589 {
590     QListWidgetItem *item = m_view.size_list->currentItem();
591     if (!item)
592         return;
593     QString currentGroup = m_view.format_list->currentItem()->text();
594
595     QString params = item->data(ParamsRole).toString();
596     QString extension = item->data(ExtensionRole).toString();
597     QString currentProfile = item->text();
598     QDomDocument doc;
599     QDomElement profileElement = doc.createElement("profile");
600     profileElement.setAttribute("name", currentProfile);
601     profileElement.setAttribute("category", i18nc("Category Name", "Custom"));
602     profileElement.setAttribute("destinationid", "favorites");
603     profileElement.setAttribute("extension", extension);
604     profileElement.setAttribute("args", params);
605     profileElement.setAttribute("bitrates", item->data(BitratesRole).toStringList().join(","));
606     profileElement.setAttribute("defaultbitrate", item->data(DefaultBitrateRole).toString());
607     profileElement.setAttribute("audiobitrates", item->data(AudioBitratesRole).toStringList().join(","));
608     profileElement.setAttribute("defaultaudiobitrate", item->data(DefaultAudioBitrateRole).toString());
609     doc.appendChild(profileElement);
610     saveProfile(doc.documentElement());
611     parseProfiles(m_view.destination_list->itemData(m_view.destination_list->currentIndex(), Qt::UserRole).toString(), currentGroup, currentProfile);
612 }
613
614 void RenderWidget::slotEditProfile()
615 {
616     QListWidgetItem *item = m_view.size_list->currentItem();
617     if (!item) return;
618     QString currentGroup = m_view.format_list->currentItem()->text();
619
620     QString params = item->data(ParamsRole).toString();
621     QString extension = item->data(ExtensionRole).toString();
622     QString currentProfile = item->text();
623
624     Ui::SaveProfile_UI ui;
625     QDialog *d = new QDialog(this);
626     ui.setupUi(d);
627
628     for (int i = 0; i < m_view.destination_list->count(); ++i)
629         ui.destination_list->addItem(m_view.destination_list->itemIcon(i), m_view.destination_list->itemText(i), m_view.destination_list->itemData(i, Qt::UserRole));
630
631     ui.destination_list->setCurrentIndex(m_view.destination_list->currentIndex());
632     QString dest = ui.destination_list->itemData(ui.destination_list->currentIndex(), Qt::UserRole).toString();
633
634     QString customGroup = m_view.format_list->currentItem()->text();
635     if (customGroup.isEmpty()) customGroup = i18nc("Group Name", "Custom");
636     ui.group_name->setText(customGroup);
637
638     ui.profile_name->setText(currentProfile);
639     ui.extension->setText(extension);
640     ui.parameters->setText(params);
641     ui.profile_name->setFocus();
642     if (ui.parameters->toPlainText().contains("%bitrate")) {
643         if ( item->data(BitratesRole).canConvert(QVariant::StringList) && item->data(BitratesRole).toStringList().count()) {
644             QStringList bitrates = item->data(BitratesRole).toStringList();
645             ui.vbitrates_list->setText(bitrates.join(","));
646             if (item->data(DefaultBitrateRole).canConvert(QVariant::String))
647                 ui.default_vbitrate->setValue(item->data(DefaultBitrateRole).toInt());
648         }
649     } else {
650         ui.vbitrates->setHidden(true);
651     }
652
653     if (ui.parameters->toPlainText().contains("%audiobitrate")) {
654       if ( item->data(AudioBitratesRole).canConvert(QVariant::StringList) && item->data(AudioBitratesRole).toStringList().count()) {
655             QStringList bitrates = item->data(AudioBitratesRole).toStringList();
656             ui.abitrates_list->setText(bitrates.join(","));
657             if (item->data(DefaultAudioBitrateRole).canConvert(QVariant::String))
658                 ui.default_abitrate->setValue(item->data(DefaultAudioBitrateRole).toInt());
659         }
660     }
661     else ui.abitrates->setHidden(true);
662     
663     d->setWindowTitle(i18n("Edit Profile"));
664     if (d->exec() == QDialog::Accepted) {
665         slotDeleteProfile(false);
666         QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
667         QDomDocument doc;
668         QFile file(exportFile);
669         doc.setContent(&file, false);
670         file.close();
671         QDomElement documentElement;
672         QDomElement profiles = doc.documentElement();
673
674         if (profiles.isNull() || profiles.tagName() != "profiles") {
675             doc.clear();
676             profiles = doc.createElement("profiles");
677             profiles.setAttribute("version", 1);
678             doc.appendChild(profiles);
679         }
680
681         int version = profiles.attribute("version", 0).toInt();
682         if (version < 1) {
683             kDebug() << "// OLD profile version";
684             doc.clear();
685             profiles = doc.createElement("profiles");
686             profiles.setAttribute("version", 1);
687             doc.appendChild(profiles);
688         }
689
690         QString newProfileName = ui.profile_name->text().simplified();
691         QString newGroupName = ui.group_name->text().simplified();
692         if (newGroupName.isEmpty()) newGroupName = i18nc("Group Name", "Custom");
693         QString newMetaGroupId = ui.destination_list->itemData(ui.destination_list->currentIndex(), Qt::UserRole).toString();
694         QDomNodeList profilelist = doc.elementsByTagName("profile");
695         int i = 0;
696         while (!profilelist.item(i).isNull()) {
697             // make sure a profile with same name doesn't exist
698             documentElement = profilelist.item(i).toElement();
699             QString profileName = documentElement.attribute("name");
700             if (profileName == newProfileName) {
701                 // a profile with that same name already exists
702                 bool ok;
703                 newProfileName = QInputDialog::getText(this, i18n("Profile already exists"), i18n("This profile name already exists. Change the name if you don't want to overwrite it."), QLineEdit::Normal, newProfileName, &ok);
704                 if (!ok) return;
705                 if (profileName == newProfileName) {
706                     profiles.removeChild(profilelist.item(i));
707                     break;
708                 }
709             }
710             ++i;
711         }
712
713         QDomElement profileElement = doc.createElement("profile");
714         profileElement.setAttribute("name", newProfileName);
715         profileElement.setAttribute("category", newGroupName);
716         profileElement.setAttribute("destinationid", newMetaGroupId);
717         profileElement.setAttribute("extension", ui.extension->text().simplified());
718         QString args = ui.parameters->toPlainText().simplified();
719         profileElement.setAttribute("args", args);
720         if (args.contains("%bitrate")) {
721             // profile has a variable bitrate
722             profileElement.setAttribute("defaultbitrate", QString::number(ui.default_vbitrate->value()));
723             profileElement.setAttribute("bitrates", ui.vbitrates_list->text());
724         }
725         if (args.contains("%audiobitrate")) {
726             // profile has a variable bitrate
727             profileElement.setAttribute("defaultaudiobitrate", QString::number(ui.default_abitrate->value()));
728             profileElement.setAttribute("audiobitrates", ui.abitrates_list->text());
729         }
730
731         profiles.appendChild(profileElement);
732
733         //QCString save = doc.toString().utf8();
734         delete d;
735         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
736             KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
737             return;
738         }
739         QTextStream out(&file);
740         out << doc.toString();
741         if (file.error() != QFile::NoError) {
742             KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
743             file.close();
744             return;
745         }
746         file.close();
747         parseProfiles(newMetaGroupId, newGroupName, newProfileName);
748     } else delete d;
749 }
750
751 void RenderWidget::slotDeleteProfile(bool refresh)
752 {
753     //TODO: delete a profile installed by KNewStuff the easy way
754     /*
755     QString edit = m_view.size_list->currentItem()->data(EditableRole).toString();
756     if (!edit.endsWith("customprofiles.xml")) {
757         // This is a KNewStuff installed file, process through KNS
758         KNS::Engine engine(0);
759         if (engine.init("kdenlive_render.knsrc")) {
760             KNS::Entry::List entries;
761         }
762         return;
763     }*/
764     QString currentGroup = m_view.format_list->currentItem()->text();
765     QString currentProfile = m_view.size_list->currentItem()->text();
766     QString metaGroupId = m_view.destination_list->itemData(m_view.destination_list->currentIndex(), Qt::UserRole).toString();
767
768     QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
769     QDomDocument doc;
770     QFile file(exportFile);
771     doc.setContent(&file, false);
772     file.close();
773
774     QDomElement documentElement;
775     QDomNodeList profiles = doc.elementsByTagName("profile");
776     int i = 0;
777     QString groupName;
778     QString profileName;
779     QString destination;
780
781     while (!profiles.item(i).isNull()) {
782         documentElement = profiles.item(i).toElement();
783         profileName = documentElement.attribute("name");
784         groupName = documentElement.attribute("category");
785         destination = documentElement.attribute("destinationid");
786
787         if (profileName == currentProfile && groupName == currentGroup && destination == metaGroupId) {
788             kDebug() << "// GOT it: " << profileName;
789             doc.documentElement().removeChild(profiles.item(i));
790             break;
791         }
792         ++i;
793     }
794
795     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
796         KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
797         return;
798     }
799     QTextStream out(&file);
800     out << doc.toString();
801     if (file.error() != QFile::NoError) {
802         KMessageBox::error(this, i18n("Cannot write to file %1", exportFile));
803         file.close();
804         return;
805     }
806     file.close();
807     if (refresh) {
808         parseProfiles(metaGroupId, currentGroup);
809         focusFirstVisibleItem();
810     }
811 }
812
813 void RenderWidget::updateButtons()
814 {
815     if (!m_view.size_list->currentItem() || m_view.size_list->currentItem()->isHidden()) {
816         m_view.buttonSave->setEnabled(false);
817         m_view.buttonDelete->setEnabled(false);
818         m_view.buttonEdit->setEnabled(false);
819         m_view.buttonRender->setEnabled(false);
820         m_view.buttonGenerateScript->setEnabled(false);
821     } else {
822         m_view.buttonSave->setEnabled(true);
823         m_view.buttonRender->setEnabled(m_view.size_list->currentItem()->toolTip().isEmpty());
824         m_view.buttonGenerateScript->setEnabled(m_view.size_list->currentItem()->toolTip().isEmpty());
825         QString edit = m_view.size_list->currentItem()->data(EditableRole).toString();
826         if (edit.isEmpty() || !edit.endsWith("customprofiles.xml")) {
827             m_view.buttonDelete->setEnabled(false);
828             m_view.buttonEdit->setEnabled(false);
829         } else {
830             m_view.buttonDelete->setEnabled(true);
831             m_view.buttonEdit->setEnabled(true);
832         }
833     }
834 }
835
836
837 void RenderWidget::focusFirstVisibleItem(const QString &profile)
838 {
839     if (!profile.isEmpty()) {
840         QList <QListWidgetItem *> child = m_view.size_list->findItems(profile, Qt::MatchExactly);
841         if (!child.isEmpty())
842             m_view.size_list->setCurrentItem(child.at(0));
843     }
844     if (m_view.size_list->currentItem()) {
845         updateButtons();
846         return;
847     }
848     m_view.size_list->setCurrentRow(0);
849     updateButtons();
850 }
851
852 void RenderWidget::slotPrepareExport(bool scriptExport)
853 {
854     if (!QFile::exists(KdenliveSettings::rendererpath())) {
855         KMessageBox::sorry(this, i18n("Cannot find the melt program required for rendering (part of Mlt)"));
856         return;
857     }
858     if (m_view.play_after->isChecked() && KdenliveSettings::defaultplayerapp().isEmpty()) {
859         KMessageBox::sorry(this, i18n("Cannot play video after rendering because the default video player application is not set.\nPlease define it in Kdenlive settings dialog."));
860     }
861     QString chapterFile;
862     if (m_view.create_chapter->isChecked()) chapterFile = m_view.out_file->url().path() + ".dvdchapter";
863
864     // mantisbt 1051
865     if (!KStandardDirs::makeDir(m_view.out_file->url().directory())) {
866         KMessageBox::sorry(this, i18n("The directory %1, could not be created.\nPlease make sure you have the required permissions.", m_view.out_file->url().directory()));
867         return;
868     }
869
870     emit prepareRenderingData(scriptExport, m_view.render_zone->isChecked(), chapterFile);
871 }
872
873
874 void RenderWidget::slotExport(bool scriptExport, int zoneIn, int zoneOut, const QMap<QString, QString> &metadata, const QString &playlistPath, const QString &scriptPath, bool exportAudio)
875 {
876     QListWidgetItem *item = m_view.size_list->currentItem();
877     if (!item)
878         return;
879
880     QString dest = m_view.out_file->url().path().trimmed();
881     if (dest.isEmpty())
882         return;
883
884     // Check whether target file has an extension.
885     // If not, ask whether extension should be added or not.
886     QString extension = item->data(ExtensionRole).toString();
887     if (!dest.endsWith(extension, Qt::CaseInsensitive)) {
888         if (KMessageBox::questionYesNo(this, i18n("File has no extension. Add extension (%1)?", extension)) == KMessageBox::Yes) {
889             dest.append('.' + extension);
890         }
891     }
892
893     QFile f(dest);
894     if (f.exists()) {
895         if (KMessageBox::warningYesNo(this, i18n("Output file already exists. Do you want to overwrite it?")) != KMessageBox::Yes)
896             return;
897     }
898
899     QStringList overlayargs;
900     if (m_view.tc_overlay->isChecked()) {
901         QString filterFile = KStandardDirs::locate("appdata", "metadata.properties");
902         overlayargs << "meta.attr.timecode=1" << "meta.attr.timecode.markup=#" + QString(m_view.tc_type->currentIndex() ? "frame" : "timecode");
903         overlayargs << "-attach" << "data_feed:attr_check" << "-attach";
904         overlayargs << "data_show:" + filterFile << "_loader=1" << "dynamic=1";
905     }
906
907     QStringList render_process_args;
908
909     if (!scriptExport) render_process_args << "-erase";
910     if (KdenliveSettings::usekuiserver()) render_process_args << "-kuiserver";
911
912     // get process id
913     render_process_args << QString("-pid:%1").arg(QCoreApplication::applicationPid());
914
915     // Set locale for render process if required
916     if (QLocale().decimalPoint() != QLocale::system().decimalPoint()) {
917         const QString currentLocale = setlocale(LC_NUMERIC, NULL);
918         render_process_args << QString("-locale:%1").arg(currentLocale);
919     }
920
921     double guideStart = 0;
922     double guideEnd = 0;
923
924     if (m_view.render_zone->isChecked()) render_process_args << "in=" + QString::number(zoneIn) << "out=" + QString::number(zoneOut);
925     else if (m_view.render_guide->isChecked()) {
926         double fps = (double) m_profile.frame_rate_num / m_profile.frame_rate_den;
927         guideStart = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
928         guideEnd = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
929         render_process_args << "in=" + QString::number((int) GenTime(guideStart).frames(fps)) << "out=" + QString::number((int) GenTime(guideEnd).frames(fps));
930     }
931
932     if (!overlayargs.isEmpty()) render_process_args << "preargs=" + overlayargs.join(" ");
933
934     if (scriptExport)
935         render_process_args << "$MELT";
936     else
937         render_process_args << KdenliveSettings::rendererpath();
938     render_process_args << m_profile.path << item->data(RenderRole).toString();
939     if (m_view.play_after->isChecked()) render_process_args << KdenliveSettings::KdenliveSettings::defaultplayerapp();
940     else render_process_args << "-";
941
942     QString renderArgs = m_view.advanced_params->toPlainText().simplified();
943     
944     // Project metadata
945     if (m_view.export_meta->isChecked()) {
946         QMap<QString, QString>::const_iterator i = metadata.constBegin();
947         while (i != metadata.constEnd()) {
948             renderArgs.append(QString(" %1=%2").arg(i.key()).arg(QString(QUrl::toPercentEncoding(i.value()))));
949             ++i;
950         }
951     }
952
953     // Adjust frame scale
954     int width;
955     int height;
956     if (m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
957         width = m_view.rescale_width->value();
958         height = m_view.rescale_height->value();
959     } else {
960         width = m_profile.width;
961         height = m_profile.height;
962     }
963
964     //renderArgs.replace("%width", QString::number((int)(m_profile.height * m_profile.display_aspect_num / (double) m_profile.display_aspect_den + 0.5)));
965     //renderArgs.replace("%height", QString::number((int)m_profile.height));
966
967     // Adjust scanning
968     if (m_view.scanning_list->currentIndex() == 1) renderArgs.append(" progressive=1");
969     else if (m_view.scanning_list->currentIndex() == 2) renderArgs.append(" progressive=0");
970
971     // disable audio if requested
972     if (!exportAudio) renderArgs.append(" an=1 ");
973
974     // Set the thread counts
975     if (!renderArgs.contains("threads=")) {
976         renderArgs.append(QString(" threads=%1").arg(KdenliveSettings::encodethreads()));
977     }
978     renderArgs.append(QString(" real_time=-%1").arg(KdenliveSettings::mltthreads()));
979
980     // Check if the rendering profile is different from project profile,
981     // in which case we need to use the producer_comsumer from MLT
982     QString std = renderArgs;
983     QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
984     const QString currentSize = QString::number(width) + 'x' + QString::number(height);
985     QString subsize = currentSize;
986     if (std.startsWith("s=")) {
987         subsize = std.section(' ', 0, 0).toLower();
988         subsize = subsize.section('=', 1, 1);
989     } else if (std.contains(" s=")) {
990         subsize = std.section(" s=", 1, 1);
991         subsize = subsize.section(' ', 0, 0).toLower();
992     } else if (destination != "audioonly" && m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
993         subsize = QString(" s=%1x%2").arg(width).arg(height);
994         // Add current size parameter
995         renderArgs.append(subsize);
996     }
997     bool resizeProfile = (subsize != currentSize);
998     QStringList paramsList = renderArgs.split(' ', QString::SkipEmptyParts);
999
1000     QScriptEngine sEngine;
1001     sEngine.globalObject().setProperty("bitrate", m_view.comboBitrates->currentText().toInt());
1002     sEngine.globalObject().setProperty("audiobitrate", m_view.comboAudioBitrates->currentText().toInt());
1003     sEngine.globalObject().setProperty("dar", '@' + QString::number(m_profile.display_aspect_num) + '/' + QString::number(m_profile.display_aspect_den));
1004     sEngine.globalObject().setProperty("passes", static_cast<int>(m_view.checkTwoPass->isChecked()) + 1);
1005
1006     for (int i = 0; i < paramsList.count(); ++i) {
1007         QString paramName = paramsList.at(i).section('=', 0, 0);
1008         QString paramValue = paramsList.at(i).section('=', 1, 1);
1009         // If the profiles do not match we need to use the consumer tag
1010         if (paramName == "mlt_profile=" && paramValue != m_profile.path) {
1011             resizeProfile = true;
1012         }
1013         // evaluate expression
1014         if (paramValue.startsWith('%')) {
1015             paramValue = sEngine.evaluate(paramValue.remove(0, 1)).toString();
1016             paramsList[i] = paramName + '=' + paramValue;
1017         }
1018         sEngine.globalObject().setProperty(paramName.toUtf8().constData(), paramValue);
1019     }
1020
1021     if (resizeProfile)
1022         render_process_args << "consumer:" + (scriptExport ? "$SOURCE" : playlistPath);
1023     else
1024         render_process_args <<  (scriptExport ? "$SOURCE" : playlistPath);
1025
1026     render_process_args << (scriptExport ? "$TARGET" : KUrl(dest).url());
1027     render_process_args << paramsList;
1028
1029     QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString();
1030
1031     //QString scriptName;
1032     if (scriptExport) {
1033         // Generate script file
1034         QFile file(scriptPath);
1035         if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
1036             KMessageBox::error(this, i18n("Cannot write to file %1", scriptPath));
1037             return;
1038         }
1039         QTextStream outStream(&file);
1040         outStream << "#! /bin/sh" << "\n" << "\n";
1041         outStream << "SOURCE=" << "\"" + QUrl(playlistPath).toEncoded() + "\"" << "\n";
1042         outStream << "TARGET=" << "\"" + QUrl(dest).toEncoded() + "\"" << "\n";
1043         outStream << "RENDERER=" << "\"" + m_renderer + "\"" << "\n";
1044         outStream << "MELT=" << "\"" + KdenliveSettings::rendererpath() + "\"" << "\n";
1045         outStream << "PARAMETERS=" << "\"" + render_process_args.join(" ") + "\"" << "\n";
1046         outStream << "$RENDERER $PARAMETERS" << "\n" << "\n";
1047         if (file.error() != QFile::NoError) {
1048             KMessageBox::error(this, i18n("Cannot write to file %1", scriptPath));
1049             file.close();
1050             return;
1051         }
1052         file.close();
1053         QFile::setPermissions(scriptPath, file.permissions() | QFile::ExeUser);
1054
1055         QTimer::singleShot(400, this, SLOT(parseScriptFiles()));
1056         m_view.tabWidget->setCurrentIndex(2);
1057         return;
1058     }
1059
1060     // Save rendering profile to document
1061     QMap <QString, QString> renderProps;
1062     renderProps.insert("renderdestination", m_view.size_list->currentItem()->data(MetaGroupRole).toString());
1063     renderProps.insert("rendercategory", m_view.size_list->currentItem()->data(GroupRole).toString());
1064     renderProps.insert("renderprofile", m_view.size_list->currentItem()->text());
1065     renderProps.insert("renderurl", dest);
1066     renderProps.insert("renderzone", QString::number(m_view.render_zone->isChecked()));
1067     renderProps.insert("renderguide", QString::number(m_view.render_guide->isChecked()));
1068     renderProps.insert("renderstartguide", QString::number(m_view.guide_start->currentIndex()));
1069     renderProps.insert("renderendguide", QString::number(m_view.guide_end->currentIndex()));
1070     renderProps.insert("renderendguide", QString::number(m_view.guide_end->currentIndex()));
1071     renderProps.insert("renderscanning", QString::number(m_view.scanning_list->currentIndex()));
1072     int export_audio = 0;
1073     if (m_view.export_audio->checkState() == Qt::Checked)
1074         export_audio = 2;
1075     else if (m_view.export_audio->checkState() == Qt::Unchecked)
1076         export_audio = 1;
1077     renderProps.insert("renderexportaudio", QString::number(export_audio));
1078     renderProps.insert("renderrescale", QString::number(m_view.rescale->isChecked()));
1079     renderProps.insert("renderrescalewidth", QString::number(m_view.rescale_width->value()));
1080     renderProps.insert("renderrescaleheight", QString::number(m_view.rescale_height->value()));
1081     renderProps.insert("rendertcoverlay", QString::number(m_view.tc_overlay->isChecked()));
1082     renderProps.insert("rendertctype", QString::number(m_view.tc_type->currentIndex()));
1083     renderProps.insert("renderratio", QString::number(m_view.rescale_keep->isChecked()));
1084     renderProps.insert("renderplay", QString::number(m_view.play_after->isChecked()));
1085     renderProps.insert("rendertwopass", QString::number(m_view.checkTwoPass->isChecked()));
1086
1087     emit selectedRenderProfile(renderProps);
1088
1089     // insert item in running jobs list
1090     RenderJobItem *renderItem = NULL;
1091     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
1092     if (!existing.isEmpty()) {
1093         renderItem = static_cast<RenderJobItem*> (existing.at(0));
1094         if (renderItem->status() == RUNNINGJOB || renderItem->status() == WAITINGJOB) {
1095             KMessageBox::information(this, i18n("There is already a job writing file:<br /><b>%1</b><br />Abort the job if you want to overwrite it...", dest), i18n("Already running"));
1096             return;
1097         }
1098         if (renderItem->type() != DirectRenderType) {
1099             delete renderItem;
1100             renderItem = NULL;
1101         }
1102         else {
1103             renderItem->setData(1, ProgressRole, 0);
1104             renderItem->setStatus(WAITINGJOB);
1105             renderItem->setIcon(0, KIcon("media-playback-pause"));
1106             renderItem->setData(1, Qt::UserRole, i18n("Waiting..."));
1107             renderItem->setData(1, ParametersRole, dest);
1108         }
1109     }
1110     if (!renderItem) renderItem = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << dest);    
1111     renderItem->setData(1, TimeRole, QTime::currentTime());
1112
1113     // Set rendering type
1114     if (group == "dvd") {
1115         if (m_view.open_dvd->isChecked()) {
1116             renderItem->setData(0, Qt::UserRole, group);
1117             if (renderArgs.contains("mlt_profile=")) {
1118                 //TODO: probably not valid anymore (no more MLT profiles in args)
1119                 // rendering profile contains an MLT profile, so pass it to the running jog item, useful for dvd
1120                 QString prof = renderArgs.section("mlt_profile=", 1, 1);
1121                 prof = prof.section(' ', 0, 0);
1122                 kDebug() << "// render profile: " << prof;
1123                 renderItem->setMetadata(prof);
1124             }
1125         }
1126     } else {
1127         if (group == "websites" && m_view.open_browser->isChecked()) {
1128             renderItem->setData(0, Qt::UserRole, group);
1129             // pass the url
1130             QString url = m_view.size_list->currentItem()->data(ExtraRole).toString();
1131             renderItem->setMetadata(url);
1132         }
1133     }
1134
1135     renderItem->setData(1, ParametersRole, render_process_args);
1136     if (exportAudio == false)
1137         renderItem->setData(1, ExtraInfoRole, i18n("Video without audio track"));
1138     else
1139         renderItem->setData(1, ExtraInfoRole, QString());
1140     m_view.running_jobs->setCurrentItem(renderItem);
1141     m_view.tabWidget->setCurrentIndex(1);
1142     checkRenderStatus();
1143 }
1144
1145 void RenderWidget::checkRenderStatus()
1146 {
1147     // check if we have a job waiting to render
1148     if (m_blockProcessing) return;
1149     RenderJobItem* item = static_cast<RenderJobItem*> (m_view.running_jobs->topLevelItem(0));
1150     
1151     // Make sure no other rendering is running
1152     while (item) {
1153         if (item->status() == RUNNINGJOB) return;
1154         item = static_cast<RenderJobItem*> (m_view.running_jobs->itemBelow(item));
1155     }
1156     item = static_cast<RenderJobItem*> (m_view.running_jobs->topLevelItem(0));
1157     bool waitingJob = false;
1158     
1159     // Find first waiting job
1160     while (item) {
1161         if (item->status() == WAITINGJOB) {
1162             item->setData(1, TimeRole, QTime::currentTime());
1163             waitingJob = true;
1164             startRendering(item);
1165             break;
1166         }
1167         item = static_cast<RenderJobItem*> (m_view.running_jobs->itemBelow(item));
1168     }
1169     if (waitingJob == false && m_view.shutdown->isChecked()) emit shutdown();
1170 }
1171
1172 void RenderWidget::startRendering(RenderJobItem *item)
1173 {
1174     if (item->type() == DirectRenderType) {
1175         // Normal render process
1176         kDebug()<<"// Normal process";
1177         if (QProcess::startDetached(m_renderer, item->data(1, ParametersRole).toStringList()) == false) {
1178             item->setStatus(FAILEDJOB);
1179         } else {
1180             KNotification::event("RenderStarted", i18n("Rendering <i>%1</i> started", item->text(1)), QPixmap(), this);
1181         }
1182     } else if (item->type() == ScriptRenderType){
1183         // Script item
1184         kDebug()<<"// SCRIPT process: "<<item->data(1, ParametersRole).toString();
1185         if (QProcess::startDetached('"' + item->data(1, ParametersRole).toString() + '"') == false) {
1186             item->setStatus(FAILEDJOB);
1187         }
1188     }
1189 }
1190
1191
1192 int RenderWidget::waitingJobsCount() const
1193 {
1194     int count = 0;
1195     RenderJobItem* item = static_cast<RenderJobItem*> (m_view.running_jobs->topLevelItem(0));
1196     while (item) {
1197         if (item->status() == WAITINGJOB) count++;
1198         item = static_cast<RenderJobItem*>(m_view.running_jobs->itemBelow(item));
1199     }
1200     return count;
1201 }
1202
1203 void RenderWidget::setProfile(const MltVideoProfile &profile)
1204 {
1205     m_view.scanning_list->setCurrentIndex(0);
1206     m_view.rescale_width->setValue(KdenliveSettings::defaultrescalewidth());
1207     if (!m_view.rescale_keep->isChecked()) {
1208         m_view.rescale_height->blockSignals(true);
1209         m_view.rescale_height->setValue(KdenliveSettings::defaultrescaleheight());
1210         m_view.rescale_height->blockSignals(false);
1211     }
1212     if (m_profile != profile) {
1213         m_profile = profile;
1214         refreshView();
1215     }
1216 }
1217
1218
1219
1220 void RenderWidget::refreshCategory(const QString &group, const QString &profile)
1221 {
1222     m_view.format_list->blockSignals(true);
1223     m_view.format_list->clear();
1224     QListWidgetItem *sizeItem;
1225     
1226     QString destination;
1227     if (m_view.destination_list->currentIndex() > 0)
1228         destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
1229
1230     if (destination == "dvd") {
1231         m_view.open_dvd->setVisible(true);
1232         m_view.create_chapter->setVisible(true);
1233     } else {
1234         m_view.open_dvd->setVisible(false);
1235         m_view.create_chapter->setVisible(false);
1236     }
1237
1238     if (destination == "websites")
1239         m_view.open_browser->setVisible(true);
1240     else
1241         m_view.open_browser->setVisible(false);
1242
1243     // hide groups that are not in the correct destination
1244     for (int i = 0; i < m_renderCategory.count(); ++i) {
1245         sizeItem = m_renderCategory.at(i);
1246         if (sizeItem->data(MetaGroupRole).toString() == destination) {
1247             m_view.format_list->addItem(sizeItem->clone());
1248             //kDebug() << "// SET GRP:: " << sizeItem->text() << ", METY:" << sizeItem->data(MetaGroupRole).toString();
1249         }
1250     }
1251
1252     // activate requested item or first visible
1253     QList<QListWidgetItem *> child;
1254     if (!group.isEmpty()) child = m_view.format_list->findItems(group, Qt::MatchExactly);
1255     if (!child.isEmpty()) {
1256         m_view.format_list->setCurrentItem(child.at(0));
1257         child.clear();
1258     } else m_view.format_list->setCurrentRow(0);
1259     QListWidgetItem * item = m_view.format_list->currentItem();
1260     m_view.format_list->blockSignals(false);
1261     if (!item) {
1262         m_view.format_list->setEnabled(false);
1263         m_view.format_list->clear();
1264         m_view.size_list->setEnabled(false);
1265         m_view.size_list->clear();
1266         m_view.size_list->blockSignals(false);
1267         m_view.format_list->blockSignals(false);
1268         return;
1269     } else {
1270         m_view.format_list->setEnabled(true);
1271         m_view.size_list->setEnabled(true);
1272     }
1273
1274     if (m_view.format_list->count() > 1)
1275         m_view.format_list->setVisible(true);
1276     else
1277         m_view.format_list->setVisible(false);
1278
1279     refreshView(profile);
1280 }
1281
1282 void RenderWidget::refreshView(const QString &profile)
1283 {
1284     if (!m_view.format_list->currentItem()) return;
1285     m_view.size_list->blockSignals(true);
1286     m_view.size_list->clear();
1287     QListWidgetItem *sizeItem;
1288     QString std;
1289     QString group = m_view.format_list->currentItem()->text();
1290     QString destination;
1291     if (m_view.destination_list->currentIndex() > 0)
1292         destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
1293     KIcon brokenIcon("dialog-close");
1294     KIcon warningIcon("dialog-warning");
1295
1296     QStringList formatsList;
1297     QStringList vcodecsList;
1298     QStringList acodecsList;
1299     if (!KdenliveSettings::bypasscodeccheck()) {
1300         formatsList= KdenliveSettings::supportedformats();
1301         vcodecsList = KdenliveSettings::videocodecs();
1302         acodecsList = KdenliveSettings::audiocodecs();
1303     }
1304
1305     KColorScheme scheme(palette().currentColorGroup(), KColorScheme::Window);
1306     const QColor disabled = scheme.foreground(KColorScheme::InactiveText).color();
1307     const QColor disabledbg = scheme.background(KColorScheme::NegativeBackground).color();
1308
1309     double project_framerate = (double) m_profile.frame_rate_num / m_profile.frame_rate_den;
1310     for (int i = 0; i < m_renderItems.count(); ++i) {
1311         sizeItem = m_renderItems.at(i);
1312         QListWidgetItem *dupItem = NULL;
1313         if ((sizeItem->data(GroupRole).toString() == group || sizeItem->data(GroupRole).toString().isEmpty()) && sizeItem->data(MetaGroupRole).toString() == destination) {
1314             std = sizeItem->data(StandardRole).toString();
1315             if (!m_view.show_all_profiles->isChecked() && !std.isEmpty()) {
1316                 if ((std.contains("PAL", Qt::CaseInsensitive) && m_profile.frame_rate_num == 25 && m_profile.frame_rate_den == 1) ||
1317                     (std.contains("NTSC", Qt::CaseInsensitive) && m_profile.frame_rate_num == 30000 && m_profile.frame_rate_den == 1001))
1318                     dupItem = sizeItem->clone();
1319             } else {
1320                 dupItem = sizeItem->clone();
1321             }
1322
1323             if (dupItem) {
1324                 m_view.size_list->addItem(dupItem);
1325                 std = dupItem->data(ParamsRole).toString();
1326                 // Make sure the selected profile uses the same frame rate as project profile
1327                 if (!m_view.show_all_profiles->isChecked() && std.contains("mlt_profile=")) {
1328                     QString profile = std.section("mlt_profile=", 1, 1).section(' ', 0, 0);
1329                     MltVideoProfile p = ProfilesDialog::getVideoProfile(profile);
1330                     if (p.frame_rate_den > 0) {
1331                         double profile_rate = (double) p.frame_rate_num / p.frame_rate_den;
1332                         if ((int) (1000.0 * profile_rate) != (int) (1000.0 * project_framerate)) {
1333                             dupItem->setToolTip(i18n("Frame rate (%1) not compatible with project profile (%2)", profile_rate, project_framerate));
1334                             dupItem->setIcon(brokenIcon);
1335                             dupItem->setForeground(disabled);
1336                         }
1337                     }
1338                 }
1339                 
1340                 // Make sure the selected profile uses an installed avformat codec / format
1341                 if (!formatsList.isEmpty()) {
1342                     QString format;
1343                     if (std.startsWith("f=")) format = std.section("f=", 1, 1);
1344                     else if (std.contains(" f=")) format = std.section(" f=", 1, 1);
1345                     if (!format.isEmpty()) {
1346                         format = format.section(' ', 0, 0).toLower();
1347                         if (!formatsList.contains(format)) {
1348                             kDebug() << "***** UNSUPPORTED F: " << format;
1349                             //sizeItem->setHidden(true);
1350                             //sizeItem-item>setFlags(Qt::ItemIsSelectable);
1351                             dupItem->setToolTip(i18n("Unsupported video format: %1", format));
1352                             dupItem->setIcon(brokenIcon);
1353                             dupItem->setForeground(disabled);
1354                         }
1355                     }
1356                 }
1357                 if (!acodecsList.isEmpty()) {
1358                     QString format;
1359                     if (std.startsWith("acodec=")) format = std.section("acodec=", 1, 1);
1360                     else if (std.contains(" acodec=")) format = std.section(" acodec=", 1, 1);
1361                     if (!format.isEmpty()) {
1362                         format = format.section(' ', 0, 0).toLower();
1363                         if (!acodecsList.contains(format)) {
1364                             kDebug() << "*****  UNSUPPORTED ACODEC: " << format;
1365                             //sizeItem->setHidden(true);
1366                             //sizeItem->setFlags(Qt::ItemIsSelectable);
1367                             dupItem->setToolTip(i18n("Unsupported audio codec: %1", format));
1368                             dupItem->setIcon(brokenIcon);
1369                             dupItem->setForeground(disabled);
1370                             dupItem->setBackground(disabledbg);
1371                         }
1372                     }
1373                 }
1374                 if (!vcodecsList.isEmpty()) {
1375                     QString format;
1376                     if (std.startsWith("vcodec=")) format = std.section("vcodec=", 1, 1);
1377                     else if (std.contains(" vcodec=")) format = std.section(" vcodec=", 1, 1);
1378                     if (!format.isEmpty()) {
1379                         format = format.section(' ', 0, 0).toLower();
1380                         if (!vcodecsList.contains(format)) {
1381                             kDebug() << "*****  UNSUPPORTED VCODEC: " << format;
1382                             //sizeItem->setHidden(true);
1383                             //sizeItem->setFlags(Qt::ItemIsSelectable);
1384                             dupItem->setToolTip(i18n("Unsupported video codec: %1", format));
1385                             dupItem->setIcon(brokenIcon);
1386                             dupItem->setForeground(disabled);
1387                         }
1388                     }
1389                 }
1390                 if (std.contains(" profile=") || std.startsWith("profile=")) {
1391                     // changed in MLT commit d8a3a5c9190646aae72048f71a39ee7446a3bd45
1392                     // (http://www.mltframework.org/gitweb/mlt.git?p=mltframework.org/mlt.git;a=commit;h=d8a3a5c9190646aae72048f71a39ee7446a3bd45)
1393                     dupItem->setToolTip(i18n("This render profile uses a 'profile' parameter.<br />Unless you know what you are doing you will probably have to change it to 'mlt_profile'."));
1394                     dupItem->setIcon(warningIcon);
1395                 }
1396             }
1397         }
1398     }
1399     focusFirstVisibleItem(profile);
1400     m_view.size_list->blockSignals(false);
1401     if (m_view.size_list->count() > 0) {
1402         refreshParams();
1403     }
1404     else {
1405         // No matching profile
1406         errorMessage(i18n("No matching profile"));
1407         m_view.advanced_params->clear();
1408     }
1409 }
1410
1411 KUrl RenderWidget::filenameWithExtension(KUrl url, const QString &extension)
1412 {
1413     if (url.isEmpty()) url = KUrl(m_projectFolder);
1414     QString directory = url.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
1415     QString filename = url.fileName(KUrl::ObeyTrailingSlash);
1416     QString ext;
1417
1418     if (extension.at(0) == '.') ext = extension;
1419     else ext = '.' + extension;
1420
1421     if (filename.isEmpty()) filename = i18n("untitled");
1422
1423     int pos = filename.lastIndexOf('.');
1424     if (pos == 0) filename.append(ext);
1425     else {
1426         if (!filename.endsWith(ext, Qt::CaseInsensitive)) {
1427             filename = filename.left(pos) + ext;
1428         }
1429     }
1430
1431     return KUrl(directory + filename);
1432 }
1433
1434 void RenderWidget::refreshParams()
1435 {
1436     // Format not available (e.g. codec not installed); Disable start button
1437     QListWidgetItem *item = m_view.size_list->currentItem();
1438     if (!item || item->isHidden()) {
1439         if (!item)
1440             errorMessage(i18n("No matching profile"));
1441         m_view.advanced_params->clear();
1442         m_view.buttonRender->setEnabled(false);
1443         m_view.buttonGenerateScript->setEnabled(false);
1444         return;
1445     }
1446     errorMessage(item->toolTip());
1447     QString params = item->data(ParamsRole).toString();
1448     QString extension = item->data(ExtensionRole).toString();
1449     m_view.advanced_params->setPlainText(params);
1450     QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
1451     if (params.contains(" s=") || params.startsWith("s=") || destination == "audioonly") {
1452         // profile has a fixed size, do not allow resize
1453         m_view.rescale->setEnabled(false);
1454         setRescaleEnabled(false);
1455     } else {
1456         m_view.rescale->setEnabled(true);
1457         setRescaleEnabled(m_view.rescale->isChecked());
1458     }
1459     KUrl url = filenameWithExtension(m_view.out_file->url(), extension);
1460     m_view.out_file->setUrl(url);
1461 //     if (!url.isEmpty()) {
1462 //         QString path = url.path();
1463 //         int pos = path.lastIndexOf('.') + 1;
1464 //  if (pos == 0) path.append('.' + extension);
1465 //         else path = path.left(pos) + extension;
1466 //         m_view.out_file->setUrl(KUrl(path));
1467 //     } else {
1468 //         m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
1469 //     }
1470     m_view.out_file->setFilter("*." + extension);
1471     QString edit = item->data(EditableRole).toString();
1472     if (edit.isEmpty() || !edit.endsWith("customprofiles.xml")) {
1473         m_view.buttonDelete->setEnabled(false);
1474         m_view.buttonEdit->setEnabled(false);
1475     } else {
1476         m_view.buttonDelete->setEnabled(true);
1477         m_view.buttonEdit->setEnabled(true);
1478     }
1479
1480     // setup comboBox with bitrates
1481     m_view.comboBitrates->clear();
1482     if (params.contains("bitrate")) {
1483         m_view.comboBitrates->setEnabled(true);
1484         m_view.bitrateLabel->setEnabled(true);
1485         if ( item->data(BitratesRole).canConvert(QVariant::StringList) && item->data(BitratesRole).toStringList().count()) {
1486             QStringList bitrates = item->data(BitratesRole).toStringList();
1487             foreach (const QString &bitrate, bitrates)
1488                 m_view.comboBitrates->addItem(bitrate);
1489             if (item->data(DefaultBitrateRole).canConvert(QVariant::String))
1490                 m_view.comboBitrates->setCurrentIndex(bitrates.indexOf(item->data(DefaultBitrateRole).toString()));
1491         }
1492     } else {
1493         m_view.comboBitrates->setEnabled(false);
1494         m_view.bitrateLabel->setEnabled(false);
1495     }
1496
1497     // setup comboBox with audiobitrates
1498     m_view.comboAudioBitrates->clear();
1499     if (params.contains("audiobitrate")) {
1500         m_view.comboAudioBitrates->setEnabled(true);
1501         m_view.audiobitrateLabel->setEnabled(true);
1502         if ( item->data(AudioBitratesRole).canConvert(QVariant::StringList) && item->data(AudioBitratesRole).toStringList().count()) {
1503             QStringList audiobitrates = item->data(AudioBitratesRole).toStringList();
1504             foreach (const QString &bitrate, audiobitrates)
1505                 m_view.comboAudioBitrates->addItem(bitrate);
1506             if (item->data(DefaultAudioBitrateRole).canConvert(QVariant::String))
1507                 m_view.comboAudioBitrates->setCurrentIndex(audiobitrates.indexOf(item->data(DefaultAudioBitrateRole).toString()));
1508         }
1509     } else {
1510         m_view.comboAudioBitrates->setEnabled(false);
1511         m_view.audiobitrateLabel->setEnabled(false);
1512     }
1513     
1514     m_view.checkTwoPass->setEnabled(params.contains("passes"));
1515
1516     m_view.encoder_threads->setEnabled(!params.contains("threads="));
1517
1518     m_view.buttonRender->setEnabled(m_view.size_list->currentItem()->toolTip().isEmpty());
1519     m_view.buttonGenerateScript->setEnabled(m_view.size_list->currentItem()->toolTip().isEmpty());
1520 }
1521
1522 void RenderWidget::reloadProfiles()
1523 {
1524     parseProfiles();
1525 }
1526
1527 void RenderWidget::parseProfiles(const QString &meta, const QString &group, const QString &profile)
1528 {
1529     m_view.destination_list->blockSignals(true);
1530     m_view.destination_list->clear();
1531     qDeleteAll(m_renderItems);
1532     qDeleteAll(m_renderCategory);
1533     m_renderItems.clear();
1534     m_renderCategory.clear();
1535     m_view.destination_list->addItem(KIcon("video-x-generic"), i18n("File rendering"));
1536     m_view.destination_list->addItem(KIcon("favorites"), i18n("Favorites"), "favorites");
1537     m_view.destination_list->addItem(KIcon("media-optical"), i18n("DVD"), "dvd");
1538     m_view.destination_list->addItem(KIcon("audio-x-generic"), i18n("Audio only"), "audioonly");
1539     m_view.destination_list->addItem(KIcon("applications-internet"), i18n("Web sites"), "websites");
1540     m_view.destination_list->addItem(KIcon("applications-multimedia"), i18n("Media players"), "mediaplayers");
1541     m_view.destination_list->addItem(KIcon("drive-harddisk"), i18n("Lossless / HQ"), "lossless");
1542     m_view.destination_list->addItem(KIcon("pda"), i18n("Mobile devices"), "mobile");
1543
1544     QString exportFile = KStandardDirs::locate("appdata", "export/profiles.xml");
1545     parseFile(exportFile, false);
1546
1547
1548     QString exportFolder = KStandardDirs::locateLocal("appdata", "export/");
1549     QDir directory = QDir(exportFolder);
1550     QStringList filter;
1551     filter << "*.xml";
1552     QStringList fileList = directory.entryList(filter, QDir::Files);
1553     // We should parse customprofiles.xml in last position, so that user profiles
1554     // can also override profiles installed by KNewStuff
1555     fileList.removeAll("customprofiles.xml");
1556     foreach(const QString &filename, fileList)
1557         parseFile(exportFolder + filename, true);
1558     if (QFile::exists(exportFolder + "customprofiles.xml")) parseFile(exportFolder + "customprofiles.xml", true);
1559
1560     int categoryIndex = m_view.destination_list->findData(meta);
1561     if (categoryIndex == -1) categoryIndex = 0;
1562     m_view.destination_list->setCurrentIndex(categoryIndex);
1563     m_view.destination_list->blockSignals(false);
1564     refreshCategory(group, profile);
1565 }
1566
1567 void RenderWidget::parseFile(const QString &exportFile, bool editable)
1568 {
1569     kDebug() << "// Parsing file: " << exportFile;
1570     kDebug() << "------------------------------";
1571     QDomDocument doc;
1572     QFile file(exportFile);
1573     doc.setContent(&file, false);
1574     file.close();
1575     QDomElement documentElement;
1576     QDomElement profileElement;
1577     QString extension;
1578     QDomNodeList groups = doc.elementsByTagName("group");
1579     QListWidgetItem *item = NULL;
1580     const QStringList acodecsList = KdenliveSettings::audiocodecs();
1581     bool replaceVorbisCodec = false;
1582     if (acodecsList.contains("libvorbis")) replaceVorbisCodec = true;
1583     bool replaceLibfaacCodec = false;
1584     if (!acodecsList.contains("aac") && acodecsList.contains("libfaac")) replaceLibfaacCodec = true;
1585
1586     if (editable || groups.count() == 0) {
1587         QDomElement profiles = doc.documentElement();
1588         if (editable && profiles.attribute("version", 0).toInt() < 1) {
1589             kDebug() << "// OLD profile version";
1590             // this is an old profile version, update it
1591             QDomDocument newdoc;
1592             QDomElement newprofiles = newdoc.createElement("profiles");
1593             newprofiles.setAttribute("version", 1);
1594             newdoc.appendChild(newprofiles);
1595             QDomNodeList profilelist = doc.elementsByTagName("profile");
1596             for (int i = 0; i < profilelist.count(); ++i) {
1597                 QString category = i18nc("Category Name", "Custom");
1598                 QString extension;
1599                 QDomNode parent = profilelist.at(i).parentNode();
1600                 if (!parent.isNull()) {
1601                     QDomElement parentNode = parent.toElement();
1602                     if (parentNode.hasAttribute("name")) category = parentNode.attribute("name");
1603                     extension = parentNode.attribute("extension");
1604                 }
1605                 profilelist.at(i).toElement().setAttribute("category", category);
1606                 if (!extension.isEmpty()) profilelist.at(i).toElement().setAttribute("extension", extension);
1607                 QDomNode n = profilelist.at(i).cloneNode();
1608                 newprofiles.appendChild(newdoc.importNode(n, true));
1609             }
1610             if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
1611                 KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
1612                 return;
1613             }
1614             QTextStream out(&file);
1615             out << newdoc.toString();
1616             file.close();
1617             parseFile(exportFile, editable);
1618             return;
1619         }
1620
1621         QDomNode node = doc.elementsByTagName("profile").at(0);
1622         if (node.isNull()) {
1623             kDebug() << "// Export file: " << exportFile << " IS BROKEN";
1624             return;
1625         }
1626         int count = 1;
1627         while (!node.isNull()) {
1628             QDomElement profile = node.toElement();
1629             QString profileName = profile.attribute("name");
1630             QString standard = profile.attribute("standard");
1631             QString params = profile.attribute("args");
1632
1633             if (replaceVorbisCodec && params.contains("acodec=vorbis")) {
1634                 // replace vorbis with libvorbis
1635                 params = params.replace("vorbis", "libvorbis");
1636             }
1637             if (replaceLibfaacCodec && params.contains("acodec=aac")) {
1638                 // replace libfaac with aac
1639                 params = params.replace("aac", "libfaac");
1640             }
1641
1642             QString category = profile.attribute("category", i18nc("Category Name", "Custom"));
1643             QString dest = profile.attribute("destinationid");
1644             QString prof_extension = profile.attribute("extension");
1645             if (!prof_extension.isEmpty()) extension = prof_extension;
1646             bool exists = false;
1647             for (int j = 0; j < m_renderCategory.count(); ++j) {
1648                 if (m_renderCategory.at(j)->text() == category && m_renderCategory.at(j)->data(MetaGroupRole) == dest) {
1649                     exists = true;
1650                     break;
1651                 }
1652             }
1653
1654             if (!exists) {
1655                 QListWidgetItem *itemcat = new QListWidgetItem(category);
1656                 itemcat->setData(MetaGroupRole, dest);
1657                 m_renderCategory.append(itemcat);
1658             }
1659
1660             // Check if item with same name already exists and replace it,
1661             // allowing to override default profiles
1662
1663
1664             for (int j = 0; j < m_renderItems.count(); ++j) {
1665                 if (m_renderItems.at(j)->text() == profileName && m_renderItems.at(j)->data(MetaGroupRole) == dest) {
1666                     QListWidgetItem *duplicate = m_renderItems.takeAt(j);
1667                     delete duplicate;
1668                     j--;
1669                 }
1670             }
1671
1672             item = new QListWidgetItem(profileName); // , m_view.size_list
1673             //kDebug() << "// ADDINg item with name: " << profileName << ", GRP" << category << ", DEST:" << dest ;
1674             item->setData(GroupRole, category);
1675             item->setData(MetaGroupRole, dest);
1676             item->setData(ExtensionRole, extension);
1677             item->setData(RenderRole, "avformat");
1678             item->setData(StandardRole, standard);
1679             item->setData(ParamsRole, params);
1680             item->setData(BitratesRole, profile.attribute("bitrates").split(',', QString::SkipEmptyParts));
1681             item->setData(DefaultBitrateRole, profile.attribute("defaultbitrate"));
1682             item->setData(AudioBitratesRole, profile.attribute("audiobitrates").split(',', QString::SkipEmptyParts));
1683             item->setData(DefaultAudioBitrateRole, profile.attribute("defaultaudiobitrate"));
1684             if (profile.hasAttribute("url")) item->setData(ExtraRole, profile.attribute("url"));
1685             if (editable) {
1686                 item->setData(EditableRole, exportFile);
1687                 if (exportFile.endsWith("customprofiles.xml")) item->setIcon(KIcon("emblem-favorite"));
1688                 else item->setIcon(KIcon("applications-internet"));
1689             }
1690             m_renderItems.append(item);
1691             node = doc.elementsByTagName("profile").at(count);
1692             count++;
1693         }
1694         return;
1695     }
1696
1697     int i = 0;
1698     QString groupName;
1699     QString profileName;
1700
1701     QString prof_extension;
1702     QString renderer;
1703     QString params;
1704     QString standard;
1705     QString bitrates, defaultBitrate, audioBitrates, defaultAudioBitrate;
1706     KIcon icon;
1707
1708     while (!groups.item(i).isNull()) {
1709         documentElement = groups.item(i).toElement();
1710         QDomNode gname = documentElement.elementsByTagName("groupname").at(0);
1711         QString metagroupName;
1712         QString metagroupId;
1713         if (!gname.isNull()) {
1714             metagroupName = gname.firstChild().nodeValue();
1715             metagroupId = gname.toElement().attribute("id");
1716
1717             if (!metagroupName.isEmpty() && m_view.destination_list->findData(metagroupId) == -1) {
1718                 if (metagroupId == "dvd") icon = KIcon("media-optical");
1719                 else if (metagroupId == "audioonly") icon = KIcon("audio-x-generic");
1720                 else if (metagroupId == "websites") icon = KIcon("applications-internet");
1721                 else if (metagroupId == "mediaplayers") icon = KIcon("applications-multimedia");
1722                 else if (metagroupId == "lossless") icon = KIcon("drive-harddisk");
1723                 else if (metagroupId == "mobile") icon = KIcon("pda");
1724                 m_view.destination_list->addItem(icon, i18n(metagroupName.toUtf8().data()), metagroupId);
1725             }
1726         }
1727         groupName = documentElement.attribute("name", i18nc("Attribute Name", "Custom"));
1728         extension = documentElement.attribute("extension", QString());
1729         renderer = documentElement.attribute("renderer", QString());
1730         bool exists = false;
1731         for (int j = 0; j < m_renderCategory.count(); ++j) {
1732             if (m_renderCategory.at(j)->text() == groupName && m_renderCategory.at(j)->data(MetaGroupRole) == metagroupId) {
1733                 exists = true;
1734                 break;
1735             }
1736         }
1737         if (!exists) {
1738             QListWidgetItem *itemcat = new QListWidgetItem(groupName); //, m_view.format_list);
1739             itemcat->setData(MetaGroupRole, metagroupId);
1740             m_renderCategory.append(itemcat);
1741         }
1742
1743         QDomNode n = groups.item(i).firstChild();
1744         while (!n.isNull()) {
1745             if (n.toElement().tagName() != "profile") {
1746                 n = n.nextSibling();
1747                 continue;
1748             }
1749             profileElement = n.toElement();
1750             profileName = profileElement.attribute("name");
1751             standard = profileElement.attribute("standard");
1752             bitrates = profileElement.attribute("bitrates");
1753             defaultBitrate = profileElement.attribute("defaultbitrate");
1754             audioBitrates = profileElement.attribute("audiobitrates");
1755             defaultAudioBitrate = profileElement.attribute("defaultaudiobitrate");
1756             params = profileElement.attribute("args");
1757
1758             if (replaceVorbisCodec && params.contains("acodec=vorbis")) {
1759                 // replace vorbis with libvorbis
1760                 params = params.replace("vorbis", "libvorbis");
1761             }
1762             if (replaceLibfaacCodec && params.contains("acodec=aac")) {
1763                 // replace libfaac with aac
1764                 params = params.replace("aac", "libfaac");
1765             }
1766
1767             prof_extension = profileElement.attribute("extension");
1768             if (!prof_extension.isEmpty()) extension = prof_extension;
1769             item = new QListWidgetItem(profileName); //, m_view.size_list);
1770             item->setData(GroupRole, groupName);
1771             item->setData(MetaGroupRole, metagroupId);
1772             item->setData(ExtensionRole, extension);
1773             item->setData(RenderRole, renderer);
1774             item->setData(StandardRole, standard);
1775             item->setData(ParamsRole, params);
1776             item->setData(BitratesRole, bitrates.split(',', QString::SkipEmptyParts));
1777             item->setData(DefaultBitrateRole, defaultBitrate);
1778             item->setData(AudioBitratesRole, audioBitrates.split(',', QString::SkipEmptyParts));
1779             item->setData(DefaultAudioBitrateRole, defaultAudioBitrate);
1780             if (profileElement.hasAttribute("url")) item->setData(ExtraRole, profileElement.attribute("url"));
1781             if (editable) item->setData(EditableRole, exportFile);
1782             m_renderItems.append(item);
1783             n = n.nextSibling();
1784         }
1785
1786         ++i;
1787     }
1788 }
1789
1790
1791
1792 void RenderWidget::setRenderJob(const QString &dest, int progress)
1793 {
1794     RenderJobItem *item;
1795     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
1796     if (!existing.isEmpty()) {
1797         item = static_cast<RenderJobItem*> (existing.at(0));
1798     } else {
1799         item = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << dest);
1800         if (progress == 0) {
1801             item->setStatus(WAITINGJOB);
1802         }
1803     }
1804     item->setData(1, ProgressRole, progress);
1805     item->setStatus(RUNNINGJOB);
1806     if (progress == 0) {
1807         item->setIcon(0, KIcon("system-run"));
1808         item->setData(1, TimeRole, QTime::currentTime());
1809         slotCheckJob();
1810     } else {
1811         QTime startTime = item->data(1, TimeRole).toTime();
1812         int seconds = startTime.secsTo(QTime::currentTime());;
1813         const QString t = i18n("Estimated time %1", QTime().addSecs(seconds * (100 - progress) / progress).toString("hh:mm:ss"));
1814         item->setData(1, Qt::UserRole, t);
1815     }
1816 }
1817
1818 void RenderWidget::setRenderStatus(const QString &dest, int status, const QString &error)
1819 {
1820     RenderJobItem *item;
1821     QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
1822     if (!existing.isEmpty()) item = static_cast<RenderJobItem*> (existing.at(0));
1823     else {
1824         item = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << dest);
1825     }
1826     if (status == -1) {
1827         // Job finished successfully
1828         item->setStatus(FINISHEDJOB);
1829         QTime startTime = item->data(1, TimeRole).toTime();
1830         int seconds = startTime.secsTo(QTime::currentTime());
1831         const QTime tm = QTime().addSecs(seconds);
1832         const QString t = i18n("Rendering finished in %1", tm.toString("hh:mm:ss"));
1833         item->setData(1, Qt::UserRole, t);
1834         QString itemGroup = item->data(0, Qt::UserRole).toString();
1835         if (itemGroup == "dvd") {
1836             emit openDvdWizard(item->text(1));
1837         } else if (itemGroup == "websites") {
1838             QString url = item->metadata();
1839             if (!url.isEmpty()) new KRun(url, this);
1840         }
1841     } else if (status == -2) {
1842         // Rendering crashed
1843         item->setStatus(FAILEDJOB);
1844         m_view.error_log->append(i18n("<strong>Rendering of %1 crashed</strong><br />", dest));
1845         m_view.error_log->append(error);
1846         m_view.error_log->append("<hr />");
1847         m_view.error_box->setVisible(true);
1848     } else if (status == -3) {
1849         // User aborted job
1850         item->setStatus(ABORTEDJOB);
1851     }
1852     slotCheckJob();
1853     checkRenderStatus();
1854 }
1855
1856 void RenderWidget::slotAbortCurrentJob()
1857 {
1858     RenderJobItem *current = static_cast<RenderJobItem*> (m_view.running_jobs->currentItem());
1859     if (current) {
1860         if (current->status() == RUNNINGJOB)
1861             emit abortProcess(current->text(1));
1862         else {
1863             delete current;
1864             slotCheckJob();
1865             checkRenderStatus();
1866         }
1867     }
1868 }
1869
1870 void RenderWidget::slotStartCurrentJob()
1871 {
1872     RenderJobItem *current = static_cast<RenderJobItem*> (m_view.running_jobs->currentItem());
1873     if (current && current->status() == WAITINGJOB)
1874         startRendering(current);
1875     m_view.start_job->setEnabled(false);
1876 }
1877
1878 void RenderWidget::slotCheckJob()
1879 {
1880     bool activate = false;
1881     RenderJobItem *current = static_cast<RenderJobItem*> (m_view.running_jobs->currentItem());
1882     if (current) {
1883         if (current->status() == RUNNINGJOB) {
1884             m_view.abort_job->setText(i18n("Abort Job"));
1885             m_view.start_job->setEnabled(false);
1886         } else {
1887             m_view.abort_job->setText(i18n("Remove Job"));
1888             m_view.start_job->setEnabled(current->status() == WAITINGJOB);
1889         }
1890         activate = true;
1891     }
1892     m_view.abort_job->setEnabled(activate);
1893     /*
1894     for (int i = 0; i < m_view.running_jobs->topLevelItemCount(); ++i) {
1895         current = static_cast<RenderJobItem*>(m_view.running_jobs->topLevelItem(i));
1896         if (current == static_cast<RenderJobItem*> (m_view.running_jobs->currentItem())) {
1897             current->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 3));
1898         } else current->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
1899     }*/
1900 }
1901
1902 void RenderWidget::slotCLeanUpJobs()
1903 {
1904     int ix = 0;
1905     RenderJobItem *current = static_cast<RenderJobItem*> (m_view.running_jobs->topLevelItem(ix));
1906     while (current) {
1907         if (current->status() == FINISHEDJOB)
1908             delete current;
1909         else ix++;
1910         current = static_cast<RenderJobItem*>(m_view.running_jobs->topLevelItem(ix));
1911     }
1912     slotCheckJob();
1913 }
1914
1915 void RenderWidget::parseScriptFiles()
1916 {
1917     QStringList scriptsFilter;
1918     scriptsFilter << "*.sh";
1919     m_view.scripts_list->clear();
1920
1921     QTreeWidgetItem *item;
1922     // List the project scripts
1923     QStringList scriptFiles = QDir(m_projectFolder + "scripts").entryList(scriptsFilter, QDir::Files);
1924     for (int i = 0; i < scriptFiles.size(); ++i) {
1925         KUrl scriptpath(m_projectFolder + "scripts/" + scriptFiles.at(i));
1926         QString target;
1927         QString renderer;
1928         QString melt;
1929         QFile file(scriptpath.path());
1930         kDebug()<<"------------------\n"<<scriptpath.path();
1931         if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
1932             QTextStream stream(&file);
1933             while (!stream.atEnd()) {
1934                 QString line = stream.readLine();
1935                 //kDebug()<<"# :"<<line;
1936                 if (line.startsWith("TARGET=")) {
1937                     target = line.section("TARGET=\"", 1);
1938                     target = target.section('"', 0, 0);
1939                 } else if (line.startsWith("RENDERER=")) {
1940                     renderer = line.section("RENDERER=\"", 1);
1941                     renderer = renderer.section('"', 0, 0);
1942                 } else if (line.startsWith("MELT=")) {
1943                     melt = line.section("MELT=\"", 1);
1944                     melt = melt.section('"', 0, 0);
1945                 }
1946             }
1947             file.close();
1948         }
1949         if (target.isEmpty()) continue;
1950         //kDebug()<<"ScRIPT RENDERER: "<<renderer<<"\n++++++++++++++++++++++++++";
1951         item = new QTreeWidgetItem(m_view.scripts_list, QStringList() << QString() << scriptpath.fileName());
1952         if (!renderer.isEmpty() && renderer.contains('/') && !QFile::exists(renderer)) {
1953             item->setIcon(0, KIcon("dialog-cancel"));
1954             item->setToolTip(1, i18n("Script contains wrong command: %1", renderer));
1955             item->setData(0, Qt::UserRole, '1');
1956         } else if (!melt.isEmpty() && melt.contains('/') && !QFile::exists(melt)) {
1957             item->setIcon(0, KIcon("dialog-cancel"));
1958             item->setToolTip(1, i18n("Script contains wrong command: %1", melt));
1959             item->setData(0, Qt::UserRole, '1');
1960         } else item->setIcon(0, KIcon("application-x-executable-script"));
1961         item->setSizeHint(0, QSize(m_view.scripts_list->columnWidth(0), fontMetrics().height() * 2));
1962         item->setData(1, Qt::UserRole, KUrl(QUrl::fromEncoded(target.toUtf8())).pathOrUrl());
1963         item->setData(1, Qt::UserRole + 1, scriptpath.path());
1964     }
1965     QTreeWidgetItem *script = m_view.scripts_list->topLevelItem(0);
1966     if (script) {
1967         m_view.scripts_list->setCurrentItem(script);
1968         script->setSelected(true);
1969     }
1970 }
1971
1972 void RenderWidget::slotCheckScript()
1973 {
1974     QTreeWidgetItem *current = m_view.scripts_list->currentItem();
1975     if (current == NULL)
1976         return;
1977     m_view.start_script->setEnabled(current->data(0, Qt::UserRole).toString().isEmpty());
1978     m_view.delete_script->setEnabled(true);
1979     for (int i = 0; i < m_view.scripts_list->topLevelItemCount(); ++i) {
1980         current = m_view.scripts_list->topLevelItem(i);
1981         if (current == m_view.scripts_list->currentItem()) {
1982             current->setSizeHint(1, QSize(m_view.scripts_list->columnWidth(1), fontMetrics().height() * 3));
1983         } else current->setSizeHint(1, QSize(m_view.scripts_list->columnWidth(1), fontMetrics().height() * 2));
1984     }
1985 }
1986
1987 void RenderWidget::slotStartScript()
1988 {
1989     RenderJobItem* item = static_cast<RenderJobItem*> (m_view.scripts_list->currentItem());
1990     if (item) {
1991         kDebug() << "// STARTING SCRIPT: "<<item->text(1);
1992         QString destination = item->data(1, Qt::UserRole).toString();
1993         QString path = item->data(1, Qt::UserRole + 1).toString();
1994         // Insert new job in queue
1995         RenderJobItem *renderItem = NULL;
1996         QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(destination, Qt::MatchExactly, 1);
1997         kDebug() << "------  START SCRIPT";
1998         if (!existing.isEmpty()) {
1999             renderItem = static_cast<RenderJobItem*> (existing.at(0));
2000             if (renderItem->status() == RUNNINGJOB || renderItem->status() == WAITINGJOB) {
2001                 KMessageBox::information(this, i18n("There is already a job writing file:<br /><b>%1</b><br />Abort the job if you want to overwrite it...", destination), i18n("Already running"));
2002                 return;
2003             }
2004             else if (renderItem->type() != ScriptRenderType) {
2005                 delete renderItem;
2006                 renderItem = NULL;
2007             }
2008         }
2009         if (!renderItem) renderItem = new RenderJobItem(m_view.running_jobs, QStringList() << QString() << destination, ScriptRenderType);
2010         renderItem->setData(1, ProgressRole, 0);
2011         renderItem->setStatus(WAITINGJOB);
2012         renderItem->setIcon(0, KIcon("media-playback-pause"));
2013         renderItem->setData(1, Qt::UserRole, i18n("Waiting..."));
2014         renderItem->setData(1, TimeRole, QTime::currentTime());
2015         renderItem->setData(1, ParametersRole, path);
2016         checkRenderStatus();
2017         m_view.tabWidget->setCurrentIndex(1);
2018     }
2019 }
2020
2021 void RenderWidget::slotDeleteScript()
2022 {
2023     QTreeWidgetItem *item = m_view.scripts_list->currentItem();
2024     if (item) {
2025         QString path = item->data(1, Qt::UserRole + 1).toString();
2026         KIO::NetAccess::del(path + ".mlt", this);
2027         KIO::NetAccess::del(path, this);
2028         parseScriptFiles();
2029     }
2030 }
2031
2032 void RenderWidget::slotGenerateScript()
2033 {
2034     slotPrepareExport(true);
2035 }
2036
2037 void RenderWidget::slotHideLog()
2038 {
2039     m_view.error_box->setVisible(false);
2040 }
2041
2042 void RenderWidget::setRenderProfile(const QMap<QString, QString> &props)
2043 {
2044     m_view.scanning_list->setCurrentIndex(props.value("renderscanning").toInt());
2045     int exportAudio = props.value("renderexportaudio").toInt();
2046     switch (exportAudio) {
2047     case 1:
2048         m_view.export_audio->setCheckState(Qt::Unchecked);
2049         break;
2050     case 2:
2051         m_view.export_audio->setCheckState(Qt::Checked);
2052         break;
2053     default:
2054         m_view.export_audio->setCheckState(Qt::PartiallyChecked);
2055     }
2056     if (props.contains("renderrescale")) m_view.rescale->setChecked(props.value("renderrescale").toInt());
2057     if (props.contains("renderrescalewidth")) m_view.rescale_width->setValue(props.value("renderrescalewidth").toInt());
2058     if (props.contains("renderrescaleheight")) m_view.rescale_height->setValue(props.value("renderrescaleheight").toInt());
2059     if (props.contains("rendertcoverlay")) m_view.tc_overlay->setChecked(props.value("rendertcoverlay").toInt());
2060     if (props.contains("rendertctype")) m_view.tc_type->setCurrentIndex(props.value("rendertctype").toInt());
2061     if (props.contains("renderratio")) m_view.rescale_keep->setChecked(props.value("renderratio").toInt());
2062     if (props.contains("renderplay")) m_view.play_after->setChecked(props.value("renderplay").toInt());
2063     if (props.contains("rendertwopass")) m_view.checkTwoPass->setChecked(props.value("rendertwopass").toInt());
2064
2065     if (props.value("renderzone") == "1") m_view.render_zone->setChecked(true);
2066     else if (props.value("renderguide") == "1") {
2067         m_view.render_guide->setChecked(true);
2068         m_view.guide_start->setCurrentIndex(props.value("renderstartguide").toInt());
2069         m_view.guide_end->setCurrentIndex(props.value("renderendguide").toInt());
2070     } else m_view.render_full->setChecked(true);
2071     slotUpdateGuideBox();
2072
2073     QString url = props.value("renderurl");
2074     if (!url.isEmpty())
2075         m_view.out_file->setUrl(KUrl(url));
2076
2077     // set destination
2078     int categoryIndex = m_view.destination_list->findData(props.value("renderdestination"));
2079     if (categoryIndex == -1) categoryIndex = 0;
2080     m_view.destination_list->blockSignals(true);
2081     m_view.destination_list->setCurrentIndex(categoryIndex);
2082     m_view.destination_list->blockSignals(false);
2083     
2084     // Clear previous error messages
2085     refreshCategory(props.value("rendercategory"), props.value("renderprofile"));
2086 }
2087
2088 bool RenderWidget::startWaitingRenderJobs()
2089 {
2090     m_blockProcessing = true;
2091     QString autoscriptFile = getFreeScriptName(KUrl(), "auto");
2092     QFile file(autoscriptFile);
2093     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
2094         kWarning() << "//////  ERROR writing to file: " << autoscriptFile;
2095         KMessageBox::error(0, i18n("Cannot write to file %1", autoscriptFile));
2096         return false;
2097     }
2098
2099     QTextStream outStream(&file);
2100     outStream << "#! /bin/sh" << "\n" << "\n";
2101     RenderJobItem *item = static_cast<RenderJobItem*> (m_view.running_jobs->topLevelItem(0));
2102     while (item) {
2103         if (item->status() == WAITINGJOB) {
2104             if (item->type() == DirectRenderType) {
2105                 // Add render process for item
2106                 const QString params = item->data(1, ParametersRole).toStringList().join(" ");
2107                 outStream << m_renderer << " " << params << "\n";
2108             } else if (item->type() == ScriptRenderType){
2109                 // Script item
2110                 outStream << item->data(1, ParametersRole).toString() << "\n";
2111             }
2112         }
2113         item = static_cast<RenderJobItem*>(m_view.running_jobs->itemBelow(item));
2114     }
2115     // erase itself when rendering is finished
2116     outStream << "rm " << autoscriptFile << "\n" << "\n";
2117     if (file.error() != QFile::NoError) {
2118         KMessageBox::error(0, i18n("Cannot write to file %1", autoscriptFile));
2119         file.close();
2120         m_blockProcessing = false;
2121         return false;
2122     }
2123     file.close();
2124     QFile::setPermissions(autoscriptFile, file.permissions() | QFile::ExeUser);
2125     QProcess::startDetached(autoscriptFile, QStringList());
2126     return true;
2127 }
2128
2129 QString RenderWidget::getFreeScriptName(const KUrl &projectName, const QString &prefix)
2130 {
2131     int ix = 0;
2132     QString scriptsFolder = m_projectFolder + "scripts/";
2133     KStandardDirs::makeDir(scriptsFolder);
2134     QString path;
2135     QString fileName;
2136     if (projectName.isEmpty()) fileName = i18n("script");
2137     else fileName = projectName.fileName().section('.', 0, -2) + "_";
2138     while (path.isEmpty() || QFile::exists(path)) {
2139         ++ix;
2140         path = scriptsFolder + prefix + fileName + QString::number(ix).rightJustified(3, '0', false) + ".sh";
2141     }
2142     return path;
2143 }
2144
2145 void RenderWidget::slotPlayRendering(QTreeWidgetItem *item, int)
2146 {
2147     RenderJobItem *renderItem = static_cast<RenderJobItem*> (item);
2148     if (KdenliveSettings::defaultplayerapp().isEmpty() || renderItem->status() != FINISHEDJOB) return;
2149     KUrl::List urls;
2150     urls.append(KUrl(item->text(1)));
2151     KRun::run(KdenliveSettings::defaultplayerapp(), urls, this);
2152 }
2153
2154 void RenderWidget::missingClips(bool hasMissing)
2155 {
2156     if (hasMissing) {
2157         m_view.errorLabel->setText(i18n("Check missing clips"));
2158         m_view.errorBox->setHidden(false);
2159     } else m_view.errorBox->setHidden(true);
2160 }
2161
2162 void RenderWidget::errorMessage(const QString &message)
2163 {
2164     if (!message.isEmpty()) {
2165 #if KDE_IS_VERSION(4,7,0)
2166         m_infoMessage->setMessageType(KMessageWidget::Warning);
2167         m_infoMessage->setText(message);
2168 #if KDE_IS_VERSION(4,10,0)
2169         m_infoMessage->animatedShow();
2170 #else
2171         // Workaround KDE bug in KMessageWidget
2172         QTimer::singleShot(0, m_infoMessage, SLOT(animatedShow()));
2173 #endif
2174 #else
2175         m_view.errorLabel->setText(message);
2176         m_view.errorBox->setHidden(false);
2177 #endif
2178     }
2179     else {
2180 #if KDE_IS_VERSION(4,7,0)
2181         if (m_view.tabWidget->currentIndex() == 0 && m_infoMessage->isVisible())  {
2182 #if KDE_IS_VERSION(4,10,0)
2183             m_infoMessage->animatedHide();
2184 #else
2185             QTimer::singleShot(0, m_infoMessage, SLOT(animatedHide()));
2186 #endif
2187         } else {
2188             // Seems like animated hide does not work when page is not visible
2189             m_infoMessage->hide();
2190         }
2191 #else
2192         m_view.errorBox->setHidden(true);
2193         m_view.errorLabel->setText(QString());
2194 #endif
2195
2196     }
2197 }
2198
2199
2200 void RenderWidget::slotUpdateEncodeThreads(int val)
2201 {
2202         KdenliveSettings::setEncodethreads(val);
2203 }
2204
2205 void RenderWidget::slotUpdateRescaleWidth(int val)
2206 {
2207     KdenliveSettings::setDefaultrescalewidth(val);
2208     if (!m_view.rescale_keep->isChecked()) return;
2209     m_view.rescale_height->blockSignals(true);
2210     m_view.rescale_height->setValue(val * m_profile.height / m_profile.width  + 0.5);
2211     KdenliveSettings::setDefaultrescaleheight(m_view.rescale_height->value());
2212     m_view.rescale_height->blockSignals(false);
2213 }
2214
2215 void RenderWidget::slotUpdateRescaleHeight(int val)
2216 {
2217     KdenliveSettings::setDefaultrescaleheight(val);
2218     if (!m_view.rescale_keep->isChecked()) return;
2219     m_view.rescale_width->blockSignals(true);
2220     m_view.rescale_width->setValue(val * m_profile.width / m_profile.height + 0.5);
2221     KdenliveSettings::setDefaultrescaleheight(m_view.rescale_width->value());
2222     m_view.rescale_width->blockSignals(false);
2223 }
2224
2225 void RenderWidget::slotSwitchAspectRatio()
2226 {
2227     KdenliveSettings::setRescalekeepratio(m_view.rescale_keep->isChecked());
2228     if (m_view.rescale_keep->isChecked()) slotUpdateRescaleWidth(m_view.rescale_width->value());
2229 }
2230
2231 void RenderWidget::slotUpdateAudioLabel(int ix)
2232 {
2233     if (ix == Qt::PartiallyChecked)
2234         m_view.export_audio->setText(i18n("Export audio (automatic)"));
2235     else
2236         m_view.export_audio->setText(i18n("Export audio"));
2237 }
2238
2239 bool RenderWidget::automaticAudioExport() const
2240 {
2241     return (m_view.export_audio->checkState() == Qt::PartiallyChecked);
2242 }
2243
2244 bool RenderWidget::selectedAudioExport() const
2245 {
2246     return (m_view.export_audio->checkState() != Qt::Unchecked);
2247 }
2248
2249 void RenderWidget::updateProxyConfig(bool enable)
2250 {
2251     m_view.proxy_render->setHidden(!enable);
2252 }
2253
2254 bool RenderWidget::proxyRendering()
2255 {
2256     return m_view.proxy_render->isChecked();
2257 }
2258
2259 void RenderWidget::setRescaleEnabled(bool enable)
2260 {
2261     for (int i = 0; i < m_view.rescale_box->layout()->count(); ++i) {
2262         if (m_view.rescale_box->itemAt(i)->widget())
2263             m_view.rescale_box->itemAt(i)->widget()->setEnabled(enable);
2264     }   
2265 }
2266
2267 void RenderWidget::keyPressEvent(QKeyEvent *e) {
2268     if(e->key()==Qt::Key_Return || e->key()==Qt::Key_Enter) {
2269         switch (m_view.tabWidget->currentIndex()) {
2270           case 1:
2271             if (m_view.start_job->isEnabled()) slotStartCurrentJob();
2272             break;
2273           case 2:
2274             if (m_view.start_script->isEnabled()) slotStartScript();
2275             break;
2276           default:
2277             if (m_view.buttonRender->isEnabled()) slotPrepareExport();
2278             break;
2279         }
2280     }
2281     else QDialog::keyPressEvent(e);
2282 }
2283
2284
2285 #include "renderwidget.moc"