diff options
author | Michael Mann <mmann78@netscape.net> | 2013-11-29 22:47:59 +0000 |
---|---|---|
committer | Michael Mann <mmann78@netscape.net> | 2013-11-29 22:47:59 +0000 |
commit | 60d6b05e2340ae90c09fbdd2f25b6513131a0bd1 (patch) | |
tree | b6e5a1637da1197aa7faad6cd480693ee1deee13 /ui | |
parent | eaaf4437aba897df51bfb31829f98cf198dd1887 (diff) | |
download | wireshark-60d6b05e2340ae90c09fbdd2f25b6513131a0bd1.tar.gz |
Stats_tree enhancements for sorting, averages and burst rate. Bug 9452 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9452)
From Deon van der Westhuysen
- Bug fix: object leak in stats_tree after a tap reset (for example apply statistics preferences with a stats_tree window open)
- Bug fix: correct sample code in README.stats_tree
- Add: slash in plug-in name now creates submenu as docs describe (was a bug?)
- Add: menu separator before the stat_tree registered plug-ins
- Add: stats_tree can now calculate averages for nodes; automatically calculated for range nodes. Add section in README.stats_tree describing averages.
- Add: stats_tree can now calculate burst rate of each node (like rate but with a shorter, sliding time window)
- Add: sorting for stats_tree plug-ins. Can sort on node name, count, average, min, max values and burst rate.
- Add: preferences for stats_tree system (default sort column, burst calc params)
- Add: stats_tree window copy to clipboard and export and plain text, csv and XML.
- Added sample of new functionality in $srcdir/plugins/stats_tree/pinfo_stats_tree.c
- Moved all stats_tree sample plug-ins to "IP Statistics" submenu.
svn path=/trunk/; revision=53657
Diffstat (limited to 'ui')
-rw-r--r-- | ui/cli/tap-stats_tree.c | 19 | ||||
-rw-r--r-- | ui/gtk/main_menubar.c | 50 | ||||
-rw-r--r-- | ui/gtk/stats_tree_stat.c | 355 | ||||
-rw-r--r-- | ui/win32/file_dlg_win32.c | 87 | ||||
-rw-r--r-- | ui/win32/file_dlg_win32.h | 11 |
5 files changed, 428 insertions, 94 deletions
diff --git a/ui/cli/tap-stats_tree.c b/ui/cli/tap-stats_tree.c index 7e893c39af..345e898579 100644 --- a/ui/cli/tap-stats_tree.c +++ b/ui/cli/tap-stats_tree.c @@ -54,23 +54,12 @@ draw_stats_tree(void *psp) { stats_tree *st = (stats_tree *)psp; GString *s; - gchar *fmt; - stat_node *child; - - s = g_string_new("\n===================================================================\n"); - fmt = g_strdup_printf(" %%s%%-%us%%12s\t%%12s\t%%12s\n",stats_tree_branch_max_namelen(&st->root,0)); - g_string_append_printf(s,fmt,"",st->cfg->name,"value","rate","percent"); - g_free(fmt); - g_string_append_printf(s,"-------------------------------------------------------------------\n"); - - for (child = st->root.children; child; child = child->next ) { - stats_tree_branch_to_str(child,s,0); - } - - s = g_string_append(s,"\n===================================================================\n"); + s= stats_tree_format_as_str(st, ST_FORMAT_PLAIN, stats_tree_get_default_sort_col(st), + stats_tree_is_default_sort_DESC(st)); + printf("%s",s->str); - + g_string_free(s,TRUE); } static void diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c index b10cf12964..7d3bf54402 100644 --- a/ui/gtk/main_menubar.c +++ b/ui/gtk/main_menubar.c @@ -1253,6 +1253,7 @@ static const char *ui_desc_menubar = " </menu>\n" " <menuitem name='UDPMulticastStreams' action='/Statistics/UDPMulticastStreams'/>\n" " <menuitem name='WLANTraffic' action='/Statistics/WLANTraffic'/>\n" +" <separator/>\n" " </menu>\n" " <menu name= 'TelephonyMenu' action='/Telephony'>\n" " <menu name= 'ANSI' action='/Telephony/ANSI'>\n" @@ -4221,6 +4222,11 @@ add_tap_plugins (guint merge_id, GtkUIManager *ui_manager) GList *iter; gchar *action_name; + gchar *submenu_path; + gchar *stat_name_buf; + gchar *stat_name; + gchar *sep; + action_group = gtk_action_group_new ("tap-plugins-group"); submenu_statistics = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_STATISTICS_PATH); @@ -4237,23 +4243,61 @@ add_tap_plugins (guint merge_id, GtkUIManager *ui_manager) while (iter) { stats_tree_cfg *cfg = (stats_tree_cfg*)iter->data; if (cfg->plugin) { - action_name = g_strdup_printf(MENU_STATISTICS_PATH "/%s", cfg->abbr); + stat_name_buf = g_strdup(cfg->name); + submenu_path = g_malloc(strlen(MENU_STATISTICS_PATH)+strlen(cfg->name)+strlen(cfg->abbr)+1); /* worst case length */ + strcpy(submenu_path, MENU_STATISTICS_PATH); + + sep= stat_name= stat_name_buf; + while (sep= strchr(sep,'/')) { + if (*(++sep)=='/') { /* escapeded slash - two slash characters after each other */ + memmove(sep,sep+1,strlen(sep)); + } + else { + /* we got a new submenu name - ignore the edge case where there is no text following this slash */ + *(sep-1)= 0; + action_name = g_strdup_printf("%s/%s", submenu_path,stat_name); + if (!gtk_ui_manager_get_widget(ui_manager, action_name)) { + action = (GtkAction *)g_object_new (GTK_TYPE_ACTION, + "name", action_name, + "label", stat_name, + NULL); + gtk_action_group_add_action (action_group, action); + g_object_unref (action); + + gtk_ui_manager_add_ui (ui_manager, merge_id, + submenu_path, + stat_name, + action_name, + GTK_UI_MANAGER_MENU, + FALSE); + } + g_free (action_name); + + strcat(submenu_path,"/"); + strcat(submenu_path,stat_name); + stat_name= sep; + } + } + + action_name = g_strdup_printf("%s/%s", submenu_path, cfg->abbr); action = (GtkAction *)g_object_new (GTK_TYPE_ACTION, "name", action_name, - "label", cfg->name, + "label", stat_name, NULL); g_signal_connect (action, "activate", G_CALLBACK (gtk_stats_tree_cb), NULL); gtk_action_group_add_action (action_group, action); g_object_unref (action); gtk_ui_manager_add_ui (ui_manager, merge_id, - MENU_STATISTICS_PATH, + submenu_path, action_name, action_name, GTK_UI_MANAGER_MENUITEM, FALSE); g_free (action_name); + g_free (stat_name_buf); + g_free (submenu_path); } iter = g_list_next(iter); } diff --git a/ui/gtk/stats_tree_stat.c b/ui/gtk/stats_tree_stat.c index c0df8bc58d..ccf9517881 100644 --- a/ui/gtk/stats_tree_stat.c +++ b/ui/gtk/stats_tree_stat.c @@ -23,6 +23,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + /* stats_tree modifications by Deon van der Westhuysen, November 2013 + * support for + * - sorting by column, + * - display a generic number of columns(driven by stats_tree.c + * - copy to clipboard + * - export to text, CSV or XML file + */ + #include "config.h" #include <string.h> @@ -38,11 +46,22 @@ #include "ui/gtk/gui_utils.h" #include "ui/gtk/dlg_utils.h" +#include "ui/gtk/file_dlg.h" #include "ui/gtk/tap_param_dlg.h" #include "ui/gtk/main.h" #include "ui/gtk/old-gtk-compat.h" +#ifdef _WIN32 +#define USE_WIN32_FILE_DIALOGS +#endif + +#ifdef USE_WIN32_FILE_DIALOGS +#include <gdk/gdkwin32.h> +#include <windows.h> +#include "ui/win32/file_dlg_win32.h" +#endif + struct _st_node_pres { GtkTreeIter* iter; }; @@ -58,26 +77,32 @@ struct _tree_pres { GtkWidget* tree; }; -/* the columns of the tree pane */ -enum _stat_tree_columns { - TITLE_COLUMN, - COUNT_COLUMN, - RATE_COLUMN, - PERCENT_COLUMN, - N_COLUMNS -}; +/* Define fixed column indexes */ +#define NODEPTR_COLUMN 0 /* Always first column */ +#define N_RESERVED_COL 1 /* Number of columns for internal use - added before visable cols */ -/* used for converting numbers */ -#define NUM_BUF_SIZE 32 -/* creates the gtk representation for a stat_node - * node: the node - */ static void -setup_gtk_node_pr(stat_node* node) +draw_gtk_node(stat_node* node) { GtkTreeIter* parent = NULL; + stat_node* child; + int num_columns= node->st->num_columns+N_RESERVED_COL; + gint *columns = (gint*) g_malloc(sizeof(gint)*num_columns); + GValue *values = (GValue*) g_malloc0(sizeof(GValue)*num_columns); + gchar **valstrs = stats_tree_get_values_from_node(node); + int count; + + columns[0]= 0; + g_value_init(values, G_TYPE_POINTER); + g_value_set_pointer(values, node); + for (count = N_RESERVED_COL; count<num_columns; count++) { + columns[count]= count; + g_value_init(values+count, G_TYPE_STRING); + g_value_take_string (values+count,valstrs[count-N_RESERVED_COL]); + } + if (!node->pr) { node->pr = (st_node_pres *)g_malloc(sizeof(st_node_pres)); if (node->st->pr->store) { @@ -87,35 +112,30 @@ setup_gtk_node_pr(stat_node* node) parent = node->parent->pr->iter; } gtk_tree_store_append (node->st->pr->store, node->pr->iter, parent); - gtk_tree_store_set(node->st->pr->store, node->pr->iter, - TITLE_COLUMN, node->name, RATE_COLUMN, "", COUNT_COLUMN, "", -1); + gtk_tree_store_set_valuesv(node->st->pr->store, node->pr->iter, + columns, values, num_columns); + } } -} - - -static void -draw_gtk_node(stat_node* node) -{ - static gchar value[NUM_BUF_SIZE]; - static gchar rate[NUM_BUF_SIZE]; - static gchar percent[NUM_BUF_SIZE]; - stat_node* child; - - stats_tree_get_strs_from_node(node, value, rate, - percent); - if (node->st->pr->store && node->pr->iter) { - gtk_tree_store_set(node->st->pr->store, node->pr->iter, - RATE_COLUMN, rate, - COUNT_COLUMN, value, - PERCENT_COLUMN, percent, - -1); + /* skip reserved columns and first entry in the stats_tree values */ + /* list (the node name). These should already be set and static. */ + gtk_tree_store_set_valuesv(node->st->pr->store, node->pr->iter, + columns+N_RESERVED_COL+1, values+N_RESERVED_COL+1, + num_columns-N_RESERVED_COL-1); + } + + for (count = 0; count<num_columns; count++) { + g_value_unset(values+count); } + g_free(columns); + g_free(values); + g_free(valstrs); if (node->children) { for (child = node->children; child; child = child->next ) draw_gtk_node(child); } + } static void @@ -123,18 +143,175 @@ draw_gtk_tree(void *psp) { stats_tree *st = (stats_tree *)psp; stat_node* child; + int count; + gint sort_column= GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID; + GtkSortType order= GTK_SORT_DESCENDING; + + for (count = 0; count<st->num_columns; count++) { + gtk_tree_view_column_set_title(gtk_tree_view_get_column(GTK_TREE_VIEW(st->pr->tree),count), + stats_tree_get_column_name(count)); + } + + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (st->pr->store), &sort_column, &order); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (st->pr->store), + GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_DESCENDING); for (child = st->root.children; child; child = child->next ) { draw_gtk_node(child); - if (child->pr->iter && st->pr->store) { + if ( (!(child->st_flags&ST_FLG_DEF_NOEXPAND)) && child->pr->iter && st->pr->store ) { gtk_tree_view_expand_row(GTK_TREE_VIEW(st->pr->tree), - gtk_tree_model_get_path(GTK_TREE_MODEL(st->pr->store), - child->pr->iter), + gtk_tree_model_get_path(GTK_TREE_MODEL(st->pr->store),child->pr->iter), FALSE); } } + if ((sort_column==GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)|| + (sort_column==GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)) { + sort_column= stats_tree_get_default_sort_col(st)+N_RESERVED_COL; + order= stats_tree_is_default_sort_DESC(st)?GTK_SORT_DESCENDING:GTK_SORT_ASCENDING; + } + + /* Only call this once the entire list is drawn - else Gtk seems */ + /* to get sorting order wrong (sorting broken when new nodes are */ + /* added after setting sort column.) Also for performance. */ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (st->pr->store), sort_column, order); +} + +static gboolean +copy_tree_to_clipboard +(GtkWidget *win _U_, stats_tree *st) +{ + gint sort_column= N_RESERVED_COL; /* default */ + GtkSortType order= GTK_SORT_DESCENDING; + GString *s; + + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (st->pr->store), &sort_column, &order); + s= stats_tree_format_as_str(st,ST_FORMAT_PLAIN,sort_column-N_RESERVED_COL,order==GTK_SORT_DESCENDING); + copy_to_clipboard(s); + g_string_free (s,TRUE); + + return TRUE; +} + + +#ifndef USE_WIN32_FILE_DIALOGS +static gboolean +gtk_save_as_statstree(GtkWidget *win, GString *file_name, int *file_type) +{ + GtkWidget *saveas_w; + GtkWidget *main_vb; + GtkWidget *ft_hb, *ft_lb, *ft_combo_box; + char *st_name; + gpointer ptr; + + saveas_w = file_selection_new("Wireshark: Save stats tree as ...", + GTK_WINDOW(win), FILE_SELECTION_SAVE); + + main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5); + file_selection_set_extra_widget(saveas_w, main_vb); + gtk_widget_show(main_vb); + + /* File type row */ + ft_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE); + gtk_box_pack_start(GTK_BOX(main_vb), ft_hb, FALSE, FALSE, 0); + gtk_widget_show(ft_hb); + + ft_lb = gtk_label_new("Save as format:"); + gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0); + gtk_widget_show(ft_lb); + + ft_combo_box = ws_combo_box_new_text_and_pointer(); + ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(ft_combo_box), "Plain text file (.txt)", GINT_TO_POINTER(ST_FORMAT_PLAIN)); + ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(ft_combo_box), "Comma separated values (.csv)", GINT_TO_POINTER(ST_FORMAT_CSV)); + ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(ft_combo_box), "XML document (.xml)", GINT_TO_POINTER(ST_FORMAT_XML)); + + gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0); + gtk_widget_show(ft_combo_box); + ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0); + + st_name = file_selection_run(saveas_w); + if (st_name == NULL) { + /* User cancelled or closed the dialog. */ + return FALSE; + } + + if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) { + g_assert_not_reached(); /* Programming error: somehow nothing is active */ + } + + /* Save result from dialog box */ + *file_type = GPOINTER_TO_INT(ptr); + g_string_printf(file_name, "%s", st_name); + + /* We've crossed the Rubicon; get rid of the file save-as box. */ + window_destroy(GTK_WIDGET(saveas_w)); + g_free(st_name); + return TRUE; +} +#endif /* USE_WIN32_FILE_DIALOGS */ + +static gboolean +save_as_dialog(GtkWidget *win _U_, stats_tree *st) +{ + gint sort_column= 1; /* default */ + GtkSortType order= GTK_SORT_DESCENDING; + GString *str_tree; + GString *file_name = g_string_new(""); + int file_type; + gchar *file_name_lower; + gchar *file_ext; + FILE *f; + gboolean success= FALSE; + int last_errno; + +#ifdef USE_WIN32_FILE_DIALOGS + if (win32_save_as_statstree(GDK_WINDOW_HWND(gtk_widget_get_window(st->pr->win)), + file_name, &file_type)) { +#else /* USE_WIN32_FILE_DIALOGS */ + if (gtk_save_as_statstree(st->pr->win,file_name,&file_type)) { +#endif /* USE_WIN32_FILE_DIALOGS */ + + /* add file extension as required */ + file_name_lower = g_utf8_strdown(file_name->str, -1); + file_ext= file_type==ST_FORMAT_XML?".xml":(file_type==ST_FORMAT_CSV?".csv":".txt"); + if (!g_str_has_suffix(file_name_lower, file_ext)) { + /* Must add extenstion */ + g_string_append(file_name,file_ext); + } + g_free(file_name_lower); + + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (st->pr->store), &sort_column, &order); + str_tree=stats_tree_format_as_str(st,file_type,sort_column-N_RESERVED_COL,order==GTK_SORT_DESCENDING); + + /* actually save the file */ + f= fopen (file_name->str,"w"); + last_errno= errno; + if (f) { + if (fputs(str_tree->str, f)!=EOF) { + success= TRUE; + } + last_errno= errno; + fclose(f); + } + if (!success) { + GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(st->pr->win), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "Error saving file '%s': %s", + file_name->str, g_strerror (last_errno)); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + + g_string_free(str_tree, TRUE); + } + + g_string_free(file_name, TRUE); + + return TRUE; } static void @@ -169,11 +346,37 @@ reset_tap(void* p) { stats_tree* st = (stats_tree *)p; stat_node* c; + for (c = st->root.children; c; c = c->next) { clear_node_pr(c); } - st->cfg->init(st); + stats_tree_reinit(st); +/* st->cfg->init(st); doesn't properly delete nodes */ +} + +static gint +st_sort_func(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + gint sort_column= 1; /* default */ + GtkSortType order= GTK_SORT_DESCENDING; + stat_node *node_a; + stat_node *node_b; + gint result; + + gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (user_data), &sort_column, &order); + + gtk_tree_model_get(model, a, NODEPTR_COLUMN, &node_a, -1); + gtk_tree_model_get(model, b, NODEPTR_COLUMN, &node_b, -1); + + result= stats_tree_sort_compare(node_a,node_b,sort_column-N_RESERVED_COL,order==GTK_SORT_DESCENDING); + if (order==GTK_SORT_DESCENDING) { + result= -result; + } + return result; } /* initializes the stats_tree window */ @@ -189,9 +392,12 @@ init_gtk_tree(const char* opt_arg, void *userdata _U_) GString* error_string; GtkWidget *scr_win; size_t init_strlen; - GtkWidget *main_vb, *bbox, *bt_close; + GtkWidget *main_vb, *bbox, *bt_close, *bt_copy, *bt_saveas; GtkTreeViewColumn* column; GtkCellRenderer* renderer; + GtkTreeSortable *sortable; + GType *col_types; + int count; if (abbr) { cfg = stats_tree_get_cfg_by_abbr(abbr); @@ -230,17 +436,17 @@ init_gtk_tree(const char* opt_arg, void *userdata _U_) cfg->in_use = TRUE; - window_name = g_strdup_printf("%s Stats Tree", cfg->name); + window_name = g_strdup_printf("%s Stats Tree", st->display_name); st->pr->win = window_new_with_geom(GTK_WINDOW_TOPLEVEL,window_name,window_name); - gtk_window_set_default_size(GTK_WINDOW(st->pr->win), 400, 400); + gtk_window_set_default_size(GTK_WINDOW(st->pr->win), st->num_columns*80+80, 400); g_free(window_name); if(st->filter){ - title=g_strdup_printf("%s with filter: %s",cfg->name,st->filter); + title=g_strdup_printf("%s with filter: %s",st->display_name,st->filter); } else { st->filter=NULL; - title=g_strdup_printf("%s", cfg->name); + title=g_strdup_printf("%s", st->display_name); } gtk_window_set_title(GTK_WINDOW(st->pr->win), title); @@ -252,47 +458,36 @@ init_gtk_tree(const char* opt_arg, void *userdata _U_) scr_win = scrolled_window_new(NULL, NULL); - st->pr->store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING); + col_types= (GType*)g_malloc(sizeof(GType)*(st->num_columns+N_RESERVED_COL)); + col_types[0] = G_TYPE_POINTER; + for (count = 0; count<st->num_columns; count++) { + col_types[count+N_RESERVED_COL] = G_TYPE_STRING; + } + st->pr->store = gtk_tree_store_newv (st->num_columns+N_RESERVED_COL,col_types); + g_free (col_types); + sortable= GTK_TREE_SORTABLE (st->pr->store); st->pr->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (st->pr->store)); + gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(st->pr->tree), FALSE); g_object_unref(G_OBJECT(st->pr->store)); gtk_container_add( GTK_CONTAINER(scr_win), st->pr->tree); /* the columns */ + for (count = 0; count<st->num_columns; count++) { renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Topic / Item", renderer, - "text", TITLE_COLUMN, - NULL); - gtk_tree_view_column_set_resizable (column,TRUE); - gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column); - - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Count", renderer, - "text", COUNT_COLUMN, - NULL); - - gtk_tree_view_column_set_resizable (column,TRUE); - gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column); - - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Rate (ms)", renderer, - "text", RATE_COLUMN, - NULL); + column = gtk_tree_view_column_new_with_attributes (stats_tree_get_column_name(count), + renderer, "text", count+N_RESERVED_COL, NULL); + if (stats_tree_is_sortable_column(count)) { + gtk_tree_view_column_set_sort_column_id(column, count+N_RESERVED_COL); + gtk_tree_sortable_set_sort_func(sortable,count+N_RESERVED_COL, st_sort_func, sortable, NULL); + } gtk_tree_view_column_set_resizable (column,TRUE); gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column); + } - renderer = gtk_cell_renderer_text_new (); - column = gtk_tree_view_column_new_with_attributes ("Percent", renderer, - "text", PERCENT_COLUMN, - NULL); - gtk_tree_view_column_set_resizable(column,TRUE); - gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE); - gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column); + gtk_tree_sortable_set_default_sort_func (sortable, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(main_vb), scr_win, TRUE, TRUE, 0); @@ -312,7 +507,7 @@ init_gtk_tree(const char* opt_arg, void *userdata _U_) } /* Button row. */ - bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL); + bbox = dlg_button_row_new(GTK_STOCK_COPY, GTK_STOCK_SAVE_AS, GTK_STOCK_CLOSE, NULL); gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0); bt_close = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE); @@ -321,6 +516,12 @@ init_gtk_tree(const char* opt_arg, void *userdata _U_) g_signal_connect(GTK_WINDOW(st->pr->win), "delete_event", G_CALLBACK(window_delete_event_cb), NULL); g_signal_connect(GTK_WINDOW(st->pr->win), "destroy", G_CALLBACK(free_gtk_tree), st); + bt_copy = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY); + g_signal_connect(GTK_WINDOW (bt_copy), "clicked", G_CALLBACK(copy_tree_to_clipboard), st); + + bt_saveas = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS); + g_signal_connect(GTK_WINDOW (bt_saveas), "clicked", G_CALLBACK(save_as_dialog), st); + gtk_widget_show_all(st->pr->win); window_present(st->pr->win); @@ -336,17 +537,19 @@ static void register_gtk_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_) { stats_tree_cfg* cfg = (stats_tree_cfg *)v; + gchar* display_name= stats_tree_get_displayname(cfg->name); cfg->pr = (tree_cfg_pres *)g_malloc(sizeof(tree_cfg_pres)); cfg->pr->stat_dlg = (tap_param_dlg *)g_malloc(sizeof(tap_param_dlg)); - cfg->pr->stat_dlg->win_title = g_strdup_printf("%s Stats Tree",cfg->name); + cfg->pr->stat_dlg->win_title = g_strdup_printf("%s Stats Tree",display_name); cfg->pr->stat_dlg->init_string = g_strdup_printf("%s,tree",cfg->abbr); cfg->pr->stat_dlg->tap_init_cb = init_gtk_tree; cfg->pr->stat_dlg->index = -1; cfg->pr->stat_dlg->nparams = G_N_ELEMENTS(tree_stat_params); cfg->pr->stat_dlg->params = tree_stat_params; + g_free(display_name); } static void @@ -360,7 +563,7 @@ register_tap_listener_stats_tree_stat(void) { stats_tree_presentation(register_gtk_stats_tree_tap, - setup_gtk_node_pr, + NULL, NULL, NULL, NULL, diff --git a/ui/win32/file_dlg_win32.c b/ui/win32/file_dlg_win32.c index 7b4d829ed2..187e38eff8 100644 --- a/ui/win32/file_dlg_win32.c +++ b/ui/win32/file_dlg_win32.c @@ -95,6 +95,7 @@ static UINT_PTR CALLBACK open_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); static UINT_PTR CALLBACK save_as_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); +static UINT_PTR CALLBACK save_as_statstree_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); static UINT_PTR CALLBACK export_specified_packets_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); static UINT_PTR CALLBACK merge_file_hook_proc(HWND mf_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); static UINT_PTR CALLBACK export_file_hook_proc(HWND of_hwnd, UINT ui_msg, WPARAM w_param, LPARAM l_param); @@ -426,6 +427,72 @@ win32_save_as_file(HWND h_wnd, capture_file *cf, GString *file_name, int *file_t return gsfn_ok; } +gboolean win32_save_as_statstree(HWND h_wnd, GString *file_name, int *file_type) +{ + OPENFILENAME *ofn; + TCHAR file_name16[MAX_PATH] = _T(""); + int ofnsize; + gboolean gsfn_ok; +#if (_MSC_VER >= 1500) + OSVERSIONINFO osvi; +#endif + + if (!file_name || !file_type) + return FALSE; + + if (file_name->len > 0) { + StringCchCopy(file_name16, MAX_PATH, utf_8to16(file_name->str)); + } + + /* see OPENFILENAME comment in win32_open_file */ +#if (_MSC_VER >= 1500) + SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + if (osvi.dwMajorVersion >= 5) { + ofnsize = sizeof(OPENFILENAME); + } else { + ofnsize = OPENFILENAME_SIZE_VERSION_400; + } +#else + ofnsize = sizeof(OPENFILENAME) + 12; +#endif + ofn = g_malloc0(ofnsize); + + ofn->lStructSize = ofnsize; + ofn->hwndOwner = h_wnd; + ofn->hInstance = (HINSTANCE) GetWindowLongPtr(h_wnd, GWLP_HINSTANCE); + ofn->lpstrFilter = _T("Plain text file (.txt)\0*.txt\0Comma separated values (.csv)\0*.csv\0XML document (.xml)\0*.xml\0"); + ofn->lpstrCustomFilter = NULL; + ofn->nMaxCustFilter = 0; + ofn->nFilterIndex = 1; /* the first entry is the best match; 1-origin indexing */ + ofn->lpstrFile = file_name16; + ofn->nMaxFile = MAX_PATH; + ofn->lpstrFileTitle = NULL; + ofn->nMaxFileTitle = 0; + ofn->lpstrInitialDir = utf_8to16(get_last_open_dir()); + ofn->lpstrTitle = _T("Wireshark: Save stats tree as ..."); + ofn->Flags = OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_EXPLORER | + OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | + OFN_PATHMUSTEXIST | OFN_ENABLEHOOK; + ofn->lpstrDefExt = NULL; + ofn->lpfnHook = save_as_statstree_hook_proc; + ofn->lpTemplateName = _T("WIRESHARK_SAVEASSTATSTREENAME_TEMPLATE"); + + gsfn_ok = GetSaveFileName(ofn); + + if (gsfn_ok) { + g_string_printf(file_name, "%s", utf_16to8(file_name16)); + /* What file format was specified? */ + *file_type = ofn->nFilterIndex - 1; + } + + g_sf_hwnd = NULL; + g_free( (void *) ofn); + return gsfn_ok; +} + + gboolean win32_export_specified_packets_file(HWND h_wnd, capture_file *cf, GString *file_name, @@ -1705,6 +1772,26 @@ save_as_file_hook_proc(HWND sf_hwnd, UINT msg, WPARAM w_param, LPARAM l_param) { return 0; } +static UINT_PTR CALLBACK +save_as_statstree_hook_proc(HWND sf_hwnd, UINT msg, WPARAM w_param, LPARAM l_param) { + + switch(msg) { + case WM_INITDIALOG: + g_sf_hwnd = sf_hwnd; + break; + + case WM_COMMAND: + break; + + case WM_NOTIFY: + break; + + default: + break; + } + return 0; +} + #define RANGE_TEXT_MAX 128 static UINT_PTR CALLBACK export_specified_packets_file_hook_proc(HWND sf_hwnd, UINT msg, WPARAM w_param, LPARAM l_param) { diff --git a/ui/win32/file_dlg_win32.h b/ui/win32/file_dlg_win32.h index a317f49d9e..94662ee6e6 100644 --- a/ui/win32/file_dlg_win32.h +++ b/ui/win32/file_dlg_win32.h @@ -122,6 +122,17 @@ void win32_export_color_file(HWND h_wnd, capture_file *cf, gpointer filter_list) */ void win32_import_color_file(HWND h_wnd, gpointer color_filters); +/** Open the "Save As" dialog box for stats_tree statistics window. + * + * @param h_wnd HWND of the parent window. + * @param file_name File name. May be empty. + * @param file_type stats_tree file type. + * + * @return FALSE if the dialog was cancelled + */ +gboolean win32_save_as_statstree(HWND h_wnd, GString *file_name, + int *file_type); + void file_set_save_marked_sensitive(); /* Open dialog defines */ |