renderwidget.cpp 36.2 KB
Newer Older
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/***************************************************************************
 *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

20 21

#include <QDomDocument>
22 23 24
#include <QItemDelegate>
#include <QTreeWidgetItem>
#include <QHeaderView>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
25 26 27

#include <KStandardDirs>
#include <KDebug>
28
#include <KMessageBox>
29
#include <KComboBox>
30
#include <KRun>
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
31 32 33

#include "kdenlivesettings.h"
#include "renderwidget.h"
34
#include "ui_saveprofile_ui.h"
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
35

36 37 38 39 40
const int GroupRole = Qt::UserRole;
const int ExtensionRole = GroupRole + 1;
const int StandardRole = GroupRole + 2;
const int RenderRole = GroupRole + 3;
const int ParamsRole = GroupRole + 4;
41
const int EditableRole = GroupRole + 5;
42
const int MetaGroupRole = GroupRole + 6;
43
const int ExtraRole = GroupRole + 7;
44

45
RenderWidget::RenderWidget(const QString &projectfolder, QWidget * parent): QDialog(parent), m_projectFolder(projectfolder) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
46
    m_view.setupUi(this);
47
    setWindowTitle(i18n("Rendering"));
48 49 50 51
    m_view.buttonDelete->setIcon(KIcon("trash-empty"));
    m_view.buttonDelete->setToolTip(i18n("Delete profile"));
    m_view.buttonDelete->setEnabled(false);

52 53 54 55
    m_view.buttonEdit->setIcon(KIcon("document-properties"));
    m_view.buttonEdit->setToolTip(i18n("Edit profile"));
    m_view.buttonEdit->setEnabled(false);

56 57 58
    m_view.buttonSave->setIcon(KIcon("document-new"));
    m_view.buttonSave->setToolTip(i18n("Create new profile"));

59 60 61 62 63 64
    m_view.buttonInfo->setIcon(KIcon("help-about"));

    if (KdenliveSettings::showrenderparams()) {
        m_view.buttonInfo->setDown(true);
    } else m_view.advanced_params->hide();

65 66
    m_view.rescale_size->setInputMask("0099\\x0099");
    m_view.rescale_size->setText("320x240");
67

68
    parseProfiles();
69

70 71
    connect(m_view.buttonInfo, SIGNAL(clicked()), this, SLOT(showInfoPanel()));

72
    connect(m_view.buttonSave, SIGNAL(clicked()), this, SLOT(slotSaveProfile()));
73
    connect(m_view.buttonEdit, SIGNAL(clicked()), this, SLOT(slotEditProfile()));
74
    connect(m_view.buttonDelete, SIGNAL(clicked()), this, SLOT(slotDeleteProfile()));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
75
    connect(m_view.buttonStart, SIGNAL(clicked()), this, SLOT(slotExport()));
76
    connect(m_view.abort_job, SIGNAL(clicked()), this, SLOT(slotAbortCurrentJob()));
77 78
    connect(m_view.buttonClose, SIGNAL(clicked()), this, SLOT(hide()));
    connect(m_view.buttonClose2, SIGNAL(clicked()), this, SLOT(hide()));
79 80
    connect(m_view.rescale, SIGNAL(toggled(bool)), m_view.rescale_size, SLOT(setEnabled(bool)));
    connect(m_view.destination_list, SIGNAL(activated(int)), this, SLOT(refreshView()));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
81
    connect(m_view.out_file, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButtons()));
82
    connect(m_view.out_file, SIGNAL(urlSelected(const KUrl &)), this, SLOT(slotUpdateButtons(const KUrl &)));
83 84
    connect(m_view.format_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshView()));
    connect(m_view.size_list, SIGNAL(currentRowChanged(int)), this, SLOT(refreshParams()));
85

86 87 88 89 90 91 92
    connect(m_view.render_guide, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
    connect(m_view.render_zone, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));
    connect(m_view.render_full, SIGNAL(clicked(bool)), this, SLOT(slotUpdateGuideBox()));

    connect(m_view.guide_end, SIGNAL(activated(int)), this, SLOT(slotCheckStartGuidePosition()));
    connect(m_view.guide_start, SIGNAL(activated(int)), this, SLOT(slotCheckEndGuidePosition()));

93
    connect(m_view.format_selection, SIGNAL(activated(int)), this, SLOT(refreshView()));
94

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
95
    m_view.buttonStart->setEnabled(false);
96
    m_view.rescale_size->setEnabled(false);
97
    m_view.guides_box->setVisible(false);
98 99 100 101
    m_view.open_dvd->setVisible(false);
    m_view.open_browser->setVisible(false);
    m_view.error_box->setVisible(false);

102 103
    m_view.splitter->setStretchFactor(1, 5);
    m_view.splitter->setStretchFactor(0, 2);
104

105
    m_view.out_file->setMode(KFile::File);
106

107
    m_view.running_jobs->setHeaderLabels(QStringList() << QString() << i18n("File") << i18n("Progress"));
108 109 110 111
    m_view.running_jobs->setItemDelegate(new RenderViewDelegate(this));
    QHeaderView *header = m_view.running_jobs->header();
    QFontMetrics fm = fontMetrics();
    //header->resizeSection(0, fm.width("typical-name-for-a-torrent.torrent"));
112 113
    header->setResizeMode(0, QHeaderView::Fixed);
    header->resizeSection(0, 30);
114
    header->setResizeMode(1, QHeaderView::Interactive);
115 116 117 118
    header->resizeSection(1, fm.width("typical-name-for-a-file.torrent"));
    header->setResizeMode(2, QHeaderView::Fixed);
    header->resizeSection(1, width() * 2 / 3);
    header->setResizeMode(2, QHeaderView::Interactive);
119 120
    //header->setResizeMode(1, QHeaderView::Fixed);

121
    focusFirstVisibleItem();
122 123 124 125 126 127 128 129 130 131 132 133
}

void RenderWidget::showInfoPanel() {
    if (m_view.advanced_params->isVisible()) {
        m_view.advanced_params->setVisible(false);
        m_view.buttonInfo->setDown(false);
        KdenliveSettings::setShowrenderparams(false);
    } else {
        m_view.advanced_params->setVisible(true);
        m_view.buttonInfo->setDown(true);
        KdenliveSettings::setShowrenderparams(true);
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
134 135
}

136 137 138 139 140 141
void RenderWidget::setDocumentPath(const QString path) {
    m_projectFolder = path;
    const QString fileName = m_view.out_file->url().fileName();
    m_view.out_file->setUrl(KUrl(m_projectFolder + '/' + fileName));
}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
void RenderWidget::slotUpdateGuideBox() {
    m_view.guides_box->setVisible(m_view.render_guide->isChecked());
}

void RenderWidget::slotCheckStartGuidePosition() {
    if (m_view.guide_start->currentIndex() > m_view.guide_end->currentIndex())
        m_view.guide_start->setCurrentIndex(m_view.guide_end->currentIndex());
}

void RenderWidget::slotCheckEndGuidePosition() {
    if (m_view.guide_end->currentIndex() < m_view.guide_start->currentIndex())
        m_view.guide_end->setCurrentIndex(m_view.guide_start->currentIndex());
}

void RenderWidget::setGuides(QDomElement guidesxml, double duration) {
    m_view.guide_start->clear();
    m_view.guide_end->clear();
    QDomNodeList nodes = guidesxml.elementsByTagName("guide");
    if (nodes.count() > 0) {
161
        m_view.guide_start->addItem(i18n("Render"), "0");
162 163 164 165 166 167 168 169 170 171 172 173 174
        m_view.render_guide->setEnabled(true);
    } else m_view.render_guide->setEnabled(false);
    for (int i = 0; i < nodes.count(); i++) {
        QDomElement e = nodes.item(i).toElement();
        if (!e.isNull()) {
            m_view.guide_start->addItem(e.attribute("comment"), e.attribute("time").toDouble());
            m_view.guide_end->addItem(e.attribute("comment"), e.attribute("time").toDouble());
        }
    }
    if (nodes.count() > 0)
        m_view.guide_end->addItem(i18n("End"), QString::number(duration));
}

175 176 177 178
// Will be called when the user selects an output file via the file dialog.
// File extension will be added automatically.
void RenderWidget::slotUpdateButtons(KUrl url) {
    if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
179
    else m_view.buttonStart->setEnabled(true);
180
    if (url != 0) {
181 182 183 184
        QListWidgetItem *item = m_view.size_list->currentItem();
        QString extension = item->data(ExtensionRole).toString();
        url = filenameWithExtension(url, extension);
        m_view.out_file->setUrl(url);
185 186 187
    }
}

188
// Will be called when the user changes the output file path in the text line.
189
// File extension must NOT be added, would make editing impossible!
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
190 191
void RenderWidget::slotUpdateButtons() {
    if (m_view.out_file->url().isEmpty()) m_view.buttonStart->setEnabled(false);
192
    else m_view.buttonStart->setEnabled(true);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
193 194
}

195 196 197 198 199 200 201 202 203 204 205 206 207
void RenderWidget::slotSaveProfile() {
    Ui::SaveProfile_UI ui;
    QDialog *d = new QDialog(this);
    ui.setupUi(d);
    QString customGroup = i18n("Custom");
    QStringList groupNames;
    for (int i = 0; i < m_view.format_list->count(); i++)
        groupNames.append(m_view.format_list->item(i)->text());
    if (!groupNames.contains(customGroup)) groupNames.prepend(customGroup);
    ui.group_name->addItems(groupNames);
    int pos = ui.group_name->findText(customGroup);
    ui.group_name->setCurrentIndex(pos);

208
    ui.parameters->setText(m_view.advanced_params->toPlainText());
209 210
    ui.extension->setText(m_view.size_list->currentItem()->data(ExtensionRole).toString());
    ui.profile_name->setFocus();
211
    if (d->exec() == QDialog::Accepted && !ui.profile_name->text().simplified().isEmpty()) {
212
        QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
213 214 215 216 217 218 219 220
        QDomDocument doc;
        QFile file(exportFile);
        doc.setContent(&file, false);
        file.close();

        QDomElement documentElement;
        bool groupExists = false;
        QString groupName;
221
        QString newProfileName = ui.profile_name->text().simplified();
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
        QString newGroupName = ui.group_name->currentText();
        QDomNodeList groups = doc.elementsByTagName("group");
        int i = 0;
        if (groups.count() == 0) {
            QDomElement profiles = doc.createElement("profiles");
            doc.appendChild(profiles);
        } else while (!groups.item(i).isNull()) {
                documentElement = groups.item(i).toElement();
                groupName = documentElement.attribute("name");
                kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
                if (groupName == newGroupName) {
                    groupExists = true;
                    break;
                }
                i++;
            }
        if (!groupExists) {
            documentElement = doc.createElement("group");
            documentElement.setAttribute("name", ui.group_name->currentText());
            documentElement.setAttribute("renderer", "avformat");
            doc.documentElement().appendChild(documentElement);
        }
        QDomElement profileElement = doc.createElement("profile");
        profileElement.setAttribute("name", newProfileName);
        profileElement.setAttribute("extension", ui.extension->text().simplified());
247
        profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
248 249 250 251 252 253 254 255 256 257 258 259
        documentElement.appendChild(profileElement);

        //QCString save = doc.toString().utf8();

        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
            delete d;
            return;
        }
        QTextStream out(&file);
        out << doc.toString();
        file.close();
260
        parseProfiles(newGroupName, newProfileName);
261 262 263 264
    }
    delete d;
}

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
void RenderWidget::slotEditProfile() {
    QListWidgetItem *item = m_view.size_list->currentItem();
    if (!item) return;
    QString currentGroup = m_view.format_list->currentItem()->text();

    QString params = item->data(ParamsRole).toString();
    QString extension = item->data(ExtensionRole).toString();
    QString currentProfile = item->text();

    Ui::SaveProfile_UI ui;
    QDialog *d = new QDialog(this);
    ui.setupUi(d);
    QStringList groupNames;
    for (int i = 0; i < m_view.format_list->count(); i++)
        groupNames.append(m_view.format_list->item(i)->text());
    if (!groupNames.contains(currentGroup)) groupNames.prepend(currentGroup);
    ui.group_name->addItems(groupNames);
    int pos = ui.group_name->findText(currentGroup);
    ui.group_name->setCurrentIndex(pos);
    ui.profile_name->setText(currentProfile);
    ui.extension->setText(extension);
    ui.parameters->setText(params);
    ui.profile_name->setFocus();

    if (d->exec() == QDialog::Accepted) {
290
        slotDeleteProfile();
291
        QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
        QDomDocument doc;
        QFile file(exportFile);
        doc.setContent(&file, false);
        file.close();

        QDomElement documentElement;
        bool groupExists = false;
        QString groupName;
        QString newProfileName = ui.profile_name->text();
        QString newGroupName = ui.group_name->currentText();
        QDomNodeList groups = doc.elementsByTagName("group");
        int i = 0;
        if (groups.count() == 0) {
            QDomElement profiles = doc.createElement("profiles");
            doc.appendChild(profiles);
        } else while (!groups.item(i).isNull()) {
                documentElement = groups.item(i).toElement();
                groupName = documentElement.attribute("name");
                kDebug() << "// SAVE, PARSING FROUP: " << i << ", name: " << groupName << ", LOOK FR: " << newGroupName;
                if (groupName == newGroupName) {
                    groupExists = true;
                    break;
                }
                i++;
            }
        if (!groupExists) {
            documentElement = doc.createElement("group");
            documentElement.setAttribute("name", ui.group_name->currentText());
            documentElement.setAttribute("renderer", "avformat");
            doc.documentElement().appendChild(documentElement);
        }
        QDomElement profileElement = doc.createElement("profile");
        profileElement.setAttribute("name", newProfileName);
        profileElement.setAttribute("extension", ui.extension->text().simplified());
326
        profileElement.setAttribute("args", ui.parameters->toPlainText().simplified());
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        documentElement.appendChild(profileElement);

        //QCString save = doc.toString().utf8();

        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
            delete d;
            return;
        }
        QTextStream out(&file);
        out << doc.toString();
        file.close();
        parseProfiles(newGroupName, newProfileName);
    }
    delete d;
}

344 345 346 347
void RenderWidget::slotDeleteProfile() {
    QString currentGroup = m_view.format_list->currentItem()->text();
    QString currentProfile = m_view.size_list->currentItem()->text();

348
    QString exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    QDomDocument doc;
    QFile file(exportFile);
    doc.setContent(&file, false);
    file.close();

    QDomElement documentElement;
    bool groupExists = false;
    QString groupName;
    QDomNodeList groups = doc.elementsByTagName("group");
    int i = 0;

    while (!groups.item(i).isNull()) {
        documentElement = groups.item(i).toElement();
        groupName = documentElement.attribute("name");
        if (groupName == currentGroup) {
            QDomNodeList children = documentElement.childNodes();
            for (int j = 0; j < children.count(); j++) {
                QDomElement pro = children.at(j).toElement();
                if (pro.attribute("name") == currentProfile) {
                    groups.item(i).removeChild(children.at(j));
                    if (groups.item(i).childNodes().isEmpty())
                        doc.documentElement().removeChild(groups.item(i));
                    break;
                }
            }
            break;
        }
        i++;
    }

    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        KMessageBox::sorry(this, i18n("Unable to write to file %1", exportFile));
        return;
    }
    QTextStream out(&file);
    out << doc.toString();
    file.close();
386
    parseProfiles(currentGroup);
387 388 389
    focusFirstVisibleItem();
}

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
void RenderWidget::updateButtons() {
    if (!m_view.size_list->currentItem() || m_view.size_list->currentItem()->isHidden()) {
        m_view.buttonSave->setEnabled(false);
        m_view.buttonDelete->setEnabled(false);
        m_view.buttonEdit->setEnabled(false);
    } else {
        m_view.buttonSave->setEnabled(true);
        if (m_view.size_list->currentItem()->data(EditableRole).toString().isEmpty()) {
            m_view.buttonDelete->setEnabled(false);
            m_view.buttonEdit->setEnabled(false);
        } else {
            m_view.buttonDelete->setEnabled(true);
            m_view.buttonEdit->setEnabled(true);
        }
    }
}


408
void RenderWidget::focusFirstVisibleItem() {
409 410 411 412
    if (m_view.size_list->currentItem() && !m_view.size_list->currentItem()->isHidden()) {
        updateButtons();
        return;
    }
413
    for (uint ix = 0; ix < m_view.size_list->count(); ix++) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
414 415 416 417 418
        QListWidgetItem *item = m_view.size_list->item(ix);
        if (item && !item->isHidden()) {
            m_view.size_list->setCurrentRow(ix);
            break;
        }
419 420
    }
    if (!m_view.size_list->currentItem()) m_view.size_list->setCurrentRow(0);
421
    updateButtons();
422 423
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
424
void RenderWidget::slotExport() {
425 426
    QListWidgetItem *item = m_view.size_list->currentItem();
    if (!item) return;
427 428 429
    const QString dest = m_view.out_file->url().path();
    if (dest.isEmpty()) return;
    QFile f(dest);
430
    if (f.exists()) {
431
        if (KMessageBox::warningYesNo(this, i18n("File already exists. Do you want to overwrite it ?")) != KMessageBox::Yes)
432 433
            return;
    }
434 435 436 437 438 439 440
    QStringList overlayargs;
    if (m_view.tc_overlay->isChecked()) {
        QString filterFile = KStandardDirs::locate("appdata", "metadata.properties");
        overlayargs << "meta.attr.timecode=1" << "meta.attr.timecode.markup=#timecode";
        overlayargs << "-attach" << "data_feed:attr_check" << "-attach";
        overlayargs << "data_show:" + filterFile << "_fezzik=1" << "dynamic=1";
    }
441 442 443 444 445 446
    double startPos = -1;
    double endPos = -1;
    if (m_view.render_guide->isChecked()) {
        startPos = m_view.guide_start->itemData(m_view.guide_start->currentIndex()).toDouble();
        endPos = m_view.guide_end->itemData(m_view.guide_end->currentIndex()).toDouble();
    }
447
    QString renderArgs = m_view.advanced_params->toPlainText();
448 449 450 451 452 453 454 455 456 457 458

    // Adjust frame scale
    int width;
    int height;
    if (m_view.rescale->isChecked() && m_view.rescale->isEnabled()) {
        width = m_view.rescale_size->text().section('x', 0, 0).toInt();
        height = m_view.rescale_size->text().section('x', 1, 1).toInt();
    } else {
        width = m_profile.width;
        height = m_profile.height;
    }
459
    renderArgs.replace("%dar", "@" + QString::number(m_profile.display_aspect_num) + "/" + QString::number(m_profile.display_aspect_den));
460 461 462 463 464 465 466 467

    // Adjust scanning
    if (m_view.scanning_list->currentIndex() == 1) renderArgs.append(" progressive=1");
    else if (m_view.scanning_list->currentIndex() == 2) renderArgs.append(" progressive=0");

    // disable audio if requested
    if (!m_view.export_audio->isChecked())
        renderArgs.append(" an=1 ");
468 469 470 471 472

    // Check if the rendering profile is different from project profile,
    // in which case we need to use the producer_comsumer from MLT
    bool resizeProfile = false;

473
    QString std = renderArgs;
474 475
    QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
    if (std.contains(" s=")) {
476 477
        QString subsize = std.section(" s=", 1, 1);
        subsize = subsize.section(' ', 0, 0).toLower();
478
        const QString currentSize = QString::number(width) + 'x' + QString::number(height);
479
        if (subsize != currentSize) resizeProfile = true;
480
    } else if (destination != "audioonly") {
481
        // Add current size parametrer
482
        renderArgs.append(QString(" s=%1x%2").arg(width).arg(height));
483 484
    }

485 486
    // insert item in running jobs list
    QTreeWidgetItem *renderItem;
487
    QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
488
    if (!existing.isEmpty()) renderItem = existing.at(0);
489 490
    else renderItem = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
    renderItem->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
491
    renderItem->setData(1, Qt::UserRole + 1, QTime::currentTime());
492

493
    // Set rendering type
494 495 496 497 498 499 500 501 502 503
    QString group = m_view.size_list->currentItem()->data(MetaGroupRole).toString();
    if (group == "dvd" && m_view.open_dvd->isChecked()) {
        renderItem->setData(0, Qt::UserRole, group);
        if (renderArgs.contains("profile=")) {
            // rendering profile contains an MLT profile, so pass it to the running jog item, usefull for dvd
            QString prof = renderArgs.section("profile=", 1, 1);
            prof = prof.section(' ', 0, 0);
            kDebug() << "// render profile: " << prof;
            renderItem->setData(0, Qt::UserRole + 1, prof);
        }
504 505 506 507 508
    } else if (group == "websites" && m_view.open_browser->isChecked()) {
        renderItem->setData(0, Qt::UserRole, group);
        // pass the url
        QString url = m_view.size_list->currentItem()->data(ExtraRole).toString();
        renderItem->setData(0, Qt::UserRole + 1, url);
509
    }
510 511

    emit doRender(dest, item->data(RenderRole).toString(), overlayargs, renderArgs.simplified().split(' '), m_view.render_zone->isChecked(), m_view.play_after->isChecked(), startPos, endPos, resizeProfile);
512
    m_view.tabWidget->setCurrentIndex(1);
513 514
}

515 516 517 518
void RenderWidget::setProfile(MltVideoProfile profile) {
    m_profile = profile;
    //WARNING: this way to tell the video standard is a bit hackish...
    if (m_profile.description.contains("pal", Qt::CaseInsensitive) || m_profile.description.contains("25", Qt::CaseInsensitive) || m_profile.description.contains("50", Qt::CaseInsensitive)) m_view.format_selection->setCurrentIndex(0);
519
    else m_view.format_selection->setCurrentIndex(1);
520
    m_view.scanning_list->setCurrentIndex(0);
521 522 523 524
    refreshView();
}

void RenderWidget::refreshView() {
525
    m_view.size_list->blockSignals(true);
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
    QListWidgetItem *sizeItem;

    QString destination;
    if (m_view.destination_list->currentIndex() > 0)
        destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();

    if (destination == "dvd") m_view.open_dvd->setVisible(true);
    else m_view.open_dvd->setVisible(false);
    if (destination == "websites") m_view.open_browser->setVisible(true);
    else m_view.open_browser->setVisible(false);
    if (!destination.isEmpty() && QString("dvd websites audioonly").contains(destination))
        m_view.rescale->setEnabled(false);
    else m_view.rescale->setEnabled(true);
    // hide groups that are not in the correct destination
    for (int i = 0; i < m_view.format_list->count(); i++) {
        sizeItem = m_view.format_list->item(i);
        if (sizeItem->data(MetaGroupRole).toString() == destination)
            sizeItem->setHidden(false);
        else sizeItem->setHidden(true);
    }

    // activate first visible item
    QListWidgetItem * item = m_view.format_list->currentItem();
    if (!item || item->isHidden()) {
        for (int i = 0; i < m_view.format_list->count(); i++) {
            if (!m_view.format_list->item(i)->isHidden()) {
                m_view.format_list->setCurrentRow(i);
                break;
            }
        }
556 557 558
        item = m_view.format_list->currentItem();
    }
    if (!item) return;
559 560 561 562 563 564
    int count = 0;
    for (int i = 0; i < m_view.format_list->count() && count < 2; i++) {
        if (!m_view.format_list->isRowHidden(i)) count++;
    }
    if (count > 1) m_view.format_list->setVisible(true);
    else m_view.format_list->setVisible(false);
565 566 567
    QString std;
    QString group = item->text();
    bool firstSelected = false;
568 569 570
    const QStringList formatsList = KdenliveSettings::supportedformats();
    const QStringList vcodecsList = KdenliveSettings::videocodecs();
    const QStringList acodecsList = KdenliveSettings::audiocodecs();
571

572 573
    for (int i = 0; i < m_view.size_list->count(); i++) {
        sizeItem = m_view.size_list->item(i);
574
        if (sizeItem->data(GroupRole) == group) {
575
            std = sizeItem->data(StandardRole).toString();
576 577 578 579 580 581 582 583
            if (!std.isEmpty()) {
                if (std.contains("PAL", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 0);
                else if (std.contains("NTSC", Qt::CaseInsensitive)) sizeItem->setHidden(m_view.format_selection->currentIndex() != 1);
            } else {
                sizeItem->setHidden(false);
                if (!firstSelected) m_view.size_list->setCurrentItem(sizeItem);
                firstSelected = true;
            }
584

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
            if (!sizeItem->isHidden()) {
                // Make sure the selected profile uses an installed avformat codec / format
                std = sizeItem->data(ParamsRole).toString();

                if (!formatsList.isEmpty()) {
                    QString format;
                    if (std.startsWith("f=")) format = std.section("f=", 1, 1);
                    else if (std.contains(" f=")) format = std.section(" f=", 1, 1);
                    if (!format.isEmpty()) {
                        format = format.section(' ', 0, 0).toLower();
                        if (!formatsList.contains(format)) {
                            kDebug() << "*****  UNSUPPORTED F: " << format;
                            sizeItem->setHidden(true);
                        }
                    }
                }
                if (!acodecsList.isEmpty() && !sizeItem->isHidden()) {
                    QString format;
                    if (std.startsWith("acodec=")) format = std.section("acodec=", 1, 1);
                    else if (std.contains(" acodec=")) format = std.section(" acodec=", 1, 1);
                    if (!format.isEmpty()) {
                        format = format.section(' ', 0, 0).toLower();
                        if (!acodecsList.contains(format)) {
                            kDebug() << "*****  UNSUPPORTED ACODEC: " << format;
                            sizeItem->setHidden(true);
                        }
                    }
                }
                if (!vcodecsList.isEmpty() && !sizeItem->isHidden()) {
                    QString format;
                    if (std.startsWith("vcodec=")) format = std.section("vcodec=", 1, 1);
                    else if (std.contains(" vcodec=")) format = std.section(" vcodec=", 1, 1);
                    if (!format.isEmpty()) {
                        format = format.section(' ', 0, 0).toLower();
                        if (!vcodecsList.contains(format)) {
                            kDebug() << "*****  UNSUPPORTED VCODEC: " << format;
                            sizeItem->setHidden(true);
                        }
                    }
                }
            }
626 627
        } else sizeItem->setHidden(true);
    }
628
    focusFirstVisibleItem();
629 630
    m_view.size_list->blockSignals(false);
    refreshParams();
631 632
}

633 634 635 636 637 638 639 640 641
KUrl RenderWidget::filenameWithExtension(KUrl url, QString extension) {
    QString path;
    if (!url.isEmpty()) {
        path = url.path();
        int pos = path.lastIndexOf('.') + 1;
        if (pos == 0) path.append('.' + extension);
        else path = path.left(pos) + extension;

    } else {
642
        path = m_projectFolder + "/untitled." + extension;
643 644 645 646 647
    }
    return KUrl(path);
}


648 649
void RenderWidget::refreshParams() {
    QListWidgetItem *item = m_view.size_list->currentItem();
650 651 652 653 654
    if (!item || item->isHidden()) {
        m_view.advanced_params->clear();
        m_view.buttonStart->setEnabled(false);
        return;
    }
655 656
    QString params = item->data(ParamsRole).toString();
    QString extension = item->data(ExtensionRole).toString();
657
    m_view.advanced_params->setPlainText(params);
658 659 660 661 662 663 664 665 666
    QString destination = m_view.destination_list->itemData(m_view.destination_list->currentIndex()).toString();
    if (params.contains(" s=") || destination == "audioonly") {
        // profile has a fixed size, do not allow resize
        m_view.rescale->setEnabled(false);
        m_view.rescale_size->setEnabled(false);
    } else {
        m_view.rescale->setEnabled(true);
        m_view.rescale_size->setEnabled(true);
    }
667 668 669 670 671 672 673 674 675 676 677
    KUrl url = filenameWithExtension(m_view.out_file->url(), extension);
    m_view.out_file->setUrl(url);
//     if (!url.isEmpty()) {
//         QString path = url.path();
//         int pos = path.lastIndexOf('.') + 1;
//  if (pos == 0) path.append('.' + extension);
//         else path = path.left(pos) + extension;
//         m_view.out_file->setUrl(KUrl(path));
//     } else {
//         m_view.out_file->setUrl(KUrl(QDir::homePath() + "/untitled." + extension));
//     }
678
    m_view.out_file->setFilter("*." + extension);
679

680
    if (item->data(EditableRole).toString().isEmpty()) {
681 682 683 684 685
        m_view.buttonDelete->setEnabled(false);
        m_view.buttonEdit->setEnabled(false);
    } else {
        m_view.buttonDelete->setEnabled(true);
        m_view.buttonEdit->setEnabled(true);
686
    }
687
    m_view.buttonStart->setEnabled(true);
688 689
}

690
void RenderWidget::parseProfiles(QString group, QString profile) {
691 692
    m_view.size_list->clear();
    m_view.format_list->clear();
693 694
    m_view.destination_list->clear();
    m_view.destination_list->addItem(KIcon("video-x-generic"), i18n("File rendering"));
695
    QString exportFile = KStandardDirs::locate("appdata", "export/profiles.xml");
696
    parseFile(exportFile, false);
697
    exportFile = KStandardDirs::locateLocal("appdata", "export/customprofiles.xml");
698
    if (QFile::exists(exportFile)) parseFile(exportFile, true);
699
    refreshView();
700 701 702 703 704
    QList<QListWidgetItem *> child;
    child = m_view.format_list->findItems(group, Qt::MatchExactly);
    if (!child.isEmpty()) m_view.format_list->setCurrentItem(child.at(0));
    child = m_view.size_list->findItems(profile, Qt::MatchExactly);
    if (!child.isEmpty()) m_view.size_list->setCurrentItem(child.at(0));
705 706 707
}

void RenderWidget::parseFile(QString exportFile, bool editable) {
708 709 710
    QDomDocument doc;
    QFile file(exportFile);
    doc.setContent(&file, false);
711
    file.close();
712 713 714 715 716 717 718 719
    QDomElement documentElement;
    QDomElement profileElement;
    QDomNodeList groups = doc.elementsByTagName("group");

    if (groups.count() == 0) {
        kDebug() << "// Export file: " << exportFile << " IS BROKEN";
        return;
    }
720

721 722 723 724 725 726 727 728
    int i = 0;
    QString groupName;
    QString profileName;
    QString extension;
    QString prof_extension;
    QString renderer;
    QString params;
    QString standard;
729
    KIcon icon;
730 731 732
    QListWidgetItem *item;
    while (!groups.item(i).isNull()) {
        documentElement = groups.item(i).toElement();
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
        QDomNode gname = documentElement.elementsByTagName("groupname").at(0);
        QString metagroupName;
        QString metagroupId;
        if (!gname.isNull()) {
            metagroupName = gname.firstChild().nodeValue();
            metagroupId = gname.toElement().attribute("id");
            if (!metagroupName.isEmpty() && !m_view.destination_list->contains(metagroupName)) {
                if (metagroupId == "dvd") icon = KIcon("media-optical");
                else if (metagroupId == "audioonly") icon = KIcon("audio-x-generic");
                else if (metagroupId == "websites") icon = KIcon("applications-internet");
                else if (metagroupId == "mediaplayers") icon = KIcon("applications-multimedia");
                else if (metagroupId == "lossless") icon = KIcon("drive-harddisk");
                m_view.destination_list->addItem(icon, i18n(metagroupName.toUtf8().data()), metagroupId);
            }
        }
748 749 750
        groupName = documentElement.attribute("name", QString::null);
        extension = documentElement.attribute("extension", QString::null);
        renderer = documentElement.attribute("renderer", QString::null);
751 752 753 754
        if (m_view.format_list->findItems(groupName, Qt::MatchExactly).isEmpty()) {
            item = new QListWidgetItem(groupName, m_view.format_list);
            item->setData(MetaGroupRole, metagroupId);
        }
755 756 757

        QDomNode n = groups.item(i).firstChild();
        while (!n.isNull()) {
758 759 760 761
            if (n.toElement().tagName() != "profile") {
                n = n.nextSibling();
                continue;
            }
762 763 764 765 766 767 768 769
            profileElement = n.toElement();
            profileName = profileElement.attribute("name");
            standard = profileElement.attribute("standard");
            params = profileElement.attribute("args");
            prof_extension = profileElement.attribute("extension");
            if (!prof_extension.isEmpty()) extension = prof_extension;
            item = new QListWidgetItem(profileName, m_view.size_list);
            item->setData(GroupRole, groupName);
770
            item->setData(MetaGroupRole, metagroupId);
771 772 773 774
            item->setData(ExtensionRole, extension);
            item->setData(RenderRole, renderer);
            item->setData(StandardRole, standard);
            item->setData(ParamsRole, params);
775
            if (profileElement.hasAttribute("url")) item->setData(ExtraRole, profileElement.attribute("url"));
776
            if (editable) item->setData(EditableRole, "true");
777 778 779 780 781
            n = n.nextSibling();
        }

        i++;
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
782 783
}

784
void RenderWidget::setRenderJob(const QString &dest, int progress) {
785
    QTreeWidgetItem *item;
786
    QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
787
    if (!existing.isEmpty()) item = existing.at(0);
788 789 790 791 792
    else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
    item->setData(2, Qt::UserRole, progress);
    if (progress == 0) {
        item->setIcon(0, KIcon("system-run"));
        item->setSizeHint(1, QSize(m_view.running_jobs->columnWidth(1), fontMetrics().height() * 2));
793
        item->setData(1, Qt::UserRole + 1, QTime::currentTime());
794
    } else {
795 796 797 798
        QTime startTime = item->data(1, Qt::UserRole + 1).toTime();
        int seconds = startTime.secsTo(QTime::currentTime());;
        const QString t = i18n("Estimated time %1", QTime().addSecs(seconds * (100 - progress) / progress).toString("hh:mm:ss"));
        item->setData(1, Qt::UserRole, t);
799
    }
800 801 802 803
}

void RenderWidget::setRenderStatus(const QString &dest, int status, const QString &error) {
    QTreeWidgetItem *item;
804
    QList<QTreeWidgetItem *> existing = m_view.running_jobs->findItems(dest, Qt::MatchExactly, 1);
805
    if (!existing.isEmpty()) item = existing.at(0);
806
    else item = new QTreeWidgetItem(m_view.running_jobs, QStringList() << QString() << dest << QString());
807
    if (status == -1) {
808 809
        // Job finished successfully
        item->setIcon(0, KIcon("dialog-ok"));
810
        item->setData(2, Qt::UserRole, 100);
811
        QTime startTime = item->data(1, Qt::UserRole + 1).toTime();
812
        int seconds = startTime.secsTo(QTime::currentTime());
813 814 815
        const QTime tm = QTime().addSecs(seconds);
        const QString t = i18n("Rendering finished in %1", tm.toString("hh:mm:ss"));
        item->setData(1, Qt::UserRole, t);
816 817
        QString itemGroup = item->data(0, Qt::UserRole).toString();
        if (itemGroup == "dvd") {
818
            emit openDvdWizard(item->text(1), item->data(0, Qt::UserRole + 1).toString());
819 820 821
        } else if (itemGroup == "websites") {
            QString url = item->data(0, Qt::UserRole + 1).toString();
            if (!url.isEmpty()) KRun *openBrowser = new KRun(url, this);
822
        }
823
    } else if (status == -2) {
824
        // Rendering crashed
825
        item->setData(1, Qt::UserRole, i18n("Rendering crashed"));
826
        item->setIcon(0, KIcon("dialog-close"));
827
        item->setData(2, Qt::UserRole, 0);
828 829 830 831 832
        m_view.error_log->append(i18n("<strong>Rendering of %1 crashed</strong><br />", dest));
        m_view.error_log->append(error);
        m_view.error_log->append("<hr />");
        m_view.error_box->setVisible(true);
    } else if (status == -3) {
833
        // User aborted job
834
        item->setData(1, Qt::UserRole, i18n("Rendering aborted"));
835
        item->setIcon(0, KIcon("dialog-cancel"));
836
        item->setData(2, Qt::UserRole, 100);
837
    }
838
}
839

840 841
void RenderWidget::slotAbortCurrentJob() {
    QTreeWidgetItem *current = m_view.running_jobs->currentItem();
842
    if (current) emit abortProcess(current->text(1));
843 844
}

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
845
#include "renderwidget.moc"