// SPDX-FileCopyrightText: 2021 Ernie Pasveer <epasveer@att.net>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "SeerVariableTrackerBrowserWidget.h"
#include "SeerUtl.h"
#include <QtWidgets/QTreeWidget>
#include <QtWidgets/QTreeWidgetItemIterator>
#include <QtWidgets/QApplication>
#include <QtWidgets/QMenu>
#include <QtGui/QFontDatabase>
#include <QtGui/QClipboard>
#include <QtCore/QTimer>
#include <QtCore/QSettings>
#include <QtCore/QDebug>

SeerVariableTrackerBrowserWidget::SeerVariableTrackerBrowserWidget (QWidget* parent) : QWidget(parent) {

    // Construct the UI.
    setupUi(this);

    // Setup the widgets
    variablesTreeWidget->setMouseTracking(true);
    variablesTreeWidget->setSortingEnabled(false);
    variablesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    variablesTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
    variablesTreeWidget->resizeColumnToContents(0); // name
    variablesTreeWidget->resizeColumnToContents(1); // value
    variablesTreeWidget->resizeColumnToContents(2); // id
    variablesTreeWidget->resizeColumnToContents(3); // used
    variablesTreeWidget->setColumnHidden(2, true);  // Hide the 'id' column.
    variablesTreeWidget->setColumnHidden(3, true);  // Hide the 'used' column.
    variablesTreeWidget->clear();

    // Connect things.
    QObject::connect(variableAddLineEdit,            &QLineEdit::returnPressed,                     this, &SeerVariableTrackerBrowserWidget::handleAddLineEdit);
    QObject::connect(variableDeleteToolButton,       &QToolButton::clicked,                         this, &SeerVariableTrackerBrowserWidget::handleDeleteToolButton);
    QObject::connect(variableDeleteAllToolButton,    &QToolButton::clicked,                         this, &SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton);
    QObject::connect(variablesTreeWidget,            &QTreeWidget::itemCollapsed,                   this, &SeerVariableTrackerBrowserWidget::handleItemCollapsed);
    QObject::connect(variablesTreeWidget,            &QTreeWidget::itemExpanded,                    this, &SeerVariableTrackerBrowserWidget::handleItemExpanded);
    QObject::connect(variablesTreeWidget,            &QTreeWidget::itemEntered,                     this, &SeerVariableTrackerBrowserWidget::handleItemEntered);
    QObject::connect(variablesTreeWidget,            &QTreeWidget::customContextMenuRequested,      this, &SeerVariableTrackerBrowserWidget::handleContextMenu);
    QObject::connect(variableRememberToolButton,     &QToolButton::toggled,                         this, &SeerVariableTrackerBrowserWidget::handleRememberToolButton);

    // Read state.
    readSettings();
}

SeerVariableTrackerBrowserWidget::~SeerVariableTrackerBrowserWidget () {
}

void SeerVariableTrackerBrowserWidget::handleText (const QString& text) {

    QApplication::setOverrideCursor(Qt::BusyCursor);

    while (1) {
        if (text.startsWith("^done,DataExpressionTable={") && text.endsWith("}")) {

            // "^done,DataExpressionTable={
            //          entry={id=\"1\",expression=\"s\"},
            //          entry={id=\"2\",expression=\"v\"},
            //          entry={id=\"4\",expression=\"l\"},
            //          entry={id=\"5\",expression=\"m\"}
            //      }"

            QString frame_text = Seer::parseFirst(text, "DataExpressionTable=", '{', '}', false);

            QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false);

            for (int i=0; i<entries_list.count(); i++) {

                QString entry_text      = entries_list[i];
                QString id_text         = Seer::parseFirst(entry_text, "id=",         '"', '"', false);
                QString expression_text = Seer::parseFirst(entry_text, "expression=", '"', '"', false);

                QList<QTreeWidgetItem*> matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2);

                if (matches.count() == 0) {

                    QTreeWidgetItem* topItem = new QTreeWidgetItem;
                    topItem->setText(0, expression_text);
                    topItem->setText(1, "");
                    topItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
                    topItem->setText(2, id_text);
                    topItem->setText(3, "new");

                    variablesTreeWidget->addTopLevelItem(topItem);
                }
            }

        }else if (text.startsWith("^done,DataExpressionAdded={") && text.endsWith("}")) {

            // "^done,DataExpressionAdded={
            //          id=\"5\",
            //          expression=\"m\"
            //      }"

            QString frame_text      = Seer::parseFirst(text,       "DataExpressionAdded=", '{', '}', false);
            QString id_text         = Seer::parseFirst(frame_text, "id=",                  '"', '"', false);
            QString expression_text = Seer::parseFirst(frame_text, "expression=",          '"', '"', false);

            QList<QTreeWidgetItem*> matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2);

            if (matches.count() == 0) {

                QTreeWidgetItem* topItem = new QTreeWidgetItem;
                topItem->setText(0, expression_text);
                topItem->setText(1, "");
                topItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
                topItem->setText(2, id_text);
                topItem->setText(3, "new");

                variablesTreeWidget->addTopLevelItem(topItem);

                refreshValues();

                emit raiseTab();
            }

        }else if (text.startsWith("^done,DataExpressionDeleted={") && text.endsWith("}")) {

            // "^done,DataExpressionDeleted={
            //          entry={id=\"1\",expression=\"s\"},
            //          entry={id=\"3\",expression=\"vb\"}
            //      }"

            QString frame_text = Seer::parseFirst(text, "DataExpressionDeleted=", '{', '}', false);

            QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false);

            for (int i=0; i<entries_list.count(); i++) {

                QString entry_text      = entries_list[i];
                QString id_text         = Seer::parseFirst(entry_text, "id=",         '"', '"', false);
                QString expression_text = Seer::parseFirst(entry_text, "expression=", '"', '"', false);

                QList<QTreeWidgetItem*> matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2);

                qDeleteAll(matches);
            }

        }else if (text.contains(QRegularExpression("^([0-9]+)\\^done,value="))) {

            // "6^done,value=\"\\\"abc\\\"\""

            QString id_text    = text.section('^', 0,0);
            QString value_text = Seer::parseFirst(text, "value=", '"', '"', false);

            // If with brackets (a structure), filter out excess '\n'
            // in case pretty-print-on is used.
            if (value_text.front() == '{' && value_text.back() == '}') {
                value_text = Seer::filterBareNewLines(value_text);
            }

            // Find the ones that match our 'id'.
            QList<QTreeWidgetItem*> matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2);

            if (matches.count() == 1) {

                Q_ASSERT(matches.count() == 1);

                // There should be only one.
                QTreeWidgetItem* item = matches[0];

                // Mark each entry initially as "unused".
                // Later, some will be marked as "reused" or "new". Then the "unused" ones will
                // be deleted.
                QTreeWidgetItemIterator itmark(item);
                while (*itmark) {
                    (*itmark)->setText(3, "unused");
                    ++itmark;
                }

                QString old_text = item->text(1);
                old_text.replace("\\\"", "\"");
                old_text.remove('\\');
                value_text.replace("\\\"", "\"");
                value_text.remove('\\');
                if (old_text == "")
                {
                    // If old_text is empty -> This node is created for the first time. Pass the same text for old and new
                    handleItemCreate (item, value_text, value_text);
                }
                else
                {
                    old_text = Seer::filterBareNewLines(old_text);
                    handleItemCreate (item, value_text, old_text);
                }

                emit raiseTab();

                // At this point, there are some new entries, some reused entries, and some unused ones.
                // For now, don't bother deleting 'unused' ones.
            }

        }else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg="))) {

            // "1^error,msg=\"No symbol \\\"j\\\" in current context.\""

            QString id_text  = text.section('^', 0,0);
            QString msg_text = Seer::parseFirst(text, "msg=", '"', '"', false);

            // Find the ones that match our 'id'.
            QList<QTreeWidgetItem*> matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2);

            if (matches.count() == 1) {

                // There should be only one.
                QTreeWidgetItem* item = matches[0];

                // Remove any children.
                QList<QTreeWidgetItem*> children = item->takeChildren();

                qDeleteAll(children);

                // Set the text with the error message.
                item->setText(1, Seer::filterEscapes(msg_text));
                item->setText(3, "used");
            }

        }else{
            // Ignore others.
        }

        variablesTreeWidget->resizeColumnToContents(0);
        variablesTreeWidget->resizeColumnToContents(1);
        variablesTreeWidget->resizeColumnToContents(2);
        variablesTreeWidget->resizeColumnToContents(3);

        break;
    }

    QApplication::restoreOverrideCursor();
}

void SeerVariableTrackerBrowserWidget::handleStoppingPointReached () {

    // Don't do any work if the widget is hidden.
    if (isHidden()) {
        return;
    }

    refresh();
}

void SeerVariableTrackerBrowserWidget::handleSessionTerminated () {

    // Remember the variable names?
    if (variableRememberToolButton->isChecked() == true) {
        return;
    }

    // Delete previous contents.
    variablesTreeWidget->clear();

    // Tell the GdbWidget to forget all tracked expressions.
    emit deleteVariableExpressions("*");
}

void SeerVariableTrackerBrowserWidget::refresh () {
    emit refreshVariableTrackerNames();
    emit refreshVariableTrackerValues();
}

void SeerVariableTrackerBrowserWidget::refreshValues () {
    emit refreshVariableTrackerValues();
}

void SeerVariableTrackerBrowserWidget::handleAddLineEdit () {

    QString variable = variableAddLineEdit->text();

    variableAddLineEdit->clear();

    if (variable != "") {
        emit addVariableExpression(variable);

        // After sending the 'add' signal, schedule a 'refresh' 200ms later.
        QTimer::singleShot(200, this, &SeerVariableTrackerBrowserWidget::refresh);
    }
}

void SeerVariableTrackerBrowserWidget::handleDeleteToolButton () {

    // Get selected tree items.
    QList<QTreeWidgetItem*> items = variablesTreeWidget->selectedItems();

    // Build a string that is a list of variable ids.
    QString variableids;

    QList<QTreeWidgetItem*>::iterator i;
    for (i = items.begin(); i != items.end(); ++i) {

        if (i != items.begin()) {
            variableids += " ";
        }

        variableids += (*i)->text(2);
    }

    // Don't do anything if the list of variables is empty.
    if (variableids == "") {
        return;
    }

    // Send the signal.
    emit deleteVariableExpressions(variableids);
}

void SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton () {

    emit deleteVariableExpressions("*");
}

void SeerVariableTrackerBrowserWidget::handleItemExpanded (QTreeWidgetItem* item) {

    Q_UNUSED(item);

    variablesTreeWidget->resizeColumnToContents(0);
    variablesTreeWidget->resizeColumnToContents(1);
    variablesTreeWidget->resizeColumnToContents(2);
    variablesTreeWidget->resizeColumnToContents(3);
}

void SeerVariableTrackerBrowserWidget::handleItemCollapsed (QTreeWidgetItem* item) {

    Q_UNUSED(item);

    variablesTreeWidget->resizeColumnToContents(0);
    variablesTreeWidget->resizeColumnToContents(1);
    variablesTreeWidget->resizeColumnToContents(2);
    variablesTreeWidget->resizeColumnToContents(3);
}

void SeerVariableTrackerBrowserWidget::handleItemEntered (QTreeWidgetItem* item, int column) {

    Q_UNUSED(column);

    item->setToolTip(0, item->text(0) + " : " + Seer::elideText(item->text(1), Qt::ElideRight, 100));

    for (int i=1; i<variablesTreeWidget->columnCount(); i++) { // Copy tooltip to other columns.
        item->setToolTip(i, item->toolTip(0));
    }
}

void SeerVariableTrackerBrowserWidget::showEvent (QShowEvent* event) {

    QWidget::showEvent(event);

    refresh();
}

void SeerVariableTrackerBrowserWidget::handleItemCreate (QTreeWidgetItem* item, const QString& value_text, const QString& old_text) {

    handleItemCreate(item, item->text(2), item->text(0), value_text, old_text);
}

void SeerVariableTrackerBrowserWidget::handleItemCreate (QTreeWidgetItem* parentItem, const QString& id_text, const QString& name_text, const QString& value_text, const QString& old_text) {

    // Fill in parent item. Whether is a simple or complex entry.
    parentItem->setText(0, name_text);
    parentItem->setText(1, Seer::filterEscapes(value_text));
    parentItem->setText(2, id_text);
    parentItem->setText(3, "reused");

    // Look for bookmarks. This indicates a nested structure.
    // There are two types. With and without an address.

    QString capture0; // With bookends.
    QString capture1; // Without.

    QRegularExpression withaddress_re("^@0[xX][0-9a-fA-F]+: \\{(.*?)\\}$");
    QRegularExpressionMatch withaddress_match = withaddress_re.match(value_text, 0, QRegularExpression::PartialPreferCompleteMatch);

    if (withaddress_match.hasMatch()) {
        capture0 = withaddress_match.captured(0);
        capture1 = withaddress_match.captured(1);

    }else{
        QRegularExpression noaddress_re("^\\{(.*?)\\}$");
        QRegularExpressionMatch noaddress_match = noaddress_re.match(value_text, 0, QRegularExpression::PartialPreferCompleteMatch);

        if (noaddress_match.hasMatch()) {
            capture0 = noaddress_match.captured(0);
            capture1 = noaddress_match.captured(1);
        }
    }

    // Simple entries don't have children. Delete them.
    // Then we're done.
    if (capture0 == "" || capture1 == "") {

        while (parentItem->childCount() > 0) {
            QTreeWidgetItem* item = parentItem->child(0);
            delete item;
        }
    }

    // Do the same for old_text
    QString captureOld0; // With bookends.
    QString captureOld1; // Without.

    QRegularExpressionMatch withaddress_match_Old = withaddress_re.match(old_text, 0, QRegularExpression::PartialPreferCompleteMatch);

    if (withaddress_match_Old.hasMatch()) {
        captureOld0 = withaddress_match_Old.captured(0);
        captureOld1 = withaddress_match_Old.captured(1);

    }else{
        QRegularExpression noaddress_re("^\\{(.*?)\\}$");
        QRegularExpressionMatch noaddress_match = noaddress_re.match(old_text, 0, QRegularExpression::PartialPreferCompleteMatch);

        if (noaddress_match.hasMatch()) {
            captureOld0 = noaddress_match.captured(0);
            captureOld1 = noaddress_match.captured(1);
        }
    }

    // Add the complex entry to the tree. Reuse, if possible.
    // Instead of creating a new tree each time, we will reuse existing items, if they are there.
    // This allows the expanded items to remain expanded. We start by looking for matches that
    // may already be there. If there are matches, the code will reuse it.  If not, a new item
    // is created by the code. Note, when searching, we only look at the current level. Not any
    // children.

    // Use the one without bookends.
    QString text = capture1;

    // Convert to a list of name/value pairs.
    QStringList nv_pairs;
    if (text.startsWith("{"))
    {
        // String might describe an array: {a=1, b=1},{a=1, b=1},{a=1, b=1},{a=1, b=1}
        nv_pairs = Seer::parseArray(parentItem->text(0), text);
    }
    else
    {
        nv_pairs = Seer::parseCommaList(text, '{', '}');
    }

    QStringList nv_old_pairs;
    if (text.startsWith("{"))
    {
        // String might describe an array: {a=1, b=1},{a=1, b=1},{a=1, b=1},{a=1, b=1}
        nv_old_pairs = Seer::parseArray(parentItem->text(0), captureOld1);
    }
    else
    {
        nv_old_pairs = Seer::parseCommaList(captureOld1, '{', '}');
    }

    if (nv_old_pairs.size() != nv_pairs.size())         // Check if variable has changed type or is out of scope
    {
        // If sizes don't match, it seems that variables is out of scope or changed type
        // First, delete all children, otherwise we might have leftovers
        while (parentItem->childCount() > 0) {
            QTreeWidgetItem* item = parentItem->child(0);
            delete item;
        }
        // Then, recreate all children
        for (const auto& nv : nv_pairs)
        {
            QStringPair pair = Seer::parseNameValue(nv, '=');
            // Look for the existing child, if any so we can reuse it.
            QTreeWidgetItem* childItem = 0;
            for (int i=0; i<parentItem->childCount(); i++) {
                if (parentItem->child(i)->text(0) == pair.first) {
                    childItem = parentItem->child(i);
                    childItem->setText(0, pair.first);
                    childItem->setText(1, pair.second);
                    childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
                    childItem->setText(2, id_text);
                    childItem->setText(3, "reused");
                    break;
                }
            }

            // Otherwise, create a new child.
            if (childItem == 0) {
                childItem = new QTreeWidgetItem;
                childItem->setText(0, pair.first);
                childItem->setText(1, pair.second);
                childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
                childItem->setText(2, id_text);
                childItem->setText(3, "new");

                parentItem->addChild(childItem);
            }
            handleItemCreate(childItem, id_text, childItem->text(0), childItem->text(1), "");
        }
        return;
    }

    QFont parentFont = parentItem->font(0);
    parentFont.setBold(old_text != value_text);     // if value has changed -> set font to bold
    parentItem->setFont(0, parentFont);
    parentItem->setFont(1, parentFont);

    // Go through each pair and add the name and its value to the tree.
    for (int i = 0; i < nv_pairs.size(); ++i) {
        QString nv      = nv_pairs[i];
        QString nv_old  = nv_old_pairs[i];

        QStringPair pair        = Seer::parseNameValue(nv, '=');
        QStringPair old_pair    = Seer::parseNameValue(nv_old, '=');
        // Look for the existing child, if any so we can reuse it.
        QTreeWidgetItem* childItem = 0;
        for (int i=0; i<parentItem->childCount(); i++) {
            if (parentItem->child(i)->text(0) == pair.first) {
                childItem = parentItem->child(i);
                childItem->setText(0, pair.first);
                childItem->setText(1, pair.second);
                childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
                childItem->setText(2, id_text);
                childItem->setText(3, "reused");

                QFont childFontCol0 = childItem->font(0);
                QFont childFontCol1 = QFontDatabase::systemFont(QFontDatabase::FixedFont);
                childFontCol0.setBold(pair.second != old_pair.second);
                childFontCol1.setBold(pair.second != old_pair.second);
                childItem->setFont(0, childFontCol0);
                childItem->setFont(1, childFontCol1);

                break;
            }
        }

        // Otherwise, create a new child.
        if (childItem == 0) {
            childItem = new QTreeWidgetItem;
            childItem->setText(0, pair.first);
            childItem->setText(1, pair.second);
            childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont));
            childItem->setText(2, id_text);
            childItem->setText(3, "new");

            parentItem->addChild(childItem);
        }

        handleItemCreate(childItem, id_text, childItem->text(0), childItem->text(1), old_pair.second);
    }
}

void SeerVariableTrackerBrowserWidget::handleContextMenu (const QPoint& pos) {

    // Get the item at the cursor.
    QTreeWidgetItem* item = variablesTreeWidget->itemAt(pos);

    // Construct the menu.
    QAction* addMemoryVisualizerAction           = new QAction();
    QAction* addMemoryAsteriskVisualizerAction   = new QAction();
    QAction* addMemoryAmpersandVisualizerAction  = new QAction();
    QAction* addArrayVisualizerAction            = new QAction();
    QAction* addArrayAsteriskVisualizerAction    = new QAction();
    QAction* addArrayAmpersandVisualizerAction   = new QAction();
    QAction* addMatrixVisualizerAction           = new QAction();
    QAction* addMatrixAsteriskVisualizerAction   = new QAction();
    QAction* addMatrixAmpersandVisualizerAction  = new QAction();
    QAction* addStructVisualizerAction           = new QAction();
    QAction* addStructAsteriskVisualizerAction   = new QAction();
    QAction* addStructAmpersandVisualizerAction  = new QAction();

    QMenu menu("Options", this);

    QMenu memoryVisualizerMenu("Add variable to a Memory Visualizer");
    memoryVisualizerMenu.addAction(addMemoryVisualizerAction);
    memoryVisualizerMenu.addAction(addMemoryAsteriskVisualizerAction);
    memoryVisualizerMenu.addAction(addMemoryAmpersandVisualizerAction);
    menu.addMenu(&memoryVisualizerMenu);

    QMenu arrayVisualizerMenu("Add variable to an Array Visualizer");
    arrayVisualizerMenu.addAction(addArrayVisualizerAction);
    arrayVisualizerMenu.addAction(addArrayAsteriskVisualizerAction);
    arrayVisualizerMenu.addAction(addArrayAmpersandVisualizerAction);
    menu.addMenu(&arrayVisualizerMenu);

    QMenu matrixVisualizerMenu("Add variable to a Matrix Visualizer");
    matrixVisualizerMenu.addAction(addMatrixVisualizerAction);
    matrixVisualizerMenu.addAction(addMatrixAsteriskVisualizerAction);
    matrixVisualizerMenu.addAction(addMatrixAmpersandVisualizerAction);
    menu.addMenu(&matrixVisualizerMenu);

    QMenu structVisualizerMenu("Add variable to a Struct Visualizer");
    structVisualizerMenu.addAction(addStructVisualizerAction);
    structVisualizerMenu.addAction(addStructAsteriskVisualizerAction);
    structVisualizerMenu.addAction(addStructAmpersandVisualizerAction);
    menu.addMenu(&structVisualizerMenu);

    QAction* deleteAction        = menu.addAction("Delete selected");
    QAction* deleteAllAction     = menu.addAction("Delete all");
    QAction* copyAction          = menu.addAction("Copy selected");
    QAction* copyValueOnlyAction = menu.addAction("Copy selected value");
    QAction* copyAllAction       = menu.addAction("Copy all");

    QString actionText;
    if (item != 0) {
        actionText = item->text(0);
    }

    QString variable;
    if (item != 0) {
        // Build up a variable string, incase it is a nested struct.
        QTreeWidgetItem* i = item;
        variable = item->text(0);
        while (i->parent() != 0) {
            variable = i->parent()->text(0) + "." + variable;
            i = i->parent();
        }
    }

    // Remove potential '&' and '*' characters at start of string.
    variable.remove(QRegularExpression("^[\\*\\&]*"));

    addMemoryVisualizerAction->setText(QString("\"%1\"").arg(actionText));
    addMemoryAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText));
    addMemoryAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText));
    addArrayVisualizerAction->setText(QString("\"%1\"").arg(actionText));
    addArrayAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText));
    addArrayAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText));
    addMatrixVisualizerAction->setText(QString("\"%1\"").arg(actionText));
    addMatrixAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText));
    addMatrixAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText));
    addStructVisualizerAction->setText(QString("\"%1\"").arg(actionText));
    addStructAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText));
    addStructAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText));

    // If no selected item, disable everything but allow 'copyall' and 'deleteall'.
    if (item == 0) {
        memoryVisualizerMenu.setEnabled(false);
        arrayVisualizerMenu.setEnabled(false);
        matrixVisualizerMenu.setEnabled(false);
        structVisualizerMenu.setEnabled(false);
        deleteAction->setEnabled(false);
        copyAction->setEnabled(false);
    }

    // Execute the menu. Return if nothing.
    QAction* action = menu.exec(variablesTreeWidget->mapToGlobal(pos));

    if (action == 0) {
        return;
    }

    if (action == deleteAction) {
        handleDeleteToolButton();
    }

    if (action == deleteAllAction) {
        handleDeleteAllToolButton();
    }

    if (action == copyAction || action == copyAllAction || action == copyValueOnlyAction) {
        // Get selected tree items.
        QList<QTreeWidgetItem*> items;

        // Get list of 'select' items.
        if (action == copyAction || copyValueOnlyAction) {
            items = variablesTreeWidget->selectedItems();
        }

        // Get list of 'all' items.
        if (action == copyAllAction) {
            items = variablesTreeWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard);
        }

        // Populate the clipboard.
        if (items.count() == 0) {
            return;
        }

        QClipboard* clipboard = QGuiApplication::clipboard();

        QString text;

        for (int i=0; i<items.count(); i++) {

            if (i != 0) {
                text += '\n';
            }

            if (action != copyValueOnlyAction) {
                text += items[i]->text(0) + ":" + items[i]->text(1);
            } else {
                text += items[i]->text(1);
            }
        }

        clipboard->setText(text, QClipboard::Clipboard);
        clipboard->setText(text, QClipboard::Selection);
    }

    // Handle adding memory to visualize.
    if (action == addMemoryVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMemoryVisualizer(variable);
        }

        return;
    }

    // Handle adding memory to visualize.
    if (action == addMemoryAsteriskVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMemoryVisualizer(QString("*") + variable);
        }

        return;
    }

    // Handle adding memory to visualize.
    if (action == addMemoryAmpersandVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMemoryVisualizer(QString("&") + variable);
        }

        return;
    }

    // Handle adding array to visualize.
    if (action == addArrayVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addArrayVisualizer(variable);
        }

        return;
    }

    // Handle adding array to visualize.
    if (action == addArrayAsteriskVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addArrayVisualizer(QString("*") + variable);
        }

        return;
    }

    // Handle adding array to visualize.
    if (action == addArrayAmpersandVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addArrayVisualizer(QString("&") + variable);
        }

        return;
    }

    // Handle adding matrix to visualize.
    if (action == addMatrixVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMatrixVisualizer(variable);
        }

        return;
    }

    // Handle adding matrix to visualize.
    if (action == addMatrixAsteriskVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMatrixVisualizer(QString("*") + variable);
        }

        return;
    }

    // Handle adding matrix to visualize.
    if (action == addMatrixAmpersandVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addMatrixVisualizer(QString("&") + variable);
        }

        return;
    }

    // Handle adding struct to visualize.
    if (action == addStructVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addStructVisualizer(variable);
        }

        return;
    }

    // Handle adding struct to visualize.
    if (action == addStructAsteriskVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addStructVisualizer(QString("*") + variable);
        }

        return;
    }

    // Handle adding struct to visualize.
    if (action == addStructAmpersandVisualizerAction) {

        // Emit the signals.
        if (variable != "") {
            emit addStructVisualizer(QString("&") + variable);
        }

        return;
    }
}

void SeerVariableTrackerBrowserWidget::handleRememberToolButton (bool checked) {

    Q_UNUSED(checked);

    writeSettings();
}

void SeerVariableTrackerBrowserWidget::readSettings () {

    QSettings settings;
    bool      remember = false;

    settings.beginGroup("variabletrackerwindow"); {
        remember = settings.value("remembervariablenames", remember).toBool();
    } settings.endGroup();

    variableRememberToolButton->setChecked(remember);
}

void SeerVariableTrackerBrowserWidget::writeSettings () {

    QSettings settings;

    settings.beginGroup("variabletrackerwindow"); {
        settings.setValue("remembervariablenames", variableRememberToolButton->isChecked());
    } settings.endGroup();
}

