File indexing completed on 2024-04-21 04:51:43

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
0003     This file is part of Kdenlive. See www.kdenlive.org.
0004 
0005     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "librarywidget.h"
0009 #include "core.h"
0010 #include "doc/kthumb.h"
0011 #include "kdenlivesettings.h"
0012 #include "project/projectmanager.h"
0013 
0014 #include <QAction>
0015 #include <QDropEvent>
0016 #include <QInputDialog>
0017 #include <QMimeData>
0018 #include <QProgressBar>
0019 #include <QStandardPaths>
0020 #include <QToolBar>
0021 #include <QTreeWidgetItem>
0022 #include <QVBoxLayout>
0023 
0024 #include <KIO/FileCopyJob>
0025 #include <KIO/OpenFileManagerWindowJob>
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 
0029 enum LibraryItem { PlayList, Clip, Folder };
0030 
0031 LibraryTree::LibraryTree(QWidget *parent)
0032     : QTreeWidget(parent)
0033 {
0034     int size = QFontInfo(font()).pixelSize();
0035     setIconSize(QSize(size * 4, size * 2));
0036     setDragDropMode(QAbstractItemView::DragDrop);
0037     setAcceptDrops(true);
0038     setDragEnabled(true);
0039     setDropIndicatorShown(true);
0040     viewport()->setAcceptDrops(true);
0041 }
0042 
0043 // virtual
0044 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0045 QMimeData *LibraryTree::mimeData(const QList<QTreeWidgetItem *> list) const
0046 #else
0047 QMimeData *LibraryTree::mimeData(const QList<QTreeWidgetItem *> &list) const
0048 #endif
0049 {
0050     QList<QUrl> urls;
0051     urls.reserve(list.count());
0052     for (QTreeWidgetItem *item : list) {
0053         urls << QUrl::fromLocalFile(item->data(0, Qt::UserRole).toString());
0054     }
0055     auto *mime = new QMimeData;
0056     mime->setUrls(urls);
0057     return mime;
0058 }
0059 
0060 QStringList LibraryTree::mimeTypes() const
0061 {
0062     return QStringList() << QStringLiteral("text/uri-list") << QStringLiteral("kdenlive/clip") << QStringLiteral("text/producerslist");
0063 }
0064 
0065 void LibraryTree::slotUpdateThumb(const QString &path, const QString &iconPath)
0066 {
0067     QString name = QUrl::fromLocalFile(path).fileName();
0068     QList<QTreeWidgetItem *> list = findItems(name, Qt::MatchExactly | Qt::MatchRecursive);
0069     for (QTreeWidgetItem *item : qAsConst(list)) {
0070         if (item->data(0, Qt::UserRole).toString() == path) {
0071             // We found our item
0072             blockSignals(true);
0073             item->setData(0, Qt::DecorationRole, QIcon(iconPath));
0074             blockSignals(false);
0075             break;
0076         }
0077     }
0078 }
0079 
0080 void LibraryTree::slotUpdateThumb(const QString &path, const QPixmap &pix)
0081 {
0082     QString name = QUrl::fromLocalFile(path).fileName();
0083     QList<QTreeWidgetItem *> list = findItems(name, Qt::MatchExactly | Qt::MatchRecursive);
0084     for (QTreeWidgetItem *item : qAsConst(list)) {
0085         if (item->data(0, Qt::UserRole).toString() == path) {
0086             // We found our item
0087             blockSignals(true);
0088             item->setData(0, Qt::DecorationRole, QIcon(pix));
0089             blockSignals(false);
0090             break;
0091         }
0092     }
0093 }
0094 
0095 void LibraryTree::mousePressEvent(QMouseEvent *event)
0096 {
0097     QTreeWidgetItem *clicked = this->itemAt(event->pos());
0098     QList<QAction *> act = actions();
0099     if (clicked) {
0100         for (QAction *a : qAsConst(act)) {
0101             a->setEnabled(true);
0102         }
0103     } else {
0104         // Clicked in empty area, disable clip actions
0105         clearSelection();
0106         for (QAction *a : qAsConst(act)) {
0107             if (a->data().toInt() == 1) {
0108                 a->setEnabled(false);
0109             }
0110         }
0111     }
0112     QTreeWidget::mousePressEvent(event);
0113 }
0114 
0115 void LibraryTree::dropEvent(QDropEvent *event)
0116 {
0117     const QMimeData *qMimeData = event->mimeData();
0118 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0119     QTreeWidgetItem *dropped = this->itemAt(event->pos());
0120 #else
0121     QTreeWidgetItem *dropped = this->itemAt(event->position().toPoint());
0122 #endif
0123     QString dest;
0124     if (dropped) {
0125         dest = dropped->data(0, Qt::UserRole).toString();
0126         if (dropped->data(0, Qt::UserRole + 2).toInt() != LibraryItem::Folder) {
0127             dest = QUrl::fromLocalFile(dest).adjusted(QUrl::RemoveFilename).toLocalFile();
0128         }
0129     }
0130     if (qMimeData->hasUrls()) {
0131         QList<QUrl> urls = qMimeData->urls();
0132         Q_EMIT moveData(urls, dest);
0133     } else if (qMimeData->hasFormat(QStringLiteral("kdenlive/clip"))) {
0134         Q_EMIT importSequence(QString(qMimeData->data(QStringLiteral("kdenlive/clip"))).split(QLatin1Char(';')), dest);
0135     } else if (qMimeData->hasFormat(QStringLiteral("text/producerslist"))) {
0136         QStringList list = QString(qMimeData->data(QStringLiteral("text/producerslist"))).split(QLatin1Char(';'));
0137         for (const QString &prodslist : qAsConst(list)) {
0138             if (prodslist.startsWith(QLatin1Char('#'))) {
0139                 // Bin folder, not supported yet
0140                 continue;
0141             }
0142             if (prodslist.contains(QLatin1Char('/'))) {
0143                 // Clip zone
0144                 Q_EMIT importSequence(prodslist.split(QLatin1Char('/')), dest);
0145             } else {
0146                 // Full clip
0147                 Q_EMIT importSequence(QStringList() << prodslist << QStringLiteral("-1") << QStringLiteral("-1"), dest);
0148             }
0149         }
0150     }
0151     event->accept();
0152     QTreeWidget::dropEvent(event);
0153 }
0154 
0155 LibraryWidget::LibraryWidget(ProjectManager *manager, QWidget *parent)
0156     : QWidget(parent)
0157     , m_manager(manager)
0158     , m_previewJob(nullptr)
0159     , m_selectNewFile(false)
0160 {
0161     auto *lay = new QVBoxLayout(this);
0162     m_libraryTree = new LibraryTree(this);
0163     m_libraryTree->setColumnCount(1);
0164     m_libraryTree->setHeaderHidden(true);
0165     m_libraryTree->setDragEnabled(true);
0166     m_libraryTree->setItemDelegate(new LibraryItemDelegate(this));
0167     m_libraryTree->setAlternatingRowColors(true);
0168     lay->addWidget(m_libraryTree);
0169 
0170     // Library path
0171     QString path;
0172     if (KdenliveSettings::librarytodefaultfolder() || KdenliveSettings::libraryfolder().isEmpty()) {
0173         path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library");
0174     } else {
0175         path = KdenliveSettings::libraryfolder();
0176     }
0177 
0178     // Info message
0179     m_infoWidget = new KMessageWidget;
0180     lay->addWidget(m_infoWidget);
0181     m_infoWidget->hide();
0182     // Download progress bar
0183     m_progressBar = new QProgressBar(this);
0184     lay->addWidget(m_progressBar);
0185     m_toolBar = new QToolBar(this);
0186     m_toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
0187     int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
0188     m_toolBar->setIconSize(QSize(size, size));
0189     m_progressBar->setRange(0, 100);
0190     m_progressBar->setFormat(i18nc("%p is the percent value, % is the percent sign", "%p%"));
0191     m_progressBar->setOrientation(Qt::Horizontal);
0192     m_progressBar->setVisible(false);
0193     lay->addWidget(m_toolBar);
0194     setLayout(lay);
0195 
0196     m_directory = QDir(path);
0197     if (!m_directory.exists()) {
0198         m_directory.mkpath(QStringLiteral("."));
0199     }
0200     QFileInfo fi(m_directory.absolutePath());
0201     if (!m_directory.exists() || !fi.isWritable()) {
0202         // Something went wrong
0203         showMessage(i18n("Check your settings, Library path is invalid: %1", m_directory.absolutePath()), KMessageWidget::Warning);
0204         setEnabled(false);
0205     }
0206 
0207     m_libraryTree->setContextMenuPolicy(Qt::ActionsContextMenu);
0208     m_timer.setSingleShot(true);
0209     m_timer.setInterval(4000);
0210     connect(&m_timer, &QTimer::timeout, m_infoWidget, &KMessageWidget::animatedHide);
0211     connect(m_libraryTree, &LibraryTree::moveData, this, &LibraryWidget::slotMoveData);
0212     connect(m_libraryTree, &LibraryTree::importSequence, this, &LibraryWidget::slotSaveSequence);
0213 
0214     m_coreLister = new KCoreDirLister(this);
0215     m_coreLister->setDelayedMimeTypes(false);
0216     connect(m_coreLister, &KCoreDirLister::itemsAdded, this, &LibraryWidget::slotItemsAdded);
0217     connect(m_coreLister, &KCoreDirLister::itemsDeleted, this, &LibraryWidget::slotItemsDeleted);
0218     connect(m_coreLister, SIGNAL(clear()), this, SLOT(slotClearAll()));
0219     m_coreLister->openUrl(QUrl::fromLocalFile(m_directory.absolutePath()));
0220     m_libraryTree->setSortingEnabled(true);
0221     m_libraryTree->sortByColumn(0, Qt::AscendingOrder);
0222     connect(m_libraryTree, &LibraryTree::itemChanged, this, &LibraryWidget::slotItemEdited, Qt::UniqueConnection);
0223 }
0224 
0225 void LibraryWidget::setupActions()
0226 {
0227     QList<QAction *> menuList;
0228     m_addAction = new QAction(QIcon::fromTheme(QStringLiteral("kdenlive-add-clip")), i18n("Add Clip to Project"), this);
0229     m_addAction->setWhatsThis(xi18nc("@info:whatsthis", "Adds the selected library clip to the project bin."));
0230     connect(m_addAction, &QAction::triggered, this, &LibraryWidget::slotAddToProject);
0231     m_addAction->setData(1);
0232     m_deleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete Clip from Library"), this);
0233     m_deleteAction->setWhatsThis(xi18nc("@info:whatsthis", "Deletes the currently selected library clip from the library."));
0234     connect(m_deleteAction, &QAction::triggered, this, &LibraryWidget::slotDeleteFromLibrary);
0235     m_deleteAction->setData(1);
0236     QAction *addFolder = new QAction(QIcon::fromTheme(QStringLiteral("folder-new")), i18n("Create Library Folder"), this);
0237     addFolder->setWhatsThis(xi18nc("@info:whatsthis", "Creates a new folder in the library."));
0238     connect(addFolder, &QAction::triggered, this, &LibraryWidget::slotAddFolder);
0239     QAction *renameFolder = new QAction(QIcon(), i18n("Rename Library Clip"), this);
0240     renameFolder->setData(1);
0241     connect(renameFolder, &QAction::triggered, this, &LibraryWidget::slotRenameItem);
0242     menuList << m_addAction << addFolder << renameFolder << m_deleteAction;
0243     m_toolBar->addAction(m_addAction);
0244     m_toolBar->addSeparator();
0245     m_toolBar->addAction(addFolder);
0246     QAction *sentToLibrary = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18n("Add Timeline Selection to Library"), this);
0247     sentToLibrary->setWhatsThis(xi18nc("@info:whatsthis", "Adds the clip(s) currently selected in the timeline to the library. Note that the paths to images, "
0248                                                           "video and audio files are saved as absolute paths."));
0249     connect(sentToLibrary, &QAction::triggered, this, &LibraryWidget::slotAddToLibrary);
0250     pCore->addActionToCollection(QStringLiteral("send_library"), sentToLibrary);
0251     sentToLibrary->setEnabled(false);
0252     m_toolBar->addAction(sentToLibrary);
0253     menuList << sentToLibrary;
0254     connect(this, &LibraryWidget::enableAddSelection, sentToLibrary, &QAction::setEnabled);
0255 
0256     QAction *openFolder = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Open Containing Folder"), this);
0257     connect(openFolder, &QAction::triggered, this, &LibraryWidget::slotOpenFolder);
0258     menuList << openFolder;
0259 
0260     // Create spacer
0261     QWidget *spacer = new QWidget();
0262     spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0263     m_toolBar->addWidget(spacer);
0264     m_toolBar->addSeparator();
0265     m_toolBar->addAction(m_deleteAction);
0266     m_libraryTree->addActions(menuList);
0267     connect(m_libraryTree, &QTreeWidget::itemSelectionChanged, this, &LibraryWidget::updateActions);
0268 }
0269 
0270 void LibraryWidget::slotAddToLibrary()
0271 {
0272     if (!isEnabled()) {
0273         return;
0274     }
0275     QTreeWidgetItem *current = m_libraryTree->currentItem();
0276     m_selectNewFile = true;
0277     if (current) {
0278         if (current->data(0, Qt::UserRole + 2).toInt() != LibraryItem::Folder) {
0279             current = current->parent();
0280         }
0281         if (current && current->data(0, Qt::UserRole + 2).toInt() == LibraryItem::Folder) {
0282             // We are in a folder, send correct path
0283             const QString path = current->data(0, Qt::UserRole).toString();
0284             QDir directory(path);
0285             if (path.isEmpty() || !directory.exists()) {
0286                 directory = m_directory;
0287             }
0288             Q_EMIT saveTimelineSelection(directory);
0289             return;
0290         }
0291     }
0292     Q_EMIT saveTimelineSelection(m_directory);
0293 }
0294 
0295 void LibraryWidget::showMessage(const QString &text, KMessageWidget::MessageType type)
0296 {
0297     m_timer.stop();
0298     m_infoWidget->setText(text);
0299     m_infoWidget->setWordWrap(m_infoWidget->text().length() > 35);
0300     m_infoWidget->setMessageType(type);
0301     m_infoWidget->animatedShow();
0302     m_timer.start();
0303 }
0304 
0305 void LibraryWidget::slotAddToProject()
0306 {
0307     QTreeWidgetItem *current = m_libraryTree->currentItem();
0308     if (!current) {
0309         return;
0310     }
0311     const QList<QUrl> list = {QUrl::fromLocalFile(current->data(0, Qt::UserRole).toString())};
0312     Q_EMIT addProjectClips(list);
0313 }
0314 
0315 void LibraryWidget::updateActions()
0316 {
0317     QTreeWidgetItem *current = m_libraryTree->currentItem();
0318     if (!current) {
0319         m_addAction->setEnabled(false);
0320         m_deleteAction->setEnabled(false);
0321         return;
0322     }
0323     m_addAction->setEnabled(true);
0324     m_deleteAction->setEnabled(true);
0325 }
0326 
0327 void LibraryWidget::slotDeleteFromLibrary()
0328 {
0329     QTreeWidgetItem *current = m_libraryTree->currentItem();
0330     if (!current) {
0331         qCDebug(KDENLIVE_LOG) << " * * *Deleting no item ";
0332         return;
0333     }
0334     QString path = current->data(0, Qt::UserRole).toString();
0335     if (path.isEmpty()) {
0336         return;
0337     }
0338     if (current->data(0, Qt::UserRole + 2).toInt() == LibraryItem::Folder) {
0339         // Deleting a folder
0340         QDir dir(path);
0341         // Make sure we are really trying to remove a directory located in the library folder
0342         if (!path.startsWith(m_directory.absolutePath())) {
0343             showMessage(i18n("You are trying to remove an invalid folder: %1", path));
0344             return;
0345         }
0346         const QStringList fileList = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
0347         if (!fileList.isEmpty()) {
0348             if (KMessageBox::warningContinueCancel(this, i18n("This will delete the folder %1, including all playlists in it.\nThis cannot be undone", path)) !=
0349                 KMessageBox::Continue) {
0350                 return;
0351             }
0352         }
0353         dir.removeRecursively();
0354     } else {
0355         QString message;
0356         if (current->data(0, Qt::UserRole + 2).toInt() == LibraryItem::PlayList) {
0357             message = i18n("This will delete the MLT playlist:\n%1", path);
0358         } else {
0359             message = i18n("This will delete the file:\n%1", path);
0360         }
0361         if (KMessageBox::warningContinueCancel(this, message) != KMessageBox::Continue) {
0362             return;
0363         }
0364         // Remove playlist
0365         if (!QFile::remove(path)) {
0366             showMessage(i18n("Error removing %1", path));
0367         }
0368     }
0369 }
0370 
0371 void LibraryWidget::slotOpenFolder()
0372 {
0373     QTreeWidgetItem *current = m_libraryTree->currentItem();
0374     if (current) {
0375         const QString filePath = current->data(0, Qt::UserRole).toString();
0376         KIO::highlightInFileManager({QUrl::fromLocalFile(filePath)});
0377     }
0378 }
0379 
0380 void LibraryWidget::slotAddFolder()
0381 {
0382     bool ok;
0383     QString name = QInputDialog::getText(this, i18nc("@title:window", "Add Folder to Library"), i18n("Enter a folder name"), QLineEdit::Normal, QString(), &ok);
0384     if (name.isEmpty() || !ok) {
0385         return;
0386     }
0387     QTreeWidgetItem *current = m_libraryTree->currentItem();
0388     QString parentFolder;
0389     if (current) {
0390         if (current->data(0, Qt::UserRole + 2).toInt() == LibraryItem::Folder) {
0391             // Creating a subfolder
0392             parentFolder = current->data(0, Qt::UserRole).toString();
0393         } else {
0394             QTreeWidgetItem *parentItem = current->parent();
0395             if (parentItem) {
0396                 parentFolder = parentItem->data(0, Qt::UserRole).toString();
0397             }
0398         }
0399     }
0400     if (parentFolder.isEmpty()) {
0401         parentFolder = m_directory.absolutePath();
0402     }
0403     QDir dir(parentFolder);
0404     if (dir.exists(name)) {
0405         showMessage(i18n("Folder %1 already exists", name));
0406         return;
0407     }
0408     if (!dir.mkdir(name)) {
0409         showMessage(i18n("Error creating folder %1", name));
0410         return;
0411     }
0412 }
0413 
0414 void LibraryWidget::slotRenameItem()
0415 {
0416     QTreeWidgetItem *current = m_libraryTree->currentItem();
0417     if (!current) {
0418         // This is not a folder, abort
0419         return;
0420     }
0421     m_libraryTree->editItem(current);
0422 }
0423 
0424 void LibraryWidget::slotMoveData(const QList<QUrl> &urls, QString dest)
0425 {
0426     if (urls.isEmpty()) {
0427         return;
0428     }
0429     if (dest.isEmpty()) {
0430         // moving to library's root
0431         dest = m_directory.absolutePath();
0432     }
0433     QDir dir(dest);
0434     if (!dir.exists()) {
0435         return;
0436     }
0437     for (const QUrl &url : urls) {
0438         if (!url.toLocalFile().startsWith(m_directory.absolutePath())) {
0439             // Dropped an external file, attempt to copy it to library
0440             KIO::FileCopyJob *copyJob = KIO::file_copy(url, QUrl::fromLocalFile(dir.absoluteFilePath(url.fileName())));
0441             connect(copyJob, &KJob::result, this, &LibraryWidget::slotDownloadFinished);
0442             connect(copyJob, SIGNAL(percent(KJob *, ulong)), this, SLOT(slotDownloadProgress(KJob *, ulong)));
0443         } else {
0444             // Internal drag/drop
0445             dir.rename(url.toLocalFile(), url.fileName());
0446         }
0447     }
0448 }
0449 
0450 void LibraryWidget::slotSaveSequence(const QStringList &info, QString dest)
0451 {
0452     if (info.isEmpty()) {
0453         return;
0454     }
0455     if (dest.isEmpty()) {
0456         // moving to library's root
0457         dest = m_directory.absolutePath();
0458     }
0459     QDir dir(dest);
0460     if (!dir.exists()) {
0461         return;
0462     }
0463     m_selectNewFile = true;
0464     m_manager->saveZone(info, dir);
0465 }
0466 
0467 void LibraryWidget::slotItemEdited(QTreeWidgetItem *item, int column)
0468 {
0469     if ((item == nullptr) || column != 0) {
0470         return;
0471     }
0472     if (item->data(0, Qt::UserRole + 2).toInt() == LibraryItem::Folder) {
0473         QDir dir(item->data(0, Qt::UserRole).toString());
0474         dir.cdUp();
0475         dir.rename(item->data(0, Qt::UserRole).toString(), item->text(0));
0476         // item->setData(0, Qt::UserRole, dir.absoluteFilePath(item->text(0)));
0477     } else {
0478         QString oldPath = item->data(0, Qt::UserRole).toString();
0479         QDir dir(QUrl::fromLocalFile(oldPath).adjusted(QUrl::RemoveFilename).toLocalFile());
0480         dir.rename(oldPath, item->text(0));
0481         // item->setData(0, Qt::UserRole, dir.absoluteFilePath(item->text(0)));
0482     }
0483 }
0484 
0485 void LibraryWidget::slotDownloadFinished(KJob *)
0486 {
0487     m_progressBar->setValue(100);
0488     m_progressBar->setVisible(false);
0489 }
0490 
0491 void LibraryWidget::slotDownloadProgress(KJob *, int progress)
0492 {
0493     m_progressBar->setVisible(true);
0494     m_progressBar->setValue(progress);
0495 }
0496 
0497 void LibraryWidget::slotUpdateLibraryPath()
0498 {
0499     // Library path changed, reload library with updated path
0500     m_libraryTree->blockSignals(true);
0501     m_folders.clear();
0502     m_libraryTree->clear();
0503     QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/library");
0504     if (KdenliveSettings::librarytodefaultfolder() || KdenliveSettings::libraryfolder().isEmpty()) {
0505         m_directory.setPath(defaultPath);
0506         if (!m_directory.exists()) {
0507             m_directory.mkpath(QStringLiteral("."));
0508         }
0509         showMessage(i18n("Library path set to default: %1", defaultPath), KMessageWidget::Information);
0510     } else {
0511         m_directory.setPath(KdenliveSettings::libraryfolder());
0512         if (!m_directory.exists()) {
0513             m_directory.mkpath(QStringLiteral("."));
0514         }
0515         showMessage(i18n("Library path set to custom: %1", KdenliveSettings::libraryfolder()), KMessageWidget::Information);
0516     }
0517     QFileInfo fi(m_directory.absolutePath());
0518     if (!m_directory.exists() || !fi.isWritable()) {
0519         // Cannot write to new Library, try default one
0520         if (m_directory.absolutePath() != defaultPath) {
0521             showMessage(i18n("Cannot write to Library path: %1, using default", KdenliveSettings::libraryfolder()), KMessageWidget::Warning);
0522             m_directory.setPath(defaultPath);
0523             if (!m_directory.exists()) {
0524                 m_directory.mkpath(QStringLiteral("."));
0525             }
0526         }
0527     }
0528     fi.setFile(m_directory.absolutePath());
0529     if (!m_directory.exists() || !fi.isWritable()) {
0530         // Something is really broken, disable library
0531         showMessage(i18n("Check your settings, Library path is invalid: %1", m_directory.absolutePath()), KMessageWidget::Warning);
0532         setEnabled(false);
0533     } else {
0534         m_coreLister->openUrl(QUrl::fromLocalFile(m_directory.absolutePath()));
0535         setEnabled(true);
0536     }
0537     m_libraryTree->blockSignals(false);
0538 }
0539 
0540 void LibraryWidget::slotGotPreview(const KFileItem &item, const QPixmap &pix)
0541 {
0542     const QString path = item.url().toLocalFile();
0543     m_libraryTree->blockSignals(true);
0544     m_libraryTree->slotUpdateThumb(path, pix);
0545     m_libraryTree->blockSignals(false);
0546 }
0547 
0548 void LibraryWidget::slotItemsDeleted(const KFileItemList &list)
0549 {
0550     m_libraryTree->blockSignals(true);
0551     QMutexLocker lock(&m_treeMutex);
0552     for (const KFileItem &fitem : list) {
0553         QUrl fileUrl = fitem.url();
0554         QString path;
0555         if (fitem.isDir()) {
0556             path = fileUrl.toLocalFile();
0557         } else {
0558             path = fileUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
0559         }
0560         QTreeWidgetItem *matchingFolder = nullptr;
0561         if (path != m_directory.absolutePath()) {
0562             for (QTreeWidgetItem *folder : qAsConst(m_folders)) {
0563                 if (folder->data(0, Qt::UserRole).toString() == path) {
0564                     // Found parent folder
0565                     matchingFolder = folder;
0566                     break;
0567                 }
0568             }
0569         }
0570         if (fitem.isDir()) {
0571             if (matchingFolder) {
0572                 m_folders.removeAll(matchingFolder);
0573                 // warning, we also need to remove all subfolders since they will be recreated
0574                 QList<QTreeWidgetItem *> subList;
0575                 for (QTreeWidgetItem *folder : qAsConst(m_folders)) {
0576                     if (folder->data(0, Qt::UserRole).toString().startsWith(path)) {
0577                         subList << folder;
0578                     }
0579                 }
0580                 for (QTreeWidgetItem *sub : qAsConst(subList)) {
0581                     m_folders.removeAll(sub);
0582                 }
0583                 delete matchingFolder;
0584             }
0585         } else {
0586             if (matchingFolder == nullptr) {
0587                 matchingFolder = m_libraryTree->invisibleRootItem();
0588             }
0589             for (int i = 0; i < matchingFolder->childCount(); i++) {
0590                 QTreeWidgetItem *item = matchingFolder->child(i);
0591                 if (item->data(0, Qt::UserRole).toString() == fileUrl.toLocalFile()) {
0592                     // Found deleted item
0593                     delete item;
0594                     break;
0595                 }
0596             }
0597         }
0598     }
0599     m_libraryTree->blockSignals(false);
0600 }
0601 
0602 void LibraryWidget::slotItemsAdded(const QUrl &url, const KFileItemList &list)
0603 {
0604     m_libraryTree->blockSignals(true);
0605     QMutexLocker lock(&m_treeMutex);
0606     for (const KFileItem &fitem : list) {
0607         QUrl fileUrl = fitem.url();
0608         QString name = fileUrl.fileName();
0609         QTreeWidgetItem *treeItem;
0610         QTreeWidgetItem *parent = nullptr;
0611         if (url != QUrl::fromLocalFile(m_directory.absolutePath())) {
0612             // not a top level item
0613             QString directory = fileUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
0614             for (QTreeWidgetItem *folder : qAsConst(m_folders)) {
0615                 if (folder->data(0, Qt::UserRole).toString() == directory) {
0616                     // Found parent folder
0617                     parent = folder;
0618                     break;
0619                 }
0620             }
0621         }
0622         if (parent) {
0623             treeItem = new QTreeWidgetItem(parent, QStringList() << name);
0624         } else {
0625             treeItem = new QTreeWidgetItem(m_libraryTree, QStringList() << name);
0626         }
0627         treeItem->setData(0, Qt::UserRole, fileUrl.toLocalFile());
0628         treeItem->setData(0, Qt::UserRole + 1, fitem.timeString());
0629         if (fitem.isDir()) {
0630             treeItem->setData(0, Qt::UserRole + 2, int(LibraryItem::Folder));
0631             m_folders << treeItem;
0632             m_coreLister->openUrl(fileUrl, KCoreDirLister::Keep);
0633         } else if (name.endsWith(QLatin1String(".mlt")) || name.endsWith(QLatin1String(".kdenlive"))) {
0634             treeItem->setData(0, Qt::UserRole + 2, int(LibraryItem::PlayList));
0635         } else {
0636             treeItem->setData(0, Qt::UserRole + 2, int(LibraryItem::Clip));
0637         }
0638         treeItem->setData(0, Qt::DecorationRole, QIcon::fromTheme(fitem.iconName()));
0639         treeItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable);
0640         if (m_selectNewFile) {
0641             m_libraryTree->setCurrentItem(treeItem);
0642             m_selectNewFile = false;
0643         }
0644     }
0645     QStringList plugins = KIO::PreviewJob::availablePlugins();
0646     m_previewJob = KIO::filePreview(list, QSize(80, 80), &plugins);
0647     m_previewJob->setIgnoreMaximumSize();
0648     connect(m_previewJob, &KIO::PreviewJob::gotPreview, this, &LibraryWidget::slotGotPreview);
0649     m_libraryTree->blockSignals(false);
0650 }
0651 
0652 void LibraryWidget::slotClearAll()
0653 {
0654     m_libraryTree->blockSignals(true);
0655     m_folders.clear();
0656     m_libraryTree->clear();
0657     m_libraryTree->blockSignals(false);
0658 }