QCMakeCacheView.cxx 13 KB

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