// Copyright (C) 2022 The Qt Company Ltd // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "propertytreeitem.h" #include "squishtr.h" #include using namespace Utils; namespace Squish { namespace Internal { // Squish IDE uses lower-case for "Is" - shall we too? const QString Property::OPERATOR_IS = "Is"; const QString Property::OPERATOR_EQUALS = "Equals"; const QString Property::OPERATOR_REGEX = "RegEx"; const QString Property::OPERATOR_WILDCARD = "Wildcard"; /*************************************** Property *******************************************/ Property::Property() {} Property::Property(const QByteArray &data) { const int equalsPosition = data.indexOf('='); // no equals sign found or name is empty? if (equalsPosition <= 0) return; QByteArray namePart = data.left(equalsPosition).trimmed(); QByteArray valuePart = data.mid(equalsPosition + 1).trimmed(); if (!valuePart.startsWith('\'') || !valuePart.endsWith('\'')) return; const int namePartSize = namePart.size(); if (namePartSize > 1) { char lastChar = namePart.at(namePartSize - 1); if (lastChar == '~' || lastChar == '?') { namePart.chop(1); m_type = lastChar == '~' ? RegularExpression : Wildcard; } m_name = QLatin1String(namePart.trimmed()); } m_value = QLatin1String(valuePart.mid(1, valuePart.size() - 2)); } bool Property::set(const QString &propName, const QString &oper, const QString &propValue) { if (oper == "=") m_type = Equals; else if (oper == "~=") m_type = RegularExpression; else if (oper == "?=") m_type = Wildcard; else return false; m_name = propName; m_value = propValue; return true; } const QStringList Property::toStringList() const { QStringList result(m_name); switch (m_type) { case Equals: if (isContainer() || isRelativeWidget()) result << OPERATOR_IS; else result << OPERATOR_EQUALS; break; case RegularExpression: result << OPERATOR_REGEX; break; case Wildcard: result << OPERATOR_WILDCARD; break; default: QTC_ASSERT(false, result << QString()); break; } result << m_value; return result; } bool Property::isContainer() const { static const char container[] = "container"; static const char window[] = "window"; return m_name == container || m_name == window; } bool Property::isRelativeWidget() const { static const QStringList relatives({"buddy", "aboveWidget", "leftWidget", "parentWidget"}); return relatives.contains(m_name); } PropertyType Property::typeFromString(const QString &typeString) { if (typeString == OPERATOR_EQUALS || typeString == OPERATOR_IS) return Equals; if (typeString == OPERATOR_REGEX) return RegularExpression; if (typeString == OPERATOR_WILDCARD) return Wildcard; QTC_ASSERT(false, return Equals); } const QString Property::toString() const { switch (m_type) { case Equals: return QString::fromLatin1("%1='%2'").arg(m_name, m_value); case RegularExpression: return QString::fromLatin1("%1~='%2'").arg(m_name, m_value); case Wildcard: return QString::fromLatin1("%1?='%2'").arg(m_name, m_value); } QTC_ASSERT(false, return QString()); } /*********************************** PropertyTreeItem ***************************************/ enum ViewColumn { NameColumn, OperatorColumn, ValueColumn }; PropertyTreeItem::PropertyTreeItem(const Property &property, Qt::ItemFlags flags) : m_property(property) , m_flags(flags) {} QVariant PropertyTreeItem::data(int column, int role) const { if (role == Qt::DisplayRole && column >= NameColumn && column <= ValueColumn) return m_property.toStringList().at(column); return TreeItem::data(column, role); } bool PropertyTreeItem::setData(int column, const QVariant &data, int /*role*/) { // only accept untrimmed data for ValueColumn const QString value = column == ValueColumn ? data.toString() : data.toString().trimmed(); if (value.isEmpty() && column != ValueColumn) return false; switch (column) { case NameColumn: m_property.m_name = value; return true; case OperatorColumn: m_property.m_type = Property::typeFromString(value); return true; case ValueColumn: m_property.m_value = value; return true; } return false; } Qt::ItemFlags PropertyTreeItem::flags(int column) const { if (m_flags != Qt::NoItemFlags) return m_flags; return TreeItem::flags(column); } /*********************************** PropertiesModel ****************************************/ PropertiesModel::PropertiesModel(ObjectsMapTreeItem *parentItem) : TreeModel(new PropertyTreeItem({})) , m_parentItem(parentItem) { setHeader({Tr::tr("Name"), Tr::tr("Operator"), Tr::tr("Value")}); } bool PropertiesModel::setData(const QModelIndex &idx, const QVariant &data, int role) { // only editing is supported if (role != Qt::EditRole || !data.isValid()) return false; const int column = idx.column(); if (column < NameColumn || column > ValueColumn) return false; const QString old = idx.data().toString(); bool result = TreeModel::setData(idx, data, role); if (result) emit propertyChanged(m_parentItem, old, data.toString(), idx.row(), idx.column()); return result; } void PropertiesModel::addNewProperty(PropertyTreeItem *item) { QTC_ASSERT(item, return ); QTC_ASSERT(rootItem(), return ); rootItem()->appendChild(item); emit propertyAdded(m_parentItem); } void PropertiesModel::removeProperty(PropertyTreeItem *item) { QTC_ASSERT(item, return ); Property property = item->property(); delete takeItem(item); emit propertyRemoved(m_parentItem, property); } void PropertiesModel::modifySpecialProperty(const QString &oldValue, const QString &newValue) { TreeItem *root = rootItem(); QTC_ASSERT(root, return ); TreeItem *itemToChange = root->findChildAtLevel(1, [oldValue](TreeItem *child) { auto propertyItem = static_cast(child); Property property = propertyItem->property(); return (property.m_value == oldValue && (property.isContainer() || property.isRelativeWidget())); }); if (!itemToChange) return; auto propertyItem = static_cast(itemToChange); propertyItem->setData(ValueColumn, newValue, Qt::EditRole); const QModelIndex idx = indexForItem(propertyItem); emit propertyChanged(m_parentItem, oldValue, newValue, idx.row(), idx.column()); } QStringList PropertiesModel::allPropertyNames() const { TreeItem *root = rootItem(); if (!root) return QStringList(); QStringList result; result.reserve(root->childCount()); root->forChildrenAtLevel(1, [&result](TreeItem *child) { result.append(child->data(NameColumn, Qt::DisplayRole).toString()); }); return result; } /********************************* PropertiesSortModel **************************************/ PropertiesSortModel::PropertiesSortModel(QObject *parent) : QSortFilterProxyModel(parent) {} bool PropertiesSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { return left.data().toString() > right.data().toString(); } } // namespace Internal } // namespace Squish