Implement search filters in the proxy model. Partially closes #972

This commit is contained in:
Eugene Shalygin 2015-09-23 22:20:22 +02:00 committed by Eugene Shalygin
parent 93d8cadaaf
commit 1336cb7a61
16 changed files with 704 additions and 133 deletions

View file

@ -86,7 +86,9 @@ static struct { const char *source; const char *comment; } units[] = {
QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)")
QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
};
#ifndef DISABLE_GUI
@ -318,30 +320,58 @@ QString Utils::Misc::pythonVersionComplete() {
return version;
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
QString Utils::Misc::unitString(Utils::Misc::SizeUnit unit)
{
return QCoreApplication::translate("misc",
units[static_cast<int>(unit)].source, units[static_cast<int>(unit)].comment);
}
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
// value must be given in bytes
// to send numbers instead of strings with suffixes
QString Utils::Misc::friendlyUnit(qreal val, bool is_speed)
bool Utils::Misc::friendlyUnit(qint64 sizeInBytes, qreal &val, Utils::Misc::SizeUnit &unit)
{
if (val < 0)
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
if (sizeInBytes < 0) return false;
int i = 0;
while(val >= 1024. && i < 4) {
val /= 1024.;
qreal rawVal = static_cast<qreal>(sizeInBytes);
while ((rawVal >= 1024.) && (i <= static_cast<int>(SizeUnit::ExbiByte))) {
rawVal /= 1024.;
++i;
}
val = rawVal;
unit = static_cast<SizeUnit>(i);
return true;
}
QString Utils::Misc::friendlyUnit(qint64 bytesValue, bool isSpeed)
{
SizeUnit unit;
qreal friendlyVal;
if (!friendlyUnit(bytesValue, friendlyVal, unit)) {
return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
}
QString ret;
if (i == 0)
ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment);
if (unit == SizeUnit::Byte)
ret = QString::number(bytesValue) + " " + unitString(unit);
else
ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment);
if (is_speed)
ret = Utils::String::fromDouble(friendlyVal, 1) + " " + unitString(unit);
if (isSpeed)
ret += QCoreApplication::translate("misc", "/s", "per second");
return ret;
}
qlonglong Utils::Misc::sizeInBytes(qreal size, Utils::Misc::SizeUnit unit)
{
for (int i = 0; i < static_cast<int>(unit); ++i) {
size *= 1024;
}
return size;
}
bool Utils::Misc::isPreviewable(const QString& extension)
{
static QSet<QString> multimedia_extensions;

View file

@ -48,6 +48,22 @@ namespace Utils
{
namespace Misc
{
// use binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
enum class SizeUnit
{
Byte, // 1024^0,
KibiByte, // 1024^1,
MebiByte, // 1024^2,
GibiByte, // 1024^3,
TebiByte, // 1024^4,
PebiByte, // 1024^5,
ExbiByte // 1024^6,
// int64 is used for sizes and thus the next units can not be handled
// ZebiByte, // 1024^7,
// YobiByte, // 1024^8
};
QString parseHtmlLinks(const QString &raw_text);
bool isUrl(const QString &s);
@ -64,11 +80,15 @@ namespace Utils
int pythonVersion();
QString pythonExecutable();
QString pythonVersionComplete();
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
// use Binary prefix standards from IEC 60027-2
// see http://en.wikipedia.org/wiki/Kilobyte
QString unitString(SizeUnit unit);
// return best user friendly storage unit (B, KiB, MiB, GiB, TiB)
// value must be given in bytes
QString friendlyUnit(qreal val, bool is_speed = false);
bool friendlyUnit(qint64 sizeInBytes, qreal& val, SizeUnit& unit);
QString friendlyUnit(qint64 bytesValue, bool isSpeed = false);
qint64 sizeInBytes(qreal size, SizeUnit unit);
bool isPreviewable(const QString& extension);
// Take a number of seconds and return an user-friendly

View file

@ -113,6 +113,7 @@ FORMS += \
$$PWD/torrentcreatordlg.ui \
$$PWD/search/searchwidget.ui \
$$PWD/search/pluginselectdlg.ui \
$$PWD/search/pluginsourcedlg.ui
$$PWD/search/pluginsourcedlg.ui \
$$PWD/search/searchtab.ui
RESOURCES += $$PWD/about.qrc

View file

@ -56,7 +56,7 @@ void SearchListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown"));
break;
case SearchSortModel::LEECHS:
case SearchSortModel::LEECHES:
QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, option.rect, (index.data().toLongLong() >= 0) ? index.data().toString() : tr("Unknown"));
break;

View file

@ -29,26 +29,136 @@
#include "searchsortmodel.h"
SearchSortModel::SearchSortModel(QObject *parent)
: QSortFilterProxyModel(parent)
: base(parent)
, m_isNameFilterEnabled(false)
, m_minSeeds(0)
, m_maxSeeds(-1)
, m_minLeeches(0)
, m_maxLeeches(-1)
, m_minSize(0)
, m_maxSize(-1)
{
}
void SearchSortModel::enableNameFilter(bool enable)
{
m_isNameFilterEnabled = enable;
}
void SearchSortModel::setNameFilter(const QString &searchTerm)
{
m_searchTerm = searchTerm;
if (searchTerm.length() > 2
&& searchTerm.startsWith(QLatin1Char('"')) && searchTerm.endsWith(QLatin1Char('"'))) {
m_searchTermWords = QStringList(m_searchTerm.mid(1, m_searchTerm.length() - 2));
}
else {
m_searchTermWords = searchTerm.split(QLatin1Char(' '), QString::SkipEmptyParts);
}
}
void SearchSortModel::setSizeFilter(qint64 minSize, qint64 maxSize)
{
m_minSize = std::max(static_cast<qint64>(0), minSize);
m_maxSize = std::max(static_cast<qint64>(-1), maxSize);
}
void SearchSortModel::setSeedsFilter(int minSeeds, int maxSeeds)
{
m_minSeeds = std::max(0, minSeeds);
m_maxSeeds = std::max(-1, maxSeeds);
}
void SearchSortModel::setLeechesFilter(int minLeeches, int maxLeeches)
{
m_minLeeches = std::max(0, minLeeches);
m_maxLeeches = std::max(-1, maxLeeches);
}
bool SearchSortModel::isNameFilterEnabled() const
{
return m_isNameFilterEnabled;
}
QString SearchSortModel::searchTerm() const
{
return m_searchTerm;
}
int SearchSortModel::minSeeds() const
{
return m_minSeeds;
}
int SearchSortModel::maxSeeds() const
{
return m_maxSeeds;
}
qint64 SearchSortModel::minSize() const
{
return m_minSize;
}
qint64 SearchSortModel::maxSize() const
{
return m_maxSize;
}
bool SearchSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if ((sortColumn() == NAME) || (sortColumn() == ENGINE_URL)) {
QVariant vL = sourceModel()->data(left);
QVariant vR = sourceModel()->data(right);
if (!(vL.isValid() && vR.isValid()))
return QSortFilterProxyModel::lessThan(left, right);
return base::lessThan(left, right);
Q_ASSERT(vL.isValid());
Q_ASSERT(vR.isValid());
bool res = false;
if (Utils::String::naturalSort(vL.toString(), vR.toString(), res))
return res;
return QSortFilterProxyModel::lessThan(left, right);
}
return QSortFilterProxyModel::lessThan(left, right);
return base::lessThan(left, right);
}
bool SearchSortModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
const QAbstractItemModel* const sourceModel = this->sourceModel();
if (m_isNameFilterEnabled && !m_searchTerm.isEmpty()) {
QString name = sourceModel->data(sourceModel->index(sourceRow, NAME, sourceParent)).toString();
for (const QString& word: m_searchTermWords) {
int i = name.indexOf(word, 0, Qt::CaseInsensitive);
if (i == -1) {
return false;
}
}
}
if (m_minSize > 0 || m_maxSize >= 0) {
qlonglong size = sourceModel->data(sourceModel->index(sourceRow, SIZE, sourceParent)).toLongLong();
if ((m_minSize > 0 && size < m_minSize)
|| (m_maxSize > 0 && size > m_maxSize)) {
return false;
}
}
if (m_minSeeds > 0 || m_maxSeeds >= 0) {
int seeds = sourceModel->data(sourceModel->index(sourceRow, SEEDS, sourceParent)).toInt();
if ((m_minSeeds > 0 && seeds < m_minSeeds)
|| (m_maxSeeds > 0 && seeds > m_maxSeeds)) {
return false;
}
}
if (m_minLeeches > 0 || m_maxLeeches >= 0) {
int leeches = sourceModel->data(sourceModel->index(sourceRow, LEECHES, sourceParent)).toInt();
if ((m_minLeeches > 0 && leeches < m_minLeeches)
|| (m_maxLeeches > 0 && leeches > m_maxLeeches)) {
return false;
}
}
return base::filterAcceptsRow(sourceRow, sourceParent);
}

View file

@ -30,17 +30,20 @@
#define SEARCHSORTMODEL_H
#include <QSortFilterProxyModel>
#include <QStringList>
#include "base/utils/string.h"
class SearchSortModel: public QSortFilterProxyModel
{
using base = QSortFilterProxyModel;
public:
enum SearchColumn
{
NAME,
SIZE,
SEEDS,
LEECHS,
LEECHES,
ENGINE_URL,
DL_LINK,
DESC_LINK,
@ -49,8 +52,45 @@ public:
explicit SearchSortModel(QObject *parent = 0);
void enableNameFilter(bool enabled);
void setNameFilter(const QString& searchTerm = QString());
//! \brief Sets parameters for filtering by size
//! \param minSize minimal size in bytes
//! \param maxSize maximal size in bytes, negative value to disable filtering
void setSizeFilter(qint64 minSize, qint64 maxSize);
//! \brief Sets parameters for filtering by seeds number
//! \param minSeeds minimal number of seeders
//! \param maxSeeds maximal number of seeders, negative value to disable filtering
void setSeedsFilter(int minSeeds, int maxSeeds);
//! \brief Sets parameters for filtering by leeches number
//! \param minLeeches minimal number of leechers
//! \param maxLeeches maximal number of leechers, negative value to disable filtering
void setLeechesFilter(int minLeeches, int maxLeeches);
bool isNameFilterEnabled() const;
QString searchTerm() const;
int minSeeds() const;
int maxSeeds() const;
qint64 minSize() const;
qint64 maxSize() const;
protected:
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
bool m_isNameFilterEnabled;
QString m_searchTerm;
QStringList m_searchTermWords;
int m_minSeeds, m_maxSeeds;
int m_minLeeches, m_maxLeeches;
qint64 m_minSize, m_maxSize;
};
#endif // SEARCHSORTMODEL_H

View file

@ -29,6 +29,7 @@
*/
#include <QDir>
#include <QMetaEnum>
#include <QTreeView>
#include <QStandardItemModel>
#include <QHeaderView>
@ -41,16 +42,28 @@
#include "base/utils/misc.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "guiiconprovider.h"
#include "searchsortmodel.h"
#include "searchlistdelegate.h"
#include "searchwidget.h"
#include "searchtab.h"
namespace
{
#define SETTINGS_KEY(name) "Search/" name
const QString KEY_FILTER_MODE_SETTING_NAME = SETTINGS_KEY("FilteringMode");
}
SearchTab::SearchTab(SearchWidget *parent)
: QWidget(parent)
, m_parent(parent)
{
m_box = new QVBoxLayout(this);
setupUi(this);
retranslateUi(this);
m_box = static_cast<QVBoxLayout*>(this->layout());
m_resultsLbl = new QLabel(this);
m_resultsBrowser = new QTreeView(this);
#ifdef QBT_USES_QT5
@ -65,14 +78,12 @@ SearchTab::SearchTab(SearchWidget *parent)
m_box->addWidget(m_resultsLbl);
m_box->addWidget(m_resultsBrowser);
setLayout(m_box);
// Set Search results list model
m_searchListModel = new QStandardItemModel(0, SearchSortModel::NB_SEARCH_COLUMNS, this);
m_searchListModel->setHeaderData(SearchSortModel::NAME, Qt::Horizontal, tr("Name", "i.e: file name"));
m_searchListModel->setHeaderData(SearchSortModel::SIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
m_searchListModel->setHeaderData(SearchSortModel::SEEDS, Qt::Horizontal, tr("Seeders", "i.e: Number of full sources"));
m_searchListModel->setHeaderData(SearchSortModel::LEECHS, Qt::Horizontal, tr("Leechers", "i.e: Number of partial sources"));
m_searchListModel->setHeaderData(SearchSortModel::LEECHES, Qt::Horizontal, tr("Leechers", "i.e: Number of partial sources"));
m_searchListModel->setHeaderData(SearchSortModel::ENGINE_URL, Qt::Horizontal, tr("Search engine"));
m_proxyModel = new SearchSortModel(this);
@ -99,6 +110,22 @@ SearchTab::SearchTab(SearchWidget *parent)
// Sort by Seeds
m_resultsBrowser->sortByColumn(SearchSortModel::SEEDS, Qt::DescendingOrder);
fillFilterComboBoxes();
updateFilter();
connect(filterMode, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
connect(minSeeds, SIGNAL(editingFinished()), this, SLOT(updateFilter()));
connect(minSeeds, SIGNAL(valueChanged(int)), this, SLOT(updateFilter()));
connect(maxSeeds, SIGNAL(editingFinished()), this, SLOT(updateFilter()));
connect(maxSeeds, SIGNAL(valueChanged(int)), this, SLOT(updateFilter()));
connect(minSize, SIGNAL(editingFinished()), this, SLOT(updateFilter()));
connect(minSize, SIGNAL(valueChanged(double)), this, SLOT(updateFilter()));
connect(maxSize, SIGNAL(editingFinished()), this, SLOT(updateFilter()));
connect(maxSize, SIGNAL(valueChanged(double)), this, SLOT(updateFilter()));
connect(minSizeUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
connect(maxSizeUnit, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFilter()));
}
void SearchTab::downloadSelectedItem(const QModelIndex &index)
@ -123,24 +150,18 @@ bool SearchTab::loadColWidthResultsList()
return false;
unsigned int listSize = widthList.size();
for (unsigned int i = 0; i < listSize; ++i) {
for (unsigned int i = 0; i < listSize; ++i)
m_resultsBrowser->header()->resizeSection(i, widthList.at(i).toInt());
}
return true;
}
QLabel* SearchTab::getCurrentLabel() const
{
return m_resultsLbl;
}
QTreeView* SearchTab::getCurrentTreeView() const
{
return m_resultsBrowser;
}
QSortFilterProxyModel* SearchTab::getCurrentSearchListProxy() const
SearchSortModel* SearchTab::getCurrentSearchListProxy() const
{
return m_proxyModel;
}
@ -154,19 +175,128 @@ QStandardItemModel* SearchTab::getCurrentSearchListModel() const
void SearchTab::setRowColor(int row, QString color)
{
m_proxyModel->setDynamicSortFilter(false);
for (int i = 0; i < m_proxyModel->columnCount(); ++i) {
for (int i = 0; i < m_proxyModel->columnCount(); ++i)
m_proxyModel->setData(m_proxyModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
}
m_proxyModel->setDynamicSortFilter(true);
}
QString SearchTab::status() const
SearchTab::Status SearchTab::status() const
{
return m_status;
}
void SearchTab::setStatus(const QString &value)
void SearchTab::setStatus(Status value)
{
m_status = value;
setStatusTip(statusText(value));
const int thisTabIndex = m_parent->searchTabs()->indexOf(this);
m_parent->searchTabs()->setTabToolTip(thisTabIndex, statusTip());
m_parent->searchTabs()->setTabIcon(thisTabIndex, GuiIconProvider::instance()->getIcon(statusIconName(value)));
}
void SearchTab::updateResultsCount()
{
const int totalResults = getCurrentSearchListModel() ? getCurrentSearchListModel()->rowCount(QModelIndex()) : 0;
const int filteredResults = getCurrentSearchListProxy() ? getCurrentSearchListProxy()->rowCount(QModelIndex()) : totalResults;
m_resultsLbl->setText(tr("Results (showing <i>%1</i> out of <i>%2</i>):", "i.e: Search results")
.arg(filteredResults).arg(totalResults));
}
void SearchTab::updateFilter()
{
using Utils::Misc::SizeUnit;
SearchSortModel* filterModel = getCurrentSearchListProxy();
filterModel->enableNameFilter(filteringMode() == OnlyNames);
// we update size and seeds filter parameters in the model even if they are disabled
// because we need to read them from the model when search tabs switch
filterModel->setSeedsFilter(minSeeds->value(), maxSeeds->value());
filterModel->setSizeFilter(
sizeInBytes(minSize->value(), static_cast<SizeUnit>(minSizeUnit->currentIndex())),
sizeInBytes(maxSize->value(), static_cast<SizeUnit>(maxSizeUnit->currentIndex())));
SettingsStorage::instance()->storeValue(KEY_FILTER_MODE_SETTING_NAME,
filterMode->itemData(filterMode->currentIndex()));
filterModel->invalidate();
updateResultsCount();
}
void SearchTab::fillFilterComboBoxes()
{
using Utils::Misc::SizeUnit;
QStringList unitStrings;
unitStrings.append(unitString(SizeUnit::Byte));
unitStrings.append(unitString(SizeUnit::KibiByte));
unitStrings.append(unitString(SizeUnit::MebiByte));
unitStrings.append(unitString(SizeUnit::GibiByte));
unitStrings.append(unitString(SizeUnit::TebiByte));
unitStrings.append(unitString(SizeUnit::PebiByte));
unitStrings.append(unitString(SizeUnit::ExbiByte));
minSizeUnit->clear();
maxSizeUnit->clear();
minSizeUnit->addItems(unitStrings);
maxSizeUnit->addItems(unitStrings);
minSize->setValue(0);
minSizeUnit->setCurrentIndex(static_cast<int>(SizeUnit::MebiByte));
maxSize->setValue(-1);
maxSizeUnit->setCurrentIndex(static_cast<int>(SizeUnit::TebiByte));
filterMode->clear();
QMetaEnum nameFilteringModeEnum =
this->metaObject()->enumerator(this->metaObject()->indexOfEnumerator("NameFilteringMode"));
filterMode->addItem(tr("Torrent names only"), nameFilteringModeEnum.valueToKey(OnlyNames));
filterMode->addItem(tr("Everywhere"), nameFilteringModeEnum.valueToKey(Everywhere));
QVariant selectedMode = SettingsStorage::instance()->loadValue(
KEY_FILTER_MODE_SETTING_NAME, nameFilteringModeEnum.valueToKey(OnlyNames));
int index = filterMode->findData(selectedMode);
filterMode->setCurrentIndex(index == -1 ? 0 : index);
}
QString SearchTab::statusText(SearchTab::Status st)
{
switch (st) {
case Status::Ongoing:
return tr("Searching...");
case Status::Finished:
return tr("Search has finished");
case Status::Aborted:
return tr("Search aborted");
case Status::Error:
return tr("An error occurred during search...");
case Status::NoResults:
return tr("Search returned no results");
default:
return QString();
}
}
QString SearchTab::statusIconName(SearchTab::Status st)
{
switch (st) {
case Status::Ongoing:
return QLatin1String("task-ongoing");
case Status::Finished:
return QLatin1String("task-complete");
case Status::Aborted:
return QLatin1String("task-reject");
case Status::Error:
return QLatin1String("task-attention");
case Status::NoResults:
return QLatin1String("task-attention");
default:
return QString();
}
}
SearchTab::NameFilteringMode SearchTab::filteringMode() const
{
QMetaEnum metaEnum =
this->metaObject()->enumerator(this->metaObject()->indexOfEnumerator("NameFilteringMode"));
return static_cast<NameFilteringMode>(metaEnum.keyToValue(filterMode->itemData(filterMode->currentIndex()).toByteArray()));
}

View file

@ -31,42 +31,71 @@
#ifndef SEARCHTAB_H
#define SEARCHTAB_H
#include <QWidget>
#include <QVariant> // I don't know why <QMetaType> is not enought for Qt's 4.8.7 moc
#include "ui_searchtab.h"
#define ENGINE_URL_COLUMN 4
#define URL_COLUMN 5
class QLabel;
class QModelIndex;
class QTreeView;
class QHeaderView;
class QStandardItemModel;
class QSortFilterProxyModel;
class QModelIndex;
class QVBoxLayout;
class SearchSortModel;
class SearchListDelegate;
class SearchWidget;
class SearchTab: public QWidget
class SearchTab: public QWidget, private Ui::SearchTab
{
Q_OBJECT
public:
explicit SearchTab(SearchWidget *m_parent);
QLabel* getCurrentLabel() const;
enum NameFilteringMode
{
Everywhere,
OnlyNames
};
Q_ENUMS(NameFilteringMode)
explicit SearchTab(SearchWidget *parent);
QStandardItemModel* getCurrentSearchListModel() const;
QSortFilterProxyModel* getCurrentSearchListProxy() const;
SearchSortModel* getCurrentSearchListProxy() const;
QTreeView* getCurrentTreeView() const;
QHeaderView* header() const;
QString status() const;
bool loadColWidthResultsList();
void setRowColor(int row, QString color);
void setStatus(const QString &value);
enum class Status
{
Ongoing,
Finished,
Error,
Aborted,
NoResults
};
void setStatus(Status value);
Status status() const;
void updateResultsCount();
private slots:
void downloadSelectedItem(const QModelIndex &index);
void updateFilter();
private:
void fillFilterComboBoxes();
NameFilteringMode filteringMode() const;
static QString statusText(Status st);
static QString statusIconName(Status st);
QVBoxLayout *m_box;
QLabel *m_resultsLbl;
QTreeView *m_resultsBrowser;
@ -74,8 +103,9 @@ private:
SearchSortModel *m_proxyModel;
SearchListDelegate *m_searchDelegate;
SearchWidget *m_parent;
QString m_status;
Status m_status;
};
#endif // SEARCHTAB_H
Q_DECLARE_METATYPE(SearchTab::NameFilteringMode)
#endif // SEARCHTAB_H

240
src/gui/search/searchtab.ui Normal file
View file

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SearchTab</class>
<widget class="QWidget" name="SearchTab">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1216</width>
<height>364</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Search in:</string>
</property>
<property name="buddy">
<cstring>filterMode</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="filterMode">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Some search engines search in torrent description and in torrent file names too. Whether such results will be shown in the list below is controlled by this mode.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Everywhere &lt;/span&gt;disables filtering and shows everyhing returned by the search engines.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Torrent names only&lt;/span&gt; shows only torrents whose names match the search query.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>12</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set minimal and maximal allowed number of seeders&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Seeds:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="minSeeds">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimal number of seeds&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>to</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="maxSeeds">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximal number of seeds&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="specialValueText">
<string>∞</string>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>-1</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>12</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set minimal and maximal allowed size of a torrent&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QDoubleSpinBox" name="minSize">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimal torrent size&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="minSizeUnit">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>to</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QDoubleSpinBox" name="maxSize">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximal torrent size&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="specialValueText">
<string>∞</string>
</property>
<property name="minimum">
<double>-1.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="maxSizeUnit">
<property name="currentIndex">
<number>-1</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -45,6 +45,7 @@
#include <QClipboard>
#include <QProcess>
#include <QDebug>
#include <QTextStream>
#include <iostream>
#ifdef Q_OS_WIN
@ -74,7 +75,6 @@ SearchWidget::SearchWidget(MainWindow *mainWindow)
, m_mainWindow(mainWindow)
, m_isNewQueryString(false)
, m_noSearchResults(true)
, m_nbSearchResults(0)
{
setupUi(this);
@ -82,6 +82,26 @@ SearchWidget::SearchWidget(MainWindow *mainWindow)
searchBarLayout->insertWidget(0, m_searchPattern);
connect(m_searchPattern, SIGNAL(returnPressed()), searchButton, SLOT(click()));
QString searchPatternHint;
QTextStream stream(&searchPatternHint, QIODevice::WriteOnly);
stream << "<html><head/><body><p>"
<< tr("A phrase to search for.") << "<br>"
<< tr("Spaces in a search term may be protected by double quotes.")
<< "</p><p>"
<< tr("Example:", "Search phrase example")
<< "<br>"
<< tr("<b>foo bar</b>: search for <b>foo</b> and <b>bar</b>",
"Search phrase example, illustrates quotes usage, a pair of "
"space delimited words, individal words are highlighted")
<< "<br>"
<< tr("<b>&quot;foo bar&quot;</b>: search for <b>foo bar</b>",
"Search phrase example, illustrates quotes usage, double quoted"
"pair of space delimited words, the whole pair is highlighted")
<< "</p></body></html>" << flush;
m_searchPattern->setToolTip(searchPatternHint);
// Icons
searchButton->setIcon(GuiIconProvider::instance()->getIcon("edit-find"));
downloadButton->setIcon(GuiIconProvider::instance()->getIcon("download"));
@ -158,7 +178,6 @@ void SearchWidget::tab_changed(int t)
goToDescBtn->setEnabled(false);
copyURLBtn->setEnabled(false);
}
searchStatus->setText(m_currentSearchTab->status());
}
}
@ -187,6 +206,11 @@ void SearchWidget::giveFocusToSearchInput()
m_searchPattern->setFocus();
}
QTabWidget *SearchWidget::searchTabs() const
{
return tabWidget;
}
// Function called when we click on search button
void SearchWidget::on_searchButton_clicked()
{
@ -222,6 +246,7 @@ void SearchWidget::on_searchButton_clicked()
tabName.replace(QRegExp("&{1}"), "&&");
tabWidget->addTab(m_currentSearchTab, tabName);
tabWidget->setCurrentWidget(m_currentSearchTab);
m_currentSearchTab->getCurrentSearchListProxy()->setNameFilter(pattern);
QStringList plugins;
if (selectedPlugin() == "all") plugins = m_searchEngine->allPlugins();
@ -233,10 +258,9 @@ void SearchWidget::on_searchButton_clicked()
// Update SearchEngine widgets
m_noSearchResults = true;
m_nbSearchResults = 0;
// Changing the text of the current label
m_activeSearchTab->getCurrentLabel()->setText(tr("Results <i>(%1)</i>:", "i.e: Search results").arg(0));
m_activeSearchTab->updateResultsCount();
// Launch search
m_searchEngine->startSearch(pattern, selectedCategory(), plugins);
@ -268,9 +292,7 @@ void SearchWidget::downloadTorrent(QString url)
void SearchWidget::searchStarted()
{
// Update SearchEngine widgets
m_activeSearchTab->setStatus(tr("Searching..."));
searchStatus->setText(m_currentSearchTab->status());
searchStatus->repaint();
m_activeSearchTab->setStatus(SearchTab::Status::Ongoing);
searchButton->setText(tr("Stop"));
}
@ -285,13 +307,12 @@ void SearchWidget::searchFinished(bool cancelled)
if (m_activeSearchTab.isNull()) return; // The active tab was closed
if (cancelled)
m_activeSearchTab->setStatus(tr("Search aborted"));
m_activeSearchTab->setStatus(SearchTab::Status::Aborted);
else if (m_noSearchResults)
m_activeSearchTab->setStatus(tr("Search returned no results"));
m_activeSearchTab->setStatus(SearchTab::Status::NoResults);
else
m_activeSearchTab->setStatus(tr("Search has finished"));
m_activeSearchTab->setStatus(SearchTab::Status::Finished);
searchStatus->setText(m_currentSearchTab->status());
m_activeSearchTab = 0;
searchButton->setText(tr("Search"));
}
@ -304,9 +325,9 @@ void SearchWidget::searchFailed()
if (m_activeSearchTab.isNull()) return; // The active tab was closed
#ifdef Q_OS_WIN
m_activeSearchTab->setStatus(tr("Search aborted"));
m_activeSearchTab->setStatus(SearchTab::Status::Aborted);
#else
m_activeSearchTab->setStatus(tr("An error occurred during search..."));
m_activeSearchTab->setStatus(SearchTab::Status::Error);
#endif
}
@ -331,14 +352,13 @@ void SearchWidget::appendSearchResults(const QList<SearchResult> &results)
curModel->setData(curModel->index(row, SearchSortModel::NAME), result.fileName); // Name
curModel->setData(curModel->index(row, SearchSortModel::SIZE), result.fileSize); // Size
curModel->setData(curModel->index(row, SearchSortModel::SEEDS), result.nbSeeders); // Seeders
curModel->setData(curModel->index(row, SearchSortModel::LEECHS), result.nbLeechers); // Leechers
curModel->setData(curModel->index(row, SearchSortModel::LEECHES), result.nbLeechers); // Leechers
curModel->setData(curModel->index(row, SearchSortModel::ENGINE_URL), result.siteUrl); // Search site URL
curModel->setData(curModel->index(row, SearchSortModel::DESC_LINK), result.descrLink); // Description Link
}
m_noSearchResults = false;
m_nbSearchResults += results.size();
m_activeSearchTab->getCurrentLabel()->setText(tr("Results <i>(%1)</i>:", "i.e: Search results").arg(m_nbSearchResults));
m_activeSearchTab->updateResultsCount();
// Enable clear & download buttons
downloadButton->setEnabled(true);
@ -361,7 +381,6 @@ void SearchWidget::closeTab(int index)
if (!m_allTabs.size()) {
downloadButton->setEnabled(false);
goToDescBtn->setEnabled(false);
searchStatus->setText(tr("Stopped"));
copyURLBtn->setEnabled(false);
}
}

View file

@ -55,6 +55,8 @@ public:
void downloadTorrent(QString url);
void giveFocusToSearchInput();
QTabWidget* searchTabs() const;
private slots:
// Search slots
void tab_changed(int); //to prevent the use of the download button when the tab is empty
@ -89,7 +91,6 @@ private:
bool m_isNewQueryString;
bool m_noSearchResults;
QByteArray m_searchResultLineTruncated;
unsigned long m_nbSearchResults;
};
#endif // SEARCHWIDGET_H

View file

@ -6,14 +6,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>820</width>
<height>453</height>
<width>1382</width>
<height>669</height>
</rect>
</property>
<property name="windowTitle">
<string>Search</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="searchBarLayout">
<item>
@ -32,67 +32,14 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="status_lbl">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>35</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="searchStatus">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>35</height>
</size>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Stopped</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>188</width>
<height>21</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget"/>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout">

View file

@ -306,6 +306,9 @@
<file>icons/oxygen/services.png</file>
<file>icons/oxygen/tab-close.png</file>
<file>icons/oxygen/task-attention.png</file>
<file>icons/oxygen/task-complete.png</file>
<file>icons/oxygen/task-ongoing.png</file>
<file>icons/oxygen/task-reject.png</file>
<file>icons/oxygen/text-plain.png</file>
<file>icons/oxygen/tools-report-bug.png</file>
<file>icons/oxygen/unavailable.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B