File indexing completed on 2024-04-28 04:51:29

0001 /*
0002 SPDX-FileCopyrightText: 2023 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 "mediabrowser.h"
0009 #include "bin.h"
0010 #include "clipcreator.hpp"
0011 #include "core.h"
0012 #include "dialogs/clipcreationdialog.h"
0013 #include "kdenlivesettings.h"
0014 #include "mainwindow.h"
0015 #include "project/dialogs/slideshowclip.h"
0016 
0017 #include <KActionCollection>
0018 #include <KConfigGroup>
0019 #include <KDirLister>
0020 #include <KFileFilterCombo>
0021 #include <KFilePlacesModel>
0022 #include <KIconLoader>
0023 #include <KLocalizedString>
0024 #include <KRecentDirs>
0025 #include <KSharedConfig>
0026 #include <KUrlComboBox>
0027 #include <KUrlCompletion>
0028 #include <KUrlNavigator>
0029 #include <QAction>
0030 #include <QCheckBox>
0031 #include <QDesktopServices>
0032 #include <QHBoxLayout>
0033 #include <QKeyEvent>
0034 #include <QLabel>
0035 #include <QMenu>
0036 #include <QMimeDatabase>
0037 #include <QProcessEnvironment>
0038 #include <QToolBar>
0039 #include <QVBoxLayout>
0040 #include <kio_version.h>
0041 
0042 MediaBrowser::MediaBrowser(QWidget *parent)
0043     : QWidget(parent)
0044 {
0045     QVBoxLayout *lay = new QVBoxLayout(this);
0046     lay->setSpacing(0);
0047     QToolBar *tb = new QToolBar(this);
0048     int size = style()->pixelMetric(QStyle::PM_SmallIconSize);
0049     QSize iconSize(size, size);
0050     tb->setIconSize(iconSize);
0051     QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
0052     KFilePlacesModel *places = new KFilePlacesModel(this);
0053     QAction *zoomIn = new QAction(QIcon::fromTheme("zoom-in"), i18n("Zoom In"));
0054     QAction *zoomOut = new QAction(QIcon::fromTheme("zoom-out"), i18n("Zoom Out"));
0055     QAction *importAction = new QAction(QIcon::fromTheme("document-open"), i18n("Import Selection"));
0056     connect(importAction, &QAction::triggered, this, [this]() { importSelection(); });
0057     QAction *importOnDoubleClick = new QAction(QIcon::fromTheme("document-open"), i18n("Import File on Double Click"));
0058     importOnDoubleClick->setCheckable(true);
0059     importOnDoubleClick->setChecked(KdenliveSettings::mediaDoubleClickImport());
0060     connect(importOnDoubleClick, &QAction::triggered, this, [](bool enabled) { KdenliveSettings::setMediaDoubleClickImport(enabled); });
0061 
0062     // Create View
0063     m_op = new KDirOperator(QUrl(), parent);
0064     m_op->dirLister()->setAutoUpdate(false);
0065     // Ensure shortcuts are only active on this widget to avoid conflicts with app shortcuts
0066 #if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0067     QList<QAction *> actions = m_op->allActions();
0068     QAction *trash = m_op->action(KDirOperator::Trash);
0069     QAction *up = m_op->action(KDirOperator::Up);
0070     QAction *back = m_op->action(KDirOperator::Back);
0071     QAction *forward = m_op->action(KDirOperator::Forward);
0072     QAction *inlinePreview = m_op->action(KDirOperator::ShowPreviewPanel);
0073     QAction *preview = m_op->action(KDirOperator::ShowPreviewPanel);
0074     QAction *viewMenu = m_op->action(KDirOperator::ViewModeMenu);
0075 #else
0076     QList<QAction *> actions = m_op->actionCollection()->actions();
0077     QAction *trash = m_op->actionCollection()->action("trash");
0078     QAction *up = m_op->actionCollection()->action("up");
0079     QAction *back = m_op->actionCollection()->action("back");
0080     QAction *forward = m_op->actionCollection()->action("forward");
0081     QAction *inlinePreview = m_op->actionCollection()->action("inline preview");
0082     QAction *preview = m_op->actionCollection()->action("preview");
0083     QAction *viewMenu = m_op->actionCollection()->action("view menu");
0084 #endif
0085     // Disable "Del" shortcut to move item to trash - too dangerous ??
0086     trash->setShortcut({});
0087     // Plug actions
0088     tb->addAction(up);
0089     tb->addAction(back);
0090     tb->addAction(forward);
0091 
0092     tb->addAction(zoomOut);
0093     tb->addAction(zoomIn);
0094     tb->addAction(inlinePreview);
0095     tb->addSeparator();
0096     tb->addAction(importAction);
0097     preview->setIcon(QIcon::fromTheme(QStringLiteral("fileview-preview")));
0098 
0099     // Menu button with extra actions
0100     QToolButton *but = new QToolButton(this);
0101     QMenu *configMenu = new QMenu(this);
0102     configMenu->addAction(importOnDoubleClick);
0103     configMenu->addAction(preview);
0104     configMenu->addAction(viewMenu);
0105     but->setIcon(QIcon::fromTheme(QStringLiteral("application-menu")));
0106     but->setMenu(configMenu);
0107     but->setPopupMode(QToolButton::InstantPopup);
0108 
0109     // Spacer
0110     QWidget *empty = new QWidget(this);
0111     empty->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
0112     tb->addWidget(empty);
0113     tb->addWidget(but);
0114 
0115     connect(zoomIn, &QAction::triggered, this, [this]() {
0116         int iconZoom = m_op->iconSize();
0117         int newZoom = iconZoom * 1.5;
0118         m_op->setIconSize(qMin(512, newZoom));
0119     });
0120     connect(zoomOut, &QAction::triggered, this, [this]() {
0121         int iconZoom = m_op->iconSize() / 1.5;
0122         iconZoom = qMax(int(KIconLoader::SizeSmall), iconZoom);
0123         m_op->setIconSize(iconZoom);
0124     });
0125     KConfigGroup grp(KSharedConfig::openConfig(), "Media Browser");
0126     m_op->readConfig(grp);
0127 #if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0128     m_op->setViewMode(KFile::Default);
0129 #else
0130     m_op->setView(KFile::Default);
0131 #endif
0132     m_op->setMode(KFile::ExistingOnly | KFile::Files | KFile::Directory);
0133     m_op->setIconSize(KdenliveSettings::mediaIconSize());
0134     m_filterCombo = new KFileFilterCombo(this);
0135     m_filenameEdit = new KUrlComboBox(KUrlComboBox::Files, true, this);
0136     m_locationEdit = new KUrlNavigator(places, QUrl(), this);
0137     connect(m_locationEdit, &KUrlNavigator::urlChanged, this, [this](const QUrl url) { m_op->setUrl(url, true); });
0138 
0139     connect(m_op, &KDirOperator::urlEntered, this, &MediaBrowser::slotUrlEntered);
0140     connect(m_op, &KDirOperator::viewChanged, this, &MediaBrowser::connectView);
0141     connect(m_op, &KDirOperator::currentIconSizeChanged, this, [](int size) { KdenliveSettings::setMediaIconSize(size); });
0142     connect(m_op, &KDirOperator::fileHighlighted, this, [this](const KFileItem &item) {
0143         KFileItemList files = m_op->selectedItems();
0144         if (item.isDir() || files.size() == 0) {
0145             m_filenameEdit->clear();
0146         } else {
0147             const QString fileName = files.first().url().fileName();
0148             if (!fileName.isEmpty()) {
0149                 m_filenameEdit->setUrl(QUrl(fileName));
0150             }
0151         }
0152     });
0153     connect(m_op, &KDirOperator::contextMenuAboutToShow, this, [importAction](const KFileItem &, QMenu *menu) {
0154         QList<QAction *> act = menu->actions();
0155         if (act.isEmpty()) {
0156             menu->addAction(importAction);
0157         } else if (!act.contains(importAction)) {
0158             menu->insertAction(act.first(), importAction);
0159         }
0160     });
0161 
0162 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0163     QString allExtensions = ClipCreationDialog::getExtensions().join(QLatin1Char(' '));
0164     QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files");
0165     m_filterCombo->setFilter(dialogFilter);
0166     m_op->setNameFilter(m_filterCombo->currentFilter());
0167 #else
0168     QStringList allExtensions = ClipCreationDialog::getExtensions();
0169 
0170     const QList<KFileFilter> filters{
0171         KFileFilter(i18n("All Supported Files"), allExtensions, {}),
0172         KFileFilter(i18n("All Files"), {QStringLiteral("*")}, {}),
0173     };
0174 
0175     m_filterCombo->setFilters(filters, filters.first());
0176     m_op->setNameFilter(filters.first().toFilterString());
0177 #endif
0178 
0179     // Setup mime filter combo
0180     m_filterCombo->setEditable(true);
0181     m_filterCombo->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
0182     connect(m_filterCombo, &KFileFilterCombo::filterChanged, this, [this]() { slotFilterChanged(); });
0183     m_filterDelayTimer.setSingleShot(true);
0184     m_filterDelayTimer.setInterval(300);
0185     connect(m_filterCombo, &QComboBox::editTextChanged, &m_filterDelayTimer, qOverload<>(&QTimer::start));
0186     connect(&m_filterDelayTimer, &QTimer::timeout, this, [this]() { slotFilterChanged(); });
0187 
0188     // Load initial folder
0189     m_op->setUrl(QUrl::fromLocalFile(clipFolder), true);
0190 
0191     // Setup file name field
0192     m_filenameEdit->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
0193     connect(m_filenameEdit, &KUrlComboBox::editTextChanged, this, [this](const QString &text) { slotLocationChanged(text); });
0194     connect(m_filenameEdit, qOverload<const QString &>(&KUrlComboBox::returnPressed), this, [this](const QString &) {
0195         KFileItemList files = m_op->selectedItems();
0196         if (files.size() == 1 && files.first().isDir()) {
0197             m_op->setUrl(m_op->url().resolved(files.first().url()), true);
0198         } else {
0199             importSelection();
0200         }
0201     });
0202     KUrlCompletion *fileCompletionObj = new KUrlCompletion(KUrlCompletion::FileCompletion);
0203     fileCompletionObj->setDir(m_op->url());
0204     m_filenameEdit->setCompletionObject(fileCompletionObj);
0205     m_filenameEdit->setAutoDeleteCompletionObject(true);
0206 
0207     // Layout stuff
0208     // File name
0209     QHBoxLayout *hlay1 = new QHBoxLayout;
0210     hlay1->addWidget(new QLabel(i18n("Name:"), this));
0211     hlay1->addWidget(m_filenameEdit);
0212     // Mime filter
0213     QHBoxLayout *hlay2 = new QHBoxLayout;
0214     hlay2->addWidget(new QLabel(i18n("Filter:"), this));
0215     hlay2->addWidget(m_filterCombo);
0216     // Checkbox options
0217     QHBoxLayout *hlay3 = new QHBoxLayout;
0218     QCheckBox *b = new QCheckBox(i18n("Ignore subfolder structure"));
0219     b->setChecked(KdenliveSettings::ignoresubdirstructure());
0220     b->setToolTip(i18n("Do not create subfolders in Project Bin"));
0221     b->setWhatsThis(
0222         xi18nc("@info:whatsthis",
0223                "When enabled, Kdenlive will import all clips contained in the folder and its subfolders without creating the subfolders in Project Bin."));
0224     connect(b, &QCheckBox::toggled, this, [](bool enabled) { KdenliveSettings::setIgnoresubdirstructure(enabled); });
0225 
0226     QCheckBox *b2 = new QCheckBox(i18n("Import image sequence"));
0227     b2->setChecked(KdenliveSettings::autoimagesequence());
0228     connect(b2, &QCheckBox::toggled, this, [](bool enabled) { KdenliveSettings::setAutoimagesequence(enabled); });
0229     b2->setToolTip(i18n("Try to import an image sequence"));
0230     b2->setWhatsThis(
0231         xi18nc("@info:whatsthis", "When enabled, Kdenlive will look for other images with the same name pattern and import them as an image sequence."));
0232     hlay3->addWidget(b2);
0233     hlay3->addWidget(b);
0234     lay->addWidget(tb);
0235     lay->addWidget(m_locationEdit);
0236     lay->addWidget(m_op);
0237     lay->addLayout(hlay1);
0238     lay->addLayout(hlay2);
0239     lay->addLayout(hlay3);
0240     for (QObject *child : children()) {
0241         if (child->isWidgetType()) {
0242             child->installEventFilter(this);
0243         }
0244     }
0245     connectView();
0246 }
0247 
0248 MediaBrowser::~MediaBrowser()
0249 {
0250     KConfigGroup grp(KSharedConfig::openConfig(), "Media Browser");
0251     m_op->writeConfig(grp);
0252 }
0253 
0254 void MediaBrowser::connectView()
0255 {
0256     connect(m_op->view(), &QAbstractItemView::doubleClicked, this, &MediaBrowser::slotViewDoubleClicked);
0257     m_op->view()->installEventFilter(this);
0258     setFocusProxy(m_op->view());
0259     setFocusPolicy(Qt::ClickFocus);
0260 }
0261 
0262 void MediaBrowser::slotViewDoubleClicked()
0263 {
0264     if (KdenliveSettings::mediaDoubleClickImport()) {
0265         importSelection();
0266         return;
0267     }
0268     KFileItemList files = m_op->selectedItems();
0269     if (files.isEmpty()) {
0270         return;
0271     }
0272     if (files.first().isDir()) {
0273         return;
0274     }
0275     openExternalFile(files.first().url());
0276 }
0277 
0278 void MediaBrowser::detectShortcutConflicts()
0279 {
0280 #if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0281     QList<QAction *> actions = m_op->allActions();
0282 #else
0283     QList<QAction *> actions = m_op->actionCollection()->actions();
0284 #endif
0285     QList<QKeySequence> shortcutsList;
0286     m_browserActions.clear();
0287     m_conflictingAppActions.clear();
0288     for (auto &a : actions) {
0289         QKeySequence sh = a->shortcut();
0290         if (!sh.isEmpty()) {
0291             shortcutsList << sh;
0292             m_browserActions << a;
0293             a->setEnabled(false);
0294         }
0295     }
0296     // qDebug()<<"::: FOUND BROWSER SHORTCUTS: "<<shortcutsList<<"\n__________________________";
0297     QList<QAction *> appActions = pCore->window()->actionCollection()->actions();
0298     for (auto &a : appActions) {
0299         QKeySequence sh = a->shortcut();
0300         if (!sh.isEmpty() && shortcutsList.contains(sh)) {
0301             m_conflictingAppActions << a;
0302         }
0303     }
0304 }
0305 
0306 void MediaBrowser::importSelection()
0307 {
0308     KFileItemList files = m_op->selectedItems();
0309     if (KdenliveSettings::autoimagesequence() && files.size() == 1) {
0310         QMimeDatabase db;
0311         QUrl url = files.first().url();
0312         QMimeType type = db.mimeTypeForUrl(url);
0313 
0314         QDomElement prod;
0315         qDebug() << "=== GOT DROPPED MIME: " << type.name();
0316         if (type.name().startsWith(QLatin1String("image/")) && !type.name().contains(QLatin1String("image/gif"))) {
0317             QStringList patternlist;
0318             QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist);
0319             Timecode tc = pCore->timecode();
0320             QScopedPointer<SlideshowClip> dia(new SlideshowClip(tc, url.toLocalFile(), nullptr, this));
0321             if (dia->exec() == QDialog::Accepted) {
0322                 QString parentFolder = "-1";
0323                 std::unordered_map<QString, QString> properties;
0324                 properties[QStringLiteral("ttl")] = QString::number(tc.getFrameCount(dia->clipDuration()));
0325                 properties[QStringLiteral("loop")] = QString::number(static_cast<int>(dia->loop()));
0326                 properties[QStringLiteral("crop")] = QString::number(static_cast<int>(dia->crop()));
0327                 properties[QStringLiteral("fade")] = QString::number(static_cast<int>(dia->fade()));
0328                 properties[QStringLiteral("luma_duration")] = QString::number(tc.getFrameCount(dia->lumaDuration()));
0329                 properties[QStringLiteral("luma_file")] = dia->lumaFile();
0330                 properties[QStringLiteral("softness")] = QString::number(dia->softness());
0331                 properties[QStringLiteral("animation")] = dia->animation();
0332                 properties[QStringLiteral("low-pass")] = QString::number(dia->lowPass());
0333                 int duration = tc.getFrameCount(dia->clipDuration()) * dia->imageCount();
0334                 ClipCreator::createSlideshowClip(dia->selectedPath(), duration, dia->clipName(), parentFolder, properties, pCore->projectItemModel());
0335             }
0336             return;
0337         }
0338     }
0339     QList<QUrl> urls;
0340     for (auto &f : files) {
0341         urls << f.url();
0342     }
0343     pCore->bin()->droppedUrls(urls);
0344 }
0345 
0346 const QUrl MediaBrowser::url() const
0347 {
0348     return m_op->url();
0349 }
0350 
0351 bool MediaBrowser::eventFilter(QObject *watched, QEvent *event)
0352 {
0353     // To avoid shortcut conflicts between the media browser and main app, we dis/enable actions when we gain/lose focus
0354     if (event->type() == QEvent::FocusIn) {
0355         qDebug() << ":::::: \n\nFOCUS IN\n\n:::::::::::::::::";
0356         disableAppShortcuts();
0357     } else if (event->type() == QEvent::FocusOut) {
0358         qDebug() << ":::::: \n\nFOCUS OUT\n\n:::::::::::::::::";
0359         enableAppShortcuts();
0360     } else if (event->type() == QEvent::Hide) {
0361         if (m_op->dirLister()->autoUpdate()) {
0362             m_op->dirLister()->setAutoUpdate(false);
0363         }
0364     } else if (event->type() == QEvent::Show) {
0365         if (!m_op->dirLister()->autoUpdate()) {
0366             m_op->dirLister()->setAutoUpdate(true);
0367         }
0368     }
0369     const bool res = QWidget::eventFilter(watched, event);
0370     QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
0371     if (!keyEvent) {
0372         return res;
0373     }
0374 
0375     const auto type = event->type();
0376     const auto key = keyEvent->key();
0377 
0378     if (watched == m_op && type == QEvent::KeyPress && (key == Qt::Key_Return || key == Qt::Key_Enter)) {
0379         // ignore return events from the KDirOperator
0380         importSelection();
0381         event->accept();
0382         return true;
0383     }
0384     return res;
0385 }
0386 
0387 void MediaBrowser::disableAppShortcuts()
0388 {
0389     for (auto &a : m_conflictingAppActions) {
0390         if (a) {
0391             a->setEnabled(false);
0392         }
0393     }
0394     for (auto &a : m_browserActions) {
0395         if (a) {
0396             a->setEnabled(true);
0397         }
0398     }
0399 }
0400 
0401 void MediaBrowser::enableAppShortcuts()
0402 {
0403     for (auto &a : m_browserActions) {
0404         if (a) {
0405             a->setEnabled(false);
0406         }
0407     }
0408     for (auto &a : m_conflictingAppActions) {
0409         if (a) {
0410             a->setEnabled(true);
0411         }
0412     }
0413 }
0414 
0415 void MediaBrowser::slotUrlEntered(const QUrl &url)
0416 {
0417     QSignalBlocker bk(m_locationEdit);
0418     m_locationEdit->setLocationUrl(url);
0419     QSignalBlocker bk2(m_filenameEdit);
0420     KUrlCompletion *completion = dynamic_cast<KUrlCompletion *>(m_filenameEdit->completionObject());
0421     if (completion) {
0422         completion->setDir(url);
0423     }
0424 }
0425 
0426 void MediaBrowser::setUrl(const QUrl url)
0427 {
0428     m_op->setUrl(url, true);
0429     m_op->setIconSize(KdenliveSettings::mediaIconSize());
0430 }
0431 
0432 void MediaBrowser::slotFilterChanged()
0433 {
0434     m_filterDelayTimer.stop();
0435 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0436     QString filter = m_filterCombo->currentFilter();
0437     m_op->clearFilter();
0438     if (filter.contains(QLatin1Char('/'))) {
0439         QStringList types = filter.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0440         types.prepend(QStringLiteral("inode/directory"));
0441         m_op->setMimeFilter(types);
0442     } else if (filter.contains(QLatin1Char('*')) || filter.contains(QLatin1Char('?')) || filter.contains(QLatin1Char('['))) {
0443         m_op->setNameFilter(filter);
0444     } else {
0445         m_op->setNameFilter(QLatin1Char('*') + filter.replace(QLatin1Char(' '), QLatin1Char('*')) + QLatin1Char('*'));
0446     }
0447 #else
0448     KFileFilter filter = m_filterCombo->currentFilter();
0449     m_op->clearFilter();
0450     if (!filter.mimePatterns().isEmpty()) {
0451         QStringList types = filter.mimePatterns();
0452         types.prepend(QStringLiteral("inode/directory"));
0453         m_op->setMimeFilter(types);
0454     } else {
0455         m_op->setNameFilter(filter.toFilterString());
0456     }
0457 #endif
0458     m_op->updateDir();
0459 }
0460 
0461 void MediaBrowser::slotLocationChanged(const QString &text)
0462 {
0463     m_filenameEdit->lineEdit()->setModified(true);
0464     if (text.isEmpty() && m_op->view()) {
0465         m_op->view()->clearSelection();
0466     }
0467 
0468     if (!m_filenameEdit->lineEdit()->text().isEmpty()) {
0469         QUrl u = m_op->url().adjusted(QUrl::RemoveFilename);
0470         QUrl partial_url;
0471         partial_url.setPath(text);
0472         const QUrl finalUrl = u.resolved(partial_url);
0473         qDebug() << ":::: GOT FINAL URL: " << finalUrl << "\n_____________________";
0474         ;
0475         // const QList<QUrl> urlList(tokenize(text));
0476         m_op->setCurrentItem(finalUrl);
0477     }
0478 }
0479 
0480 void MediaBrowser::openExternalFile(const QUrl &url)
0481 {
0482     if (pCore->packageType() == QStringLiteral("appimage")) {
0483         qDebug() << "::::: LAUNCHING APPIMAGE BROWSER.........";
0484         QProcess process;
0485         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0486         qDebug() << "::: GOT ENV: " << env.value("LD_LIBRARY_PATH") << ", PATH: " << env.value("PATH") << "\n\nXDG:\n" << env.value("XDG_DATA_DIRS");
0487         QStringList libPath = env.value(QStringLiteral("LD_LIBRARY_PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
0488         QStringList updatedLDPath;
0489         for (auto &s : libPath) {
0490             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
0491                 updatedLDPath << s;
0492             }
0493         }
0494         if (updatedLDPath.isEmpty()) {
0495             env.remove(QStringLiteral("LD_LIBRARY_PATH"));
0496         } else {
0497             env.insert(QStringLiteral("LD_LIBRARY_PATH"), updatedLDPath.join(QLatin1Char(':')));
0498         }
0499         // Path
0500         libPath = env.value(QStringLiteral("PATH")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
0501         updatedLDPath.clear();
0502         for (auto &s : libPath) {
0503             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
0504                 updatedLDPath << s;
0505             }
0506         }
0507         if (updatedLDPath.isEmpty()) {
0508             env.remove(QStringLiteral("PATH"));
0509         } else {
0510             env.insert(QStringLiteral("PATH"), updatedLDPath.join(QLatin1Char(':')));
0511         }
0512         // XDG
0513         libPath = env.value(QStringLiteral("XDG_DATA_DIRS")).split(QLatin1Char(':'), Qt::SkipEmptyParts);
0514         updatedLDPath.clear();
0515         for (auto &s : libPath) {
0516             if (!s.startsWith(QStringLiteral("/tmp/.mount_"))) {
0517                 updatedLDPath << s;
0518             }
0519         }
0520         if (updatedLDPath.isEmpty()) {
0521             env.remove(QStringLiteral("XDG_DATA_DIRS"));
0522         } else {
0523             env.insert(QStringLiteral("XDG_DATA_DIRS"), updatedLDPath.join(QLatin1Char(':')));
0524         }
0525         env.remove(QStringLiteral("QT_QPA_PLATFORM"));
0526         process.setProcessEnvironment(env);
0527         QString openPath = QStandardPaths::findExecutable(QStringLiteral("xdg-open"));
0528         process.setProgram(openPath.isEmpty() ? QStringLiteral("xdg-open") : openPath);
0529         process.setArguments({url.toLocalFile()});
0530         process.startDetached();
0531     } else {
0532         QDesktopServices::openUrl(url);
0533     }
0534 }
0535 
0536 void MediaBrowser::back()
0537 {
0538     m_op->back();
0539 }
0540 
0541 void MediaBrowser::forward()
0542 {
0543     m_op->forward();
0544 }