author | Eckhart Koeppen <eckhart.koppen@nokia.com> |
Wed, 21 Apr 2010 11:15:19 +0300 | |
branch | RCL_3 |
changeset 11 | 25a739ee40f4 |
parent 4 | 3b1da2848fc7 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the QtGui module of the Qt Toolkit. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** No Commercial Usage |
|
11 |
** This file contains pre-release code and may not be distributed. |
|
12 |
** You may use this file in accordance with the terms and conditions |
|
13 |
** contained in the Technology Preview License Agreement accompanying |
|
14 |
** this package. |
|
15 |
** |
|
16 |
** GNU Lesser General Public License Usage |
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 |
** General Public License version 2.1 as published by the Free Software |
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 |
** packaging of this file. Please review the following information to |
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 |
** |
|
24 |
** In addition, as a special exception, Nokia gives you certain additional |
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 |
** |
|
28 |
** If you have questions regarding the use of this file, please contact |
|
29 |
** Nokia at qt-info@nokia.com. |
|
30 |
** |
|
31 |
** |
|
32 |
** |
|
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
38 |
** $QT_END_LICENSE$ |
|
39 |
** |
|
40 |
****************************************************************************/ |
|
41 |
||
42 |
#include "qdatawidgetmapper.h" |
|
43 |
||
44 |
#ifndef QT_NO_DATAWIDGETMAPPER |
|
45 |
||
46 |
#include "qabstractitemmodel.h" |
|
47 |
#include "qitemdelegate.h" |
|
48 |
#include "qmetaobject.h" |
|
49 |
#include "qwidget.h" |
|
50 |
#include "private/qobject_p.h" |
|
51 |
#include "private/qabstractitemmodel_p.h" |
|
52 |
||
53 |
QT_BEGIN_NAMESPACE |
|
54 |
||
55 |
class QDataWidgetMapperPrivate: public QObjectPrivate |
|
56 |
{ |
|
57 |
public: |
|
58 |
Q_DECLARE_PUBLIC(QDataWidgetMapper) |
|
59 |
||
60 |
QDataWidgetMapperPrivate() |
|
61 |
: model(QAbstractItemModelPrivate::staticEmptyModel()), delegate(0), |
|
62 |
orientation(Qt::Horizontal), submitPolicy(QDataWidgetMapper::AutoSubmit) |
|
63 |
{ |
|
64 |
} |
|
65 |
||
66 |
QAbstractItemModel *model; |
|
67 |
QAbstractItemDelegate *delegate; |
|
68 |
Qt::Orientation orientation; |
|
69 |
QDataWidgetMapper::SubmitPolicy submitPolicy; |
|
70 |
QPersistentModelIndex rootIndex; |
|
71 |
QPersistentModelIndex currentTopLeft; |
|
72 |
||
73 |
inline int itemCount() |
|
74 |
{ |
|
75 |
return orientation == Qt::Horizontal |
|
76 |
? model->rowCount(rootIndex) |
|
77 |
: model->columnCount(rootIndex); |
|
78 |
} |
|
79 |
||
80 |
inline int currentIdx() const |
|
81 |
{ |
|
82 |
return orientation == Qt::Horizontal ? currentTopLeft.row() : currentTopLeft.column(); |
|
83 |
} |
|
84 |
||
85 |
inline QModelIndex indexAt(int itemPos) |
|
86 |
{ |
|
87 |
return orientation == Qt::Horizontal |
|
88 |
? model->index(currentIdx(), itemPos, rootIndex) |
|
89 |
: model->index(itemPos, currentIdx(), rootIndex); |
|
90 |
} |
|
91 |
||
92 |
inline void flipEventFilters(QAbstractItemDelegate *oldDelegate, |
|
93 |
QAbstractItemDelegate *newDelegate) |
|
94 |
{ |
|
95 |
for (int i = 0; i < widgetMap.count(); ++i) { |
|
96 |
QWidget *w = widgetMap.at(i).widget; |
|
97 |
if (!w) |
|
98 |
continue; |
|
99 |
w->removeEventFilter(oldDelegate); |
|
100 |
w->installEventFilter(newDelegate); |
|
101 |
} |
|
102 |
} |
|
103 |
||
104 |
void populate(); |
|
105 |
||
106 |
// private slots |
|
107 |
void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); |
|
108 |
void _q_commitData(QWidget *); |
|
109 |
void _q_closeEditor(QWidget *, QAbstractItemDelegate::EndEditHint); |
|
110 |
void _q_modelDestroyed(); |
|
111 |
||
112 |
struct WidgetMapper |
|
113 |
{ |
|
114 |
inline WidgetMapper(QWidget *w = 0, int c = 0, const QModelIndex &i = QModelIndex()) |
|
115 |
: widget(w), section(c), currentIndex(i) {} |
|
116 |
inline WidgetMapper(QWidget *w, int c, const QModelIndex &i, const QByteArray &p) |
|
117 |
: widget(w), section(c), currentIndex(i), property(p) {} |
|
118 |
||
119 |
QPointer<QWidget> widget; |
|
120 |
int section; |
|
121 |
QPersistentModelIndex currentIndex; |
|
122 |
QByteArray property; |
|
123 |
}; |
|
124 |
||
125 |
void populate(WidgetMapper &m); |
|
126 |
int findWidget(QWidget *w) const; |
|
127 |
||
128 |
bool commit(const WidgetMapper &m); |
|
129 |
||
130 |
QList<WidgetMapper> widgetMap; |
|
131 |
}; |
|
132 |
||
133 |
int QDataWidgetMapperPrivate::findWidget(QWidget *w) const |
|
134 |
{ |
|
135 |
for (int i = 0; i < widgetMap.count(); ++i) { |
|
136 |
if (widgetMap.at(i).widget == w) |
|
137 |
return i; |
|
138 |
} |
|
139 |
return -1; |
|
140 |
} |
|
141 |
||
142 |
bool QDataWidgetMapperPrivate::commit(const WidgetMapper &m) |
|
143 |
{ |
|
144 |
if (m.widget.isNull()) |
|
145 |
return true; // just ignore |
|
146 |
||
147 |
if (!m.currentIndex.isValid()) |
|
148 |
return false; |
|
149 |
||
150 |
// Create copy to avoid passing the widget mappers data |
|
151 |
QModelIndex idx = m.currentIndex; |
|
152 |
if (m.property.isEmpty()) |
|
153 |
delegate->setModelData(m.widget, model, idx); |
|
154 |
else |
|
155 |
model->setData(idx, m.widget->property(m.property), Qt::EditRole); |
|
156 |
||
157 |
return true; |
|
158 |
} |
|
159 |
||
160 |
void QDataWidgetMapperPrivate::populate(WidgetMapper &m) |
|
161 |
{ |
|
162 |
if (m.widget.isNull()) |
|
163 |
return; |
|
164 |
||
165 |
m.currentIndex = indexAt(m.section); |
|
166 |
if (m.property.isEmpty()) |
|
167 |
delegate->setEditorData(m.widget, m.currentIndex); |
|
168 |
else |
|
169 |
m.widget->setProperty(m.property, m.currentIndex.data(Qt::EditRole)); |
|
170 |
} |
|
171 |
||
172 |
void QDataWidgetMapperPrivate::populate() |
|
173 |
{ |
|
174 |
for (int i = 0; i < widgetMap.count(); ++i) |
|
175 |
populate(widgetMap[i]); |
|
176 |
} |
|
177 |
||
178 |
static bool qContainsIndex(const QModelIndex &idx, const QModelIndex &topLeft, |
|
179 |
const QModelIndex &bottomRight) |
|
180 |
{ |
|
181 |
return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row() |
|
182 |
&& idx.column() >= topLeft.column() && idx.column() <= bottomRight.column(); |
|
183 |
} |
|
184 |
||
185 |
void QDataWidgetMapperPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
|
186 |
{ |
|
187 |
if (topLeft.parent() != rootIndex) |
|
188 |
return; // not in our hierarchy |
|
189 |
||
190 |
for (int i = 0; i < widgetMap.count(); ++i) { |
|
191 |
WidgetMapper &m = widgetMap[i]; |
|
192 |
if (qContainsIndex(m.currentIndex, topLeft, bottomRight)) |
|
193 |
populate(m); |
|
194 |
} |
|
195 |
} |
|
196 |
||
197 |
void QDataWidgetMapperPrivate::_q_commitData(QWidget *w) |
|
198 |
{ |
|
199 |
if (submitPolicy == QDataWidgetMapper::ManualSubmit) |
|
200 |
return; |
|
201 |
||
202 |
int idx = findWidget(w); |
|
203 |
if (idx == -1) |
|
204 |
return; // not our widget |
|
205 |
||
206 |
commit(widgetMap.at(idx)); |
|
207 |
} |
|
208 |
||
209 |
class QFocusHelper: public QWidget |
|
210 |
{ |
|
211 |
public: |
|
212 |
bool focusNextPrevChild(bool next) |
|
213 |
{ |
|
214 |
return QWidget::focusNextPrevChild(next); |
|
215 |
} |
|
216 |
||
217 |
static inline void focusNextPrevChild(QWidget *w, bool next) |
|
218 |
{ |
|
219 |
static_cast<QFocusHelper *>(w)->focusNextPrevChild(next); |
|
220 |
} |
|
221 |
}; |
|
222 |
||
223 |
void QDataWidgetMapperPrivate::_q_closeEditor(QWidget *w, QAbstractItemDelegate::EndEditHint hint) |
|
224 |
{ |
|
225 |
int idx = findWidget(w); |
|
226 |
if (idx == -1) |
|
227 |
return; // not our widget |
|
228 |
||
229 |
switch (hint) { |
|
230 |
case QAbstractItemDelegate::RevertModelCache: { |
|
231 |
populate(widgetMap[idx]); |
|
232 |
break; } |
|
233 |
case QAbstractItemDelegate::EditNextItem: |
|
234 |
QFocusHelper::focusNextPrevChild(w, true); |
|
235 |
break; |
|
236 |
case QAbstractItemDelegate::EditPreviousItem: |
|
237 |
QFocusHelper::focusNextPrevChild(w, false); |
|
238 |
break; |
|
239 |
case QAbstractItemDelegate::SubmitModelCache: |
|
240 |
case QAbstractItemDelegate::NoHint: |
|
241 |
// nothing |
|
242 |
break; |
|
243 |
} |
|
244 |
} |
|
245 |
||
246 |
void QDataWidgetMapperPrivate::_q_modelDestroyed() |
|
247 |
{ |
|
248 |
Q_Q(QDataWidgetMapper); |
|
249 |
||
250 |
model = 0; |
|
251 |
q->setModel(QAbstractItemModelPrivate::staticEmptyModel()); |
|
252 |
} |
|
253 |
||
254 |
/*! |
|
255 |
\class QDataWidgetMapper |
|
256 |
\brief The QDataWidgetMapper class provides mapping between a section |
|
257 |
of a data model to widgets. |
|
258 |
\since 4.2 |
|
259 |
\ingroup model-view |
|
260 |
\ingroup advanced |
|
261 |
||
262 |
QDataWidgetMapper can be used to create data-aware widgets by mapping |
|
263 |
them to sections of an item model. A section is a column of a model |
|
264 |
if the orientation is horizontal (the default), otherwise a row. |
|
265 |
||
266 |
Every time the current index changes, each widget is updated with data |
|
267 |
from the model via the property specified when its mapping was made. |
|
268 |
If the user edits the contents of a widget, the changes are read using |
|
269 |
the same property and written back to the model. |
|
270 |
By default, each widget's \l{Q_PROPERTY()}{user property} is used to |
|
271 |
transfer data between the model and the widget. Since Qt 4.3, an |
|
272 |
additional addMapping() function enables a named property to be used |
|
273 |
instead of the default user property. |
|
274 |
||
275 |
It is possible to set an item delegate to support custom widgets. By default, |
|
276 |
a QItemDelegate is used to synchronize the model with the widgets. |
|
277 |
||
278 |
Let us assume that we have an item model named \c{model} with the following contents: |
|
279 |
||
280 |
\table |
|
281 |
\row \o 1 \o Qt Norway \o Oslo |
|
282 |
\row \o 2 \o Qt Australia \o Brisbane |
|
283 |
\row \o 3 \o Qt USA \o Palo Alto |
|
284 |
\row \o 4 \o Qt China \o Beijing |
|
285 |
\row \o 5 \o Qt Germany \o Berlin |
|
286 |
\endtable |
|
287 |
||
288 |
The following code will map the columns of the model to widgets called \c mySpinBox, |
|
289 |
\c myLineEdit and \c{myCountryChooser}: |
|
290 |
||
291 |
\snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 0 |
|
292 |
||
293 |
After the call to toFirst(), \c mySpinBox displays the value \c{1}, \c myLineEdit |
|
294 |
displays \c {Nokia Corporation and/or its subsidiary(-ies)} and \c myCountryChooser displays \c{Oslo}. The |
|
295 |
navigational functions toFirst(), toNext(), toPrevious(), toLast() and setCurrentIndex() |
|
296 |
can be used to navigate in the model and update the widgets with contents from |
|
297 |
the model. |
|
298 |
||
299 |
The setRootIndex() function enables a particular item in a model to be |
|
300 |
specified as the root index - children of this item will be mapped to |
|
301 |
the relevant widgets in the user interface. |
|
302 |
||
303 |
QDataWidgetMapper supports two submit policies, \c AutoSubmit and \c{ManualSubmit}. |
|
304 |
\c AutoSubmit will update the model as soon as the current widget loses focus, |
|
305 |
\c ManualSubmit will not update the model unless submit() is called. \c ManualSubmit |
|
306 |
is useful when displaying a dialog that lets the user cancel all modifications. |
|
307 |
Also, other views that display the model won't update until the user finishes |
|
308 |
all their modifications and submits. |
|
309 |
||
310 |
Note that QDataWidgetMapper keeps track of external modifications. If the contents |
|
311 |
of the model are updated in another module of the application, the widgets are |
|
312 |
updated as well. |
|
313 |
||
314 |
\sa QAbstractItemModel, QAbstractItemDelegate |
|
315 |
*/ |
|
316 |
||
317 |
/*! \enum QDataWidgetMapper::SubmitPolicy |
|
318 |
||
319 |
This enum describes the possible submit policies a QDataWidgetMapper |
|
320 |
supports. |
|
321 |
||
322 |
\value AutoSubmit Whenever a widget loses focus, the widget's current |
|
323 |
value is set to the item model. |
|
324 |
\value ManualSubmit The model is not updated until submit() is called. |
|
325 |
*/ |
|
326 |
||
327 |
/*! |
|
328 |
\fn void QDataWidgetMapper::currentIndexChanged(int index) |
|
329 |
||
330 |
This signal is emitted after the current index has changed and |
|
331 |
all widgets were populated with new data. \a index is the new |
|
332 |
current index. |
|
333 |
||
334 |
\sa currentIndex(), setCurrentIndex() |
|
335 |
*/ |
|
336 |
||
337 |
/*! |
|
338 |
Constructs a new QDataWidgetMapper with parent object \a parent. |
|
339 |
By default, the orientation is horizontal and the submit policy |
|
340 |
is \c{AutoSubmit}. |
|
341 |
||
342 |
\sa setOrientation(), setSubmitPolicy() |
|
343 |
*/ |
|
344 |
QDataWidgetMapper::QDataWidgetMapper(QObject *parent) |
|
345 |
: QObject(*new QDataWidgetMapperPrivate, parent) |
|
346 |
{ |
|
347 |
setItemDelegate(new QItemDelegate(this)); |
|
348 |
} |
|
349 |
||
350 |
/*! |
|
351 |
Destroys the object. |
|
352 |
*/ |
|
353 |
QDataWidgetMapper::~QDataWidgetMapper() |
|
354 |
{ |
|
355 |
} |
|
356 |
||
357 |
/*! |
|
358 |
Sets the current model to \a model. If another model was set, |
|
359 |
all mappings to that old model are cleared. |
|
360 |
||
361 |
\sa model() |
|
362 |
*/ |
|
363 |
void QDataWidgetMapper::setModel(QAbstractItemModel *model) |
|
364 |
{ |
|
365 |
Q_D(QDataWidgetMapper); |
|
366 |
||
367 |
if (d->model == model) |
|
368 |
return; |
|
369 |
||
370 |
if (d->model) { |
|
371 |
disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, |
|
372 |
SLOT(_q_dataChanged(QModelIndex,QModelIndex))); |
|
373 |
disconnect(d->model, SIGNAL(destroyed()), this, |
|
374 |
SLOT(_q_modelDestroyed())); |
|
375 |
} |
|
376 |
clearMapping(); |
|
377 |
d->rootIndex = QModelIndex(); |
|
378 |
d->currentTopLeft = QModelIndex(); |
|
379 |
||
380 |
d->model = model; |
|
381 |
||
382 |
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), |
|
383 |
SLOT(_q_dataChanged(QModelIndex,QModelIndex))); |
|
384 |
connect(model, SIGNAL(destroyed()), SLOT(_q_modelDestroyed())); |
|
385 |
} |
|
386 |
||
387 |
/*! |
|
388 |
Returns the current model. |
|
389 |
||
390 |
\sa setModel() |
|
391 |
*/ |
|
392 |
QAbstractItemModel *QDataWidgetMapper::model() const |
|
393 |
{ |
|
394 |
Q_D(const QDataWidgetMapper); |
|
395 |
return d->model == QAbstractItemModelPrivate::staticEmptyModel() |
|
396 |
? static_cast<QAbstractItemModel *>(0) |
|
397 |
: d->model; |
|
398 |
} |
|
399 |
||
400 |
/*! |
|
401 |
Sets the item delegate to \a delegate. The delegate will be used to write |
|
402 |
data from the model into the widget and from the widget to the model, |
|
403 |
using QAbstractItemDelegate::setEditorData() and QAbstractItemDelegate::setModelData(). |
|
404 |
||
405 |
The delegate also decides when to apply data and when to change the editor, |
|
406 |
using QAbstractItemDelegate::commitData() and QAbstractItemDelegate::closeEditor(). |
|
407 |
||
408 |
\warning You should not share the same instance of a delegate between widget mappers |
|
409 |
or views. Doing so can cause incorrect or unintuitive editing behavior since each |
|
410 |
view connected to a given delegate may receive the \l{QAbstractItemDelegate::}{closeEditor()} |
|
411 |
signal, and attempt to access, modify or close an editor that has already been closed. |
|
412 |
*/ |
|
413 |
void QDataWidgetMapper::setItemDelegate(QAbstractItemDelegate *delegate) |
|
414 |
{ |
|
415 |
Q_D(QDataWidgetMapper); |
|
416 |
QAbstractItemDelegate *oldDelegate = d->delegate; |
|
417 |
if (oldDelegate) { |
|
418 |
disconnect(oldDelegate, SIGNAL(commitData(QWidget*)), this, SLOT(_q_commitData(QWidget*))); |
|
419 |
disconnect(oldDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
420 |
this, SLOT(_q_closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
421 |
} |
|
422 |
||
423 |
d->delegate = delegate; |
|
424 |
||
425 |
if (delegate) { |
|
426 |
connect(delegate, SIGNAL(commitData(QWidget*)), SLOT(_q_commitData(QWidget*))); |
|
427 |
connect(delegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), |
|
428 |
SLOT(_q_closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); |
|
429 |
} |
|
430 |
||
431 |
d->flipEventFilters(oldDelegate, delegate); |
|
432 |
} |
|
433 |
||
434 |
/*! |
|
435 |
Returns the current item delegate. |
|
436 |
*/ |
|
437 |
QAbstractItemDelegate *QDataWidgetMapper::itemDelegate() const |
|
438 |
{ |
|
439 |
Q_D(const QDataWidgetMapper); |
|
440 |
return d->delegate; |
|
441 |
} |
|
442 |
||
443 |
/*! |
|
444 |
Sets the root item to \a index. This can be used to display |
|
445 |
a branch of a tree. Pass an invalid model index to display |
|
446 |
the top-most branch. |
|
447 |
||
448 |
\sa rootIndex() |
|
449 |
*/ |
|
450 |
void QDataWidgetMapper::setRootIndex(const QModelIndex &index) |
|
451 |
{ |
|
452 |
Q_D(QDataWidgetMapper); |
|
453 |
d->rootIndex = index; |
|
454 |
} |
|
455 |
||
456 |
/*! |
|
457 |
Returns the current root index. |
|
458 |
||
459 |
\sa setRootIndex() |
|
460 |
*/ |
|
461 |
QModelIndex QDataWidgetMapper::rootIndex() const |
|
462 |
{ |
|
463 |
Q_D(const QDataWidgetMapper); |
|
464 |
return QModelIndex(d->rootIndex); |
|
465 |
} |
|
466 |
||
467 |
/*! |
|
468 |
Adds a mapping between a \a widget and a \a section from the model. |
|
469 |
The \a section is a column in the model if the orientation is |
|
470 |
horizontal (the default), otherwise a row. |
|
471 |
||
472 |
For the following example, we assume a model \c myModel that |
|
473 |
has two columns: the first one contains the names of people in a |
|
474 |
group, and the second column contains their ages. The first column |
|
475 |
is mapped to the QLineEdit \c nameLineEdit, and the second is |
|
476 |
mapped to the QSpinBox \c{ageSpinBox}: |
|
477 |
||
478 |
\snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 1 |
|
479 |
||
480 |
\bold{Notes:} |
|
481 |
\list |
|
482 |
\o If the \a widget is already mapped to a section, the |
|
483 |
old mapping will be replaced by the new one. |
|
484 |
\o Only one-to-one mappings between sections and widgets are allowed. |
|
485 |
It is not possible to map a single section to multiple widgets, or to |
|
486 |
map a single widget to multiple sections. |
|
487 |
\endlist |
|
488 |
||
489 |
\sa removeMapping(), mappedSection(), clearMapping() |
|
490 |
*/ |
|
491 |
void QDataWidgetMapper::addMapping(QWidget *widget, int section) |
|
492 |
{ |
|
493 |
Q_D(QDataWidgetMapper); |
|
494 |
||
495 |
removeMapping(widget); |
|
496 |
d->widgetMap.append(QDataWidgetMapperPrivate::WidgetMapper(widget, section, d->indexAt(section))); |
|
497 |
widget->installEventFilter(d->delegate); |
|
498 |
} |
|
499 |
||
500 |
/*! |
|
501 |
\since 4.3 |
|
502 |
||
503 |
Essentially the same as addMapping(), but adds the possibility to specify |
|
504 |
the property to use specifying \a propertyName. |
|
505 |
||
506 |
\sa addMapping() |
|
507 |
*/ |
|
508 |
||
509 |
void QDataWidgetMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName) |
|
510 |
{ |
|
511 |
Q_D(QDataWidgetMapper); |
|
512 |
||
513 |
removeMapping(widget); |
|
514 |
d->widgetMap.append(QDataWidgetMapperPrivate::WidgetMapper(widget, section, d->indexAt(section), propertyName)); |
|
515 |
widget->installEventFilter(d->delegate); |
|
516 |
} |
|
517 |
||
518 |
/*! |
|
519 |
Removes the mapping for the given \a widget. |
|
520 |
||
521 |
\sa addMapping(), clearMapping() |
|
522 |
*/ |
|
523 |
void QDataWidgetMapper::removeMapping(QWidget *widget) |
|
524 |
{ |
|
525 |
Q_D(QDataWidgetMapper); |
|
526 |
||
527 |
int idx = d->findWidget(widget); |
|
528 |
if (idx == -1) |
|
529 |
return; |
|
530 |
||
531 |
d->widgetMap.removeAt(idx); |
|
532 |
widget->removeEventFilter(d->delegate); |
|
533 |
} |
|
534 |
||
535 |
/*! |
|
536 |
Returns the section the \a widget is mapped to or -1 |
|
537 |
if the widget is not mapped. |
|
538 |
||
539 |
\sa addMapping(), removeMapping() |
|
540 |
*/ |
|
541 |
int QDataWidgetMapper::mappedSection(QWidget *widget) const |
|
542 |
{ |
|
543 |
Q_D(const QDataWidgetMapper); |
|
544 |
||
545 |
int idx = d->findWidget(widget); |
|
546 |
if (idx == -1) |
|
547 |
return -1; |
|
548 |
||
549 |
return d->widgetMap.at(idx).section; |
|
550 |
} |
|
551 |
||
552 |
/*! |
|
553 |
\since 4.3 |
|
554 |
Returns the name of the property that is used when mapping |
|
555 |
data to the given \a widget. |
|
556 |
||
557 |
\sa mappedSection(), addMapping(), removeMapping() |
|
558 |
*/ |
|
559 |
||
560 |
QByteArray QDataWidgetMapper::mappedPropertyName(QWidget *widget) const |
|
561 |
{ |
|
562 |
Q_D(const QDataWidgetMapper); |
|
563 |
||
564 |
int idx = d->findWidget(widget); |
|
565 |
if (idx == -1) |
|
566 |
return QByteArray(); |
|
567 |
const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(idx); |
|
568 |
if (m.property.isEmpty()) |
|
569 |
return m.widget->metaObject()->userProperty().name(); |
|
570 |
else |
|
571 |
return m.property; |
|
572 |
} |
|
573 |
||
574 |
/*! |
|
575 |
Returns the widget that is mapped at \a section, or |
|
576 |
0 if no widget is mapped at that section. |
|
577 |
||
578 |
\sa addMapping(), removeMapping() |
|
579 |
*/ |
|
580 |
QWidget *QDataWidgetMapper::mappedWidgetAt(int section) const |
|
581 |
{ |
|
582 |
Q_D(const QDataWidgetMapper); |
|
583 |
||
584 |
for (int i = 0; i < d->widgetMap.count(); ++i) { |
|
585 |
if (d->widgetMap.at(i).section == section) |
|
586 |
return d->widgetMap.at(i).widget; |
|
587 |
} |
|
588 |
||
589 |
return 0; |
|
590 |
} |
|
591 |
||
592 |
/*! |
|
593 |
Repopulates all widgets with the current data of the model. |
|
594 |
All unsubmitted changes will be lost. |
|
595 |
||
596 |
\sa submit(), setSubmitPolicy() |
|
597 |
*/ |
|
598 |
void QDataWidgetMapper::revert() |
|
599 |
{ |
|
600 |
Q_D(QDataWidgetMapper); |
|
601 |
||
602 |
d->populate(); |
|
603 |
} |
|
604 |
||
605 |
/*! |
|
606 |
Submits all changes from the mapped widgets to the model. |
|
607 |
||
608 |
For every mapped section, the item delegate reads the current |
|
609 |
value from the widget and sets it in the model. Finally, the |
|
610 |
model's \l {QAbstractItemModel::}{submit()} method is invoked. |
|
611 |
||
612 |
Returns true if all the values were submitted, otherwise false. |
|
613 |
||
614 |
Note: For database models, QSqlQueryModel::lastError() can be |
|
615 |
used to retrieve the last error. |
|
616 |
||
617 |
\sa revert(), setSubmitPolicy() |
|
618 |
*/ |
|
619 |
bool QDataWidgetMapper::submit() |
|
620 |
{ |
|
621 |
Q_D(QDataWidgetMapper); |
|
622 |
||
623 |
for (int i = 0; i < d->widgetMap.count(); ++i) { |
|
624 |
const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(i); |
|
625 |
if (!d->commit(m)) |
|
626 |
return false; |
|
627 |
} |
|
628 |
||
629 |
return d->model->submit(); |
|
630 |
} |
|
631 |
||
632 |
/*! |
|
633 |
Populates the widgets with data from the first row of the model |
|
634 |
if the orientation is horizontal (the default), otherwise |
|
635 |
with data from the first column. |
|
636 |
||
637 |
This is equivalent to calling \c setCurrentIndex(0). |
|
638 |
||
639 |
\sa toLast(), setCurrentIndex() |
|
640 |
*/ |
|
641 |
void QDataWidgetMapper::toFirst() |
|
642 |
{ |
|
643 |
setCurrentIndex(0); |
|
644 |
} |
|
645 |
||
646 |
/*! |
|
647 |
Populates the widgets with data from the last row of the model |
|
648 |
if the orientation is horizontal (the default), otherwise |
|
649 |
with data from the last column. |
|
650 |
||
651 |
Calls setCurrentIndex() internally. |
|
652 |
||
653 |
\sa toFirst(), setCurrentIndex() |
|
654 |
*/ |
|
655 |
void QDataWidgetMapper::toLast() |
|
656 |
{ |
|
657 |
Q_D(QDataWidgetMapper); |
|
658 |
setCurrentIndex(d->itemCount() - 1); |
|
659 |
} |
|
660 |
||
661 |
||
662 |
/*! |
|
663 |
Populates the widgets with data from the next row of the model |
|
664 |
if the orientation is horizontal (the default), otherwise |
|
665 |
with data from the next column. |
|
666 |
||
667 |
Calls setCurrentIndex() internally. Does nothing if there is |
|
668 |
no next row in the model. |
|
669 |
||
670 |
\sa toPrevious(), setCurrentIndex() |
|
671 |
*/ |
|
672 |
void QDataWidgetMapper::toNext() |
|
673 |
{ |
|
674 |
Q_D(QDataWidgetMapper); |
|
675 |
setCurrentIndex(d->currentIdx() + 1); |
|
676 |
} |
|
677 |
||
678 |
/*! |
|
679 |
Populates the widgets with data from the previous row of the model |
|
680 |
if the orientation is horizontal (the default), otherwise |
|
681 |
with data from the previous column. |
|
682 |
||
683 |
Calls setCurrentIndex() internally. Does nothing if there is |
|
684 |
no previous row in the model. |
|
685 |
||
686 |
\sa toNext(), setCurrentIndex() |
|
687 |
*/ |
|
688 |
void QDataWidgetMapper::toPrevious() |
|
689 |
{ |
|
690 |
Q_D(QDataWidgetMapper); |
|
691 |
setCurrentIndex(d->currentIdx() - 1); |
|
692 |
} |
|
693 |
||
694 |
/*! |
|
695 |
\property QDataWidgetMapper::currentIndex |
|
696 |
\brief the current row or column |
|
697 |
||
698 |
The widgets are populated with with data from the row at \a index |
|
699 |
if the orientation is horizontal (the default), otherwise with |
|
700 |
data from the column at \a index. |
|
701 |
||
702 |
\sa setCurrentModelIndex(), toFirst(), toNext(), toPrevious(), toLast() |
|
703 |
*/ |
|
704 |
void QDataWidgetMapper::setCurrentIndex(int index) |
|
705 |
{ |
|
706 |
Q_D(QDataWidgetMapper); |
|
707 |
||
708 |
if (index < 0 || index >= d->itemCount()) |
|
709 |
return; |
|
710 |
d->currentTopLeft = d->orientation == Qt::Horizontal |
|
711 |
? d->model->index(index, 0, d->rootIndex) |
|
712 |
: d->model->index(0, index, d->rootIndex); |
|
713 |
d->populate(); |
|
714 |
||
715 |
emit currentIndexChanged(index); |
|
716 |
} |
|
717 |
||
718 |
int QDataWidgetMapper::currentIndex() const |
|
719 |
{ |
|
720 |
Q_D(const QDataWidgetMapper); |
|
721 |
return d->currentIdx(); |
|
722 |
} |
|
723 |
||
724 |
/*! |
|
725 |
Sets the current index to the row of the \a index if the |
|
726 |
orientation is horizontal (the default), otherwise to the |
|
727 |
column of the \a index. |
|
728 |
||
729 |
Calls setCurrentIndex() internally. This convenience slot can be |
|
730 |
connected to the signal \l |
|
731 |
{QItemSelectionModel::}{currentRowChanged()} or \l |
|
732 |
{QItemSelectionModel::}{currentColumnChanged()} of another view's |
|
733 |
\l {QItemSelectionModel}{selection model}. |
|
734 |
||
735 |
The following example illustrates how to update all widgets |
|
736 |
with new data whenever the selection of a QTableView named |
|
737 |
\c myTableView changes: |
|
738 |
||
739 |
\snippet doc/src/snippets/code/src_gui_itemviews_qdatawidgetmapper.cpp 2 |
|
740 |
||
741 |
\sa currentIndex() |
|
742 |
*/ |
|
743 |
void QDataWidgetMapper::setCurrentModelIndex(const QModelIndex &index) |
|
744 |
{ |
|
745 |
Q_D(QDataWidgetMapper); |
|
746 |
||
747 |
if (!index.isValid() |
|
748 |
|| index.model() != d->model |
|
749 |
|| index.parent() != d->rootIndex) |
|
750 |
return; |
|
751 |
||
752 |
setCurrentIndex(d->orientation == Qt::Horizontal ? index.row() : index.column()); |
|
753 |
} |
|
754 |
||
755 |
/*! |
|
756 |
Clears all mappings. |
|
757 |
||
758 |
\sa addMapping(), removeMapping() |
|
759 |
*/ |
|
760 |
void QDataWidgetMapper::clearMapping() |
|
761 |
{ |
|
762 |
Q_D(QDataWidgetMapper); |
|
763 |
||
764 |
while (!d->widgetMap.isEmpty()) { |
|
765 |
QWidget *w = d->widgetMap.takeLast().widget; |
|
766 |
if (w) |
|
767 |
w->removeEventFilter(d->delegate); |
|
768 |
} |
|
769 |
} |
|
770 |
||
771 |
/*! |
|
772 |
\property QDataWidgetMapper::orientation |
|
773 |
\brief the orientation of the model |
|
774 |
||
775 |
If the orientation is Qt::Horizontal (the default), a widget is |
|
776 |
mapped to a column of a data model. The widget will be populated |
|
777 |
with the model's data from its mapped column and the row that |
|
778 |
currentIndex() points at. |
|
779 |
||
780 |
Use Qt::Horizontal for tabular data that looks like this: |
|
781 |
||
782 |
\table |
|
783 |
\row \o 1 \o Qt Norway \o Oslo |
|
784 |
\row \o 2 \o Qt Australia \o Brisbane |
|
785 |
\row \o 3 \o Qt USA \o Silicon Valley |
|
786 |
\row \o 4 \o Qt China \o Beijing |
|
787 |
\row \o 5 \o Qt Germany \o Berlin |
|
788 |
\endtable |
|
789 |
||
790 |
If the orientation is set to Qt::Vertical, a widget is mapped to |
|
791 |
a row. Calling setCurrentIndex() will change the current column. |
|
792 |
The widget will be populates with the model's data from its |
|
793 |
mapped row and the column that currentIndex() points at. |
|
794 |
||
795 |
Use Qt::Vertical for tabular data that looks like this: |
|
796 |
||
797 |
\table |
|
798 |
\row \o 1 \o 2 \o 3 \o 4 \o 5 |
|
799 |
\row \o Qt Norway \o Qt Australia \o Qt USA \o Qt China \o Qt Germany |
|
800 |
\row \o Oslo \o Brisbane \o Silicon Valley \o Beijing \i Berlin |
|
801 |
\endtable |
|
802 |
||
803 |
Changing the orientation clears all existing mappings. |
|
804 |
*/ |
|
805 |
void QDataWidgetMapper::setOrientation(Qt::Orientation orientation) |
|
806 |
{ |
|
807 |
Q_D(QDataWidgetMapper); |
|
808 |
||
809 |
if (d->orientation == orientation) |
|
810 |
return; |
|
811 |
||
812 |
clearMapping(); |
|
813 |
d->orientation = orientation; |
|
814 |
} |
|
815 |
||
816 |
Qt::Orientation QDataWidgetMapper::orientation() const |
|
817 |
{ |
|
818 |
Q_D(const QDataWidgetMapper); |
|
819 |
return d->orientation; |
|
820 |
} |
|
821 |
||
822 |
/*! |
|
823 |
\property QDataWidgetMapper::submitPolicy |
|
824 |
\brief the current submit policy |
|
825 |
||
826 |
Changing the current submit policy will revert all widgets |
|
827 |
to the current data from the model. |
|
828 |
*/ |
|
829 |
void QDataWidgetMapper::setSubmitPolicy(SubmitPolicy policy) |
|
830 |
{ |
|
831 |
Q_D(QDataWidgetMapper); |
|
832 |
if (policy == d->submitPolicy) |
|
833 |
return; |
|
834 |
||
835 |
revert(); |
|
836 |
d->submitPolicy = policy; |
|
837 |
} |
|
838 |
||
839 |
QDataWidgetMapper::SubmitPolicy QDataWidgetMapper::submitPolicy() const |
|
840 |
{ |
|
841 |
Q_D(const QDataWidgetMapper); |
|
842 |
return d->submitPolicy; |
|
843 |
} |
|
844 |
||
845 |
QT_END_NAMESPACE |
|
846 |
||
847 |
#include "moc_qdatawidgetmapper.cpp" |
|
848 |
||
849 |
#endif // QT_NO_DATAWIDGETMAPPER |