FreeCAD

Форк
0
/
DlgMacroExecuteImp.cpp 
979 строк · 38.8 Кб
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 <QInputDialog>
26
# include <QLabel>
27
# include <QMessageBox>
28
# include <QComboBox>
29
# include <QSignalBlocker>
30
# include <QTextStream>
31
#endif
32

33
#include <App/Document.h>
34
#include <Base/Interpreter.h>
35

36
#include "DlgMacroExecuteImp.h"
37
#include "ui_DlgMacroExecute.h"
38
#include "Application.h"
39
#include "BitmapFactory.h"
40
#include "Command.h"
41
#include "DlgCustomizeImp.h"
42
#include "DlgToolbarsImp.h"
43
#include "Document.h"
44
#include "EditorView.h"
45
#include "Macro.h"
46
#include "MainWindow.h"
47
#include "PythonEditor.h"
48

49

50
using namespace Gui;
51
using namespace Gui::Dialog;
52

53
namespace Gui {
54
    namespace Dialog {
55
        class MacroItem : public QTreeWidgetItem
56
        {
57
        public:
58
            MacroItem(QTreeWidget * widget, bool systemwide)
59
            : QTreeWidgetItem(widget),
60
            systemWide(systemwide){}
61

62
            ~MacroItem() override = default;
63

64
            bool systemWide;
65
        };
66
    }
67
}
68

69

70
/* TRANSLATOR Gui::Dialog::DlgMacroExecuteImp */
71

72
/**
73
 *  Constructs a DlgMacroExecuteImp which is a child of 'parent', with the
74
 *  name 'name' and widget flags set to 'f'
75
 *
76
 *  The dialog will by default be modeless, unless you set 'modal' to
77
 *  true to construct a modal dialog.
78
 */
79
DlgMacroExecuteImp::DlgMacroExecuteImp( QWidget* parent, Qt::WindowFlags fl )
80
    : QDialog( parent, fl )
81
    , WindowParameter( "Macro" )
82
    , ui(new Ui_DlgMacroExecute)
83
{
84
    watcher = std::make_unique<PythonTracingWatcher>(this);
85
    ui->setupUi(this);
86
    setupConnections();
87

88
    // retrieve the macro path from parameter or use the user data as default
89
    {
90
        QSignalBlocker blocker(ui->fileChooser);
91
        std::string path = getWindowParameter()->GetASCII("MacroPath",
92
            App::Application::getUserMacroDir().c_str());
93
        this->macroPath = QString::fromUtf8(path.c_str());
94
        ui->fileChooser->setFileName(this->macroPath);
95
    }
96

97
    // Fill the List box
98
    QStringList labels; labels << tr("Macros");
99
    ui->userMacroListBox->setHeaderLabels(labels);
100
    ui->userMacroListBox->header()->hide();
101
    ui->systemMacroListBox->setHeaderLabels(labels);
102
    ui->systemMacroListBox->header()->hide();
103
    fillUpList();
104
    ui->LineEditFind->setFocus();
105
}
106

107
/**
108
 *  Destroys the object and frees any allocated resources
109
 */
110
DlgMacroExecuteImp::~DlgMacroExecuteImp() = default;
111

112
void DlgMacroExecuteImp::setupConnections()
113
{
114
    connect(ui->fileChooser, &FileChooser::fileNameChanged,
115
            this, &DlgMacroExecuteImp::onFileChooserFileNameChanged);
116
    connect(ui->createButton, &QPushButton::clicked,
117
            this, &DlgMacroExecuteImp::onCreateButtonClicked);
118
    connect(ui->deleteButton, &QPushButton::clicked,
119
            this, &DlgMacroExecuteImp::onDeleteButtonClicked);
120
    connect(ui->editButton, &QPushButton::clicked,
121
            this, &DlgMacroExecuteImp::onEditButtonClicked);
122
    connect(ui->renameButton, &QPushButton::clicked,
123
            this, &DlgMacroExecuteImp::onRenameButtonClicked);
124
    connect(ui->duplicateButton, &QPushButton::clicked,
125
            this, &DlgMacroExecuteImp::onDuplicateButtonClicked);
126
    connect(ui->toolbarButton, &QPushButton::clicked,
127
            this, &DlgMacroExecuteImp::onToolbarButtonClicked);
128
    connect(ui->addonsButton, &QPushButton::clicked,
129
            this, &DlgMacroExecuteImp::onAddonsButtonClicked);
130
    connect(ui->userMacroListBox, &QTreeWidget::currentItemChanged,
131
            this, &DlgMacroExecuteImp::onUserMacroListBoxCurrentItemChanged);
132
    connect(ui->systemMacroListBox, &QTreeWidget::currentItemChanged,
133
            this, &DlgMacroExecuteImp::onSystemMacroListBoxCurrentItemChanged);
134
    connect(ui->tabMacroWidget, &QTabWidget::currentChanged,
135
            this, &DlgMacroExecuteImp::onTabMacroWidgetCurrentChanged);
136
    connect(ui->LineEditFind, &QLineEdit::textChanged,
137
            this, &DlgMacroExecuteImp::onLineEditFindTextChanged);
138
    connect(ui->LineEditFindInFiles, &QLineEdit::textChanged,
139
            this, &DlgMacroExecuteImp::onLineEditFindInFilesTextChanged);
140
}
141

142
/** Take a folder and return a StringList of the filenames in it
143
 * filtered by both filename *and* by content, if the user has
144
 * put text in one or both of the search line edits.
145
 *
146
 * First filtering is done by file name, which reduces the
147
 * number of files to open and read (relatively expensive operation).
148
 *
149
 * Then we filter by file content after reducing the number of files
150
 * to open and read.  But both loops are skipped if there is no text
151
 * in either of the line edits.
152
 *
153
 * We do this as another function in order to reuse this code for
154
 * doing both the User and System macro list boxes in the fillUpList() function.
155
 */
156

157
QStringList DlgMacroExecuteImp::filterFiles(const QString& folder){
158
    QDir dir(folder, QLatin1String("*.FCMacro *.py"));
159
    QStringList unfiltered = dir.entryList(); //all .fcmacro and .py files
160
    QString fileFilter = ui->LineEditFind->text(); //used to search by filename
161
    QString searchText = ui->LineEditFindInFiles->text(); //used to search in file content
162

163
    if (fileFilter.isEmpty() && searchText.isEmpty()){ //skip filtering if no filters
164
        return unfiltered;
165
    }
166
    QStringList filteredByFileName;
167
    if (fileFilter.isEmpty()){
168
        filteredByFileName = unfiltered; //skip the loop if no file filter
169
    } else {
170
        QRegularExpression regexFileName(fileFilter, QRegularExpression::CaseInsensitiveOption);
171
        bool isValidFileFilter = regexFileName.isValid(); //check here instead of inside the loop
172
        for (auto uf : unfiltered){
173
            if (isValidFileFilter){
174
                if (regexFileName.match(uf).hasMatch()) {
175
                    filteredByFileName.append(uf);
176
                }
177
            } else { //not valid, so do a simple text search
178
                if (uf.contains(fileFilter, Qt::CaseInsensitive)) {
179
                    filteredByFileName.append(uf);
180
                }
181
            }
182
        }
183
    }
184

185
    if (searchText.isEmpty()){ //skip reading file contents if no find in file filter
186
        return filteredByFileName;
187
    }
188

189
    QRegularExpression regexContent(searchText, QRegularExpression::CaseInsensitiveOption);
190
    bool isValidContentFilter = regexContent.isValid();
191
    QStringList filteredByContent;
192
    for (auto fn : filteredByFileName) {
193
        const QString &fileName = fn;
194
        QString filePath = dir.filePath(fileName);
195
        QFile file(filePath);
196
        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
197
            QTextStream in(&file);
198
            QString fileContent = in.readAll();
199
            if (isValidContentFilter){
200
                if (regexContent.match(fileContent).hasMatch()) {
201
                    filteredByContent.append(fileName);
202
                }
203
            } else {
204
                if (fileContent.contains(searchText, Qt::CaseInsensitive)){
205
                    filteredByContent.append(fileName);
206
                }
207
            }
208
            file.close();
209
        }
210
    }
211
    return filteredByContent;
212
}
213

214
/**
215
 * Fills up the list with macro files found in the specified location
216
 * that have been filtered by both filename and by content
217
 */
218
void DlgMacroExecuteImp::fillUpList()
219
{
220
    QStringList filteredByContent = this->filterFiles(this->macroPath);
221
    ui->userMacroListBox->clear();
222
    for (auto fn : filteredByContent){
223
        auto item = new MacroItem(ui->userMacroListBox,false);
224
        item->setText(0, fn);
225
    }
226

227
    QString dirstr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro");
228
    filteredByContent = this->filterFiles(dirstr);
229

230
    ui->systemMacroListBox->clear();
231
    for (auto fn : filteredByContent) {
232
        auto item = new MacroItem(ui->systemMacroListBox,true);
233
        item->setText(0, fn);
234
    }
235
}
236

237

238
/**
239
 * Selects a macro file in the list view.
240
 */
241
void DlgMacroExecuteImp::onUserMacroListBoxCurrentItemChanged(QTreeWidgetItem* item)
242
{
243
    if (item) {
244
        ui->LineEditMacroName->setText(item->text(0));
245

246
        ui->executeButton->setEnabled(true);
247
        ui->deleteButton->setEnabled(true);
248
        ui->toolbarButton->setEnabled(true);
249
        ui->createButton->setEnabled(true);
250
        ui->editButton->setEnabled(true);
251
        ui->renameButton->setEnabled(true);
252
        ui->duplicateButton->setEnabled(true);
253
    }
254
    else {
255
        ui->executeButton->setEnabled(false);
256
        ui->deleteButton->setEnabled(false);
257
        ui->toolbarButton->setEnabled(false);
258
        ui->createButton->setEnabled(true);
259
        ui->editButton->setEnabled(false);
260
        ui->renameButton->setEnabled(false);
261
        ui->duplicateButton->setEnabled(false);
262
    }
263
}
264

265
void DlgMacroExecuteImp::onLineEditFindTextChanged(const QString &text){
266
    Q_UNUSED(text);
267
    this->fillUpList();
268
}
269

270
void DlgMacroExecuteImp::onLineEditFindInFilesTextChanged(const QString &text){
271
    Q_UNUSED(text);
272
    this->fillUpList();
273
}
274

275
void DlgMacroExecuteImp::onSystemMacroListBoxCurrentItemChanged(QTreeWidgetItem* item)
276
{
277
    if (item) {
278
        ui->LineEditMacroName->setText(item->text(0));
279

280
        ui->executeButton->setEnabled(true);
281
        ui->deleteButton->setEnabled(false);
282
        ui->toolbarButton->setEnabled(false);
283
        ui->createButton->setEnabled(false);
284
        ui->editButton->setEnabled(true); //look but don't touch
285
        ui->renameButton->setEnabled(false);
286
        ui->duplicateButton->setEnabled(false);
287
    }
288
    else {
289
        ui->executeButton->setEnabled(false);
290
        ui->deleteButton->setEnabled(false);
291
        ui->toolbarButton->setEnabled(false);
292
        ui->createButton->setEnabled(false);
293
        ui->editButton->setEnabled(false);
294
        ui->renameButton->setEnabled(false);
295
        ui->duplicateButton->setEnabled(false);
296
    }
297
}
298

299
void DlgMacroExecuteImp::onTabMacroWidgetCurrentChanged(int index)
300
{
301
    QTreeWidgetItem* item;
302

303
    if (index == 0) { //user-specific
304
        item = ui->userMacroListBox->currentItem();
305
        if (item) {
306
            ui->executeButton->setEnabled(true);
307
            ui->deleteButton->setEnabled(true);
308
            ui->toolbarButton->setEnabled(true);
309
            ui->createButton->setEnabled(true);
310
            ui->editButton->setEnabled(true);
311
            ui->renameButton->setEnabled(true);
312
            ui->duplicateButton->setEnabled(true);
313
        }
314
        else {
315
            ui->executeButton->setEnabled(false);
316
            ui->deleteButton->setEnabled(false);
317
            ui->toolbarButton->setEnabled(false);
318
            ui->createButton->setEnabled(true);
319
            ui->editButton->setEnabled(false);
320
            ui->renameButton->setEnabled(false);
321
            ui->duplicateButton->setEnabled(false);
322
        }
323
    }
324
    else { //index==1 system-wide
325
        item = ui->systemMacroListBox->currentItem();
326

327
        if (item) {
328
            ui->executeButton->setEnabled(true);
329
            ui->deleteButton->setEnabled(false);
330
            ui->toolbarButton->setEnabled(false);
331
            ui->createButton->setEnabled(false);
332
            ui->editButton->setEnabled(true); //but you can't save it
333
            ui->renameButton->setEnabled(false);
334
            ui->duplicateButton->setEnabled(false);
335
        }
336
        else {
337
            ui->executeButton->setEnabled(false);
338
            ui->deleteButton->setEnabled(false);
339
            ui->toolbarButton->setEnabled(false);
340
            ui->createButton->setEnabled(false);
341
            ui->editButton->setEnabled(false);
342
            ui->renameButton->setEnabled(false);
343
            ui->duplicateButton->setEnabled(false);
344
        }
345
    }
346

347
    if (item) {
348
        ui->LineEditMacroName->setText(item->text(0));
349
    }
350
    else {
351
        ui->LineEditMacroName->clear();
352
    }
353
}
354

355
/**
356
 * Executes the selected macro file.
357
 */
358
void DlgMacroExecuteImp::accept()
359
{
360
    QTreeWidgetItem* item;
361

362
    int index = ui->tabMacroWidget->currentIndex();
363
    if (index == 0) { //user-specific
364
        item = ui->userMacroListBox->currentItem();
365
    }
366
    else {
367
        //index == 1 system-wide
368
        item = ui->systemMacroListBox->currentItem();
369
    }
370
    if (!item)
371
        return;
372

373
    QDialog::accept();
374

375
    auto mitem = static_cast<MacroItem *>(item);
376

377
    QDir dir;
378

379
    if (!mitem->systemWide){
380
        dir =QDir(this->macroPath);
381
    }
382
    else {
383
        QString dirstr = QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro");
384
        dir = QDir(dirstr);
385
    }
386

387
    QFileInfo fi(dir, item->text(0));
388
    try {
389
        getMainWindow()->setCursor(Qt::WaitCursor);
390
        PythonTracingLocker tracelock(watcher->getTrace());
391

392
        getMainWindow()->appendRecentMacro(fi.filePath());
393
        Application::Instance->macroManager()->run(Gui::MacroManager::File, fi.filePath().toUtf8());
394
        // after macro run recalculate the document
395
        if (Application::Instance->activeDocument())
396
            Application::Instance->activeDocument()->getDocument()->recompute();
397
        getMainWindow()->unsetCursor();
398
    }
399
    catch (const Base::SystemExitException&) {
400
        // handle SystemExit exceptions
401
        Base::PyGILStateLocker locker;
402
        Base::PyException e;
403
        e.ReportException();
404
        getMainWindow()->unsetCursor();
405
    }
406
}
407

408
/**
409
 * Specify the location of your macro files. The default location is FreeCAD's home path.
410
 */
411
void DlgMacroExecuteImp::onFileChooserFileNameChanged(const QString& fn)
412
{
413
    if (!fn.isEmpty())
414
    {
415
        // save the path in the parameters
416
        this->macroPath = fn;
417
        getWindowParameter()->SetASCII("MacroPath",fn.toUtf8());
418
        // fill the list box
419
        fillUpList();
420
    }
421
}
422

423
/**
424
 * Opens the macro file in an editor.
425
 */
426
void DlgMacroExecuteImp::onEditButtonClicked()
427
{
428
    QDir dir;
429
    QTreeWidgetItem* item = nullptr;
430

431
    int index = ui->tabMacroWidget->currentIndex();
432
    if (index == 0) { //user-specific
433
        item = ui->userMacroListBox->currentItem();
434
        dir.setPath(this->macroPath);
435
    }
436
    else {
437
        //index == 1 system-wide
438
        item = ui->systemMacroListBox->currentItem();
439
        dir.setPath(QString::fromStdString(App::Application::getHomePath()) + QString::fromLatin1("Macro"));
440
    }
441

442
    if (!item)
443
        return;
444

445
    auto mitem = static_cast<MacroItem *>(item);
446

447
    QString file = QString::fromLatin1("%1/%2").arg(dir.absolutePath(), item->text(0));
448
    auto editor = new PythonEditor();
449
    editor->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python"));
450
    auto edit = new PythonEditorView(editor, getMainWindow());
451
    edit->setDisplayName(PythonEditorView::FileName);
452
    edit->open(file);
453
    edit->resize(400, 300);
454
    getMainWindow()->addWindow(edit);
455
    getMainWindow()->appendRecentMacro(file);
456

457
    if (mitem->systemWide) {
458
        editor->setReadOnly(true);
459
        QString shownName;
460
        shownName = QString::fromLatin1("%1[*] - [%2]").arg(item->text(0), tr("Read-only"));
461
        edit->setWindowTitle(shownName);
462
    }
463
    close();
464
}
465

466
/** Creates a new macro file. */
467
void DlgMacroExecuteImp::onCreateButtonClicked()
468
{
469
    // query file name
470
    bool replaceSpaces = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("ReplaceSpaces", true);
471
    App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("ReplaceSpaces", replaceSpaces); //create parameter
472

473
    QString fn = QInputDialog::getText(this, tr("Macro file"), tr("Enter a file name, please:"),
474
        QLineEdit::Normal, QString(), nullptr, Qt::MSWindowsFixedSizeDialogHint);
475

476
    if(replaceSpaces){
477
        fn = fn.replace(QString::fromStdString(" "),QString::fromStdString("_"));
478
    }
479

480
    if (!fn.isEmpty())
481
    {
482
        QString suffix = QFileInfo(fn).suffix().toLower();
483
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py"))
484
            fn += QLatin1String(".FCMacro");
485
        QDir dir(this->macroPath);
486
        // create the macroPath if nonexistent
487
        if (!dir.exists()) {
488
            dir.mkpath(this->macroPath);
489
        }
490
        QFileInfo fi(dir, fn);
491
        if (fi.exists() && fi.isFile())
492
        {
493
            QMessageBox::warning(this, tr("Existing file"),
494
                tr("'%1'.\nThis file already exists.").arg(fi.fileName()));
495
        }
496
        else
497
        {
498
            QFile file(fi.absoluteFilePath());
499
            if (!file.open(QFile::WriteOnly)) {
500
                QMessageBox::warning(this, tr("Cannot create file"),
501
                    tr("Creation of file '%1' failed.").arg(fi.absoluteFilePath()));
502
                return;
503
            }
504
            file.close();
505
            auto editor = new PythonEditor();
506
            editor->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python"));
507
            auto edit = new PythonEditorView(editor, getMainWindow());
508
            edit->open(fi.absoluteFilePath());
509
            getMainWindow()->appendRecentMacro(fi.absoluteFilePath());
510
            edit->setWindowTitle(QString::fromLatin1("%1[*]").arg(fn));
511
            edit->resize(400, 300);
512
            getMainWindow()->addWindow(edit);
513
            close();
514
        }
515
    }
516
}
517

518
/** Deletes the selected macro file from your harddisc. */
519
void DlgMacroExecuteImp::onDeleteButtonClicked()
520
{
521
    QTreeWidgetItem* item = ui->userMacroListBox->currentItem();
522
    if (!item)
523
        return;
524

525
    auto mitem = static_cast<MacroItem *>(item);
526

527
    if (mitem->systemWide) {
528
        QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Delete macro"),
529
            QObject::tr("Not allowed to delete system-wide macros"));
530
        return;
531
    }
532

533
    QString fn = item->text(0);
534
    auto ret = QMessageBox::question(this, tr("Delete macro"),
535
        tr("Do you really want to delete the macro '%1'?").arg( fn ),
536
        QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
537
    if (ret == QMessageBox::Yes) {
538
        QDir dir(this->macroPath);
539
        dir.remove(fn);
540
        int index = ui->userMacroListBox->indexOfTopLevelItem(item);
541
        ui->userMacroListBox->takeTopLevelItem(index);
542
        delete item;
543
    }
544
}
545

546
/**
547
 * Walk user through process of adding macro to global custom toolbar
548
 * We create a custom customize dialog with instructions embedded
549
 * within the dialog itself for the user, and the buttons to push in red text
550
 * There are 2 dialogs we need to create: the macros dialog and the
551
 * toolbar dialog.
552
 */
553

554
void DlgMacroExecuteImp::onToolbarButtonClicked()
555
{
556
    /**
557
     * advise user of what we are doing, offer chance to cancel
558
     * unless user already said not to show this messagebox again
559
    **/
560

561
    bool showAgain = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("ShowWalkthroughMessage", true);
562
    if (showAgain){
563
        QMessageBox msgBox;
564
        QAbstractButton* doNotShowAgainButton = msgBox.addButton(tr("Do not show again"), QMessageBox::YesRole);
565
        msgBox.setText(tr("Guided Walkthrough"));
566
        msgBox.setInformativeText(tr("This will guide you in setting up this macro in a custom \
567
global toolbar.  Instructions will be in red text inside the dialog.\n\
568
\n\
569
Note: your changes will be applied when you next switch workbenches\n"));
570
        msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
571
        msgBox.setDefaultButton(QMessageBox::Ok);
572
        int result = msgBox.exec();
573
        if (result == QMessageBox::Cancel){
574
            return;
575
        }
576
        if (msgBox.clickedButton() == doNotShowAgainButton){
577
            App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("ShowWalkthroughMessage",false);
578
        }
579
    }
580

581
    QTreeWidgetItem* item = ui->userMacroListBox->currentItem();
582
    if (!item)
583
        return;
584

585
    QString fn = item->text(0);
586
    QString bareFileName = QFileInfo(fn).baseName(); //for use as default menu text (filename without extension)
587

588
    /** check if user already has custom toolbar, so we can tailor instructions accordingly **/
589
    bool hasCustomToolbar = true;
590
    if (App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Workbench/Global/Toolbar")->GetGroups().empty()) {
591
        hasCustomToolbar=false;
592
    }
593

594
    /** check if user already has this macro command created, if so skip dialog 1 **/
595
    bool hasMacroCommand = false;
596
    QString macroMenuText;
597
    CommandManager & cCmdMgr = Application::Instance->commandManager();
598
    std::vector<Command*> aCmds = cCmdMgr.getGroupCommands("Macros");
599
    for (const auto & aCmd : aCmds) {
600
        auto mc = dynamic_cast<MacroCommand*>(aCmd);
601
        if (mc && fn.compare(QLatin1String(mc->getScriptName())) == 0) {
602
            hasMacroCommand = true;
603
            macroMenuText = QString::fromLatin1(mc->getMenuText());
604
        }
605
    }
606

607
    QTabWidget* tabWidget = nullptr;
608

609
    if (!hasMacroCommand){
610
        /** first the custom macros page dialog **/
611
        Gui::Dialog::DlgCustomizeImp dlg(this);
612

613
        /** title is normally "Customize" **/
614
        dlg.setWindowTitle(tr("Walkthrough, dialog 1 of 2"));
615

616
        tabWidget = dlg.findChild<QTabWidget*>(QString::fromLatin1("Gui__Dialog__TabWidget"));
617
        if (!tabWidget) {
618
            std::cerr << "Toolbar walkthrough error: Unable to find tabwidget" << std::endl;
619
            return;
620
        }
621

622
        auto setupCustomMacrosPage = tabWidget->findChild<QWidget*>(QString::fromLatin1("Gui__Dialog__DlgCustomActions"));
623
        if (!setupCustomMacrosPage) {
624
            std::cerr << "Toolbar walkthrough error: Unable to find setupCustomMacrosPage" << std::endl;
625
            return;
626
        }
627
        tabWidget->setCurrentWidget(setupCustomMacrosPage);
628

629
        auto groupBox7 = setupCustomMacrosPage->findChild<QGroupBox*>(QString::fromLatin1("GroupBox7"));
630
        if (!groupBox7) {
631
            Base::Console().Warning("Toolbar walkthrough: Unable to find groupBox7\n");
632
            //just warn when not a fatal error
633
        } else {
634
            /** normally the groupbox title is "Setup Custom Macros", but we change it here **/
635
            groupBox7->setTitle(tr("Walkthrough instructions: Fill in missing fields (optional) then click Add, then Close"));
636
            groupBox7->setStyleSheet(QString::fromLatin1("QGroupBox::title {color:red}"));
637
        }
638

639
        auto buttonAddAction = setupCustomMacrosPage->findChild<QPushButton*>(QString::fromLatin1("buttonAddAction"));
640
        if (!buttonAddAction) {
641
            Base::Console().Warning("Toolbar walkthrough: Unable to find buttonAddAction\n");
642
        } else {
643
            buttonAddAction->setStyleSheet(QString::fromLatin1("color:red"));
644
        }
645

646
        auto macroListBox = setupCustomMacrosPage->findChild<QComboBox*>(QString::fromLatin1("actionMacros"));
647
        if (!macroListBox) {
648
            Base::Console().Warning("Toolbar walkthrough: Unable to find actionMacros combo box\n");
649
        } else {
650
            int macroIndex = macroListBox->findText(fn); //fn is the macro filename
651
            macroListBox->setCurrentIndex(macroIndex); //select it for the user so they don't have to
652
        }
653

654
        auto menuText = setupCustomMacrosPage->findChild<QLineEdit*>(QString::fromLatin1("actionMenu"));
655
        if (!menuText) {
656
            Base::Console().Warning("Toolbar walkthrough: Unable to find actionMenu menuText\n");
657
        } else {
658
            menuText->setText(bareFileName); //user can fill in other fields, e.g. tooltip
659
        }
660

661
        dlg.exec();
662
    }
663

664
    /** now for the toolbar selection dialog **/
665

666
    Gui::Dialog::DlgCustomizeImp dlg(this);
667

668
    if (hasMacroCommand){
669
        dlg.setWindowTitle(tr("Walkthrough, dialog 1 of 1"));
670
    } else {
671
        dlg.setWindowTitle(tr("Walkthrough, dialog 2 of 2"));
672
    }
673

674
    tabWidget = nullptr;
675
    tabWidget = dlg.findChild<QTabWidget*>(QString::fromLatin1("Gui__Dialog__TabWidget"));
676
    if (!tabWidget) {
677
        std::cerr << "Toolbar walkthrough: Unable to find tabWidget Gui__Dialog__TabWidget" << std::endl;
678
        return;
679
    }
680

681
    auto setupToolbarPage = tabWidget->findChild<DlgCustomToolbars*>(QString::fromLatin1("Gui__Dialog__DlgCustomToolbars"));
682
    if (!setupToolbarPage){
683
        std::cerr << "Toolbar walkthrough: Unable to find setupToolbarPage Gui__Dialog__DlgCustomToolbars" << std::endl;
684
        return;
685
    }
686

687
    tabWidget->setCurrentWidget(setupToolbarPage);
688
    auto moveActionRightButton = setupToolbarPage->findChild<QPushButton*>(QString::fromLatin1("moveActionRightButton"));
689
    if (!moveActionRightButton){
690
        Base::Console().Warning("Toolbar walkthrough: Unable to find moveActionRightButton\n");
691
    } else {
692
        moveActionRightButton->setStyleSheet(QString::fromLatin1("background-color: red"));
693
    }
694
    /** tailor instructions depending on whether user already has custom toolbar created
695
     * if not, they need to click New button to create one first
696
    **/
697

698
    QString instructions2 = tr("Walkthrough instructions: Click right arrow button (->), then Close.");
699
    auto workbenchBox = setupToolbarPage->findChild<QComboBox*>(QString::fromLatin1("workbenchBox"));
700
    if (!workbenchBox) {
701
        Base::Console().Warning("Toolbar walkthrough: Unable to find workbenchBox\n");
702
    }
703
    else {
704
        /** find the Global workbench and select it for the user **/
705

706
        int globalIdx = workbenchBox->findData(QString::fromLatin1("Global"));
707
        if (globalIdx != -1){
708
            workbenchBox->setCurrentIndex(globalIdx);
709
            QMetaObject::invokeMethod(setupToolbarPage, "on_workbenchBox_activated",
710
                Qt::DirectConnection,
711
                Q_ARG(int, globalIdx));
712
        } else {
713
            Base::Console().Warning("Toolbar walkthrough: Unable to find Global workbench\n");
714
        }
715

716
        if (!hasCustomToolbar){
717
            auto newButton = setupToolbarPage->findChild<QPushButton*>(QString::fromLatin1("newButton"));
718
            if (!newButton){
719
                Base::Console().Warning("Toolbar walkthrough: Unable to find newButton\n");
720
            } else {
721
                newButton->setStyleSheet(QString::fromLatin1("color:red"));
722
                instructions2 = tr("Walkthrough instructions: Click New, then right arrow (->) button, then Close.");
723
            }
724
        }
725
    }
726
    /** "label" normally says "Note: the changes become active the next time you load the appropriate workbench" **/
727

728
    auto label = setupToolbarPage->findChild<QLabel*>(QString::fromLatin1("label"));
729
    if (!label){
730
        Base::Console().Warning("Toolbar walkthrough: Unable to find label\n");
731
    } else {
732
        label->setText(instructions2);
733
        label->setStyleSheet(QString::fromLatin1("color:red"));
734
    }
735

736
    /** find Macros category and select it for the user **/
737
    auto categoryBox = setupToolbarPage->findChild<QComboBox*>(QString::fromLatin1("categoryBox"));
738
    if (!categoryBox){
739
        Base::Console().Warning("Toolbar walkthrough: Unable to find categoryBox\n");
740
    } else {
741
        int macrosIdx = categoryBox->findText(tr("Macros"));
742
        if (macrosIdx != -1){
743
            categoryBox->setCurrentIndex(macrosIdx);
744
            QMetaObject::invokeMethod(setupToolbarPage, "on_categoryBox_activated",
745
                Qt::DirectConnection,
746
                Q_ARG(int, macrosIdx));
747
        } else {
748
            Base::Console().Warning("Toolbar walkthrough: Unable to find Macros in categoryBox\n");
749
        }
750
    }
751

752
    /** expand custom toolbar items **/
753
    auto toolbarTreeWidget = setupToolbarPage->findChild<QTreeWidget*>(QString::fromLatin1("toolbarTreeWidget"));
754
    if (!toolbarTreeWidget) {
755
        Base::Console().Warning("Toolbar walkthrough: Unable to find toolbarTreeWidget\n");
756
    } else {
757
        toolbarTreeWidget->expandAll();
758
    }
759

760
    /** preselect macro command for user **/
761

762
    auto commandTreeWidget = setupToolbarPage->findChild<QTreeWidget*>(QString::fromLatin1("commandTreeWidget"));
763
    if (!commandTreeWidget) {
764
        Base::Console().Warning("Toolbar walkthrough: Unable to find commandTreeWidget\n");
765
    } else {
766
        if (!hasMacroCommand){  //will be the last in the list, the one just created
767
            commandTreeWidget->setCurrentItem(commandTreeWidget->topLevelItem(commandTreeWidget->topLevelItemCount()-1));
768
            commandTreeWidget->scrollToItem(commandTreeWidget->currentItem());
769
        } else {  //pre-select it for the user (will be the macro menu text)
770
            QList <QTreeWidgetItem*> items = commandTreeWidget->findItems(macroMenuText, Qt::MatchFixedString | Qt::MatchWrap,1);
771
            if (!items.empty()) {
772
                commandTreeWidget->setCurrentItem(items[0]);
773
                commandTreeWidget->scrollToItem(commandTreeWidget->currentItem());
774
            }
775
        }
776
    }
777
    dlg.exec();
778
}
779

780

781

782
/**
783
 * renames the selected macro
784
 */
785
void DlgMacroExecuteImp::onRenameButtonClicked()
786
{
787
    QDir dir;
788
    QTreeWidgetItem* item = nullptr;
789

790
    int index = ui->tabMacroWidget->currentIndex();
791
    if (index == 0) { //user-specific
792
        item = ui->userMacroListBox->currentItem();
793
        dir.setPath(this->macroPath);
794
    }
795

796
    if (!item)
797
        return;
798

799
    bool replaceSpaces = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("ReplaceSpaces", true);
800
    App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("ReplaceSpaces", replaceSpaces); //create parameter
801

802
    QString oldName = item->text(0);
803
    QFileInfo oldfi(dir, oldName);
804
    QFile oldfile(oldfi.absoluteFilePath());
805

806
    // query new name
807
    QString fn = QInputDialog::getText(this, tr("Renaming Macro File"),
808
        tr("Enter new name:"), QLineEdit::Normal, oldName, nullptr, Qt::MSWindowsFixedSizeDialogHint);
809

810
    if(replaceSpaces){
811
        fn = fn.replace(QString::fromStdString(" "),QString::fromStdString("_"));
812
    }
813

814
    if (!fn.isEmpty() && fn != oldName) {
815
        QString suffix = QFileInfo(fn).suffix().toLower();
816
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py"))
817
            fn += QLatin1String(".FCMacro");
818
        QFileInfo fi(dir, fn);
819
        // check if new name exists
820
        if (fi.exists()) {
821
            QMessageBox::warning(this, tr("Existing file"),
822
                tr("'%1'\n already exists.").arg(fi.absoluteFilePath()));
823
        }
824
        else if (!oldfile.rename(fi.absoluteFilePath())) {
825
            QMessageBox::warning(this, tr("Rename Failed"),
826
                tr("Failed to rename to '%1'.\nPerhaps a file permission error?").arg(fi.absoluteFilePath()));
827
        }
828
        else {
829
            // keep the item selected although it's not necessarily in alphabetic order
830
            item->setText(0, fn);
831
            ui->LineEditMacroName->setText(fn);
832
        }
833
    }
834
}
835

836
/**Duplicates selected macro
837
 * New file has same name as original but with "@" and 3-digit number appended
838
 * Begins with "@001" and increments until available name is found
839
 * "MyMacro.FCMacro" becomes "MyMacro@001.FCMacro"
840
 * "MyMacro@002.FCMacro.py" becomes "MyMacro@003.FCMacro.py" unless there is
841
 * no already existing "MyMacro@001.FCMacro.py"
842
 */
843
void DlgMacroExecuteImp::onDuplicateButtonClicked()
844
{
845
    QDir dir;
846
    QTreeWidgetItem* item = nullptr;
847

848
    //When duplicating a macro we can either begin trying to find a unique name with @001 or begin with the current @NNN if applicable
849

850
    bool from001 = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("DuplicateFrom001", false);
851
    App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("DuplicateFrom001", from001); //create parameter
852

853
    //A user may wish to add a note to end of the filename when duplicating
854
    //example: mymacro@005.fix_bug_in_dialog.FCMacro
855
    //and then when duplicating to have the extra note removed so the suggested new name is:
856
    //mymacro@006.FCMacro instead of mymacro@006.fix_bug_in_dialog.FCMacro since the new duplicate will be given a new note
857

858
    bool ignoreExtra = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("DuplicateIgnoreExtraNote", false);
859
    App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("DuplicateIgnoreExtraNote", ignoreExtra); //create parameter
860

861
    //when creating a note it will be convenient to convert spaces to underscores if the user desires this behavior
862

863
    bool replaceSpaces = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->GetBool("ReplaceSpaces", true);
864
    App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Macro")->SetBool("ReplaceSpaces", replaceSpaces); //create parameter
865

866
    int index = ui->tabMacroWidget->currentIndex();
867
    if (index == 0) { //user-specific
868
        item = ui->userMacroListBox->currentItem();
869
        dir.setPath(this->macroPath);
870
    }
871

872
    if (!item){
873
        return;
874
    }
875

876
    QString oldName = item->text(0);
877
    QFileInfo oldfi(dir, oldName);
878
    QFile oldfile(oldfi.absoluteFilePath());
879
    QString completeSuffix = oldfi.completeSuffix(); //everything after the first "."
880
    QString extraNote = completeSuffix.left(completeSuffix.size()-oldfi.suffix().size());
881
    QString baseName = oldfi.baseName(); //everything before first "."
882
    QString neutralSymbol = QString::fromStdString("@");
883
    QString last3 = baseName.right(3);
884
    bool ok = true; //was conversion to int successful?
885
    int nLast3 = last3.toInt(&ok);
886
    last3 = QString::fromStdString("001"); //increment beginning with 001 unless from001 = false
887
    if (ok ){
888
        //last3 were all digits, so we strip them from the base name
889
        if (baseName.size()>3){ //if <= 3 leave be (e.g. 2.py becomes 2@001.py)
890
            if(!from001){
891
                last3 = baseName.right(3); //use these instead of 001
892
            }
893
            baseName = baseName.left(baseName.size()-3); //strip digits
894
            if (baseName.endsWith(neutralSymbol)){
895
                baseName = baseName.left(baseName.size()-1); //trim the "@", will be added back later
896
            }
897
        }
898
    }
899
    //at this point baseName = the base name without any digits, e.g. "MyMacro"
900
    //neutralSymbol = "@"
901
    //last3 is a string representing 3 digits, always "001"
902
    //unless from001 = false, in which case we begin with previous numbers
903
    //completeSuffix = FCMacro or py or FCMacro.py or else suffix will become FCMacro below
904
    //if ignoreExtra any extra notes added between @NN. and .FCMacro will be ignored
905
    //when suggesting a new filename
906

907
    if(ignoreExtra && !extraNote.isEmpty()){
908
        nLast3++;
909
        last3 = QString::number(nLast3);
910
        while (last3.size()<3){
911
            last3.prepend(QString::fromStdString("0")); //pad 0's if needed
912
        }
913
    }
914

915

916
    QString oldNameDigitized = baseName+neutralSymbol+last3+QString::fromStdString(".")+completeSuffix;
917
    QFileInfo fi(dir, oldNameDigitized);
918

919

920
    // increment until we find available name with smallest digits
921
    // test from "001" through "999", then give up and let user enter name of choice
922

923
    while (fi.exists()) {
924
        nLast3 = last3.toInt()+1;
925
        if (nLast3 >=1000){ //avoid infinite loop, 999 files will have to be enough
926
            break;
927
        }
928
        last3 = QString::number(nLast3);
929
        while (last3.size()<3){
930
            last3.prepend(QString::fromStdString("0")); //pad 0's if needed
931
        }
932
        oldNameDigitized = baseName+neutralSymbol+last3+QString::fromStdString(".")+completeSuffix;
933
        fi = QFileInfo(dir,oldNameDigitized);
934
    }
935

936
    if(ignoreExtra && !extraNote.isEmpty()){
937
        oldNameDigitized = oldNameDigitized.remove(extraNote);
938
    }
939

940
    // give user a chance to pick a different name from digitized name suggested
941
    QString fn = QInputDialog::getText(this, tr("Duplicate Macro"),
942
        tr("Enter new name:"), QLineEdit::Normal, oldNameDigitized, 
943
        nullptr, Qt::MSWindowsFixedSizeDialogHint);
944
    if (replaceSpaces){
945
        fn = fn.replace(QString::fromStdString(" "),QString::fromStdString("_"));
946
    }
947
    if (!fn.isEmpty() && fn != oldName) {
948
        QString suffix = QFileInfo(fn).suffix().toLower();
949
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py")){
950
            fn += QLatin1String(".FCMacro");
951
        }
952
        QFileInfo fi(dir, fn);
953
        // check again if new name exists in case user changed it
954
        if (fi.exists()) {
955
            QMessageBox::warning(this, tr("Existing file"),
956
                tr("'%1'\n already exists.").arg(fi.absoluteFilePath()));
957
        }
958
        else if (!oldfile.copy(fi.absoluteFilePath())) {
959
            QMessageBox::warning(this, tr("Duplicate Failed"),
960
                tr("Failed to duplicate to '%1'.\nPerhaps a file permission error?").arg(fi.absoluteFilePath()));
961
        }
962

963
        this->fillUpList(); //repopulate list to show new file
964
    }
965

966
}
967

968
/**
969
 * convenience link button to open tools -> addon manager
970
 * from within macro dialog
971
 */
972
void DlgMacroExecuteImp::onAddonsButtonClicked()
973
{
974
    CommandManager& rMgr=Application::Instance->commandManager();
975
    rMgr.runCommandByName("Std_AddonMgr");
976
    this->fillUpList();
977
}
978

979
#include "moc_DlgMacroExecuteImp.cpp"
980

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

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

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

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