ctkCmdLineModuleExplorerTreeWidget.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkCmdLineModuleExplorerTreeWidget.h"
  16. #include "ctkCmdLineModuleExplorerShowXmlAction.h"
  17. #include <ctkCmdLineModuleFrontend.h>
  18. #include <ctkCmdLineModuleBackend.h>
  19. #include <ctkCmdLineModuleFrontendFactory.h>
  20. #include <ctkCmdLineModuleDescription.h>
  21. #include <ctkCmdLineModuleXmlException.h>
  22. #include <QStandardItemModel>
  23. #include <QSortFilterProxyModel>
  24. #include <QContextMenuEvent>
  25. #include <QMenu>
  26. #include <QDebug>
  27. #include <QUrl>
  28. #include <QApplication>
  29. #include <QMessageBox>
  30. QString ctkCmdLineModuleExplorerTreeWidget::CATEGORY_UNKNOWN = "Uncategorized";
  31. class ctkCmdLineModuleTreeWidgetItem : public QStandardItem
  32. {
  33. public:
  34. ctkCmdLineModuleTreeWidgetItem(const ctkCmdLineModuleReference& moduleRef)
  35. : QStandardItem()
  36. , ModuleRef(moduleRef)
  37. {
  38. QString title;
  39. try
  40. {
  41. title = ModuleRef.description().title();
  42. }
  43. catch (const ctkCmdLineModuleXmlException&)
  44. {
  45. title = ModuleRef.location().toString();
  46. }
  47. this->setText(title + " [" + ModuleRef.backend()->name() + "]");
  48. this->setData(QVariant::fromValue(ModuleRef));
  49. QString toolTip = ModuleRef.location().toString();
  50. if (!ModuleRef.xmlValidationErrorString().isEmpty())
  51. {
  52. this->setIcon(QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning));
  53. toolTip += "\n\nWarning:\n\n" + ModuleRef.xmlValidationErrorString();
  54. }
  55. this->setToolTip(toolTip);
  56. }
  57. ctkCmdLineModuleReference moduleReference() const
  58. {
  59. return ModuleRef;
  60. }
  61. private:
  62. ctkCmdLineModuleReference ModuleRef;
  63. };
  64. ctkCmdLineModuleExplorerTreeWidget::ctkCmdLineModuleExplorerTreeWidget(QWidget *parent)
  65. : QTreeView(parent)
  66. , DefaultFrontendFactory(NULL)
  67. {
  68. this->ContextMenu = new QMenu(this);
  69. this->ShowFrontendMenu = this->ContextMenu->addMenu("Create Frontend");
  70. this->ShowXmlAction = new ctkCmdLineModuleExplorerShowXmlAction(this);
  71. this->ContextMenu->addAction(ShowXmlAction);
  72. connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(moduleDoubleClicked(QModelIndex)));
  73. TreeModel = new QStandardItemModel(this);
  74. FilterProxyModel = new ModuleSortFilterProxyModel(this);
  75. FilterProxyModel->setSourceModel(TreeModel);
  76. FilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
  77. FilterProxyModel->setDynamicSortFilter(true);
  78. this->setModel(FilterProxyModel);
  79. }
  80. void ctkCmdLineModuleExplorerTreeWidget::setModuleFrontendFactories(const QList<ctkCmdLineModuleFrontendFactory *> &frontendFactories,
  81. ctkCmdLineModuleFrontendFactory* defaultFactory)
  82. {
  83. this->FrontendFactories = frontendFactories;
  84. this->DefaultFrontendFactory = defaultFactory;
  85. this->ShowFrontendMenu->clear();
  86. foreach(ctkCmdLineModuleFrontendFactory* factory, this->FrontendFactories)
  87. {
  88. QAction* action = this->ShowFrontendMenu->addAction(factory->name(), this, SLOT(frontendFactoryActionTriggered()));
  89. this->ActionsToFrontendFactoryMap[action] = factory;
  90. action->setToolTip(factory->description());
  91. }
  92. }
  93. void ctkCmdLineModuleExplorerTreeWidget::addModuleItem(const ctkCmdLineModuleReference &moduleRef)
  94. {
  95. QString categories;
  96. try
  97. {
  98. categories = moduleRef.description().category();
  99. }
  100. catch (const ctkCmdLineModuleXmlException&)
  101. {
  102. categories = CATEGORY_UNKNOWN;
  103. }
  104. if (categories.isEmpty())
  105. {
  106. categories = CATEGORY_UNKNOWN;
  107. }
  108. QString currentCategories;
  109. QStandardItem* oldRootItem = NULL;
  110. foreach (const QString& category, categories.split('.', QString::SkipEmptyParts))
  111. {
  112. currentCategories += (currentCategories.isEmpty() ? QString() : QString(".")) + category;
  113. QStandardItem* rootItem = TreeWidgetCategories[currentCategories];
  114. if (rootItem == NULL)
  115. {
  116. rootItem = new QStandardItem(category);
  117. TreeWidgetCategories[currentCategories] = rootItem;
  118. if (oldRootItem != NULL)
  119. {
  120. oldRootItem->appendRow(rootItem);
  121. }
  122. else
  123. {
  124. TreeModel->appendRow(rootItem);
  125. }
  126. }
  127. oldRootItem = rootItem;
  128. }
  129. QStandardItem* moduleItem = new ctkCmdLineModuleTreeWidgetItem(moduleRef);
  130. TreeWidgetItems[moduleRef] = moduleItem;
  131. oldRootItem->appendRow(moduleItem);
  132. }
  133. void ctkCmdLineModuleExplorerTreeWidget::removeModuleItem(const ctkCmdLineModuleReference &moduleRef)
  134. {
  135. QStandardItem* treeWidgetItem = TreeWidgetItems.take(moduleRef);
  136. if (treeWidgetItem == NULL) return;
  137. QString categories;
  138. try
  139. {
  140. categories = moduleRef.description().category();
  141. }
  142. catch (const ctkCmdLineModuleXmlException&)
  143. {
  144. categories = CATEGORY_UNKNOWN;
  145. }
  146. if (categories.isEmpty())
  147. {
  148. categories = CATEGORY_UNKNOWN;
  149. }
  150. QStringList categoryList = categories.split('.', QString::SkipEmptyParts);
  151. while (!categoryList.isEmpty())
  152. {
  153. QStandardItem* rootItem = TreeWidgetCategories[categoryList.join(".")];
  154. Q_ASSERT(rootItem);
  155. rootItem->removeRow(treeWidgetItem->row());
  156. if (rootItem->rowCount() == 0)
  157. {
  158. treeWidgetItem = rootItem;
  159. TreeWidgetCategories.remove(categoryList.join("."));
  160. categoryList.pop_back();
  161. }
  162. else
  163. {
  164. break;
  165. }
  166. }
  167. if (categoryList.isEmpty())
  168. {
  169. TreeModel->removeRow(treeWidgetItem->row());
  170. }
  171. }
  172. void ctkCmdLineModuleExplorerTreeWidget::contextMenuEvent(QContextMenuEvent *event)
  173. {
  174. QModelIndex index = this->indexAt(this->viewport()->mapFromGlobal(event->globalPos()));
  175. if (index.isValid() && index.data(Qt::UserRole+1).isValid())
  176. {
  177. this->ContextReference = index.data(Qt::UserRole+1).value<ctkCmdLineModuleReference>();
  178. this->ShowXmlAction->setModuleReference(this->ContextReference);
  179. this->ContextMenu->exec(event->globalPos());
  180. event->accept();
  181. }
  182. else
  183. {
  184. this->ContextReference = ctkCmdLineModuleReference();
  185. event->ignore();
  186. }
  187. }
  188. void ctkCmdLineModuleExplorerTreeWidget::moduleDoubleClicked(const QModelIndex &index)
  189. {
  190. if (this->DefaultFrontendFactory == NULL) return;
  191. QVariant data = index.data(Qt::UserRole+1);
  192. if (!data.isValid()) return;
  193. ctkCmdLineModuleReference moduleRef = data.value<ctkCmdLineModuleReference>();
  194. if (!moduleRef) return;
  195. this->createFrontend(moduleRef, this->DefaultFrontendFactory);
  196. }
  197. void ctkCmdLineModuleExplorerTreeWidget::frontendFactoryActionTriggered()
  198. {
  199. ctkCmdLineModuleFrontendFactory* frontendFactory = this->ActionsToFrontendFactoryMap[static_cast<QAction*>(this->sender())];
  200. this->createFrontend(this->ContextReference, frontendFactory);
  201. }
  202. void ctkCmdLineModuleExplorerTreeWidget::setFilter(const QString &filter)
  203. {
  204. this->FilterProxyModel->setFilterWildcard(filter);
  205. }
  206. ctkCmdLineModuleFrontend* ctkCmdLineModuleExplorerTreeWidget::createFrontend(const ctkCmdLineModuleReference &moduleRef,
  207. ctkCmdLineModuleFrontendFactory* frontendFactory)
  208. {
  209. try
  210. {
  211. moduleRef.description();
  212. ctkCmdLineModuleFrontend* moduleFrontend = frontendFactory->create(moduleRef);
  213. emit moduleFrontendCreated(moduleFrontend);
  214. return moduleFrontend;
  215. }
  216. catch (const ctkException& e)
  217. {
  218. QMessageBox::information(this, "Frontend creation failed", "Creating a " + frontendFactory->name()
  219. + " frontend failed:\n\n" + e.what());
  220. return NULL;
  221. }
  222. }
  223. bool ModuleSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
  224. {
  225. QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
  226. QModelIndex childIndex = index.child(0, 0);
  227. if (childIndex.isValid())
  228. {
  229. int i = 0;
  230. bool accept = false;
  231. while(childIndex.isValid())
  232. {
  233. accept = this->filterAcceptsRow(childIndex.row(), index);
  234. if (accept) return true;
  235. childIndex = index.child(++i, 0);
  236. }
  237. return false;
  238. }
  239. return (sourceModel()->data(index).toString().contains(filterRegExp()));
  240. }
  241. bool ModuleSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
  242. {
  243. QVariant l = (left.model() ? left.model()->data(left, this->sortRole()) : QVariant());
  244. QVariant r = (right.model() ? right.model()->data(right, this->sortRole()) : QVariant());
  245. return l.toString().compare(r.toString(), this->sortCaseSensitivity()) > 0;
  246. }
  247. ModuleSortFilterProxyModel::ModuleSortFilterProxyModel(QObject *parent)
  248. : QSortFilterProxyModel(parent)
  249. {
  250. }