aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlmodels/qqmldmlistaccessordata_p.h
blob: e02c3a88f6ea686203de36c8ed4f491bb060d133 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QQMLDMLISTACCESSORDATA_P_H
#define QQMLDMLISTACCESSORDATA_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <private/qqmladaptormodelenginedata_p.h>
#include <private/qqmldelegatemodel_p_p.h>
#include <private/qobject_p.h>

QT_BEGIN_NAMESPACE

class VDMListDelegateDataType;

class QQmlDMListAccessorData : public QQmlDelegateModelItem
{
    Q_OBJECT
    Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
    QT_ANONYMOUS_PROPERTY(QVariant READ modelData WRITE setModelData NOTIFY modelDataChanged FINAL)
public:
    QQmlDMListAccessorData(
            const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
            VDMListDelegateDataType *dataType, int index, int row, int column,
            const QVariant &value);
    ~QQmlDMListAccessorData();

    QVariant modelData() const
    {
        return cachedData;
    }

    void setModelData(const QVariant &data);

    static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
    {
        QV4::ExecutionEngine *v4 = b->engine();
        const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
        if (!o)
            return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));

        return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
    }

    static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
    {
        QV4::ExecutionEngine *v4 = b->engine();
        const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
        if (!o)
            return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
        if (!argc)
            return v4->throwTypeError();

        static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(
                    QV4::ExecutionEngine::toVariant(argv[0], QMetaType {}));
        return QV4::Encode::undefined();
    }

    QV4::ReturnedValue get() override
    {
        QQmlAdaptorModelEngineData *data = QQmlAdaptorModelEngineData::get(v4);
        QV4::Scope scope(v4);
        QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
        QV4::ScopedObject p(scope, data->listItemProto.value());
        o->setPrototypeOf(p);
        ++scriptRef;
        return o.asReturnedValue();
    }

    void setValue(const QString &role, const QVariant &value) override;
    bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;

Q_SIGNALS:
    void modelDataChanged();

private:
    friend class VDMListDelegateDataType;
    QVariant cachedData;

    // Gets cleaned when the metaobject has processed it.
    bool cachedDataClean = false;
};


class VDMListDelegateDataType final
    : public QQmlRefCounted<VDMListDelegateDataType>
    , public QQmlAdaptorModel::Accessors
    , public QAbstractDynamicMetaObject
{
public:
    VDMListDelegateDataType(QQmlAdaptorModel *model)
        : model(model)
    {
        QQmlAdaptorModelEngineData::setModelDataType<QQmlDMListAccessorData>(&builder, this);
        metaObject.reset(builder.toMetaObject());
        *static_cast<QMetaObject *>(this) = *metaObject.data();
    }

    void cleanup(QQmlAdaptorModel &) const override
    {
        release();
    }

    int rowCount(const QQmlAdaptorModel &model) const override
    {
        return model.list.count();
    }

    int columnCount(const QQmlAdaptorModel &model) const override
    {
        switch (model.list.type()) {
        case QQmlListAccessor::Invalid:
            return 0;
        case QQmlListAccessor::StringList:
        case QQmlListAccessor::UrlList:
        case QQmlListAccessor::Integer:
            return 1;
        default:
            break;
        }

        // If there are no properties, we can get modelData itself.
        return std::max(1, propertyCount() - propertyOffset);
    }

    static const QMetaObject *metaObjectFromType(QMetaType type)
    {
        if (const QMetaObject *metaObject = type.metaObject())
            return metaObject;

        // NB: This acquires the lock on QQmlMetaTypeData. If we had a QQmlEngine here,
        //     we could use QQmlGadgetPtrWrapper::instance() to avoid this.
        if (const QQmlValueType *valueType = QQmlMetaType::valueType(type))
            return valueType->staticMetaObject();

        return nullptr;
    }

    template<typename String>
    static QString toQString(const String &string)
    {
        if constexpr (std::is_same_v<String, QString>)
            return string;
        else if constexpr (std::is_same_v<String, QByteArray>)
            return QString::fromUtf8(string);
        else if constexpr (std::is_same_v<String, const char *>)
            return QString::fromUtf8(string);
        Q_UNREACHABLE_RETURN(QString());
    }

    template<typename String>
    static QByteArray toUtf8(const String &string)
    {
        if constexpr (std::is_same_v<String, QString>)
            return string.toUtf8();
        else if constexpr (std::is_same_v<String, QByteArray>)
            return string;
        else if constexpr (std::is_same_v<String, const char *>)
            return QByteArray::fromRawData(string, qstrlen(string));
        Q_UNREACHABLE_RETURN(QByteArray());
    }

    template<typename String>
    static QVariant value(const QVariant *row, const String &role)
    {
        const QMetaType type = row->metaType();
        if (type == QMetaType::fromType<QVariantMap>())
            return row->toMap().value(toQString(role));

        if (type == QMetaType::fromType<QVariantHash>())
            return row->toHash().value(toQString(role));

        const QMetaType::TypeFlags typeFlags = type.flags();
        if (typeFlags & QMetaType::PointerToQObject)
            return row->value<QObject *>()->property(toUtf8(role));

        if (const QMetaObject *metaObject = metaObjectFromType(type)) {
            const int propertyIndex = metaObject->indexOfProperty(toUtf8(role));
            if (propertyIndex >= 0)
                return metaObject->property(propertyIndex).readOnGadget(row->constData());
        }

        return QVariant();
    }

    template<typename String>
    void createPropertyIfMissing(const String &string)
    {
        for (int i = 0, end = propertyCount(); i < end; ++i) {
            if (QAnyStringView(property(i).name()) == QAnyStringView(string))
                return;
        }

        createProperty(toUtf8(string), nullptr);
    }

    void createMissingProperties(const QVariant *row)
    {
        const QMetaType type = row->metaType();
        if (type == QMetaType::fromType<QVariantMap>()) {
            const QVariantMap map = row->toMap();
            for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
                createPropertyIfMissing(*it);
        } else if (type == QMetaType::fromType<QVariantHash>()) {
            const QVariantHash map = row->toHash();
            for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
                createPropertyIfMissing(*it);
        } else if (type.flags() & QMetaType::PointerToQObject) {
            const QMetaObject *metaObject = row->value<QObject *>()->metaObject();
            for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
                createPropertyIfMissing(metaObject->property(i).name());
        } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
            for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
                createPropertyIfMissing(metaObject->property(i).name());
        }
    }

    template<typename String>
    static void setValue(QVariant *row, const String &role, const QVariant &value)
    {
        const QMetaType type = row->metaType();
        if (type == QMetaType::fromType<QVariantMap>()) {
            static_cast<QVariantMap *>(row->data())->insert(toQString(role), value);
        } else if (type == QMetaType::fromType<QVariantHash>()) {
            static_cast<QVariantHash *>(row->data())->insert(toQString(role), value);
        } else if (type.flags() & QMetaType::PointerToQObject) {
            row->value<QObject *>()->setProperty(toUtf8(role), value);
        } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
            const int propertyIndex = metaObject->indexOfProperty(toUtf8(role));
            if (propertyIndex >= 0)
                metaObject->property(propertyIndex).writeOnGadget(row->data(), value);
        }
    }

    QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
    {
        const QVariant entry = model.list.at(index);
        if (role == QLatin1String("modelData") || role.isEmpty())
            return entry;

        return value(&entry, role);
    }

    QQmlDelegateModelItem *createItem(
            QQmlAdaptorModel &model,
            const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
            int index, int row, int column) override
    {
        const QVariant value = (index >= 0 && index < model.list.count())
                ? model.list.at(index)
                : QVariant();
        return new QQmlDMListAccessorData(metaType, this, index, row, column, value);
    }

    bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
    {
        for (auto modelItem : items) {
            const int modelItemIndex = modelItem->index;
            if (modelItemIndex < index || modelItemIndex >= index + count)
                continue;

            auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
            QVariant updatedModelData = model.list.at(listModelItem->index);
            listModelItem->setModelData(updatedModelData);
        }
        return true;
    }

    void emitAllSignals(QQmlDMListAccessorData *accessor) const;

    int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) final;
    int createProperty(const char *name, const char *) final;
    QMetaObject *toDynamicMetaObject(QObject *accessors) final;

    QMetaObjectBuilder builder;
    QQmlAdaptorModel *model = nullptr;
    int propertyOffset = 0;
    int signalOffset = 0;
};

QT_END_NAMESPACE

#endif // QQMLDMLISTACCESSORDATA_P_H