FreeCAD

Форк
0
/
DlgAddPropertyVarSet.cpp 
472 строки · 16.4 Кб
1
/****************************************************************************
2
 *   Copyright (c) 2024 Ondsel <development@ondsel.com>                     *
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 <QMessageBox>
26
# include <QString>
27
# include <QCompleter>
28
#endif
29

30
#include <App/Application.h>
31
#include <App/Document.h>
32
#include <App/DocumentObject.h>
33
#include <App/PropertyUnits.h>
34
#include <Base/Tools.h>
35

36
#include "DlgAddPropertyVarSet.h"
37
#include "ui_DlgAddPropertyVarSet.h"
38
#include "MainWindow.h"
39
#include "ViewProviderDocumentObject.h"
40
#include "ViewProviderVarSet.h"
41

42
FC_LOG_LEVEL_INIT("DlgAddPropertyVarSet", true, true)
43

44
using namespace Gui;
45
using namespace Gui::Dialog;
46

47
const std::string DlgAddPropertyVarSet::GROUP_BASE = "Base";
48

49
const bool CLEAR_NAME = true;
50

51
DlgAddPropertyVarSet::DlgAddPropertyVarSet(QWidget* parent,
52
                                           ViewProviderVarSet* viewProvider)
53
    : QDialog(parent),
54
      varSet(dynamic_cast<App::VarSet*>(viewProvider->getObject())),
55
      ui(new Ui_DlgAddPropertyVarSet),
56
      comboBoxGroup(this),
57
      completerType(this),
58
      editor(nullptr)
59
{
60
    ui->setupUi(this);
61

62
    initializeWidgets(viewProvider);
63
}
64

65
DlgAddPropertyVarSet::~DlgAddPropertyVarSet() = default;
66

67
void DlgAddPropertyVarSet::initializeGroup()
68
{
69
    connect(&comboBoxGroup, &EditFinishedComboBox::editFinished,
70
            this, &DlgAddPropertyVarSet::onEditFinished);
71
    comboBoxGroup.setObjectName(QString::fromUtf8("comboBoxGroup"));
72
    comboBoxGroup.setInsertPolicy(QComboBox::InsertAtTop);
73
    comboBoxGroup.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
74
    comboBoxGroup.setEditable(true);
75
    auto formLayout = qobject_cast<QFormLayout*>(layout());
76
    formLayout->setWidget(1, QFormLayout::FieldRole, &comboBoxGroup);
77

78
    std::vector<App::Property*> properties;
79
    varSet->getPropertyList(properties);
80
    std::unordered_set<std::string> groupNames;
81
    for (auto prop : properties) {
82
        const char* groupName = varSet->getPropertyGroup(prop);
83
        groupNames.insert(groupName ? groupName : GROUP_BASE);
84
    }
85
    std::vector<std::string> groupNamesSorted(groupNames.begin(), groupNames.end());
86
    std::sort(groupNamesSorted.begin(), groupNamesSorted.end(), [](std::string& a, std::string& b) {
87
        // prefer anything else other than Base, so move it to the back
88
        if (a == GROUP_BASE) {
89
            return false;
90
        }
91
        else if (b == GROUP_BASE) {
92
            return true;
93
        }
94
        else {
95
            return a < b;
96
        }
97
    });
98

99
    for (const auto& groupName : groupNamesSorted) {
100
        comboBoxGroup.addItem(QString::fromStdString(groupName));
101
    }
102

103
    comboBoxGroup.setEditText(QString::fromStdString(groupNamesSorted[0]));
104
}
105

106
void DlgAddPropertyVarSet::getSupportedTypes(std::vector<Base::Type>& types)
107
{
108
    std::vector<Base::Type> proptypes;
109
    Base::Type::getAllDerivedFrom(Base::Type::fromName("App::Property"), proptypes);
110
    std::copy_if(proptypes.begin(), proptypes.end(), std::back_inserter(types), [](const Base::Type& type) {
111
        return type.canInstantiate();
112
    });
113
    std::sort(types.begin(), types.end(), [](Base::Type a, Base::Type b) {
114
        return strcmp(a.getName(), b.getName()) < 0;
115
    });
116
}
117

118
void DlgAddPropertyVarSet::initializeTypes()
119
{
120
    auto paramGroup = App::GetApplication().GetParameterGroupByPath(
121
            "User parameter:BaseApp/Preferences/PropertyView");
122
    auto lastType = Base::Type::fromName(
123
            paramGroup->GetASCII("NewPropertyType","App::PropertyLength").c_str());
124
    if(lastType.isBad()) {
125
        lastType = App::PropertyLength::getClassTypeId();
126
    }
127

128
    std::vector<Base::Type> types;
129
    getSupportedTypes(types);
130

131
    for(const auto& type : types) {
132
        ui->comboBoxType->addItem(QString::fromLatin1(type.getName()));
133
        if(type == lastType)
134
            ui->comboBoxType->setCurrentIndex(ui->comboBoxType->count()-1);
135
    }
136

137
    completerType.setModel(ui->comboBoxType->model());
138
    completerType.setCaseSensitivity(Qt::CaseInsensitive);
139
    completerType.setFilterMode(Qt::MatchContains);
140
    ui->comboBoxType->setCompleter(&completerType);
141
    ui->comboBoxType->setInsertPolicy(QComboBox::NoInsert);
142

143
    connect(ui->comboBoxType, &QComboBox::currentTextChanged,
144
            this, &DlgAddPropertyVarSet::onEditFinished);
145
}
146

147
/*
148
// keep some debugging code for debugging tab order
149
static void printFocusChain(QWidget *widget) {
150
    FC_ERR("Focus Chain:");
151
    QWidget* start = widget;
152
    int i = 0;
153
    do {
154
        FC_ERR(" " << widget->objectName().toStdString();
155
        widget = widget->nextInFocusChain();
156
        i++;
157
    } while (widget != nullptr && i < 30 && start != widget);
158
    QWidget *currentWidget = QApplication::focusWidget();
159
    FC_ERR("  Current focus widget:" << (currentWidget ? currentWidget->objectName().toStdString() : "None") << std::endl << std::endl);
160
}
161
*/
162

163
void DlgAddPropertyVarSet::initializeWidgets(ViewProviderVarSet* viewProvider)
164
{
165
    initializeGroup();
166
    initializeTypes();
167

168
    connect(this, &QDialog::finished,
169
            this, [viewProvider](int result) { viewProvider->onFinished(result); });
170
    connect(ui->lineEditName, &QLineEdit::editingFinished,
171
            this, &DlgAddPropertyVarSet::onEditFinished);
172
    connect(ui->lineEditName, &QLineEdit::textChanged,
173
            this, &DlgAddPropertyVarSet::onNamePropertyChanged);
174

175
    std::string title = "Add a property to " + varSet->getFullName();
176
    setWindowTitle(QString::fromStdString(title));
177

178
    setOkEnabled(false);
179

180
    ui->lineEditName->setFocus();
181

182
    QWidget::setTabOrder(ui->lineEditName, &comboBoxGroup);
183
    QWidget::setTabOrder(&comboBoxGroup, ui->comboBoxType);
184

185
    // FC_ERR("Initialize widgets");
186
    // printFocusChain(ui->lineEditName);
187
}
188

189
void DlgAddPropertyVarSet::setOkEnabled(bool enabled)
190
{
191
    QPushButton *okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
192
    okButton->setEnabled(enabled);
193
}
194

195
void DlgAddPropertyVarSet::clearEditors(bool clearName)
196
{
197
    if (clearName) {
198
        bool beforeBlocked = ui->lineEditName->blockSignals(true);
199
        ui->lineEditName->clear();
200
        ui->lineEditName->blockSignals(beforeBlocked);
201
    }
202
    removeEditor();
203
    setOkEnabled(false);
204
    namePropertyToAdd.clear();
205
}
206

207
void DlgAddPropertyVarSet::removeEditor()
208
{
209
    if (editor) {
210
        layout()->removeWidget(editor.get());
211
        QWidget::setTabOrder(ui->comboBoxType, ui->checkBoxAdd);
212
        editor = nullptr;
213

214
        // FC_ERR("remove editor");
215
        // printFocusChain(ui->comboBoxType);
216
    }
217
}
218

219
static PropertyEditor::PropertyItem *createPropertyItem(App::Property *prop)
220
{
221
    const char *editor = prop->getEditorName();
222
    if (!editor || !editor[0]) {
223
        return nullptr;
224
    }
225
    auto item = static_cast<PropertyEditor::PropertyItem*>(
226
            PropertyEditor::PropertyItemFactory::instance().createPropertyItem(editor));
227
    if (!item) {
228
        qWarning("No property item for type %s found\n", editor);
229
    }
230
    return item;
231
}
232

233
void DlgAddPropertyVarSet::addEditor(PropertyEditor::PropertyItem* propertyItem, std::string& type)
234
{
235
    editor.reset(propertyItem->createEditor(this, [this]() {
236
        this->valueChanged();
237
    }));
238
    if (type == "App::PropertyFont") {
239
        propertyItem->setEditorData(editor.get(), QVariant());
240
    }
241
    editor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
242
    editor->setObjectName(QString::fromUtf8("editor"));
243
    auto formLayout = qobject_cast<QFormLayout*>(layout());
244
    formLayout->setWidget(3, QFormLayout::FieldRole, editor.get());
245

246
    QWidget::setTabOrder(ui->comboBoxType, editor.get());
247
    QWidget::setTabOrder(editor.get(), ui->checkBoxAdd);
248

249
    // FC_ERR("add editor");
250
    // printFocusChain(editor.get());
251
}
252

253
bool DlgAddPropertyVarSet::isTypeWithEditor(const std::string& type)
254
{
255
    return typesWithoutEditor.find(type) == typesWithoutEditor.end();
256
}
257

258
void DlgAddPropertyVarSet::createProperty()
259
{
260
    std::string name = ui->lineEditName->text().toStdString();
261
    std::string group = comboBoxGroup.currentText().toStdString();
262
    std::string type = ui->comboBoxType->currentText().toStdString();
263

264
    App::Property* prop;
265
    try {
266
        prop = varSet->addDynamicProperty(type.c_str(), name.c_str(),
267
                                          group.c_str());
268
    }
269
    catch (Base::Exception& e) {
270
        e.ReportException();
271
        QMessageBox::critical(this,
272
                              QObject::tr("Add property"),
273
                              QObject::tr("Failed to add property to '%1': %2").arg(
274
                                      QString::fromLatin1(varSet->getFullName().c_str()),
275
                                      QString::fromUtf8(e.what())));
276
        clearEditors();
277
        return;
278
    }
279

280
    namePropertyToAdd = name;
281

282
    objectIdentifier = std::make_unique<App::ObjectIdentifier>(*prop);
283
    // creating a propertyItem here because it has all kinds of logic for
284
    // editors that we can reuse
285
    removeEditor();
286
    propertyItem.reset(createPropertyItem(prop));
287
    if (propertyItem && isTypeWithEditor(type)) {
288
        propertyItem->setPropertyData({prop});
289
        propertyItem->bind(*objectIdentifier);
290
        addEditor(propertyItem.get(), type);
291
    }
292

293
    setOkEnabled(true);
294
}
295

296
void DlgAddPropertyVarSet::changePropertyToAdd() {
297
    // we were already adding a new property, the only option to get here
298
    // is a change of type or group.
299

300
    std::string name = ui->lineEditName->text().toStdString();
301
    assert(name == namePropertyToAdd);
302

303
    App::Property* prop = varSet->getPropertyByName(namePropertyToAdd.c_str());
304

305
    std::string group = comboBoxGroup.currentText().toStdString();
306
    if (prop->getGroup() != group) {
307
        varSet->changeDynamicProperty(prop, group.c_str(), nullptr);
308
    }
309

310
    std::string type = ui->comboBoxType->currentText().toStdString();
311
    if (prop->getTypeId() != Base::Type::fromName(type.c_str())) {
312
        // the property should have a different type
313
        varSet->removeDynamicProperty(namePropertyToAdd.c_str());
314
        createProperty();
315
    }
316
}
317

318

319
void DlgAddPropertyVarSet::clearCurrentProperty()
320
{
321
    removeEditor();
322
    varSet->removeDynamicProperty(namePropertyToAdd.c_str());
323
    App::Document* doc = varSet->getDocument();
324
    if (doc->hasPendingTransaction()) {
325
        doc->abortTransaction();
326
    }
327
    setOkEnabled(false);
328
    namePropertyToAdd.clear();
329
}
330

331
class CreatePropertyException : public std::exception {
332
public:
333
    explicit CreatePropertyException(const std::string& message) : msg(message) {}
334

335
    const char* what() const noexcept override {
336
        return msg.c_str();
337
    }
338

339
private:
340
    std::string msg;
341
};
342

343
void DlgAddPropertyVarSet::checkName() {
344
    std::string name = ui->lineEditName->text().toStdString();
345
    if(name.empty() || name != Base::Tools::getIdentifier(name)) {
346
        QMessageBox::critical(getMainWindow(),
347
                              QObject::tr("Invalid name"),
348
                              QObject::tr("The property name must only contain alpha numericals,\n"
349
                                          "underscore, and must not start with a digit."));
350
        clearEditors(!CLEAR_NAME);
351
        throw CreatePropertyException("Invalid name");
352
    }
353

354
    if (namePropertyToAdd.empty()) {
355
        // we are adding a new property, check whether it doesn't already exist
356
        auto prop = varSet->getPropertyByName(name.c_str());
357
        if(prop && prop->getContainer() == varSet) {
358
            QMessageBox::critical(this,
359
                                  QObject::tr("Invalid name"),
360
                                  QObject::tr("The property '%1' already exists in '%2'").arg(
361
                                          QString::fromLatin1(name.c_str()),
362
                                          QString::fromLatin1(varSet->getFullName().c_str())));
363
            clearEditors(!CLEAR_NAME);
364
            throw CreatePropertyException("Invalid name");
365
        }
366
    }
367
}
368

369
void DlgAddPropertyVarSet::checkGroup() {
370
    std::string group = comboBoxGroup.currentText().toStdString();
371

372
    if (group.empty() || group != Base::Tools::getIdentifier(group)) {
373
        QMessageBox::critical(this,
374
            QObject::tr("Invalid name"),
375
            QObject::tr("The group name must only contain alpha numericals,\n"
376
                        "underscore, and must not start with a digit."));
377
        comboBoxGroup.setEditText(QString::fromUtf8("Base"));
378
        throw CreatePropertyException("Invalid name");
379
    }
380
}
381

382
void DlgAddPropertyVarSet::checkType() {
383
    std::string type = ui->comboBoxType->currentText().toStdString();
384

385
    if (Base::Type::fromName(type.c_str()) == Base::Type::badType()) {
386
        throw CreatePropertyException("Invalid name");
387
    }
388
}
389

390
void DlgAddPropertyVarSet::onEditFinished() {
391
    /* The editor for the value is dynamically created if 1) the name has been
392
     * determined and 2) if the type of the property has been determined.  The
393
     * group of the property is important too, but it is not essential, because
394
     * we can change the group after the property has been created.
395
     *
396
     * In this function we check whether we can create a property and therefore
397
     * an editor.
398
     */
399

400
    try {
401
        checkName();
402
        checkGroup();
403
        checkType();
404
    }
405
    catch (const CreatePropertyException&) {
406
        if (!namePropertyToAdd.empty()) {
407
            clearCurrentProperty();
408
        }
409
        return;
410
    }
411

412
    if (namePropertyToAdd.empty()) {
413
        // we are adding a new property
414
        App::Document* doc = varSet->getDocument();
415
        doc->openTransaction("Add property VarSet");
416
        createProperty();
417
    }
418
    else {
419
        // we were already adding a new property that should now be changed
420
        changePropertyToAdd();
421
    }
422
}
423

424
void DlgAddPropertyVarSet::onNamePropertyChanged(const QString& text)
425
{
426
    if (!namePropertyToAdd.empty() && text.toStdString() != namePropertyToAdd) {
427
        // The user decided to change the name of the property.  This
428
        // invalidates the editor that is strictly associated with the property.
429
        clearCurrentProperty();
430
    }
431
}
432

433
void DlgAddPropertyVarSet::valueChanged()
434
{
435
    QVariant data;
436
    data = propertyItem->editorData(editor.get());
437
    propertyItem->setData(data);
438
}
439

440
void DlgAddPropertyVarSet::accept()
441
{
442
    App::Document* doc = varSet->getDocument();
443
    doc->commitTransaction();
444

445
    if (ui->checkBoxAdd->isChecked()) {
446
        clearEditors();
447
        doc->openTransaction();
448
        ui->lineEditName->setFocus();
449
        return;
450
    }
451

452
    std::string group = comboBoxGroup.currentText().toStdString();
453
    std::string type = ui->comboBoxType->currentText().toStdString();
454
    auto paramGroup = App::GetApplication().GetParameterGroupByPath(
455
            "User parameter:BaseApp/Preferences/PropertyView");
456
    paramGroup->SetASCII("NewPropertyType", type.c_str());
457
    paramGroup->SetASCII("NewPropertyGroup", group.c_str());
458
    QDialog::accept();
459
}
460

461
void DlgAddPropertyVarSet::reject()
462
{
463
    App::Document* doc = varSet->getDocument();
464
    // a transaction is not pending if a name has not been determined.
465
    if (doc->hasPendingTransaction()) {
466
        doc->abortTransaction();
467
    }
468
    QDialog::reject();
469
}
470

471

472
#include "moc_DlgAddPropertyVarSet.cpp"
473

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

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

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

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