MailJD nbsp;·nbsp; Test Dashboard nbsp;·nbsp; Coverage
LCOV - code coverage report
Current view: top level - ui - FolderSubscriptionDialog.cpp (source / functions) Coverage Total Hit
Test: MailJD Coverage (Unit + E2E) Lines: 89.6 % 260 233
Test Date: 2026-06-21 21:10:19 Functions: 95.5 % 22 21
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 48.7 % 674 328

             Branch data     Line data    Source code
       1                 :             : #include "FolderSubscriptionDialog.h"
       2                 :             : 
       3                 :             : #include <QDialogButtonBox>
       4                 :             : #include <QDir>
       5                 :             : #include <QFile>
       6                 :             : #include <QHBoxLayout>
       7                 :             : #include <QJsonArray>
       8                 :             : #include <QJsonDocument>
       9                 :             : #include <QJsonObject>
      10                 :             : #include <QLabel>
      11                 :             : #include <QListWidget>
      12                 :             : #include <QLoggingCategory>
      13                 :             : #include <QPushButton>
      14                 :             : #include <QSaveFile>
      15                 :             : #include <QTreeWidget>
      16                 :             : #include <QVBoxLayout>
      17                 :             : #include <QEvent>
      18                 :             : 
      19   [ +  +  +  -  :          93 : Q_LOGGING_CATEGORY(lcSubscription, "mailjd.subscription")
             +  -  -  - ]
      20                 :             : 
      21                 :             : // ═══════════════════════════════════════════════════════
      22                 :             : // FolderSubscriptionDialog
      23                 :             : // ═══════════════════════════════════════════════════════
      24                 :             : 
      25                 :             : static const QString SUBSCRIPTIONS_FILENAME =
      26                 :             :     QStringLiteral("subscriptions.json");
      27                 :             : 
      28                 :             : namespace {
      29                 :             : 
      30                 :          39 : QJsonObject loadSubscriptionObject(const QString &filePath) {
      31         [ +  - ]:          39 :   QFile readFile(filePath);
      32   [ +  -  +  + ]:          39 :   if (!readFile.open(QIODevice::ReadOnly))
      33         [ +  - ]:          10 :     return {};
      34                 :             : 
      35   [ +  -  +  - ]:          29 :   auto doc = QJsonDocument::fromJson(readFile.readAll());
      36   [ +  -  +  -  :          29 :   return doc.isObject() ? doc.object() : QJsonObject{};
             +  -  -  - ]
      37                 :          39 : }
      38                 :             : 
      39                 :          39 : bool writeSubscriptionObject(const QString &filePath,
      40                 :             :                              const QJsonObject &object) {
      41         [ +  - ]:          39 :   QSaveFile file(filePath);
      42   [ +  -  -  + ]:          39 :   if (!file.open(QIODevice::WriteOnly)) {
      43   [ #  #  #  #  :           0 :     qCWarning(lcSubscription) << "Failed to write" << filePath
          #  #  #  #  #  
                      # ]
      44   [ #  #  #  # ]:           0 :                               << file.errorString();
      45                 :           0 :     return false;
      46                 :             :   }
      47                 :             : 
      48   [ +  -  +  - ]:          39 :   const QByteArray data = QJsonDocument(object).toJson(QJsonDocument::Indented);
      49   [ +  -  -  + ]:          39 :   if (file.write(data) != data.size()) {
      50   [ #  #  #  #  :           0 :     qCWarning(lcSubscription) << "Failed to write complete subscriptions file"
             #  #  #  # ]
      51   [ #  #  #  #  :           0 :                               << filePath << file.errorString();
                   #  # ]
      52         [ #  # ]:           0 :     file.cancelWriting();
      53                 :           0 :     return false;
      54                 :             :   }
      55                 :             : 
      56   [ +  -  -  + ]:          39 :   if (!file.commit()) {
      57   [ #  #  #  #  :           0 :     qCWarning(lcSubscription) << "Failed to commit subscriptions file"
             #  #  #  # ]
      58   [ #  #  #  #  :           0 :                               << filePath << file.errorString();
                   #  # ]
      59                 :           0 :     return false;
      60                 :             :   }
      61                 :             : 
      62                 :          39 :   return true;
      63                 :          39 : }
      64                 :             : 
      65                 :             : } // namespace
      66                 :             : 
      67                 :          13 : FolderSubscriptionDialog::FolderSubscriptionDialog(
      68                 :             :     const QStringList &allFolders, const QStringList &subscribedFolders,
      69                 :          13 :     const QStringList &hiddenFolders, QWidget *parent)
      70         [ +  - ]:          13 :     : QDialog(parent), m_hidden(hiddenFolders) {
      71   [ +  -  +  - ]:          13 :   setWindowTitle(tr("Folder Subscriptions"));
      72         [ +  - ]:          13 :   setMinimumSize(400, 500);
      73                 :             : 
      74   [ +  -  +  -  :          13 :   auto *layout = new QVBoxLayout(this);
             -  +  -  - ]
      75                 :             : 
      76                 :             :   auto *label = new QLabel(
      77                 :          26 :       QStringLiteral("Abonnierte Ordner werden regelmäßig auf neue "
      78                 :             :                      "Nachrichten geprüft:"),
      79   [ +  -  +  -  :          39 :       this);
             -  +  -  - ]
      80         [ +  - ]:          13 :   label->setWordWrap(true);
      81         [ +  - ]:          13 :   layout->addWidget(label);
      82                 :             : 
      83                 :             :   // T-068: Select All / Select None buttons
      84   [ +  -  +  -  :          13 :   auto *buttonRow = new QHBoxLayout();
             -  +  -  - ]
      85   [ +  -  +  -  :          26 :   auto *selectAll = new QPushButton(QStringLiteral("Alle auswählen"), this);
             -  +  -  - ]
      86   [ +  -  +  -  :          26 :   auto *selectNone = new QPushButton(QStringLiteral("Keine auswählen"), this);
             -  +  -  - ]
      87                 :          13 :   connect(selectAll, &QPushButton::clicked, this,
      88         [ +  - ]:          13 :           [this]() { setCheckAll(Qt::Checked); });
      89                 :          13 :   connect(selectNone, &QPushButton::clicked, this,
      90         [ +  - ]:          13 :           [this]() { setCheckAll(Qt::Unchecked); });
      91         [ +  - ]:          13 :   buttonRow->addWidget(selectAll);
      92         [ +  - ]:          13 :   buttonRow->addWidget(selectNone);
      93         [ +  - ]:          13 :   buttonRow->addStretch();
      94         [ +  - ]:          13 :   layout->addLayout(buttonRow);
      95                 :             : 
      96   [ +  -  +  -  :          13 :   m_tree = new QTreeWidget(this);
             -  +  -  - ]
      97         [ +  - ]:          13 :   m_tree->setHeaderHidden(true);
      98         [ +  - ]:          13 :   m_tree->setRootIsDecorated(true); // T-068
      99         [ +  - ]:          13 :   layout->addWidget(m_tree);
     100                 :             : 
     101         [ +  - ]:          13 :   buildTree(allFolders, subscribedFolders);
     102                 :             : 
     103                 :             :   // T-077: Hidden folders section
     104         [ +  + ]:          13 :   if (!hiddenFolders.isEmpty()) {
     105                 :             :     auto *hiddenLabel = new QLabel(
     106   [ +  -  +  -  :          10 :         QStringLiteral("Ausgeblendete Ordner:"), this);
             -  +  -  - ]
     107         [ +  - ]:           5 :     layout->addWidget(hiddenLabel);
     108                 :             : 
     109   [ +  -  +  -  :           5 :     m_hiddenList = new QListWidget(this);
             -  +  -  - ]
     110         [ +  - ]:           5 :     m_hiddenList->setMaximumHeight(120);
     111         [ +  + ]:          13 :     for (const auto &h : hiddenFolders) {
     112         [ +  - ]:           8 :       m_hiddenList->addItem(h);
     113                 :             :     }
     114         [ +  - ]:           5 :     layout->addWidget(m_hiddenList);
     115                 :             : 
     116                 :             :     auto *unhideBtn =
     117   [ +  -  +  -  :          10 :         new QPushButton(QStringLiteral("Einblenden"), this);
             -  +  -  - ]
     118         [ +  - ]:           5 :     connect(unhideBtn, &QPushButton::clicked, this, [this]() {
     119         [ +  - ]:           2 :       auto *item = m_hiddenList->currentItem();
     120         [ +  + ]:           2 :       if (!item)
     121                 :           1 :         return;
     122         [ +  - ]:           1 :       QString path = item->text();
     123         [ +  - ]:           1 :       m_hidden.removeAll(path);
     124   [ +  -  +  -  :           1 :       delete m_hiddenList->takeItem(m_hiddenList->row(item));
                   +  - ]
     125                 :           1 :     });
     126   [ +  -  +  -  :           5 :     auto *unhideRow = new QHBoxLayout();
             -  +  -  - ]
     127         [ +  - ]:           5 :     unhideRow->addWidget(unhideBtn);
     128         [ +  - ]:           5 :     unhideRow->addStretch();
     129         [ +  - ]:           5 :     layout->addLayout(unhideRow);
     130                 :             :   }
     131                 :             : 
     132                 :             :   auto *buttons =
     133                 :             :       new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
     134   [ +  -  +  -  :          13 :                            this);
             -  +  -  - ]
     135         [ +  - ]:          13 :   connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
     136         [ +  - ]:          13 :   connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
     137         [ +  - ]:          13 :   layout->addWidget(buttons);
     138                 :          13 : }
     139                 :             : 
     140                 :          13 : void FolderSubscriptionDialog::buildTree(const QStringList &allFolders,
     141                 :             :                                          const QStringList &subscribed) {
     142         [ +  - ]:          13 :   QSet<QString> subscribedSet(subscribed.begin(), subscribed.end());
     143                 :             : 
     144                 :             :   // T-068: Build hierarchical tree.
     145                 :             :   // We need to detect the delimiter for each folder path.
     146                 :             :   // Common delimiters: "." and "/"
     147                 :          13 :   QMap<QString, QTreeWidgetItem *> pathToItem;
     148                 :             : 
     149                 :             :   // Sort by path length so parents are created before children
     150                 :          13 :   QStringList sorted = allFolders;
     151   [ +  -  +  -  :          13 :   std::sort(sorted.begin(), sorted.end(),
                   +  - ]
     152                 :         115 :             [](const QString &a, const QString &b) {
     153                 :         115 :               return a.length() < b.length();
     154                 :             :             });
     155                 :             : 
     156   [ +  -  +  -  :          71 :   for (const auto &folder : sorted) {
                   +  + ]
     157                 :             :     // Detect delimiter (prefer / over .)
     158                 :          58 :     QChar delim = '.';
     159   [ +  -  +  + ]:          58 :     if (folder.contains('/')) {
     160                 :           5 :       delim = '/';
     161                 :             :     }
     162                 :             : 
     163                 :             :     // Extract display name (last segment)
     164         [ +  - ]:          58 :     QString displayName = folder.section(delim, -1);
     165         [ -  + ]:          58 :     if (displayName.isEmpty())
     166                 :           0 :       displayName = folder;
     167                 :             : 
     168                 :             :     // Find or create parent
     169         [ +  - ]:          58 :     QString parentPath = folder.section(delim, 0, -2);
     170                 :          58 :     QTreeWidgetItem *parentItem = nullptr;
     171   [ +  +  +  -  :          58 :     if (!parentPath.isEmpty() && parentPath != folder) {
                   +  + ]
     172         [ +  - ]:          12 :       auto it = pathToItem.find(parentPath);
     173   [ +  -  +  + ]:          12 :       if (it != pathToItem.end()) {
     174                 :          11 :         parentItem = it.value();
     175                 :             :       } else {
     176                 :             :         // Create intermediate parent
     177         [ +  - ]:           1 :         QString parentName = parentPath.section(delim, -1);
     178         [ -  + ]:           1 :         if (parentName.isEmpty())
     179                 :           0 :           parentName = parentPath;
     180   [ +  -  +  -  :           1 :         auto *intermediate = new QTreeWidgetItem(m_tree);
             -  +  -  - ]
     181         [ +  - ]:           1 :         intermediate->setText(0, parentName);
     182         [ +  - ]:           1 :         intermediate->setData(0, Qt::UserRole, parentPath);
     183   [ +  -  +  - ]:           1 :         intermediate->setFlags(intermediate->flags() | Qt::ItemIsUserCheckable);
     184         [ +  - ]:           1 :         intermediate->setCheckState(0, Qt::Unchecked);
     185         [ +  - ]:           1 :         pathToItem[parentPath] = intermediate;
     186                 :           1 :         parentItem = intermediate;
     187                 :           1 :       }
     188                 :             :     }
     189                 :             : 
     190   [ +  +  +  -  :         174 :     auto *item = parentItem ? new QTreeWidgetItem(parentItem)
          +  -  -  +  +  
          +  +  -  -  -  
          -  -  -  -  -  
                      - ]
     191   [ +  -  +  -  :         104 :                             : new QTreeWidgetItem(m_tree);
                   +  + ]
     192         [ +  - ]:          58 :     item->setText(0, displayName);
     193         [ +  - ]:          58 :     item->setData(0, Qt::UserRole, folder); // store full path
     194   [ +  -  +  - ]:          58 :     item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
     195   [ +  +  +  - ]:          58 :     item->setCheckState(0, subscribedSet.contains(folder) ? Qt::Checked
     196                 :             :                                                           : Qt::Unchecked);
     197         [ +  - ]:          58 :     pathToItem[folder] = item;
     198                 :          58 :   }
     199                 :             : 
     200         [ +  - ]:          13 :   m_tree->expandAll();
     201                 :             : 
     202                 :             :   // T-204: Propagate check state between parent and child items
     203                 :          13 :   connect(m_tree, &QTreeWidget::itemChanged, this,
     204         [ +  - ]:          13 :           [this](QTreeWidgetItem *item, int column) {
     205         [ -  + ]:          15 :             if (column != 0)
     206                 :           0 :               return;
     207                 :             : 
     208                 :             :             // Block signals to avoid recursive triggers
     209                 :          15 :             m_tree->blockSignals(true);
     210                 :             : 
     211         [ +  - ]:          15 :             auto state = item->checkState(0);
     212                 :             : 
     213                 :             :             // Propagate to children: set all children to same state
     214                 :          15 :             std::function<void(QTreeWidgetItem *, Qt::CheckState)> setChildren;
     215                 :          45 :             setChildren = [&setChildren](QTreeWidgetItem *parent,
     216                 :             :                                          Qt::CheckState cs) {
     217         [ -  + ]:          15 :               for (int i = 0; i < parent->childCount(); ++i) {
     218                 :           0 :                 auto *child = parent->child(i);
     219                 :           0 :                 child->setCheckState(0, cs);
     220                 :           0 :                 setChildren(child, cs);
     221                 :             :               }
     222                 :          15 :             };
     223                 :             : 
     224         [ +  - ]:          15 :             if (state != Qt::PartiallyChecked) {
     225         [ +  - ]:          15 :               setChildren(item, state);
     226                 :             :             }
     227                 :             : 
     228                 :             :             // Update parent state based on children
     229                 :          15 :             std::function<void(QTreeWidgetItem *)> updateParent;
     230                 :          49 :             updateParent = [&updateParent](QTreeWidgetItem *child) {
     231                 :          19 :               auto *parent = child->parent();
     232         [ +  + ]:          19 :               if (!parent)
     233                 :          15 :                 return;
     234                 :           4 :               int checked = 0, unchecked = 0;
     235         [ +  + ]:          12 :               for (int i = 0; i < parent->childCount(); ++i) {
     236                 :           8 :                 auto cs = parent->child(i)->checkState(0);
     237         [ +  + ]:           8 :                 if (cs == Qt::Checked)
     238                 :           4 :                   ++checked;
     239         [ +  - ]:           4 :                 else if (cs == Qt::Unchecked)
     240                 :           4 :                   ++unchecked;
     241                 :             :               }
     242                 :           4 :               int total = parent->childCount();
     243         [ +  + ]:           4 :               if (checked == total)
     244                 :           1 :                 parent->setCheckState(0, Qt::Checked);
     245         [ +  + ]:           3 :               else if (unchecked == total)
     246                 :           1 :                 parent->setCheckState(0, Qt::Unchecked);
     247                 :             :               else
     248                 :           2 :                 parent->setCheckState(0, Qt::PartiallyChecked);
     249                 :           4 :               updateParent(parent);
     250                 :          15 :             };
     251         [ +  - ]:          15 :             updateParent(item);
     252                 :             : 
     253                 :          15 :             m_tree->blockSignals(false);
     254                 :          15 :           });
     255                 :          13 : }
     256                 :             : 
     257                 :          12 : QStringList FolderSubscriptionDialog::subscribedFolders() const {
     258                 :          12 :   QStringList result;
     259   [ +  -  +  + ]:          71 :   for (int i = 0; i < m_tree->topLevelItemCount(); ++i) {
     260   [ +  -  +  - ]:          59 :     collectChecked(m_tree->topLevelItem(i), result);
     261                 :             :   }
     262                 :          12 :   return result;
     263                 :           0 : }
     264                 :             : 
     265                 :          72 : void FolderSubscriptionDialog::collectChecked(QTreeWidgetItem *item,
     266                 :             :                                               QStringList &result) const {
     267         [ +  + ]:          72 :   if (item->checkState(0) == Qt::Checked) {
     268   [ +  -  +  - ]:          33 :     QString path = item->data(0, Qt::UserRole).toString();
     269         [ +  - ]:          33 :     if (!path.isEmpty()) {
     270         [ +  - ]:          33 :       result.append(path);
     271                 :             :     }
     272                 :          33 :   }
     273         [ +  + ]:          85 :   for (int i = 0; i < item->childCount(); ++i) {
     274                 :          13 :     collectChecked(item->child(i), result);
     275                 :             :   }
     276                 :          72 : }
     277                 :             : 
     278                 :           2 : void FolderSubscriptionDialog::setCheckAll(Qt::CheckState state) {
     279                 :           2 :   std::function<void(QTreeWidgetItem *)> setAll;
     280                 :          14 :   setAll = [&](QTreeWidgetItem *item) {
     281                 :          10 :     item->setCheckState(0, state);
     282         [ -  + ]:          10 :     for (int i = 0; i < item->childCount(); ++i) {
     283                 :           0 :       setAll(item->child(i));
     284                 :             :     }
     285                 :           2 :   };
     286   [ +  -  +  + ]:          12 :   for (int i = 0; i < m_tree->topLevelItemCount(); ++i) {
     287   [ +  -  +  - ]:          10 :     setAll(m_tree->topLevelItem(i));
     288                 :             :   }
     289                 :           2 : }
     290                 :             : 
     291                 :             : // ── Persistence ──
     292                 :             : 
     293                 :             : QStringList
     294                 :          30 : FolderSubscriptionDialog::loadSubscriptions(const QString &configDir) {
     295                 :             :   QString filePath =
     296   [ +  -  +  - ]:          30 :       QDir(configDir).filePath(SUBSCRIPTIONS_FILENAME);
     297         [ +  - ]:          30 :   QFile file(filePath);
     298   [ +  -  +  + ]:          30 :   if (!file.exists()) {
     299   [ +  -  +  -  :          16 :     qCInfo(lcSubscription) << "No subscriptions file found at" << filePath;
          +  -  +  -  +  
                      + ]
     300                 :           8 :     return {};
     301                 :             :   }
     302                 :             : 
     303   [ +  -  -  + ]:          22 :   if (!file.open(QIODevice::ReadOnly)) {
     304   [ #  #  #  #  :           0 :     qCWarning(lcSubscription) << "Failed to open" << filePath;
          #  #  #  #  #  
                      # ]
     305                 :           0 :     return {};
     306                 :             :   }
     307                 :             : 
     308   [ +  -  +  - ]:          22 :   auto doc = QJsonDocument::fromJson(file.readAll());
     309   [ +  -  -  + ]:          22 :   if (!doc.isObject()) {
     310   [ #  #  #  #  :           0 :     qCWarning(lcSubscription) << "Invalid subscriptions JSON";
             #  #  #  # ]
     311                 :           0 :     return {};
     312                 :             :   }
     313                 :             : 
     314                 :          22 :   QStringList result;
     315   [ +  -  +  -  :          22 :   auto arr = doc.object().value("subscribed").toArray();
             +  -  +  - ]
     316   [ +  -  +  -  :          85 :   for (const auto &val : arr) {
                   +  + ]
     317   [ +  -  +  - ]:          63 :     result.append(val.toString());
     318                 :             :   }
     319                 :             : 
     320   [ +  -  +  -  :          44 :   qCInfo(lcSubscription) << "Loaded" << result.size() << "subscriptions from"
          +  -  +  -  +  
                -  +  + ]
     321         [ +  - ]:          22 :                          << filePath;
     322                 :          22 :   return result;
     323                 :          30 : }
     324                 :             : 
     325                 :          21 : void FolderSubscriptionDialog::saveSubscriptions(const QString &configDir,
     326                 :             :                                                  const QStringList &folders) {
     327         [ +  - ]:          21 :   QDir dir(configDir);
     328   [ +  -  +  + ]:          21 :   if (!dir.exists()) {
     329   [ +  -  +  -  :           2 :     if (!dir.mkpath(".")) {
                   +  + ]
     330   [ +  -  +  -  :           2 :       qCWarning(lcSubscription) << "Failed to create config directory"
             +  -  +  + ]
     331         [ +  - ]:           1 :                                 << configDir;
     332                 :           1 :       return;
     333                 :             :     }
     334                 :             :   }
     335                 :             : 
     336         [ +  - ]:          20 :   QString filePath = dir.filePath(SUBSCRIPTIONS_FILENAME);
     337                 :             : 
     338                 :             :   // Load existing JSON to preserve "hidden" key
     339         [ +  - ]:          20 :   QJsonObject obj = loadSubscriptionObject(filePath);
     340                 :             : 
     341         [ +  - ]:          20 :   QJsonArray arr;
     342         [ +  + ]:          67 :   for (const auto &f : folders) {
     343   [ +  -  +  - ]:          47 :     arr.append(f);
     344                 :             :   }
     345   [ +  -  +  -  :          20 :   obj["subscribed"] = arr;
             +  -  +  - ]
     346                 :             : 
     347   [ +  -  +  - ]:          20 :   if (writeSubscriptionObject(filePath, obj))
     348   [ +  -  +  -  :          40 :     qCInfo(lcSubscription) << "Saved" << folders.size() << "subscriptions to"
          +  -  +  -  +  
                -  +  + ]
     349         [ +  - ]:          20 :                            << filePath;
     350         [ +  + ]:          21 : }
     351                 :             : 
     352                 :             : // T-069: Hidden folder persistence
     353                 :             : 
     354                 :             : QStringList
     355                 :          38 : FolderSubscriptionDialog::loadHidden(const QString &configDir) {
     356   [ +  -  +  - ]:          38 :   QString filePath = QDir(configDir).filePath(SUBSCRIPTIONS_FILENAME);
     357         [ +  - ]:          38 :   QFile file(filePath);
     358   [ +  -  +  +  :          38 :   if (!file.exists() || !file.open(QIODevice::ReadOnly))
          +  -  -  +  +  
                      + ]
     359                 :          16 :     return {};
     360                 :             : 
     361   [ +  -  +  - ]:          22 :   auto doc = QJsonDocument::fromJson(file.readAll());
     362   [ +  -  -  + ]:          22 :   if (!doc.isObject())
     363                 :           0 :     return {};
     364                 :             : 
     365                 :          22 :   QStringList result;
     366   [ +  -  +  -  :          22 :   auto arr = doc.object().value("hidden").toArray();
             +  -  +  - ]
     367   [ +  -  +  -  :          39 :   for (const auto &val : arr) {
                   +  + ]
     368   [ +  -  +  - ]:          17 :     result.append(val.toString());
     369                 :             :   }
     370                 :             : 
     371   [ +  -  +  -  :          44 :   qCInfo(lcSubscription) << "Loaded" << result.size() << "hidden folders";
          +  -  +  -  +  
                -  +  + ]
     372                 :          22 :   return result;
     373                 :          38 : }
     374                 :             : 
     375                 :          20 : void FolderSubscriptionDialog::saveHidden(const QString &configDir,
     376                 :             :                                           const QStringList &folders) {
     377         [ +  - ]:          20 :   QDir dir(configDir);
     378   [ +  -  +  + ]:          20 :   if (!dir.exists()) {
     379   [ +  -  +  -  :           1 :     if (!dir.mkpath(".")) {
                   +  - ]
     380   [ +  -  +  -  :           2 :       qCWarning(lcSubscription) << "Failed to create config directory"
             +  -  +  + ]
     381         [ +  - ]:           1 :                                 << configDir;
     382                 :           1 :       return;
     383                 :             :     }
     384                 :             :   }
     385                 :             : 
     386         [ +  - ]:          19 :   QString filePath = dir.filePath(SUBSCRIPTIONS_FILENAME);
     387                 :             : 
     388                 :             :   // Load existing JSON to preserve "subscribed" key
     389         [ +  - ]:          19 :   QJsonObject obj = loadSubscriptionObject(filePath);
     390                 :             : 
     391         [ +  - ]:          19 :   QJsonArray arr;
     392         [ +  + ]:          34 :   for (const auto &f : folders) {
     393   [ +  -  +  - ]:          15 :     arr.append(f);
     394                 :             :   }
     395   [ +  -  +  -  :          19 :   obj["hidden"] = arr;
             +  -  +  - ]
     396                 :             : 
     397   [ +  -  +  - ]:          19 :   if (writeSubscriptionObject(filePath, obj))
     398   [ +  -  +  -  :          38 :     qCInfo(lcSubscription) << "Saved" << folders.size() << "hidden folders to"
          +  -  +  -  +  
                -  +  + ]
     399         [ +  - ]:          19 :                            << filePath;
     400         [ +  + ]:          20 : }
     401                 :             : 
     402                 :             : // T-304: Runtime language switching
     403                 :          26 : void FolderSubscriptionDialog::changeEvent(QEvent *event) {
     404         [ -  + ]:          26 :   if (event->type() == QEvent::LanguageChange)
     405                 :           0 :     retranslateUi();
     406                 :          26 :   QDialog::changeEvent(event);
     407                 :          26 : }
     408                 :             : 
     409                 :           0 : void FolderSubscriptionDialog::retranslateUi() {
     410   [ #  #  #  # ]:           0 :   setWindowTitle(tr("Folder Subscriptions"));
     411                 :           0 : }
        

Generated by: LCOV version 2.0-1