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