1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: QCMakeCacheView.cxx,v $
6 <<<<<<< QCMakeCacheView.cxx
7 Date: $Date: 2008/02/15 00:58:31 $
8 Version: $Revision: 1.26 $
10 Date: $Date: 2008-04-02 19:28:17 $
11 Version: $Revision: 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>
36 static QRegExp AdvancedRegExp
[2] = { QRegExp("(false)"), QRegExp("(true|false)") };
38 // filter for searches
39 class QCMakeSearchFilter
: public QSortFilterProxyModel
42 QCMakeSearchFilter(QObject
* o
) : QSortFilterProxyModel(o
) {}
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
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
)
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);
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
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
)
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();
180 this->Properties
+= tmp
;
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
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));
254 QModelIndex
QCMakeCacheModel::parent (const QModelIndex
& /*idx*/) const
256 return QModelIndex();
259 int QCMakeCacheModel::rowCount (const QModelIndex
& p
) const
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";
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
;
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();
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);
340 bool QCMakeCacheModel::removeRows(int row
, int num
, const QModelIndex
&)
342 if(row
< 0 || row
+num
> this->Properties
.count())
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)
355 this->endRemoveRows();
359 bool QCMakeCacheModel::insertRows(int row
, int num
, const QModelIndex
&)
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
)
375 this->endInsertRows();
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
)
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)));
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)));
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
))
431 QVariant value
= index
.data(Qt::CheckStateRole
);
432 if (!value
.isValid())
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
)
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
)
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
)
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
513 QFileInfo
info(this->text());
515 if(this->Variable
.isEmpty())
517 title
= tr("Select File");
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);
530 this->setText(QDir::fromNativeSeparators(path
));
534 void QCMakeCachePathEditor::chooseFile()
536 // choose a file and set it
539 if(this->Variable
.isEmpty())
541 title
= tr("Select Path");
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);
553 this->setText(QDir::fromNativeSeparators(path
));
557 QCMakeFileCompleter::QCMakeFileCompleter(QObject
* o
, bool dirs
)
560 QDirModel
* model
= new QDirModel(this);
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
));