FreeCAD

Форк
0
/
PropertyView.cpp 
575 строк · 20.9 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *                                                                         *
4
 *   This file is part of the FreeCAD CAx development system.              *
5
 *                                                                         *
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.      *
10
 *                                                                         *
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.                  *
15
 *                                                                         *
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                                *
20
 *                                                                         *
21
 ***************************************************************************/
22

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <QEvent>
26
# include <QGridLayout>
27
# include <QTimer>
28
#endif
29

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>
35

36
#include "PropertyView.h"
37
#include "Application.h"
38
#include "Document.h"
39
#include "MainWindow.h"
40
#include "SelectionObject.h"
41
#include "Tree.h"
42
#include "ViewParams.h"
43
#include "ViewProvider.h"
44
#include "ViewProviderDocumentObject.h"
45
#include "propertyeditor/PropertyEditor.h"
46

47

48
using namespace std;
49
using namespace Gui;
50
using namespace Gui::DockWnd;
51
using namespace Gui::PropertyEditor;
52
namespace sp = std::placeholders;
53

54
static ParameterGrp::handle _GetParam() {
55
    static ParameterGrp::handle hGrp;
56
    if(!hGrp) {
57
        hGrp = App::GetApplication().GetParameterGroupByPath(
58
                "User parameter:BaseApp/Preferences/PropertyView");
59
    }
60
    return hGrp;
61
}
62

63
/* TRANSLATOR Gui::PropertyView */
64

65
/*! Property Editor Widget
66
 *
67
 * Provides two Gui::PropertyEditor::PropertyEditor widgets, for "View" and "Data",
68
 * in two tabs.
69
 */
70
PropertyView::PropertyView(QWidget *parent)
71
  : QWidget(parent), SelectionObserver(false, ResolveMode::NoResolve)
72
{
73
    auto pLayout = new QGridLayout( this );
74
    pLayout->setSpacing(0);
75
    pLayout->setContentsMargins(0, 0, 0, 0);
76

77
    timer = new QTimer(this);
78
    timer->setSingleShot(true);
79
    connect(timer, &QTimer::timeout, this, &PropertyView::onTimer);
80

81
    tabs = new QTabWidget (this);
82
    tabs->setObjectName(QString::fromUtf8("propertyTab"));
83
    tabs->setTabPosition(QTabWidget::South);
84
    pLayout->addWidget(tabs, 0, 0);
85

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"));
91

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"));
97

98
    int preferredTab = _GetParam()->GetInt("LastTabIndex", 1);
99

100
    if ( preferredTab > 0 && preferredTab < tabs->count() )
101
        tabs->setCurrentIndex(preferredTab);
102

103
    // connect after adding all tabs, so adding doesn't thrash the parameter
104
    connect(tabs, &QTabWidget::currentChanged, this, &PropertyView::tabChanged);
105

106
    //NOLINTBEGIN
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));
142
    //NOLINTEND
143
}
144

145
PropertyView::~PropertyView()
146
{
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();
159
}
160

161
static bool _ShowAll;
162

163
bool PropertyView::showAll() {
164
    return _ShowAll;
165
}
166

167
void PropertyView::setShowAll(bool enable) {
168
    if(_ShowAll != enable) {
169
        _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();
175
                view->onTimer();
176
            }
177
        }
178
    }
179
}
180

181
void PropertyView::hideEvent(QHideEvent *ev) {
182
    this->timer->stop();
183
    this->detachSelection();
184
    // clear the properties before hiding.
185
    propertyEditorData->buildUp();
186
    propertyEditorView->buildUp();
187
    clearPropertyItemSelection();
188
    QWidget::hideEvent(ev);
189
}
190

191
void PropertyView::showEvent(QShowEvent *ev) {
192
    this->attachSelection();
193
    this->timer->start(ViewParams::instance()->getPropertyViewTimer());
194
    QWidget::showEvent(ev);
195
}
196

197
void PropertyView::clearPropertyItemSelection() {
198
    QModelIndex index;
199
    propertyEditorData->clearSelection();
200
    propertyEditorData->setCurrentIndex(index);
201
    propertyEditorView->clearSelection();
202
    propertyEditorView->setCurrentIndex(index);
203
}
204

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
211
    // undo/redo
212
    clearPropertyItemSelection();
213
}
214

215
void PropertyView::slotChangePropertyData(const App::Property& prop)
216
{
217
    if (propertyEditorData->propOwners.count(prop.getContainer())) {
218
        propertyEditorData->updateProperty(prop);
219
        timer->start(ViewParams::instance()->getPropertyViewTimer());
220
    }
221
}
222

223
void PropertyView::slotChangePropertyView(const Gui::ViewProvider&, const App::Property& prop)
224
{
225
    if (propertyEditorView->propOwners.count(prop.getContainer())) {
226
        propertyEditorView->updateProperty(prop);
227
        timer->start(ViewParams::instance()->getPropertyViewTimer());
228
    }
229
}
230

231
bool PropertyView::isPropertyHidden(const App::Property *prop) {
232
    return prop && !showAll() &&
233
        ((prop->getType() & App::Prop_Hidden) || prop->testStatus(App::Property::Hidden));
234
}
235

236
void PropertyView::slotAppendDynamicProperty(const App::Property& prop)
237
{
238
    if (isPropertyHidden(&prop))
239
        return;
240

241
    App::PropertyContainer* parent = prop.getContainer();
242
    if (propertyEditorData->propOwners.count(parent)
243
            || propertyEditorView->propOwners.count(parent))
244
    {
245
        timer->start(ViewParams::instance()->getPropertyViewTimer());
246
    }
247
}
248

249
void PropertyView::slotRemoveDynamicProperty(const App::Property& prop)
250
{
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);
256
    else
257
        return;
258
    timer->start(ViewParams::instance()->getPropertyViewTimer());
259
}
260

261
void PropertyView::slotChangePropertyEditor(const App::Document &, const App::Property& prop)
262
{
263
    App::PropertyContainer* parent = prop.getContainer();
264
    if (propertyEditorData->propOwners.count(parent)
265
            || propertyEditorView->propOwners.count(parent))
266
        timer->start(ViewParams::instance()->getPropertyViewTimer());
267
}
268

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());
275
    }
276
}
277

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());
284
    }
285
}
286

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());
293
    }
294
}
295

296
void PropertyView::slotActiveDocument(const Gui::Document &doc)
297
{
298
    checkEnable(doc.getDocument()->getName());
299
}
300

301
void PropertyView::checkEnable(const char *doc) {
302
    if(ViewParams::instance()->getEnablePropertyViewForInactiveDocument()) {
303
        setEnabled(true);
304
        return;
305
    }
306
    // check if at least one selected object is part of the active document
307
    setEnabled(!Selection().hasSelection()
308
            || Selection().hasSelection(doc, ResolveMode::NoResolve));
309
}
310

311
struct PropertyView::PropInfo
312
{
313
    std::string propName;
314
    int propId;
315
    std::vector<App::Property*> propList;
316
};
317

318
struct PropertyView::PropFind {
319
    const PropInfo& item;
320
    explicit PropFind(const PropInfo& item) : item(item) {}
321
    bool operator () (const PropInfo& elem) const
322
    {
323
        return (elem.propId == item.propId) &&
324
               (elem.propName == item.propName);
325
    }
326
};
327

328
void PropertyView::onSelectionChanged(const SelectionChanges& msg)
329
{
330
    if (msg.Type != SelectionChanges::AddSelection &&
331
        msg.Type != SelectionChanges::RmvSelection &&
332
        msg.Type != SelectionChanges::SetSelection &&
333
        msg.Type != SelectionChanges::ClrSelection)
334
        return;
335

336
    // clear the properties.
337
    timer->start(ViewParams::instance()->getPropertyViewTimer());
338
}
339

340
void PropertyView::onTimer()
341
{
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");
345
        return;
346
    }
347
    Base::StateLocker guard(this->updating);
348

349
    timer->stop();
350

351
    if(!this->isSelectionAttached()) {
352
        propertyEditorData->buildUp();
353
        propertyEditorView->buildUp();
354
        clearPropertyItemSelection();
355
        return;
356
    }
357

358
    if(!Gui::Selection().hasSelection()) {
359
        auto gdoc = TreeWidget::selectedDocument();
360
        if(!gdoc || !gdoc->getDocument()) {
361
            propertyEditorData->buildUp();
362
            propertyEditorView->buildUp();
363
            clearPropertyItemSelection();
364
            return;
365
        }
366

367
        PropertyModel::PropertyList docProps;
368

369
        auto doc = gdoc->getDocument();
370
        std::map<std::string,App::Property*> props;
371
        doc->getPropertyMap(props);
372
        for(auto &v : 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);
377
        return;
378
    }
379

380
    std::set<App::DocumentObject *> objSet;
381

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();
390
        if(!ob) continue;
391

392
        // Do not process an object more than once
393
        if(!objSet.insert(ob).second)
394
            continue;
395

396
        std::vector<App::Property*> dataList;
397
        std::map<std::string, App::Property*> viewList;
398

399
        auto vp = Application::Instance->getViewProvider(ob);
400
        if(!vp) {
401
            checkLink = false;
402
            ob->getPropertyList(dataList);
403
            continue;
404
        }
405

406
        if(vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) {
407
            auto cvp = static_cast<ViewProviderDocumentObject*>(vp);
408
            if(vpLast && cvp!=vpLast)
409
                checkLink = false;
410
            vpLast = cvp;
411
        }
412

413
        ob->getPropertyList(dataList);
414

415
        // get the properties as map here because it doesn't matter to have them sorted alphabetically
416
        vp->getPropertyMap(viewList);
417

418
        // store the properties with <name,id> as key in a map
419
        {
420
            for (auto prop : dataList) {
421
                if (isPropertyHidden(prop))
422
                    continue;
423

424
                PropInfo nameType;
425
                nameType.propName = prop->getName();
426
                nameType.propId = prop->getTypeId().getKey();
427

428
                auto pi = std::find_if(propDataMap.begin(), propDataMap.end(), PropFind(nameType));
429
                if (pi != propDataMap.end()) {
430
                    pi->propList.push_back(prop);
431
                }
432
                else {
433
                    nameType.propList.push_back(prop);
434
                    propDataMap.push_back(nameType);
435
                }
436
            }
437
        }
438
        // the same for the view properties
439
        {
440
            std::map<std::string, App::Property*>::iterator pt;
441
            for (pt = viewList.begin(); pt != viewList.end(); ++pt) {
442
                if (isPropertyHidden(pt->second))
443
                    continue;
444

445
                PropInfo nameType;
446
                nameType.propName = pt->first;
447
                nameType.propId = pt->second->getTypeId().getKey();
448

449
                auto pi = std::find_if(propViewMap.begin(), propViewMap.end(), PropFind(nameType));
450
                if (pi != propViewMap.end()) {
451
                    pi->propList.push_back(pt->second);
452
                }
453
                else {
454
                    nameType.propList.push_back(pt->second);
455
                    propViewMap.push_back(nameType);
456
                }
457
            }
458
        }
459
    }
460

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
463
    // name and id
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;
468

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();
473
        auto linked = obj;
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))
481
                    continue;
482
                std::string name(prop->getName());
483
                auto it = propMap.find(name);
484
                if(it!=propMap.end() && !isPropertyHidden(it->second))
485
                    continue;
486
                std::vector<App::Property*> items(1,prop);
487
                if(prop->testStatus(App::Property::PropDynamic))
488
                    dataPropsMap.emplace(name+"*",std::move(items));
489
                else
490
                    dataProps.emplace_back(name+"*", std::move(items));
491
            }
492
            auto vpLinked = Application::Instance->getViewProvider(linked);
493
            if(vpLinked) {
494
                propMap.clear();
495
                vpLast->getPropertyMap(propMap);
496
                dataList.clear();
497
                vpLinked->getPropertyList(dataList);
498
                for(auto prop : dataList) {
499
                    if(isPropertyHidden(prop))
500
                        continue;
501
                    std::string name(prop->getName());
502
                    auto it = propMap.find(name);
503
                    if(it!=propMap.end() && !isPropertyHidden(it->second))
504
                        continue;
505
                    std::vector<App::Property*> items(1,prop);
506
                    viewProps.emplace_back(name+"*", std::move(items));
507
                }
508
            }
509
        }
510
    }
511

512
    for(auto &v : dataPropsMap)
513
        dataProps.emplace_back(v.first,std::move(v.second));
514

515
    dataPropsMap.clear();
516

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));
521
            else
522
                dataProps.emplace_back(it->propName, std::move(it->propList));
523
        }
524
    }
525

526
    for(auto &v : dataPropsMap)
527
        dataProps.emplace_back(v.first,std::move(v.second));
528

529
    propertyEditorData->buildUp(std::move(dataProps),true);
530

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));
534
    }
535

536
    propertyEditorView->buildUp(std::move(viewProps));
537

538
    // make sure the editors are enabled/disabled properly
539
    checkEnable();
540
}
541

542
void PropertyView::tabChanged(int index)
543
{
544
    _GetParam()->SetInt("LastTabIndex",index);
545
}
546

547
void PropertyView::changeEvent(QEvent *e)
548
{
549
    if (e->type() == QEvent::LanguageChange) {
550
        tabs->setTabText(0, tr("View"));
551
        tabs->setTabText(1, tr("Data"));
552
    }
553

554
    QWidget::changeEvent(e);
555
}
556

557
/* TRANSLATOR Gui::DockWnd::PropertyDockView */
558

559
PropertyDockView::PropertyDockView(Gui::Document* pcDocument, QWidget *parent)
560
  : DockWindow(pcDocument,parent)
561
{
562
    setWindowTitle(tr("Property View"));
563

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);
569

570
    resize( 200, 400 );
571
}
572

573
PropertyDockView::~PropertyDockView() = default;
574

575
#include "moc_PropertyView.cpp"
576

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.