Commit 9842b9e1 authored by jan.koester's avatar jan.koester
Browse files

speedup

parent a9852d28
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -825,3 +825,35 @@ QString DocumentManager::publishTarget() const { return _publishTarget; }
void DocumentManager::setPublishTarget(const QString &target) { _publishTarget = target; }
QString DocumentManager::publishParams() const { return _publishParams; }
void DocumentManager::setPublishParams(const QString &paramsJson) { _publishParams = paramsJson; }

EditPlugin *DocumentManager::findParentOf(const EditPlugin *child) const {
    if (!child || !_docRoot) return nullptr;

    // Check if child is a top-level sibling (parent = virtual root = nullptr)
    const EditPlugin *el = _docRoot;
    while (el) {
        if (el == child) return nullptr;
        el = el->nextElement();
    }

    // Recursive search through the tree
    std::function<EditPlugin*(EditPlugin*)> search = [&](EditPlugin *node) -> EditPlugin* {
        const EditPlugin *c = node->getChildElement();
        while (c) {
            if (c == child) return node;
            // Search deeper
            auto *found = search(const_cast<EditPlugin*>(c));
            if (found) return found;
            c = c->nextElement();
        }
        return nullptr;
    };

    el = _docRoot;
    while (el) {
        auto *found = search(const_cast<EditPlugin*>(el));
        if (found) return found;
        el = el->nextElement();
    }
    return nullptr;
}
+4 −0
Original line number Diff line number Diff line
@@ -88,6 +88,10 @@ public:
    QString publishParams() const;
    void setPublishParams(const QString &paramsJson);

    /* Tree access for DocumentTreeModel */
    blogi::webedit::EditPlugin *docRoot() const { return _docRoot; }
    blogi::webedit::EditPlugin *findParentOf(const blogi::webedit::EditPlugin *child) const;

private:
    void clearDocument();
    void loadWidgetsFromXml(tinyxml2::XMLElement *xmlEl,
+201 −0
Original line number Diff line number Diff line
/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "documenttreemodel.h"
#include "documentmanager.h"

/* Undefine Qt's 'slots' macro before including plugin.h */
#ifdef slots
#undef slots
#endif
#include "plugin.h"

using blogi::webedit::EditPlugin;

DocumentTreeModel::DocumentTreeModel(DocumentManager *doc, QObject *parent)
    : QAbstractItemModel(parent), _doc(doc)
{
}

QHash<int, QByteArray> DocumentTreeModel::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[UuidRole] = "uuid";
    roles[TypeIdRole] = "typeId";
    roles[HasChildrenRole] = "hasChildren";
    roles[Qt::DisplayRole] = "display";
    return roles;
}

QModelIndex DocumentTreeModel::index(int row, int column, const QModelIndex &parent) const {
    if (column != 0)
        return QModelIndex();

    EditPlugin *parentPlugin = pluginFromIndex(parent);

    // Get the Nth child
    const EditPlugin *child = nullptr;
    if (!parentPlugin) {
        // Top-level: walk the docRoot linked list
        child = _doc->docRoot();
    } else {
        child = parentPlugin->getChildElement();
    }

    for (int i = 0; i < row && child; ++i)
        child = child->nextElement();

    if (!child)
        return QModelIndex();

    return createIndex(row, 0, const_cast<EditPlugin*>(child));
}

QModelIndex DocumentTreeModel::parent(const QModelIndex &child) const {
    if (!child.isValid())
        return QModelIndex();

    auto *childPlugin = static_cast<EditPlugin*>(child.internalPointer());
    if (!childPlugin)
        return QModelIndex();

    EditPlugin *par = _doc->findParentOf(childPlugin);
    if (!par)
        return QModelIndex(); // top-level node

    // Find row of parent in its own parent's children
    int row = childRow(par);
    return createIndex(row, 0, par);
}

int DocumentTreeModel::rowCount(const QModelIndex &parent) const {
    const EditPlugin *node = pluginFromIndex(parent);
    const EditPlugin *child = nullptr;

    if (!node) {
        // Top-level count
        child = _doc->docRoot();
    } else {
        child = node->getChildElement();
    }

    int count = 0;
    while (child) {
        ++count;
        child = child->nextElement();
    }
    return count;
}

int DocumentTreeModel::columnCount(const QModelIndex &) const {
    return 1;
}

QVariant DocumentTreeModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid())
        return QVariant();

    auto *plugin = static_cast<EditPlugin*>(index.internalPointer());
    if (!plugin)
        return QVariant();

    switch (role) {
    case Qt::DisplayRole:
    case NameRole:
        return QString::fromStdString(plugin->getName());
    case UuidRole:
        return QString::fromStdString(plugin->getInstanceId().c_str());
    case TypeIdRole:
        return QString::fromStdString(plugin->getTypeId().c_str());
    case HasChildrenRole:
        return plugin->getChildElement() != nullptr;
    default:
        return QVariant();
    }
}

void DocumentTreeModel::notifyFullReset() {
    beginResetModel();
    endResetModel();
}

void DocumentTreeModel::notifyNodeChanged(const QString &uuid) {
    // Find the node and emit dataChanged for it
    QModelIndex idx = indexForPlugin(nullptr);
    // For simplicity, do a full reset for now — still much cheaper than
    // recreating hundreds of QML Loader items
    Q_UNUSED(uuid)
    beginResetModel();
    endResetModel();
}

/* ---- Private helpers ---- */

EditPlugin *DocumentTreeModel::pluginFromIndex(const QModelIndex &index) const {
    if (!index.isValid())
        return nullptr;
    return static_cast<EditPlugin*>(index.internalPointer());
}

QModelIndex DocumentTreeModel::indexForPlugin(const EditPlugin *plugin, const QModelIndex &parent) const {
    if (!plugin) return QModelIndex();

    int rows = rowCount(parent);
    for (int i = 0; i < rows; ++i) {
        QModelIndex idx = index(i, 0, parent);
        if (idx.internalPointer() == plugin)
            return idx;
        // Recurse into children
        QModelIndex found = indexForPlugin(plugin, idx);
        if (found.isValid())
            return found;
    }
    return QModelIndex();
}

EditPlugin *DocumentTreeModel::parentPlugin(const EditPlugin *child) const {
    return _doc->findParentOf(child);
}

int DocumentTreeModel::childRow(const EditPlugin *child) const {
    if (!child) return 0;

    EditPlugin *par = _doc->findParentOf(child);
    const EditPlugin *sibling = nullptr;
    if (!par) {
        sibling = _doc->docRoot();
    } else {
        sibling = par->getChildElement();
    }

    int row = 0;
    while (sibling && sibling != child) {
        ++row;
        sibling = sibling->nextElement();
    }
    return row;
}
+45 −0
Original line number Diff line number Diff line
#pragma once

#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QString>

/* Forward-declare EditPlugin without pulling in netplus headers */
namespace blogi { namespace webedit { class EditPlugin; } }

class DocumentManager;

class DocumentTreeModel : public QAbstractItemModel {
    Q_OBJECT
public:
    enum Roles {
        NameRole = Qt::UserRole + 1,
        UuidRole,
        TypeIdRole,
        HasChildrenRole
    };
    Q_ENUM(Roles)

    explicit DocumentTreeModel(DocumentManager *doc, QObject *parent = nullptr);

    /* QAbstractItemModel interface */
    QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &child) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QHash<int, QByteArray> roleNames() const override;

    /* Notify model of structural changes */
    void notifyFullReset();
    void notifyNodeChanged(const QString &uuid);

private:
    blogi::webedit::EditPlugin *pluginFromIndex(const QModelIndex &index) const;
    QModelIndex indexForPlugin(const blogi::webedit::EditPlugin *plugin, const QModelIndex &parent = QModelIndex()) const;
    blogi::webedit::EditPlugin *parentPlugin(const blogi::webedit::EditPlugin *child) const;
    int childRow(const blogi::webedit::EditPlugin *child) const;

    DocumentManager *_doc;
};
+7 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "localapi.h"
#include "documentmanager.h"
#include "publishclient.h"
#include "documenttreemodel.h"

#include <QFileDialog>
#include <QFile>
@@ -46,12 +47,18 @@
LocalApi::LocalApi(DocumentManager *doc, PublishClient *pub, QObject *parent)
    : QObject(parent), _doc(doc), _pub(pub) {
    _fsModel = new GitFileSystemModel(this);
    _treeModel = new DocumentTreeModel(doc, this);
    connect(this, &LocalApi::documentChanged, _treeModel, &DocumentTreeModel::notifyFullReset);
}

GitFileSystemModel* LocalApi::fileSystemModel() {
    return _fsModel;
}

DocumentTreeModel* LocalApi::documentTreeModel() {
    return _treeModel;
}

void LocalApi::setFileSystemRoot(const QString &path) {
    _fsModel->setRootPath(path);
}
Loading