bin.cpp 125 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
Copyright (C) 2012  Till Theato <root@ttill.de>
Copyright (C) 2014  Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.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) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy 
defined in Section 14 of version 3 of the license.

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, see <http://www.gnu.org/licenses/>.
*/

#include "bin.h"
#include "mainwindow.h"
#include "projectitemmodel.h"
#include "projectclip.h"
27
#include "projectsubclip.h"
28
#include "projectfolder.h"
29
#include "projectfolderup.h"
30 31 32
#include "kdenlivesettings.h"
#include "project/projectmanager.h"
#include "project/clipmanager.h"
33
#include "project/dialogs/slideshowclip.h"
34 35 36
#include "project/jobs/jobmanager.h"
#include "monitor/monitor.h"
#include "doc/kdenlivedoc.h"
37
#include "dialogs/clipcreationdialog.h"
Vincent Pinon's avatar
Vincent Pinon committed
38
#include "ui_qtextclip_ui.h"
39
#include "titler/titlewidget.h"
40
#include "core.h"
41
#include "utils/KoIconUtils.h"
42
#include "mltcontroller/clipcontroller.h"
43 44
#include "mltcontroller/clippropertiescontroller.h"
#include "project/projectcommands.h"
45
#include "project/invaliddialog.h"
46
#include "projectsortproxymodel.h"
47
#include "bincommands.h"
48 49
#include "mlt++/Mlt.h"

50
#include <QToolBar>
51
#include <KColorScheme>
52
#include <KMessageBox>
53
#include <KXMLGUIFactory>
54

55 56
#include <QDesktopServices>
#include <QUrl>
57
#include <QDialogButtonBox>
58
#include <QDrag>
59
#include <QVBoxLayout>
60
#include <QTimeLine>
61 62
#include <QSlider>
#include <QMenu>
63
#include <QDebug>
64
#include <QtConcurrent>
65
#include <QUndoCommand>
66

67

68
MyListView::MyListView(QWidget * parent) : QListView(parent)
69 70 71 72 73 74 75 76 77 78 79 80 81 82
{
    setViewMode(QListView::IconMode);
    setMovement(QListView::Static);
    setResizeMode(QListView::Adjust);
    setUniformItemSizes(true);
    setDragDropMode(QAbstractItemView::DragDrop);
    setAcceptDrops(true);
    setDragEnabled(true);
    viewport()->setAcceptDrops(true);
}

void MyListView::focusInEvent(QFocusEvent *event)
{
    QListView::focusInEvent(event);
83
    if (event->reason() == Qt::MouseFocusReason) emit focusView();
84 85
}

86 87 88 89
MyTreeView::MyTreeView(QWidget * parent) : QTreeView(parent)
{
    setEditing(false);
}
90 91 92 93 94 95 96 97 98

void MyTreeView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_startPos = event->pos();
    }
    QTreeView::mousePressEvent(event);
}

99 100 101
void MyTreeView::focusInEvent(QFocusEvent *event)
{
    QTreeView::focusInEvent(event);
102
    if (event->reason() == Qt::MouseFocusReason) emit focusView();
103 104
}

105 106 107 108 109 110 111 112 113 114 115
void MyTreeView::mouseMoveEvent(QMouseEvent * event) 
{
    bool dragged = false;
    if (event->buttons() & Qt::LeftButton) {
        int distance = (event->pos() - m_startPos).manhattanLength();
        if (distance >= QApplication::startDragDistance())
        dragged = performDrag();
    }
    if (!dragged) QTreeView::mouseMoveEvent(event);
}

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
void MyTreeView::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
{
    QAbstractItemView::closeEditor(editor, hint);
    setEditing(false);
}

void MyTreeView::editorDestroyed(QObject *editor)
{
    QAbstractItemView::editorDestroyed(editor);
    setEditing(false);
}

void MyTreeView::keyPressEvent(QKeyEvent* event)
{
    if (isEditing() == true) {
        QTreeView::keyPressEvent(event);
        return;
    }
    QModelIndex currentIndex = this->currentIndex();
    if( event->key() == Qt::Key_Return && currentIndex.isValid() ) {
        if (this->isExpanded( currentIndex )) {
            this->collapse(currentIndex);
        } else {
            this->expand(currentIndex);
        }
    }
    QTreeView::keyPressEvent(event);
}

bool MyTreeView::isEditing()
{
    return m_editing;
}

void MyTreeView::setEditing(bool edit)
{
    m_editing = edit;
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
bool MyTreeView::performDrag()
{
    QModelIndexList bases = selectedIndexes();
    QModelIndexList indexes;
    for (int i = 0; i < bases.count(); i++) {
        if (bases.at(i).column() == 0) {
            indexes << bases.at(i);
        }
    }
    if (indexes.isEmpty()) return false;
    QDrag *drag = new QDrag(this);
    drag->setMimeData(model()->mimeData(indexes));
    QModelIndex ix = indexes.first();
    if (ix.isValid()) {
        QIcon icon = ix.data(AbstractProjectItem::DataThumbnail).value<QIcon>();
        QPixmap pix = icon.pixmap(iconSize());
        QSize size = pix.size();
        QImage image(size, QImage::Format_ARGB32_Premultiplied);
        image.fill(Qt::transparent);
        QPainter p(&image);
        p.setOpacity(0.7);
        p.drawPixmap(0, 0, pix);
        p.setOpacity(1);
        if (indexes.count() > 1) {
            QPalette palette;
            int radius = size.height() / 3;
            p.setBrush(palette.highlight());
            p.setPen(palette.highlightedText().color());
            p.drawEllipse(QPoint(size.width() / 2, size.height() / 2), radius, radius);
            p.drawText(size.width() / 2 - radius, size.height() / 2 - radius, 2 * radius, 2 * radius, Qt::AlignCenter, QString::number(indexes.count()));
        }
        p.end();
        drag->setPixmap(QPixmap::fromImage(image));
    }
    drag->exec();
    return true;
}

193 194 195 196 197 198 199 200 201
BinMessageWidget::BinMessageWidget(QWidget *parent) : KMessageWidget(parent) {}
BinMessageWidget::BinMessageWidget(const QString &text, QWidget *parent) : KMessageWidget(text, parent) {}


bool BinMessageWidget::event(QEvent* ev) {
    if (ev->type() == QEvent::Hide || ev->type() == QEvent::Close) emit messageClosing();
    return KMessageWidget::event(ev);
}

202
SmallJobLabel::SmallJobLabel(QWidget *parent) : QPushButton(parent)
203
    , m_action(NULL)
204 205 206 207 208 209 210 211 212 213 214 215 216 217
{
    setFixedWidth(0);
    setFlat(true);
    m_timeLine = new QTimeLine(500, this);
    QObject::connect(m_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(slotTimeLineChanged(qreal)));
    QObject::connect(m_timeLine, SIGNAL(finished()), this, SLOT(slotTimeLineFinished()));
    hide();
}

const QString SmallJobLabel::getStyleSheet(const QPalette &p)
{
    KColorScheme scheme(p.currentColorGroup(), KColorScheme::Window, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
    QColor bg = scheme.background(KColorScheme::LinkBackground).color();
    QColor fg = scheme.foreground(KColorScheme::LinkText).color();
218
    QString style = QStringLiteral("QPushButton {margin:3px;padding:2px;background-color: rgb(%1, %2, %3);border-radius: 4px;border: none;color: rgb(%4, %5, %6)}").arg(bg.red()).arg(bg.green()).arg(bg.blue()).arg(fg.red()).arg(fg.green()).arg(fg.blue());
219 220 221
    
    bg = scheme.background(KColorScheme::ActiveBackground).color();
    fg = scheme.foreground(KColorScheme::ActiveText).color();
222
    style.append(QStringLiteral("\nQPushButton:hover {margin:3px;padding:2px;background-color: rgb(%1, %2, %3);border-radius: 4px;border: none;color: rgb(%4, %5, %6)}").arg(bg.red()).arg(bg.green()).arg(bg.blue()).arg(fg.red()).arg(fg.green()).arg(fg.blue()));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
223

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    return style;
}

void SmallJobLabel::setAction(QAction *action)
{
    m_action = action;
}

void SmallJobLabel::slotTimeLineChanged(qreal value)
{
    setFixedWidth(qMin(value * 2, qreal(1.0)) * sizeHint().width());
    update();
}

void SmallJobLabel::slotTimeLineFinished()
{
    if (m_timeLine->direction() == QTimeLine::Forward) {
        // Show
        m_action->setVisible(true);
    } else {
        // Hide
        m_action->setVisible(false);
        setText(QString());
    }
}

void SmallJobLabel::slotSetJobCount(int jobCount)
{
    if (jobCount > 0) {
        // prepare animation
        setText(i18np("%1 job", "%1 jobs", jobCount));
        setToolTip(i18np("%1 pending job", "%1 pending jobs", jobCount));
256

257 258 259 260 261
        if (style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) {
            setFixedWidth(sizeHint().width());
            m_action->setVisible(true);
            return;
        }
262

263 264 265 266 267
        if (m_action->isVisible()) {
            setFixedWidth(sizeHint().width());
            update();
            return;
        }
268

269 270 271 272 273 274 275 276
        setFixedWidth(0);
        m_action->setVisible(true);
        int wantedWidth = sizeHint().width();
        setGeometry(-wantedWidth, 0, wantedWidth, height());
        m_timeLine->setDirection(QTimeLine::Forward);
        if (m_timeLine->state() == QTimeLine::NotRunning) {
            m_timeLine->start();
        }
277
    } else {
278 279 280 281 282 283 284 285 286 287 288 289 290
        if (style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) {
            setFixedWidth(0);
            m_action->setVisible(false);
            return;
        }
        // hide
        m_timeLine->setDirection(QTimeLine::Backward);
        if (m_timeLine->state() == QTimeLine::NotRunning) {
            m_timeLine->start();
        }
    }
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304
LineEventEater::LineEventEater(QObject *parent) : QObject(parent)
{
}

bool LineEventEater::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::ShortcutOverride) {
        if (((QKeyEvent*)event)->key() == Qt::Key_Escape) {
            emit clearSearchLine();
        }
    }
    return QObject::eventFilter(obj, event);
}

305 306 307 308


Bin::Bin(QWidget* parent) :
    QWidget(parent)
309
  , isLoading(false)
310 311 312
  , m_itemModel(NULL)
  , m_itemView(NULL)
  , m_rootFolder(NULL)
313
  , m_folderUp(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
314
  , m_jobManager(NULL)
315
  , m_doc(NULL)
316
  , m_extractAudioAction(NULL)
317
  , m_transcodeAction(NULL)
318 319
  , m_clipsActionsMenu(NULL)
  , m_inTimelineAction(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
320
  , m_listType((BinViewType) KdenliveSettings::binMode())
321 322
  , m_iconSize(160, 90)
  , m_propertiesPanel(NULL)
Vincent Pinon's avatar
Vincent Pinon committed
323
  , m_blankThumb()
324
  , m_invalidClipDialog(NULL)
325
  , m_gainedFocus(false)
326 327
  , m_audioDuration(0)
  , m_processedAudio(0)
328
{
329
    m_layout = new QVBoxLayout(this);
330 331

    // Create toolbar for buttons
332
    m_toolbar = new QToolBar(this);
333
    m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
334
    m_layout->addWidget(m_toolbar);
335 336 337 338 339

    // Search line
    m_proxyModel = new ProjectSortProxyModel(this);
    m_proxyModel->setDynamicSortFilter(true);
    QLineEdit *searchLine = new QLineEdit(this);
340
    searchLine->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
341
    searchLine->setClearButtonEnabled(true);
342
    searchLine->setPlaceholderText(i18n("Search"));
343
    searchLine->setFocusPolicy(Qt::ClickFocus);
344
    connect(searchLine, SIGNAL(textChanged(const QString &)), m_proxyModel, SLOT(slotSetSearchString(const QString &)));
345 346 347 348

    LineEventEater *leventEater = new LineEventEater(this);
    searchLine->installEventFilter(leventEater);
    connect(leventEater, SIGNAL(clearSearchLine()), searchLine, SLOT(clear()));
349

350
    setFocusPolicy(Qt::ClickFocus);
351 352 353 354 355
    // Build item view model
    m_itemModel = new ProjectItemModel(this);

    // Connect models
    m_proxyModel->setSourceModel(m_itemModel);
356 357
    connect(m_itemModel, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), m_proxyModel, SLOT(slotDataChanged(const QModelIndex&,const
    QModelIndex&)));
358 359 360
    connect(m_itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
    connect(m_itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
    connect(m_proxyModel, SIGNAL(selectModel(QModelIndex)), this, SLOT(selectProxyModel(QModelIndex)));
361 362
    connect(m_itemModel, SIGNAL(itemDropped(QStringList, const QModelIndex &)), this, SLOT(slotItemDropped(QStringList, const QModelIndex &)));
    connect(m_itemModel, SIGNAL(itemDropped(const QList<QUrl>&, const QModelIndex &)), this, SLOT(slotItemDropped(const QList<QUrl>&, const QModelIndex &)));
363
    connect(m_itemModel, SIGNAL(effectDropped(QString, const QModelIndex &)), this, SLOT(slotEffectDropped(QString, const QModelIndex &)));
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
364
    connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(slotItemEdited(QModelIndex,QModelIndex,QVector<int>)));
365
    connect(m_itemModel, SIGNAL(addClipCut(QString,int,int)), this, SLOT(slotAddClipCut(QString,int,int)));
366
    connect(this, &Bin::refreshPanel, this, &Bin::doRefreshPanel);
367

368
    // Zoom slider
369 370
    m_slider = new QSlider(Qt::Horizontal, this);
    m_slider->setMaximumWidth(100);
371
    m_slider->setMinimumWidth(40);
372
    m_slider->setRange(0, 10);
373
    m_slider->setValue(KdenliveSettings::bin_zoom());
374
    connect(m_slider, SIGNAL(valueChanged(int)), this, SLOT(slotSetIconSize(int)));
375 376
    QWidgetAction * widgetslider = new QWidgetAction(this);
    widgetslider->setDefaultWidget(m_slider);
377 378

    // View type
379 380 381
    KSelectAction *listType = new KSelectAction(KoIconUtils::themedIcon(QStringLiteral("view-list-tree")), i18n("View Mode"), this);
    pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode"), listType);
    QAction *treeViewAction = listType->addAction(KoIconUtils::themedIcon(QStringLiteral("view-list-tree")), i18n("Tree View"));
382
    listType->addAction(treeViewAction);
383 384 385 386
    treeViewAction->setData(BinTreeView);
    if (m_listType == treeViewAction->data().toInt()) {
        listType->setCurrentAction(treeViewAction);
    }
387
    pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode_tree"), treeViewAction);
388

389
    QAction *iconViewAction = listType->addAction(KoIconUtils::themedIcon(QStringLiteral("view-list-icons")), i18n("Icon View"));
390 391 392 393
    iconViewAction->setData(BinIconView);
    if (m_listType == iconViewAction->data().toInt()) {
        listType->setCurrentAction(iconViewAction);
    }
394
    pCore->window()->actionCollection()->addAction(QStringLiteral("bin_view_mode_icon"), iconViewAction);
395 396 397

    QAction *disableEffects = new QAction(i18n("Disable Bin Effects"), this);
    connect(disableEffects, SIGNAL(triggered(bool)), this, SLOT(slotDisableEffects(bool)));
398
    disableEffects->setIcon(KoIconUtils::themedIcon(QStringLiteral("favorite")));
399 400 401
    disableEffects->setData("disable_bin_effects");
    disableEffects->setCheckable(true);
    disableEffects->setChecked(false);
402
    pCore->window()->actionCollection()->addAction(QStringLiteral("disable_bin_effects"), disableEffects);
403

404 405 406 407 408
    m_renameFolderAction = new QAction(i18n("Rename Folder"), this);
    connect(m_renameFolderAction, SIGNAL(triggered(bool)), this, SLOT(slotRenameFolder()));
    m_renameFolderAction->setData("rename_folder");
    pCore->window()->actionCollection()->addAction("rename_folder", m_renameFolderAction);

409 410
    listType->setToolBarMode(KSelectAction::MenuMode);
    connect(listType, SIGNAL(triggered(QAction*)), this, SLOT(slotInitView(QAction*)));
411 412 413 414 415

    // Settings menu
    QMenu *settingsMenu = new QMenu(i18n("Settings"), this);
    settingsMenu->addAction(listType);
    QMenu *sliderMenu = new QMenu(i18n("Zoom"), this);
416
    sliderMenu->setIcon(KoIconUtils::themedIcon(QStringLiteral("zoom-in")));
417 418
    sliderMenu->addAction(widgetslider);
    settingsMenu->addMenu(sliderMenu);
419

420 421 422 423 424 425 426 427 428
    // Column show / hide actions
    m_showDate = new QAction(i18n("Show date"), this);
    m_showDate->setCheckable(true);
    connect(m_showDate, SIGNAL(triggered(bool)), this, SLOT(slotShowDateColumn(bool)));
    m_showDesc = new QAction(i18n("Show description"), this);
    m_showDesc->setCheckable(true);
    connect(m_showDesc, SIGNAL(triggered(bool)), this, SLOT(slotShowDescColumn(bool)));
    settingsMenu->addAction(m_showDate);
    settingsMenu->addAction(m_showDesc);
429
    settingsMenu->addAction(disableEffects);
430
    QToolButton *button = new QToolButton;
431 432
    button->setIcon(KoIconUtils::themedIcon(QStringLiteral("kdenlive-menu")));
    button->setToolTip(i18n("Options"));
433 434 435
    button->setMenu(settingsMenu);
    button->setPopupMode(QToolButton::InstantPopup);
    m_toolbar->addWidget(button);
436

437 438 439 440 441 442 443 444 445 446
    // small info button for pending jobs
    m_infoLabel = new SmallJobLabel(this);
    m_infoLabel->setStyleSheet(SmallJobLabel::getStyleSheet(palette()));
    QAction *infoAction = m_toolbar->addWidget(m_infoLabel);
    m_jobsMenu = new QMenu(this);
    connect(m_jobsMenu, SIGNAL(aboutToShow()), this, SLOT(slotPrepareJobsMenu()));
    m_cancelJobs = new QAction(i18n("Cancel All Jobs"), this);
    m_cancelJobs->setCheckable(false);
    m_discardCurrentClipJobs = new QAction(i18n("Cancel Current Clip Jobs"), this);
    m_discardCurrentClipJobs->setCheckable(false);
447 448
    m_discardPendingJobs = new QAction(i18n("Cancel Pending Jobs"), this);
    m_discardPendingJobs->setCheckable(false);
449 450
    m_jobsMenu->addAction(m_cancelJobs);
    m_jobsMenu->addAction(m_discardCurrentClipJobs);
451
    m_jobsMenu->addAction(m_discardPendingJobs);
452 453 454 455 456 457 458 459 460 461 462
    m_infoLabel->setMenu(m_jobsMenu);
    m_infoLabel->setAction(infoAction);

    // Hack, create toolbar spacer
    QWidget* spacer = new QWidget();
    spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_toolbar->addWidget(spacer);

    // Add search line
    m_toolbar->addWidget(searchLine);

463 464 465
    m_binTreeViewDelegate = new BinItemDelegate(this);
    //connect(pCore->projectManager(), SIGNAL(projectOpened(Project*)), this, SLOT(setProject(Project*)));
    m_headerInfo = QByteArray::fromBase64(KdenliveSettings::treeviewheaders().toLatin1());
466 467
    m_propertiesPanel = new QScrollArea(this);
    m_propertiesPanel->setFrameShape(QFrame::NoFrame);
468 469
    // Info widget for failed jobs, other errors
    m_infoMessage = new BinMessageWidget;
470
    m_layout->addWidget(m_infoMessage);
471 472
    m_infoMessage->setCloseButtonVisible(false);
    connect(m_infoMessage, SIGNAL(linkActivated(const QString &)), this, SLOT(slotShowJobLog()));
473 474 475
    connect(m_infoMessage, SIGNAL(messageClosing()), this, SLOT(slotResetInfoMessage()));
    //m_infoMessage->setWordWrap(true);
    m_infoMessage->hide();
476
    connect(this, SIGNAL(requesteInvalidRemoval(QString,QUrl,QString)), this, SLOT(slotQueryRemoval(QString,QUrl,QString)));
477
    connect(this, &Bin::refreshAudioThumbs, this, &Bin::doRefreshAudioThumbs);
478
    connect(this, SIGNAL(displayBinMessage(QString,KMessageWidget::MessageType)), this, SLOT(doDisplayMessage(QString,KMessageWidget::MessageType)));
479 480 481 482
}

Bin::~Bin()
{
483
    blockSignals(true);
484
    m_proxyModel->selectionModel()->blockSignals(true);
485
    setEnabled(false);
486
    abortAudioThumbs();
487 488
    if (m_propertiesPanel) {
        foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
489
            delete w;
490
        }
491
    }
492 493 494 495 496 497 498 499 500
    if (m_rootFolder) {
        while (!m_rootFolder->isEmpty()) {
            AbstractProjectItem *child = m_rootFolder->at(0);
            m_rootFolder->removeChild(child);
            delete child;
        }
    }
    delete m_rootFolder;
    delete m_itemView;
501
    delete m_jobManager;
502
    delete m_infoMessage;
503
    delete m_propertiesPanel;
504 505
}

506 507 508 509 510
QDockWidget *Bin::clipPropertiesDock()
{
    return m_propertiesDock;
}

511
void Bin::slotAbortAudioThumb(const QString &id, long duration)
512 513 514
{
    if (!m_audioThumbsThread.isRunning()) return;
    QMutexLocker aMutex(&m_audioThumbMutex);
515 516
    if (m_audioThumbsList.removeAll(id) > 0)
        m_audioDuration -= duration;
517 518
}

519
void Bin::requestAudioThumbs(const QString &id, long duration)
520 521 522 523
{
    if (!m_audioThumbsList.contains(id) && m_processingAudioThumb != id) {
        m_audioThumbMutex.lock();
        m_audioThumbsList.append(id);
524
        m_audioDuration += duration;
525 526 527 528 529
        m_audioThumbMutex.unlock();
        processAudioThumbs();
    }
}

530 531 532 533 534 535
void Bin::doUpdateThumbsProgress(long ms)
{
    int progress = (m_processedAudio + ms) * 100 / m_audioDuration;
    emitMessage(i18n("Creating audio thumbnails"), progress, ProcessingJobMessage);
}

536 537 538 539 540 541 542 543 544 545 546
void Bin::processAudioThumbs()
{
    if (m_audioThumbsThread.isRunning()) return;
    m_audioThumbsThread = QtConcurrent::run(this, &Bin::slotCreateAudioThumbs);
}

void Bin::abortAudioThumbs()
{
    if (!m_audioThumbsThread.isRunning()) return;
    if (!m_processingAudioThumb.isEmpty()) {
        ProjectClip *clip = m_rootFolder->clip(m_processingAudioThumb);
547
        if (clip) clip->abortAudioThumbs();
548 549
    }
    m_audioThumbMutex.lock();
550 551 552 553
    foreach(const QString &id, m_audioThumbsList) {
        ProjectClip *clip = m_rootFolder->clip(id);
        if (clip) clip->setJobStatus(AbstractClipJob::THUMBJOB, JobDone, 0);
    }
554 555 556 557 558 559 560 561 562
    m_audioThumbsList.clear();
    m_audioThumbMutex.unlock();
    m_audioThumbsThread.waitForFinished();
}

void Bin::slotCreateAudioThumbs()
{
    int max = m_audioThumbsList.count();
    int count = 0;
563
    m_processedAudio = 0;
564 565 566 567 568 569 570
    while (!m_audioThumbsList.isEmpty()) {
        m_audioThumbMutex.lock();
        max = qMax(max, m_audioThumbsList.count());
        m_processingAudioThumb = m_audioThumbsList.takeFirst();
        count++;
        m_audioThumbMutex.unlock();
        ProjectClip *clip = m_rootFolder->clip(m_processingAudioThumb);
571 572 573 574
        if (clip) {
            clip->slotCreateAudioThumbs();
            m_processedAudio += clip->duration().ms();
        }
575 576 577
    }
    m_audioThumbMutex.lock();
    m_processingAudioThumb.clear();
578 579
    m_processedAudio = 0;
    m_audioDuration = 0;
580
    m_audioThumbMutex.unlock();
581
    emitMessage(i18n("Audio thumbnails done"), 100, OperationCompletedMessage);
582 583
}

584 585
bool Bin::eventFilter(QObject *obj, QEvent *event)
{
586
    if (event->type() == QEvent::MouseButtonRelease) {
587
        m_monitor->slotActivateMonitor();
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
        bool success = QWidget::eventFilter(obj, event);
        if (m_gainedFocus) {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            QAbstractItemView *view = qobject_cast<QAbstractItemView*>(obj->parent());
            if (view) {
                QModelIndex idx = view->indexAt(mouseEvent->pos());
                ClipController *ctl = NULL;
                if (idx.isValid()) {
                    AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(idx).internalPointer());
                    if (item) {
                        ProjectClip *clip = qobject_cast<ProjectClip*>(item);
                        if (clip) {
                            ctl = clip->controller();
                        }
                    }
                }
                m_gainedFocus = false;
                editMasterEffect(ctl);
            }
            // make sure we discard the focus indicator
            m_gainedFocus = false;
        }
        return success;
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
    }
    if (event->type() == QEvent::MouseButtonDblClick) {
        QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
        QAbstractItemView *view = qobject_cast<QAbstractItemView*>(obj->parent());
        if (view) {
            QModelIndex idx = view->indexAt(mouseEvent->pos());
            if (!idx.isValid()) {
                // User double clicked on empty area
                slotAddClip();
            }
            else {
                slotItemDoubleClicked(idx, mouseEvent->pos());
            }
        }
        else {
            qDebug()<<" +++++++ NO VIEW-------!!";
        }
        return true;
    } else if (event->type() == QEvent::Wheel) {
        QWheelEvent * e = static_cast<QWheelEvent*>(event);
        if (e && e->modifiers() == Qt::ControlModifier) {
            slotZoomView(e->delta() > 0);
            //emit zoomView(e->delta() > 0);
            return true;
        }
    }
637
    return QWidget::eventFilter(obj, event);
638 639 640
}


641 642 643 644 645 646
void Bin::refreshIcons()
{
    QList<QMenu *> allMenus = this->findChildren<QMenu *>();
    for (int i = 0; i < allMenus.count(); i++) {
        QMenu *m = allMenus.at(i);
        QIcon ic = m->icon();
647
        if (ic.isNull() || ic.name().isEmpty()) continue;
648 649 650 651 652 653 654
        QIcon newIcon = KoIconUtils::themedIcon(ic.name());
        m->setIcon(newIcon);
    }
    QList<QToolButton *> allButtons = this->findChildren<QToolButton *>();
    for (int i = 0; i < allButtons.count(); i++) {
        QToolButton *m = allButtons.at(i);
        QIcon ic = m->icon();
655
        if (ic.isNull() || ic.name().isEmpty()) continue;
656 657 658 659 660
        QIcon newIcon = KoIconUtils::themedIcon(ic.name());
        m->setIcon(newIcon);
    }
}

661 662 663 664 665 666 667 668 669 670
void Bin::slotSaveHeaders()
{
    if (m_itemView && m_listType == BinTreeView) {
        // save current treeview state (column width)
        QTreeView *view = static_cast<QTreeView*>(m_itemView);
        m_headerInfo = view->header()->saveState();
        KdenliveSettings::setTreeviewheaders(m_headerInfo.toBase64());
    }
}

671 672 673 674 675 676 677 678 679 680
void Bin::slotZoomView(bool zoomIn)
{
    if (m_itemModel->rowCount() == 0) {
        //Don't zoom on empty bin
        return;
    }
    int progress = (zoomIn == true) ? 1 : -1;
    m_slider->setValue(m_slider->value() + progress);
}

681 682 683 684 685
Monitor *Bin::monitor()
{
    return m_monitor;
}

686
const QStringList Bin::getFolderInfo(QModelIndex selectedIx)
687
{
688
    QStringList folderInfo;
689 690 691 692 693 694
    QModelIndexList indexes;
    if (selectedIx.isValid()) {
        indexes << selectedIx;
    } else {
        indexes = m_proxyModel->selectionModel()->selectedIndexes();
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
695 696 697 698
    if (indexes.isEmpty()) {
        return folderInfo;
    }
    QModelIndex ix = indexes.first();
699
    if (ix.isValid() && (m_proxyModel->selectionModel()->isSelected(ix) || selectedIx.isValid())) {
700
        AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
701
        while (currentItem->itemType() != AbstractProjectItem::FolderItem) {
702 703 704 705 706
            currentItem = currentItem->parent();
        }
        if (currentItem == m_rootFolder) {
            // clip was added to root folder, leave folder info empty
        } else {
707
            folderInfo << currentItem->clipId();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
708
            folderInfo << currentItem->name();
709 710
        }
    }
711 712 713 714 715 716 717
    return folderInfo;
}

void Bin::slotAddClip()
{
    // Check if we are in a folder
    QStringList folderInfo = getFolderInfo();
718
    ClipCreationDialog::createClipsCommand(m_doc, folderInfo, this);
719 720 721 722
}

void Bin::deleteClip(const QString &id)
{
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
723
    if (m_monitor->activeClipId() == id) {
724
	emit openClip(NULL);
Jean-Baptiste Mardelle's avatar
cleanup  
Jean-Baptiste Mardelle committed
725
    }
726
    ProjectClip *clip = m_rootFolder->clip(id);
727 728 729 730
    if (!clip) {
	qWarning()<<"Cannot bin find clip to delete: "<<id;
	return;
    }
731
    m_jobManager->discardJobs(id);
Jean-Baptiste Mardelle's avatar
Cleanup  
Jean-Baptiste Mardelle committed
732 733
    ClipType type = clip->clipType();
    QString url = clip->url().toLocalFile();
734
    clip->setClipStatus(AbstractProjectItem::StatusDeleting);
735 736
    AbstractProjectItem *parent = clip->parent();
    parent->removeChild(clip);
737
    delete clip;
Jean-Baptiste Mardelle's avatar
Cleanup  
Jean-Baptiste Mardelle committed
738
    m_doc->deleteClip(id, type, url);
739 740
}

741
ProjectClip *Bin::getFirstSelectedClip()
742 743 744 745 746 747
{
    QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
    if (indexes.isEmpty()) {
        return NULL;
    }
    foreach (const QModelIndex &ix, indexes) {
748 749 750 751
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
        ProjectClip *clip = qobject_cast<ProjectClip*>(item);
        if (clip) {
            return clip;
752 753 754 755 756
        }
    }
    return NULL;
}

757 758 759
void Bin::slotDeleteClip()
{
    QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
760
    QStringList clipIds;
761
    QStringList subClipIds;
762
    QStringList foldersIds;
763 764 765
    ProjectSubClip *sub;
    QString subId;
    QPoint zone;
766 767
    // check folders, remove child folders if there is any
    QList <ProjectFolder*> topFolders;
768
    foreach (const QModelIndex &ix, indexes) {
769
        if (!ix.isValid() || ix.column() != 0) continue;
770
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
771 772 773
	if (!item) continue;
	if (item->itemType() == AbstractProjectItem::SubClipItem) {
                QString subId = item->clipId();
Vincent Pinon's avatar
Vincent Pinon committed
774
                sub = static_cast<ProjectSubClip*>(item);
775 776 777
                zone = sub->zone();
                subId.append(":" + QString::number(zone.x()) + ":" + QString::number(zone.y()));
                subClipIds << subId;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
		continue;
	}
        if (item->itemType() != AbstractProjectItem::FolderItem) continue;
	ProjectFolder *current = static_cast<ProjectFolder*>(item);
	if (topFolders.isEmpty()) {
	    topFolders << current;
	    continue;
	}
	// parse all folders to check for children
	bool isChild = false;
	foreach (ProjectFolder *f, topFolders) {
	    if (f->folder(current->clipId())) {
		// Current is a child, no need to take it into account
		isChild = true;
		break;
	    }
	}
	if (isChild) continue;
	QList <ProjectFolder*> childFolders;
	// parse all folders to check for children
	foreach (ProjectFolder *f, topFolders) {
	    if (current->folder(f->clipId())) {
		childFolders << f;
	    }
	}
	if (!childFolders.isEmpty()) {
	    // children are in the list, remove from
	    foreach (ProjectFolder *f, childFolders) {
		topFolders.removeAll(f);
	    }
	}
	topFolders << current;
    }
    foreach (const ProjectFolder *f, topFolders) {
	foldersIds << f->clipId();
    }
    
    QList <ProjectFolder*> topClips;
    // Check if clips are in already selected folders
    foreach (const QModelIndex &ix, indexes) {
        if (!ix.isValid() || ix.column() != 0) continue;
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
        if (!item || item->itemType() != AbstractProjectItem::ClipItem) continue;
	ProjectClip *current = static_cast<ProjectClip*>(item);
	bool isChild = false;
	foreach (const ProjectFolder *f, topFolders) {
	    if (current->hasParent(f->clipId())) {
		  isChild = true;
		  break;
	    }
	}
	if (!isChild) clipIds << current->clipId();
830
    }
831
    m_doc->clipManager()->deleteProjectItems(clipIds, foldersIds, subClipIds);
832 833 834 835
}

void Bin::slotReloadClip()
{
836 837
    QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
    foreach (const QModelIndex &ix, indexes) {
838
        if (!ix.isValid() || ix.column() != 0) {
839 840
            continue;
        }
841 842 843
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
        ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
        if (currentItem) {
844
	    emit openClip(NULL);
845 846
            QDomDocument doc;
            QDomElement xml = currentItem->toXml(doc);
847 848 849
            if (!xml.isNull()) {
                currentItem->setClipStatus(AbstractProjectItem::StatusWaiting);
                // We need to set a temporary id before all outdated producers are replaced;
850
                m_doc->getFileProperties(xml, currentItem->clipId(), 150, true);
851
            }
852 853 854 855
        }
    }
}

856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
void Bin::slotLocateClip()
{
    QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
    foreach (const QModelIndex &ix, indexes) {
        if (!ix.isValid() || ix.column() != 0) {
            continue;
        }
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
        ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
        if (currentItem) {
	  QUrl url = currentItem->url().adjusted(QUrl::RemoveFilename);
	  bool exists = QFile(url.toLocalFile()).exists();
	  if (currentItem->hasUrl() && exists) {
	    QDesktopServices::openUrl(url);
	    qDebug()<<"  / / "+url.toString();
	  } else {
	    if(!exists){
873
	      emitMessage(i18n("Couldn't locate ") + QString(" ("+url.toString()+")"), 100, ErrorMessage);
874 875 876 877 878 879 880
	    }
	    return;
	  }
        }
    }
}

881 882 883 884
void Bin::slotDuplicateClip()
{
    QModelIndexList indexes = m_proxyModel->selectionModel()->selectedIndexes();
    foreach (const QModelIndex &ix, indexes) {
885
        if (!ix.isValid() || ix.column() != 0) {
886 887 888 889 890 891 892 893
            continue;
        }
        AbstractProjectItem *item = static_cast<AbstractProjectItem*>(m_proxyModel->mapToSource(ix).internalPointer());
        ProjectClip *currentItem = qobject_cast<ProjectClip*>(item);
        if (currentItem) {
            QStringList folderInfo = getFolderInfo(ix);
            QDomDocument doc;
            QDomElement xml = currentItem->toXml(doc);
894 895 896 897 898 899 900 901 902 903 904 905 906 907
            if (!xml.isNull()) {
		QString currentName = EffectsList::property(xml, "kdenlive:clipname");
		if (currentName.isEmpty()) {
		    QUrl url = QUrl::fromLocalFile(EffectsList::property(xml, "resource"));
		    if (url.isValid()) {
			QString currentName = url.fileName();
		    }
		}
		if (!currentName.isEmpty()) {
		     currentName.append(i18nc("append to clip name to indicate a copied idem", " (copy)"));
		     EffectsList::setProperty(xml, "kdenlive:clipname", currentName);
		}
		ClipCreationDialog::createClipFromXml(m_doc, xml, folderInfo, this);
	    }
908 909
        }
    }
910 911 912 913 914 915 916
}

ProjectFolder *Bin::rootFolder()
{
    return m_rootFolder;
}

917 918 919 920 921
QUrl Bin::projectFolder() const
{
    return m_doc->projectFolder();
}

922 923 924
void Bin::setMonitor(Monitor *monitor)
{
    m_monitor = monitor;
925
    connect(m_monitor, SIGNAL(addClipToProject(QUrl)), this, SLOT(slotAddClipToProject(QUrl)));
926
    connect(m_monitor, SIGNAL(requestAudioThumb(QString)), this, SLOT(slotSendAudioThumb(QString)));
927
    connect(m_monitor, SIGNAL(refreshCurrentClip()), this, SLOT(slotOpenCurrent()));
928
    connect(m_monitor, SIGNAL(updateClipMarker(QString, QList<CommentedTime>)), this, SLOT(slotAddClipMarker(QString,QList<CommentedTime>)));
929
    connect(this, SIGNAL(openClip(ClipController*,int,int)), m_monitor, SLOT(slotOpenClip(ClipController*,int,int)));
930 931
}

932 933 934 935 936
int Bin::getFreeFolderId()
{
    return m_folderCounter++;
}

937 938 939 940 941 942 943 944 945 946 947 948
int Bin::getFreeClipId()
{
    return m_clipCounter++;
}

int Bin::lastClipId() const
{
    return qMax(0, m_clipCounter - 1);
}

void Bin::setDocument(KdenliveDoc* project)
{
949
    // Remove clip from Bin's monitor
950
    if (m_doc) emit openClip(NULL);
951
    m_infoMessage->hide();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
952 953
    blockSignals(true);
    m_proxyModel->selectionModel()->blockSignals(true);
954
    setEnabled(false);
955 956 957 958 959 960 961 962 963

    // Cleanup previous project
    if (m_rootFolder) {
        while (!m_rootFolder->isEmpty()) {
            AbstractProjectItem *child = m_rootFolder->at(0);
            m_rootFolder->removeChild(child);
            delete child;
        }
    }
964
    delete m_rootFolder;
965 966 967 968
    delete m_itemView;
    m_itemView = NULL;
    delete m_jobManager;
    m_clipCounter = 1;
969
    m_folderCounter = 1;
970
    m_doc = project;
971
    int iconHeight = QFontInfo(font()).pixelSize() * 3.5;
972
    m_iconSize = QSize(iconHeight * m_doc->dar(), iconHeight);
973
    m_jobManager = new JobManager(this);
974
    m_rootFolder = new ProjectFolder(this);
975
    setEnabled(true);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
976 977
    blockSignals(false);
    m_proxyModel->selectionModel()->blockSignals(false);
978
    connect(m_jobManager, SIGNAL(addClip(QString)), this, SLOT(slotAddUrl(QString)));
979
    connect(m_proxyAction, SIGNAL(toggled(bool)), m_doc, SLOT(slotProxyCurrentItem(bool)));
980 981 982
    connect(m_jobManager, SIGNAL(jobCount(int)), m_infoLabel, SLOT(slotSetJobCount(int)));
    connect(m_discardCurrentClipJobs, SIGNAL(triggered()), m_jobManager, SLOT(slotDiscardClipJobs()));
    connect(m_cancelJobs, SIGNAL(triggered()), m_jobManager, SLOT(slotCancelJobs()));
983
    connect(m_discardPendingJobs, SIGNAL(triggered()), m_jobManager, SLOT(slotCancelPendingJobs()));
984
    connect(m_jobManager, SIGNAL(updateJobStatus(QString,int,int,QString,QString,QString)), this, SLOT(slotUpdateJobStatus(QString,int,int,QString,QString,QString)));
985

986
    connect(m_jobManager, SIGNAL(gotFilterJobResults(QString,int,int,stringMap,stringMap)), this, SLOT(slotGotFilterJobResults(QString,int,int,stringMap,stringMap)));
987

988 989 990
    //connect(m_itemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), m_itemView
    //connect(m_itemModel, SIGNAL(updateCurrentItem()), this, SLOT(autoSelect()));
    slotInitView(NULL);
991
    bool binEffectsDisabled = getDocumentProperty(QStringLiteral("disablebineffects")).toInt() == 1;
992
    setBinEffectsDisabledStatus(binEffectsDisabled);
993 994 995
    autoSelect();
}

996
void Bin::slotAddUrl(QString url, QMap <QString, QString> data)
997 998 999 1000
{
    QList <QUrl>urls;
    urls << QUrl::fromLocalFile(url);
    QStringList folderInfo = getFolderInfo();
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1001
    ClipCreationDialog::createClipsCommand(m_doc, urls, folderInfo, this, data);
1002 1003
}

1004 1005
void Bin::createClip(QDomElement xml)
{
1006
    // Check if clip should be in a folder
1007
    QString groupId = ProjectClip::getXmlProperty(xml, QStringLiteral("kdenlive:folderid"));
1008 1009 1010 1011 1012 1013 1014 1015
    ProjectFolder *parentFolder = m_rootFolder;
    if (!groupId.isEmpty()) {
        parentFolder = m_rootFolder->folder(groupId);
        if (!parentFolder) {
            // parent folder does not exist, put in root folder
            parentFolder = m_rootFolder;
        }
    }
Vincent Pinon's avatar
Vincent Pinon committed
1016
    new ProjectClip(xml, m_blankThumb, parentFolder);
1017 1018
}

1019
QString Bin::slotAddFolder(const QString &folderName)
1020
{
1021 1022 1023
    // Check parent item
    QModelIndex ix = m_proxyModel->selectionModel()->currentIndex();
    ProjectFolder *parentFolder  = m_rootFolder;
1024
    if (ix.isValid() && m_proxyModel->selectionModel()->isSelected(ix)) {
1025
        AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(m_proxyModel->mapToSource(ix).internalPointer());
1026
        while (currentItem->itemType() != AbstractProjectItem::FolderItem) {
1027 1028
            currentItem = currentItem->parent();
        }
1029 1030
        if (currentItem->itemType() == AbstractProjectItem::FolderItem) {
            parentFolder = qobject_cast<ProjectFolder *>(currentItem);
1031 1032
        }
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1033
    QString newId = QString::number(getFreeFolderId());
1034
    AddBinFolderCommand *command = new AddBinFolderCommand(this, newId, folderName.isEmpty() ? i18n("Folder") : folderName, parentFolder->clipId());
1035
    m_doc->commandStack()->push(command);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1036

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1037
    // Edit folder name
1038 1039 1040 1041
    if (!folderName.isEmpty()) {
	// We already have a name, no need to edit
	return newId;
    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1042 1043
    ix = getIndexForId(newId, true);
    if (ix.isValid()) {
1044 1045
        m_proxyModel->selectionModel()->clearSelection();
        int row =ix.row();
1046 1047 1048 1049
        const QModelIndex id = m_itemModel->index(row, 0, ix.parent());
        const QModelIndex id2 = m_itemModel->index(row, m_rootFolder->supportedDataCount() - 1, ix.parent());
        if (id.isValid() && id2.isValid()) {
            m_proxyModel->selectionModel()->select(QItemSelection(m_proxyModel->mapFromSource(id), m_proxyModel->mapFromSource(id2)), QItemSelectionModel::Select);
1050
        }
1051
        m_itemView->setProperty("editing", true);
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1052 1053
        m_itemView->edit(m_proxyModel->mapFromSource(ix));
    }
1054
    return newId;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
1055 1056 1057 1058 1059 1060 1061
}

QModelIndex Bin::getIndexForId(const QString &id, bool folderWanted) const
{
    QModelIndexList items = m_itemModel->match(m_itemModel->index(0, 0), AbstractProjectItem::DataId, QVariant::fromValue(id), 2, Qt::MatchRecursive);
    for (int i = 0; i < items.count(); i++) {
        AbstractProjectItem *currentItem = static_cast<AbstractProjectItem *>(items.at(i).internalPointer());
1062 1063
        AbstractProjectItem::PROJECTITEMTYPE type = currentItem->itemType();
        if (folderWanted && type == AbstractProjectItem::FolderItem) {