Gui: property view related changes
* Display property from linked object, colored green, * Change DlgPropertyLink to support external linking and sub-object selection * Improve large selection performance by using a timer * Improve TAB key behavior in property editor * Add context menu to show hidden properties, change property status, set expression on any and property, and add/remove dynamic properties * Optimize expression completer model construction, as the original implementation gets prohibitively slow for moderate number of objects.
This commit is contained in:
@@ -367,6 +367,7 @@ set(Gui_MOC_HDRS
|
||||
DAGView/DAGModel.h
|
||||
TaskElementColors.h
|
||||
DlgObjectSelection.h
|
||||
DlgAddProperty.h
|
||||
${FreeCADGui_SDK_MOC_HDRS}
|
||||
)
|
||||
|
||||
@@ -441,6 +442,7 @@ SET(Gui_UIC_SRCS
|
||||
TaskView/TaskSelectLinkProperty.ui
|
||||
TaskElementColors.ui
|
||||
DlgObjectSelection.ui
|
||||
DlgAddProperty.ui
|
||||
)
|
||||
|
||||
SET(Gui_RES_SRCS
|
||||
@@ -516,6 +518,7 @@ SET(Dialog_CPP_SRCS
|
||||
DocumentRecovery.cpp
|
||||
TaskElementColors.cpp
|
||||
DlgObjectSelection.cpp
|
||||
DlgAddProperty.cpp
|
||||
)
|
||||
|
||||
SET(Dialog_HPP_SRCS
|
||||
@@ -551,6 +554,7 @@ SET(Dialog_HPP_SRCS
|
||||
DocumentRecovery.h
|
||||
TaskElementColors.h
|
||||
DlgObjectSelection.h
|
||||
DlgAddProperty.h
|
||||
)
|
||||
|
||||
SET(Dialog_SRCS
|
||||
@@ -564,6 +568,7 @@ SET(Dialog_SRCS
|
||||
DlgAuthorization.ui
|
||||
DlgDisplayProperties.ui
|
||||
DlgInputDialog.ui
|
||||
DlgAddProperty.ui
|
||||
DlgLocationAngle.ui
|
||||
DlgLocationPos.ui
|
||||
DlgMacroExecute.ui
|
||||
|
||||
151
src/Gui/DlgAddProperty.cpp
Normal file
151
src/Gui/DlgAddProperty.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2019 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <QMessageBox>
|
||||
#endif
|
||||
|
||||
#include "ui_DlgAddProperty.h"
|
||||
|
||||
#include <Base/Tools.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include "MainWindow.h"
|
||||
#include <ViewProviderDocumentObject.h>
|
||||
#include "DlgAddProperty.h"
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
DlgAddProperty::DlgAddProperty(QWidget* parent,
|
||||
std::unordered_set<App::PropertyContainer *> &&c)
|
||||
: QDialog( parent )
|
||||
, containers(std::move(c))
|
||||
, ui(new Ui_DlgAddProperty)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
auto hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/PropertyView");
|
||||
auto defType = Base::Type::fromName(
|
||||
hGrp->GetASCII("NewPropertyType","App::PropertyString").c_str());
|
||||
if(defType.isBad())
|
||||
defType = App::PropertyString::getClassTypeId();
|
||||
|
||||
std::vector<Base::Type> types;
|
||||
Base::Type::getAllDerivedFrom(Base::Type::fromName("App::Property"),types);
|
||||
for(auto type : types) {
|
||||
ui->comboType->addItem(QString::fromLatin1(type.getName()));
|
||||
if(type == defType)
|
||||
ui->comboType->setCurrentIndex(ui->comboType->count()-1);
|
||||
}
|
||||
|
||||
ui->edtGroup->setText(QString::fromLatin1(
|
||||
hGrp->GetASCII("NewPropertyGroup","Base").c_str()));
|
||||
ui->chkAppend->setChecked(hGrp->GetBool("NewPropertyAppend",true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the object and frees any allocated resources
|
||||
*/
|
||||
DlgAddProperty::~DlgAddProperty()
|
||||
{
|
||||
// no need to delete child widgets, Qt does it all for us
|
||||
}
|
||||
|
||||
static std::string containerName(const App::PropertyContainer *c) {
|
||||
auto doc = Base::freecad_dynamic_cast<App::Document>(c);
|
||||
if(doc)
|
||||
return doc->getName();
|
||||
auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(c);
|
||||
if(obj)
|
||||
return obj->getFullName();
|
||||
auto vpd = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(c);
|
||||
if(vpd)
|
||||
return vpd->getObject()->getFullName();
|
||||
return "?";
|
||||
}
|
||||
|
||||
void DlgAddProperty::accept()
|
||||
{
|
||||
std::string name = ui->edtName->text().toUtf8().constData();
|
||||
std::string group = ui->edtGroup->text().toUtf8().constData();
|
||||
if(name.empty() || group.empty()
|
||||
|| name != Base::Tools::getIdentifier(name)
|
||||
|| group != Base::Tools::getIdentifier(group))
|
||||
{
|
||||
QMessageBox::critical(getMainWindow(),
|
||||
QObject::tr("Invalid name"),
|
||||
QObject::tr("The property name or group name must only contain alpha numericals,\n"
|
||||
"underscore, and must not start with a digit."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ui->chkAppend->isChecked())
|
||||
name = group + "_" + name;
|
||||
|
||||
for(auto c : containers) {
|
||||
if(c->getPropertyByName(name.c_str())) {
|
||||
QMessageBox::critical(getMainWindow(),
|
||||
QObject::tr("Invalid name"),
|
||||
QObject::tr("The property '%1' already exists in '%2'").arg(
|
||||
QString::fromLatin1(name.c_str()),
|
||||
QString::fromLatin1(containerName(c).c_str())));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string type = ui->comboType->currentText().toLatin1().constData();
|
||||
|
||||
for(auto it=containers.begin();it!=containers.end();++it) {
|
||||
try {
|
||||
(*it)->addDynamicProperty(type.c_str(),name.c_str(),
|
||||
group.c_str(),ui->edtDoc->toPlainText().toUtf8().constData());
|
||||
} catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
for(auto it2=containers.begin();it2!=it;++it2) {
|
||||
try {
|
||||
(*it)->removeDynamicProperty(name.c_str());
|
||||
} catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
QMessageBox::critical(getMainWindow(),
|
||||
QObject::tr("Add property"),
|
||||
QObject::tr("Failed to add property to '%1': %2").arg(
|
||||
QString::fromLatin1(containerName(*it).c_str()),
|
||||
QString::fromUtf8(e.what())));
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/PropertyView");
|
||||
hGrp->SetASCII("NewPropertyType",type.c_str());
|
||||
hGrp->SetASCII("NewPropertyGroup",group.c_str());
|
||||
hGrp->SetBool("NewPropertyAppend",ui->chkAppend->isChecked());
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
#include "moc_DlgAddProperty.cpp"
|
||||
54
src/Gui/DlgAddProperty.h
Normal file
54
src/Gui/DlgAddProperty.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2019 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GUI_DIALOG_DLGADDPROPERTY_H
|
||||
#define GUI_DIALOG_DLGADDPROPERTY_H
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace App {
|
||||
class PropertyContainer;
|
||||
}
|
||||
|
||||
namespace Gui {
|
||||
namespace Dialog {
|
||||
|
||||
class Ui_DlgAddProperty;
|
||||
class GuiExport DlgAddProperty : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DlgAddProperty(QWidget *parent, std::unordered_set<App::PropertyContainer*> &&);
|
||||
~DlgAddProperty();
|
||||
|
||||
virtual void accept() override;
|
||||
|
||||
private:
|
||||
std::unordered_set<App::PropertyContainer*> containers;
|
||||
std::unique_ptr<Ui_DlgAddProperty> ui;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_DIALOG_DLGADDPROPERTY_H
|
||||
119
src/Gui/DlgAddProperty.ui
Normal file
119
src/Gui/DlgAddProperty.ui
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Gui::Dialog::DlgAddProperty</class>
|
||||
<widget class="QDialog" name="Gui::Dialog::DlgAddProperty">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>354</width>
|
||||
<height>258</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add property</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="comboType"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Group</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="edtGroup"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="edtName"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Document</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPlainTextEdit" name="edtDoc"/>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="chkAppend">
|
||||
<property name="toolTip">
|
||||
<string>Append the group name in front of the property name in the form of 'group'_'name' to avoid conflict with existing property. The prefixed group name will be auto trimmed when shown in the property editor.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Append group name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>comboType</tabstop>
|
||||
<tabstop>edtGroup</tabstop>
|
||||
<tabstop>edtName</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Gui::Dialog::DlgAddProperty</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>199</x>
|
||||
<y>99</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>58</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Gui::Dialog::DlgAddProperty</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>199</x>
|
||||
<y>99</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>199</x>
|
||||
<y>58</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -90,6 +90,11 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path,
|
||||
ui->horizontalSpacer_3->changeSize(0, 2);
|
||||
ui->verticalLayout->setContentsMargins(9, 9, 9, 9);
|
||||
this->adjustSize();
|
||||
// It is strange that (at least on Linux) DlgExpressionInput will shrink
|
||||
// to be narrower than ui->expression after calling adjustSize() above.
|
||||
// Why?
|
||||
if(this->width() < ui->expression->width() + 18)
|
||||
this->resize(ui->expression->width()+18,this->height());
|
||||
}
|
||||
ui->expression->setFocus();
|
||||
}
|
||||
@@ -138,10 +143,12 @@ void DlgExpressionInput::textChanged(const QString &text)
|
||||
if (n) {
|
||||
Base::Quantity value = n->getQuantity();
|
||||
|
||||
if (!value.getUnit().isEmpty() && value.getUnit() != impliedUnit)
|
||||
throw Base::UnitsMismatchError("Unit mismatch between result and required unit");
|
||||
if(!impliedUnit.isEmpty()) {
|
||||
if (!value.getUnit().isEmpty() && value.getUnit() != impliedUnit)
|
||||
throw Base::UnitsMismatchError("Unit mismatch between result and required unit");
|
||||
|
||||
value.setUnit(impliedUnit);
|
||||
value.setUnit(impliedUnit);
|
||||
}
|
||||
|
||||
ui->msg->setText(value.getUserString());
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <algorithm>
|
||||
# include <sstream>
|
||||
# include <QListWidgetItem>
|
||||
# include <QTreeWidgetItem>
|
||||
# include <QMessageBox>
|
||||
#endif
|
||||
|
||||
@@ -34,24 +34,85 @@
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/GeoFeature.h>
|
||||
|
||||
#include "BitmapFactory.h"
|
||||
#include "DlgPropertyLink.h"
|
||||
#include "Application.h"
|
||||
#include "ViewProvider.h"
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "ui_DlgPropertyLink.h"
|
||||
|
||||
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
/* TRANSLATOR Gui::Dialog::DlgPropertyLink */
|
||||
|
||||
DlgPropertyLink::DlgPropertyLink(const QStringList& list, QWidget* parent, Qt::WindowFlags fl)
|
||||
DlgPropertyLink::DlgPropertyLink(const QStringList& list, QWidget* parent, Qt::WindowFlags fl, bool xlink)
|
||||
: QDialog(parent, fl), link(list), ui(new Ui_DlgPropertyLink)
|
||||
{
|
||||
#ifdef FC_DEBUG
|
||||
assert(list.size() >= 5);
|
||||
assert(list.size() >= 4);
|
||||
#endif
|
||||
|
||||
// populate inList to filter out any objects that contains the owner object
|
||||
// of the editing link property
|
||||
auto doc = App::GetApplication().getDocument(qPrintable(link[0]));
|
||||
if(doc) {
|
||||
auto obj = doc->getObject(qPrintable(link[3]));
|
||||
if(obj && obj->getNameInDocument()) {
|
||||
inList = obj->getInListEx(true);
|
||||
inList.insert(obj);
|
||||
}
|
||||
}
|
||||
|
||||
ui->setupUi(this);
|
||||
findObjects(ui->checkObjectType->isChecked(), QString());
|
||||
ui->typeTree->hide();
|
||||
|
||||
if(!xlink)
|
||||
ui->comboBox->hide();
|
||||
else {
|
||||
std::string linkDoc = qPrintable(link[0]);
|
||||
for(auto doc : App::GetApplication().getDocuments()) {
|
||||
QString name(QString::fromUtf8(doc->Label.getValue()));
|
||||
ui->comboBox->addItem(name,QVariant(QString::fromLatin1(doc->getName())));
|
||||
if(linkDoc == doc->getName())
|
||||
ui->comboBox->setCurrentIndex(ui->comboBox->count()-1);
|
||||
}
|
||||
}
|
||||
|
||||
Base::Type baseType;
|
||||
|
||||
App::Document *linkedDoc = doc;
|
||||
if (link.size()>FC_XLINK_VALUE_INDEX)
|
||||
linkedDoc = App::GetApplication().getDocument(qPrintable(link[FC_XLINK_VALUE_INDEX]));
|
||||
if(linkedDoc) {
|
||||
QString objName = link[1]; // linked object name
|
||||
auto obj = linkedDoc->getObject((const char*)objName.toLatin1());
|
||||
if (obj && inList.find(obj)==inList.end()) {
|
||||
Base::Type objType = obj->getTypeId();
|
||||
// get only geometric types
|
||||
if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
|
||||
baseType = App::GeoFeature::getClassTypeId();
|
||||
else
|
||||
baseType = App::DocumentObject::getClassTypeId();
|
||||
|
||||
// get the direct base class of App::DocumentObject which 'obj' is derived from
|
||||
while (!objType.isBad()) {
|
||||
std::string name = objType.getName();
|
||||
Base::Type parType = objType.getParent();
|
||||
if (parType == baseType) {
|
||||
baseType = objType;
|
||||
break;
|
||||
}
|
||||
objType = parType;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!baseType.isBad()) {
|
||||
types.insert(baseType.getName());
|
||||
ui->checkObjectType->setChecked(true);
|
||||
}else
|
||||
findObjects();
|
||||
|
||||
connect(ui->treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
||||
this, SLOT(onItemExpanded(QTreeWidgetItem*)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,15 +126,14 @@ DlgPropertyLink::~DlgPropertyLink()
|
||||
|
||||
void DlgPropertyLink::setSelectionMode(QAbstractItemView::SelectionMode mode)
|
||||
{
|
||||
ui->listWidget->setSelectionMode(mode);
|
||||
ui->listWidget->clear();
|
||||
findObjects(ui->checkObjectType->isChecked(), ui->searchBox->text());
|
||||
ui->treeWidget->setSelectionMode(mode);
|
||||
findObjects();
|
||||
}
|
||||
|
||||
void DlgPropertyLink::accept()
|
||||
{
|
||||
if (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection) {
|
||||
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
|
||||
if (ui->treeWidget->selectionMode() == QAbstractItemView::SingleSelection) {
|
||||
QList<QTreeWidgetItem*> items = ui->treeWidget->selectedItems();
|
||||
if (items.isEmpty()) {
|
||||
QMessageBox::warning(this, tr("No selection"), tr("Please select an object from the list"));
|
||||
return;
|
||||
@@ -83,149 +143,248 @@ void DlgPropertyLink::accept()
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
static QStringList getLinkFromItem(const QStringList &link, QTreeWidgetItem *selItem) {
|
||||
QStringList list = link;
|
||||
if(link.size()>FC_XLINK_VALUE_INDEX) {
|
||||
QString subname;
|
||||
auto parent = selItem;
|
||||
for(auto item=parent;;item=parent) {
|
||||
parent = item->parent();
|
||||
if(!parent) {
|
||||
list[1] = item->data(0,Qt::UserRole).toString();
|
||||
break;
|
||||
}
|
||||
subname = QString::fromLatin1("%1.%2").
|
||||
arg(item->data(0,Qt::UserRole).toString()).arg(subname);
|
||||
}
|
||||
list[FC_XLINK_VALUE_INDEX] = subname;
|
||||
if(subname.size())
|
||||
list[2] = QString::fromLatin1("%1 (%2.%3)").
|
||||
arg(selItem->text(0)).arg(list[1]).arg(subname);
|
||||
else
|
||||
list[2] = selItem->text(0);
|
||||
QString docName(selItem->data(0, Qt::UserRole+1).toString());
|
||||
if(list.size()>FC_XLINK_VALUE_INDEX+1)
|
||||
list[FC_XLINK_VALUE_INDEX+1] = docName;
|
||||
else
|
||||
list << docName;
|
||||
}else{
|
||||
list[1] = selItem->data(0,Qt::UserRole).toString();
|
||||
list[2] = selItem->text(0);
|
||||
if (list[1].isEmpty())
|
||||
list[2] = QString::fromUtf8("");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
QStringList DlgPropertyLink::propertyLink() const
|
||||
{
|
||||
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
|
||||
auto items = ui->treeWidget->selectedItems();
|
||||
if (items.isEmpty()) {
|
||||
return link;
|
||||
}
|
||||
else {
|
||||
QStringList list = link;
|
||||
list[1] = items[0]->data(Qt::UserRole).toString();
|
||||
list[2] = items[0]->text();
|
||||
if (list[1].isEmpty())
|
||||
list[2] = QString::fromUtf8("");
|
||||
return list;
|
||||
}
|
||||
return getLinkFromItem(link,items[0]);
|
||||
}
|
||||
|
||||
QVariantList DlgPropertyLink::propertyLinkList() const
|
||||
{
|
||||
QVariantList varList;
|
||||
QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
|
||||
if (items.isEmpty()) {
|
||||
varList << link;
|
||||
}
|
||||
else {
|
||||
for (QList<QListWidgetItem*>::iterator it = items.begin(); it != items.end(); ++it) {
|
||||
QStringList list = link;
|
||||
list[1] = (*it)->data(Qt::UserRole).toString();
|
||||
list[2] = (*it)->text();
|
||||
if (list[1].isEmpty())
|
||||
list[2] = QString::fromUtf8("");
|
||||
varList << list;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> items = ui->treeWidget->selectedItems();
|
||||
for (QList<QTreeWidgetItem*>::iterator it = items.begin(); it != items.end(); ++it)
|
||||
varList << getLinkFromItem(link,*it);
|
||||
return varList;
|
||||
}
|
||||
|
||||
void DlgPropertyLink::findObjects(bool on, const QString& searchText)
|
||||
void DlgPropertyLink::findObjects()
|
||||
{
|
||||
QString docName = link[0]; // document name
|
||||
QString objName = link[1]; // internal object name
|
||||
QString parName = link[3]; // internal object name of the parent of the link property
|
||||
QString proName = link[4]; // property name
|
||||
bool filterType = ui->checkObjectType->isChecked();
|
||||
ui->treeWidget->clear();
|
||||
|
||||
bool isSingleSelection = (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection);
|
||||
QString docName = link[0]; // document name of the owner object of this editing property
|
||||
|
||||
bool isSingleSelection = (ui->treeWidget->selectionMode() == QAbstractItemView::SingleSelection);
|
||||
App::Document* doc = App::GetApplication().getDocument((const char*)docName.toLatin1());
|
||||
if (doc) {
|
||||
Base::Type baseType = App::DocumentObject::getClassTypeId();
|
||||
if (!on) {
|
||||
App::DocumentObject* obj = doc->getObject((const char*)objName.toLatin1());
|
||||
if (obj) {
|
||||
Base::Type objType = obj->getTypeId();
|
||||
// get only geometric types
|
||||
if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
|
||||
baseType = App::GeoFeature::getClassTypeId();
|
||||
|
||||
// get the direct base class of App::DocumentObject which 'obj' is derived from
|
||||
while (!objType.isBad()) {
|
||||
Base::Type parType = objType.getParent();
|
||||
if (parType == baseType) {
|
||||
baseType = objType;
|
||||
break;
|
||||
}
|
||||
objType = parType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build list of objects names already in property so we can mark them as selected later on
|
||||
std::vector<const char *> selectedNames;
|
||||
std::set<std::string> selectedNames;
|
||||
|
||||
// build ignore list
|
||||
std::vector<App::DocumentObject*> ignoreList;
|
||||
App::DocumentObject* par = doc->getObject((const char*)parName.toLatin1());
|
||||
App::Property* prop = par->getPropertyByName((const char*)proName.toLatin1());
|
||||
if (prop) {
|
||||
// for multi-selection we need all objects
|
||||
if (isSingleSelection) {
|
||||
ignoreList = par->getOutListOfProperty(prop);
|
||||
} else {
|
||||
// Add a "None" entry on top
|
||||
if (isSingleSelection) {
|
||||
auto* item = new QTreeWidgetItem(ui->treeWidget);
|
||||
item->setText(0,tr("None (Remove link)"));
|
||||
QByteArray ba("");
|
||||
item->setData(0,Qt::UserRole, ba);
|
||||
}else {
|
||||
QString ownerName = link[3];
|
||||
QString proName = link[4];
|
||||
auto owner = doc->getObject(ownerName.toLatin1());
|
||||
if(owner) {
|
||||
App::Property* prop = owner->getPropertyByName((const char*)proName.toLatin1());
|
||||
// gather names of objects currently in property
|
||||
if (prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId())) {
|
||||
if (prop && prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId())) {
|
||||
const App::PropertyLinkList* propll = static_cast<const App::PropertyLinkList*>(prop);
|
||||
std::vector<App::DocumentObject*> links = propll->getValues();
|
||||
for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
|
||||
selectedNames.push_back((*it)->getNameInDocument());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the inlist to the ignore list to avoid dependency loops
|
||||
std::vector<App::DocumentObject*> inList = par->getInListRecursive();
|
||||
ignoreList.insert(ignoreList.end(), inList.begin(), inList.end());
|
||||
ignoreList.push_back(par);
|
||||
}
|
||||
|
||||
// Add a "None" entry on top
|
||||
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
|
||||
item->setText(tr("None (Remove link)"));
|
||||
QByteArray ba("");
|
||||
item->setData(Qt::UserRole, ba);
|
||||
|
||||
std::vector<App::DocumentObject*> obj = doc->getObjectsOfType(baseType);
|
||||
for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
|
||||
Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(*it);
|
||||
bool nameOk = true;
|
||||
if (!searchText.isEmpty()) {
|
||||
QString label = QString::fromUtf8((*it)->Label.getValue());
|
||||
if (!label.contains(searchText,Qt::CaseInsensitive))
|
||||
nameOk = false;
|
||||
}
|
||||
if (vp && nameOk) {
|
||||
// filter out the objects
|
||||
if (std::find(ignoreList.begin(), ignoreList.end(), *it) == ignoreList.end()) {
|
||||
QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
|
||||
item->setIcon(vp->getIcon());
|
||||
item->setText(QString::fromUtf8((*it)->Label.getValue()));
|
||||
QByteArray ba((*it)->getNameInDocument());
|
||||
item->setData(Qt::UserRole, ba);
|
||||
// mark items as selected if needed
|
||||
for (std::vector<const char *>::iterator nit = selectedNames.begin(); nit != selectedNames.end(); ++nit) {
|
||||
if (strcmp(*nit,(*it)->getNameInDocument()) == 0) {
|
||||
item->setSelected(true);
|
||||
break;
|
||||
}
|
||||
selectedNames.insert((*it)->getNameInDocument());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Base::PyGILStateLocker lock;
|
||||
std::map<std::string,QIcon> typeInfos;
|
||||
|
||||
QTreeWidgetItem *selected = 0;
|
||||
for(auto obj : doc->getObjects()) {
|
||||
auto it = types.end();
|
||||
auto prop = Base::freecad_dynamic_cast<App::PropertyPythonObject>(
|
||||
obj->getPropertyByName("Proxy"));
|
||||
bool pass = false;
|
||||
if(prop && !prop->getValue().isNone()) {
|
||||
std::string typeName = prop->getValue().type().as_string();
|
||||
if(refreshTypes) {
|
||||
QIcon &icon = typeInfos[typeName];
|
||||
if(icon.isNull()) {
|
||||
auto vp = Application::Instance->getViewProvider(obj);
|
||||
if(vp)
|
||||
icon = vp->getIcon();
|
||||
}
|
||||
}
|
||||
if(filterType)
|
||||
pass = types.count(typeName);
|
||||
}
|
||||
if(refreshTypes) {
|
||||
auto type = obj->getTypeId();
|
||||
QIcon &icon = typeInfos[type.getName()];
|
||||
if(!prop && icon.isNull()) {
|
||||
auto vp = Application::Instance->getViewProvider(obj);
|
||||
if(vp)
|
||||
icon = vp->getIcon();
|
||||
}
|
||||
for(type = type.getParent();!type.isBad();type=type.getParent())
|
||||
typeInfos.emplace(type.getName(),QIcon());
|
||||
}
|
||||
if(filterType && !pass && types.size()) {
|
||||
for(auto type = obj->getTypeId();!type.isBad();type=type.getParent()) {
|
||||
it = types.find(type.getName());
|
||||
if(it!=types.end())
|
||||
break;
|
||||
}
|
||||
if(it == types.end())
|
||||
continue;
|
||||
}
|
||||
|
||||
auto item = createItem(obj,0);
|
||||
if(item && selectedNames.count(obj->getNameInDocument())) {
|
||||
if(!selected)
|
||||
selected = item;
|
||||
item->setSelected(true);
|
||||
}
|
||||
}
|
||||
if(selected)
|
||||
ui->treeWidget->scrollToItem(selected);
|
||||
|
||||
if(refreshTypes) {
|
||||
refreshTypes = false;
|
||||
ui->typeTree->blockSignals(true);
|
||||
ui->typeTree->clear();
|
||||
QTreeWidgetItem *selected = 0;
|
||||
QIcon icon = BitmapFactory().pixmap("px");
|
||||
for(auto &v : typeInfos) {
|
||||
auto item = new QTreeWidgetItem(ui->typeTree);
|
||||
item->setText(0,QString::fromLatin1(v.first.c_str()));
|
||||
item->setIcon(0,v.second.isNull()?icon:v.second);
|
||||
if(types.count(v.first)) {
|
||||
item->setSelected(true);
|
||||
if(!selected)
|
||||
selected = item;
|
||||
}
|
||||
}
|
||||
if(selected)
|
||||
ui->typeTree->scrollToItem(selected);
|
||||
ui->typeTree->blockSignals(false);
|
||||
if(types.size() && !selected) {
|
||||
types.clear();
|
||||
findObjects();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QTreeWidgetItem *DlgPropertyLink::createItem(App::DocumentObject *obj, QTreeWidgetItem *parent) {
|
||||
if(!obj || !obj->getNameInDocument())
|
||||
return 0;
|
||||
|
||||
if(inList.find(obj)!=inList.end())
|
||||
return 0;
|
||||
|
||||
auto vp = Gui::Application::Instance->getViewProvider(obj);
|
||||
if(!vp)
|
||||
return 0;
|
||||
QString searchText = ui->searchBox->text();
|
||||
if (!searchText.isEmpty()) {
|
||||
QString label = QString::fromUtf8((obj)->Label.getValue());
|
||||
if (!label.contains(searchText,Qt::CaseInsensitive))
|
||||
return 0;
|
||||
}
|
||||
QTreeWidgetItem* item;
|
||||
if(parent)
|
||||
item = new QTreeWidgetItem(parent);
|
||||
else
|
||||
item = new QTreeWidgetItem(ui->treeWidget);
|
||||
item->setIcon(0, vp->getIcon());
|
||||
item->setText(0, QString::fromUtf8((obj)->Label.getValue()));
|
||||
item->setData(0, Qt::UserRole, QByteArray(obj->getNameInDocument()));
|
||||
item->setData(0, Qt::UserRole+1, QByteArray(obj->getDocument()->getName()));
|
||||
if(link.size()>=5) {
|
||||
item->setChildIndicatorPolicy(obj->hasChildElement()||vp->getChildRoot()?
|
||||
QTreeWidgetItem::ShowIndicator:QTreeWidgetItem::DontShowIndicator);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
void DlgPropertyLink::onItemExpanded(QTreeWidgetItem * item) {
|
||||
if(link.size()<5 || item->childCount())
|
||||
return;
|
||||
|
||||
std::string name(qPrintable(item->data(0, Qt::UserRole).toString()));
|
||||
std::string docName(qPrintable(item->data(0, Qt::UserRole+1).toString()));
|
||||
auto doc = App::GetApplication().getDocument(docName.c_str());
|
||||
if(doc) {
|
||||
auto obj = doc->getObject(name.c_str());
|
||||
if(!obj) return;
|
||||
auto vp = Application::Instance->getViewProvider(obj);
|
||||
if(!vp) return;
|
||||
for(auto obj : vp->claimChildren())
|
||||
createItem(obj,item);
|
||||
}
|
||||
}
|
||||
|
||||
void DlgPropertyLink::on_checkObjectType_toggled(bool on)
|
||||
{
|
||||
ui->listWidget->clear();
|
||||
findObjects(on, ui->searchBox->text());
|
||||
ui->typeTree->setVisible(on);
|
||||
findObjects();
|
||||
}
|
||||
|
||||
void DlgPropertyLink::on_searchBox_textChanged(const QString& search)
|
||||
{
|
||||
ui->listWidget->clear();
|
||||
bool on = ui->checkObjectType->isChecked();
|
||||
findObjects(on, search);
|
||||
void DlgPropertyLink::on_typeTree_itemSelectionChanged() {
|
||||
types.clear();
|
||||
for(auto item : ui->typeTree->selectedItems())
|
||||
types.insert(item->text(0).toLatin1().constData());
|
||||
findObjects();
|
||||
}
|
||||
|
||||
void DlgPropertyLink::on_searchBox_textChanged(const QString& /*search*/)
|
||||
{
|
||||
findObjects();
|
||||
}
|
||||
|
||||
void DlgPropertyLink::on_comboBox_currentIndexChanged(int index)
|
||||
{
|
||||
link[0] = ui->comboBox->itemData(index).toString();
|
||||
refreshTypes = true;
|
||||
findObjects();
|
||||
}
|
||||
|
||||
|
||||
#include "moc_DlgPropertyLink.cpp"
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <QDialog>
|
||||
#include <QAbstractItemView>
|
||||
|
||||
#define FC_XLINK_VALUE_INDEX 5
|
||||
|
||||
namespace Gui { namespace Dialog {
|
||||
|
||||
class Ui_DlgPropertyLink;
|
||||
@@ -35,7 +37,7 @@ class DlgPropertyLink : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DlgPropertyLink(const QStringList& list, QWidget* parent = 0, Qt::WindowFlags fl = 0);
|
||||
DlgPropertyLink(const QStringList& list, QWidget* parent = 0, Qt::WindowFlags fl = 0, bool xlink=false);
|
||||
~DlgPropertyLink();
|
||||
|
||||
void setSelectionMode(QAbstractItemView::SelectionMode mode);
|
||||
@@ -45,14 +47,21 @@ public:
|
||||
|
||||
private Q_SLOTS:
|
||||
void on_checkObjectType_toggled(bool);
|
||||
void on_typeTree_itemSelectionChanged();
|
||||
void on_searchBox_textChanged(const QString&);
|
||||
void on_comboBox_currentIndexChanged(int);
|
||||
void onItemExpanded(QTreeWidgetItem * item);
|
||||
|
||||
private:
|
||||
void findObjects(bool on, const QString& searchText);
|
||||
QTreeWidgetItem *createItem(App::DocumentObject *obj, QTreeWidgetItem *parent);
|
||||
void findObjects();
|
||||
|
||||
private:
|
||||
QStringList link;
|
||||
Ui_DlgPropertyLink* ui;
|
||||
std::set<App::DocumentObject*> inList;
|
||||
std::set<std::string> types;
|
||||
bool refreshTypes = true;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
|
||||
@@ -6,15 +6,37 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>257</width>
|
||||
<height>428</height>
|
||||
<width>436</width>
|
||||
<height>438</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Link</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="checkObjectType">
|
||||
<property name="text">
|
||||
<string>Filter by type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
@@ -32,23 +54,34 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="checkObjectType">
|
||||
<property name="text">
|
||||
<string>Show all object types</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="listWidget"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="comboBox"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QTreeWidget" name="typeTree">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -30,10 +30,14 @@
|
||||
#include <App/Expression.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <App/ObjectIdentifier.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/Application.h>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
FC_LOG_LEVEL_INIT("Expression",true,true)
|
||||
|
||||
using namespace Gui;
|
||||
using namespace App;
|
||||
|
||||
@@ -67,10 +71,22 @@ void Gui::ExpressionBinding::setExpression(boost::shared_ptr<Expression> expr)
|
||||
}
|
||||
|
||||
lastExpression = getExpression();
|
||||
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
if(transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << (expr?"Set":"Discard") << " expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
}
|
||||
|
||||
docObj->ExpressionEngine.setValue(path, expr);
|
||||
|
||||
|
||||
if(m_autoApply)
|
||||
apply();
|
||||
|
||||
if(transaction)
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
|
||||
}
|
||||
|
||||
void ExpressionBinding::bind(const App::ObjectIdentifier &_path)
|
||||
@@ -105,17 +121,34 @@ boost::shared_ptr<App::Expression> ExpressionBinding::getExpression() const
|
||||
return docObj->getExpression(path).expression;
|
||||
}
|
||||
|
||||
std::string ExpressionBinding::getExpressionString() const
|
||||
std::string ExpressionBinding::getExpressionString(bool no_throw) const
|
||||
{
|
||||
if (!getExpression())
|
||||
throw Base::RuntimeError("No expression found.");
|
||||
|
||||
return getExpression()->toString();
|
||||
try {
|
||||
if (!getExpression())
|
||||
throw Base::RuntimeError("No expression found.");
|
||||
return getExpression()->toString();
|
||||
} catch (Base::Exception &e) {
|
||||
if(no_throw)
|
||||
FC_ERR("failed to get expression string: " << e.what());
|
||||
else
|
||||
throw;
|
||||
} catch (std::exception &e) {
|
||||
if(no_throw)
|
||||
FC_ERR("failed to get expression string: " << e.what());
|
||||
else
|
||||
throw;
|
||||
} catch (...) {
|
||||
if(no_throw)
|
||||
FC_ERR("failed to get expression string: unknown exception");
|
||||
else
|
||||
throw;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string ExpressionBinding::getEscapedExpressionString() const
|
||||
{
|
||||
return Base::Tools::escapedUnicodeFromUtf8(getExpressionString().c_str());
|
||||
return Base::Tools::escapedUnicodeFromUtf8(getExpressionString(false).c_str());
|
||||
}
|
||||
|
||||
QPixmap ExpressionBinding::getIcon(const char* name, const QSize& size) const
|
||||
@@ -142,11 +175,20 @@ bool ExpressionBinding::apply(const std::string & propName)
|
||||
|
||||
if (!docObj)
|
||||
throw Base::RuntimeError("Document object not found.");
|
||||
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
if(transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << "Set expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
}
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').%s.setExpression('%s', u'%s')",
|
||||
docObj->getDocument()->getName(),
|
||||
docObj->getNameInDocument(),
|
||||
path.toEscapedString().c_str(),
|
||||
getEscapedExpressionString().c_str());
|
||||
if(transaction)
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -156,11 +198,20 @@ bool ExpressionBinding::apply(const std::string & propName)
|
||||
if (!docObj)
|
||||
throw Base::RuntimeError("Document object not found.");
|
||||
|
||||
if (lastExpression)
|
||||
if (lastExpression) {
|
||||
bool transaction = !App::GetApplication().getActiveTransaction();
|
||||
if(transaction) {
|
||||
std::ostringstream ss;
|
||||
ss << "Discard expression " << docObj->Label.getValue();
|
||||
App::GetApplication().setActiveTransaction(ss.str().c_str());
|
||||
}
|
||||
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').%s.setExpression('%s', None)",
|
||||
docObj->getDocument()->getName(),
|
||||
docObj->getNameInDocument(),
|
||||
path.toEscapedString().c_str());
|
||||
if(transaction)
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -183,9 +234,7 @@ bool ExpressionBinding::apply()
|
||||
if (prop->isReadOnly())
|
||||
return true;
|
||||
|
||||
std::string name = docObj->getNameInDocument();
|
||||
|
||||
return apply("App.ActiveDocument." + name + "." + getPath().toEscapedString());
|
||||
return apply(Gui::Command::getObjectCmd(docObj) + "." + getPath().toEscapedString());
|
||||
}
|
||||
|
||||
void ExpressionBinding::expressionChange(const ObjectIdentifier& id) {
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
protected:
|
||||
const App::ObjectIdentifier & getPath() const { return path; }
|
||||
boost::shared_ptr<App::Expression> getExpression() const;
|
||||
std::string getExpressionString() const;
|
||||
std::string getExpressionString(bool no_throw=true) const;
|
||||
std::string getEscapedExpressionString() const;
|
||||
virtual void setExpression(boost::shared_ptr<App::Expression> expr);
|
||||
|
||||
|
||||
@@ -5,21 +5,292 @@
|
||||
#include <QStandardItemModel>
|
||||
#include <QLineEdit>
|
||||
#include <QAbstractItemView>
|
||||
#include <QTextBlock>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <App/ObjectIdentifier.h>
|
||||
#include "ExpressionCompleter.h"
|
||||
#include <App/Expression.h>
|
||||
#include <App/PropertyLinks.h>
|
||||
|
||||
FC_LOG_LEVEL_INIT("Completer",true,true);
|
||||
|
||||
Q_DECLARE_METATYPE(App::ObjectIdentifier);
|
||||
|
||||
using namespace App;
|
||||
using namespace Gui;
|
||||
|
||||
class ExpressionCompleterModel: public QAbstractItemModel {
|
||||
public:
|
||||
ExpressionCompleterModel(QObject *parent, const App::DocumentObject *obj)
|
||||
:QAbstractItemModel(parent)
|
||||
{
|
||||
if(obj) {
|
||||
currentDoc = obj->getDocument()->getName();
|
||||
currentObj = obj->getNameInDocument();
|
||||
inList = obj->getInListEx(true);
|
||||
}
|
||||
}
|
||||
|
||||
// This ExpressionCompleter model works without any pysical items.
|
||||
// Everything item related is stored inside QModelIndex.InternalPointer/InternalId(),
|
||||
// using the following Info structure.
|
||||
//
|
||||
// The Info contains two indices, one for document and the other for object.
|
||||
// For 32-bit system, the index is 16bit which limits the size to 64K. For
|
||||
// 64-bit system, the index is 32bit.
|
||||
//
|
||||
// The "virtual" items are organized as a tree. The root items are special,
|
||||
// which consists of three types in the following order,
|
||||
//
|
||||
// * Document, even index contains item using document's name, while
|
||||
// odd index with quoted document label.
|
||||
// * Objects of the current document, even index with object's internal
|
||||
// name, and odd index with quoted object label.
|
||||
// * Properties of the current object.
|
||||
//
|
||||
// Document item contains object item as child, and object item contains
|
||||
// property item.
|
||||
//
|
||||
// The QModelIndex of a root item has both the doc field and obj field set
|
||||
// to -1, and uses the row as the item index. We can figure out the type of
|
||||
// the item solely based on this row index.
|
||||
//
|
||||
// QModelIndex of a non-root object item has doc field as the document
|
||||
// index, and obj field set to -1.
|
||||
//
|
||||
// QModelIndex of a non-root property item has doc field as the document
|
||||
// index, and obj field as the object index.
|
||||
union Info {
|
||||
struct {
|
||||
qint32 doc;
|
||||
qint32 obj;
|
||||
}d;
|
||||
struct {
|
||||
qint16 doc;
|
||||
qint16 obj;
|
||||
}d32;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
static void *infoId(const Info &info) {
|
||||
if(sizeof(void*) >= sizeof(info))
|
||||
return info.ptr;
|
||||
|
||||
Info info32;
|
||||
info32.d32.doc = (qint16)info.d.doc;
|
||||
info32.d32.obj = (qint16)info.d.obj;
|
||||
return info32.ptr;
|
||||
};
|
||||
|
||||
static Info getInfo(const QModelIndex &index) {
|
||||
Info info;
|
||||
info.ptr = index.internalPointer();
|
||||
if(sizeof(void*) >= sizeof(Info))
|
||||
return info;
|
||||
Info res;
|
||||
res.d.doc = info.d32.doc;
|
||||
res.d.obj = info.d32.obj;
|
||||
return res;
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const {
|
||||
if(role!=Qt::EditRole && role!=Qt::DisplayRole && role!=Qt::UserRole)
|
||||
return QVariant();
|
||||
QVariant v;
|
||||
Info info = getInfo(index);
|
||||
_data(info,index.row(),&v,0,role==Qt::UserRole);
|
||||
FC_TRACE(info.d.doc << "," << info.d.obj << "," << index.row()
|
||||
<< ": " << v.toString().toUtf8().constData());
|
||||
return v;
|
||||
}
|
||||
|
||||
void _data(const Info &info, int row, QVariant *v, int *count, bool sep=false) const {
|
||||
int idx;
|
||||
idx = info.d.doc<0?row:info.d.doc;
|
||||
const auto &docs = App::GetApplication().getDocuments();
|
||||
int docSize = (int)docs.size()*2;
|
||||
int objSize = 0;
|
||||
int propSize = 0;
|
||||
std::vector<App::Property*> props;
|
||||
App::Document *doc = 0;
|
||||
App::DocumentObject *obj = 0;
|
||||
App::Property *prop = 0;
|
||||
if(idx>=0 && idx<docSize)
|
||||
doc = docs[idx/2];
|
||||
else {
|
||||
doc = App::GetApplication().getDocument(currentDoc.c_str());
|
||||
if(!doc)
|
||||
return;
|
||||
idx -= docSize;
|
||||
if(info.d.doc<0)
|
||||
row = idx;
|
||||
const auto &objs = doc->getObjects();
|
||||
objSize = (int)objs.size()*2;
|
||||
if(idx>=0 && idx<objSize) {
|
||||
obj = objs[idx/2];
|
||||
if(inList.count(obj))
|
||||
return;
|
||||
} else {
|
||||
auto cobj = doc->getObject(currentObj.c_str());
|
||||
if(cobj) {
|
||||
idx -= objSize;
|
||||
if(info.d.doc<0)
|
||||
row = idx;
|
||||
cobj->getPropertyList(props);
|
||||
propSize = (int)props.size();
|
||||
if(idx >= propSize)
|
||||
return;
|
||||
if(idx>=0) {
|
||||
obj = cobj;
|
||||
prop = props[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(info.d.doc<0) {
|
||||
if(count)
|
||||
*count = docSize + objSize + propSize;
|
||||
if(idx>=0 && v) {
|
||||
QString res;
|
||||
if(prop)
|
||||
res = QString::fromLatin1(prop->getName());
|
||||
else if(obj) {
|
||||
if(idx & 1)
|
||||
res = QString::fromUtf8(quote(obj->Label.getStrValue()).c_str());
|
||||
else
|
||||
res = QString::fromLatin1(obj->getNameInDocument());
|
||||
if(sep)
|
||||
res += QLatin1Char('.');
|
||||
}else {
|
||||
if(idx & 1)
|
||||
res = QString::fromUtf8(quote(doc->Label.getStrValue()).c_str());
|
||||
else
|
||||
res = QString::fromLatin1(doc->getName());
|
||||
if(sep)
|
||||
res += QLatin1Char('#');
|
||||
}
|
||||
v->setValue(res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!obj) {
|
||||
idx = info.d.obj<0?row:info.d.obj;
|
||||
const auto &objs = doc->getObjects();
|
||||
objSize = (int)objs.size()*2;
|
||||
if(idx<0 || idx>=objSize || inList.count(obj))
|
||||
return;
|
||||
obj = objs[idx/2];
|
||||
if(info.d.obj<0) {
|
||||
if(count)
|
||||
*count = objSize;
|
||||
if(v) {
|
||||
QString res;
|
||||
if(idx&1)
|
||||
res = QString::fromUtf8(quote(obj->Label.getStrValue()).c_str());
|
||||
else
|
||||
res = QString::fromLatin1(obj->getNameInDocument());
|
||||
if(sep)
|
||||
res += QLatin1Char('.');
|
||||
v->setValue(res);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!prop) {
|
||||
idx = row;
|
||||
obj->getPropertyList(props);
|
||||
propSize = (int)props.size();
|
||||
if(idx<0 || idx>=propSize)
|
||||
return;
|
||||
prop = props[idx];
|
||||
if(count)
|
||||
*count = propSize;
|
||||
}
|
||||
if(v)
|
||||
*v = QString::fromLatin1(prop->getName());
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex parent(const QModelIndex & index) const {
|
||||
if(!index.isValid())
|
||||
return QModelIndex();
|
||||
Info info;
|
||||
Info parentInfo;
|
||||
info = parentInfo = getInfo(index);
|
||||
if(info.d.obj>=0) {
|
||||
parentInfo.d.obj = -1;
|
||||
return createIndex(info.d.obj,0,infoId(parentInfo));
|
||||
}
|
||||
if(info.d.doc>=0) {
|
||||
parentInfo.d.doc = -1;
|
||||
return createIndex(info.d.doc,0,infoId(parentInfo));
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const {
|
||||
if(row<0)
|
||||
return QModelIndex();
|
||||
Info info;
|
||||
if(!parent.isValid()) {
|
||||
info.d.doc = -1;
|
||||
info.d.obj = -1;
|
||||
}else{
|
||||
info = getInfo(parent);
|
||||
if(info.d.doc<=0)
|
||||
info.d.doc = parent.row();
|
||||
else if(info.d.obj<=0)
|
||||
info.d.obj = parent.row();
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
return createIndex(row,column,infoId(info));
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const {
|
||||
const auto &docs = App::GetApplication().getDocuments();
|
||||
Info info;
|
||||
int row = 0;
|
||||
if(!parent.isValid()) {
|
||||
info.d.doc = -1;
|
||||
info.d.obj = -1;
|
||||
row = -1;
|
||||
}else{
|
||||
info = getInfo(parent);
|
||||
if(info.d.doc<0)
|
||||
info.d.doc = parent.row();
|
||||
else if(info.d.obj<0)
|
||||
info.d.obj = parent.row();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
_data(info,row,0,&count);
|
||||
FC_TRACE(info.d.doc << "," << info.d.obj << "," << row << " row count " << count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int columnCount(const QModelIndex &) const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<App::DocumentObject*> inList;
|
||||
std::string currentDoc;
|
||||
std::string currentObj;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct an ExpressionCompleter object.
|
||||
* @param currentDoc Current document to generate the model from.
|
||||
@@ -27,208 +298,88 @@ using namespace Gui;
|
||||
* @param parent Parent object owning the completer.
|
||||
*/
|
||||
|
||||
ExpressionCompleter::ExpressionCompleter(const App::Document * currentDoc, const App::DocumentObject * currentDocObj, QObject *parent)
|
||||
: QCompleter(parent), prefixStart(0)
|
||||
ExpressionCompleter::ExpressionCompleter(const App::DocumentObject * currentDocObj, QObject *parent)
|
||||
: QCompleter(parent), prefixStart(0), currentObj(currentDocObj)
|
||||
{
|
||||
QStandardItemModel* model = new QStandardItemModel(this);
|
||||
|
||||
std::vector<App::Document*> docs = App::GetApplication().getDocuments();
|
||||
std::vector<App::Document*>::const_iterator di = docs.begin();
|
||||
|
||||
std::vector<DocumentObject*> deps;
|
||||
if (currentDocObj)
|
||||
deps = currentDocObj->getInList();
|
||||
std::set<const DocumentObject*> forbidden;
|
||||
|
||||
for (std::vector<DocumentObject*>::const_iterator it = deps.begin(); it != deps.end(); ++it)
|
||||
forbidden.insert(*it);
|
||||
|
||||
/* Create tree with full path to all objects */
|
||||
while (di != docs.end()) {
|
||||
QStandardItem* docItem = new QStandardItem(QString::fromLatin1((*di)->getName()));
|
||||
|
||||
docItem->setData(QString::fromLatin1((*di)->getName()) + QString::fromLatin1("#"), Qt::UserRole);
|
||||
createModelForDocument(*di, docItem, forbidden);
|
||||
|
||||
model->appendRow(docItem);
|
||||
|
||||
++di;
|
||||
}
|
||||
|
||||
/* Create branch with current document object */
|
||||
|
||||
if (currentDocObj) {
|
||||
createModelForDocument(currentDocObj->getDocument(), model->invisibleRootItem(), forbidden);
|
||||
createModelForDocumentObject(currentDocObj, model->invisibleRootItem());
|
||||
}
|
||||
else {
|
||||
if (currentDoc)
|
||||
createModelForDocument(currentDoc, model->invisibleRootItem(), forbidden);
|
||||
}
|
||||
|
||||
setModel(model);
|
||||
|
||||
setCaseSensitivity(Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create model node given document, the parent and forbidden node.
|
||||
* @param doc Document
|
||||
* @param parent Parent item
|
||||
* @param forbidden Forbidden document objects; typically those that will create a loop in the DAG if used.
|
||||
*/
|
||||
void ExpressionCompleter::init() {
|
||||
if(model())
|
||||
return;
|
||||
|
||||
void ExpressionCompleter::createModelForDocument(const App::Document * doc, QStandardItem * parent,
|
||||
const std::set<const DocumentObject*> & forbidden) {
|
||||
std::vector<App::DocumentObject*> docObjs = doc->getObjects();
|
||||
std::vector<App::DocumentObject*>::const_iterator doi = docObjs.begin();
|
||||
|
||||
while (doi != docObjs.end()) {
|
||||
std::set<const DocumentObject*>::const_iterator it = forbidden.find(*doi);
|
||||
|
||||
// Skip?
|
||||
if (it != forbidden.end()) {
|
||||
++doi;
|
||||
continue;
|
||||
}
|
||||
|
||||
QStandardItem* docObjItem = new QStandardItem(QString::fromLatin1((*doi)->getNameInDocument()));
|
||||
|
||||
docObjItem->setData(QString::fromLatin1((*doi)->getNameInDocument()) + QString::fromLatin1("."), Qt::UserRole);
|
||||
createModelForDocumentObject(*doi, docObjItem);
|
||||
parent->appendRow(docObjItem);
|
||||
|
||||
if (strcmp((*doi)->getNameInDocument(), (*doi)->Label.getValue()) != 0) {
|
||||
std::string label = (*doi)->Label.getValue();
|
||||
|
||||
if (!ExpressionParser::isTokenAnIndentifier(label))
|
||||
label = quote(label);
|
||||
|
||||
docObjItem = new QStandardItem(QString::fromUtf8(label.c_str()));
|
||||
|
||||
docObjItem->setData( QString::fromUtf8(label.c_str()) + QString::fromLatin1("."), Qt::UserRole);
|
||||
createModelForDocumentObject(*doi, docObjItem);
|
||||
parent->appendRow(docObjItem);
|
||||
}
|
||||
|
||||
++doi;
|
||||
}
|
||||
setModel(new ExpressionCompleterModel(this,currentObj.getObject()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create model nodes for document object
|
||||
* @param docObj Document object
|
||||
* @param parent Parent item
|
||||
*/
|
||||
|
||||
void ExpressionCompleter::createModelForDocumentObject(const DocumentObject * docObj, QStandardItem * parent)
|
||||
{
|
||||
std::vector<App::Property*> props;
|
||||
docObj->getPropertyList(props);
|
||||
|
||||
std::vector<App::Property*>::const_iterator pi = props.begin();
|
||||
while (pi != props.end()) {
|
||||
|
||||
// Skip all types of links
|
||||
if ((*pi)->isDerivedFrom(App::PropertyLink::getClassTypeId()) ||
|
||||
(*pi)->isDerivedFrom(App::PropertyLinkSub::getClassTypeId())) {
|
||||
++pi;
|
||||
continue;
|
||||
}
|
||||
|
||||
createModelForPaths(*pi, parent);
|
||||
++pi;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create nodes for a property.
|
||||
* @param prop
|
||||
* @param docObjItem
|
||||
*/
|
||||
|
||||
void ExpressionCompleter::createModelForPaths(const App::Property * prop, QStandardItem *docObjItem)
|
||||
{
|
||||
std::vector<ObjectIdentifier> paths;
|
||||
std::vector<ObjectIdentifier>::const_iterator ppi;
|
||||
|
||||
prop->getPaths(paths);
|
||||
|
||||
for (ppi = paths.begin(); ppi != paths.end(); ++ppi) {
|
||||
QStandardItem* pathItem = new QStandardItem(Base::Tools::fromStdString(ppi->toString()));
|
||||
|
||||
QVariant value;
|
||||
|
||||
value.setValue(*ppi);
|
||||
pathItem->setData(value, Qt::UserRole);
|
||||
|
||||
docObjItem->appendRow(pathItem);
|
||||
}
|
||||
}
|
||||
|
||||
QString ExpressionCompleter::pathFromIndex ( const QModelIndex & index ) const
|
||||
{
|
||||
QStandardItemModel * m = static_cast<QStandardItemModel*>(model());
|
||||
|
||||
if (m->data(index, Qt::UserRole).canConvert<App::ObjectIdentifier>()) {
|
||||
App::ObjectIdentifier p = m->data(index, Qt::UserRole).value<App::ObjectIdentifier>();
|
||||
QString pStr = Base::Tools::fromStdString(p.toString());
|
||||
|
||||
QString parentStr;
|
||||
QModelIndex parent = index.parent();
|
||||
while (parent.isValid()) {
|
||||
QString thisParentStr = m->data(parent, Qt::UserRole).toString();
|
||||
|
||||
parentStr = thisParentStr + parentStr;
|
||||
|
||||
parent = parent.parent();
|
||||
}
|
||||
|
||||
return parentStr + pStr;
|
||||
}
|
||||
else if (m->data(index, Qt::UserRole).canConvert<QString>()) {
|
||||
QModelIndex parent = index;
|
||||
QString parentStr;
|
||||
|
||||
while (parent.isValid()) {
|
||||
QString thisParentStr = m->data(parent, Qt::UserRole).toString();
|
||||
|
||||
parentStr = thisParentStr + parentStr;
|
||||
|
||||
parent = parent.parent();
|
||||
}
|
||||
|
||||
return parentStr;
|
||||
}
|
||||
else
|
||||
auto m = model();
|
||||
if(!m || !index.isValid())
|
||||
return QString();
|
||||
|
||||
QString res;
|
||||
auto parent = index;
|
||||
do {
|
||||
res = m->data(parent, Qt::UserRole).toString() + res;
|
||||
parent = parent.parent();
|
||||
}while(parent.isValid());
|
||||
|
||||
auto info = ExpressionCompleterModel::getInfo(index);
|
||||
FC_TRACE("join path " << info.d.doc << "," << info.d.obj << "," << index.row()
|
||||
<< ": " << res.toUtf8().constData());
|
||||
return res;
|
||||
}
|
||||
|
||||
QStringList ExpressionCompleter::splitPath ( const QString & path ) const
|
||||
QStringList ExpressionCompleter::splitPath ( const QString & input ) const
|
||||
{
|
||||
try {
|
||||
App::ObjectIdentifier p = ObjectIdentifier::parse(0, path.toUtf8().constData());
|
||||
QStringList l;
|
||||
QStringList l;
|
||||
std::string path = input.toUtf8().constData();
|
||||
if(path.empty())
|
||||
return l;
|
||||
|
||||
int retry = 0;
|
||||
std::string trim;
|
||||
while(1) {
|
||||
try {
|
||||
App::ObjectIdentifier p = ObjectIdentifier::parse(
|
||||
currentObj.getObject(), path);
|
||||
|
||||
if (p.getProperty()) {
|
||||
for (int i = 0; i < p.numComponents(); ++i)
|
||||
l << Base::Tools::fromStdString(p.getPropertyComponent(i).toString());
|
||||
return l;
|
||||
}
|
||||
else {
|
||||
std::vector<std::string> sl = p.getStringList();
|
||||
std::vector<std::string>::const_iterator sli = sl.begin();
|
||||
|
||||
if(retry && sl.size())
|
||||
sl.pop_back();
|
||||
if(trim.size() && boost::ends_with(sl.back(),trim))
|
||||
sl.back().resize(sl.back().size()-trim.size());
|
||||
while (sli != sl.end()) {
|
||||
l << Base::Tools::fromStdString(*sli);
|
||||
++sli;
|
||||
}
|
||||
|
||||
FC_TRACE("split path " << path
|
||||
<< " -> " << l.join(QLatin1String("/")).toUtf8().constData());
|
||||
return l;
|
||||
}
|
||||
}
|
||||
catch (const Base::Exception &) {
|
||||
return QStringList() << path;
|
||||
catch (const Base::Exception &e) {
|
||||
FC_TRACE("split path " << path << " error: " << e.what());
|
||||
if(!retry) {
|
||||
char last = path[path.size()-1];
|
||||
if(last!='#' && last!='.' && path.find('#')!=std::string::npos) {
|
||||
path += "._self";
|
||||
++retry;
|
||||
continue;
|
||||
}
|
||||
}else if(retry==1) {
|
||||
path.resize(path.size()-6);
|
||||
char last = path[path.size()-1];
|
||||
if(last!='.' && last!='<' && path.find("#<<")!=std::string::npos) {
|
||||
path += ">>._self";
|
||||
++retry;
|
||||
trim = ">>";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return QStringList() << input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +443,7 @@ void ExpressionCompleter::slotUpdate(const QString & prefix)
|
||||
ExpressionLineEdit::ExpressionLineEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
, completer(0)
|
||||
, block(false)
|
||||
, block(true)
|
||||
{
|
||||
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged(const QString&)));
|
||||
}
|
||||
@@ -305,7 +456,7 @@ void ExpressionLineEdit::setDocumentObject(const App::DocumentObject * currentDo
|
||||
}
|
||||
|
||||
if (currentDocObj != 0) {
|
||||
completer = new ExpressionCompleter(currentDocObj->getDocument(), currentDocObj, this);
|
||||
completer = new ExpressionCompleter(currentDocObj, this);
|
||||
completer->setWidget(this);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
connect(completer, SIGNAL(activated(QString)), this, SLOT(slotCompleteText(QString)));
|
||||
@@ -338,10 +489,79 @@ void ExpressionLineEdit::slotCompleteText(const QString & completionPrefix)
|
||||
QString before(text().left(start));
|
||||
QString after(text().mid(cursorPosition()));
|
||||
|
||||
block = true;
|
||||
Base::FlagToggler<bool> flag(block,false);
|
||||
setText(before + completionPrefix + after);
|
||||
setCursorPosition(QString(before + completionPrefix).length());
|
||||
block = false;
|
||||
}
|
||||
|
||||
void ExpressionLineEdit::keyPressEvent(QKeyEvent *e) {
|
||||
Base::FlagToggler<bool> flag(block,true);
|
||||
QLineEdit::keyPressEvent(e);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExpressionTextEdit::ExpressionTextEdit(QWidget *parent)
|
||||
: QPlainTextEdit(parent)
|
||||
, completer(0)
|
||||
, block(true)
|
||||
{
|
||||
connect(this, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
|
||||
}
|
||||
|
||||
void ExpressionTextEdit::setDocumentObject(const App::DocumentObject * currentDocObj)
|
||||
{
|
||||
if (completer) {
|
||||
delete completer;
|
||||
completer = 0;
|
||||
}
|
||||
|
||||
if (currentDocObj != 0) {
|
||||
completer = new ExpressionCompleter(currentDocObj, this);
|
||||
completer->setWidget(this);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
connect(completer, SIGNAL(activated(QString)), this, SLOT(slotCompleteText(QString)));
|
||||
connect(completer, SIGNAL(highlighted(QString)), this, SLOT(slotCompleteText(QString)));
|
||||
connect(this, SIGNAL(textChanged2(QString)), completer, SLOT(slotUpdate(QString)));
|
||||
}
|
||||
}
|
||||
|
||||
bool ExpressionTextEdit::completerActive() const
|
||||
{
|
||||
return completer && completer->popup() && completer->popup()->isVisible();
|
||||
}
|
||||
|
||||
void ExpressionTextEdit::hideCompleter()
|
||||
{
|
||||
if (completer && completer->popup())
|
||||
completer->popup()->setVisible(false);
|
||||
}
|
||||
|
||||
void ExpressionTextEdit::slotTextChanged()
|
||||
{
|
||||
if (!block) {
|
||||
QTextCursor cursor = textCursor();
|
||||
Q_EMIT textChanged2(cursor.block().text().left(cursor.positionInBlock()));
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionTextEdit::slotCompleteText(const QString & completionPrefix)
|
||||
{
|
||||
QTextCursor cursor = textCursor();
|
||||
int start = completer->getPrefixStart();
|
||||
int pos = cursor.positionInBlock();
|
||||
if(pos>=start) {
|
||||
Base::FlagToggler<bool> flag(block,false);
|
||||
if(pos>start)
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter,QTextCursor::KeepAnchor,pos-start);
|
||||
cursor.insertText(completionPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionTextEdit::keyPressEvent(QKeyEvent *e) {
|
||||
Base::FlagToggler<bool> flag(block,true);
|
||||
QPlainTextEdit::keyPressEvent(e);
|
||||
}
|
||||
|
||||
#include "moc_ExpressionCompleter.cpp"
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
#include <QObject>
|
||||
#include <QCompleter>
|
||||
#include <QLineEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <App/DocumentObserver.h>
|
||||
|
||||
class QStandardItem;
|
||||
|
||||
@@ -25,7 +28,7 @@ class GuiExport ExpressionCompleter : public QCompleter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExpressionCompleter(const App::Document * currentDoc, const App::DocumentObject * currentDocObj, QObject *parent = 0);
|
||||
ExpressionCompleter(const App::DocumentObject * currentDocObj, QObject *parent = 0);
|
||||
|
||||
int getPrefixStart() const { return prefixStart; }
|
||||
|
||||
@@ -33,14 +36,12 @@ public Q_SLOTS:
|
||||
void slotUpdate(const QString &prefix);
|
||||
|
||||
private:
|
||||
void createModelForDocument(const App::Document * doc, QStandardItem * parent, const std::set<const App::DocumentObject *> &forbidden);
|
||||
void createModelForDocumentObject(const App::DocumentObject * docObj, QStandardItem * parent);
|
||||
void createModelForPaths(const App::Property * prop, QStandardItem *docObjItem);
|
||||
|
||||
void init();
|
||||
virtual QString pathFromIndex ( const QModelIndex & index ) const;
|
||||
virtual QStringList splitPath ( const QString & path ) const;
|
||||
|
||||
int prefixStart;
|
||||
App::DocumentObjectT currentObj;
|
||||
|
||||
};
|
||||
|
||||
@@ -56,6 +57,27 @@ Q_SIGNALS:
|
||||
public Q_SLOTS:
|
||||
void slotTextChanged(const QString & text);
|
||||
void slotCompleteText(const QString & completionPrefix);
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent * event);
|
||||
private:
|
||||
ExpressionCompleter * completer;
|
||||
bool block;
|
||||
};
|
||||
|
||||
class GuiExport ExpressionTextEdit : public QPlainTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExpressionTextEdit(QWidget *parent = 0);
|
||||
void setDocumentObject(const App::DocumentObject *currentDocObj);
|
||||
bool completerActive() const;
|
||||
void hideCompleter();
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent * event);
|
||||
Q_SIGNALS:
|
||||
void textChanged2(QString text);
|
||||
public Q_SLOTS:
|
||||
void slotTextChanged();
|
||||
void slotCompleteText(const QString & completionPrefix);
|
||||
private:
|
||||
ExpressionCompleter * completer;
|
||||
bool block;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
# include <QGridLayout>
|
||||
# include <QHeaderView>
|
||||
# include <QEvent>
|
||||
# include <QTimer>
|
||||
#endif
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
@@ -38,12 +39,17 @@
|
||||
#include <App/PropertyContainer.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/Document.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include "PropertyView.h"
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Document.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "ViewProvider.h"
|
||||
#include "ViewProviderDocumentObject.h"
|
||||
#include "Tree.h"
|
||||
#include "ViewParams.h"
|
||||
|
||||
#include "propertyeditor/PropertyEditor.h"
|
||||
|
||||
@@ -52,6 +58,14 @@ using namespace Gui;
|
||||
using namespace Gui::DockWnd;
|
||||
using namespace Gui::PropertyEditor;
|
||||
|
||||
static ParameterGrp::handle _GetParam() {
|
||||
static ParameterGrp::handle hGrp;
|
||||
if(!hGrp) {
|
||||
hGrp = App::GetApplication().GetParameterGroupByPath(
|
||||
"User parameter:BaseApp/Preferences/PropertyView");
|
||||
}
|
||||
return hGrp;
|
||||
}
|
||||
|
||||
/* TRANSLATOR Gui::PropertyView */
|
||||
|
||||
@@ -61,12 +75,16 @@ using namespace Gui::PropertyEditor;
|
||||
* in two tabs.
|
||||
*/
|
||||
PropertyView::PropertyView(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
: QWidget(parent),SelectionObserver(false)
|
||||
{
|
||||
QGridLayout* pLayout = new QGridLayout( this );
|
||||
pLayout->setSpacing(0);
|
||||
pLayout->setMargin (0);
|
||||
|
||||
timer = new QTimer(this);
|
||||
timer->setSingleShot(true);
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
|
||||
|
||||
tabs = new QTabWidget (this);
|
||||
tabs->setObjectName(QString::fromUtf8("propertyTab"));
|
||||
tabs->setTabPosition(QTabWidget::South);
|
||||
@@ -83,14 +101,10 @@ PropertyView::PropertyView(QWidget *parent)
|
||||
propertyEditorData->setAutomaticDocumentUpdate(true);
|
||||
tabs->addTab(propertyEditorData, tr("Data"));
|
||||
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("PropertyView");
|
||||
if ( hGrp ) {
|
||||
int preferredTab = hGrp->GetInt("LastTabIndex", 1);
|
||||
int preferredTab = _GetParam()->GetInt("LastTabIndex", 1);
|
||||
|
||||
if ( preferredTab > 0 && preferredTab < tabs->count() )
|
||||
tabs->setCurrentIndex(preferredTab);
|
||||
}
|
||||
if ( preferredTab > 0 && preferredTab < tabs->count() )
|
||||
tabs->setCurrentIndex(preferredTab);
|
||||
|
||||
// connect after adding all tabs, so adding doesn't thrash the parameter
|
||||
connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
|
||||
@@ -109,10 +123,19 @@ PropertyView::PropertyView(QWidget *parent)
|
||||
(&PropertyView::slotRemoveDynamicProperty, this, _1));
|
||||
this->connectPropChange =
|
||||
App::GetApplication().signalChangePropertyEditor.connect(boost::bind
|
||||
(&PropertyView::slotChangePropertyEditor, this, _1));
|
||||
(&PropertyView::slotChangePropertyEditor, this, _1, _2));
|
||||
this->connectUndoDocument =
|
||||
App::GetApplication().signalUndoDocument.connect(boost::bind
|
||||
(&PropertyView::slotRollback, this));
|
||||
this->connectRedoDocument =
|
||||
App::GetApplication().signalRedoDocument.connect(boost::bind
|
||||
(&PropertyView::slotRollback, this));
|
||||
this->connectActiveDoc =
|
||||
Application::Instance->signalActiveDocument.connect(boost::bind
|
||||
(&PropertyView::slotActiveDocument, this, _1));
|
||||
this->connectDelDocument =
|
||||
Application::Instance->signalDeleteDocument.connect(
|
||||
boost::bind(&PropertyView::slotDeleteDocument, this, _1));
|
||||
}
|
||||
|
||||
PropertyView::~PropertyView()
|
||||
@@ -122,7 +145,60 @@ PropertyView::~PropertyView()
|
||||
this->connectPropAppend.disconnect();
|
||||
this->connectPropRemove.disconnect();
|
||||
this->connectPropChange.disconnect();
|
||||
this->connectUndoDocument.disconnect();
|
||||
this->connectRedoDocument.disconnect();
|
||||
this->connectActiveDoc.disconnect();
|
||||
this->connectDelDocument.disconnect();
|
||||
}
|
||||
|
||||
static bool _ShowAll;
|
||||
|
||||
bool PropertyView::showAll() {
|
||||
return _ShowAll;
|
||||
}
|
||||
|
||||
void PropertyView::setShowAll(bool enable) {
|
||||
if(_ShowAll != enable) {
|
||||
_ShowAll = enable;
|
||||
for(auto view : getMainWindow()->findChildren<PropertyView*>()) {
|
||||
if(view->isVisible())
|
||||
view->onTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyView::hideEvent(QHideEvent *ev) {
|
||||
this->timer->stop();
|
||||
this->detachSelection();
|
||||
// clear the properties before hiding.
|
||||
propertyEditorData->buildUp();
|
||||
propertyEditorView->buildUp();
|
||||
clearPropertyItemSelection();
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
void PropertyView::showEvent(QShowEvent *ev) {
|
||||
this->attachSelection();
|
||||
this->timer->start(100);
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void PropertyView::clearPropertyItemSelection() {
|
||||
QModelIndex index;
|
||||
propertyEditorData->clearSelection();
|
||||
propertyEditorData->setCurrentIndex(index);
|
||||
propertyEditorView->clearSelection();
|
||||
propertyEditorView->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void PropertyView::slotRollback() {
|
||||
// PropertyItemDelegate will setup application active transaction on
|
||||
// entering edit mode, and close active transaction when exit editing. But,
|
||||
// when the user clicks undo/redo button while editing some property, the
|
||||
// current active transaction will be closed by design, which cause further
|
||||
// editing to be not recorded. Hence, we force unselect any property item on
|
||||
// undo/redo
|
||||
clearPropertyItemSelection();
|
||||
}
|
||||
|
||||
void PropertyView::slotChangePropertyData(const App::DocumentObject&, const App::Property& prop)
|
||||
@@ -135,32 +211,33 @@ void PropertyView::slotChangePropertyView(const Gui::ViewProvider&, const App::P
|
||||
propertyEditorView->updateProperty(prop);
|
||||
}
|
||||
|
||||
bool PropertyView::isPropertyHidden(const App::Property *prop) {
|
||||
return prop && !showAll() &&
|
||||
((prop->getType() & App::Prop_Hidden) || prop->testStatus(App::Property::Hidden));
|
||||
}
|
||||
|
||||
void PropertyView::slotAppendDynamicProperty(const App::Property& prop)
|
||||
{
|
||||
App::PropertyContainer* parent = prop.getContainer();
|
||||
if (parent->isHidden(&prop))
|
||||
if (isPropertyHidden(&prop))
|
||||
return;
|
||||
|
||||
if (parent->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
propertyEditorData->appendProperty(prop);
|
||||
}
|
||||
else if (parent->isDerivedFrom(Gui::ViewProvider::getClassTypeId())) {
|
||||
propertyEditorView->appendProperty(prop);
|
||||
if (propertyEditorData->appendProperty(prop)
|
||||
|| propertyEditorView->appendProperty(prop))
|
||||
{
|
||||
timer->start(100);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyView::slotRemoveDynamicProperty(const App::Property& prop)
|
||||
{
|
||||
App::PropertyContainer* parent = prop.getContainer();
|
||||
if (parent && parent->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
if(propertyEditorData->propOwners.count(parent))
|
||||
propertyEditorData->removeProperty(prop);
|
||||
}
|
||||
else if (parent && parent->isDerivedFrom(Gui::ViewProvider::getClassTypeId())) {
|
||||
else if(propertyEditorView->propOwners.count(parent))
|
||||
propertyEditorView->removeProperty(prop);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyView::slotChangePropertyEditor(const App::Property& prop)
|
||||
void PropertyView::slotChangePropertyEditor(const App::Document &, const App::Property& prop)
|
||||
{
|
||||
App::PropertyContainer* parent = prop.getContainer();
|
||||
if (parent && parent->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
@@ -171,25 +248,27 @@ void PropertyView::slotChangePropertyEditor(const App::Property& prop)
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyView::slotDeleteDocument(const Gui::Document &doc) {
|
||||
if(propertyEditorData->propOwners.count(doc.getDocument())) {
|
||||
propertyEditorView->buildUp();
|
||||
propertyEditorData->buildUp();
|
||||
clearPropertyItemSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyView::slotActiveDocument(const Gui::Document &doc)
|
||||
{
|
||||
// allow to disable the auto-deactivation
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
bool enableEditor = hGrp->GetBool("EnablePropertyViewForInactiveDocument", false);
|
||||
if (enableEditor) {
|
||||
checkEnable(doc.getDocument()->getName());
|
||||
}
|
||||
|
||||
void PropertyView::checkEnable(const char *doc) {
|
||||
if(ViewParams::instance()->getEnablePropertyViewForInactiveDocument()) {
|
||||
setEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if at least one selected object is part of the active document
|
||||
std::vector<SelectionSingleton::SelObj> array = Gui::Selection().getCompleteSelection();
|
||||
for (std::vector<SelectionSingleton::SelObj>::const_iterator it = array.begin(); it != array.end(); ++it) {
|
||||
if (Gui::Application::Instance->getDocument(it->pDoc) == &doc) {
|
||||
enableEditor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setEnabled(enableEditor || array.empty());
|
||||
setEnabled(!Selection().hasSelection()
|
||||
|| Selection().hasSelection(doc,false));
|
||||
}
|
||||
|
||||
struct PropertyView::PropInfo
|
||||
@@ -217,53 +296,119 @@ void PropertyView::onSelectionChanged(const SelectionChanges& msg)
|
||||
msg.Type != SelectionChanges::ClrSelection)
|
||||
return;
|
||||
|
||||
// allow to disable the auto-deactivation
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
|
||||
bool enableEditor = hGrp->GetBool("EnablePropertyViewForInactiveDocument", false);
|
||||
Gui::Document *activeDoc = Application::Instance->activeDocument();
|
||||
// clear the properties.
|
||||
propertyEditorData->buildUp();
|
||||
propertyEditorView->buildUp();
|
||||
clearPropertyItemSelection();
|
||||
timer->start(100);
|
||||
}
|
||||
|
||||
void PropertyView::onTimer() {
|
||||
|
||||
propertyEditorData->buildUp();
|
||||
propertyEditorView->buildUp();
|
||||
clearPropertyItemSelection();
|
||||
timer->stop();
|
||||
|
||||
if(!Gui::Selection().hasSelection()) {
|
||||
auto gdoc = TreeWidget::selectedDocument();
|
||||
if(!gdoc || !gdoc->getDocument())
|
||||
return;
|
||||
|
||||
PropertyModel::PropertyList docProps;
|
||||
|
||||
auto doc = gdoc->getDocument();
|
||||
std::vector<App::Property*> props;
|
||||
doc->getPropertyList(props);
|
||||
for(auto prop : props)
|
||||
docProps.emplace_back(prop->getName(),
|
||||
std::vector<App::Property*>(1,prop));
|
||||
propertyEditorData->buildUp(std::move(docProps));
|
||||
tabs->setCurrentIndex(1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<App::DocumentObject *> objSet;
|
||||
|
||||
// group the properties by <name,id>
|
||||
std::vector<PropInfo> propDataMap;
|
||||
std::vector<PropInfo> propViewMap;
|
||||
std::vector<SelectionSingleton::SelObj> array = Gui::Selection().getCompleteSelection();
|
||||
for (std::vector<SelectionSingleton::SelObj>::const_iterator it = array.begin(); it != array.end(); ++it) {
|
||||
App::DocumentObject *ob=0;
|
||||
ViewProvider *vp=0;
|
||||
bool checkLink = true;
|
||||
ViewProviderDocumentObject *vpLast = 0;
|
||||
const auto &array = Gui::Selection().getCompleteSelection(false);
|
||||
for(auto &sel : array) {
|
||||
if(!sel.pObject) continue;
|
||||
App::DocumentObject *parent = 0;
|
||||
App::DocumentObject *ob = sel.pObject->resolve(sel.SubName,&parent);
|
||||
if(!ob) continue;
|
||||
|
||||
// App::Link should be able to handle special case below now, besides, the new
|
||||
// support of plain group in App::Link breaks because of the code below
|
||||
#if 0
|
||||
if(parent) {
|
||||
auto parentVp = Application::Instance->getViewProvider(parent);
|
||||
if(parentVp) {
|
||||
// For special case where the SubName reference can resolve to
|
||||
// a non-child object (e.g. link array element), the tree view
|
||||
// will select the parent instead. So we shall show the
|
||||
// property of the parent as well.
|
||||
bool found = false;
|
||||
for(auto child : parentVp->claimChildren()) {
|
||||
if(ob == child) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
ob = parent;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Do not process an object more than once
|
||||
if(!objSet.insert(ob).second)
|
||||
continue;
|
||||
|
||||
std::vector<App::Property*> dataList;
|
||||
std::map<std::string, App::Property*> viewList;
|
||||
if ((*it).pObject) {
|
||||
(*it).pObject->getPropertyList(dataList);
|
||||
ob = (*it).pObject;
|
||||
|
||||
// get also the properties of the associated view provider
|
||||
Gui::Document* doc = Gui::Application::Instance->getDocument(it->pDoc);
|
||||
vp = doc->getViewProvider((*it).pObject);
|
||||
if(!vp) continue;
|
||||
// get the properties as map here because it doesn't matter to have them sorted alphabetically
|
||||
vp->getPropertyMap(viewList);
|
||||
if (activeDoc == doc) {
|
||||
enableEditor = true;
|
||||
}
|
||||
auto vp = Application::Instance->getViewProvider(ob);
|
||||
if(!vp) {
|
||||
checkLink = false;
|
||||
ob->getPropertyList(dataList);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
|
||||
auto cvp = static_cast<ViewProviderDocumentObject*>(vp);
|
||||
if(vpLast && cvp!=vpLast)
|
||||
checkLink = false;
|
||||
vpLast = cvp;
|
||||
}
|
||||
|
||||
ob->getPropertyList(dataList);
|
||||
|
||||
// get the properties as map here because it doesn't matter to have them sorted alphabetically
|
||||
vp->getPropertyMap(viewList);
|
||||
|
||||
// store the properties with <name,id> as key in a map
|
||||
std::vector<App::Property*>::iterator pt;
|
||||
if (ob) {
|
||||
for (pt = dataList.begin(); pt != dataList.end(); ++pt) {
|
||||
if (isPropertyHidden(*pt))
|
||||
continue;
|
||||
|
||||
PropInfo nameType;
|
||||
nameType.propName = ob->getPropertyName(*pt);
|
||||
nameType.propName = (*pt)->getName();
|
||||
nameType.propId = (*pt)->getTypeId().getKey();
|
||||
|
||||
if (!ob->isHidden(*pt)) {
|
||||
std::vector<PropInfo>::iterator pi = std::find_if(propDataMap.begin(), propDataMap.end(), PropFind(nameType));
|
||||
if (pi != propDataMap.end()) {
|
||||
pi->propList.push_back(*pt);
|
||||
}
|
||||
else {
|
||||
nameType.propList.push_back(*pt);
|
||||
propDataMap.push_back(nameType);
|
||||
}
|
||||
std::vector<PropInfo>::iterator pi = std::find_if(propDataMap.begin(), propDataMap.end(), PropFind(nameType));
|
||||
if (pi != propDataMap.end()) {
|
||||
pi->propList.push_back(*pt);
|
||||
}
|
||||
else {
|
||||
nameType.propList.push_back(*pt);
|
||||
propDataMap.push_back(nameType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,19 +416,20 @@ void PropertyView::onSelectionChanged(const SelectionChanges& msg)
|
||||
if (vp) {
|
||||
std::map<std::string, App::Property*>::iterator pt;
|
||||
for (pt = viewList.begin(); pt != viewList.end(); ++pt) {
|
||||
if (isPropertyHidden(pt->second))
|
||||
continue;
|
||||
|
||||
PropInfo nameType;
|
||||
nameType.propName = pt->first;
|
||||
nameType.propId = pt->second->getTypeId().getKey();
|
||||
|
||||
if (!vp->isHidden(pt->second)) {
|
||||
std::vector<PropInfo>::iterator pi = std::find_if(propViewMap.begin(), propViewMap.end(), PropFind(nameType));
|
||||
if (pi != propViewMap.end()) {
|
||||
pi->propList.push_back(pt->second);
|
||||
}
|
||||
else {
|
||||
nameType.propList.push_back(pt->second);
|
||||
propViewMap.push_back(nameType);
|
||||
}
|
||||
std::vector<PropInfo>::iterator pi = std::find_if(propViewMap.begin(), propViewMap.end(), PropFind(nameType));
|
||||
if (pi != propViewMap.end()) {
|
||||
pi->propList.push_back(pt->second);
|
||||
}
|
||||
else {
|
||||
nameType.propList.push_back(pt->second);
|
||||
propViewMap.push_back(nameType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,32 +440,69 @@ void PropertyView::onSelectionChanged(const SelectionChanges& msg)
|
||||
// name and id
|
||||
std::vector<PropInfo>::const_iterator it;
|
||||
PropertyModel::PropertyList dataProps;
|
||||
for (it = propDataMap.begin(); it != propDataMap.end(); ++it) {
|
||||
if (it->propList.size() == array.size()) {
|
||||
dataProps.push_back(std::make_pair(it->propName, it->propList));
|
||||
}
|
||||
}
|
||||
propertyEditorData->buildUp(dataProps);
|
||||
|
||||
PropertyModel::PropertyList viewProps;
|
||||
for (it = propViewMap.begin(); it != propViewMap.end(); ++it) {
|
||||
if (it->propList.size() == array.size()) {
|
||||
viewProps.push_back(std::make_pair(it->propName, it->propList));
|
||||
|
||||
if(checkLink && vpLast) {
|
||||
// In case the only selected object is a link, insert the link's own
|
||||
// property before the linked object
|
||||
App::DocumentObject *obj = vpLast->getObject();
|
||||
auto linked = obj;
|
||||
if(obj && obj->canLinkProperties() && (linked=obj->getLinkedObject(true))!=obj && linked) {
|
||||
std::vector<App::Property*> dataList;
|
||||
std::map<std::string, App::Property*> propMap;
|
||||
obj->getPropertyMap(propMap);
|
||||
linked->getPropertyList(dataList);
|
||||
for(auto prop : dataList) {
|
||||
if(isPropertyHidden(prop))
|
||||
continue;
|
||||
std::string name(prop->getName());
|
||||
auto it = propMap.find(name);
|
||||
if(it!=propMap.end() && !isPropertyHidden(it->second))
|
||||
continue;
|
||||
std::vector<App::Property*> v(1,prop);
|
||||
dataProps.emplace_back(name+"*", v);
|
||||
}
|
||||
auto vpLinked = Application::Instance->getViewProvider(linked);
|
||||
if(vpLinked) {
|
||||
propMap.clear();
|
||||
vpLast->getPropertyMap(propMap);
|
||||
dataList.clear();
|
||||
vpLinked->getPropertyList(dataList);
|
||||
for(auto prop : dataList) {
|
||||
if(isPropertyHidden(prop))
|
||||
continue;
|
||||
std::string name(prop->getName());
|
||||
auto it = propMap.find(name);
|
||||
if(it!=propMap.end() && !isPropertyHidden(it->second))
|
||||
continue;
|
||||
std::vector<App::Property*> v(1,prop);
|
||||
viewProps.emplace_back(name+"*", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
propertyEditorView->buildUp(viewProps);
|
||||
|
||||
for (it = propDataMap.begin(); it != propDataMap.end(); ++it) {
|
||||
if (it->propList.size() == array.size())
|
||||
dataProps.push_back(std::make_pair(it->propName, it->propList));
|
||||
}
|
||||
|
||||
propertyEditorData->buildUp(std::move(dataProps));
|
||||
|
||||
for (it = propViewMap.begin(); it != propViewMap.end(); ++it) {
|
||||
if (it->propList.size() == array.size())
|
||||
viewProps.push_back(std::make_pair(it->propName, it->propList));
|
||||
}
|
||||
|
||||
propertyEditorView->buildUp(std::move(viewProps));
|
||||
|
||||
// make sure the editors are enabled/disabled properly
|
||||
setEnabled(enableEditor || array.empty());
|
||||
checkEnable();
|
||||
}
|
||||
|
||||
void PropertyView::tabChanged(int index)
|
||||
{
|
||||
ParameterGrp::handle hGrp = App::GetApplication().GetUserParameter().
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("PropertyView");
|
||||
if (hGrp) {
|
||||
hGrp->SetInt("LastTabIndex", index);
|
||||
}
|
||||
_GetParam()->SetInt("LastTabIndex",index);
|
||||
}
|
||||
|
||||
void PropertyView::changeEvent(QEvent *e)
|
||||
|
||||
@@ -64,13 +64,20 @@ public:
|
||||
|
||||
Gui::PropertyEditor::PropertyEditor* propertyEditorView;
|
||||
Gui::PropertyEditor::PropertyEditor* propertyEditorData;
|
||||
void clearPropertyItemSelection();
|
||||
static bool showAll();
|
||||
static void setShowAll(bool);
|
||||
static bool isPropertyHidden(const App::Property *);
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Stores a preference for the last tab selected
|
||||
void tabChanged(int index);
|
||||
void onTimer();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e);
|
||||
void showEvent(QShowEvent *) override;
|
||||
void hideEvent(QHideEvent *) override;
|
||||
|
||||
private:
|
||||
void onSelectionChanged(const SelectionChanges& msg);
|
||||
@@ -78,8 +85,12 @@ private:
|
||||
void slotChangePropertyView(const Gui::ViewProvider&, const App::Property&);
|
||||
void slotAppendDynamicProperty(const App::Property&);
|
||||
void slotRemoveDynamicProperty(const App::Property&);
|
||||
void slotChangePropertyEditor(const App::Property&);
|
||||
void slotChangePropertyEditor(const App::Document&, const App::Property&);
|
||||
void slotRollback();
|
||||
void slotActiveDocument(const Gui::Document&);
|
||||
void slotDeleteDocument(const Gui::Document&);
|
||||
|
||||
void checkEnable(const char *doc = 0);
|
||||
|
||||
private:
|
||||
struct PropInfo;
|
||||
@@ -90,8 +101,12 @@ private:
|
||||
Connection connectPropAppend;
|
||||
Connection connectPropRemove;
|
||||
Connection connectPropChange;
|
||||
Connection connectUndoDocument;
|
||||
Connection connectRedoDocument;
|
||||
Connection connectActiveDoc;
|
||||
Connection connectDelDocument;
|
||||
QTabWidget* tabs;
|
||||
QTimer* timer;
|
||||
};
|
||||
|
||||
namespace DockWnd {
|
||||
|
||||
@@ -38,18 +38,26 @@
|
||||
# include <QTextBlock>
|
||||
# include <QTimer>
|
||||
# include <QToolTip>
|
||||
# include <QDebug>
|
||||
#endif
|
||||
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Exception.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <App/Expression.h>
|
||||
|
||||
#include "Command.h"
|
||||
#include "Widgets.h"
|
||||
#include "Application.h"
|
||||
#include "Action.h"
|
||||
#include "PrefWidgets.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "DlgExpressionInput.h"
|
||||
#include "QuantitySpinBox_p.h"
|
||||
|
||||
using namespace Gui;
|
||||
using namespace App;
|
||||
using namespace Base;
|
||||
|
||||
/**
|
||||
* Constructs an empty command view with parent \a parent.
|
||||
@@ -1403,4 +1411,173 @@ void LabelEditor::setInputType(InputType t)
|
||||
this->type = t;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
ExpLineEdit::ExpLineEdit(QWidget* parent, bool expressionOnly)
|
||||
: QLineEdit(parent), autoClose(expressionOnly)
|
||||
{
|
||||
defaultPalette = palette();
|
||||
|
||||
/* Icon for f(x) */
|
||||
QFontMetrics fm(font());
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
|
||||
iconHeight = fm.height() - frameWidth;
|
||||
iconLabel = new ExpressionLabel(this);
|
||||
iconLabel->setCursor(Qt::ArrowCursor);
|
||||
QPixmap pixmap = getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight));
|
||||
iconLabel->setPixmap(pixmap);
|
||||
iconLabel->setStyleSheet(QString::fromLatin1("QLabel { border: none; padding: 0px; padding-top: %2px; width: %1px; height: %1px }").arg(iconHeight).arg(frameWidth/2));
|
||||
iconLabel->hide();
|
||||
setStyleSheet(QString::fromLatin1("QLineEdit { padding-right: %1px } ").arg(iconHeight+frameWidth));
|
||||
|
||||
QObject::connect(iconLabel, SIGNAL(clicked()), this, SLOT(openFormulaDialog()));
|
||||
if(expressionOnly)
|
||||
QMetaObject::invokeMethod(this, "openFormulaDialog", Qt::QueuedConnection, QGenericReturnArgument());
|
||||
}
|
||||
|
||||
bool ExpLineEdit::apply(const std::string& propName) {
|
||||
|
||||
if (!ExpressionBinding::apply(propName)) {
|
||||
QString val = QString::fromUtf8(Base::Interpreter().strToPython(text().toUtf8()).c_str());
|
||||
Gui::Command::doCommand(Gui::Command::Doc, "%s = \"%s\"", propName.c_str(), val.constData());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExpLineEdit::bind(const ObjectIdentifier& _path) {
|
||||
|
||||
ExpressionBinding::bind(_path);
|
||||
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
|
||||
setStyleSheet(QString::fromLatin1("QLineEdit { padding-right: %1px } ").arg(iconLabel->sizeHint().width() + frameWidth + 1));
|
||||
|
||||
iconLabel->show();
|
||||
}
|
||||
|
||||
void ExpLineEdit::setExpression(boost::shared_ptr<Expression> expr)
|
||||
{
|
||||
Q_ASSERT(isBound());
|
||||
|
||||
try {
|
||||
ExpressionBinding::setExpression(expr);
|
||||
}
|
||||
catch (const Base::Exception & e) {
|
||||
setReadOnly(true);
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Active, QPalette::Text, Qt::red);
|
||||
setPalette(p);
|
||||
iconLabel->setToolTip(QString::fromLatin1(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void ExpLineEdit::onChange() {
|
||||
|
||||
if (getExpression()) {
|
||||
std::unique_ptr<Expression> result(getExpression()->eval());
|
||||
if(result->isDerivedFrom(App::StringExpression::getClassTypeId()))
|
||||
setText(QString::fromUtf8(static_cast<App::StringExpression*>(
|
||||
result.get())->getText().c_str()));
|
||||
else
|
||||
setText(QString::fromUtf8(result->toString().c_str()));
|
||||
setReadOnly(true);
|
||||
iconLabel->setPixmap(getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight)));
|
||||
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Text, Qt::lightGray);
|
||||
setPalette(p);
|
||||
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
|
||||
}
|
||||
else {
|
||||
setReadOnly(false);
|
||||
iconLabel->setPixmap(getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight)));
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
|
||||
setPalette(p);
|
||||
|
||||
}
|
||||
iconLabel->setToolTip(QString());
|
||||
}
|
||||
|
||||
void ExpLineEdit::resizeEvent(QResizeEvent * event)
|
||||
{
|
||||
QLineEdit::resizeEvent(event);
|
||||
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_SpinBoxFrameWidth);
|
||||
|
||||
QSize sz = iconLabel->sizeHint();
|
||||
iconLabel->move(rect().right() - frameWidth - sz.width(), 0);
|
||||
|
||||
try {
|
||||
if (isBound() && getExpression()) {
|
||||
setReadOnly(true);
|
||||
QPixmap pixmap = getIcon(":/icons/bound-expression.svg", QSize(iconHeight, iconHeight));
|
||||
iconLabel->setPixmap(pixmap);
|
||||
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Text, Qt::lightGray);
|
||||
setPalette(p);
|
||||
setToolTip(Base::Tools::fromStdString(getExpression()->toString()));
|
||||
}
|
||||
else {
|
||||
setReadOnly(false);
|
||||
QPixmap pixmap = getIcon(":/icons/bound-expression-unset.svg", QSize(iconHeight, iconHeight));
|
||||
iconLabel->setPixmap(pixmap);
|
||||
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Active, QPalette::Text, defaultPalette.color(QPalette::Text));
|
||||
setPalette(p);
|
||||
|
||||
}
|
||||
iconLabel->setToolTip(QString());
|
||||
}
|
||||
catch (const Base::Exception & e) {
|
||||
setReadOnly(true);
|
||||
QPalette p(palette());
|
||||
p.setColor(QPalette::Active, QPalette::Text, Qt::red);
|
||||
setPalette(p);
|
||||
iconLabel->setToolTip(QString::fromLatin1(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void ExpLineEdit::openFormulaDialog()
|
||||
{
|
||||
Q_ASSERT(isBound());
|
||||
|
||||
Gui::Dialog::DlgExpressionInput* box = new Gui::Dialog::DlgExpressionInput(
|
||||
getPath(), getExpression(),Unit(), this);
|
||||
connect(box, SIGNAL(finished(int)), this, SLOT(finishFormulaDialog()));
|
||||
box->show();
|
||||
|
||||
QPoint pos = mapToGlobal(QPoint(0,0));
|
||||
box->move(pos-box->expressionPosition());
|
||||
box->setExpressionInputSize(width(), height());
|
||||
}
|
||||
|
||||
void ExpLineEdit::finishFormulaDialog()
|
||||
{
|
||||
Gui::Dialog::DlgExpressionInput* box = qobject_cast<Gui::Dialog::DlgExpressionInput*>(sender());
|
||||
if (!box) {
|
||||
qWarning() << "Sender is not a Gui::Dialog::DlgExpressionInput";
|
||||
return;
|
||||
}
|
||||
|
||||
if (box->result() == QDialog::Accepted)
|
||||
setExpression(box->getExpression());
|
||||
else if (box->discardedFormula())
|
||||
setExpression(boost::shared_ptr<Expression>());
|
||||
|
||||
box->deleteLater();
|
||||
|
||||
if(autoClose)
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
void ExpLineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (!hasExpression())
|
||||
QLineEdit::keyPressEvent(event);
|
||||
}
|
||||
|
||||
#include "moc_Widgets.cpp"
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <QBasicTimer>
|
||||
#include <QTime>
|
||||
#include <QToolButton>
|
||||
#include <QModelIndex>
|
||||
#include "ExpressionBinding.h"
|
||||
|
||||
namespace Gui {
|
||||
class PrefCheckBox;
|
||||
@@ -449,6 +451,33 @@ private:
|
||||
QPushButton *button;
|
||||
};
|
||||
|
||||
/**
|
||||
* The ExpLineEdit class provides a lineedit that support expressing binding.
|
||||
* \author realthunder
|
||||
*/
|
||||
class GuiExport ExpLineEdit : public QLineEdit, public ExpressionBinding
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExpLineEdit ( QWidget * parent=0, bool expressionOnly=false );
|
||||
|
||||
void setExpression(boost::shared_ptr<App::Expression> expr);
|
||||
void bind(const App::ObjectIdentifier &_path);
|
||||
bool apply(const std::string &propName);
|
||||
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private Q_SLOTS:
|
||||
void finishFormulaDialog();
|
||||
void openFormulaDialog();
|
||||
virtual void onChange();
|
||||
|
||||
private:
|
||||
bool autoClose;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_WIDGETS_H
|
||||
|
||||
@@ -26,24 +26,38 @@
|
||||
#ifndef _PreComp_
|
||||
# include <QApplication>
|
||||
# include <QPainter>
|
||||
# include <QMenu>
|
||||
# include <QDebug>
|
||||
# include <QDialog>
|
||||
# include <QMessageBox>
|
||||
# include <QCheckBox>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include "MainWindow.h"
|
||||
#include "DlgAddProperty.h"
|
||||
#include "PropertyEditor.h"
|
||||
#include "PropertyItemDelegate.h"
|
||||
#include "PropertyModel.h"
|
||||
#include "PropertyView.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("PropertyView",true,true);
|
||||
|
||||
using namespace Gui::PropertyEditor;
|
||||
|
||||
PropertyEditor::PropertyEditor(QWidget *parent)
|
||||
: QTreeView(parent), autoupdate(false), committing(false), delaybuild(false)
|
||||
: QTreeView(parent), autoupdate(false), committing(false), delaybuild(false), binding(false)
|
||||
{
|
||||
propertyModel = new PropertyModel(this);
|
||||
setModel(propertyModel);
|
||||
|
||||
PropertyItemDelegate* delegate = new PropertyItemDelegate(this);
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
delegate = new PropertyItemDelegate(this);
|
||||
delegate->setItemEditorFactory(new PropertyItemEditorFactory);
|
||||
setItemDelegate(delegate);
|
||||
|
||||
@@ -54,8 +68,10 @@ PropertyEditor::PropertyEditor(QWidget *parent)
|
||||
this->background = opt.palette.dark();
|
||||
this->groupColor = opt.palette.color(QPalette::BrightText);
|
||||
|
||||
connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(onItemActivated(QModelIndex)));
|
||||
connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(onItemActivated(QModelIndex)));
|
||||
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
connect(this, SIGNAL(activated(const QModelIndex &)), this, SLOT(onItemActivated(const QModelIndex &)));
|
||||
connect(this, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onItemActivated(const QModelIndex &)));
|
||||
}
|
||||
|
||||
PropertyEditor::~PropertyEditor()
|
||||
@@ -137,14 +153,19 @@ void PropertyEditor::commitData (QWidget * editor)
|
||||
|
||||
void PropertyEditor::editorDestroyed (QObject * editor)
|
||||
{
|
||||
delegate->editorClosed(0,QAbstractItemDelegate::NoHint);
|
||||
QTreeView::editorDestroyed(editor);
|
||||
}
|
||||
|
||||
void PropertyEditor::currentChanged ( const QModelIndex & current, const QModelIndex & previous )
|
||||
{
|
||||
FC_LOG("current changed " << current.row()<<","<<current.column()
|
||||
<< " " << previous.row()<<","<<previous.column());
|
||||
|
||||
QTreeView::currentChanged(current, previous);
|
||||
if (previous.isValid())
|
||||
closePersistentEditor(model()->buddy(previous));
|
||||
|
||||
// if (previous.isValid())
|
||||
// closePersistentEditor(model()->buddy(previous));
|
||||
|
||||
// DO NOT activate editor here, use onItemActivate() which response to
|
||||
// signals of activated and clicked.
|
||||
@@ -153,16 +174,62 @@ void PropertyEditor::currentChanged ( const QModelIndex & current, const QModelI
|
||||
// openPersistentEditor(model()->buddy(current));
|
||||
}
|
||||
|
||||
void PropertyEditor::setupTransaction(const QModelIndex &index) {
|
||||
if(!autoupdate)
|
||||
return;
|
||||
if(this->state()!=EditingState) {
|
||||
FC_LOG("editor not editing");
|
||||
return;
|
||||
}
|
||||
auto &app = App::GetApplication();
|
||||
if(app.getActiveTransaction()) {
|
||||
FC_LOG("editor already transacting " << app.getActiveTransaction());
|
||||
return;
|
||||
}
|
||||
PropertyItem* item = static_cast<PropertyItem*>(index.internalPointer());
|
||||
auto items = item->getPropertyData();
|
||||
for(auto propItem=item->parent();items.empty() && propItem;propItem=propItem->parent())
|
||||
items = propItem->getPropertyData();
|
||||
if(items.empty()) {
|
||||
FC_LOG("editor no item");
|
||||
return;
|
||||
}
|
||||
auto prop = items[0];
|
||||
auto parent = prop->getContainer();
|
||||
auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(parent);
|
||||
if(!obj || !obj->getDocument()) {
|
||||
FC_LOG("invalid object");
|
||||
return;
|
||||
}
|
||||
if(obj->getDocument()->hasPendingTransaction()) {
|
||||
FC_LOG("pending transaction");
|
||||
return;
|
||||
}
|
||||
std::ostringstream str;
|
||||
str << tr("Edit").toUtf8().constData() << ' ';
|
||||
for(auto prop : items) {
|
||||
if(prop->getContainer()!=obj) {
|
||||
obj = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(obj && obj->getNameInDocument())
|
||||
str << obj->getNameInDocument() << '.';
|
||||
else
|
||||
str << tr("property").toUtf8().constData() << ' ';
|
||||
str << prop->getName();
|
||||
if(items.size()>1)
|
||||
str << "...";
|
||||
app.setActiveTransaction(str.str().c_str());
|
||||
FC_LOG("editor transaction " << app.getActiveTransaction());
|
||||
}
|
||||
|
||||
void PropertyEditor::onItemActivated ( const QModelIndex & index )
|
||||
{
|
||||
if (autoupdate) {
|
||||
PropertyItem* property = static_cast<PropertyItem*>(index.internalPointer());
|
||||
QString edit = tr("Edit %1").arg(property->propertyName());
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (doc)
|
||||
doc->openTransaction(edit.toUtf8());
|
||||
}
|
||||
openPersistentEditor(model()->buddy(index));
|
||||
if(index.column() != 1)
|
||||
return;
|
||||
edit(model()->buddy(index),AllEditTriggers,0);
|
||||
setupTransaction(index);
|
||||
}
|
||||
|
||||
void PropertyEditor::closeEditor (QWidget * editor, QAbstractItemDelegate::EndEditHint hint)
|
||||
@@ -171,35 +238,38 @@ void PropertyEditor::closeEditor (QWidget * editor, QAbstractItemDelegate::EndEd
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (doc) {
|
||||
if (!doc->isTransactionEmpty()) {
|
||||
doc->commitTransaction();
|
||||
// Between opening and committing a transaction a recompute
|
||||
// could already have been done
|
||||
if (doc->isTouched())
|
||||
doc->recompute();
|
||||
}
|
||||
else {
|
||||
doc->abortTransaction();
|
||||
}
|
||||
}
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
}
|
||||
|
||||
QModelIndex indexSaved = currentIndex();
|
||||
FC_LOG("index saved " << indexSaved.row() << ", " << indexSaved.column());
|
||||
|
||||
QTreeView::closeEditor(editor, hint);
|
||||
|
||||
// If after closing the editor this widget is still in editing state
|
||||
// then a new editor must have been created. So, a transaction must be
|
||||
// opened, too.
|
||||
if (autoupdate && this->state() == EditingState) {
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
if (doc) {
|
||||
QString edit;
|
||||
QModelIndex index = currentIndex();
|
||||
if (index.isValid()) {
|
||||
PropertyItem* property = static_cast<PropertyItem*>(index.internalPointer());
|
||||
edit = tr("Edit %1").arg(property->propertyName());
|
||||
}
|
||||
doc->openTransaction(edit.toUtf8());
|
||||
QModelIndex lastIndex;
|
||||
while(this->state()!=EditingState) {
|
||||
QModelIndex index;
|
||||
if (hint == QAbstractItemDelegate::EditNextItem) {
|
||||
index = moveCursor(MoveDown,Qt::NoModifier);
|
||||
} else if(hint == QAbstractItemDelegate::EditPreviousItem) {
|
||||
index = moveCursor(MoveUp,Qt::NoModifier);
|
||||
} else
|
||||
break;
|
||||
if(!index.isValid() || index==lastIndex) {
|
||||
setCurrentIndex(indexSaved);
|
||||
break;
|
||||
}
|
||||
lastIndex = index;
|
||||
setCurrentIndex(index);
|
||||
edit(index,AllEditTriggers,0);
|
||||
}
|
||||
setupTransaction(currentIndex());
|
||||
}
|
||||
|
||||
void PropertyEditor::reset()
|
||||
@@ -237,7 +307,7 @@ void PropertyEditor::drawBranches(QPainter *painter, const QRect &rect, const QM
|
||||
//painter->setPen(savedPen);
|
||||
}
|
||||
|
||||
void PropertyEditor::buildUp(const PropertyModel::PropertyList& props)
|
||||
void PropertyEditor::buildUp(PropertyModel::PropertyList &&props)
|
||||
{
|
||||
if (committing) {
|
||||
Base::Console().Warning("While committing the data to the property the selection has changed.\n");
|
||||
@@ -255,7 +325,12 @@ void PropertyEditor::buildUp(const PropertyModel::PropertyList& props)
|
||||
this->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
propList = props;
|
||||
propList = std::move(props);
|
||||
propOwners.clear();
|
||||
for(auto &v : propList) {
|
||||
for(auto prop : v.second)
|
||||
propOwners.insert(prop->getContainer());
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyEditor::updateProperty(const App::Property& prop)
|
||||
@@ -271,7 +346,7 @@ void PropertyEditor::setEditorMode(const QModelIndex & parent, int start, int en
|
||||
for (int i=start; i<=end; i++) {
|
||||
QModelIndex item = propertyModel->index(i, column, parent);
|
||||
PropertyItem* propItem = static_cast<PropertyItem*>(item.internalPointer());
|
||||
if (propItem && propItem->testStatus(App::Property::Hidden)) {
|
||||
if (!PropertyView::showAll() && propItem && propItem->testStatus(App::Property::Hidden)) {
|
||||
setRowHidden (i, parent, true);
|
||||
}
|
||||
if (propItem && propItem->isSeparator()) {
|
||||
@@ -285,10 +360,10 @@ void PropertyEditor::updateEditorMode(const App::Property& prop)
|
||||
{
|
||||
// check if the parent object is selected
|
||||
std::string editor = prop.getEditorName();
|
||||
if (editor.empty())
|
||||
if (!PropertyView::showAll() && editor.empty())
|
||||
return;
|
||||
|
||||
bool hidden = prop.testStatus(App::Property::Hidden);
|
||||
bool hidden = PropertyView::isPropertyHidden(&prop);
|
||||
bool readOnly = prop.testStatus(App::Property::ReadOnly);
|
||||
|
||||
int column = 1;
|
||||
@@ -324,34 +399,8 @@ void PropertyEditor::updateItemEditor(bool enable, int column, const QModelIndex
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyEditor::appendProperty(const App::Property& prop)
|
||||
{
|
||||
// check if the parent object is selected
|
||||
std::string editor = prop.getEditorName();
|
||||
if (editor.empty())
|
||||
return;
|
||||
App::PropertyContainer* parent = prop.getContainer();
|
||||
std::string context = prop.getName();
|
||||
|
||||
bool canAddProperty = (!propList.empty());
|
||||
for (PropertyModel::PropertyList::iterator it = propList.begin(); it != propList.end(); ++it) {
|
||||
if (it->second.empty() || it->second.size() > 1) {
|
||||
canAddProperty = false;
|
||||
break;
|
||||
}
|
||||
else if (it->second.front()->getContainer() != parent) {
|
||||
canAddProperty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (canAddProperty) {
|
||||
std::vector<App::Property*> list;
|
||||
list.push_back(const_cast<App::Property*>(&prop));
|
||||
std::pair< std::string, std::vector<App::Property*> > pair = std::make_pair(context, list);
|
||||
propList.push_back(pair);
|
||||
propertyModel->appendProperty(prop);
|
||||
}
|
||||
bool PropertyEditor::appendProperty(const App::Property& prop) {
|
||||
return !!propOwners.count(prop.getContainer());
|
||||
}
|
||||
|
||||
void PropertyEditor::removeProperty(const App::Property& prop)
|
||||
@@ -371,4 +420,166 @@ void PropertyEditor::removeProperty(const App::Property& prop)
|
||||
}
|
||||
}
|
||||
|
||||
enum MenuAction {
|
||||
MA_ShowAll,
|
||||
MA_Expression,
|
||||
MA_RemoveProp,
|
||||
MA_AddProp,
|
||||
MA_Transient,
|
||||
MA_Output,
|
||||
MA_NoRecompute,
|
||||
MA_ReadOnly,
|
||||
MA_Hidden,
|
||||
MA_Touched,
|
||||
MA_EvalOnRestore,
|
||||
};
|
||||
|
||||
void PropertyEditor::contextMenuEvent(QContextMenuEvent *) {
|
||||
QMenu menu;
|
||||
QAction *showAll = menu.addAction(tr("Show all"));
|
||||
showAll->setCheckable(true);
|
||||
showAll->setChecked(PropertyView::showAll());
|
||||
showAll->setData(QVariant(MA_ShowAll));
|
||||
|
||||
auto contextIndex = currentIndex();
|
||||
|
||||
std::unordered_set<App::Property*> props;
|
||||
|
||||
if(PropertyView::showAll()) {
|
||||
for(auto index : selectedIndexes()) {
|
||||
auto item = static_cast<PropertyItem*>(index.internalPointer());
|
||||
if(item->isSeparator())
|
||||
continue;
|
||||
for(auto parent=item;parent;parent=item->parent()) {
|
||||
const auto &ps = parent->getPropertyData();
|
||||
if(ps.size()) {
|
||||
props.insert(ps.begin(),ps.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(props.size())
|
||||
menu.addAction(tr("Add property"))->setData(QVariant(MA_AddProp));
|
||||
|
||||
bool canRemove = !props.empty();
|
||||
unsigned long propType = 0;
|
||||
unsigned long propStatus = 0xffffffff;
|
||||
for(auto prop : props) {
|
||||
propType |= prop->getType();
|
||||
propStatus &= prop->getStatus();
|
||||
if(!prop->testStatus(App::Property::PropDynamic)
|
||||
|| prop->testStatus(App::Property::LockDynamic))
|
||||
{
|
||||
canRemove = false;
|
||||
}
|
||||
}
|
||||
if(canRemove)
|
||||
menu.addAction(tr("Remove property"))->setData(QVariant(MA_RemoveProp));
|
||||
|
||||
if(props.size() == 1) {
|
||||
auto item = static_cast<PropertyItem*>(contextIndex.internalPointer());
|
||||
auto prop = *props.begin();
|
||||
if(item->isBound()
|
||||
&& !prop->isDerivedFrom(App::PropertyExpressionEngine::getClassTypeId())
|
||||
&& !prop->isReadOnly()
|
||||
&& !(prop->getType() & App::Prop_ReadOnly))
|
||||
{
|
||||
contextIndex = propertyModel->buddy(contextIndex);
|
||||
setCurrentIndex(contextIndex);
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Expression..."))->setData(QVariant(MA_Expression));
|
||||
}
|
||||
}
|
||||
|
||||
if(props.size()) {
|
||||
menu.addSeparator();
|
||||
|
||||
QAction *action;
|
||||
QString text;
|
||||
#define _ACTION_SETUP(_name) do {\
|
||||
text = tr(#_name);\
|
||||
action = menu.addAction(text);\
|
||||
action->setData(QVariant(MA_##_name));\
|
||||
action->setCheckable(true);\
|
||||
if(propStatus & (1<<App::Property::_name))\
|
||||
action->setChecked(true);\
|
||||
}while(0)
|
||||
#define ACTION_SETUP(_name) do {\
|
||||
_ACTION_SETUP(_name);\
|
||||
if(propType & App::Prop_##_name) {\
|
||||
action->setText(text + QString::fromLatin1(" *"));\
|
||||
action->setChecked(true);\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
ACTION_SETUP(Hidden);
|
||||
ACTION_SETUP(Output);
|
||||
ACTION_SETUP(NoRecompute);
|
||||
ACTION_SETUP(ReadOnly);
|
||||
ACTION_SETUP(Transient);
|
||||
_ACTION_SETUP(Touched);
|
||||
_ACTION_SETUP(EvalOnRestore);
|
||||
}
|
||||
}
|
||||
|
||||
auto action = menu.exec(QCursor::pos());
|
||||
if(!action)
|
||||
return;
|
||||
|
||||
switch(action->data().toInt()) {
|
||||
case MA_ShowAll:
|
||||
PropertyView::setShowAll(action->isChecked());
|
||||
return;
|
||||
#define ACTION_CHECK(_name) \
|
||||
case MA_##_name:\
|
||||
for(auto prop : props) \
|
||||
prop->setStatus(App::Property::_name,action->isChecked());\
|
||||
break
|
||||
ACTION_CHECK(Transient);
|
||||
ACTION_CHECK(ReadOnly);
|
||||
ACTION_CHECK(Output);
|
||||
ACTION_CHECK(Hidden);
|
||||
ACTION_CHECK(EvalOnRestore);
|
||||
case MA_Touched:
|
||||
for(auto prop : props) {
|
||||
if(action->isChecked())
|
||||
prop->touch();
|
||||
else
|
||||
prop->purgeTouched();
|
||||
}
|
||||
break;
|
||||
case MA_Expression:
|
||||
if(contextIndex == currentIndex()) {
|
||||
closePersistentEditor(contextIndex);
|
||||
Base::FlagToggler<> flag(binding);
|
||||
edit(contextIndex,AllEditTriggers,0);
|
||||
}
|
||||
break;
|
||||
case MA_AddProp: {
|
||||
App::AutoTransaction committer("Add property");
|
||||
std::unordered_set<App::PropertyContainer*> containers;
|
||||
for(auto prop : props)
|
||||
containers.insert(prop->getContainer());
|
||||
Gui::Dialog::DlgAddProperty dlg(
|
||||
Gui::getMainWindow(),std::move(containers));
|
||||
dlg.exec();
|
||||
return;
|
||||
}
|
||||
case MA_RemoveProp: {
|
||||
App::AutoTransaction committer("Remove property");
|
||||
for(auto prop : props) {
|
||||
try {
|
||||
prop->getContainer()->removeDynamicProperty(prop->getName());
|
||||
}catch(Base::Exception &e) {
|
||||
e.ReportException();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_PropertyEditor.cpp"
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
#include <App/DocumentObserver.h>
|
||||
#include "PropertyItem.h"
|
||||
#include "PropertyModel.h"
|
||||
|
||||
@@ -38,8 +40,12 @@ class Property;
|
||||
}
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class PropertyView;
|
||||
|
||||
namespace PropertyEditor {
|
||||
|
||||
class PropertyItemDelegate;
|
||||
class PropertyModel;
|
||||
/*!
|
||||
Put this into the .qss file after Gui--PropertyEditor--PropertyEditor
|
||||
@@ -66,10 +72,10 @@ public:
|
||||
~PropertyEditor();
|
||||
|
||||
/** Builds up the list view with the properties. */
|
||||
void buildUp(const PropertyModel::PropertyList& props);
|
||||
void buildUp(PropertyModel::PropertyList &&props = PropertyModel::PropertyList());
|
||||
void updateProperty(const App::Property&);
|
||||
void updateEditorMode(const App::Property&);
|
||||
void appendProperty(const App::Property&);
|
||||
bool appendProperty(const App::Property&);
|
||||
void removeProperty(const App::Property&);
|
||||
void setAutomaticDocumentUpdate(bool);
|
||||
bool isAutomaticDocumentUpdate(bool) const;
|
||||
@@ -81,7 +87,9 @@ public:
|
||||
QColor groupTextColor() const;
|
||||
void setGroupTextColor(const QColor& c);
|
||||
|
||||
public Q_SLOTS:
|
||||
bool isBinding() const { return binding; }
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onItemActivated(const QModelIndex &index);
|
||||
|
||||
protected:
|
||||
@@ -92,21 +100,29 @@ protected:
|
||||
virtual void rowsInserted (const QModelIndex & parent, int start, int end);
|
||||
virtual void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const;
|
||||
virtual QStyleOptionViewItem viewOptions() const;
|
||||
virtual void contextMenuEvent(QContextMenuEvent *event);
|
||||
virtual bool event(QEvent*);
|
||||
|
||||
private:
|
||||
void setEditorMode(const QModelIndex & parent, int start, int end);
|
||||
void updateItemEditor(bool enable, int column, const QModelIndex& parent);
|
||||
void setupTransaction(const QModelIndex &);
|
||||
|
||||
private:
|
||||
PropertyItemDelegate *delegate;
|
||||
PropertyModel* propertyModel;
|
||||
QStringList selectedProperty;
|
||||
PropertyModel::PropertyList propList;
|
||||
std::unordered_set<App::PropertyContainer*> propOwners;
|
||||
bool autoupdate;
|
||||
bool committing;
|
||||
bool delaybuild;
|
||||
QColor groupColor;
|
||||
QBrush background;
|
||||
|
||||
bool binding;
|
||||
|
||||
friend class Gui::PropertyView;
|
||||
};
|
||||
|
||||
} //namespace PropertyEditor
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <App/PropertyUnits.h>
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/Control.h>
|
||||
#include <Gui/Widgets.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Document.h>
|
||||
#include <Gui/Selection.h>
|
||||
@@ -58,6 +59,7 @@
|
||||
#include <Gui/QuantitySpinBox.h>
|
||||
|
||||
#include "PropertyItem.h"
|
||||
#include "PropertyView.h"
|
||||
#include <Gui/SpinBox.h>
|
||||
|
||||
using namespace Gui::PropertyEditor;
|
||||
@@ -84,10 +86,11 @@ PropertyItem* PropertyItemFactory::createPropertyItem (const char* sName) const
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
Q_DECLARE_METATYPE(Py::Object)
|
||||
|
||||
PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyItem)
|
||||
|
||||
PropertyItem::PropertyItem() : parentItem(0), readonly(false), cleared(false)
|
||||
PropertyItem::PropertyItem() : parentItem(0), readonly(false), cleared(false), linked(false)
|
||||
{
|
||||
precision = Base::UnitsApi::getDecimals();
|
||||
setAutoApply(true);
|
||||
@@ -262,6 +265,18 @@ bool PropertyItem::isReadOnly() const
|
||||
return readonly;
|
||||
}
|
||||
|
||||
void PropertyItem::setLinked(bool l)
|
||||
{
|
||||
linked = l;
|
||||
for (QList<PropertyItem*>::iterator it = childItems.begin(); it != childItems.end(); ++it)
|
||||
(*it)->setLinked(l);
|
||||
}
|
||||
|
||||
bool PropertyItem::isLinked() const
|
||||
{
|
||||
return linked;
|
||||
}
|
||||
|
||||
bool PropertyItem::testStatus(App::Property::Status pos) const
|
||||
{
|
||||
std::vector<App::Property*>::const_iterator it;
|
||||
@@ -301,7 +316,47 @@ QVariant PropertyItem::decoration(const QVariant&) const
|
||||
|
||||
QVariant PropertyItem::toString(const QVariant& prop) const
|
||||
{
|
||||
return prop;
|
||||
if(prop != QVariant() || propertyItems.size()!=1)
|
||||
return prop;
|
||||
Base::PyGILStateLocker lock;
|
||||
Py::Object pyobj(propertyItems[0]->getPyObject(),true);
|
||||
std::ostringstream ss;
|
||||
if(pyobj.isNone())
|
||||
ss << "<None>";
|
||||
else if(pyobj.isSequence()) {
|
||||
ss << '[';
|
||||
Py::Sequence seq(pyobj);
|
||||
bool first = true;
|
||||
size_t i=0;
|
||||
for(i=0;i<2 && i<seq.size(); ++i) {
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << Py::Object(seq[i]).as_string();
|
||||
}
|
||||
if(i<seq.size())
|
||||
ss << "...";
|
||||
ss << ']';
|
||||
}else if(pyobj.isMapping()) {
|
||||
ss << '{';
|
||||
Py::Mapping map(pyobj);
|
||||
bool first = true;
|
||||
auto it = map.begin();
|
||||
for(int i=0;i<2 && it!=map.end(); ++it) {
|
||||
if(first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
const auto &v = *it;
|
||||
ss << Py::Object(v.first).as_string() << ':' << Py::Object(v.second).as_string();
|
||||
}
|
||||
if(it!=map.end())
|
||||
ss << "...";
|
||||
ss << '}';
|
||||
}else
|
||||
ss << pyobj.as_string();
|
||||
return QVariant(QString::fromUtf8(ss.str().c_str()));
|
||||
}
|
||||
|
||||
QVariant PropertyItem::value(const App::Property* /*prop*/) const
|
||||
@@ -316,20 +371,21 @@ void PropertyItem::setValue(const QVariant& /*value*/)
|
||||
QString PropertyItem::pythonIdentifier(const App::Property* prop) const
|
||||
{
|
||||
App::PropertyContainer* parent = prop->getContainer();
|
||||
QString propPrefix = QString::fromLatin1(parent->getPropertyPrefix());
|
||||
if (parent->getTypeId() == App::Document::getClassTypeId()) {
|
||||
App::Document* doc = static_cast<App::Document*>(parent);
|
||||
QString docName = QString::fromLatin1(App::GetApplication().getDocumentName(doc));
|
||||
QString propName = QString::fromLatin1(parent->getPropertyName(prop));
|
||||
return QString::fromLatin1("FreeCAD.getDocument(\"%1\").%2").arg(docName, propName);
|
||||
QString propName = QString::fromLatin1(prop->getName());
|
||||
return QString::fromLatin1("FreeCAD.getDocument(\"%1\").%3%2").arg(docName).arg(propName).arg(propPrefix);
|
||||
}
|
||||
if (parent->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObject*>(parent);
|
||||
App::Document* doc = obj->getDocument();
|
||||
QString docName = QString::fromLatin1(App::GetApplication().getDocumentName(doc));
|
||||
QString objName = QString::fromLatin1(obj->getNameInDocument());
|
||||
QString propName = QString::fromLatin1(parent->getPropertyName(prop));
|
||||
return QString::fromLatin1("FreeCAD.getDocument(\"%1\").getObject(\"%2\").%3")
|
||||
.arg(docName, objName, propName);
|
||||
QString propName = QString::fromLatin1(prop->getName());
|
||||
return QString::fromLatin1("FreeCAD.getDocument(\"%1\").getObject(\"%2\").%4%3")
|
||||
.arg(docName,objName,propName,propPrefix);
|
||||
}
|
||||
auto* vp = dynamic_cast<Gui::ViewProviderDocumentObject*>(parent);
|
||||
if (vp) {
|
||||
@@ -337,9 +393,9 @@ QString PropertyItem::pythonIdentifier(const App::Property* prop) const
|
||||
App::Document* doc = obj->getDocument();
|
||||
QString docName = QString::fromLatin1(App::GetApplication().getDocumentName(doc));
|
||||
QString objName = QString::fromLatin1(obj->getNameInDocument());
|
||||
QString propName = QString::fromLatin1(parent->getPropertyName(prop));
|
||||
return QString::fromLatin1("FreeCADGui.getDocument(\"%1\").getObject(\"%2\").%3")
|
||||
.arg(docName, objName, propName);
|
||||
QString propName = QString::fromLatin1(prop->getName());
|
||||
return QString::fromLatin1("FreeCADGui.getDocument(\"%1\").getObject(\"%2\").%4%3")
|
||||
.arg(docName,objName,propName,propPrefix);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
@@ -358,6 +414,34 @@ QVariant PropertyItem::editorData(QWidget * /*editor*/) const
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QWidget* PropertyItem::createExpressionEditor(QWidget* parent, const QObject* receiver, const char* method) const
|
||||
{
|
||||
if(!isBound())
|
||||
return 0;
|
||||
ExpLineEdit *le = new ExpLineEdit(parent,true);
|
||||
le->setFrame(false);
|
||||
le->setReadOnly(true);
|
||||
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
|
||||
le->bind(getPath());
|
||||
le->setAutoApply(autoApply());
|
||||
return le;
|
||||
}
|
||||
|
||||
void PropertyItem::setExpressionEditorData(QWidget *editor, const QVariant& data) const
|
||||
{
|
||||
QLineEdit *le = qobject_cast<QLineEdit*>(editor);
|
||||
if(le)
|
||||
le->setText(data.toString());
|
||||
}
|
||||
|
||||
QVariant PropertyItem::expressionEditorData(QWidget *editor) const
|
||||
{
|
||||
QLineEdit *le = qobject_cast<QLineEdit*>(editor);
|
||||
if(le)
|
||||
return QVariant(le->text());
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QString PropertyItem::propertyName() const
|
||||
{
|
||||
if (propName.isEmpty())
|
||||
@@ -417,13 +501,32 @@ QVariant PropertyItem::data(int column, int role) const
|
||||
{
|
||||
// property name
|
||||
if (column == 0) {
|
||||
if (role == Qt::BackgroundRole) {
|
||||
if(PropertyView::showAll()
|
||||
&& propertyItems.size() == 1
|
||||
&& propertyItems.front()->testStatus(App::Property::PropDynamic)
|
||||
&& !propertyItems.front()->testStatus(App::Property::LockDynamic))
|
||||
{
|
||||
return QBrush(QColor(0xFF,0xFF,0x99));
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
if (role == Qt::DisplayRole)
|
||||
return displayName();
|
||||
// no properties set
|
||||
if (propertyItems.empty())
|
||||
return QVariant();
|
||||
else if (role == Qt::ToolTipRole)
|
||||
return toolTip(propertyItems[0]);
|
||||
else if (role == Qt::ToolTipRole) {
|
||||
if(!PropertyView::showAll())
|
||||
return toolTip(propertyItems[0]);
|
||||
QString type = QString::fromLatin1("Type: %1").arg(
|
||||
QString::fromLatin1(propertyItems[0]->getTypeId().getName()));
|
||||
QString doc = toolTip(propertyItems[0]).toString();
|
||||
if(doc.size())
|
||||
return type + QLatin1String("\n\n") + doc;
|
||||
return type;
|
||||
} else if (role == Qt::TextColorRole && linked)
|
||||
return QVariant::fromValue(QColor(0,0x80,0));
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
@@ -456,7 +559,11 @@ QVariant PropertyItem::data(int column, int role) const
|
||||
return toString(value(propertyItems[0]));
|
||||
else if (role == Qt::ToolTipRole)
|
||||
return toolTip(propertyItems[0]);
|
||||
else
|
||||
else if( role == Qt::TextColorRole) {
|
||||
if(hasExpression())
|
||||
return QVariant::fromValue(QColor(0,0,255.0));
|
||||
return QVariant();
|
||||
} else
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
@@ -544,20 +651,27 @@ QVariant PropertyStringItem::value(const App::Property* prop) const
|
||||
|
||||
void PropertyStringItem::setValue(const QVariant& value)
|
||||
{
|
||||
if (!value.canConvert(QVariant::String))
|
||||
return;
|
||||
QString val = value.toString();
|
||||
val = QString::fromUtf8(Base::Interpreter().strToPython(val.toUtf8()).c_str());
|
||||
QString data = QString::fromLatin1("\"%1\"").arg(val);
|
||||
setPropertyValue(data);
|
||||
if(!hasExpression()) {
|
||||
if (!value.canConvert(QVariant::String))
|
||||
return;
|
||||
QString val = value.toString();
|
||||
val = QString::fromUtf8(Base::Interpreter().strToPython(val.toUtf8()).c_str());
|
||||
QString data = QString::fromLatin1("\"%1\"").arg(val);
|
||||
setPropertyValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* PropertyStringItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
|
||||
{
|
||||
QLineEdit *le = new QLineEdit(parent);
|
||||
ExpLineEdit *le = new ExpLineEdit(parent);
|
||||
le->setFrame(false);
|
||||
le->setReadOnly(isReadOnly());
|
||||
QObject::connect(le, SIGNAL(textChanged(const QString&)), receiver, method);
|
||||
if(isBound()) {
|
||||
le->bind(getPath());
|
||||
le->setAutoApply(autoApply());
|
||||
}
|
||||
|
||||
return le;
|
||||
}
|
||||
|
||||
@@ -3381,14 +3495,15 @@ void LinkSelection::select()
|
||||
{
|
||||
Gui::Selection().clearSelection();
|
||||
Gui::Selection().addSelection((const char*)link[0].toLatin1(),
|
||||
(const char*)link[1].toLatin1());
|
||||
(const char*)link[1].toLatin1(),
|
||||
link.size()>=6?(const char*)link[5].toUtf8():0);
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
LinkLabel::LinkLabel (QWidget * parent) : QWidget(parent)
|
||||
{
|
||||
LinkLabel::LinkLabel (QWidget * parent, bool xlink) : QWidget(parent), isXLink(xlink)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(1);
|
||||
@@ -3447,7 +3562,7 @@ void LinkLabel::onLinkActivated (const QString& s)
|
||||
|
||||
void LinkLabel::onEditClicked ()
|
||||
{
|
||||
Gui::Dialog::DlgPropertyLink dlg(link, this);
|
||||
Gui::Dialog::DlgPropertyLink dlg(link, this, 0, isXLink);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
setPropertyLink(dlg.propertyLink());
|
||||
/*emit*/ linkChanged(link);
|
||||
@@ -3463,7 +3578,7 @@ void LinkLabel::resizeEvent(QResizeEvent* e)
|
||||
|
||||
PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyLinkItem)
|
||||
|
||||
PropertyLinkItem::PropertyLinkItem()
|
||||
PropertyLinkItem::PropertyLinkItem():isXLink(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3473,23 +3588,72 @@ QVariant PropertyLinkItem::toString(const QVariant& prop) const
|
||||
return QVariant(list[2]);
|
||||
}
|
||||
|
||||
QVariant PropertyLinkItem::data(int column, int role) const {
|
||||
if(propertyItems.size() && column == 1 && role == Qt::TextColorRole) {
|
||||
auto xlink = Base::freecad_dynamic_cast<const App::PropertyXLink>(propertyItems[0]);
|
||||
if(xlink && xlink->checkRestore()>1)
|
||||
return QVariant::fromValue(QColor(0xff,0,0));
|
||||
}
|
||||
return PropertyItem::data(column,role);
|
||||
}
|
||||
|
||||
QVariant PropertyLinkItem::value(const App::Property* prop) const
|
||||
{
|
||||
assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyLink::getClassTypeId()));
|
||||
|
||||
auto xlink = Base::freecad_dynamic_cast<const App::PropertyXLink>(prop);
|
||||
isXLink = xlink!=0;
|
||||
|
||||
const App::PropertyLink* prop_link = static_cast<const App::PropertyLink*>(prop);
|
||||
App::PropertyContainer* c = prop_link->getContainer();
|
||||
|
||||
// the list has five elements:
|
||||
// [document name, internal name, label, internal name of container, property name]
|
||||
// The list has five mandatory elements:
|
||||
//
|
||||
// document name of the container,
|
||||
// internal name of the linked object,
|
||||
// label,
|
||||
// internal name of container,
|
||||
// property name
|
||||
//
|
||||
// and two additional elements if it is a PropertyXLink
|
||||
//
|
||||
// subname
|
||||
// (optional) document name of linked object if it is different from the container
|
||||
//
|
||||
|
||||
App::DocumentObject* obj = prop_link->getValue();
|
||||
QStringList list;
|
||||
if (obj) {
|
||||
list << QString::fromLatin1(obj->getDocument()->getName());
|
||||
list << QString::fromLatin1(obj->getNameInDocument());
|
||||
list << QString::fromUtf8(obj->Label.getValue());
|
||||
}
|
||||
else {
|
||||
|
||||
std::string _objName;
|
||||
const char *objName = obj->getNameInDocument();
|
||||
auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(c);
|
||||
if(!objName || (owner && owner->getDocument()!=obj->getDocument())) {
|
||||
_objName = obj->getFullName();
|
||||
objName = _objName.c_str();
|
||||
}
|
||||
|
||||
if(xlink && xlink->getSubName(false)[0]) {
|
||||
auto subObj = obj->getSubObject(xlink->getSubName(false));
|
||||
if(subObj)
|
||||
list << QString::fromLatin1("%1 (%2.%3)").
|
||||
arg(QString::fromUtf8(subObj->Label.getValue()),
|
||||
QString::fromLatin1(objName),
|
||||
QString::fromUtf8(xlink->getSubName(false)));
|
||||
else
|
||||
list << QString::fromLatin1("%1.%2").
|
||||
arg(QString::fromLatin1(objName),
|
||||
QString::fromUtf8(xlink->getSubName(false)));
|
||||
}else if(objName!=obj->Label.getValue()) {
|
||||
list << QString::fromLatin1("%1 (%2)").
|
||||
arg(QString::fromUtf8(obj->Label.getValue()),
|
||||
QString::fromLatin1(objName));
|
||||
} else {
|
||||
list << QString::fromUtf8(obj->Label.getValue());
|
||||
}
|
||||
} else {
|
||||
// no object assigned
|
||||
// the document name
|
||||
if (c->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
|
||||
@@ -3502,8 +3666,13 @@ QVariant PropertyLinkItem::value(const App::Property* prop) const
|
||||
|
||||
// the internal object name
|
||||
list << QString::fromLatin1("Null");
|
||||
|
||||
// the object label
|
||||
list << QString::fromLatin1("");
|
||||
std::string msg;
|
||||
if(xlink && xlink->checkRestore(&msg)>1)
|
||||
list << QString::fromUtf8(msg.c_str());
|
||||
else
|
||||
list << QString::fromLatin1("");
|
||||
}
|
||||
|
||||
// the name of this object
|
||||
@@ -3511,11 +3680,18 @@ QVariant PropertyLinkItem::value(const App::Property* prop) const
|
||||
App::DocumentObject* obj = static_cast<App::DocumentObject*>(c);
|
||||
list << QString::fromLatin1(obj->getNameInDocument());
|
||||
}
|
||||
else {
|
||||
else
|
||||
list << QString::fromLatin1("Null");
|
||||
}
|
||||
|
||||
list << QString::fromLatin1(prop->getName());
|
||||
assert(list.size() == FC_XLINK_VALUE_INDEX);
|
||||
|
||||
if(xlink) {
|
||||
list << QString::fromUtf8(xlink->getSubName(false));
|
||||
auto cobj = dynamic_cast<App::DocumentObject*>(c);
|
||||
if(cobj && obj && cobj->getDocument()!=obj->getDocument())
|
||||
list << QString::fromLatin1(obj->getDocument()->getName());
|
||||
}
|
||||
|
||||
return QVariant(list);
|
||||
}
|
||||
@@ -3531,15 +3707,23 @@ void PropertyLinkItem::setValue(const QVariant& value)
|
||||
QString data;
|
||||
if ( o.isEmpty() )
|
||||
data = QString::fromLatin1("None");
|
||||
else
|
||||
data = QString::fromLatin1("App.getDocument('%1').getObject('%2')").arg(d, o);
|
||||
else if(isXLink && items.size()>FC_XLINK_VALUE_INDEX+1) {
|
||||
QString doc;
|
||||
if(items.size()>=FC_XLINK_VALUE_INDEX+2)
|
||||
doc = items[FC_XLINK_VALUE_INDEX+1];
|
||||
else
|
||||
doc = d;
|
||||
data = QString::fromLatin1("(App.getDocument('%1').getObject('%2'),'%3')").
|
||||
arg(doc,o,items[FC_XLINK_VALUE_INDEX]);
|
||||
}else
|
||||
data = QString::fromLatin1("App.getDocument('%1').getObject('%2')").arg(d,o);
|
||||
setPropertyValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
QWidget* PropertyLinkItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
|
||||
{
|
||||
LinkLabel *ll = new LinkLabel(parent);
|
||||
LinkLabel *ll = new LinkLabel(parent, isXLink);
|
||||
ll->setAutoFillBackground(true);
|
||||
ll->setDisabled(isReadOnly());
|
||||
QObject::connect(ll, SIGNAL(linkChanged(const QStringList&)), receiver, method);
|
||||
@@ -3718,8 +3902,10 @@ void PropertyLinkListItem::setValue(const QVariant& value)
|
||||
if (!o.isEmpty())
|
||||
data << QString::fromLatin1("App.getDocument('%1').getObject('%2')").arg(d, o);
|
||||
}
|
||||
|
||||
setPropertyValue(QString::fromLatin1("[%1]").arg(data.join(QString::fromLatin1(", "))));
|
||||
if(data.size()==0)
|
||||
setPropertyValue(QLatin1String("[]"));
|
||||
else
|
||||
setPropertyValue(QString::fromLatin1("[%1]").arg(data.join(QString::fromLatin1(", "))));
|
||||
}
|
||||
|
||||
QWidget* PropertyLinkListItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const
|
||||
|
||||
@@ -125,6 +125,10 @@ public:
|
||||
virtual QVariant editorData(QWidget *editor) const;
|
||||
virtual bool isSeparator() const { return false; }
|
||||
|
||||
QWidget* createExpressionEditor(QWidget* parent, const QObject* receiver, const char* method) const;
|
||||
void setExpressionEditorData(QWidget *editor, const QVariant& data) const;
|
||||
QVariant expressionEditorData(QWidget *editor) const;
|
||||
|
||||
/**override the bind functions to ensure we issue the propertyBound() call, which is then overloaded by
|
||||
childs which like to be informed of a binding*/
|
||||
virtual void bind(const App::Property& prop);
|
||||
@@ -145,13 +149,16 @@ public:
|
||||
void setDecimals(int);
|
||||
int decimals() const;
|
||||
|
||||
void setLinked(bool);
|
||||
bool isLinked() const;
|
||||
|
||||
PropertyItem *child(int row);
|
||||
int childCount() const;
|
||||
int columnCount() const;
|
||||
QString propertyName() const;
|
||||
void setPropertyName(const QString&);
|
||||
void setPropertyValue(const QString&);
|
||||
QVariant data(int column, int role) const;
|
||||
virtual QVariant data(int column, int role) const;
|
||||
bool setData (const QVariant& value);
|
||||
Qt::ItemFlags flags(int column) const;
|
||||
int row() const;
|
||||
@@ -169,16 +176,16 @@ protected:
|
||||
virtual void initialize();
|
||||
QString pythonIdentifier(const App::Property*) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
QString propName;
|
||||
QString displayText;
|
||||
QVariant propData;
|
||||
std::vector<App::Property*> propertyItems;
|
||||
PropertyItem *parentItem;
|
||||
QList<PropertyItem*> childItems;
|
||||
bool readonly;
|
||||
int precision;
|
||||
bool cleared;
|
||||
bool linked;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -948,7 +955,7 @@ class LinkLabel : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LinkLabel (QWidget * parent = 0);
|
||||
LinkLabel (QWidget * parent = 0, bool xlink = false);
|
||||
virtual ~LinkLabel();
|
||||
void setPropertyLink(const QStringList& o);
|
||||
QStringList propertyLink() const;
|
||||
@@ -967,6 +974,7 @@ private:
|
||||
QLabel* label;
|
||||
QPushButton* editButton;
|
||||
QStringList link;
|
||||
bool isXLink;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -986,9 +994,13 @@ protected:
|
||||
virtual QVariant toString(const QVariant&) const;
|
||||
virtual QVariant value(const App::Property*) const;
|
||||
virtual void setValue(const QVariant&);
|
||||
virtual QVariant data(int column, int role) const override;
|
||||
|
||||
protected:
|
||||
PropertyLinkItem();
|
||||
|
||||
private:
|
||||
mutable bool isXLink;
|
||||
};
|
||||
|
||||
class LinkListLabel : public QWidget
|
||||
|
||||
@@ -29,14 +29,23 @@
|
||||
# include <QPainter>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include "PropertyItemDelegate.h"
|
||||
#include "PropertyItem.h"
|
||||
#include "PropertyEditor.h"
|
||||
|
||||
FC_LOG_LEVEL_INIT("PropertyView",true,true);
|
||||
|
||||
using namespace Gui::PropertyEditor;
|
||||
|
||||
|
||||
PropertyItemDelegate::PropertyItemDelegate(QObject* parent)
|
||||
: QItemDelegate(parent), pressed(false)
|
||||
: QItemDelegate(parent), expressionEditor(0)
|
||||
, pressed(false), changed(false)
|
||||
{
|
||||
connect(this, SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)),
|
||||
this, SLOT(editorClosed(QWidget*, QAbstractItemDelegate::EndEditHint)));
|
||||
@@ -116,9 +125,21 @@ bool PropertyItemDelegate::editorEvent (QEvent * event, QAbstractItemModel* mode
|
||||
|
||||
void PropertyItemDelegate::editorClosed(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
|
||||
{
|
||||
#if 0
|
||||
int id = 0;
|
||||
auto &app = App::GetApplication();
|
||||
const char *name = app.getActiveTransaction(&id);
|
||||
if(id && id==activeTransactionID) {
|
||||
FC_LOG("editor close transaction " << name);
|
||||
app.closeActiveTransaction(false,id);
|
||||
activeTransactionID = 0;
|
||||
}
|
||||
FC_LOG("editor close " << editor);
|
||||
#endif
|
||||
|
||||
// don't close the editor when pressing Tab or Shift+Tab
|
||||
// https://forum.freecadweb.org/viewtopic.php?f=3&t=34627#p290957
|
||||
if (hint != EditNextItem && hint != EditPreviousItem)
|
||||
if (editor && hint != EditNextItem && hint != EditPreviousItem)
|
||||
editor->close();
|
||||
}
|
||||
|
||||
@@ -131,7 +152,16 @@ QWidget * PropertyItemDelegate::createEditor (QWidget * parent, const QStyleOpti
|
||||
PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
|
||||
if (!childItem)
|
||||
return 0;
|
||||
QWidget* editor = childItem->createEditor(parent, this, SLOT(valueChanged()));
|
||||
|
||||
FC_LOG("create editor " << index.row() << "," << index.column());
|
||||
|
||||
PropertyEditor *parentEditor = qobject_cast<PropertyEditor*>(this->parent());
|
||||
QWidget* editor;
|
||||
expressionEditor = 0;
|
||||
if(parentEditor && parentEditor->isBinding())
|
||||
expressionEditor = editor = childItem->createExpressionEditor(parent, this, SLOT(valueChanged()));
|
||||
else
|
||||
editor = childItem->createEditor(parent, this, SLOT(valueChanged()));
|
||||
if (editor) // Make sure the editor background is painted so the cell content doesn't show through
|
||||
editor->setAutoFillBackground(true);
|
||||
if (editor && childItem->isReadOnly())
|
||||
@@ -144,14 +174,17 @@ QWidget * PropertyItemDelegate::createEditor (QWidget * parent, const QStyleOpti
|
||||
editor->setFocus();
|
||||
}
|
||||
this->pressed = false;
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
void PropertyItemDelegate::valueChanged()
|
||||
{
|
||||
QWidget* editor = qobject_cast<QWidget*>(sender());
|
||||
if (editor)
|
||||
if (editor) {
|
||||
Base::FlagToggler<> flag(changed);
|
||||
commitData(editor);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
@@ -161,17 +194,24 @@ void PropertyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &ind
|
||||
QVariant data = index.data(Qt::EditRole);
|
||||
PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
|
||||
editor->blockSignals(true);
|
||||
childItem->setEditorData(editor, data);
|
||||
if(expressionEditor == editor)
|
||||
childItem->setExpressionEditorData(editor, data);
|
||||
else
|
||||
childItem->setEditorData(editor, data);
|
||||
editor->blockSignals(false);
|
||||
return;
|
||||
}
|
||||
|
||||
void PropertyItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
if (!index.isValid() || !changed)
|
||||
return;
|
||||
PropertyItem *childItem = static_cast<PropertyItem*>(index.internalPointer());
|
||||
QVariant data = childItem->editorData(editor);
|
||||
QVariant data;
|
||||
if(expressionEditor == editor)
|
||||
data = childItem->expressionEditorData(editor);
|
||||
else
|
||||
data = childItem->editorData(editor);
|
||||
model->setData(index, data, Qt::EditRole);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ public Q_SLOTS:
|
||||
void editorClosed (QWidget * editor, QAbstractItemDelegate::EndEditHint hint);
|
||||
|
||||
private:
|
||||
mutable QWidget *expressionEditor;
|
||||
mutable bool pressed;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
} // namespace PropertyEditor
|
||||
|
||||
@@ -27,8 +27,11 @@
|
||||
# include <cfloat>
|
||||
#endif
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include "PropertyModel.h"
|
||||
#include "PropertyItem.h"
|
||||
#include "PropertyView.h"
|
||||
|
||||
using namespace Gui::PropertyEditor;
|
||||
|
||||
@@ -206,6 +209,24 @@ QModelIndex PropertyModel::propertyIndexFromPath(const QStringList& path) const
|
||||
return parent;
|
||||
}
|
||||
|
||||
struct PropItemInfo {
|
||||
const std::string &name;
|
||||
const std::vector<App::Property*> &props;
|
||||
|
||||
PropItemInfo(const std::string &n, const std::vector<App::Property*> &p)
|
||||
:name(n),props(p)
|
||||
{}
|
||||
};
|
||||
|
||||
static void setPropertyItemName(PropertyItem *item, const char *propName, QString groupName) {
|
||||
QString name = QString::fromLatin1(propName);
|
||||
if(name.size()>groupName.size()+1
|
||||
&& name.startsWith(groupName + QLatin1Char('_')))
|
||||
name = name.right(name.size()-groupName.size()-1);
|
||||
|
||||
item->setPropertyName(name);
|
||||
}
|
||||
|
||||
void PropertyModel::buildUp(const PropertyModel::PropertyList& props)
|
||||
{
|
||||
beginResetModel();
|
||||
@@ -214,42 +235,47 @@ void PropertyModel::buildUp(const PropertyModel::PropertyList& props)
|
||||
rootItem->reset();
|
||||
|
||||
// sort the properties into their groups
|
||||
std::map<std::string, std::vector<std::vector<App::Property*> > > propGroup;
|
||||
std::map<std::string, std::vector<PropItemInfo> > propGroup;
|
||||
PropertyModel::PropertyList::const_iterator jt;
|
||||
for (jt = props.begin(); jt != props.end(); ++jt) {
|
||||
App::Property* prop = jt->second.front();
|
||||
const char* group = prop->getGroup();
|
||||
bool isEmpty = (group == 0 || group[0] == '\0');
|
||||
std::string grp = isEmpty ? QT_TRANSLATE_NOOP("App::Property", "Base") : group;
|
||||
propGroup[grp].push_back(jt->second);
|
||||
propGroup[grp].emplace_back(jt->first,jt->second);
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::vector<App::Property*> > >
|
||||
::const_iterator kt;
|
||||
for (kt = propGroup.begin(); kt != propGroup.end(); ++kt) {
|
||||
for (auto kt = propGroup.begin(); kt != propGroup.end(); ++kt) {
|
||||
// set group item
|
||||
PropertyItem* group = static_cast<PropertyItem*>(PropertySeparatorItem::create());
|
||||
group->setParent(rootItem);
|
||||
rootItem->appendChild(group);
|
||||
group->setPropertyName(QString::fromLatin1(kt->first.c_str()));
|
||||
QString groupName = QString::fromLatin1(kt->first.c_str());
|
||||
group->setPropertyName(groupName);
|
||||
|
||||
// setup the items for the properties
|
||||
std::vector<std::vector<App::Property*> >::const_iterator it;
|
||||
for (it = kt->second.begin(); it != kt->second.end(); ++it) {
|
||||
App::Property* prop = it->front();
|
||||
QString editor = QString::fromLatin1(prop->getEditorName());
|
||||
if (!editor.isEmpty()) {
|
||||
PropertyItem* item = PropertyItemFactory::instance().createPropertyItem(prop->getEditorName());
|
||||
for (auto it = kt->second.begin(); it != kt->second.end(); ++it) {
|
||||
const auto &info = *it;
|
||||
App::Property* prop = info.props.front();
|
||||
std::string editor(prop->getEditorName());
|
||||
if(editor.empty() && PropertyView::showAll())
|
||||
editor = "Gui::PropertyEditor::PropertyItem";
|
||||
if (!editor.empty()) {
|
||||
PropertyItem* item = PropertyItemFactory::instance().createPropertyItem(editor.c_str());
|
||||
if (!item) {
|
||||
qWarning("No property item for type %s found\n", prop->getEditorName());
|
||||
qWarning("No property item for type %s found\n", editor.c_str());
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if(boost::ends_with(info.name,"*"))
|
||||
item->setLinked(true);
|
||||
PropertyItem* child = (PropertyItem*)item;
|
||||
child->setParent(rootItem);
|
||||
rootItem->appendChild(child);
|
||||
child->setPropertyName(QString::fromLatin1(prop->getName()));
|
||||
child->setPropertyData(*it);
|
||||
|
||||
setPropertyItemName(child,prop->getName(),groupName);
|
||||
|
||||
child->setPropertyData(info.props);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,11 +305,13 @@ void PropertyModel::updateProperty(const App::Property& prop)
|
||||
|
||||
void PropertyModel::appendProperty(const App::Property& prop)
|
||||
{
|
||||
QString editor = QString::fromLatin1(prop.getEditorName());
|
||||
if (!editor.isEmpty()) {
|
||||
PropertyItem* item = PropertyItemFactory::instance().createPropertyItem(prop.getEditorName());
|
||||
std::string editor(prop.getEditorName());
|
||||
if(editor.empty() && PropertyView::showAll())
|
||||
editor = "Gui::PropertyEditor::PropertyItem";
|
||||
if (!editor.empty()) {
|
||||
PropertyItem* item = PropertyItemFactory::instance().createPropertyItem(editor.c_str());
|
||||
if (!item) {
|
||||
qWarning("No property item for type %s found\n", prop.getEditorName());
|
||||
qWarning("No property item for type %s found\n", editor.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -354,7 +382,8 @@ void PropertyModel::appendProperty(const App::Property& prop)
|
||||
|
||||
std::vector<App::Property*> data;
|
||||
data.push_back(const_cast<App::Property*>(&prop));
|
||||
item->setPropertyName(QString::fromLatin1(prop.getName()));
|
||||
|
||||
setPropertyItemName(item,prop.getName(),groupName);
|
||||
item->setPropertyData(data);
|
||||
|
||||
endInsertRows();
|
||||
|
||||
Reference in New Issue
Block a user