MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - ui - AttachmentBar.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 96.5 % 86 83
Test Date: 2026-06-21 21:10:19 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 52.3 % 172 90

             Branch data     Line data    Source code
       1                 :             : #include "AttachmentBar.h"
       2                 :             : 
       3                 :             : #include <QEvent>
       4                 :             : #include <QHBoxLayout>
       5                 :             : #include <QIcon>
       6                 :             : #include <QToolButton>
       7                 :             : 
       8         [ +  - ]:          94 : AttachmentBar::AttachmentBar(QWidget *parent) : QFrame(parent) {
       9         [ +  - ]:         188 :   setObjectName(QStringLiteral("attachmentBar"));
      10   [ +  -  +  -  :          94 :   m_layout = new QHBoxLayout(this);
             -  +  -  - ]
      11         [ +  - ]:          94 :   m_layout->setContentsMargins(4, 2, 4, 2);
      12         [ +  - ]:          94 :   m_layout->setSpacing(4);
      13         [ +  - ]:          94 :   setFrameShape(QFrame::NoFrame);
      14         [ +  - ]:          94 :   setVisible(false);
      15                 :          94 : }
      16                 :             : 
      17                 :          94 : void AttachmentBar::setAttachments(const QList<Attachment> &attachments) {
      18                 :          94 :   clear();
      19                 :          94 :   m_attachments = attachments;
      20                 :             : 
      21         [ +  + ]:          94 :   if (attachments.isEmpty()) {
      22                 :          78 :     return;
      23                 :             :   }
      24                 :             : 
      25         [ +  + ]:          37 :   for (const auto &att : attachments) {
      26   [ +  -  +  -  :          21 :     auto *btn = new QToolButton(this);
             -  +  -  - ]
      27                 :             :     QString label =
      28   [ -  +  -  - ]:          21 :         att.filename.isEmpty() ? tr("(unnamed)") : att.filename;
      29   [ +  -  +  -  :          42 :     label += QStringLiteral(" (%1)").arg(formatSize(att.size));
                   +  - ]
      30         [ +  - ]:          21 :     btn->setText(label);
      31         [ +  - ]:          21 :     btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
      32   [ +  -  +  - ]:          42 :     btn->setIcon(QIcon::fromTheme(QStringLiteral("mail-attachment")));
      33         [ +  - ]:          21 :     btn->setAutoRaise(true);
      34                 :             : 
      35                 :          21 :     const qint64 id = att.id;
      36                 :          21 :     const QString filename = att.filename;
      37                 :             :     // T-71.6: PDFs open inline on click; everything else downloads.
      38                 :             :     // Detection covers both a proper MIME type and a .pdf extension so
      39                 :             :     // mis-typed attachments still work.
      40                 :             :     const bool isPdf =
      41   [ +  -  -  -  :          42 :         att.contentType.startsWith(QStringLiteral("application/pdf"),
                   -  - ]
      42   [ +  -  +  + ]:          57 :                                    Qt::CaseInsensitive) ||
      43   [ +  -  +  +  :          51 :         att.filename.endsWith(QStringLiteral(".pdf"), Qt::CaseInsensitive);
          +  +  +  +  +  
             -  -  -  -  
                      - ]
      44                 :             : 
      45         [ +  + ]:          21 :     if (isPdf) {
      46                 :           8 :       connect(btn, &QToolButton::clicked, this,
      47         [ +  - ]:          19 :               [this, id]() { emit viewInlineRequested(id); });
      48                 :             :     } else {
      49         [ +  - ]:          13 :       connect(btn, &QToolButton::clicked, this,
      50                 :          26 :               [this, id, filename]() {
      51                 :           1 :                 emit downloadRequested(id, filename);
      52                 :           1 :               });
      53                 :             :     }
      54                 :             : 
      55         [ +  - ]:          21 :     m_layout->addWidget(btn);
      56                 :             : 
      57                 :             :     // T-71.6: PDF chips get a small dedicated download button so users can
      58                 :             :     // still save the file (the main chip now opens the inline viewer).
      59         [ +  + ]:          21 :     if (isPdf) {
      60   [ +  -  +  -  :           8 :       auto *dlBtn = new QToolButton(this);
             -  +  -  - ]
      61   [ +  -  +  - ]:           8 :       dlBtn->setText(tr("Save"));
      62   [ +  -  +  -  :          16 :       dlBtn->setToolTip(tr("Save %1 to disk").arg(
                   +  - ]
      63   [ -  +  -  - ]:          16 :           att.filename.isEmpty() ? tr("attachment") : att.filename));
      64         [ +  - ]:           8 :       dlBtn->setAutoRaise(true);
      65         [ +  - ]:           8 :       connect(dlBtn, &QToolButton::clicked, this,
      66                 :          16 :               [this, id, filename]() {
      67                 :           0 :                 emit downloadRequested(id, filename);
      68                 :           0 :               });
      69         [ +  - ]:           8 :       m_layout->addWidget(dlBtn);
      70                 :             :     }
      71                 :          21 :   }
      72                 :             : 
      73                 :             :   // "Save All" button if multiple attachments
      74         [ +  + ]:          16 :   if (attachments.size() > 1) {
      75                 :           3 :     m_layout->addStretch();
      76   [ +  -  -  +  :           3 :     auto *allBtn = new QToolButton(this);
                   -  - ]
      77   [ +  -  +  - ]:           3 :     allBtn->setText(tr("Save All"));
      78   [ +  -  +  - ]:           6 :     allBtn->setIcon(QIcon::fromTheme(QStringLiteral("document-save-all")));
      79                 :           3 :     allBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
      80                 :           3 :     allBtn->setAutoRaise(true);
      81                 :           3 :     connect(allBtn, &QToolButton::clicked, this,
      82         [ +  - ]:           3 :             &AttachmentBar::downloadAllRequested);
      83         [ +  - ]:           3 :     m_layout->addWidget(allBtn);
      84                 :             :   } else {
      85                 :          13 :     m_layout->addStretch();
      86                 :             :   }
      87                 :             : 
      88                 :          16 :   setVisible(true);
      89                 :             : }
      90                 :             : 
      91                 :         129 : void AttachmentBar::clear() {
      92                 :         129 :   m_attachments.clear();
      93                 :             : 
      94                 :             :   // Remove all widgets from layout
      95                 :             :   QLayoutItem *item;
      96         [ +  + ]:         161 :   while ((item = m_layout->takeAt(0)) != nullptr) {
      97         [ +  + ]:          32 :     delete item->widget();
      98         [ +  - ]:          32 :     delete item;
      99                 :             :   }
     100                 :         129 :   setVisible(false);
     101                 :         129 : }
     102                 :             : 
     103                 :          21 : QString AttachmentBar::formatSize(qint64 bytes) {
     104         [ +  + ]:          21 :   if (bytes < 1024) {
     105   [ +  -  +  - ]:          26 :     return QString("%1 B").arg(bytes);
     106                 :             :   }
     107         [ +  + ]:           8 :   if (bytes < 1024 * 1024) {
     108   [ +  -  +  - ]:          12 :     return QString("%1 KB").arg(bytes / 1024.0, 0, 'f', 1);
     109                 :             :   }
     110         [ +  - ]:           2 :   if (bytes < 1024LL * 1024 * 1024) {
     111   [ +  -  +  - ]:           4 :     return QString("%1 MB").arg(bytes / (1024.0 * 1024.0), 0, 'f', 1);
     112                 :             :   }
     113   [ #  #  #  # ]:           0 :   return QString("%1 GB").arg(bytes / (1024.0 * 1024.0 * 1024.0), 0, 'f', 1);
     114                 :             : }
     115                 :             : 
     116                 :             : // T-76.B3: Runtime language switching.
     117                 :             : // The chips are built dynamically in setAttachments() (one Save button per
     118                 :             : // PDF, a Save All button, "(unnamed)" fallback), so re-applying tr() means
     119                 :             : // rebuilding. A local copy is made first — setAttachments() takes a const
     120                 :             : // ref and clear() empties m_attachments, which would otherwise dangle it.
     121                 :        1660 : void AttachmentBar::changeEvent(QEvent *event) {
     122         [ +  + ]:        1660 :   if (event->type() == QEvent::LanguageChange)
     123                 :          90 :     retranslateUi();
     124                 :        1660 :   QFrame::changeEvent(event);
     125                 :        1660 : }
     126                 :             : 
     127                 :          90 : void AttachmentBar::retranslateUi() {
     128                 :             :   // T-76.B3: setAttachments() rebuilds the widget tree (deletes/creates child
     129                 :             :   // widgets). Calling it from within changeEvent can re-enter if a child
     130                 :             :   // removal posts further events; guard against that.
     131   [ +  -  +  +  :          90 :   if (m_retranslating || m_attachments.isEmpty())
                   +  + ]
     132                 :          88 :     return;
     133                 :           2 :   m_retranslating = true;
     134                 :           2 :   QList<Attachment> copy = m_attachments;
     135         [ +  - ]:           2 :   setAttachments(copy);
     136                 :           2 :   m_retranslating = false;
     137                 :           2 : }
        

Generated by: LCOV version 2.0-1