QCMakeCacheView.cxx 13 KB

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