import sys import re with open('composeview.cpp', 'r') as f: lines = f.readlines() # 1. Remove AddressInputWidget class definition (lines approx 27-52) # Find start and end lines. new_lines = [] i = 0 while i < len(lines): if lines[i].strip() == '// Address input widget with tokenized entries, delete button, drag-drop reorder, and autocomplete': # Skip until after the closing brace of the class # Find the line with '};' after the class j = i while j < len(lines) and not lines[j].strip().startswith('};'): j += 1 # include the '};' line j += 1 i = j continue new_lines.append(lines[i]) i += 1 lines = new_lines # 2. Replace member variable types # We'll process line by line, replacing specific patterns. out_lines = [] for line in lines: # Replace member declarations if 'AddressInputWidget *m_toWidget = nullptr;' in line: line = line.replace('AddressInputWidget *m_toWidget = nullptr;', 'QLineEdit *m_toField = nullptr;') if 'AddressInputWidget *m_ccWidget = nullptr;' in line: line = line.replace('AddressInputWidget *m_ccWidget = nullptr;', 'QLineEdit *m_ccField = nullptr;') if 'AddressInputWidget *m_bccWidget = nullptr;' in line: line = line.replace('AddressInputWidget *m_bccWidget = nullptr;', 'QLineEdit *m_bccField = nullptr;') # Remove duplicate m_ccField and m_bccField lines (they are later) if 'QLineEdit *m_ccField = nullptr; // kept for compatibility? actually not used now' in line: # skip this line continue if 'QLineEdit *m_bccField = nullptr;' in line and '// kept for compatibility' not in line: # This is the second duplicate; we already have one above; we can keep only one. # We'll skip this line. continue out_lines.append(line) lines = out_lines # Now we need to modify setAccountService, setupUI, slots, etc. # We'll do a more aggressive approach: replace entire sections using markers. # Let's join back to string and use regex. text = ''.join(lines) # Replace setAccountService function pattern_setAccountService = re.compile(r'void ComposeView::setAccountService\(AccountService \*service\)\s*\{[\s\S]*?\}', re.MULTILINE) def repl_setAccountService(match): return '''void ComposeView::setAccountService(AccountService *service) { m_accountService = service; populateAccountCombo(); // Update completer models for address widgets with known emails if (m_accountService) { QStringList knownEmails; const QVector accounts = m_accountService->getAllAccounts(); for (const Account &acc : accounts) { knownEmails << acc.email(); } // Set completion model for each widget QStringListModel *model = new QStringListModel(knownEmails, this); QCompleter *comp = new QCompleter(model, this); comp->setCaseSensitivity(Qt::CaseInsensitive); if (m_toField) m_toField->setCompleter(comp); // For cc and bcc we need separate completers but can share model QCompleter *comp2 = new QCompleter(model, this); comp2->setCaseSensitivity(Qt::CaseInsensitive); if (m_ccField) m_ccField->setCompleter(comp2); QCompleter *comp3 = new QCompleter(model, this); comp3->setCaseSensitivity(Qt::CaseInsensitive); if (m_bccField) m_bccField->setCompleter(comp3); } }''' text = re.sub(pattern_setAccountService, repl_setAccountService, text) # Replace setupCore function: we need to replace the whole function. # We'll rewrite from scratch using a marker: we will replace the whole function body. # Let's capture the function and replace. pattern_setupUI = re.compile(r'void ComposeView::setupUI\(\)\s*\{[\s\S]*?\n\}\s*\n(?=void|\Z)', re.MULTILINE) def repl_setupUI(match): return '''void ComposeView::setupUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(20,15,20,15); mainLayout->setSpacing(8); // Top row: Account selector and detach button QHBoxLayout *topLayout = new QHBoxLayout(); m_accountLabel = new QLabel(tr(\"From:\"), this); m_accountLabel->setFixedWidth(40); m_accountLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\")); m_accountCombo = new QComboBox(this); m_accountCombo->setFixedWidth(180); m_accountCombo->setStyleSheet( \"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 8px; \" \"background: white; min-height: 20px; }\"\n \"QComboBox:hover { border-color: #bdbdbd; }\"\n \"QComboBox:focus { border-color: #1976D2; }\" ); m_detachButton = new QPushButton(this); m_detachButton->setToolTip(tr(\"Detach compose window\")); m_detachButton->setText(QStringLiteral(\"⤢\")); m_detachButton->setFixedSize(28,28); m_detachButton->setStyleSheet( \"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; \" \"padding: 6px; color: #555; font-size: 14px; }\"\n \"QPushButton:hover { background: #f0f0f0; }\"\n ); connect(m_detachButton, &QPushButton::clicked, this, &ComposeView::onDetachClicked); topLayout->addWidget(m_accountLabel); topLayout->addWidget(m_accountCombo, 1); topLayout->addWidget(m_detachButton); mainLayout->addLayout(topLayout); // Separator line QFrame *line1 = new QFrame(this); line1->setFrameShape(QFrame::HLine); line1->setStyleSheet(QStringLiteral(\"color: #e0e0e0;\")); mainLayout->addWidget(line1); // To row QHBoxLayout *toLayout = new QHBoxLayout(); QLabel *toLabel = new QLabel(tr(\"To:\"), this); toLabel->setFixedWidth(40); toLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\")); m_toField = new QLineEdit(this); m_toField->setPlaceholderText(tr(\"To recipients\")); toLayout->addWidget(toLabel); toLayout->addWidget(m_toField, 1); mainLayout->addLayout(toLayout); // CC/BCC toggle buttons QHBoxLayout *toggleLayout = new QHBoxLayout(); m_ccButton = new QPushButton(tr(\"CC\"), this); m_ccButton->setCheckable(true); m_ccButton->setChecked(false); connect(m_ccButton, &QPushButton::toggled, this, &ComposeView::onCcToggled); m_bccButton = new QPushButton(tr(\"BCC\"), this); m_bccButton->setCheckable(true); m_bccButton->setChecked(false); connect(m_bccButton, &QPushButton::toggled, this, &ComposeView::onBccToggled); toggleLayout->addWidget(m_ccButton); toggleLayout->addWidget(m_bccButton); toggleLayout->addStretch(); mainLayout->addLayout(toggleLayout); // Cc row (hidden by default) m_ccRow = new QWidget(this); QHBoxLayout *ccLayout = new QHBoxLayout(m_ccRow); ccLayout->setContentsMargins(0,0,0,0); QLabel *ccLabel = new QLabel(tr(\"Cc:\"), this); ccLabel->setFixedWidth(40); ccLabel->setStyleSheet(QStringLiteral(\"color: #555;\")); m_ccField = new QLineEdit(this); m_ccField->setPlaceholderText(tr(\"Cc recipients\")); ccLayout->addWidget(ccLabel); ccLayout->addWidget(m_ccField, 1); m_ccRow->setVisible(false); mainLayout->addWidget(m_ccRow); // Bcc row (hidden by default) m_bccRow = new QWidget(this); QHBoxLayout *bccLayout = new QHBoxLayout(m_bccRow); bccLayout->setContentsMargins(0,0,0,0); QLabel *bccLabel = new QLabel(tr(\"Bcc:\"), this); bccLabel->setFixedWidth(40); bccLabel->setStyleSheet(QStringLiteral(\"color: #555;\")); m_bccField = new QLineEdit(this); m_bccField->setPlaceholderText(tr(\"Bcc recipients\")); bccLayout->addWidget(bccLabel); bccLayout->addWidget(m_bccField, 1); m_bccRow->setVisible(false); mainLayout->addWidget(m_bccRow); // Separator QFrame *line2 = new QFrame(this); line2->setFrameShape(QFrame::HLine); line2->setStyleSheet(QStringLiteral(\"color: #e0e0e0;\")); mainLayout->addWidget(line2); // Subject field QLabel *subjectLabel = new QLabel(tr(\"Subject:\"), this); subjectLabel->setFixedWidth(50); subjectLabel->setStyleSheet(QStringLiteral(\"font-weight: bold; color: #555;\")); m_subjectField = new QLineEdit(this); m_subjectField->setPlaceholderText(tr(\"Subject\")); m_subjectField->setFixedHeight(36); QFont subjectFont = m_subjectField->font(); subjectFont.setPointSize(14); subjectFont.setBold(true); m_subjectField->setFont(subjectFont); QHBoxLayout *subjectLayout = new QHBoxLayout(); subjectLayout->addWidget(subjectLabel); subjectLayout->addWidget(m_subjectField, 1); mainLayout->addLayout(subjectLayout); // Body editor with toolbar m_bodyEditor = new RichTextEditor(this); m_bodyEditor->setupToolbar(mainLayout); mainLayout->addWidget(m_bodyEditor, 1); // Schedule panel (hidden by default) m_schedulePanel = new QWidget(this); QHBoxLayout *scheduleLayout = new QHBoxLayout(m_schedulePanel); scheduleLayout->setContentsMargins(0,0,0,0); QLabel *scheduleLabel = new QLabel(tr(\"Send at:\"), this); scheduleLabel->setStyleSheet(QStringLiteral(\"color: #555; font-weight: bold;\")); m_schedulePicker = new QDateTimeEdit(QDateTime::currentDateTime().addSecs(3600), this); m_schedulePicker->setCalendarPopup(true); m_schedulePicker->setDisplayFormat(QStringLiteral(\"dd/MM/yyyy hh:mm AP\")); scheduleLayout->addWidget(scheduleLabel); scheduleLayout->addWidget(m_schedulePicker); scheduleLayout->addStretch(); m_scheduleSendButton = new QPushButton(tr(\"Schedule Send\"), this); m_scheduleSendButton->setStyleSheet( \"QPushButton { background-color: #1976D2; color: white; border: none; border-radius: 4px; \" \"padding: 6px 16px; font-weight: bold; }\"\n \"QPushButton:hover { background-color: #1565C0; }\"\n ); connect(m_scheduleSendButton, &QPushButton::clicked, this, &ComposeView::onScheduleClicked); scheduleLayout->addWidget(m_scheduleSendButton); m_schedulePanel->setVisible(false); mainLayout->addWidget(m_schedulePanel); // Action buttons row QHBoxLayout *actionsLayout = new QHBoxLayout(); actionsLayout->addStretch(); m_discardButton = new QPushButton(tr(\"Discard\"), this); m_discardButton->setStyleSheet( \"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; \" \"padding: 8px 20px; color: #555; }\"\n \"QPushButton:hover { background: #f5f5f5; }\"\n ); actionsLayout->addWidget(m_discardButton); connect(m_discardButton, &QPushButton::clicked, this, &ComposeView::discardRequested); // Split button for Send / Schedule m_sendMenu = new QMenu(this); m_sendNowAction = m_sendMenu->addAction(tr(\"Send Now\")); connect(m_sendNowAction, &QAction::triggered, this, &ComposeView::onSendClicked); m_scheduleAction = m_sendMenu->addAction(tr(\"Schedule for later...\")); connect(m_scheduleAction, &QAction::triggered, [this]() { m_schedulePanel->setVisible(true); }); m_sendSplit = new QToolButton(this); m_sendSplit->setText(tr(\"Send\")); m_sendSplit->setToolButtonStyle(Qt::ToolButtonTextOnly); m_sendSplit->setPopupMode(QToolButton::MenuButtonPopup); m_sendSplit->setMenu(m_sendMenu); m_sendSplit->setStyleSheet( \"QToolButton { background-color: #1976D2; color: white; border: none; border-radius: 4px; \" \"padding: 8px 24px; font-weight: bold; }\"\n \"QToolButton:hover { background-color: #1565C0; }\"\n \"QToolButton::menu-button { border-left: 1px solid rgba(255,255,255,0.3); padding-left: 8px; padding-right: 8px; }\"\n \"QToolButton::menu-button:hover { background-color: #1565C0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; }\"\n ); connect(m_sendSplit, &QToolButton::clicked, this, &ComposeView::onSendClicked); actionsLayout->addWidget(m_sendSplit); mainLayout->addLayout(actionsLayout); // Connect rich text editor signature signals connect(m_bodyEditor, &RichTextEditor::signatureClicked, this, &ComposeView::onSignatureClicked); connect(m_bodyEditor, &RichTextEditor::signatureEditRequested, this, &ComposeView::onSignatureEditRequested); } ''' text = re.sub(pattern_setupUI, repl_setupUI, text) # Replace onCcToggle slot (we renamed to onCcToggled with bool) pattern_onCcToggle = re.compile(r'void ComposeView::onCcToggle\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_onCcToggle(match): return '''void ComposeView::onCcToggled(bool checked) { m_ccRow->setVisible(checked); if (checked) { m_ccField->setFocus(); } } ''' text = re.sub(pattern_onCcToggle, repl_onCcToggle, text) # Replace onBccToggle similarly pattern_onBccToggle = re.compile(r'void ComposeView::onBccToggle\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_onBccToggle(match): return '''void ComposeView::onBccToggled(bool checked) { m_bccRow->setVisible(checked); if (checked) { m_bccField->setFocus(); } } ''' text = re.sub(pattern_onBccToggle, repl_onBccToggle, text) # Replace onSendClicked pattern_onSendClicked = re.compile(r'void ComposeView::onSendClicked\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_onSendClicked(match): return '''void ComposeView::onSendClicked() { QString to = m_toField->text(); QString cc = m_ccButton->isChecked() ? m_ccField->text() : QString(); QString bcc = m_bccButton->isChecked() ? m_bccField->text() : QString(); QString subject = m_subjectField->text(); QString body = m_bodyEditor->toHtml(); QDateTime scheduleTime = m_schedulePanel->isVisible() ? m_schedulePicker->dateTime() : QDateTime(); QString fromAddress; if (m_currentAccountId > 0 && m_accountCombo->currentIndex() > 0) { fromAddress = m_accountCombo->currentData().toString(); } emit sendRequested(to, cc, bcc, subject, body, scheduleTime, fromAddress); } ''' text = re.sub(pattern_onSendClicked, repl_onSendClicked, text) # Replace initializeComposition pattern_initComp = re.compile(r'void ComposeView::initializeComposition\(\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_initComp(match): return '''void ComposeView::initializeComposition() { m_subjectField->clear(); m_bodyEditor->clear(); m_toField->clear(); m_ccField->clear(); m_bccField->clear(); m_ccRow->setVisible(false); m_bccRow->setVisible(false); m_schedulePanel->setVisible(false); m_subjectField->setFocus(); } ''' text = re.sub(pattern_initComp, repl_initComp, text) # Replace startNewEmail pattern_startNew = re.compile(r'void ComposeView::startNewEmail\(const QString \&initialRecipient\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_startNew(match): return '''void ComposeView::startNewEmail(const QString &initialRecipient) { initializeComposition(); if (!initialRecipient.isEmpty()) { m_toField->setText(initialRecipient); } m_toField->setFocus(); } ''' text = re.sub(pattern_startNew, repl_startNew, text) # Replace setTo pattern_setTo = re.compile(r'void ComposeView::setTo\(const QString \&to\)\s*\{[\s\S]*?\n\}\s*\n', re.MULTILINE) def repl_setTo(match): return '''void ComposeView::setTo(const QString &to) { if (to.isEmpty()) { m_toField->clear(); return; } // Split by semicolon or comma QStringList parts = to.split(QRegularExpression(\"[,;]\"), Qt::SkipEmptyParts); QStringList cleaned; for (const QString &part : parts) { QString trimmed = part.trimmed(); if (!trimmed.isEmpty()) cleaned << trimmed; } m_toField->setText(cleaned.join(\"; \")); } ''' text = re.sub(pattern_setTo, repl_setTo, text) # We also need to add a helper function splitAddresses if we used it, but we didn't. # Ensure we have the forward declaration for the slot in header (already done). # Now write the file. with open('composeview.cpp', 'w') as f: f.write(text)