/ src / qt / bantablemodel.cpp
bantablemodel.cpp
  1  // Copyright (c) 2011-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <qt/bantablemodel.h>
  6  
  7  #include <interfaces/node.h>
  8  #include <net_types.h>
  9  
 10  #include <utility>
 11  
 12  #include <QDateTime>
 13  #include <QList>
 14  #include <QLocale>
 15  #include <QModelIndex>
 16  #include <QVariant>
 17  
 18  bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan& right) const
 19  {
 20      const CCombinedBan* pLeft = &left;
 21      const CCombinedBan* pRight = &right;
 22  
 23      if (order == Qt::DescendingOrder)
 24          std::swap(pLeft, pRight);
 25  
 26      switch (static_cast<BanTableModel::ColumnIndex>(column)) {
 27      case BanTableModel::Address:
 28          return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
 29      case BanTableModel::Bantime:
 30          return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
 31      } // no default case, so the compiler can warn about missing cases
 32      assert(false);
 33  }
 34  
 35  // private implementation
 36  class BanTablePriv
 37  {
 38  public:
 39      /** Local cache of peer information */
 40      QList<CCombinedBan> cachedBanlist;
 41      /** Column to sort nodes by (default to unsorted) */
 42      int sortColumn{-1};
 43      /** Order (ascending or descending) to sort nodes by */
 44      Qt::SortOrder sortOrder;
 45  
 46      /** Pull a full list of banned nodes from interfaces::Node into our cache */
 47      void refreshBanlist(interfaces::Node& node)
 48      {
 49          banmap_t banMap;
 50          node.getBanned(banMap);
 51  
 52          cachedBanlist.clear();
 53          cachedBanlist.reserve(banMap.size());
 54          for (const auto& entry : banMap)
 55          {
 56              CCombinedBan banEntry;
 57              banEntry.subnet = entry.first;
 58              banEntry.banEntry = entry.second;
 59              cachedBanlist.append(banEntry);
 60          }
 61  
 62          if (sortColumn >= 0)
 63              // sort cachedBanlist (use stable sort to prevent rows jumping around unnecessarily)
 64              std::stable_sort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
 65      }
 66  
 67      int size() const
 68      {
 69          return cachedBanlist.size();
 70      }
 71  
 72      CCombinedBan *index(int idx)
 73      {
 74          if (idx >= 0 && idx < cachedBanlist.size())
 75              return &cachedBanlist[idx];
 76  
 77          return nullptr;
 78      }
 79  };
 80  
 81  BanTableModel::BanTableModel(interfaces::Node& node, QObject* parent) :
 82      QAbstractTableModel(parent),
 83      m_node(node)
 84  {
 85      columns << tr("IP/Netmask") << tr("Banned Until");
 86      priv.reset(new BanTablePriv());
 87  
 88      // load initial data
 89      refresh();
 90  }
 91  
 92  BanTableModel::~BanTableModel() = default;
 93  
 94  int BanTableModel::rowCount(const QModelIndex &parent) const
 95  {
 96      if (parent.isValid()) {
 97          return 0;
 98      }
 99      return priv->size();
100  }
101  
102  int BanTableModel::columnCount(const QModelIndex &parent) const
103  {
104      if (parent.isValid()) {
105          return 0;
106      }
107      return columns.length();
108  }
109  
110  QVariant BanTableModel::data(const QModelIndex &index, int role) const
111  {
112      if(!index.isValid())
113          return QVariant();
114  
115      CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());
116  
117      const auto column = static_cast<ColumnIndex>(index.column());
118      if (role == Qt::DisplayRole) {
119          switch (column) {
120          case Address:
121              return QString::fromStdString(rec->subnet.ToString());
122          case Bantime:
123              QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
124              date = date.addSecs(rec->banEntry.nBanUntil);
125              return QLocale::system().toString(date, QLocale::LongFormat);
126          } // no default case, so the compiler can warn about missing cases
127          assert(false);
128      }
129  
130      return QVariant();
131  }
132  
133  QVariant BanTableModel::headerData(int section, Qt::Orientation orientation, int role) const
134  {
135      if(orientation == Qt::Horizontal)
136      {
137          if(role == Qt::DisplayRole && section < columns.size())
138          {
139              return columns[section];
140          }
141      }
142      return QVariant();
143  }
144  
145  Qt::ItemFlags BanTableModel::flags(const QModelIndex &index) const
146  {
147      if (!index.isValid()) return Qt::NoItemFlags;
148  
149      Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
150      return retval;
151  }
152  
153  QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) const
154  {
155      Q_UNUSED(parent);
156      CCombinedBan *data = priv->index(row);
157  
158      if (data)
159          return createIndex(row, column, data);
160      return QModelIndex();
161  }
162  
163  void BanTableModel::refresh()
164  {
165      Q_EMIT layoutAboutToBeChanged();
166      priv->refreshBanlist(m_node);
167      Q_EMIT layoutChanged();
168  }
169  
170  void BanTableModel::sort(int column, Qt::SortOrder order)
171  {
172      priv->sortColumn = column;
173      priv->sortOrder = order;
174      refresh();
175  }
176  
177  bool BanTableModel::shouldShow()
178  {
179      return priv->size() > 0;
180  }
181  
182  bool BanTableModel::unban(const QModelIndex& index)
183  {
184      CCombinedBan* ban{static_cast<CCombinedBan*>(index.internalPointer())};
185      return ban != nullptr && m_node.unban(ban->subnet);
186  }