FreeCAD

Форк
0
/
Placement.cpp 
1491 строка · 46.7 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net>     *
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 <QClipboard>
26
#include <QDockWidget>
27
#include <QKeyEvent>
28
#include <QMessageBox>
29
#include <QMetaObject>
30
#include <QSignalMapper>
31
#endif
32

33
#include <App/ComplexGeoData.h>
34
#include <App/Document.h>
35
#include <App/DocumentObjectPy.h>
36
#include <App/GeoFeature.h>
37
#include <Base/Console.h>
38
#include <Base/PlacementPy.h>
39
#include <Base/Tools.h>
40
#include <Gui/Application.h>
41
#include <Gui/Command.h>
42
#include <Gui/DockWindowManager.h>
43
#include <Gui/Document.h>
44
#include <Gui/PythonWrapper.h>
45
#include <Gui/Selection.h>
46
#include <Gui/ViewProvider.h>
47
#include <Gui/Window.h>
48

49
#include "Placement.h"
50
#include "ui_Placement.h"
51

52

53
using namespace Gui::Dialog;
54
namespace sp = std::placeholders;
55

56
namespace Gui { namespace Dialog {
57
class find_placement
58
{
59
public:
60
    explicit find_placement(const std::string& name) : propertyname(name)
61
    {
62
    }
63
    bool operator () (const std::pair<std::string, App::Property*>& elem) const
64
    {
65
        if (elem.first == propertyname) {
66
            //  flag set that property is read-only or hidden
67
            if (elem.second->testStatus(App::Property::ReadOnly) || elem.second->testStatus(App::Property::Hidden))
68
                return false;
69
            App::PropertyContainer* parent = elem.second->getContainer();
70
            if (parent) {
71
                //  flag set that property is read-only or hidden
72
                if (parent->isReadOnly(elem.second) ||
73
                    parent->isHidden(elem.second))
74
                    return false;
75
            }
76
            return elem.second->isDerivedFrom
77
                (Base::Type::fromName("App::PropertyPlacement"));
78
        }
79

80
        return false;
81
    }
82

83
    static App::PropertyPlacement* getProperty(const App::DocumentObject* obj,
84
                                               const std::string& propertyName)
85
    {
86
        std::map<std::string,App::Property*> props;
87
        obj->getPropertyMap(props);
88

89
        // search for the placement property
90
        std::map<std::string,App::Property*>::iterator jt;
91
        jt = std::find_if(props.begin(), props.end(), find_placement(propertyName));
92
        if (jt != props.end()) {
93
            return dynamic_cast<App::PropertyPlacement*>(jt->second);
94
        }
95

96
        return nullptr;
97
    }
98

99
    std::string propertyname;
100
};
101

102
}
103
}
104

105
PlacementHandler::PlacementHandler()
106
  : propertyName{"Placement"}
107
  , changeProperty{false}
108
  , ignoreTransaction{false}
109
{
110
    setupDocument();
111
}
112

113
void PlacementHandler::openTransactionIfNeeded()
114
{
115
    if (changeProperty) {
116
        openTransaction();
117
    }
118
}
119

120
void PlacementHandler::setPropertyName(const std::string& name)
121
{
122
    propertyName = name;
123
    // Only with the Placement property it's possible to directly change the Inventor representation.
124
    // For other placement properties with a different name the standard property handling must be used.
125
    changeProperty = (propertyName != "Placement");
126
}
127

128
void PlacementHandler::setIgnoreTransactions(bool value)
129
{
130
    ignoreTransaction = value;
131
}
132

133
void PlacementHandler::setSelection(const std::vector<Gui::SelectionObject>& selection)
134
{
135
    selectionObjects = selection;
136
}
137

138
void PlacementHandler::reselectObjects()
139
{
140
    //we have to clear selection and reselect original object(s)
141
    //else later on the rotation is applied twice because there will
142
    //be 2 (vertex) objects in the selection, and even if both are subobjects
143
    //of the same object the rotation still gets applied twice
144
    Gui::Selection().clearSelection();
145
    //reselect original object that was selected when placement dlg first opened
146
    for (const auto& it : selectionObjects) {
147
        Gui::Selection().addSelection(it);
148
    }
149
}
150

151
const App::DocumentObject* PlacementHandler::getFirstOfSelection() const
152
{
153
    if (!selectionObjects.empty()) {
154
        return selectionObjects.front().getObject();
155
    }
156

157
    return nullptr;
158
}
159

160
const std::string& PlacementHandler::getPropertyName() const
161
{
162
    return propertyName;
163
}
164

165
void PlacementHandler::appendDocument(const std::string& name)
166
{
167
    documents.insert(name);
168
}
169

170
void PlacementHandler::activatedDocument(const std::string& name)
171
{
172
    appendDocument(name);
173

174
    if (changeProperty) {
175
        QMetaObject::invokeMethod(this, "openTransaction", Qt::QueuedConnection);
176
    }
177
}
178

179
void PlacementHandler::openTransaction()
180
{
181
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
182
    if (activeDoc)
183
        activeDoc->openTransaction("Placement");
184
}
185

186
void PlacementHandler::revertTransformation()
187
{
188
    for (const auto & it : documents) {
189
        Gui::Document* document = Application::Instance->getDocument(it.c_str());
190
        if (document) {
191
            if (!changeProperty) {
192
                revertTransformationOfViewProviders(document);
193
            }
194
            else {
195
                abortCommandIfActive(document);
196
            }
197
        }
198
    }
199
}
200

201
std::vector<const App::DocumentObject*> PlacementHandler::getObjects(const Gui::Document* document) const
202
{
203
    auto objs = document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId());
204
    std::vector<const App::DocumentObject*> list;
205
    list.insert(list.begin(), objs.begin(), objs.end());
206
    return list;
207
}
208

209
std::vector<const App::DocumentObject*> PlacementHandler::getSelectedObjects(const Gui::Document* document) const
210
{
211
    App::Document* doc = document->getDocument();
212
    std::vector<const App::DocumentObject*> list;
213
    list.reserve(selectionObjects.size());
214
    for (const auto& it : selectionObjects) {
215
        const App::DocumentObject* obj = it.getObject();
216
        if (obj && obj->getDocument() == doc) {
217
            list.push_back(obj);
218
        }
219
    }
220

221
    if (list.empty()) {
222
        auto objs = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), doc->getName());
223
        list.insert(list.begin(), objs.begin(), objs.end());
224
    }
225

226
    return list;
227
}
228

229
void PlacementHandler::revertTransformationOfViewProviders(Gui::Document* document)
230
{
231
    std::vector<const App::DocumentObject*> obj = getObjects(document);
232
    for (const auto & it : obj) {
233
        auto property = find_placement::getProperty(it, this->propertyName);
234
        if (property) {
235
            Base::Placement cur = property->getValue();
236
            Gui::ViewProvider* vp = document->getViewProvider(it);
237
            if (vp) {
238
                vp->setTransformation(cur.toMatrix());
239
            }
240
        }
241
    }
242
}
243

244
void PlacementHandler::setRefPlacement(const Base::Placement& plm)
245
{
246
    ref = plm;
247
}
248

249
const Base::Placement& PlacementHandler::getRefPlacement() const
250
{
251
    return ref;
252
}
253

254
void PlacementHandler::applyPlacement(const Base::Placement& p, bool incremental)
255
{
256
    Gui::Document* document = Application::Instance->activeDocument();
257
    if (!document)
258
        return;
259

260
    std::vector<const App::DocumentObject*> sel = getSelectedObjects(document);
261
    if (!sel.empty()) {
262
        for (const auto & it : sel) {
263
            applyPlacement(document, it, p, incremental);
264
        }
265
    }
266
    else {
267
        Base::Console().Warning("No object selected.\n");
268
    }
269
}
270

271
void PlacementHandler::applyPlacement(const Gui::Document* document, const App::DocumentObject* obj,
272
                                      const Base::Placement& p, bool incremental)
273
{
274
    auto property = find_placement::getProperty(obj, this->propertyName);
275
    if (property) {
276
        Base::Placement cur = property->getValue();
277
        if (incremental)
278
            cur = p * cur;
279
        else
280
            cur = p;
281

282
        if (!changeProperty) {
283
            Gui::ViewProvider* vp = document->getViewProvider(obj);
284
            if (vp) {
285
                vp->setTransformation(cur.toMatrix());
286
            }
287
        }
288
        else {
289
            property->setValue(cur);
290
        }
291
    }
292
}
293

294
void PlacementHandler::applyPlacement(const QString& data, bool incremental)
295
{
296
    Gui::Document* document = Application::Instance->activeDocument();
297
    if (!document)
298
        return;
299

300
    // When directly changing the property we now only have to commit the transaction,
301
    // do a recompute and open a new transaction
302
    if (changeProperty) {
303
        commitCommandIfActive(document);
304
        tryRecompute(document);
305
        openCommandIfActive(document);
306
    }
307
    else {
308
        std::vector<const App::DocumentObject*> sel = getSelectedObjects(document);
309
        if (!sel.empty()) {
310
            openCommandIfActive(document);
311
            for (const auto & it : sel) {
312
                applyPlacement(it, data, incremental);
313
            }
314
            commitCommandIfActive(document);
315
            tryRecompute(document);
316
        }
317
        else {
318
            Base::Console().Warning("No object selected.\n");
319
        }
320
    }
321
}
322

323
void PlacementHandler::applyPlacement(const App::DocumentObject* obj, const QString& data, bool incremental)
324
{
325
    auto property = find_placement::getProperty(obj, this->propertyName);
326
    if (property) {
327
        QString cmd;
328
        if (incremental) {
329
            cmd = getIncrementalPlacement(obj, data);
330
        }
331
        else {
332
            cmd = getSimplePlacement(obj, data);
333
        }
334

335
        Gui::Command::runCommand(Gui::Command::App, cmd.toLatin1());
336
    }
337
}
338

339
QString PlacementHandler::getIncrementalPlacement(const App::DocumentObject* obj, const QString& data) const
340
{
341
    return QString::fromLatin1(
342
        R"(App.getDocument("%1").%2.%3=%4.multiply(App.getDocument("%1").%2.%3))")
343
        .arg(QString::fromLatin1(obj->getDocument()->getName()),
344
             QString::fromLatin1(obj->getNameInDocument()),
345
             QString::fromLatin1(this->propertyName.c_str()),
346
             data);
347
}
348

349
QString PlacementHandler::getSimplePlacement(const App::DocumentObject* obj, const QString& data) const
350
{
351
    return QString::fromLatin1(
352
        "App.getDocument(\"%1\").%2.%3=%4")
353
        .arg(QString::fromLatin1(obj->getDocument()->getName()),
354
             QString::fromLatin1(obj->getNameInDocument()),
355
             QString::fromLatin1(this->propertyName.c_str()),
356
             data);
357
}
358

359
Base::Vector3d PlacementHandler::computeCenterOfMass() const
360
{
361
    Base::Vector3d centerOfMass;
362
    std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
363
        (App::GeoFeature::getClassTypeId());
364
    if (!sel.empty()) {
365
        for (auto it : sel) {
366
            const App::PropertyComplexGeoData* propgeo = static_cast<App::GeoFeature*>(it)->getPropertyOfGeometry();
367
            const Data::ComplexGeoData* geodata = propgeo ? propgeo->getComplexData() : nullptr;
368
            if (geodata && geodata->getCenterOfGravity(centerOfMass)) {
369
                break;
370
            }
371
        }
372
    }
373
    return centerOfMass;
374
}
375

376
void PlacementHandler::setCenterOfMass(const Base::Vector3d& pnt)
377
{
378
    cntOfMass = pnt;
379
}
380

381
Base::Vector3d PlacementHandler::getCenterOfMass() const
382
{
383
    return cntOfMass;
384
}
385

386
std::tuple<Base::Vector3d, std::vector<Base::Vector3d>> PlacementHandler::getSelectedPoints() const
387
{
388
    std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
389
    std::vector<Base::Vector3d> picked;
390
    //combine all pickedpoints into single vector
391
    //even if points are from separate objects
392
    Base::Vector3d firstSelected; //first selected will be central point when 3 points picked
393
    for (auto it = selection.begin(); it != selection.end(); ++it) {
394
        std::vector<Base::Vector3d> points = it->getPickedPoints();
395
        if (it == selection.begin() && !points.empty()) {
396
            firstSelected = points[0];
397
        }
398

399
        picked.insert(picked.begin(), points.begin(), points.end());
400
    }
401

402
    return std::make_tuple(firstSelected, picked);
403
}
404

405
void PlacementHandler::tryRecompute(Gui::Document* document)
406
{
407
    try {
408
        document->getDocument()->recompute();
409
    }
410
    catch (...) {
411
    }
412
}
413

414
void PlacementHandler::setupDocument()
415
{
416
    //NOLINTBEGIN
417
    connectAct = Application::Instance->signalActiveDocument.connect
418
        (std::bind(&PlacementHandler::slotActiveDocument, this, sp::_1));
419
    //NOLINTEND
420
    App::Document* activeDoc = App::GetApplication().getActiveDocument();
421
    if (activeDoc) {
422
        appendDocument(activeDoc->getName());
423
    }
424
}
425

426
void PlacementHandler::slotActiveDocument(const Gui::Document& doc)
427
{
428
    activatedDocument(doc.getDocument()->getName());
429
}
430

431
void PlacementHandler::openCommandIfActive(Gui::Document* doc)
432
{
433
    if (!ignoreTransaction) {
434
        doc->openCommand(QT_TRANSLATE_NOOP("Command", "Placement"));
435
    }
436
}
437

438
void PlacementHandler::commitCommandIfActive(Gui::Document* doc)
439
{
440
    if (!ignoreTransaction) {
441
        doc->commitCommand();
442
    }
443
}
444

445
void PlacementHandler::abortCommandIfActive(Gui::Document* doc)
446
{
447
    if (!ignoreTransaction) {
448
        doc->abortCommand();
449
    }
450
}
451

452
// ----------------------------------------------------------------------------
453

454
/* TRANSLATOR Gui::Dialog::Placement */
455

456
Placement::Placement(QWidget* parent, Qt::WindowFlags fl)
457
  : QDialog(parent, fl)
458
  , ui{nullptr}
459
{
460
    setupUi();
461
    setupConnections();
462
    setupUnits();
463
    setupSignalMapper();
464
    setupRotationMethod();
465
}
466

467
Placement::~Placement()
468
{
469
    delete ui;
470
}
471

472
void Placement::setupUi()
473
{
474
    ui = new Ui_Placement();
475
    ui->setupUi(this);
476
    ui->gridLayout->removeItem(ui->vSpacer);
477
}
478

479
void Placement::setupConnections()
480
{
481
    QPushButton* applyButton = ui->buttonBox->button(QDialogButtonBox::Apply);
482
    connect(applyButton, &QPushButton::clicked,
483
            this, &Placement::onApplyButtonClicked);
484
    connect(ui->applyIncrementalPlacement, &QCheckBox::toggled,
485
            this, &Placement::onApplyIncrementalPlacementToggled);
486
    connect(ui->resetButton, &QPushButton::clicked,
487
            this, &Placement::onResetButtonClicked);
488
    connect(ui->centerOfMass, &QCheckBox::toggled,
489
            this, &Placement::onCenterOfMassToggled);
490
    connect(ui->selectedVertex, &QPushButton::clicked,
491
            this, &Placement::onSelectedVertexClicked);
492
    connect(ui->applyAxial, &QPushButton::clicked,
493
            this, &Placement::onApplyAxialClicked);
494
}
495

496
void Placement::setupUnits()
497
{
498
    ui->xPos->setUnit(Base::Unit::Length);
499
    ui->yPos->setUnit(Base::Unit::Length);
500
    ui->zPos->setUnit(Base::Unit::Length);
501
    ui->axialPos->setUnit(Base::Unit::Length);
502
    ui->xCnt->setValue(Base::Quantity(0, Base::Unit::Length));
503
    ui->yCnt->setValue(Base::Quantity(0, Base::Unit::Length));
504
    ui->zCnt->setValue(Base::Quantity(0, Base::Unit::Length));
505
    ui->angle->setUnit(Base::Unit::Angle);
506
    ui->yawAngle->setMaximum(180.0F);
507
    ui->yawAngle->setMinimum(-180.0F);
508
    ui->yawAngle->setUnit(Base::Unit::Angle);
509
    ui->yawAngle->checkRangeInExpression(true);
510
    ui->pitchAngle->setMaximum(90.0F);
511
    ui->pitchAngle->setMinimum(-90.0F);
512
    ui->pitchAngle->setUnit(Base::Unit::Angle);
513
    ui->pitchAngle->checkRangeInExpression(true);
514
    ui->rollAngle->setMaximum(180.0F);
515
    ui->rollAngle->setMinimum(-180.0F);
516
    ui->rollAngle->setUnit(Base::Unit::Angle);
517
    ui->rollAngle->checkRangeInExpression(true);
518
}
519

520
void Placement::setupSignalMapper()
521
{
522
    // create a signal mapper in order to have one slot to perform the change
523
    signalMapper = new QSignalMapper(this);
524
    signalMapper->setMapping(this, 0);
525

526
    int id = 1;
527
    QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
528
    for (const auto & it : sb) {
529
        connect(it, qOverload<double>(&QuantitySpinBox::valueChanged), signalMapper, qOverload<>(&QSignalMapper::map));
530
        signalMapper->setMapping(it, id++);
531
    }
532

533
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
534
    connect(signalMapper, qOverload<int>(&QSignalMapper::mapped),
535
            this, &Placement::onPlacementChanged);
536
#else
537
    connect(signalMapper, &QSignalMapper::mappedInt,
538
            this, &Placement::onPlacementChanged);
539
#endif
540
}
541

542
void Placement::setupRotationMethod()
543
{
544
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement");
545
    long index = hGrp->GetInt("RotationMethod");
546
    ui->rotationInput->setCurrentIndex(index);
547
    ui->stackedWidget->setCurrentIndex(index);
548
}
549

550
void Placement::showDefaultButtons(bool ok)
551
{
552
    ui->buttonBox->setVisible(ok);
553
    ui->buttonBoxLayout->invalidate();
554
    if (ok) {
555
        ui->buttonBoxLayout->insertSpacerItem(0, ui->buttonBoxSpacer);
556
    }
557
    else {
558
        ui->buttonBoxLayout->removeItem(ui->buttonBoxSpacer);
559
    }
560
}
561

562
void Placement::open()
563
{
564
    handler.openTransactionIfNeeded();
565
}
566

567
QWidget* Placement::getInvalidInput() const
568
{
569
    QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
570
    for (const auto & it : sb) {
571
        if (!it->hasValidInput())
572
            return it;
573
    }
574
    return nullptr;
575
}
576

577
void Placement::onPlacementChanged(int)
578
{
579
    // If there are listeners to the 'placementChanged' signal we rely
580
    // on that the listener updates any placement. If no listeners
581
    // are connected the placement is applied to all selected objects
582
    // automatically.
583
    bool incr = ui->applyIncrementalPlacement->isChecked();
584
    Base::Placement plm = this->getPlacement();
585
    handler.applyPlacement(plm, incr);
586

587
    QVariant data = QVariant::fromValue<Base::Placement>(plm);
588
    Q_EMIT placementChanged(data, incr, false);
589
}
590

591
void Placement::onCenterOfMassToggled(bool on)
592
{
593
    ui->xCnt->setDisabled(on);
594
    ui->yCnt->setDisabled(on);
595
    ui->zCnt->setDisabled(on);
596

597
    if (on) {
598
        Base::Vector3d pnt = handler.computeCenterOfMass();
599
        handler.setCenterOfMass(pnt);
600
        ui->xCnt->setValue(pnt.x);
601
        ui->yCnt->setValue(pnt.y);
602
        ui->zCnt->setValue(pnt.z);
603
    }
604
}
605

606
void Placement::onSelectedVertexClicked()
607
{
608
    ui->centerOfMass->setChecked(false);
609

610
    Base::Vector3d center;
611
    bool success = false;
612
    auto [firstSelected, picked] = handler.getSelectedPoints();
613
    handler.reselectObjects();
614

615
    if (picked.size() == 1) {
616
        center = picked[0];
617
        success = true;
618
    }
619
    else if (picked.size() == 2) {
620
        //average the coords to get center of rotation
621
        center = (picked[0] + picked[1]) / 2.0;
622

623
        //setup a customized axis since the user selected 2 points
624
        //keep any existing angle, but setup our own axis
625
        Base::Placement plm = getPlacement();
626
        Base::Rotation rot = plm.getRotation();
627
        Base::Vector3d tmp;
628
        double angle;
629
        rot.getRawValue(tmp, angle);
630
        Base::Vector3d axis;
631
        if (firstSelected == picked[0]){
632
            axis = Base::Vector3d(picked[1] - picked[0]);
633
        }
634
        else {
635
            axis = Base::Vector3d(picked[0] - picked[1]);
636
        }
637
        double length = axis.Length();
638
        Base::Console().Message("Distance: %.8f\n",length);
639
        if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click
640
            QLocale loc;
641
            QApplication::clipboard()->setText(loc.toString(length,'g',8));
642
        }
643
        else {
644
            Base::Console().Message("(Shift + click Selected points button to copy distance to clipboard)\n");
645
        }
646
        axis.Normalize();
647
        rot.setValue(axis, angle);
648
        plm.setRotation(rot);
649
        setPlacementData(plm); //creates custom axis, if needed
650
        ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler
651
        ui->stackedWidget->setCurrentIndex(0);
652
        success = true;
653
    }
654
    else if (picked.size() == 3){
655
        /* User selected 3 points, so we find the plane defined by those
656
         * and use the normal vector that contains the first point picked
657
         * as the axis of rotation.
658
         */
659

660
        Base::Vector3d a, b(firstSelected), c; //b is on central axis
661
        if (picked[0] == firstSelected){
662
            a = picked[1];
663
            c = picked[2];
664
        }
665
        else if (picked[1] == firstSelected){
666
            a = picked[0];
667
            c = picked[2];
668
        }
669
        else if (picked[2] == firstSelected){
670
            a = picked[0];
671
            c = picked[1];
672
        }
673

674
        Base::Vector3d norm((a-b).Cross(c-b));
675
        norm.Normalize();
676
        center = b;
677

678
        //setup a customized axis normal to the plane
679
        //keep any existing angle, but setup our own axis
680
        Base::Placement plm = getPlacement();
681
        Base::Rotation rot = plm.getRotation();
682
        Base::Vector3d tmp;
683
        double angle;
684
        rot.getRawValue(tmp, angle);
685
        double length = (a-c).Length();
686
        Base::Console().Message("Distance: %.8f\n",length);
687
        Base::Vector3d v1(a-b);
688
        Base::Vector3d v2(c-b);
689
        v1.Normalize();
690
        v2.Normalize();
691
        double targetAngle = Base::toDegrees(v2.GetAngle(v1));
692
        Base::Console().Message("Target angle: %.8f degrees, complementary: %.8f degrees\n",targetAngle, 90.0-targetAngle);
693
        if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click
694
            QLocale loc;
695
            QApplication::clipboard()->setText(loc.toString(targetAngle,'g',8));
696
            Base::Console().Message("(Angle copied to clipboard, but you might need to use a negative (-) angle sometimes.)\n");
697
        }
698
        else {
699
            Base::Console().Message("(Shift + click Selected points button to copy angle to clipboard)\n");
700
        }
701
        rot.setValue(norm, angle);
702
        plm.setRotation(rot);
703
        setPlacementData(plm); //creates custom axis, if needed
704
        ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler
705
        ui->stackedWidget->setCurrentIndex(0);
706
        success = true;
707
    }
708

709
    handler.setCenterOfMass(center);
710
    ui->xCnt->setValue(center.x);
711
    ui->yCnt->setValue(center.y);
712
    ui->zCnt->setValue(center.z);
713

714
    if (!success) {
715
        Base::Console().Warning("Placement selection error.  Select either 1 or 2 points.\n");
716
        QMessageBox msgBox;
717
        msgBox.setText(tr("Please select 1, 2, or 3 points before clicking this button.  A point may be on a vertex, \
718
face, or edge.  If on a face or edge the point used will be the point at the mouse position along \
719
face or edge.  If 1 point is selected it will be used as the center of rotation.  If 2 points are \
720
selected the midpoint between them will be the center of rotation and a new custom axis will be \
721
created, if needed.  If 3 points are selected the first point becomes the center of rotation and \
722
lies on the vector that is normal to the plane defined by the 3 points.  Some distance and angle \
723
information is provided in the report view, which can be useful when aligning objects.  For your \
724
convenience when Shift + click is used the appropriate distance or angle is copied to the clipboard."));
725
        msgBox.exec();
726
    }
727
}
728

729
void Placement::onApplyAxialClicked()
730
{
731
    signalMapper->blockSignals(true);
732
    double axPos = ui->axialPos->value().getValue();
733
    Base::Placement p = getPlacementData();
734
    double angle;
735
    Base::Vector3d axis;
736
    p.getRotation().getValue(axis, angle);
737
    Base::Vector3d curPos (p.getPosition());
738
    Base::Vector3d newPos;
739
    Qt::KeyboardModifiers km = QApplication::keyboardModifiers();
740
    if (km == Qt::ShiftModifier){ //go opposite direction on Shift+click
741
        newPos = Base::Vector3d(curPos.x-(axis.x*axPos),curPos.y-(axis.y*axPos),curPos.z-(axis.z*axPos));
742
    }
743
    else {
744
        newPos = Base::Vector3d(curPos.x+(axis.x*axPos),curPos.y+(axis.y*axPos),curPos.z+(axis.z*axPos));
745
    }
746
    ui->xPos->setValue(Base::Quantity(newPos.x,Base::Unit::Length));
747
    ui->yPos->setValue(Base::Quantity(newPos.y,Base::Unit::Length));
748
    ui->zPos->setValue(Base::Quantity(newPos.z,Base::Unit::Length));
749
    signalMapper->blockSignals(false);
750
    onPlacementChanged(0);
751
}
752

753
void Placement::onApplyIncrementalPlacementToggled(bool on)
754
{
755
    if (on) {
756
        handler.setRefPlacement(getPlacementData());
757
        onResetButtonClicked();
758
    }
759
    else {
760
        Base::Placement p = getPlacementData();
761
        p = p * handler.getRefPlacement();
762
        setPlacementData(p);
763
        onPlacementChanged(0);
764
    }
765
}
766

767
void Placement::keyPressEvent(QKeyEvent* ke)
768
{
769
    // The placement dialog is embedded into a task panel
770
    // which is a parent widget and will handle the event
771
    ke->ignore();
772
}
773

774
void Placement::reject()
775
{
776
    Base::Placement plm;
777
    handler.applyPlacement(plm, true);
778

779
    QVariant data = QVariant::fromValue<Base::Placement>(plm);
780
    Q_EMIT placementChanged(data, true, false);
781

782
    handler.revertTransformation();
783

784
    // One of the quantity spin boxes still can emit a signal when it has the focus
785
    // but its content is not fully updated.
786
    // In order to override again the placement the signalMapper is blocked
787
    // See related forum thread:
788
    // https://forum.freecad.org/viewtopic.php?f=3&t=44341#p378659
789
    QSignalBlocker block(signalMapper);
790
    QDialog::reject();
791
}
792

793
void Placement::accept()
794
{
795
    if (onApply()) {
796
        handler.revertTransformation();
797
        QDialog::accept();
798
    }
799
}
800

801
void Placement::onApplyButtonClicked()
802
{
803
    onApply();
804
}
805

806
void Placement::showErrorMessage()
807
{
808
    QMessageBox msg(this);
809
    msg.setWindowTitle(tr("Incorrect quantity"));
810
    msg.setIcon(QMessageBox::Critical);
811
    msg.setText(tr("There are input fields with incorrect input, please ensure valid placement values!"));
812
    msg.exec();
813
}
814

815
bool Placement::onApply()
816
{
817
    //only process things when we have valid inputs!
818
    QWidget* input = getInvalidInput();
819
    if (input) {
820
        input->setFocus();
821
        showErrorMessage();
822
        return false;
823
    }
824

825
    // If there are listeners to the 'placementChanged' signal we rely
826
    // on that the listener updates any placement. If no listeners
827
    // are connected the placement is applied to all selected objects
828
    // automatically.
829
    bool incr = ui->applyIncrementalPlacement->isChecked();
830
    Base::Placement plm = this->getPlacement();
831
    handler.applyPlacement(getPlacementString(), incr);
832

833
    QVariant data = QVariant::fromValue<Base::Placement>(plm);
834
    Q_EMIT placementChanged(data, incr, true);
835

836
    if (ui->applyIncrementalPlacement->isChecked()) {
837
        QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
838
        for (auto & it : sb) {
839
            it->blockSignals(true);
840
            it->setValue(0);
841
            it->blockSignals(false);
842
        }
843
    }
844

845
    ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement");
846
    hGrp->SetInt("RotationMethod", ui->rotationInput->currentIndex());
847

848
    return true;
849
}
850

851
void Placement::onResetButtonClicked()
852
{
853
    QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
854
    for (auto & it : sb) {
855
        it->blockSignals(true);
856
        it->setValue(0);
857
        it->blockSignals(false);
858
    }
859

860
    onPlacementChanged(0);
861
}
862

863
/*!
864
 * \brief Placement::setSelection
865
 * Sets the array of selection objects.
866
 * \param selection
867
 */
868
void Placement::setSelection(const std::vector<Gui::SelectionObject>& selection)
869
{
870
    handler.setSelection(selection);
871
}
872

873
void Placement::setPropertyName(const std::string& name)
874
{
875
    handler.setPropertyName(name);
876
}
877

878
void Placement::setIgnoreTransactions(bool value)
879
{
880
    handler.setIgnoreTransactions(value);
881
}
882

883
/*!
884
 * \brief Placement::bindObject
885
 * Binds the spin boxes to the placement components of the first object of the selection.
886
 * This requires the call of \a setSelection() beforehand.
887
 */
888
void Placement::bindObject()
889
{
890
    if (const App::DocumentObject* obj = handler.getFirstOfSelection()) {
891
        std::string propertyName = handler.getPropertyName();
892
        bindProperty(obj, propertyName);
893
    }
894
}
895

896
/*!
897
 * \brief Placement::setPlacementAndBindObject
898
 * Sets the placement, binds the spin boxes to the placement components of the passed object and
899
 * sets the name of the placement property.
900
 */
901
void Placement::setPlacementAndBindObject(const App::DocumentObject* obj, const std::string& propertyName)
902
{
903
    if (obj) {
904
        App::PropertyPlacement* prop = find_placement::getProperty(obj, propertyName);
905
        if (prop) {
906
            setPlacement(prop->getValue());
907
            handler.setPropertyName(propertyName);
908
            bindProperty(obj, propertyName);
909
            handler.setSelection({SelectionObject{obj}});
910
        }
911
    }
912
}
913

914
void Placement::bindProperty(const App::DocumentObject* obj, const std::string& propertyName)
915
{
916
    // clang-format off
917
    if (obj) {
918
        App::ObjectIdentifier path = App::ObjectIdentifier::parse(obj, propertyName);
919
        if (path.getProperty()) {
920
            ui->xPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.x")));
921
            ui->yPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.y")));
922
            ui->zPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.z")));
923

924
            ui->xAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.x")));
925
            ui->yAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.y")));
926
            ui->zAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.z")));
927
            ui->angle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Angle")));
928

929
            ui->yawAngle  ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Yaw")));
930
            ui->pitchAngle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Pitch")));
931
            ui->rollAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Roll")));
932

933
            ui->yawAngle->evaluateExpression();
934
            ui->pitchAngle->evaluateExpression();
935
            ui->rollAngle->evaluateExpression();
936
        }
937
    }
938
    // clang-format on
939
}
940

941
Base::Vector3d Placement::getDirection() const
942
{
943
    double x = ui->xAxis->value().getValue();
944
    double y = ui->yAxis->value().getValue();
945
    double z = ui->zAxis->value().getValue();
946
    return Base::Vector3d(x, y, z);
947
}
948

949
void Placement::setPlacement(const Base::Placement& p)
950
{
951
    setPlacementData(p);
952
}
953

954
void Placement::setPlacementData(const Base::Placement& p)
955
{
956
    QSignalBlocker block(signalMapper);
957
    ui->xPos->setValue(Base::Quantity(p.getPosition().x, Base::Unit::Length));
958
    ui->yPos->setValue(Base::Quantity(p.getPosition().y, Base::Unit::Length));
959
    ui->zPos->setValue(Base::Quantity(p.getPosition().z, Base::Unit::Length));
960

961
    double Y,P,R;
962
    p.getRotation().getYawPitchRoll(Y,P,R);
963
    ui->yawAngle->setValue(Base::Quantity(Y, Base::Unit::Angle));
964
    ui->pitchAngle->setValue(Base::Quantity(P, Base::Unit::Angle));
965
    ui->rollAngle->setValue(Base::Quantity(R, Base::Unit::Angle));
966

967
    double angle;
968
    Base::Vector3d axis;
969
    p.getRotation().getRawValue(axis, angle);
970
    ui->xAxis->setValue(axis.x);
971
    ui->yAxis->setValue(axis.y);
972
    ui->zAxis->setValue(axis.z);
973
    ui->angle->setValue(Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle));
974
}
975

976
Base::Placement Placement::getPlacement() const
977
{
978
    Base::Placement p = getPlacementData();
979
    return p;
980
}
981

982
Base::Rotation Placement::getRotationData() const
983
{
984
    Base::Rotation rot;
985
    int index = ui->rotationInput->currentIndex();
986
    if (index == 0) {
987
        Base::Vector3d dir = getDirection();
988
        rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),Base::toRadians(ui->angle->value().getValue()));
989
    }
990
    else if (index == 1) { // Euler angles (XY'Z'')
991
        rot.setYawPitchRoll(
992
            ui->yawAngle->value().getValue(),
993
            ui->pitchAngle->value().getValue(),
994
            ui->rollAngle->value().getValue());
995
    }
996

997
    return rot;
998
}
999

1000
Base::Vector3d Placement::getPositionData() const
1001
{
1002
    return Base::Vector3d(ui->xPos->value().getValue(),
1003
                          ui->yPos->value().getValue(),
1004
                          ui->zPos->value().getValue());
1005
}
1006

1007
Base::Vector3d Placement::getAnglesData() const
1008
{
1009
    return Base::Vector3d(ui->yawAngle->value().getValue(),
1010
                          ui->pitchAngle->value().getValue(),
1011
                          ui->rollAngle->value().getValue());
1012
}
1013

1014
Base::Vector3d Placement::getCenterData() const
1015
{
1016
    if (ui->centerOfMass->isChecked()) {
1017
        return handler.getCenterOfMass();
1018
    }
1019
    return Base::Vector3d(ui->xCnt->value().getValue(),
1020
                          ui->yCnt->value().getValue(),
1021
                          ui->zCnt->value().getValue());
1022
}
1023

1024
Base::Placement Placement::getPlacementData() const
1025
{
1026
    Base::Rotation rot = getRotationData();
1027
    Base::Vector3d pos = getPositionData();
1028
    Base::Vector3d cnt = getCenterData();
1029

1030
    Base::Placement plm(pos, rot, cnt);
1031
    return plm;
1032
}
1033

1034
QString Placement::getPlacementFromEulerAngles() const
1035
{
1036
    Base::Vector3d pos = getPositionData();
1037
    Base::Vector3d ypr = getAnglesData();
1038
    Base::Vector3d cnt = getCenterData();
1039
    return QString::fromLatin1(
1040
        "App.Placement(App.Vector(%1,%2,%3), App.Rotation(%4,%5,%6), App.Vector(%7,%8,%9))")
1041
        .arg(pos.x)
1042
        .arg(pos.y)
1043
        .arg(pos.z)
1044
        .arg(ypr.x)
1045
        .arg(ypr.y)
1046
        .arg(ypr.z)
1047
        .arg(cnt.x)
1048
        .arg(cnt.y)
1049
        .arg(cnt.z);
1050
}
1051

1052
QString Placement::getPlacementFromAxisWithAngle() const
1053
{
1054
    Base::Vector3d pos = getPositionData();
1055
    Base::Vector3d cnt = getCenterData();
1056
    Base::Vector3d dir = getDirection();
1057
    double angle = ui->angle->value().getValue();
1058
    return QString::fromLatin1(
1059
        "App.Placement(App.Vector(%1,%2,%3), App.Rotation(App.Vector(%4,%5,%6),%7), App.Vector(%8,%9,%10))")
1060
        .arg(pos.x)
1061
        .arg(pos.y)
1062
        .arg(pos.z)
1063
        .arg(dir.x)
1064
        .arg(dir.y)
1065
        .arg(dir.z)
1066
        .arg(angle)
1067
        .arg(cnt.x)
1068
        .arg(cnt.y)
1069
        .arg(cnt.z);
1070
}
1071

1072
QString Placement::getPlacementString() const
1073
{
1074
    QString cmd;
1075
    int index = ui->rotationInput->currentIndex();
1076

1077
    if (index == 0) {
1078
        cmd = getPlacementFromAxisWithAngle();
1079
    }
1080
    else if (index == 1) {
1081
        cmd = getPlacementFromEulerAngles();
1082
    }
1083

1084
    return cmd;
1085
}
1086

1087
void Placement::changeEvent(QEvent *e)
1088
{
1089
    if (e->type() == QEvent::LanguageChange) {
1090
        ui->retranslateUi(this);
1091
    }
1092
    else {
1093
        QDialog::changeEvent(e);
1094
    }
1095
}
1096

1097
// ----------------------------------------------
1098

1099
/* TRANSLATOR Gui::Dialog::DockablePlacement */
1100

1101
DockablePlacement::DockablePlacement(QWidget* parent, Qt::WindowFlags fl) : Placement(parent, fl)
1102
{
1103
    Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1104
    QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Placement"),
1105
        this, Qt::BottomDockWidgetArea);
1106
    dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable);
1107
    dw->show();
1108
}
1109

1110
DockablePlacement::~DockablePlacement() = default;
1111

1112
void DockablePlacement::accept()
1113
{
1114
    // closes the dock window
1115
    Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1116
    pDockMgr->removeDockWindow(this);
1117
    Placement::accept();
1118
}
1119

1120
void DockablePlacement::reject()
1121
{
1122
    // closes the dock window
1123
    Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1124
    pDockMgr->removeDockWindow(this);
1125
    Placement::reject();
1126
}
1127

1128
// ----------------------------------------------
1129

1130
/* TRANSLATOR Gui::Dialog::TaskPlacement */
1131

1132
TaskPlacement::TaskPlacement()
1133
{
1134
    this->setButtonPosition(TaskPlacement::South);
1135
    widget = new Placement();
1136
    widget->showDefaultButtons(false);
1137
    addTaskBox(widget);
1138
    connect(widget, &Placement::placementChanged, this, &TaskPlacement::slotPlacementChanged);
1139
}
1140

1141
TaskPlacement::~TaskPlacement() = default;
1142

1143

1144
/*!
1145
 * \brief TaskPlacement::setSelection
1146
 * Sets the array of selection objects.
1147
 * \param selection
1148
 */
1149
void TaskPlacement::setSelection(const std::vector<Gui::SelectionObject>& selection)
1150
{
1151
    widget->setSelection(selection);
1152
}
1153

1154
/*!
1155
 * \brief TaskPlacement::clearSelection
1156
 * Clears the array of selection objects.
1157
 */
1158
void TaskPlacement::clearSelection()
1159
{
1160
    widget->setSelection({});
1161
}
1162

1163
/*!
1164
 * \brief TaskPlacement::bindObject
1165
 * Binds the spin boxes to the placement components of the first object of the selection.
1166
 * This requires the call of \a setSelection() beforehand.
1167
 */
1168
void TaskPlacement::bindObject()
1169
{
1170
    widget->bindObject();
1171
}
1172

1173
void TaskPlacement::setPlacementAndBindObject(const App::DocumentObject* obj,
1174
                                              const std::string& propertyName)
1175
{
1176
    widget->setPlacementAndBindObject(obj, propertyName);
1177
}
1178

1179
void TaskPlacement::open()
1180
{
1181
    widget->open();
1182
}
1183

1184
void TaskPlacement::setPropertyName(const QString& name)
1185
{
1186
    widget->setPropertyName(name.toStdString());
1187
}
1188

1189
QDialogButtonBox::StandardButtons TaskPlacement::getStandardButtons() const
1190
{
1191
    return QDialogButtonBox::Ok|
1192
           QDialogButtonBox::Cancel|
1193
           QDialogButtonBox::Apply;
1194
}
1195

1196
void TaskPlacement::setPlacement(const Base::Placement& p)
1197
{
1198
    widget->setPlacement(p);
1199
}
1200

1201
void TaskPlacement::slotPlacementChanged(const QVariant & p, bool incr, bool data)
1202
{
1203
    Q_EMIT placementChanged(p, incr, data);
1204
}
1205

1206
bool TaskPlacement::accept()
1207
{
1208
    widget->accept();
1209
    return (widget->result() == QDialog::Accepted);
1210
}
1211

1212
bool TaskPlacement::reject()
1213
{
1214
    widget->reject();
1215
    return (widget->result() == QDialog::Rejected);
1216
}
1217

1218
void TaskPlacement::clicked(int id)
1219
{
1220
    if (id == QDialogButtonBox::Apply) {
1221
        widget->onApplyButtonClicked();
1222
    }
1223
}
1224

1225
// ----------------------------------------------
1226

1227
void TaskPlacementPy::init_type()
1228
{
1229
    behaviors().name("TaskPlacement");
1230
    behaviors().doc("TaskPlacement");
1231
    behaviors().set_tp_new(PyMake);
1232
    // you must have overwritten the virtual functions
1233
    behaviors().supportRepr();
1234
    behaviors().supportGetattr();
1235
    behaviors().supportSetattr();
1236
    // clang-format off
1237
    add_varargs_method("setPropertyName", &TaskPlacementPy::setPropertyName,
1238
                       "setPropertyName(string)");
1239
    add_varargs_method("setPlacement", &TaskPlacementPy::setPlacement,
1240
                       "setPlacement(Placement)");
1241
    add_varargs_method("setSelection", &TaskPlacementPy::setSelection,
1242
                       "setSelection(list)");
1243
    add_varargs_method("bindObject", &TaskPlacementPy::bindObject,
1244
                       "bindObject()");
1245
    add_varargs_method("setPlacementAndBindObject", &TaskPlacementPy::setPlacementAndBindObject,
1246
                       "setPlacementAndBindObject(obj, string)");
1247
    add_varargs_method("setIgnoreTransactions", &TaskPlacementPy::setIgnoreTransactions,
1248
                       "setIgnoreTransactions(bool)");
1249
    add_varargs_method("showDefaultButtons", &TaskPlacementPy::showDefaultButtons,
1250
                       "showDefaultButtons(bool)");
1251
    add_varargs_method("accept", &TaskPlacementPy::accept,
1252
                       "accept()");
1253
    add_varargs_method("reject", &TaskPlacementPy::reject,
1254
                       "reject()");
1255
    add_varargs_method("clicked", &TaskPlacementPy::clicked,
1256
                       "clicked()");
1257
    add_varargs_method("open", &TaskPlacementPy::open,
1258
                       "open()");
1259
    add_varargs_method("isAllowedAlterDocument", &TaskPlacementPy::isAllowedAlterDocument,
1260
                       "isAllowedAlterDocument()");
1261
    add_varargs_method("isAllowedAlterView", &TaskPlacementPy::isAllowedAlterView,
1262
                       "isAllowedAlterView()");
1263
    add_varargs_method("isAllowedAlterSelection", &TaskPlacementPy::isAllowedAlterSelection,
1264
                       "isAllowedAlterSelection()");
1265
    add_varargs_method("getStandardButtons", &TaskPlacementPy::getStandardButtons,
1266
                       "getStandardButtons()");
1267
    // clang-format on
1268
}
1269

1270
PyObject* TaskPlacementPy::PyMake(struct _typeobject * type, PyObject * args, PyObject * kwds)
1271
{
1272
    Q_UNUSED(type)
1273
    Q_UNUSED(kwds)
1274
    if (!PyArg_ParseTuple(args, "")) {
1275
        return nullptr;
1276
    }
1277
    return new TaskPlacementPy();
1278
}
1279

1280
TaskPlacementPy::TaskPlacementPy()
1281
    : widget{new Placement}
1282
{
1283
}
1284

1285
TaskPlacementPy::~TaskPlacementPy() = default;
1286

1287
Py::Object TaskPlacementPy::repr()
1288
{
1289
    return Py::String("TaskPlacement");
1290
}
1291

1292
Py::Object TaskPlacementPy::getattr(const char * name)
1293
{
1294
    if (strcmp(name, "form") == 0) {
1295
        Gui::PythonWrapper wrap;
1296
        wrap.loadWidgetsModule();
1297
        return wrap.fromQWidget(widget, "QDialog");
1298
    }
1299
    return BaseType::getattr(name);
1300
}
1301

1302
int TaskPlacementPy::setattr(const char* name, const Py::Object& attr)
1303
{
1304
    if (strcmp(name, "form") == 0 && attr.isNone()) {
1305
        delete widget;
1306
        widget = nullptr;
1307
        return {};
1308
    }
1309
    return BaseType::setattr(name, attr);
1310
}
1311

1312
Py::Object TaskPlacementPy::setPropertyName(const Py::Tuple& args)
1313
{
1314
    const char* propname {};
1315
    if (!PyArg_ParseTuple(args.ptr(), "s", &propname)) {
1316
        throw Py::Exception();
1317
    }
1318

1319
    if (widget) {
1320
        widget->setPropertyName(propname);
1321
    }
1322
    return Py::None();
1323
}
1324

1325
Py::Object TaskPlacementPy::setPlacement(const Py::Tuple& args)
1326
{
1327
    PyObject* plm {};
1328
    if (!PyArg_ParseTuple(args.ptr(), "O!", &Base::PlacementPy::Type, &plm)) {
1329
        throw Py::Exception();
1330
    }
1331

1332
    if (widget) {
1333
        widget->setPlacement(*static_cast<Base::PlacementPy*>(plm)->getPlacementPtr());
1334
    }
1335
    return Py::None();
1336
}
1337

1338
Py::Object TaskPlacementPy::setSelection(const Py::Tuple& args)
1339
{
1340
    std::vector<Gui::SelectionObject> sel;
1341
    Py::Sequence list(args[0]);
1342

1343
    for (const auto& obj : list) {
1344
        if (PyObject_TypeCheck(obj.ptr(), &App::DocumentObjectPy::Type)) {
1345
            auto doc = static_cast<App::DocumentObjectPy*>(obj.ptr());
1346
            sel.emplace_back(doc->getDocumentObjectPtr());
1347
        }
1348
    }
1349

1350
    if (widget) {
1351
        widget->setSelection(sel);
1352
    }
1353
    return Py::None();
1354
}
1355

1356
Py::Object TaskPlacementPy::bindObject(const Py::Tuple& args)
1357
{
1358
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1359
        throw Py::Exception();
1360
    }
1361

1362
    if (widget) {
1363
        widget->bindObject();
1364
    }
1365

1366
    return Py::None();
1367
}
1368

1369
Py::Object TaskPlacementPy::setPlacementAndBindObject(const Py::Tuple& args)
1370
{
1371
    Py::Object object = args[0];
1372
    Py::String name = args[1];
1373
    std::string propName = static_cast<std::string>(name);
1374

1375
    if (PyObject_TypeCheck(object.ptr(), &App::DocumentObjectPy::Type)) {
1376
        auto py = static_cast<App::DocumentObjectPy*>(object.ptr());
1377
        auto obj = py->getDocumentObjectPtr();
1378

1379
        if (widget) {
1380
            widget->setPlacementAndBindObject(obj, propName);
1381
        }
1382
    }
1383

1384
    return Py::None();
1385
}
1386

1387
Py::Object TaskPlacementPy::setIgnoreTransactions(const Py::Tuple& args)
1388
{
1389
    if (widget) {
1390
        widget->setIgnoreTransactions(Py::Boolean(args[0]));
1391
    }
1392
    return Py::None();
1393
}
1394

1395
Py::Object TaskPlacementPy::showDefaultButtons(const Py::Tuple& args)
1396
{
1397
    if (widget) {
1398
        widget->showDefaultButtons(Py::Boolean(args[0]));
1399
    }
1400
    return Py::None();
1401
}
1402

1403
Py::Object TaskPlacementPy::accept(const Py::Tuple& args)
1404
{
1405
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1406
        throw Py::Exception();
1407
    }
1408

1409
    bool res = true;
1410
    if (widget) {
1411
        widget->accept();
1412
        res = widget->result() == QDialog::Accepted;
1413
    }
1414
    return Py::Boolean(res);
1415
}
1416

1417
Py::Object TaskPlacementPy::reject(const Py::Tuple& args)
1418
{
1419
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1420
        throw Py::Exception();
1421
    }
1422

1423
    bool res = true;
1424
    if (widget) {
1425
        widget->reject();
1426
        res = widget->result() == QDialog::Rejected;
1427
    }
1428
    return Py::Boolean(res);
1429
}
1430

1431
Py::Object TaskPlacementPy::clicked(const Py::Tuple& args)
1432
{
1433
    int index {};
1434
    if (!PyArg_ParseTuple(args.ptr(), "i", &index)) {
1435
        throw Py::Exception();
1436
    }
1437

1438
    if (widget && index == QDialogButtonBox::Apply) {
1439
        widget->onApplyButtonClicked();
1440
    }
1441
    return Py::None();
1442
}
1443

1444
Py::Object TaskPlacementPy::open(const Py::Tuple& args)
1445
{
1446
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1447
        throw Py::Exception();
1448
    }
1449

1450
    if (widget) {
1451
        widget->open();
1452
    }
1453
    return Py::None();
1454
}
1455

1456
Py::Object TaskPlacementPy::isAllowedAlterDocument(const Py::Tuple& args)
1457
{
1458
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1459
        throw Py::Exception();
1460
    }
1461
    return Py::Boolean(true);
1462
}
1463

1464
Py::Object TaskPlacementPy::isAllowedAlterView(const Py::Tuple& args)
1465
{
1466
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1467
        throw Py::Exception();
1468
    }
1469
    return Py::Boolean(true);
1470
}
1471

1472
Py::Object TaskPlacementPy::isAllowedAlterSelection(const Py::Tuple& args)
1473
{
1474
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1475
        throw Py::Exception();
1476
    }
1477
    return Py::Boolean(true);
1478
}
1479

1480
Py::Object TaskPlacementPy::getStandardButtons(const Py::Tuple& args)
1481
{
1482
    if (!PyArg_ParseTuple(args.ptr(), "")) {
1483
        throw Py::Exception();
1484
    }
1485
    auto buttons = QDialogButtonBox::Ok|
1486
            QDialogButtonBox::Cancel|
1487
            QDialogButtonBox::Apply;
1488
    return Py::Long(static_cast<int>(buttons));
1489
}
1490

1491
#include "moc_Placement.cpp"
1492

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

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

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

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