#include "mainmainwindow.h" #include "core/models/account.h" #include "core/mailitem.h" #include "db/dao/mailitemdao.h" #include "db/dao/folderdao.h" #include #include #include "ui/accountsetupdialog.h" #include #include #include #include #include #include MainMainWindow::MainMainWindow(QWidget *parent) : QMainWindow(parent), m_currentFolderId(-1), m_currentMailId(-1) { setupUI(); connectModels(); setWindowTitle("Wino Mail DTK"); resize(1280, 820); } void MainMainWindow::setupUI() { // === Global stylesheet === this->setStyleSheet( "QMainWindow { background-color: #f5f5f7; }" "QSplitter::handle { background-color: #d1d1d6; width: 1px; }" "QTreeView { background-color: #ffffff; border: none; font-family: 'Segoe UI', Helvetica; font-size: 13px; }" "QToolBar { background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6; spacing: 10px; }" ); createToolBar(); // === Central widget === QWidget *central = new QWidget(); QHBoxLayout *centralLayout = new QHBoxLayout(central); centralLayout->setContentsMargins(0, 0, 0, 0); centralLayout->setSpacing(0); // === Sidebar === setupSidebar(); centralLayout->addWidget(m_sidebar); // === Separator line === QFrame *separator = new QFrame(); separator->setFrameShape(QFrame::VLine); separator->setStyleSheet("color: #d1d1d6;"); centralLayout->addWidget(separator); // === Stacked pages === m_stack = new QStackedWidget(); m_stack->setStyleSheet("background-color: #f5f5f7;"); // Page 0: Mail (folder tree + mail list + reader) setupMailPage(); m_stack->addWidget(m_mailPage); // 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) { QString msg; if (scheduleTime.isValid()) { msg = QString("Message scheduled for: %1").arg(scheduleTime.toString("dd/MM/yyyy hh:mm AP")); } else { msg = "Message sent (simulated)"; } if (!cc.isEmpty()) msg += QString(" | Cc: %1").arg(cc); if (!bcc.isEmpty()) msg += QString(" | Bcc: %1").arg(bcc); statusBar()->showMessage(msg, 5000); switchToPage(PageMail); }); connect(m_composeView, &ComposeView::discardRequested, [this]() { switchToPage(PageMail); }); connect(m_composeView, &ComposeView::detachRequested, [this](QWidget *composeView) { // Detach compose view to a standalone window QStackedWidget *stack = qobject_cast(composeView->parentWidget()); if (stack) { stack->removeWidget(composeView); } // Create standalone window QMainWindow *detachedWin = new QMainWindow(); detachedWin->setWindowTitle("Compose - Wino Mail"); // Assign central widget and ensure it's visible and sized detachedWin->setCentralWidget(composeView); composeView->setMinimumSize(800, 600); composeView->update(); detachedWin->resize(800, 600); detachedWin->setAttribute(Qt::WA_DeleteOnClose); detachedWin->show(); statusBar()->showMessage("Compose view detached to separate window", 3000); }); m_stack->addWidget(m_composeView); // Page 2: Settings m_settingsView = new SettingsView(); connect(m_settingsView, &SettingsView::accountAddRequested, [this]() { AccountSetupDialog dlg(m_accountService, this); dlg.exec(); }); connect(m_settingsView, &SettingsView::themeChanged, [this](const QString &theme) { statusBar()->showMessage(QString("Theme changed to: %1 (restart may be required)").arg(theme), 3000); }); m_stack->addWidget(m_settingsView); // Page 3: Contacts m_contactsView = new ContactsView(); m_stack->addWidget(m_contactsView); // Page 4: Calendar m_calendarView = new CalendarView(); m_stack->addWidget(m_calendarView); centralLayout->addWidget(m_stack, 1); setCentralWidget(central); // Show mail page by default switchToPage(PageMail); } void MainMainWindow::setupSidebar() { m_sidebar = new QListWidget(); m_sidebar->setFixedWidth(64); m_sidebar->setIconSize(QSize(24, 24)); m_sidebar->setSpacing(4); m_sidebar->setFrameShape(QFrame::NoFrame); m_sidebar->setStyleSheet( "QListWidget { background-color: #2c2c2e; border: none; padding: 8px 0; }" "QListWidget::item { color: #8e8e93; padding: 12px 0; text-align: center; font-size: 10px; border: none; border-radius: 8px; margin: 2px 8px; }" "QListWidget::item:selected { background-color: #3a3a3c; color: #ffffff; }" "QListWidget::item:hover { background-color: #3a3a3c; color: #ffffff; }" ); m_sidebar->addItem("📧\nMail"); m_sidebar->addItem("✏️\nCompose"); m_sidebar->addItem("⚙️\nSettings"); m_sidebar->addItem("👥\nContacts"); m_sidebar->addItem("📅\nCalendar"); m_sidebar->setCurrentRow(0); connect(m_sidebar, &QListWidget::currentRowChanged, this, &MainMainWindow::onNavChanged); } void MainMainWindow::setupMailPage() { m_mailPage = new QWidget(); QHBoxLayout *mailLayout = new QHBoxLayout(m_mailPage); mailLayout->setContentsMargins(0, 0, 0, 0); mailLayout->setSpacing(0); m_folderSplitter = new QSplitter(Qt::Horizontal); m_folderSplitter->setHandleWidth(1); // Folder tree m_folderTree = new QTreeView(); m_folderTree->setHeaderHidden(true); m_folderTree->setIndentation(20); m_folderTree->setMinimumWidth(220); m_folderTree->setMaximumWidth(350); m_folderTree->setFrameShape(QFrame::NoFrame); m_folderTree->setExpandsOnDoubleClick(true); m_folderSplitter->addWidget(m_folderTree); // Mail list (QTableView with sorting) m_mailListView = new MailListView(); connect(m_mailListView, &MailListView::emailSelected, this, &MainMainWindow::onEmailSelected); connect(m_mailListView, &MailListView::composeRequested, this, &MainMainWindow::onComposeRequested); connect(m_mailListView, &MailListView::emailOpenRequested, this, [this](int mailId) { openMailInIndependentWindow(mailId); }); m_folderSplitter->addWidget(m_mailListView); // Reader m_emailViewer = new ReaderView(); m_emailViewer->setMinimumWidth(350); connect(m_emailViewer, &ReaderView::replyRequested, this, &MainMainWindow::onReaderReplyRequested); connect(m_emailViewer, &ReaderView::detachRequested, this, [this]() { if (m_currentMailId >= 0) { openMailInIndependentWindow(m_currentMailId); } }); m_folderSplitter->addWidget(m_emailViewer); // Default sizes: folder 240, list 380, reader flex m_folderSplitter->setSizes({240, 380, 600}); mailLayout->addWidget(m_folderSplitter); } void MainMainWindow::connectModels() { m_accountService = new AccountService(this); m_mailService = new MailService(this); m_folderModel = new FolderListModel(m_accountService, this); m_emailModel = new EmailListModel(this); m_folderTree->setModel(m_folderModel); m_mailListView->setModel(m_emailModel); m_folderTree->expandAll(); connect(m_folderTree, &QTreeView::clicked, this, &MainMainWindow::onFolderSelected); } void MainMainWindow::onNavChanged(int index) { switchToPage(static_cast(index)); } void MainMainWindow::switchToPage(int pageIndex) { m_stack->setCurrentIndex(pageIndex); m_sidebar->blockSignals(true); m_sidebar->setCurrentRow(pageIndex); m_sidebar->blockSignals(false); // Show/hide toolbar actions per page } void MainMainWindow::onFolderSelected(const QModelIndex &index) { if (!index.isValid()) return; int itemType = index.data(FolderListModel::ItemTypeRole).toInt(); if (itemType == FolderTreeItem::FolderNode) { m_currentFolderId = index.data(FolderListModel::FolderIdRole).toInt(); m_emailModel->setFolderId(m_currentFolderId); // Fetch mails for this folder std::optional optFolder = FolderDao::findById(m_currentFolderId); if (optFolder.has_value()) { Folder folder = optFolder.value(); Account* account = m_accountService->findAccountById(folder.accountId()); if (account) { QString accountId = QString::number(account->id()); QString folderId = QString::number(m_currentFolderId); // Fetch mails asynchronously to avoid blocking UI QMetaObject::invokeMethod(m_mailService, "fetchMails", Qt::QueuedConnection, Q_ARG(QString, accountId), Q_ARG(QString, folderId)); delete account; } } m_emailModel->refresh(); } } void MainMainWindow::onEmailSelected(int mailId) { m_currentMailId = mailId; std::optional item = MailItemDao::findById(mailId); if (!item.has_value()) { m_emailViewer->setMailItem(nullptr); return; } MailItem &mail = item.value(); if (!mail.isRead()) { mail.setRead(true); MailItemDao::update(mail); } m_emailViewer->setMailItem(&mail); } void MainMainWindow::onComposeRequested() { switchToPage(PageCompose); } void MainMainWindow::onReaderReplyRequested(const MailItem *item) { if (item) { m_composeView->setTo(item->sender()); m_composeView->setSubject("Re: " + item->subject()); } switchToPage(PageCompose); } void MainMainWindow::onNewMessage() { switchToPage(PageCompose); } void MainMainWindow::openMailInIndependentWindow(int mailId) { std::optional item = MailItemDao::findById(mailId); if (!item.has_value()) return; QMainWindow *detachedWin = new QMainWindow(); detachedWin->setWindowTitle(QString("Mail - %1").arg(item->subject())); ReaderView *detachedReader = new ReaderView(); detachedReader->setMailItem(&item.value()); detachedWin->setCentralWidget(detachedReader); detachedWin->resize(800, 600); detachedWin->setAttribute(Qt::WA_DeleteOnClose); detachedWin->show(); statusBar()->showMessage("Opened mail in independent window", 3000); } void MainMainWindow::onAddAccountRequested() { AccountSetupDialog *dialog = new AccountSetupDialog(m_accountService, this); connect(dialog, &AccountSetupDialog::accountCreated, this, [this](const Account &account) { statusBar()->showMessage(QString("Account added: %1").arg(account.email()), 3000); m_folderModel->refresh(); // Refresh folder list to show new account's folders }); dialog->open(); } void MainMainWindow::onAccountEditRequested(int accountId) { Account *account = m_accountService->findAccountById(accountId); if (!account) { statusBar()->showMessage("Account not found", 3000); return; } AccountSetupDialog *dialog = new AccountSetupDialog(m_accountService, this); dialog->loadAccountForEditing(*account); connect(dialog, &AccountSetupDialog::accountCreated, this, [this](const Account &account) { statusBar()->showMessage(QString("Account updated: %1").arg(account.email()), 3000); m_folderModel->refresh(); // Refresh folder list }); dialog->open(); } void MainMainWindow::onAccountDeleteRequested(int accountId) { Account *account = m_accountService->findAccountById(accountId); if (!account) { statusBar()->showMessage("Account not found", 3000); return; } if (QMessageBox::warning(this, "Delete Account", QString("Are you sure you want to delete the account '%1'?").arg(account->email()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { m_accountService->removeAccount(accountId); statusBar()->showMessage(QString("Account deleted: %1").arg(account->email()), 3000); m_folderModel->refresh(); // Refresh folder list to remove deleted account's folders } } void MainMainWindow::createToolBar() { m_toolBar = addToolBar("Main Toolbar"); m_toolBar->setMovable(false); QAction *newMsgAction = m_toolBar->addAction("✉ New Message"); m_toolBar->addSeparator(); QAction *syncAction = m_toolBar->addAction("⟳ Sync/Refresh"); QAction *openWinAction = m_toolBar->addAction("↗ Abrir en ventana"); QAction *deleteAction = m_toolBar->addAction("🗑 Delete"); connect(newMsgAction, &QAction::triggered, this, &MainMainWindow::onNewMessage); connect(syncAction, &QAction::triggered, [this]() { if (m_currentFolderId >= 0) { m_emailModel->refresh(); statusBar()->showMessage("Refreshed", 2000); } }); connect(openWinAction, &QAction::triggered, [this]() { if (m_currentMailId >= 0) { openMailInIndependentWindow(m_currentMailId); } else { statusBar()->showMessage("Selecciona un correo primero para abrirlo en una ventana", 3000); } }); connect(deleteAction, &QAction::triggered, [this]() { statusBar()->showMessage("Delete would be implemented here", 3000); }); }