2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
30 #include "searchjobwidget.h"
32 #include <QApplication>
34 #include <QDesktopServices>
35 #include <QHeaderView>
39 #include <QStandardItemModel>
42 #include "base/preferences.h"
43 #include "base/search/searchdownloadhandler.h"
44 #include "base/search/searchhandler.h"
45 #include "base/search/searchpluginmanager.h"
46 #include "base/utils/misc.h"
47 #include "gui/interfaces/iguiapplication.h"
48 #include "gui/lineedit.h"
49 #include "gui/uithememanager.h"
50 #include "searchsortmodel.h"
51 #include "ui_searchjobwidget.h"
57 LinkVisitedRole
= Qt::UserRole
+ 100
60 QColor
visitedRowColor()
62 return QApplication::palette().color(QPalette::Disabled
, QPalette::WindowText
);
66 SearchJobWidget::SearchJobWidget(SearchHandler
*searchHandler
, IGUIApplication
*app
, QWidget
*parent
)
67 : GUIApplicationComponent(app
, parent
)
68 , m_ui
{new Ui::SearchJobWidget
}
69 , m_searchHandler
{searchHandler
}
70 , m_nameFilteringMode
{u
"Search/FilteringMode"_s
}
76 header()->setFirstSectionMovable(true);
77 header()->setStretchLastSection(false);
78 header()->setTextElideMode(Qt::ElideRight
);
80 // Set Search results list model
81 m_searchListModel
= new QStandardItemModel(0, SearchSortModel::NB_SEARCH_COLUMNS
, this);
82 m_searchListModel
->setHeaderData(SearchSortModel::NAME
, Qt::Horizontal
, tr("Name", "i.e: file name"));
83 m_searchListModel
->setHeaderData(SearchSortModel::SIZE
, Qt::Horizontal
, tr("Size", "i.e: file size"));
84 m_searchListModel
->setHeaderData(SearchSortModel::SEEDS
, Qt::Horizontal
, tr("Seeders", "i.e: Number of full sources"));
85 m_searchListModel
->setHeaderData(SearchSortModel::LEECHES
, Qt::Horizontal
, tr("Leechers", "i.e: Number of partial sources"));
86 m_searchListModel
->setHeaderData(SearchSortModel::ENGINE_NAME
, Qt::Horizontal
, tr("Engine"));
87 m_searchListModel
->setHeaderData(SearchSortModel::ENGINE_URL
, Qt::Horizontal
, tr("Engine URL"));
88 m_searchListModel
->setHeaderData(SearchSortModel::PUB_DATE
, Qt::Horizontal
, tr("Published On"));
89 // Set columns text alignment
90 m_searchListModel
->setHeaderData(SearchSortModel::SIZE
, Qt::Horizontal
, QVariant(Qt::AlignRight
| Qt::AlignVCenter
), Qt::TextAlignmentRole
);
91 m_searchListModel
->setHeaderData(SearchSortModel::SEEDS
, Qt::Horizontal
, QVariant(Qt::AlignRight
| Qt::AlignVCenter
), Qt::TextAlignmentRole
);
92 m_searchListModel
->setHeaderData(SearchSortModel::LEECHES
, Qt::Horizontal
, QVariant(Qt::AlignRight
| Qt::AlignVCenter
), Qt::TextAlignmentRole
);
94 m_proxyModel
= new SearchSortModel(this);
95 m_proxyModel
->setDynamicSortFilter(true);
96 m_proxyModel
->setSourceModel(m_searchListModel
);
97 m_proxyModel
->setNameFilter(searchHandler
->pattern());
98 m_ui
->resultsBrowser
->setModel(m_proxyModel
);
100 m_ui
->resultsBrowser
->hideColumn(SearchSortModel::DL_LINK
); // Hide url column
101 m_ui
->resultsBrowser
->hideColumn(SearchSortModel::DESC_LINK
);
103 m_ui
->resultsBrowser
->setSelectionMode(QAbstractItemView::ExtendedSelection
);
104 m_ui
->resultsBrowser
->setRootIsDecorated(false);
105 m_ui
->resultsBrowser
->setAllColumnsShowFocus(true);
106 m_ui
->resultsBrowser
->setSortingEnabled(true);
107 m_ui
->resultsBrowser
->setEditTriggers(QAbstractItemView::NoEditTriggers
);
109 // Ensure that at least one column is visible at all times
110 bool atLeastOne
= false;
111 for (int i
= 0; i
< SearchSortModel::DL_LINK
; ++i
)
113 if (!m_ui
->resultsBrowser
->isColumnHidden(i
))
120 m_ui
->resultsBrowser
->setColumnHidden(SearchSortModel::NAME
, false);
121 // To also mitigate the above issue, we have to resize each column when
122 // its size is 0, because explicitly 'showing' the column isn't enough
123 // in the above scenario.
124 for (int i
= 0; i
< SearchSortModel::DL_LINK
; ++i
)
126 if ((m_ui
->resultsBrowser
->columnWidth(i
) <= 0) && !m_ui
->resultsBrowser
->isColumnHidden(i
))
127 m_ui
->resultsBrowser
->resizeColumnToContents(i
);
130 header()->setContextMenuPolicy(Qt::CustomContextMenu
);
131 connect(header(), &QWidget::customContextMenuRequested
, this, &SearchJobWidget::displayColumnHeaderMenu
);
132 connect(header(), &QHeaderView::sectionResized
, this, &SearchJobWidget::saveSettings
);
133 connect(header(), &QHeaderView::sectionMoved
, this, &SearchJobWidget::saveSettings
);
134 connect(header(), &QHeaderView::sortIndicatorChanged
, this, &SearchJobWidget::saveSettings
);
136 fillFilterComboBoxes();
140 m_lineEditSearchResultsFilter
= new LineEdit(this);
141 m_lineEditSearchResultsFilter
->setPlaceholderText(tr("Filter search results..."));
142 m_lineEditSearchResultsFilter
->setContextMenuPolicy(Qt::CustomContextMenu
);
143 connect(m_lineEditSearchResultsFilter
, &QWidget::customContextMenuRequested
, this, &SearchJobWidget::showFilterContextMenu
);
144 connect(m_lineEditSearchResultsFilter
, &LineEdit::textChanged
, this, &SearchJobWidget::filterSearchResults
);
145 m_ui
->horizontalLayout
->insertWidget(0, m_lineEditSearchResultsFilter
);
147 connect(m_ui
->filterMode
, qOverload
<int>(&QComboBox::currentIndexChanged
)
148 , this, &SearchJobWidget::updateFilter
);
149 connect(m_ui
->minSeeds
, &QAbstractSpinBox::editingFinished
, this, &SearchJobWidget::updateFilter
);
150 connect(m_ui
->minSeeds
, qOverload
<int>(&QSpinBox::valueChanged
)
151 , this, &SearchJobWidget::updateFilter
);
152 connect(m_ui
->maxSeeds
, &QAbstractSpinBox::editingFinished
, this, &SearchJobWidget::updateFilter
);
153 connect(m_ui
->maxSeeds
, qOverload
<int>(&QSpinBox::valueChanged
)
154 , this, &SearchJobWidget::updateFilter
);
155 connect(m_ui
->minSize
, &QAbstractSpinBox::editingFinished
, this, &SearchJobWidget::updateFilter
);
156 connect(m_ui
->minSize
, qOverload
<double>(&QDoubleSpinBox::valueChanged
)
157 , this, &SearchJobWidget::updateFilter
);
158 connect(m_ui
->maxSize
, &QAbstractSpinBox::editingFinished
, this, &SearchJobWidget::updateFilter
);
159 connect(m_ui
->maxSize
, qOverload
<double>(&QDoubleSpinBox::valueChanged
)
160 , this, &SearchJobWidget::updateFilter
);
161 connect(m_ui
->minSizeUnit
, qOverload
<int>(&QComboBox::currentIndexChanged
)
162 , this, &SearchJobWidget::updateFilter
);
163 connect(m_ui
->maxSizeUnit
, qOverload
<int>(&QComboBox::currentIndexChanged
)
164 , this, &SearchJobWidget::updateFilter
);
166 connect(m_ui
->resultsBrowser
, &QAbstractItemView::doubleClicked
, this, &SearchJobWidget::onItemDoubleClicked
);
168 connect(searchHandler
, &SearchHandler::newSearchResults
, this, &SearchJobWidget::appendSearchResults
);
169 connect(searchHandler
, &SearchHandler::searchFinished
, this, &SearchJobWidget::searchFinished
);
170 connect(searchHandler
, &SearchHandler::searchFailed
, this, &SearchJobWidget::searchFailed
);
171 connect(this, &QObject::destroyed
, searchHandler
, &QObject::deleteLater
);
173 setStatusTip(statusText(m_status
));
175 connect(UIThemeManager::instance(), &UIThemeManager::themeChanged
, this, &SearchJobWidget::onUIThemeChanged
);
178 SearchJobWidget::~SearchJobWidget()
184 void SearchJobWidget::onItemDoubleClicked(const QModelIndex
&index
)
186 downloadTorrent(index
);
189 QHeaderView
*SearchJobWidget::header() const
191 return m_ui
->resultsBrowser
->header();
194 // Set the color of a row in data model
195 void SearchJobWidget::setRowColor(int row
, const QColor
&color
)
197 for (int i
= 0; i
< m_proxyModel
->columnCount(); ++i
)
198 m_proxyModel
->setData(m_proxyModel
->index(row
, i
), color
, Qt::ForegroundRole
);
201 void SearchJobWidget::setRowVisited(const int row
)
203 m_proxyModel
->setDynamicSortFilter(false);
205 m_proxyModel
->setData(m_proxyModel
->index(row
, 0), true, LinkVisitedRole
);
206 setRowColor(row
, visitedRowColor());
208 m_proxyModel
->setDynamicSortFilter(true);
211 void SearchJobWidget::onUIThemeChanged()
213 m_proxyModel
->setDynamicSortFilter(false);
215 for (int row
= 0; row
< m_proxyModel
->rowCount(); ++row
)
217 const QVariant userData
= m_proxyModel
->data(m_proxyModel
->index(row
, 0), LinkVisitedRole
);
218 const bool isVisited
= userData
.toBool();
220 setRowColor(row
, visitedRowColor());
223 m_proxyModel
->setDynamicSortFilter(true);
226 SearchJobWidget::Status
SearchJobWidget::status() const
231 int SearchJobWidget::visibleResultsCount() const
233 return m_proxyModel
->rowCount();
236 LineEdit
*SearchJobWidget::lineEditSearchResultsFilter() const
238 return m_lineEditSearchResultsFilter
;
241 void SearchJobWidget::cancelSearch()
243 m_searchHandler
->cancelSearch();
246 void SearchJobWidget::downloadTorrents(const AddTorrentOption option
)
248 const QModelIndexList rows
= m_ui
->resultsBrowser
->selectionModel()->selectedRows();
249 for (const QModelIndex
&rowIndex
: rows
)
250 downloadTorrent(rowIndex
, option
);
253 void SearchJobWidget::openTorrentPages() const
255 const QModelIndexList rows
{m_ui
->resultsBrowser
->selectionModel()->selectedRows()};
256 for (const QModelIndex
&rowIndex
: rows
)
258 const QString descrLink
= m_proxyModel
->data(
259 m_proxyModel
->index(rowIndex
.row(), SearchSortModel::DESC_LINK
)).toString();
260 if (!descrLink
.isEmpty())
261 QDesktopServices::openUrl(QUrl::fromEncoded(descrLink
.toUtf8()));
265 void SearchJobWidget::copyTorrentURLs() const
267 copyField(SearchSortModel::DESC_LINK
);
270 void SearchJobWidget::copyTorrentDownloadLinks() const
272 copyField(SearchSortModel::DL_LINK
);
275 void SearchJobWidget::copyTorrentNames() const
277 copyField(SearchSortModel::NAME
);
280 void SearchJobWidget::copyField(const int column
) const
282 const QModelIndexList rows
{m_ui
->resultsBrowser
->selectionModel()->selectedRows()};
285 for (const QModelIndex
&rowIndex
: rows
)
287 const QString field
= m_proxyModel
->data(
288 m_proxyModel
->index(rowIndex
.row(), column
)).toString();
289 if (!field
.isEmpty())
294 QApplication::clipboard()->setText(list
.join(u
'\n'));
297 void SearchJobWidget::setStatus(Status value
)
299 if (m_status
== value
) return;
302 setStatusTip(statusText(value
));
303 emit
statusChanged();
306 void SearchJobWidget::downloadTorrent(const QModelIndex
&rowIndex
, const AddTorrentOption option
)
308 const QString torrentUrl
= m_proxyModel
->data(
309 m_proxyModel
->index(rowIndex
.row(), SearchSortModel::DL_LINK
)).toString();
310 const QString engineName
= m_proxyModel
->data(
311 m_proxyModel
->index(rowIndex
.row(), SearchSortModel::ENGINE_NAME
)).toString();
313 if (torrentUrl
.startsWith(u
"magnet:", Qt::CaseInsensitive
))
315 addTorrentToSession(torrentUrl
, option
);
319 SearchDownloadHandler
*downloadHandler
= m_searchHandler
->manager()->downloadTorrent(engineName
, torrentUrl
);
320 connect(downloadHandler
, &SearchDownloadHandler::downloadFinished
321 , this, [this, option
](const QString
&source
) { addTorrentToSession(source
, option
); });
322 connect(downloadHandler
, &SearchDownloadHandler::downloadFinished
, downloadHandler
, &SearchDownloadHandler::deleteLater
);
325 setRowVisited(rowIndex
.row());
328 void SearchJobWidget::addTorrentToSession(const QString
&source
, const AddTorrentOption option
)
330 app()->addTorrentManager()->addTorrent(source
, {}, option
);
333 void SearchJobWidget::updateResultsCount()
335 const int totalResults
= m_searchListModel
->rowCount();
336 const int filteredResults
= m_proxyModel
->rowCount();
337 m_ui
->resultsLbl
->setText(tr("Results (showing <i>%1</i> out of <i>%2</i>):", "i.e: Search results")
338 .arg(filteredResults
).arg(totalResults
));
340 m_noSearchResults
= (totalResults
== 0);
341 emit
resultsCountUpdated();
344 void SearchJobWidget::updateFilter()
346 using Utils::Misc::SizeUnit
;
348 m_proxyModel
->enableNameFilter(filteringMode() == NameFilteringMode::OnlyNames
);
349 // we update size and seeds filter parameters in the model even if they are disabled
350 m_proxyModel
->setSeedsFilter(m_ui
->minSeeds
->value(), m_ui
->maxSeeds
->value());
351 m_proxyModel
->setSizeFilter(
352 sizeInBytes(m_ui
->minSize
->value(), static_cast<SizeUnit
>(m_ui
->minSizeUnit
->currentIndex())),
353 sizeInBytes(m_ui
->maxSize
->value(), static_cast<SizeUnit
>(m_ui
->maxSizeUnit
->currentIndex())));
355 m_nameFilteringMode
= filteringMode();
357 m_proxyModel
->invalidate();
358 updateResultsCount();
361 void SearchJobWidget::fillFilterComboBoxes()
363 using Utils::Misc::SizeUnit
;
364 using Utils::Misc::unitString
;
366 QStringList unitStrings
;
367 unitStrings
.append(unitString(SizeUnit::Byte
));
368 unitStrings
.append(unitString(SizeUnit::KibiByte
));
369 unitStrings
.append(unitString(SizeUnit::MebiByte
));
370 unitStrings
.append(unitString(SizeUnit::GibiByte
));
371 unitStrings
.append(unitString(SizeUnit::TebiByte
));
372 unitStrings
.append(unitString(SizeUnit::PebiByte
));
373 unitStrings
.append(unitString(SizeUnit::ExbiByte
));
375 m_ui
->minSizeUnit
->clear();
376 m_ui
->maxSizeUnit
->clear();
377 m_ui
->minSizeUnit
->addItems(unitStrings
);
378 m_ui
->maxSizeUnit
->addItems(unitStrings
);
380 m_ui
->minSize
->setValue(0);
381 m_ui
->minSizeUnit
->setCurrentIndex(static_cast<int>(SizeUnit::MebiByte
));
383 m_ui
->maxSize
->setValue(-1);
384 m_ui
->maxSizeUnit
->setCurrentIndex(static_cast<int>(SizeUnit::GibiByte
));
386 m_ui
->filterMode
->clear();
388 m_ui
->filterMode
->addItem(tr("Torrent names only"), static_cast<int>(NameFilteringMode::OnlyNames
));
389 m_ui
->filterMode
->addItem(tr("Everywhere"), static_cast<int>(NameFilteringMode::Everywhere
));
391 const QVariant selectedMode
= static_cast<int>(m_nameFilteringMode
.get(NameFilteringMode::OnlyNames
));
392 const int index
= m_ui
->filterMode
->findData(selectedMode
);
393 m_ui
->filterMode
->setCurrentIndex((index
== -1) ? 0 : index
);
396 void SearchJobWidget::filterSearchResults(const QString
&name
)
398 const QString pattern
= (Preferences::instance()->getRegexAsFilteringPatternForSearchJob()
399 ? name
: Utils::String::wildcardToRegexPattern(name
));
400 m_proxyModel
->setFilterRegularExpression(QRegularExpression(pattern
, QRegularExpression::CaseInsensitiveOption
));
401 updateResultsCount();
404 void SearchJobWidget::showFilterContextMenu()
406 const Preferences
*pref
= Preferences::instance();
408 QMenu
*menu
= m_lineEditSearchResultsFilter
->createStandardContextMenu();
409 menu
->setAttribute(Qt::WA_DeleteOnClose
);
410 menu
->addSeparator();
412 QAction
*useRegexAct
= menu
->addAction(tr("Use regular expressions"));
413 useRegexAct
->setCheckable(true);
414 useRegexAct
->setChecked(pref
->getRegexAsFilteringPatternForSearchJob());
415 connect(useRegexAct
, &QAction::toggled
, pref
, &Preferences::setRegexAsFilteringPatternForSearchJob
);
416 connect(useRegexAct
, &QAction::toggled
, this, [this]() { filterSearchResults(m_lineEditSearchResultsFilter
->text()); });
418 menu
->popup(QCursor::pos());
421 void SearchJobWidget::contextMenuEvent(QContextMenuEvent
*event
)
423 auto *menu
= new QMenu(this);
424 menu
->setAttribute(Qt::WA_DeleteOnClose
);
426 menu
->addAction(UIThemeManager::instance()->getIcon(u
"download"_s
)
427 , tr("Open download window"), this, [this]() { downloadTorrents(AddTorrentOption::ShowDialog
); });
428 menu
->addAction(UIThemeManager::instance()->getIcon(u
"downloading"_s
, u
"download"_s
)
429 , tr("Download"), this, [this]() { downloadTorrents(AddTorrentOption::SkipDialog
); });
430 menu
->addSeparator();
431 menu
->addAction(UIThemeManager::instance()->getIcon(u
"application-url"_s
), tr("Open description page")
432 , this, &SearchJobWidget::openTorrentPages
);
434 QMenu
*copySubMenu
= menu
->addMenu(
435 UIThemeManager::instance()->getIcon(u
"edit-copy"_s
), tr("Copy"));
437 copySubMenu
->addAction(UIThemeManager::instance()->getIcon(u
"name"_s
, u
"edit-copy"_s
), tr("Name")
438 , this, &SearchJobWidget::copyTorrentNames
);
439 copySubMenu
->addAction(UIThemeManager::instance()->getIcon(u
"insert-link"_s
, u
"edit-copy"_s
), tr("Download link")
440 , this, &SearchJobWidget::copyTorrentDownloadLinks
);
441 copySubMenu
->addAction(UIThemeManager::instance()->getIcon(u
"application-url"_s
, u
"edit-copy"_s
), tr("Description page URL")
442 , this, &SearchJobWidget::copyTorrentURLs
);
444 menu
->popup(event
->globalPos());
447 QString
SearchJobWidget::statusText(SearchJobWidget::Status st
)
451 case Status::Ongoing
:
452 return tr("Searching...");
453 case Status::Finished
:
454 return tr("Search has finished");
455 case Status::Aborted
:
456 return tr("Search aborted");
458 return tr("An error occurred during search...");
459 case Status::NoResults
:
460 return tr("Search returned no results");
466 SearchJobWidget::NameFilteringMode
SearchJobWidget::filteringMode() const
468 return static_cast<NameFilteringMode
>(m_ui
->filterMode
->itemData(m_ui
->filterMode
->currentIndex()).toInt());
471 void SearchJobWidget::loadSettings()
473 header()->restoreState(Preferences::instance()->getSearchTabHeaderState());
476 void SearchJobWidget::saveSettings() const
478 Preferences::instance()->setSearchTabHeaderState(header()->saveState());
481 int SearchJobWidget::visibleColumnsCount() const
484 for (int i
= 0, iMax
= m_ui
->resultsBrowser
->header()->count(); i
< iMax
; ++i
)
486 if (!m_ui
->resultsBrowser
->isColumnHidden(i
))
493 void SearchJobWidget::displayColumnHeaderMenu()
495 auto *menu
= new QMenu(this);
496 menu
->setAttribute(Qt::WA_DeleteOnClose
);
497 menu
->setTitle(tr("Column visibility"));
498 menu
->setToolTipsVisible(true);
500 for (int i
= 0; i
< SearchSortModel::DL_LINK
; ++i
)
502 const auto columnName
= m_searchListModel
->headerData(i
, Qt::Horizontal
, Qt::DisplayRole
).toString();
503 QAction
*action
= menu
->addAction(columnName
, this, [this, i
](const bool checked
)
505 if (!checked
&& (visibleColumnsCount() <= 1))
508 m_ui
->resultsBrowser
->setColumnHidden(i
, !checked
);
510 if (checked
&& (m_ui
->resultsBrowser
->columnWidth(i
) <= 5))
511 m_ui
->resultsBrowser
->resizeColumnToContents(i
);
515 action
->setCheckable(true);
516 action
->setChecked(!m_ui
->resultsBrowser
->isColumnHidden(i
));
519 menu
->addSeparator();
520 QAction
*resizeAction
= menu
->addAction(tr("Resize columns"), this, [this]()
522 for (int i
= 0, count
= m_ui
->resultsBrowser
->header()->count(); i
< count
; ++i
)
524 if (!m_ui
->resultsBrowser
->isColumnHidden(i
))
525 m_ui
->resultsBrowser
->resizeColumnToContents(i
);
529 resizeAction
->setToolTip(tr("Resize all non-hidden columns to the size of their contents"));
531 menu
->popup(QCursor::pos());
534 void SearchJobWidget::searchFinished(bool cancelled
)
537 setStatus(Status::Aborted
);
538 else if (m_noSearchResults
)
539 setStatus(Status::NoResults
);
541 setStatus(Status::Finished
);
544 void SearchJobWidget::searchFailed()
546 setStatus(Status::Error
);
549 void SearchJobWidget::appendSearchResults(const QList
<SearchResult
> &results
)
551 for (const SearchResult
&result
: results
)
553 // Add item to search result list
554 int row
= m_searchListModel
->rowCount();
555 m_searchListModel
->insertRow(row
);
557 const auto setModelData
= [this, row
] (const int column
, const QString
&displayData
558 , const QVariant
&underlyingData
, const Qt::Alignment textAlignmentData
= {})
560 const QMap
<int, QVariant
> data
=
562 {Qt::DisplayRole
, displayData
},
563 {SearchSortModel::UnderlyingDataRole
, underlyingData
},
564 {Qt::TextAlignmentRole
, QVariant
{textAlignmentData
}}
566 m_searchListModel
->setItemData(m_searchListModel
->index(row
, column
), data
);
569 setModelData(SearchSortModel::NAME
, result
.fileName
, result
.fileName
);
570 setModelData(SearchSortModel::DL_LINK
, result
.fileUrl
, result
.fileUrl
);
571 setModelData(SearchSortModel::ENGINE_NAME
, result
.engineName
, result
.engineName
);
572 setModelData(SearchSortModel::ENGINE_URL
, result
.siteUrl
, result
.siteUrl
);
573 setModelData(SearchSortModel::DESC_LINK
, result
.descrLink
, result
.descrLink
);
574 setModelData(SearchSortModel::SIZE
, Utils::Misc::friendlyUnit(result
.fileSize
), result
.fileSize
, (Qt::AlignRight
| Qt::AlignVCenter
));
575 setModelData(SearchSortModel::SEEDS
, QString::number(result
.nbSeeders
), result
.nbSeeders
, (Qt::AlignRight
| Qt::AlignVCenter
));
576 setModelData(SearchSortModel::LEECHES
, QString::number(result
.nbLeechers
), result
.nbLeechers
, (Qt::AlignRight
| Qt::AlignVCenter
));
577 setModelData(SearchSortModel::PUB_DATE
, QLocale().toString(result
.pubDate
.toLocalTime(), QLocale::ShortFormat
), result
.pubDate
);
580 updateResultsCount();
583 void SearchJobWidget::keyPressEvent(QKeyEvent
*event
)
585 switch (event
->key())
592 QWidget::keyPressEvent(event
);