redmineitemtree.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. mephi-tasks — a client to NRNU MEPhI Redmine server
  3. Copyright (C) 2015 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "redmineitemtree.h"
  16. RedmineItemTree::RedmineItemTree()
  17. {
  18. this->displayRetryTimer.setSingleShot ( true );
  19. connect ( &this->displayRetryTimer, SIGNAL ( timeout() ), this, SLOT ( display_retry() ) );
  20. this->filterRetryTimer.setSingleShot ( true );
  21. connect ( &this->filterRetryTimer, SIGNAL ( timeout() ), this, SLOT ( filter_retry() ) );
  22. }
  23. void RedmineItemTree::clear()
  24. {
  25. this->real.clear();
  26. }
  27. void RedmineItemTree::add ( QJsonObject jsonObj )
  28. {
  29. this->real.add ( jsonObj );
  30. }
  31. QList<QJsonObject> RedmineItemTree::get()
  32. {
  33. return this->real.get();
  34. }
  35. QJsonObject RedmineItemTree::get ( int item_id )
  36. {
  37. return this->real.get ( item_id );
  38. }
  39. QJsonObject RedmineItemTree::get ( QTreeWidgetItem *widgetItem )
  40. {
  41. return this->widgetItem2item[widgetItem];
  42. }
  43. bool RedmineItemTree::isDescendant ( int descendant_id, int ancestor_id )
  44. {
  45. QJsonObject descendant = this->get ( descendant_id );
  46. int parent_id = 0;
  47. //qDebug("RedmineItemTree::isDescendant(%i, %i)", descendant_id, ancestor_id);
  48. while ( true ) {
  49. parent_id = descendant["parent"].toObject() ["id"].toInt();
  50. if ( parent_id == 0 )
  51. break;
  52. //qDebug("%i <> %i", parent_id, ancestor_id);
  53. if ( parent_id == ancestor_id )
  54. return true;
  55. descendant = this->get ( parent_id );
  56. };
  57. return false;
  58. }
  59. void RedmineItemTree::set ( QJsonArray array )
  60. {
  61. /* refilling this->list */
  62. this->clear();
  63. foreach ( const QJsonValue & val, array )
  64. this->add ( val.toObject() );
  65. /* rebuilding this->real.hierarchy */
  66. QList<QJsonObject> item_list;
  67. item_list = this->get();
  68. foreach ( const QJsonObject & item, item_list ) {
  69. int parent_id;
  70. if ( item.contains ( "parent" ) )
  71. parent_id = item["parent"].toObject() ["id"].toInt();
  72. else
  73. parent_id = 0;
  74. this->real.hierarchy[parent_id].append ( item );
  75. }
  76. return;
  77. }
  78. void RedmineItemTree::filter ( QWidget *initiator, itemFilterFunct_t filterFunct )
  79. {
  80. if ( !this->displayMutex.tryLock() ) {
  81. if ( !this->displayExceptionMutex.tryLock() )
  82. return;
  83. this->filterRetryArgs.initiator = initiator;
  84. this->filterRetryArgs.filterFunct = filterFunct;
  85. this->filterRetryTimer.start ( 70 );
  86. this->displayExceptionMutex.unlock();
  87. return;
  88. }
  89. QList<QJsonObject> item_list = this->real.get();
  90. QHash<int, bool> filtered_hash;
  91. this->filtered_old = this->filtered;
  92. this->filtered.clear();
  93. foreach ( const QJsonObject & item, item_list )
  94. if ( filterFunct ( initiator, item ) ) {
  95. this->filtered.add ( item );
  96. filtered_hash.insert ( item["id"].toInt(), true );
  97. }
  98. QList<QJsonObject> filtered_list = this->filtered.get();
  99. foreach ( const QJsonObject & item, filtered_list ) {
  100. int parent_id;
  101. if ( item.contains ( "parent" ) )
  102. parent_id = item["parent"].toObject() ["id"].toInt();
  103. else
  104. parent_id = 0;
  105. while ( !filtered_hash.contains ( parent_id ) ) {
  106. QJsonObject parent = this->real.get ( parent_id ) ["parent"].toObject();
  107. if ( parent.contains ( "id" ) )
  108. parent_id = parent["id"].toInt();
  109. else
  110. parent_id = 0;
  111. if ( parent_id == 0 )
  112. break;
  113. }
  114. this->filtered.parent.insert ( item["id"].toInt(), parent_id );
  115. this->filtered.hierarchy[parent_id].append ( item );
  116. }
  117. this->displayMutex.unlock();
  118. return;
  119. }
  120. void RedmineItemTree::filter_retry()
  121. {
  122. this->filter ( this->filterRetryArgs.initiator,
  123. this->filterRetryArgs.filterFunct );
  124. return;
  125. }
  126. void RedmineItemTree::widgetItemReset ( int item_id )
  127. {
  128. if ( !this->id2widgetItem.contains ( item_id ) )
  129. return;
  130. QTreeWidgetItem *widgetItem = this->id2widgetItem[item_id];
  131. this->id2widgetItem.remove ( item_id );
  132. this->widgetItem2item.remove ( widgetItem );
  133. //this->filtered.removechild(item_id);
  134. return;
  135. }
  136. void RedmineItemTree::widgetItemResetRecursive ( int item_id )
  137. {
  138. QList<QJsonObject> children = this->real.getchildren ( item_id );
  139. if ( !this->id2widgetItem.contains ( item_id ) )
  140. return;
  141. foreach ( const QJsonObject & child, children ) {
  142. int child_id = child["id"].toInt();
  143. this->widgetItemResetRecursive ( child_id );
  144. this->widgetItemReset ( child_id );
  145. }
  146. QTreeWidgetItem *widgetItem = this->id2widgetItem[item_id];
  147. this->widgetItemReset ( item_id );
  148. delete widgetItem;
  149. return;
  150. }
  151. void RedmineItemTree::widgetItemsResetIfUpdated ( int item_id, QJsonObject item )
  152. {
  153. /*
  154. * Remove the project from list if it had been updated
  155. */
  156. QJsonObject item_old = this->get ( item_id );
  157. int parent_id = this->filtered.parent[item_id];
  158. int parent_old_id = this->filtered_old.parent[item_id];
  159. if ( item_old["id"].toInt() > 0 ) // If exists
  160. if ( ( item_old != item ) || ( parent_id != parent_old_id ) ) // If changed
  161. this->widgetItemResetRecursive ( item_id );
  162. return;
  163. }
  164. void RedmineItemTree::display_recursive ( QTreeWidgetItem *widgetItem, QWidget *initiator, widgetItemSetTextFunct_t setTextFunct, QJsonObject item, int level, QHash<int, int> &toremove_ids )
  165. {
  166. int item_id = item["id"].toInt();
  167. toremove_ids.remove ( item_id );
  168. setTextFunct ( initiator, widgetItem, item, this, level );
  169. foreach ( const QJsonObject & child, this->filtered.getchildren ( item_id ) ) {
  170. this->display_child ( widgetItem, initiator, setTextFunct, child, level + 1, toremove_ids );
  171. }
  172. }
  173. void RedmineItemTree::display_child ( QTreeWidgetItem *parent, QWidget *initiator, widgetItemSetTextFunct_t setTextFunct, QJsonObject child, int level, QHash<int, int> &toremove_ids )
  174. {
  175. int item_id = child["id"].toInt();
  176. QTreeWidgetItem *widgetItem;
  177. this->widgetItemsResetIfUpdated ( item_id, child );
  178. if ( this->id2widgetItem.contains ( item_id ) )
  179. widgetItem = this->id2widgetItem[item_id];
  180. else {
  181. widgetItem = new QTreeWidgetItem ( parent );
  182. this->id2widgetItem.insert ( item_id, widgetItem );
  183. this->widgetItem2item.insert ( widgetItem, child );
  184. }
  185. this->display_recursive ( widgetItem, initiator, setTextFunct, child, level, toremove_ids );
  186. }
  187. void RedmineItemTree::display_topOne ( QTreeWidget *widget, QWidget *initiator, widgetItemSetTextFunct_t setTextFunct, int pos, QHash<int, int> &toremove_ids )
  188. {
  189. QJsonObject item = this->row2item[pos];
  190. int item_id = item["id"].toInt();
  191. QTreeWidgetItem *widgetItem;
  192. this->widgetItemsResetIfUpdated ( item_id, item );
  193. if ( this->id2widgetItem.contains ( item_id ) )
  194. widgetItem = this->id2widgetItem[item_id];
  195. else {
  196. widgetItem = new QTreeWidgetItem ( widget );
  197. this->id2widgetItem.insert ( item_id, widgetItem );
  198. this->widgetItem2item.insert ( widgetItem, item );
  199. }
  200. this->display_recursive ( widgetItem, initiator, setTextFunct, item, 0, toremove_ids );
  201. }
  202. void RedmineItemTree::display ( QTreeWidget *widget, QWidget *initiator, widgetItemSetTextFunct_t setTextFunct )
  203. {
  204. //qDebug("RedmineItemTree::display()");
  205. if ( !this->displayMutex.tryLock() ) {
  206. if ( !this->displayExceptionMutex.tryLock() )
  207. return;
  208. this->displayRetryArgs.widget = widget;
  209. this->displayRetryArgs.initiator = initiator;
  210. this->displayRetryArgs.setTextFunct = setTextFunct;
  211. this->displayRetryTimer.start ( 100 );
  212. this->displayExceptionMutex.unlock();
  213. return;
  214. }
  215. /*\
  216. * Preparing
  217. \*/
  218. widget->setSortingEnabled ( false );
  219. /*\
  220. * Building a table of items' id
  221. \*/
  222. QHash <int, int> toremove_ids;
  223. foreach ( const QJsonObject & item, this->widgetItem2item )
  224. toremove_ids.insert ( item["id"].toInt(), item["id"].toInt() );
  225. /*\
  226. * Displaying new items
  227. \*/
  228. this->row2item.clear();
  229. int topitems_count = 0;
  230. foreach ( const QJsonObject & item, this->filtered.getchildren ( 0 ) ) {
  231. this->row2item.insert ( topitems_count, item );
  232. this->display_topOne ( widget, initiator, setTextFunct, topitems_count, toremove_ids );
  233. topitems_count++;
  234. }
  235. /*\
  236. * Removing stale items
  237. \*/
  238. foreach ( const int &item_id, toremove_ids )
  239. this->widgetItemResetRecursive ( item_id );
  240. /*\
  241. * Redisplaying lost items (due to ascentor removing)
  242. \*/
  243. topitems_count = 0;
  244. foreach ( const QJsonObject & item, this->filtered.getchildren ( 0 ) )
  245. this->display_topOne ( widget, initiator, setTextFunct, topitems_count++, toremove_ids );
  246. /*\
  247. * Finishing
  248. \*/
  249. widget->setSortingEnabled ( true );
  250. this->displayMutex.unlock();
  251. return;
  252. }
  253. void RedmineItemTree::display ( QComboBox *widget, QWidget *initiator, comboBoxAddItemFunct_t addItemFunct )
  254. {
  255. ( void ) initiator;
  256. qDebug ( "RedmineItemTree::display()" );
  257. this->displayMutex.lock();
  258. /*\
  259. * Building a table of items' id
  260. \*/
  261. QHash <int, int> toremove_ids;
  262. foreach ( const QJsonObject & item, this->widgetItem2item )
  263. toremove_ids.insert ( item["id"].toInt(), item["id"].toInt() );
  264. /*\
  265. * Displaying new items
  266. \*/
  267. int itemIdx = 0;
  268. this->row2item.clear();
  269. foreach ( const QJsonObject & item, this->filtered.get() ) {
  270. this->row2item.insert ( itemIdx++, item );
  271. addItemFunct ( widget, item );
  272. // display item
  273. //this->display_topOne(widget, initiator, setTextFunct, topitems_count, toremove_ids);
  274. }
  275. /*\
  276. * Removing stale items
  277. \*/
  278. foreach ( const int &item_id, toremove_ids )
  279. this->widgetItemResetRecursive ( item_id );
  280. /*\
  281. * Finishing
  282. \*/
  283. this->displayMutex.unlock();
  284. return;
  285. }
  286. void RedmineItemTree::display_retry()
  287. {
  288. this->display ( this->displayRetryArgs.widget,
  289. this->displayRetryArgs.initiator,
  290. this->displayRetryArgs.setTextFunct );
  291. return;
  292. }