ctkDICOMModel.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QStringList>
  16. #include <QSqlDriver>
  17. #include <QSqlError>
  18. #include <QSqlQuery>
  19. #include <QSqlRecord>
  20. #include <QSqlResult>
  21. #include <QTime>
  22. #include <QDebug>
  23. // dcmtk includes
  24. #include "dcvrpn.h"
  25. // ctkDICOMCore includes
  26. #include "ctkDICOMModel.h"
  27. #include "ctkLogger.h"
  28. static ctkLogger logger ( "org.commontk.dicom.DICOMModel" );
  29. struct Node;
  30. Q_DECLARE_METATYPE(Qt::CheckState);
  31. Q_DECLARE_METATYPE(QStringList);
  32. //------------------------------------------------------------------------------
  33. class ctkDICOMModelPrivate
  34. {
  35. Q_DECLARE_PUBLIC(ctkDICOMModel);
  36. protected:
  37. ctkDICOMModel* const q_ptr;
  38. public:
  39. ctkDICOMModelPrivate(ctkDICOMModel&);
  40. virtual ~ctkDICOMModelPrivate();
  41. void init();
  42. void fetch(const QModelIndex& indexValue, int limit);
  43. Node* createNode(int row, const QModelIndex& parentValue)const;
  44. Node* nodeFromIndex(const QModelIndex& indexValue)const;
  45. //QModelIndexList indexListFromNode(const Node* node)const;
  46. //QModelIndexList modelIndexList(Node* node = 0)const;
  47. //int childrenCount(Node* node = 0)const;
  48. // move it in the Node struct
  49. QVariant value(Node* parentValue, int row, int field)const;
  50. QVariant value(const QModelIndex& indexValue, int row, int field)const;
  51. QString generateQuery(const QString& fields, const QString& table, const QString& conditions = QString())const;
  52. void updateQueries(Node* node)const;
  53. Node* RootNode;
  54. QSqlDatabase DataBase;
  55. QList<QMap<int, QVariant> > Headers;
  56. QString Sort;
  57. QMap<QString, QVariant> SearchParameters;
  58. ctkDICOMModel::IndexType StartLevel;
  59. ctkDICOMModel::IndexType EndLevel;
  60. };
  61. //------------------------------------------------------------------------------
  62. // 1 node per row
  63. // TBD: should probably use the QStandardItems instead.
  64. struct Node
  65. {
  66. ~Node()
  67. {
  68. foreach(Node* node, this->Children)
  69. {
  70. delete node;
  71. }
  72. this->Children.clear();
  73. }
  74. ctkDICOMModel::IndexType Type;
  75. Node* Parent;
  76. QVector<Node*> Children;
  77. int Row;
  78. QSqlQuery Query;
  79. QString UID;
  80. int RowCount;
  81. bool AtEnd;
  82. bool Fetching;
  83. QMap<int, QVariant> Data;
  84. };
  85. //------------------------------------------------------------------------------
  86. ctkDICOMModelPrivate::ctkDICOMModelPrivate(ctkDICOMModel& o):q_ptr(&o)
  87. {
  88. this->RootNode = 0;
  89. this->StartLevel = ctkDICOMModel::RootType;
  90. this->EndLevel = ctkDICOMModel::ImageType;
  91. }
  92. //------------------------------------------------------------------------------
  93. ctkDICOMModelPrivate::~ctkDICOMModelPrivate()
  94. {
  95. delete this->RootNode;
  96. this->RootNode = 0;
  97. }
  98. //------------------------------------------------------------------------------
  99. void ctkDICOMModelPrivate::init()
  100. {
  101. QMap<int, QVariant> data;
  102. data[Qt::DisplayRole] = QString("Name");
  103. this->Headers << data;
  104. data[Qt::DisplayRole] = QString("Age");
  105. this->Headers << data;
  106. data[Qt::DisplayRole] = QString("Scan");
  107. this->Headers << data;
  108. data[Qt::DisplayRole] = QString("Date");
  109. this->Headers << data;
  110. data[Qt::DisplayRole] = QString("Subject ID");
  111. this->Headers << data;
  112. data[Qt::DisplayRole] = QString("Number");
  113. this->Headers << data;
  114. data[Qt::DisplayRole] = QString("Institution");
  115. this->Headers << data;
  116. data[Qt::DisplayRole] = QString("Referrer");
  117. this->Headers << data;
  118. data[Qt::DisplayRole] = QString("Performer");
  119. this->Headers << data;
  120. }
  121. //------------------------------------------------------------------------------
  122. Node* ctkDICOMModelPrivate::nodeFromIndex(const QModelIndex& indexValue)const
  123. {
  124. return indexValue.isValid() ? reinterpret_cast<Node*>(indexValue.internalPointer()) : this->RootNode;
  125. }
  126. /*
  127. //------------------------------------------------------------------------------
  128. QModelIndexList ctkDICOMModelPrivate::indexListFromNode(const Node* node)const
  129. {
  130. Q_Q(const ctkDICOMModel);
  131. Q_ASSERT(node);
  132. QModelIndexList indexList;
  133. Node* parentNode = node->Parent;
  134. if (parentNode == 0)
  135. {
  136. return indexList;
  137. }
  138. int field = parentNode->Query.record().indexOf("UID");
  139. int row = -1;
  140. for (row = 0; row < parentNode->RowCount; ++row)
  141. {
  142. QString uid = this->value(parentNode, row, field).toString();
  143. if (uid == node->UID)
  144. {
  145. break;
  146. }
  147. }
  148. if (row >= parentNode->RowCount)
  149. {
  150. return indexList;
  151. }
  152. for (int column = 0 ; column < this->Headers.size(); ++column)
  153. {
  154. indexList.append(q->createIndex(row, column, parentNode));
  155. }
  156. return indexList;
  157. }
  158. //------------------------------------------------------------------------------
  159. QModelIndexList ctkDICOMModelPrivate::modelIndexList(Node* node)const
  160. {
  161. QModelIndexList list;
  162. if (node == 0)
  163. {
  164. node = this->RootNode;
  165. }
  166. foreach(Node* child, node->Children)
  167. {
  168. list.append(this->indexListFromNode(child));
  169. }
  170. foreach(Node* child, node->Children)
  171. {
  172. list.append(this->modelIndexList(child));
  173. }
  174. return list;
  175. }
  176. //------------------------------------------------------------------------------
  177. int ctkDICOMModelPrivate::childrenCount(Node* node)const
  178. {
  179. int count = 0;
  180. if (node == 0)
  181. {
  182. node = this->RootNode;
  183. }
  184. count += node->Children.size();
  185. foreach(Node* child, node->Children)
  186. {
  187. count += this->childrenCount(child);
  188. }
  189. return count;
  190. }
  191. */
  192. //------------------------------------------------------------------------------
  193. Node* ctkDICOMModelPrivate::createNode(int row, const QModelIndex& parentValue)const
  194. {
  195. Node* node = new Node;
  196. Node* nodeParent = 0;
  197. if (row == -1)
  198. {// root node
  199. node->Type = ctkDICOMModel::RootType;
  200. node->Parent = 0;
  201. #if CHECKABLE_COLUMNS
  202. // CHECKABLE_COLUMNS are disabled by default - they are not yet used by other
  203. // parts of the ctkDICOM infrastructure so they are misleading to the user
  204. node->Data[Qt::CheckStateRole] = Qt::Unchecked;
  205. #endif
  206. }
  207. else
  208. {
  209. nodeParent = this->nodeFromIndex(parentValue);
  210. nodeParent->Children.push_back(node);
  211. node->Parent = nodeParent;
  212. node->Type = ctkDICOMModel::IndexType(nodeParent->Type + 1);
  213. }
  214. node->Row = row;
  215. if (node->Type != ctkDICOMModel::RootType)
  216. {
  217. int field = 0;//nodeParent->Query.record().indexOf("UID");
  218. node->UID = this->value(parentValue, row, field).toString();
  219. #if CHECKABLE_COLUMNS
  220. node->Data[Qt::CheckStateRole] = node->Parent->Data[Qt::CheckStateRole];
  221. #endif
  222. }
  223. node->RowCount = 0;
  224. node->AtEnd = false;
  225. node->Fetching = false;
  226. this->updateQueries(node);
  227. return node;
  228. }
  229. //------------------------------------------------------------------------------
  230. QVariant ctkDICOMModelPrivate::value(const QModelIndex& parentValue, int row, int column) const
  231. {
  232. Node* node = this->nodeFromIndex(parentValue);
  233. if (row >= node->RowCount)
  234. {
  235. const_cast<ctkDICOMModelPrivate *>(this)->fetch(parentValue, row + 256);
  236. }
  237. return this->value(node, row, column);
  238. }
  239. //------------------------------------------------------------------------------
  240. QVariant ctkDICOMModelPrivate::value(Node* parentNode, int row, int column) const
  241. {
  242. if (row < 0 || column < 0 || !parentNode || row >= parentNode->RowCount)
  243. {
  244. return QVariant();
  245. }
  246. if (!parentNode->Query.seek(row))
  247. {
  248. qDebug() << parentNode->Query.lastError();
  249. Q_ASSERT(parentNode->Query.seek(row));
  250. return QVariant();
  251. }
  252. QVariant res = parentNode->Query.value(column);
  253. Q_ASSERT(res.isValid());
  254. return res;
  255. }
  256. //------------------------------------------------------------------------------
  257. QString ctkDICOMModelPrivate::generateQuery(const QString& fields, const QString& table, const QString& conditions)const
  258. {
  259. QString res = QString("SELECT ") + fields + QString(" FROM ") + table;
  260. if (!conditions.isEmpty())
  261. {
  262. res += QString(" WHERE ") + conditions;
  263. }
  264. if (!this->Sort.isEmpty())
  265. {
  266. res += QString(" ORDER BY ") + this->Sort;
  267. }
  268. logger.debug ( "ctkDICOMModelPrivate::generateQuery: query is: " + res );
  269. return res;
  270. }
  271. //------------------------------------------------------------------------------
  272. void ctkDICOMModelPrivate::updateQueries(Node* node)const
  273. {
  274. // are you kidding me, it should be virtualized here :-)
  275. QString query;
  276. QString condition;
  277. switch(node->Type)
  278. {
  279. default:
  280. Q_ASSERT(node->Type == ctkDICOMModel::RootType);
  281. break;
  282. case ctkDICOMModel::RootType:
  283. //query = QString("SELECT FROM ");
  284. if(this->SearchParameters["Name"].toString() != ""){
  285. condition.append("PatientsName LIKE \"%" + this->SearchParameters["Name"].toString() + "%\"");
  286. }
  287. query = this->generateQuery("UID as UID, PatientsName as Name, PatientsAge as Age, PatientsBirthDate as Date, PatientID as \"Subject ID\"","Patients", condition);
  288. logger.debug ( "ctkDICOMModelPrivate::updateQueries for Root: query is: " + query );
  289. break;
  290. case ctkDICOMModel::PatientType:
  291. //query = QString("SELECT FROM Studies WHERE PatientsUID='%1'").arg(node->UID);
  292. if(this->SearchParameters["Study"].toString() != "")
  293. {
  294. condition.append("StudyDescription LIKE \"%" + this->SearchParameters["Study"].toString() + "%\"" + " AND ");
  295. }
  296. if(this->SearchParameters["Modalities"].value<QStringList>().count() > 0)
  297. {
  298. condition.append("ModalitiesInStudy IN (\"" + this->SearchParameters["Modalities"].value<QStringList>().join("\",\"") + "\") AND ");
  299. }
  300. if(this->SearchParameters["StartDate"].toString() != "" &&
  301. this->SearchParameters["EndDate"].toString() != "")
  302. {
  303. condition.append(" ( StudyDate BETWEEN \'" + QDate::fromString(this->SearchParameters["StartDate"].toString(), "yyyyMMdd").toString("yyyy-MM-dd")
  304. + "\' AND \'" + QDate::fromString(this->SearchParameters["EndDate"].toString(), "yyyyMMdd").toString("yyyy-MM-dd") + "\' ) AND ");
  305. }
  306. query = this->generateQuery("StudyInstanceUID as UID, StudyDescription as Name, ModalitiesInStudy as Scan, StudyDate as Date, AccessionNumber as Number, InstitutionName as Institution, ReferringPhysician as Referrer, PerformingPhysiciansName as Performer", "Studies", condition + QString("PatientsUID='%1'").arg(node->UID));
  307. logger.debug ( "ctkDICOMModelPrivate::updateQueries for Patient: query is: " + query );
  308. break;
  309. case ctkDICOMModel::StudyType:
  310. //query = QString("SELECT SeriesInstanceUID as UID, SeriesDescription as Name, BodyPartExamined as Scan, SeriesDate as Date, AcquisitionNumber as Number FROM Series WHERE StudyInstanceUID='%1'").arg(node->UID);
  311. if(this->SearchParameters["Series"].toString() != "")
  312. {
  313. condition.append("SeriesDescription LIKE \"%" + this->SearchParameters["Series"].toString() + "%\"" + " AND ");
  314. }
  315. query = this->generateQuery("SeriesInstanceUID as UID, SeriesDescription as Name, Modality as Age, SeriesNumber as Scan, BodyPartExamined as \"Subject ID\", SeriesDate as Date, AcquisitionNumber as Number","Series",condition + QString("StudyInstanceUID='%1'").arg(node->UID));
  316. logger.debug ( "ctkDICOMModelPrivate::updateQueries for Study: query is: " + query );
  317. break;
  318. case ctkDICOMModel::SeriesType:
  319. if(this->SearchParameters["ID"].toString() != "")
  320. {
  321. condition.append("SOPInstanceUID LIKE \"%" + this->SearchParameters["ID"].toString() + "%\"" + " AND ");
  322. }
  323. //query = QString("SELECT Filename as UID, Filename as Name, SeriesInstanceUID as Date FROM Images WHERE SeriesInstanceUID='%1'").arg(node->UID);
  324. query = this->generateQuery("SOPInstanceUID as UID, Filename as Name, SeriesInstanceUID as Date", "Images", condition + QString("SeriesInstanceUID='%1'").arg(node->UID));
  325. logger.debug ( "ctkDICOMModelPrivate::updateQueries for Series: query is: " + query );
  326. break;
  327. case ctkDICOMModel::ImageType:
  328. break;
  329. }
  330. node->Query = QSqlQuery(query, this->DataBase);
  331. foreach(Node* child, node->Children)
  332. {
  333. this->updateQueries(child);
  334. }
  335. }
  336. //------------------------------------------------------------------------------
  337. void ctkDICOMModelPrivate::fetch(const QModelIndex& indexValue, int limit)
  338. {
  339. Q_Q(ctkDICOMModel);
  340. Node* node = this->nodeFromIndex(indexValue);
  341. if (node->AtEnd || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
  342. {
  343. return;
  344. }
  345. node->Fetching = true;
  346. int newRowCount;
  347. const int oldRowCount = node->RowCount;
  348. // try to seek directly
  349. if (node->Query.seek(limit - 1))
  350. {
  351. newRowCount = limit;
  352. }
  353. else
  354. {
  355. newRowCount = qMax(oldRowCount, 1);
  356. if (node->Query.seek(newRowCount - 1))
  357. {
  358. while (node->Query.next())
  359. {
  360. ++newRowCount;
  361. }
  362. }
  363. else
  364. {
  365. // empty or invalid query
  366. newRowCount = 0;
  367. }
  368. node->AtEnd = true; // this is the end.
  369. }
  370. if (newRowCount > 0 && newRowCount > node->RowCount)
  371. {
  372. q->beginInsertRows(indexValue, node->RowCount, newRowCount - 1);
  373. node->RowCount = newRowCount;
  374. node->Fetching = false;
  375. q->endInsertRows();
  376. }
  377. else
  378. {
  379. node->RowCount = newRowCount;
  380. node->Fetching = false;
  381. }
  382. }
  383. //------------------------------------------------------------------------------
  384. ctkDICOMModel::ctkDICOMModel(QObject* parentObject)
  385. : Superclass(parentObject)
  386. , d_ptr(new ctkDICOMModelPrivate(*this))
  387. {
  388. Q_D(ctkDICOMModel);
  389. d->init();
  390. }
  391. //------------------------------------------------------------------------------
  392. ctkDICOMModel::~ctkDICOMModel()
  393. {
  394. }
  395. //------------------------------------------------------------------------------
  396. bool ctkDICOMModel::canFetchMore ( const QModelIndex & parentValue ) const
  397. {
  398. Q_D(const ctkDICOMModel);
  399. Node* node = d->nodeFromIndex(parentValue);
  400. return node ? !node->AtEnd : false;
  401. }
  402. //------------------------------------------------------------------------------
  403. int ctkDICOMModel::columnCount ( const QModelIndex & _parent ) const
  404. {
  405. Q_D(const ctkDICOMModel);
  406. Q_UNUSED(_parent);
  407. return d->RootNode != 0 ? d->Headers.size() : 0;
  408. }
  409. //------------------------------------------------------------------------------
  410. QVariant ctkDICOMModel::data ( const QModelIndex & dataIndex, int role ) const
  411. {
  412. Q_D(const ctkDICOMModel);
  413. if ( role == UIDRole )
  414. {
  415. Node* node = d->nodeFromIndex(dataIndex);
  416. return node ? node->UID : QString() ;
  417. }
  418. else if ( role == TypeRole )
  419. {
  420. Node* node = d->nodeFromIndex(dataIndex);
  421. return node ? node->Type : 0;
  422. }
  423. else if ( dataIndex.column() == 0 && role == Qt::CheckStateRole)
  424. {
  425. Node* node = d->nodeFromIndex(dataIndex);
  426. return node ? node->Data[Qt::CheckStateRole] : 0;
  427. }
  428. if (role != Qt::DisplayRole && role != Qt::EditRole)
  429. {
  430. if (dataIndex.column() != 0)
  431. {
  432. return QVariant();
  433. }
  434. Node* node = d->nodeFromIndex(dataIndex);
  435. if (!node)
  436. {
  437. return QVariant();
  438. }
  439. return node->Data[role];
  440. }
  441. QModelIndex parentIndex = this->parent(dataIndex);
  442. Node* parentNode = d->nodeFromIndex(parentIndex);
  443. if (dataIndex.row() >= parentNode->RowCount)
  444. {
  445. const_cast<ctkDICOMModelPrivate *>(d)->fetch(dataIndex, dataIndex.row());
  446. }
  447. QString columnName = d->Headers[dataIndex.column()][Qt::DisplayRole].toString();
  448. int field = parentNode->Query.record().indexOf(columnName);
  449. if (field < 0)
  450. {
  451. // Not all the columns are in the record, it's ok to have no field here.
  452. // Return an empty string in that case (not a QVariant() that means it's
  453. // invalid).
  454. return QString();
  455. }
  456. QVariant dataValue=d->value(parentIndex, dataIndex.row(), field);
  457. if (dataValue.isNull())
  458. {
  459. if (columnName.compare("Name")==0)
  460. {
  461. return QString("No description");
  462. }
  463. }
  464. if (columnName.compare("Name")==0)
  465. {
  466. OFString dicomName = dataValue.toString().toStdString().c_str();
  467. OFString formattedName;
  468. OFString lastName, firstName, middleName, namePrefix, nameSuffix;
  469. OFCondition l_error = DcmPersonName::getNameComponentsFromString(dicomName,
  470. lastName, firstName, middleName, namePrefix, nameSuffix);
  471. if (l_error.good())
  472. {
  473. formattedName.clear();
  474. /* concatenate name components per this convention
  475. * Last, First Middle, Suffix (Prefix)
  476. * */
  477. if (!lastName.empty())
  478. {
  479. formattedName += lastName;
  480. if ( !(firstName.empty() && middleName.empty()) )
  481. {
  482. formattedName += ",";
  483. }
  484. }
  485. if (!firstName.empty())
  486. {
  487. formattedName += " ";
  488. formattedName += firstName;
  489. }
  490. if (!middleName.empty())
  491. {
  492. formattedName += " ";
  493. formattedName += middleName;
  494. }
  495. if (!nameSuffix.empty())
  496. {
  497. formattedName += ", ";
  498. formattedName += nameSuffix;
  499. }
  500. if (!namePrefix.empty())
  501. {
  502. formattedName += " (";
  503. formattedName += namePrefix;
  504. formattedName += ")";
  505. }
  506. }
  507. return QString(formattedName.c_str());
  508. }
  509. return dataValue;
  510. }
  511. //------------------------------------------------------------------------------
  512. void ctkDICOMModel::fetchMore ( const QModelIndex & parentValue )
  513. {
  514. Q_D(ctkDICOMModel);
  515. Node* node = d->nodeFromIndex(parentValue);
  516. d->fetch(parentValue, qMax(node->RowCount, 0) + 256);
  517. }
  518. //------------------------------------------------------------------------------
  519. Qt::ItemFlags ctkDICOMModel::flags ( const QModelIndex & modelIndex ) const
  520. {
  521. Q_D(const ctkDICOMModel);
  522. Qt::ItemFlags indexFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  523. if (modelIndex.column() != 0)
  524. {
  525. return indexFlags;
  526. }
  527. Node* node = d->nodeFromIndex(modelIndex);
  528. if (!node)
  529. {
  530. return indexFlags;
  531. }
  532. bool checkable = true;
  533. node->Data[Qt::CheckStateRole].toInt(&checkable);
  534. indexFlags = indexFlags | (checkable ? Qt::ItemIsUserCheckable : Qt::NoItemFlags);
  535. return indexFlags;
  536. }
  537. //------------------------------------------------------------------------------
  538. bool ctkDICOMModel::hasChildren ( const QModelIndex & parentIndex ) const
  539. {
  540. Q_D(const ctkDICOMModel);
  541. // only items in the first columns have index, shortcut the following for
  542. // speed issues.
  543. if (parentIndex.column() > 0)
  544. {
  545. return false;
  546. }
  547. Node* node = d->nodeFromIndex(parentIndex);
  548. if (!node)
  549. {
  550. return false;
  551. }
  552. // We want to show only until EndLevel
  553. if(node->Type >= d->EndLevel)return false;
  554. // It's not because we don't have row that we don't have children, maybe it
  555. // just means that the children haven't been fetched yet
  556. if (node->RowCount == 0 && !node->AtEnd)
  557. {
  558. // We don't want to fetch the data because we don't want to add children
  559. // to the index yet (it would be a mess to add rows inside a hasChildren)
  560. //const_cast<qCTKDCMTKModelPrivate*>(d)->fetch(parentIndex, 1);
  561. bool res = node->Query.seek(0);
  562. if (!res)
  563. {
  564. // now we know there is no children to the node, don't try next time.
  565. node->AtEnd = true;
  566. }
  567. return res;
  568. }
  569. return node->RowCount > 0;
  570. }
  571. //------------------------------------------------------------------------------
  572. QVariant ctkDICOMModel::headerData(int section, Qt::Orientation orientation, int role)const
  573. {
  574. Q_D(const ctkDICOMModel);
  575. if (orientation == Qt::Vertical)
  576. {
  577. if (role != Qt::DisplayRole)
  578. {
  579. return QVariant();
  580. }
  581. return section;
  582. }
  583. if (section < 0 || section >= d->Headers.size())
  584. {
  585. return QVariant();
  586. }
  587. return d->Headers[section][role];
  588. }
  589. //------------------------------------------------------------------------------
  590. QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parentIndex ) const
  591. {
  592. Q_D(const ctkDICOMModel);
  593. // only the first column has children
  594. if (d->RootNode == 0 || parentIndex.column() > 0)
  595. {
  596. return QModelIndex();
  597. }
  598. Node* parentNode = d->nodeFromIndex(parentIndex);
  599. int field = 0;// always 0//parentNode->Query.record().indexOf("UID");
  600. QString uid = d->value(parentIndex, row, field).toString();
  601. Node* node = 0;
  602. foreach(Node* tmpNode, parentNode->Children)
  603. {
  604. if (tmpNode->UID == uid)
  605. {
  606. node = tmpNode;
  607. break;
  608. }
  609. }
  610. // TODO: Here it is assumed that ctkDICOMModel::index is called with valid
  611. // arguments, we should probably be a bit more careful.
  612. if (node == 0)
  613. {
  614. node = d->createNode(row, parentIndex);
  615. }
  616. return this->createIndex(row, column, node);
  617. }
  618. //------------------------------------------------------------------------------
  619. QModelIndex ctkDICOMModel::parent ( const QModelIndex & indexValue ) const
  620. {
  621. Q_D(const ctkDICOMModel);
  622. if (!indexValue.isValid())
  623. {
  624. return QModelIndex();
  625. }
  626. Node* node = d->nodeFromIndex(indexValue);
  627. Q_ASSERT(node);
  628. Node* parentNode = node->Parent;
  629. if (parentNode == 0)
  630. {// node is root
  631. return QModelIndex();
  632. }
  633. return parentNode == d->RootNode ? QModelIndex() : this->createIndex(parentNode->Row, 0, parentNode);
  634. /* need to recalculate the parent row
  635. Node* greatParentNode = parentNode->Parent;
  636. if (greatParentNode == 0)
  637. {
  638. return QModelIndex();
  639. }
  640. int field = greatParentNode->Query.record().indexOf("UID");
  641. int row = -1;
  642. for (row = 0; row < greatParentNode->RowCount; ++row)
  643. {
  644. QString uid = d->value(greatParentNode, row, field).toString();
  645. if (uid == parentNode->UID)
  646. {
  647. break;
  648. }
  649. }
  650. Q_ASSERT(row < greatParentNode->RowCount);
  651. return this->createIndex(row, 0, parentNode);
  652. */
  653. }
  654. //------------------------------------------------------------------------------
  655. int ctkDICOMModel::rowCount ( const QModelIndex & parentValue ) const
  656. {
  657. Q_D(const ctkDICOMModel);
  658. if (d->RootNode == 0 || parentValue.column() > 0)
  659. {
  660. return 0;
  661. }
  662. Node* node = d->nodeFromIndex(parentValue);
  663. Q_ASSERT(node);
  664. // Returns the amount of rows currently cached on the client.
  665. return node ? node->RowCount : 0;
  666. }
  667. //------------------------------------------------------------------------------
  668. bool ctkDICOMModel::setData(const QModelIndex &index, const QVariant &value, int role)
  669. {
  670. Q_D(const ctkDICOMModel);
  671. if (role != Qt::CheckStateRole)
  672. {
  673. return false;
  674. }
  675. Node* node = d->nodeFromIndex(index);
  676. if (!node || node->Data[role] == value)
  677. {
  678. return false;
  679. }
  680. node->Data[role] = value;
  681. emit dataChanged(index, index);
  682. for(int i=0; i<node->Children.count(); i++)
  683. {
  684. this->setChildData(index.child(i,0), value, role);
  685. }
  686. if(index.parent().isValid())
  687. {
  688. this->setParentData(index.parent(), value, role);
  689. }
  690. return true;
  691. }
  692. //------------------------------------------------------------------------------
  693. bool ctkDICOMModel::setChildData(const QModelIndex &index, const QVariant &value, int role)
  694. {
  695. Q_D(const ctkDICOMModel);
  696. if (role != Qt::CheckStateRole)
  697. {
  698. return false;
  699. }
  700. Node* node = d->nodeFromIndex(index);
  701. if (!node || node->Data[role] == value)
  702. {
  703. return false;
  704. }
  705. node->Data[role] = value;
  706. emit dataChanged(index, index);
  707. for(int i=0; i<node->Children.count(); i++)
  708. {
  709. this->setData(index.child(i,0), value, role);
  710. }
  711. return true;
  712. }
  713. //------------------------------------------------------------------------------
  714. bool ctkDICOMModel::setParentData(const QModelIndex &index, const QVariant &value, int role)
  715. {
  716. #ifdef CHECKABLE_COLUMNS
  717. Q_D(const ctkDICOMModel);
  718. #endif
  719. if(!index.isValid()){
  720. return false;
  721. }
  722. if (role != Qt::CheckStateRole)
  723. {
  724. return false;
  725. }
  726. else
  727. {
  728. #ifdef CHECKABLE_COLUMNS
  729. bool checkedExist = false;
  730. bool partiallyCheckedExist = false;
  731. bool uncheckedExist = false;
  732. for(int i=0; i<index.model()->rowCount(index); i++)
  733. {
  734. Node* childNode = d->nodeFromIndex(index.child(i,0));
  735. if(childNode->Data[Qt::CheckStateRole].toUInt() == Qt::Checked)
  736. {
  737. checkedExist = true;
  738. }
  739. else if(childNode->Data[Qt::CheckStateRole].toUInt() == Qt::PartiallyChecked)
  740. {
  741. partiallyCheckedExist = true;
  742. }
  743. else if(childNode->Data[Qt::CheckStateRole].toUInt() == Qt::Unchecked)
  744. {
  745. uncheckedExist = true;
  746. }
  747. }
  748. if(partiallyCheckedExist || (checkedExist && uncheckedExist))
  749. {
  750. node->Data[Qt::CheckStateRole].toUInt() = Qt::PartiallyChecked;
  751. }
  752. else if(checkedExist)
  753. {
  754. node->Data[Qt::CheckStateRole].toUInt() = Qt::Checked;
  755. }
  756. else if(uncheckedExist)
  757. {
  758. node->Data[Qt::CheckStateRole].toUInt() = Qt::Unchecked;
  759. }
  760. else
  761. {
  762. node->Data[Qt::CheckStateRole].toUInt() = Qt::Unchecked;
  763. }
  764. #endif
  765. emit dataChanged(index, index);
  766. this->setParentData(index.parent(), value, role);
  767. }
  768. return true;
  769. }
  770. //------------------------------------------------------------------------------
  771. void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
  772. {
  773. Q_D(ctkDICOMModel);
  774. this->beginResetModel();
  775. d->DataBase = db;
  776. delete d->RootNode;
  777. d->RootNode = 0;
  778. if (d->DataBase.tables().empty())
  779. {
  780. //Q_ASSERT(d->DataBase.isOpen());
  781. this->endResetModel();
  782. return;
  783. }
  784. d->RootNode = d->createNode(-1, QModelIndex());
  785. this->endResetModel();
  786. // TODO, use hasQuerySize everywhere, not only in setDataBase()
  787. bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
  788. if (hasQuerySize && d->RootNode->Query.size() > 0)
  789. {
  790. int newRowCount= d->RootNode->Query.size();
  791. beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
  792. d->RootNode->RowCount = newRowCount;
  793. d->RootNode->AtEnd = true;
  794. endInsertRows();
  795. }
  796. d->fetch(QModelIndex(), 256);
  797. }
  798. //------------------------------------------------------------------------------
  799. void ctkDICOMModel::setDatabase(const QSqlDatabase &db,const QMap<QString, QVariant>& parameters)
  800. {
  801. Q_D(ctkDICOMModel);
  802. this->beginResetModel();
  803. d->DataBase = db;
  804. d->SearchParameters = parameters;
  805. delete d->RootNode;
  806. d->RootNode = 0;
  807. if (d->DataBase.tables().empty())
  808. {
  809. //Q_ASSERT(d->DataBase.isOpen());
  810. this->endResetModel();
  811. return;
  812. }
  813. d->RootNode = d->createNode(-1, QModelIndex());
  814. this->endResetModel();
  815. // TODO, use hasQuerySize everywhere, not only in setDataBase()
  816. bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
  817. if (hasQuerySize && d->RootNode->Query.size() > 0)
  818. {
  819. int newRowCount= d->RootNode->Query.size();
  820. beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
  821. d->RootNode->RowCount = newRowCount;
  822. d->RootNode->AtEnd = true;
  823. endInsertRows();
  824. }
  825. d->fetch(QModelIndex(), 256);
  826. }
  827. //------------------------------------------------------------------------------
  828. ctkDICOMModel::IndexType ctkDICOMModel::endLevel()const
  829. {
  830. Q_D(const ctkDICOMModel);
  831. return d->EndLevel;
  832. }
  833. //------------------------------------------------------------------------------
  834. void ctkDICOMModel::setEndLevel(ctkDICOMModel::IndexType level)
  835. {
  836. Q_D(ctkDICOMModel);
  837. d->EndLevel = level;
  838. }
  839. //------------------------------------------------------------------------------
  840. void ctkDICOMModel::reset()
  841. {
  842. Q_D(ctkDICOMModel);
  843. // this could probably be done in a more elegant way
  844. this->setDatabase(d->DataBase);
  845. }
  846. //------------------------------------------------------------------------------
  847. void ctkDICOMModel::sort(int column, Qt::SortOrder order)
  848. {
  849. Q_D(ctkDICOMModel);
  850. /* The following would work if there is no fetch involved.
  851. ORDER BY doesn't just apply on the fetched item. By sorting
  852. new items can show up in the model, and we need to be more
  853. careful
  854. emit layoutAboutToBeChanged();
  855. QModelIndexList oldIndexList = d->modelIndexList();
  856. d->Sort = QString("\"%1\" %2")
  857. .arg(d->Headers[column])
  858. .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
  859. d->updateQueries(d->RootNode);
  860. QModelIndexList newIndexList = d->modelIndexList();
  861. Q_ASSERT(oldIndexList.count() == newIndexList.count());
  862. this->changePersistentIndexList(oldIndexList, newIndexList);
  863. emit layoutChanged();
  864. */
  865. this->beginResetModel();
  866. delete d->RootNode;
  867. d->RootNode = 0;
  868. d->Sort = QString("\"%1\" %2")
  869. .arg(d->Headers[column][Qt::DisplayRole].toString())
  870. .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
  871. d->RootNode = d->createNode(-1, QModelIndex());
  872. this->endResetModel();
  873. }
  874. //------------------------------------------------------------------------------
  875. bool ctkDICOMModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role)
  876. {
  877. Q_D(ctkDICOMModel);
  878. if (orientation == Qt::Vertical)
  879. {
  880. return false;
  881. }
  882. if (section < 0 || section >= d->Headers.size() ||
  883. d->Headers[section][role] == value)
  884. {
  885. return false;
  886. }
  887. d->Headers[section][role] = value;
  888. emit this->headerDataChanged(orientation, section, section);
  889. return true;
  890. }