feat: comprehensive Wino Mail updates
- ReaderView: complete rewrite with CSS styling, headers, attachments, zoom, find, dark mode, image blocking - Categories: DAO + UI tree widget with colors, nested categories, assignment - Rules engine: DAO + evaluation + actions (move, mark read, flag, delete, category, forward) - Templates: DAO + editor + manager with variables support - Signatures: DAO + manager + auto-per-account + manual selector in compose - ComposeView: signature combo, template selector, auto-signature on new email - MainWindow: frameless with rounded corners, 1px border, resize from edges, no status bar - Database: new tables (Category, MailCategory, Rule, Template, Signature) with indexes
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
#include "signaturemanagerdialog.h"
|
||||
#include "db/dao/accountdao.h"
|
||||
#include "services/accountservice.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QTextEdit>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
#include <QDebug>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QMenu>
|
||||
|
||||
SignatureEditorDialog::SignatureEditorDialog(const QString& signatureHtml, QWidget *parent)
|
||||
: QDialog(parent), m_signatureHtml(signatureHtml)
|
||||
{
|
||||
setWindowTitle(tr("Editor de firma"));
|
||||
resize(600, 400);
|
||||
setupUI();
|
||||
}
|
||||
|
||||
void SignatureEditorDialog::setupUI()
|
||||
{
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||
|
||||
// Name
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout();
|
||||
nameLayout->addWidget(new QLabel(tr("Nombre:")));
|
||||
m_nameEdit = new QLineEdit();
|
||||
m_nameEdit->setPlaceholderText(tr("Ej: Firma personal, Firma trabajo, etc."));
|
||||
nameLayout->addWidget(m_nameEdit);
|
||||
mainLayout->addLayout(nameLayout);
|
||||
|
||||
// Editor
|
||||
m_editor = new QTextEdit();
|
||||
m_editor->setAcceptRichText(true);
|
||||
m_editor->setHtml(m_signatureHtml);
|
||||
m_editor->setPlaceholderText(tr("Escribe tu firma aquí (HTML soportado)..."));
|
||||
mainLayout->addWidget(m_editor, 1);
|
||||
|
||||
// Buttons
|
||||
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &SignatureEditorDialog::onAccept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
}
|
||||
|
||||
void SignatureEditorDialog::onAccept()
|
||||
{
|
||||
if (m_nameEdit->text().trimmed().isEmpty()) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("El nombre de la firma es obligatorio."));
|
||||
m_nameEdit->setFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
m_signatureHtml = m_editor->toHtml();
|
||||
emit signatureSaved(m_nameEdit->text().trimmed(), m_signatureHtml);
|
||||
accept();
|
||||
}
|
||||
|
||||
SignatureManagerDialog::SignatureManagerDialog(AccountService* accountService, qint64 accountId, QWidget *parent)
|
||||
: QDialog(parent), m_accountService(accountService), m_accountId(accountId)
|
||||
{
|
||||
setWindowTitle(tr("Gestor de firmas"));
|
||||
resize(500, 400);
|
||||
setupUI();
|
||||
loadSignatures();
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::setupUI()
|
||||
{
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||
|
||||
// Header
|
||||
QLabel* titleLabel = new QLabel(tr("Firmas disponibles"));
|
||||
titleLabel->setStyleSheet("font-weight: bold; font-size: 14px; padding: 8px;");
|
||||
mainLayout->addWidget(titleLabel);
|
||||
|
||||
// List
|
||||
m_list = new QListWidget();
|
||||
m_list->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_list, &QListWidget::customContextMenuRequested, this, [this](const QPoint& pos) {
|
||||
QListWidgetItem* item = m_list->itemAt(pos);
|
||||
if (!item) return;
|
||||
|
||||
QMenu menu(this);
|
||||
QAction* editAct = menu.addAction(tr("Editar"), [this, item]() { onEditSignature(item); });
|
||||
QAction* delAct = menu.addAction(tr("Eliminar"), [this, item]() { onDeleteSignature(item); });
|
||||
menu.exec(m_list->viewport()->mapToGlobal(pos));
|
||||
});
|
||||
connect(m_list, &QListWidget::itemClicked, this, &SignatureManagerDialog::onSignatureSelected);
|
||||
connect(m_list, &QListWidget::itemDoubleClicked, this, &SignatureManagerDialog::onUseSignature);
|
||||
mainLayout->addWidget(m_list, 1);
|
||||
|
||||
// Toolbar
|
||||
QHBoxLayout* toolLayout = new QHBoxLayout();
|
||||
m_btnNew = new QPushButton(tr("Nueva firma"));
|
||||
m_btnEdit = new QPushButton(tr("Editar"));
|
||||
m_btnDelete = new QPushButton(tr("Eliminar"));
|
||||
m_btnUse = new QPushButton(tr("Usar"));
|
||||
m_btnClose = new QPushButton(tr("Cerrar"));
|
||||
|
||||
m_btnNew->setStyleSheet("QPushButton { background: #1976D2; color: white; font-weight: bold; padding: 6px 12px; border-radius: 4px; }");
|
||||
m_btnUse->setStyleSheet("QPushButton { background: #4CAF50; color: white; padding: 6px 12px; border-radius: 4px; }");
|
||||
m_btnDelete->setStyleSheet("QPushButton { color: #d32f2f; }");
|
||||
|
||||
toolLayout->addWidget(m_btnNew);
|
||||
toolLayout->addWidget(m_btnEdit);
|
||||
toolLayout->addWidget(m_btnDelete);
|
||||
toolLayout->addStretch();
|
||||
toolLayout->addWidget(m_btnUse);
|
||||
toolLayout->addWidget(m_btnClose);
|
||||
mainLayout->addLayout(toolLayout);
|
||||
|
||||
// Status
|
||||
QLabel* statusLabel = new QLabel(tr("Selecciona una firma y pulsa 'Usar' para insertarla en el correo."));
|
||||
statusLabel->setStyleSheet("color: #666; font-size: 11px; padding: 4px;");
|
||||
mainLayout->addWidget(statusLabel);
|
||||
|
||||
// Connect
|
||||
connect(m_btnNew, &QPushButton::clicked, this, &SignatureManagerDialog::onNewSignature);
|
||||
connect(m_btnEdit, &QPushButton::clicked, this, [this]() { onEditSignature(m_list->currentItem()); });
|
||||
connect(m_btnDelete, &QPushButton::clicked, this, [this]() { onDeleteSignature(m_list->currentItem()); });
|
||||
connect(m_btnUse, &QPushButton::clicked, this, [this]() { onUseSignature(m_list->currentItem()); });
|
||||
connect(m_btnClose, &QPushButton::clicked, this, &QDialog::accept);
|
||||
|
||||
// Disable buttons when no selection
|
||||
connect(m_list, &QListWidget::itemSelectionChanged, this, [this]() {
|
||||
bool hasSel = m_list->currentItem() != nullptr;
|
||||
m_btnEdit->setEnabled(hasSel);
|
||||
m_btnDelete->setEnabled(hasSel);
|
||||
m_btnUse->setEnabled(hasSel);
|
||||
});
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::loadSignatures()
|
||||
{
|
||||
m_list->clear();
|
||||
|
||||
// For now, we'll use a simple SQLite table for signatures
|
||||
// In a more complete implementation, this could be per-account
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
|
||||
// Create table if not exists
|
||||
QSqlQuery createQuery(db);
|
||||
createQuery.exec(
|
||||
"CREATE TABLE IF NOT EXISTS Signature ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"accountId INTEGER, " // -1 = global
|
||||
"name TEXT NOT NULL, "
|
||||
"html TEXT NOT NULL, "
|
||||
"createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
||||
"updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP, "
|
||||
"FOREIGN KEY(accountId) REFERENCES Account(id) ON DELETE CASCADE"
|
||||
")"
|
||||
);
|
||||
|
||||
QSqlQuery query(db);
|
||||
if (m_accountId >= 0) {
|
||||
query.prepare("SELECT id, name, html FROM Signature WHERE accountId = :accountId OR accountId IS NULL ORDER BY name");
|
||||
query.bindValue(":accountId", m_accountId);
|
||||
} else {
|
||||
query.prepare("SELECT id, name, html FROM Signature WHERE accountId IS NULL ORDER BY name");
|
||||
}
|
||||
|
||||
if (query.exec()) {
|
||||
while (query.next()) {
|
||||
QListWidgetItem* item = new QListWidgetItem(query.value("name").toString());
|
||||
item->setData(Qt::UserRole, query.value("id").toLongLong());
|
||||
item->setData(Qt::UserRole + 1, query.value("html").toString());
|
||||
item->setToolTip(query.value("html").toString().left(200) + "...");
|
||||
m_list->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::onNewSignature()
|
||||
{
|
||||
SignatureEditorDialog dlg(QString(), this);
|
||||
connect(&dlg, &SignatureEditorDialog::signatureSaved, this, [this](const QString& name, const QString& html) {
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
query.prepare("INSERT INTO Signature (accountId, name, html, createdAt, updatedAt) VALUES (:accountId, :name, :html, :createdAt, :updatedAt)");
|
||||
query.bindValue(":accountId", m_accountId >= 0 ? m_accountId : QVariant());
|
||||
query.bindValue(":name", name);
|
||||
query.bindValue(":html", html);
|
||||
query.bindValue(":createdAt", QDateTime::currentDateTime());
|
||||
query.bindValue(":updatedAt", QDateTime::currentDateTime());
|
||||
|
||||
if (query.exec()) {
|
||||
loadSignatures();
|
||||
emit signaturesChanged();
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Error"), tr("No se pudo guardar la firma: %1").arg(query.lastError().text()));
|
||||
}
|
||||
});
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::onEditSignature(QListWidgetItem* item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
qint64 id = item->data(Qt::UserRole).toLongLong();
|
||||
QString html = item->data(Qt::UserRole + 1).toString();
|
||||
QString name = item->text();
|
||||
|
||||
SignatureEditorDialog dlg(html, this);
|
||||
dlg.setSignatureName(name);
|
||||
|
||||
connect(&dlg, &SignatureEditorDialog::signatureSaved, this, [this, id](const QString& name, const QString& html) {
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
query.prepare("UPDATE Signature SET name = :name, html = :html, updatedAt = :updatedAt WHERE id = :id");
|
||||
query.bindValue(":id", id);
|
||||
query.bindValue(":name", name);
|
||||
query.bindValue(":html", html);
|
||||
query.bindValue(":updatedAt", QDateTime::currentDateTime());
|
||||
|
||||
if (query.exec()) {
|
||||
loadSignatures();
|
||||
emit signaturesChanged();
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Error"), tr("No se pudo actualizar la firma: %1").arg(query.lastError().text()));
|
||||
}
|
||||
});
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::onDeleteSignature(QListWidgetItem* item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
qint64 id = item->data(Qt::UserRole).toLongLong();
|
||||
QString name = item->text();
|
||||
|
||||
if (QMessageBox::question(this, tr("Eliminar firma"),
|
||||
tr("¿Eliminar la firma \"%1\"?").arg(name),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
query.prepare("DELETE FROM Signature WHERE id = :id");
|
||||
query.bindValue(":id", id);
|
||||
|
||||
if (query.exec()) {
|
||||
loadSignatures();
|
||||
emit signaturesChanged();
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Error"), tr("No se pudo eliminar la firma: %1").arg(query.lastError().text()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::onUseSignature(QListWidgetItem* item)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
m_selectedSignatureHtml = item->data(Qt::UserRole + 1).toString();
|
||||
emit signatureSelected(m_selectedSignatureHtml);
|
||||
accept();
|
||||
}
|
||||
|
||||
void SignatureManagerDialog::onSignatureSelected(QListWidgetItem* item)
|
||||
{
|
||||
if (item) {
|
||||
m_selectedSignatureHtml = item->data(Qt::UserRole + 1).toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user