Qt: Interface Toolbar improvements

- Select one of the capturing interfaces when start capture
- Only send user changed control values when start capture
- Don't show hidden interfaces
- Allow a toolbar with no interfaces
- Renamed button role "reset" to "restore"
- Improved control number validation
- Updated documentation

Change-Id: Icc8d04043c95c1f3ef8d7cdc3b251be4471cba0a
Reviewed-on: https://code.wireshark.org/review/21445
Petri-Dish: Stig Bjørlykke <stig@bjorlykke.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Stig Bjørlykke <stig@bjorlykke.org>
This commit is contained in:
Stig Bjørlykke 2017-04-28 21:26:53 +02:00
parent afb4de370a
commit 741d4f5898
8 changed files with 167 additions and 66 deletions

View File

@ -238,7 +238,7 @@ TYPE provides the type of control to add to the toolbar and DISPLAY the name in
Additionally TOOLTIP and PLACEHOLDER may be provided, which will give the user an
explanation within the GUI.
All controls, except from the logger, help and reset buttons, may be disabled
All controls, except from the logger, help and restore buttons, may be disabled
(and enabled) in GUI by the extcap during capture. This can be because of set-once
operations, or operations which takes some time to complete.
@ -260,8 +260,9 @@ This TYPEs are defined as controls:
* BUTTON - This provides a button with different ROLEs:
** CONTROL - This button will send a signal.
This is the default role if nothing is configured.
** CONTROL - This button will send a signal when pressed.
This is the default if no role is configured.
The button is only enabled when capturing.
The extcap utility can set the button text at startup, and can change (set) the
button text and receive button press signals while capturing. The button is
@ -282,12 +283,13 @@ This TYPEs are defined as controls:
The Set command will clear the log before adding the entry.
** HELP - This button opens the help page, if configured.
This type has no controls and will not be used in communication.
This role has no controls and will not be used in communication.
Valid Commands: NONE.
** RESET - This button will restore all control values to default.
This type has no controls and will not be used in communication.
** RESTORE - This button will restore all control values to default.
This role has no controls and will not be used in communication.
The button is only enabled when not capturing.
Valid Commands: NONE.
@ -376,7 +378,7 @@ Control packet:
The length of the following payload. Maximum length is 65535 bytes.
The Initialized command will be sent from the GUI to the extcap utility when all
initial control values are sent after starting a capture. This is an indication
user changed control values are sent after starting a capture. This is an indication
that the GUI is ready to receive control values.
The GUI will only send Initialized and Set commands. The extcap utility shall not

View File

@ -73,7 +73,7 @@ CTRL_ARG_DELAY = 1
CTRL_ARG_VERIFY = 2
CTRL_ARG_BUTTON = 3
CTRL_ARG_HELP = 4
CTRL_ARG_RESET = 5
CTRL_ARG_RESTORE = 5
CTRL_ARG_LOGGER = 6
CTRL_ARG_NONE = 255
@ -161,7 +161,7 @@ def extcap_interfaces():
print ("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
print ("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
print ("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
print ("control {number=%d}{type=button}{role=reset}{display=Reset}{tooltip=Restore default values}" % CTRL_ARG_RESET)
print ("control {number=%d}{type=button}{role=restore}{display=Restore}{tooltip=Restore default values}" % CTRL_ARG_RESTORE)
print ("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
print ("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
print ("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
@ -320,7 +320,7 @@ def control_write_defaults(fn_out):
# Write startup configuration to Toolbar controls
control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(delay))
control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(int(delay)))
control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
for i in range(1,16):

View File

@ -730,8 +730,8 @@ static iface_toolbar_control *extcap_parse_control_sentence(GList *control_items
control->ctrl_role = INTERFACE_ROLE_HELP;
} else if (g_ascii_strcasecmp(param_value, "logger") == 0) {
control->ctrl_role = INTERFACE_ROLE_LOGGER;
} else if (g_ascii_strcasecmp(param_value, "reset") == 0) {
control->ctrl_role = INTERFACE_ROLE_RESET;
} else if (g_ascii_strcasecmp(param_value, "restore") == 0) {
control->ctrl_role = INTERFACE_ROLE_RESTORE;
} else {
printf("invalid role %s in CONTROL sentence\n", param_value);
control->ctrl_role = INTERFACE_ROLE_UNKNOWN;

View File

@ -41,7 +41,7 @@ typedef enum {
INTERFACE_ROLE_CONTROL,
INTERFACE_ROLE_HELP,
INTERFACE_ROLE_LOGGER,
INTERFACE_ROLE_RESET
INTERFACE_ROLE_RESTORE
} iface_toolbar_ctrl_role;
typedef struct _iface_toolbar_value {

View File

@ -29,6 +29,8 @@
#include "ui/main_statusbar.h"
#include <ui_interface_toolbar.h>
#include "capture_opts.h"
#include "ui/capture_globals.h"
#include "sync_pipe.h"
#include "wsutil/file_util.h"
@ -69,16 +71,13 @@ InterfaceToolbar::InterfaceToolbar(QWidget *parent, const iface_toolbar *toolbar
ui->setupUi(this);
// Fill inn interfaces list and initialize default interface values
ui->interfacesComboBox->blockSignals(true);
for (GList *walker = toolbar->ifnames; walker; walker = walker->next)
{
QString ifname((gchar *)walker->data);
ui->interfacesComboBox->addItem(ifname);
interface_[ifname].reader_thread = NULL;
interface_[ifname].out_fd = -1;
interface_[ifname].log_dialog = NULL;
}
ui->interfacesComboBox->blockSignals(false);
initializeControls(toolbar);
@ -194,7 +193,7 @@ QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control)
{
case INTERFACE_ROLE_CONTROL:
setDefaultValue(control->num, (gchar *)control->display);
connect(button, SIGNAL(pressed()), this, SLOT(onButtonPressed()));
connect(button, SIGNAL(pressed()), this, SLOT(onControlButtonPressed()));
break;
case INTERFACE_ROLE_HELP:
@ -210,13 +209,12 @@ QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control)
connect(button, SIGNAL(pressed()), this, SLOT(onLogButtonPressed()));
break;
case INTERFACE_ROLE_RESET:
button->setText("Reset");
button->setToolTip("Restore default values");
connect(button, SIGNAL(pressed()), this, SLOT(onResetButtonPressed()));
case INTERFACE_ROLE_RESTORE:
connect(button, SIGNAL(pressed()), this, SLOT(onRestoreButtonPressed()));
break;
default:
// Not supported
break;
}
@ -237,7 +235,7 @@ QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control)
{
iface_toolbar_value *val = (iface_toolbar_value *)walker->data;
QString value = QString().fromUtf8((gchar *)val->value);
if (value.length() == 0)
if (value.isEmpty())
{
// Invalid value
continue;
@ -246,7 +244,7 @@ QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control)
QByteArray interface_value;
interface_value.append(value);
if (display.length() == 0)
if (display.isEmpty())
{
display = value;
}
@ -422,6 +420,10 @@ void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int nu
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'))
@ -462,6 +464,10 @@ void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int nu
switch (command)
{
case commandControlSet:
if (interface_[ifname].value[num] != payload)
{
interface_[ifname].value_changed[num] = false;
}
interface_[ifname].value[num] = payload;
break;
@ -492,11 +498,22 @@ void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int nu
else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
{
// QCheckBox or QPushButton
interface_[ifname].value[num] = payload;
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)
@ -504,8 +521,9 @@ void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QBy
case commandControlSet:
case commandControlAdd:
case commandControlRemove:
if (QWidget *widget = control_widget_[num])
if (control_widget_.contains(num))
{
QWidget *widget = control_widget_[num];
setInterfaceValue(ifname, widget, num, command, payload);
if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
@ -517,8 +535,9 @@ void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QBy
case commandControlEnable:
case commandControlDisable:
if (QWidget *widget = control_widget_[num])
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);
@ -566,7 +585,7 @@ void InterfaceToolbar::controlSend(QString ifname, int num, int command, const Q
return;
}
if (interface_[ifname].out_fd == -1)
if (ifname.isEmpty() || interface_[ifname].out_fd == -1)
{
// Does not have a control out channel
return;
@ -595,7 +614,7 @@ void InterfaceToolbar::controlSend(QString ifname, int num, int command, const Q
}
}
void InterfaceToolbar::onButtonPressed()
void InterfaceToolbar::onControlButtonPressed()
{
const QString &ifname = ui->interfacesComboBox->currentText();
QPushButton *button = static_cast<QPushButton *>(sender());
@ -613,6 +632,7 @@ void InterfaceToolbar::onCheckBoxChanged(int state)
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)
@ -625,6 +645,7 @@ void InterfaceToolbar::onComboBoxChanged(int idx)
QByteArray payload(value.toUtf8());
controlSend(ifname, num, commandControlSet, payload);
interface_[ifname].value[num] = payload;
interface_[ifname].value_changed[num] = true;
}
void InterfaceToolbar::onLineEditChanged()
@ -636,6 +657,7 @@ void InterfaceToolbar::onLineEditChanged()
QByteArray payload(lineedit->text().toUtf8());
controlSend(ifname, num, commandControlSet, payload);
interface_[ifname].value[num] = payload;
interface_[ifname].value_changed[num] = true;
}
void InterfaceToolbar::onLogButtonPressed()
@ -661,7 +683,8 @@ void InterfaceToolbar::onHelpButtonPressed()
{
QUrl help_url(help_link_);
if (help_url.scheme().compare("file") != 0) {
if (help_url.scheme().compare("file") != 0)
{
QDesktopServices::openUrl(help_url);
}
}
@ -697,24 +720,48 @@ void InterfaceToolbar::startReaderThread(QString ifname, QString control_in)
thread->start();
}
void InterfaceToolbar::startCapture(QString ifname, QString control_in, QString control_out)
void InterfaceToolbar::startCapture(GArray *ifaces)
{
if (!interface_.contains(ifname) || // This interface is not for us
interface_[ifname].out_fd != -1) // Already have control channels for this interface
{
if (!ifaces || ifaces->len == 0)
return;
const QString &selected_ifname = ui->interfacesComboBox->currentText();
QString first_capturing_ifname;
bool selected_found = false;
for (guint 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;
// The reader thread will open control in channel
startReaderThread(ifname, interface_opts.extcap_control_in);
// Open control out channel
interface_[ifname].out_fd = ws_open(interface_opts.extcap_control_out, O_WRONLY | O_BINARY, 0);
sendChangedValues(ifname);
controlSend(ifname, 0, commandControlInitialized);
}
// The reader thread will open control in channel
startReaderThread(ifname, control_in);
// Open control out channel
interface_[ifname].out_fd = ws_open(control_out.toUtf8(), O_WRONLY | O_BINARY, 0);
sendChangedValues(ifname);
controlSend(ifname, 0, commandControlInitialized);
updateWidgets();
if (!selected_found && !first_capturing_ifname.isEmpty())
ui->interfacesComboBox->setCurrentText(first_capturing_ifname);
else
updateWidgets();
}
void InterfaceToolbar::stopCapture()
@ -764,7 +811,7 @@ void InterfaceToolbar::sendChangedValues(QString ifname)
foreach (int num, control_widget_.keys())
{
QWidget *widget = control_widget_[num];
if ((interface_[ifname].value[num] != default_value_[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))
{
@ -773,7 +820,7 @@ void InterfaceToolbar::sendChangedValues(QString ifname)
}
}
void InterfaceToolbar::onResetButtonPressed()
void InterfaceToolbar::onRestoreButtonPressed()
{
const QString &ifname = ui->interfacesComboBox->currentText();
@ -799,6 +846,7 @@ void InterfaceToolbar::onResetButtonPressed()
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:
@ -828,20 +876,29 @@ void InterfaceToolbar::updateWidgets()
foreach (int num, control_widget_.keys())
{
QWidget *widget = control_widget_[num];
if (!is_capturing &&
(widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
(widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
bool widget_enabled = true;
if (ifname.isEmpty() &&
(widget->property(interface_role_property).toInt() != INTERFACE_ROLE_HELP))
{
widget->setEnabled(false);
// 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)
{
bool widget_enabled = !interface_[ifname].widget_disabled[num];
widget->setEnabled(widget_enabled);
if (label_widget_.contains(num))
{
label_widget_[num]->setEnabled(widget_enabled);
}
widget_enabled = !interface_[ifname].widget_disabled[num];
}
widget->setEnabled(widget_enabled);
if (label_widget_.contains(num))
{
label_widget_[num]->setEnabled(widget_enabled);
}
}
@ -849,13 +906,50 @@ void InterfaceToolbar::updateWidgets()
{
QWidget *widget = control_widget_[num];
if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
(widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESET))
(widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESTORE))
{
widget->setEnabled(!is_capturing);
}
}
}
void InterfaceToolbar::interfaceListChanged()
{
const QString &selected_ifname = ui->interfacesComboBox->currentText();
bool keep_selected = false;
ui->interfacesComboBox->blockSignals(true);
ui->interfacesComboBox->clear();
for (guint 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_currentIndexChanged(ui->interfacesComboBox->currentText());
}
updateWidgets();
}
void InterfaceToolbar::on_interfacesComboBox_currentIndexChanged(const QString &ifname)
{
foreach (int num, control_widget_.keys())

View File

@ -29,6 +29,7 @@
#include "interface_toolbar_reader.h"
#include <QFrame>
#include <QList>
#include <QMap>
#include <QString>
@ -42,6 +43,7 @@ struct interface_values
QThread *reader_thread;
int out_fd;
QMap<int, QByteArray> value;
QMap<int, bool> value_changed;
QMap<int, QList<QByteArray> > list;
FunnelTextDialog *log_dialog;
QString log_text;
@ -56,11 +58,12 @@ public:
explicit InterfaceToolbar(QWidget *parent = 0, const iface_toolbar *toolbar = NULL);
~InterfaceToolbar();
void startCapture(QString ifname, QString control_in, QString control_out);
void startCapture(GArray *ifaces);
void stopCapture();
bool hasInterface(QString ifname);
public slots:
void interfaceListChanged();
void controlReceived(QString ifname, int num, int command, QByteArray message);
signals:
@ -70,13 +73,13 @@ private slots:
void startReaderThread(QString ifname, QString control_in);
void updateWidgets();
void onButtonPressed();
void onControlButtonPressed();
void onLogButtonPressed();
void onHelpButtonPressed();
void onRestoreButtonPressed();
void onCheckBoxChanged(int state);
void onComboBoxChanged(int idx);
void onLineEditChanged();
void onLogButtonPressed();
void onHelpButtonPressed();
void onResetButtonPressed();
void closeLog();

View File

@ -38,6 +38,9 @@
</item>
<item>
<widget class="QComboBox" name="interfacesComboBox">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="toolTip">
<string>Select interface</string>
</property>

View File

@ -855,6 +855,8 @@ void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
menu->insertAction(before, action);
InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
connect(wsApp, SIGNAL(appInitialized()), interface_toolbar, SLOT(interfaceListChanged()));
connect(wsApp, SIGNAL(localInterfaceListChanged()), interface_toolbar, SLOT(interfaceListChanged()));
QToolBar *toolbar = new QToolBar(this);
toolbar->addWidget(interface_toolbar);
@ -2483,10 +2485,7 @@ void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *iface
QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
foreach (InterfaceToolbar *toolbar, toolbars) {
if (capture_in_progress && ifaces) {
for (guint i = 0; i < ifaces->len; i++) {
interface_options interface_opts = g_array_index(ifaces, interface_options, i);
toolbar->startCapture(interface_opts.name, interface_opts.extcap_control_in, interface_opts.extcap_control_out);
}
toolbar->startCapture(ifaces);
} else {
toolbar->stopCapture();
}