#include "newmessagedialog.h" #include #include #include #include #include #include #include #include #include #include NewMessageDialog::NewMessageDialog(const QStringList &accounts, QWidget *parent) : QDialog(parent) { setWindowTitle("New Message"); setMinimumSize(750, 600); resize(850, 700); setupUI(); // Init state m_ccEdit->hide(); m_bccEdit->hide(); m_schedulePicker->hide(); // Capture initial content for dirty detection m_initialContent = m_bodyEdit->toPlainText(); // Signals connect(m_sendNowBtn, &QPushButton::clicked, this, &NewMessageDialog::onSendNow); connect(m_sendLaterBtn, &QPushButton::clicked, this, &NewMessageDialog::onSendLater); } void NewMessageDialog::setupUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(16, 12, 16, 12); mainLayout->setSpacing(8); // ===== HEADER FIELDS ===== QVBoxLayout *headerLayout = new QVBoxLayout(); headerLayout->setSpacing(6); // From QHBoxLayout *fromRow = new QHBoxLayout(); m_fromCombo = new QComboBox(this); m_fromCombo->setMinimumWidth(400); fromRow->addWidget(new QLabel("From:", this)); fromRow->addWidget(m_fromCombo, 1); headerLayout->addLayout(fromRow); // To + CC/CCO buttons QHBoxLayout *toRow = new QHBoxLayout(); m_toEdit = new QLineEdit(this); m_toEdit->setPlaceholderText("To"); toRow->addWidget(new QLabel("To:", this)); toRow->addWidget(m_toEdit, 1); m_ccBtn = new QPushButton("CC", this); m_ccBtn->setFixedWidth(48); m_ccBtn->setStyleSheet("QPushButton { border: 1px solid #ccc; border-radius: 4px; padding: 4px 8px; font-size: 11px; background: #f0f0f0; } QPushButton:hover { background: #e0e0e0; }"); m_bccBtn = new QPushButton("BCC", this); m_bccBtn->setFixedWidth(48); m_bccBtn->setStyleSheet(m_ccBtn->styleSheet()); toRow->addWidget(m_ccBtn); toRow->addWidget(m_bccBtn); headerLayout->addLayout(toRow); connect(m_ccBtn, &QPushButton::clicked, this, &NewMessageDialog::toggleCc); connect(m_bccBtn, &QPushButton::clicked, this, &NewMessageDialog::toggleBcc); // CC m_ccEdit = new QLineEdit(this); m_ccEdit->setPlaceholderText("Cc"); QHBoxLayout *ccRow = new QHBoxLayout(); ccRow->addWidget(new QLabel("Cc:", this)); ccRow->addWidget(m_ccEdit, 1); headerLayout->addLayout(ccRow); // BCC m_bccEdit = new QLineEdit(this); m_bccEdit->setPlaceholderText("Bcc"); QHBoxLayout *bccRow = new QHBoxLayout(); bccRow->addWidget(new QLabel("Bcc:", this)); bccRow->addWidget(m_bccEdit, 1); headerLayout->addLayout(bccRow); // Subject QHBoxLayout *subjRow = new QHBoxLayout(); m_subjectEdit = new QLineEdit(this); m_subjectEdit->setPlaceholderText("Subject"); subjRow->addWidget(new QLabel("Subject:", this)); subjRow->addWidget(m_subjectEdit, 1); headerLayout->addLayout(subjRow); mainLayout->addLayout(headerLayout); // ===== RICH TEXT TOOLBAR ===== m_richToolbar = new QToolBar("Format", this); m_richToolbar->setIconSize(QSize(16, 16)); m_richToolbar->setStyleSheet( "QToolBar { border: 1px solid #d1d1d6; border-radius: 4px; background: #f9f9f9; spacing: 4px; padding: 2px; }" ); setupRichTextToolbar(m_richToolbar); mainLayout->addWidget(m_richToolbar); // ===== BODY EDITOR ===== m_bodyEdit = new QTextEdit(this); m_bodyEdit->setAcceptRichText(true); m_bodyEdit->setPlaceholderText("Write your message..."); m_bodyEdit->setFrameShape(QFrame::StyledPanel); m_bodyEdit->setStyleSheet( "QTextEdit { border: 1px solid #d1d1d6; border-radius: 4px; padding: 8px; font-size: 13px; font-family: 'Segoe UI', Roboto, Helvetica; }" "QTextEdit:focus { border-color: #0078d4; }" ); mainLayout->addWidget(m_bodyEdit, 1); connect(m_bodyEdit, &QTextEdit::currentCharFormatChanged, this, &NewMessageDialog::currentCharFormatChanged); // ===== ATTACHMENTS ===== QHBoxLayout *attachRow = new QHBoxLayout(); m_attachBtn = new QPushButton("Attach Files...", this); m_attachBtn->setStyleSheet("QPushButton { border: 1px solid #ccc; border-radius: 4px; padding: 6px 12px; font-size: 12px; background: #f0f0f0; } QPushButton:hover { background: #e0e0e0; }"); m_attachmentList = new QListWidget(this); m_attachmentList->setMaximumHeight(60); m_attachmentList->setMinimumWidth(300); m_attachmentList->setStyleSheet("QListWidget { border: 1px solid #e0e0e0; border-radius: 4px; font-size: 11px; }"); m_attachmentList->setVisible(false); attachRow->addWidget(m_attachBtn); attachRow->addWidget(m_attachmentList, 1); mainLayout->addLayout(attachRow); connect(m_attachBtn, &QPushButton::clicked, this, &NewMessageDialog::onAttachFile); connect(m_attachmentList, &QListWidget::itemDoubleClicked, this, &NewMessageDialog::removeAttachment); // ===== BOTTOM ACTION BAR ===== QHBoxLayout *actionBar = new QHBoxLayout(); // Schedule picker m_schedulePicker = new QDateTimeEdit(QDateTime::currentDateTime().addSecs(3600), this); m_schedulePicker->setCalendarPopup(true); m_schedulePicker->setDisplayFormat("dd/MM/yyyy hh:mm AP"); m_schedulePicker->setStyleSheet("QDateTimeEdit { border: 1px solid #ccc; border-radius: 4px; padding: 6px 8px; font-size: 12px; }"); actionBar->addStretch(); m_cancelBtn = new QPushButton("Discard", this); m_cancelBtn->setStyleSheet("QPushButton { border: 1px solid #ccc; border-radius: 6px; padding: 8px 20px; font-size: 13px; font-weight: bold; background: #f0f0f0; color: #333; } QPushButton:hover { background: #e0e0e0; }"); m_sendLaterBtn = new QPushButton("Schedule...", this); m_sendLaterBtn->setStyleSheet("QPushButton { border: 1px solid #0078d4; border-radius: 6px; padding: 8px 20px; font-size: 13px; font-weight: bold; background: #ffffff; color: #0078d4; } QPushButton:hover { background: #e8f4ff; }"); m_sendNowBtn = new QPushButton("Send Now", this); m_sendNowBtn->setStyleSheet("QPushButton { border: none; border-radius: 6px; padding: 8px 24px; font-size: 13px; font-weight: bold; background: #0078d4; color: white; } QPushButton:hover { background: #106ebe; }"); actionBar->addWidget(m_cancelBtn); actionBar->addWidget(m_sendLaterBtn); actionBar->addWidget(m_sendNowBtn); mainLayout->addLayout(actionBar); connect(m_cancelBtn, &QPushButton::clicked, this, &QDialog::reject); } void NewMessageDialog::setupRichTextToolbar(QToolBar *tb) { m_boldAction = tb->addAction("B"); m_boldAction->setCheckable(true); m_boldAction->setToolTip("Bold (Ctrl+B)"); m_boldAction->setFont(QFont("Segoe UI", 11, QFont::Bold)); m_italicAction = tb->addAction("I"); m_italicAction->setCheckable(true); m_italicAction->setToolTip("Italic (Ctrl+I)"); m_italicAction->setFont(QFont("Segoe UI", 11, QFont::StyleItalic)); m_underlineAction = tb->addAction("U"); m_underlineAction->setCheckable(true); m_underlineAction->setToolTip("Underline (Ctrl+U)"); QFont uFont("Segoe UI", 11); uFont.setUnderline(true); m_underlineAction->setFont(uFont); tb->addSeparator(); QAction *bulletAction = tb->addAction("• List"); bulletAction->setToolTip("Bullet list"); QAction *numAction = tb->addAction("1. List"); numAction->setToolTip("Numbered list"); tb->addSeparator(); QAction *tableAction = tb->addAction("▦ Table"); tableAction->setToolTip("Insert table"); QAction *imageAction = tb->addAction("🖼 Image"); imageAction->setToolTip("Insert image"); connect(m_boldAction, &QAction::triggered, this, &NewMessageDialog::onFormatBold); connect(m_italicAction, &QAction::triggered, this, &NewMessageDialog::onFormatItalic); connect(m_underlineAction, &QAction::triggered, this, &NewMessageDialog::onFormatUnderline); connect(bulletAction, &QAction::triggered, this, &NewMessageDialog::onInsertBulletList); connect(numAction, &QAction::triggered, this, &NewMessageDialog::onInsertNumberedList); connect(tableAction, &QAction::triggered, this, &NewMessageDialog::onInsertTable); connect(imageAction, &QAction::triggered, this, &NewMessageDialog::onInsertImage); tb->addSeparator(); QAction *undoAction = tb->addAction("↶"); undoAction->setToolTip("Undo"); connect(undoAction, &QAction::triggered, m_bodyEdit, &QTextEdit::undo); } // ===== FORMAT SLOTS ===== void NewMessageDialog::mergeFormatOnWordOrSelection(const QTextCharFormat &fmt) { QTextCursor cursor = m_bodyEdit->textCursor(); if (!cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor); cursor.mergeCharFormat(fmt); m_bodyEdit->mergeCurrentCharFormat(fmt); } void NewMessageDialog::currentCharFormatChanged(const QTextCharFormat &fmt) { m_boldAction->setChecked(fmt.fontWeight() >= QFont::Bold); m_italicAction->setChecked(fmt.fontItalic()); m_underlineAction->setChecked(fmt.fontUnderline()); } void NewMessageDialog::onFormatBold() { QTextCharFormat fmt; fmt.setFontWeight(m_boldAction->isChecked() ? QFont::Bold : QFont::Normal); mergeFormatOnWordOrSelection(fmt); } void NewMessageDialog::onFormatItalic() { QTextCharFormat fmt; fmt.setFontItalic(m_italicAction->isChecked()); mergeFormatOnWordOrSelection(fmt); } void NewMessageDialog::onFormatUnderline() { QTextCharFormat fmt; fmt.setFontUnderline(m_underlineAction->isChecked()); mergeFormatOnWordOrSelection(fmt); } void NewMessageDialog::onInsertBulletList() { QTextCursor cursor = m_bodyEdit->textCursor(); cursor.insertList(QTextListFormat::ListDisc); } void NewMessageDialog::onInsertNumberedList() { QTextCursor cursor = m_bodyEdit->textCursor(); cursor.insertList(QTextListFormat::ListDecimal); } void NewMessageDialog::onInsertTable() { QTextCursor cursor = m_bodyEdit->textCursor(); QTextTable *table = cursor.insertTable(3, 3); Q_UNUSED(table); } void NewMessageDialog::onInsertImage() { QString filePath = QFileDialog::getOpenFileName(this, "Insert Image", QString(), "Images (*.png *.jpg *.jpeg *.gif *.bmp)"); if (filePath.isEmpty()) return; QImage image(filePath); if (image.isNull()) return; // Scale large images to fit if (image.width() > 600) image = image.scaledToWidth(600, Qt::SmoothTransformation); QTextCursor cursor = m_bodyEdit->textCursor(); cursor.insertImage(image); } // ===== CC / BCC TOGGLES ===== void NewMessageDialog::toggleCc() { m_ccVisible = !m_ccVisible; m_ccEdit->setVisible(m_ccVisible); m_ccBtn->setStyleSheet(m_ccVisible ? "QPushButton { border: 1px solid #0078d4; border-radius: 4px; padding: 4px 8px; font-size: 11px; background: #e8f4ff; color: #0078d4; }" : "QPushButton { border: 1px solid #ccc; border-radius: 4px; padding: 4px 8px; font-size: 11px; background: #f0f0f0; } QPushButton:hover { background: #e0e0e0; }"); } void NewMessageDialog::toggleBcc() { m_bccVisible = !m_bccVisible; m_bccEdit->setVisible(m_bccVisible); m_bccBtn->setStyleSheet(m_bccVisible ? "QPushButton { border: 1px solid #0078d4; border-radius: 4px; padding: 4px 8px; font-size: 11px; background: #e8f4ff; color: #0078d4; }" : "QPushButton { border: 1px solid #ccc; border-radius: 4px; padding: 4px 8px; font-size: 11px; background: #f0f0f0; } QPushButton:hover { background: #e0e0e0; }"); } // ===== SEND ===== void NewMessageDialog::onSendNow() { if (m_toEdit->text().trimmed().isEmpty()) { QMessageBox::warning(this, "Missing Recipient", "Please enter at least one recipient."); m_toEdit->setFocus(); return; } m_sendLater = false; accept(); } void NewMessageDialog::onSendLater() { if (m_toEdit->text().trimmed().isEmpty()) { QMessageBox::warning(this, "Missing Recipient", "Please enter at least one recipient."); m_toEdit->setFocus(); return; } if (m_schedulePicker->dateTime() <= QDateTime::currentDateTime()) { QMessageBox::warning(this, "Invalid Time", "Scheduled time must be in the future."); return; } m_sendLater = true; m_scheduledTime = m_schedulePicker->dateTime(); accept(); } // ===== ATTACHMENTS ===== void NewMessageDialog::onAttachFile() { QStringList files = QFileDialog::getOpenFileNames(this, "Attach Files"); if (files.isEmpty()) return; for (const QString &file : files) { QFileInfo fi(file); m_attachedFiles << file; m_attachmentList->addItem(fi.fileName()); } m_attachmentList->setVisible(!m_attachedFiles.isEmpty()); } void NewMessageDialog::removeAttachment() { QListWidgetItem *item = m_attachmentList->currentItem(); if (!item) return; int row = m_attachmentList->row(item); m_attachedFiles.removeAt(row); delete m_attachmentList->takeItem(row); m_attachmentList->setVisible(!m_attachedFiles.isEmpty()); } // ===== CLOSE WITH UNSAVED CHANGES ===== bool NewMessageDialog::hasUnsavedChanges() const { // Check if anything was typed if (!m_toEdit->text().trimmed().isEmpty()) return true; if (!m_subjectEdit->text().trimmed().isEmpty()) return true; if (m_bodyEdit->toPlainText() != m_initialContent && !m_bodyEdit->toPlainText().trimmed().isEmpty()) return true; if (!m_attachedFiles.isEmpty()) return true; return false; } void NewMessageDialog::closeEvent(QCloseEvent *event) { if (hasUnsavedChanges()) { QMessageBox::StandardButton reply = QMessageBox::question( this, "Unsaved Changes", "You have unsaved changes. Would you like to save to drafts before closing?", QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); if (reply == QMessageBox::Save) { // Accept the dialog so the caller can handle saving to drafts accept(); return; } else if (reply == QMessageBox::Cancel) { event->ignore(); return; } // Discard: fall through to reject } event->accept(); } // ===== GETTERS ===== QString NewMessageDialog::fromAccount() const { return m_fromCombo->currentText(); } QString NewMessageDialog::to() const { return m_toEdit->text().trimmed(); } QString NewMessageDialog::cc() const { return m_ccEdit->text().trimmed(); } QString NewMessageDialog::bcc() const { return m_bccEdit->text().trimmed(); } QString NewMessageDialog::subject() const { return m_subjectEdit->text().trimmed(); } QString NewMessageDialog::body() const { return m_bodyEdit->toHtml(); }