From 17dfe02d2826e7bb8154f672599978af66844db4 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 05:53:18 -0400
Subject: [PATCH 1/9] [3rdparty] Remove QJsonModel and support

---
 3rdparty/qjsonitem.cpp     | 140 ----------------------------
 3rdparty/qjsonitem.h       |  46 ---------
 3rdparty/qjsonmodel.cpp    | 186 -------------------------------------
 3rdparty/qjsonmodel.h      |  42 ---------
 LICENSES/GPLv3+-QJsonModel |  18 ----
 5 files changed, 432 deletions(-)
 delete mode 100644 3rdparty/qjsonitem.cpp
 delete mode 100644 3rdparty/qjsonitem.h
 delete mode 100644 3rdparty/qjsonmodel.cpp
 delete mode 100644 3rdparty/qjsonmodel.h
 delete mode 100644 LICENSES/GPLv3+-QJsonModel

diff --git a/3rdparty/qjsonitem.cpp b/3rdparty/qjsonitem.cpp
deleted file mode 100644
index 6bb1fa4c7..000000000
--- a/3rdparty/qjsonitem.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *   SPDX-License-Identifier: GPL-3.0+
- *   License-Filename: LICENSES/GPLv3+-QJsonModel
- */
-
-/***********************************************
-    Copyright (C) 2014  Schutz Sacha
-    This file is part of QJsonModel (https://github.com/dridk/QJsonmodel).
-
-    QJsonModel is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    QJsonModel is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with QJsonModel.  If not, see <http://www.gnu.org/licenses/>.
-
-**********************************************/
-
-#include "qjsonitem.h"
-
-QJsonTreeItem::QJsonTreeItem(QJsonTreeItem *parent)
-    : mParent( parent )
-    , mType( QJsonValue::Type::Null )
-{
-}
-
-QJsonTreeItem::~QJsonTreeItem()
-{
-    qDeleteAll(mChilds);
-
-}
-
-void QJsonTreeItem::appendChild(QJsonTreeItem *item)
-{
-    mChilds.append(item);
-}
-
-QJsonTreeItem *QJsonTreeItem::child(int row)
-{
-    return mChilds.value(row);
-}
-
-QJsonTreeItem *QJsonTreeItem::parent()
-{
-    return mParent;
-}
-
-int QJsonTreeItem::childCount() const
-{
-    return mChilds.count();
-}
-
-int QJsonTreeItem::row() const
-{
-    if (mParent)
-        return mParent->mChilds.indexOf(const_cast<QJsonTreeItem*>(this));
-
-    return 0;
-}
-
-void QJsonTreeItem::setKey(const QString &key)
-{
-    mKey = key;
-}
-
-void QJsonTreeItem::setValue(const QString &value)
-{
-    mValue = value;
-}
-
-void QJsonTreeItem::setType(const QJsonValue::Type &type)
-{
-    mType = type;
-}
-
-QString QJsonTreeItem::key() const
-{
-    return mKey;
-}
-
-QString QJsonTreeItem::value() const
-{
-    return mValue;
-}
-
-QJsonValue::Type QJsonTreeItem::type() const
-{
-    return mType;
-}
-
-QJsonTreeItem* QJsonTreeItem::load(const QJsonValue& value, QJsonTreeItem* parent)
-{
-
-
-    QJsonTreeItem * rootItem = new QJsonTreeItem(parent);
-    rootItem->setKey("root");
-
-    if ( value.isObject())
-    {
-
-        //Get all QJsonValue childs
-        foreach (QString key , value.toObject().keys()){
-            QJsonValue v = value.toObject().value(key);
-            QJsonTreeItem * child = load(v,rootItem);
-            child->setKey(key);
-            child->setType(v.type());
-            rootItem->appendChild(child);
-
-        }
-
-    }
-
-    else if ( value.isArray())
-    {
-        //Get all QJsonValue childs
-        int index = 0;
-        foreach (QJsonValue v , value.toArray()){
-
-            QJsonTreeItem * child = load(v,rootItem);
-            child->setKey(QString::number(index));
-            child->setType(v.type());
-            rootItem->appendChild(child);
-            ++index;
-        }
-    }
-    else
-    {
-        rootItem->setValue(value.toVariant().toString());
-        rootItem->setType(value.type());
-    }
-
-    return rootItem;
-}
-
diff --git a/3rdparty/qjsonitem.h b/3rdparty/qjsonitem.h
deleted file mode 100644
index 137c59000..000000000
--- a/3rdparty/qjsonitem.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *   SPDX-License-Identifier: GPL-3.0+
- *   License-Filename: LICENSES/GPLv3+-QJsonModel
- */
-
-#ifndef JSONITEM_H
-#define JSONITEM_H
-#include <QtCore>
-#include <QJsonValue>
-#include <QJsonArray>
-#include <QJsonObject>
-class QJsonTreeItem
-{
-public:
-    QJsonTreeItem(QJsonTreeItem * parent = nullptr);
-    ~QJsonTreeItem();
-    void appendChild(QJsonTreeItem * item);
-    QJsonTreeItem *child(int row);
-    QJsonTreeItem *parent();
-    int childCount() const;
-    int row() const;
-    void setKey(const QString& key);
-    void setValue(const QString& value);
-    void setType(const QJsonValue::Type& type);
-    QString key() const;
-    QString value() const;
-    QJsonValue::Type type() const;
-
-
-    static QJsonTreeItem* load(const QJsonValue& value, QJsonTreeItem * parent = nullptr);
-
-protected:
-
-
-private:
-    QString mKey;
-    QString mValue;
-    QJsonValue::Type mType;
-
-    QList<QJsonTreeItem*> mChilds;
-    QJsonTreeItem * mParent;
-
-
-};
-
-#endif // JSONITEM_H
diff --git a/3rdparty/qjsonmodel.cpp b/3rdparty/qjsonmodel.cpp
deleted file mode 100644
index 020d27e42..000000000
--- a/3rdparty/qjsonmodel.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- *   SPDX-License-Identifier: GPL-3.0+
- *   License-Filename: LICENSES/GPLv3+-QJsonModel
- */
-
-/***********************************************
-    Copyright (C) 2014  Schutz Sacha
-    This file is part of QJsonModel (https://github.com/dridk/QJsonmodel).
-
-    QJsonModel is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    QJsonModel is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with QJsonModel.  If not, see <http://www.gnu.org/licenses/>.
-
-**********************************************/
-
-#include "qjsonmodel.h"
-#include <QFile>
-#include <QDebug>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QIcon>
-#include <QFont>
-
-QJsonModel::QJsonModel(QObject *parent) :
-    QAbstractItemModel(parent)
-    , mRootItem( new QJsonTreeItem )
-{
-    mHeaders.append("key");
-    mHeaders.append("value");
-
-
-}
-
-QJsonModel::~QJsonModel()
-{
-    delete mRootItem;
-}
-
-bool QJsonModel::load(const QString &fileName)
-{
-    QFile file(fileName);
-    bool success = false;
-    if (file.open(QIODevice::ReadOnly)) {
-        success = load(&file);
-        file.close();
-    }
-    else success = false;
-
-    return success;
-}
-
-bool QJsonModel::load(QIODevice *device)
-{
-    return loadJson(device->readAll());
-}
-
-bool QJsonModel::loadJson(const QByteArray &json)
-{
-    mDocument = QJsonDocument::fromJson(json);
-
-    if (!mDocument.isNull())
-    {
-        beginResetModel();
-        delete mRootItem;
-        if (mDocument.isArray()) {
-            mRootItem = QJsonTreeItem::load(QJsonValue(mDocument.array()));
-        } else {
-            mRootItem = QJsonTreeItem::load(QJsonValue(mDocument.object()));
-        }
-        endResetModel();
-        return true;
-    }
-    return false;
-}
-
-
-QVariant QJsonModel::data(const QModelIndex &index, int role) const
-{
-
-    if (!index.isValid())
-        return QVariant();
-
-
-    QJsonTreeItem *item = static_cast<QJsonTreeItem*>(index.internalPointer());
-
-
-    if ((role == Qt::DecorationRole) && (index.column() == 0)){
-
-        return mTypeIcons.value(item->type());
-    }
-
-
-    if (role == Qt::DisplayRole) {
-
-        if (index.column() == 0)
-            return QString("%1").arg(item->key());
-
-        if (index.column() == 1)
-            return QString("%1").arg(item->value());
-    }
-
-
-
-    return QVariant();
-
-}
-
-QVariant QJsonModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    if (role != Qt::DisplayRole)
-        return QVariant();
-
-    if (orientation == Qt::Horizontal) {
-
-        return mHeaders.value(section);
-    }
-    else
-        return QVariant();
-}
-
-QModelIndex QJsonModel::index(int row, int column, const QModelIndex &parent) const
-{
-    if (!hasIndex(row, column, parent))
-        return QModelIndex();
-
-    QJsonTreeItem *parentItem;
-
-    if (!parent.isValid())
-        parentItem = mRootItem;
-    else
-        parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());
-
-    QJsonTreeItem *childItem = parentItem->child(row);
-    if (childItem)
-        return createIndex(row, column, childItem);
-    else
-        return QModelIndex();
-}
-
-QModelIndex QJsonModel::parent(const QModelIndex &index) const
-{
-    if (!index.isValid())
-        return QModelIndex();
-
-    QJsonTreeItem *childItem = static_cast<QJsonTreeItem*>(index.internalPointer());
-    QJsonTreeItem *parentItem = childItem->parent();
-
-    if (parentItem == mRootItem)
-        return QModelIndex();
-
-    return createIndex(parentItem->row(), 0, parentItem);
-}
-
-int QJsonModel::rowCount(const QModelIndex &parent) const
-{
-    QJsonTreeItem *parentItem;
-    if (parent.column() > 0)
-        return 0;
-
-    if (!parent.isValid())
-        parentItem = mRootItem;
-    else
-        parentItem = static_cast<QJsonTreeItem*>(parent.internalPointer());
-
-    return parentItem->childCount();
-}
-
-int QJsonModel::columnCount(const QModelIndex &parent) const
-{
-    Q_UNUSED(parent)
-    return 2;
-}
-
-void QJsonModel::setIcon(const QJsonValue::Type &type, const QIcon &icon)
-{
-    mTypeIcons.insert(type,icon);
-}
diff --git a/3rdparty/qjsonmodel.h b/3rdparty/qjsonmodel.h
deleted file mode 100644
index 32c062ba8..000000000
--- a/3rdparty/qjsonmodel.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *   SPDX-License-Identifier: GPL-3.0+
- *   License-Filename: LICENSES/GPLv3+-QJsonModel
- */
-
-#ifndef QJSONMODEL_H
-#define QJSONMODEL_H
-
-#include <QAbstractItemModel>
-#include "qjsonitem.h"
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QIcon>
-class QJsonModel : public QAbstractItemModel
-{
-    Q_OBJECT
-public:
-    explicit QJsonModel(QObject *parent = nullptr);
-    ~QJsonModel();
-    bool load(const QString& fileName);
-    bool load(QIODevice * device);
-    bool loadJson(const QByteArray& json);
-    QVariant data(const QModelIndex &index, int role) const;
-    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-    QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const;
-    QModelIndex parent(const QModelIndex &index) const;
-    int rowCount(const QModelIndex &parent = QModelIndex()) const;
-    int columnCount(const QModelIndex &parent = QModelIndex()) const;
-    void setIcon(const QJsonValue::Type& type, const QIcon& icon);
-
-
-
-private:
-    QJsonTreeItem * mRootItem;
-    QJsonDocument mDocument;
-    QStringList mHeaders;
-    QHash<QJsonValue::Type, QIcon> mTypeIcons;
-
-
-};
-
-#endif // QJSONMODEL_H
diff --git a/LICENSES/GPLv3+-QJsonModel b/LICENSES/GPLv3+-QJsonModel
deleted file mode 100644
index 8a8e272c0..000000000
--- a/LICENSES/GPLv3+-QJsonModel
+++ /dev/null
@@ -1,18 +0,0 @@
-/***********************************************
-    Copyright (C) 2014  Schutz Sacha
-    This file is part of QJsonModel (https://github.com/dridk/QJsonmodel).
-
-    QJsonModel is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    QJsonModel is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with QJsonModel.  If not, see <http://www.gnu.org/licenses/>.
-
-**********************************************/

From 695e16bb87ae95313ceed206be98df2fd2b25f00 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 06:21:07 -0400
Subject: [PATCH 2/9] [libcalamaresui] Drop QJsonModel from the library

---
 src/libcalamaresui/CMakeLists.txt | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt
index 04565206e..89a50e2ec 100644
--- a/src/libcalamaresui/CMakeLists.txt
+++ b/src/libcalamaresui/CMakeLists.txt
@@ -18,9 +18,6 @@ set( calamaresui_SOURCES
     utils/ImageRegistry.cpp
     utils/Paste.cpp
 
-    ${CMAKE_SOURCE_DIR}/3rdparty/qjsonmodel.cpp
-    ${CMAKE_SOURCE_DIR}/3rdparty/qjsonitem.cpp
-
     viewpages/BlankViewStep.cpp
     viewpages/ViewStep.cpp
 

From 419329ee494faeeff16d6ba08baca4ce75d3bc00 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 06:27:13 -0400
Subject: [PATCH 3/9] [libcalamares] Remove friend, add accessor, make includes
 explicit

---
 src/libcalamares/GlobalStorage.h | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h
index 6fdb43fa0..b070e23f6 100644
--- a/src/libcalamares/GlobalStorage.h
+++ b/src/libcalamares/GlobalStorage.h
@@ -22,6 +22,8 @@
 
 #include "CalamaresConfig.h"
 
+#include <QObject>
+#include <QString>
 #include <QVariantMap>
 
 #ifdef WITH_PYTHON
@@ -87,13 +89,19 @@ public:
     /// @brief reads settings from the given filename
     bool loadYaml( const QString& filename );
 
+    /** @brief Get internal mapping as a constant object
+     *
+     * Note that the VariantMap underneath may change, because
+     * it's not constant in itself. Connect to the changed()
+     * signal for notifications.
+     */
+    const QVariantMap& data() const { return m; }
+
 signals:
     void changed();
 
 private:
     QVariantMap m;
-
-    friend DebugWindow;
 };
 
 }  // namespace Calamares

From 85d28af1e20768c8dd99cfa8acd424f6d7bcc3d2 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 06:34:38 -0400
Subject: [PATCH 4/9] [libcalamaresui] Don't include QJsonModel

---
 src/libcalamaresui/utils/DebugWindow.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/libcalamaresui/utils/DebugWindow.cpp b/src/libcalamaresui/utils/DebugWindow.cpp
index 094511be4..237b8a7ed 100644
--- a/src/libcalamaresui/utils/DebugWindow.cpp
+++ b/src/libcalamaresui/utils/DebugWindow.cpp
@@ -30,8 +30,6 @@
 #include "utils/Logger.h"
 #include "utils/Retranslator.h"
 
-#include "3rdparty/qjsonmodel.h"
-
 #ifdef WITH_PYTHONQT
 #include "ViewManager.h"
 #include "viewpages/PythonQtViewStep.h"
@@ -39,10 +37,10 @@
 #include <gui/PythonQtScriptingConsole.h>
 #endif
 
-#include <QJsonDocument>
 #include <QSplitter>
 #include <QStringListModel>
 #include <QTreeView>
+#include <QWidget>
 
 /**
  * @brief crash makes Calamares crash immediately.

From e31a498c9b70abebe50bdeaeec661545eab4bc40 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 06:35:40 -0400
Subject: [PATCH 5/9] [calamares] Move DebugWindow

 - This is a fairly specialized class, for use only in the
   whole-application where it ties in with the module system.
   Move it to the application directory and slim down the UI library.
 - Include it from the new location.
 - Add UIC to Calamares (the application) because there's now
   a designer-based widget in it.
---
 src/calamares/CMakeLists.txt                            | 3 +++
 src/calamares/CalamaresWindow.cpp                       | 2 +-
 src/{libcalamaresui/utils => calamares}/DebugWindow.cpp | 0
 src/{libcalamaresui/utils => calamares}/DebugWindow.h   | 0
 src/{libcalamaresui/utils => calamares}/DebugWindow.ui  | 0
 src/libcalamaresui/CMakeLists.txt                       | 6 ------
 6 files changed, 4 insertions(+), 7 deletions(-)
 rename src/{libcalamaresui/utils => calamares}/DebugWindow.cpp (100%)
 rename src/{libcalamaresui/utils => calamares}/DebugWindow.h (100%)
 rename src/{libcalamaresui/utils => calamares}/DebugWindow.ui (100%)

diff --git a/src/calamares/CMakeLists.txt b/src/calamares/CMakeLists.txt
index 5dbf137bb..92521f17a 100644
--- a/src/calamares/CMakeLists.txt
+++ b/src/calamares/CMakeLists.txt
@@ -5,6 +5,8 @@ set( calamaresSources
     CalamaresApplication.cpp
     CalamaresWindow.cpp
 
+    DebugWindow.cpp
+
     progresstree/ProgressTreeDelegate.cpp
     progresstree/ProgressTreeItem.cpp
     progresstree/ProgressTreeModel.cpp
@@ -35,6 +37,7 @@ set_target_properties(calamares_bin
         RUNTIME_OUTPUT_NAME calamares
 )
 calamares_automoc( calamares_bin )
+calamares_autouic( calamares_bin )
 
 target_link_libraries( calamares_bin
     PRIVATE
diff --git a/src/calamares/CalamaresWindow.cpp b/src/calamares/CalamaresWindow.cpp
index d80b6cb67..68f29ef18 100644
--- a/src/calamares/CalamaresWindow.cpp
+++ b/src/calamares/CalamaresWindow.cpp
@@ -26,7 +26,7 @@
 #include "ViewManager.h"
 #include "progresstree/ProgressTreeView.h"
 #include "utils/CalamaresUtilsGui.h"
-#include "utils/DebugWindow.h"
+#include "DebugWindow.h"
 #include "utils/Logger.h"
 #include "utils/Retranslator.h"
 
diff --git a/src/libcalamaresui/utils/DebugWindow.cpp b/src/calamares/DebugWindow.cpp
similarity index 100%
rename from src/libcalamaresui/utils/DebugWindow.cpp
rename to src/calamares/DebugWindow.cpp
diff --git a/src/libcalamaresui/utils/DebugWindow.h b/src/calamares/DebugWindow.h
similarity index 100%
rename from src/libcalamaresui/utils/DebugWindow.h
rename to src/calamares/DebugWindow.h
diff --git a/src/libcalamaresui/utils/DebugWindow.ui b/src/calamares/DebugWindow.ui
similarity index 100%
rename from src/libcalamaresui/utils/DebugWindow.ui
rename to src/calamares/DebugWindow.ui
diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt
index 89a50e2ec..111113a47 100644
--- a/src/libcalamaresui/CMakeLists.txt
+++ b/src/libcalamaresui/CMakeLists.txt
@@ -14,7 +14,6 @@ set( calamaresui_SOURCES
     modulesystem/ViewModule.cpp
 
     utils/CalamaresUtilsGui.cpp
-    utils/DebugWindow.cpp
     utils/ImageRegistry.cpp
     utils/Paste.cpp
 
@@ -38,10 +37,6 @@ mark_thirdparty_code(
     ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp
 )
 
-set( calamaresui_UI
-    utils/DebugWindow.ui
-)
-
 if( WITH_PYTHON )
     list( APPEND calamaresui_SOURCES
         modulesystem/PythonJobModule.cpp
@@ -69,7 +64,6 @@ endif()
 
 calamares_add_library( calamaresui
     SOURCES ${calamaresui_SOURCES}
-    UI ${calamaresui_UI}
     EXPORT_MACRO UIDLLEXPORT_PRO
     LINK_PRIVATE_LIBRARIES
         ${OPTIONAL_PYTHON_LIBRARIES}

From 2a3ab4dbe768792ac87ec637ffdcfad21cf5ff69 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 07:21:48 -0400
Subject: [PATCH 6/9] [calamares] Add a model for viewing QVariants directly

---
 src/calamares/CMakeLists.txt   |   1 +
 src/calamares/VariantModel.cpp | 234 +++++++++++++++++++++++++++++++++
 src/calamares/VariantModel.h   |  74 +++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 src/calamares/VariantModel.cpp
 create mode 100644 src/calamares/VariantModel.h

diff --git a/src/calamares/CMakeLists.txt b/src/calamares/CMakeLists.txt
index 92521f17a..259e3bf56 100644
--- a/src/calamares/CMakeLists.txt
+++ b/src/calamares/CMakeLists.txt
@@ -6,6 +6,7 @@ set( calamaresSources
     CalamaresWindow.cpp
 
     DebugWindow.cpp
+    VariantModel.cpp
 
     progresstree/ProgressTreeDelegate.cpp
     progresstree/ProgressTreeItem.cpp
diff --git a/src/calamares/VariantModel.cpp b/src/calamares/VariantModel.cpp
new file mode 100644
index 000000000..bdcf8f75c
--- /dev/null
+++ b/src/calamares/VariantModel.cpp
@@ -0,0 +1,234 @@
+/* === This file is part of Calamares - <https://github.com/calamares> ===
+ *
+ *   Copyright 2019, Adriaan de Groot <groot@kde.org>
+ *
+ *   Calamares is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   Calamares is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "VariantModel.h"
+
+static void
+overallLength( const QVariant& item, int& c, int parent, VariantModel::IndexVector* skiplist )
+{
+    if ( skiplist )
+    {
+        skiplist->append( parent );
+    }
+
+    parent = c++;
+    if ( item.canConvert< QVariantList >() )
+    {
+        for ( const auto& subitem : item.toList() )
+        {
+            overallLength( subitem, c, parent, skiplist );
+        }
+    }
+    else if ( item.canConvert< QVariantMap >() )
+    {
+        for ( const auto& subitem : item.toMap() )
+        {
+            overallLength( subitem, c, parent, skiplist );
+        }
+    }
+}
+
+static quintptr
+findNth( const VariantModel::IndexVector& skiplist, quintptr value, int n )
+{
+    if ( n < 0 )
+    {
+        return -1;
+    }
+
+    int index = 0;
+    while ( ( n >= 0 ) && ( index < skiplist.count() ) )
+    {
+        if ( skiplist[ index ] == value )
+        {
+            if ( --n < 0 )
+            {
+                return index;
+            }
+        }
+        index++;
+    }
+    return -1;
+}
+
+
+VariantModel::VariantModel( const QVariant* p )
+    : m_p( p )
+{
+    int x = 0;
+    overallLength( *p, x, -1, nullptr );
+    m_rows.reserve( x );
+    x = 0;
+    overallLength( *p, x, -1, &m_rows );
+}
+
+VariantModel::~VariantModel() {}
+
+int
+VariantModel::columnCount( const QModelIndex& index ) const
+{
+    return 2;
+}
+
+int
+VariantModel::rowCount( const QModelIndex& index ) const
+{
+    quintptr p = index.isValid() ? index.internalId() : 0;
+    return m_rows.count( p );
+}
+
+QModelIndex
+VariantModel::index( int row, int column, const QModelIndex& parent ) const
+{
+    quintptr p = 0;
+
+    if ( parent.isValid() )
+    {
+        if ( !( parent.internalId() >= m_rows.count() ) )
+        {
+            p = parent.internalId();
+        }
+    }
+
+    return createIndex( row, column, findNth( m_rows, p, row ) );
+}
+
+QModelIndex
+VariantModel::parent( const QModelIndex& index ) const
+{
+    if ( !index.isValid() || ( index.internalId() > m_rows.count() ) )
+    {
+        return QModelIndex();
+    }
+
+    quintptr p = m_rows[ index.internalId() ];
+    if ( p == 0 )
+    {
+        return QModelIndex();
+    }
+
+    if ( p >= m_rows.count() )
+    {
+        return QModelIndex();
+    }
+    quintptr p_pid = m_rows[ p ];
+    int row = 0;
+    for ( int i = 0; i < p; ++i )
+    {
+        if ( m_rows[ i ] == p_pid )
+        {
+            row++;
+        }
+    }
+
+    return createIndex( row, index.column(), p );
+}
+
+QVariant
+VariantModel::data( const QModelIndex& index, int role ) const
+{
+    if ( role != Qt::DisplayRole )
+    {
+        return QVariant();
+    }
+
+    if ( !index.isValid() )
+    {
+        return QVariant();
+    }
+
+    if ( ( index.column() < 0 ) || ( index.column() > 1 ) )
+    {
+        return QVariant();
+    }
+
+    if ( index.internalId() >= m_rows.count() )
+    {
+        return QVariant();
+    }
+
+    const QVariant thing = underlying( parent( index ) );
+
+    if ( !thing.isValid() )
+    {
+        return QVariant();
+    }
+
+    if ( thing.canConvert< QVariantMap >() )
+    {
+        QVariantMap the_map = thing.toMap();
+        const auto key = the_map.keys().at( index.row() );
+        if ( index.column() == 0 )
+        {
+            return key;
+        }
+        else
+        {
+            return the_map[ key ];
+        }
+    }
+    else if ( thing.canConvert< QVariantList >() )
+    {
+        if ( index.column() == 0 )
+        {
+            return index.row();
+        }
+        else
+        {
+            QVariantList the_list = thing.toList();
+            return the_list.at( index.row() );
+        }
+    }
+    else
+    {
+        if ( index.column() == 0 )
+        {
+            return QVariant();
+        }
+        else
+        {
+            return thing;
+        }
+    }
+}
+
+const QVariant
+VariantModel::underlying( const QModelIndex& index ) const
+{
+    if ( !index.isValid() )
+    {
+        return *m_p;
+    }
+
+    const auto& thing = underlying( parent( index ) );
+    if ( thing.canConvert< QVariantMap >() )
+    {
+        const auto& the_map = thing.toMap();
+        return the_map[ the_map.keys()[ index.row() ] ];
+    }
+    else if ( thing.canConvert< QVariantList >() )
+    {
+        return thing.toList()[ index.row() ];
+    }
+    else
+    {
+        return thing;
+    }
+
+    return QVariant();
+}
diff --git a/src/calamares/VariantModel.h b/src/calamares/VariantModel.h
new file mode 100644
index 000000000..9ea6f6737
--- /dev/null
+++ b/src/calamares/VariantModel.h
@@ -0,0 +1,74 @@
+/* === This file is part of Calamares - <https://github.com/calamares> ===
+ *
+ *   Copyright 2019, Adriaan de Groot <groot@kde.org>
+ *
+ *   Calamares is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   Calamares is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with Calamares. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef VARIANTMODEL_H
+#define VARIANTMODEL_H
+
+#include <QAbstractItemModel>
+#include <QVariantMap>
+#include <QVector>
+
+/** @brief A model that operates directly on a QVariant
+ *
+ * A VariantModel operates directly on an underlying
+ * QVariant, treating QVariantMap and QVariantList as
+ * nodes with multiple children. In general, putting
+ * a QVariantMap into a QVariant and passing that into
+ * the model will get you a tree-like model of the
+ * VariantMap's data structure.
+ *
+ * Take care of object lifetimes and that the underlying
+ * QVariant does not change during use.
+ */
+class VariantModel : public QAbstractItemModel
+{
+public:
+    /** @brief Auxiliary data
+     *
+     * The nodes of the tree are enumerated into a vector
+     * (of length equal to the number of nodes in the tree + 1)
+     * which are used to do index and parent calculations.
+     */
+    using IndexVector = QVector< quintptr >;
+
+    /** @brief Constructor
+     *
+     * The QVariant's lifetime is **not** affected by the model,
+     * so take care that the QVariant lives at least as long as
+     * the model). Also, don't change the QVariant underneath the model.
+     */
+    VariantModel( const QVariant* p );
+
+    ~VariantModel() override;
+
+    int columnCount( const QModelIndex& index ) const override;
+    int rowCount( const QModelIndex& index ) const override;
+
+    QModelIndex index( int row, int column, const QModelIndex& parent ) const override;
+    QModelIndex parent( const QModelIndex& index ) const override;
+    QVariant data( const QModelIndex& index, int role ) const override;
+
+private:
+    const QVariant* const m_p;
+    IndexVector m_rows;
+
+    /// @brief Implementation of walking an index through the variant-tree
+    const QVariant underlying( const QModelIndex& index ) const;
+};
+
+#endif

From 2bd03ad3c09129c7b078e73193c10d20cddfa891 Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 07:40:23 -0400
Subject: [PATCH 7/9] [calamares] Add reload() to update model after underlying
 data

---
 src/calamares/VariantModel.cpp | 16 +++++++++++-----
 src/calamares/VariantModel.h   | 11 ++++++++++-
 2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/calamares/VariantModel.cpp b/src/calamares/VariantModel.cpp
index bdcf8f75c..fc11794bc 100644
--- a/src/calamares/VariantModel.cpp
+++ b/src/calamares/VariantModel.cpp
@@ -70,15 +70,21 @@ findNth( const VariantModel::IndexVector& skiplist, quintptr value, int n )
 VariantModel::VariantModel( const QVariant* p )
     : m_p( p )
 {
-    int x = 0;
-    overallLength( *p, x, -1, nullptr );
-    m_rows.reserve( x );
-    x = 0;
-    overallLength( *p, x, -1, &m_rows );
+    reload();
 }
 
 VariantModel::~VariantModel() {}
 
+void VariantModel::reload()
+{
+    int x = 0;
+    overallLength( *m_p, x, -1, nullptr );
+    m_rows.clear();  // Start over
+    m_rows.reserve( x );  // We'll need this much
+    x = 0;
+    overallLength( *m_p, x, -1, &m_rows );
+}
+
 int
 VariantModel::columnCount( const QModelIndex& index ) const
 {
diff --git a/src/calamares/VariantModel.h b/src/calamares/VariantModel.h
index 9ea6f6737..b0c93e91c 100644
--- a/src/calamares/VariantModel.h
+++ b/src/calamares/VariantModel.h
@@ -33,7 +33,9 @@
  * VariantMap's data structure.
  *
  * Take care of object lifetimes and that the underlying
- * QVariant does not change during use.
+ * QVariant does not change during use. If the QVariant
+ * **does** change, call reload() to re-build the internal
+ * representation of the tree.
  */
 class VariantModel : public QAbstractItemModel
 {
@@ -56,6 +58,13 @@ public:
 
     ~VariantModel() override;
 
+    /** @brief Re-build the internal tree
+     *
+     * Call this when the underlying variant is changed, which
+     * might impact how the tree is laid out.
+     */
+    void reload();
+
     int columnCount( const QModelIndex& index ) const override;
     int rowCount( const QModelIndex& index ) const override;
 

From eba4dc8df131d247c05395d56ceb5081ddcf3f0e Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 07:48:52 -0400
Subject: [PATCH 8/9] [calamares] Use VariantModel instead of QJsonModel

 - Drop the round-trip of forming a JSON document from a QVariant,
   then parsing the document into JSON objects and building a
   model out of that. View the Variant directly.
---
 src/calamares/DebugWindow.cpp | 28 ++++++++++++++++------------
 src/calamares/DebugWindow.h   |  8 ++++++++
 2 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/src/calamares/DebugWindow.cpp b/src/calamares/DebugWindow.cpp
index 237b8a7ed..1680fb7de 100644
--- a/src/calamares/DebugWindow.cpp
+++ b/src/calamares/DebugWindow.cpp
@@ -20,6 +20,8 @@
 #include "DebugWindow.h"
 #include "ui_DebugWindow.h"
 
+#include "VariantModel.h"
+
 #include "Branding.h"
 #include "modulesystem/Module.h"
 #include "modulesystem/ModuleManager.h"
@@ -72,23 +74,25 @@ namespace Calamares {
 DebugWindow::DebugWindow()
     : QWidget( nullptr )
     , m_ui( new Ui::DebugWindow )
+    , m_globals( JobQueue::instance()->globalStorage()->data() )
+    , m_globals_model( std::make_unique< VariantModel >( &m_globals ) )
+    , m_module_model( std::make_unique< VariantModel >( &m_module ) )
 {
-    m_ui->setupUi( this );
+    GlobalStorage* gs = JobQueue::instance()->globalStorage();
 
-    // GlobalStorage page
-    QJsonModel* jsonModel = new QJsonModel( this );
+    m_ui->setupUi( this );
 
-    m_ui->globalStorageView->setModel( jsonModel );
-    GlobalStorage* gs = JobQueue::instance()->globalStorage();
+    m_ui->globalStorageView->setModel( m_globals_model.get() );
+    m_ui->globalStorageView->expandAll();
 
+    // Do above when the GS changes, too
     connect( gs, &GlobalStorage::changed,
              this, [ = ]
     {
-        jsonModel->loadJson( QJsonDocument::fromVariant( gs->m ).toJson() );
+        m_globals = JobQueue::instance()->globalStorage()->data();
+        m_globals_model->reload();
         m_ui->globalStorageView->expandAll();
     } );
-    jsonModel->loadJson( QJsonDocument::fromVariant( gs->m ).toJson() );
-    m_ui->globalStorageView->expandAll();
 
     // JobQueue page
     m_ui->jobQueueText->setReadOnly( true );
@@ -109,8 +113,7 @@ DebugWindow::DebugWindow()
     m_ui->modulesListView->setModel( modulesModel );
     m_ui->modulesListView->setSelectionMode( QAbstractItemView::SingleSelection );
 
-    QJsonModel* moduleConfigModel = new QJsonModel( this );
-    m_ui->moduleConfigView->setModel( moduleConfigModel );
+    m_ui->moduleConfigView->setModel( m_module_model.get() );
 
 #ifdef WITH_PYTHONQT
     QPushButton* pythonConsoleButton = new QPushButton;
@@ -181,7 +184,7 @@ DebugWindow::DebugWindow()
 #endif
 
     connect( m_ui->modulesListView->selectionModel(), &QItemSelectionModel::selectionChanged,
-             this, [ this, moduleConfigModel
+             this, [ this
 #ifdef WITH_PYTHONQT
              , pythonConsoleButton
 #endif
@@ -191,7 +194,8 @@ DebugWindow::DebugWindow()
         Module* module = ModuleManager::instance()->moduleInstance( moduleName );
         if ( module )
         {
-            moduleConfigModel->loadJson( QJsonDocument::fromVariant( module->configurationMap() ).toJson() );
+            m_module = module->configurationMap();
+            m_module_model->reload();
             m_ui->moduleConfigView->expandAll();
             m_ui->moduleTypeLabel->setText( module->typeString() );
             m_ui->moduleInterfaceLabel->setText( module->interfaceString() );
diff --git a/src/calamares/DebugWindow.h b/src/calamares/DebugWindow.h
index e97f5727b..1a121455e 100644
--- a/src/calamares/DebugWindow.h
+++ b/src/calamares/DebugWindow.h
@@ -20,8 +20,12 @@
 #ifndef CALAMARES_DEBUGWINDOW_H
 #define CALAMARES_DEBUGWINDOW_H
 
+#include "VariantModel.h"
+
+#include <QVariant>
 #include <QWidget>
 
+#include <memory>
 
 namespace Calamares {
 
@@ -46,6 +50,10 @@ protected:
 
 private:
     Ui::DebugWindow *m_ui;
+    QVariant m_globals;
+    QVariant m_module;
+    std::unique_ptr< VariantModel> m_globals_model;
+    std::unique_ptr< VariantModel> m_module_model;
 };
 
 

From ab7a559e03476ff98b4bbd61a8a96d1dbd3bdeeb Mon Sep 17 00:00:00 2001
From: Adriaan de Groot <groot@kde.org>
Date: Fri, 9 Aug 2019 07:58:20 -0400
Subject: [PATCH 9/9] [calamares] Add headerData to model (Key, Value columns)

---
 src/calamares/VariantModel.cpp | 32 +++++++++++++++++++++++++++++++-
 src/calamares/VariantModel.h   |  1 +
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/src/calamares/VariantModel.cpp b/src/calamares/VariantModel.cpp
index fc11794bc..965f0f3e0 100644
--- a/src/calamares/VariantModel.cpp
+++ b/src/calamares/VariantModel.cpp
@@ -75,7 +75,8 @@ VariantModel::VariantModel( const QVariant* p )
 
 VariantModel::~VariantModel() {}
 
-void VariantModel::reload()
+void
+VariantModel::reload()
 {
     int x = 0;
     overallLength( *m_p, x, -1, nullptr );
@@ -213,6 +214,35 @@ VariantModel::data( const QModelIndex& index, int role ) const
     }
 }
 
+QVariant
+VariantModel::headerData( int section, Qt::Orientation orientation, int role ) const
+{
+    if ( role != Qt::DisplayRole )
+    {
+        return QVariant();
+    }
+
+    if ( orientation == Qt::Horizontal )
+    {
+        if ( section == 0 )
+        {
+            return tr( "Key" );
+        }
+        else if ( section == 1 )
+        {
+            return tr( "Value" );
+        }
+        else
+        {
+            return QVariant();
+        }
+    }
+    else
+    {
+        return QVariant();
+    }
+}
+
 const QVariant
 VariantModel::underlying( const QModelIndex& index ) const
 {
diff --git a/src/calamares/VariantModel.h b/src/calamares/VariantModel.h
index b0c93e91c..9c4256f6a 100644
--- a/src/calamares/VariantModel.h
+++ b/src/calamares/VariantModel.h
@@ -71,6 +71,7 @@ public:
     QModelIndex index( int row, int column, const QModelIndex& parent ) const override;
     QModelIndex parent( const QModelIndex& index ) const override;
     QVariant data( const QModelIndex& index, int role ) const override;
+    QVariant headerData( int section, Qt::Orientation orientation, int role ) const override;
 
 private:
     const QVariant* const m_p;