Files
wino-mail-dtkqt/src/ui/rulesmanagerdialog.cpp
T

299 lines
12 KiB
C++
Raw Normal View History

2026-08-23 14:49:58 +02:00
#include "rulesmanagerdialog.h"
#include "ruleditordialog.h"
#include "db/dao/ruledao.h"
#include "db/dao/categorydao.h"
#include "db/dao/folderdao.h"
#include "services/rulesengine.h"
#include "services/accountservice.h"
#include <QHeaderView>
#include <QMessageBox>
#include <QInputDialog>
#include <QFileDialog>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QMenu>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
RulesManagerDialog::RulesManagerDialog(qint64 accountId, QWidget *parent)
: QDialog(parent), m_accountId(accountId)
{
setWindowTitle(m_accountId >= 0 ? tr("Reglas de la cuenta") : tr("Reglas globales"));
resize(900, 600);
setupUI();
loadRules();
}
void RulesManagerDialog::setupUI()
{
QVBoxLayout* mainLayout = new QVBoxLayout(this);
// Toolbar
QHBoxLayout* toolLayout = new QHBoxLayout();
m_btnNew = new QPushButton(tr("Nueva regla"));
m_btnRun = new QPushButton(tr("Ejecutar ahora"));
m_btnImport = new QPushButton(tr("Importar"));
m_btnExport = new QPushButton(tr("Exportar"));
m_btnClose = new QPushButton(tr("Cerrar"));
m_btnNew->setStyleSheet("QPushButton { background: #1976D2; color: white; font-weight: bold; padding: 6px 12px; border-radius: 4px; } QPushButton:hover { background: #1565C0; }");
m_btnRun->setStyleSheet("QPushButton { background: #4CAF50; color: white; padding: 6px 12px; border-radius: 4px; } QPushButton:hover { background: #43A047; }");
toolLayout->addWidget(m_btnNew);
toolLayout->addWidget(m_btnRun);
toolLayout->addStretch();
toolLayout->addWidget(m_btnImport);
toolLayout->addWidget(m_btnExport);
toolLayout->addWidget(m_btnClose);
mainLayout->addLayout(toolLayout);
// Tree widget
m_tree = new QTreeWidget();
m_tree->setHeaderLabels({tr("Activa"), tr("Nombre"), tr("Prioridad"), tr("Condiciones"), tr("Acciones"), tr("Cuenta"), tr("Última ejecución"), tr("Veces")});
m_tree->setAlternatingRowColors(true);
m_tree->setSelectionBehavior(QAbstractItemView::SelectRows);
m_tree->setSelectionMode(QAbstractItemView::SingleSelection);
m_tree->setContextMenuPolicy(Qt::CustomContextMenu);
m_tree->header()->setStretchLastSection(false);
m_tree->header()->setSectionResizeMode(1, QHeaderView::Stretch);
m_tree->setColumnWidth(0, 60);
m_tree->setColumnWidth(2, 80);
m_tree->setColumnWidth(3, 200);
m_tree->setColumnWidth(4, 200);
m_tree->setColumnWidth(5, 100);
m_tree->setColumnWidth(6, 140);
m_tree->setColumnWidth(7, 60);
connect(m_tree, &QTreeWidget::customContextMenuRequested, this, [this](const QPoint& pos) {
QTreeWidgetItem* item = m_tree->itemAt(pos);
if (!item) return;
QMenu menu(this);
QAction* editAct = menu.addAction(tr("Editar"), [this, item]() { onEditRule(item); });
QAction* delAct = menu.addAction(tr("Eliminar"), [this, item]() { onDeleteRule(item); });
menu.addSeparator();
QAction* toggleAct = menu.addAction(item->checkState(0) == Qt::Checked ? tr("Desactivar") : tr("Activar"), [this, item]() { onToggleEnabled(item); });
menu.addSeparator();
QAction* moveUpAct = menu.addAction(tr("Subir prioridad"), [this, item]() {
int idx = m_tree->indexOfTopLevelItem(item);
if (idx > 0) {
QTreeWidgetItem* prev = m_tree->takeTopLevelItem(idx);
m_tree->insertTopLevelItem(idx - 1, prev);
// TODO: update priority in DB
}
});
QAction* moveDownAct = menu.addAction(tr("Bajar prioridad"), [this, item]() {
int idx = m_tree->indexOfTopLevelItem(item);
if (idx < m_tree->topLevelItemCount() - 1) {
QTreeWidgetItem* next = m_tree->takeTopLevelItem(idx + 1);
m_tree->insertTopLevelItem(idx + 1, item);
// TODO: update priority in DB
}
});
menu.exec(m_tree->viewport()->mapToGlobal(pos));
});
connect(m_tree, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* item) { onEditRule(item); });
mainLayout->addWidget(m_tree, 1);
// Status
QLabel* statusLabel = new QLabel(tr("Doble click para editar. Click derecho para más opciones."));
statusLabel->setStyleSheet("color: #666; font-size: 11px; padding: 4px;");
mainLayout->addWidget(statusLabel);
// Connect buttons
connect(m_btnNew, &QPushButton::clicked, this, &RulesManagerDialog::onNewRule);
connect(m_btnRun, &QPushButton::clicked, this, &RulesManagerDialog::onRunRules);
connect(m_btnImport, &QPushButton::clicked, this, [this]() { onImportExport(true); });
connect(m_btnExport, &QPushButton::clicked, this, [this]() { onImportExport(false); });
connect(m_btnClose, &QPushButton::clicked, this, &QDialog::accept);
}
void RulesManagerDialog::loadRules()
{
m_tree->clear();
QVector<Rule> rules = RuleDao::findByAccount(m_accountId);
for (const Rule& rule : rules) {
addRuleToTree(rule);
}
}
void RulesManagerDialog::addRuleToTree(const Rule& rule)
{
QTreeWidgetItem* item = new QTreeWidgetItem(m_tree);
item->setData(0, Qt::UserRole, rule.id);
item->setCheckState(0, rule.enabled ? Qt::Checked : Qt::Unchecked);
item->setText(1, rule.name);
item->setText(2, QString::number(rule.priority));
// Conditions summary
QStringList condTexts;
for (const RuleCondition& cond : rule.conditions) {
QString fieldName;
switch (cond.field) {
case RuleCondition::From: fieldName = "De"; break;
case RuleCondition::To: fieldName = "Para"; break;
case RuleCondition::Cc: fieldName = "CC"; break;
case RuleCondition::Subject: fieldName = "Asunto"; break;
case RuleCondition::Body: fieldName = "Cuerpo"; break;
case RuleCondition::HasAttachment: fieldName = "Tiene adjuntos"; break;
case RuleCondition::Size: fieldName = "Tamaño"; break;
case RuleCondition::Date: fieldName = "Fecha"; break;
case RuleCondition::Flagged: fieldName = "Marcado"; break;
case RuleCondition::Read: fieldName = "Leído"; break;
}
QString opName;
switch (cond.op) {
case RuleCondition::Contains: opName = "contiene"; break;
case RuleCondition::NotContains: opName = "no contiene"; break;
case RuleCondition::Equals: opName = "es"; break;
case RuleCondition::NotEquals: opName = "no es"; break;
case RuleCondition::StartsWith: opName = "empieza por"; break;
case RuleCondition::EndsWith: opName = "termina en"; break;
case RuleCondition::Regex: opName = "regex"; break;
case RuleCondition::GreaterThan: opName = ">"; break;
case RuleCondition::LessThan: opName = "<"; break;
case RuleCondition::Before: opName = "antes de"; break;
case RuleCondition::After: opName = "después de"; break;
}
condTexts.append(QString("%1 %2 \"%3\"").arg(fieldName, opName, cond.value));
}
item->setText(3, condTexts.join(rule.matchAll ? " Y " : " O "));
// Actions summary
QStringList actTexts;
for (const RuleAction& act : rule.actions) {
QString actName;
switch (act.type) {
case RuleAction::MoveToFolder: actName = QString("Mover a carpeta %1").arg(act.value); break;
case RuleAction::MarkAsRead: actName = QString("Marcar como %1").arg(act.value == "false" ? "no leído" : "leído"); break;
case RuleAction::MarkAsFlagged: actName = QString("Marcar como %1").arg(act.value == "false" ? "no marcado" : "marcado"); break;
case RuleAction::Delete: actName = "Eliminar"; break;
case RuleAction::AssignCategory: actName = QString("Categoría %1").arg(act.value); break;
case RuleAction::RemoveCategory: actName = QString("Quitar categoría %1").arg(act.value); break;
case RuleAction::ForwardTo: actName = QString("Reenviar a %1").arg(act.value); break;
case RuleAction::SetPriority: actName = QString("Prioridad %1").arg(act.value); break;
case RuleAction::StopProcessing: actName = "Detener procesamiento"; break;
}
actTexts.append(actName);
}
item->setText(4, actTexts.join(", "));
item->setText(5, rule.isGlobal() ? tr("Global") : QString::number(rule.accountId));
item->setText(6, rule.lastRun.isValid() ? rule.lastRun.toString("dd/MM/yyyy hh:mm") : tr("Nunca"));
item->setText(7, QString::number(rule.runCount));
if (!rule.enabled) {
for (int i = 0; i < m_tree->columnCount(); ++i) {
item->setForeground(i, QBrush(QColor("#999")));
}
}
}
Rule RulesManagerDialog::itemToRule(QTreeWidgetItem* item) const
{
qint64 id = item->data(0, Qt::UserRole).toLongLong();
auto ruleOpt = RuleDao::findById(id);
return ruleOpt.value_or(Rule());
}
void RulesManagerDialog::onNewRule()
{
RuleEditorDialog dlg(Rule(), m_accountId, this);
connect(&dlg, &RuleEditorDialog::ruleSaved, this, &RulesManagerDialog::onRuleSaved);
dlg.exec();
}
void RulesManagerDialog::onEditRule(QTreeWidgetItem* item)
{
Rule rule = itemToRule(item);
if (!rule.isValid()) return;
RuleEditorDialog dlg(rule, m_accountId, this);
connect(&dlg, &RuleEditorDialog::ruleSaved, this, &RulesManagerDialog::onRuleSaved);
dlg.exec();
}
void RulesManagerDialog::onRuleSaved(const Rule& rule)
{
refresh();
emit rulesChanged();
}
void RulesManagerDialog::onDeleteRule(QTreeWidgetItem* item)
{
Rule rule = itemToRule(item);
if (!rule.isValid()) return;
if (QMessageBox::question(this, tr("Eliminar regla"),
tr("¿Eliminar la regla \"%1\"?").arg(rule.name),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (RuleDao::remove(rule.id)) {
refresh();
emit rulesChanged();
}
}
}
void RulesManagerDialog::onToggleEnabled(QTreeWidgetItem* item)
{
Rule rule = itemToRule(item);
if (!rule.isValid()) return;
bool newState = item->checkState(0) != Qt::Checked;
if (RuleDao::setEnabled(rule.id, newState)) {
item->setCheckState(0, newState ? Qt::Checked : Qt::Unchecked);
if (!newState) {
for (int i = 0; i < m_tree->columnCount(); ++i) {
item->setForeground(i, QBrush(QColor("#999")));
}
} else {
for (int i = 0; i < m_tree->columnCount(); ++i) {
item->setForeground(i, QBrush(QColor("#000")));
}
}
emit rulesChanged();
}
}
void RulesManagerDialog::onRunRules()
{
// TODO: Get selected folder/account from main window
QMessageBox::information(this, tr("Ejecutar reglas"), tr("Seleccione una carpeta en la ventana principal y pulse 'Ejecutar reglas' en el menú de herramientas."));
}
void RulesManagerDialog::onImportExport(bool import)
{
if (import) {
QString file = QFileDialog::getOpenFileName(this, tr("Importar reglas"), QString(), tr("JSON (*.json)"));
if (file.isEmpty()) return;
// TODO: Parse JSON and create rules
QMessageBox::information(this, tr("Importar"), tr("Función de importación en desarrollo."));
} else {
QString file = QFileDialog::getSaveFileName(this, tr("Exportar reglas"), "rules.json", tr("JSON (*.json)"));
if (file.isEmpty()) return;
QVector<Rule> rules = RuleDao::findByAccount(m_accountId);
QJsonArray arr;
for (const Rule& rule : rules) arr.append(rule.toJson());
QJsonDocument doc(arr);
QFile f(file);
if (f.open(QIODevice::WriteOnly)) {
f.write(doc.toJson(QJsonDocument::Indented));
QMessageBox::information(this, tr("Exportar"), tr("Reglas exportadas a %1").arg(file));
}
}
}
void RulesManagerDialog::refresh()
{
loadRules();
}