File indexing completed on 2024-11-10 04:30:16

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
0004    Copyright (C) 2007 Urs Wolfer <uwolfer @ kde.org>
0005    Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>
0006 
0007    This program is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 */
0012 
0013 #include "kget_linkview.h"
0014 #include "core/kget.h"
0015 #include "core/linkimporter.h"
0016 #include "kget_sortfilterproxymodel.h"
0017 #include "settings.h"
0018 #include "ui/newtransferdialog.h"
0019 
0020 #include "kget_debug.h"
0021 #include <QAction>
0022 #include <QActionGroup>
0023 #include <QApplication>
0024 #include <QClipboard>
0025 #include <QDebug>
0026 #include <QIcon>
0027 #include <QMenu>
0028 #include <QMimeDatabase>
0029 #include <QMimeType>
0030 #include <QStandardItemModel>
0031 
0032 #include <KLocalizedString>
0033 #include <KWindowSystem>
0034 
0035 KGetLinkView::KGetLinkView(QWidget *parent)
0036     : KGetSaveSizeDialog("KGetLinkView", parent)
0037     , m_linkImporter(nullptr)
0038     , m_nameAction(nullptr)
0039     , m_urlAction(nullptr)
0040 {
0041     setAttribute(Qt::WA_DeleteOnClose);
0042     setWindowTitle(i18n("Import Links"));
0043 
0044     /*if (parent) {
0045         KWindowInfo info = KWindowSystem::windowInfo(parent->winId(), NET::WMDesktop, NET::WMDesktop);
0046         KWindowSystem::setCurrentDesktop(info.desktop());
0047         KWindowSystem::forceActiveWindow(parent->winId());
0048     }*///TODO: Port all KWindowSystem stuff
0049 
0050     // proxy model to filter links
0051     m_proxyModel = new KGetSortFilterProxyModel(1, this);
0052     m_proxyModel->setDynamicSortFilter(true);
0053     m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
0054 
0055     ui.setupUi(this);
0056 
0057     m_proxyModel->setShowWebContent(ui.showWebContent->isChecked());
0058 
0059     ui.filterMode->addItem(i18n("Contains"), KGetSortFilterProxyModel::Contain);
0060     ui.filterMode->addItem(i18n("Does Not Contain"), KGetSortFilterProxyModel::DoesNotContain);
0061 
0062     // set the Icons
0063     ui.importLinks->setIcon(QIcon::fromTheme("document-import"));
0064     ui.showCombo->addItem(QIcon::fromTheme("view-list-icons"), i18n("All"), KGetSortFilterProxyModel::NoFilter);
0065     ui.showCombo->addItem(QIcon::fromTheme("video-x-generic"), i18n("Videos"), KGetSortFilterProxyModel::VideoFiles);
0066     ui.showCombo->addItem(QIcon::fromTheme("image-x-generic"), i18n("Images"), KGetSortFilterProxyModel::ImageFiles);
0067     ui.showCombo->addItem(QIcon::fromTheme("audio-x-generic"), i18n("Audio"), KGetSortFilterProxyModel::AudioFiles);
0068     ui.showCombo->addItem(QIcon::fromTheme("package-x-generic"), i18n("Archives"), KGetSortFilterProxyModel::CompressedFiles);
0069 
0070     ui.treeView->setModel(m_proxyModel);
0071     ui.progressBar->hide();
0072 
0073     // creates pattern syntax menu for the text filter
0074     m_patternSyntaxMenu = new QMenu(i18nc("of a filter, e.g. RegExp or Wildcard", "Pattern Syntax"), this);
0075     auto *wildcardAction = new QAction(i18n("Escape Sequences"), this);
0076     wildcardAction->setCheckable(true);
0077     wildcardAction->setChecked(Settings::linkViewFilterPatternSyntax() == Wildcard);
0078     auto *regExpAction = new QAction(i18n("Regular Expression"), this);
0079     regExpAction->setCheckable(true);
0080     regExpAction->setChecked(Settings::linkViewFilterPatternSyntax() == RegExp);
0081     auto *actionGroup = new QActionGroup(this);
0082     actionGroup->addAction(wildcardAction);
0083     actionGroup->addAction(regExpAction);
0084     m_patternSyntaxMenu->addActions(actionGroup->actions());
0085 
0086     // Filter for name/url actions
0087     auto *columnGroup = new QActionGroup(this);
0088     m_nameAction = new QAction(i18nc("name of a file", "Name"), this);
0089     m_nameAction->setCheckable(true);
0090     m_nameAction->setChecked(true);
0091     m_urlAction = new QAction(i18n("URL"), this);
0092     m_urlAction->setCheckable(true);
0093     columnGroup->addAction(m_nameAction);
0094     columnGroup->addAction(m_urlAction);
0095     connect(columnGroup, &QActionGroup::triggered, this, &KGetLinkView::slotFilterColumn);
0096 
0097     connect(wildcardAction, &QAction::toggled, this, &KGetLinkView::wildcardPatternToggled);
0098     connect(ui.treeView, &QAbstractItemView::doubleClicked, this, &KGetLinkView::uncheckItem);
0099     connect(ui.textFilter, &QLineEdit::textChanged, this, &KGetLinkView::setTextFilter);
0100     connect(ui.textFilter, &KLineEdit::aboutToShowContextMenu, this, &KGetLinkView::contextMenuDisplayed);
0101     connect(ui.filterMode, &QComboBox::currentIndexChanged, this, &KGetLinkView::slotFilterModeChanged);
0102     connect(ui.showCombo, &QComboBox::currentIndexChanged, this, &KGetLinkView::slotMimeTypeChanged);
0103     connect(ui.showCombo, &QComboBox::currentIndexChanged, this, &KGetLinkView::updateSelectionButtons);
0104     connect(ui.urlRequester, &KUrlRequester::textChanged, this, &KGetLinkView::updateImportButtonStatus);
0105     connect(ui.urlRequester, &KUrlRequester::urlSelected, this, &KGetLinkView::slotStartImport);
0106     connect(ui.selectAll, &QAbstractButton::clicked, this, &KGetLinkView::checkAll);
0107     connect(ui.deselectAll, &QAbstractButton::clicked, this, &KGetLinkView::uncheckAll);
0108     connect(ui.invertSelection, &QAbstractButton::clicked, this, &KGetLinkView::slotInvertSelection);
0109     connect(this, &QDialog::accepted, this, &KGetLinkView::slotStartLeech);
0110     connect(ui.showWebContent, &QCheckBox::stateChanged, m_proxyModel, [this](int show) {
0111         m_proxyModel->setShowWebContent(show);
0112     });
0113     connect(ui.importLinks, &QAbstractButton::clicked, this, &KGetLinkView::slotStartImport);
0114     connect(ui.treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KGetLinkView::selectionChanged);
0115     connect(ui.dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0116     connect(ui.dialogButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0117 
0118     m_downloadButton = ui.dialogButtonBox->addButton(i18nc("Download the items which have been selected", "&Download"), QDialogButtonBox::AcceptRole);
0119     m_downloadButton->setIcon(QIcon::fromTheme("kget"));
0120 
0121     checkClipboard();
0122 }
0123 
0124 KGetLinkView::~KGetLinkView()
0125 {
0126     delete (m_linkImporter);
0127 }
0128 
0129 void KGetLinkView::checkClipboard()
0130 {
0131     QString clipboardContent = QApplication::clipboard()->text(QClipboard::Clipboard);
0132 
0133     if (clipboardContent.length() > 0) {
0134         delete m_linkImporter;
0135 
0136         m_linkImporter = new LinkImporter(this);
0137 
0138         m_linkImporter->checkClipboard(clipboardContent);
0139 
0140         slotImportFinished();
0141     }
0142 }
0143 
0144 void KGetLinkView::setLinks(const QStringList &links)
0145 {
0146     m_links = links;
0147     showLinks(m_links, false);
0148 }
0149 
0150 void KGetLinkView::showLinks(const QStringList &links, bool urlRequestVisible)
0151 {
0152     ui.importWidget->setVisible(urlRequestVisible);
0153 
0154     auto *model = new QStandardItemModel(0, 5, this);
0155 
0156     model->setHeaderData(0, Qt::Horizontal, i18n("Auxiliary header"));
0157     model->setHeaderData(1, Qt::Horizontal, i18n("File Name"));
0158     model->setHeaderData(2, Qt::Horizontal, i18n("Description"));
0159     model->setHeaderData(3, Qt::Horizontal, i18nc("list header: type of file", "File Type"));
0160     model->setHeaderData(4, Qt::Horizontal, i18n("Location (URL)"));
0161 
0162     foreach (const QString &linkitem, links) {
0163         QUrl url;
0164         QMimeDatabase db;
0165         QMimeType mt;
0166 
0167         if (linkitem.contains(QLatin1String("url "), Qt::CaseInsensitive) && linkitem.contains(QLatin1String("type "), Qt::CaseInsensitive)) {
0168             const QStringList items = linkitem.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0169             const int count = items.count();
0170             int index = items.indexOf(QLatin1String("url"));
0171             if (index > -1 && index + 1 < count)
0172                 url = QUrl(items.at(index + 1));
0173             index = items.indexOf(QLatin1String("type"));
0174             if (index > -1 && index + 1 < count)
0175                 mt = db.mimeTypeForName(items.at(index + 1));
0176         } else {
0177             url = QUrl(linkitem);
0178             mt = db.mimeTypeForFile(linkitem, QMimeDatabase::MatchExtension);
0179         }
0180 
0181         qCDebug(KGET_DEBUG) << "Adding:" << linkitem;
0182 
0183         QString file = url.fileName();
0184         if (file.isEmpty())
0185             file = QString(url.host());
0186 
0187         QString mimeTypeName, mimeTypeIcon, mimeTypeComment;
0188         if (mt.isValid()) {
0189             mimeTypeName = mt.name();
0190             mimeTypeIcon = mt.iconName();
0191             mimeTypeComment = mt.comment();
0192         }
0193 
0194         auto *item = new QStandardItem(file);
0195         item->setIcon(QIcon::fromTheme(mimeTypeIcon));
0196         item->setCheckable(true);
0197         item->setCheckState(Qt::Checked);
0198         item->setData(QVariant(url.fileName()), Qt::DisplayRole);
0199         item->setData(QVariant(mimeTypeName), Qt::UserRole); // used for filtering DownloadFilterType
0200 
0201         QList<QStandardItem *> items;
0202         auto *number = new QStandardItem();
0203         number->setData(model->rowCount(), Qt::DisplayRole); // used for initial sorting
0204         items << number;
0205         items << item;
0206         items << new QStandardItem();
0207         items << new QStandardItem(mimeTypeComment);
0208         items << new QStandardItem(url.toDisplayString());
0209 
0210         model->insertRow(model->rowCount(), items);
0211     }
0212 
0213     connect(model, &QStandardItemModel::itemChanged, this, &KGetLinkView::selectionChanged);
0214     m_proxyModel->setSourceModel(model);
0215     m_proxyModel->setFilterKeyColumn(1);
0216     m_proxyModel->sort(0);
0217 
0218     ui.treeView->header()->hideSection(0);
0219     ui.treeView->setColumnWidth(1, 200); // make the filename column bigger by default
0220 
0221     selectionChanged(); // adapt buttons to the new situation
0222 }
0223 
0224 void KGetLinkView::slotMimeTypeChanged(int index)
0225 {
0226     m_proxyModel->setFilterType(ui.showCombo->itemData(index).toInt());
0227 }
0228 
0229 void KGetLinkView::slotFilterModeChanged(int index)
0230 {
0231     m_proxyModel->setFilterMode(ui.filterMode->itemData(index).toInt());
0232 }
0233 
0234 void KGetLinkView::slotFilterColumn(QAction *action)
0235 {
0236     // FIXME make this not depend on "magic numbers"?
0237     m_proxyModel->setFilterColumn(action == m_urlAction ? 4 : 1);
0238 }
0239 
0240 void KGetLinkView::slotStartLeech()
0241 {
0242     auto *model = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0243     if (model) {
0244         QList<QUrl> urls;
0245 
0246         for (int row = 0; row < model->rowCount(); row++) {
0247             QStandardItem *checkeableItem = model->item(row, 1);
0248 
0249             if (checkeableItem->checkState() == Qt::Checked) {
0250                 urls.append(QUrl(model->data(model->index(row, 4)).toString()));
0251             }
0252         }
0253 
0254         NewTransferDialogHandler::showNewTransferDialog(urls);
0255     }
0256 }
0257 
0258 void KGetLinkView::setPageUrl(const QString &url)
0259 {
0260     setWindowTitle(i18n("Links in: %1 - KGet", url));
0261 }
0262 
0263 void KGetLinkView::importUrl(const QString &url)
0264 {
0265     if (url.isEmpty()) {
0266         QUrl clipboardUrl = QUrl(QApplication::clipboard()->text(QClipboard::Clipboard).trimmed());
0267         if (clipboardUrl.isValid() && ((!clipboardUrl.scheme().isEmpty() && !clipboardUrl.host().isEmpty()) || (clipboardUrl.isLocalFile()))) {
0268             ui.urlRequester->setUrl(clipboardUrl);
0269         }
0270     } else {
0271         ui.urlRequester->setUrl(QUrl(url));
0272         slotStartImport();
0273     }
0274 }
0275 
0276 void KGetLinkView::selectionChanged()
0277 {
0278     auto *model = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0279     if (model) {
0280         const int modelRowCount = model->rowCount();
0281         bool buttonEnabled = false;
0282         int count = 0;
0283 
0284         for (int row = 0; row < modelRowCount; row++) {
0285             QStandardItem *checkeableItem = model->item(row, 1);
0286 
0287             if ((checkeableItem->checkState() == Qt::Checked)) {
0288                 buttonEnabled = true;
0289 
0290                 // only count the checked files that are currently visible
0291                 if (m_proxyModel->mapFromSource(model->index(row, 1)).isValid()) {
0292                     count++;
0293                 }
0294             }
0295         }
0296 
0297         ui.selectAll->setEnabled(!(!modelRowCount || count == m_proxyModel->rowCount()));
0298         ui.deselectAll->setEnabled(count > 0);
0299         ui.invertSelection->setEnabled(count > 0);
0300         m_downloadButton->setEnabled(buttonEnabled);
0301     }
0302 }
0303 
0304 void KGetLinkView::setTextFilter(const QString &text)
0305 {
0306     // TODO: escape user input for avoiding malicious user input! (FiNEX)
0307     QString temp = text.isEmpty() ? ui.textFilter->text() : text;
0308     if (Settings::linkViewFilterPatternSyntax() == Wildcard) {
0309         m_proxyModel->setFilterWildcard(temp);
0310     } else {
0311         QRegularExpression rx(temp);
0312         m_proxyModel->setFilterRegularExpression(rx);
0313     }
0314 
0315     updateSelectionButtons();
0316 }
0317 
0318 void KGetLinkView::updateSelectionButtons()
0319 {
0320     const bool isFiltered = !ui.textFilter->text().isEmpty() || (ui.showCombo->currentIndex() != KGetSortFilterProxyModel::NoFilter);
0321     ui.selectAll->setText(isFiltered ? i18n("&Select All Filtered") : i18n("&Select All"));
0322     ui.deselectAll->setText(isFiltered ? i18n("D&eselect All Filtered") : i18n("D&eselect All"));
0323 
0324     selectionChanged();
0325 }
0326 
0327 void KGetLinkView::checkAll()
0328 {
0329     auto *itemsModel = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0330     if (itemsModel) {
0331         for (int row = 0; row < m_proxyModel->rowCount(); row++) {
0332             const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3));
0333             QStandardItem *item = itemsModel->item(index.row(), 1);
0334             item->setCheckState(Qt::Checked);
0335         }
0336     }
0337 }
0338 
0339 void KGetLinkView::uncheckAll()
0340 {
0341     auto *itemsModel = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0342     if (itemsModel) {
0343         for (int row = 0; row < m_proxyModel->rowCount(); row++) {
0344             const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3));
0345             QStandardItem *item = itemsModel->item(index.row(), 1);
0346             item->setCheckState(Qt::Unchecked);
0347         }
0348     }
0349 }
0350 
0351 void KGetLinkView::uncheckItem(const QModelIndex &index)
0352 {
0353     auto *model = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0354     if (model) {
0355         if (index.column() != 0) {
0356             QStandardItem *item = model->itemFromIndex(model->index(m_proxyModel->mapToSource(index).row(), 1));
0357             item->setCheckState(item->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
0358         }
0359     }
0360 }
0361 
0362 void KGetLinkView::slotCheckSelected()
0363 {
0364     auto *model = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0365     if (model) {
0366         foreach (const QModelIndex &index, ui.treeView->selectionModel()->selectedIndexes()) {
0367             QModelIndex sourceIndex = m_proxyModel->mapToSource(index);
0368             QStandardItem *item = model->item(sourceIndex.row(), 1);
0369 
0370             item->setCheckState(Qt::Checked);
0371         }
0372     }
0373 }
0374 
0375 void KGetLinkView::slotInvertSelection()
0376 {
0377     auto *itemsModel = qobject_cast<QStandardItemModel *>(m_proxyModel->sourceModel());
0378     if (itemsModel) {
0379         for (int row = 0; row < m_proxyModel->rowCount(); row++) {
0380             const QModelIndex index = m_proxyModel->mapToSource(m_proxyModel->index(row, 3));
0381             QStandardItem *item = itemsModel->item(index.row(), 1);
0382             item->setCheckState((item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked);
0383         }
0384     }
0385 }
0386 
0387 void KGetLinkView::slotStartImport()
0388 {
0389     delete m_linkImporter;
0390 
0391     m_linkImporter = new LinkImporter(ui.urlRequester->url(), this);
0392 
0393     connect(m_linkImporter, &LinkImporter::progress, this, &KGetLinkView::slotImportProgress);
0394     connect(m_linkImporter, &QThread::finished, this, &KGetLinkView::slotImportFinished);
0395 
0396     if (!ui.urlRequester->url().isLocalFile()) {
0397         m_linkImporter->copyRemoteFile();
0398     }
0399 
0400     m_linkImporter->start();
0401     ui.progressBar->show();
0402 }
0403 
0404 void KGetLinkView::slotImportProgress(int progress)
0405 {
0406     ui.progressBar->setValue(progress);
0407 }
0408 
0409 void KGetLinkView::slotImportFinished()
0410 {
0411     ui.progressBar->hide();
0412     m_links = m_linkImporter->links();
0413     showLinks(m_links, true);
0414 }
0415 
0416 void KGetLinkView::updateImportButtonStatus(const QString &text)
0417 {
0418     bool enabled = false;
0419     if (!text.isEmpty()) {
0420         QUrl url(text);
0421         if (url.isValid()) {
0422             enabled = true;
0423         }
0424     }
0425     ui.importLinks->setEnabled(enabled);
0426 }
0427 
0428 void KGetLinkView::contextMenuDisplayed(QMenu *menu)
0429 {
0430     menu->addSeparator();
0431     menu->addMenu(m_patternSyntaxMenu);
0432     menu->addSeparator()->setText(i18n("Filter Column"));
0433     menu->addAction(m_nameAction);
0434     menu->addAction(m_urlAction);
0435 }
0436 
0437 void KGetLinkView::wildcardPatternToggled(bool enabled)
0438 {
0439     if (enabled) {
0440         Settings::setLinkViewFilterPatternSyntax(Wildcard);
0441     } else {
0442         Settings::setLinkViewFilterPatternSyntax(RegExp);
0443     }
0444 }
0445 
0446 #include "moc_kget_linkview.cpp"