working on manual sorting, it is starting to get shape, some problems arised but I will either solve them or declare them to actually be features

This commit is contained in:
Martin Rotter 2022-03-04 14:14:37 +01:00
parent 659cb4cbf7
commit 84aa24255c
10 changed files with 157 additions and 34 deletions

View File

@ -26,7 +26,7 @@
<url type="donation">https://github.com/sponsors/martinrotter</url> <url type="donation">https://github.com/sponsors/martinrotter</url>
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="4.1.2" date="2022-03-03"/> <release version="4.1.2" date="2022-03-04"/>
</releases> </releases>
<content_rating type="oars-1.0"> <content_rating type="oars-1.0">
<content_attribute id="violence-cartoon">none</content_attribute> <content_attribute id="violence-cartoon">none</content_attribute>

View File

@ -27,6 +27,13 @@ FROM backup_Feeds;
-- ! -- !
DROP TABLE backup_Feeds; DROP TABLE backup_Feeds;
-- ! -- !
UPDATE Feeds
SET ordr = (
SELECT COUNT(*)
FROM Feeds ct
WHERE Feeds.account_id = ct.account_id AND Feeds.category = ct.category AND ct.id < Feeds.id
);
-- !
ALTER TABLE Categories RENAME TO backup_Categories; ALTER TABLE Categories RENAME TO backup_Categories;
-- ! -- !
CREATE TABLE Categories ( CREATE TABLE Categories (
@ -49,6 +56,13 @@ FROM backup_Categories;
-- ! -- !
DROP TABLE backup_Categories; DROP TABLE backup_Categories;
-- ! -- !
UPDATE Categories
SET ordr = (
SELECT COUNT(*)
FROM Categories ct
WHERE Categories.account_id = ct.account_id AND Categories.parent_id = ct.parent_id AND ct.id < Categories.id
);
-- !
ALTER TABLE Accounts RENAME TO backup_Accounts; ALTER TABLE Accounts RENAME TO backup_Accounts;
-- ! -- !
CREATE TABLE Accounts ( CREATE TABLE Accounts (
@ -65,7 +79,7 @@ CREATE TABLE Accounts (
); );
-- ! -- !
INSERT INTO Accounts (id, ordr, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data) INSERT INTO Accounts (id, ordr, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data)
SELECT id, id, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data SELECT id, id - 1, type, proxy_type, proxy_host, proxy_port, proxy_username, proxy_password, custom_data
FROM backup_Accounts; FROM backup_Accounts;
-- ! -- !
DROP TABLE backup_Accounts; DROP TABLE backup_Accounts;

View File

@ -4,6 +4,7 @@
#include "3rd-party/boolinq/boolinq.h" #include "3rd-party/boolinq/boolinq.h"
#include "database/databasefactory.h" #include "database/databasefactory.h"
#include "database/databasequeries.h"
#include "definitions/definitions.h" #include "definitions/definitions.h"
#include "gui/dialogs/formmain.h" #include "gui/dialogs/formmain.h"
#include "miscellaneous/feedreader.h" #include "miscellaneous/feedreader.h"
@ -523,6 +524,12 @@ bool FeedsModel::emptyAllBins() {
return result; return result;
} }
void FeedsModel::changeSortOrder(RootItem* item, bool move_top, bool move_bottom, int new_sort_order) {
QSqlDatabase db = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::moveItem(item, move_top, move_bottom, new_sort_order, db);
}
void FeedsModel::loadActivatedServiceAccounts() { void FeedsModel::loadActivatedServiceAccounts() {
auto serv = qApp->feedReader()->feedServices(); auto serv = qApp->feedReader()->feedServices();

View File

@ -107,6 +107,8 @@ class RSSGUARD_DLLSPEC FeedsModel : public QAbstractItemModel {
bool restoreAllBins(); bool restoreAllBins();
bool emptyAllBins(); bool emptyAllBins();
void changeSortOrder(RootItem* item, bool move_top, bool move_bottom, int new_sort_order);
// Feeds operations. // Feeds operations.
bool markItemRead(RootItem* item, RootItem::ReadStatus read); bool markItemRead(RootItem* item, RootItem::ReadStatus read);
bool markItemCleared(RootItem* item, bool clean_read_only); bool markItemCleared(RootItem* item, bool clean_read_only);

View File

@ -1939,6 +1939,23 @@ void DatabaseQueries::createOverwriteCategory(const QSqlDatabase& db, Category*
if (category->id() <= 0) { if (category->id() <= 0) {
// We need to insert category first. // We need to insert category first.
if (category->sortOrder() < 0) {
q.exec(QSL("SELECT MAX(ordr) FROM Categories WHERE account_id = :account_id AND parent_id = :parent_id;"));
q.bindValue(QSL(":account_id"), account_id);
q.bindValue(QSL(":parent_id"), parent_id);
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
q.next();
int next_order = (q.value(0).isNull() ? 0 : q.value(0).toInt()) + 1;
category->setSortOrder(next_order);
q.finish();
}
q.prepare(QSL("INSERT INTO " q.prepare(QSL("INSERT INTO "
"Categories (parent_id, ordr, title, date_created, account_id) " "Categories (parent_id, ordr, title, date_created, account_id) "
"VALUES (0, 0, 'new', 0, %1);").arg(QString::number(account_id))); "VALUES (0, 0, 'new', 0, %1);").arg(QString::number(account_id)));
@ -1975,6 +1992,23 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
if (feed->id() <= 0) { if (feed->id() <= 0) {
// We need to insert feed first. // We need to insert feed first.
if (feed->sortOrder() < 0) {
q.exec(QSL("SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category;"));
q.bindValue(QSL(":account_id"), account_id);
q.bindValue(QSL(":category"), parent_id);
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
q.next();
int next_order = (q.value(0).isNull() ? 0 : q.value(0).toInt()) + 1;
feed->setSortOrder(next_order);
q.finish();
}
q.prepare(QSL("INSERT INTO " q.prepare(QSL("INSERT INTO "
"Feeds (title, ordr, date_created, category, update_type, update_interval, account_id, custom_id) " "Feeds (title, ordr, date_created, category, update_type, update_interval, account_id, custom_id) "
"VALUES ('new', 0, 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id))); "VALUES ('new', 0, 0, 0, 0, 1, %1, 'new');").arg(QString::number(account_id)));
@ -2008,6 +2042,9 @@ void DatabaseQueries::createOverwriteFeed(const QSqlDatabase& db, Feed* feed, in
q.bindValue(QSL(":account_id"), account_id); q.bindValue(QSL(":account_id"), account_id);
q.bindValue(QSL(":custom_id"), feed->customId()); q.bindValue(QSL(":custom_id"), feed->customId());
q.bindValue(QSL(":id"), feed->id()); q.bindValue(QSL(":id"), feed->id());
// TODO: upravit set na ordr = (SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category) + 1;
// to ale pokud je sortOrder < 0
q.bindValue(QSL(":ordr"), feed->sortOrder()); q.bindValue(QSL(":ordr"), feed->sortOrder());
q.bindValue(QSL(":is_off"), feed->isSwitchedOff()); q.bindValue(QSL(":is_off"), feed->isSwitchedOff());
q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly()); q.bindValue(QSL(":open_articles"), feed->openArticlesDirectly());
@ -2026,8 +2063,22 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
QSqlQuery q(db); QSqlQuery q(db);
if (account->accountId() <= 0) { if (account->accountId() <= 0) {
// We need to insert account first. // We need to insert account and generate sort order first.
q.prepare(QSL("INSERT INTO Accounts (ordr, type) VALUES (0, :type);")); if (account->sortOrder() < 0) {
if (!q.exec(QSL("SELECT MAX(ordr) FROM Accounts;"))) {
throw ApplicationException(q.lastError().text());
}
q.next();
int next_order = (q.value(0).isNull() ? 0 : q.value(0).toInt()) + 1;
account->setSortOrder(next_order);
q.finish();
}
q.prepare(QSL("INSERT INTO Accounts (ordr, type) "
"VALUES (0, :type);"));
q.bindValue(QSL(":type"), account->code()); q.bindValue(QSL(":type"), account->code());
if (!q.exec()) { if (!q.exec()) {
@ -2095,36 +2146,80 @@ bool DatabaseQueries::deleteCategory(const QSqlDatabase& db, int id) {
return q.exec(); return q.exec();
} }
void DatabaseQueries::fixupOrders(const QSqlDatabase& db) { void DatabaseQueries::moveItem(RootItem* item, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db) {
// We first determine if there are same orders assigned to some items switch (item->kind()) {
// which have same parent category/acc. case RootItem::Kind::Feed:
QSqlQuery res = db.exec(QSL("SELECT COUNT(*) FROM Accounts GROUP BY ordr HAVING COUNT(*) > 1 " moveFeed(item->toFeed(), move_top, move_bottom, move_index, db);
"UNION ALL " break;
"SELECT COUNT(*) FROM Categories GROUP BY account_id, parent_id, ordr HAVING COUNT(*) > 1 "
"UNION ALL "
"SELECT COUNT(*) FROM Feeds GROUP BY account_id, category, ordr HAVING COUNT(*) > 1;"));
bool should_fixup = res.lastError().isValid() || res.size() > 0;
if (should_fixup) { case RootItem::Kind::Category:
// Some orders are messed up, fix. break;
qCriticalNN << LOGSEC_DB << "Order of items is messed up, fixing.";
for (const QString& table : { QSL("Accounts"), QSL("Categories"), QSL("Feeds") }) { case RootItem::Kind::ServiceRoot:
QSqlQuery q = db.exec(QSL("UPDATE %1 SET ordr = id;").arg(table));
if (q.lastError().isValid()) { break;
qFatal("Fixup of messed up order failed: '%s'.", qPrintable(q.lastError().text()));
} default:
} return;
}
else {
qDebugNN << LOGSEC_DB << "No fixing of item order is needed.";
} }
} }
void DatabaseQueries::moveItemUp(RootItem* item, const QSqlDatabase& db) {} void DatabaseQueries::moveFeed(Feed* feed, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db) {
if (feed->sortOrder() == move_index || /* Item is already sorted OK. */
(!move_top && !move_bottom && move_index < 0 ) || /* Order cannot be smaller than 0 if we do not move to begin/end. */
(move_top && feed->sortOrder() == 0)) { /* Item is already on top. */
return;
}
void DatabaseQueries::moveItemDown(RootItem* item, const QSqlDatabase& db) {} QSqlQuery q(db);
q.prepare(QSL("SELECT MAX(ordr) FROM Feeds WHERE account_id = :account_id AND category = :category;"));
q.bindValue(QSL(":account_id"), feed->getParentServiceRoot()->accountId());
q.bindValue(QSL(":category"), feed->parent()->id());
int max_sort_order;
if (q.exec() && q.next()) {
max_sort_order = q.value(0).toInt();
}
else {
throw ApplicationException(q.lastError().text());
}
if (max_sort_order == 0 || /* We only have 1 item, nothing to sort. */
max_sort_order == feed->sortOrder() || /* Item is already sorted OK. */
move_index > max_sort_order) { /* Cannot move past biggest sort order. */
return;
}
if (move_top) {
move_index = 0;
}
else if (move_bottom) {
move_index = max_sort_order;
}
int move_low = qMin(move_index, feed->sortOrder());
int move_high = qMax(move_index, feed->sortOrder());
if (feed->sortOrder() > move_index) {
q.prepare(QSL("UPDATE Feeds SET ordr = ordr + 1 "
"WHERE account_id = :account_id AND category = :category AND ordr < :move_high AND ordr >= :move_low;"));
}
else {
q.prepare(QSL("UPDATE Feeds SET ordr = ordr - 1 "
"WHERE account_id = :account_id AND category = :category AND ordr > :move_low AND ordr <= :move_high;"));
}
q.bindValue(QSL(":account_id"), feed->getParentServiceRoot()->accountId());
q.bindValue(QSL(":category"), feed->parent()->id());
q.bindValue(QSL(":move_low"), move_low);
q.bindValue(QSL(":move_high"), move_high);
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
}
MessageFilter* DatabaseQueries::addMessageFilter(const QSqlDatabase& db, const QString& title, MessageFilter* DatabaseQueries::addMessageFilter(const QSqlDatabase& db, const QString& title,
const QString& script) { const QString& script) {

View File

@ -135,9 +135,8 @@ class DatabaseQueries {
int account_id, bool* ok = nullptr); int account_id, bool* ok = nullptr);
// Item order methods. // Item order methods.
static void fixupOrders(const QSqlDatabase& db); static void moveItem(RootItem* item, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db);
static void moveItemUp(RootItem* item, const QSqlDatabase& db); static void moveFeed(Feed* feed, bool move_top, bool move_bottom, int move_index, const QSqlDatabase& db);
static void moveItemDown(RootItem* item, const QSqlDatabase& db);
// Message filters operators. // Message filters operators.
static bool purgeLeftoverMessageFilterAssignments(const QSqlDatabase& db, int account_id); static bool purgeLeftoverMessageFilterAssignments(const QSqlDatabase& db, int account_id);
@ -338,9 +337,6 @@ Assignment DatabaseQueries::getFeeds(const QSqlDatabase& db,
template<typename Categ, typename Fee> template<typename Categ, typename Fee>
void DatabaseQueries::loadRootFromDatabase(ServiceRoot* root) { void DatabaseQueries::loadRootFromDatabase(ServiceRoot* root) {
QSqlDatabase database = qApp->database()->driver()->connection(root->metaObject()->className()); QSqlDatabase database = qApp->database()->driver()->connection(root->metaObject()->className());
fixupOrders(database);
Assignment categories = DatabaseQueries::getCategories<Categ>(database, root->accountId()); Assignment categories = DatabaseQueries::getCategories<Categ>(database, root->accountId());
Assignment feeds = DatabaseQueries::getFeeds<Fee>(database, qApp->feedReader()->messageFilters(), root->accountId()); Assignment feeds = DatabaseQueries::getFeeds<Fee>(database, qApp->feedReader()->messageFilters(), root->accountId());
auto labels = DatabaseQueries::getLabelsForAccount(database, root->accountId()); auto labels = DatabaseQueries::getLabelsForAccount(database, root->accountId());

View File

@ -897,6 +897,8 @@ void FormMain::createConnections() {
qApp->feedReader()->showMessageFiltersManager(); qApp->feedReader()->showMessageFiltersManager();
tabWidget()->feedMessageViewer()->messagesView()->reloadSelections(); tabWidget()->feedMessageViewer()->messagesView()->reloadSelections();
}); });
connect(m_ui->m_actionFeedMoveUp, &QAction::triggered,
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::moveSelectedItemUp);
} }
void FormMain::backupDatabaseSettings() { void FormMain::backupDatabaseSettings() {

View File

@ -288,6 +288,10 @@ void FeedsView::deleteSelectedItem() {
qApp->feedUpdateLock()->unlock(); qApp->feedUpdateLock()->unlock();
} }
void FeedsView::moveSelectedItemUp() {
m_sourceModel->changeSortOrder(selectedItem(), false, false, selectedItem()->sortOrder() - 1);
}
void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) { void FeedsView::markSelectedItemReadStatus(RootItem::ReadStatus read) {
m_sourceModel->markItemRead(selectedItem(), read); m_sourceModel->markItemRead(selectedItem(), read);
} }

View File

@ -66,6 +66,9 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
void editSelectedItem(); void editSelectedItem();
void deleteSelectedItem(); void deleteSelectedItem();
// Sort order manipulations.
void moveSelectedItemUp();
// Selects next/previous item (feed/category) in the list. // Selects next/previous item (feed/category) in the list.
void selectNextItem(); void selectNextItem();
void selectPreviousItem(); void selectPreviousItem();

View File

@ -16,7 +16,7 @@
RootItem::RootItem(RootItem* parent_item) RootItem::RootItem(RootItem* parent_item)
: QObject(nullptr), m_kind(RootItem::Kind::Root), m_id(NO_PARENT_CATEGORY), m_customId(QL1S("")), : QObject(nullptr), m_kind(RootItem::Kind::Root), m_id(NO_PARENT_CATEGORY), m_customId(QL1S("")),
m_title(QString()), m_description(QString()), m_creationDate(QDateTime::currentDateTimeUtc()), m_title(QString()), m_description(QString()), m_creationDate(QDateTime::currentDateTimeUtc()),
m_keepOnTop(false), m_sortOrder(0), m_childItems(QList<RootItem*>()), m_parentItem(parent_item) {} m_keepOnTop(false), m_sortOrder(NO_PARENT_CATEGORY), m_childItems(QList<RootItem*>()), m_parentItem(parent_item) {}
RootItem::RootItem(const RootItem& other) : RootItem(nullptr) { RootItem::RootItem(const RootItem& other) : RootItem(nullptr) {
setTitle(other.title()); setTitle(other.title());