summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStig Bjørlykke <stig@bjorlykke.org>2017-04-15 23:30:30 +0200
committerStig Bjørlykke <stig@bjorlykke.org>2017-04-25 06:19:39 +0000
commit77751c94f17e2c110ae9e88b1780e279d610b96b (patch)
treecd3a06ccf7944b3e131234ac86ae7b26ced8ea6b
parentcd55bd29258b8e0ffae9ea9471059b457ebb59ae (diff)
downloadwireshark-77751c94f17e2c110ae9e88b1780e279d610b96b.tar.gz
Qt: Add interface toolbar support
An extcap utility can provide configuration for controls to use in a GUI interface toolbar. This controls are bidirectional and can be used to control the extcap utility while capturing. This is useful in scenarios where configuration can be done based on findings in the capture process, setting temporary values or give other inputs without restarting current capture. Todo: - Add support for Windows Change-Id: Ie15fa67f92eb27d8b73df6bb36f66b9a7d81932d Reviewed-on: https://code.wireshark.org/review/19982 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>
-rw-r--r--capture_opts.c8
-rw-r--r--capture_opts.h2
-rw-r--r--doc/README.extcap189
-rwxr-xr-xdoc/extcap_example.py175
-rw-r--r--docbook/release-notes.asciidoc2
-rw-r--r--extcap.c171
-rw-r--r--extcap.h7
-rw-r--r--extcap_parser.c215
-rw-r--r--extcap_parser.h9
-rw-r--r--sync_pipe.h1
-rw-r--r--ui/CMakeLists.txt1
-rw-r--r--ui/Makefile.am2
-rw-r--r--ui/iface_toolbar.c68
-rw-r--r--ui/iface_toolbar.h105
-rw-r--r--ui/qt/CMakeLists.txt7
-rw-r--r--ui/qt/Makefile.am10
-rw-r--r--ui/qt/funnel_text_dialog.cpp2
-rw-r--r--ui/qt/interface_toolbar.cpp895
-rw-r--r--ui/qt/interface_toolbar.h120
-rw-r--r--ui/qt/interface_toolbar.ui69
-rw-r--r--ui/qt/interface_toolbar_lineedit.cpp149
-rw-r--r--ui/qt/interface_toolbar_lineedit.h71
-rw-r--r--ui/qt/interface_toolbar_reader.cpp140
-rw-r--r--ui/qt/interface_toolbar_reader.h65
-rw-r--r--ui/qt/main_window.cpp126
-rw-r--r--ui/qt/main_window.h6
-rw-r--r--ui/qt/main_window.ui6
-rw-r--r--ui/qt/main_window_slots.cpp40
-rw-r--r--ui/recent.c14
-rw-r--r--ui/recent.h1
30 files changed, 2648 insertions, 28 deletions
diff --git a/capture_opts.c b/capture_opts.c
index 69a24579c2..4a7c8f2ac2 100644
--- a/capture_opts.c
+++ b/capture_opts.c
@@ -68,6 +68,8 @@ capture_opts_init(capture_options *capture_opts)
capture_opts->default_options.extcap_args = NULL;
capture_opts->default_options.extcap_userdata = NULL;
capture_opts->default_options.extcap_pid = INVALID_EXTCAP_PID;
+ capture_opts->default_options.extcap_control_in = NULL;
+ capture_opts->default_options.extcap_control_out = NULL;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
capture_opts->default_options.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE;
@@ -702,6 +704,8 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str
interface_opts.extcap_args = NULL;
interface_opts.extcap_pid = INVALID_EXTCAP_PID;
interface_opts.extcap_userdata = NULL;
+ interface_opts.extcap_control_in = g_strdup(capture_opts->default_options.extcap_control_in);
+ interface_opts.extcap_control_out = g_strdup(capture_opts->default_options.extcap_control_out);
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
interface_opts.buffer_size = capture_opts->default_options.buffer_size;
@@ -1129,6 +1133,8 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index)
if (interface_opts.extcap_pid != INVALID_EXTCAP_PID)
g_spawn_close_pid(interface_opts.extcap_pid);
g_free(interface_opts.extcap_userdata);
+ g_free(interface_opts.extcap_control_in);
+ g_free(interface_opts.extcap_control_out);
#endif
#ifdef HAVE_PCAP_REMOTE
if (interface_opts.src_type == CAPTURE_IFREMOTE) {
@@ -1180,6 +1186,8 @@ collect_ifaces(capture_options *capture_opts)
if (interface_opts.extcap_args)
g_hash_table_ref(interface_opts.extcap_args);
interface_opts.extcap_userdata = NULL;
+ interface_opts.extcap_control_in = NULL;
+ interface_opts.extcap_control_out = NULL;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
interface_opts.buffer_size = device.buffer;
diff --git a/capture_opts.h b/capture_opts.h
index 780bbca526..d9ab3d86ab 100644
--- a/capture_opts.h
+++ b/capture_opts.h
@@ -226,6 +226,8 @@ typedef struct interface_options_tag {
GPid extcap_pid; /* pid of running process or INVALID_EXTCAP_PID */
gpointer extcap_userdata;
guint extcap_child_watch;
+ gchar *extcap_control_in;
+ gchar *extcap_control_out;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
int buffer_size;
diff --git a/doc/README.extcap b/doc/README.extcap
index 94ca73584b..50db8f67a3 100644
--- a/doc/README.extcap
+++ b/doc/README.extcap
@@ -196,6 +196,195 @@ such a check is the same as for Qt RegExp classes. This feature is only active i
Qt version of Wireshark.
+TOOLBAR CONTROLS
+================
+An extcap utility can provide configuration for controls to use in an interface toolbar.
+This controls are bidirectional and can be used to control the extcap utility while
+capturing.
+
+This is useful in scenarios where configuration can be done based on findings in the
+capture process, setting temporary values or give other inputs without restarting the
+current capture.
+
+Example:
+
+$ extcapbin --extcap-interfaces
+extcap {version=1.0}{display=Example extcap interface}
+interface {value=example1}{display=Example interface 1 for extcap}
+interface {value=example2}{display=Example interface 2 for extcap}
+control {number=0}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{validation=[A-Z]+}{required=true}
+control {number=1}{type=selector}{display=Time delay}{tooltip=Time delay between packages}
+control {number=2}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}
+control {number=3}{type=button}{display=Turn on}{tooltip=Turn on or off}
+control {number=4}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}
+value {control=1}{value=1}{display=1 sec}
+value {control=1}{value=2}{display=2 sec}{default=true}
+
+All controls will be presented as GUI elements in a toolbar specific to the extcap
+utility. The extcap must not rely on using those controls (they are optional) because
+of other capturing tools not using GUI (e.g. tshark, tfshark).
+
+
+CONTROLS
+========
+The controls are similar to the ARGUMENTS, but without the CALL element. All controls
+may be given a default value at startup and most can be changed during capture, both
+by the extcap and the user (depending on the type of control).
+
+All controls must provide a NUMBER, by which they are identified. No NUMBER may be
+provided twice. Also all options must present the elements TYPE and DISPLAY, where
+TYPE provides the type of control to add to the toolbar and DISPLAY the name in the GUI.
+
+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
+(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.
+
+All control values which are changed by the user (not equal to the default value) will
+be sent to the extcap utility when starting a capture. The extcap utility may choose
+to discard initial values and set new values, depending on implementation.
+
+This TYPEs are defined as controls:
+
+ * BOOLEAN - This provides a checkbox with the possibility to set a true/false value.
+
+ The extcap utility can set a default value at startup, and can change (set) and receive
+ value changes while capturing. When starting a capture the GUI will send the value if
+ different from the default value.
+
+ The payload is one byte with binary value 0 or 1.
+
+ Valid Commands: Set value, Enable, Disable.
+
+ * BUTTON - This provides a button with different ROLEs:
+
+ ** CONTROL - This button will send a signal.
+ This is the default role if nothing is configured.
+
+ 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
+ disabled and the button text is restored to the default text when not capturing.
+
+ The payload is either the button text or empty (signal).
+
+ Valid Commands: Set value, Enable, Disable.
+
+ ** LOGGER - This provides a logger mechanism where the extcap utility can send log
+ entries to be presented in a log window. This communication is unidirectional.
+
+ The payload is the log entry, and should be ended with a newline.
+ Maximum length is 65535 bytes.
+
+ Valid Commands: Set log entry, Add log entry.
+
+ 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.
+
+ 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.
+
+ Valid Commands: NONE.
+
+ * SELECTOR - This provides a combo box with fixed values which can be selected.
+
+ The extcap utility can set default values at startup, and add and remove values and
+ receive change in value selection while capturing. When starting a capture the GUI
+ will send the value if different from the default value.
+
+ The payload is a string with the value, and optionally a string with a display
+ value if this is different from the value. This two string values are separated
+ by a null character.
+
+ Valid Commands: Set selected value, Add value, Remove value, Enable, Disable.
+
+ If value is empty the Remove command will remove all entries.
+
+ * STRING - This provides a text edit line with the possibility to set a string or any
+ value which can be represented in a string (integer, float, date, etc.).
+
+ The extcap utility can set a default string value at startup, and can change (set) and
+ receive value changes while capturing. When starting a capture the GUI will send the
+ value if different from the default value.
+
+ The payload is a string with the value. Maximum length is 32767 bytes.
+
+ Valid Commands: Set value, Enable, Disable.
+
+ The element VALIDATION allows to provide a regular expression string, which is used
+ to check the user input for validity beyond normal data type or range checks.
+ Back-slashes must be escaped (as in \\b for \b).
+
+
+MESSAGES
+========
+In addition to the controls it's possible to send a single message from the extcap
+utility to the user. This message can be put in the status bar or displayed in a
+information, warning or error dialog which must be accepted by the user. This message
+does not use the NUMBER argument so this can have any value.
+
+
+CONTROL PROTOCOL
+================
+The protocol used to communicate over the control pipes has a fixed size header of
+6 bytes and a payload with 0 - 65535 bytes.
+
+Control packet:
+
+ +----+----+----+----+----+----+----+----+
+ | Sync Pipe Indication (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Message Length |
+ | (3 bytes network order) |
+ +----+----+----+----+----+----+----+----+
+ | Control Number (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Command (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Payload |
+ | (0 - 65535 bytes) |
+ +----+----+----+----+----+----+----+----+
+
+ Sync Pipe Indication:
+ The common sync pipe indication. This protocol uses the value 'T'.
+
+ Message Length:
+ Payload length + 2 bytes for argument number and command.
+
+ Control Number:
+ Unique number to identify the control. This number also gives the order of
+ the controls in the interface toolbar.
+
+ Command: Control type:
+ 0 = Initialized none
+ 1 = Set boolean / button / logger / selector / string
+ 2 = Add logger / selector
+ 3 = Remove selector
+ 4 = Enable boolean / button / selector / string
+ 5 = Disable boolean / button / selector / string
+ 6 = Statusbar message none
+ 7 = Information message none
+ 8 = Warning message none
+ 9 = Error message none
+
+ Payload Length:
+ 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
+that the GUI is ready to receive control values.
+
+The GUI will only send Initialized and Set commands. The extcap utility shall not
+send the Initialized command.
+
+Messages with unknown control number or command will be silently ignored.
+
+
DEVELOPMENT
===========
To have extcap support, extcap must be enabled. Moreover the specific extcap must
diff --git a/doc/extcap_example.py b/doc/extcap_example.py
index 6ab5699afb..e7ea7b2d6b 100755
--- a/doc/extcap_example.py
+++ b/doc/extcap_example.py
@@ -51,13 +51,38 @@ import struct
import binascii
from threading import Thread
-ERROR_USAGE = 0
-ERROR_ARG = 1
-ERROR_INTERFACE = 2
-ERROR_FIFO = 3
-ERROR_DELAY = 4
-
-globalinterface = 0
+ERROR_USAGE = 0
+ERROR_ARG = 1
+ERROR_INTERFACE = 2
+ERROR_FIFO = 3
+ERROR_DELAY = 4
+
+CTRL_CMD_INITIALIZED = 0
+CTRL_CMD_SET = 1
+CTRL_CMD_ADD = 2
+CTRL_CMD_REMOVE = 3
+CTRL_CMD_ENABLE = 4
+CTRL_CMD_DISABLE = 5
+CTRL_CMD_STATUSBAR = 6
+CTRL_CMD_INFORMATION = 7
+CTRL_CMD_WARNING = 8
+CTRL_CMD_ERROR = 9
+
+CTRL_ARG_MESSAGE = 0
+CTRL_ARG_DELAY = 1
+CTRL_ARG_VERIFY = 2
+CTRL_ARG_BUTTON = 3
+CTRL_ARG_HELP = 4
+CTRL_ARG_RESET = 5
+CTRL_ARG_LOGGER = 6
+CTRL_ARG_NONE = 255
+
+initialized = False
+message = ''
+delay = 0.0
+verify = False
+button = False
+button_disabled = False
"""
This code has been taken from http://stackoverflow.com/questions/5943249/python-argparse-and-controlling-overriding-the-exit-status-code - originally developed by Rob Cowie http://stackoverflow.com/users/46690/rob-cowie
@@ -128,12 +153,29 @@ def extcap_config(interface):
def extcap_interfaces():
- print ("extcap {version=1.0}{help=http://www.wireshark.org}")
- print ("interface {value=example1}{display=Example interface usage for extcap}")
+ print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
+ print ("interface {value=example1}{display=Example interface 1 for extcap}")
+ print ("interface {value=example2}{display=Example interface 2 for extcap}")
+ print ("control {number=%d}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{placeholder=Enter package message content here ...}{validation=^[A-Z]+}" % CTRL_ARG_MESSAGE)
+ print ("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
+ 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=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)
+ print ("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
+
def extcap_dlts(interface):
if ( interface == '1' ):
print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
+ elif ( interface == '2' ):
+ print ("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
"""
@@ -216,20 +258,125 @@ def pcap_fake_package ( message, fake_ip ):
pcap += message
return pcap
-def extcap_capture(interface, fifo, delay, verify, message, remote, fake_ip):
- tdelay = delay if delay != 0 else 5
+def control_read(fn):
+ try:
+ header = fn.read(6)
+ sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
+ if length > 2:
+ payload = fn.read(length - 2)
+ else:
+ payload = ''
+ return arg, typ, payload
+ except:
+ return None, None, None
+
+def control_read_thread(control_in, fn_out):
+ global initialized, message, delay, verify, button, button_disabled
+ with open(control_in, 'rb', 0 ) as fn:
+ arg = 0
+ while arg != None:
+ arg, typ, payload = control_read(fn)
+ log = ''
+ if typ == CTRL_CMD_INITIALIZED:
+ initialized = True
+ elif arg == CTRL_ARG_MESSAGE:
+ message = payload
+ log = "Message = " + payload
+ elif arg == CTRL_ARG_DELAY:
+ delay = float(payload)
+ log = "Time delay = " + payload
+ elif arg == CTRL_ARG_VERIFY:
+ # Only read this after initialized
+ if initialized:
+ verify = (payload[0] != '\0')
+ log = "Verify = " + str(verify)
+ control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
+ elif arg == CTRL_ARG_BUTTON:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
+ button_disabled = True
+ if button == True:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
+ button = False
+ log = "Button turned off"
+ else:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
+ button = True
+ log = "Button turned on"
+
+ if len(log) > 0:
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
+
+def control_write(fn, arg, typ, payload):
+ packet = bytearray()
+ packet += struct.pack('>sBHBB', 'T', 0, len(payload) + 2, arg, typ)
+ packet += payload
+ fn.write(packet)
+
+def control_write_defaults(fn_out):
+ global initialized, message, delay, verify
+
+ while not initialized:
+ time.sleep(.1) # Wait for initial control values
+
+ # 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_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
+
+ for i in range(1,16):
+ item = bytearray()
+ item += str(i) + struct.pack('B', 0) + str(i) + " sec"
+ control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
+
+ control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
+
+def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
+ global message, delay, verify, button_disabled
+ delay = in_delay if in_delay != 0 else 5
+ message = in_message
+ verify = in_verify
+ counter = 1
if not os.path.exists(fifo):
print ( "Fifo does not exist, exiting!", file=sys.stderr )
sys.exit(1)
+ fn_out = None
+ if control_out != None:
+ fn_out = open(control_out, 'wb', 0)
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
+
+
+ if control_in != None:
+ # Start reading thread
+ thread = Thread(target = control_read_thread, args = (control_in, fn_out))
+ thread.start()
+
+
+ if fn_out != None:
+ control_write_defaults(fn_out)
+
with open(fifo, 'wb', 0 ) as fh:
fh.write (pcap_fake_header())
while True:
+ if fn_out != None:
+ log = "Received packet #" + str(counter) + "\n"
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
+ counter = counter + 1
+
+ if button_disabled == True:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
+ control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
+ button_disabled = False
+
out = ("%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify )).encode("utf8")
fh.write (pcap_fake_package(out, fake_ip))
- time.sleep(tdelay)
+ time.sleep(delay)
+
+ thread.join()
+ if fn_out != None:
+ fn_out.close()
def extcap_close_fifo(fifo):
if not os.path.exists(fifo):
@@ -268,6 +415,8 @@ if __name__ == '__main__':
parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
+ parser.add_argument("--extcap-control-in", help="Use together with capture to provide the fifo to dump data to")
+ parser.add_argument("--extcap-control-out", help="Use together with capture to provide the fifo to dump data to")
# Interface Arguments
parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
@@ -334,7 +483,7 @@ if __name__ == '__main__':
sys.exit(ERROR_DELAY)
try:
- extcap_capture(interface, args.fifo, args.delay, args.verify, message, args.remote, fake_ip)
+ extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
except KeyboardInterrupt:
pass
else:
diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc
index a76cb578d3..d95bc6c41b 100644
--- a/docbook/release-notes.asciidoc
+++ b/docbook/release-notes.asciidoc
@@ -48,6 +48,8 @@ since version 2.2.0:
* You can move back and forth in the selection history in the Qt UI.
* IEEE 802.15.4 dissector now uses an UAT for decryption keys. The original
decryption key preference has been obsoleted.
+* Extcap utilities can now provide configuration for a GUI interface toolbar to
+ control the extcap utility while capturing.
//=== Removed Dissectors
diff --git a/extcap.c b/extcap.c
index 87ec4f5d00..c9e357bf19 100644
--- a/extcap.c
+++ b/extcap.c
@@ -49,6 +49,8 @@
#include <epan/prefs.h>
+#include "ui/iface_toolbar.h"
+
#include <wsutil/file_util.h>
#include <wsutil/filesystem.h>
#include <wsutil/tempfile.h>
@@ -77,6 +79,11 @@ static GHashTable * _loaded_interfaces = NULL;
*/
static GHashTable * _tool_for_ifname = NULL;
+/* internal container, for all the extcap executables that have been found
+ * and that provides a toolbar with controls to be added to a Interface Toolbar
+ */
+static GHashTable *_toolbars = NULL;
+
/* internal container, to map preference names to pointers that hold preference
* values. These ensure that preferences can survive extcap if garbage
* collection, and does not lead to dangling pointers in the prefs subsystem.
@@ -198,6 +205,56 @@ extcap_find_interface_for_ifname(const gchar *ifname)
return result;
}
+static void
+extcap_free_toolbar_value(iface_toolbar_value *value)
+{
+ if (!value)
+ {
+ return;
+ }
+
+ g_free(value->value);
+ g_free(value->display);
+ g_free(value);
+}
+
+static void
+extcap_free_toolbar_control(iface_toolbar_control *control)
+{
+ if (!control)
+ {
+ return;
+ }
+
+ g_free(control->display);
+ g_free(control->validation);
+ g_free(control->tooltip);
+ if (control->ctrl_type == INTERFACE_TYPE_STRING) {
+ g_free(control->default_value.string);
+ }
+ g_list_foreach(control->values, (GFunc)extcap_free_toolbar_value, NULL);
+ g_list_free(control->values);
+ g_free(control);
+}
+
+static void
+extcap_free_toolbar(gpointer data)
+{
+ if (!data)
+ {
+ return;
+ }
+
+ iface_toolbar *toolbar = (iface_toolbar *)data;
+
+ g_free(toolbar->menu_title);
+ g_free(toolbar->help);
+ g_list_free_full(toolbar->ifnames, g_free);
+ g_list_foreach(toolbar->controls, (GFunc)extcap_free_toolbar_control, NULL);
+ g_list_free(toolbar->controls);
+ g_free(toolbar);
+}
+
static gboolean
extcap_if_exists_for_extcap(const gchar *ifname, const gchar *extcap)
{
@@ -218,6 +275,26 @@ extcap_if_executable(const gchar *ifname)
return interface != NULL ? interface->extcap_path : NULL;
}
+static void
+extcap_iface_toolbar_add(const gchar *extcap, iface_toolbar *toolbar_entry)
+{
+ char *toolname;
+
+ if (!extcap || !toolbar_entry)
+ {
+ return;
+ }
+
+ toolname = g_path_get_basename(extcap);
+
+ if (!g_hash_table_lookup(_toolbars, toolname))
+ {
+ g_hash_table_insert(_toolbars, g_strdup(toolname), toolbar_entry);
+ }
+
+ g_free(toolname);
+}
+
/* Note: args does not need to be NULL-terminated. */
static gboolean extcap_foreach(gint argc, gchar **args,
extcap_cb_t cb, extcap_callback_info_t cb_info)
@@ -815,6 +892,27 @@ extcap_has_configuration(const char *ifname, gboolean is_required)
return found;
}
+gboolean
+extcap_has_toolbar(const char *ifname)
+{
+ if (!iface_toolbar_use())
+ {
+ return FALSE;
+ }
+
+ GList *toolbar_list = g_hash_table_get_values (_toolbars);
+ for (GList *walker = toolbar_list; walker; walker = walker->next)
+ {
+ iface_toolbar *toolbar = (iface_toolbar *) walker->data;
+ if (g_list_find_custom(toolbar->ifnames, ifname, (GCompareFunc) strcmp))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/* taken from capchild/capture_sync.c */
static gboolean pipe_data_available(int pipe_fd)
{
@@ -896,6 +994,16 @@ void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg)
ws_unlink(interface_opts.extcap_fifo);
interface_opts.extcap_fifo = NULL;
}
+ if (interface_opts.extcap_control_in && file_exists(interface_opts.extcap_control_in))
+ {
+ ws_unlink(interface_opts.extcap_control_in);
+ interface_opts.extcap_control_in = NULL;
+ }
+ if (interface_opts.extcap_control_out && file_exists(interface_opts.extcap_control_out))
+ {
+ ws_unlink(interface_opts.extcap_control_out);
+ interface_opts.extcap_control_out = NULL;
+ }
#endif
/* Maybe the client closed and removed fifo, but ws should check if
* pid should be closed */
@@ -1088,6 +1196,16 @@ GPtrArray *extcap_prepare_arguments(interface_options interface_opts)
}
add_arg(EXTCAP_ARGUMENT_RUN_PIPE);
add_arg(interface_opts.extcap_fifo);
+ if (interface_opts.extcap_control_in)
+ {
+ add_arg(EXTCAP_ARGUMENT_CONTROL_OUT);
+ add_arg(interface_opts.extcap_control_in);
+ }
+ if (interface_opts.extcap_control_out)
+ {
+ add_arg(EXTCAP_ARGUMENT_CONTROL_IN);
+ add_arg(interface_opts.extcap_control_out);
+ }
if (interface_opts.extcap_args == NULL || g_hash_table_size(interface_opts.extcap_args) == 0)
{
/* User did not perform interface configuration.
@@ -1189,6 +1307,13 @@ extcap_init_interfaces(capture_options *capture_opts)
continue;
}
+ /* create control pipes if having toolbar */
+ if (extcap_has_toolbar(interface_opts.name))
+ {
+ extcap_create_pipe(&interface_opts.extcap_control_in);
+ extcap_create_pipe(&interface_opts.extcap_control_out);
+ }
+
/* create pipe for fifo */
if (!extcap_create_pipe(&interface_opts.extcap_fifo))
{
@@ -1393,15 +1518,22 @@ static void remove_extcap_entry(gpointer entry, gpointer data _U_)
static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
{
- GList * interfaces = NULL, * walker = NULL;
+ GList * interfaces = NULL, * control_items = NULL, * walker = NULL;
extcap_interface * int_iter = NULL;
extcap_info * element = NULL;
+ iface_toolbar * toolbar_entry = NULL;
gchar * toolname = g_path_get_basename(cb_info.extcap);
GList * interface_keys = g_hash_table_get_keys(_loaded_interfaces);
/* Load interfaces from utility */
- interfaces = extcap_parse_interfaces(cb_info.output);
+ interfaces = extcap_parse_interfaces(cb_info.output, &control_items);
+
+ if (control_items)
+ {
+ toolbar_entry = g_new0(iface_toolbar, 1);
+ toolbar_entry->controls = control_items;
+ }
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Loading interface list for %s ", cb_info.extcap);
@@ -1447,6 +1579,11 @@ static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
}
help = int_iter->help;
+ if (toolbar_entry)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ toolbar_entry->help = g_strdup(int_iter->help);
+ }
walker = g_list_next(walker);
continue;
@@ -1476,11 +1613,26 @@ static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
element->interfaces = g_list_append(element->interfaces, int_iter);
g_hash_table_insert(_tool_for_ifname, g_strdup(int_iter->call), g_strdup(toolname));
+
+ if (toolbar_entry)
+ {
+ if (!toolbar_entry->menu_title)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ }
+ toolbar_entry->ifnames = g_list_append(toolbar_entry->ifnames, g_strdup(int_iter->call));
+ }
}
walker = g_list_next(walker);
}
+ if (toolbar_entry && toolbar_entry->menu_title)
+ {
+ iface_toolbar_add(toolbar_entry);
+ extcap_iface_toolbar_add(cb_info.extcap, toolbar_entry);
+ }
+
g_list_foreach(interfaces, remove_extcap_entry, NULL);
g_list_free(interfaces);
g_list_free(interface_keys);
@@ -1499,6 +1651,21 @@ extcap_load_interface_list(void)
gchar *argv;
gchar *error;
+ if (_toolbars)
+ {
+ // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
+ // to avoid flicker in shown toolbars when refreshing interfaces.
+ GList *toolbar_list = g_hash_table_get_values (_toolbars);
+ for (GList *walker = toolbar_list; walker; walker = walker->next)
+ {
+ iface_toolbar *toolbar = (iface_toolbar *) walker->data;
+ iface_toolbar_remove(toolbar->menu_title);
+ }
+ g_hash_table_remove_all(_toolbars);
+ } else {
+ _toolbars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_toolbar);
+ }
+
if (_loaded_interfaces == NULL)
{
_loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface_info);
diff --git a/extcap.h b/extcap.h
index 7c7a82baaf..297745ee63 100644
--- a/extcap.h
+++ b/extcap.h
@@ -43,13 +43,15 @@
#define EXTCAP_PIPE_PREFIX "wireshark_extcap"
#define EXTCAP_ARGUMENT_CONFIG "--extcap-config"
-#define EXTCAP_ARGUMENT_LIST_INTERFACES "--extcap-interfaces"
+#define EXTCAP_ARGUMENT_LIST_INTERFACES "--extcap-interfaces"
#define EXTCAP_ARGUMENT_INTERFACE "--extcap-interface"
#define EXTCAP_ARGUMENT_LIST_DLTS "--extcap-dlts"
#define EXTCAP_ARGUMENT_RUN_CAPTURE "--capture"
#define EXTCAP_ARGUMENT_CAPTURE_FILTER "--extcap-capture-filter"
#define EXTCAP_ARGUMENT_RUN_PIPE "--fifo"
+#define EXTCAP_ARGUMENT_CONTROL_IN "--extcap-control-in"
+#define EXTCAP_ARGUMENT_CONTROL_OUT "--extcap-control-out"
typedef struct _extcap_info {
gchar * basename;
@@ -117,6 +119,9 @@ extcap_free_if_configuration(GList *list, gboolean free_args);
gboolean
extcap_has_configuration(const char * ifname, gboolean is_required);
+gboolean
+extcap_has_toolbar(const char *ifname);
+
#ifdef WIN32
HANDLE
extcap_get_win32_handle();
diff --git a/extcap_parser.c b/extcap_parser.c
index 78c48dd94f..7f7b6ceb0c 100644
--- a/extcap_parser.c
+++ b/extcap_parser.c
@@ -28,6 +28,9 @@
#include <glib.h>
#include <string.h>
+#include "ui/iface_toolbar.h"
+#include "wsutil/strtoi.h"
+
#include "extcap.h"
#include "extcap_parser.h"
@@ -120,7 +123,7 @@ static extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) {
rs->sentence = NULL;
/* Regex for catching just the allowed values for sentences */
- if ((regex = g_regex_new("^[\\t| ]*(arg|value|interface|extcap|dlt)(?=[\\t| ]+\\{)",
+ if ((regex = g_regex_new("^[\\t| ]*(arg|value|interface|extcap|dlt|control)(?=[\\t| ]+\\{)",
(GRegexCompileFlags) G_REGEX_CASELESS, (GRegexMatchFlags) 0, NULL)) != NULL) {
g_regex_match(regex, s, (GRegexMatchFlags) 0, &match_info);
@@ -193,6 +196,10 @@ static extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) {
param_type = EXTCAP_PARAM_VERSION;
} else if (g_ascii_strcasecmp(arg, "help") == 0) {
param_type = EXTCAP_PARAM_HELP;
+ } else if (g_ascii_strcasecmp(arg, "control") == 0) {
+ param_type = EXTCAP_PARAM_CONTROL;
+ } else if (g_ascii_strcasecmp(arg, "role") == 0) {
+ param_type = EXTCAP_PARAM_ROLE;
} else {
param_type = EXTCAP_PARAM_UNKNOWN;
}
@@ -269,6 +276,24 @@ void extcap_free_arg(extcap_arg *a) {
g_free(a);
}
+void extcap_free_toolbar_value(iface_toolbar_value *v) {
+ if (v == NULL)
+ return;
+
+ g_free(v->value);
+ g_free(v->display);
+}
+
+void extcap_free_toolbar_control(iface_toolbar_control *c) {
+ if (c == NULL)
+ return;
+
+ g_free(c->display);
+ g_free(c->validation);
+ g_free(c->tooltip);
+ g_free(c->placeholder);
+}
+
void extcap_free_arg_list(GList *a) {
g_list_foreach(a, (GFunc)extcap_free_arg, NULL);
g_list_free(a);
@@ -280,6 +305,12 @@ static gint glist_find_numbered_arg(gconstpointer listelem, gconstpointer needle
return 1;
}
+static gint glist_find_numbered_control(gconstpointer listelem, gconstpointer needle) {
+ if (((const iface_toolbar_control *) listelem)->num == *((const int *) needle))
+ return 0;
+ return 1;
+}
+
static void extcap_free_tokenized_sentence(gpointer s, gpointer user_data _U_) {
extcap_token_sentence *t = (extcap_token_sentence *)s;
@@ -604,7 +635,179 @@ static extcap_interface *extcap_parse_interface_sentence(extcap_token_sentence *
return ri;
}
-GList *extcap_parse_interfaces(gchar *output) {
+static iface_toolbar_control *extcap_parse_control_sentence(GList *control_items, extcap_token_sentence *s)
+{
+ extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN;
+ gchar *param_value = NULL;
+ iface_toolbar_control *control = NULL;
+ iface_toolbar_value *value = NULL;
+ GList *entry = NULL;
+ guint32 num = 0;
+
+ if (s == NULL)
+ return NULL;
+
+ if (g_ascii_strcasecmp(s->sentence, "control") == 0) {
+ sent = EXTCAP_SENTENCE_CONTROL;
+ } else if (g_ascii_strcasecmp(s->sentence, "value") == 0) {
+ sent = EXTCAP_SENTENCE_VALUE;
+ }
+
+ if (sent == EXTCAP_SENTENCE_UNKNOWN)
+ return NULL;
+
+ if (sent == EXTCAP_SENTENCE_CONTROL) {
+ control = g_new0(iface_toolbar_control, 1);
+ control->ctrl_type = INTERFACE_TYPE_UNKNOWN;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_ARGNUM));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ if (!ws_strtou32(param_value, NULL, &num)) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+ control->num = (int)num;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DISPLAY));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+ control->display = g_strdup(param_value);
+
+ if ((param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_VALIDATION)))
+ != NULL) {
+ control->validation = g_strdup(param_value);
+ }
+
+ if ((param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_REQUIRED)))
+ != NULL) {
+ control->is_required = g_regex_match_simple(EXTCAP_BOOLEAN_REGEX, param_value, G_REGEX_CASELESS, (GRegexMatchFlags)0);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_TOOLTIP));
+ if (param_value != NULL) {
+ control->tooltip = g_strdup(param_value);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_PLACEHOLDER));
+ if (param_value != NULL) {
+ control->placeholder = g_strdup(param_value);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_TYPE));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ extcap_arg_type arg_type = EXTCAP_ARG_UNKNOWN;
+ if (g_ascii_strcasecmp(param_value, "boolean") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_BOOLEAN;
+ arg_type = EXTCAP_ARG_BOOLEAN;
+ } else if (g_ascii_strcasecmp(param_value, "button") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_BUTTON;
+ } else if (g_ascii_strcasecmp(param_value, "selector") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_SELECTOR;
+ } else if (g_ascii_strcasecmp(param_value, "string") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_STRING;
+ arg_type = EXTCAP_ARG_STRING;
+ } else {
+ printf("invalid type %s in CONTROL sentence\n", param_value);
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_ROLE));
+ if (param_value != NULL) {
+ if (g_ascii_strcasecmp(param_value, "control") == 0) {
+ control->ctrl_role = INTERFACE_ROLE_CONTROL;
+ } else if (g_ascii_strcasecmp(param_value, "help") == 0) {
+ 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 {
+ printf("invalid role %s in CONTROL sentence\n", param_value);
+ control->ctrl_role = INTERFACE_ROLE_UNKNOWN;
+ }
+ } else {
+ /* Default role */
+ control->ctrl_role = INTERFACE_ROLE_CONTROL;
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DEFAULT));
+ if (param_value != NULL) {
+ if (arg_type != EXTCAP_ARG_UNKNOWN) {
+ extcap_complex *complex = extcap_parse_complex(arg_type, param_value);
+ if (complex != NULL) {
+ if (arg_type == EXTCAP_ARG_BOOLEAN) {
+ control->default_value.boolean = extcap_complex_get_bool(complex);
+ } else if (arg_type == EXTCAP_ARG_STRING) {
+ control->default_value.string = g_strdup(complex->_val);
+ }
+ extcap_free_complex(complex);
+ } else {
+ printf("invalid default, couldn't parse %s\n", param_value);
+ }
+ }
+ }
+
+ } else if (sent == EXTCAP_SENTENCE_VALUE) {
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_CONTROL));
+ if (param_value == NULL) {
+ printf("no control in VALUE sentence\n");
+ return NULL;
+ }
+
+ if (!ws_strtou32(param_value, NULL, &num)) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ entry = g_list_find_custom(control_items, &num, glist_find_numbered_control);
+ if (entry == NULL) {
+ printf("couldn't find control %u in list for VALUE sentence\n", num);
+ return NULL;
+ }
+
+ value = g_new0(iface_toolbar_value, 1);
+ value->num = (int)num;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_VALUE));
+ if (param_value == NULL) {
+ extcap_free_toolbar_value(value);
+ return NULL;
+ }
+ value->value = g_strdup(param_value);
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DISPLAY));
+ if (param_value == NULL) {
+ extcap_free_toolbar_value(value);
+ return NULL;
+ }
+ value->display = g_strdup(param_value);
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DEFAULT));
+ if (param_value != NULL) {
+ value->is_default = g_regex_match_simple(EXTCAP_BOOLEAN_REGEX, param_value, G_REGEX_CASELESS, (GRegexMatchFlags)0);
+ }
+
+ control = (iface_toolbar_control *)entry->data;
+ control->values = g_list_append(control->values, value);
+
+ return NULL;
+ }
+
+ return control;
+}
+
+GList *extcap_parse_interfaces(gchar *output, GList **control_items) {
GList *result = NULL;
GList *tokens = NULL;
@@ -613,6 +816,7 @@ GList *extcap_parse_interfaces(gchar *output) {
while (walker) {
extcap_interface *ri = NULL;
+ iface_toolbar_control *ti = NULL;
extcap_token_sentence *if_sentence = (extcap_token_sentence *) walker->data;
if (if_sentence) {
@@ -622,6 +826,13 @@ GList *extcap_parse_interfaces(gchar *output) {
if ((ri = extcap_parse_interface_sentence(if_sentence))) {
result = g_list_append(result, ri);
}
+ } else if (control_items &&
+ ((g_ascii_strcasecmp(if_sentence->sentence, "control") == 0) ||
+ (g_ascii_strcasecmp(if_sentence->sentence, "value") == 0)))
+ {
+ if ((ti = extcap_parse_control_sentence(*control_items, if_sentence))) {
+ *control_items = g_list_append(*control_items, ti);
+ }
}
}
diff --git a/extcap_parser.h b/extcap_parser.h
index 1fd4d42071..3d4c8d0d7b 100644
--- a/extcap_parser.h
+++ b/extcap_parser.h
@@ -34,7 +34,8 @@ typedef enum {
EXTCAP_SENTENCE_VALUE,
EXTCAP_SENTENCE_EXTCAP,
EXTCAP_SENTENCE_INTERFACE,
- EXTCAP_SENTENCE_DLT
+ EXTCAP_SENTENCE_DLT,
+ EXTCAP_SENTENCE_CONTROL
} extcap_sentence_type;
typedef enum {
@@ -78,7 +79,9 @@ typedef enum {
EXTCAP_PARAM_SAVE,
EXTCAP_PARAM_VALIDATION,
EXTCAP_PARAM_VERSION,
- EXTCAP_PARAM_HELP
+ EXTCAP_PARAM_HELP,
+ EXTCAP_PARAM_CONTROL,
+ EXTCAP_PARAM_ROLE
} extcap_param_type;
#define ENUM_KEY(s) GUINT_TO_POINTER((guint)s)
@@ -197,7 +200,7 @@ void extcap_free_arg_list(GList *a);
GList * extcap_parse_args(gchar *output);
/* Parse all sentences for interfaces */
-GList * extcap_parse_interfaces(gchar *output);
+GList * extcap_parse_interfaces(gchar *output, GList **control_items);
/* Parse all sentences for DLTs */
GList * extcap_parse_dlts(gchar *output);
diff --git a/sync_pipe.h b/sync_pipe.h
index 455b2b4bd6..d713305297 100644
--- a/sync_pipe.h
+++ b/sync_pipe.h
@@ -58,6 +58,7 @@
#define SP_PACKET_COUNT 'P' /* count of packets captured since last message */
#define SP_DROPS 'D' /* count of packets dropped in capture */
#define SP_SUCCESS 'S' /* success indication, no extra data */
+#define SP_TOOLBAR_CTRL 'T' /* interface toolbar control packet */
/*
* Win32 only: Indications sent out on the signal pipe (from parent to child)
* (UNIX-like sends signals for this)
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 8a52ef9463..e02eab1c6e 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -34,6 +34,7 @@ set(COMMON_UI_SRC
failure_message.c
filter_files.c
firewall_rules.c
+ iface_toolbar.c
iface_lists.c
io_graph_item.c
language.c
diff --git a/ui/Makefile.am b/ui/Makefile.am
index 776ad4230b..49699c6615 100644
--- a/ui/Makefile.am
+++ b/ui/Makefile.am
@@ -60,6 +60,7 @@ WIRESHARK_UI_SRC = \
failure_message.c \
filter_files.c \
firewall_rules.c \
+ iface_toolbar.c \
iface_lists.c \
io_graph_item.c \
language.c \
@@ -109,6 +110,7 @@ WIRESHARK_UI_INCLUDES = \
help_url.h \
packet_list_utils.h \
firewall_rules.h \
+ iface_toolbar.h \
iface_lists.h \
io_graph_item.h \
language.h \
diff --git a/ui/iface_toolbar.c b/ui/iface_toolbar.c
new file mode 100644
index 0000000000..0e16456ee4
--- /dev/null
+++ b/ui/iface_toolbar.c
@@ -0,0 +1,68 @@
+/* iface_toolbar.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "iface_toolbar.h"
+
+
+static iface_toolbar_add_cb_t iface_toolbar_add_cb;
+static iface_toolbar_remove_cb_t iface_toolbar_remove_cb;
+
+void iface_toolbar_add(const iface_toolbar *toolbar)
+{
+ if (iface_toolbar_add_cb) {
+ iface_toolbar_add_cb(toolbar);
+ }
+}
+
+void iface_toolbar_remove(const gchar *menu_title)
+{
+ if (iface_toolbar_remove_cb) {
+ iface_toolbar_remove_cb(menu_title);
+ }
+}
+
+gboolean iface_toolbar_use(void)
+{
+ return iface_toolbar_add_cb ? TRUE : FALSE;
+}
+
+void iface_toolbar_register_cb(iface_toolbar_add_cb_t add_cb, iface_toolbar_remove_cb_t remove_cb)
+{
+ iface_toolbar_add_cb = add_cb;
+ iface_toolbar_remove_cb = remove_cb;
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/iface_toolbar.h b/ui/iface_toolbar.h
new file mode 100644
index 0000000000..888c817230
--- /dev/null
+++ b/ui/iface_toolbar.h
@@ -0,0 +1,105 @@
+/* iface_toolbar.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __IFACE_TOOLBAR_H__
+#define __IFACE_TOOLBAR_H__
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ INTERFACE_TYPE_UNKNOWN,
+ INTERFACE_TYPE_BOOLEAN,
+ INTERFACE_TYPE_BUTTON,
+ INTERFACE_TYPE_SELECTOR,
+ INTERFACE_TYPE_STRING
+} iface_toolbar_ctrl_type;
+
+typedef enum {
+ INTERFACE_ROLE_UNKNOWN,
+ INTERFACE_ROLE_CONTROL,
+ INTERFACE_ROLE_HELP,
+ INTERFACE_ROLE_LOGGER,
+ INTERFACE_ROLE_RESET
+} iface_toolbar_ctrl_role;
+
+typedef struct _iface_toolbar_value {
+ int num;
+ gchar *value;
+ gchar *display;
+ gboolean is_default;
+} iface_toolbar_value;
+
+typedef struct _iface_toolbar_control {
+ int num;
+ iface_toolbar_ctrl_type ctrl_type;
+ iface_toolbar_ctrl_role ctrl_role;
+ gchar *display;
+ gchar *validation;
+ gboolean is_required;
+ gchar *tooltip;
+ gchar *placeholder;
+ union {
+ gboolean boolean;
+ gchar *string;
+ } default_value;
+ GList *values;
+} iface_toolbar_control;
+
+typedef struct _iface_toolbar {
+ gchar *menu_title;
+ gchar *help;
+ GList *ifnames;
+ GList *controls;
+} iface_toolbar;
+
+typedef void (*iface_toolbar_add_cb_t)(const iface_toolbar *);
+typedef void (*iface_toolbar_remove_cb_t)(const gchar *);
+
+void iface_toolbar_add(const iface_toolbar *toolbar);
+
+void iface_toolbar_remove(const gchar *menu_title);
+
+gboolean iface_toolbar_use(void);
+
+void iface_toolbar_register_cb(iface_toolbar_add_cb_t, iface_toolbar_remove_cb_t);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IFACE_TOOLBAR_H__ */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index e8936e9119..ca3d67f530 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -87,6 +87,9 @@ set(WIRESHARK_QT_HEADERS
gsm_map_summary_dialog.h
iax2_analysis_dialog.h
import_text_dialog.h
+ interface_toolbar.h
+ interface_toolbar_reader.h
+ interface_toolbar_lineedit.h
interface_tree_model.h
interface_tree_cache_model.h
interface_sort_filter_model.h
@@ -257,6 +260,9 @@ set(WIRESHARK_QT_SRC
geometry_state_dialog.cpp
iax2_analysis_dialog.cpp
import_text_dialog.cpp
+ interface_toolbar.cpp
+ interface_toolbar_reader.cpp
+ interface_toolbar_lineedit.cpp
interface_tree_model.cpp
interface_tree_cache_model.cpp
interface_sort_filter_model.cpp
@@ -418,6 +424,7 @@ set(WIRESHARK_QT_UI
iax2_analysis_dialog.ui
import_text_dialog.ui
interface_frame.ui
+ interface_toolbar.ui
io_graph_dialog.ui
layout_preferences_frame.ui
lbm_lbtrm_transport_dialog.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index 77d17ea449..7f52918fcf 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -70,6 +70,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_iax2_analysis_dialog.h \
ui_import_text_dialog.h \
ui_interface_frame.h \
+ ui_interface_toolbar.h \
ui_io_graph_dialog.h \
ui_layout_preferences_frame.h \
ui_lbm_lbtrm_transport_dialog.h \
@@ -215,6 +216,9 @@ MOC_HDRS = \
iax2_analysis_dialog.h \
import_text_dialog.h \
interface_frame.h \
+ interface_toolbar.h \
+ interface_toolbar_lineedit.h \
+ interface_toolbar_reader.h \
interface_tree_model.h \
interface_tree_cache_model.h \
interface_sort_filter_model.h \
@@ -340,6 +344,7 @@ UI_FILES = \
iax2_analysis_dialog.ui \
import_text_dialog.ui \
interface_frame.ui \
+ interface_toolbar.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
lbm_lbtrm_transport_dialog.ui \
@@ -499,6 +504,9 @@ WIRESHARK_QT_SRC = \
iax2_analysis_dialog.cpp \
import_text_dialog.cpp \
interface_frame.cpp \
+ interface_toolbar.cpp \
+ interface_toolbar_lineedit.cpp \
+ interface_toolbar_reader.cpp \
interface_tree_model.cpp \
interface_tree_cache_model.cpp \
interface_sort_filter_model.cpp \
@@ -822,6 +830,8 @@ io_graph_dialog.$(OBJEXT): ui_io_graph_dialog.h
interface_frame.$(OBJEXT): ui_interface_frame.h
+interface_toolbar.$(OBJEXT): ui_interface_toolbar.h
+
layout_preferences_frame.$(OBJEXT): ui_layout_preferences_frame.h
lbm_lbtrm_transport_dialog.$(OBJEXT): ui_lbm_lbtrm_transport_dialog.h
diff --git a/ui/qt/funnel_text_dialog.cpp b/ui/qt/funnel_text_dialog.cpp
index 6ba873f451..8be47b032c 100644
--- a/ui/qt/funnel_text_dialog.cpp
+++ b/ui/qt/funnel_text_dialog.cpp
@@ -47,6 +47,7 @@ FunnelTextDialog::FunnelTextDialog(const QString &title) :
if (!title.isEmpty()) {
loadGeometry(0, 0, QString("Funnel %1").arg(title));
}
+ setWindowTitle(wsApp->windowTitleString(title));
funnel_text_window_.funnel_text_dialog = this;
@@ -75,7 +76,6 @@ void FunnelTextDialog::reject()
struct _funnel_text_window_t *FunnelTextDialog::textWindowNew(const QString title)
{
FunnelTextDialog *ftd = new FunnelTextDialog(title);
- ftd->setWindowTitle(wsApp->windowTitleString(title));
ftd->show();
return &ftd->funnel_text_window_;
}
diff --git a/ui/qt/interface_toolbar.cpp b/ui/qt/interface_toolbar.cpp
new file mode 100644
index 0000000000..56a1a53b34
--- /dev/null
+++ b/ui/qt/interface_toolbar.cpp
@@ -0,0 +1,895 @@
+/* interface_toolbar.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+
+#include "interface_toolbar.h"
+#include "interface_toolbar_lineedit.h"
+#include "simple_dialog.h"
+#include "ui/main_statusbar.h"
+#include <ui_interface_toolbar.h>
+
+#include "sync_pipe.h"
+#include "wsutil/file_util.h"
+
+#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
+ 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);
+
+#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())
+ {
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->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((gchar *)control->display));
+ button->setMaximumHeight(27);
+ button->setToolTip(QString().fromUtf8(control->tooltip));
+
+ switch (control->ctrl_role)
+ {
+ case INTERFACE_ROLE_CONTROL:
+ setDefaultValue(control->num, (gchar *)control->display);
+ connect(button, SIGNAL(pressed()), this, SLOT(onButtonPressed()));
+ break;
+
+ case INTERFACE_ROLE_HELP:
+ connect(button, SIGNAL(pressed()), this, SLOT(onHelpButtonPressed()));
+ if (help_link_.isEmpty())
+ {
+ // No help URL provided
+ button->hide();
+ }
+ break;
+
+ case INTERFACE_ROLE_LOGGER:
+ 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()));
+ break;
+
+ default:
+ 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((gchar *)val->value);
+ if (value.length() == 0)
+ {
+ // Invalid value
+ continue;
+ }
+ QString display = QString().fromUtf8((gchar *)val->display);
+ QByteArray interface_value;
+
+ interface_value.append(value);
+ if (display.length() == 0)
+ {
+ display = value;
+ }
+ else
+ {
+ interface_value.append('\0' + display);
+ }
+ 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)
+{
+ if (QComboBox *combobox = dynamic_cast<QComboBox *>(widget))
+ {
+ combobox->blockSignals(true);
+ switch (command)
+ {
+ case commandControlSet:
+ {
+ int idx = combobox->findData(payload);
+ if (idx != -1)
+ {
+ combobox->setCurrentIndex(idx);
+ }
+ break;
+ }
+
+ case commandControlAdd:
+ {
+ QString value;
+ QString 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 = dynamic_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 = dynamic_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 = dynamic_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 (dynamic_cast<QComboBox *>(widget))
+ {
+ switch (command)
+ {
+ case commandControlSet:
+ 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 (dynamic_cast<InterfaceToolbarLineEdit *>(widget))
+ {
+ switch (command)
+ {
+ case commandControlSet:
+ 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)
+ {
+ interface_[ifname].log_dialog->clearText();
+ }
+ interface_[ifname].log_text.clear();
+ }
+ if (command == commandControlSet || command == commandControlAdd)
+ {
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->appendText(payload);
+ }
+ interface_[ifname].log_text.append(payload);
+ }
+ }
+ else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ // QCheckBox or QPushButton
+ interface_[ifname].value[num] = payload;
+ }
+}
+
+
+void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QByteArray payload)
+{
+ switch (command)
+ {
+ case commandControlSet:
+ case commandControlAdd:
+ case commandControlRemove:
+ if (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 (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:
+ statusbar_push_temporary_msg("%s", payload.data());
+ break;
+
+ case commandInformationMessage:
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "%s", payload.data());
+ break;
+
+ case commandWarningMessage:
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "%s", payload.data());
+ break;
+
+ case commandErrorMessage:
+ simple_dialog(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 (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(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Unable to send control message:\n%s.",
+ g_strerror(errno));
+ }
+}
+
+void InterfaceToolbar::onButtonPressed()
+{
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+void InterfaceToolbar::onLogButtonPressed()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+
+ if (!interface_[ifname].log_dialog)
+ {
+ QPushButton *button = static_cast<QPushButton *>(sender());
+ interface_[ifname].log_dialog = new FunnelTextDialog(ifname + " " + button->text());
+ connect(interface_[ifname].log_dialog, SIGNAL(accepted()), this, SLOT(closeLog()));
+ connect(interface_[ifname].log_dialog, SIGNAL(rejected()), this, SLOT(closeLog()));
+
+ interface_[ifname].log_dialog->setText(interface_[ifname].log_text);
+ }
+
+ interface_[ifname].log_dialog->show();
+ interface_[ifname].log_dialog->raise();
+ interface_[ifname].log_dialog->activateWindow();
+}
+
+void InterfaceToolbar::onHelpButtonPressed()
+{
+ 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())
+ {
+ if (interface_[ifname].log_dialog == log_dialog)
+ {
+ interface_[ifname].log_dialog = NULL;
+ }
+ }
+}
+
+void InterfaceToolbar::startReaderThread(QString ifname, QString 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(QString ifname, QString control_in, QString control_out)
+{
+ if (!interface_.contains(ifname) || // This interface is not for us
+ interface_[ifname].out_fd != -1) // Already have control channels for this interface
+ {
+ return;
+ }
+
+ // 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();
+}
+
+void InterfaceToolbar::stopCapture()
+{
+ foreach (QString ifname, interface_.keys())
+ {
+ if (interface_[ifname].reader_thread)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+ interface_[ifname].reader_thread->requestInterruption();
+#endif
+ interface_[ifname].reader_thread = NULL;
+ }
+
+ if (interface_[ifname].out_fd != -1)
+ {
+ ws_close (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[num] != default_value_[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::onResetButtonPressed()
+{
+ 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];
+ break;
+
+ case INTERFACE_ROLE_LOGGER:
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->clearText();
+ }
+ interface_[ifname].log_text.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];
+ if (!is_capturing &&
+ (widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
+ {
+ widget->setEnabled(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);
+ }
+ }
+ }
+
+ 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_RESET))
+ {
+ widget->setEnabled(!is_capturing);
+ }
+ }
+}
+
+void InterfaceToolbar::on_interfacesComboBox_currentIndexChanged(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();
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar.h b/ui/qt/interface_toolbar.h
new file mode 100644
index 0000000000..ebde37db4d
--- /dev/null
+++ b/ui/qt/interface_toolbar.h
@@ -0,0 +1,120 @@
+/* interface_toolbar.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_H
+#define INTERFACE_TOOLBAR_H
+
+#include <glib.h>
+
+#include "ui/iface_toolbar.h"
+#include "funnel_text_dialog.h"
+#include "interface_toolbar_reader.h"
+
+#include <QFrame>
+#include <QMap>
+#include <QString>
+
+
+namespace Ui {
+class InterfaceToolbar;
+}
+
+struct interface_values
+{
+ QThread *reader_thread;
+ int out_fd;
+ QMap<int, QByteArray> value;
+ QMap<int, QList<QByteArray> > list;
+ FunnelTextDialog *log_dialog;
+ QString log_text;
+ QMap<int, bool> widget_disabled;
+};
+
+class InterfaceToolbar : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit InterfaceToolbar(QWidget *parent = 0, const iface_toolbar *toolbar = NULL);
+ ~InterfaceToolbar();
+
+ void startCapture(QString ifname, QString control_in, QString control_out);
+ void stopCapture();
+ bool hasInterface(QString ifname);
+
+public slots:
+ void controlReceived(QString ifname, int num, int command, QByteArray message);
+
+signals:
+ void closeReader();
+
+private slots:
+ void startReaderThread(QString ifname, QString control_in);
+ void updateWidgets();
+
+ void onButtonPressed();
+ void onCheckBoxChanged(int state);
+ void onComboBoxChanged(int idx);
+ void onLineEditChanged();
+ void onLogButtonPressed();
+ void onHelpButtonPressed();
+ void onResetButtonPressed();
+
+ void closeLog();
+
+ void on_interfacesComboBox_currentIndexChanged(const QString &ifname);
+
+private:
+ void initializeControls(const iface_toolbar *toolbar);
+ void setDefaultValue(int num, const QByteArray &value);
+ void sendChangedValues(QString ifname);
+ QWidget *createCheckbox(iface_toolbar_control *control);
+ QWidget *createButton(iface_toolbar_control *control);
+ QWidget *createSelector(iface_toolbar_control *control);
+ QWidget *createString(iface_toolbar_control *control);
+ void controlSend(QString ifname, int num, int type, const QByteArray &payload);
+ void setWidgetValue(QWidget *widget, int type, QByteArray payload);
+ void setInterfaceValue(QString ifname, QWidget *widget, int num, int type, QByteArray payload);
+
+ Ui::InterfaceToolbar *ui;
+ QMap<QString, struct interface_values> interface_;
+ QMap<int, QByteArray> default_value_;
+ QMap<int, QList<QByteArray> > default_list_;
+ QMap<int, QWidget *> control_widget_;
+ QMap<int, QWidget *> label_widget_;
+ QString help_link_;
+ bool use_spacer_;
+};
+
+#endif // INTERFACE_TOOLBAR_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar.ui b/ui/qt/interface_toolbar.ui
new file mode 100644
index 0000000000..20faae5a57
--- /dev/null
+++ b/ui/qt/interface_toolbar.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>InterfaceToolbar</class>
+ <widget class="QFrame" name="InterfaceToolbar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>32</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="interfacesLabel">
+ <property name="toolTip">
+ <string>Select interface</string>
+ </property>
+ <property name="text">
+ <string>Interface</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="interfacesComboBox">
+ <property name="toolTip">
+ <string>Select interface</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="leftLayout"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="rightLayout"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/qt/interface_toolbar_lineedit.cpp b/ui/qt/interface_toolbar_lineedit.cpp
new file mode 100644
index 0000000000..181bcb3be5
--- /dev/null
+++ b/ui/qt/interface_toolbar_lineedit.cpp
@@ -0,0 +1,149 @@
+/* interface_toolbar_lineedit.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include "interface_toolbar_lineedit.h"
+#include "stock_icon_tool_button.h"
+#include "epan/prefs.h"
+#include "color_utils.h"
+
+#include <QStyle>
+
+// To do:
+// - Make a narrower apply button
+
+InterfaceToolbarLineEdit::InterfaceToolbarLineEdit(QWidget *parent, QString validation_regex, bool is_required) :
+ QLineEdit(parent),
+ validation_regex_(validation_regex),
+ is_required_(is_required),
+ text_edited_(false)
+{
+ apply_button_ = new StockIconToolButton(this, "x-filter-apply");
+ apply_button_->setCursor(Qt::ArrowCursor);
+ apply_button_->setEnabled(false);
+ apply_button_->setToolTip(tr("Apply changes"));
+ apply_button_->setIconSize(QSize(24, 14));
+ apply_button_->setStyleSheet(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ "}"
+ );
+
+ updateStyleSheet(isValid());
+
+ connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(validateText()));
+ connect(this, SIGNAL(textEdited(const QString &)), this, SLOT(validateEditedText()));
+ connect(this, SIGNAL(returnPressed()), this, SLOT(applyEditedText()));
+ connect(apply_button_, SIGNAL(clicked()), this, SLOT(applyEditedText()));
+}
+
+void InterfaceToolbarLineEdit::validateText()
+{
+ bool valid = isValid();
+
+ apply_button_->setEnabled(valid);
+ updateStyleSheet(valid);
+}
+
+void InterfaceToolbarLineEdit::validateEditedText()
+{
+ text_edited_ = true;
+}
+
+void InterfaceToolbarLineEdit::applyEditedText()
+{
+ if (text_edited_ && isValid())
+ {
+ emit editedTextApplied();
+ disableApplyButton();
+ }
+}
+
+void InterfaceToolbarLineEdit::disableApplyButton()
+{
+ apply_button_->setEnabled(false);
+ text_edited_ = false;
+}
+
+bool InterfaceToolbarLineEdit::isValid()
+{
+ bool valid = true;
+
+ if (is_required_ && text().length() == 0)
+ {
+ valid = false;
+ }
+
+ if (!validation_regex_.isEmpty() && text().length() > 0)
+ {
+ QRegExp expr(validation_regex_);
+ if (!expr.isValid() || expr.indexIn(text(), 0) == -1)
+ {
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+void InterfaceToolbarLineEdit::updateStyleSheet(bool is_valid)
+{
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize apsz = apply_button_->sizeHint();
+
+ QString style_sheet = QString(
+ "InterfaceToolbarLineEdit {"
+ " padding-right: %1px;"
+ " background-color: %2;"
+ "}"
+ )
+ .arg(apsz.width() + frameWidth)
+ .arg(is_valid ? QString("") : ColorUtils::fromColorT(prefs.gui_text_invalid).name());
+
+ setStyleSheet(style_sheet);
+}
+
+void InterfaceToolbarLineEdit::resizeEvent(QResizeEvent *)
+{
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize apsz = apply_button_->sizeHint();
+
+ apply_button_->move(contentsRect().right() - frameWidth - apsz.width() + 2,
+ contentsRect().top());
+ apply_button_->setMinimumHeight(contentsRect().height());
+ apply_button_->setMaximumHeight(contentsRect().height());
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_lineedit.h b/ui/qt/interface_toolbar_lineedit.h
new file mode 100644
index 0000000000..e26a50c976
--- /dev/null
+++ b/ui/qt/interface_toolbar_lineedit.h
@@ -0,0 +1,71 @@
+/* interface_toolbar_lineedit.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_LINEEDIT_H
+#define INTERFACE_TOOLBAR_LINEEDIT_H
+
+#include <QLineEdit>
+
+class StockIconToolButton;
+
+class InterfaceToolbarLineEdit : public QLineEdit
+{
+ Q_OBJECT
+
+public:
+ explicit InterfaceToolbarLineEdit(QWidget *parent = 0, QString validation_regex = QString(), bool is_required = false);
+ void disableApplyButton();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+
+signals:
+ void editedTextApplied();
+
+private slots:
+ void validateText();
+ void validateEditedText();
+ void applyEditedText();
+
+private:
+ bool isValid();
+ void updateStyleSheet(bool is_valid);
+
+ StockIconToolButton *apply_button_;
+ QString validation_regex_;
+ bool is_required_;
+ bool text_edited_;
+};
+
+#endif // INTERFACE_TOOLBAR_LINEEDIT_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_reader.cpp b/ui/qt/interface_toolbar_reader.cpp
new file mode 100644
index 0000000000..5c8c52a247
--- /dev/null
+++ b/ui/qt/interface_toolbar_reader.cpp
@@ -0,0 +1,140 @@
+/* interface_toolbar_reader.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include "interface_toolbar_reader.h"
+#include "sync_pipe.h"
+#include "wsutil/file_util.h"
+
+#include <QThread>
+
+const int header_size = 6;
+
+// To do:
+// - Add support for WIN32
+
+void InterfaceToolbarReader::loop()
+{
+#ifndef _WIN32
+ struct timeval timeout;
+ QByteArray header;
+ QByteArray payload;
+ fd_set readfds;
+
+ int fd = ws_open(control_in_.toUtf8(), O_RDONLY | O_BINARY | O_NONBLOCK, 0);
+ if (fd == -1)
+ {
+ emit finished();
+ return;
+ }
+
+ forever
+ {
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ int ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret == -1)
+ {
+ break;
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+ if (QThread::currentThread()->isInterruptionRequested())
+ {
+ break;
+ }
+#endif
+
+ if (ret > 0 && FD_ISSET(fd, &readfds))
+ {
+ header.resize(header_size);
+ if (ws_read(fd, header.data(), header_size) != header_size)
+ {
+ break;
+ }
+
+ unsigned char high_nibble = header[1] & 0xFF;
+ unsigned char mid_nibble = header[2] & 0xFF;
+ unsigned char low_nibble = header[3] & 0xFF;
+ ssize_t payload_len = (ssize_t)((high_nibble << 16) + (mid_nibble << 8) + low_nibble) - 2;
+
+ payload.resize(payload_len);
+ if (payload_len > 0)
+ {
+ ssize_t total_len = 0;
+ while (total_len < payload_len)
+ {
+ ssize_t read_len = ws_read(fd, payload.data() + total_len, payload_len - total_len);
+ if (read_len == -1)
+ {
+ if (errno != EAGAIN)
+ {
+ break;
+ }
+ }
+ else
+ {
+ total_len += read_len;
+ }
+ }
+ if (total_len != payload_len)
+ {
+ break;
+ }
+ }
+ if (header[0] == SP_TOOLBAR_CTRL)
+ {
+ emit received(ifname_, (unsigned char)header[4], (unsigned char)header[5], payload);
+ }
+ }
+ }
+
+ ws_close(fd);
+#endif
+ emit finished();
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_reader.h b/ui/qt/interface_toolbar_reader.h
new file mode 100644
index 0000000000..1b34db4fb0
--- /dev/null
+++ b/ui/qt/interface_toolbar_reader.h
@@ -0,0 +1,65 @@
+/* interface_toolbar_reader.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_READER_H
+#define INTERFACE_TOOLBAR_READER_H
+
+#include <QObject>
+#include <QByteArray>
+
+namespace Ui {
+class InterfaceToolbarReader;
+}
+
+class InterfaceToolbarReader : public QObject
+{
+ Q_OBJECT
+
+public:
+ InterfaceToolbarReader(QString ifname, QString control_in, QObject *parent = 0) :
+ QObject(parent), ifname_(ifname), control_in_(control_in) {}
+
+public slots:
+ void loop();
+
+signals:
+ void received(QString ifname, int num, int command, QByteArray payload);
+ void finished();
+
+private:
+ QString ifname_;
+ QString control_in_;
+};
+
+#endif // INTERFACE_TOOLBAR_READER_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp
index 1570b4f4cc..0952594516 100644
--- a/ui/qt/main_window.cpp
+++ b/ui/qt/main_window.cpp
@@ -33,6 +33,7 @@
#include <epan/export_object.h>
#include "ui/commandline.h"
+#include "ui/iface_toolbar.h"
#ifdef HAVE_LIBPCAP
#include "ui/capture.h"
@@ -61,6 +62,7 @@
#include "file_set_dialog.h"
#include "funnel_statistics.h"
#include "import_text_dialog.h"
+#include "interface_toolbar.h"
#include "packet_list.h"
#include "proto_tree.h"
#include "simple_dialog.h"
@@ -205,6 +207,23 @@ static void plugin_if_mainwindow_update_toolbars(gconstpointer user_data)
if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
+
+ }
+}
+
+static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
+{
+ if (gbl_cur_main_window_ && toolbar_entry)
+ {
+ gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
+ }
+}
+
+static void mainwindow_remove_toolbar(const gchar *menu_title)
+{
+ if (gbl_cur_main_window_ && menu_title)
+ {
+ gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
}
}
@@ -759,6 +778,17 @@ MainWindow::MainWindow(QWidget *parent) :
#endif
plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(_WIN32)
+ // Register Interface Toolbar callbacks
+ //
+ // Qt version must be 5.2 or higher because the use of
+ // QThread::requestInterruption() in interface_toolbar.cpp and
+ // QThread::isInterruptionRequested() in interface_toolbar_reader.cpp
+ //
+ // The toolbar in/out control pipes are not supported on WIN32 yet.
+ iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
+#endif
+
main_ui_->mainStack->setCurrentWidget(main_welcome_);
}
@@ -779,6 +809,13 @@ QMenu *MainWindow::createPopupMenu()
menu->addAction(main_ui_->actionViewFilterToolbar);
menu->addAction(main_ui_->actionViewWirelessToolbar);
+ if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
+ QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ submenu->addAction(action);
+ }
+ }
+
if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
foreach (QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
@@ -795,6 +832,76 @@ QMenu *MainWindow::createPopupMenu()
return menu;
}
+void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
+{
+ QMenu *menu = main_ui_->menuInterfaceToolbars;
+ bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc) strcmp) ? true : false;
+
+ QString title = QString().fromUtf8(toolbar_entry->menu_title);
+ QAction *action = new QAction(title, menu);
+ action->setEnabled(true);
+ action->setCheckable(true);
+ action->setChecked(visible);
+ action->setToolTip(tr("Show or hide the toolbar"));
+
+ QAction *before = NULL;
+ foreach (QAction *action, menu->actions()) {
+ // Ensure we add the menu entries in sorted order
+ if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
+ before = action;
+ break;
+ }
+ }
+ menu->insertAction(before, action);
+
+ InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
+
+ QToolBar *toolbar = new QToolBar(this);
+ toolbar->addWidget(interface_toolbar);
+ toolbar->setMovable(false);
+ toolbar->setVisible(visible);
+
+ action->setData(qVariantFromValue(toolbar));
+
+ addToolBar(Qt::TopToolBarArea, toolbar);
+ insertToolBarBreak(toolbar);
+
+ if (show_hide_actions_) {
+ show_hide_actions_->addAction(action);
+ }
+
+ menu->menuAction()->setVisible(true);
+}
+
+void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
+{
+ QMenu *menu = main_ui_->menuInterfaceToolbars;
+ QAction *action = NULL;
+ QMap<QAction *, QWidget *>::iterator i;
+
+ QString title = QString().fromUtf8(menu_title);
+ foreach (action, menu->actions()) {
+ if (title.compare(action->text()) == 0) {
+ break;
+ }
+ }
+
+ if (action) {
+ if (show_hide_actions_) {
+ show_hide_actions_->removeAction(action);
+ }
+ menu->removeAction(action);
+
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ removeToolBar(toolbar);
+
+ delete action;
+ delete toolbar;
+ }
+
+ menu->menuAction()->setVisible(!menu->actions().isEmpty());
+}
+
void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
{
pipe_source_ = source;
@@ -1895,6 +2002,9 @@ void MainWindow::initShowHideMainWidgets()
showHideMainWidgets(shmwa);
}
+ // Initial hide the Interface Toolbar submenu
+ main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
+
/* Initially hide the additional toolbars menus */
main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
@@ -2356,7 +2466,7 @@ void MainWindow::resizeEvent(QResizeEvent *event)
}
/* Update main window items based on whether there's a capture in progress. */
-void MainWindow::setForCaptureInProgress(bool capture_in_progress)
+void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *ifaces)
{
setMenusForCaptureInProgress(capture_in_progress);
@@ -2368,6 +2478,20 @@ void MainWindow::setForCaptureInProgress(bool capture_in_progress)
// set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
#endif
+
+#ifdef HAVE_EXTCAP
+ 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);
+ }
+ } else {
+ toolbar->stopCapture();
+ }
+ }
+#endif
}
static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index aeb3a18052..23e1de7cce 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -31,6 +31,7 @@
#include "file.h"
#include "ui/ui_util.h"
+#include "ui/iface_toolbar.h"
#include <epan/prefs.h>
#include <epan/plugin_if.h>
@@ -100,6 +101,9 @@ public:
void removeAdditionalToolbar(QString toolbarName);
+ void addInterfaceToolbar(const iface_toolbar *toolbar_entry);
+ void removeInterfaceToolbar(const gchar *menu_title);
+
protected:
virtual bool eventFilter(QObject *obj, QEvent *event);
virtual void keyPressEvent(QKeyEvent *event);
@@ -224,7 +228,7 @@ private:
void externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth);
- void setForCaptureInProgress(bool capture_in_progress = false);
+ void setForCaptureInProgress(bool capture_in_progress = false, GArray *ifaces = NULL);
QMenu* findOrAddMenu(QMenu *parent_menu, QString& menu_text);
void recursiveCopyProtoTreeItems(QTreeWidgetItem *item, QString &clip, int ident_level);
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 39bfc55cb1..8ee6ec69c9 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -268,6 +268,11 @@
<property name="title">
<string>&amp;View</string>
</property>
+ <widget class="QMenu" name="menuInterfaceToolbars">
+ <property name="title">
+ <string>Interface Toolbars</string>
+ </property>
+ </widget>
<widget class="QMenu" name="menuZoom">
<property name="title">
<string>&amp;Zoom</string>
@@ -345,6 +350,7 @@
<addaction name="actionViewMainToolbar"/>
<addaction name="actionViewFilterToolbar"/>
<addaction name="actionViewWirelessToolbar"/>
+ <addaction name="menuInterfaceToolbars"/>
<addaction name="menuAdditionalToolbars" />
<addaction name="actionViewStatusBar"/>
<addaction name="separator"/>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 4103da00b8..33eaa56318 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -119,6 +119,7 @@
#include "funnel_statistics.h"
#include "gsm_map_summary_dialog.h"
#include "iax2_analysis_dialog.h"
+#include "interface_toolbar.h"
#include "io_graph_dialog.h"
#include <additional_toolbar.h>
#include "lbm_stream_dialog.h"
@@ -486,6 +487,15 @@ void MainWindow::layoutToolbars()
main_ui_->wirelessToolBar->setVisible(recent.wireless_toolbar_show);
main_ui_->statusBar->setVisible(recent.statusbar_show);
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp)) {
+ toolbar->setVisible(true);
+ } else {
+ toolbar->setVisible(false);
+ }
+ }
+
QList<QToolBar *> toolbars = findChildren<QToolBar *>();
foreach (QToolBar *bar, toolbars) {
AdditionalToolBar *iftoolbar = dynamic_cast<AdditionalToolBar *>(bar);
@@ -495,6 +505,7 @@ void MainWindow::layoutToolbars()
visible = true;
iftoolbar->setVisible(visible);
+
}
}
}
@@ -523,6 +534,14 @@ void MainWindow::updateRecentActions()
main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show && prefs_has_layout_pane_content(layout_pane_content_pdetails));
main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show && prefs_has_layout_pane_content(layout_pane_content_pbytes));
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp)) {
+ action->setChecked(true);
+ } else {
+ action->setChecked(false);
+ }
+ }
+
foreach (QAction * action, main_ui_->menuAdditionalToolbars->actions()) {
ext_toolbar_t * toolbar = VariantPointer<ext_toolbar_t>::asPtr(action->data());
bool checked = false;
@@ -628,7 +647,7 @@ void MainWindow::queuedFilterAction(QString action_filter, FilterAction::Action
// Capture callbacks
-void MainWindow::captureCapturePrepared(capture_session *) {
+void MainWindow::captureCapturePrepared(capture_session *session) {
#ifdef HAVE_LIBPCAP
setTitlebarForCaptureInProgress();
@@ -636,7 +655,7 @@ void MainWindow::captureCapturePrepared(capture_session *) {
/* Disable menu items that make no sense if you're currently running
a capture. */
- setForCaptureInProgress(true);
+ setForCaptureInProgress(true, session->capture_opts->ifaces);
// set_capture_if_dialog_for_capture_in_progress(TRUE);
// /* Don't set up main window for a capture file. */
@@ -645,14 +664,14 @@ void MainWindow::captureCapturePrepared(capture_session *) {
#endif // HAVE_LIBPCAP
}
-void MainWindow::captureCaptureUpdateStarted(capture_session *) {
+void MainWindow::captureCaptureUpdateStarted(capture_session *session) {
#ifdef HAVE_LIBPCAP
/* We've done this in "prepared" above, but it will be cleared while
switching to the next multiple file. */
setTitlebarForCaptureInProgress();
- setForCaptureInProgress(true);
+ setForCaptureInProgress(true, session->capture_opts->ifaces);
setForCapturedPackets(true);
#endif // HAVE_LIBPCAP
@@ -2257,6 +2276,19 @@ void MainWindow::showHideMainWidgets(QAction *action)
recent.byte_view_show = show;
main_ui_->actionViewPacketBytes->setChecked(show);
} else {
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ if (widget == toolbar) {
+ GList *entry = g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp);
+ if (show && !entry) {
+ recent.interface_toolbars = g_list_append(recent.interface_toolbars, g_strdup(action->text().toUtf8()));
+ } else if (!show && entry) {
+ recent.interface_toolbars = g_list_remove(recent.interface_toolbars, entry->data);
+ }
+ action->setChecked(show);
+ }
+ }
+
ext_toolbar_t * toolbar = VariantPointer<ext_toolbar_t>::asPtr(action->data());
if (toolbar) {
GList *entry = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc) strcmp);
diff --git a/ui/recent.c b/ui/recent.c
index 30c44f485a..898296c1c0 100644
--- a/ui/recent.c
+++ b/ui/recent.c
@@ -75,6 +75,7 @@
#define RECENT_GUI_RLC_PDUS_FROM_MAC_FRAMES "gui.rlc_pdus_from_mac_frames"
#define RECENT_GUI_CUSTOM_COLORS "gui.custom_colors"
#define RECENT_GUI_TOOLBAR_SHOW "gui.additional_toolbar_show"
+#define RECENT_GUI_INTERFACE_TOOLBAR_SHOW "gui.interface_toolbar_show"
#define RECENT_GUI_GEOMETRY "gui.geom."
@@ -866,6 +867,12 @@ write_profile_recent(void)
fprintf(rf, RECENT_GUI_TOOLBAR_SHOW ": %s\n", string_list);
g_free(string_list);
+ fprintf(rf, "\n# Interface Toolbars show.\n");
+ fprintf(rf, "# List of interface toolbars to show.\n");
+ string_list = join_string_list(recent.interface_toolbars);
+ fprintf(rf, RECENT_GUI_INTERFACE_TOOLBAR_SHOW ": %s\n", string_list);
+ g_free(string_list);
+
fclose(rf);
/* XXX - catch I/O errors (e.g. "ran out of disk space") and return
@@ -1121,6 +1128,8 @@ read_set_recent_pair_static(gchar *key, const gchar *value,
recent.gui_fileopen_remembered_dir = g_strdup(value);
} else if (strcmp(key, RECENT_GUI_TOOLBAR_SHOW) == 0) {
recent.gui_additional_toolbars = prefs_get_string_list(value);
+ } else if (strcmp(key, RECENT_GUI_INTERFACE_TOOLBAR_SHOW) == 0) {
+ recent.interface_toolbars = prefs_get_string_list(value);
}
return PREFS_SET_OK;
@@ -1294,6 +1303,11 @@ recent_read_profile_static(char **rf_path_return, int *rf_errno_return)
recent.gui_additional_toolbars = NULL;
}
+ if (recent.interface_toolbars) {
+ g_list_free_full (recent.interface_toolbars, g_free);
+ recent.interface_toolbars = NULL;
+ }
+
/* Construct the pathname of the user's profile recent file. */
rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE);
diff --git a/ui/recent.h b/ui/recent.h
index 2f33302b9b..d41dd60d3d 100644
--- a/ui/recent.h
+++ b/ui/recent.h
@@ -111,6 +111,7 @@ typedef struct recent_settings_tag {
gboolean gui_rlc_use_pdus_from_mac;
GList *custom_colors;
GList *gui_additional_toolbars;
+ GList *interface_toolbars;
} recent_settings_t;
/** Global recent settings. */