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"