LCOV - code coverage report
Current view: top level - src/models - NewsList.h (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 100.0 % 10 10
Test Date: 2026-03-23 10:19:47 Functions: 100.0 % 10 10

            Line data    Source code
       1              : #ifndef NEWSLIST_H
       2              : #define NEWSLIST_H
       3              : 
       4              : #include <QList>
       5              : #include <functional>
       6              : 
       7              : #include "../FangObject.h"
       8              : #include "NewsPosition.h"
       9              : 
      10              : class NewsItem;
      11              : 
      12              : /*!
      13              :     \brief Represents a slot in the news list that may or may not have a loaded NewsItem.
      14              : 
      15              :     The position (timestamp + ID) is always present for ordering purposes.
      16              :     The item pointer may be nullptr if the item has been unloaded to save memory.
      17              :  */
      18              : struct NewsSlot {
      19              :     NewsPosition position;  // Always present (timestamp + ID)
      20              :     NewsItem* item;         // nullptr when unloaded
      21              : 
      22              :     NewsSlot(NewsItem* newsItem);
      23              : 
      24          647 :     inline bool isLoaded() const { return item != nullptr; }
      25        89057 :     inline qint64 id() const { return position.id(); }
      26              : };
      27              : 
      28              : /*!
      29              :     \brief Callback type for reloading unloaded news items from the database.
      30              :     Takes a list of news IDs and returns the corresponding NewsItem objects.
      31              :  */
      32              : using ReloadCallback = std::function<QList<NewsItem*>(const QList<qint64>&)>;
      33              : 
      34              : /*!
      35              :     \brief The NewsList class contains a custom list type that internally is also stored as a set.
      36              : 
      37              :     The list maintains ALL loaded items in memory, with a "display window" that tracks
      38              :     which items are currently visible to the UI. This allows paging through previously
      39              :     loaded items without re-fetching from the database.
      40              : 
      41              :     Public methods (size, first, last, at, indexOf) operate on the display window.
      42              :     Internal methods (fullSize, fullAt, etc.) operate on the complete list.
      43              :  */
      44              : class NewsList : public FangObject
      45              : {
      46              : public:
      47              :     explicit NewsList(FangObject *parent = nullptr);
      48              :     virtual ~NewsList();
      49              : 
      50              :     /*!
      51              :         \brief Delete everything in the list and clear it.
      52              :      */
      53              :     void clear();
      54              : 
      55              :     //////////////////////////////////////////
      56              :     // Display Window (reflects the web views)
      57              :     //////////////////////////////////////////
      58              : 
      59              :     qsizetype size() const;
      60              :     bool isEmpty() const;
      61              :     NewsItem* first() const;
      62              :     NewsItem* last() const;
      63              :     NewsItem* at(qsizetype i) const;
      64              :     bool contains(NewsItem* value) const;
      65              :     qsizetype indexOf(const NewsItem* value, qsizetype from = 0) const;
      66              : 
      67              :     ///////////////////////////////////////
      68              :     // Full List Methods (all loaded items)
      69              :     ///////////////////////////////////////
      70              : 
      71              :     /*!
      72              :         \brief Returns the total number of items loaded (including those outside display window).
      73              :      */
      74              :     qsizetype fullSize() const;
      75              : 
      76              :     /*!
      77              :         \brief Returns item at index in the full list.
      78              :      */
      79              :     NewsItem* fullAt(qsizetype i) const;
      80              : 
      81              :     /*!
      82              :         \brief Checks if an item with the given ID exists anywhere in the full list.
      83              :      */
      84              :     bool containsID(qint64 id) const;
      85              : 
      86              :     /*!
      87              :         \brief Returns the index in the full list for a given ID, or -1 if not found.
      88              :      */
      89              :     qsizetype fullIndexForItemID(qint64 id) const;
      90              : 
      91              :     ////////////////////////////
      92              :     // Display Window Navigation
      93              :     ////////////////////////////
      94              : 
      95              :     /*!
      96              :         \brief Returns true if there are loaded items before the display window.
      97              :      */
      98              :     bool canPageUp() const;
      99              : 
     100              :     /*!
     101              :         \brief Returns true if there are loaded items after the display window.
     102              :      */
     103              :     bool canPageDown() const;
     104              : 
     105              :     /*!
     106              :         \brief Expands the display window to include N more items at the start.
     107              :         \return The actual number of items added to the window.
     108              :      */
     109              :     qsizetype pageUp(qsizetype count);
     110              : 
     111              :     /*!
     112              :         \brief Expands the display window to include N more items at the end.
     113              :         \return The actual number of items added to the window.
     114              :      */
     115              :     qsizetype pageDown(qsizetype count);
     116              : 
     117              :     /*!
     118              :         \brief Gets the display window start index in the full list.
     119              :      */
     120           12 :     inline qsizetype getDisplayStart() const { return displayStart; }
     121              : 
     122              :     /*!
     123              :         \brief Gets the display window end index (exclusive) in the full list.
     124              :      */
     125           23 :     inline qsizetype getDisplayEnd() const { return displayEnd; }
     126              : 
     127              :     ///////////////////////
     128              :     // Modification Methods
     129              :     ///////////////////////
     130              : 
     131              :     void append(NewsItem* value);
     132              :     void prepend(NewsItem* value);
     133              : 
     134              :     /*!
     135              :         \brief Shrinks the display window from the start or end.
     136              : 
     137              :         Items are NOT deleted, they remain in the full list for potential paging.
     138              :         \param fromStart True to shrink from start, false to shrink from end.
     139              :         \param numberToRemove Number of items to remove from the display window.
     140              :      */
     141              :     void shrinkDisplayWindow(bool fromStart, qsizetype numberToRemove);
     142              : 
     143              :     /*!
     144              :         \brief Shrinks the display window. Items remain in memory for paging.
     145              :      */
     146              :     void removeAndDelete(bool fromStart, qsizetype numberToRemove);
     147              : 
     148              :     /*!
     149              :         \brief If loaded, returns the news item for a given ID (searches display window only).
     150              :         Linear time complexity.
     151              :      */
     152              :     NewsItem* newsItemForID(const qint64 id) const;
     153              : 
     154              :     /*!
     155              :         \brief Returns the news item for a given ID (searches FULL list, not just display window).
     156              :         Linear time complexity.
     157              :      */
     158              :     NewsItem* fullNewsItemForID(const qint64 id) const;
     159              : 
     160              :     /*!
     161              :         \brief Returns the index within the display window for a given ID, or -1 if not found.
     162              :         Linear time complexity.
     163              :      */
     164              :     qsizetype indexForItemID(const qint64 id) const;
     165              : 
     166              :     ////////////////////////////
     167              :     // Reload Callback
     168              :     ////////////////////////////
     169              : 
     170              :     /*!
     171              :         \brief Sets the callback function for reloading unloaded items from the database.
     172              :         \param callback Function that takes a list of IDs and returns NewsItem objects.
     173              :      */
     174              :     void setReloadCallback(ReloadCallback callback);
     175              : 
     176              :     ////////////////////////////
     177              :     // Slot Access
     178              :     ////////////////////////////
     179              : 
     180              :     /*!
     181              :         \brief Returns the NewsPosition for an item at the given full index.
     182              :         Works for both loaded and unloaded items since positions are always present.
     183              :         \param i Index in the full list (not display window)
     184              :         \return The position, or invalid NewsPosition if index is out of bounds
     185              :      */
     186              :     NewsPosition positionAt(qsizetype i) const;
     187              : 
     188              :     /*!
     189              :         \brief Returns the count of currently loaded items.
     190              :      */
     191              :     qsizetype loadedCount() const;
     192              : 
     193              :     // Iterator class for iterating over NewsItem* in the display window
     194              :     class Iterator {
     195              :     public:
     196              :         using iterator_category = std::forward_iterator_tag;
     197              :         using value_type = NewsItem*;
     198              :         using difference_type = qsizetype;
     199              :         using pointer = NewsItem**;
     200              :         using reference = NewsItem*&;
     201              : 
     202            8 :         Iterator(QList<NewsSlot>* slotList, qsizetype index) : slotList(slotList), index(index) {}
     203              : 
     204           12 :         NewsItem* operator*() const { return slotList->at(index).item; }
     205           12 :         Iterator& operator++() { ++index; return *this; }
     206              :         Iterator operator++(int) { Iterator tmp = *this; ++index; return tmp; }
     207              :         bool operator==(const Iterator& other) const { return index == other.index; }
     208           16 :         bool operator!=(const Iterator& other) const { return index != other.index; }
     209              : 
     210              :     private:
     211              :         QList<NewsSlot>* slotList;
     212              :         qsizetype index;
     213              :     };
     214              : 
     215              :     // For list iteration (iterates display window only).
     216            4 :     inline Iterator begin() { return Iterator(&slotList, displayStart); }
     217            4 :     inline Iterator end() { return Iterator(&slotList, displayEnd); }
     218              : 
     219              : private:
     220              :     // Ordered list of ALL news slots (items may be loaded or unloaded).
     221              :     QList<NewsSlot> slotList;
     222              : 
     223              :     // Set of LOADED news items for fast "contains" check.
     224              :     QSet<NewsItem*> loadedSet;
     225              : 
     226              :     // Callback for reloading items from database.
     227              :     ReloadCallback reloadCallback;
     228              : 
     229              :     // Display window: [displayStart, displayEnd) - indices into `slotList`
     230              :     qsizetype displayStart = 0;
     231              :     qsizetype displayEnd = 0;
     232              : 
     233              :     ////////////////////////////
     234              :     // Memory Management
     235              :     ////////////////////////////
     236              : 
     237              :     /*!
     238              :         \brief Checks if loaded item count exceeds threshold and unloads if needed.
     239              :         Called after append/prepend operations.
     240              :      */
     241              :     void checkMemoryBounds();
     242              : 
     243              :     /*!
     244              :         \brief Unloads items furthest from the display window.
     245              :         \param count Number of items to unload.
     246              :      */
     247              :     void unloadDistantItems(qsizetype count);
     248              : 
     249              :     /*!
     250              :         \brief Populates slotList with reloaded NewsItem objects.
     251              :         \param items The reloaded items (matched by ID to existing slotList).
     252              :      */
     253              :     void populateSlots(const QList<NewsItem*>& items);
     254              : 
     255              :     /*!
     256              :         \brief Verifies all items in the display window are loaded.
     257              :         Logs errors for any unloaded items found.
     258              :         \return true if all items are loaded, false otherwise.
     259              :      */
     260              :     bool verifyDisplayWindowIntegrity() const;
     261              : };
     262              : 
     263              : #endif // NEWSLIST_H
        

Generated by: LCOV version 2.0-1