1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: QCMakeCacheView.cxx,v $
6 Date: $Date: 2008/02/15 00:58:31 $
7 Version: $Revision: 1.26 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
18 #include "QCMakeCacheView.h"
20 #include <QToolButton>
21 #include <QFileDialog>
22 #include <QHBoxLayout>
23 #include <QHeaderView>
31 static QRegExp AdvancedRegExp
[2] = { QRegExp("(false)"), QRegExp("(true|false)") };
33 // filter for searches
34 class QCMakeSearchFilter
: public QSortFilterProxyModel
37 QCMakeSearchFilter(QObject
* o
) : QSortFilterProxyModel(o
) {}
39 bool filterAcceptsRow(int row
, const QModelIndex
& p
) const
41 // accept row if either column matches
42 QModelIndex idx0
= this->sourceModel()->index(row
, 0, p
);
43 QModelIndex idx1
= this->sourceModel()->index(row
, 1, p
);
44 QString str0
= this->sourceModel()->data(idx0
).toString();
45 QString str1
= this->sourceModel()->data(idx1
).toString();
47 return str0
.contains(this->filterRegExp()) ||
48 str1
.contains(this->filterRegExp());
52 QCMakeCacheView::QCMakeCacheView(QWidget
* p
)
53 : QTableView(p
), Init(false)
55 // hook up our model and search/filter proxies
56 this->CacheModel
= new QCMakeCacheModel(this);
57 this->AdvancedFilter
= new QSortFilterProxyModel(this);
58 this->AdvancedFilter
->setSourceModel(this->CacheModel
);
59 this->AdvancedFilter
->setFilterRole(QCMakeCacheModel::AdvancedRole
);
60 this->AdvancedFilter
->setFilterRegExp(AdvancedRegExp
[0]);
61 this->AdvancedFilter
->setDynamicSortFilter(true);
62 this->SearchFilter
= new QCMakeSearchFilter(this);
63 this->SearchFilter
->setSourceModel(this->AdvancedFilter
);
64 this->SearchFilter
->setFilterCaseSensitivity(Qt::CaseInsensitive
);
65 this->SearchFilter
->setDynamicSortFilter(true);
66 this->setModel(this->SearchFilter
);
68 // our delegate for creating our editors
69 QCMakeCacheModelDelegate
* delegate
= new QCMakeCacheModelDelegate(this);
70 this->setItemDelegate(delegate
);
72 this->setEditTriggers(QAbstractItemView::DoubleClicked
|
73 QAbstractItemView::SelectedClicked
|
74 QAbstractItemView::EditKeyPressed
|
75 QAbstractItemView::AnyKeyPressed
);
77 // tab, backtab doesn't step through items
78 this->setTabKeyNavigation(false);
80 // set up headers and sizes
82 QFontMetrics
met(this->font());
83 h
= qMax(met
.height(), this->style()->pixelMetric(QStyle::PM_IndicatorHeight
));
84 this->verticalHeader()->setDefaultSectionSize(h
+ 4);
85 this->horizontalHeader()->setStretchLastSection(true);
86 this->verticalHeader()->hide();
89 void QCMakeCacheView::showEvent(QShowEvent
* e
)
93 // initialize the table view column size
94 int colWidth
= this->columnWidth(0) + this->columnWidth(1);
95 this->setColumnWidth(0, colWidth
/2);
96 this->setColumnWidth(1, colWidth
/2);
99 return QTableView::showEvent(e
);
102 QCMakeCacheModel
* QCMakeCacheView::cacheModel() const
104 return this->CacheModel
;
107 QModelIndex
QCMakeCacheView::moveCursor(CursorAction act
,
108 Qt::KeyboardModifiers mod
)
110 // want home/end to go to begin/end of rows, not columns
113 return this->model()->index(0, 1);
115 else if(act
== MoveEnd
)
117 return this->model()->index(this->model()->rowCount()-1, 1);
119 return QTableView::moveCursor(act
, mod
);
122 void QCMakeCacheView::setShowAdvanced(bool s
)
124 this->AdvancedFilter
->setFilterRegExp(
125 s
? AdvancedRegExp
[1] : AdvancedRegExp
[0]);
128 bool QCMakeCacheView::showAdvanced() const
130 return this->AdvancedFilter
->filterRegExp() == AdvancedRegExp
[1];
133 void QCMakeCacheView::setSearchFilter(const QString
& s
)
135 this->SearchFilter
->setFilterFixedString(s
);
138 QCMakeCacheModel::QCMakeCacheModel(QObject
* p
)
139 : QAbstractTableModel(p
),
140 NewCount(0), EditEnabled(true)
144 QCMakeCacheModel::~QCMakeCacheModel()
148 static uint
qHash(const QCMakeCacheProperty
& p
)
153 void QCMakeCacheModel::clear()
155 this->setProperties(QCMakeCachePropertyList());
158 void QCMakeCacheModel::setProperties(const QCMakeCachePropertyList
& props
)
160 QSet
<QCMakeCacheProperty
> newProps
= props
.toSet();
161 QSet
<QCMakeCacheProperty
> newProps2
= props
.toSet();
162 QSet
<QCMakeCacheProperty
> oldProps
= this->Properties
.toSet();
164 oldProps
.intersect(newProps
);
165 newProps
.subtract(oldProps
);
166 newProps2
.subtract(newProps
);
168 this->NewCount
= newProps
.count();
169 this->Properties
.clear();
171 this->Properties
= newProps
.toList();
172 qSort(this->Properties
);
173 QCMakeCachePropertyList tmp
= newProps2
.toList();
175 this->Properties
+= tmp
;
180 QCMakeCachePropertyList
QCMakeCacheModel::properties() const
182 return this->Properties
;
185 void QCMakeCacheModel::setEditEnabled(bool e
)
187 this->EditEnabled
= e
;
190 bool QCMakeCacheModel::editEnabled() const
192 return this->EditEnabled
;
195 int QCMakeCacheModel::newCount() const
197 return this->NewCount
;
200 int QCMakeCacheModel::columnCount (const QModelIndex
& /*p*/ ) const
205 QVariant
QCMakeCacheModel::data (const QModelIndex
& idx
, int role
) const
207 if(idx
.column() == 0 && (role
== Qt::DisplayRole
|| role
== Qt::EditRole
))
209 return this->Properties
[idx
.row()].Key
;
211 else if(idx
.column() == 0 && role
== Qt::ToolTipRole
)
213 return this->data(idx
, Qt::DisplayRole
).toString() + "\n" +
214 this->data(idx
, QCMakeCacheModel::HelpRole
).toString();
216 else if(idx
.column() == 1 && (role
== Qt::DisplayRole
|| role
== Qt::EditRole
))
218 if(this->Properties
[idx
.row()].Type
!= QCMakeCacheProperty::BOOL
)
220 return this->Properties
[idx
.row()].Value
;
223 else if(idx
.column() == 1 && role
== Qt::CheckStateRole
)
225 if(this->Properties
[idx
.row()].Type
== QCMakeCacheProperty::BOOL
)
227 return this->Properties
[idx
.row()].Value
.toBool() ? Qt::Checked
: Qt::Unchecked
;
230 else if(role
== QCMakeCacheModel::HelpRole
)
232 return this->Properties
[idx
.row()].Help
;
234 else if(role
== QCMakeCacheModel::TypeRole
)
236 return this->Properties
[idx
.row()].Type
;
238 else if(role
== QCMakeCacheModel::AdvancedRole
)
240 return this->Properties
[idx
.row()].Advanced
;
242 else if(role
== Qt::BackgroundRole
&& idx
.row()+1 <= this->NewCount
)
244 return QBrush(QColor(255,100,100));
249 QModelIndex
QCMakeCacheModel::parent (const QModelIndex
& /*idx*/) const
251 return QModelIndex();
254 int QCMakeCacheModel::rowCount (const QModelIndex
& p
) const
260 return this->Properties
.count();
263 QVariant
QCMakeCacheModel::headerData (int section
, Qt::Orientation orient
, int role
) const
265 // return header labels
266 if(role
== Qt::DisplayRole
&& orient
== Qt::Horizontal
)
268 return section
== 0 ? "Name" : "Value";
273 Qt::ItemFlags
QCMakeCacheModel::flags (const QModelIndex
& idx
) const
275 Qt::ItemFlags f
= Qt::ItemIsEnabled
| Qt::ItemIsSelectable
;
276 // all column 1's are editable
277 if(idx
.column() == 1 && this->EditEnabled
)
279 f
|= Qt::ItemIsEditable
;
280 // booleans are editable in place
281 if(this->Properties
[idx
.row()].Type
== QCMakeCacheProperty::BOOL
)
283 f
|= Qt::ItemIsUserCheckable
;
290 bool QCMakeCacheModel::setData (const QModelIndex
& idx
, const QVariant
& value
, int role
)
292 if(idx
.column() == 0 && (role
== Qt::DisplayRole
|| role
== Qt::EditRole
))
294 this->Properties
[idx
.row()].Key
= value
.toString();
295 emit
this->dataChanged(idx
, idx
);
297 else if(idx
.column() == 1 && (role
== Qt::DisplayRole
|| role
== Qt::EditRole
))
299 this->Properties
[idx
.row()].Value
= value
.toString();
300 emit
this->dataChanged(idx
, idx
);
302 else if(idx
.column() == 1 && (role
== Qt::CheckStateRole
))
304 this->Properties
[idx
.row()].Value
= value
.toInt() == Qt::Checked
;
305 emit
this->dataChanged(idx
, idx
);
307 else if(role
== QCMakeCacheModel::HelpRole
)
309 this->Properties
[idx
.row()].Help
= value
.toString();
310 emit
this->dataChanged(idx
, idx
);
312 else if(role
== QCMakeCacheModel::TypeRole
)
314 this->Properties
[idx
.row()].Type
= static_cast<QCMakeCacheProperty::PropertyType
>(value
.toInt());
316 else if(role
== QCMakeCacheModel::AdvancedRole
)
318 this->Properties
[idx
.row()].Advanced
= value
.toBool();
323 QModelIndex
QCMakeCacheModel::buddy(const QModelIndex
& idx
) const
325 if(idx
.column() == 0)
327 if(this->Properties
[idx
.row()].Type
!= QCMakeCacheProperty::BOOL
)
329 return this->index(idx
.row(), 1);
335 bool QCMakeCacheModel::removeRows(int row
, int num
, const QModelIndex
&)
337 if(row
< 0 || row
+num
> this->Properties
.count())
341 this->beginRemoveRows(QModelIndex(), row
, row
+num
-1);
342 for(int i
=0; i
<num
; i
++)
344 this->Properties
.removeAt(row
);
345 if(this->NewCount
>= row
+1)
350 this->endRemoveRows();
354 bool QCMakeCacheModel::insertRows(int row
, int num
, const QModelIndex
&)
358 if(row
> this->rowCount())
359 row
= this->rowCount();
361 this->beginInsertRows(QModelIndex(), row
, row
+num
-1);
362 for(int i
=0; i
<num
; i
++)
364 this->Properties
.insert(row
+i
, QCMakeCacheProperty());
365 if(this->NewCount
>= row
)
370 this->endInsertRows();
374 QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject
* p
)
379 QWidget
* QCMakeCacheModelDelegate::createEditor(QWidget
* p
,
380 const QStyleOptionViewItem
&, const QModelIndex
& idx
) const
382 const QAbstractItemModel
* model
= idx
.model();
383 QModelIndex var
= model
->index(idx
.row(), 0);
384 QVariant type
= idx
.data(QCMakeCacheModel::TypeRole
);
385 if(type
== QCMakeCacheProperty::BOOL
)
389 else if(type
== QCMakeCacheProperty::PATH
)
391 return new QCMakeCachePathEditor(p
,
392 var
.data(Qt::DisplayRole
).toString());
394 else if(type
== QCMakeCacheProperty::FILEPATH
)
396 return new QCMakeCacheFilePathEditor(p
,
397 var
.data(Qt::DisplayRole
).toString());
400 return new QLineEdit(p
);
403 bool QCMakeCacheModelDelegate::editorEvent(QEvent
* e
, QAbstractItemModel
* model
,
404 const QStyleOptionViewItem
& option
, const QModelIndex
& index
)
406 Qt::ItemFlags flags
= model
->flags(index
);
407 if (!(flags
& Qt::ItemIsUserCheckable
) || !(option
.state
& QStyle::State_Enabled
)
408 || !(flags
& Qt::ItemIsEnabled
))
413 QVariant value
= index
.data(Qt::CheckStateRole
);
414 if (!value
.isValid())
419 if ((e
->type() == QEvent::MouseButtonRelease
)
420 || (e
->type() == QEvent::MouseButtonDblClick
))
422 // eat the double click events inside the check rect
423 if (e
->type() == QEvent::MouseButtonDblClick
)
428 else if (e
->type() == QEvent::KeyPress
)
430 if(static_cast<QKeyEvent
*>(e
)->key() != Qt::Key_Space
&&
431 static_cast<QKeyEvent
*>(e
)->key() != Qt::Key_Select
)
441 Qt::CheckState state
= (static_cast<Qt::CheckState
>(value
.toInt()) == Qt::Checked
442 ? Qt::Unchecked
: Qt::Checked
);
443 return model
->setData(index
, state
, Qt::CheckStateRole
);
446 QCMakeCacheFileEditor::QCMakeCacheFileEditor(QWidget
* p
, const QString
& var
)
447 : QLineEdit(p
), Variable(var
)
449 // this *is* instead of has a line edit so QAbstractItemView
450 // doesn't get confused with what the editor really is
451 this->setContentsMargins(0, 0, 0, 0);
452 this->ToolButton
= new QToolButton(this);
453 this->ToolButton
->setText("...");
454 this->ToolButton
->setCursor(QCursor(Qt::ArrowCursor
));
455 QObject::connect(this->ToolButton
, SIGNAL(clicked(bool)),
456 this, SLOT(chooseFile()));
459 QCMakeCacheFilePathEditor::QCMakeCacheFilePathEditor(QWidget
* p
, const QString
& var
)
460 : QCMakeCacheFileEditor(p
, var
)
462 this->setCompleter(new QCMakeFileCompleter(this, false));
465 QCMakeCachePathEditor::QCMakeCachePathEditor(QWidget
* p
, const QString
& var
)
466 : QCMakeCacheFileEditor(p
, var
)
468 this->setCompleter(new QCMakeFileCompleter(this, true));
471 void QCMakeCacheFileEditor::resizeEvent(QResizeEvent
* e
)
473 // make the tool button fit on the right side
474 int h
= e
->size().height();
475 this->ToolButton
->resize(h
, h
);
476 this->ToolButton
->move(this->width() - h
, 0);
477 this->setContentsMargins(0, 0, h
, 0);
480 void QCMakeCacheFilePathEditor::chooseFile()
482 // choose a file and set it
484 QFileInfo
info(this->text());
486 if(this->Variable
.isEmpty())
488 title
= tr("Select File");
492 title
= tr("Select File for %1");
493 title
= title
.arg(this->Variable
);
495 path
= QFileDialog::getOpenFileName(this, title
, info
.absolutePath());
499 this->setText(QDir::fromNativeSeparators(path
));
503 void QCMakeCachePathEditor::chooseFile()
505 // choose a file and set it
508 if(this->Variable
.isEmpty())
510 title
= tr("Select Path");
514 title
= tr("Select Path for %1");
515 title
= title
.arg(this->Variable
);
517 path
= QFileDialog::getExistingDirectory(this, title
, this->text());
520 this->setText(QDir::fromNativeSeparators(path
));
524 QCMakeFileCompleter::QCMakeFileCompleter(QObject
* o
, bool dirs
)
527 QDirModel
* model
= new QDirModel(this);
530 model
->setFilter(QDir::AllDirs
| QDir::Drives
| QDir::NoDotAndDotDot
);
532 this->setModel(model
);
535 QString
QCMakeFileCompleter::pathFromIndex(const QModelIndex
& idx
) const
537 return QDir::fromNativeSeparators(QCompleter::pathFromIndex(idx
));