blocks_tree_widget.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. /************************************************************************
  2. * file name : blocks_tree_widget.cpp
  3. * ----------------- :
  4. * creation time : 2016/06/26
  5. * author : Victor Zarubkin
  6. * email : [email protected]
  7. * ----------------- :
  8. * description : The file contains implementation of EasyTreeWidget and it's auxiliary classes
  9. * : for displyaing easy_profiler blocks tree.
  10. * ----------------- :
  11. * change log : * 2016/06/26 Victor Zarubkin: Moved sources from tree_view.h
  12. * : and renamed classes from My* to Prof*.
  13. * :
  14. * : * 2016/06/27 Victor Zarubkin: Added possibility to colorize rows
  15. * : with profiler blocks' colors.
  16. * : Also added displaying frame statistics for blocks.
  17. * : Disabled sorting by name to save order of threads displayed on graphics view.
  18. * :
  19. * : * 2016/06/29 Victor Zarubkin: Added clearSilent() method.
  20. * :
  21. * : * 2016/08/18 Victor Zarubkin: Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp
  22. * ----------------- :
  23. * license : Lightweight profiler library for c++
  24. * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
  25. * :
  26. * : Licensed under either of
  27. * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
  28. * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  29. * : at your option.
  30. * :
  31. * : The MIT License
  32. * :
  33. * : Permission is hereby granted, free of charge, to any person obtaining a copy
  34. * : of this software and associated documentation files (the "Software"), to deal
  35. * : in the Software without restriction, including without limitation the rights
  36. * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  37. * : of the Software, and to permit persons to whom the Software is furnished
  38. * : to do so, subject to the following conditions:
  39. * :
  40. * : The above copyright notice and this permission notice shall be included in all
  41. * : copies or substantial portions of the Software.
  42. * :
  43. * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  44. * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  45. * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  46. * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  47. * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  48. * : USE OR OTHER DEALINGS IN THE SOFTWARE.
  49. * :
  50. * : The Apache License, Version 2.0 (the "License")
  51. * :
  52. * : You may not use this file except in compliance with the License.
  53. * : You may obtain a copy of the License at
  54. * :
  55. * : http://www.apache.org/licenses/LICENSE-2.0
  56. * :
  57. * : Unless required by applicable law or agreed to in writing, software
  58. * : distributed under the License is distributed on an "AS IS" BASIS,
  59. * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  60. * : See the License for the specific language governing permissions and
  61. * : limitations under the License.
  62. ************************************************************************/
  63. #include <QMenu>
  64. #include <QAction>
  65. #include <QHeaderView>
  66. #include <QContextMenuEvent>
  67. #include <QSignalBlocker>
  68. #include <QSettings>
  69. #include <QProgressDialog>
  70. #include <QResizeEvent>
  71. #include <QMoveEvent>
  72. #include <QLineEdit>
  73. #include <QLabel>
  74. #include <QToolBar>
  75. #include <QHBoxLayout>
  76. #include <QVBoxLayout>
  77. #include <QByteArray>
  78. #include <QDebug>
  79. #include "blocks_tree_widget.h"
  80. #include "globals.h"
  81. #ifdef _WIN32
  82. #include <Windows.h>
  83. #ifdef __MINGW32__
  84. #include <processthreadsapi.h>
  85. #endif
  86. #endif
  87. #ifdef max
  88. #undef max
  89. #endif
  90. #ifdef min
  91. #undef min
  92. #endif
  93. //////////////////////////////////////////////////////////////////////////
  94. const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40;
  95. const bool SIMPLIFIED_REGIME_COLUMNS[COL_COLUMNS_NUMBER] = {
  96. true, //COL_NAME,
  97. true, //COL_BEGIN,
  98. true, //COL_DURATION,
  99. true, //COL_SELF_DURATION,
  100. false, //COL_DURATION_SUM_PER_PARENT,
  101. false, //COL_DURATION_SUM_PER_FRAME,
  102. true, //COL_DURATION_SUM_PER_THREAD,
  103. true, //COL_SELF_DURATION_PERCENT,
  104. false, //COL_PERCENT_PER_PARENT,
  105. true, //COL_PERCENT_PER_FRAME,
  106. false, //COL_PERCENT_SUM_PER_PARENT,
  107. false, //COL_PERCENT_SUM_PER_FRAME,
  108. true, //COL_PERCENT_SUM_PER_THREAD,
  109. true, //COL_END,
  110. true, //COL_MIN_PER_FRAME,
  111. true, //COL_MAX_PER_FRAME,
  112. true, //COL_AVERAGE_PER_FRAME,
  113. true, //COL_NCALLS_PER_FRAME,
  114. true, //COL_MIN_PER_THREAD,
  115. true, //COL_MAX_PER_THREAD,
  116. true, //COL_AVERAGE_PER_THREAD,
  117. true, //COL_NCALLS_PER_THREAD,
  118. false, //COL_MIN_PER_PARENT,
  119. false, //COL_MAX_PER_PARENT,
  120. false, //COL_AVERAGE_PER_PARENT,
  121. false, //COL_NCALLS_PER_PARENT,
  122. true, //COL_ACTIVE_TIME,
  123. true //COL_ACTIVE_PERCENT,
  124. };
  125. //////////////////////////////////////////////////////////////////////////
  126. EasyTreeWidget::EasyTreeWidget(QWidget* _parent)
  127. : Parent(_parent)
  128. , m_beginTime(::std::numeric_limits<decltype(m_beginTime)>::max())
  129. , m_lastFound(nullptr)
  130. , m_progress(nullptr)
  131. , m_hintLabel(nullptr)
  132. , m_mode(EasyTreeMode_Plain)
  133. , m_bColorRows(true)
  134. , m_bLocked(false)
  135. , m_bSilentExpandCollapse(false)
  136. {
  137. memset(m_columnsHiddenStatus, 0, sizeof(m_columnsHiddenStatus));
  138. setAutoFillBackground(false);
  139. setAlternatingRowColors(true);
  140. setItemsExpandable(true);
  141. setAnimated(true);
  142. setSortingEnabled(false);
  143. setColumnCount(COL_COLUMNS_NUMBER);
  144. auto header_item = new QTreeWidgetItem();
  145. auto f = header()->font();
  146. f.setBold(true);
  147. header()->setFont(f);// ::profiler_gui::EFont("Helvetica", 9, QFont::Bold));
  148. header_item->setText(COL_NAME, "Name");
  149. header_item->setText(COL_BEGIN, "Begin, ms");
  150. header_item->setText(COL_DURATION, "Duration");
  151. header_item->setText(COL_SELF_DURATION, "Self dur.");
  152. //header_item->setToolTip(COL_SELF_DURATION, "");
  153. header_item->setText(COL_DURATION_SUM_PER_PARENT, "Total / Parent");
  154. header_item->setText(COL_DURATION_SUM_PER_FRAME, "Total / Frame");
  155. header_item->setText(COL_DURATION_SUM_PER_THREAD, "Total / Thread");
  156. header_item->setText(COL_SELF_DURATION_PERCENT, "Self %");
  157. header_item->setText(COL_PERCENT_PER_PARENT, "% / Parent");
  158. header_item->setText(COL_PERCENT_PER_FRAME, "% / Frame");
  159. header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Sum % / Frame");
  160. header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Sum % / Parent");
  161. header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Sum % / Thread");
  162. header_item->setText(COL_END, "End, ms");
  163. header_item->setText(COL_MIN_PER_FRAME, "Min / Frame");
  164. header_item->setText(COL_MAX_PER_FRAME, "Max / Frame");
  165. header_item->setText(COL_AVERAGE_PER_FRAME, "Avg / Frame");
  166. header_item->setText(COL_NCALLS_PER_FRAME, "N Calls / Frame");
  167. header_item->setText(COL_MIN_PER_PARENT, "Min / Parent");
  168. header_item->setText(COL_MAX_PER_PARENT, "Max / Parent");
  169. header_item->setText(COL_AVERAGE_PER_PARENT, "Avg / Parent");
  170. header_item->setText(COL_NCALLS_PER_PARENT, "N Calls / Parent");
  171. header_item->setText(COL_MIN_PER_THREAD, "Min / Thread");
  172. header_item->setText(COL_MAX_PER_THREAD, "Max / Thread");
  173. header_item->setText(COL_AVERAGE_PER_THREAD, "Avg / Thread");
  174. header_item->setText(COL_NCALLS_PER_THREAD, "N Calls / Thread");
  175. header_item->setText(COL_ACTIVE_TIME, "Active time");
  176. header_item->setText(COL_ACTIVE_PERCENT, "Active %");
  177. auto color = QColor::fromRgb(::profiler::colors::DeepOrange900);
  178. header_item->setForeground(COL_MIN_PER_THREAD, color);
  179. header_item->setForeground(COL_MAX_PER_THREAD, color);
  180. header_item->setForeground(COL_AVERAGE_PER_THREAD, color);
  181. header_item->setForeground(COL_NCALLS_PER_THREAD, color);
  182. header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color);
  183. header_item->setForeground(COL_DURATION_SUM_PER_THREAD, color);
  184. color = QColor::fromRgb(::profiler::colors::Blue900);
  185. header_item->setForeground(COL_MIN_PER_FRAME, color);
  186. header_item->setForeground(COL_MAX_PER_FRAME, color);
  187. header_item->setForeground(COL_AVERAGE_PER_FRAME, color);
  188. header_item->setForeground(COL_NCALLS_PER_FRAME, color);
  189. header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color);
  190. header_item->setForeground(COL_DURATION_SUM_PER_FRAME, color);
  191. header_item->setForeground(COL_PERCENT_PER_FRAME, color);
  192. color = QColor::fromRgb(::profiler::colors::Teal900);
  193. header_item->setForeground(COL_MIN_PER_PARENT, color);
  194. header_item->setForeground(COL_MAX_PER_PARENT, color);
  195. header_item->setForeground(COL_AVERAGE_PER_PARENT, color);
  196. header_item->setForeground(COL_NCALLS_PER_PARENT, color);
  197. header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color);
  198. header_item->setForeground(COL_DURATION_SUM_PER_PARENT, color);
  199. header_item->setForeground(COL_PERCENT_PER_PARENT, color);
  200. setHeaderItem(header_item);
  201. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
  202. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
  203. connect(&m_fillTimer, &QTimer::timeout, this, &This::onFillTimerTimeout);
  204. loadSettings();
  205. if (m_mode == EasyTreeMode_Full)
  206. {
  207. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  208. m_columnsHiddenStatus[i] = isColumnHidden(i) ? 1 : 0;
  209. }
  210. else
  211. {
  212. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  213. {
  214. if (SIMPLIFIED_REGIME_COLUMNS[i])
  215. {
  216. if (isColumnHidden(i))
  217. m_columnsHiddenStatus[i] = 1;
  218. }
  219. else if (!isColumnHidden(i))
  220. {
  221. setColumnHidden(i, true);
  222. }
  223. }
  224. }
  225. m_progress = new QProgressDialog("Building blocks hierarchy...", "", 0, 100, this, Qt::FramelessWindowHint);
  226. m_progress->setAttribute(Qt::WA_TranslucentBackground);
  227. m_progress->setCancelButton(nullptr);
  228. m_progress->setValue(100);
  229. //m_progress->hide();
  230. m_hintLabel = new QLabel("Use Right Mouse Button on the Diagram to build a hierarchy...\nPress and hold, move, release", this);
  231. m_hintLabel->setAlignment(Qt::AlignCenter);
  232. m_hintLabel->setStyleSheet("QLabel { color: gray; font: 12pt; }");
  233. QTimer::singleShot(1500, this, &This::alignProgressBar);
  234. }
  235. EasyTreeWidget::~EasyTreeWidget()
  236. {
  237. saveSettings();
  238. delete m_progress;
  239. delete m_hintLabel;
  240. }
  241. //////////////////////////////////////////////////////////////////////////
  242. void EasyTreeWidget::onFillTimerTimeout()
  243. {
  244. if (m_hierarchyBuilder.done())
  245. {
  246. m_fillTimer.stop();
  247. ThreadedItems toplevelitems;
  248. m_hierarchyBuilder.takeItems(m_items);
  249. m_hierarchyBuilder.takeTopLevelItems(toplevelitems);
  250. m_hierarchyBuilder.interrupt();
  251. {
  252. const QSignalBlocker b(this);
  253. for (auto& item : toplevelitems)
  254. {
  255. addTopLevelItem(item.second);
  256. m_roots[item.first] = item.second;
  257. }
  258. }
  259. if (m_progress)
  260. {
  261. m_progress->setValue(100);
  262. //m_progress->hide();
  263. }
  264. m_bLocked = false;
  265. m_inputBlocks.clear();
  266. setSortingEnabled(true);
  267. sortByColumn(COL_BEGIN, Qt::AscendingOrder); // sort by begin time
  268. if (m_mode == EasyTreeMode_Plain) // and after that, sort by frame %
  269. sortByColumn(COL_PERCENT_PER_FRAME, Qt::DescendingOrder);
  270. //resizeColumnToContents(COL_NAME);
  271. resizeColumnsToContents();
  272. connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
  273. connect(this, &Parent::itemCollapsed, this, &This::onItemCollapse);
  274. connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
  275. onSelectedThreadChange(EASY_GLOBALS.selected_thread);
  276. onSelectedBlockChange(EASY_GLOBALS.selected_block);
  277. }
  278. else
  279. {
  280. m_progress->setValue(m_hierarchyBuilder.progress());
  281. }
  282. }
  283. void EasyTreeWidget::setTree(const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree)
  284. {
  285. clearSilent();
  286. if (!_blocksTree.empty())
  287. {
  288. m_bLocked = true;
  289. m_hintLabel->hide();
  290. m_progress->setValue(0);
  291. m_progress->show();
  292. m_hierarchyBuilder.fillTree(m_beginTime, _blocksNumber, _blocksTree, m_bColorRows, m_mode);
  293. m_fillTimer.start(HIERARCHY_BUILDER_TIMER_INTERVAL);
  294. }
  295. //StubLocker l;
  296. //ThreadedItems toplevelitems;
  297. //FillTreeClass<StubLocker>::setTreeInternal1(l, m_items, toplevelitems, m_beginTime, _blocksNumber, _blocksTree, m_bColorRows);
  298. //{
  299. // const QSignalBlocker b(this);
  300. // for (auto& item : toplevelitems)
  301. // {
  302. // addTopLevelItem(item.second);
  303. // m_roots[item.first] = item.second;
  304. // if (item.first == EASY_GLOBALS.selected_thread)
  305. // item.second->colorize(true);
  306. // }
  307. //}
  308. }
  309. void EasyTreeWidget::setTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict)
  310. {
  311. clearSilent();
  312. m_beginTime = _session_begin_time;
  313. _left += m_beginTime;// - ::std::min(m_beginTime, 1000ULL);
  314. _right += m_beginTime;// + 1000;
  315. m_inputBlocks = _blocks;
  316. if (!m_inputBlocks.empty())
  317. {
  318. m_bLocked = true;
  319. m_hintLabel->hide();
  320. m_progress->setValue(0);
  321. m_progress->show();
  322. m_hierarchyBuilder.fillTreeBlocks(m_inputBlocks, _session_begin_time, _left, _right, _strict, m_bColorRows, m_mode);
  323. m_fillTimer.start(HIERARCHY_BUILDER_TIMER_INTERVAL);
  324. }
  325. //StubLocker l;
  326. //ThreadedItems toplevelitems;
  327. //FillTreeClass<StubLocker>::setTreeInternal2(l, m_items, toplevelitems, m_beginTime, _blocks, _left, _right, _strict, m_bColorRows);
  328. //{
  329. // const QSignalBlocker b(this);
  330. // for (auto& item : toplevelitems)
  331. // {
  332. // addTopLevelItem(item.second);
  333. // m_roots[item.first] = item.second;
  334. // if (item.first == EASY_GLOBALS.selected_thread)
  335. // item.second->colorize(true);
  336. // }
  337. //}
  338. //setSortingEnabled(true);
  339. //sortByColumn(COL_BEGIN, Qt::AscendingOrder);
  340. //resizeColumnToContents(COL_NAME);
  341. //connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
  342. //connect(this, &Parent::itemCollapsed, this, &This::onItemCollapse);
  343. //onSelectedBlockChange(EASY_GLOBALS.selected_block);
  344. }
  345. //////////////////////////////////////////////////////////////////////////
  346. void EasyTreeWidget::clearSilent(bool _global)
  347. {
  348. const QSignalBlocker b(this);
  349. m_hierarchyBuilder.interrupt();
  350. if (m_progress)
  351. {
  352. m_progress->setValue(100);
  353. //m_progress->hide();
  354. }
  355. m_hintLabel->show();
  356. m_bLocked = false;
  357. m_beginTime = ::std::numeric_limits<decltype(m_beginTime)>::max();
  358. setSortingEnabled(false);
  359. disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand);
  360. disconnect(this, &Parent::itemCollapsed, this, &This::onItemCollapse);
  361. disconnect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
  362. m_lastFound = nullptr;
  363. m_lastSearch.clear();
  364. if (!_global)
  365. {
  366. if (EASY_GLOBALS.collapse_items_on_tree_close)
  367. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  368. for (auto item : m_items)
  369. #else
  370. for (auto& item : m_items)
  371. #endif
  372. {
  373. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  374. auto& gui_block = item->guiBlock();
  375. gui_block.expanded = false;
  376. ::profiler_gui::set_max(gui_block.tree_item);
  377. #else
  378. item.second->guiBlock().expanded = false;
  379. #endif
  380. }
  381. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  382. else for (auto item : m_items)
  383. {
  384. ::profiler_gui::set_max(item->guiBlock().tree_item);
  385. }
  386. #endif
  387. }
  388. m_items.clear();
  389. m_roots.clear();
  390. ::std::vector<QTreeWidgetItem*> topLevelItems;
  391. topLevelItems.reserve(topLevelItemCount());
  392. for (int i = topLevelItemCount() - 1; i >= 0; --i)
  393. topLevelItems.push_back(takeTopLevelItem(i));
  394. auto deleter_thread = ::std::thread([](decltype(topLevelItems) _items)
  395. {
  396. #ifdef _WIN32
  397. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
  398. #endif
  399. for (auto item : _items)
  400. delete item;
  401. }, ::std::move(topLevelItems));
  402. deleter_thread.detach();
  403. //clear();
  404. if (!_global)
  405. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  406. }
  407. //////////////////////////////////////////////////////////////////////////
  408. int EasyTreeWidget::findNext(const QString& _str, Qt::MatchFlags _flags)
  409. {
  410. if (m_bLocked || _str.isEmpty())
  411. return 0;
  412. const bool isNewSearch = (m_lastSearch != _str);
  413. auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME);
  414. if (!isNewSearch)
  415. {
  416. if (!itemsList.empty())
  417. {
  418. bool stop = false;
  419. decltype(m_lastFound) next = nullptr;
  420. for (auto item : itemsList)
  421. {
  422. if (item->parent() == nullptr)
  423. continue;
  424. if (stop)
  425. {
  426. next = item;
  427. break;
  428. }
  429. stop = item == m_lastFound;
  430. }
  431. m_lastFound = next == nullptr ? itemsList.front() : next;
  432. }
  433. else
  434. {
  435. m_lastFound = nullptr;
  436. }
  437. }
  438. else
  439. {
  440. m_lastSearch = _str;
  441. m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr;
  442. }
  443. if (m_lastFound != nullptr)
  444. {
  445. scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter);
  446. setCurrentItem(m_lastFound);
  447. }
  448. return itemsList.size();
  449. }
  450. int EasyTreeWidget::findPrev(const QString& _str, Qt::MatchFlags _flags)
  451. {
  452. if (m_bLocked || _str.isEmpty())
  453. return 0;
  454. const bool isNewSearch = (m_lastSearch != _str);
  455. auto itemsList = findItems(_str, Qt::MatchContains | Qt::MatchRecursive | _flags, COL_NAME);
  456. if (!isNewSearch)
  457. {
  458. if (!itemsList.empty())
  459. {
  460. decltype(m_lastFound) prev = nullptr;
  461. for (auto item : itemsList)
  462. {
  463. if (item->parent() == nullptr)
  464. continue;
  465. if (item == m_lastFound)
  466. break;
  467. prev = item;
  468. }
  469. m_lastFound = prev == nullptr ? itemsList.back() : prev;
  470. }
  471. else
  472. {
  473. m_lastFound = nullptr;
  474. }
  475. }
  476. else
  477. {
  478. m_lastSearch = _str;
  479. m_lastFound = !itemsList.empty() ? itemsList.front() : nullptr;
  480. }
  481. if (m_lastFound != nullptr)
  482. {
  483. scrollToItem(m_lastFound, QAbstractItemView::PositionAtCenter);
  484. setCurrentItem(m_lastFound);
  485. }
  486. return itemsList.size();
  487. }
  488. //////////////////////////////////////////////////////////////////////////
  489. void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
  490. {
  491. if (m_bLocked)
  492. {
  493. _event->accept();
  494. return;
  495. }
  496. const auto col = currentColumn();
  497. auto item = static_cast<EasyTreeWidgetItem*>(currentItem());
  498. QMenu menu;
  499. menu.setToolTipsVisible(true);
  500. QAction* action = nullptr;
  501. if (!m_items.empty())
  502. {
  503. action = menu.addAction("Expand all");
  504. connect(action, &QAction::triggered, this, &This::onExpandAllClicked);
  505. SET_ICON(action, ":/Expand");
  506. action = menu.addAction("Collapse all");
  507. connect(action, &QAction::triggered, this, &This::onCollapseAllClicked);
  508. SET_ICON(action, ":/Collapse");
  509. if (item != nullptr && col >= 0)
  510. {
  511. menu.addSeparator();
  512. action = menu.addAction("Expand all children");
  513. connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked);
  514. SET_ICON(action, ":/Expand");
  515. action = menu.addAction("Collapse all children");
  516. connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked);
  517. SET_ICON(action, ":/Collapse");
  518. }
  519. menu.addSeparator();
  520. }
  521. action = menu.addAction("Hierarchy mode");
  522. action->setToolTip("Display full blocks hierarchy");
  523. action->setCheckable(true);
  524. action->setChecked(m_mode == EasyTreeMode_Full);
  525. action->setData((quint32)EasyTreeMode_Full);
  526. connect(action, &QAction::triggered, this, &This::onModeChange);
  527. action = menu.addAction("Plain mode");
  528. action->setToolTip("Display plain list of blocks per frame.\nSome columns are disabled with this mode.");
  529. action->setCheckable(true);
  530. action->setChecked(m_mode == EasyTreeMode_Plain);
  531. action->setData((quint32)EasyTreeMode_Plain);
  532. connect(action, &QAction::triggered, this, &This::onModeChange);
  533. menu.addSeparator();
  534. action = menu.addAction("Color rows");
  535. action->setToolTip("Colorize rows with same colors as on diagram");
  536. action->setCheckable(true);
  537. action->setChecked(m_bColorRows);
  538. connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered);
  539. if (m_bColorRows) {
  540. auto f = action->font();
  541. f.setBold(true);
  542. action->setFont(f);
  543. SET_ICON(action, ":/Color");
  544. }
  545. else SET_ICON(action, ":/NoColor");
  546. if (item != nullptr && item->parent() != nullptr)
  547. {
  548. if (col >= 0)
  549. {
  550. switch (col)
  551. {
  552. case COL_MIN_PER_THREAD:
  553. case COL_MIN_PER_PARENT:
  554. case COL_MIN_PER_FRAME:
  555. case COL_MAX_PER_THREAD:
  556. case COL_MAX_PER_PARENT:
  557. case COL_MAX_PER_FRAME:
  558. {
  559. auto& block = item->block();
  560. auto i = ::profiler_gui::numeric_max<uint32_t>();
  561. switch (col)
  562. {
  563. case COL_MIN_PER_THREAD: i = block.per_thread_stats->min_duration_block; break;
  564. case COL_MIN_PER_PARENT: i = block.per_parent_stats->min_duration_block; break;
  565. case COL_MIN_PER_FRAME: i = block.per_frame_stats->min_duration_block; break;
  566. case COL_MAX_PER_THREAD: i = block.per_thread_stats->max_duration_block; break;
  567. case COL_MAX_PER_PARENT: i = block.per_parent_stats->max_duration_block; break;
  568. case COL_MAX_PER_FRAME: i = block.per_frame_stats->max_duration_block; break;
  569. }
  570. if (i != ::profiler_gui::numeric_max(i))
  571. {
  572. menu.addSeparator();
  573. auto itemAction = new QAction("Jump to such item", nullptr);
  574. itemAction->setData(i);
  575. itemAction->setToolTip("Jump to item with min/max duration (depending on clicked column)");
  576. connect(itemAction, &QAction::triggered, this, &This::onJumpToItemClicked);
  577. menu.addAction(itemAction);
  578. }
  579. break;
  580. }
  581. }
  582. }
  583. const auto& desc = easyDescriptor(item->block().node->id());
  584. auto submenu = menu.addMenu("Block status");
  585. submenu->setToolTipsVisible(true);
  586. #define ADD_STATUS_ACTION(NameValue, StatusValue, ToolTipValue)\
  587. action = submenu->addAction(NameValue);\
  588. action->setCheckable(true);\
  589. action->setChecked(desc.status() == StatusValue);\
  590. action->setData(static_cast<quint32>(StatusValue));\
  591. action->setToolTip(ToolTipValue);\
  592. connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked)
  593. ADD_STATUS_ACTION("Off", ::profiler::OFF, "Do not profile this block.");
  594. ADD_STATUS_ACTION("On", ::profiler::ON, "Profile this block\nif parent enabled children.");
  595. ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON, "Always profile this block even\nif it's parent disabled children.");
  596. ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE, "Do not profile neither this block\nnor it's children.");
  597. ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN, "Profile this block, but\ndo not profile it's children.");
  598. ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN, "Always profile this block, but\ndo not profile it's children.");
  599. #undef ADD_STATUS_ACTION
  600. submenu->setEnabled(EASY_GLOBALS.connected);
  601. if (!EASY_GLOBALS.connected)
  602. submenu->setTitle(QString("%1 (connection needed)").arg(submenu->title()));
  603. }
  604. menu.addSeparator();
  605. auto hidemenu = menu.addMenu("Select columns");
  606. auto hdr = headerItem();
  607. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  608. {
  609. auto columnAction = new QAction(hdr->text(i), nullptr);
  610. columnAction->setData(i);
  611. columnAction->setCheckable(true);
  612. columnAction->setChecked(m_columnsHiddenStatus[i] == 0);// !isColumnHidden(i));
  613. if (m_mode == EasyTreeMode_Full || SIMPLIFIED_REGIME_COLUMNS[i])
  614. connect(columnAction, &QAction::triggered, this, &This::onHideShowColumn);
  615. else
  616. columnAction->setEnabled(false);
  617. hidemenu->addAction(columnAction);
  618. }
  619. menu.exec(QCursor::pos());
  620. _event->accept();
  621. }
  622. //////////////////////////////////////////////////////////////////////////
  623. void EasyTreeWidget::resizeEvent(QResizeEvent* _event)
  624. {
  625. Parent::resizeEvent(_event);
  626. alignProgressBar();
  627. }
  628. void EasyTreeWidget::moveEvent(QMoveEvent* _event)
  629. {
  630. Parent::moveEvent(_event);
  631. alignProgressBar();
  632. }
  633. void EasyTreeWidget::alignProgressBar()
  634. {
  635. auto center = rect().center();
  636. auto pos = mapToGlobal(center);
  637. m_progress->move(pos.x() - (m_progress->width() >> 1), pos.y() - (m_progress->height() >> 1));
  638. m_hintLabel->move(center.x() - (m_hintLabel->width() >> 1), std::max(center.y() - (m_hintLabel->height() >> 1), header()->height()));
  639. }
  640. //////////////////////////////////////////////////////////////////////////
  641. void EasyTreeWidget::onJumpToItemClicked(bool)
  642. {
  643. auto action = qobject_cast<QAction*>(sender());
  644. if (action == nullptr)
  645. return;
  646. auto block_index = action->data().toUInt();
  647. EASY_GLOBALS.selected_block = block_index;
  648. if (block_index < EASY_GLOBALS.gui_blocks.size())
  649. EASY_GLOBALS.selected_block_id = easyBlock(block_index).tree.node->id();
  650. else
  651. ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
  652. emit EASY_GLOBALS.events.selectedBlockChanged(block_index);
  653. }
  654. void EasyTreeWidget::onCollapseAllClicked(bool)
  655. {
  656. const QSignalBlocker b(this);
  657. m_bSilentExpandCollapse = true;
  658. collapseAll();
  659. m_bSilentExpandCollapse = false;
  660. if (EASY_GLOBALS.bind_scene_and_tree_expand_status)
  661. {
  662. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  663. for (auto item : m_items)
  664. item->guiBlock().expanded = false;
  665. #else
  666. for (auto& item : m_items)
  667. item.second->guiBlock().expanded = false;
  668. #endif
  669. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  670. }
  671. }
  672. void EasyTreeWidget::onExpandAllClicked(bool)
  673. {
  674. const QSignalBlocker b(this);
  675. m_bSilentExpandCollapse = true;
  676. expandAll();
  677. resizeColumnsToContents();
  678. m_bSilentExpandCollapse = false;
  679. if (EASY_GLOBALS.bind_scene_and_tree_expand_status)
  680. {
  681. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  682. for (auto item : m_items){
  683. auto& b = item->guiBlock();
  684. #else
  685. for (auto& item : m_items){
  686. auto& b = item.second->guiBlock();
  687. #endif
  688. b.expanded = !b.tree.children.empty();
  689. }
  690. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  691. }
  692. }
  693. void EasyTreeWidget::onCollapseAllChildrenClicked(bool)
  694. {
  695. auto current = static_cast<EasyTreeWidgetItem*>(currentItem());
  696. if (current != nullptr)
  697. {
  698. const QSignalBlocker b(this);
  699. m_bSilentExpandCollapse = true;
  700. current->collapseAll();
  701. m_bSilentExpandCollapse = false;
  702. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  703. }
  704. }
  705. void EasyTreeWidget::onExpandAllChildrenClicked(bool)
  706. {
  707. auto current = static_cast<EasyTreeWidgetItem*>(currentItem());
  708. if (current != nullptr)
  709. {
  710. const QSignalBlocker b(this);
  711. m_bSilentExpandCollapse = true;
  712. current->expandAll();
  713. resizeColumnsToContents();
  714. m_bSilentExpandCollapse = false;
  715. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  716. }
  717. }
  718. //////////////////////////////////////////////////////////////////////////
  719. void EasyTreeWidget::onBlockStatusChangeClicked(bool _checked)
  720. {
  721. if (!_checked)
  722. return;
  723. auto item = static_cast<EasyTreeWidgetItem*>(currentItem());
  724. if (item == nullptr)
  725. return;
  726. auto action = qobject_cast<QAction*>(sender());
  727. if (action != nullptr)
  728. {
  729. auto& desc = easyDescriptor(item->block().node->id());
  730. desc.setStatus(static_cast<::profiler::EasyBlockStatus>(action->data().toUInt()));
  731. emit EASY_GLOBALS.events.blockStatusChanged(desc.id(), desc.status());
  732. }
  733. }
  734. //////////////////////////////////////////////////////////////////////////
  735. void EasyTreeWidget::onItemExpand(QTreeWidgetItem* _item)
  736. {
  737. if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr)
  738. {
  739. resizeColumnsToContents();
  740. return;
  741. }
  742. static_cast<EasyTreeWidgetItem*>(_item)->guiBlock().expanded = true;
  743. if (!m_bSilentExpandCollapse)
  744. {
  745. resizeColumnsToContents();
  746. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  747. }
  748. }
  749. void EasyTreeWidget::onItemCollapse(QTreeWidgetItem* _item)
  750. {
  751. if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr)
  752. return;
  753. static_cast<EasyTreeWidgetItem*>(_item)->guiBlock().expanded = false;
  754. if (!m_bSilentExpandCollapse)
  755. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  756. }
  757. //////////////////////////////////////////////////////////////////////////
  758. void EasyTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidgetItem* _previous)
  759. {
  760. if (_previous != nullptr)
  761. {
  762. auto f = font();
  763. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  764. _previous->setFont(i, f);
  765. }
  766. if (_item == nullptr)
  767. {
  768. ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
  769. ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
  770. }
  771. else
  772. {
  773. auto f = font();
  774. f.setBold(true);
  775. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  776. _item->setFont(i, f);
  777. EASY_GLOBALS.selected_block = static_cast<EasyTreeWidgetItem*>(_item)->block_index();
  778. if (EASY_GLOBALS.selected_block < EASY_GLOBALS.gui_blocks.size())
  779. EASY_GLOBALS.selected_block_id = easyBlock(EASY_GLOBALS.selected_block).tree.node->id();
  780. else
  781. ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
  782. }
  783. disconnect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
  784. emit EASY_GLOBALS.events.selectedBlockChanged(EASY_GLOBALS.selected_block);
  785. connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
  786. }
  787. //////////////////////////////////////////////////////////////////////////
  788. void EasyTreeWidget::onColorizeRowsTriggered(bool _colorize)
  789. {
  790. const QSignalBlocker b(this);
  791. m_bColorRows = _colorize;
  792. auto current = currentItem();
  793. collapseAll(); // Without collapseAll() changing items process is VERY VERY SLOW.
  794. // TODO: Find the reason of such behavior. QSignalBlocker(this) does not help. QSignalBlocker(item) does not work, because items are not inherited from QObject.
  795. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  796. for (auto item : m_items)
  797. {
  798. if (item->parent() != nullptr)
  799. item->colorize(m_bColorRows);
  800. }
  801. #else
  802. for (auto& item : m_items)
  803. {
  804. if (item.second->parent() != nullptr)
  805. item.second->colorize(m_bColorRows);
  806. }
  807. #endif
  808. // Scroll back to previously selected item
  809. if (current)
  810. {
  811. scrollToItem(current, QAbstractItemView::PositionAtCenter);
  812. setCurrentItem(current);
  813. }
  814. }
  815. //////////////////////////////////////////////////////////////////////////
  816. void EasyTreeWidget::onSelectedThreadChange(::profiler::thread_id_t _id)
  817. {
  818. for (auto& it : m_roots)
  819. {
  820. it.second->colorize(it.first == _id);
  821. }
  822. }
  823. void EasyTreeWidget::onSelectedBlockChange(uint32_t _block_index)
  824. {
  825. disconnect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
  826. EasyTreeWidgetItem* item = nullptr;
  827. if (_block_index < EASY_GLOBALS.gui_blocks.size())
  828. {
  829. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  830. const auto i = easyBlock(_block_index).tree_item;
  831. if (i < m_items.size())
  832. item = m_items[i];
  833. #else
  834. auto it = m_items.find(_block_index);
  835. if (it != m_items.end())
  836. item = it->second;
  837. #endif
  838. }
  839. if (item != nullptr)
  840. {
  841. //const QSignalBlocker b(this);
  842. auto previous = currentItem();
  843. auto f = font();
  844. if (previous != nullptr) for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  845. previous->setFont(i, f);
  846. if (EASY_GLOBALS.bind_scene_and_tree_expand_status)
  847. {
  848. m_bSilentExpandCollapse = true;
  849. setCurrentItem(item);
  850. scrollToItem(item, QAbstractItemView::PositionAtCenter);
  851. if (item->guiBlock().expanded)
  852. expandItem(item);
  853. else
  854. collapseItem(item);
  855. resizeColumnsToContents();
  856. m_bSilentExpandCollapse = false;
  857. emit EASY_GLOBALS.events.itemsExpandStateChanged();
  858. }
  859. else
  860. {
  861. disconnect(this, &Parent::itemExpanded, this, &This::onItemExpand);
  862. setCurrentItem(item);
  863. scrollToItem(item, QAbstractItemView::PositionAtCenter);
  864. resizeColumnsToContents();
  865. connect(this, &Parent::itemExpanded, this, &This::onItemExpand);
  866. }
  867. f.setBold(true);
  868. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  869. item->setFont(i, f);
  870. }
  871. else
  872. {
  873. auto previous = currentItem();
  874. if (previous != nullptr)
  875. {
  876. auto f = font();
  877. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  878. previous->setFont(i, f);
  879. }
  880. setCurrentItem(item);
  881. }
  882. connect(this, &Parent::currentItemChanged, this, &This::onCurrentItemChange);
  883. }
  884. //////////////////////////////////////////////////////////////////////////
  885. void EasyTreeWidget::resizeColumnsToContents()
  886. {
  887. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  888. {
  889. resizeColumnToContents(i);
  890. }
  891. }
  892. //////////////////////////////////////////////////////////////////////////
  893. void EasyTreeWidget::onHideShowColumn(bool)
  894. {
  895. auto action = qobject_cast<QAction*>(sender());
  896. if (action == nullptr)
  897. return;
  898. const auto col = action->data().toInt();
  899. const bool hideCol = m_columnsHiddenStatus[col] == 0;
  900. setColumnHidden(col, hideCol);
  901. m_columnsHiddenStatus[col] = hideCol ? 1 : 0;
  902. }
  903. void EasyTreeWidget::onModeChange(bool)
  904. {
  905. auto action = qobject_cast<QAction*>(sender());
  906. if (action == nullptr)
  907. return;
  908. const auto prev = m_mode;
  909. m_mode = static_cast<EasyTreeMode>(action->data().toUInt());
  910. if (m_mode == prev)
  911. return;
  912. if (m_mode == EasyTreeMode_Full)
  913. {
  914. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  915. setColumnHidden(i, m_columnsHiddenStatus[i] != 0);
  916. }
  917. else
  918. {
  919. for (int i = 0; i < COL_COLUMNS_NUMBER; ++i)
  920. setColumnHidden(i, m_columnsHiddenStatus[i] != 0 || !SIMPLIFIED_REGIME_COLUMNS[i]);
  921. }
  922. emit EASY_GLOBALS.events.blocksTreeModeChanged();
  923. }
  924. //////////////////////////////////////////////////////////////////////////
  925. void EasyTreeWidget::loadSettings()
  926. {
  927. QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
  928. settings.beginGroup("tree_widget");
  929. auto val = settings.value("color_rows");
  930. if (!val.isNull())
  931. m_bColorRows = val.toBool();
  932. val = settings.value("regime");
  933. if (!val.isNull())
  934. m_mode = static_cast<EasyTreeMode>(val.toUInt());
  935. val = settings.value("columns");
  936. if (!val.isNull())
  937. {
  938. auto byteArray = val.toByteArray();
  939. memcpy(m_columnsHiddenStatus, byteArray.constData(), ::std::min(sizeof(m_columnsHiddenStatus), (size_t)byteArray.size()));
  940. }
  941. auto state = settings.value("headerState").toByteArray();
  942. if (!state.isEmpty())
  943. header()->restoreState(state);
  944. settings.endGroup();
  945. }
  946. void EasyTreeWidget::saveSettings()
  947. {
  948. QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
  949. settings.beginGroup("tree_widget");
  950. settings.setValue("color_rows", m_bColorRows);
  951. settings.setValue("regime", static_cast<uint8_t>(m_mode));
  952. settings.setValue("columns", QByteArray(m_columnsHiddenStatus, COL_COLUMNS_NUMBER));
  953. settings.setValue("headerState", header()->saveState());
  954. settings.endGroup();
  955. }
  956. //////////////////////////////////////////////////////////////////////////
  957. //////////////////////////////////////////////////////////////////////////
  958. EasyHierarchyWidget::EasyHierarchyWidget(QWidget* _parent) : Parent(_parent)
  959. , m_tree(new EasyTreeWidget(this))
  960. , m_searchBox(new QLineEdit(this))
  961. , m_foundNumber(new QLabel("Found 0 matches", this))
  962. , m_searchButton(nullptr)
  963. , m_bCaseSensitiveSearch(false)
  964. {
  965. loadSettings();
  966. m_searchBox->setFixedWidth(200);
  967. m_searchBox->setContentsMargins(5, 0, 0, 0);
  968. QMenu* menu = new QMenu(this);
  969. m_searchButton = menu->menuAction();
  970. m_searchButton->setText("Find next");
  971. m_searchButton->setIcon(QIcon(":/Search-next"));
  972. m_searchButton->setData(true);
  973. connect(m_searchButton, &QAction::triggered, this, &This::findNext);
  974. auto actionGroup = new QActionGroup(this);
  975. actionGroup->setExclusive(true);
  976. auto a = new QAction(tr("Find next"), actionGroup);
  977. a->setCheckable(true);
  978. a->setChecked(true);
  979. connect(a, &QAction::triggered, this, &This::findNextFromMenu);
  980. menu->addAction(a);
  981. a = new QAction(tr("Find previous"), actionGroup);
  982. a->setCheckable(true);
  983. connect(a, &QAction::triggered, this, &This::findPrevFromMenu);
  984. menu->addAction(a);
  985. menu->addSeparator();
  986. a = menu->addAction("Case sensitive");
  987. a->setCheckable(true);
  988. a->setChecked(m_bCaseSensitiveSearch);
  989. connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; });
  990. menu->addAction(a);
  991. auto tb = new QToolBar(this);
  992. tb->setIconSize(::profiler_gui::ICONS_SIZE);
  993. tb->setContentsMargins(0, 0, 0, 0);
  994. tb->addAction(m_searchButton);
  995. tb->addWidget(m_searchBox);
  996. auto searchbox = new QHBoxLayout();
  997. searchbox->setContentsMargins(0, 0, 5, 0);
  998. searchbox->addWidget(tb);
  999. searchbox->addStretch(100);
  1000. searchbox->addWidget(m_foundNumber, Qt::AlignRight);
  1001. auto lay = new QVBoxLayout(this);
  1002. lay->setContentsMargins(1, 1, 1, 1);
  1003. lay->addLayout(searchbox);
  1004. lay->addWidget(m_tree);
  1005. connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed);
  1006. }
  1007. EasyHierarchyWidget::~EasyHierarchyWidget()
  1008. {
  1009. saveSettings();
  1010. }
  1011. void EasyHierarchyWidget::loadSettings()
  1012. {
  1013. QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
  1014. settings.beginGroup("EasyHierarchyWidget");
  1015. auto val = settings.value("case_sensitive");
  1016. if (!val.isNull())
  1017. m_bCaseSensitiveSearch = val.toBool();
  1018. settings.endGroup();
  1019. }
  1020. void EasyHierarchyWidget::saveSettings()
  1021. {
  1022. QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
  1023. settings.beginGroup("EasyHierarchyWidget");
  1024. settings.setValue("case_sensitive", m_bCaseSensitiveSearch);
  1025. settings.endGroup();
  1026. }
  1027. void EasyHierarchyWidget::keyPressEvent(QKeyEvent* _event)
  1028. {
  1029. if (_event->key() == Qt::Key_F3)
  1030. {
  1031. if (_event->modifiers() & Qt::ShiftModifier)
  1032. findPrev(true);
  1033. else
  1034. findNext(true);
  1035. }
  1036. _event->accept();
  1037. }
  1038. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1039. void EasyHierarchyWidget::contextMenuEvent(QContextMenuEvent* _event)
  1040. {
  1041. m_tree->contextMenuEvent(_event);
  1042. }
  1043. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1044. EasyTreeWidget* EasyHierarchyWidget::tree()
  1045. {
  1046. return m_tree;
  1047. }
  1048. void EasyHierarchyWidget::clear(bool _global)
  1049. {
  1050. m_tree->clearSilent(_global);
  1051. m_foundNumber->setText(QString("Found 0 matches"));
  1052. }
  1053. void EasyHierarchyWidget::onSeachBoxReturnPressed()
  1054. {
  1055. if (m_searchButton->data().toBool() == true)
  1056. findNext(true);
  1057. else
  1058. findPrev(true);
  1059. }
  1060. void EasyHierarchyWidget::findNext(bool)
  1061. {
  1062. auto matches = m_tree->findNext(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags());
  1063. if (matches == 1)
  1064. m_foundNumber->setText(QString("Found 1 match"));
  1065. else
  1066. m_foundNumber->setText(QString("Found %1 matches").arg(matches));
  1067. }
  1068. void EasyHierarchyWidget::findPrev(bool)
  1069. {
  1070. auto matches = m_tree->findPrev(m_searchBox->text(), m_bCaseSensitiveSearch ? Qt::MatchCaseSensitive : Qt::MatchFlags());
  1071. if (matches == 1)
  1072. m_foundNumber->setText(QString("Found 1 match"));
  1073. else
  1074. m_foundNumber->setText(QString("Found %1 matches").arg(matches));
  1075. }
  1076. void EasyHierarchyWidget::findNextFromMenu(bool _checked)
  1077. {
  1078. if (!_checked)
  1079. return;
  1080. if (m_searchButton->data().toBool() == false)
  1081. {
  1082. m_searchButton->setData(true);
  1083. m_searchButton->setText(tr("Find next"));
  1084. m_searchButton->setIcon(QIcon(":/Search-next"));
  1085. disconnect(m_searchButton, &QAction::triggered, this, &This::findPrev);
  1086. connect(m_searchButton, &QAction::triggered, this, &This::findNext);
  1087. }
  1088. findNext(true);
  1089. }
  1090. void EasyHierarchyWidget::findPrevFromMenu(bool _checked)
  1091. {
  1092. if (!_checked)
  1093. return;
  1094. if (m_searchButton->data().toBool() == true)
  1095. {
  1096. m_searchButton->setData(false);
  1097. m_searchButton->setText(tr("Find prev"));
  1098. m_searchButton->setIcon(QIcon(":/Search-prev"));
  1099. disconnect(m_searchButton, &QAction::triggered, this, &This::findNext);
  1100. connect(m_searchButton, &QAction::triggered, this, &This::findPrev);
  1101. }
  1102. findPrev(true);
  1103. }
  1104. //////////////////////////////////////////////////////////////////////////