feat: mark-spam, rule priority persistence, run-rules-on-selected, icons fix, scheduled send + reader polish
- Mark as spam: moves mail to Spam/Junk folder of account - Rule priority: persistPriorityOrder persists 1..N via RuleDao::update - Run rules on selected: uses MailListView::selectedMailIds() + onRunRulesOnSelected - Icons: switch code to qrc short aliases (close/send/bold/etc) + new detach.svg -> resolves all 'Cannot open file' SVG warnings at startup - Categories: CompactMailDelegate paints category chips from CategoryDao - Scheduled send: MailService::scheduleSend with QTimer; wire both composer paths - Reader: show real 'Para:' recipient; delete attachment removes stored file
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
<file alias="reply.svg">resources/icons/SVG/Linear/Arrows Action/Reply.svg</file>
|
||||
<file alias="forward.svg">resources/icons/SVG/Linear/Arrows Action/Forward.svg</file>
|
||||
<file alias="trash.svg">resources/icons/SVG/Linear/Essentional, UI/Trash Bin Minimalistic.svg</file>
|
||||
<file alias="detach.svg">resources/icons/SVG/Linear/Arrows Action/Square Top Down.svg</file>
|
||||
<file alias="filter.svg">resources/icons/SVG/Linear/Design, Tools/Filters.svg</file>
|
||||
<file alias="play.svg">resources/icons/SVG/Linear/Time/Stopwatch Play.svg</file>
|
||||
<file alias="file-text.svg">resources/icons/SVG/Linear/Files/File Text.svg</file>
|
||||
|
||||
@@ -353,6 +353,39 @@ void MailService::sendMail(const MailItem &mail, const QString &accountId,
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
void MailService::scheduleSend(const MailItem &mail, const QString &accountId,
|
||||
const QStringList &attachmentPaths, const QDateTime &when)
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
if (!when.isValid() || when <= now) {
|
||||
// Send immediately
|
||||
sendMail(mail, accountId, attachmentPaths);
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingMail = mail;
|
||||
m_pendingAccountId = accountId;
|
||||
m_pendingAttachments = attachmentPaths;
|
||||
|
||||
int ms = qMax(1000, int(now.msecsTo(when)));
|
||||
m_sendTimer.stop();
|
||||
// Re-arm the single-shot timer with the delay until `when`.
|
||||
m_sendTimer.disconnect(this);
|
||||
connect(&m_sendTimer, &QTimer::timeout, this, [this]() {
|
||||
MailItem mail = m_pendingMail;
|
||||
QString accountId = m_pendingAccountId;
|
||||
QStringList attachments = m_pendingAttachments;
|
||||
m_pendingMail = MailItem();
|
||||
m_pendingAccountId.clear();
|
||||
m_pendingAttachments.clear();
|
||||
sendMail(mail, accountId, attachments);
|
||||
});
|
||||
m_sendTimer.start(ms);
|
||||
|
||||
emit statusMessage(
|
||||
tr("Envío programado para %1").arg(when.toString("dd/MM/yyyy HH:mm")));
|
||||
}
|
||||
|
||||
void MailService::fetchMails(const QString &accountId, const QString &folderId)
|
||||
{
|
||||
// Determine provider type from account
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QVector>
|
||||
#include <QSslSocket>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include "core/mailitem.h"
|
||||
#include "core/emailcomposerbridge.h"
|
||||
#include "core/synchronizerprovider.h"
|
||||
@@ -24,6 +25,9 @@ public:
|
||||
void sendMail(const MailItem &mail, const QString &accountId);
|
||||
void sendMail(const MailItem &mail, const QString &accountId,
|
||||
const QStringList &attachmentPaths);
|
||||
/// Schedule a mail to be sent at `when`. Emits mailSent(mailItemId) when sent.
|
||||
void scheduleSend(const MailItem &mail, const QString &accountId,
|
||||
const QStringList &attachmentPaths, const QDateTime &when);
|
||||
void fetchMails(const QString &accountId, const QString &folderId);
|
||||
void moveMail(const QString &mailItemId, const QString &targetFolderId);
|
||||
void deleteMail(const QString &mailItemId);
|
||||
@@ -50,6 +54,10 @@ private:
|
||||
const QString &folderId);
|
||||
EmailComposerBridge *m_composer;
|
||||
AccountService *m_accountService;
|
||||
QTimer m_sendTimer{this}; // one-shot timer driving scheduled sends
|
||||
MailItem m_pendingMail;
|
||||
QString m_pendingAccountId;
|
||||
QStringList m_pendingAttachments;
|
||||
};
|
||||
|
||||
#endif // MAILSERVICE_H
|
||||
|
||||
@@ -292,7 +292,7 @@ void ComposeView::setupUI() {
|
||||
ccLayout->addWidget(m_ccField, 1);
|
||||
|
||||
m_hideCcButton = new QPushButton("");
|
||||
m_hideCcButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Essentional, UI/Close Square.svg")));
|
||||
m_hideCcButton->setIcon(QIcon(QStringLiteral(":/icons/close.svg")));
|
||||
m_hideCcButton->setFixedSize(20, 20);
|
||||
m_hideCcButton->setToolTip(tr("Hide Cc"));
|
||||
m_hideCcButton->setStyleSheet(
|
||||
@@ -319,7 +319,7 @@ void ComposeView::setupUI() {
|
||||
|
||||
m_hideBccButton = new QPushButton("");
|
||||
m_hideBccButton->setFixedSize(20, 20);
|
||||
m_hideBccButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Essentional, UI/Close Square.svg")));
|
||||
m_hideBccButton->setIcon(QIcon(QStringLiteral(":/icons/close.svg")));
|
||||
m_hideBccButton->setToolTip(tr("Hide Bcc"));
|
||||
m_hideBccButton->setStyleSheet(
|
||||
"QPushButton { background: transparent; border: none; color: blue; font-weight: bold; }"
|
||||
@@ -348,7 +348,7 @@ void ComposeView::setupUI() {
|
||||
|
||||
// Detach button placed to the right of the subject line
|
||||
m_detachButton = new QPushButton("");//("↥ Detach");
|
||||
m_detachButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Linear/Arrows Action/Square Top Down.svg")));
|
||||
m_detachButton->setIcon(QIcon(QStringLiteral(":/icons/detach.svg")));
|
||||
m_detachButton->setToolTip(tr("Open compose window in a separate window"));
|
||||
m_detachButton->setStyleSheet(
|
||||
"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 12px; color: #555; font-size: 12px; }"
|
||||
@@ -421,7 +421,7 @@ void ComposeView::setupUI() {
|
||||
// Attachments and Templates buttons (right-aligned)
|
||||
QHBoxLayout *buttonRow = new QHBoxLayout();
|
||||
m_attachButton = new QToolButton();
|
||||
m_attachButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Messages, Conversation/Paperclip.svg")));
|
||||
m_attachButton->setIcon(QIcon(QStringLiteral(":/icons/attachment.svg")));
|
||||
m_attachButton->setToolTip(tr("Add attachment"));
|
||||
m_attachButton->setIconSize(QSize(20,20));
|
||||
m_attachButton->setStyleSheet("QToolButton { border: none; padding: 5px; } QToolButton:hover { background: #e0e0e0; border-radius: 3px; }");
|
||||
@@ -429,7 +429,7 @@ void ComposeView::setupUI() {
|
||||
buttonRow->addWidget(m_attachButton);
|
||||
|
||||
m_templateButton = new QToolButton();
|
||||
m_templateButton->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Files/File Text.svg"))); // Assuming you have a template icon
|
||||
m_templateButton->setIcon(QIcon(QStringLiteral(":/icons/file-text.svg"))); // Assuming you have a template icon
|
||||
m_templateButton->setToolTip(tr("Email templates"));
|
||||
m_templateButton->setIconSize(QSize(20,20));
|
||||
m_templateButton->setStyleSheet("QToolButton { border: none; padding: 5px; } QToolButton:hover { background: #e0e0e0; border-radius: 3px; }");
|
||||
@@ -459,7 +459,7 @@ void ComposeView::setupUI() {
|
||||
|
||||
m_sendSplit = new QToolButton();
|
||||
m_sendSplit->setText(tr("Send"));
|
||||
m_sendSplit->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Bold Duotone/Messages, Conversation/Plain.svg")));
|
||||
m_sendSplit->setIcon(QIcon(QStringLiteral(":/icons/send.svg")));
|
||||
m_templateButton->setIconSize(QSize(20,20));
|
||||
//m_sendSplit->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
m_sendSplit->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CompactMailDelegate.h"
|
||||
#include "db/dao/categorydao.h"
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QCursor>
|
||||
@@ -269,8 +270,38 @@ void CompactMailDelegate::drawCard(QPainter *painter, const QStyleOptionViewItem
|
||||
painter->drawText(QRect(x, line3Y, previewMaxWidth, previewFont.pointSizeF()), Qt::AlignLeft | Qt::AlignVCenter, elidedPreview);
|
||||
}
|
||||
|
||||
// Categories (if enabled and present) - simplified for now
|
||||
// TODO: Add category support from model
|
||||
// Category badges (if any)
|
||||
int mailId = index.data(EmailListModel::IdRole).toInt();
|
||||
QVector<qint64> catIds = CategoryDao::categoriesForMail(mailId);
|
||||
if (!catIds.isEmpty() && catIds.size() <= 3 && m_config.displayMode != Compact) {
|
||||
int catY = line2Y + subjectFont.pointSizeF() + (m_config.showPreviewText && m_config.displayMode != Compact
|
||||
? previewFont.pointSizeF() + 8 : 6);
|
||||
int cx = contentRect.left() + (m_config.showAvatar ? m_config.avatarSize + 10 : 0);
|
||||
QFont catFont = subjectFont;
|
||||
catFont.setPointSizeF(qMax(6.5, subjectFont.pointSizeF() - 3.0));
|
||||
catFont.setBold(true);
|
||||
painter->setFont(catFont);
|
||||
int maxCatWidth = availableWidth;
|
||||
for (qint64 cid : catIds) {
|
||||
auto cat = CategoryDao::findById(cid);
|
||||
if (!cat.has_value()) continue;
|
||||
QString label = " " + cat->name + " ";
|
||||
QColor catColor = cat->color.isValid() ? cat->color : QColor(0x00, 0x71, 0xe3);
|
||||
QFontMetrics fm(catFont);
|
||||
int w = fm.horizontalAdvance(label) + 8;
|
||||
if (maxCatWidth < w) break;
|
||||
QRect chip(cx, catY, w, fm.height() + 4);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
QColor fill = catColor; fill.setAlpha(45);
|
||||
painter->setPen(catColor);
|
||||
painter->setBrush(fill);
|
||||
painter->drawRoundedRect(chip, 4, 4);
|
||||
painter->setPen(catColor);
|
||||
painter->drawText(chip.adjusted(4, 2, -2, 0), Qt::AlignLeft | Qt::AlignVCenter, label.trimmed());
|
||||
cx += w + 6;
|
||||
maxCatWidth -= w + 6;
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons (bottom)
|
||||
int buttonAreaY = cardRect.bottom() - 28;
|
||||
|
||||
@@ -27,6 +27,8 @@ public:
|
||||
~MailListView() override;
|
||||
|
||||
void setModel(EmailListModel *model);
|
||||
/// Currently selected mail ids (multi-selection or current row).
|
||||
QVector<int> selectedMailIds() const { return m_selectedMailIds; }
|
||||
QTreeView* treeView() const { return m_treeView; }
|
||||
QListView* listView() const { return m_listView; }
|
||||
|
||||
|
||||
+59
-12
@@ -108,10 +108,6 @@ void MainMainWindow::setupUI()
|
||||
// Page 1: Compose
|
||||
m_composeView = new ComposeView();
|
||||
connect(m_composeView, &ComposeView::sendRequested, [this](const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QDateTime &scheduleTime, const QString &fromAddr, const QStringList &attachmentPaths) {
|
||||
if (scheduleTime.isValid()) {
|
||||
statusBar()->showMessage(tr("Scheduled sending is not available yet"), 5000);
|
||||
return;
|
||||
}
|
||||
if (fromAddr.isEmpty()) {
|
||||
statusBar()->showMessage(tr("Select an account before sending"), 5000);
|
||||
return;
|
||||
@@ -124,8 +120,13 @@ void MainMainWindow::setupUI()
|
||||
mail.setSubject(subject);
|
||||
mail.setBodyHtml(body);
|
||||
mail.setDate(QDateTime::currentDateTimeUtc());
|
||||
if (scheduleTime.isValid()) {
|
||||
m_mailService->scheduleSend(mail, fromAddr, attachmentPaths, scheduleTime);
|
||||
statusBar()->showMessage(tr("Envío programado"), 5000);
|
||||
} else {
|
||||
m_mailService->sendMail(mail, fromAddr, attachmentPaths);
|
||||
statusBar()->showMessage(tr("Sending message…"), 5000);
|
||||
}
|
||||
switchToPage(PageMail);
|
||||
});
|
||||
connect(m_composeView, &ComposeView::discardRequested, [this]() {
|
||||
@@ -305,6 +306,7 @@ void MainMainWindow::setupMailPage()
|
||||
connect(m_emailViewer, &ReaderView::forwardRequested, this, &MainMainWindow::onReaderForwardRequested);
|
||||
connect(m_emailViewer, &ReaderView::deleteRequested, this, &MainMainWindow::onReaderDeleteRequested);
|
||||
connect(m_emailViewer, &ReaderView::markUnreadRequested, this, &MainMainWindow::onMarkUnreadRequested);
|
||||
connect(m_emailViewer, &ReaderView::markSpamRequested, this, &MainMainWindow::onMarkSpamRequested);
|
||||
connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() {
|
||||
if (m_currentMailId >= 0) {
|
||||
openMailInIndependentWindow(m_currentMailId);
|
||||
@@ -685,6 +687,39 @@ void MainMainWindow::onMarkUnreadRequested(int mailId)
|
||||
}
|
||||
}
|
||||
|
||||
void MainMainWindow::onMarkSpamRequested(int mailId)
|
||||
{
|
||||
std::optional<MailItem> optMail = MailItemDao::findById(mailId);
|
||||
if (!optMail) return;
|
||||
std::optional<Folder> curFolder = FolderDao::findById(optMail->folderId());
|
||||
if (!curFolder) return;
|
||||
int accountId = curFolder->accountId();
|
||||
|
||||
// Find a Spam/Junk folder for this account.
|
||||
QVector<Folder> folders = FolderDao::findByAccountId(accountId);
|
||||
int spamFolderId = -1;
|
||||
for (const Folder &f : folders) {
|
||||
QString n = f.name().toLower();
|
||||
if (n == "spam" || n == "junk" || n.contains("spam") || n.contains("junk")) {
|
||||
spamFolderId = f.id();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (spamFolderId < 0) {
|
||||
QMessageBox::information(this, tr("Marcar como spam"),
|
||||
tr("No hay una carpeta de Spam en esta cuenta. Mueve el correo manualmente con «Mover a...»."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_mailService->moveMail(QString::number(mailId), QString::number(spamFolderId));
|
||||
if (m_currentMailId == mailId) m_currentMailId = -1;
|
||||
m_emailModel->refresh();
|
||||
m_mailListView->treeView()->clearSelection();
|
||||
m_viewerStack->setCurrentIndex(0);
|
||||
statusBar()->showMessage(tr("Marcado como spam y movido a la carpeta de spam"), 3000);
|
||||
}
|
||||
|
||||
void MainMainWindow::onNewMessage()
|
||||
{ // Show compose view in the viewer stack
|
||||
m_embeddedComposeView->initializeComposition();
|
||||
@@ -784,10 +819,21 @@ void MainMainWindow::onRunRulesOnFolder()
|
||||
|
||||
void MainMainWindow::onRunRulesOnSelected()
|
||||
{
|
||||
// Get selected mail IDs
|
||||
QMessageBox::information(this, tr("Ejecutar reglas"), tr("Ejecutando reglas en correos seleccionados..."));
|
||||
// TODO: Get selected mail IDs from MailListView
|
||||
statusBar()->showMessage(tr("Reglas ejecutadas"), 3000);
|
||||
QVector<int> ids = m_mailListView->selectedMailIds();
|
||||
if (ids.isEmpty() && m_currentMailId >= 0) {
|
||||
ids.append(m_currentMailId);
|
||||
}
|
||||
if (ids.isEmpty()) {
|
||||
statusBar()->showMessage(tr("Selecciona al menos un correo"), 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<qint64> mailIds;
|
||||
for (int id : ids) mailIds.append(id);
|
||||
int processed = RulesEngine::runRulesOnMails(mailIds);
|
||||
statusBar()->showMessage(tr("Reglas ejecutadas en %1 correos").arg(processed), 3000);
|
||||
m_emailModel->refresh();
|
||||
m_categoryTree->refresh();
|
||||
}
|
||||
|
||||
void MainMainWindow::showRulesManager()
|
||||
@@ -1197,10 +1243,6 @@ void MainMainWindow::onEmbeddedSendRequested(const QString &to, const QString &c
|
||||
const QString &fromAddress,
|
||||
const QStringList &attachmentPaths)
|
||||
{
|
||||
if (scheduleTime.isValid()) {
|
||||
statusBar()->showMessage(tr("Scheduled sending is not available yet"), 5000);
|
||||
return;
|
||||
}
|
||||
if (fromAddress.isEmpty()) {
|
||||
statusBar()->showMessage(tr("Select an account before sending"), 5000);
|
||||
return;
|
||||
@@ -1213,8 +1255,13 @@ void MainMainWindow::onEmbeddedSendRequested(const QString &to, const QString &c
|
||||
mail.setSubject(subject);
|
||||
mail.setBodyHtml(body);
|
||||
mail.setDate(QDateTime::currentDateTimeUtc());
|
||||
if (scheduleTime.isValid()) {
|
||||
m_mailService->scheduleSend(mail, fromAddress, attachmentPaths, scheduleTime);
|
||||
statusBar()->showMessage(tr("Envío programado"), 5000);
|
||||
} else {
|
||||
m_mailService->sendMail(mail, fromAddress, attachmentPaths);
|
||||
statusBar()->showMessage(tr("Sending message…"), 5000);
|
||||
}
|
||||
m_viewerStack->setCurrentIndex(0); // placeholder
|
||||
m_embeddedComposeView->initializeComposition();
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ private slots:
|
||||
void onBatchDeleteRequested(const QVector<int> &mailIds);
|
||||
void onBatchMarkUnreadRequested(const QVector<int> &mailIds);
|
||||
void onBatchFlagRequested(const QVector<int> &mailIds, bool flagged);
|
||||
void onMarkSpamRequested(int mailId);
|
||||
void onMarkUnreadRequested(int mailId);
|
||||
// Online search
|
||||
void onOnlineSearchRequested(const QString& query);
|
||||
|
||||
+23
-6
@@ -22,6 +22,7 @@
|
||||
#include <QScrollBar>
|
||||
#include <QShortcut>
|
||||
#include <QFileDialog>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <functional>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
@@ -370,8 +371,7 @@ void ReaderView::setupUI() {
|
||||
});
|
||||
moreMenu->addAction("Marcar como spam", [this]() {
|
||||
if (m_currentMailId < 0) return;
|
||||
QMessageBox::information(this, tr("Marcar como spam"),
|
||||
tr("Función: mover a carpeta «Spam». Disponible en una próxima versión."));
|
||||
emit markSpamRequested(m_currentMailId);
|
||||
});
|
||||
m_moreButton->setMenu(moreMenu);
|
||||
m_moreButton->setStyleSheet(
|
||||
@@ -729,8 +729,23 @@ void ReaderView::showAttachmentContextMenu(const QPoint &pos, const QString &pat
|
||||
|
||||
menu.addSeparator();
|
||||
QAction *deleteAct = menu.addAction("Eliminar adjunto", [this, name]() {
|
||||
// TODO: Implement attachment deletion from stored mail
|
||||
QMessageBox::information(this, "No implementado", "Eliminar adjuntos del almacenamiento local aún no está implementado.");
|
||||
if (m_currentMailId < 0) return;
|
||||
QVector<StoredAttachmentRecord> all = MailItemDao::attachmentsForMail(m_currentMailId);
|
||||
QVector<StoredAttachmentRecord> remaining;
|
||||
// Delete the matching stored file and keep the others.
|
||||
for (const StoredAttachmentRecord &a : all) {
|
||||
if (a.fileName == name) {
|
||||
if (!a.storedPath.isEmpty())
|
||||
QFile::remove(a.storedPath);
|
||||
} else {
|
||||
remaining.append(a);
|
||||
}
|
||||
}
|
||||
if (MailItemDao::replaceAttachments(m_currentMailId, remaining)) {
|
||||
QMessageBox::information(this, "Adjunto", tr("Adjunto «%1» eliminado.").arg(name));
|
||||
} else {
|
||||
QMessageBox::warning(this, "Adjunto", tr("No se pudo eliminar el adjunto."));
|
||||
}
|
||||
});
|
||||
deleteAct->setIcon(QIcon::fromTheme("edit-delete", QApplication::style()->standardIcon(QStyle::SP_TrashIcon)));
|
||||
|
||||
@@ -776,8 +791,10 @@ void ReaderView::setMailItem(const MailItem* item) {
|
||||
// From
|
||||
m_fromLabel->setText("De: " + formatAddress(sender));
|
||||
|
||||
// To - MailItem doesn't have recipients easily accessible, skip for now
|
||||
m_toLabel->setText("Para: —"); // TODO: fetch from MailItem or DB
|
||||
// To - prefer recipient()/to(), fallback to placeholder
|
||||
QString toText = item->recipient();
|
||||
if (toText.isEmpty()) toText = item->to();
|
||||
m_toLabel->setText("Para: " + (toText.isEmpty() ? QString("—") : formatAddress(toText)));
|
||||
|
||||
// Date
|
||||
m_dateLabel->setText("Fecha: " + formatDate(item->date()));
|
||||
|
||||
@@ -30,6 +30,7 @@ signals:
|
||||
void deleteRequested(int mailId);
|
||||
void detachRequested();
|
||||
void markUnreadRequested(int mailId);
|
||||
void markSpamRequested(int mailId);
|
||||
void openAttachmentRequested(const QString &path);
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -68,19 +68,19 @@ void RichTextEditor::setupToolbar(QVBoxLayout *layout) {
|
||||
// Bold / Italic / Underline
|
||||
QAction *boldAct = m_toolbar->addAction("B");
|
||||
boldAct->setCheckable(true);
|
||||
boldAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Bold/Text Formatting/Text Bold.svg")));
|
||||
boldAct->setIcon(QIcon(QStringLiteral(":/icons/bold.svg")));
|
||||
QFont boldFont = boldAct->font(); boldFont.setBold(true); boldAct->setFont(boldFont);
|
||||
connect(boldAct, &QAction::triggered, this, &RichTextEditor::onBold);
|
||||
|
||||
QAction *italicAct = m_toolbar->addAction("I");
|
||||
italicAct->setCheckable(true);
|
||||
italicAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Text Formatting/Text Italic.svg")));
|
||||
italicAct->setIcon(QIcon(QStringLiteral(":/icons/italic.svg")));
|
||||
QFont italicFont = italicAct->font(); italicFont.setItalic(true); italicAct->setFont(italicFont);
|
||||
connect(italicAct, &QAction::triggered, this, &RichTextEditor::onItalic);
|
||||
|
||||
QAction *underlineAct = m_toolbar->addAction("U");
|
||||
underlineAct->setCheckable(true);
|
||||
underlineAct->setIcon(QIcon(QStringLiteral(":/icons/resources/icons/SVG/Outline/Text Formatting/Text Underline.svg")));
|
||||
underlineAct->setIcon(QIcon(QStringLiteral(":/icons/underline.svg")));
|
||||
QFont uFont = underlineAct->font(); uFont.setUnderline(true); underlineAct->setFont(uFont);
|
||||
connect(underlineAct, &QAction::triggered, this, &RichTextEditor::onUnderline);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "rulesmanagerdialog.h"
|
||||
#include "ruleditordialog.h"
|
||||
#include "db/dao/ruledao.h"
|
||||
#include "db/dao/common_structs.h"
|
||||
#include "db/dao/categorydao.h"
|
||||
#include "db/dao/folderdao.h"
|
||||
#include "services/rulesengine.h"
|
||||
@@ -81,7 +82,7 @@ void RulesManagerDialog::setupUI()
|
||||
if (idx > 0) {
|
||||
QTreeWidgetItem* prev = m_tree->takeTopLevelItem(idx);
|
||||
m_tree->insertTopLevelItem(idx - 1, prev);
|
||||
// TODO: update priority in DB
|
||||
persistPriorityOrder();
|
||||
}
|
||||
});
|
||||
QAction* moveDownAct = menu.addAction(tr("Bajar prioridad"), [this, item]() {
|
||||
@@ -89,7 +90,7 @@ void RulesManagerDialog::setupUI()
|
||||
if (idx < m_tree->topLevelItemCount() - 1) {
|
||||
QTreeWidgetItem* next = m_tree->takeTopLevelItem(idx + 1);
|
||||
m_tree->insertTopLevelItem(idx + 1, item);
|
||||
// TODO: update priority in DB
|
||||
persistPriorityOrder();
|
||||
}
|
||||
});
|
||||
menu.exec(m_tree->viewport()->mapToGlobal(pos));
|
||||
@@ -296,3 +297,20 @@ void RulesManagerDialog::refresh()
|
||||
{
|
||||
loadRules();
|
||||
}
|
||||
|
||||
void RulesManagerDialog::persistPriorityOrder()
|
||||
{
|
||||
// Re-assign priority based on tree order (top = lowest number = highest priority).
|
||||
for (int i = 0; i < m_tree->topLevelItemCount(); ++i) {
|
||||
QTreeWidgetItem *item = m_tree->topLevelItem(i);
|
||||
qint64 ruleId = item->data(0, Qt::UserRole).toLongLong();
|
||||
int newPriority = i + 1;
|
||||
item->setText(2, QString::number(newPriority));
|
||||
auto rule = RuleDao::findById(ruleId);
|
||||
if (rule.has_value()) {
|
||||
Rule r = rule.value();
|
||||
r.priority = newPriority;
|
||||
RuleDao::update(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,5 @@ private:
|
||||
void loadRules();
|
||||
void addRuleToTree(const Rule& rule);
|
||||
Rule itemToRule(QTreeWidgetItem* item) const;
|
||||
void persistPriorityOrder();
|
||||
};
|
||||
Reference in New Issue
Block a user