Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / QtDialog / QCMakeCacheView.cxx
blob3b680940b2d35ac857e7fd51250493afb21b5ace
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: QCMakeCacheView.cxx,v $
5 Language: C++
6 <<<<<<< QCMakeCacheView.cxx
7 Date: $Date: 2008/02/15 00:58:31 $
8 Version: $Revision: 1.26 $
9 =======
10 Date: $Date: 2008-04-02 19:28:17 $
11 Version: $Revision: 1.27 $
12 >>>>>>> 1.27
14 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
15 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
17 This software is distributed WITHOUT ANY WARRANTY; without even
18 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 PURPOSE. See the above copyright notices for more information.
21 =========================================================================*/
23 #include "QCMakeCacheView.h"
25 #include <QToolButton>
26 #include <QFileDialog>
27 #include <QHBoxLayout>
28 #include <QHeaderView>
29 #include <QEvent>
30 #include <QFileInfo>
31 #include <QStyle>
32 #include <QKeyEvent>
33 #include <QMenu>
34 #include <QDirModel>
36 static QRegExp AdvancedRegExp[2] = { QRegExp("(false)"), QRegExp("(true|false)") };
38 // filter for searches
39 class QCMakeSearchFilter : public QSortFilterProxyModel
41 public:
42 QCMakeSearchFilter(QObject* o) : QSortFilterProxyModel(o) {}
43 protected:
44 bool filterAcceptsRow(int row, const QModelIndex& p) const
46 // accept row if either column matches
47 QModelIndex idx0 = this->sourceModel()->index(row, 0, p);
48 QModelIndex idx1 = this->sourceModel()->index(row, 1, p);
49 QString str0 = this->sourceModel()->data(idx0).toString();
50 QString str1 = this->sourceModel()->data(idx1).toString();
52 return str0.contains(this->filterRegExp()) ||
53 str1.contains(this->filterRegExp());
57 QCMakeCacheView::QCMakeCacheView(QWidget* p)
58 : QTableView(p), Init(false)
60 // hook up our model and search/filter proxies
61 this->CacheModel = new QCMakeCacheModel(this);
62 this->AdvancedFilter = new QSortFilterProxyModel(this);
63 this->AdvancedFilter->setSourceModel(this->CacheModel);
64 this->AdvancedFilter->setFilterRole(QCMakeCacheModel::AdvancedRole);
65 this->AdvancedFilter->setFilterRegExp(AdvancedRegExp[0]);
66 this->AdvancedFilter->setDynamicSortFilter(true);
67 this->SearchFilter = new QCMakeSearchFilter(this);
68 this->SearchFilter->setSourceModel(this->AdvancedFilter);
69 this->SearchFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
70 this->SearchFilter->setDynamicSortFilter(true);
71 this->setModel(this->SearchFilter);
73 // our delegate for creating our editors
74 QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this);
75 this->setItemDelegate(delegate);
77 this->setEditTriggers(QAbstractItemView::DoubleClicked |
78 QAbstractItemView::SelectedClicked |
79 QAbstractItemView::EditKeyPressed |
80 QAbstractItemView::AnyKeyPressed);
82 // tab, backtab doesn't step through items
83 this->setTabKeyNavigation(false);
85 // set up headers and sizes
86 int h = 0;
87 QFontMetrics met(this->font());
88 h = qMax(met.height(), this->style()->pixelMetric(QStyle::PM_IndicatorHeight));
89 this->verticalHeader()->setDefaultSectionSize(h + 4);
90 this->horizontalHeader()->setStretchLastSection(true);
91 this->verticalHeader()->hide();
94 void QCMakeCacheView::showEvent(QShowEvent* e)
96 if(!this->Init)
98 // initialize the table view column size
99 int colWidth = this->columnWidth(0) + this->columnWidth(1);
100 this->setColumnWidth(0, colWidth/2);
101 this->setColumnWidth(1, colWidth/2);
102 this->Init = true;
104 return QTableView::showEvent(e);
107 QCMakeCacheModel* QCMakeCacheView::cacheModel() const
109 return this->CacheModel;
112 QModelIndex QCMakeCacheView::moveCursor(CursorAction act,
113 Qt::KeyboardModifiers mod)
115 // want home/end to go to begin/end of rows, not columns
116 if(act == MoveHome)
118 return this->model()->index(0, 1);
120 else if(act == MoveEnd)
122 return this->model()->index(this->model()->rowCount()-1, 1);
124 return QTableView::moveCursor(act, mod);
127 void QCMakeCacheView::setShowAdvanced(bool s)
129 this->AdvancedFilter->setFilterRegExp(
130 s ? AdvancedRegExp[1] : AdvancedRegExp[0]);
133 bool QCMakeCacheView::showAdvanced() const
135 return this->AdvancedFilter->filterRegExp() == AdvancedRegExp[1];
138 void QCMakeCacheView::setSearchFilter(const QString& s)
140 this->SearchFilter->setFilterFixedString(s);
143 QCMakeCacheModel::QCMakeCacheModel(QObject* p)
144 : QAbstractTableModel(p),
145 NewCount(0), EditEnabled(true)
149 QCMakeCacheModel::~QCMakeCacheModel()
153 static uint qHash(const QCMakeCacheProperty& p)
155 return qHash(p.Key);
158 void QCMakeCacheModel::clear()
160 this->setProperties(QCMakeCachePropertyList());
163 void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList& props)
165 QSet<QCMakeCacheProperty> newProps = props.toSet();
166 QSet<QCMakeCacheProperty> newProps2 = props.toSet();
167 QSet<QCMakeCacheProperty> oldProps = this->Properties.toSet();
169 oldProps.intersect(newProps);
170 newProps.subtract(oldProps);
171 newProps2.subtract(newProps);
173 this->NewCount = newProps.count();
174 this->Properties.clear();
176 this->Properties = newProps.toList();
177 qSort(this->Properties);
178 QCMakeCachePropertyList tmp = newProps2.toList();
179 qSort(tmp);
180 this->Properties += tmp;
182 this->reset();
185 QCMakeCachePropertyList QCMakeCacheModel::properties() const
187 return this->Properties;
190 void QCMakeCacheModel::setEditEnabled(bool e)
192 this->EditEnabled = e;
195 bool QCMakeCacheModel::editEnabled() const
197 return this->EditEnabled;
200 int QCMakeCacheModel::newCount() const
202 return this->NewCount;
205 int QCMakeCacheModel::columnCount (const QModelIndex& /*p*/ ) const
207 return 2;
210 QVariant QCMakeCacheModel::data (const QModelIndex& idx, int role) const
212 if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
214 return this->Properties[idx.row()].Key;
216 else if(idx.column() == 0 && role == Qt::ToolTipRole)
218 return this->data(idx, Qt::DisplayRole).toString() + "\n" +
219 this->data(idx, QCMakeCacheModel::HelpRole).toString();
221 else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
223 if(this->Properties[idx.row()].Type != QCMakeCacheProperty::BOOL)
225 return this->Properties[idx.row()].Value;
228 else if(idx.column() == 1 && role == Qt::CheckStateRole)
230 if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
232 return this->Properties[idx.row()].Value.toBool() ? Qt::Checked : Qt::Unchecked;
235 else if(role == QCMakeCacheModel::HelpRole)
237 return this->Properties[idx.row()].Help;
239 else if(role == QCMakeCacheModel::TypeRole)
241 return this->Properties[idx.row()].Type;
243 else if(role == QCMakeCacheModel::AdvancedRole)
245 return this->Properties[idx.row()].Advanced;
247 else if(role == Qt::BackgroundRole && idx.row()+1 <= this->NewCount)
249 return QBrush(QColor(255,100,100));
251 return QVariant();
254 QModelIndex QCMakeCacheModel::parent (const QModelIndex& /*idx*/) const
256 return QModelIndex();
259 int QCMakeCacheModel::rowCount (const QModelIndex& p) const
261 if(p.isValid())
263 return 0;
265 return this->Properties.count();
268 QVariant QCMakeCacheModel::headerData (int section, Qt::Orientation orient, int role) const
270 // return header labels
271 if(role == Qt::DisplayRole && orient == Qt::Horizontal)
273 return section == 0 ? "Name" : "Value";
275 return QVariant();
278 Qt::ItemFlags QCMakeCacheModel::flags (const QModelIndex& idx) const
280 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
281 // all column 1's are editable
282 if(idx.column() == 1 && this->EditEnabled)
284 f |= Qt::ItemIsEditable;
285 // booleans are editable in place
286 if(this->Properties[idx.row()].Type == QCMakeCacheProperty::BOOL)
288 f |= Qt::ItemIsUserCheckable;
291 return f;
295 bool QCMakeCacheModel::setData (const QModelIndex& idx, const QVariant& value, int role)
297 if(idx.column() == 0 && (role == Qt::DisplayRole || role == Qt::EditRole))
299 this->Properties[idx.row()].Key = value.toString();
300 emit this->dataChanged(idx, idx);
302 else if(idx.column() == 1 && (role == Qt::DisplayRole || role == Qt::EditRole))
304 this->Properties[idx.row()].Value = value.toString();
305 emit this->dataChanged(idx, idx);
307 else if(idx.column() == 1 && (role == Qt::CheckStateRole))
309 this->Properties[idx.row()].Value = value.toInt() == Qt::Checked;
310 emit this->dataChanged(idx, idx);
312 else if(role == QCMakeCacheModel::HelpRole)
314 this->Properties[idx.row()].Help = value.toString();
315 emit this->dataChanged(idx, idx);
317 else if(role == QCMakeCacheModel::TypeRole)
319 this->Properties[idx.row()].Type = static_cast<QCMakeCacheProperty::PropertyType>(value.toInt());
321 else if(role == QCMakeCacheModel::AdvancedRole)
323 this->Properties[idx.row()].Advanced = value.toBool();
325 return false;
328 QModelIndex QCMakeCacheModel::buddy(const QModelIndex& idx) const
330 if(idx.column() == 0)
332 if(this->Properties[idx.row()].Type != QCMakeCacheProperty::BOOL)
334 return this->index(idx.row(), 1);
337 return idx;
340 bool QCMakeCacheModel::removeRows(int row, int num, const QModelIndex&)
342 if(row < 0 || row+num > this->Properties.count())
344 return false;
346 this->beginRemoveRows(QModelIndex(), row, row+num-1);
347 for(int i=0; i<num; i++)
349 this->Properties.removeAt(row);
350 if(this->NewCount >= row+1)
352 this->NewCount--;
355 this->endRemoveRows();
356 return true;
359 bool QCMakeCacheModel::insertRows(int row, int num, const QModelIndex&)
361 if(row < 0)
362 row = 0;
363 if(row > this->rowCount())
364 row = this->rowCount();
366 this->beginInsertRows(QModelIndex(), row, row+num-1);
367 for(int i=0; i<num; i++)
369 this->Properties.insert(row+i, QCMakeCacheProperty());
370 if(this->NewCount >= row)
372 this->NewCount++;
375 this->endInsertRows();
376 return true;
379 QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p)
380 : QItemDelegate(p), FileDialogFlag(false)
384 void QCMakeCacheModelDelegate::setFileDialogFlag(bool f)
386 this->FileDialogFlag = f;
389 QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* p,
390 const QStyleOptionViewItem&, const QModelIndex& idx) const
392 const QAbstractItemModel* model = idx.model();
393 QModelIndex var = model->index(idx.row(), 0);
394 QVariant type = idx.data(QCMakeCacheModel::TypeRole);
395 if(type == QCMakeCacheProperty::BOOL)
397 return NULL;
399 else if(type == QCMakeCacheProperty::PATH)
401 QCMakeCachePathEditor* editor =
402 new QCMakeCachePathEditor(p,
403 var.data(Qt::DisplayRole).toString());
404 QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this,
405 SLOT(setFileDialogFlag(bool)));
406 return editor;
408 else if(type == QCMakeCacheProperty::FILEPATH)
410 QCMakeCacheFilePathEditor* editor =
411 new QCMakeCacheFilePathEditor(p,
412 var.data(Qt::DisplayRole).toString());
413 QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this,
414 SLOT(setFileDialogFlag(bool)));
415 return editor;
418 return new QLineEdit(p);
421 bool QCMakeCacheModelDelegate::editorEvent(QEvent* e, QAbstractItemModel* model,
422 const QStyleOptionViewItem& option, const QModelIndex& index)
424 Qt::ItemFlags flags = model->flags(index);
425 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
426 || !(flags & Qt::ItemIsEnabled))
428 return false;
431 QVariant value = index.data(Qt::CheckStateRole);
432 if (!value.isValid())
434 return false;
437 if ((e->type() == QEvent::MouseButtonRelease)
438 || (e->type() == QEvent::MouseButtonDblClick))
440 // eat the double click events inside the check rect
441 if (e->type() == QEvent::MouseButtonDblClick)
443 return true;
446 else if (e->type() == QEvent::KeyPress)
448 if(static_cast<QKeyEvent*>(e)->key() != Qt::Key_Space &&
449 static_cast<QKeyEvent*>(e)->key() != Qt::Key_Select)
451 return false;
454 else
456 return false;
459 Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
460 ? Qt::Unchecked : Qt::Checked);
461 return model->setData(index, state, Qt::CheckStateRole);
464 bool QCMakeCacheModelDelegate::eventFilter(QObject* object, QEvent* event)
466 // workaround for what looks like a bug in Qt on Mac OS X
467 if(event->type() == QEvent::FocusOut && this->FileDialogFlag)
469 return false;
471 return QItemDelegate::eventFilter(object, event);
475 QCMakeCacheFileEditor::QCMakeCacheFileEditor(QWidget* p, const QString& var)
476 : QLineEdit(p), Variable(var)
478 // this *is* instead of has a line edit so QAbstractItemView
479 // doesn't get confused with what the editor really is
480 this->setContentsMargins(0, 0, 0, 0);
481 this->ToolButton = new QToolButton(this);
482 this->ToolButton->setText("...");
483 this->ToolButton->setCursor(QCursor(Qt::ArrowCursor));
484 QObject::connect(this->ToolButton, SIGNAL(clicked(bool)),
485 this, SLOT(chooseFile()));
488 QCMakeCacheFilePathEditor::QCMakeCacheFilePathEditor(QWidget* p, const QString& var)
489 : QCMakeCacheFileEditor(p, var)
491 this->setCompleter(new QCMakeFileCompleter(this, false));
494 QCMakeCachePathEditor::QCMakeCachePathEditor(QWidget* p, const QString& var)
495 : QCMakeCacheFileEditor(p, var)
497 this->setCompleter(new QCMakeFileCompleter(this, true));
500 void QCMakeCacheFileEditor::resizeEvent(QResizeEvent* e)
502 // make the tool button fit on the right side
503 int h = e->size().height();
504 this->ToolButton->resize(h, h);
505 this->ToolButton->move(this->width() - h, 0);
506 this->setContentsMargins(0, 0, h, 0);
509 void QCMakeCacheFilePathEditor::chooseFile()
511 // choose a file and set it
512 QString path;
513 QFileInfo info(this->text());
514 QString title;
515 if(this->Variable.isEmpty())
517 title = tr("Select File");
519 else
521 title = tr("Select File for %1");
522 title = title.arg(this->Variable);
524 this->fileDialogExists(true);
525 path = QFileDialog::getOpenFileName(this, title, info.absolutePath());
526 this->fileDialogExists(false);
528 if(!path.isEmpty())
530 this->setText(QDir::fromNativeSeparators(path));
534 void QCMakeCachePathEditor::chooseFile()
536 // choose a file and set it
537 QString path;
538 QString title;
539 if(this->Variable.isEmpty())
541 title = tr("Select Path");
543 else
545 title = tr("Select Path for %1");
546 title = title.arg(this->Variable);
548 this->fileDialogExists(true);
549 path = QFileDialog::getExistingDirectory(this, title, this->text());
550 this->fileDialogExists(false);
551 if(!path.isEmpty())
553 this->setText(QDir::fromNativeSeparators(path));
557 QCMakeFileCompleter::QCMakeFileCompleter(QObject* o, bool dirs)
558 : QCompleter(o)
560 QDirModel* model = new QDirModel(this);
561 if(dirs)
563 model->setFilter(QDir::AllDirs | QDir::Drives | QDir::NoDotAndDotDot);
565 this->setModel(model);
568 QString QCMakeFileCompleter::pathFromIndex(const QModelIndex& idx) const
570 return QDir::fromNativeSeparators(QCompleter::pathFromIndex(idx));