23
#include "PreCompiled.h"
25
#include <QApplication>
31
#include <App/Application.h>
32
#include <App/Document.h>
33
#include <App/DocumentObject.h>
34
#include <App/ExpressionParser.h>
35
#include <App/VarSet.h>
36
#include <Base/Tools.h>
38
#include "DlgExpressionInput.h"
39
#include "ui_DlgExpressionInput.h"
40
#include "Application.h"
43
#include "ExpressionBinding.h"
44
#include "BitmapFactory.h"
45
#include "ViewProviderDocumentObject.h"
48
using namespace Gui::Dialog;
50
bool DlgExpressionInput::varSetsVisible = false;
52
DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path,
53
std::shared_ptr<const Expression> _expression,
54
const Base::Unit & _impliedUnit, QWidget *parent)
56
, ui(new Ui::DlgExpressionInput)
57
, expression(_expression ? _expression->copy() : nullptr)
60
, impliedUnit(_impliedUnit)
63
assert(path.getDocumentObject());
71
connect(ui->expression, &ExpressionLineEdit::textChanged,
72
this, &DlgExpressionInput::textChanged);
73
connect(ui->discardBtn, &QPushButton::clicked,
74
this, &DlgExpressionInput::setDiscarded);
77
ui->expression->setText(Base::Tools::fromStdString(expression->toString()));
80
QVariant text = parent->property("text");
81
if (text.canConvert<QString>()) {
82
ui->expression->setText(text.toString());
87
DocumentObject * docObj = path.getDocumentObject();
88
ui->expression->setDocumentObject(docObj);
93
bool noBackground = App::GetApplication().GetParameterGroupByPath
94
("User parameter:BaseApp/Preferences/Expression")->GetBool("NoSystemBackground", false);
97
#if defined(Q_OS_MACOS)
98
setWindowFlags(Qt::Widget | Qt::Popup | Qt::FramelessWindowHint);
100
setWindowFlags(Qt::SubWindow | Qt::Widget | Qt::Popup | Qt::FramelessWindowHint);
102
setAttribute(Qt::WA_NoSystemBackground, true);
103
setAttribute(Qt::WA_TranslucentBackground, true);
106
ui->expression->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
107
ui->horizontalSpacer_3->changeSize(0, 2);
108
ui->verticalLayout->setContentsMargins(9, 9, 9, 9);
113
if(this->width() < ui->expression->width() + 18)
114
this->resize(ui->expression->width()+18,this->height());
116
ui->expression->setFocus();
119
DlgExpressionInput::~DlgExpressionInput()
124
static void getVarSetsDocument(std::vector<App::VarSet*>& varSets, App::Document* doc) {
125
for (auto obj : doc->getObjects()) {
126
auto varSet = dynamic_cast<App::VarSet*>(obj);
128
varSets.push_back(varSet);
133
static std::vector<App::VarSet*> getAllVarSets()
135
std::vector<App::Document*> docs = App::GetApplication().getDocuments();
136
std::vector<App::VarSet*> varSets;
138
for (auto doc : docs) {
139
getVarSetsDocument(varSets, doc);
145
Base::Type DlgExpressionInput::getTypePath()
147
return path.getProperty()->getTypeId();
150
Base::Type DlgExpressionInput::determineTypeVarSet()
152
Base::Type typePath = getTypePath();
156
if (typePath == App::PropertyString::getClassTypeId() ||
157
typePath.isDerivedFrom(App::PropertyFloat::getClassTypeId()) ||
158
typePath.isDerivedFrom(App::PropertyInteger::getClassTypeId())) {
168
std::string unitTypeString = impliedUnit.getTypeString().toStdString();
169
if (unitTypeString.empty()) {
171
return Base::Type::badType();
174
std::string typeString = "App::Property" + unitTypeString;
176
return Base::Type::fromName(typeString.c_str());
179
bool DlgExpressionInput::typeOkForVarSet()
181
std::string unitType = impliedUnit.getTypeString().toStdString();
182
return determineTypeVarSet() != Base::Type::badType();
185
void DlgExpressionInput::initializeVarSets()
187
ui->labelInfoActive->setAlignment(Qt::AlignTop | Qt::AlignLeft);
188
ui->labelInfoActive->setWordWrap(true);
190
connect(ui->checkBoxVarSets, &QCheckBox::stateChanged,
191
this, &DlgExpressionInput::onCheckVarSets);
192
connect(ui->comboBoxVarSet, qOverload<int>(&QComboBox::currentIndexChanged),
193
this, &DlgExpressionInput::onVarSetSelected);
194
connect(ui->lineEditGroup, &QLineEdit::textChanged,
195
this, &DlgExpressionInput::onTextChangedGroup);
196
connect(ui->lineEditPropNew, &QLineEdit::textChanged,
197
this, &DlgExpressionInput::namePropChanged);
199
std::vector<App::VarSet*> varSets = getAllVarSets();
200
if (!varSets.empty() && typeOkForVarSet()) {
201
ui->checkBoxVarSets->setVisible(true);
202
ui->checkBoxVarSets->setCheckState(varSetsVisible ? Qt::Checked : Qt::Unchecked);
203
ui->groupBoxVarSets->setVisible(varSetsVisible);
204
if (varSetsVisible) {
210
varSetsVisible = false;
211
ui->checkBoxVarSets->setVisible(false);
212
ui->groupBoxVarSets->setVisible(false);
216
void NumberRange::setRange(double min, double max)
223
void NumberRange::clearRange()
228
void NumberRange::throwIfOutOfRange(const Base::Quantity& value) const
233
if (value.getValue() < minimum || value.getValue() > maximum) {
234
Base::Quantity minVal(minimum, value.getUnit());
235
Base::Quantity maxVal(maximum, value.getUnit());
236
QString valStr = value.getUserString();
237
QString minStr = minVal.getUserString();
238
QString maxStr = maxVal.getUserString();
239
QString error = QString::fromLatin1("Value out of range (%1 out of [%2, %3])").arg(valStr, minStr, maxStr);
241
throw Base::ValueError(error.toStdString());
245
void DlgExpressionInput::setRange(double minimum, double maximum)
247
numberRange.setRange(minimum, maximum);
250
void DlgExpressionInput::clearRange()
252
numberRange.clearRange();
255
QPoint DlgExpressionInput::expressionPosition() const
257
return ui->expression->pos();
260
void DlgExpressionInput::checkExpression(const QString& text)
263
std::shared_ptr<Expression> expr(ExpressionParser::parse(path.getDocumentObject(), text.toUtf8().constData()));
266
std::string error = path.getDocumentObject()->ExpressionEngine.validateExpression(path, expr);
269
throw Base::RuntimeError(error.c_str());
271
std::unique_ptr<Expression> result(expr->eval());
274
ui->okBtn->setEnabled(true);
278
ui->msg->setPalette(ui->okBtn->palette());
280
auto * n = Base::freecad_dynamic_cast<NumberExpression>(result.get());
282
Base::Quantity value = n->getQuantity();
283
QString msg = value.getUserString();
285
if (!value.isValid()) {
286
throw Base::ValueError("Not a number");
288
else if (!impliedUnit.isEmpty()) {
289
if (!value.getUnit().isEmpty() && value.getUnit() != impliedUnit)
290
throw Base::UnitsMismatchError("Unit mismatch between result and required unit");
292
value.setUnit(impliedUnit);
295
else if (!value.getUnit().isEmpty()) {
296
msg += QString::fromUtf8(" (Warning: unit discarded)");
298
QPalette p(ui->msg->palette());
299
p.setColor(QPalette::WindowText, Qt::red);
300
ui->msg->setPalette(p);
303
numberRange.throwIfOutOfRange(value);
305
ui->msg->setText(msg);
308
ui->msg->setText(Base::Tools::fromStdString(result->toString()));
314
static const bool NO_CHECK_EXPR = false;
316
void DlgExpressionInput::textChanged(const QString &text)
318
if (text.isEmpty()) {
319
ui->okBtn->setDisabled(true);
320
ui->discardBtn->setDefault(true);
324
ui->okBtn->setDefault(true);
328
QFontMetrics fm(ui->expression->font());
329
int width = QtTools::horizontalAdvance(fm, text) + 15;
330
if (width < minimumWidth)
331
ui->expression->setMinimumWidth(minimumWidth);
333
ui->expression->setMinimumWidth(width);
335
if(this->width() < ui->expression->minimumWidth())
336
setMinimumWidth(ui->expression->minimumWidth());
338
checkExpression(text);
339
if (varSetsVisible) {
343
updateVarSetInfo(NO_CHECK_EXPR);
346
catch (Base::Exception & e) {
347
ui->msg->setText(QString::fromUtf8(e.what()));
348
QPalette p(ui->msg->palette());
349
p.setColor(QPalette::WindowText, Qt::red);
350
ui->msg->setPalette(p);
351
ui->okBtn->setDisabled(true);
355
void DlgExpressionInput::setDiscarded()
361
void DlgExpressionInput::setExpressionInputSize(int width, int height)
363
if (ui->expression->minimumHeight() < height)
364
ui->expression->setMinimumHeight(height);
366
if (ui->expression->minimumWidth() < width)
367
ui->expression->setMinimumWidth(width);
369
minimumWidth = width;
372
void DlgExpressionInput::mouseReleaseEvent(QMouseEvent* ev)
377
void DlgExpressionInput::mousePressEvent(QMouseEvent* ev)
382
if (windowFlags() & Qt::FramelessWindowHint) {
385
bool on = ui->expression->completerActive();
391
void DlgExpressionInput::show()
394
this->activateWindow();
395
ui->expression->selectAll();
398
class Binding : public Gui::ExpressionBinding
405
void setExpression(std::shared_ptr<App::Expression> expr) override
407
ExpressionBinding::setExpression(expr);
411
static bool isNamePropOk(const QString& nameProp, App::DocumentObject* obj,
412
std::stringstream& message)
415
message << "Unknown object";
419
std::string name = nameProp.toStdString();
421
message << "Please provide a name for the property.";
425
if (name != Base::Tools::getIdentifier(name)) {
426
message << "Invalid property name (must only contain alphanumericals, underscore, "
427
<< "and must not start with digit";
431
auto prop = obj->getPropertyByName(name.c_str());
432
if (prop && prop->getContainer() == obj) {
433
message << name << " already exists";
440
static const int ROLE_DOC = Qt::UserRole;
441
static const int ROLE_VARSET_NAME = Qt::UserRole + 1;
442
static const int ROLE_VARSET_LABEL = Qt::UserRole + 2;
443
static const int ROLE_GROUP = Qt::UserRole + 3;
445
static QString getValue(QTreeWidgetItem* item, int role)
447
QVariant variant = item->data(0, role);
448
return variant.toString();
451
void DlgExpressionInput::acceptWithVarSet()
457
QTreeWidgetItem *selected = treeWidget->currentItem();
458
QString nameVarSet = getValue(selected, ROLE_VARSET_NAME);
459
QString nameGroup = ui->lineEditGroup->text();
460
QString nameProp = ui->lineEditPropNew->text();
462
QString nameDoc = getValue(selected, ROLE_DOC);
463
App::Document* doc = App::GetApplication().getDocument(nameDoc.toUtf8());
464
App::DocumentObject* obj = doc->getObject(nameVarSet.toUtf8());
466
std::string name = nameProp.toStdString();
467
std::string group = nameGroup.toStdString();
468
std::string type = getType();
469
auto prop = obj->addDynamicProperty(type.c_str(), name.c_str(), group.c_str());
475
Expression* exprSimplfied = expression->simplify();
476
auto ne = dynamic_cast<NumberExpression*>(exprSimplfied);
477
auto se = dynamic_cast<StringExpression*>(exprSimplfied);
481
Gui::Command::doCommand(Gui::Command::Doc, "App.getDocument('%s').getObject('%s').%s = %f",
482
obj->getDocument()->getName(),
483
obj->getNameInDocument(),
484
prop->getName(), ne->getValue());
488
Gui::Command::doCommand(Gui::Command::Doc, "App.getDocument('%s').getObject('%s').%s = \"%s\"",
489
obj->getDocument()->getName(),
490
obj->getNameInDocument(),
491
prop->getName(), se->getText().c_str());
495
ObjectIdentifier objId(*prop);
498
binding.setExpression(expression);
504
expression.reset(ExpressionParser::parse(path.getDocumentObject(),
505
prop->getFullName().c_str()));
508
void DlgExpressionInput::accept() {
509
if (varSetsVisible) {
515
static void addGroupsVarSetComboBox(App::VarSet* varSet, QTreeWidgetItem* varSetItem)
517
std::vector<Property*> properties;
518
std::set<std::string> namesGroup;
519
varSet->getPropertyList(properties);
520
for (auto prop : properties) {
521
const char* nameGroup = prop->getGroup();
522
if (!nameGroup || strcmp(nameGroup, "") == 0) {
523
namesGroup.insert("Base");
526
namesGroup.insert(nameGroup);
529
for (const auto& nameGroup : namesGroup) {
531
auto item = new QTreeWidgetItem(varSetItem);
532
item->setText(0, QString::fromStdString(nameGroup));
533
item->setData(0, ROLE_GROUP, QString::fromStdString(nameGroup));
534
item->setData(0, ROLE_VARSET_NAME, varSetItem->data(0, ROLE_VARSET_NAME));
535
item->setData(0, ROLE_VARSET_LABEL, varSetItem->data(0, ROLE_VARSET_LABEL));
536
item->setData(0, ROLE_DOC, varSetItem->data(0, ROLE_DOC));
540
static void addVarSetsVarSetComboBox(std::vector<App::VarSet*>& varSets, QTreeWidgetItem* docItem)
542
for (auto varSet : varSets) {
543
auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
544
Gui::Application::Instance->getViewProvider(varSet));
546
auto item = new QTreeWidgetItem(docItem);
547
item->setIcon(0, vp->getIcon());
548
item->setText(0, QString::fromUtf8(varSet->Label.getValue()));
549
item->setData(0, ROLE_VARSET_LABEL, QString::fromUtf8(varSet->Label.getValue()));
550
item->setData(0, ROLE_VARSET_NAME, QString::fromUtf8(varSet->getNameInDocument()));
551
item->setData(0, ROLE_DOC, docItem->data(0, ROLE_DOC));
552
addGroupsVarSetComboBox(varSet, item);
556
static void addDocVarSetComboBox(App::Document* doc, QPixmap& docIcon, QTreeWidgetItem* rootItem)
558
if (!doc->testStatus(App::Document::TempDoc)) {
559
std::vector<App::VarSet*> varSets;
560
getVarSetsDocument(varSets, doc);
561
if (!varSets.empty()) {
563
auto item = new QTreeWidgetItem(rootItem);
564
item->setIcon(0, docIcon);
565
item->setText(0, QString::fromUtf8(doc->Label.getValue()));
566
item->setData(0, ROLE_DOC, QByteArray(doc->getName()));
567
item->setFlags(Qt::ItemIsEnabled);
568
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
569
addVarSetsVarSetComboBox(varSets, item);
574
static QTreeWidget* createVarSetTreeWidget()
577
auto treeWidget = new QTreeWidget();
578
treeWidget->setColumnCount(1);
579
treeWidget->setHeaderHidden(true);
581
QTreeWidgetItem *rootItem = treeWidget->invisibleRootItem();
583
QPixmap docIcon(Gui::BitmapFactory().pixmap("Document"));
584
std::vector<App::Document*> docs = App::GetApplication().getDocuments();
586
for (auto doc : docs) {
587
addDocVarSetComboBox(doc, docIcon, rootItem);
589
treeWidget->expandAll();
594
void DlgExpressionInput::setupVarSets()
596
ui->comboBoxVarSet->clear();
599
treeWidget.reset(createVarSetTreeWidget());
600
ui->comboBoxVarSet->setModel(treeWidget->model());
601
ui->comboBoxVarSet->setView(treeWidget.get());
603
ui->okBtn->setEnabled(false);
606
std::string DlgExpressionInput::getType()
608
return determineTypeVarSet().getName();
611
void DlgExpressionInput::onCheckVarSets(int state) {
612
varSetsVisible = state == Qt::Checked;
613
ui->groupBoxVarSets->setVisible(varSetsVisible);
614
if (varSetsVisible) {
618
ui->okBtn->setEnabled(true);
622
void DlgExpressionInput::onVarSetSelected(int)
624
QTreeWidgetItem* selected = treeWidget->currentItem();
628
QVariant variantGroup = selected->data(0, ROLE_GROUP);
629
if (variantGroup.isValid()) {
630
ui->lineEditGroup->setText(variantGroup.toString());
633
ui->lineEditGroup->clear();
640
void DlgExpressionInput::onTextChangedGroup(const QString&)
645
void DlgExpressionInput::namePropChanged(const QString&)
650
static bool isNameGroupOk(const QString& nameGroup,
651
std::stringstream& message)
653
std::string name = nameGroup.toStdString();
654
if (name.empty() || name != Base::Tools::getIdentifier(name)) {
655
message << "Invalid group name (must only contain alphanumericals, underscore, "
656
<< "and must not start with digit";
663
void DlgExpressionInput::reportVarSetInfo(const char* message)
665
ui->labelInfoActive->setText(QString::fromUtf8(message));
668
bool DlgExpressionInput::reportGroup(QString& nameGroup)
670
if (nameGroup.isEmpty()) {
671
reportVarSetInfo("Please provide a group.");
675
std::stringstream message;
676
if (!isNameGroupOk(nameGroup, message)) {
677
reportVarSetInfo(message.str().c_str());
684
bool DlgExpressionInput::reportName(QTreeWidgetItem* item)
686
QString nameProp = ui->lineEditPropNew->text();
687
QString nameVarSet = getValue(item, ROLE_VARSET_NAME);
688
QString nameDoc = getValue(item, ROLE_DOC);
689
App::Document* doc = App::GetApplication().getDocument(nameDoc.toUtf8());
690
App::DocumentObject* obj = doc->getObject(nameVarSet.toUtf8());
691
std::stringstream message;
692
if (!isNamePropOk(nameProp, obj, message)) {
693
reportVarSetInfo(message.str().c_str());
700
void DlgExpressionInput::updateVarSetInfo(bool checkExpr)
702
QTreeWidgetItem* selected = treeWidget->currentItem();
705
QString nameGroup = ui->lineEditGroup->text();
706
if (reportGroup(nameGroup)) {
708
ui->okBtn->setEnabled(false);
712
if (reportName(selected)) {
714
ui->okBtn->setEnabled(false);
718
QString nameProp = ui->lineEditPropNew->text();
719
QString labelVarSet = getValue(selected, ROLE_VARSET_LABEL);
720
QString nameDoc = getValue(selected, ROLE_DOC);
721
std::stringstream message;
722
message << "Adding property " << nameProp.toStdString() << std::endl
723
<< "of type " << getType() << std::endl
724
<< "to variable set " << labelVarSet.toStdString() << std::endl
725
<< "in group " << nameGroup.toStdString() << std::endl
726
<< "in document " << nameDoc.toStdString() << ".";
728
reportVarSetInfo(message.str().c_str());
732
checkExpression(ui->expression->text());
733
ui->okBtn->setEnabled(true);
735
catch (Base::Exception&) {
736
ui->okBtn->setDisabled(true);
741
ui->okBtn->setEnabled(false);
742
reportVarSetInfo("Please select a variable set.");
746
#include "moc_DlgExpressionInput.cpp"