Add a Qt I/O Graph dialog.

For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing

You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.

The time scale (X axis) can be toggled between relative seconds and the
time of day.

Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.

To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.

A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.

Move common syntax checking to SyntaxLineEdit.

Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.

Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.

Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2014-02-11 16:07:10 -08:00
parent cc3c05ed5f
commit a5cb72fe9e
30 changed files with 3590 additions and 433 deletions

View File

@ -28,6 +28,10 @@
#include "ws_symbol_export.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** Initialize all the memory allocation pools described below.
* This function must be called once when *shark initialize to set up the
* required structures.
@ -326,4 +330,8 @@ gboolean ep_verify_pointer(const void *ptr);
*/
gboolean se_verify_pointer(const void *ptr);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* emem.h */

View File

@ -762,7 +762,7 @@ extern void
proto_tree_set_fake_protocols(proto_tree *tree, gboolean fake_protocols);
/** Mark a field/protocol ID as "interesting".
@param tree the tree to be set
@param tree the tree to be set (currently ignored)
@param hfid the interesting field id
@todo what *does* interesting mean? */
extern void

5
file.c
View File

@ -2382,6 +2382,11 @@ cf_retap_packets(capture_file *cf)
guint tap_flags;
psp_return_t ret;
/* Presumably the user closed the capture file. */
if (cf == NULL) {
return CF_READ_ABORTED;
}
/* Do we have any tap listeners with filters? */
filtering_tap_listeners = have_filtering_tap_listeners();

View File

@ -31,6 +31,7 @@ set(COMMON_UI_SRC
help_url.c
packet_list_utils.c
iface_lists.c
io_graph_item.c
persfilepath_opt.c
preference_utils.c
profile.c

View File

@ -50,6 +50,7 @@ WIRESHARK_UI_SRC = \
export_object_smb.c \
follow.c \
iface_lists.c \
io_graph_item.c \
help_url.c \
packet_list_utils.c \
persfilepath_opt.c \
@ -79,6 +80,7 @@ noinst_HEADERS = \
help_url.h \
packet_list_utils.h \
iface_lists.h \
io_graph_item.h \
main_statusbar.h \
persfilepath_opt.h \
preference_utils.h \

View File

@ -35,8 +35,9 @@
#include <epan/tap.h>
#include <epan/strutil.h>
#include "../stat_menu.h"
#include "../../stat_menu.h"
#include "ui/alert_box.h"
#include "ui/io_graph_item.h"
#include "ui/simple_dialog.h"
#include "ui/gtk/gtkglobals.h"
@ -95,6 +96,11 @@ static const char *plot_style_name[MAX_PLOT_STYLES] = {
"Dot",
};
/*
* XXX - "Count types" and "calc types" are combined in io_graph_item_unit_t
* in io_graph_item_t. The Qt port treats these as a single Y Axis "value unit"
* type. Should we do the same here?
*/
#define DEFAULT_COUNT_TYPE 0
#define COUNT_TYPE_FRAMES 0
#define COUNT_TYPE_BYTES 1
@ -130,36 +136,20 @@ static const char *calc_type_names[MAX_CALC_TYPES] = {
"AVG(*)",
"LOAD(*)"};
#define CALC_TYPE_TO_ITEM_UNIT(ct) ((io_graph_item_unit_t)(ct + IOG_ITEM_UNIT_CALC_SUM))
/* Unused? */
#if 0
typedef struct _io_stat_calc_type_t {
struct _io_stat_graph_t *gio;
int calc_type;
} io_stat_calc_type_t;
#endif
#define NUM_IO_ITEMS 100000
typedef struct _io_item_t {
guint32 frames; /* always calculated, will hold number of frames*/
guint64 bytes; /* always calculated, will hold number of bytes*/
guint64 fields;
gint64 int_max;
gint64 int_min;
gint64 int_tot;
gfloat float_max;
gfloat float_min;
gfloat float_tot;
gdouble double_max;
gdouble double_min;
gdouble double_tot;
nstime_t time_max;
nstime_t time_min;
nstime_t time_tot;
guint32 first_frame_in_invl;
guint32 last_frame_in_invl;
} io_item_t;
typedef struct _io_stat_graph_t {
struct _io_stat_t *io;
gpointer items[NUM_IO_ITEMS];
io_graph_item_t items[NUM_IO_ITEMS];
int plot_style;
gboolean display;
GtkWidget *display_button;
@ -226,32 +216,11 @@ io_stat_set_title(io_stat_t *io)
static void
io_stat_reset(io_stat_t *io)
{
int i, j;
int i;
io->needs_redraw = TRUE;
for (i=0; i<MAX_GRAPHS; i++) {
for (j=0; j<NUM_IO_ITEMS; j++) {
io_item_t *ioi;
ioi = (io_item_t *)io->graphs[i].items[j];
ioi->frames = 0;
ioi->bytes = 0;
ioi->fields = 0;
ioi->int_max = 0;
ioi->int_min = 0;
ioi->int_tot = 0;
ioi->float_max = 0;
ioi->float_min = 0;
ioi->float_tot = 0;
ioi->double_max = 0;
ioi->double_min = 0;
ioi->double_tot = 0;
nstime_set_zero(&ioi->time_max);
nstime_set_zero(&ioi->time_min);
nstime_set_zero(&ioi->time_tot);
ioi->first_frame_in_invl = 0;
ioi->last_frame_in_invl = 0;
}
reset_io_graph_items((io_graph_item_t *)io->graphs[i].items, NUM_IO_ITEMS);
}
io->last_interval = 0xffffffff;
io->max_interval = 0;
@ -274,8 +243,7 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
{
io_stat_graph_t *graph = (io_stat_graph_t *)g;
io_stat_t *io;
io_item_t *it;
nstime_t time_delta;
epan_dissect_t *adv_edt = NULL;
int idx;
/* we sometimes get called when the graph is disabled.
@ -287,19 +255,8 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
io = graph->io; /* Point up to the parent io_stat_t struct */
io->needs_redraw = TRUE;
/*
* Find in which interval this is supposed to go and store the interval index as idx
*/
time_delta = pinfo->rel_ts;
if (time_delta.nsecs<0) {
time_delta.secs--;
time_delta.nsecs += 1000000000;
}
if (time_delta.secs<0) {
return FALSE;
}
idx = (int) ((time_delta.secs*1000 + time_delta.nsecs/1000000) / io->interval);
idx = get_io_graph_index(pinfo, io->interval);
/* some sanity checks */
if ((idx < 0) || (idx >= NUM_IO_ITEMS)) {
io->num_items = NUM_IO_ITEMS-1;
@ -316,184 +273,14 @@ tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *
nstime_delta(&io->start_time, &pinfo->fd->abs_ts, &pinfo->rel_ts);
}
/* Point to the appropriate io_item_t struct */
it = (io_item_t *)graph->items[idx];
/* Set the first and last frame num in current interval matching the target field+filter */
if (it->first_frame_in_invl == 0) {
it->first_frame_in_invl = pinfo->fd->num;
}
it->last_frame_in_invl = pinfo->fd->num;
/*
* For ADVANCED mode we need to keep track of some more stuff than just frame and byte counts */
/* For ADVANCED mode we need to keep track of some more stuff than just frame and byte counts */
if (io->count_type == COUNT_TYPE_ADVANCED) {
GPtrArray *gp;
guint i;
gp = proto_get_finfo_ptr_array(edt->tree, graph->hf_index);
if (!gp) {
return FALSE;
}
/* Update the appropriate counters. If fields == 0, this is the first seen
* value so set any min/max values accordingly. */
for (i=0; i<gp->len; i++) {
int new_int;
gint64 new_int64;
float new_float;
double new_double;
nstime_t *new_time;
switch (proto_registrar_get_ftype(graph->hf_index)) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
new_int = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
if ((new_int > it->int_max) || (it->fields == 0)) {
it->int_max = new_int;
}
if ((new_int < it->int_min) || (it->fields == 0)) {
it->int_min = new_int;
}
it->int_tot += new_int;
it->fields++;
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
new_int = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
if ((new_int > it->int_max) || (it->fields == 0)) {
it->int_max = new_int;
}
if ((new_int < it->int_min) || (it->fields == 0)) {
it->int_min = new_int;
}
it->int_tot += new_int;
it->fields++;
break;
case FT_UINT64:
case FT_INT64:
new_int64 = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
if ((new_int64 > it->int_max) || (it->fields == 0)) {
it->int_max = new_int64;
}
if ((new_int64 < it->int_min) || (it->fields == 0)) {
it->int_min = new_int64;
}
it->int_tot += new_int64;
it->fields++;
break;
case FT_FLOAT:
new_float = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
if ((new_float > it->float_max) || (it->fields == 0)) {
it->float_max = new_float;
}
if ((new_float < it->float_min) || (it->fields == 0)) {
it->float_min = new_float;
}
it->float_tot += new_float;
it->fields++;
break;
case FT_DOUBLE:
new_double = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
if ((new_double > it->double_max) || (it->fields == 0)) {
it->double_max = new_double;
}
if ((new_double < it->double_min) || (it->fields == 0)) {
it->double_min = new_double;
}
it->double_tot += new_double;
it->fields++;
break;
case FT_RELATIVE_TIME:
new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
switch (graph->calc_type) {
guint64 t, pt; /* time in us */
int j;
case CALC_TYPE_LOAD:
/*
* Add the time this call spanned each interval according to its contribution
* to that interval.
*/
t = new_time->secs;
t = t * 1000000 + new_time->nsecs / 1000;
j = idx;
/*
* Handle current interval */
pt = pinfo->rel_ts.secs * 1000000 + pinfo->rel_ts.nsecs / 1000;
pt = pt % (io->interval * 1000);
if (pt > t) {
pt = t;
}
while (t) {
io_item_t *item;
item = (io_item_t*)graph->items[j];
item->time_tot.nsecs += (int) (pt * 1000);
if (item->time_tot.nsecs > 1000000000) {
item->time_tot.secs++;
item->time_tot.nsecs -= 1000000000;
}
if (j == 0) {
break;
}
j--;
t -= pt;
if (t > (guint64) io->interval * 1000) {
pt = (guint64) io->interval * 1000;
} else {
pt = t;
}
}
break;
default:
if ( (new_time->secs > it->time_max.secs)
|| ( (new_time->secs == it->time_max.secs)
&& (new_time->nsecs > it->time_max.nsecs))
|| (it->fields == 0)) {
it->time_max = *new_time;
}
if ( (new_time->secs<it->time_min.secs)
|| ( (new_time->secs == it->time_min.secs)
&& (new_time->nsecs < it->time_min.nsecs))
|| (it->fields == 0)) {
it->time_min = *new_time;
}
nstime_add(&it->time_tot, new_time);
it->fields++;
}
break;
default:
if ((graph->calc_type == CALC_TYPE_COUNT_FRAMES) ||
(graph->calc_type == CALC_TYPE_COUNT_FIELDS)) {
/*
* It's not an integeresque type, but
* all we want to do is count it, so
* that's all right.
*/
it->fields++;
}
else {
/*
* "Can't happen"; see the "check that the
* type is compatible" check in
* filter_callback().
*/
g_assert_not_reached();
}
break;
}
}
adv_edt = edt;
}
it->frames++;
it->bytes += pinfo->fd->pkt_len;
if (!update_io_graph_item((io_graph_item_t*) graph->items, idx, pinfo, adv_edt, graph->hf_index, CALC_TYPE_TO_ITEM_UNIT(graph->calc_type), io->interval)) {
return FALSE;
}
return TRUE;
}
@ -503,13 +290,13 @@ get_it_value(io_stat_t *io, int graph, int idx)
{
guint64 value = 0; /* FIXME: loss of precision, visible on the graph for small values */
int adv_type;
io_item_t *it;
io_graph_item_t *it;
guint32 interval;
g_assert(graph < MAX_GRAPHS);
g_assert(idx < NUM_IO_ITEMS);
it = (io_item_t *)io->graphs[graph].items[idx];
it = &io->graphs[graph].items[idx];
switch (io->count_type) {
case COUNT_TYPE_FRAMES:
@ -533,7 +320,6 @@ get_it_value(io_stat_t *io, int graph, int idx)
break;
}
adv_type = proto_registrar_get_ftype(io->graphs[graph].hf_index);
switch (adv_type) {
case FT_UINT8:
@ -1374,7 +1160,7 @@ static void
iostat_init(const char *opt_arg _U_, void* userdata _U_)
{
io_stat_t *io;
int i = 0, j = 0;
int i = 0;
static GdkColor col[MAX_GRAPHS] = {
{0, 0x0000, 0x0000, 0x0000}, /* Black */
{0, 0xffff, 0x0000, 0x0000}, /* Red */
@ -1442,9 +1228,6 @@ iostat_init(const char *opt_arg _U_, void* userdata _U_)
io->graphs[i].filter_bt = NULL;
for (j=0; j<NUM_IO_ITEMS; j++) {
io->graphs[i].items[j] = g_new(io_item_t,1);
}
io->graphs[i].follow_smooth = GRAPH_FOLLOWFILTER;
}
io_stat_reset(io);
@ -1477,7 +1260,7 @@ static void
draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
{
io_stat_t *io = (io_stat_t *)user_data;
int i,j;
int i;
GtkWidget *save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(io->window), "save_bt");
surface_info_t *surface_info = (surface_info_t *)g_object_get_data(G_OBJECT(save_bt), "surface-info");
@ -1492,10 +1275,6 @@ draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
g_free(io->graphs[i].args);
io->graphs[i].args = NULL;
for (j=0; j<NUM_IO_ITEMS; j++) {
g_free(io->graphs[i].items[j]);
io->graphs[i].items[j] = NULL;
}
}
}
g_free(io);
@ -1508,7 +1287,7 @@ pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer g)
{
io_stat_t *io = (io_stat_t *)g;
io_stat_graph_t *graph;
io_item_t *it;
io_graph_item_t *it;
guint32 draw_width, interval, last_interval;
guint32 frame_num = 0;
int i;
@ -1555,7 +1334,7 @@ pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer g)
for (i=0; i<MAX_GRAPHS; i++) {
graph = &io->graphs[i];
if (graph->display) {
it = (io_item_t *)graph->items[interval];
it = &graph->items[interval];
if (event->button == 1) {
if ((frame_num == 0) || (it->first_frame_in_invl < frame_num))
frame_num = it->first_frame_in_invl;
@ -2023,9 +1802,8 @@ filter_callback(GtkWidget *widget, gpointer user_data)
{
io_stat_graph_t *gio = (io_stat_graph_t *)user_data;
const char *filter;
const char *field = NULL;
header_field_info *hfi;
dfilter_t *dfilter;
const char *field_name = NULL;
/* this graph is not active, just update display and redraw */
if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))) {
@ -2036,85 +1814,22 @@ filter_callback(GtkWidget *widget, gpointer user_data)
/* first check if the field string is valid */
if (gio->io->count_type == COUNT_TYPE_ADVANCED) {
field = gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
GString *err_str;
field_name = gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
/* warn and bail out if there was no field specified */
if ((field == NULL) || (field[0] == 0)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
err_str = check_field_unit(field_name, &gio->hf_index, CALC_TYPE_TO_ITEM_UNIT(gio->calc_type));
if (err_str) {
/* warn and bail out */
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str->str);
g_string_free(err_str, TRUE);
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
/* warn and bail out if the field could not be found */
hfi = proto_registrar_get_byname(field);
if (hfi == NULL) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
gio->hf_index = hfi->id;
/* check that the type is compatible */
switch (hfi->type) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_INT64:
case FT_FLOAT:
case FT_DOUBLE:
/* these values support all calculations except LOAD */
switch (gio->calc_type) {
case CALC_TYPE_LOAD:
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"LOAD(*) is only supported for relative-time fields.");
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
/* these types support all calculations */
break;
case FT_RELATIVE_TIME:
/* this type only supports COUNT, MAX, MIN, AVG */
switch (gio->calc_type) {
case CALC_TYPE_SUM:
case CALC_TYPE_COUNT_FRAMES:
case CALC_TYPE_COUNT_FIELDS:
case CALC_TYPE_MAX:
case CALC_TYPE_MIN:
case CALC_TYPE_AVG:
case CALC_TYPE_LOAD:
break;
default:
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%s is a relative-time field, so %s calculations are not supported on it.",
field,
calc_type_names[gio->calc_type]);
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
break;
default:
if ((gio->calc_type != CALC_TYPE_COUNT_FRAMES) &&
(gio->calc_type != CALC_TYPE_COUNT_FIELDS)) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"%s doesn't have integral or float values, so %s calculations are not supported on it.",
field,
calc_type_names[gio->calc_type]);
disable_graph(gio);
io_stat_redraw(gio->io);
return;
}
break;
}
}
/* first check if the filter string is valid. */
filter = gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
if (!dfilter_compile(filter, &dfilter)) {
@ -2134,7 +1849,7 @@ filter_callback(GtkWidget *widget, gpointer user_data)
remove_tap_listener(gio);
io_stat_reset(gio->io);
enable_graph(gio, filter, field);
enable_graph(gio, filter, field_name);
cf_retap_packets(&cfile);
gdk_window_raise(gtk_widget_get_window(gio->io->window));
io_stat_redraw(gio->io);

152
ui/io_graph_item.c Normal file
View File

@ -0,0 +1,152 @@
/* io_graph_item.h
* Definitions and functions for IO graph items
*
* Copied from gtk/io_stat.c, (c) 2002 Ronnie Sahlberg
*
* 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 <epan/epan_dissect.h>
#include <epan/packet_info.h>
#include "ui/io_graph_item.h"
int get_io_graph_index(packet_info *pinfo, int interval) {
nstime_t time_delta;
/*
* Find in which interval this is supposed to go and store the interval index as idx
*/
time_delta = pinfo->rel_ts;
if (time_delta.nsecs<0) {
time_delta.secs--;
time_delta.nsecs += 1000000000;
}
if (time_delta.secs<0) {
return -1;
}
return (int) ((time_delta.secs*1000 + time_delta.nsecs/1000000) / interval);
}
GString *check_field_unit(const char *field_name, int *hf_index, io_graph_item_unit_t item_unit)
{
GString *err_str = NULL;
if (item_unit >= IOG_ITEM_UNIT_CALC_SUM) {
header_field_info *hfi;
const char *item_unit_names[NUM_IOG_ITEM_UNITS] = {
"Packets",
"Bytes",
"Bits",
"SUM",
"COUNT FRAMES",
"COUNT FIELDS",
"MAX",
"MIN",
"AVG",
"LOAD"
};
/* There was no field specified */
if ((field_name == NULL) || (field_name[0] == 0)) {
err_str = g_string_new("You didn't specify a field name.");
return err_str;
}
/* The field could not be found */
hfi = proto_registrar_get_byname(field_name);
if (hfi == NULL) {
err_str = g_string_new("");
g_string_printf(err_str, "There is no field named '%s'.", field_name);
return err_str;
}
if (hf_index) *hf_index = hfi->id;
/* Check that the type is compatible */
switch (hfi->type) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_INT64:
case FT_FLOAT:
case FT_DOUBLE:
/* These values support all calculations except LOAD */
switch (item_unit) {
case IOG_ITEM_UNIT_CALC_LOAD:
err_str = g_string_new("LOAD is only supported for relative-time fields.");
default:
break;
}
/* These types support all calculations */
break;
case FT_RELATIVE_TIME:
/* This type only supports COUNT, MAX, MIN, AVG */
switch (item_unit) {
case IOG_ITEM_UNIT_CALC_SUM:
case IOG_ITEM_UNIT_CALC_FRAMES:
case IOG_ITEM_UNIT_CALC_FIELDS:
case IOG_ITEM_UNIT_CALC_MAX:
case IOG_ITEM_UNIT_CALC_MIN:
case IOG_ITEM_UNIT_CALC_AVERAGE:
case IOG_ITEM_UNIT_CALC_LOAD:
break;
default:
err_str = g_string_new("");
g_string_printf(err_str, "\"%s\" is a relative-time field. %s calculations are not supported on it.",
field_name,
item_unit_names[item_unit]);
}
break;
default:
if ((item_unit != IOG_ITEM_UNIT_CALC_FRAMES) &&
(item_unit != IOG_ITEM_UNIT_CALC_FIELDS)) {
err_str = g_string_new("");
g_string_printf(err_str, "\"%s\" doesn't have integral or float values. %s calculations are not supported on it.",
field_name,
item_unit_names[item_unit]);
}
break;
}
}
return err_str;
}
/*
* 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:
*/

338
ui/io_graph_item.h Normal file
View File

@ -0,0 +1,338 @@
/* io_graph_item.h
* Definitions and functions for IO graph items
*
* Copied from gtk/io_stat.c, (c) 2002 Ronnie Sahlberg
*
* 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 __IO_GRAPH_ITEM_H__
#define __IO_GRAPH_ITEM_H__
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef enum {
IOG_ITEM_UNIT_FIRST,
IOG_ITEM_UNIT_PACKETS = IOG_ITEM_UNIT_FIRST,
IOG_ITEM_UNIT_BYTES,
IOG_ITEM_UNIT_BITS,
IOG_ITEM_UNIT_CALC_SUM,
IOG_ITEM_UNIT_CALC_FRAMES,
IOG_ITEM_UNIT_CALC_FIELDS,
IOG_ITEM_UNIT_CALC_MAX,
IOG_ITEM_UNIT_CALC_MIN,
IOG_ITEM_UNIT_CALC_AVERAGE,
IOG_ITEM_UNIT_CALC_LOAD,
IOG_ITEM_UNIT_LAST = IOG_ITEM_UNIT_CALC_LOAD,
NUM_IOG_ITEM_UNITS
} io_graph_item_unit_t;
typedef struct _io_graph_item_t {
guint32 frames; /* always calculated, will hold number of frames*/
guint64 bytes; /* always calculated, will hold number of bytes*/
guint64 fields;
gint64 int_max;
gint64 int_min;
gint64 int_tot;
/* XXX - Why do we always use 64-bit ints but split floats between
* gfloat and gdouble?
*/
gfloat float_max;
gfloat float_min;
gfloat float_tot;
gdouble double_max;
gdouble double_min;
gdouble double_tot;
nstime_t time_max;
nstime_t time_min;
nstime_t time_tot;
guint32 first_frame_in_invl;
guint32 last_frame_in_invl;
} io_graph_item_t;
/** Reset (zero) an io_graph_item_t.
*
* @param items [in,out] Array containing the items to reset.
* @param count [in] The number of items in the array.
*/
static inline void
reset_io_graph_items(io_graph_item_t *items, int count) {
io_graph_item_t *item;
int i;
for (i = 0; i < count; i++) {
item = &items[i];
item->frames = 0;
item->bytes = 0;
item->fields = 0;
item->int_max = 0;
item->int_min = 0;
item->int_tot = 0;
item->float_max = 0;
item->float_min = 0;
item->float_tot = 0;
item->double_max = 0;
item->double_min = 0;
item->double_tot = 0;
nstime_set_zero(&item->time_max);
nstime_set_zero(&item->time_min);
nstime_set_zero(&item->time_tot);
item->first_frame_in_invl = 0;
item->last_frame_in_invl = 0;
}
}
/** Get the interval (array index) for a packet
*
* It is up to the caller to determine if the return value is valid.
*
* @param [in] pinfo Packet of interest.
* @param [in] interval Time interval in milliseconds.
* @return Array index on success, -1 on failure.
*/
int get_io_graph_index(packet_info *pinfo, int interval);
/** Check field and item unit compatibility
*
* @param field_name [in] Header field name to check
* @param hf_index [out] Assigned the header field index corresponding to field_name if valid.
* Can be NULL.
* @param item_unit [in] The type of unit to calculate. From IOG_ITEM_UNITS.
* @return NULL if compatible, otherwise an error string. The string must
* be freed by the caller.
*/
GString *check_field_unit(const char *field_name, int *hf_index, io_graph_item_unit_t item_unit);
/** Update the values of an io_graph_item_t.
*
* Frame and byte counts are always calculated. If edt is non-NULL advanced
* statistics are calculated using hfindex.
*
* @param items [in,out] Array containing the item to update.
* @param idx [in] Index of the item to update.
* @param pinfo [in] Packet containing update information.
* @param edt [in] Dissection information for advanced statistics. May be NULL.
* @param hf_index [in] Header field index for advanced statistics.
* @param item_unit [in] The type of unit to calculate. From IOG_ITEM_UNITS.
* @param interval [in] Timing interval in ms.
* @return TRUE if the update was successful, otherwise FALSE.
*/
static inline gboolean
update_io_graph_item(io_graph_item_t *items, int idx, packet_info *pinfo, epan_dissect_t *edt, int hf_index, int item_unit, guint32 interval) {
io_graph_item_t *item = &items[idx];
/* Set the first and last frame num in current interval matching the target field+filter */
if (item->first_frame_in_invl == 0) {
item->first_frame_in_invl = pinfo->fd->num;
}
item->last_frame_in_invl = pinfo->fd->num;
if (edt && hf_index >= 0) {
GPtrArray *gp;
guint i;
gp = proto_get_finfo_ptr_array(edt->tree, hf_index);
if (!gp) {
return FALSE;
}
/* Update the appropriate counters. If fields == 0, this is the first seen
* value so set any min/max values accordingly. */
for (i=0; i < gp->len; i++) {
int new_int;
gint64 new_int64;
float new_float;
double new_double;
nstime_t *new_time;
switch (proto_registrar_get_ftype(hf_index)) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
new_int = fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
if ((new_int > item->int_max) || (item->fields == 0)) {
item->int_max = new_int;
}
if ((new_int < item->int_min) || (item->fields == 0)) {
item->int_min = new_int;
}
item->int_tot += new_int;
item->fields++;
break;
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
new_int = fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
if ((new_int > item->int_max) || (item->fields == 0)) {
item->int_max = new_int;
}
if ((new_int < item->int_min) || (item->fields == 0)) {
item->int_min = new_int;
}
item->int_tot += new_int;
item->fields++;
break;
case FT_UINT64:
case FT_INT64:
new_int64 = fvalue_get_integer64(&((field_info *)gp->pdata[i])->value);
if ((new_int64 > item->int_max) || (item->fields == 0)) {
item->int_max = new_int64;
}
if ((new_int64 < item->int_min) || (item->fields == 0)) {
item->int_min = new_int64;
}
item->int_tot += new_int64;
item->fields++;
break;
case FT_FLOAT:
new_float = (gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
if ((new_float > item->float_max) || (item->fields == 0)) {
item->float_max = new_float;
}
if ((new_float < item->float_min) || (item->fields == 0)) {
item->float_min = new_float;
}
item->float_tot += new_float;
item->fields++;
break;
case FT_DOUBLE:
new_double = fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
if ((new_double > item->double_max) || (item->fields == 0)) {
item->double_max = new_double;
}
if ((new_double < item->double_min) || (item->fields == 0)) {
item->double_min = new_double;
}
item->double_tot += new_double;
item->fields++;
break;
case FT_RELATIVE_TIME:
new_time = (nstime_t *)fvalue_get(&((field_info *)gp->pdata[i])->value);
switch (item_unit) {
guint64 t, pt; /* time in us */
int j;
case IOG_ITEM_UNIT_CALC_LOAD:
/*
* Add the time this call spanned each interval according to its contribution
* to that interval.
*/
t = new_time->secs;
t = t * 1000000 + new_time->nsecs / 1000;
j = idx;
/*
* Handle current interval */
pt = pinfo->rel_ts.secs * 1000000 + pinfo->rel_ts.nsecs / 1000;
pt = pt % (interval * 1000);
if (pt > t) {
pt = t;
}
while (t) {
io_graph_item_t *load_item;
load_item = &items[j];
load_item->time_tot.nsecs += (int) (pt * 1000);
if (load_item->time_tot.nsecs > 1000000000) {
load_item->time_tot.secs++;
load_item->time_tot.nsecs -= 1000000000;
}
if (j == 0) {
break;
}
j--;
t -= pt;
if (t > (guint64) interval * 1000) {
pt = (guint64) interval * 1000;
} else {
pt = t;
}
}
break;
default:
if ( (new_time->secs > item->time_max.secs)
|| ( (new_time->secs == item->time_max.secs)
&& (new_time->nsecs > item->time_max.nsecs))
|| (item->fields == 0)) {
item->time_max = *new_time;
}
if ( (new_time->secs<item->time_min.secs)
|| ( (new_time->secs == item->time_min.secs)
&& (new_time->nsecs < item->time_min.nsecs))
|| (item->fields == 0)) {
item->time_min = *new_time;
}
nstime_add(&item->time_tot, new_time);
item->fields++;
}
break;
default:
if ((item_unit == IOG_ITEM_UNIT_CALC_FRAMES) ||
(item_unit == IOG_ITEM_UNIT_CALC_FIELDS)) {
/*
* It's not an integeresque type, but
* all we want to do is count it, so
* that's all right.
*/
item->fields++;
}
else {
/*
* "Can't happen"; see the "check that the
* type is compatible" check in
* filter_callback().
*/
g_assert_not_reached();
}
break;
}
}
}
item->frames++;
item->bytes += pinfo->fd->pkt_len;
return TRUE;
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __IO_GRAPH_ITEM_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:
*/

View File

@ -51,6 +51,7 @@ set(WIRESHARK_QT_HEADERS
font_color_preferences_frame.h
import_text_dialog.h
interface_tree.h
io_graph_dialog.h
label_stack.h
layout_preferences_frame.h
main_status_bar.h
@ -126,6 +127,7 @@ set(WIRESHARK_QT_SRC
font_color_preferences_frame.cpp
import_text_dialog.cpp
interface_tree.cpp
io_graph_dialog.cpp
label_stack.cpp
layout_preferences_frame.cpp
main.cpp
@ -195,6 +197,7 @@ set(WIRESHARK_QT_UI
follow_stream_dialog.ui
font_color_preferences_frame.ui
import_text_dialog.ui
io_graph_dialog.ui
layout_preferences_frame.ui
main_welcome.ui
main_window.ui

View File

@ -149,6 +149,8 @@ font_color_preferences_frame.cpp font_color_preferences_frame.h: ui_font_color_p
import_text_dialog.cpp import_text_dialog.h: ui_import_text_dialog.h
io_graph_dialog.cpp io_graph_dialog.h: ui_io_graph_dialog.h
layout_preferences_frame.cpp layout_preferences_frame.h: ui_layout_preferences_frame.h
main_welcome.cpp main_welcome.h: ui_main_welcome.h

View File

@ -41,6 +41,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_follow_stream_dialog.h \
ui_font_color_preferences_frame.h \
ui_import_text_dialog.h \
ui_io_graph_dialog.h \
ui_layout_preferences_frame.h \
ui_main_welcome.h \
ui_main_window.h \
@ -130,6 +131,7 @@ MOC_HDRS = \
font_color_preferences_frame.h \
import_text_dialog.h \
interface_tree.h \
io_graph_dialog.h \
label_stack.h \
layout_preferences_frame.h \
main_status_bar.h \
@ -187,6 +189,7 @@ UI_FILES = \
follow_stream_dialog.ui \
font_color_preferences_frame.ui \
import_text_dialog.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
main_welcome.ui \
main_window.ui \
@ -292,6 +295,7 @@ WIRESHARK_QT_SRC = \
font_color_preferences_frame.cpp \
import_text_dialog.cpp \
interface_tree.cpp \
io_graph_dialog.cpp \
label_stack.cpp \
layout_preferences_frame.cpp \
main.cpp \

View File

@ -231,6 +231,7 @@ FORMS += \
follow_stream_dialog.ui \
font_color_preferences_frame.ui \
import_text_dialog.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
main_welcome.ui \
main_window.ui \
@ -529,6 +530,7 @@ HEADERS += \
file_set_dialog.h \
import_text_dialog.h \
interface_tree.h \
io_graph_dialog.h \
label_stack.h \
main_status_bar.h \
main_welcome.h \
@ -583,6 +585,7 @@ SOURCES += \
font_color_preferences_frame.cpp \
import_text_dialog.cpp \
interface_tree.cpp \
io_graph_dialog.cpp \
label_stack.cpp \
layout_preferences_frame.cpp \
main.cpp \

View File

@ -259,7 +259,7 @@ void ColumnPreferencesFrame::on_columnTreeWidget_itemActivated(QTreeWidgetItem *
SyntaxLineEdit *syntax_edit = new SyntaxLineEdit();
saved_col_string_ = item->text(custom_field_col_);
connect(syntax_edit, SIGNAL(textChanged(QString)),
this, SLOT(customFieldTextChanged(QString)));
syntax_edit, SLOT(checkFieldName(QString)));
connect(syntax_edit, SIGNAL(editingFinished()), this, SLOT(customFieldEditingFinished()));
editor = cur_line_edit_ = syntax_edit;
@ -348,24 +348,6 @@ void ColumnPreferencesFrame::columnTypeCurrentIndexChanged(int index)
}
}
void ColumnPreferencesFrame::customFieldTextChanged(QString)
{
SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(cur_line_edit_);
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (!syntax_edit || !item) return;
dfilter_t *dfp = NULL;
const char *field_text = syntax_edit->text().toUtf8().constData();
if (strlen(field_text) < 1) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Empty);
} else if (!dfilter_compile(field_text, &dfp)) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Invalid);
} else {
syntax_edit->setSyntaxState(SyntaxLineEdit::Valid);
}
dfilter_free(dfp);
}
void ColumnPreferencesFrame::customFieldEditingFinished()
{
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();

View File

@ -62,7 +62,6 @@ private slots:
void comboDestroyed();
void columnTitleEditingFinished();
void columnTypeCurrentIndexChanged(int index);
void customFieldTextChanged(QString);
void customFieldEditingFinished();
void customOccurrenceTextChanged(QString);
void customOccurrenceEditingFinished();

View File

@ -23,7 +23,6 @@
#include <glib.h>
#include <epan/proto.h>
#include <epan/dfilter/dfilter.h>
#include "display_filter_edit.h"
@ -85,7 +84,6 @@ UIMiniCancelButton::UIMiniCancelButton(QWidget *pParent /* = 0 */)
DisplayFilterEdit::DisplayFilterEdit(QWidget *parent, bool plain) :
SyntaxLineEdit(parent),
plain_(plain),
field_name_only_(false),
apply_button_(NULL)
{
@ -266,43 +264,34 @@ void DisplayFilterEdit::resizeEvent(QResizeEvent *)
void DisplayFilterEdit::checkFilter(const QString& text)
{
dfilter_t *dfp;
guchar c;
clear_button_->setVisible(!text.isEmpty());
popFilterSyntaxStatus();
checkDisplayFilter(text);
if (field_name_only_ && (c = proto_check_field_name(text.toUtf8().constData()))) {
setSyntaxState(Invalid);
emit pushFilterSyntaxStatus(QString().sprintf("Illegal character in field name: '%c'", c));
} else if (dfilter_compile(text.toUtf8().constData(), &dfp)) {
GPtrArray *depr = NULL;
if (dfp != NULL) {
depr = dfilter_deprecated_tokens(dfp);
}
if (text.isEmpty()) {
setSyntaxState(Empty);
} else if (depr) {
/* You keep using that word. I do not think it means what you think it means. */
setSyntaxState(Deprecated);
/*
* We're being lazy and only printing the first "problem" token.
* Would it be better to print all of them?
*/
emit pushFilterSyntaxWarning(QString().sprintf("\"%s\" may have unexpected results (see the User's Guide)",
(const char *) g_ptr_array_index(depr, 0)));
} else {
setSyntaxState(Valid);
}
dfilter_free(dfp);
} else {
setSyntaxState(Invalid);
switch (syntaxState()) {
case Deprecated:
{
/*
* We're being lazy and only printing the first "problem" token.
* Would it be better to print all of them?
*/
QString deprecatedMsg(tr("\"%1\" may have unexpected results (see the User's Guide)")
.arg(deprecatedToken()));
emit pushFilterSyntaxWarning(deprecatedMsg);
break;
}
case Invalid:
{
QString invalidMsg(tr("Invalid filter"));
if (dfilter_error_msg) {
invalidMsg.append(QString().sprintf(": %s", dfilter_error_msg));
}
emit pushFilterSyntaxStatus(invalidMsg);
break;
}
default:
break;
}
bookmark_button_->setEnabled(syntaxState() == Valid || syntaxState() == Deprecated);

View File

@ -48,7 +48,6 @@ private slots:
private:
bool plain_;
bool field_name_only_;
QString empty_filter_message_;
QToolButton *bookmark_button_;
QToolButton *clear_button_;

View File

@ -196,7 +196,6 @@ void FilterExpressionsPreferencesFrame::on_expressionTreeWidget_itemActivated(QT
case label_col_:
{
cur_line_edit_ = new QLineEdit();
cur_column_ = column;
saved_col_string_ = item->text(label_col_);
connect(cur_line_edit_, SIGNAL(editingFinished()), this, SLOT(labelEditingFinished()));
editor = cur_line_edit_;
@ -207,7 +206,7 @@ void FilterExpressionsPreferencesFrame::on_expressionTreeWidget_itemActivated(QT
SyntaxLineEdit *syntax_edit = new SyntaxLineEdit();
saved_col_string_ = item->text(expression_col_);
connect(syntax_edit, SIGNAL(textChanged(QString)),
this, SLOT(expressionTextChanged(QString)));
syntax_edit, SLOT(checkDisplayFilter(QString)));
connect(syntax_edit, SIGNAL(editingFinished()), this, SLOT(expressionEditingFinished()));
editor = cur_line_edit_ = syntax_edit;
break;
@ -257,24 +256,6 @@ void FilterExpressionsPreferencesFrame::labelEditingFinished()
ui->expressionTreeWidget->removeItemWidget(item, label_col_);
}
void FilterExpressionsPreferencesFrame::expressionTextChanged(QString)
{
SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(cur_line_edit_);
QTreeWidgetItem *item = ui->expressionTreeWidget->currentItem();
if (!syntax_edit || !item) return;
dfilter_t *dfp = NULL;
const char *field_text = syntax_edit->text().toUtf8().constData();
if (strlen(field_text) < 1) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Empty);
} else if (proto_check_field_name(field_text) != 0 || !dfilter_compile(field_text, &dfp)) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Invalid);
} else {
syntax_edit->setSyntaxState(SyntaxLineEdit::Valid);
}
dfilter_free(dfp);
}
void FilterExpressionsPreferencesFrame::expressionEditingFinished()
{
QTreeWidgetItem *item = ui->expressionTreeWidget->currentItem();

View File

@ -58,7 +58,6 @@ private slots:
void on_expressionTreeWidget_itemActivated(QTreeWidgetItem *item, int column);
void lineEditDestroyed();
void labelEditingFinished();
void expressionTextChanged(QString);
void expressionEditingFinished();
void on_newToolButton_clicked();
void on_deleteToolButton_clicked();

2156
ui/qt/io_graph_dialog.cpp Normal file

File diff suppressed because it is too large Load Diff

253
ui/qt/io_graph_dialog.h Normal file
View File

@ -0,0 +1,253 @@
/* io_graph_dialog.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 IO_GRAPH_DIALOG_H
#define IO_GRAPH_DIALOG_H
#include "config.h"
#include <glib.h>
#include <file.h>
#include "epan/epan_dissect.h"
#include "epan/uat.h"
#include "ui/io_graph_item.h"
#include "syntax_line_edit.h"
#include <QComboBox>
#include <QDialog>
#include <QIcon>
#include <QLineEdit>
#include <QMenu>
#include <QRubberBand>
#include <QTimer>
#include <QTreeWidgetItem>
#include "qcustomplot.h"
// GTK+ sets this to 100000 (NUM_IO_ITEMS)
const int max_io_items_ = 250000;
// XXX - Move to its own file?
class IOGraph : public QObject {
Q_OBJECT
public:
// COUNT_TYPE_* in gtk/io_graph.c
enum PlotStyles { psLine, psImpulse, psBar, psStackedBar, psDot, psSquare, psDiamond };
explicit IOGraph(QCustomPlot *parent);
~IOGraph();
const QString configError() { return config_err_; }
const QString name() { return name_; }
void setName(const QString &name);
const QString filter() { return filter_; }
void setFilter(const QString &filter);
void applyCurrentColor();
bool visible() { return visible_; }
void setVisible(bool visible);
QRgb color();
void setColor(const QRgb color);
void setPlotStyle(int style);
const QString valueUnitLabel();
void setValueUnits(int val_units);
const QString valueUnitField() { return vu_field_; }
void setValueUnitField(const QString &vu_field);
unsigned int movingAveragePeriod() { return moving_avg_period_; }
void setInterval(int interval);
bool addToLegend();
QCPGraph *graph() { return graph_; }
QCPBars *bars() { return bars_; }
double startOffset();
int packetFromTime(double ts);
void clearAllData();
static QMap<io_graph_item_unit_t, QString> valueUnitsToNames();
static QMap<PlotStyles, QString> plotStylesToNames();
static QMap<int, QString> movingAveragesToNames();
unsigned int moving_avg_period_;
public slots:
void recalcGraphData(capture_file *cap_file);
signals:
void requestReplot();
void requestRecalc();
void requestRetap();
private:
double getItemValue(int idx, capture_file *cap_file);
// Callbacks for register_tap_listener
static void tapReset(void *iog_ptr);
static gboolean tapPacket(void *iog_ptr, packet_info *pinfo, epan_dissect_t *edt, const void *data);
static void tapDraw(void *iog_ptr);
QCustomPlot *parent_;
QString config_err_;
QString name_;
bool visible_;
QCPGraph *graph_;
QCPBars *bars_;
QString filter_;
QBrush color_;
io_graph_item_unit_t val_units_;
QString vu_field_;
int hf_index_;
int interval_;
double start_time_;
// Cached data. We should be able to change the Y axis without retapping as
// much as is feasible.
io_graph_item_t items_[max_io_items_];
int cur_idx_;
};
namespace Ui {
class IOGraphDialog;
}
class IOGraphDialog : public QDialog
{
Q_OBJECT
public:
explicit IOGraphDialog(QWidget *parent = 0, capture_file *cf = NULL);
~IOGraphDialog();
void addGraph(bool checked, QString name, QString dfilter, int color_idx, IOGraph::PlotStyles style,
io_graph_item_unit_t value_units, QString yfield, int moving_average);
void addGraph(bool copy_from_current = false);
void addDefaultGraph(bool enabled, int idx = 0);
void syncGraphSettings(QTreeWidgetItem *item);
public slots:
void setCaptureFile(capture_file *cf);
void scheduleReplot(bool now = false);
void scheduleRecalc(bool now = false);
void scheduleRetap(bool now = false);
protected:
void keyPressEvent(QKeyEvent *event);
void reject();
signals:
void goToPacket(int packet_num);
void recalcGraphData(capture_file *);
void intervalChanged(int interval);
private:
Ui::IOGraphDialog *ui;
capture_file *cap_file_;
QLineEdit *name_line_edit_;
SyntaxLineEdit *dfilter_line_edit_;
SyntaxLineEdit *yfield_line_edit_;
QComboBox *color_combo_box_;
QComboBox *style_combo_box_;
QComboBox *yaxis_combo_box_;
QComboBox *sma_combo_box_;
QString hint_err_;
QCPGraph *base_graph_;
QCPItemTracer *tracer_;
guint32 packet_num_;
double start_time_;
bool mouse_drags_;
QRubberBand *rubber_band_;
QPoint rb_origin_;
QMenu ctx_menu_;
QTimer *stat_timer_;
bool need_replot_; // Light weight: tell QCP to replot existing data
bool need_recalc_; // Medium weight: recalculate values, then replot
bool need_retap_; // Heavy weight: re-read packet data
bool auto_axes_;
// void fillGraph();
void zoomAxes(bool in);
void panAxes(int x_pixels, int y_pixels);
QIcon graphColorIcon(int color_idx);
void toggleTracerStyle(bool force_default = false);
void getGraphInfo();
void updateLegend();
QRectF getZoomRanges(QRect zoom_rect);
void itemEditingFinished(QTreeWidgetItem *item);
void loadProfileGraphs();
private slots:
void graphClicked(QMouseEvent *event);
void mouseMoved(QMouseEvent *event);
void mouseReleased(QMouseEvent *event);
void focusChanged(QWidget *previous, QWidget *current);
void activateLastItem();
void lineEditDestroyed();
void comboDestroyed();
void resetAxes();
void updateStatistics(void);
void on_intervalComboBox_currentIndexChanged(int index);
void on_todCheckBox_toggled(bool checked);
void on_graphTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_graphTreeWidget_itemActivated(QTreeWidgetItem *item, int column);
void on_graphTreeWidget_itemSelectionChanged();
void on_graphTreeWidget_itemChanged(QTreeWidgetItem *item, int column);
void on_resetButton_clicked();
void on_logCheckBox_toggled(bool checked);
void on_newToolButton_clicked();
void on_deleteToolButton_clicked();
void on_copyToolButton_clicked();
void on_dragRadioButton_toggled(bool checked);
void on_zoomRadioButton_toggled(bool checked);
void on_actionReset_triggered();
void on_actionZoomIn_triggered();
void on_actionZoomOut_triggered();
void on_actionMoveUp10_triggered();
void on_actionMoveLeft10_triggered();
void on_actionMoveRight10_triggered();
void on_actionMoveDown10_triggered();
void on_actionMoveUp1_triggered();
void on_actionMoveLeft1_triggered();
void on_actionMoveRight1_triggered();
void on_actionMoveDown1_triggered();
void on_actionGoToPacket_triggered();
void on_actionDragZoom_triggered();
void on_actionToggleTimeOrigin_triggered();
void on_actionCrosshairs_triggered();
void on_buttonBox_helpRequested();
void on_buttonBox_accepted();
};
#endif // IO_GRAPH_DIALOG_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:
*/

506
ui/qt/io_graph_dialog.ui Normal file
View File

@ -0,0 +1,506 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>IOGraphDialog</class>
<widget class="QDialog" name="IOGraphDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>850</width>
<height>640</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCustomPlot" name="ioPlot" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="ElidedLabel" name="hintLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
&lt;h3&gt;Valuable and amazing time-saving keyboard shortcuts&lt;/h3&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;+&lt;/th&gt;&lt;td&gt;Zoom in&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;-&lt;/th&gt;&lt;td&gt;Zoom out&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;→&lt;/th&gt;&lt;td&gt;Move right 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;←&lt;/th&gt;&lt;td&gt;Move left 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10 pixels&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;→&lt;/th&gt;&lt;td&gt;Move right 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;←&lt;/th&gt;&lt;td&gt;Move left 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 1 pixel&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="graphTreeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Display filter</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
<column>
<property name="text">
<string>Style</string>
</property>
</column>
<column>
<property name="text">
<string>Y Axis</string>
</property>
</column>
<column>
<property name="text">
<string>Y Field</string>
</property>
</column>
<column>
<property name="text">
<string>Smoothing</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="newToolButton">
<property name="toolTip">
<string>Change the dissection behavior for a protocol.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../image/toolbar.qrc">
<normaloff>:/stock/plus-8.png</normaloff>:/stock/plus-8.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteToolButton">
<property name="toolTip">
<string>Remove this dissection behavior.</string>
</property>
<property name="icon">
<iconset resource="../../image/toolbar.qrc">
<normaloff>:/stock/minus-8.png</normaloff>:/stock/minus-8.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="copyToolButton">
<property name="toolTip">
<string>Copy this dissection behavior.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../image/toolbar.qrc">
<normaloff>:/stock/copy-8.png</normaloff>:/stock/copy-8.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="mouseLabel">
<property name="text">
<string>Mouse</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dragRadioButton">
<property name="toolTip">
<string>Drag using the mouse button.</string>
</property>
<property name="text">
<string>drags</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="zoomRadioButton">
<property name="toolTip">
<string>Select using the mouse button.</string>
</property>
<property name="text">
<string>zooms</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Interval</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="intervalComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="todCheckBox">
<property name="text">
<string>Time of day</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="logCheckBox">
<property name="text">
<string>Log scale</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="resetButton">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
<action name="actionReset">
<property name="text">
<string>Reset Graph</string>
</property>
<property name="toolTip">
<string>Reset the graph to its initial state.</string>
</property>
<property name="shortcut">
<string>0</string>
</property>
</action>
<action name="actionZoomIn">
<property name="text">
<string>Zoom In</string>
</property>
<property name="toolTip">
<string>Zoom in</string>
</property>
<property name="shortcut">
<string>+</string>
</property>
</action>
<action name="actionZoomOut">
<property name="text">
<string>Zoom Out</string>
</property>
<property name="toolTip">
<string>Zoom out</string>
</property>
<property name="shortcut">
<string>-</string>
</property>
</action>
<action name="actionMoveUp10">
<property name="text">
<string>Move Up 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move up 10 pixels</string>
</property>
<property name="shortcut">
<string>Up</string>
</property>
</action>
<action name="actionMoveLeft10">
<property name="text">
<string>Move Left 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move left 10 pixels</string>
</property>
<property name="shortcut">
<string>Left</string>
</property>
</action>
<action name="actionMoveRight10">
<property name="text">
<string>Move Right 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move right 10 pixels</string>
</property>
<property name="shortcut">
<string>Right</string>
</property>
</action>
<action name="actionMoveDown10">
<property name="text">
<string>Move Down 10 Pixels</string>
</property>
<property name="toolTip">
<string>Move down 10 pixels</string>
</property>
<property name="shortcut">
<string>Down</string>
</property>
</action>
<action name="actionMoveUp1">
<property name="text">
<string>Move Up 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move up 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Up</string>
</property>
</action>
<action name="actionMoveLeft1">
<property name="text">
<string>Move Left 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move left 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Left</string>
</property>
</action>
<action name="actionMoveRight1">
<property name="text">
<string>Move Right 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move right 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Right</string>
</property>
</action>
<action name="actionMoveDown1">
<property name="text">
<string>Move Down 1 Pixel</string>
</property>
<property name="toolTip">
<string>Move down 1 pixel</string>
</property>
<property name="shortcut">
<string>Shift+Down</string>
</property>
</action>
<action name="actionGoToPacket">
<property name="text">
<string>Go To Packet Under Cursor</string>
</property>
<property name="toolTip">
<string>Go to packet currently under the cursor</string>
</property>
<property name="shortcut">
<string>G</string>
</property>
</action>
<action name="actionDragZoom">
<property name="text">
<string>Drag / Zoom</string>
</property>
<property name="toolTip">
<string>Toggle mouse drag / zoom behavior</string>
</property>
<property name="shortcut">
<string>Z</string>
</property>
</action>
<action name="actionToggleTimeOrigin">
<property name="text">
<string>Capture / Session Time Origin</string>
</property>
<property name="toolTip">
<string>Toggle capture / session time origin</string>
</property>
<property name="shortcut">
<string>T</string>
</property>
</action>
<action name="actionCrosshairs">
<property name="text">
<string>Crosshairs</string>
</property>
<property name="toolTip">
<string>Toggle crosshairs</string>
</property>
<property name="shortcut">
<string>Space</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>QCustomPlot</class>
<extends>QWidget</extends>
<header>qcustomplot.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>elided_label.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../image/toolbar.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>IOGraphDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>IOGraphDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1521,6 +1521,7 @@ void MainWindow::setForCapturedPackets(bool have_captured_packets)
// have_captured_packets);
// set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
// have_captured_packets);
main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
}
void MainWindow::setMenusForFileSet(bool enable_list_files) {

View File

@ -352,6 +352,7 @@ private slots:
void on_actionStatisticsHTTPRequests_triggered();
void on_actionStatisticsHTTPLoadDistribution_triggered();
void on_actionStatisticsPacketLen_triggered();
void on_actionStatisticsIOGraph_triggered();
void on_actionStatisticsSametime_triggered();
void on_actionTelephonyISUPMessages_triggered();

View File

@ -329,6 +329,7 @@
<addaction name="actionSummary"/>
<addaction name="actionProtocol_Hierarchy"/>
<addaction name="actionStatisticsPacketLen"/>
<addaction name="actionStatisticsIOGraph"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionStatisticsANCP"/>
@ -1551,6 +1552,14 @@
<string>Export PDUs to File</string>
</property>
</action>
<action name="actionStatisticsIOGraph">
<property name="text">
<string>&amp;I/O Graph</string>
</property>
<property name="toolTip">
<string>Create graphs based on display filter fields</string>
</property>
</action>
<action name="actionViewToolbarMainToolbar">
<property name="checkable">
<bool>true</bool>

View File

@ -71,6 +71,7 @@
#include "decode_as_dialog.h"
#include "export_object_dialog.h"
#include "export_pdu_dialog.h"
#include "io_graph_dialog.h"
#include "packet_comment_dialog.h"
#include "preferences_dialog.h"
#include "print_dialog.h"
@ -1965,6 +1966,15 @@ void MainWindow::on_actionStatisticsPacketLen_triggered()
openStatisticsTreeDialog("plen");
}
void MainWindow::on_actionStatisticsIOGraph_triggered()
{
IOGraphDialog *iog_dialog = new IOGraphDialog(this, cap_file_);
connect(iog_dialog, SIGNAL(goToPacket(int)), packet_list_, SLOT(goToPacket(int)));
connect(this, SIGNAL(setCaptureFile(capture_file*)),
iog_dialog, SLOT(setCaptureFile(capture_file*)));
iog_dialog->show();
}
void MainWindow::on_actionStatisticsSametime_triggered()
{
openStatisticsTreeDialog("sametime");

View File

@ -133,7 +133,6 @@ void SearchFrame::enableWidgets()
return;
}
dfilter_t *dfp = NULL;
bool enable = sf_ui_->searchTypeComboBox->currentIndex() == string_search;
sf_ui_->searchInComboBox->setEnabled(enable);
sf_ui_->caseCheckBox->setEnabled(enable);
@ -141,24 +140,7 @@ void SearchFrame::enableWidgets()
switch (sf_ui_->searchTypeComboBox->currentIndex()) {
case df_search:
// XXX - Merge this with DisplayFitlerEdit::checkFilter
if (dfilter_compile(sf_ui_->searchLineEdit->text().toUtf8().constData(), &dfp)) {
GPtrArray *depr = NULL;
if (dfp != NULL) {
depr = dfilter_deprecated_tokens(dfp);
}
if (sf_ui_->searchLineEdit->text().isEmpty()) {
sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Empty);
} else if (depr) {
/* You keep using that word. I do not think it means what you think it means. */
sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Deprecated);
} else {
sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Valid);
}
dfilter_free(dfp);
} else {
sf_ui_->searchLineEdit->setSyntaxState(SyntaxLineEdit::Invalid);
}
sf_ui_->searchLineEdit->checkDisplayFilter(sf_ui_->searchLineEdit->text());
break;
case hex_search:
if (sf_ui_->searchLineEdit->text().isEmpty()) {

View File

@ -184,11 +184,11 @@ void SequenceDialog::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up:
case Qt::Key_K:
panAxes(0, -1 * pan_pixels);
panAxes(0, pan_pixels);
break;
case Qt::Key_Down:
case Qt::Key_J:
panAxes(0, pan_pixels);
panAxes(0, -1 * pan_pixels);
break;
case Qt::Key_0:
@ -474,12 +474,12 @@ void SequenceDialog::on_actionMoveLeft10_triggered()
void SequenceDialog::on_actionMoveUp10_triggered()
{
panAxes(0, -10);
panAxes(0, 10);
}
void SequenceDialog::on_actionMoveDown10_triggered()
{
panAxes(0, 10);
panAxes(0, -10);
}
void SequenceDialog::on_actionMoveRight1_triggered()
@ -494,12 +494,12 @@ void SequenceDialog::on_actionMoveLeft1_triggered()
void SequenceDialog::on_actionMoveUp1_triggered()
{
panAxes(0, -1);
panAxes(0, 1);
}
void SequenceDialog::on_actionMoveDown1_triggered()
{
panAxes(0, 1);
panAxes(0, -1);
}
/*

View File

@ -24,11 +24,12 @@
#include <glib.h>
#include <epan/prefs.h>
#include <epan/proto.h>
#include <epan/dfilter/dfilter.h>
#include "syntax_line_edit.h"
#include "color_utils.h"
#include <QDebug>
SyntaxLineEdit::SyntaxLineEdit(QWidget *parent) :
QLineEdit(parent)
@ -69,7 +70,55 @@ QString SyntaxLineEdit::styleSheet() const {
return style_sheet_;
}
QString SyntaxLineEdit::deprecatedToken()
{
return deprecated_token_;
}
void SyntaxLineEdit::setStyleSheet(const QString &style_sheet) {
style_sheet_ = style_sheet;
QLineEdit::setStyleSheet(style_sheet_ + state_style_sheet_);
}
void SyntaxLineEdit::checkDisplayFilter(QString filter)
{
if (filter.isEmpty()) {
setSyntaxState(SyntaxLineEdit::Empty);
return;
}
deprecated_token_.clear();
dfilter_t *dfp = NULL;
bool valid = dfilter_compile(filter.toUtf8().constData(), &dfp);
if (valid) {
setSyntaxState(SyntaxLineEdit::Valid);
} else {
GPtrArray *depr = NULL;
if (dfp) {
depr = dfilter_deprecated_tokens(dfp);
}
if (depr) {
setSyntaxState(SyntaxLineEdit::Deprecated);
deprecated_token_ = (const char *) g_ptr_array_index(depr, 0);
} else {
setSyntaxState(SyntaxLineEdit::Invalid);
}
}
dfilter_free(dfp);
}
void SyntaxLineEdit::checkFieldName(QString field)
{
if (field.isEmpty()) {
setSyntaxState(SyntaxLineEdit::Empty);
return;
}
char invalid_char = proto_check_field_name(field.toUtf8().constData());
if (invalid_char) {
setSyntaxState(SyntaxLineEdit::Invalid);
} else {
checkDisplayFilter(field);
}
}

View File

@ -36,16 +36,23 @@ public:
SyntaxState syntaxState() const { return syntax_state_; }
void setSyntaxState(SyntaxState state = Empty);
QString styleSheet() const;
QString deprecatedToken();
public slots:
void setStyleSheet(const QString &style_sheet);
// Built-in syntax checks. Connect textChanged to these as needed.
void checkDisplayFilter(QString filter);
void checkFieldName(QString field);
private:
SyntaxState syntax_state_;
QString style_sheet_;
QString state_style_sheet_;
QString deprecated_token_;
signals:
public slots:
void setStyleSheet(const QString &style_sheet);
};
#endif // SYNTAX_LINE_EDIT_H

View File

@ -60,6 +60,7 @@
#ifndef MA_1_SECOND
const int moving_avg_period_ = 20;
#endif
const QRgb graph_color_1 = tango_sky_blue_5;
const QRgb graph_color_2 = tango_butter_6;
const QRgb graph_color_3 = tango_chameleon_5;
@ -256,11 +257,11 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
break;
case Qt::Key_Up:
case Qt::Key_K:
panAxes(0, -1 * pan_pixels);
panAxes(0, pan_pixels);
break;
case Qt::Key_Down:
case Qt::Key_J:
panAxes(0, pan_pixels);
panAxes(0, -1 * pan_pixels);
break;
case Qt::Key_Space:
@ -805,7 +806,7 @@ void TCPStreamDialog::graphClicked(QMouseEvent *event)
on_actionGoToPacket_triggered();
} else {
if (!rubber_band_) {
rubber_band_ = new QRubberBand(QRubberBand::Rectangle, ui->streamPlot);
rubber_band_ = new QRubberBand(QRubberBand::Rectangle, sp);
}
rb_origin_ = event->pos();
rubber_band_->setGeometry(QRect(rb_origin_, QSize()));
@ -917,7 +918,7 @@ void TCPStreamDialog::mouseMoved(QMouseEvent *event)
.arg(packet_seg->th_ack)
.arg(packet_seg->th_win);
tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
ui->streamPlot->replot();
sp->replot();
} else {
if (rubber_band_ && rubber_band_->isVisible() && event) {
rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized());
@ -1085,12 +1086,12 @@ void TCPStreamDialog::on_actionMoveLeft10_triggered()
void TCPStreamDialog::on_actionMoveUp10_triggered()
{
panAxes(0, -10);
panAxes(0, 10);
}
void TCPStreamDialog::on_actionMoveDown10_triggered()
{
panAxes(0, 10);
panAxes(0, -10);
}
void TCPStreamDialog::on_actionMoveRight1_triggered()
@ -1105,12 +1106,12 @@ void TCPStreamDialog::on_actionMoveLeft1_triggered()
void TCPStreamDialog::on_actionMoveUp1_triggered()
{
panAxes(0, -1);
panAxes(0, 1);
}
void TCPStreamDialog::on_actionMoveDown1_triggered()
{
panAxes(0, 1);
panAxes(0, -1);
}
void TCPStreamDialog::on_actionNextStream_triggered()