Advertisement
VRonin

MergeProxy

Oct 11th, 2016
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // mergeproxy.h
  2. #ifndef MERGEPROXY_H
  3. #define MERGEPROXY_H
  4.  
  5. #include <QSortFilterProxyModel>
  6. class MergeProxyPrivate;
  7. class MergeProxy : public QSortFilterProxyModel
  8. {
  9.     Q_OBJECT
  10.     Q_PROPERTY(int mergeKeyColumn READ mergeKeyColumn WRITE setMergeKeyColumn NOTIFY mergeKeyColumnChanged)
  11.     Q_PROPERTY(int mergeKeyRole READ mergeKeyRole WRITE setMergeKeyRole NOTIFY mergeKeyRoleChanged)
  12.     Q_DECLARE_PRIVATE(MergeProxy)
  13. public:
  14.     explicit MergeProxy(QObject* parent = nullptr);
  15.     virtual ~MergeProxy();
  16.     int mergeKeyColumn() const;
  17.     void setMergeKeyColumn(int val);
  18.     int mergeKeyRole() const;
  19.     void setMergeKeyRole(int val);
  20.     virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
  21.     virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
  22. signals:
  23.     void mergeKeyColumnChanged();
  24.     void mergeKeyRoleChanged();
  25. protected:
  26.     virtual QVariant mergeValues(const QVariant& a, const QVariant& b) const;
  27.     virtual bool uniqueCompare(const QVariant& a, const QVariant& b) const;
  28.     virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
  29. private:
  30.     MergeProxyPrivate* d_ptr;
  31. };
  32.  
  33. #endif // MERGEPROXY_H
  34.  
  35. /////////////////////////////////////////////////////////////////////////////
  36. // mergeproxy.cpp
  37. #include "mergeproxy.h"
  38. #include <QDateTime>
  39. class MergeProxyPrivate{
  40.     Q_DECLARE_PUBLIC(MergeProxy)
  41.     MergeProxyPrivate(MergeProxy* q);
  42.     virtual ~MergeProxyPrivate()=default;
  43.     int mergeKeyColumn;
  44.     int mergeKeyRole;
  45.     MergeProxy* q_ptr;
  46. };
  47. MergeProxyPrivate::MergeProxyPrivate(MergeProxy *q)
  48.     :q_ptr(q)
  49.     ,mergeKeyColumn(-1)
  50.     ,mergeKeyRole(Qt::EditRole)
  51. {}
  52.  
  53. /*!
  54.     \class MergeProxy
  55.     \brief MergeProxy is a proxy model that uses a key to determine if 2 rows should be merged
  56.  
  57.  
  58.     MergeProxy can be used to merge different rows based on a key.
  59.  
  60.     The model will be treated as read only.
  61.     You can use setMergeKeyColumn and setMergeKeyRole to determine what column should be used as key to the merge.
  62.     Rows with the same data in this column will be merged together.
  63.  
  64.     Rows with the same key but different parent index in a tree will not be aggregated
  65.  
  66.     All non-key columns will be aggragated using mergeValues.
  67.  
  68.     All the filtering functionalities of QSortFilterProxyModel are still available
  69.  
  70. */
  71.  
  72. /*!
  73.     Constructs a merged sorting filter model with the given \a parent.
  74. */
  75. MergeProxy::MergeProxy(QObject* parent)
  76.     :QSortFilterProxyModel(parent)
  77.     ,d_ptr(new MergeProxyPrivate(this))
  78. {
  79.     connect(this,&MergeProxy::mergeKeyColumnChanged,this,&MergeProxy::invalidateFilter);
  80.     connect(this,&MergeProxy::mergeKeyRoleChanged,this,&MergeProxy::invalidateFilter);
  81. }
  82. //! Destructor
  83. MergeProxy::~MergeProxy()
  84. {
  85.     delete d_ptr;
  86. }
  87. /*!
  88.     \property MergeProxy::mergeKeyColumn
  89.     \brief the column where the key used to merge the contents of the source model is read from.
  90.  
  91.     The default value is -1. If the value is -1, no merging will be performed
  92. */
  93. int MergeProxy::mergeKeyColumn() const
  94. {
  95.     Q_D(const MergeProxy);
  96.     return d->mergeKeyColumn;
  97. }
  98. void MergeProxy::setMergeKeyColumn(int val)
  99. {
  100.     Q_D(MergeProxy);
  101.     if(val<0)
  102.         val=-1;
  103.     if(d->mergeKeyColumn!=val){
  104.         d->mergeKeyColumn=val;
  105.         emit mergeKeyColumnChanged();
  106.     }
  107. }
  108. /*!
  109.     \property MergeProxy::mergeKeyRole
  110.     \brief the item role that is used to query the source model's data when merging items
  111.  
  112.     The default value is Qt::EditRole.
  113. */
  114. int MergeProxy::mergeKeyRole() const
  115. {
  116.     Q_D(const MergeProxy);
  117.     return d->mergeKeyRole;
  118. }
  119.  
  120. void MergeProxy::setMergeKeyRole(int val)
  121. {
  122.     if(val<0)
  123.         return;
  124.     Q_D(MergeProxy);
  125.     if(d->mergeKeyRole!=val){
  126.         d->mergeKeyRole=val;
  127.         emit mergeKeyRoleChanged();
  128.     }
  129. }
  130. /*!
  131.     \reimp
  132. */
  133. QVariant MergeProxy::data(const QModelIndex &index, int role) const
  134. {
  135.     Q_D(const MergeProxy);
  136.     QVariant baseData= QSortFilterProxyModel::data(index,role);
  137.     const QModelIndex mappedIdx= mapToSource(index);
  138.     if(d->mergeKeyColumn<0 || d->mergeKeyColumn==mappedIdx.column())
  139.         return baseData;
  140.     const int rowCount=sourceModel()->rowCount(mappedIdx.parent());
  141.     for(int i=0;i<rowCount;++i){
  142.         if(i==mappedIdx.row())
  143.             continue;
  144.         if(!QSortFilterProxyModel::filterAcceptsRow(i,mappedIdx.parent()))
  145.             continue;
  146.         if(uniqueCompare(
  147.                     sourceModel()->index(mappedIdx.row(),d->mergeKeyColumn,mappedIdx.parent()).data(d->mergeKeyRole)
  148.                     , sourceModel()->index(i,d->mergeKeyColumn,mappedIdx.parent()).data(d->mergeKeyRole)
  149.                     ))
  150.             baseData=mergeValues(baseData,sourceModel()->index(i,mappedIdx.column(),mappedIdx.parent()).data(role));
  151.     }
  152.     return baseData;
  153. }
  154. /*!
  155.     \reimp
  156. */
  157. Qt::ItemFlags MergeProxy::flags(const QModelIndex &index) const
  158. {
  159.     return QSortFilterProxyModel::flags(index) & (~Qt::ItemIsEditable);
  160. }
  161. /*!
  162.     \brief Merges data from two rows together
  163.  
  164.     This method will be used to merge data from different rows together. The default implementation will use:
  165.     for numeric types (including char), addition;
  166.     for booleans the logic or;
  167.     for QChar and QString concatenation;
  168.     for QDate, QTime and QDateTime the difference between the two will be added to the first one.
  169.  
  170.     \warning The default implementation implicitly assumes that all data in the same colum is of the same type
  171.  
  172. */
  173. QVariant MergeProxy::mergeValues(const QVariant &a, const QVariant &b) const
  174. {
  175.     if(a.isNull())
  176.         return b;
  177.     if(b.isNull())
  178.         return a;
  179.     switch (a.userType()) {
  180.         case QMetaType::UnknownType:
  181.             Q_ASSERT_X(false, "MergeProxy::mergeValues", "Trying to use unregistered type.");
  182.             return QVariant();
  183.         case QMetaType::Bool: return a.toBool() || b.toBool();
  184.         case QMetaType::Long:
  185.         case QMetaType::Int: return a.toInt() + b.toInt();
  186.         case QMetaType::ULong:
  187.         case QMetaType::UInt: return a.toUInt()+ b.toUInt();
  188.         case QMetaType::LongLong: return a.toLongLong()+ b.toLongLong();
  189.         case QMetaType::ULongLong: return a.toULongLong()+ b.toULongLong();
  190.         case QMetaType::Double: return a.toDouble()+ b.toDouble();
  191.         case QMetaType::Short: return static_cast<short>(a.toInt()+ b.toInt());
  192.         case QMetaType::SChar:
  193.         case QMetaType::Char: return static_cast<char>(a.toInt()+ b.toInt());
  194.         case QMetaType::UShort: return static_cast<unsigned short>(a.toUInt()+ b.toUInt());
  195.         case QMetaType::UChar: return static_cast<unsigned char>(a.toUInt()+ b.toUInt());
  196.         case QMetaType::Float: return a.toFloat()+ b.toFloat();
  197.         case QMetaType::QString: return a.toString() + b.toString();
  198.         case QMetaType::QChar:
  199.             return QString(a.toChar()) + b.toChar();
  200.         case QMetaType::QDate:{
  201.             const QDate aDate=a.toDate();
  202.             return aDate.addDays(qAbs(aDate.daysTo(b.toDate())));
  203.         }
  204.         case QMetaType::QTime: {
  205.             const QTime aTime=a.toTime();
  206.             return aTime.addMSecs(qAbs(aTime.msecsTo(b.toTime())));
  207.         }
  208.         case QMetaType::QDateTime: {
  209.             const QDateTime aTime=a.toDateTime();
  210.             return aTime.addMSecs(qAbs(aTime.msecsTo(b.toDateTime())));
  211.         }
  212.         default:
  213.             qWarning("MergeProxy::mergeValues, Unhandled merge type");
  214.             return a;
  215.     }
  216. }
  217. /*!
  218.     \brief Compares merge keys
  219.  
  220.     This method will be used to compare data in the merge key column to determine if two rows are to be aggragated.
  221.     Rows will be aggragated if this method returns true
  222.  
  223.     The default implementation uses operator== for comparison
  224. */
  225. bool MergeProxy::uniqueCompare(const QVariant &a, const QVariant &b) const
  226. {
  227.     return a==b;
  228. }
  229. /*!
  230.     \reimp
  231. */
  232. bool MergeProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
  233. {
  234.     Q_D(const MergeProxy);
  235.     const bool baseFilter = QSortFilterProxyModel::filterAcceptsRow(source_row,source_parent);
  236.     if(!baseFilter || d->mergeKeyColumn<0 || d->mergeKeyColumn>=sourceModel()->columnCount(source_parent))
  237.         return baseFilter;
  238.     for(int i=0;i<source_row;++i){
  239.         if(uniqueCompare(
  240.                     sourceModel()->index(i,d->mergeKeyColumn,source_parent).data(d->mergeKeyRole)
  241.                     , sourceModel()->index(source_row,d->mergeKeyColumn,source_parent).data(d->mergeKeyRole)
  242.                     ))
  243.             return false;
  244.     }
  245.     return true;
  246. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement