QCMakeCacheView.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "QCMakeCacheView.h"
  14. #include <QToolButton>
  15. #include <QFileDialog>
  16. #include <QHBoxLayout>
  17. #include <QHeaderView>
  18. #include <QEvent>
  19. #include <QFileInfo>
  20. #include <QStyle>
  21. #include <QKeyEvent>
  22. #include <QMenu>
  23. #include <QDialog>
  24. #include <QLabel>
  25. #include <QDialogButtonBox>
  26. #include <QDirModel>
  27. #include <QCompleter>
  28. static QRegExp AdvancedRegExp[2] = { QRegExp("(false)"), QRegExp("(true|false)") };
  29. QCMakeCacheView::QCMakeCacheView(QWidget* p)
  30. : QTableView(p), Init(false)
  31. {
  32. // hook up our model and search/filter proxies
  33. this->CacheModel = new QCMakeCacheModel(this);
  34. this->AdvancedFilter = new QSortFilterProxyModel(this);
  35. this->AdvancedFilter->setSourceModel(this->CacheModel);
  36. this->AdvancedFilter->setFilterRole(QCMakeCacheModel::AdvancedRole);
  37. this->AdvancedFilter->setFilterRegExp(AdvancedRegExp[0]);
  38. this->SearchFilter = new QSortFilterProxyModel(this);
  39. this->SearchFilter->setSourceModel(this->AdvancedFilter);
  40. this->SearchFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
  41. this->SearchFilter->setFilterKeyColumn(-1); // all columns
  42. this->setModel(this->SearchFilter);
  43. // our delegate for creating our editors
  44. QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this);
  45. this->setItemDelegate(delegate);
  46. // set up headers and sizes
  47. int h = 0;
  48. QFontMetrics met(this->font());
  49. h = qMax(met.height(), this->style()->pixelMetric(QStyle::PM_IndicatorHeight));
  50. this->verticalHeader()->setDefaultSectionSize(h + 4);
  51. this->horizontalHeader()->setStretchLastSection(true);
  52. this->verticalHeader()->hide();
  53. }
  54. void QCMakeCacheView::showEvent(QShowEvent* e)
  55. {
  56. if(!this->Init)
  57. {
  58. // initialize the table view column size
  59. int colWidth = this->columnWidth(0) + this->columnWidth(1);
  60. this->setColumnWidth(0, colWidth/2);
  61. this->setColumnWidth(1, colWidth/2);
  62. this->Init = true;
  63. }
  64. return QTableView::showEvent(e);
  65. }
  66. QCMakeCacheModel* QCMakeCacheView::cacheModel() const
  67. {
  68. return this->CacheModel;
  69. }
  70. void QCMakeCacheView::contextMenuEvent(QContextMenuEvent* /*e*/)
  71. {
  72. QList<QModelIndex> idxs = this->selectionModel()->selectedRows();
  73. if(idxs.count())
  74. {
  75. QMenu* menu = new QMenu(this);
  76. QAction* deleteAction = NULL;
  77. QAction* ignoreAction = NULL;
  78. if(this->cacheModel()->editEnabled())
  79. {
  80. QString t = idxs.count() > 1 ? tr("Delete Cache Entries") :
  81. tr("Delete Cache Entry");
  82. deleteAction = menu->addAction(t);
  83. t = idxs.count() > 1 ? tr("Ignore Cache Entries") :
  84. tr("Ignore Cache Entry");
  85. ignoreAction = menu->addAction(t);
  86. }
  87. QAction* helpAction = menu->addAction(tr("Help For Cache Entry"));
  88. QAction* which = menu->exec(QCursor::pos());
  89. if(!which)
  90. {
  91. return;
  92. }
  93. if(which == helpAction)
  94. {
  95. QModelIndex idx = this->selectionModel()->currentIndex();
  96. idx = this->SearchFilter->mapToSource(idx);
  97. idx = this->AdvancedFilter->mapToSource(idx);
  98. idx = this->cacheModel()->index(idx.row(), 0);
  99. QString msg = this->cacheModel()->data(idx, Qt::DisplayRole).toString() +
  100. "\n\n" +
  101. this->cacheModel()->data(idx, QCMakeCacheModel::HelpRole).toString();
  102. QDialog dialog;
  103. dialog.setWindowTitle(tr("CMakeSetup Help"));
  104. QVBoxLayout* l = new QVBoxLayout(&dialog);
  105. QLabel* lab = new QLabel(&dialog);
  106. l->addWidget(lab);
  107. lab->setText(msg);
  108. lab->setWordWrap(true);
  109. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  110. Qt::Horizontal, &dialog);
  111. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  112. l->addWidget(btns);
  113. dialog.exec();
  114. }
  115. else
  116. {
  117. QList<QPersistentModelIndex> pidxs;
  118. foreach(QModelIndex i, idxs)
  119. {
  120. i = this->SearchFilter->mapToSource(i);
  121. i = this->AdvancedFilter->mapToSource(i);
  122. pidxs.append(i);
  123. }
  124. if(which == deleteAction)
  125. {
  126. foreach(QPersistentModelIndex j, pidxs)
  127. {
  128. this->cacheModel()->removeRows(j.row(), 1);
  129. }
  130. }
  131. else if(which == ignoreAction)
  132. {
  133. foreach(QPersistentModelIndex j, pidxs)
  134. {
  135. j = this->cacheModel()->index(j.row(), 1);
  136. this->cacheModel()->setData(j, "IGNORE", Qt::DisplayRole);
  137. }
  138. }
  139. }
  140. }
  141. }
  142. QModelIndex QCMakeCacheView::moveCursor(CursorAction act,
  143. Qt::KeyboardModifiers mod)
  144. {
  145. // tab through values only (not names)
  146. QModelIndex current = this->currentIndex();
  147. if(act == MoveNext)
  148. {
  149. if(!current.isValid())
  150. {
  151. return this->model()->index(0, 1);
  152. }
  153. else if(current.column() == 0)
  154. {
  155. return this->model()->index(current.row(), 1);
  156. }
  157. else
  158. {
  159. return this->model()->index(current.row()+1, 1);
  160. }
  161. }
  162. else if(act == MovePrevious)
  163. {
  164. if(!current.isValid())
  165. {
  166. return this->model()->index(0, 1);
  167. }
  168. else
  169. {
  170. return this->model()->index(current.row()-1, 1);
  171. }
  172. }
  173. return QTableView::moveCursor(act, mod);
  174. }
  175. void QCMakeCacheView::setShowAdvanced(bool s)
  176. {
  177. this->AdvancedFilter->setFilterRegExp(
  178. s ? AdvancedRegExp[1] : AdvancedRegExp[0]);
  179. }
  180. bool QCMakeCacheView::showAdvanced() const
  181. {
  182. return this->AdvancedFilter->filterRegExp() == AdvancedRegExp[1];
  183. }
  184. void QCMakeCacheView::setSearchFilter(const QString& s)
  185. {
  186. this->SearchFilter->setFilterFixedString(s);
  187. }
  188. QCMakeCacheModel::QCMakeCacheModel(QObject* p)
  189. : QAbstractTableModel(p),
  190. NewCount(0), ModifiedValues(false), EditEnabled(true)
  191. {
  192. }
  193. QCMakeCacheModel::~QCMakeCacheModel()
  194. {
  195. }
  196. bool QCMakeCacheModel::modifiedValues() const
  197. {
  198. return this->ModifiedValues;
  199. }
  200. static uint qHash(const QCMakeCacheProperty& p)
  201. {
  202. return qHash(p.Key);
  203. }
  204. void QCMakeCacheModel::clear()
  205. {
  206. this->setProperties(QCMakeCachePropertyList());
  207. }
  208. void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList& props)
  209. {
  210. QSet<QCMakeCacheProperty> newProps = props.toSet();
  211. QSet<QCMakeCacheProperty> oldProps = this->Properties.toSet();
  212. oldProps.intersect(newProps);
  213. newProps.subtract(oldProps);
  214. this->NewCount = newProps.count();
  215. this->Properties.clear();
  216. this->Properties = newProps.toList();
  217. qSort(this->Properties);
  218. QCMakeCachePropertyList tmp = oldProps.toList();
  219. qSort(tmp);
  220. this->Properties += tmp;
  221. this->ModifiedValues = NewCount != 0;
  222. this->reset();
  223. }
  224. QCMakeCachePropertyList QCMakeCacheModel::properties() const
  225. {
  226. return this->Properties;
  227. }
  228. void QCMakeCacheModel::setEditEnabled(bool e)
  229. {
  230. this->EditEnabled = e;
  231. }
  232. bool QCMakeCacheModel::editEnabled() const
  233. {
  234. return this->EditEnabled;
  235. }
  236. int QCMakeCacheModel::columnCount (const QModelIndex& /*p*/ ) const
  237. {
  238. return 2;
  239. }
  240. QVariant QCMakeCacheModel::data (const QModelIndex& idx, int role) const
  241. {
  242. if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
  243. {
  244. return this->Properties[idx.row()].Key;
  245. }
  246. else if(idx.column() == 0 && role == Qt::ToolTipRole)
  247. {
  248. return this->data(idx, Qt::DisplayRole).toString() + "\n" +
  249. this->data(idx, QCMakeCacheModel::HelpRole).toString();
  250. }
  251. else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
  252. {
  253. if(this->Properties[idx.row()].Type != QCMakeCacheProperty::BOOL)
  254. {
  255. return this->Properties[idx.row()].Value;
  256. }
  257. }
  258. else if(idx.column() == 1 && role == Qt::CheckStateRole)
  259. {
  260. if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
  261. {
  262. return this->Properties[idx.row()].Value.toBool() ? Qt::Checked : Qt::Unchecked;
  263. }
  264. }
  265. else if(role == QCMakeCacheModel::HelpRole)
  266. {
  267. return this->Properties[idx.row()].Help;
  268. }
  269. else if(role == QCMakeCacheModel::TypeRole)
  270. {
  271. return this->Properties[idx.row()].Type;
  272. }
  273. else if(role == QCMakeCacheModel::AdvancedRole)
  274. {
  275. return this->Properties[idx.row()].Advanced;
  276. }
  277. else if(role == Qt::BackgroundRole && idx.row()+1 <= this->NewCount)
  278. {
  279. return QBrush(QColor(255,100,100));
  280. }
  281. return QVariant();
  282. }
  283. QModelIndex QCMakeCacheModel::parent (const QModelIndex& /*idx*/) const
  284. {
  285. return QModelIndex();
  286. }
  287. int QCMakeCacheModel::rowCount (const QModelIndex& p) const
  288. {
  289. if(p.isValid())
  290. {
  291. return 0;
  292. }
  293. return this->Properties.count();
  294. }
  295. QVariant QCMakeCacheModel::headerData (int section, Qt::Orientation orient, int role) const
  296. {
  297. // return header labels
  298. if(role == Qt::DisplayRole && orient == Qt::Horizontal)
  299. {
  300. return section == 0 ? "Name" : "Value";
  301. }
  302. return QVariant();
  303. }
  304. Qt::ItemFlags QCMakeCacheModel::flags (const QModelIndex& idx) const
  305. {
  306. Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
  307. // all column 1's are editable
  308. if(idx.column() == 1 && this->EditEnabled)
  309. {
  310. f |= Qt::ItemIsEditable;
  311. // booleans are editable in place
  312. if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
  313. {
  314. f |= Qt::ItemIsUserCheckable;
  315. }
  316. }
  317. return f;
  318. }
  319. bool QCMakeCacheModel::setData (const QModelIndex& idx, const QVariant& value, int role)
  320. {
  321. if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
  322. {
  323. this->Properties[idx.row()].Key = value.toString();
  324. this->ModifiedValues = true;
  325. emit this->dataChanged(idx, idx);
  326. }
  327. else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
  328. {
  329. this->Properties[idx.row()].Value = value.toString();
  330. this->ModifiedValues = true;
  331. emit this->dataChanged(idx, idx);
  332. }
  333. else if(idx.column() == 1 && (role == Qt::CheckStateRole))
  334. {
  335. this->Properties[idx.row()].Value = value.toInt() == Qt::Checked;
  336. this->ModifiedValues = true;
  337. emit this->dataChanged(idx, idx);
  338. }
  339. return false;
  340. }
  341. QModelIndex QCMakeCacheModel::buddy ( const QModelIndex& idx ) const
  342. {
  343. if(idx.column() == 0)
  344. {
  345. return this->index(idx.row(), 1);
  346. }
  347. return idx;
  348. }
  349. bool QCMakeCacheModel::removeRows(int row, int, const QModelIndex&)
  350. {
  351. if(row < 0 || row >= this->Properties.count())
  352. {
  353. return false;
  354. }
  355. this->beginRemoveRows(QModelIndex(), row, row);
  356. this->Properties.removeAt(row);
  357. if(this->NewCount >= row+1)
  358. {
  359. this->NewCount--;
  360. }
  361. this->endRemoveRows();
  362. return true;
  363. }
  364. QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p)
  365. : QItemDelegate(p)
  366. {
  367. }
  368. QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* p,
  369. const QStyleOptionViewItem&, const QModelIndex& idx) const
  370. {
  371. QVariant type = idx.data(QCMakeCacheModel::TypeRole);
  372. if(type == QCMakeCacheProperty::BOOL)
  373. {
  374. return NULL;
  375. }
  376. else if(type == QCMakeCacheProperty::PATH)
  377. {
  378. return new QCMakeCachePathEditor(false, p);
  379. }
  380. else if(type == QCMakeCacheProperty::FILEPATH)
  381. {
  382. return new QCMakeCachePathEditor(true, p);
  383. }
  384. return new QLineEdit(p);
  385. }
  386. QCMakeCachePathEditor::QCMakeCachePathEditor(bool fp, QWidget* p)
  387. : QLineEdit(p), IsFilePath(fp)
  388. {
  389. // this *is* instead of has a line edit so QAbstractItemView
  390. // doesn't get confused with what the editor really is
  391. this->setContentsMargins(0, 0, 0, 0);
  392. this->ToolButton = new QToolButton(this);
  393. this->ToolButton->setText("...");
  394. this->ToolButton->setCursor(QCursor(Qt::ArrowCursor));
  395. QObject::connect(this->ToolButton, SIGNAL(clicked(bool)),
  396. this, SLOT(chooseFile()));
  397. QCompleter* comp = new QCompleter(this);
  398. QDirModel* model = new QDirModel(comp);
  399. if(!fp)
  400. {
  401. // only dirs
  402. model->setFilter(QDir::AllDirs | QDir::Drives);
  403. }
  404. comp->setModel(model);
  405. this->setCompleter(comp);
  406. }
  407. void QCMakeCachePathEditor::resizeEvent(QResizeEvent* e)
  408. {
  409. // make the tool button fit on the right side
  410. int h = e->size().height();
  411. this->ToolButton->resize(h, h);
  412. this->ToolButton->move(this->width() - h, 0);
  413. this->setContentsMargins(0, 0, h, 0);
  414. }
  415. void QCMakeCachePathEditor::chooseFile()
  416. {
  417. // choose a file and set it
  418. QString path;
  419. if(this->IsFilePath)
  420. {
  421. QFileInfo info(this->text());
  422. path = QFileDialog::getOpenFileName(this, tr("Select File"),
  423. info.absolutePath());
  424. }
  425. else
  426. {
  427. path = QFileDialog::getExistingDirectory(this, tr("Select Path"),
  428. this->text());
  429. }
  430. if(!path.isEmpty())
  431. {
  432. this->setText(path);
  433. }
  434. }