MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - ui - TaskEditDialog.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 81.7 % 153 125
Test Date: 2026-06-21 21:10:19 Functions: 90.0 % 10 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 37.7 % 440 166

             Branch data     Line data    Source code
       1                 :             : #include "TaskEditDialog.h"
       2                 :             : 
       3                 :             : #include "ui/ThemeManager.h"
       4                 :             : 
       5                 :             : #include <QCheckBox>
       6                 :             : #include <QComboBox>
       7                 :             : #include <QDateEdit>
       8                 :             : #include <QDialogButtonBox>
       9                 :             : #include <QFormLayout>
      10                 :             : #include <QLabel>
      11                 :             : #include <QLineEdit>
      12                 :             : #include <QTextEdit>
      13                 :             : #include "ui/MarkdownHighlighter.h"
      14                 :             : #include <QPushButton>
      15                 :             : #include <QUuid>
      16                 :             : #include <QVBoxLayout>
      17                 :             : 
      18         [ +  - ]:           7 : TaskEditDialog::TaskEditDialog(QWidget *parent) : QDialog(parent) {
      19         [ +  - ]:          14 :   setObjectName(QStringLiteral("taskEditDialog"));
      20         [ +  - ]:           7 :   setAttribute(Qt::WA_StyledBackground, true);
      21         [ +  - ]:           7 :   setupUi();
      22                 :           7 : }
      23                 :             : 
      24                 :             : // T-76.B3: Runtime language switching
      25                 :          20 : void TaskEditDialog::changeEvent(QEvent *event) {
      26                 :          20 :   QDialog::changeEvent(event);
      27         [ -  + ]:          20 :   if (event->type() == QEvent::LanguageChange)
      28                 :           0 :     retranslateUi();
      29                 :          20 : }
      30                 :             : 
      31                 :           0 : void TaskEditDialog::retranslateUi() {
      32   [ #  #  #  #  :           0 :   setWindowTitle(m_isNew ? tr("New task") : tr("Edit task"));
             #  #  #  # ]
      33   [ #  #  #  #  :           0 :   m_headerLabel->setText((m_isNew ? QStringLiteral("\u2705 ")
                   #  # ]
      34   [ #  #  #  #  :           0 :                                   : QStringLiteral("\u270f\ufe0f "))
                   #  # ]
      35   [ #  #  #  # ]:           0 :                          + windowTitle());
      36   [ #  #  #  # ]:           0 :   m_titleLabel->setText(tr("Title:"));
      37   [ #  #  #  # ]:           0 :   m_calendarLabel->setText(tr("Calendar:"));
      38   [ #  #  #  # ]:           0 :   m_dueLabel->setText(tr("Due:"));
      39   [ #  #  #  # ]:           0 :   m_priorityLabel->setText(tr("Priority:"));
      40   [ #  #  #  # ]:           0 :   m_descriptionLabel->setText(tr("Description:"));
      41   [ #  #  #  # ]:           0 :   m_titleEdit->setPlaceholderText(tr("Enter task..."));
      42   [ #  #  #  # ]:           0 :   m_descEdit->setPlaceholderText(tr("Description (Markdown)..."));
      43   [ #  #  #  # ]:           0 :   m_priorityCombo->setItemText(0, tr("Normal"));
      44   [ #  #  #  # ]:           0 :   m_priorityCombo->setItemText(1, tr("High"));
      45   [ #  #  #  # ]:           0 :   m_priorityCombo->setItemText(2, tr("Urgent"));
      46   [ #  #  #  # ]:           0 :   m_cancelBtn->setText(tr("Cancel"));
      47   [ #  #  #  # ]:           0 :   m_saveBtn->setText(tr("Save"));
      48                 :           0 : }
      49                 :             : 
      50                 :           7 : void TaskEditDialog::setupUi() {
      51         [ +  - ]:           7 :   setMinimumWidth(400);
      52   [ +  -  +  - ]:           7 :   setWindowTitle(tr("New task"));
      53                 :             : 
      54   [ +  -  +  -  :           7 :   auto *mainLayout = new QVBoxLayout(this);
             -  +  -  - ]
      55         [ +  - ]:           7 :   mainLayout->setSpacing(12);
      56         [ +  - ]:           7 :   mainLayout->setContentsMargins(16, 16, 16, 16);
      57                 :             : 
      58                 :             :   // Header
      59   [ +  -  +  -  :          14 :   m_headerLabel = new QLabel(QStringLiteral("\u2705 ") + windowTitle(), this);
          +  -  +  -  -  
                +  -  - ]
      60         [ +  - ]:           7 :   QFont hf = m_headerLabel->font();
      61         [ +  - ]:           7 :   hf.setPointSize(14);
      62         [ +  - ]:           7 :   hf.setBold(true);
      63         [ +  - ]:           7 :   m_headerLabel->setFont(hf);
      64         [ +  - ]:           7 :   mainLayout->addWidget(m_headerLabel);
      65                 :             : 
      66                 :             :   // Form
      67   [ +  -  +  -  :           7 :   auto *form = new QFormLayout();
             -  +  -  - ]
      68         [ +  - ]:           7 :   form->setLabelAlignment(Qt::AlignRight);
      69         [ +  - ]:           7 :   form->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
      70                 :             : 
      71                 :             :   // Title
      72   [ +  -  +  -  :           7 :   m_titleEdit = new QLineEdit(this);
             -  +  -  - ]
      73   [ +  -  +  - ]:           7 :   m_titleEdit->setPlaceholderText(tr("Enter task..."));
      74   [ +  -  +  -  :           7 :   m_titleLabel = new QLabel(tr("Title:"), this);
          +  -  -  +  -  
                      - ]
      75         [ +  - ]:           7 :   form->addRow(m_titleLabel, m_titleEdit);
      76                 :             : 
      77                 :             :   // Calendar
      78   [ +  -  +  -  :           7 :   m_calendarCombo = new QComboBox(this);
             -  +  -  - ]
      79   [ +  -  +  -  :           7 :   m_calendarLabel = new QLabel(tr("Calendar:"), this);
          +  -  -  +  -  
                      - ]
      80         [ +  - ]:           7 :   form->addRow(m_calendarLabel, m_calendarCombo);
      81                 :             : 
      82                 :             :   // Due date (optional)
      83   [ +  -  +  -  :           7 :   auto *dueRow = new QHBoxLayout();
             -  +  -  - ]
      84   [ +  -  +  -  :           7 :   m_dueEnabled = new QCheckBox(this);
             -  +  -  - ]
      85   [ +  -  +  -  :           7 :   m_dueDate = new QDateEdit(QDate::currentDate().addDays(7), this);
          +  -  +  -  -  
                +  -  - ]
      86         [ +  - ]:           7 :   m_dueDate->setCalendarPopup(true);
      87         [ +  - ]:          14 :   m_dueDate->setDisplayFormat(QStringLiteral("dd.MM.yyyy"));
      88         [ +  - ]:           7 :   m_dueDate->setEnabled(false);
      89         [ +  - ]:           7 :   dueRow->addWidget(m_dueEnabled);
      90         [ +  - ]:           7 :   dueRow->addWidget(m_dueDate);
      91   [ +  -  +  -  :           7 :   m_dueLabel = new QLabel(tr("Due:"), this);
          +  -  -  +  -  
                      - ]
      92         [ +  - ]:           7 :   form->addRow(m_dueLabel, dueRow);
      93                 :           7 :   connect(m_dueEnabled, &QCheckBox::toggled, m_dueDate,
      94         [ +  - ]:           7 :           &QDateEdit::setEnabled);
      95                 :             : 
      96                 :             :   // Priority
      97   [ +  -  +  -  :           7 :   m_priorityCombo = new QComboBox(this);
             -  +  -  - ]
      98   [ +  -  +  - ]:           7 :   m_priorityCombo->addItem(tr("Normal"), 0);
      99   [ +  -  +  - ]:           7 :   m_priorityCombo->addItem(tr("High"), 5);
     100   [ +  -  +  - ]:           7 :   m_priorityCombo->addItem(tr("Urgent"), 9);
     101         [ +  - ]:          14 :   m_priorityCombo->addItem(QStringLiteral("★ Starred"), 1);
     102   [ +  -  +  -  :           7 :   m_priorityLabel = new QLabel(tr("Priority:"), this);
          +  -  -  +  -  
                      - ]
     103         [ +  - ]:           7 :   form->addRow(m_priorityLabel, m_priorityCombo);
     104                 :             : 
     105                 :             :   // Description
     106   [ +  -  +  -  :           7 :   m_descEdit = new QTextEdit(this);
             -  +  -  - ]
     107         [ +  - ]:           7 :   m_descEdit->setAcceptRichText(false);
     108         [ +  - ]:           7 :   m_descEdit->setMaximumHeight(160);
     109   [ +  -  +  - ]:           7 :   m_descEdit->setPlaceholderText(tr("Description (Markdown)..."));
     110   [ +  -  +  -  :           7 :   new MarkdownHighlighter(m_descEdit->document());
          +  -  -  +  -  
                      - ]
     111   [ +  -  +  -  :           7 :   m_descriptionLabel = new QLabel(tr("Description:"), this);
          +  -  -  +  -  
                      - ]
     112         [ +  - ]:           7 :   form->addRow(m_descriptionLabel, m_descEdit);
     113                 :             : 
     114         [ +  - ]:           7 :   mainLayout->addLayout(form);
     115                 :             : 
     116                 :             :   // Buttons
     117   [ +  -  +  -  :           7 :   auto *btnBox = new QDialogButtonBox(this);
             -  +  -  - ]
     118   [ +  -  +  - ]:           7 :   m_cancelBtn = btnBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
     119   [ +  -  +  - ]:           7 :   m_saveBtn = btnBox->addButton(tr("Save"), QDialogButtonBox::AcceptRole);
     120         [ +  - ]:          14 :   m_saveBtn->setObjectName(QStringLiteral("taskSaveButton"));
     121         [ +  - ]:           7 :   m_saveBtn->setDefault(true);
     122         [ +  - ]:           7 :   mainLayout->addWidget(btnBox);
     123                 :             : 
     124                 :             :   // Connections
     125         [ +  - ]:           7 :   connect(btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
     126         [ +  - ]:           7 :   connect(btnBox, &QDialogButtonBox::accepted, this, [this]() {
     127   [ #  #  #  #  :           0 :     if (m_titleEdit->text().trimmed().isEmpty()) {
                   #  # ]
     128                 :           0 :       m_titleEdit->setFocus();
     129         [ #  # ]:           0 :       m_titleEdit->setStyleSheet(
     130                 :           0 :           QStringLiteral("QLineEdit { border: 1px solid %1; }")
     131   [ #  #  #  #  :           0 :               .arg(ThemeManager::instance().color(
                   #  # ]
     132                 :           0 :                   QStringLiteral("@danger"))));
     133                 :           0 :       return;
     134                 :             :     }
     135                 :           0 :     accept();
     136                 :             :   });
     137         [ +  - ]:           7 :   connect(m_titleEdit, &QLineEdit::textChanged, this, [this]() {
     138         [ +  - ]:           3 :     m_titleEdit->setStyleSheet(QString());
     139                 :           3 :   });
     140                 :             : 
     141                 :             :   // Dialog styling lives in main.qss (67.B3); the save button picks up
     142                 :             :   // the QPushButton#taskSaveButton accent rule.
     143                 :           7 : }
     144                 :             : 
     145                 :           6 : void TaskEditDialog::setCalendars(const QList<CalendarInfo> &calendars) {
     146                 :           6 :   m_calendars = calendars;
     147                 :           6 :   m_calendarCombo->clear();
     148         [ +  + ]:          14 :   for (int i = 0; i < calendars.size(); ++i) {
     149                 :           8 :     const auto &cal = calendars.at(i);
     150         [ -  + ]:           8 :     QString name = cal.displayName.isEmpty() ? cal.path : cal.displayName;
     151         [ +  - ]:           8 :     m_calendarCombo->addItem(name, i);
     152                 :           8 :   }
     153                 :           6 : }
     154                 :             : 
     155                 :           3 : void TaskEditDialog::setTask(const CalendarTask &task) {
     156                 :           3 :   m_isNew = false;
     157                 :           3 :   m_originalTask = task;
     158                 :             :   // Sprint 56: Update title for edit mode
     159   [ +  -  +  - ]:           3 :   setWindowTitle(tr("Edit task"));
     160   [ +  -  +  -  :           6 :   m_headerLabel->setText(QStringLiteral("\u270f\ufe0f ") + windowTitle());
                   +  - ]
     161                 :             : 
     162                 :           3 :   m_titleEdit->setText(task.summary);
     163                 :             : 
     164                 :             :   // Due
     165         [ +  + ]:           3 :   if (task.due.isValid()) {
     166                 :           1 :     m_dueEnabled->setChecked(true);
     167                 :           1 :     m_dueDate->setDate(task.due.date());
     168                 :             :   }
     169                 :             : 
     170                 :             :   // Priority
     171         [ +  - ]:           6 :   for (int i = 0; i < m_priorityCombo->count(); ++i) {
     172   [ +  -  +  -  :           6 :     if (m_priorityCombo->itemData(i).toInt() == task.priority) {
                   +  + ]
     173                 :           3 :       m_priorityCombo->setCurrentIndex(i);
     174                 :           3 :       break;
     175                 :             :     }
     176                 :             :   }
     177                 :             : 
     178                 :             :   // Calendar
     179         [ +  - ]:           4 :   for (int i = 0; i < m_calendars.size(); ++i) {
     180                 :           4 :     const auto &cal = m_calendars.at(i);
     181   [ +  +  -  +  :           7 :     if (cal.path == task.calendarPath &&
                   +  + ]
     182         [ -  - ]:           3 :         (task.accountId.isEmpty() || cal.accountId == task.accountId)) {
     183                 :           3 :       m_calendarCombo->setCurrentIndex(i);
     184                 :           3 :       break;
     185                 :             :     }
     186                 :             :   }
     187                 :             :   // Sprint 56: Track calendar changes in edit mode
     188         [ +  - ]:           3 :   connect(m_calendarCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
     189                 :           3 :           this, [this]() { m_calendarChanged = true; });
     190                 :             : 
     191                 :           3 :   m_descEdit->setPlainText(task.description);
     192                 :           3 : }
     193                 :             : 
     194                 :           2 : CalendarTask TaskEditDialog::result() const {
     195                 :           2 :   CalendarTask t = m_originalTask;
     196   [ +  -  +  - ]:           2 :   t.summary = m_titleEdit->text().trimmed();
     197         [ +  - ]:           2 :   t.description = m_descEdit->toPlainText();
     198   [ +  -  +  - ]:           2 :   t.priority = m_priorityCombo->currentData().toInt();
     199                 :             : 
     200   [ +  -  +  + ]:           2 :   if (m_dueEnabled->isChecked()) {
     201   [ +  -  +  -  :           1 :     t.due = QDateTime(m_dueDate->date(), QTime(0, 0));
                   +  - ]
     202                 :             :   } else {
     203                 :           1 :     t.due = QDateTime();
     204                 :             :   }
     205                 :             : 
     206         [ +  + ]:           2 :   if (m_isNew) {
     207                 :           1 :     t.status = QStringLiteral("NEEDS-ACTION");
     208   [ +  -  +  - ]:           1 :     if (m_calendarCombo->currentIndex() >= 0) {
     209   [ +  -  +  - ]:           1 :       const int calIndex = m_calendarCombo->currentData().toInt();
     210   [ +  -  +  -  :           1 :       if (calIndex >= 0 && calIndex < m_calendars.size()) {
                   +  - ]
     211                 :           1 :         t.calendarPath = m_calendars.at(calIndex).path;
     212                 :           1 :         t.accountId = m_calendars.at(calIndex).accountId;
     213                 :             :       }
     214                 :             :     }
     215         [ +  - ]:           1 :     if (t.uid.isEmpty())
     216   [ +  -  +  - ]:           1 :       t.uid = QUuid::createUuid().toString(QUuid::WithoutBraces);
     217         [ +  - ]:           1 :   } else if (m_calendarChanged) {
     218                 :             :     // Sprint 56: Calendar move — update path
     219   [ +  -  +  - ]:           1 :     const int calIndex = m_calendarCombo->currentData().toInt();
     220   [ +  -  +  -  :           1 :     if (calIndex >= 0 && calIndex < m_calendars.size()) {
                   +  - ]
     221                 :           1 :       t.calendarPath = m_calendars.at(calIndex).path;
     222                 :           1 :       t.accountId = m_calendars.at(calIndex).accountId;
     223                 :             :     }
     224                 :             :   }
     225                 :             : 
     226                 :           2 :   return t;
     227                 :           0 : }
        

Generated by: LCOV version 2.0-1