Warning, file /graphics/tikzkit/src/ui/propertybrowser/PropertyBrowser.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the TikZKit project.
0002  *
0003  * Copyright (C) 2014 Dominik Haumann <dhaumann@kde.org>
0004  *
0005  * This library is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU Library General Public License as published
0007  * by the Free Software Foundation, either version 2 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, see
0017  * <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "PropertyBrowser.h"
0021 #include "ValuePropertyManager.h"
0022 #include "ValueSpinBoxFactory.h"
0023 #include "OpacityEditorFactory.h"
0024 #include "OpacityPropertyManager.h"
0025 #include "UidPropertyManager.h"
0026 
0027 #include <tikz/core/Uid.h>
0028 #include <tikz/core/Document.h>
0029 #include <tikz/core/Node.h>
0030 #include <tikz/core/Path.h>
0031 #include <tikz/core/Style.h>
0032 #include <tikz/core/PropertyManager.h>
0033 #include <tikz/core/UndoSetProperty.h>
0034 
0035 #include <tikz/ui/View.h>
0036 #include <tikz/ui/NodeItem.h>
0037 #include <tikz/ui/PathItem.h>
0038 #include <tikz/ui/Document.h>
0039 
0040 #include <QDebug>
0041 #include <QVBoxLayout>
0042 #include <QPointer>
0043 #include <QMetaProperty>
0044 
0045 #include <QtTreePropertyBrowser>
0046 #include <QtDoublePropertyManager>
0047 #include <QtDoubleSpinBoxFactory>
0048 #include <QtBoolPropertyManager>
0049 #include <QtColorPropertyManager>
0050 #include <QtColorEditorFactory>
0051 
0052 #include <QFile>
0053 #include <QJsonDocument>
0054 
0055 tikz::core::Style * styleForItem(const tikz::core::Uid & uid)
0056 {
0057     if (! uid.isValid()) {
0058         return nullptr;
0059     }
0060 
0061     auto e = uid.entity();
0062     switch (e->entityType()) {
0063         case tikz::EntityType::Document: return qvariant_cast<tikz::core::Style*>(uid.entity()->property("style"));
0064         case tikz::EntityType::Path: return qvariant_cast<tikz::core::Uid>(uid.entity()->property("style")).entity<tikz::core::Style>();
0065         case tikz::EntityType::Node: return qvariant_cast<tikz::core::Uid>(uid.entity()->property("style")).entity<tikz::core::Style>();
0066         case tikz::EntityType::Style: return uid.entity<tikz::core::Style>();
0067     }
0068 
0069     return nullptr;
0070 }
0071 
0072 namespace tikz {
0073 namespace ui {
0074 
0075 class PropertyBrowserPrivate
0076 {
0077 public:
0078     QtTreePropertyBrowser * browser = nullptr;
0079     ValuePropertyManager * valueManager = nullptr;
0080     QtBoolPropertyManager * boolManager = nullptr;
0081     QtColorPropertyManager * colorManager = nullptr;
0082     OpacityPropertyManager * opacityManager = nullptr;
0083     QtDoublePropertyManager * doubleManager = nullptr;
0084     QtEnumPropertyManager * enumManager = nullptr;
0085     QtStringPropertyManager * stringManager = nullptr;
0086     UidPropertyManager * uidManager = nullptr;
0087 
0088     QPointer<View> view = nullptr;
0089     tikz::core::Uid uid;
0090     QHash<QtProperty *, QString> propertyMap;
0091 
0092 public:
0093     void addProperty(QtProperty *property, const QString & name)
0094     {
0095         Q_ASSERT(! propertyMap.contains(property));
0096         propertyMap[property] = name;
0097 //         QtBrowserItem *item =
0098         browser->addProperty(property);
0099 //         browser->setExpanded(item, idToExpanded[name]);
0100     }
0101 
0102     void addSubProperty(QtProperty *parent, QtProperty *property, const QString & name)
0103     {
0104         Q_ASSERT(! propertyMap.contains(property));
0105         propertyMap[property] = name;
0106         parent->addSubProperty(property);
0107 //         browser->setExpanded(item, idToExpanded[name]);
0108     }
0109 
0110     void addProperty(QObject * object, const QString & name)
0111     {
0112         const auto info = tikz::core::propertyManager().info(name);
0113 
0114         if (!info.isValid()) {
0115             qDebug() << "Unknown property:" << name;
0116             return;
0117         }
0118 
0119         QVariant prop = object->property(name.toLatin1().data());
0120         if (!prop.isValid()) {
0121             qDebug() << "Unknown property type:" << name;
0122             return;
0123         }
0124 
0125         QtProperty *property = nullptr;
0126 
0127         const QString type = info.type();
0128         if (type == "string") {
0129             property = stringManager->addProperty(info.title());
0130             stringManager->setValue(property, prop.toString());
0131         }
0132         else if (type == "uid") {
0133             property = uidManager->addProperty(info.title());
0134             uidManager->setValue(property, prop.value<tikz::core::Uid>());
0135         }
0136         else if (type == "value") {
0137             property = valueManager->addProperty(info.title());
0138             valueManager->setRange(property, tikz::Value(0, tikz::Unit::Millimeter), tikz::Value(10, tikz::Unit::Millimeter));
0139             valueManager->setMinimum(property, tikz::Value::fromString(info.minimum().toString()));
0140             valueManager->setMaximum(property, tikz::Value::fromString(info.maximum().toString()));
0141             valueManager->setSingleStep(property, info.singleStep());
0142             valueManager->setValue(property, prop.value<tikz::Value>());
0143         }
0144         else if (type == "bool") {
0145             property = boolManager->addProperty(info.title());
0146             boolManager->setValue(property, prop.toBool());
0147         }
0148         else if (type == "color") {
0149             property = colorManager->addProperty(info.title());
0150             colorManager->setValue(property, prop.value<QColor>());
0151         }
0152         else if (type == "opacity") {
0153             property = opacityManager->addProperty(info.title());
0154             opacityManager->setValue(property, prop.toDouble());
0155         }
0156         else if (type == "double") {
0157             property = doubleManager->addProperty(info.title());
0158             doubleManager->setMinimum(property, info.minimum().toDouble());
0159             doubleManager->setMaximum(property, info.maximum().toDouble());
0160             doubleManager->setSingleStep(property, info.singleStep());
0161             doubleManager->setValue(property, prop.toDouble());
0162         }
0163         else if (type == "enum") {
0164             // find out property type
0165             auto metaProp = object->metaObject()->property(object->metaObject()->indexOfProperty(name.toLatin1().data()));
0166             if (metaProp.userType() == QMetaType::UnknownType) {
0167                 qDebug() << "Unknown enum type";
0168             }
0169 
0170             // remove namespace, if applicable
0171             QString typeName = metaProp.typeName();
0172             if (typeName.startsWith("tikz::")) {
0173                 typeName.remove(0, 6);
0174             }
0175 
0176             const int enumIndex = tikz::staticMetaObject.indexOfEnumerator(typeName.toLatin1().data());
0177             const QMetaEnum metaEnum = tikz::staticMetaObject.enumerator(enumIndex);
0178             QStringList enumNames;
0179             for (int i = 0; i < metaEnum.keyCount(); ++i) {
0180                 enumNames << metaEnum.key(i);
0181             }
0182             property = enumManager->addProperty(info.title());
0183             enumManager->setEnumNames(property, enumNames);
0184             enumManager->setValue(property, prop.toInt());
0185         }
0186         else {
0187             qDebug() << "Unknown property type:" << name;
0188         }
0189         if (property) {
0190             const QString modifiedFunction = info.modifiedFunction();
0191             if (!modifiedFunction.isEmpty()) {
0192                 bool isModified = false;
0193                 const bool ok = QMetaObject::invokeMethod(object,
0194                                                           modifiedFunction.toLatin1().data(),
0195                                                           Qt::DirectConnection,
0196                                                           Q_RETURN_ARG(bool, isModified)
0197                 );
0198                 if (ok) {
0199                     property->setModified(isModified);
0200                 } else {
0201                     tikz::debug("properties.json: Invalid modified getter " + modifiedFunction);
0202                 }
0203             }
0204             addProperty(property, name);
0205         }
0206     }
0207 
0208     template <typename T>
0209     void setValue(QtProperty * property, const T & val)
0210     {
0211         // if items are inserted, the slot valueChanged() is also called.
0212         // In this case, the item is not yet registered in the map. Hence,
0213         // we just return.
0214         if (! propertyMap.contains(property)) {
0215             return;
0216         }
0217 
0218         auto style = styleForItem(uid);
0219         if (style) {
0220             const QString name = propertyMap[property];
0221             style->document()->addUndoItem(new tikz::core::UndoSetProperty(style->uid(), name, val));
0222         }
0223     }
0224 };
0225 
0226 
0227 
0228 
0229 
0230 PropertyBrowser::PropertyBrowser(QWidget *parent)
0231     : QWidget(parent)
0232     , d(new PropertyBrowserPrivate)
0233 {
0234     d->browser = new QtTreePropertyBrowser(this);
0235     auto vboxLayout = new QVBoxLayout(this);
0236     vboxLayout->setContentsMargins(0, 0, 0, 0);
0237     vboxLayout->addWidget(d->browser);
0238 
0239     // setup property managers
0240     d->valueManager = new ValuePropertyManager(this);
0241     d->boolManager = new QtBoolPropertyManager(this);
0242     d->colorManager = new QtColorPropertyManager(this);
0243     d->opacityManager = new OpacityPropertyManager(this);
0244     d->doubleManager = new QtDoublePropertyManager(this);
0245     d->enumManager = new QtEnumPropertyManager(this);
0246     d->stringManager = new QtStringPropertyManager(this);
0247     d->uidManager = new UidPropertyManager(this);
0248 
0249     // setup propertybrowser
0250     d->browser->setFactoryForManager(d->valueManager, new ValueSpinBoxFactory(this));
0251     d->browser->setFactoryForManager(d->boolManager, new QtCheckBoxFactory(this));
0252     d->browser->setFactoryForManager(d->colorManager, new QtColorEditorFactory(this));
0253     d->browser->setFactoryForManager(d->opacityManager, new OpacityEditorFactory(this));
0254     d->browser->setFactoryForManager(d->doubleManager, new QtDoubleSpinBoxFactory(this));
0255     d->browser->setFactoryForManager(d->enumManager, new QtEnumEditorFactory(this));
0256     d->browser->setFactoryForManager(d->stringManager, new QtLineEditFactory(this));
0257 //  d->uidManager: Uids are read-only, no editor required
0258 
0259     d->browser->setPropertiesWithoutValueMarked(true);
0260     d->browser->setRootIsDecorated(false);
0261     d->browser->setResizeMode(QtTreePropertyBrowser::ResizeToContents);
0262 
0263     // setup connections
0264     connect(d->valueManager, SIGNAL(valueChanged(QtProperty*, const tikz::Value &)),
0265             this, SLOT(valueChanged(QtProperty*, const tikz::Value &)));
0266     connect(d->boolManager, SIGNAL(valueChanged(QtProperty*, bool)),
0267             this, SLOT(valueChanged(QtProperty*, bool)));
0268     connect(d->colorManager, SIGNAL(valueChanged(QtProperty*, const QColor &)),
0269             this, SLOT(valueChanged(QtProperty*, const QColor &)));
0270     connect(d->opacityManager, SIGNAL(valueChanged(QtProperty*, qreal)),
0271             this, SLOT(valueChanged(QtProperty*, qreal)));
0272     connect(d->doubleManager, SIGNAL(valueChanged(QtProperty*, double)),
0273             this, SLOT(doubleValueChanged(QtProperty*, double)));
0274     connect(d->enumManager, SIGNAL(valueChanged(QtProperty*, int)),
0275             this, SLOT(enumValueChanged(QtProperty*, int)));
0276     connect(d->stringManager, SIGNAL(valueChanged(QtProperty*, QString)),
0277             this, SLOT(valueChanged(QtProperty*, QString)));
0278     // d->uidManager: read-only, no connection required
0279 }
0280 
0281 PropertyBrowser::~PropertyBrowser()
0282 {
0283     delete d;
0284 }
0285 
0286 void PropertyBrowser::setView(tikz::ui::View * view)
0287 {
0288     if (d->view == view) {
0289         return;
0290     }
0291 
0292     if (d->view) {
0293         disconnect(d->view, SIGNAL(selectionChanged(tikz::ui::View*)), this, SLOT(updateCurrentItem()));
0294     }
0295 
0296     d->view = view;
0297     if (d->view) {
0298         connect(d->view, SIGNAL(selectionChanged(tikz::ui::View*)), this, SLOT(updateCurrentItem()));
0299     }
0300 
0301     updateCurrentItem();
0302 }
0303 
0304 void PropertyBrowser::setItem(const tikz::core::Uid & uid)
0305 {
0306     if (d->uid == uid) {
0307         return;
0308     }
0309 
0310     d->uid = uid;
0311 
0312     // clear all managers and mappings
0313     d->valueManager->clear();
0314     d->boolManager->clear();
0315     d->colorManager->clear();
0316     d->opacityManager->clear();
0317     d->doubleManager->clear();
0318     d->enumManager->clear();
0319     d->stringManager->clear();
0320     d->uidManager->clear();
0321     d->propertyMap.clear();
0322 
0323     auto style = styleForItem(d->uid);
0324     if (style) {
0325         auto metaObj = style->metaObject();
0326         while (metaObj) {
0327             for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
0328                 d->addProperty(style, metaObj->property(i).name());
0329             }
0330             metaObj = metaObj->superClass();
0331         }
0332     }
0333 }
0334 
0335 void PropertyBrowser::valueChanged(QtProperty *property, const tikz::Value & val)
0336 {
0337     d->setValue(property, val);
0338 }
0339 
0340 void PropertyBrowser::valueChanged(QtProperty *property, bool val)
0341 {
0342     d->setValue(property, val);
0343 }
0344 
0345 void PropertyBrowser::valueChanged(QtProperty *property, const QColor & val)
0346 {
0347     d->setValue(property, val);
0348 }
0349 
0350 void PropertyBrowser::valueChanged(QtProperty *property, int val)
0351 {
0352     d->setValue(property, val);
0353 }
0354 
0355 void PropertyBrowser::valueChanged(QtProperty *property, double val)
0356 {
0357     d->setValue(property, val);
0358 }
0359 
0360 void PropertyBrowser::doubleValueChanged(QtProperty *property, double val)
0361 {
0362     d->setValue(property, val);
0363 }
0364 
0365 void PropertyBrowser::enumValueChanged(QtProperty *property, int val)
0366 {
0367     d->setValue(property, val);
0368 }
0369 
0370 void PropertyBrowser::valueChanged(QtProperty *property, const QString & val)
0371 {
0372     d->setValue(property, val);
0373 }
0374 
0375 void PropertyBrowser::updateCurrentItem()
0376 {
0377     auto tikzItems = d->view->selectedItems();
0378     if (tikzItems.size() != 1) {
0379         if (d->view) {
0380             setItem(d->view->document()->uid());
0381         } else {
0382             setItem(tikz::core::Uid());
0383         }
0384         return;
0385     }
0386 
0387     auto item = tikzItems.front();
0388     if (qobject_cast<NodeItem*>(item)) {
0389         setItem(qobject_cast<NodeItem*>(item)->uid());
0390     } else if (qobject_cast<PathItem*>(item)) {
0391         setItem(qobject_cast<PathItem*>(item)->uid());
0392     }
0393 }
0394 
0395 }
0396 }
0397 
0398 // kate: indent-width 4; replace-tabs on;