|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
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 |