Merge branch 'master' into multiple_apis

Conflicts:
	CMakeLists.txt
	src/libdanbooru/danboorupool.cpp
	src/libdanbooru/danboorupost.cpp
	src/libdanbooru/danboorutag.cpp
	src/libdanbooru/utils.cpp
This commit is contained in:
Luca Beltrame 2019-06-08 15:20:17 +02:00
commit 3409fcecf9
Signed by: einar
GPG key ID: 8DF631FD021DB0C5
13 changed files with 373 additions and 141 deletions

28
.drone.yml Normal file
View file

@ -0,0 +1,28 @@
kind: pipeline
name: default
steps:
- name: build
image: einar/kde
pull: if-not-exists
commands:
- mkdir build
- cmake . -d build
- make -C build -j2
when:
event:
- push
- name: pack
image: einar/kde
pull: if-not-exists
commands:
- mkdir build
- cmake . -d build
- make dist -C build
when:
event:
- tag

View file

@ -7,7 +7,20 @@ set(danbooru_client_VERSION_PATCH 0)
set (danbooru_VERSION ${danbooru_client_VERSION_MAJOR}.${danbooru_client_VERSION_MINOR}.${danbooru_client_VERSION_PATCH}) set (danbooru_VERSION ${danbooru_client_VERSION_MAJOR}.${danbooru_client_VERSION_MINOR}.${danbooru_client_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION_MAJOR "${danbooru_client_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${danbooru_client_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${danbooru_client_VERSION_PATCH}")
set(CPACK_SOURCE_GENERATOR "TXZ")
set(CPACK_SOURCE_PACKAGE_FILE_NAME
"${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
set(CPACK_SOURCE_IGNORE_FILES
"/build/;/.bzr/;~$;/.git/;${CPACK_SOURCE_IGNORE_FILES}")
include(CPack)
add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
find_package (ECM REQUIRED NO_MODULE) find_package (ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
set(REQUIRED_QT_VERSION "5.4.0") set(REQUIRED_QT_VERSION "5.4.0")
@ -29,6 +42,7 @@ find_package(Qt5Test ${REQUIRED_QT_VERSION})
find_package(KF5 ${KF5_VERSION} REQUIRED find_package(KF5 ${KF5_VERSION} REQUIRED
IconThemes # Handling of icons IconThemes # Handling of icons
CoreAddons # Caches CoreAddons # Caches
TextWidgets # Spin boxes
GuiAddons # Image cache GuiAddons # Image cache
Completion # KComboBox Completion # KComboBox
XmlGui # User interface XmlGui # User interface
@ -37,23 +51,17 @@ find_package(KF5 ${KF5_VERSION} REQUIRED
Wallet # Password handling Wallet # Password handling
Declarative # QML Declarative # QML
I18n # i18n I18n # i18n
FileMetaData # Tagging
# DocTools # Disabled until manual's ready # DocTools # Disabled until manual's ready
) )
# Not a framework yet, hence separate
find_package(KF5 "5.6.0" COMPONENTS
FileMetaData)
include(ECMInstallIcons) include(ECMInstallIcons)
include(KDEInstallDirs) include(KDEInstallDirs)
include(KDECompilerSettings) include(KDECompilerSettings)
include(KDECMakeSettings) include(KDECMakeSettings)
include(FeatureSummary) include(FeatureSummary)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
set_package_properties(KF5FileMetaData PROPERTIES TYPE OPTIONAL PURPOSE "Required for file tagging")
set_package_properties(Qt5Test PROPERTIES TYPE OPTIONAL PURPOSE "Required to build tests") set_package_properties(Qt5Test PROPERTIES TYPE OPTIONAL PURPOSE "Required to build tests")
# add_subdirectory( doc ) # add_subdirectory( doc )

View file

@ -42,6 +42,7 @@ target_link_libraries(danbooru_client PUBLIC
Qt5::Qml Qt5::Qml
Qt5::QuickWidgets Qt5::QuickWidgets
KF5::CoreAddons KF5::CoreAddons
KF5::TextWidgets
KF5::IconThemes KF5::IconThemes
KF5::Completion KF5::Completion
KF5::I18n KF5::I18n

View file

@ -33,25 +33,25 @@ int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
QCoreApplication::setApplicationName(QLatin1String("danbooru-client")); QCoreApplication::setApplicationName(QStringLiteral("danbooru-client"));
QCoreApplication::setApplicationVersion(QLatin1String(DANBOORU_CLIENT_VERSION_STRING)); QCoreApplication::setApplicationVersion(QStringLiteral(DANBOORU_CLIENT_VERSION_STRING));
QCoreApplication::setOrganizationDomain(QLatin1String("dennogumi.org")); QCoreApplication::setOrganizationDomain(QStringLiteral("dennogumi.org"));
QApplication::setApplicationDisplayName(i18n("Danbooru Client")); QApplication::setApplicationDisplayName(i18n("Danbooru Client"));
KLocalizedString::setApplicationDomain("danbooru-client"); KLocalizedString::setApplicationDomain("danbooru-client");
KAboutData aboutData(I18N_NOOP(QLatin1String("danbooru-client")), KAboutData aboutData(I18N_NOOP(QLatin1String("danbooru-client")),
i18n("Danbooru Client"), i18n("Danbooru Client"),
QLatin1String(DANBOORU_CLIENT_VERSION_STRING), QStringLiteral(DANBOORU_CLIENT_VERSION_STRING),
i18n("KF5 based Danbooru client"), i18n("KF5 based Danbooru client"),
KAboutLicense::GPL_V3, KAboutLicense::GPL_V3,
i18n("(C) 2015 Luca Beltrame"), i18n("(C) 2015 Luca Beltrame"),
QString("Using libdanbooru version %1").arg(LIBDANBOORU_VERSION_STRING), QString("Using libdanbooru version %1").arg(LIBDANBOORU_VERSION_STRING),
QLatin1String("https://git.dennogumi.org/kde/danbooru-client"), QStringLiteral("https://git.dennogumi.org/kde/danbooru-client"),
QLatin1String("https://git.dennogumi.org/kde/danbooru-client") QStringLiteral("https://git.dennogumi.org/kde/danbooru-client")
); );
aboutData.addAuthor(i18n("Luca Beltrame"), i18n("Developer"), aboutData.addAuthor(i18n("Luca Beltrame"), i18n("Developer"),
QLatin1String("lbeltrame@kde.org") QStringLiteral("lbeltrame@kde.org")
); );
KAboutData::setApplicationData(aboutData); KAboutData::setApplicationData(aboutData);

View file

@ -38,14 +38,14 @@ const QMap< QUrl, QString > initBoardSalts()
QMap< QUrl, QString > boardSalts; QMap< QUrl, QString > boardSalts;
boardSalts.insert(QUrl("http://konachan.com"), boardSalts.insert(QUrl(QStringLiteral("http://konachan.com")),
QString("So-I-Heard-You-Like-Mupkids-?--%1--")); QStringLiteral("So-I-Heard-You-Like-Mupkids-?--%1--"));
boardSalts.insert(QUrl("http://konachan.net"), boardSalts.insert(QUrl(QStringLiteral("http://konachan.net")),
QString("So-I-Heard-You-Like-Mupkids-?--%1--")); QStringLiteral("So-I-Heard-You-Like-Mupkids-?--%1--"));
boardSalts.insert(QUrl("http://yande.re"), boardSalts.insert(QUrl(QStringLiteral("http://yande.re")),
QString("choujin-steiner--%1--")); QStringLiteral("choujin-steiner--%1--"));
boardSalts.insert(QUrl("http://danbooru.donmai.us"), boardSalts.insert(QUrl(QStringLiteral("http://danbooru.donmai.us")),
QString("choujin-steiner--%1--")); QStringLiteral("choujin-steiner--%1--"));
return boardSalts; return boardSalts;
} }
@ -61,7 +61,7 @@ DanbooruConnectWidget::DanbooruConnectWidget(QVector< QUrl > urlList,
setupUi(this); setupUi(this);
danbooruUrlComboBox->setFocus(); danbooruUrlComboBox->setFocus();
closeButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-close"))); closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
closeButton->setToolTip(i18n("Close dialog and discard changes")); closeButton->setToolTip(i18n("Close dialog and discard changes"));
userLineEdit->setClearButtonEnabled(true); userLineEdit->setClearButtonEnabled(true);
passwdLineEdit->setClearButtonEnabled(true); passwdLineEdit->setClearButtonEnabled(true);
@ -74,7 +74,7 @@ DanbooruConnectWidget::DanbooruConnectWidget(QVector< QUrl > urlList,
danbooruUrlComboBox->clear(); danbooruUrlComboBox->clear();
for (auto item : urlList) { Q_FOREACH(const auto& item, urlList) {
danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item); danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item);
} }
@ -128,20 +128,20 @@ void DanbooruConnectWidget::getWalletData()
return; return;
} }
m_username = valueMap[QLatin1String("username")]; m_username = valueMap[QStringLiteral("username")];
QString hashedPassword; QString hashedPassword;
// TODO: Handle the case where the "salt" is not known // TODO: Handle the case where the "salt" is not known
hashedPassword = boardSalts.value(key); hashedPassword = boardSalts.value(key);
hashedPassword = hashedPassword.arg(valueMap[QLatin1String("password")]); hashedPassword = hashedPassword.arg(valueMap[QStringLiteral("password")]);
hashedPassword = QCryptographicHash::hash(hashedPassword.toUtf8(), hashedPassword = QCryptographicHash::hash(hashedPassword.toUtf8(),
QCryptographicHash::Sha1).toHex(); QCryptographicHash::Sha1).toHex();
m_password = hashedPassword; m_password = hashedPassword;
userLineEdit->setText(m_username); userLineEdit->setText(m_username);
passwdLineEdit->setText(valueMap[QLatin1String("password")]); passwdLineEdit->setText(valueMap[QStringLiteral("password")]);
} }
} }
@ -162,7 +162,7 @@ void DanbooruConnectWidget::setBoards(const QVector<QUrl> &urlList)
danbooruUrlComboBox->clear(); danbooruUrlComboBox->clear();
for (auto item : urlList) { Q_FOREACH(const auto& item, urlList) {
danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item); danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item);
} }
@ -195,8 +195,8 @@ void DanbooruConnectWidget::accept()
if (m_wallet && !m_wallet->hasEntry(currentBoard)) { if (m_wallet && !m_wallet->hasEntry(currentBoard)) {
QMap<QString, QString> dataMap; QMap<QString, QString> dataMap;
dataMap.insert(QLatin1String("username"), m_username); dataMap.insert(QStringLiteral("username"), m_username);
dataMap.insert(QLatin1String("password"), passwdLineEdit->text()); dataMap.insert(QStringLiteral("password"), passwdLineEdit->text());
m_wallet->writeMap(m_boardUrl.url(), dataMap); m_wallet->writeMap(m_boardUrl.url(), dataMap);
} }

View file

@ -31,6 +31,12 @@ DanbooruSearchWidget::DanbooruSearchWidget(QWidget *parent): QWidget(parent)
tagLineEdit->setPlaceholderText(i18n("Type search tags.")); tagLineEdit->setPlaceholderText(i18n("Type search tags."));
tagLineEdit->setToolTip(i18n("Type search tags. An empty string searches all posts.")); tagLineEdit->setToolTip(i18n("Type search tags. An empty string searches all posts."));
widthSpinBox->setSuffix(ki18np(" pixel", " pixels"));
heightSpinBox->setSuffix(ki18np(" pixel", " pixels"));
widthSpinBox->setValue(0);
heightSpinBox->setValue(0);
connect(searchButton, &QPushButton::clicked, this, &DanbooruSearchWidget::accept); connect(searchButton, &QPushButton::clicked, this, &DanbooruSearchWidget::accept);
connect(tagLineEdit, &QLineEdit::returnPressed, this, &DanbooruSearchWidget::accept); connect(tagLineEdit, &QLineEdit::returnPressed, this, &DanbooruSearchWidget::accept);
@ -49,11 +55,24 @@ QStringList DanbooruSearchWidget::selectedTags() const
return m_tags; return m_tags;
} }
unsigned int DanbooruSearchWidget::selectedWidth() const
{
return m_width;
}
unsigned int DanbooruSearchWidget::selectedHeight() const
{
return m_height;
}
void DanbooruSearchWidget::accept() void DanbooruSearchWidget::accept()
{ {
m_tags = tagLineEdit->text().split(","); m_tags = tagLineEdit->text().split(QStringLiteral(","));
m_width = widthSpinBox->value();
m_height = heightSpinBox->value();
Q_EMIT(accepted()); Q_EMIT(accepted());
} }
} // namespace Danbooru } // namespace Danbooru

View file

@ -34,9 +34,13 @@ public:
explicit DanbooruSearchWidget(QWidget *parent = 0); explicit DanbooruSearchWidget(QWidget *parent = 0);
~DanbooruSearchWidget(); ~DanbooruSearchWidget();
QStringList selectedTags() const; QStringList selectedTags() const;
unsigned int selectedWidth() const;
unsigned int selectedHeight() const;
private: private:
QStringList m_tags; QStringList m_tags;
unsigned int m_width;
unsigned int m_height;
private Q_SLOTS: private Q_SLOTS:
void accept(); void accept();
@ -49,4 +53,4 @@ Q_SIGNALS:
} // namespace Danbooru } // namespace Danbooru
#endif #endif

View file

@ -46,12 +46,12 @@ namespace Danbooru
using KIO::StoredTransferJob; using KIO::StoredTransferJob;
using KIO::MultiGetJob; using KIO::MultiGetJob;
const QString DanbooruService::POST_URL = "post/index.json" ; const QString DanbooruService::POST_URL = QStringLiteral("post/index.json");
const QString DanbooruService::TAG_URL = "tag/index.xml"; const QString DanbooruService::TAG_URL = QStringLiteral("tag/index.xml");
const QString DanbooruService::POOL_URL = "pool/index.json"; const QString DanbooruService::POOL_URL = QStringLiteral("pool/index.json");
const QString DanbooruService::ARTIST_URL = "artist/index.json"; const QString DanbooruService::ARTIST_URL = QStringLiteral("artist/index.json");
const QString DanbooruService::POOL_DATA_URL = "pool/show.xml"; const QString DanbooruService::POOL_DATA_URL = QStringLiteral("pool/show.xml");
const QString DanbooruService::RELATED_TAG_URL = "tag/related.json"; const QString DanbooruService::RELATED_TAG_URL = QStringLiteral("tag/related.json");
DanbooruService::DanbooruService(QUrl boardUrl, QString username, DanbooruService::DanbooruService(QUrl boardUrl, QString username,
QString password, KImageCache *cache, QString password, KImageCache *cache,
@ -81,8 +81,8 @@ void DanbooruService::getPostList()
QMap<QString, QString> parameters; QMap<QString, QString> parameters;
parameters.insert("limit", QString::number(m_maxPosts)); parameters.insert(QStringLiteral("limit"), QString::number(m_maxPosts));
parameters.insert("page", QString::number(m_currentPage)); parameters.insert(QStringLiteral("page"), QString::number(m_currentPage));
QUrl danbooruUrl = requestUrl(m_url, POST_URL, m_username, QUrl danbooruUrl = requestUrl(m_url, POST_URL, m_username,
m_password, parameters, m_tags); m_password, parameters, m_tags);
@ -102,12 +102,12 @@ void DanbooruService::getPostList()
void DanbooruService::getTagList(int limit, QString name) void DanbooruService::getTagList(int limit, QString name)
{ {
QMap<QString, QString> parameters; QMap<QString, QString> parameters;
parameters.insert("limit", QString::number(limit)); parameters.insert(QStringLiteral("limit"), QString::number(limit));
if (!name.isEmpty()) { if (!name.isEmpty()) {
parameters.insert("name", name); parameters.insert(QStringLiteral("name"), name);
} }
parameters.insert("order", "date"); parameters.insert(QStringLiteral("order"), QStringLiteral("date"));
QUrl danbooruUrl = requestUrl(m_url, TAG_URL, m_username, m_password, QUrl danbooruUrl = requestUrl(m_url, TAG_URL, m_username, m_password,
parameters); parameters);
@ -124,10 +124,10 @@ void DanbooruService::getPool(int poolId, int page)
QMap<QString, QString> parameters; QMap<QString, QString> parameters;
parameters.insert("id", QString::number(poolId)); parameters.insert(QStringLiteral("id"), QString::number(poolId));
if (page > 1) { if (page > 1) {
parameters.insert("page", QString::number(page)); parameters.insert(QStringLiteral("page"), QString::number(page));
} }
QUrl danbooruUrl = requestUrl(m_url, POOL_DATA_URL, m_username, QUrl danbooruUrl = requestUrl(m_url, POOL_DATA_URL, m_username,
@ -156,7 +156,7 @@ void DanbooruService::getPoolList()
danbooruUrl = requestUrl(m_url, POOL_URL, m_username, m_password); danbooruUrl = requestUrl(m_url, POOL_URL, m_username, m_password);
} else { } else {
QMap<QString, QString> map; QMap<QString, QString> map;
map.insert("page", QString::number(m_currentPage)); map.insert(QStringLiteral("page"), QString::number(m_currentPage));
danbooruUrl = requestUrl(m_url, POOL_URL, m_username, danbooruUrl = requestUrl(m_url, POOL_URL, m_username,
m_password, map); m_password, map);
@ -185,13 +185,12 @@ void DanbooruService::getPoolList()
QList<QVariant> poolList = parseDanbooruResult(data, &ok).toList(); QList<QVariant> poolList = parseDanbooruResult(data, &ok).toList();
if (!ok) { if (!ok) {
Q_EMIT(downloadError(QString("Unable to decode data"))); Q_EMIT(downloadError(QStringLiteral("Unable to decode data")));
return; return;
} }
for (auto element : poolList) { Q_FOREACH(const auto & element, poolList) {
QVariantMap map = element.toMap(); QVariantMap map = element.toMap();
DanbooruPool *pool = new DanbooruPool(map); DanbooruPool *pool = new DanbooruPool(map);
Q_EMIT(poolDownloaded(pool)); Q_EMIT(poolDownloaded(pool));
} }
@ -211,24 +210,24 @@ void DanbooruService::getRelatedTags(const QStringList &tags,
QString type; QString type;
switch (tagType) { switch (tagType) {
case DanbooruTag::General: case DanbooruTag::General:
type = "general"; type = QStringLiteral("general");
break; break;
case DanbooruTag::Artist: case DanbooruTag::Artist:
type = "artist"; type = QStringLiteral("artist");
break; break;
case DanbooruTag::Copyright: case DanbooruTag::Copyright:
type = "copyright"; type = QStringLiteral("copyright");
break; break;
case DanbooruTag::Character: case DanbooruTag::Character:
type = "character"; type = QStringLiteral("character");
break; break;
case DanbooruTag::Unknown: case DanbooruTag::Unknown:
type = "unknown"; type = QStringLiteral("unknown");
break; break;
} }
QMap<QString, QString> parameters; QMap<QString, QString> parameters;
parameters.insert("type", type); parameters.insert(QStringLiteral("type"), type);
QUrl danbooruUrl = requestUrl(m_url, RELATED_TAG_URL, m_username, QUrl danbooruUrl = requestUrl(m_url, RELATED_TAG_URL, m_username,
m_password, parameters, tags); m_password, parameters, tags);
@ -254,7 +253,7 @@ void DanbooruService::getRelatedTags(const QStringList &tags,
QVariantMap tagList = parseDanbooruResult(data, &ok).toMap(); QVariantMap tagList = parseDanbooruResult(data, &ok).toMap();
if (!ok) { if (!ok) {
Q_EMIT(downloadError(QString("Unable to decode data"))); Q_EMIT(downloadError(QStringLiteral("Unable to decode data")));
return; return;
} }
@ -273,7 +272,7 @@ void DanbooruService::getRelatedTags(const QStringList &tags,
continue; continue;
} }
for (auto tag : tags) { Q_FOREACH(auto &tag, tags) {
// We get the first element in the list, the second is // We get the first element in the list, the second is
// the ID which is useless (no API methods in Danbooru) // the ID which is useless (no API methods in Danbooru)
QString tagName = tag.toList()[0].toString(); QString tagName = tag.toList()[0].toString();
@ -298,15 +297,15 @@ const QStringList DanbooruService::allowedRatings() const
QStringList ratings; QStringList ratings;
if (m_maxRating.testFlag(DanbooruPost::Safe)) { if (m_maxRating.testFlag(DanbooruPost::Safe)) {
ratings.append("Safe"); ratings.append(QStringLiteral("Safe"));
} }
if (m_maxRating.testFlag(DanbooruPost::Questionable)) { if (m_maxRating.testFlag(DanbooruPost::Questionable)) {
ratings.append("Questionable"); ratings.append(QStringLiteral("Questionable"));
} }
if (m_maxRating.testFlag(DanbooruPost::Explicit)) { if (m_maxRating.testFlag(DanbooruPost::Explicit)) {
ratings.append("Explicit"); ratings.append(QStringLiteral("Explicit"));
} }
return ratings; return ratings;
@ -330,6 +329,16 @@ int DanbooruService::maxPosts() const
return m_maxPosts; return m_maxPosts;
} }
int DanbooruService::minimumWidth() const
{
return m_minimumWidth > 0 ? m_minimumWidth: -1;
}
int DanbooruService::minimumHeight() const
{
return m_minimumHeight > 0 ? m_minimumHeight: -1;
}
void DanbooruService::nextPostPage() void DanbooruService::nextPostPage()
{ {
m_currentPage++; m_currentPage++;
@ -371,14 +380,15 @@ void DanbooruService::processTagList(KJob *job)
// Most Danbooru implementations return tags in wrong order when // Most Danbooru implementations return tags in wrong order when
// using JSON, so we have to fall back to XML // using JSON, so we have to fall back to XML
QList<QVariant> tagList = parseDanbooruResult(data, "tag", &ok); QList<QVariant> tagList = parseDanbooruResult(data, QStringLiteral("tag"),
&ok);
if (!ok) { if (!ok) {
Q_EMIT(downloadError(QString("Unable to decode data"))); Q_EMIT(downloadError(QStringLiteral("Unable to decode data")));
return; return;
} }
for (auto element : tagList) { Q_FOREACH(const auto& element, tagList) {
QVariantMap map = element.toMap(); QVariantMap map = element.toMap();
DanbooruTag *tag = new DanbooruTag(map); DanbooruTag *tag = new DanbooruTag(map);
@ -402,7 +412,7 @@ void DanbooruService::processPostList(KJob *job)
StoredTransferJob *jobResult = qobject_cast<StoredTransferJob *>(job); StoredTransferJob *jobResult = qobject_cast<StoredTransferJob *>(job);
if (jobResult == 0) { if (jobResult == 0) {
Q_EMIT(downloadError(QString("Internal error"))); Q_EMIT(downloadError(QStringLiteral("Internal error")));
return; return;
} }
@ -416,13 +426,13 @@ void DanbooruService::processPostList(KJob *job)
if (needsXML) { if (needsXML) {
// Special cases for pools // Special cases for pools
postList = parseDanbooruResult(data, QString("post"), &ok); postList = parseDanbooruResult(data, QStringLiteral("post"), &ok);
} else { } else {
postList = parseDanbooruResult(data, &ok).toList(); postList = parseDanbooruResult(data, &ok).toList();
} }
if (!ok) { if (!ok) {
Q_EMIT(downloadError(QString("Unable to decode data"))); Q_EMIT(downloadError(QStringLiteral("Unable to decode data")));
return; return;
} }
@ -436,7 +446,7 @@ void DanbooruService::processPostList(KJob *job)
m_postsToFetch = postList.length(); m_postsToFetch = postList.length();
for (auto element : postList) { Q_FOREACH(const auto& element, postList) {
QVariantMap map = element.toMap(); QVariantMap map = element.toMap();
DanbooruPost *post = new DanbooruPost(map); DanbooruPost *post = new DanbooruPost(map);
@ -449,6 +459,18 @@ void DanbooruService::processPostList(KJob *job)
continue; continue;
} }
if (post->width() < minimumWidth()) {
m_postsToFetch--;
delete post;
continue;
}
if (post->height() < minimumHeight()) {
m_postsToFetch--;
delete post;
continue;
}
QPixmap pix; QPixmap pix;
// qCDebug(LIBDANBOORU) << "About to donwload images"; // qCDebug(LIBDANBOORU) << "About to donwload images";
@ -491,7 +513,7 @@ void DanbooruService::processPostList(KJob *job)
if (!pix.loadFromData(jobResult->data())) if (!pix.loadFromData(jobResult->data()))
{ {
Q_EMIT(downloadError(QString("Pixmap data could not be loaded"))); Q_EMIT(downloadError(QStringLiteral("Pixmap data could not be loaded")));
return; return;
} }
@ -546,7 +568,7 @@ void DanbooruService::setBlacklist(const QStringList &blacklist)
m_blacklist.clear(); m_blacklist.clear();
for (auto element : blacklist) { for (const auto& element : blacklist) {
m_blacklist.insert(element); m_blacklist.insert(element);
} }
@ -618,4 +640,16 @@ void DanbooruService::setCurrentPage(int page)
m_currentPage = page; m_currentPage = page;
} }
void Danbooru::DanbooruService::setResolution(unsigned int width, unsigned int height)
{
if (width > 0) {
m_minimumWidth = width;
}
if (height > 0) {
m_minimumHeight = height;
}
}
} // namespace Danbooru } // namespace Danbooru

View file

@ -93,6 +93,8 @@ private:
Danbooru::Ratings m_maxRating; Danbooru::Ratings m_maxRating;
int m_maxPosts; int m_maxPosts;
int m_currentPage; int m_currentPage;
unsigned int m_minimumHeight = 0;
unsigned int m_minimumWidth = 0;
QStringList m_tags; QStringList m_tags;
unsigned int m_postsToFetch; // To tell when to quit unsigned int m_postsToFetch; // To tell when to quit
@ -162,7 +164,7 @@ public:
* @param name The name of the tag to retrieve, or an empty string * @param name The name of the tag to retrieve, or an empty string
* *
**/ **/
void getTagList(int limit = 10, QString name = ""); void getTagList(int limit = 10, QString name = QStringLiteral(""));
/** /**
* @brief Get tags related to a specific, user supplied list. * @brief Get tags related to a specific, user supplied list.
@ -207,6 +209,10 @@ public:
**/ **/
QStringList postTags() const; QStringList postTags() const;
int minimumWidth() const;
int minimumHeight() const;
/** /**
* @brief Resets the service to the default state, clearing the page counters. * @brief Resets the service to the default state, clearing the page counters.
**/ **/
@ -282,6 +288,11 @@ public:
void setPostTags(const QStringList &tags); void setPostTags(const QStringList &tags);
void setResolution(unsigned int width = 0, unsigned int height = 0);
void setMinimumWidth(unsigned int width) { m_minimumWidth = width; };
void setMinimumHeight(unsigned int height) { m_minimumHeight = height; };
private Q_SLOTS: private Q_SLOTS:
void processPostList(KJob *job); void processPostList(KJob *job);
void processTagList(KJob *job); void processTagList(KJob *job);

View file

@ -119,16 +119,18 @@ DanbooruMainWindow::DanbooruMainWindow(QWidget *parent)
KDeclarative::KDeclarative declarative; KDeclarative::KDeclarative declarative;
declarative.setDeclarativeEngine(m_view->engine()); declarative.setDeclarativeEngine(m_view->engine());
declarative.setupBindings(); declarative.setupBindings();
m_view->setFocusPolicy(Qt::StrongFocus);
m_view->setFocus();
auto qmlViewPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, auto qmlViewPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
qApp->applicationName() + QLatin1String("/danbooruimageview.qml")); qApp->applicationName() + QStringLiteral("/danbooruimageview.qml"));
QQmlContext *ctxt = m_view->rootContext(); QQmlContext *ctxt = m_view->rootContext();
ctxt->setContextProperty("danbooruModel", m_model); ctxt->setContextProperty(QStringLiteral("danbooruModel"), m_model);
ctxt->setContextProperty("danbooruService", m_service); ctxt->setContextProperty(QStringLiteral("danbooruService"), m_service);
m_view->setSource(QUrl::fromLocalFile(qmlViewPath)); m_view->setSource(QUrl::fromLocalFile(qmlViewPath));
m_view->rootObject()->setProperty("poolMode", QVariant(false)); m_view->rootObject()->setProperty("poolMode", QVariant(false));
ctxt->setContextProperty("infiniteScroll", DanbooruSettings::self()->infiniteScrolling()); ctxt->setContextProperty(QStringLiteral("infiniteScroll"), DanbooruSettings::self()->infiniteScrolling());
auto rootObj = m_view->rootObject(); auto rootObj = m_view->rootObject();
connect(m_service, SIGNAL(postDownloadFinished()), rootObj, SIGNAL(downloadFinished())); connect(m_service, SIGNAL(postDownloadFinished()), rootObj, SIGNAL(downloadFinished()));
@ -140,7 +142,8 @@ DanbooruMainWindow::DanbooruMainWindow(QWidget *parent)
// then, setup our actions // then, setup our actions
setupActions(); setupActions();
setupGUI(KXmlGuiWindow::ToolBar | Keys | Save | Create, "danbooru-clientui.rc"); setupGUI(KXmlGuiWindow::ToolBar | Keys | Save | Create,
QStringLiteral("danbooru-clientui.rc"));
// connections // connections
@ -154,11 +157,11 @@ DanbooruMainWindow::DanbooruMainWindow(QWidget *parent)
m_service->setPassword(m_connectWidget->password()); m_service->setPassword(m_connectWidget->password());
} }
actionCollection()->action(QLatin1String("fetch"))->setEnabled(true); actionCollection()->action(QStringLiteral("fetch"))->setEnabled(true);
actionCollection()->action(QLatin1String("find"))->setEnabled(true); actionCollection()->action(QStringLiteral("find"))->setEnabled(true);
actionCollection()->action(QLatin1String("poolDownload"))->setEnabled(true); actionCollection()->action(QStringLiteral("poolDownload"))->setEnabled(true);
actionCollection()->action(QLatin1String("tags"))->setEnabled(true); actionCollection()->action(QStringLiteral("tags"))->setEnabled(true);
actionCollection()->action(QLatin1String("morePosts"))->setEnabled(true); actionCollection()->action(QStringLiteral("morePosts"))->setEnabled(true);
if (DanbooruSettings::self()->autoDownload()) { if (DanbooruSettings::self()->autoDownload()) {
@ -196,15 +199,19 @@ DanbooruMainWindow::DanbooruMainWindow(QWidget *parent)
connect(m_searchWidget, &DanbooruSearchWidget::accepted, [this]() { connect(m_searchWidget, &DanbooruSearchWidget::accepted, [this]() {
QDockWidget *searchDockWidget = findChild<QDockWidget *>(QLatin1String("SearchView")); QDockWidget *searchDockWidget = findChild<QDockWidget *>(QStringLiteral("SearchView"));
searchDockWidget->hide(); searchDockWidget->hide();
handlePostDownload(m_searchWidget->selectedTags(), true /* relatedTags */); handlePostDownload(m_searchWidget->selectedTags(),
true /* relatedTags */,
m_searchWidget->selectedWidth() /* minimumWidth */,
m_searchWidget->selectedHeight() /* minimumHeight */
);
}); });
connect(m_searchWidget, &DanbooruSearchWidget::rejected, [this]() { connect(m_searchWidget, &DanbooruSearchWidget::rejected, [this]() {
QDockWidget *searchDockWidget = findChild<QDockWidget *>(QLatin1String("SearchView")); QDockWidget *searchDockWidget = findChild<QDockWidget *>(QStringLiteral("SearchView"));
searchDockWidget->hide(); searchDockWidget->hide();
}); });
@ -212,7 +219,7 @@ DanbooruMainWindow::DanbooruMainWindow(QWidget *parent)
if (m_tagModel->rowCount() == 0) { if (m_tagModel->rowCount() == 0) {
// Only get tags if we don't have any already // Only get tags if we don't have any already
for (auto tag : m_model->postTags()) { Q_FOREACH(const auto& tag, m_model->postTags()) {
m_service->getTagList(1, tag); m_service->getTagList(1, tag);
} }
} }
@ -243,10 +250,11 @@ void DanbooruMainWindow::loadSettings()
QVector<QUrl> boardsList; QVector<QUrl> boardsList;
QStringList::const_iterator it; QStringList::const_iterator it;
auto configList = DanbooruSettings::self()->boards();
for (it = DanbooruSettings::self()->boards().constBegin(); for (it = configList.constBegin();
it != DanbooruSettings::self()->boards().constEnd(); it != configList.constEnd();
++it) { ++it) {
boardsList.append(QUrl::fromUserInput(*it)); boardsList.append(QUrl::fromUserInput(*it));
@ -260,7 +268,7 @@ void DanbooruMainWindow::loadSettings()
m_tagWidget->setBlackList(DanbooruSettings::self()->tagBlacklist()); m_tagWidget->setBlackList(DanbooruSettings::self()->tagBlacklist());
m_view->rootContext()->setContextProperty("infiniteScroll", m_view->rootContext()->setContextProperty(QStringLiteral("infiniteScroll"),
DanbooruSettings::self()->infiniteScrolling()); DanbooruSettings::self()->infiniteScrolling());
} }
@ -269,23 +277,23 @@ void DanbooruMainWindow::setupActions()
{ {
QAction *connectAction = new QAction( QAction *connectAction = new QAction(
QIcon::fromTheme(QLatin1String("document-open-remote")), QIcon::fromTheme(QStringLiteral("document-open-remote")),
i18n("Connect..."), i18n("Connect..."),
this); this);
QAction *fetchAction = new QAction(QIcon::fromTheme(QLatin1String("download")), QAction *fetchAction = new QAction(QIcon::fromTheme(QStringLiteral("download")),
i18n("Download"), this); i18n("Download"), this);
KToggleAction *findAction = new KToggleAction(QIcon::fromTheme(QLatin1String("edit-find")), KToggleAction *findAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("edit-find")),
i18n("Search"), this); i18n("Search"), this);
KToggleAction *poolAction = new KToggleAction(QIcon::fromTheme(QLatin1String("image-x-generic")), KToggleAction *poolAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("image-x-generic")),
i18n("Pools"), this); i18n("Pools"), this);
QAction *nextPageAction = new QAction(QIcon::fromTheme(QLatin1String("go-next")), QAction *nextPageAction = new QAction(QIcon::fromTheme(QStringLiteral("go-next")),
i18n("More posts"), this); i18n("More posts"), this);
QAction *nextPoolAction = new QAction(QIcon::fromTheme(QLatin1String("go-next")), QAction *nextPoolAction = new QAction(QIcon::fromTheme(QStringLiteral("go-next")),
i18n("More pools"), this); i18n("More pools"), this);
KDualAction *tagAction = new KDualAction(i18n("Show tags"), i18n("Hide tags"), this); KDualAction *tagAction = new KDualAction(i18n("Show tags"), i18n("Hide tags"), this);
tagAction->setIconForStates(QIcon::fromTheme(QLatin1String("tag"))); tagAction->setIconForStates(QIcon::fromTheme(QStringLiteral("tag")));
fetchAction->setEnabled(false); fetchAction->setEnabled(false);
findAction->setEnabled(false); findAction->setEnabled(false);
@ -297,13 +305,13 @@ void DanbooruMainWindow::setupActions()
findAction->setChecked(false); findAction->setChecked(false);
tagAction->setEnabled(false); tagAction->setEnabled(false);
actionCollection()->addAction(QLatin1String("connect"), connectAction); actionCollection()->addAction(QStringLiteral("connect"), connectAction);
actionCollection()->addAction(QLatin1String("fetch"), fetchAction); actionCollection()->addAction(QStringLiteral("fetch"), fetchAction);
actionCollection()->addAction(QLatin1String("find"), findAction); actionCollection()->addAction(QStringLiteral("find"), findAction);
actionCollection()->addAction(QLatin1String("poolDownload"), poolAction); actionCollection()->addAction(QStringLiteral("poolDownload"), poolAction);
actionCollection()->addAction(QLatin1String("tags"), tagAction); actionCollection()->addAction(QStringLiteral("tags"), tagAction);
actionCollection()->addAction(QLatin1String("morePosts"), nextPageAction); actionCollection()->addAction(QStringLiteral("morePosts"), nextPageAction);
actionCollection()->addAction(QLatin1String("morePools"), nextPoolAction); actionCollection()->addAction(QStringLiteral("morePools"), nextPoolAction);
actionCollection()->setDefaultShortcut(connectAction, KStandardShortcut::Open); actionCollection()->setDefaultShortcut(connectAction, KStandardShortcut::Open);
actionCollection()->setDefaultShortcut(findAction, KStandardShortcut::Find); actionCollection()->setDefaultShortcut(findAction, KStandardShortcut::Find);
@ -319,7 +327,7 @@ void DanbooruMainWindow::setupActions()
return; return;
} }
QDockWidget *poolDockWidget = findChild<QDockWidget *>(QLatin1String("PoolView")); QDockWidget *poolDockWidget = findChild<QDockWidget *>(QStringLiteral("PoolView"));
if (checked) { if (checked) {
@ -329,19 +337,19 @@ void DanbooruMainWindow::setupActions()
} }
poolDockWidget->show(); poolDockWidget->show();
actionCollection()->action(QLatin1String("morePools"))->setEnabled(true); actionCollection()->action(QStringLiteral("morePools"))->setEnabled(true);
m_tableView->show(); m_tableView->show();
} else { } else {
poolDockWidget->hide(); poolDockWidget->hide();
actionCollection()->action(QLatin1String("morePools"))->setEnabled(false); actionCollection()->action(QStringLiteral("morePools"))->setEnabled(false);
m_tableView->hide(); m_tableView->hide();
} }
}); });
connect(findAction, &KToggleAction::toggled, [this](bool checked) { connect(findAction, &KToggleAction::toggled, [this](bool checked) {
QDockWidget *searchDockWidget = findChild<QDockWidget *>(QLatin1String("SearchView")); QDockWidget *searchDockWidget = findChild<QDockWidget *>(QStringLiteral("SearchView"));
if (checked) { if (checked) {
searchDockWidget->show(); searchDockWidget->show();
@ -355,7 +363,7 @@ void DanbooruMainWindow::setupActions()
connect(tagAction, &KDualAction::activeChanged, [this](bool checked) { connect(tagAction, &KDualAction::activeChanged, [this](bool checked) {
QDockWidget *tagDockWidget = findChild<QDockWidget *>(QLatin1String("TagView")); QDockWidget *tagDockWidget = findChild<QDockWidget *>(QStringLiteral("TagView"));
if (checked) { if (checked) {
tagDockWidget->show(); tagDockWidget->show();
@ -392,7 +400,7 @@ void DanbooruMainWindow::setupDockWidgets()
QDockWidget *poolDockWidget = new QDockWidget(i18n("Pools"), this); QDockWidget *poolDockWidget = new QDockWidget(i18n("Pools"), this);
poolDockWidget->setAllowedAreas(Qt::BottomDockWidgetArea); poolDockWidget->setAllowedAreas(Qt::BottomDockWidgetArea);
poolDockWidget->setWidget(m_tableView); poolDockWidget->setWidget(m_tableView);
poolDockWidget->setObjectName("PoolView"); poolDockWidget->setObjectName(QStringLiteral("PoolView"));
// Prevent the use of winId() when detached, leads to QQuickWidget bugs // Prevent the use of winId() when detached, leads to QQuickWidget bugs
poolDockWidget->setFeatures(QDockWidget::DockWidgetClosable); poolDockWidget->setFeatures(QDockWidget::DockWidgetClosable);
@ -404,7 +412,7 @@ void DanbooruMainWindow::setupDockWidgets()
QDockWidget *searchDockWidget = new QDockWidget(QLatin1String(""), this); QDockWidget *searchDockWidget = new QDockWidget(QLatin1String(""), this);
searchDockWidget->setAllowedAreas(Qt::TopDockWidgetArea); searchDockWidget->setAllowedAreas(Qt::TopDockWidgetArea);
searchDockWidget->setWidget(m_searchWidget); searchDockWidget->setWidget(m_searchWidget);
searchDockWidget->setObjectName("SearchView"); searchDockWidget->setObjectName(QStringLiteral("SearchView"));
searchDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); searchDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
addDockWidget(Qt::TopDockWidgetArea, searchDockWidget); addDockWidget(Qt::TopDockWidgetArea, searchDockWidget);
@ -414,10 +422,10 @@ void DanbooruMainWindow::setupDockWidgets()
searchDockWidget->hide(); searchDockWidget->hide();
m_searchWidget->hide(); m_searchWidget->hide();
QDockWidget *tagDockWidget = new QDockWidget(QLatin1String("Tags"), this); QDockWidget *tagDockWidget = new QDockWidget(QStringLiteral("Tags"), this);
tagDockWidget->setAllowedAreas(Qt::RightDockWidgetArea); tagDockWidget->setAllowedAreas(Qt::RightDockWidgetArea);
tagDockWidget->setWidget(m_tagWidget); tagDockWidget->setWidget(m_tagWidget);
tagDockWidget->setObjectName("TagView"); tagDockWidget->setObjectName(QStringLiteral("TagView"));
tagDockWidget->setFeatures(QDockWidget::DockWidgetClosable); tagDockWidget->setFeatures(QDockWidget::DockWidgetClosable);
addDockWidget(Qt::RightDockWidgetArea, tagDockWidget); addDockWidget(Qt::RightDockWidgetArea, tagDockWidget);
@ -427,15 +435,15 @@ void DanbooruMainWindow::setupDockWidgets()
// Connections // Connections
connect(poolDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) { connect(poolDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) {
actionCollection()->action(QLatin1String("poolDownload"))->setChecked(visible); actionCollection()->action(QStringLiteral("poolDownload"))->setChecked(visible);
}); });
connect(searchDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) { connect(searchDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) {
actionCollection()->action(QLatin1String("find"))->setChecked(visible); actionCollection()->action(QStringLiteral("find"))->setChecked(visible);
}); });
connect(tagDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) { connect(tagDockWidget, &QDockWidget::visibilityChanged, [this](bool visible) {
qobject_cast<KDualAction *>(actionCollection()->action(QLatin1String("tags")))->setActive(visible); qobject_cast<KDualAction *>(actionCollection()->action(QStringLiteral("tags")))->setActive(visible);
}); });
} }
@ -467,13 +475,13 @@ void DanbooruMainWindow::downloadPosts()
void DanbooruMainWindow::optionsPreferences() void DanbooruMainWindow::optionsPreferences()
{ {
KConfigDialog *dialog = new KConfigDialog(this, "danboorusettings", KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("danboorusettings"),
DanbooruSettings::self()); DanbooruSettings::self());
dialog->addPage(new GeneralPage(DanbooruSettings::self(), this), i18n("General"), dialog->addPage(new GeneralPage(DanbooruSettings::self(), this), i18n("General"),
"table"); QStringLiteral("table"));
dialog->addPage(new BlacklistPage(DanbooruSettings::self(), this), i18n("Tag blacklist"), dialog->addPage(new BlacklistPage(DanbooruSettings::self(), this), i18n("Tag blacklist"),
"configure"); QStringLiteral("configure"));
connect(dialog, &KConfigDialog::settingsChanged, this, &DanbooruMainWindow::loadSettings); connect(dialog, &KConfigDialog::settingsChanged, this, &DanbooruMainWindow::loadSettings);
dialog->show(); dialog->show();
} }
@ -506,13 +514,15 @@ void DanbooruMainWindow::slotHandleDownload(const QUrl &url, const QVariant tags
saveDialog->setMimeTypeFilters(filters); saveDialog->setMimeTypeFilters(filters);
} else { } else {
filters.reserve(2); filters.reserve(2);
filters << "Images (*.png *.gif *.jpg)" << "All files (*.*)"; filters << QStringLiteral("Images (*.png *.gif *.jpg)")
<< QStringLiteral("All files (*.*)");
saveDialog->setNameFilters(filters); saveDialog->setNameFilters(filters);
} }
// Prevent invalid characters (":" can be a tag in Danbooru) // Prevent invalid characters (":" can be a tag in Danbooru)
if (remoteFile.contains(":")) { if (remoteFile.contains(QStringLiteral(":"))) {
remoteFile.replace(":", "_"); remoteFile.replace(QStringLiteral(":"),
QStringLiteral("_"));
} }
saveDialog->selectFile(remoteFile); saveDialog->selectFile(remoteFile);
@ -578,12 +588,21 @@ void DanbooruMainWindow::clearModels()
} }
void DanbooruMainWindow::handlePostDownload(const QStringList &tags, bool relatedTags) void DanbooruMainWindow::handlePostDownload(const QStringList &tags, bool relatedTags,
unsigned int minimumWidth, unsigned int minimumHeight)
{ {
clearModels(); clearModels();
m_view->rootObject()->setProperty("poolMode", QVariant(false)); m_view->rootObject()->setProperty("poolMode", QVariant(false));
m_service->setPostTags(tags); m_service->setPostTags(tags);
if (minimumWidth > 0) {
m_service->setMinimumWidth(minimumWidth);
}
if (minimumHeight > 0) {
m_service->setMinimumHeight(minimumHeight);
}
if (relatedTags) { if (relatedTags) {
m_service->getRelatedTags(tags); m_service->getRelatedTags(tags);
} }

View file

@ -90,7 +90,8 @@ private:
void setupDockWidgets(); void setupDockWidgets();
void setupConnections(); void setupConnections();
void clearModels(); void clearModels();
void handlePostDownload(const QStringList &tags = QStringList(), bool relatedTags = false); void handlePostDownload(const QStringList &tags = QStringList(), bool relatedTags = false,
unsigned int minimumWidth = 0, unsigned int minimumHeight = 0);
private Q_SLOTS: private Q_SLOTS:
void connectToBoard(); void connectToBoard();

View file

@ -42,12 +42,16 @@ Rectangle {
signal downloadRequested(url url, var tags) signal downloadRequested(url url, var tags)
signal downloadStarted() signal downloadStarted()
signal fileInfo(url name, var tags) signal fileInfo(url name, var tags)
signal shareButtonClicked(url url)
KRun { KRun {
id: runner id: runner
} }
Clipboard {
id: clipboard
}
onDownloadFinished: { onDownloadFinished: {
grid.opacity = 1 grid.opacity = 1
@ -64,6 +68,10 @@ Rectangle {
runningIndicator.running = true runningIndicator.running = true
} }
onShareButtonClicked: {
clipboard.content = url;
}
BusyIndicator { BusyIndicator {
id: runningIndicator id: runningIndicator
z: 1 z: 1
@ -121,8 +129,6 @@ Rectangle {
height: parent.height height: parent.height
width: parent.width width: parent.width
//
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
@ -137,11 +143,13 @@ Rectangle {
onEntered: { onEntered: {
viewButton.opacity = 1 viewButton.opacity = 1
downloadButton.opacity = 1 downloadButton.opacity = 1
shareButton.opacity = 1
} }
onExited: { onExited: {
viewButton.opacity = 0 viewButton.opacity = 0
downloadButton.opacity = 0 downloadButton.opacity = 0
shareButton.opacity = 0
} }
Button { Button {
@ -198,19 +206,51 @@ Rectangle {
} }
} }
} }
Button {
id: shareButton
iconName: "edit-copy"
tooltip: i18n("Copy link to clipboard")
visible: opacity > 0
opacity: 0
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: (pixItem.height - pixItem.paintedHeight) / 2
anchors.rightMargin: (pixItem.width - pixItem.paintedWidth) / 2
height: pixItem.height * 0.15
width: pixItem.height * 0.15
z: 1
onClicked: {
rootObj.shareButtonClicked(fileUrl)
}
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
}
} }
} }
Text { Text {
id: sizeText id: sizeText
anchors.left: pixItem.left
text: i18n("File size: %1", KCoreAddons.Format.formatByteSize(fileSize)) text: i18n("File size: %1", KCoreAddons.Format.formatByteSize(fileSize))
} }
Text { Text {
id: resolutionText id: resolutionText
anchors.left: pixItem.left
text: i18n("Resolution: %1 x %2", resolution.width, resolution.height) text: i18n("Resolution: %1 x %2", resolution.width, resolution.height)
} }
Text { Text {
id: ratingText id: ratingText
anchors.left: pixItem.left
text: { text: {
if (rating == DanbooruPost.Safe) { if (rating == DanbooruPost.Safe) {
@ -246,6 +286,7 @@ Rectangle {
model: danbooruModel model: danbooruModel
delegate: viewDelegate delegate: viewDelegate
interactive: true
focus: true focus: true
Component.onCompleted: { currentIndex = -1; forceActiveFocus()} Component.onCompleted: { currentIndex = -1; forceActiveFocus()}

View file

@ -6,12 +6,12 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>387</width> <width>371</width>
<height>45</height> <height>160</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -19,21 +19,76 @@
<property name="windowTitle"> <property name="windowTitle">
<string/> <string/>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QGridLayout" name="gridLayout">
<item> <item row="1" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>37</width>
<height>29</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLabel" name="heightLabel">
<property name="text">
<string>Minimum height:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="widthLabel">
<property name="text">
<string>Minimum width:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="closeButton"> <widget class="QPushButton" name="closeButton">
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="dialog-close"/> <iconset theme="dialog-close">
<normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="flat"> <property name="flat">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>37</width>
<height>29</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="KPluralHandlingSpinBox" name="heightSpinBox">
<property name="maximum">
<number>16834</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="KPluralHandlingSpinBox" name="widthSpinBox">
<property name="maximum">
<number>16834</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="tagLabel"> <widget class="QLabel" name="tagLabel">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
@ -46,7 +101,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="3" column="4">
<widget class="QPushButton" name="searchButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="KLineEdit" name="tagLineEdit"> <widget class="KLineEdit" name="tagLineEdit">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@ -62,16 +130,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="searchButton">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>KPluralHandlingSpinBox</class>
<extends>QSpinBox</extends>
<header>kpluralhandlingspinbox.h</header>
</customwidget>
<customwidget> <customwidget>
<class>KLineEdit</class> <class>KLineEdit</class>
<extends>QLineEdit</extends> <extends>QLineEdit</extends>