1
/***************************************************************************
2
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
26
# include <QGridLayout>
30
#include <App/Document.h>
31
#include <App/DocumentObject.h>
32
#include <Base/Console.h>
33
#include <Base/Parameter.h>
34
#include <Base/Tools.h>
36
#include "PropertyView.h"
37
#include "Application.h"
39
#include "MainWindow.h"
40
#include "SelectionObject.h"
42
#include "ViewParams.h"
43
#include "ViewProvider.h"
44
#include "ViewProviderDocumentObject.h"
45
#include "propertyeditor/PropertyEditor.h"
50
using namespace Gui::DockWnd;
51
using namespace Gui::PropertyEditor;
52
namespace sp = std::placeholders;
54
static ParameterGrp::handle _GetParam() {
55
static ParameterGrp::handle hGrp;
57
hGrp = App::GetApplication().GetParameterGroupByPath(
58
"User parameter:BaseApp/Preferences/PropertyView");
63
/* TRANSLATOR Gui::PropertyView */
65
/*! Property Editor Widget
67
* Provides two Gui::PropertyEditor::PropertyEditor widgets, for "View" and "Data",
70
PropertyView::PropertyView(QWidget *parent)
71
: QWidget(parent), SelectionObserver(false, ResolveMode::NoResolve)
73
auto pLayout = new QGridLayout( this );
74
pLayout->setSpacing(0);
75
pLayout->setContentsMargins(0, 0, 0, 0);
77
timer = new QTimer(this);
78
timer->setSingleShot(true);
79
connect(timer, &QTimer::timeout, this, &PropertyView::onTimer);
81
tabs = new QTabWidget (this);
82
tabs->setObjectName(QString::fromUtf8("propertyTab"));
83
tabs->setTabPosition(QTabWidget::South);
84
pLayout->addWidget(tabs, 0, 0);
86
propertyEditorView = new Gui::PropertyEditor::PropertyEditor();
87
propertyEditorView->setObjectName(QStringLiteral("propertyEditorView"));
88
propertyEditorView->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionView", false));
89
propertyEditorView->setAutomaticExpand(_GetParam()->GetBool("AutoExpandView", false));
90
tabs->addTab(propertyEditorView, tr("View"));
92
propertyEditorData = new Gui::PropertyEditor::PropertyEditor();
93
propertyEditorData->setObjectName(QStringLiteral("propertyEditorData"));
94
propertyEditorData->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionData", true));
95
propertyEditorData->setAutomaticExpand(_GetParam()->GetBool("AutoExpandData", false));
96
tabs->addTab(propertyEditorData, tr("Data"));
98
int preferredTab = _GetParam()->GetInt("LastTabIndex", 1);
100
if ( preferredTab > 0 && preferredTab < tabs->count() )
101
tabs->setCurrentIndex(preferredTab);
103
// connect after adding all tabs, so adding doesn't thrash the parameter
104
connect(tabs, &QTabWidget::currentChanged, this, &PropertyView::tabChanged);
107
this->connectPropData =
108
App::GetApplication().signalChangedObject.connect(std::bind
109
(&PropertyView::slotChangePropertyData, this, sp::_2));
110
this->connectPropView =
111
Gui::Application::Instance->signalChangedObject.connect(std::bind
112
(&PropertyView::slotChangePropertyView, this, sp::_1, sp::_2));
113
this->connectPropAppend =
114
App::GetApplication().signalAppendDynamicProperty.connect(std::bind
115
(&PropertyView::slotAppendDynamicProperty, this, sp::_1));
116
this->connectPropRemove =
117
App::GetApplication().signalRemoveDynamicProperty.connect(std::bind
118
(&PropertyView::slotRemoveDynamicProperty, this, sp::_1));
119
this->connectPropChange =
120
App::GetApplication().signalChangePropertyEditor.connect(std::bind
121
(&PropertyView::slotChangePropertyEditor, this, sp::_1, sp::_2));
122
this->connectUndoDocument =
123
App::GetApplication().signalUndoDocument.connect(std::bind
124
(&PropertyView::slotRollback, this));
125
this->connectRedoDocument =
126
App::GetApplication().signalRedoDocument.connect(std::bind
127
(&PropertyView::slotRollback, this));
128
this->connectActiveDoc =
129
Application::Instance->signalActiveDocument.connect(std::bind
130
(&PropertyView::slotActiveDocument, this, sp::_1));
131
this->connectDelDocument =
132
Application::Instance->signalDeleteDocument.connect(
133
std::bind(&PropertyView::slotDeleteDocument, this, sp::_1));
134
this->connectDelViewObject =
135
Application::Instance->signalDeletedObject.connect(
136
std::bind(&PropertyView::slotDeletedViewObject, this, sp::_1));
137
this->connectDelObject =
138
App::GetApplication().signalDeletedObject.connect(
139
std::bind(&PropertyView::slotDeletedObject, this, sp::_1));
140
this->connectChangedDocument = App::GetApplication().signalChangedDocument.connect(
141
std::bind(&PropertyView::slotChangePropertyData, this, sp::_2));
145
PropertyView::~PropertyView()
147
this->connectPropData.disconnect();
148
this->connectPropView.disconnect();
149
this->connectPropAppend.disconnect();
150
this->connectPropRemove.disconnect();
151
this->connectPropChange.disconnect();
152
this->connectUndoDocument.disconnect();
153
this->connectRedoDocument.disconnect();
154
this->connectActiveDoc.disconnect();
155
this->connectDelDocument.disconnect();
156
this->connectDelObject.disconnect();
157
this->connectDelViewObject.disconnect();
158
this->connectChangedDocument.disconnect();
163
bool PropertyView::showAll() {
167
void PropertyView::setShowAll(bool enable) {
168
if(_ShowAll != enable) {
170
const auto views = getMainWindow()->findChildren<PropertyView*>();
171
for(auto view : views) {
172
if(view->isVisible()) {
173
view->propertyEditorData->buildUp();
174
view->propertyEditorView->buildUp();
181
void PropertyView::hideEvent(QHideEvent *ev) {
183
this->detachSelection();
184
// clear the properties before hiding.
185
propertyEditorData->buildUp();
186
propertyEditorView->buildUp();
187
clearPropertyItemSelection();
188
QWidget::hideEvent(ev);
191
void PropertyView::showEvent(QShowEvent *ev) {
192
this->attachSelection();
193
this->timer->start(ViewParams::instance()->getPropertyViewTimer());
194
QWidget::showEvent(ev);
197
void PropertyView::clearPropertyItemSelection() {
199
propertyEditorData->clearSelection();
200
propertyEditorData->setCurrentIndex(index);
201
propertyEditorView->clearSelection();
202
propertyEditorView->setCurrentIndex(index);
205
void PropertyView::slotRollback() {
206
// PropertyItemDelegate will setup application active transaction on
207
// entering edit mode, and close active transaction when exit editing. But,
208
// when the user clicks undo/redo button while editing some property, the
209
// current active transaction will be closed by design, which cause further
210
// editing to be not recorded. Hence, we force unselect any property item on
212
clearPropertyItemSelection();
215
void PropertyView::slotChangePropertyData(const App::Property& prop)
217
if (propertyEditorData->propOwners.count(prop.getContainer())) {
218
propertyEditorData->updateProperty(prop);
219
timer->start(ViewParams::instance()->getPropertyViewTimer());
223
void PropertyView::slotChangePropertyView(const Gui::ViewProvider&, const App::Property& prop)
225
if (propertyEditorView->propOwners.count(prop.getContainer())) {
226
propertyEditorView->updateProperty(prop);
227
timer->start(ViewParams::instance()->getPropertyViewTimer());
231
bool PropertyView::isPropertyHidden(const App::Property *prop) {
232
return prop && !showAll() &&
233
((prop->getType() & App::Prop_Hidden) || prop->testStatus(App::Property::Hidden));
236
void PropertyView::slotAppendDynamicProperty(const App::Property& prop)
238
if (isPropertyHidden(&prop))
241
App::PropertyContainer* parent = prop.getContainer();
242
if (propertyEditorData->propOwners.count(parent)
243
|| propertyEditorView->propOwners.count(parent))
245
timer->start(ViewParams::instance()->getPropertyViewTimer());
249
void PropertyView::slotRemoveDynamicProperty(const App::Property& prop)
251
App::PropertyContainer* parent = prop.getContainer();
252
if(propertyEditorData->propOwners.count(parent))
253
propertyEditorData->removeProperty(prop);
254
else if(propertyEditorView->propOwners.count(parent))
255
propertyEditorView->removeProperty(prop);
258
timer->start(ViewParams::instance()->getPropertyViewTimer());
261
void PropertyView::slotChangePropertyEditor(const App::Document &, const App::Property& prop)
263
App::PropertyContainer* parent = prop.getContainer();
264
if (propertyEditorData->propOwners.count(parent)
265
|| propertyEditorView->propOwners.count(parent))
266
timer->start(ViewParams::instance()->getPropertyViewTimer());
269
void PropertyView::slotDeleteDocument(const Gui::Document &doc) {
270
if(propertyEditorData->propOwners.count(doc.getDocument())) {
271
propertyEditorView->buildUp();
272
propertyEditorData->buildUp();
273
clearPropertyItemSelection();
274
timer->start(ViewParams::instance()->getPropertyViewTimer());
278
void PropertyView::slotDeletedViewObject(const Gui::ViewProvider &vp) {
279
if(propertyEditorView->propOwners.count(&vp)) {
280
propertyEditorView->buildUp();
281
propertyEditorData->buildUp();
282
clearPropertyItemSelection();
283
timer->start(ViewParams::instance()->getPropertyViewTimer());
287
void PropertyView::slotDeletedObject(const App::DocumentObject &obj) {
288
if(propertyEditorData->propOwners.count(&obj)) {
289
propertyEditorView->buildUp();
290
propertyEditorData->buildUp();
291
clearPropertyItemSelection();
292
timer->start(ViewParams::instance()->getPropertyViewTimer());
296
void PropertyView::slotActiveDocument(const Gui::Document &doc)
298
checkEnable(doc.getDocument()->getName());
301
void PropertyView::checkEnable(const char *doc) {
302
if(ViewParams::instance()->getEnablePropertyViewForInactiveDocument()) {
306
// check if at least one selected object is part of the active document
307
setEnabled(!Selection().hasSelection()
308
|| Selection().hasSelection(doc, ResolveMode::NoResolve));
311
struct PropertyView::PropInfo
313
std::string propName;
315
std::vector<App::Property*> propList;
318
struct PropertyView::PropFind {
319
const PropInfo& item;
320
explicit PropFind(const PropInfo& item) : item(item) {}
321
bool operator () (const PropInfo& elem) const
323
return (elem.propId == item.propId) &&
324
(elem.propName == item.propName);
328
void PropertyView::onSelectionChanged(const SelectionChanges& msg)
330
if (msg.Type != SelectionChanges::AddSelection &&
331
msg.Type != SelectionChanges::RmvSelection &&
332
msg.Type != SelectionChanges::SetSelection &&
333
msg.Type != SelectionChanges::ClrSelection)
336
// clear the properties.
337
timer->start(ViewParams::instance()->getPropertyViewTimer());
340
void PropertyView::onTimer()
342
// See https://forum.freecad.org/viewtopic.php?f=8&t=72526
343
if (this->updating) {
344
Base::Console().Log("Ignore recursive call of PropertyView::onTimer()\n");
347
Base::StateLocker guard(this->updating);
351
if(!this->isSelectionAttached()) {
352
propertyEditorData->buildUp();
353
propertyEditorView->buildUp();
354
clearPropertyItemSelection();
358
if(!Gui::Selection().hasSelection()) {
359
auto gdoc = TreeWidget::selectedDocument();
360
if(!gdoc || !gdoc->getDocument()) {
361
propertyEditorData->buildUp();
362
propertyEditorView->buildUp();
363
clearPropertyItemSelection();
367
PropertyModel::PropertyList docProps;
369
auto doc = gdoc->getDocument();
370
std::map<std::string,App::Property*> props;
371
doc->getPropertyMap(props);
373
docProps.emplace_back(v.first,
374
std::vector<App::Property*>(1,v.second));
375
propertyEditorData->buildUp(std::move(docProps));
376
tabs->setCurrentIndex(1);
380
std::set<App::DocumentObject *> objSet;
382
// group the properties by <name,id>
383
std::vector<PropInfo> propDataMap;
384
std::vector<PropInfo> propViewMap;
385
bool checkLink = true;
386
ViewProviderDocumentObject *vpLast = nullptr;
387
auto sels = Gui::Selection().getSelectionEx("*");
388
for(auto &sel : sels) {
389
App::DocumentObject *ob = sel.getObject();
392
// Do not process an object more than once
393
if(!objSet.insert(ob).second)
396
std::vector<App::Property*> dataList;
397
std::map<std::string, App::Property*> viewList;
399
auto vp = Application::Instance->getViewProvider(ob);
402
ob->getPropertyList(dataList);
406
if(vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
407
auto cvp = static_cast<ViewProviderDocumentObject*>(vp);
408
if(vpLast && cvp!=vpLast)
413
ob->getPropertyList(dataList);
415
// get the properties as map here because it doesn't matter to have them sorted alphabetically
416
vp->getPropertyMap(viewList);
418
// store the properties with <name,id> as key in a map
420
for (auto prop : dataList) {
421
if (isPropertyHidden(prop))
425
nameType.propName = prop->getName();
426
nameType.propId = prop->getTypeId().getKey();
428
auto pi = std::find_if(propDataMap.begin(), propDataMap.end(), PropFind(nameType));
429
if (pi != propDataMap.end()) {
430
pi->propList.push_back(prop);
433
nameType.propList.push_back(prop);
434
propDataMap.push_back(nameType);
438
// the same for the view properties
440
std::map<std::string, App::Property*>::iterator pt;
441
for (pt = viewList.begin(); pt != viewList.end(); ++pt) {
442
if (isPropertyHidden(pt->second))
446
nameType.propName = pt->first;
447
nameType.propId = pt->second->getTypeId().getKey();
449
auto pi = std::find_if(propViewMap.begin(), propViewMap.end(), PropFind(nameType));
450
if (pi != propViewMap.end()) {
451
pi->propList.push_back(pt->second);
454
nameType.propList.push_back(pt->second);
455
propViewMap.push_back(nameType);
461
// the property must be part of each selected object, i.e. the number
462
// of selected objects is equal to the number of properties with same
464
std::vector<PropInfo>::const_iterator it;
465
PropertyModel::PropertyList dataProps;
466
std::map<std::string, std::vector<App::Property*> > dataPropsMap;
467
PropertyModel::PropertyList viewProps;
469
if(checkLink && vpLast) {
470
// In case the only selected object is a link, insert the link's own
471
// property before the linked object
472
App::DocumentObject *obj = vpLast->getObject();
474
if(obj && obj->canLinkProperties() && (linked=obj->getLinkedObject(true))!=obj && linked) {
475
std::vector<App::Property*> dataList;
476
std::map<std::string, App::Property*> propMap;
477
obj->getPropertyMap(propMap);
478
linked->getPropertyList(dataList);
479
for(auto prop : dataList) {
480
if(isPropertyHidden(prop))
482
std::string name(prop->getName());
483
auto it = propMap.find(name);
484
if(it!=propMap.end() && !isPropertyHidden(it->second))
486
std::vector<App::Property*> items(1,prop);
487
if(prop->testStatus(App::Property::PropDynamic))
488
dataPropsMap.emplace(name+"*",std::move(items));
490
dataProps.emplace_back(name+"*", std::move(items));
492
auto vpLinked = Application::Instance->getViewProvider(linked);
495
vpLast->getPropertyMap(propMap);
497
vpLinked->getPropertyList(dataList);
498
for(auto prop : dataList) {
499
if(isPropertyHidden(prop))
501
std::string name(prop->getName());
502
auto it = propMap.find(name);
503
if(it!=propMap.end() && !isPropertyHidden(it->second))
505
std::vector<App::Property*> items(1,prop);
506
viewProps.emplace_back(name+"*", std::move(items));
512
for(auto &v : dataPropsMap)
513
dataProps.emplace_back(v.first,std::move(v.second));
515
dataPropsMap.clear();
517
for (it = propDataMap.begin(); it != propDataMap.end(); ++it) {
518
if (it->propList.size() == sels.size()) {
519
if(it->propList[0]->testStatus(App::Property::PropDynamic))
520
dataPropsMap.emplace(it->propName, std::move(it->propList));
522
dataProps.emplace_back(it->propName, std::move(it->propList));
526
for(auto &v : dataPropsMap)
527
dataProps.emplace_back(v.first,std::move(v.second));
529
propertyEditorData->buildUp(std::move(dataProps),true);
531
for (it = propViewMap.begin(); it != propViewMap.end(); ++it) {
532
if (it->propList.size() == sels.size())
533
viewProps.emplace_back(it->propName, std::move(it->propList));
536
propertyEditorView->buildUp(std::move(viewProps));
538
// make sure the editors are enabled/disabled properly
542
void PropertyView::tabChanged(int index)
544
_GetParam()->SetInt("LastTabIndex",index);
547
void PropertyView::changeEvent(QEvent *e)
549
if (e->type() == QEvent::LanguageChange) {
550
tabs->setTabText(0, tr("View"));
551
tabs->setTabText(1, tr("Data"));
554
QWidget::changeEvent(e);
557
/* TRANSLATOR Gui::DockWnd::PropertyDockView */
559
PropertyDockView::PropertyDockView(Gui::Document* pcDocument, QWidget *parent)
560
: DockWindow(pcDocument,parent)
562
setWindowTitle(tr("Property View"));
564
auto view = new PropertyView(this);
565
auto pLayout = new QGridLayout(this);
566
pLayout->setSpacing(0);
567
pLayout->setContentsMargins(0, 0, 0, 0);
568
pLayout->addWidget(view, 0, 0);
573
PropertyDockView::~PropertyDockView() = default;
575
#include "moc_PropertyView.cpp"