QCMakeCacheView.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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 <QDirModel>
  24. #include <QCompleter>
  25. static QRegExp AdvancedRegExp[2] = { QRegExp("(false)"), QRegExp("(true|false)") };
  26. // filter for searches
  27. class QCMakeSearchFilter : public QSortFilterProxyModel
  28. {
  29. public:
  30. QCMakeSearchFilter(QObject* o) : QSortFilterProxyModel(o) {}
  31. protected:
  32. bool filterAcceptsRow(int row, const QModelIndex& p) const
  33. {
  34. // accept row if either column matches
  35. QModelIndex idx0 = this->sourceModel()->index(row, 0, p);
  36. QModelIndex idx1 = this->sourceModel()->index(row, 1, p);
  37. QString str0 = this->sourceModel()->data(idx0).toString();
  38. QString str1 = this->sourceModel()->data(idx1).toString();
  39. return str0.contains(this->filterRegExp()) ||
  40. str1.contains(this->filterRegExp());
  41. }
  42. };
  43. QCMakeCacheView::QCMakeCacheView(QWidget* p)
  44. : QTableView(p), Init(false)
  45. {
  46. // hook up our model and search/filter proxies
  47. this->CacheModel = new QCMakeCacheModel(this);
  48. this->AdvancedFilter = new QSortFilterProxyModel(this);
  49. this->AdvancedFilter->setSourceModel(this->CacheModel);
  50. this->AdvancedFilter->setFilterRole(QCMakeCacheModel::AdvancedRole);
  51. this->AdvancedFilter->setFilterRegExp(AdvancedRegExp[0]);
  52. this->AdvancedFilter->setDynamicSortFilter(true);
  53. this->SearchFilter = new QCMakeSearchFilter(this);
  54. this->SearchFilter->setSourceModel(this->AdvancedFilter);
  55. this->SearchFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
  56. this->SearchFilter->setDynamicSortFilter(true);
  57. this->setModel(this->SearchFilter);
  58. // our delegate for creating our editors
  59. QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this);
  60. this->setItemDelegate(delegate);
  61. this->setEditTriggers(QAbstractItemView::DoubleClicked |
  62. QAbstractItemView::SelectedClicked |
  63. QAbstractItemView::EditKeyPressed |
  64. QAbstractItemView::AnyKeyPressed);
  65. // set up headers and sizes
  66. int h = 0;
  67. QFontMetrics met(this->font());
  68. h = qMax(met.height(), this->style()->pixelMetric(QStyle::PM_IndicatorHeight));
  69. this->verticalHeader()->setDefaultSectionSize(h + 4);
  70. this->horizontalHeader()->setStretchLastSection(true);
  71. this->verticalHeader()->hide();
  72. }
  73. void QCMakeCacheView::showEvent(QShowEvent* e)
  74. {
  75. if(!this->Init)
  76. {
  77. // initialize the table view column size
  78. int colWidth = this->columnWidth(0) + this->columnWidth(1);
  79. this->setColumnWidth(0, colWidth/2);
  80. this->setColumnWidth(1, colWidth/2);
  81. this->Init = true;
  82. }
  83. return QTableView::showEvent(e);
  84. }
  85. QCMakeCacheModel* QCMakeCacheView::cacheModel() const
  86. {
  87. return this->CacheModel;
  88. }
  89. QModelIndex QCMakeCacheView::moveCursor(CursorAction act,
  90. Qt::KeyboardModifiers mod)
  91. {
  92. // tab through values only (not names)
  93. QModelIndex current = this->currentIndex();
  94. if(act == MoveNext)
  95. {
  96. if(!current.isValid())
  97. {
  98. return this->model()->index(0, 1);
  99. }
  100. else if(current.column() == 0)
  101. {
  102. return this->model()->index(current.row(), 1);
  103. }
  104. else
  105. {
  106. return this->model()->index(current.row()+1, 1);
  107. }
  108. }
  109. else if(act == MovePrevious)
  110. {
  111. if(!current.isValid())
  112. {
  113. return this->model()->index(0, 1);
  114. }
  115. else
  116. {
  117. return this->model()->index(current.row()-1, 1);
  118. }
  119. }
  120. return QTableView::moveCursor(act, mod);
  121. }
  122. void QCMakeCacheView::setShowAdvanced(bool s)
  123. {
  124. this->AdvancedFilter->setFilterRegExp(
  125. s ? AdvancedRegExp[1] : AdvancedRegExp[0]);
  126. }
  127. bool QCMakeCacheView::showAdvanced() const
  128. {
  129. return this->AdvancedFilter->filterRegExp() == AdvancedRegExp[1];
  130. }
  131. void QCMakeCacheView::setSearchFilter(const QString& s)
  132. {
  133. this->SearchFilter->setFilterFixedString(s);
  134. }
  135. QCMakeCacheModel::QCMakeCacheModel(QObject* p)
  136. : QAbstractTableModel(p),
  137. NewCount(0), EditEnabled(true)
  138. {
  139. }
  140. QCMakeCacheModel::~QCMakeCacheModel()
  141. {
  142. }
  143. static uint qHash(const QCMakeCacheProperty& p)
  144. {
  145. return qHash(p.Key);
  146. }
  147. void QCMakeCacheModel::clear()
  148. {
  149. this->setProperties(QCMakeCachePropertyList());
  150. }
  151. void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList& props)
  152. {
  153. QSet<QCMakeCacheProperty> newProps = props.toSet();
  154. QSet<QCMakeCacheProperty> newProps2 = props.toSet();
  155. QSet<QCMakeCacheProperty> oldProps = this->Properties.toSet();
  156. oldProps.intersect(newProps);
  157. newProps.subtract(oldProps);
  158. newProps2.subtract(newProps);
  159. this->NewCount = newProps.count();
  160. this->Properties.clear();
  161. this->Properties = newProps.toList();
  162. qSort(this->Properties);
  163. QCMakeCachePropertyList tmp = newProps2.toList();
  164. qSort(tmp);
  165. this->Properties += tmp;
  166. this->reset();
  167. }
  168. QCMakeCachePropertyList QCMakeCacheModel::properties() const
  169. {
  170. return this->Properties;
  171. }
  172. void QCMakeCacheModel::setEditEnabled(bool e)
  173. {
  174. this->EditEnabled = e;
  175. }
  176. bool QCMakeCacheModel::editEnabled() const
  177. {
  178. return this->EditEnabled;
  179. }
  180. int QCMakeCacheModel::newCount() const
  181. {
  182. return this->NewCount;
  183. }
  184. int QCMakeCacheModel::columnCount (const QModelIndex& /*p*/ ) const
  185. {
  186. return 2;
  187. }
  188. QVariant QCMakeCacheModel::data (const QModelIndex& idx, int role) const
  189. {
  190. if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
  191. {
  192. return this->Properties[idx.row()].Key;
  193. }
  194. else if(idx.column() == 0 && role == Qt::ToolTipRole)
  195. {
  196. return this->data(idx, Qt::DisplayRole).toString() + "\n" +
  197. this->data(idx, QCMakeCacheModel::HelpRole).toString();
  198. }
  199. else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
  200. {
  201. if(this->Properties[idx.row()].Type != QCMakeCacheProperty::BOOL)
  202. {
  203. return this->Properties[idx.row()].Value;
  204. }
  205. }
  206. else if(idx.column() == 1 && role == Qt::CheckStateRole)
  207. {
  208. if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
  209. {
  210. return this->Properties[idx.row()].Value.toBool() ? Qt::Checked : Qt::Unchecked;
  211. }
  212. }
  213. else if(role == QCMakeCacheModel::HelpRole)
  214. {
  215. return this->Properties[idx.row()].Help;
  216. }
  217. else if(role == QCMakeCacheModel::TypeRole)
  218. {
  219. return this->Properties[idx.row()].Type;
  220. }
  221. else if(role == QCMakeCacheModel::AdvancedRole)
  222. {
  223. return this->Properties[idx.row()].Advanced;
  224. }
  225. else if(role == Qt::BackgroundRole && idx.row()+1 <= this->NewCount)
  226. {
  227. return QBrush(QColor(255,100,100));
  228. }
  229. return QVariant();
  230. }
  231. QModelIndex QCMakeCacheModel::parent (const QModelIndex& /*idx*/) const
  232. {
  233. return QModelIndex();
  234. }
  235. int QCMakeCacheModel::rowCount (const QModelIndex& p) const
  236. {
  237. if(p.isValid())
  238. {
  239. return 0;
  240. }
  241. return this->Properties.count();
  242. }
  243. QVariant QCMakeCacheModel::headerData (int section, Qt::Orientation orient, int role) const
  244. {
  245. // return header labels
  246. if(role == Qt::DisplayRole && orient == Qt::Horizontal)
  247. {
  248. return section == 0 ? "Name" : "Value";
  249. }
  250. return QVariant();
  251. }
  252. Qt::ItemFlags QCMakeCacheModel::flags (const QModelIndex& idx) const
  253. {
  254. Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
  255. // all column 1's are editable
  256. if(idx.column() == 1 && this->EditEnabled)
  257. {
  258. f |= Qt::ItemIsEditable;
  259. // booleans are editable in place
  260. if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
  261. {
  262. f |= Qt::ItemIsUserCheckable;
  263. }
  264. }
  265. return f;
  266. }
  267. bool QCMakeCacheModel::setData (const QModelIndex& idx, const QVariant& value, int role)
  268. {
  269. if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
  270. {
  271. this->Properties[idx.row()].Key = value.toString();
  272. emit this->dataChanged(idx, idx);
  273. }
  274. else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
  275. {
  276. this->Properties[idx.row()].Value = value.toString();
  277. emit this->dataChanged(idx, idx);
  278. }
  279. else if(idx.column() == 1 && (role == Qt::CheckStateRole))
  280. {
  281. this->Properties[idx.row()].Value = value.toInt() == Qt::Checked;
  282. emit this->dataChanged(idx, idx);
  283. }
  284. else if(role == QCMakeCacheModel::HelpRole)
  285. {
  286. this->Properties[idx.row()].Help = value.toString();
  287. emit this->dataChanged(idx, idx);
  288. }
  289. else if(role == QCMakeCacheModel::TypeRole)
  290. {
  291. this->Properties[idx.row()].Type = static_cast<QCMakeCacheProperty::PropertyType>(value.toInt());
  292. }
  293. else if(role == QCMakeCacheModel::AdvancedRole)
  294. {
  295. this->Properties[idx.row()].Advanced = value.toBool();
  296. }
  297. return false;
  298. }
  299. QModelIndex QCMakeCacheModel::buddy(const QModelIndex& idx) const
  300. {
  301. if(idx.column() == 0)
  302. {
  303. if(this->Properties[idx.row()].Type != QCMakeCacheProperty::BOOL)
  304. {
  305. return this->index(idx.row(), 1);
  306. }
  307. }
  308. return idx;
  309. }
  310. bool QCMakeCacheModel::removeRows(int row, int num, const QModelIndex&)
  311. {
  312. if(row < 0 || row+num > this->Properties.count())
  313. {
  314. return false;
  315. }
  316. this->beginRemoveRows(QModelIndex(), row, row+num-1);
  317. for(int i=0; i<num; i++)
  318. {
  319. this->Properties.removeAt(row);
  320. if(this->NewCount >= row+1)
  321. {
  322. this->NewCount--;
  323. }
  324. }
  325. this->endRemoveRows();
  326. return true;
  327. }
  328. bool QCMakeCacheModel::insertRows(int row, int num, const QModelIndex&)
  329. {
  330. if(row < 0)
  331. row = 0;
  332. if(row > this->rowCount())
  333. row = this->rowCount();
  334. this->beginInsertRows(QModelIndex(), row, row+num-1);
  335. for(int i=0; i<num; i++)
  336. {
  337. this->Properties.insert(row+i, QCMakeCacheProperty());
  338. if(this->NewCount >= row)
  339. {
  340. this->NewCount++;
  341. }
  342. }
  343. this->endInsertRows();
  344. return true;
  345. }
  346. QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p)
  347. : QItemDelegate(p)
  348. {
  349. }
  350. QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* p,
  351. const QStyleOptionViewItem&, const QModelIndex& idx) const
  352. {
  353. const QAbstractItemModel* model = idx.model();
  354. QModelIndex var = model->index(idx.row(), 0);
  355. QVariant type = idx.data(QCMakeCacheModel::TypeRole);
  356. if(type == QCMakeCacheProperty::BOOL)
  357. {
  358. return NULL;
  359. }
  360. else if(type == QCMakeCacheProperty::PATH)
  361. {
  362. return new QCMakeCachePathEditor(p,
  363. var.data(Qt::DisplayRole).toString());
  364. }
  365. else if(type == QCMakeCacheProperty::FILEPATH)
  366. {
  367. return new QCMakeCacheFilePathEditor(p,
  368. var.data(Qt::DisplayRole).toString());
  369. }
  370. return new QLineEdit(p);
  371. }
  372. bool QCMakeCacheModelDelegate::editorEvent(QEvent* e, QAbstractItemModel* model,
  373. const QStyleOptionViewItem& option, const QModelIndex& index)
  374. {
  375. Qt::ItemFlags flags = model->flags(index);
  376. if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
  377. || !(flags & Qt::ItemIsEnabled))
  378. {
  379. return false;
  380. }
  381. QVariant value = index.data(Qt::CheckStateRole);
  382. if (!value.isValid())
  383. {
  384. return false;
  385. }
  386. if ((e->type() == QEvent::MouseButtonRelease)
  387. || (e->type() == QEvent::MouseButtonDblClick))
  388. {
  389. // eat the double click events inside the check rect
  390. if (e->type() == QEvent::MouseButtonDblClick)
  391. {
  392. return true;
  393. }
  394. }
  395. else if (e->type() == QEvent::KeyPress)
  396. {
  397. if(static_cast<QKeyEvent*>(e)->key() != Qt::Key_Space &&
  398. static_cast<QKeyEvent*>(e)->key() != Qt::Key_Select)
  399. {
  400. return false;
  401. }
  402. }
  403. else
  404. {
  405. return false;
  406. }
  407. Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
  408. ? Qt::Unchecked : Qt::Checked);
  409. return model->setData(index, state, Qt::CheckStateRole);
  410. }
  411. QCMakeCacheFileEditor::QCMakeCacheFileEditor(QWidget* p, const QString& var)
  412. : QLineEdit(p), Variable(var)
  413. {
  414. // this *is* instead of has a line edit so QAbstractItemView
  415. // doesn't get confused with what the editor really is
  416. this->setContentsMargins(0, 0, 0, 0);
  417. this->ToolButton = new QToolButton(this);
  418. this->ToolButton->setText("...");
  419. this->ToolButton->setCursor(QCursor(Qt::ArrowCursor));
  420. QObject::connect(this->ToolButton, SIGNAL(clicked(bool)),
  421. this, SLOT(chooseFile()));
  422. }
  423. QCMakeCacheFilePathEditor::QCMakeCacheFilePathEditor(QWidget* p, const QString& var)
  424. : QCMakeCacheFileEditor(p, var)
  425. {
  426. QCompleter* comp = new QCompleter(this);
  427. QDirModel* model = new QDirModel(comp);
  428. comp->setModel(model);
  429. this->setCompleter(comp);
  430. }
  431. QCMakeCachePathEditor::QCMakeCachePathEditor(QWidget* p, const QString& var)
  432. : QCMakeCacheFileEditor(p, var)
  433. {
  434. QCompleter* comp = new QCompleter(this);
  435. QDirModel* model = new QDirModel(comp);
  436. model->setFilter(QDir::AllDirs | QDir::Drives);
  437. comp->setModel(model);
  438. this->setCompleter(comp);
  439. }
  440. void QCMakeCacheFileEditor::resizeEvent(QResizeEvent* e)
  441. {
  442. // make the tool button fit on the right side
  443. int h = e->size().height();
  444. this->ToolButton->resize(h, h);
  445. this->ToolButton->move(this->width() - h, 0);
  446. this->setContentsMargins(0, 0, h, 0);
  447. }
  448. void QCMakeCacheFilePathEditor::chooseFile()
  449. {
  450. // choose a file and set it
  451. QString path;
  452. QFileInfo info(this->text());
  453. QString title;
  454. if(this->Variable.isEmpty())
  455. {
  456. title = tr("Select File");
  457. }
  458. else
  459. {
  460. title = tr("Select File for %1");
  461. title = title.arg(this->Variable);
  462. }
  463. path = QFileDialog::getOpenFileName(this, title, info.absolutePath());
  464. if(!path.isEmpty())
  465. {
  466. this->setText(path);
  467. }
  468. }
  469. void QCMakeCachePathEditor::chooseFile()
  470. {
  471. // choose a file and set it
  472. QString path;
  473. QString title;
  474. if(this->Variable.isEmpty())
  475. {
  476. title = tr("Select Path");
  477. }
  478. else
  479. {
  480. title = tr("Select Path for %1");
  481. title = title.arg(this->Variable);
  482. }
  483. path = QFileDialog::getExistingDirectory(this, title, this->text());
  484. if(!path.isEmpty())
  485. {
  486. this->setText(path);
  487. }
  488. }