/* interface_toolbar.cpp
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <errno.h>

#include <ws_diag_control.h>

#if WS_IS_AT_LEAST_GNUC_VERSION(12,1)
DIAG_OFF(stringop-overflow)
#if WS_IS_AT_LEAST_GNUC_VERSION(13,0)
DIAG_OFF(restrict)
#endif
#endif
#include "interface_toolbar.h"
#if WS_IS_AT_LEAST_GNUC_VERSION(12,1)
DIAG_ON(stringop-overflow)
#if WS_IS_AT_LEAST_GNUC_VERSION(13,0)
DIAG_ON(restrict)
#endif
#endif
#include <ui/qt/widgets/interface_toolbar_lineedit.h>
#include "simple_dialog.h"
#include "main_application.h"
#include <ui_interface_toolbar.h>

#include "capture_opts.h"
#include "ui/capture_globals.h"
#include "sync_pipe.h"
#include "wsutil/file_util.h"

#ifdef _WIN32
#include <wsutil/win32-utils.h>
#endif

#include <QCheckBox>
#include <QComboBox>
#include <QDesktopServices>
#include <QLineEdit>
#include <QPushButton>
#include <QThread>
#include <QUrl>

static const char *interface_type_property = "control_type";
static const char *interface_role_property = "control_role";

// From interface control protocol.
enum InterfaceControlCommand {
    commandControlInitialized  = 0,
    commandControlSet          = 1,
    commandControlAdd          = 2,
    commandControlRemove       = 3,
    commandControlEnable       = 4,
    commandControlDisable      = 5,
    commandStatusMessage       = 6,
    commandInformationMessage  = 7,
    commandWarningMessage      = 8,
    commandErrorMessage        = 9
};

// To do:
// - Move control pipe handling to extcap

InterfaceToolbar::InterfaceToolbar(QWidget *parent, const iface_toolbar *toolbar) :
    QFrame(parent),
    ui(new Ui::InterfaceToolbar),
    help_link_(toolbar->help),
    use_spacer_(true)
{
    ui->setupUi(this);

    // Fill inn interfaces list and initialize default interface values
    for (GList *walker = toolbar->ifnames; walker; walker = walker->next)
    {
        QString ifname((char *)walker->data);
        interface_[ifname].reader_thread = NULL;
        interface_[ifname].out_fd = -1;
    }

    initializeControls(toolbar);

#ifdef Q_OS_MAC
    foreach (QWidget *w, findChildren<QWidget *>())
    {
        w->setAttribute(Qt::WA_MacSmallSize, true);
    }
#endif

    if (!use_spacer_)
    {
        ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
    }

    updateWidgets();
}

InterfaceToolbar::~InterfaceToolbar()
{
    foreach (QString ifname, interface_.keys())
    {
        foreach (int num, control_widget_.keys())
        {
            if (interface_[ifname].log_dialog.contains(num))
            {
                interface_[ifname].log_dialog[num]->close();
            }
        }
    }

    delete ui;
}

void InterfaceToolbar::initializeControls(const iface_toolbar *toolbar)
{
    for (GList *walker = toolbar->controls; walker; walker = walker->next)
    {
        iface_toolbar_control *control = (iface_toolbar_control *)walker->data;

        if (control_widget_.contains(control->num))
        {
            // Already have a widget with this number
            continue;
        }

        QWidget *widget = NULL;
        switch (control->ctrl_type)
        {
            case INTERFACE_TYPE_BOOLEAN:
                widget = createCheckbox(control);
                break;

            case INTERFACE_TYPE_BUTTON:
                widget = createButton(control);
                break;

            case INTERFACE_TYPE_SELECTOR:
                widget = createSelector(control);
                break;

            case INTERFACE_TYPE_STRING:
                widget = createString(control);
                break;

            default:
                // Not supported
                break;
        }

        if (widget)
        {
            widget->setProperty(interface_type_property, control->ctrl_type);
            widget->setProperty(interface_role_property, control->ctrl_role);
            control_widget_[control->num] = widget;
        }
    }
}

void InterfaceToolbar::setDefaultValue(int num, const QByteArray &value)
{
    foreach (QString ifname, interface_.keys())
    {
        // Adding default value to all interfaces
        interface_[ifname].value[num] = value;
    }
    default_value_[num] = value;
}

QWidget *InterfaceToolbar::createCheckbox(iface_toolbar_control *control)
{
    QCheckBox *checkbox = new QCheckBox(QString().fromUtf8(control->display));
    checkbox->setToolTip(QString().fromUtf8(control->tooltip));

    if (control->default_value.boolean)
    {
        checkbox->setCheckState(Qt::Checked);
        QByteArray default_value(1, 1);
        setDefaultValue(control->num, default_value);
    }

    connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxChanged(int)));

    ui->leftLayout->addWidget(checkbox);

    return checkbox;
}

QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control)
{
    QPushButton *button = new QPushButton(QString().fromUtf8((char *)control->display));
    button->setMaximumHeight(27);
    button->setToolTip(QString().fromUtf8(control->tooltip));

    switch (control->ctrl_role)
    {
        case INTERFACE_ROLE_CONTROL:
            setDefaultValue(control->num, (char *)control->display);
            connect(button, SIGNAL(clicked()), this, SLOT(onControlButtonClicked()));
            break;

        case INTERFACE_ROLE_HELP:
            connect(button, SIGNAL(clicked()), this, SLOT(onHelpButtonClicked()));
            if (help_link_.isEmpty())
            {
                // No help URL provided
                button->hide();
            }
            break;

        case INTERFACE_ROLE_LOGGER:
            connect(button, SIGNAL(clicked()), this, SLOT(onLogButtonClicked()));
            break;

        case INTERFACE_ROLE_RESTORE:
            connect(button, SIGNAL(clicked()), this, SLOT(onRestoreButtonClicked()));
            break;

        default:
            // Not supported
            break;
    }

    ui->rightLayout->addWidget(button);

    return button;
}

QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control)
{
    QLabel *label = new QLabel(QString().fromUtf8(control->display));
    label->setToolTip(QString().fromUtf8(control->tooltip));
    QComboBox *combobox = new QComboBox();
    combobox->setToolTip(QString().fromUtf8(control->tooltip));
    combobox->setSizeAdjustPolicy(QComboBox::AdjustToContents);

    for (GList *walker = control->values; walker; walker = walker->next)
    {
        iface_toolbar_value *val = (iface_toolbar_value *)walker->data;
        QString value = QString().fromUtf8((char *)val->value);
        if (value.isEmpty())
        {
            // Invalid value
            continue;
        }
        QString display = QString().fromUtf8((char *)val->display);
        QByteArray interface_value;

        interface_value.append(value.toUtf8());
        if (display.isEmpty())
        {
            display = value;
        }
        else
        {
            interface_value.append(QString('\0' + display).toUtf8());
        }
        combobox->addItem(display, value);
        if (val->is_default)
        {
            combobox->setCurrentText(display);
            setDefaultValue(control->num, value.toUtf8());
        }
        foreach (QString ifname, interface_.keys())
        {
            // Adding values to all interfaces
            interface_[ifname].list[control->num].append(interface_value);
        }
        default_list_[control->num].append(interface_value);
    }

    connect(combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboBoxChanged(int)));

    ui->leftLayout->addWidget(label);
    ui->leftLayout->addWidget(combobox);
    label_widget_[control->num] = label;

    return combobox;
}

QWidget *InterfaceToolbar::createString(iface_toolbar_control *control)
{
    QLabel *label = new QLabel(QString().fromUtf8(control->display));
    label->setToolTip(QString().fromUtf8(control->tooltip));
    InterfaceToolbarLineEdit *lineedit = new InterfaceToolbarLineEdit(NULL, control->validation, control->is_required);
    lineedit->setToolTip(QString().fromUtf8(control->tooltip));
    lineedit->setPlaceholderText(QString().fromUtf8(control->placeholder));

    if (control->default_value.string)
    {
        lineedit->setText(QString().fromUtf8(control->default_value.string));
        setDefaultValue(control->num, control->default_value.string);
    }

    connect(lineedit, SIGNAL(editedTextApplied()), this, SLOT(onLineEditChanged()));

    ui->leftLayout->addWidget(label);
    ui->leftLayout->addWidget(lineedit);
    label_widget_[control->num] = label;
    use_spacer_ = false;

    return lineedit;
}

void InterfaceToolbar::setWidgetValue(QWidget *widget, int command, QByteArray payload)
{
    // The QString(const QByteArray&) constructor will implicitly convert
    // payload to a QString. In Qt5 this truncates at the first '\0'.
    // (So string array payloads must be split first before converting.)
    // In Qt6 those are converted to UTF-16 U+0000. (So convert then split is OK.)
    // Other functions, like QComboBox::findData(), take a QVariant.
    // In Qt5 QVariants from QStrings and QByteArrays compare equal if the
    // QByteArray would convert to the same string; in Qt6 they don't.
    if (QComboBox *combobox = qobject_cast<QComboBox *>(widget))
    {
        combobox->blockSignals(true);
        switch (command)
        {
            case commandControlSet:
            {
                int idx = combobox->findData(payload);
                if (idx != -1)
                {
                    combobox->setCurrentIndex(idx);
                }
                break;
            }

            case commandControlAdd:
            {
                QByteArray value;
                QByteArray display;
                if (payload.contains('\0'))
                {
                    // The payload contains "value\0display"
                    QList<QByteArray> values = payload.split('\0');
                    value = values[0];
                    display = values[1];
                }
                else
                {
                    value = display = payload;
                }

                int idx = combobox->findData(value);
                if (idx != -1)
                {
                    // The value already exists, update item text
                    combobox->setItemText(idx, display);
                }
                else
                {
                    combobox->addItem(display, value);
                }
                break;
            }

            case commandControlRemove:
            {
                if (payload.size() == 0)
                {
                    combobox->clear();
                }
                else
                {
                    int idx = combobox->findData(payload);
                    if (idx != -1)
                    {
                        combobox->removeItem(idx);
                    }
                }
                break;
            }

            default:
                break;
        }
        combobox->blockSignals(false);
    }
    else if (InterfaceToolbarLineEdit *lineedit = qobject_cast<InterfaceToolbarLineEdit *>(widget))
    {
        // We don't block signals here because changes are applied with enter or apply button,
        // and we want InterfaceToolbarLineEdit to always syntax check the text.
        switch (command)
        {
            case commandControlSet:
                lineedit->setText(payload);
                lineedit->disableApplyButton();
                break;

            default:
                break;
        }
    }
    else if (QCheckBox *checkbox = qobject_cast<QCheckBox *>(widget))
    {
        checkbox->blockSignals(true);
        switch (command)
        {
            case commandControlSet:
            {
                Qt::CheckState state = Qt::Unchecked;
                if (payload.size() > 0 && payload.at(0) != 0)
                {
                    state = Qt::Checked;
                }
                checkbox->setCheckState(state);
                break;
            }

            default:
                break;
        }
        checkbox->blockSignals(false);
    }
    else if (QPushButton *button = qobject_cast<QPushButton *>(widget))
    {
        if ((command == commandControlSet) &&
            widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
        {
            button->setText(payload);
        }
    }
}

void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int num, int command, QByteArray payload)
{
    if (!widget) {
        return;
    }

    if (qobject_cast<QComboBox *>(widget))
    {
        switch (command)
        {
            case commandControlSet:
                if (interface_[ifname].value[num] != payload)
                {
                    interface_[ifname].value_changed[num] = false;
                }
                foreach (QByteArray entry, interface_[ifname].list[num])
                {
                    if (entry == payload || entry.startsWith(payload + '\0'))
                    {
                        interface_[ifname].value[num] = payload;
                    }
                }
                break;

            case commandControlAdd:
                interface_[ifname].list[num].append(payload);
                break;

            case commandControlRemove:
                if (payload.size() == 0)
                {
                    interface_[ifname].value[num].clear();
                    interface_[ifname].list[num].clear();
                }
                else
                {
                    foreach (QByteArray entry, interface_[ifname].list[num])
                    {
                        if (entry == payload || entry.startsWith(payload + '\0'))
                        {
                            interface_[ifname].list[num].removeAll(entry);
                        }
                    }
                }
                break;

            default:
                break;
        }
    }
    else if (qobject_cast<InterfaceToolbarLineEdit *>(widget))
    {
        switch (command)
        {
            case commandControlSet:
                if (interface_[ifname].value[num] != payload)
                {
                    interface_[ifname].value_changed[num] = false;
                }
                interface_[ifname].value[num] = payload;
                break;

            default:
                break;
        }
    }
    else if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
             (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_LOGGER))
    {
        if (command == commandControlSet)
        {
            if (interface_[ifname].log_dialog.contains(num))
            {
                interface_[ifname].log_dialog[num]->clearText();
            }
            interface_[ifname].log_text.clear();
        }
        if (command == commandControlSet || command == commandControlAdd)
        {
            if (interface_[ifname].log_dialog.contains(num))
            {
                interface_[ifname].log_dialog[num]->appendText(payload);
            }
            interface_[ifname].log_text[num].append(payload);
        }
    }
    else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
    {
        // QCheckBox or QPushButton
        switch (command)
        {
            case commandControlSet:
                if (interface_[ifname].value[num] != payload)
                {
                    interface_[ifname].value_changed[num] = false;
                }
                interface_[ifname].value[num] = payload;
                break;

            default:
                break;
        }
    }
}

void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QByteArray payload)
{
    switch (command)
    {
        case commandControlSet:
        case commandControlAdd:
        case commandControlRemove:
            if (control_widget_.contains(num))
            {
                QWidget *widget = control_widget_[num];
                setInterfaceValue(ifname, widget, num, command, payload);

                if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
                {
                    setWidgetValue(widget, command, payload);
                }
            }
            break;

        case commandControlEnable:
        case commandControlDisable:
            if (control_widget_.contains(num))
            {
                QWidget *widget = control_widget_[num];
                if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
                {
                    bool enable = (command == commandControlEnable ? true : false);
                    interface_[ifname].widget_disabled[num] = !enable;

                    if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
                    {
                        widget->setEnabled(enable);
                        if (label_widget_.contains(num))
                        {
                            label_widget_[num]->setEnabled(enable);
                        }
                    }
                }
            }
            break;

        case commandStatusMessage:
            mainApp->pushStatus(MainApplication::TemporaryStatus, payload);
            break;

        case commandInformationMessage:
            simple_dialog_async(ESD_TYPE_INFO, ESD_BTN_OK, "%s", payload.data());
            break;

        case commandWarningMessage:
            simple_dialog_async(ESD_TYPE_WARN, ESD_BTN_OK, "%s", payload.data());
            break;

        case commandErrorMessage:
            simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", payload.data());
            break;

        default:
            // Unknown commands are silently ignored
            break;
    }
}

void InterfaceToolbar::controlSend(QString ifname, int num, int command, const QByteArray &payload = QByteArray())
{
    if (payload.length() > 65535)
    {
        // Not supported
        return;
    }

    if (ifname.isEmpty() || interface_[ifname].out_fd == -1)
    {
        // Does not have a control out channel
        return;
    }

    ssize_t payload_length = payload.length() + 2;
    unsigned char high_nibble = (payload_length >> 16) & 0xFF;
    unsigned char mid_nibble = (payload_length >> 8) & 0xFF;
    unsigned char low_nibble = (payload_length >> 0) & 0xFF;

    QByteArray ba;

    ba.append(SP_TOOLBAR_CTRL);
    ba.append(high_nibble);
    ba.append(mid_nibble);
    ba.append(low_nibble);
    ba.append(num);
    ba.append(command);
    ba.append(payload);

    if (ws_write(interface_[ifname].out_fd, ba.data(), ba.length()) != ba.length())
    {
        simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK,
                            "Unable to send control message:\n%s.",
                            g_strerror(errno));
    }
}

void InterfaceToolbar::onControlButtonClicked()
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    QPushButton *button = static_cast<QPushButton *>(sender());
    int num = control_widget_.key(button);

    controlSend(ifname, num, commandControlSet);
}

void InterfaceToolbar::onCheckBoxChanged(int state)
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    QCheckBox *checkbox = static_cast<QCheckBox *>(sender());
    int num = control_widget_.key(checkbox);

    QByteArray payload(1, state == Qt::Unchecked ? 0 : 1);
    controlSend(ifname, num, commandControlSet, payload);
    interface_[ifname].value[num] = payload;
    interface_[ifname].value_changed[num] = true;
}

void InterfaceToolbar::onComboBoxChanged(int idx)
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    QComboBox *combobox = static_cast<QComboBox *>(sender());
    int num = control_widget_.key(combobox);
    QString value = combobox->itemData(idx).toString();

    QByteArray payload(value.toUtf8());
    controlSend(ifname, num, commandControlSet, payload);
    interface_[ifname].value[num] = payload;
    interface_[ifname].value_changed[num] = true;
}

void InterfaceToolbar::onLineEditChanged()
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    InterfaceToolbarLineEdit *lineedit = static_cast<InterfaceToolbarLineEdit *>(sender());
    int num = control_widget_.key(lineedit);

    QByteArray payload(lineedit->text().toUtf8());
    controlSend(ifname, num, commandControlSet, payload);
    interface_[ifname].value[num] = payload;
    interface_[ifname].value_changed[num] = true;
}

void InterfaceToolbar::onLogButtonClicked()
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    QPushButton *button = static_cast<QPushButton *>(sender());
    int num = control_widget_.key(button);

    if (!interface_[ifname].log_dialog.contains(num))
    {
        interface_[ifname].log_dialog[num] = new FunnelTextDialog(window(), ifname + " " + button->text());
        connect(interface_[ifname].log_dialog[num], SIGNAL(accepted()), this, SLOT(closeLog()));
        connect(interface_[ifname].log_dialog[num], SIGNAL(rejected()), this, SLOT(closeLog()));

        interface_[ifname].log_dialog[num]->setText(interface_[ifname].log_text[num]);
    }

    interface_[ifname].log_dialog[num]->show();
    interface_[ifname].log_dialog[num]->raise();
    interface_[ifname].log_dialog[num]->activateWindow();
}

void InterfaceToolbar::onHelpButtonClicked()
{
    QUrl help_url(help_link_);

    if (help_url.scheme().compare("file") != 0)
    {
        QDesktopServices::openUrl(help_url);
    }
}

void InterfaceToolbar::closeLog()
{
    FunnelTextDialog *log_dialog = static_cast<FunnelTextDialog *>(sender());

    foreach (QString ifname, interface_.keys())
    {
        foreach (int num, control_widget_.keys())
        {
            if (interface_[ifname].log_dialog.value(num) == log_dialog)
            {
                interface_[ifname].log_dialog.remove(num);
            }
        }
    }
}


void InterfaceToolbar::startReaderThread(QString ifname, void *control_in)
{
    QThread *thread = new QThread;
    InterfaceToolbarReader *reader = new InterfaceToolbarReader(ifname, control_in);
    reader->moveToThread(thread);

    connect(thread, SIGNAL(started()), reader, SLOT(loop()));
    connect(reader, SIGNAL(finished()), thread, SLOT(quit()));
    connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), reader, SLOT(deleteLater()));
    connect(reader, SIGNAL(received(QString, int, int, QByteArray)),
            this, SLOT(controlReceived(QString, int, int, QByteArray)));

    interface_[ifname].reader_thread = thread;

    thread->start();
}

void InterfaceToolbar::startCapture(GArray *ifaces)
{
    if (!ifaces || ifaces->len == 0)
        return;

    const QString &selected_ifname = ui->interfacesComboBox->currentText();
    QString first_capturing_ifname;
    bool selected_found = false;

    for (unsigned i = 0; i < ifaces->len; i++)
    {
        interface_options *interface_opts = &g_array_index(ifaces, interface_options, i);
        QString ifname(interface_opts->name);

        if (!interface_.contains(ifname))
            // This interface is not for us
            continue;

        if (first_capturing_ifname.isEmpty())
            first_capturing_ifname = ifname;

        if (ifname.compare(selected_ifname) == 0)
            selected_found = true;

        if (interface_[ifname].out_fd != -1)
            // Already have control channels for this interface
            continue;

        // Open control out channel
#ifdef _WIN32
        startReaderThread(ifname, interface_opts->extcap_control_in_h);
        // Duplicate control out handle and pass the duplicate handle to _open_osfhandle().
        // This allows the C run-time file descriptor (out_fd) and the extcap_control_out_h to be closed independently.
        // The duplicated handle will get closed at the same time the file descriptor is closed.
        // The control out pipe will close when both out_fd and extcap_control_out_h are closed.
        HANDLE duplicate_out_handle = INVALID_HANDLE_VALUE;
        if (!DuplicateHandle(GetCurrentProcess(), interface_opts->extcap_control_out_h,
                             GetCurrentProcess(), &duplicate_out_handle, 0, true, DUPLICATE_SAME_ACCESS))
        {
            simple_dialog_async(ESD_TYPE_ERROR, ESD_BTN_OK,
                                "Failed to duplicate extcap control out handle: %s\n.",
                                win32strerror(GetLastError()));
        }
        else
        {
            interface_[ifname].out_fd = _open_osfhandle((intptr_t)duplicate_out_handle, O_APPEND | O_BINARY);
        }
#else
        startReaderThread(ifname, interface_opts->extcap_control_in);
        interface_[ifname].out_fd = ws_open(interface_opts->extcap_control_out, O_WRONLY | O_BINARY, 0);
#endif
        sendChangedValues(ifname);
        controlSend(ifname, 0, commandControlInitialized);
    }

    if (!selected_found && !first_capturing_ifname.isEmpty())
    {
        ui->interfacesComboBox->setCurrentText(first_capturing_ifname);
    }
    else
    {
        updateWidgets();
    }
}

void InterfaceToolbar::stopCapture()
{
    foreach (QString ifname, interface_.keys())
    {
        if (interface_[ifname].reader_thread)
        {
            if (!interface_[ifname].reader_thread->isFinished())
            {
                interface_[ifname].reader_thread->requestInterruption();
            }
            interface_[ifname].reader_thread = NULL;
        }

        if (interface_[ifname].out_fd != -1)
        {
            ws_close_if_possible (interface_[ifname].out_fd);

            interface_[ifname].out_fd = -1;
        }

        foreach (int num, control_widget_.keys())
        {
            // Reset disabled property for all widgets
            interface_[ifname].widget_disabled[num] = false;

            QWidget *widget = control_widget_[num];
            if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
                (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
            {
                // Reset default value for control buttons
                interface_[ifname].value[num] = default_value_[num];

                if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
                {
                    setWidgetValue(widget, commandControlSet, default_value_[num]);
                }
            }
        }
    }

    updateWidgets();
}

void InterfaceToolbar::sendChangedValues(QString ifname)
{
    // Send all values which has changed
    foreach (int num, control_widget_.keys())
    {
        QWidget *widget = control_widget_[num];
        if ((interface_[ifname].value_changed[num]) &&
            (widget->property(interface_type_property).toInt() != INTERFACE_TYPE_BUTTON) &&
            (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
        {
            controlSend(ifname, num, commandControlSet, interface_[ifname].value[num]);
        }
    }
}

void InterfaceToolbar::onRestoreButtonClicked()
{
    const QString &ifname = ui->interfacesComboBox->currentText();

    // Set default values to all widgets and interfaces
    foreach (int num, control_widget_.keys())
    {
        QWidget *widget = control_widget_[num];
        if (default_list_[num].size() > 0)
        {
            // This is a QComboBox.  Clear list and add new entries.
            setWidgetValue(widget, commandControlRemove, QByteArray());
            interface_[ifname].list[num].clear();

            foreach (QByteArray value, default_list_[num])
            {
                setWidgetValue(widget, commandControlAdd, value);
                interface_[ifname].list[num].append(value);
            }
        }

        switch (widget->property(interface_role_property).toInt())
        {
            case INTERFACE_ROLE_CONTROL:
                setWidgetValue(widget, commandControlSet, default_value_[num]);
                interface_[ifname].value[num] = default_value_[num];
                interface_[ifname].value_changed[num] = false;
                break;

            case INTERFACE_ROLE_LOGGER:
                if (interface_[ifname].log_dialog.contains(num))
                {
                    interface_[ifname].log_dialog[num]->clearText();
                }
                interface_[ifname].log_text[num].clear();
                break;

            default:
                break;
        }
    }
}

bool InterfaceToolbar::hasInterface(QString ifname)
{
    return interface_.contains(ifname);
}

void InterfaceToolbar::updateWidgets()
{
    const QString &ifname = ui->interfacesComboBox->currentText();
    bool is_capturing = (interface_[ifname].out_fd == -1 ? false : true);

    foreach (int num, control_widget_.keys())
    {
        QWidget *widget = control_widget_[num];
        bool widget_enabled = true;

        if (ifname.isEmpty() &&
            (widget->property(interface_role_property).toInt() != INTERFACE_ROLE_HELP))
        {
            // No interface selected, disable all but Help button
            widget_enabled = false;
        }
        else if (!is_capturing &&
                 (widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
                 (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
        {
            widget_enabled = false;
        }
        else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
        {
            widget_enabled = !interface_[ifname].widget_disabled[num];
        }

        widget->setEnabled(widget_enabled);
        if (label_widget_.contains(num))
        {
            label_widget_[num]->setEnabled(widget_enabled);
        }
    }

    foreach (int num, control_widget_.keys())
    {
        QWidget *widget = control_widget_[num];
        if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
            (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESTORE))
        {
            widget->setEnabled(!is_capturing);
        }
    }
}

void InterfaceToolbar::interfaceListChanged()
{
#ifdef HAVE_LIBPCAP
    const QString &selected_ifname = ui->interfacesComboBox->currentText();
    bool keep_selected = false;

    ui->interfacesComboBox->blockSignals(true);
    ui->interfacesComboBox->clear();

    for (unsigned i = 0; i < global_capture_opts.all_ifaces->len; i++)
    {
        interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
        if (device->hidden)
            continue;

        if (interface_.keys().contains(device->name))
        {
            ui->interfacesComboBox->addItem(device->name);
            if (selected_ifname.compare(device->name) == 0)
            {
                // Keep selected interface
                ui->interfacesComboBox->setCurrentText(device->name);
                keep_selected = true;
            }
        }
    }

    ui->interfacesComboBox->blockSignals(false);

    if (!keep_selected)
    {
        // Select the first interface
        on_interfacesComboBox_currentTextChanged(ui->interfacesComboBox->currentText());
    }

    updateWidgets();
#endif
}

void InterfaceToolbar::on_interfacesComboBox_currentTextChanged(const QString &ifname)
{
    foreach (int num, control_widget_.keys())
    {
        QWidget *widget = control_widget_[num];
        if (interface_[ifname].list[num].size() > 0)
        {
            // This is a QComboBox.  Clear list and add new entries.
            setWidgetValue(widget, commandControlRemove, QByteArray());

            foreach (QByteArray value, interface_[ifname].list[num])
            {
                setWidgetValue(widget, commandControlAdd, value);
            }
        }

        if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
        {
            setWidgetValue(widget, commandControlSet, interface_[ifname].value[num]);
        }
    }

    updateWidgets();
}
