Hi,
I'm expiriencing some problems with selection using a custom tree model and QTreeView.
Here is the code for the model:

Qt Code:
  1. #include "sessionstartendmodel.h"
  2.  
  3. #include <QDebug>
  4.  
  5. static constexpr quintptr STATION = quintptr(-1);
  6.  
  7. SessionStartEndModel::SessionStartEndModel(QObject *parent) :
  8. m_mode(StartOfSession)
  9. {
  10.  
  11. }
  12.  
  13. QVariant SessionStartEndModel::headerData(int section, Qt::Orientation orientation, int role) const
  14. {
  15. if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
  16. {
  17. switch (section)
  18. {
  19. case RsNameCol:
  20. return tr("Rollingstock");
  21. case JobCol:
  22. return tr("Job");
  23. case PlatfCol:
  24. return tr("Platform");
  25. case DepartureCol:
  26. return m_mode == StartOfSession ? tr("Departure") : tr("Arrival");
  27. default:
  28. break;
  29. }
  30. }
  31. return QAbstractItemModel::headerData(section, orientation, role);
  32. }
  33.  
  34. QModelIndex SessionStartEndModel::index(int row, int column, const QModelIndex &p) const
  35. {
  36. if(p.isValid())
  37. {
  38. if(p.internalId() != STATION)
  39. return QModelIndex();
  40.  
  41. //It's a rs
  42. int parentRow = p.row();
  43. if(parentRow < 0 || parentRow >= stations.size())
  44. return QModelIndex();
  45.  
  46. if(row < 0 || (row + stations[parentRow].firstIdx) >= rsData.size() || column < 0 || column >= NCols)
  47. return QModelIndex();
  48.  
  49. QModelIndex par = parent(createIndex(row, column, parentRow));
  50. if(par != p)
  51. {
  52. qWarning() << "IDX ERROR" << p << par;
  53. par = parent(createIndex(row, column, parentRow));
  54. }
  55.  
  56. return createIndex(row, column, parentRow);
  57. }
  58.  
  59. //Station
  60. if(row < 0 || row >= stations.size() || column != 0)
  61. return QModelIndex();
  62.  
  63. Q_ASSERT(parent(createIndex(row, column, STATION)) == p);
  64.  
  65. return createIndex(row, column, STATION);
  66. }
  67.  
  68. QModelIndex SessionStartEndModel::parent(const QModelIndex &idx) const
  69. {
  70. if(!idx.isValid() || idx.internalId() == STATION)
  71. return QModelIndex();
  72.  
  73. int parentRow = int(idx.internalId());
  74.  
  75. return createIndex(parentRow, 0, STATION);
  76. }
  77.  
  78. int SessionStartEndModel::rowCount(const QModelIndex &p) const
  79. {
  80. if(p.isValid())
  81. {
  82. if(p.internalId() != STATION)
  83. return 0; //RS
  84.  
  85. //Station
  86. int firstIdx = stations.at(p.row()).firstIdx;
  87. int lastIdx = rsData.size(); //Until end
  88. if(p.row() + 1 < stations.size())
  89. lastIdx = stations.at(p.row() + 1).firstIdx; //Until next station first index
  90. return lastIdx - firstIdx;
  91. }
  92.  
  93. //Root
  94. return stations.size();
  95. }
  96.  
  97. int SessionStartEndModel::columnCount(const QModelIndex &parent) const
  98. {
  99. return parent.isValid() ? 1 : NCols;
  100. }
  101.  
  102. QVariant SessionStartEndModel::data(const QModelIndex &idx, int role) const
  103. {
  104. if(!idx.isValid())
  105. return QVariant();
  106.  
  107. if(idx.internalId() == STATION)
  108. {
  109. //Station
  110. switch (role)
  111. {
  112. case Qt::DisplayRole:
  113. //return stations.at(idx.row()).stationId;
  114. return Session->mStationsModel->getStName(stations.at(idx.row()).stationId); //TODO Station::name
  115. case Qt::FontRole:
  116. {
  117. QFont f;
  118. f.setBold(true);
  119. return f;
  120. }
  121. }
  122. }
  123. else
  124. {
  125. //RS
  126. int realIdx = stations.at(int(idx.internalId())).firstIdx + idx.row();
  127. const RSItem& item = rsData.at(realIdx);
  128. switch (role)
  129. {
  130. case Qt::DisplayRole:
  131. {
  132. switch (idx.column())
  133. {
  134. case RsNameCol:
  135. //return item.rsId;
  136. return Session->mRsModel->getRSName(item.rsId);
  137. case JobCol:
  138. //return item.jobId;
  139. return Session->mJobsModel->getJobName(item.jobId);
  140. case PlatfCol:
  141. return utils::platformName(item.platform);
  142. case DepartureCol:
  143. return item.time;
  144. }
  145. }
  146. }
  147. }
  148.  
  149. return QVariant();
  150. }
  151.  
  152. bool SessionStartEndModel::hasChildren(const QModelIndex &parent) const
  153. {
  154. if(parent.isValid())
  155. {
  156. if(parent.internalId() == STATION)
  157. return true;
  158. return false;
  159. }
  160.  
  161. return stations.size();
  162. }
  163.  
  164. QModelIndex SessionStartEndModel::sibling(int row, int column, const QModelIndex &idx) const
  165. {
  166. if(!idx.isValid())
  167. return QModelIndex();
  168.  
  169. if(idx.internalId() == STATION)
  170. {
  171. if(row < stations.size() && column == 1)
  172. return createIndex(row, column, STATION);
  173. }
  174. else
  175. {
  176. int stIdx = int(idx.internalId());
  177. int firstIdx = stations.at(stIdx).firstIdx;
  178. int lastIdx = rsData.size();
  179. if(stIdx + 1 < stations.size())
  180. lastIdx = stations.at(stIdx + 1).firstIdx;
  181.  
  182. int count = firstIdx - lastIdx;
  183. if(row < count)
  184. return createIndex(row, column, stIdx);
  185. }
  186.  
  187. return QModelIndex();
  188. }
  189.  
  190. Qt::ItemFlags SessionStartEndModel::flags(const QModelIndex &idx) const
  191. {
  192. Qt::ItemFlags f;
  193.  
  194. if(!idx.isValid())
  195. return f;
  196.  
  197. f.setFlag(Qt::ItemIsEnabled);
  198. f.setFlag(Qt::ItemIsSelectable);
  199.  
  200. if(idx.internalId() != STATION)
  201. f.setFlag(Qt::ItemNeverHasChildren);
  202.  
  203. return f;
  204. }
To copy to clipboard, switch view to plain text mode 

Qt Code:
  1. #ifndef SESSIONSTARTENDMODEL_H
  2. #define SESSIONSTARTENDMODEL_H
  3.  
  4. #include <QAbstractItemModel>
  5.  
  6. #include <QVector>
  7. #include <QTime>
  8.  
  9. #include "utils/types.h"
  10.  
  11. class SessionStartEndModel : public QAbstractItemModel
  12. {
  13. Q_OBJECT
  14.  
  15. public:
  16. typedef struct Station_
  17. {
  18. db_id stationId;
  19. QString name;
  20. int firstIdx; //First RS index
  21. } Station;
  22.  
  23. typedef struct RSItem_
  24. {
  25. db_id rsId;
  26. int stationIdx;
  27. int platform;
  28. db_id jobId;
  29. QTime time;
  30. } RSItem;
  31.  
  32. enum Columns
  33. {
  34. RsNameCol = 0,
  35. JobCol,
  36. PlatfCol,
  37. DepartureCol,
  38. NCols
  39. };
  40.  
  41. enum Mode
  42. {
  43. StartOfSession,
  44. EndOfSession
  45. };
  46.  
  47.  
  48. explicit SessionStartEndModel(QObject *parent = nullptr);
  49.  
  50. // Header:
  51. QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
  52.  
  53. // Basic functionality:
  54. QModelIndex index(int row, int column,
  55. const QModelIndex &parent = QModelIndex()) const override;
  56. QModelIndex parent(const QModelIndex &idx) const override;
  57.  
  58. int rowCount(const QModelIndex &parent = QModelIndex()) const override;
  59. int columnCount(const QModelIndex &parent = QModelIndex()) const override;
  60.  
  61. QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
  62.  
  63. bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
  64.  
  65. QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
  66.  
  67. Qt::ItemFlags flags(const QModelIndex& idx) const override;
  68.  
  69. private:
  70. QVector<Station> stations;
  71. QVector<RSItem> rsData;
  72.  
  73. Mode m_mode;
  74. };
  75.  
  76. #endif // SESSIONSTARTENDMODEL_H
To copy to clipboard, switch view to plain text mode 

The QTreeView should select the complete row:
Qt Code:
  1. view = new QTreeView(this);
  2. view->setModel(model);
  3. view->setSelectionBehavior(QAbstractItemView::SelectRows);
  4. view->setSelectionMode(QAbstractItemView::SingleSelection);
To copy to clipboard, switch view to plain text mode 

Instead when I click on indexed on first column only 1 index gets selectd, when clicking on other columns nothing happens instead of selecting entire row.
When single clicking parent rows nothing happens (maybe this is intended behaviour)

Thanks in advance