@ 261105.1
current
/***************************************************************************
                               thememodel.cpp
                             -------------------
    begin                : 2026
    copyright            : (C) 2009-2026 by Andre Simon
    email                : a.simon@mailbox.org
 ***************************************************************************/

/*
This file is part of Highlight.

Highlight 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.

Highlight 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 Highlight.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "thememodel.h"

#include "themereader.h"

namespace
{
QColor toQColor(const highlight::Colour &c)
{
    return QColor(c.getRed(), c.getGreen(), c.getBlue());
}
}

ThemeListModel::ThemeListModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

QStringList ThemeListModel::addThemes(const QDir &dir, const QString &idPrefix)
{
    QStringList errors;
    QList<ThemeEntry> newThemes;

    const QStringList files = dir.entryList(QStringList("*.theme"), QDir::Files, QDir::Name);
    for (const auto &fileName : files) {
        highlight::ThemeReader reader;
        if (!reader.load(dir.absoluteFilePath(fileName).toStdString())) {
            errors << QString("%1: %2").arg(fileName, QString::fromStdString(reader.getErrorMessage()));
            continue;
        }

        ThemeEntry entry;
        entry.id = idPrefix + fileName.section('.', 0, 0);
        entry.description = QString::fromStdString(reader.getDescription());
        if (entry.description.isEmpty())
            entry.description = fileName.section('.', 0, 0);
        entry.categories = QString::fromStdString(reader.getCategoryDescription())
                               .split(',', Qt::SkipEmptyParts);
        entry.background = toQColor(reader.getBgColour());
        entry.foreground = toQColor(reader.getDefaultStyle().getColour());
        entry.keyword = toQColor(reader.getKeywordStyle("kwa").getColour());
        entry.type = toQColor(reader.getKeywordStyle("kwb").getColour());
        entry.string = toQColor(reader.getStringStyle().getColour());
        entry.number = toQColor(reader.getNumberStyle().getColour());
        entry.comment = toQColor(reader.getCommentStyle().getColour());
        newThemes.append(entry);
    }

    if (!newThemes.isEmpty()) {
        beginInsertRows(QModelIndex(), themes.size(), themes.size() + newThemes.size() - 1);
        themes.append(newThemes);
        endInsertRows();
    }
    return errors;
}

int ThemeListModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : themes.size();
}

QVariant ThemeListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= themes.size())
        return QVariant();

    const ThemeEntry &t = themes.at(index.row());
    switch (role) {
    case Qt::DisplayRole:
        return t.description;
    case Qt::ToolTipRole:
        return QString("%1\n%2").arg(t.id, t.categories.join(", "));
    case ThemeIdRole:
        return t.id;
    case CategoriesRole:
        return t.categories;
    case SearchTextRole:
        return QString("%1 %2 %3").arg(t.id, t.description, t.categories.join(' '));
    case BackgroundRole:
        return t.background;
    case ForegroundRole:
        return t.foreground;
    case KeywordRole:
        return t.keyword;
    case TypeRole:
        return t.type;
    case StringRole:
        return t.string;
    case NumberRole:
        return t.number;
    case CommentRole:
        return t.comment;
    default:
        return QVariant();
    }
}

int ThemeListModel::rowOfTheme(const QString &id) const
{
    for (int i = 0; i < themes.size(); ++i) {
        if (themes.at(i).id == id)
            return i;
    }
    return -1;
}

ThemeFilterProxyModel::ThemeFilterProxyModel(QObject *parent)
    : QSortFilterProxyModel(parent)
{
}

void ThemeFilterProxyModel::setSearchText(const QString &text)
{
    searchTerms = text.split(' ', Qt::SkipEmptyParts);
    invalidate();
}

bool ThemeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
    if (searchTerms.isEmpty())
        return true;

    QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent);
    QString haystack = idx.data(ThemeListModel::SearchTextRole).toString();
    for (const auto &term : searchTerms) {
        if (!haystack.contains(term, Qt::CaseInsensitive))
            return false;
    }
    return true;
}