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:
+182
-40
@@ -1,6 +1,9 @@
|
||||
// ===================== ComposeView =====================
|
||||
|
||||
#include "composeview.h"
|
||||
#include "db/dao/templatedao.h"
|
||||
#include "ui/templatesmanagerdialog.h"
|
||||
#include "ui/signaturemanagerdialog.h"
|
||||
#include <QDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QVBoxLayout>
|
||||
@@ -16,6 +19,9 @@
|
||||
#include <QStyle>
|
||||
#include <QApplication>
|
||||
#include <QFileIconProvider>
|
||||
#include <QMainWindow>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
ComposeView::ComposeView(QWidget *parent) : QWidget(parent) {
|
||||
setupUI();
|
||||
// Connect the rich text editor's signature signal to our slot
|
||||
@@ -27,36 +33,31 @@ ComposeView::~ComposeView() {
|
||||
// Destructor implementation
|
||||
}
|
||||
|
||||
void ComposeView::onSignatureClicked() {
|
||||
// This slot is called when the signature button in the rich text editor is clicked
|
||||
// The actual menu handling is done in RichTextEditor, so we just need to
|
||||
// handle any ComposeView-specific logic here if needed
|
||||
void ComposeView::onSignatureClicked()
|
||||
{
|
||||
// Open signature manager dialog
|
||||
qint64 accountId = -1;
|
||||
if (m_accountService && m_accountCombo && m_accountCombo->currentIndex() > 0) {
|
||||
accountId = m_accountCombo->itemData(m_accountCombo->currentIndex()).toLongLong();
|
||||
}
|
||||
|
||||
SignatureManagerDialog dlg(m_accountService, accountId, this);
|
||||
connect(&dlg, &SignatureManagerDialog::signatureSelected, this, [this](const QString& html) {
|
||||
if (!html.isEmpty()) {
|
||||
QTextCursor cursor = m_bodyEditor->textCursor();
|
||||
cursor.insertHtml(html);
|
||||
}
|
||||
});
|
||||
connect(&dlg, &SignatureManagerDialog::signaturesChanged, this, [this]() {
|
||||
emit statusMessageRequested(tr("Firmas actualizadas"));
|
||||
});
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void ComposeView::onSignatureEditRequested() {
|
||||
QDialog dialog(this);
|
||||
dialog.setWindowTitle(tr("Edit Signature"));
|
||||
dialog.setMinimumSize(600, 400);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(&dialog);
|
||||
|
||||
RichTextEditor *editor = new RichTextEditor(&dialog);
|
||||
editor->setupToolbar(layout);
|
||||
layout->addWidget(editor);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
|
||||
layout->addWidget(buttonBox);
|
||||
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QString signatureHtml = editor->toHtml();
|
||||
if (!signatureHtml.isEmpty()) {
|
||||
QTextCursor cursor = m_bodyEditor->textCursor();
|
||||
cursor.insertHtml(signatureHtml);
|
||||
}
|
||||
}
|
||||
void ComposeView::onSignatureEditRequested()
|
||||
{
|
||||
// Same as click - open manager
|
||||
onSignatureClicked();
|
||||
}
|
||||
|
||||
void ComposeView::setAccountService(AccountService *service) {
|
||||
@@ -89,31 +90,123 @@ void ComposeView::onAccountChanged(int index) {
|
||||
if (index <= 0) {
|
||||
// Placeholder item selected or invalid index
|
||||
m_currentAccountId = -1;
|
||||
m_signatureCombo->clear();
|
||||
m_signatureCombo->addItem(tr("Sin cuenta seleccionada"), QVariant());
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentAccountId = m_accountCombo->itemData(index).toInt();
|
||||
populateSignatureCombo(m_currentAccountId);
|
||||
loadSignatureForCurrentAccount();
|
||||
}
|
||||
|
||||
void ComposeView::loadSignatureForCurrentAccount() {
|
||||
void ComposeView::populateSignatureCombo(qint64 accountId)
|
||||
{
|
||||
m_signatureCombo->clear();
|
||||
m_signatureCombo->addItem(tr("Firma automática"), QVariant()); // Index 0 = auto
|
||||
|
||||
if (!m_accountService || accountId <= 0) {
|
||||
m_signatureCombo->addItem(tr("Sin firmas disponibles"), QVariant());
|
||||
return;
|
||||
}
|
||||
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
|
||||
// Get signatures for this account + global
|
||||
query.prepare("SELECT id, name FROM Signature WHERE accountId = :accountId OR accountId IS NULL ORDER BY isDefault DESC, name");
|
||||
query.bindValue(":accountId", accountId);
|
||||
|
||||
if (query.exec()) {
|
||||
while (query.next()) {
|
||||
QString name = query.value("name").toString();
|
||||
qint64 id = query.value("id").toLongLong();
|
||||
m_signatureCombo->addItem(name, id);
|
||||
}
|
||||
}
|
||||
|
||||
// Set to first item (automatic)
|
||||
m_signatureCombo->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void ComposeView::onSignatureComboChanged(int index)
|
||||
{
|
||||
if (index <= 0) {
|
||||
// Automatic mode - use default signature
|
||||
loadSignatureForCurrentAccount();
|
||||
return;
|
||||
}
|
||||
|
||||
qint64 signatureId = m_signatureCombo->itemData(index).toLongLong();
|
||||
if (signatureId <= 0) return;
|
||||
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
query.prepare("SELECT html FROM Signature WHERE id = :id");
|
||||
query.bindValue(":id", signatureId);
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
QString html = query.value(0).toString();
|
||||
if (!html.isEmpty()) {
|
||||
m_bodyEditor->setHtml(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeView::loadSignatureForCurrentAccount()
|
||||
{
|
||||
if (!m_accountService || m_currentAccountId <= 0) {
|
||||
// Clear signature if no account selected
|
||||
m_bodyEditor->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Account* account = m_accountService->findAccountById(m_currentAccountId);
|
||||
if (account) {
|
||||
// For now, we'll just clear the editor since we don't have a signature field in Account yet
|
||||
// In a real implementation, we would retrieve and display the signature
|
||||
m_bodyEditor->clear();
|
||||
delete account;
|
||||
QString signatureHtml = getDefaultSignatureForAccount(m_currentAccountId);
|
||||
if (!signatureHtml.isEmpty()) {
|
||||
m_bodyEditor->setHtml(signatureHtml);
|
||||
} else {
|
||||
m_bodyEditor->clear();
|
||||
}
|
||||
}
|
||||
|
||||
QString ComposeView::getDefaultSignatureForAccount(qint64 accountId)
|
||||
{
|
||||
QSqlDatabase db = DatabaseManager::instance().database();
|
||||
QSqlQuery query(db);
|
||||
|
||||
// First try to get account-specific default signature
|
||||
query.prepare("SELECT html FROM Signature WHERE accountId = :accountId AND name LIKE '%default%' ORDER BY updatedAt DESC LIMIT 1");
|
||||
query.bindValue(":accountId", accountId);
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).toString();
|
||||
}
|
||||
|
||||
// Fallback to global default signature
|
||||
query.prepare("SELECT html FROM Signature WHERE accountId IS NULL AND name LIKE '%default%' ORDER BY updatedAt DESC LIMIT 1");
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).toString();
|
||||
}
|
||||
|
||||
// Last resort: any signature for this account
|
||||
query.prepare("SELECT html FROM Signature WHERE accountId = :accountId ORDER BY updatedAt DESC LIMIT 1");
|
||||
query.bindValue(":accountId", accountId);
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).toString();
|
||||
}
|
||||
|
||||
// Global any signature
|
||||
query.prepare("SELECT html FROM Signature WHERE accountId IS NULL ORDER BY updatedAt DESC LIMIT 1");
|
||||
|
||||
if (query.exec() && query.next()) {
|
||||
return query.value(0).toString();
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void ComposeView::setupUI() {
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(20, 15, 20, 15);
|
||||
@@ -143,6 +236,16 @@ void ComposeView::setupUI() {
|
||||
this, &ComposeView::onAccountChanged);
|
||||
fromLayout->addWidget(fromLabel);
|
||||
fromLayout->addWidget(m_accountCombo);
|
||||
|
||||
// Signature selector
|
||||
m_signatureCombo = new QComboBox();
|
||||
m_signatureCombo->setFixedWidth(180);
|
||||
m_signatureCombo->setStyleSheet(m_accountCombo->styleSheet());
|
||||
m_signatureCombo->setToolTip(tr("Seleccionar firma"));
|
||||
connect(m_signatureCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &ComposeView::onSignatureComboChanged);
|
||||
fromLayout->addWidget(m_signatureCombo);
|
||||
|
||||
fromLayout->addStretch(); // Este resorte empuja el par (label+combo) a la izquierda
|
||||
mainLayout->addLayout(fromLayout);
|
||||
|
||||
@@ -426,13 +529,16 @@ void ComposeView::initializeComposition() {
|
||||
m_ccField->tags(QStringList{});
|
||||
m_bccField->tags(QStringList{});
|
||||
m_subjectField->clear();
|
||||
m_bodyEditor->clear();
|
||||
m_attachmentFiles.clear();
|
||||
m_attachmentList->clear();
|
||||
m_attachmentList->setVisible(false);
|
||||
m_ccRow->setVisible(false);
|
||||
m_bccRow->setVisible(false);
|
||||
m_schedulePanel->setVisible(false);
|
||||
|
||||
// Load signature for current account
|
||||
loadSignatureForCurrentAccount();
|
||||
|
||||
m_toField->setFocus();
|
||||
}
|
||||
|
||||
@@ -528,9 +634,45 @@ void ComposeView::startNewEmail(const QString &initialRecipient) {
|
||||
m_attachmentList->setVisible(!m_attachmentFiles.isEmpty());
|
||||
}
|
||||
|
||||
void ComposeView::onTemplateClicked() {
|
||||
// Placeholder for template functionality
|
||||
QMessageBox::information(this, tr("Email Templates"), tr("Email templates feature not yet implemented."));
|
||||
void ComposeView::onTemplateClicked()
|
||||
{
|
||||
// Get current account ID if available
|
||||
qint64 accountId = -1;
|
||||
if (m_accountService) {
|
||||
// Try to get current account from combo
|
||||
if (m_accountCombo && m_accountCombo->currentIndex() > 0) {
|
||||
accountId = m_accountCombo->itemData(m_accountCombo->currentIndex()).toLongLong();
|
||||
}
|
||||
}
|
||||
|
||||
TemplatesManagerDialog dlg(accountId, this);
|
||||
connect(&dlg, &TemplatesManagerDialog::templateSelected, this, [this](const Template& tmpl) {
|
||||
applyTemplate(tmpl);
|
||||
});
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
void ComposeView::applyTemplate(const Template& tmpl)
|
||||
{
|
||||
if (!tmpl.isValid()) return;
|
||||
|
||||
// Set subject
|
||||
if (!tmpl.subject.isEmpty()) {
|
||||
m_subjectField->setText(tmpl.subject);
|
||||
}
|
||||
|
||||
// Set body HTML
|
||||
if (!tmpl.bodyHtml.isEmpty()) {
|
||||
m_bodyEditor->setHtml(tmpl.bodyHtml);
|
||||
}
|
||||
|
||||
// If there are variables, we could show a dialog to fill them
|
||||
// For now, just apply as-is
|
||||
emit statusMessageRequested(tr("Plantilla aplicada: %1").arg(tmpl.name));
|
||||
}
|
||||
|
||||
void ComposeView::statusBarMessage(const QString& msg)
|
||||
{
|
||||
emit statusMessageRequested(msg);
|
||||
}
|
||||
|
||||
#include "composeview.moc"
|
||||
|
||||
Reference in New Issue
Block a user