MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - ui - FilterPopoverWidget.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 92.6 % 189 175
Test Date: 2026-06-21 21:10:19 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 43.3 % 614 266

             Branch data     Line data    Source code
       1                 :             : #include "FilterPopoverWidget.h"
       2                 :             : #include "CheckableFolderCombo.h"
       3                 :             : 
       4                 :             : #include <QComboBox>
       5                 :             : #include <QDateEdit>
       6                 :             : #include <QEvent>
       7                 :             : #include <QFormLayout>
       8                 :             : #include <QLabel>
       9                 :             : #include <QLineEdit>
      10                 :             : #include <QVBoxLayout>
      11                 :             : #include <QGroupBox>
      12                 :             : #include <QSignalBlocker>
      13                 :             : 
      14                 :             : using Tri = MailCache::SearchFilter::Tri;
      15                 :             : 
      16                 :             : namespace {
      17                 :         384 : int triToIndex(Tri t) {
      18   [ +  +  +  - ]:         384 :   switch (t) {
      19                 :           9 :   case Tri::Yes: return 1;
      20                 :           2 :   case Tri::No: return 2;
      21                 :         373 :   case Tri::Any: break;
      22                 :             :   }
      23                 :         373 :   return 0;
      24                 :             : }
      25                 :         540 : Tri indexToTri(int index) {
      26      [ +  +  + ]:         540 :   switch (index) {
      27                 :           4 :   case 1: return Tri::Yes;
      28                 :           2 :   case 2: return Tri::No;
      29                 :         534 :   default: return Tri::Any;
      30                 :             :   }
      31                 :             : }
      32                 :             : enum DatePreset { PresetAny = 0, PresetToday, PresetWeek, PresetMonth, PresetYear, PresetCustom };
      33                 :             : }
      34                 :             : 
      35         [ +  - ]:          66 : FilterPopoverWidget::FilterPopoverWidget(QWidget *parent) : QWidget(parent) {
      36                 :             :   // Make it a popup that closes when clicking outside
      37         [ +  - ]:          66 :   setWindowFlags(Qt::Popup | Qt::FramelessWindowHint);
      38         [ +  - ]:          66 :   setAttribute(Qt::WA_StyledBackground, true);
      39         [ +  - ]:         132 :   setObjectName("filterPopover");
      40         [ +  - ]:          66 :   setupUi();
      41                 :          66 : }
      42                 :             : 
      43                 :          66 : void FilterPopoverWidget::setupUi() {
      44   [ +  -  +  -  :          66 :   auto *mainLayout = new QVBoxLayout(this);
             -  +  -  - ]
      45         [ +  - ]:          66 :   mainLayout->setContentsMargins(12, 12, 12, 12);
      46         [ +  - ]:          66 :   mainLayout->setSpacing(12);
      47                 :             : 
      48                 :             :   // Helper to connect signals
      49                 :         726 :   auto connectEdit = [this](QWidget *w) {
      50         [ +  + ]:         726 :     if (auto *le = qobject_cast<QLineEdit*>(w))
      51         [ +  - ]:         264 :       connect(le, &QLineEdit::textEdited, this, &FilterPopoverWidget::onFieldChanged);
      52         [ +  + ]:         462 :     else if (auto *cb = qobject_cast<QComboBox*>(w))
      53         [ +  - ]:         330 :       connect(cb, &QComboBox::currentIndexChanged, this, &FilterPopoverWidget::onFieldChanged);
      54         [ +  - ]:         132 :     else if (auto *de = qobject_cast<QDateEdit*>(w))
      55         [ +  - ]:         132 :       connect(de, &QDateEdit::dateChanged, this, &FilterPopoverWidget::onFieldChanged);
      56         [ #  # ]:           0 :     else if (auto *cfc = qobject_cast<CheckableFolderCombo*>(w))
      57         [ #  # ]:           0 :       connect(cfc, &CheckableFolderCombo::selectionChanged, this, &FilterPopoverWidget::onFieldChanged);
      58                 :         726 :   };
      59                 :             : 
      60                 :             :   // Text Form
      61   [ +  -  +  -  :          66 :   auto *textForm = new QFormLayout();
             -  +  -  - ]
      62   [ +  -  +  -  :          66 :   m_subject = new QLineEdit(this);
             -  +  -  - ]
      63         [ +  - ]:         132 :   m_subject->setObjectName("subjectEdit");
      64   [ +  -  +  -  :          66 :   m_from = new QLineEdit(this);
             -  +  -  - ]
      65         [ +  - ]:         132 :   m_from->setObjectName("fromEdit");
      66   [ +  -  +  -  :          66 :   m_to = new QLineEdit(this);
             -  +  -  - ]
      67         [ +  - ]:         132 :   m_to->setObjectName("toEdit");
      68   [ +  -  +  -  :          66 :   m_subjectLabel = new QLabel(tr("Subject:"), this);
          +  -  -  +  -  
                      - ]
      69   [ +  -  +  -  :          66 :   m_fromLabel = new QLabel(tr("From:"), this);
          +  -  -  +  -  
                      - ]
      70   [ +  -  +  -  :          66 :   m_toLabel = new QLabel(tr("To:"), this);
          +  -  -  +  -  
                      - ]
      71         [ +  - ]:          66 :   textForm->addRow(m_subjectLabel, m_subject);
      72         [ +  - ]:          66 :   textForm->addRow(m_fromLabel, m_from);
      73         [ +  - ]:          66 :   textForm->addRow(m_toLabel, m_to);
      74   [ +  -  +  -  :          66 :   connectEdit(m_subject); connectEdit(m_from); connectEdit(m_to);
                   +  - ]
      75         [ +  - ]:          66 :   mainLayout->addLayout(textForm);
      76                 :             : 
      77                 :             :   // Status Form
      78   [ +  -  +  -  :          66 :   auto *statusForm = new QFormLayout();
             -  +  -  - ]
      79                 :         264 :   auto makeTriCombo = [this, connectEdit]() {
      80   [ +  -  -  +  :         264 :     auto *cb = new QComboBox(this);
                   -  - ]
      81   [ +  -  +  +  :        1056 :     cb->addItems({tr("Any"), tr("Yes"), tr("No")});
                   -  - ]
      82                 :         264 :     connectEdit(cb);
      83                 :         264 :     return cb;
      84   [ +  -  +  -  :         264 :   };
          +  -  +  -  -  
                -  -  - ]
      85         [ +  - ]:          66 :   m_unread = makeTriCombo();
      86         [ +  - ]:          66 :   m_flagged = makeTriCombo();
      87         [ +  - ]:          66 :   m_answered = makeTriCombo();
      88         [ +  - ]:          66 :   m_attachment = makeTriCombo();
      89   [ +  -  +  -  :          66 :   m_unreadLabel = new QLabel(tr("Unread:"), this);
          +  -  -  +  -  
                      - ]
      90   [ +  -  +  -  :          66 :   m_flaggedLabel = new QLabel(tr("Flagged:"), this);
          +  -  -  +  -  
                      - ]
      91   [ +  -  +  -  :          66 :   m_answeredLabel = new QLabel(tr("Answered:"), this);
          +  -  -  +  -  
                      - ]
      92   [ +  -  +  -  :          66 :   m_attachmentLabel = new QLabel(tr("Attachment:"), this);
          +  -  -  +  -  
                      - ]
      93         [ +  - ]:          66 :   statusForm->addRow(m_unreadLabel, m_unread);
      94         [ +  - ]:          66 :   statusForm->addRow(m_flaggedLabel, m_flagged);
      95         [ +  - ]:          66 :   statusForm->addRow(m_answeredLabel, m_answered);
      96         [ +  - ]:          66 :   statusForm->addRow(m_attachmentLabel, m_attachment);
      97         [ +  - ]:          66 :   mainLayout->addLayout(statusForm);
      98                 :             : 
      99                 :             :   // Location Form
     100   [ +  -  +  -  :          66 :   auto *locForm = new QFormLayout();
             -  +  -  - ]
     101   [ +  -  +  -  :          66 :   m_folder = new CheckableFolderCombo(this);
             -  +  -  - ]
     102   [ +  -  +  -  :          66 :   m_tags = new QLineEdit(this);
             -  +  -  - ]
     103   [ +  -  +  -  :          66 :   m_folderLabel = new QLabel(tr("Folder:"), this);
          +  -  -  +  -  
                      - ]
     104   [ +  -  +  -  :          66 :   m_tagsLabel = new QLabel(tr("Tags:"), this);
          +  -  -  +  -  
                      - ]
     105         [ +  - ]:          66 :   locForm->addRow(m_folderLabel, m_folder);
     106         [ +  - ]:          66 :   locForm->addRow(m_tagsLabel, m_tags);
     107   [ +  -  +  - ]:          66 :   connectEdit(m_folder); connectEdit(m_tags);
     108         [ +  - ]:          66 :   mainLayout->addLayout(locForm);
     109                 :             : 
     110                 :             :   // Date Form
     111   [ +  -  +  -  :          66 :   auto *dateForm = new QFormLayout();
             -  +  -  - ]
     112   [ +  -  +  -  :          66 :   m_datePreset = new QComboBox(this);
             -  +  -  - ]
     113   [ +  -  +  +  :         462 :   m_datePreset->addItems({tr("Any time"), tr("Today"), tr("Past week"), tr("Past month"), tr("Past year"), tr("Custom range")});
                   -  - ]
     114   [ +  -  +  -  :          66 :   m_dateFrom = new QDateEdit(this);
             -  +  -  - ]
     115         [ +  - ]:          66 :   m_dateFrom->setCalendarPopup(true);
     116   [ +  -  +  -  :          66 :   m_dateTo = new QDateEdit(this);
             -  +  -  - ]
     117         [ +  - ]:          66 :   m_dateTo->setCalendarPopup(true);
     118         [ +  - ]:          66 :   m_dateFrom->setEnabled(false);
     119         [ +  - ]:          66 :   m_dateTo->setEnabled(false);
     120                 :             :   
     121         [ +  - ]:          66 :   connect(m_datePreset, &QComboBox::currentIndexChanged, this, [this](int idx) {
     122                 :           0 :     bool custom = (idx == PresetCustom);
     123                 :           0 :     m_dateFrom->setEnabled(custom);
     124                 :           0 :     m_dateTo->setEnabled(custom);
     125                 :           0 :     onFieldChanged();
     126                 :           0 :   });
     127                 :             : 
     128   [ +  -  +  -  :          66 :   m_dateLabel = new QLabel(tr("Date:"), this);
          +  -  -  +  -  
                      - ]
     129   [ +  -  +  -  :          66 :   m_sinceLabel = new QLabel(tr("Since:"), this);
          +  -  -  +  -  
                      - ]
     130   [ +  -  +  -  :          66 :   m_untilLabel = new QLabel(tr("Until:"), this);
          +  -  -  +  -  
                      - ]
     131         [ +  - ]:          66 :   dateForm->addRow(m_dateLabel, m_datePreset);
     132         [ +  - ]:          66 :   dateForm->addRow(m_sinceLabel, m_dateFrom);
     133         [ +  - ]:          66 :   dateForm->addRow(m_untilLabel, m_dateTo);
     134   [ +  -  +  - ]:          66 :   connectEdit(m_dateFrom); connectEdit(m_dateTo);
     135         [ +  - ]:          66 :   mainLayout->addLayout(dateForm);
     136   [ +  -  +  -  :         132 : }
          +  -  +  -  +  
          -  +  -  +  -  
             -  -  -  - ]
     137                 :             : 
     138                 :        1799 : void FilterPopoverWidget::changeEvent(QEvent *event) {
     139         [ +  + ]:        1799 :   if (event->type() == QEvent::LanguageChange)
     140                 :         170 :     retranslateUi();
     141                 :        1799 :   QWidget::changeEvent(event);
     142                 :        1799 : }
     143                 :             : 
     144                 :         170 : void FilterPopoverWidget::retranslateUi() {
     145   [ +  -  +  - ]:         170 :   m_subjectLabel->setText(tr("Subject:"));
     146   [ +  -  +  - ]:         170 :   m_fromLabel->setText(tr("From:"));
     147   [ +  -  +  - ]:         170 :   m_toLabel->setText(tr("To:"));
     148   [ +  -  +  - ]:         170 :   m_unreadLabel->setText(tr("Unread:"));
     149   [ +  -  +  - ]:         170 :   m_flaggedLabel->setText(tr("Flagged:"));
     150   [ +  -  +  - ]:         170 :   m_answeredLabel->setText(tr("Answered:"));
     151   [ +  -  +  - ]:         170 :   m_attachmentLabel->setText(tr("Attachment:"));
     152   [ +  -  +  - ]:         170 :   m_folderLabel->setText(tr("Folder:"));
     153   [ +  -  +  - ]:         170 :   m_tagsLabel->setText(tr("Tags:"));
     154   [ +  -  +  - ]:         170 :   m_dateLabel->setText(tr("Date:"));
     155   [ +  -  +  - ]:         170 :   m_sinceLabel->setText(tr("Since:"));
     156   [ +  -  +  - ]:         170 :   m_untilLabel->setText(tr("Until:"));
     157                 :             : 
     158         [ +  + ]:         850 :   for (auto *cb : {m_unread, m_flagged, m_answered, m_attachment}) {
     159   [ +  -  +  - ]:         680 :     cb->setItemText(0, tr("Any"));
     160   [ +  -  +  - ]:         680 :     cb->setItemText(1, tr("Yes"));
     161   [ +  -  +  - ]:         680 :     cb->setItemText(2, tr("No"));
     162                 :             :   }
     163                 :             : 
     164   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetAny, tr("Any time"));
     165   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetToday, tr("Today"));
     166   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetWeek, tr("Past week"));
     167   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetMonth, tr("Past month"));
     168   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetYear, tr("Past year"));
     169   [ +  -  +  - ]:         170 :   m_datePreset->setItemText(PresetCustom, tr("Custom range"));
     170                 :         170 : }
     171                 :             : 
     172                 :          36 : void FilterPopoverWidget::onFieldChanged() {
     173                 :          36 :   emit filterEdited();
     174                 :          36 : }
     175                 :             : 
     176                 :          96 : void FilterPopoverWidget::setFilter(const MailCache::SearchFilter &f) {
     177                 :          96 :   QSignalBlocker bSub(m_subject), bFrom(m_from), bTo(m_to);
     178                 :          96 :   QSignalBlocker bUnr(m_unread), bFlg(m_flagged), bAns(m_answered), bAtt(m_attachment);
     179                 :          96 :   QSignalBlocker bFol(m_folder), bTag(m_tags), bDatP(m_datePreset), bDatF(m_dateFrom), bDatT(m_dateTo);
     180                 :             : 
     181         [ +  - ]:          96 :   m_subject->setText(f.subjectFilter);
     182         [ +  - ]:          96 :   m_from->setText(f.fromFilter);
     183         [ +  - ]:          96 :   m_to->setText(f.toFilter);
     184                 :             : 
     185         [ +  - ]:          96 :   m_unread->setCurrentIndex(triToIndex(f.unread));
     186         [ +  - ]:          96 :   m_flagged->setCurrentIndex(triToIndex(f.flagged));
     187         [ +  - ]:          96 :   m_answered->setCurrentIndex(triToIndex(f.answered));
     188         [ +  - ]:          96 :   m_attachment->setCurrentIndex(triToIndex(f.hasAttachment));
     189                 :             : 
     190         [ +  - ]:          96 :   m_folder->setCheckedFolders(f.folderPatterns);
     191   [ +  -  +  -  :          96 :   m_tags->setText(f.tags.join(", "));
                   +  - ]
     192                 :             : 
     193   [ +  -  +  +  :          96 :   if (f.dateFrom.isValid() || f.dateTo.isValid()) {
          +  -  -  +  +  
                      + ]
     194         [ +  - ]:           1 :     m_datePreset->setCurrentIndex(PresetCustom);
     195   [ +  -  +  -  :           1 :     m_dateFrom->setDate(f.dateFrom.isValid() ? f.dateFrom.date() : QDate::currentDate());
          +  -  -  -  +  
                      - ]
     196   [ +  -  +  -  :           1 :     m_dateTo->setDate(f.dateTo.isValid() ? f.dateTo.date() : QDate::currentDate());
          +  -  -  -  +  
                      - ]
     197         [ +  - ]:           1 :     m_dateFrom->setEnabled(true);
     198         [ +  - ]:           1 :     m_dateTo->setEnabled(true);
     199                 :             :   } else {
     200         [ +  - ]:          95 :     m_datePreset->setCurrentIndex(PresetAny);
     201         [ +  - ]:          95 :     m_dateFrom->setEnabled(false);
     202         [ +  - ]:          95 :     m_dateTo->setEnabled(false);
     203                 :             :   }
     204                 :          96 : }
     205                 :             : 
     206                 :         135 : MailCache::SearchFilter FilterPopoverWidget::filter() const {
     207                 :         135 :   MailCache::SearchFilter f;
     208         [ +  - ]:         135 :   f.subjectFilter = m_subject->text();
     209         [ +  - ]:         135 :   f.fromFilter = m_from->text();
     210         [ +  - ]:         135 :   f.toFilter = m_to->text();
     211                 :             : 
     212         [ +  - ]:         135 :   f.unread = indexToTri(m_unread->currentIndex());
     213         [ +  - ]:         135 :   f.flagged = indexToTri(m_flagged->currentIndex());
     214         [ +  - ]:         135 :   f.answered = indexToTri(m_answered->currentIndex());
     215         [ +  - ]:         135 :   f.hasAttachment = indexToTri(m_attachment->currentIndex());
     216                 :             : 
     217         [ +  - ]:         135 :   f.folderPatterns = m_folder->checkedFolders();
     218   [ +  -  +  - ]:         135 :   const QStringList rawTags = m_tags->text().split(QLatin1Char(','), Qt::SkipEmptyParts);
     219         [ +  + ]:         137 :   for (const QString &t : rawTags) {
     220         [ +  - ]:           2 :     const QString trimmed = t.trimmed();
     221         [ +  - ]:           2 :     if (!trimmed.isEmpty())
     222         [ +  - ]:           2 :       f.tags.append(trimmed);
     223                 :           2 :   }
     224                 :             : 
     225   [ +  -  +  + ]:         135 :   if (m_datePreset->currentIndex() == PresetCustom) {
     226   [ +  -  +  -  :           1 :     f.dateFrom = QDateTime(m_dateFrom->date(), QTime(0, 0, 0));
                   +  - ]
     227   [ +  -  +  -  :           1 :     f.dateTo = QDateTime(m_dateTo->date(), QTime(23, 59, 59));
                   +  - ]
     228   [ +  -  -  + ]:         134 :   } else if (m_datePreset->currentIndex() != PresetAny) {
     229         [ #  # ]:           0 :     QDate now = QDate::currentDate();
     230   [ #  #  #  # ]:           0 :     f.dateTo = QDateTime(now, QTime(23, 59, 59));
     231   [ #  #  #  #  :           0 :     switch (m_datePreset->currentIndex()) {
                #  #  # ]
     232   [ #  #  #  # ]:           0 :     case PresetToday: f.dateFrom = QDateTime(now, QTime(0, 0)); break;
     233   [ #  #  #  #  :           0 :     case PresetWeek: f.dateFrom = QDateTime(now.addDays(-7), QTime(0, 0)); break;
                   #  # ]
     234   [ #  #  #  #  :           0 :     case PresetMonth: f.dateFrom = QDateTime(now.addMonths(-1), QTime(0, 0)); break;
                   #  # ]
     235   [ #  #  #  #  :           0 :     case PresetYear: f.dateFrom = QDateTime(now.addYears(-1), QTime(0, 0)); break;
                   #  # ]
     236                 :             :     }
     237                 :             :   }
     238                 :             : 
     239                 :         135 :   return f;
     240                 :         135 : }
     241                 :             : 
     242                 :          36 : void FilterPopoverWidget::setKnownFolders(const QStringList &paths) {
     243                 :          36 :   m_folder->setFolders(paths);
     244                 :          36 : }
     245                 :             : 
     246                 :          36 : void FilterPopoverWidget::setKnownTags(const QStringList &tags) {
     247                 :          36 :   m_knownTags = tags;
     248                 :             :   // Could set up a QCompleter here if needed.
     249                 :          36 : }
        

Generated by: LCOV version 2.0-1