/***************************************************************************
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "PreCompiled.h"
#ifndef _PreComp_
# include <QInputDialog>
# include <QLabel>
# include <QHeaderView>
# include <QMessageBox>
# include <QComboBox>
#endif

#include "DlgMacroExecuteImp.h"
#include "ui_DlgMacroExecute.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "Command.h"
#include "MainWindow.h"
#include "FileDialog.h"
#include "Macro.h"
#include "Document.h"
#include "EditorView.h"
#include "PythonEditor.h"
#include "DlgCustomizeImp.h"
#include "DlgToolbarsImp.h"

#include <App/Application.h>
#include <App/Document.h>
#include <Base/Interpreter.h>

using namespace Gui;
using namespace Gui::Dialog;

namespace Gui {
    namespace Dialog {
        class MacroItem : public QTreeWidgetItem
        {
        public:
            MacroItem(QTreeWidget * widget, bool systemwide)
            : QTreeWidgetItem(widget),
            systemWide(systemwide){}

            ~MacroItem(){}

            bool systemWide;
        };
    }
}


/* TRANSLATOR Gui::Dialog::DlgMacroExecuteImp */

/**
 *  Constructs a DlgMacroExecuteImp which is a child of 'parent', with the
 *  name 'name' and widget flags set to 'f'
 *
 *  The dialog will by default be modeless, unless you set 'modal' to
 *  true to construct a modal dialog.
 */
DlgMacroExecuteImp::DlgMacroExecuteImp( QWidget* parent, Qt::WindowFlags fl )
    : QDialog( parent, fl )
    , WindowParameter( "Macro" )
    , ui(new Ui_DlgMacroExecute)
{
    ui->setupUi(this);
    // retrieve the macro path from parameter or use the user data as default
    std::string path = getWindowParameter()->GetASCII("MacroPath",
        App::Application::getUserMacroDir().c_str());
    this->macroPath = QString::fromUtf8(path.c_str());
    ui->fileChooser->setFileName(this->macroPath);

    // Fill the List box
    QStringList labels; labels << tr("Macros");
    ui->userMacroListBox->setHeaderLabels(labels);
    ui->userMacroListBox->header()->hide();
    ui->systemMacroListBox->setHeaderLabels(labels);
    ui->systemMacroListBox->header()->hide();
    fillUpList();
}

/**
 *  Destroys the object and frees any allocated resources
 */
DlgMacroExecuteImp::~DlgMacroExecuteImp()
{
    // no need to delete child widgets, Qt does it all for us
}

/**
 * Fills up the list with all macro files found in the specified location.
 */
void DlgMacroExecuteImp::fillUpList(void)
{
    // lists all files in macro path
    QDir dir(this->macroPath, QLatin1String("*.FCMacro *.py"));

    // fill up with the directory
    ui->userMacroListBox->clear();
    for (unsigned int i=0; i<dir.count(); i++ ) {
        MacroItem* item = new MacroItem(ui->userMacroListBox,false);
        item->setText(0, dir[i]);
    }

    QString dirstr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro");
    dir = QDir(dirstr, QLatin1String("*.FCMacro *.py"));

    ui->systemMacroListBox->clear();
    if (dir.exists()) {
        for (unsigned int i=0; i<dir.count(); i++ ) {
            MacroItem* item = new MacroItem(ui->systemMacroListBox,true);
            item->setText(0, dir[i]);
        }
    }
}

/**
 * Selects a macro file in the list view.
 */
void DlgMacroExecuteImp::on_userMacroListBox_currentItemChanged(QTreeWidgetItem* item)
{
    if (item) {
        ui->LineEditMacroName->setText(item->text(0));

        ui->executeButton->setEnabled(true);
        ui->deleteButton->setEnabled(true);
        ui->toolbarButton->setEnabled(true);
        ui->createButton->setEnabled(true);
        ui->editButton->setEnabled(true);
        ui->renameButton->setEnabled(true);
        ui->duplicateButton->setEnabled(true);
    }
    else {
        ui->executeButton->setEnabled(false);
        ui->deleteButton->setEnabled(false);
        ui->toolbarButton->setEnabled(false);
        ui->createButton->setEnabled(true);
        ui->editButton->setEnabled(false);
        ui->renameButton->setEnabled(false);
        ui->duplicateButton->setEnabled(false);
    }
}

void DlgMacroExecuteImp::on_systemMacroListBox_currentItemChanged(QTreeWidgetItem* item)
{
    if (item) {
        ui->LineEditMacroName->setText(item->text(0));

        ui->executeButton->setEnabled(true);
        ui->deleteButton->setEnabled(false);
        ui->toolbarButton->setEnabled(false);
        ui->createButton->setEnabled(false);
        ui->editButton->setEnabled(true); //look but don't touch
        ui->renameButton->setEnabled(false);
        ui->duplicateButton->setEnabled(false);
    }
    else {
        ui->executeButton->setEnabled(false);
        ui->deleteButton->setEnabled(false);
        ui->toolbarButton->setEnabled(false);
        ui->createButton->setEnabled(false);
        ui->editButton->setEnabled(false);
        ui->renameButton->setEnabled(false);
        ui->duplicateButton->setEnabled(false);
    }
}

void DlgMacroExecuteImp::on_tabMacroWidget_currentChanged(int index)
{
    QTreeWidgetItem* item;

    if (index == 0) { //user-specific
        item = ui->userMacroListBox->currentItem();
        if (item) {
            ui->executeButton->setEnabled(true);
            ui->deleteButton->setEnabled(true);
            ui->toolbarButton->setEnabled(true);
            ui->createButton->setEnabled(true);
            ui->editButton->setEnabled(true);
            ui->renameButton->setEnabled(true);
            ui->duplicateButton->setEnabled(true);
        }
        else {
            ui->executeButton->setEnabled(false);
            ui->deleteButton->setEnabled(false);
            ui->toolbarButton->setEnabled(false);
            ui->createButton->setEnabled(true);
            ui->editButton->setEnabled(false);
            ui->renameButton->setEnabled(false);
            ui->duplicateButton->setEnabled(false);
        }
    }
    else { //index==1 system-wide
        item = ui->systemMacroListBox->currentItem();

        if (item) {
            ui->executeButton->setEnabled(true);
            ui->deleteButton->setEnabled(false);
            ui->toolbarButton->setEnabled(false);
            ui->createButton->setEnabled(false);
            ui->editButton->setEnabled(true); //but you can't save it
            ui->renameButton->setEnabled(false);
            ui->duplicateButton->setEnabled(false);
        }
        else {
            ui->executeButton->setEnabled(false);
            ui->deleteButton->setEnabled(false);
            ui->toolbarButton->setEnabled(false);
            ui->createButton->setEnabled(false);
            ui->editButton->setEnabled(false);
            ui->renameButton->setEnabled(false);
            ui->duplicateButton->setEnabled(false);
        }
    }

    if (item) {
        ui->LineEditMacroName->setText(item->text(0));
    }
    else {
        ui->LineEditMacroName->clear();
    }
}

/**
 * Executes the selected macro file.
 */
void DlgMacroExecuteImp::accept()
{
    QTreeWidgetItem* item;

    int index = ui->tabMacroWidget->currentIndex();
    if (index == 0) { //user-specific
        item = ui->userMacroListBox->currentItem();
    }
    else {
        //index == 1 system-wide
        item = ui->systemMacroListBox->currentItem();
    }
    if (!item)
        return;

    QDialog::accept();

    MacroItem * mitem = static_cast<MacroItem *>(item);

    QDir dir;

    if (!mitem->systemWide){
        dir =QDir(this->macroPath);
    }
    else {
        QString dirstr = QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro");
        dir = QDir(dirstr);
    }

    QFileInfo fi(dir, item->text(0));
    try {
        getMainWindow()->appendRecentMacro(fi.filePath());
        Application::Instance->macroManager()->run(Gui::MacroManager::File, fi.filePath().toUtf8());
        // after macro run recalculate the document
        if (Application::Instance->activeDocument())
            Application::Instance->activeDocument()->getDocument()->recompute();
    }
    catch (const Base::SystemExitException&) {
        // handle SystemExit exceptions
        Base::PyGILStateLocker locker;
        Base::PyException e;
        e.ReportException();
    }
}

/**
 * Specify the location of your macro files. The default location is FreeCAD's home path.
 */
void DlgMacroExecuteImp::on_fileChooser_fileNameChanged(const QString& fn)
{
    if (!fn.isEmpty())
    {
        // save the path in the parameters
        this->macroPath = fn;
        getWindowParameter()->SetASCII("MacroPath",fn.toUtf8());
        // fill the list box
        fillUpList();
    }
}

/**
 * Opens the macro file in an editor.
 */
void DlgMacroExecuteImp::on_editButton_clicked()
{
    QDir dir;
    QTreeWidgetItem* item = 0;

    int index = ui->tabMacroWidget->currentIndex();
    if (index == 0) { //user-specific
        item = ui->userMacroListBox->currentItem();
        dir.setPath(this->macroPath);
    }
    else {
        //index == 1 system-wide
        item = ui->systemMacroListBox->currentItem();
        dir.setPath(QString::fromUtf8(App::GetApplication().getHomePath()) + QString::fromUtf8("Macro"));
    }

    if (!item)
        return;

    MacroItem * mitem = static_cast<MacroItem *>(item);

    QString file = QString::fromLatin1("%1/%2").arg(dir.absolutePath(), item->text(0));
    PythonEditor* editor = new PythonEditor();
    editor->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python"));
    PythonEditorView* edit = new PythonEditorView(editor, getMainWindow());
    edit->setDisplayName(PythonEditorView::FileName);
    edit->open(file);
    edit->resize(400, 300);
    getMainWindow()->addWindow(edit);
    getMainWindow()->appendRecentMacro(file);

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

/** Creates a new macro file. */
void DlgMacroExecuteImp::on_createButton_clicked()
{
    // query file name
    QString fn = QInputDialog::getText(this, tr("Macro file"), tr("Enter a file name, please:"),
        QLineEdit::Normal, QString(), nullptr, Qt::MSWindowsFixedSizeDialogHint);
    if (!fn.isEmpty())
    {
        QString suffix = QFileInfo(fn).suffix().toLower();
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py"))
            fn += QLatin1String(".FCMacro");
        QDir dir(this->macroPath);
        // create the macroPath if nonexistent
        if (!dir.exists()) {
            dir.mkpath(this->macroPath);
        }
        QFileInfo fi(dir, fn);
        if (fi.exists() && fi.isFile())
        {
            QMessageBox::warning(this, tr("Existing file"),
                tr("'%1'.\nThis file already exists.").arg(fi.fileName()));
        }
        else
        {
            QFile file(fi.absoluteFilePath());
            if (!file.open(QFile::WriteOnly)) {
                QMessageBox::warning(this, tr("Cannot create file"),
                    tr("Creation of file '%1' failed.").arg(fi.absoluteFilePath()));
                return;
            }
            file.close();
            PythonEditor* editor = new PythonEditor();
            editor->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python"));
            PythonEditorView* edit = new PythonEditorView(editor, getMainWindow());
            edit->open(fi.absoluteFilePath());
            getMainWindow()->appendRecentMacro(fi.absoluteFilePath());
            edit->setWindowTitle(QString::fromLatin1("%1[*]").arg(fn));
            edit->resize(400, 300);
            getMainWindow()->addWindow(edit);
            close();
        }
    }
}

/** Deletes the selected macro file from your harddisc. */
void DlgMacroExecuteImp::on_deleteButton_clicked()
{
    QTreeWidgetItem* item = ui->userMacroListBox->currentItem();
    if (!item)
        return;

    MacroItem * mitem = static_cast<MacroItem *>(item);

    if (mitem->systemWide) {
        QMessageBox::critical(qApp->activeWindow(), QObject::tr("Delete macro"),
            QObject::tr("Not allowed to delete system-wide macros"));
        return;
    }

    QString fn = item->text(0);
    int ret = QMessageBox::question(this, tr("Delete macro"),
        tr("Do you really want to delete the macro '%1'?").arg( fn ),
        QMessageBox::Yes, QMessageBox::No|QMessageBox::Default|QMessageBox::Escape);
    if (ret == QMessageBox::Yes)
    {
        QDir dir(this->macroPath);
        dir.remove(fn);
        int index = ui->userMacroListBox->indexOfTopLevelItem(item);
        ui->userMacroListBox->takeTopLevelItem(index);
        delete item;
    }
}

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

void DlgMacroExecuteImp::on_toolbarButton_clicked()
{
    /**
     * advise user of what we are doing, offer chance to cancel
     * unless user already said not to show this messagebox again
    **/

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

    QTreeWidgetItem* item = ui->userMacroListBox->currentItem();
    if (!item)
        return;

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

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

    /** check if user already has this macro command created, if so skip dialog 1 **/
    bool hasMacroCommand = false;
    QString macroMenuText;
    CommandManager & cCmdMgr = Application::Instance->commandManager();
    std::vector<Command*> aCmds = cCmdMgr.getGroupCommands("Macros");
    for (std::vector<Command*>::iterator it = aCmds.begin(); it != aCmds.end(); ++it) {
        MacroCommand* mc = dynamic_cast<MacroCommand*>(*it);
        if (mc && fn.compare(QLatin1String(mc->getScriptName())) == 0) {
            hasMacroCommand = true;
            macroMenuText = QString::fromLatin1(mc->getMenuText());
        }
    }

    QTabWidget* tabWidget = nullptr;

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

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

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

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

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

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

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

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

        dlg.exec();
    }

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

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

    if (hasMacroCommand){
        dlg.setWindowTitle(tr("Walkthrough, dialog 1 of 1"));
    } else {
        dlg.setWindowTitle(tr("Walkthrough, dialog 2 of 2"));
    }

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

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

    tabWidget->setCurrentWidget(setupToolbarPage);
    QPushButton* moveActionRightButton = setupToolbarPage->findChild<QPushButton*>(QString::fromLatin1("moveActionRightButton"));
    if (!moveActionRightButton){
        Base::Console().Warning("Toolbar walkthrough: Unable to find moveActionRightButton\n");
    } else {
        moveActionRightButton->setStyleSheet(QString::fromLatin1("background-color: red"));
    }
    /** tailor instructions depending on whether user already has custom toolbar created
     * if not he needs to click New button to create one first
    **/

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

        int globalIdx = workbenchBox->findData(QString::fromLatin1("Global"));
        if (globalIdx != -1){
            workbenchBox->setCurrentIndex(globalIdx);
            QMetaObject::invokeMethod(setupToolbarPage, "on_workbenchBox_activated",
                Qt::DirectConnection,
                QGenericReturnArgument(),
                Q_ARG(int, globalIdx));
        } else {
            Base::Console().Warning("Toolbar walkthrough: Unable to find Global workbench\n");
        }

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

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

    /** find Macros category and select it for the user **/
    QComboBox* categoryBox = setupToolbarPage->findChild<QComboBox*>(QString::fromLatin1("categoryBox"));
    if (!categoryBox){
        Base::Console().Warning("Toolbar walkthrough: Unable to find categoryBox\n");
    } else {
        int macrosIdx = categoryBox->findText(tr("Macros"));
        if (macrosIdx != -1){
            categoryBox->setCurrentIndex(macrosIdx);
            QMetaObject::invokeMethod(setupToolbarPage, "on_categoryBox_activated",
                Qt::DirectConnection,
                QGenericReturnArgument(),
                Q_ARG(int, macrosIdx));
        } else {
            Base::Console().Warning("Toolbar walkthrough: Unable to find Macros in categoryBox\n");
        }
    }

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

    /** preselect macro command for user **/

    QTreeWidget* commandTreeWidget = setupToolbarPage->findChild<QTreeWidget*>(QString::fromLatin1("commandTreeWidget"));
    if (!commandTreeWidget) {
        Base::Console().Warning("Toolbar walkthrough: Unable to find commandTreeWidget\n");
    } else {
        if (!hasMacroCommand){  //will be the last in the list, the one just created
            commandTreeWidget->setCurrentItem(commandTreeWidget->topLevelItem(commandTreeWidget->topLevelItemCount()-1));
            commandTreeWidget->scrollToItem(commandTreeWidget->currentItem());
        } else {  //pre-select it for the user (will be the macro menu text)
            QList <QTreeWidgetItem*> items = commandTreeWidget->findItems(macroMenuText, Qt::MatchFixedString | Qt::MatchWrap,1);
            if (!items.empty()) {
                commandTreeWidget->setCurrentItem(items[0]);
                commandTreeWidget->scrollToItem(commandTreeWidget->currentItem());
            }
        }
    }
    dlg.exec();
}



/**
 * renames the selected macro
 */
void DlgMacroExecuteImp::on_renameButton_clicked()
{
    QDir dir;
    QTreeWidgetItem* item = 0;

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

    if (!item)
        return;

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

    // query new name
    QString fn = QInputDialog::getText(this, tr("Renaming Macro File"),
        tr("Enter new name:"), QLineEdit::Normal, oldName, nullptr, Qt::MSWindowsFixedSizeDialogHint);
    if (!fn.isEmpty() && fn != oldName) {
        QString suffix = QFileInfo(fn).suffix().toLower();
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py"))
            fn += QLatin1String(".FCMacro");
        QFileInfo fi(dir, fn);
        // check if new name exists
        if (fi.exists()) {
            QMessageBox::warning(this, tr("Existing file"),
                tr("'%1'\n already exists.").arg(fi.absoluteFilePath()));
        }
        else if (!oldfile.rename(fi.absoluteFilePath())) {
            QMessageBox::warning(this, tr("Rename Failed"),
                tr("Failed to rename to '%1'.\nPerhaps a file permission error?").arg(fi.absoluteFilePath()));
        }
        else {
            // keep the item selected although it's not necessarily in alphabetic order
            item->setText(0, fn);
            ui->LineEditMacroName->setText(fn);
        }
    }
}
/**Duplicates selected macro
 * New file has same name as original but with "@" and 3-digit number appended
 * Begins with "@001" and increments until available name is found
 * "MyMacro.FCMacro" becomes "MyMacro@001.FCMacro"
 * "MyMacro@002.FCMacro.py" becomes "MyMacro@003.FCMacro.py" unless there is
 * no already existing "MyMacro@001.FCMacro.py"
 */
void DlgMacroExecuteImp::on_duplicateButton_clicked()
{
    QDir dir;
    QTreeWidgetItem* item = 0;

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

    if (!item){
        return;
    }

    QString oldName = item->text(0);
    QFileInfo oldfi(dir, oldName);
    QFile oldfile(oldfi.absoluteFilePath());
    QString completeSuffix = oldfi.completeSuffix(); //everything after the first "."
    QString baseName = oldfi.baseName(); //everything before first "."
    QString neutralSymbol = QString::fromStdString("@");
    QString last3 = baseName.right(3);
    bool ok = true; //was conversion to int successful?
    int nLast3 = last3.toInt(&ok);
    last3 = QString::fromStdString("001"); //increment beginning with 001 no matter what
    if (ok ){
        //last3 were all digits, so we strip them from the base name
        if (baseName.size()>3){ //if <= 3 leave be (e.g. 2.py becomes 2@001.py)
            baseName = baseName.left(baseName.size()-3); //strip digits
            if (baseName.endsWith(neutralSymbol)){
                baseName = baseName.left(baseName.size()-1); //trim the "@", will be added back later
            }
        }
    }
    //at this point baseName = the base name without any digits, e.g. "MyMacro"
    //neutralSymbol = "@"
    //last3 is a string representing 3 digits, always "001" at this time
    //completeSuffix = FCMacro or py or FCMacro.py or else suffix will become FCMacro below

    QString oldNameDigitized = baseName+neutralSymbol+last3+QString::fromStdString(".")+completeSuffix;
    QFileInfo fi(dir, oldNameDigitized);
    // increment until we find available name with smallest digits
    // test from "001" through "999", then give up and let user enter name of choice
    while (fi.exists()) {
        nLast3 = last3.toInt()+1;
        if (nLast3 >=1000){ //avoid infinite loop, 999 files will have to be enough
            break;
        }
        last3 = QString::number(nLast3);
        while (last3.size()<3){
            last3.prepend(QString::fromStdString("0")); //pad 0's if needed
        }
        oldNameDigitized = baseName+neutralSymbol+last3+QString::fromStdString(".")+completeSuffix;
        fi = QFileInfo(dir,oldNameDigitized);
    }

    // give user a chance to pick a different name from digitized name suggested
    QString fn = QInputDialog::getText(this, tr("Duplicate Macro"),
        tr("Enter new name:"), QLineEdit::Normal, oldNameDigitized, 
        nullptr, Qt::MSWindowsFixedSizeDialogHint);
    if (!fn.isEmpty() && fn != oldName) {
        QString suffix = QFileInfo(fn).suffix().toLower();
        if (suffix != QLatin1String("fcmacro") && suffix != QLatin1String("py")){
            fn += QLatin1String(".FCMacro");
        }
        QFileInfo fi(dir, fn);
        // check again if new name exists in case user changed it
        if (fi.exists()) {
            QMessageBox::warning(this, tr("Existing file"),
                tr("'%1'\n already exists.").arg(fi.absoluteFilePath()));
        }
        else if (!oldfile.copy(fi.absoluteFilePath())) {
            QMessageBox::warning(this, tr("Duplicate Failed"),
                tr("Failed to duplicate to '%1'.\nPerhaps a file permission error?").arg(fi.absoluteFilePath()));
        }

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

}

/**
 * convenience link button to open tools -> addon manager
 * from within macro dialog
 */
void DlgMacroExecuteImp::on_addonsButton_clicked()
{
    CommandManager& rMgr=Application::Instance->commandManager();
    rMgr.runCommandByName("Std_AddonMgr");
    this->fillUpList();
}

#include "moc_DlgMacroExecuteImp.cpp"
