Files
qad/libs/qglview/propertyeditor.cpp
2022-12-14 14:14:33 +03:00

551 lines
20 KiB
C++

#include "propertyeditor.h"
#include "clineedit.h"
#include "colorbutton.h"
#include "qpointedit.h"
#include "qrectedit.h"
#include <QApplication>
#include <QCheckBox>
#include <QPainter>
#include <QSpinBox>
QWidget * Delegate::widgetForProperty(QWidget * parent, const QModelIndex & index) const {
QWidget * w = 0;
int type = 0;
QVariant value = index.data(Qt::UserRole);
if (index.data(Qt::UserRole + 2).toString() == "__flags") return 0;
if (index.data(Qt::UserRole + 1).toString() == "__flag") {
qulonglong key = index.data(Qt::UserRole).toULongLong();
value = index.parent().data(Qt::UserRole);
w = new QCheckBox(parent);
type = 14;
((QCheckBox *)w)->setChecked(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0));
((QCheckBox *)w)->setText("0x" + QString::number(key, 16).toUpper());
connect((QCheckBox *)w, SIGNAL(clicked(bool)), this, SLOT(changedFlag()));
// qDebug() << prop.enumerator().name();
} else {
if (value.canConvert<PropertyValuePair>()) {
PropertyValuePair prop = value.value<PropertyValuePair>();
if (prop.first.isEnumType()) {
w = new QComboBox(parent);
type = 13;
((QComboBox *)w)->setCurrentIndex(value.toInt());
w->setProperty("__prop", QVariant::fromValue<QMetaProperty>(prop.first));
QMetaEnum menum = prop.first.enumerator();
for (int i = 0; i < menum.keyCount(); ++i) {
((QComboBox *)w)
->addItem(QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")", menum.value(i));
if (menum.value(i) == prop.second.toInt()) ((QComboBox *)w)->setCurrentIndex(i);
}
connect((QComboBox *)w, SIGNAL(currentIndexChanged(int)), this, SLOT(changed()));
}
} else {
switch (value.type()) {
case QVariant::Int:
w = new QSpinBox(parent);
type = 2;
((QSpinBox *)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF);
connect((QSpinBox *)w, SIGNAL(valueChanged(int)), this, SLOT(changed()));
break;
case QVariant::UInt:
w = new QSpinBox(parent);
type = 3;
((QSpinBox *)w)->setRange(0, 0xFFFFFFFF);
connect((QSpinBox *)w, SIGNAL(valueChanged(int)), this, SLOT(changed()));
break;
case QVariant::LongLong:
w = new QSpinBox(parent);
type = 4;
((QSpinBox *)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF);
connect((QSpinBox *)w, SIGNAL(valueChanged(int)), this, SLOT(changed()));
break;
case QVariant::ULongLong:
w = new QSpinBox(parent);
type = 5;
((QSpinBox *)w)->setRange(0, 0xFFFFFFFF);
connect((QSpinBox *)w, SIGNAL(valueChanged(int)), this, SLOT(changed()));
break;
case QVariant::Double:
w = new QDoubleSpinBox(parent);
type = 6;
((QDoubleSpinBox *)w)->setRange(-999999999, 999999999);
((QDoubleSpinBox *)w)->setDecimals(3);
connect((QDoubleSpinBox *)w, SIGNAL(valueChanged(double)), this, SLOT(changed()));
break;
case QVariant::Bool:
w = new QCheckBox(parent);
type = 7;
((QCheckBox *)w)->setChecked(value.toBool());
connect((QCheckBox *)w, SIGNAL(toggled(bool)), this, SLOT(changed()));
break;
case QVariant::Color:
w = new ColorButton(parent);
type = 8;
((ColorButton *)w)->setUseAlphaChannel(true);
((ColorButton *)w)->setColor(value.value<QColor>());
connect((ColorButton *)w, SIGNAL(colorChanged(QColor)), this, SLOT(changed()));
break;
case QVariant::Point:
w = new QPointEdit(parent);
type = 9;
((QPointEdit *)w)->setDecimals(0);
((QPointEdit *)w)->setValue(QPointF(value.toPoint()));
connect((QPointEdit *)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed()));
break;
case QVariant::PointF:
w = new QPointEdit(parent);
type = 10;
((QPointEdit *)w)->setDecimals(3);
((QPointEdit *)w)->setValue(value.toPointF());
connect((QPointEdit *)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed()));
break;
case QVariant::Rect:
w = new QRectEdit(parent);
type = 11;
((QRectEdit *)w)->setDecimals(0);
((QRectEdit *)w)->setValue(QRectF(value.toRect()));
connect((QRectEdit *)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed()));
break;
case QVariant::RectF:
w = new QRectEdit(parent);
type = 12;
((QRectEdit *)w)->setDecimals(3);
((QRectEdit *)w)->setValue(value.toRectF());
connect((QRectEdit *)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed()));
break;
case QVariant::String:
default:
w = new CLineEdit(parent);
type = 1;
((CLineEdit *)w)->setDefaultText(value.toString());
connect((CLineEdit *)w, SIGNAL(textChanged(QString)), this, SLOT(changed()));
break;
}
}
}
if (w == 0) return 0;
/*QPalette pal = w->palette();
pal.setColor(QPalette::Window, Qt::white);
w->setPalette(pal);*/
w->setAutoFillBackground(true);
w->setProperty("__type", type);
return w;
}
void Delegate::setWidgetProperty(QWidget * w, const QVariant & value) const {
if (w == 0) return;
switch (w->property("__type").toInt()) {
case 1: ((CLineEdit *)w)->setText(value.toString()); break;
case 2:
case 3:
case 4:
case 5: ((QSpinBox *)w)->setValue(value.toInt()); break;
case 6: ((QDoubleSpinBox *)w)->setValue(value.toDouble()); break;
case 7: ((QCheckBox *)w)->setChecked(value.toBool()); break;
case 8: ((ColorButton *)w)->setColor(value.value<QColor>()); break;
case 9: ((QPointEdit *)w)->setValue(value.value<QPoint>()); break;
case 10: ((QPointEdit *)w)->setValue(value.value<QPointF>()); break;
case 11: ((QRectEdit *)w)->setValue(value.value<QRect>()); break;
case 12: ((QRectEdit *)w)->setValue(value.value<QRectF>()); break;
}
}
const QVariant Delegate::widgetProperty(QWidget * w) const {
if (w == 0) return QVariant();
switch (w->property("__type").toInt()) {
case 1: return QVariant::fromValue<QString>(((CLineEdit *)w)->text()); break;
case 2: return QVariant::fromValue<int>(((QSpinBox *)w)->value()); break;
case 3: return QVariant::fromValue<uint>(((QSpinBox *)w)->value()); break;
case 4: return QVariant::fromValue<qlonglong>(((QSpinBox *)w)->value()); break;
case 5: return QVariant::fromValue<qulonglong>(((QSpinBox *)w)->value()); break;
case 6: return QVariant::fromValue<double>(((QDoubleSpinBox *)w)->value()); break;
case 7: return QVariant::fromValue<bool>(((QCheckBox *)w)->isChecked()); break;
case 8: return QVariant::fromValue<QColor>(((ColorButton *)w)->color()); break;
case 9: return QVariant::fromValue<QPoint>(((QPointEdit *)w)->value().toPoint()); break;
case 10: return QVariant::fromValue<QPointF>(((QPointEdit *)w)->value()); break;
case 11: return QVariant::fromValue<QRect>(((QRectEdit *)w)->value().toRect()); break;
case 12: return QVariant::fromValue<QRectF>(((QRectEdit *)w)->value()); break;
case 13:
return QVariant::fromValue<PropertyValuePair>(
PropertyValuePair(w->property("__prop").value<QMetaProperty>(), ((QComboBox *)w)->itemData(((QComboBox *)w)->currentIndex())));
break;
default: return QVariant(); break;
}
return QVariant();
}
void Delegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
if (index.data(Qt::UserRole + 1).toString() != "__flag") model->setData(index, widgetProperty(editor), Qt::UserRole);
}
void Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
QStyledItemDelegate::paint(painter, option, index);
QVariant value = index.data(Qt::UserRole);
QStyle * style = QApplication::style();
QStyleOption * so = 0;
QStyleOptionComplex * soc = 0;
QString text;
QRect rect;
QPalette::ColorRole role =
(option.state.testFlag(QStyle::State_Selected) && option.state.testFlag(QStyle::State_Active) ? QPalette::HighlightedText
: QPalette::WindowText);
if (index.data(Qt::UserRole + 2).toString() == "__flags") {
text = "0x" + QString::number(value.toInt(), 16).toUpper();
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
return;
}
if (index.data(Qt::UserRole + 1) == "__flag") {
qulonglong key = index.data(Qt::UserRole).toULongLong();
value = index.parent().data(Qt::UserRole);
so = new QStyleOptionButton();
so->rect = option.rect;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionButton *)so)->state =
(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0) ? QStyle::State_On
: QStyle::State_Off) |
option.state;
((QStyleOptionButton *)so)->text = "0x" + QString::number(key, 16).toUpper();
if (option.state.testFlag(QStyle::State_Selected))
so->palette.setColor(QPalette::WindowText, so->palette.color(QPalette::HighlightedText));
style->drawControl(QStyle::CE_CheckBox, so, painter);
} else {
if (value.canConvert<PropertyValuePair>()) {
PropertyValuePair prop = value.value<PropertyValuePair>();
if (prop.first.isEnumType()) {
QMetaEnum menum = prop.first.enumerator();
for (int i = 0; i < menum.keyCount(); ++i) {
if (menum.value(i) == prop.second.toInt()) {
text = QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")";
break;
}
}
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
}
} else {
switch (value.type()) {
case QVariant::Int:
text.setNum(value.toInt());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::UInt:
text.setNum(value.toUInt());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::LongLong:
text.setNum(value.toLongLong());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::ULongLong:
text.setNum(value.toULongLong());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::Double:
text.setNum(value.toDouble(), 'f', 3);
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::Bool:
so = new QStyleOptionButton();
so->rect = option.rect;
so->state = option.state;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionButton *)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off) | option.state;
style->drawControl(QStyle::CE_CheckBox, so, painter);
break;
case QVariant::Color:
rect = option.rect; // style->subElementRect(QStyle::QStyle::SE_FrameContents, so);
rect.setRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6);
painter->fillRect(rect, ab);
painter->fillRect(rect, value.value<QColor>());
break;
case QVariant::Point:
text = pointString(value.toPoint());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::PointF:
text = pointString(value.toPointF());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::Rect:
text = rectString(value.toRect());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::RectF:
text = rectString(value.toRectF());
style->drawItemText(painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
text,
role);
break;
case QVariant::String:
default:
style->drawItemText(
painter,
style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, value.toString()),
Qt::AlignLeft | Qt::AlignVCenter,
option.palette,
true,
value.toString(),
role);
break;
}
}
}
/*so = new QStyleOptionFrame();
so->rect = option.rect;
so->state = option.state;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionFrame*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off);
style->drawPrimitive(QStyle::PE_PanelLineEdit, so, painter);
style->drawPrimitive(QStyle::PE_FrameLineEdit, so, painter);
break;*/
if (so != 0) delete so;
if (soc != 0) delete soc;
}
void Delegate::changedFlag() {
QAbstractItemModel * model = const_cast<QAbstractItemModel *>(cmi.model());
model->setData(cmi, qobject_cast<QCheckBox *>(sender())->isChecked(), Qt::UserRole + 3);
QModelIndex p = cmi.parent(), mi;
int row = 0;
qulonglong val = 0;
QList<QModelIndex> chldr;
mi = model->index(row, 1, p);
while (mi.isValid()) {
chldr << mi;
model->setData(mi, !mi.data(Qt::UserRole + 4).toBool(), Qt::UserRole + 4);
mi = model->index(++row, 1, p);
}
bool cc = cmi.data(Qt::UserRole + 3).toBool();
qulonglong cv = cmi.data(Qt::UserRole).toULongLong();
// qDebug() << "*****";
if (cc && cv == 0) {
val = 0;
// qDebug() << "null" << cv;
} else {
if (!cc && cv != 0) {
// qDebug() << "uncheck" << cv;
for (int i = 0; i < chldr.size(); ++i) {
if (chldr[i] == cmi) continue;
// qDebug() << (chldr[i].data(Qt::UserRole).toULongLong() & cv);
if (chldr[i].data(Qt::UserRole).toULongLong() & cv) model->setData(chldr[i], false, Qt::UserRole + 3);
}
}
for (int i = 0; i < chldr.size(); ++i) {
// qDebug() << chldr[i].data(Qt::UserRole + 3).toBool();
if (chldr[i].data(Qt::UserRole + 3).toBool()) val |= chldr[i].data(Qt::UserRole).toULongLong();
}
}
for (int i = 0; i < chldr.size(); ++i) {
if (chldr[i] == cmi) continue;
cv = chldr[i].data(Qt::UserRole).toULongLong();
model->setData(chldr[i], ((val & cv) == cv && cv != 0) || (val == 0 && cv == 0), Qt::UserRole + 3);
}
// qDebug() << val;
model->setData(p, val, Qt::UserRole);
model->setData(p.sibling(p.row(), 1), val, Qt::UserRole);
}
PropertyEditor::PropertyEditor(QWidget * parent): QTreeWidget(parent) {
object = 0;
active_ = false;
configTree();
connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(itemClicked(QTreeWidgetItem *, int)));
connect(this, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(itemChanged(QTreeWidgetItem *, int)));
}
PropertyEditor::~PropertyEditor() {}
void PropertyEditor::changeEvent(QEvent * e) {
QTreeWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
configTree();
return;
}
}
void PropertyEditor::configTree() {
setColumnCount(2);
setRootIsDecorated(false);
setColumnWidth(0, 170);
setColumnWidth(1, 10);
header()->setStretchLastSection(true);
QStringList lbls;
lbls << tr("Property") << tr("Value");
setHeaderLabels(lbls);
setAlternatingRowColors(true);
setItemDelegateForColumn(1, new Delegate());
}
void PropertyEditor::itemClicked(QTreeWidgetItem * item, int column) {
if (column == 0)
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
else {
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
editItem(item, 1);
}
}
void PropertyEditor::itemChanged(QTreeWidgetItem * item, int column) {
if (!active_) return;
if (column != 1) return;
QVariant value = item->data(1, Qt::UserRole);
if (value.canConvert<PropertyValuePair>()) {
value = value.value<PropertyValuePair>().second;
}
object->setProperty(item->text(0).toLatin1(), value);
}
void PropertyEditor::rebuild() {
clear();
configTree();
if (object == 0) return;
active_ = false;
const QMetaObject * mo = object->metaObject();
QList<const QMetaObject *> mol;
while (mo != 0) {
mol.push_front(mo);
mo = mo->superClass();
}
int ps, pe;
QTreeWidgetItem *ti, *tli, *tfi;
QVariant value;
// QWidget * pw = 0;
int chue = 0;
QColor bc;
font_b = font();
font_b.setBold(true);
foreach(const QMetaObject * o, mol) {
ps = o->propertyOffset();
pe = o->propertyCount(); // - ps;
// qDebug() << i->className() << ps << pe;
tli = new QTreeWidgetItem();
tli->setText(0, o->className());
tli->setFont(0, font_b);
setItemBackColor(tli, Qt::darkGray);
setItemForeColor(tli, Qt::white);
tli->setFirstColumnSpanned(true);
addTopLevelItem(tli);
tli->setExpanded(true);
for (int i = ps; i < pe; ++i) {
props << o->property(i);
value = o->property(i).read(object);
ti = new QTreeWidgetItem();
ti->setSizeHint(1, QSize(20, 20));
bc.setHsv(chue, 60, 245 + (i % 2) * 20 - 10);
setItemBackColor(ti, bc);
ti->setText(0, o->property(i).name());
if (props.back().isFlagType()) {
QMetaEnum menum = props.back().enumerator();
for (int j = 0; j < menum.keyCount(); ++j) {
tfi = new QTreeWidgetItem();
tfi->setText(0, menum.key(j));
tfi->setData(1, Qt::UserRole, menum.value(j));
tfi->setData(1, Qt::UserRole + 1, "__flag");
tfi->setData(1, Qt::UserRole + 2, value.toULongLong());
tfi->setData(1, Qt::UserRole + 3, (value.toULongLong() & menum.value(j)) > 0);
tfi->setSizeHint(1, QSize(20, 20));
bc.setHsv(chue, 60, 245 + ((i + j + 1) % 2) * 20 - 10);
setItemBackColor(tfi, bc);
ti->addChild(tfi);
}
ti->setData(0, Qt::UserRole, value);
ti->setData(1, Qt::UserRole, value);
ti->setData(1, Qt::UserRole + 2, "__flags");
ti->setData(0, Qt::UserRole + 1, QVariant::fromValue<QMetaProperty>(props.back()));
} else if (props.back().isEnumType())
value.setValue<PropertyValuePair>(PropertyValuePair(props.back(), value));
// ti->setText(1, value.toString());
ti->setData(1, Qt::UserRole, value);
tli->addChild(ti);
// const_cast<QModelIndex & >(indexFromItem(ti, 1)).;
// if (pw != 0) setItemWidget(ti, 1, pw);
}
chue += 60;
chue %= 360;
}
active_ = true;
}
void PropertyEditor::refresh() {}