de4692b58abd3edf15395dcb6120dc49696d5da9
[jlayton/wireshark.git] / ui / gtk / service_response_time_table.c
1 /* service_response_time_table.c
2  * service_response_time_table   2003 Ronnie Sahlberg
3  * Helper routines common to all service response time statistics
4  * tap.
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <gtk/gtk.h>
28
29 #include "epan/packet_info.h"
30 #include "epan/proto.h"
31
32 #include "ui/simple_dialog.h"
33 #include <wsutil/utf8_entities.h>
34
35 #include "ui/gtk/filter_utils.h"
36 #include "ui/gtk/gui_utils.h"
37 #include "ui/gtk/dlg_utils.h"
38 #include "ui/gtk/service_response_time_table.h"
39 #include "ui/gtk/tap_param_dlg.h"
40 #include "ui/gtk/main.h"
41
42 /* XXX - Part of temporary hack */
43 #include "epan/conversation.h"
44 #include "epan/dissectors/packet-scsi.h"
45
46 #define NANOSECS_PER_SEC 1000000000
47
48 enum
49 {
50         INDEX_COLUMN,
51         PROCEDURE_COLUMN,
52         CALLS_COLUMN,
53         MIN_SRT_COLUMN,
54         MAX_SRT_COLUMN,
55         AVG_SRT_COLUMN,
56         SUM_SRT_COLUMN,
57         N_COLUMNS
58 };
59
60 typedef struct _srt_t {
61         const char *type;
62         const char *filter;
63         gtk_srt_t gtk_data;
64         register_srt_t* srt;
65         srt_data_t data;
66 } srt_t;
67
68
69 static void
70 srt_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
71 {
72         gtk_srt_table_t *rst_table = (gtk_srt_table_t*)callback_data;
73         srt_stat_table* rst = rst_table->rst;
74         char *str = NULL;
75         GtkTreeIter iter;
76         GtkTreeModel *model;
77         GtkTreeSelection  *sel;
78         int selection;
79
80         if(rst->filter_string==NULL){
81                 return;
82         }
83
84         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(rst_table->table));
85
86         if (!gtk_tree_selection_get_selected(sel, &model, &iter))
87                 return;
88
89         gtk_tree_model_get (model, &iter, SRT_COLUMN_INDEX, &selection, -1);
90         if(selection>=(int)rst->num_procs){
91                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No procedure selected");
92                 return;
93         }
94
95         str = g_strdup_printf("%s==%d", rst->filter_string, selection);
96
97         apply_selected_filter (callback_action, str);
98
99         g_free(str);
100 }
101
102 static gboolean
103 srt_show_popup_menu_cb(void *widg _U_, GdkEvent *event, gtk_srt_table_t *rst)
104 {
105         GdkEventButton *bevent = (GdkEventButton *)event;
106
107         if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
108                 gtk_menu_popup(GTK_MENU(rst->menu), NULL, NULL, NULL, NULL,
109                         bevent->button, bevent->time);
110         }
111
112         return FALSE;
113 }
114
115
116 /* Action callbacks */
117 static void
118 apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
119 {
120         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
121 }
122 static void
123 apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
124 {
125         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
126 }
127 static void
128 apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
129 {
130         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
131 }
132 static void
133 apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
134 {
135         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
136 }
137 static void
138 apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
139 {
140         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
141 }
142 static void
143 apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
144 {
145         srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
146 }
147
148 static void
149 prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
150 {
151         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
152 }
153 static void
154 prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
155 {
156         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
157 }
158 static void
159 prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
160 {
161         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
162 }
163 static void
164 prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
165 {
166         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
167 }
168 static void
169 prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
170 {
171         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
172 }
173 static void
174 prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
175 {
176         srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
177 }
178
179 static void
180 find_selected_cb(GtkWidget *widget, gpointer user_data)
181 {
182         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
183 }
184 static void
185 find_not_selected_cb(GtkWidget *widget, gpointer user_data)
186 {
187         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
188 }
189 static void
190 find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
191 {
192         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
193 }
194 static void
195 find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
196 {
197         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
198 }
199 static void
200 find_next_selected_cb(GtkWidget *widget, gpointer user_data)
201 {
202         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
203 }
204 static void
205 find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
206 {
207         srt_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
208 }
209 static void
210 color_selected_cb(GtkWidget *widget, gpointer user_data)
211 {
212         srt_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
213 }
214 static void
215 color_not_selected_cb(GtkWidget *widget, gpointer user_data)
216 {
217         srt_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
218 }
219
220 static const char *ui_desc_service_resp_t_filter_popup =
221 "<ui>\n"
222 "  <popup name='ServiceRespTFilterPopup'>\n"
223 "    <menu action='/Apply as Filter'>\n"
224 "      <menuitem action='/Apply as Filter/Selected'/>\n"
225 "      <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
226 "      <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
227 "      <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
228 "      <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
229 "      <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
230 "    </menu>\n"
231 "    <menu action='/Prepare a Filter'>\n"
232 "      <menuitem action='/Prepare a Filter/Selected'/>\n"
233 "      <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
234 "      <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
235 "      <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
236 "      <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
237 "      <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
238 "    </menu>\n"
239 "    <menu action='/Find Frame'>\n"
240 "      <menu action='/Find Frame/Find Frame'>\n"
241 "        <menuitem action='/Find Frame/Selected'/>\n"
242 "        <menuitem action='/Find Frame/Not Selected'/>\n"
243 "      </menu>\n"
244 "      <menu action='/Find Frame/Find Next'>\n"
245 "        <menuitem action='/Find Next/Selected'/>\n"
246 "        <menuitem action='/Find Next/Not Selected'/>\n"
247 "      </menu>\n"
248 "      <menu action='/Find Frame/Find Previous'>\n"
249 "        <menuitem action='/Find Previous/Selected'/>\n"
250 "        <menuitem action='/Find Previous/Not Selected'/>\n"
251 "      </menu>\n"
252 "    </menu>\n"
253 "    <menu action='/Colorize Procedure'>\n"
254 "     <menuitem action='/Colorize Procedure/Selected'/>\n"
255 "     <menuitem action='/Colorize Procedure/Not Selected'/>\n"
256 "    </menu>\n"
257 "  </popup>\n"
258 "</ui>\n";
259
260 /*
261  * GtkActionEntry
262  * typedef struct {
263  *   const gchar     *name;
264  *   const gchar     *stock_id;
265  *   const gchar     *label;
266  *   const gchar     *accelerator;
267  *   const gchar     *tooltip;
268  *   GCallback  callback;
269  * } GtkActionEntry;
270  * const gchar *name;           The name of the action.
271  * const gchar *stock_id;       The stock id for the action, or the name of an icon from the icon theme.
272  * const gchar *label;          The label for the action. This field should typically be marked for translation,
273  *                              see gtk_action_group_set_translation_domain().
274  *                              If label is NULL, the label of the stock item with id stock_id is used.
275  * const gchar *accelerator;    The accelerator for the action, in the format understood by gtk_accelerator_parse().
276  * const gchar *tooltip;        The tooltip for the action. This field should typically be marked for translation,
277  *                              see gtk_action_group_set_translation_domain().
278  * GCallback callback;          The function to call when the action is activated.
279  *
280  */
281 static const GtkActionEntry service_resp_t__popup_entries[] = {
282         { "/Apply as Filter",                         NULL, "Apply as Filter",        NULL, NULL,                     NULL },
283         { "/Prepare a Filter",                        NULL, "Prepare a Filter",       NULL, NULL,                     NULL },
284         { "/Find Frame",                              NULL, "Find Frame",             NULL, NULL,                     NULL },
285         { "/Find Frame/Find Frame",                   NULL, "Find Frame",             NULL, NULL,                     NULL },
286         { "/Find Frame/Find Next",                    NULL, "Find Next" ,             NULL, NULL,                     NULL },
287         { "/Find Frame/Find Previous",                NULL, "Find Previous",          NULL, NULL,                     NULL },
288         { "/Colorize Procedure",                      NULL, "Colorize Procedure",     NULL, NULL,                     NULL },
289         { "/Apply as Filter/Selected",                NULL, "Selected",               NULL, "Selected",               G_CALLBACK(apply_as_selected_cb) },
290         { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected",       NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected",     NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected",     G_CALLBACK(apply_as_not_selected_cb) },
291         { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected",       NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected",     NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected",     G_CALLBACK(apply_as_and_selected_cb) },
292         { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected",            NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected",      NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected",      G_CALLBACK(apply_as_or_selected_cb) },
293         { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected",   NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(apply_as_and_not_selected_cb) },
294         { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected",        NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected",  NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected",  G_CALLBACK(apply_as_or_not_selected_cb) },
295         { "/Prepare a Filter/Selected",               NULL, "Selected",               NULL, "selcted",                G_CALLBACK(prep_as_selected_cb) },
296         { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected",      NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected",     NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected",     G_CALLBACK(prep_as_not_selected_cb) },
297         { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected",      NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected",     NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected",     G_CALLBACK(prep_as_and_selected_cb) },
298         { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected",       NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected",      NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected",      G_CALLBACK(prep_as_or_selected_cb) },
299         { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected",  NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(prep_as_and_not_selected_cb) },
300         { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected",   NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected",  NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected",  G_CALLBACK(prep_as_or_not_selected_cb) },
301         { "/Find Frame/Selected",                     NULL, "Selected",               NULL, "Selected",               G_CALLBACK(find_selected_cb) },
302         { "/Find Frame/Not Selected",                 NULL, "Not Selected",           NULL, "Not Selected",           G_CALLBACK(find_not_selected_cb) },
303         { "/Find Previous/Selected",                  NULL, "Selected",               NULL, "Selected",               G_CALLBACK(find_prev_selected_cb) },
304         { "/Find Previous/Not Selected",              NULL, "Not Selected",           NULL, "Not Selected",           G_CALLBACK(find_prev_not_selected_cb) },
305         { "/Find Next/Selected",                      NULL, "Selected",               NULL, "Selected",               G_CALLBACK(find_next_selected_cb) },
306         { "/Find Next/Not Selected",                  NULL, "Not Selected",           NULL, "Not Selected",           G_CALLBACK(find_next_not_selected_cb) },
307         { "/Colorize Procedure/Selected",             NULL, "Selected",               NULL, "Selected",               G_CALLBACK(color_selected_cb) },
308         { "/Colorize Procedure/Not Selected",         NULL, "Not Selected",           NULL, "Not Selected",           G_CALLBACK(color_not_selected_cb) },
309 };
310
311 static void
312 srt_create_popup_menu(gtk_srt_table_t* rst_table)
313 {
314         GtkUIManager *ui_manager;
315         GtkActionGroup *action_group;
316         GError *error = NULL;
317
318         action_group = gtk_action_group_new ("ServiceRespTFilterPopupActionGroup");
319         gtk_action_group_add_actions (action_group,                                             /* the action group */
320                                       (GtkActionEntry *)service_resp_t__popup_entries,          /* an array of action descriptions */
321                                       G_N_ELEMENTS(service_resp_t__popup_entries),      /* the number of entries */
322                                       rst_table);                                                                                       /* data to pass to the action callbacks */
323
324         ui_manager = gtk_ui_manager_new ();
325         gtk_ui_manager_insert_action_group (ui_manager,
326                 action_group,
327                 0); /* the position at which the group will be inserted */
328         gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_service_resp_t_filter_popup, -1, &error);
329         if (error != NULL)
330         {
331                 fprintf (stderr, "Warning: building service response time filter popup failed: %s\n",
332                          error->message);
333                 g_error_free (error);
334                 error = NULL;
335         }
336
337         rst_table->menu = gtk_ui_manager_get_widget(ui_manager, "/ServiceRespTFilterPopup");
338         g_signal_connect(rst_table->table, "button_press_event", G_CALLBACK(srt_show_popup_menu_cb), rst_table);
339 }
340
341 /* ---------------- */
342 static void
343 srt_time_func (GtkTreeViewColumn *column _U_,
344                GtkCellRenderer   *renderer,
345                GtkTreeModel      *model,
346                GtkTreeIter       *iter,
347                gpointer           user_data)
348 {
349          gchar *str;
350          nstime_t *data;
351
352          /* The col to get data from is in userdata */
353          gint data_column = GPOINTER_TO_INT(user_data);
354
355          gtk_tree_model_get(model, iter, data_column, &data, -1);
356          if (!data) {
357                  g_object_set(renderer, "text", "", NULL);
358                  return;
359          }
360          str = g_strdup_printf("%3d.%06d", (int)data->secs, (data->nsecs+500)/1000);
361          g_object_set(renderer, "text", str, NULL);
362          g_free(str);
363 }
364
365 static void
366 srt_avg_func (GtkTreeViewColumn *column _U_,
367               GtkCellRenderer   *renderer,
368               GtkTreeModel      *model,
369               GtkTreeIter       *iter,
370               gpointer           user_data)
371 {
372         gchar *str;
373         guint64 td;
374         gint data_column = GPOINTER_TO_INT(user_data);
375
376         gtk_tree_model_get(model, iter, data_column, &td, -1);
377         str=g_strdup_printf("%3d.%06d",
378                             (int)(td/1000000), (int)(td%1000000));
379         g_object_set(renderer, "text", str, NULL);
380         g_free(str);
381 }
382
383 static gint
384 srt_time_sort_func(GtkTreeModel *model,
385                    GtkTreeIter *a,
386                    GtkTreeIter *b,
387                    gpointer user_data)
388 {
389          nstime_t *ns_a;
390          nstime_t *ns_b;
391          gint ret = 0;
392          gint data_column = GPOINTER_TO_INT(user_data);
393
394          gtk_tree_model_get(model, a, data_column, &ns_a, -1);
395          gtk_tree_model_get(model, b, data_column, &ns_b, -1);
396
397         if (ns_a == ns_b) {
398                 ret = 0;
399         }
400         else if (ns_a == NULL || ns_b == NULL) {
401                 ret = (ns_a == NULL) ? -1 : 1;
402         }
403         else {
404                 ret = nstime_cmp(ns_a,ns_b);
405         }
406         return ret;
407 }
408
409 static void
410 srt_set_title(srt_t *ss)
411 {
412         gchar *str;
413
414         str = g_strdup_printf("%s Service Response Time statistics", proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(ss->srt))));
415         set_window_title(ss->gtk_data.win, str);
416         g_free(str);
417 }
418
419
420 static gtk_srt_table_t*
421 get_gtk_table_from_srt(srt_stat_table* rst, gtk_srt_t* gtk)
422 {
423         guint i;
424         gtk_srt_table_t* srt;
425
426         for (i = 0; i < gtk->gtk_srt_array->len; i++) {
427                 srt = g_array_index(gtk->gtk_srt_array, gtk_srt_table_t*, i);
428
429                 if (srt->rst == rst)
430                         return srt;
431         }
432
433         return NULL;
434 }
435
436 void
437 free_table_data(srt_stat_table* rst, void* gui_data)
438 {
439         gtk_srt_t* gtk_data = (gtk_srt_t*)gui_data;
440         gtk_srt_table_t* gtk_table = get_gtk_table_from_srt(rst, gtk_data);
441         g_assert(gtk_table);
442
443         g_free(gtk_table);
444 }
445
446 static void
447 win_destroy_cb(GtkWindow *win _U_, gpointer data)
448 {
449         srt_t *ss=(srt_t *)data;
450
451         remove_tap_listener(&ss->data);
452
453         free_srt_table(ss->srt, ss->data.srt_array, free_table_data, &ss->gtk_data);
454
455         g_free(ss);
456 }
457
458 void
459 init_gtk_srt_table(srt_stat_table* rst, void* gui_data)
460 {
461         int i;
462         GtkListStore *store;
463         GtkWidget *tree;
464         GtkTreeViewColumn *column;
465         GtkCellRenderer *renderer;
466         GtkTreeSortable *sortable;
467         GtkWidget *label;
468         GtkWidget *tab_page;
469         gtk_srt_t *ss = (gtk_srt_t*)gui_data;
470         GtkWidget *parent_box = ss->vbox;
471         GtkTreeSelection  *sel;
472         gtk_srt_table_t *gtk_table_data = g_new0(gtk_srt_table_t, 1);
473
474         /* Create GTK data for the table here */
475         gtk_table_data->rst = rst;
476         g_array_insert_val(ss->gtk_srt_array, ss->gtk_srt_array->len, gtk_table_data);
477
478         /* Create the label for the table here */
479         label=gtk_label_new(rst->name);
480         if (ss->main_nb == NULL)
481         {
482                 gtk_box_pack_start(GTK_BOX(ss->vbox), label, FALSE, FALSE, 0);
483         }
484         else
485         {
486                 GtkWidget *tab_label=gtk_label_new(rst->short_name);
487                 tab_page = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 6, FALSE);
488                 gtk_notebook_append_page(GTK_NOTEBOOK(ss->main_nb), tab_page, tab_label);
489                 gtk_box_pack_start(GTK_BOX(tab_page), label, FALSE, FALSE, 0);
490                 parent_box = tab_page;
491         }
492
493         /* Create the store */
494         store = gtk_list_store_new (NUM_SRT_COLUMNS,  /* Total number of columns */
495                                     G_TYPE_INT,         /* Index     */
496                                     G_TYPE_STRING,   /* Procedure */
497                                     G_TYPE_UINT,        /* Calls     */
498                                     G_TYPE_POINTER,  /* Min SRT   */
499                                     G_TYPE_POINTER,  /* Max SRT   */
500                                     G_TYPE_UINT64,   /* Avg SRT   */
501                                     G_TYPE_UINT64);  /* Sum SRT   */
502
503         /* Create a view */
504         tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
505         gtk_table_data->table = GTK_TREE_VIEW(tree);
506         sortable = GTK_TREE_SORTABLE(store);
507
508         /* The view now holds a reference.  We can get rid of our own reference */
509         g_object_unref (G_OBJECT (store));
510
511         for (i = 0; i < NUM_SRT_COLUMNS; i++) {
512                 renderer = gtk_cell_renderer_text_new ();
513                 if (i != SRT_COLUMN_PROCEDURE) {
514                         /* right align numbers */
515                         g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
516                 }
517                 g_object_set(renderer, "ypad", 0, NULL);
518                 switch (i) {
519                 case SRT_COLUMN_MIN:
520                 case SRT_COLUMN_MAX:
521                         column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, NULL);
522                         gtk_tree_view_column_set_cell_data_func(column, renderer, srt_time_func,  GINT_TO_POINTER(i), NULL);
523                         gtk_tree_sortable_set_sort_func(sortable, i, srt_time_sort_func, GINT_TO_POINTER(i), NULL);
524                         break;
525                 case SRT_COLUMN_AVG:
526                 case SRT_COLUMN_SUM:
527                         column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, NULL);
528                         gtk_tree_view_column_set_cell_data_func(column, renderer, srt_avg_func,  GINT_TO_POINTER(i), NULL);
529                         break;
530                 case PROCEDURE_COLUMN:
531                         column = gtk_tree_view_column_new_with_attributes ((rst->proc_column_name != NULL) ? rst->proc_column_name : service_response_time_get_column_name(i), renderer, "text",
532                                         i, NULL);
533                         break;
534                 default:
535                         column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, "text", i, NULL);
536                         break;
537                 }
538
539                 gtk_tree_view_column_set_sort_column_id(column, i);
540                 gtk_tree_view_column_set_resizable(column, TRUE);
541                 gtk_tree_view_append_column (gtk_table_data->table, column);
542                 if (i == SRT_COLUMN_CALLS) {
543                         /* XXX revert order sort */
544                         gtk_tree_view_column_clicked(column);
545                         gtk_tree_view_column_clicked(column);
546                 }
547         }
548
549         gtk_table_data->scrolled_window=scrolled_window_new(NULL, NULL);
550         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(gtk_table_data->scrolled_window),
551                                             GTK_SHADOW_IN);
552         gtk_container_add(GTK_CONTAINER(gtk_table_data->scrolled_window), GTK_WIDGET (gtk_table_data->table));
553         gtk_box_pack_start(GTK_BOX(parent_box), gtk_table_data->scrolled_window, TRUE, TRUE, 0);
554
555         gtk_tree_view_set_reorderable (gtk_table_data->table, FALSE);
556         /* Now enable the sorting of each column */
557         gtk_tree_view_set_rules_hint(gtk_table_data->table, TRUE);
558         gtk_tree_view_set_headers_clickable(gtk_table_data->table, TRUE);
559
560         gtk_widget_show(gtk_table_data->scrolled_window);
561
562         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_table_data->table));
563         gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
564
565         /* create popup menu for this table */
566         if(rst->filter_string){
567                 srt_create_popup_menu(gtk_table_data);
568         }
569 }
570
571 void
572 draw_srt_table_data(srt_stat_table *rst, gtk_srt_t* gtk_data)
573 {
574         int idx, new_idx;
575         GtkTreeIter iter;
576         gboolean first = TRUE;
577         gtk_srt_table_t* gtk_table;
578         GtkListStore *store;
579         gboolean iter_valid;
580
581         gtk_table = get_gtk_table_from_srt(rst, gtk_data);
582         g_assert(gtk_table);
583
584         store = GTK_LIST_STORE(gtk_tree_view_get_model(gtk_table->table));
585         iter_valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
586
587         new_idx = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
588
589         /* Update list items (which may not be in "idx" order), then add new items */
590         while (iter_valid || (new_idx < rst->num_procs)) {
591                 srt_procedure_t* procedure;
592                 guint64 td;
593                 guint64 sum;
594
595                 if (iter_valid) {
596                         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, INDEX_COLUMN, &idx, -1);
597                 } else {
598                         idx = new_idx;
599                         new_idx++;
600                 }
601
602                 procedure = &rst->procedures[idx];
603                 if ((procedure->procedure == NULL) || (procedure->stats.num == 0)) {
604                         iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
605                         continue;
606                 }
607
608                 if (first) {
609                         g_object_ref(store);
610                         gtk_tree_view_set_model(GTK_TREE_VIEW(gtk_table->table), NULL);
611
612                         first = FALSE;
613                 }
614
615                 /* Scale the average SRT in units of 1us and round to the nearest us.
616                     tot.secs is a time_t which may be 32 or 64 bits (or even floating)
617                     depending uon the platform.  After casting tot.secs to 64 bits, it
618                     would take a capture with a duration of over 136 *years* to
619                     overflow the secs portion of td. */
620                 td = ((guint64)(procedure->stats.tot.secs))*NANOSECS_PER_SEC + procedure->stats.tot.nsecs;
621                 sum = (td + 500) / 1000;
622                 td = ((td / procedure->stats.num) + 500) / 1000;
623
624                 if (iter_valid) {
625                         /* Existing row. Only changeable entries */
626
627                         gtk_list_store_set(store, &iter,
628                                                 PROCEDURE_COLUMN, procedure->procedure,
629                                                 CALLS_COLUMN,     procedure->stats.num,
630                                                 MIN_SRT_COLUMN,   &procedure->stats.min,
631                                                 MAX_SRT_COLUMN,   &procedure->stats.max,
632                                                 AVG_SRT_COLUMN,   td,
633                                                 SUM_SRT_COLUMN,   sum,
634                                                 -1);
635                 } else {
636                         /* New row. All entries, including fixed ones */
637                         gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
638                                                 PROCEDURE_COLUMN, procedure->procedure,
639                                                 CALLS_COLUMN,     procedure->stats.num,
640                                                 MIN_SRT_COLUMN,   &procedure->stats.min,
641                                                 MAX_SRT_COLUMN,   &procedure->stats.max,
642                                                 AVG_SRT_COLUMN,   td,
643                                                 SUM_SRT_COLUMN,   sum,
644                                                 INDEX_COLUMN,    idx,
645                                                 -1);
646                 }
647
648                 iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
649         }
650
651         if (!first) {
652                 gtk_tree_view_set_model(GTK_TREE_VIEW(gtk_table->table), GTK_TREE_MODEL(store));
653                 g_object_unref(store);
654         }
655 }
656
657 static void
658 srt_draw(void *arg)
659 {
660         guint i = 0;
661         srt_stat_table *srt_table;
662         srt_data_t *srt = (srt_data_t*)arg;
663         srt_t *ss = (srt_t*)srt->user_data;
664
665         for (i = 0; i < srt->srt_array->len; i++)
666         {
667                 srt_table = g_array_index(srt->srt_array, srt_stat_table*, i);
668                 draw_srt_table_data(srt_table, &ss->gtk_data);
669         }
670 }
671
672 void
673 reset_table_data(srt_stat_table* rst, void* gui_data)
674 {
675         GtkListStore *store;
676         gtk_srt_t* gtk_data = (gtk_srt_t*)gui_data;
677         gtk_srt_table_t* gtk_table = get_gtk_table_from_srt(rst, gtk_data);
678         g_assert(gtk_table);
679
680         store = GTK_LIST_STORE(gtk_tree_view_get_model(gtk_table->table));
681         gtk_list_store_clear(store);
682 }
683
684 static void
685 srt_reset(void *arg)
686 {
687         srt_data_t *srt = (srt_data_t*)arg;
688         srt_t *ss = (srt_t *)srt->user_data;
689
690         reset_srt_table(ss->data.srt_array, reset_table_data, &ss->gtk_data);
691
692         srt_set_title(ss);
693 }
694
695 static void
696 init_srt_tables(register_srt_t* srt, const char *filter)
697 {
698         srt_t *ss;
699         gchar *str;
700         GtkWidget *label;
701         char *filter_string, *tmp_filter_string;
702         gchar *error_string;
703         GtkWidget *bbox;
704         GtkWidget *close_bt;
705
706         ss = g_new0(srt_t, 1);
707
708         str = g_strdup_printf("%s-stat", proto_get_protocol_filter_name(get_srt_proto_id(srt)));
709         ss->gtk_data.win=dlg_window_new(str);  /* transient_for top_level */
710         g_free(str);
711         gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->gtk_data.win), TRUE);
712         gtk_window_set_default_size(GTK_WINDOW(ss->gtk_data.win), SRT_PREFERRED_WIDTH, 600);
713
714         str = g_strdup_printf("%s Service Response Time Statistics", proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))));
715         set_window_title(ss->gtk_data.win, str);
716
717         ss->gtk_data.vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
718         gtk_container_add(GTK_CONTAINER(ss->gtk_data.win), ss->gtk_data.vbox);
719         gtk_container_set_border_width(GTK_CONTAINER(ss->gtk_data.vbox), 12);
720
721         label=gtk_label_new(str);
722         gtk_box_pack_start(GTK_BOX(ss->gtk_data.vbox), label, FALSE, FALSE, 0);
723         g_free(str);
724
725         if ((filter != NULL) && (strlen(filter) > MAX_FILTER_STRING_LENGTH))
726         {
727                 tmp_filter_string = g_strndup(filter, MAX_FILTER_STRING_LENGTH);
728                 filter_string = g_strdup_printf("Filter: %s...", tmp_filter_string);
729                 g_free(tmp_filter_string);
730         }
731         else
732         {
733                 filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
734         }
735
736         label=gtk_label_new(filter_string);
737         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
738         gtk_widget_set_tooltip_text (label, filter ? filter : "");
739         g_free(filter_string);
740         gtk_box_pack_start(GTK_BOX(ss->gtk_data.vbox), label, FALSE, FALSE, 0);
741
742         /* up to 3 tables is reasonable real estate to display tables.  Any more than
743          *  that and we need to switch to a tab view
744          */
745         if (get_srt_max_tables(srt) > 3)
746         {
747                 ss->gtk_data.main_nb = gtk_notebook_new();
748                 gtk_box_pack_start(GTK_BOX(ss->gtk_data.vbox), ss->gtk_data.main_nb, TRUE, TRUE, 0);
749         }
750
751         /* We must display TOP LEVEL Widget before calling srt_table_dissector_init() */
752         gtk_widget_show_all(ss->gtk_data.win);
753
754         ss->type = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
755         ss->filter = g_strdup(filter);
756         ss->srt = srt;
757         ss->gtk_data.gtk_srt_array = g_array_new(FALSE, TRUE, sizeof(gtk_srt_table_t*));
758         ss->data.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*));
759         ss->data.user_data = ss;
760
761         srt_table_dissector_init(srt, ss->data.srt_array, init_gtk_srt_table, &ss->gtk_data);
762
763         error_string = register_tap_listener(get_srt_tap_listener_name(srt), &ss->data, filter, 0, srt_reset, get_srt_packet_func(srt), srt_draw);
764         if(error_string){
765                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string);
766                 wmem_free(NULL, error_string);
767                 free_srt_table(ss->srt, ss->data.srt_array, NULL, NULL);
768                 g_free(ss);
769                 return;
770         }
771
772         /* Button row. */
773         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
774         gtk_box_pack_end(GTK_BOX(ss->gtk_data.vbox), bbox, FALSE, FALSE, 0);
775
776         close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
777         window_set_cancel_button(ss->gtk_data.win, close_bt, window_cancel_button_cb);
778
779         g_signal_connect(ss->gtk_data.win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
780         g_signal_connect(ss->gtk_data.win, "destroy", G_CALLBACK(win_destroy_cb), ss);
781
782         gtk_widget_show_all(ss->gtk_data.win);
783         window_present(ss->gtk_data.win);
784
785         cf_retap_packets(&cfile);
786         gdk_window_raise(gtk_widget_get_window(ss->gtk_data.win));
787 }
788
789 static void
790 gtk_srtstat_init(const char *opt_arg, void *userdata _U_)
791 {
792         gchar** dissector_name;
793         register_srt_t *srt;
794         const char *filter=NULL;
795         char* err;
796
797         /* Use first comma to find dissector name */
798         dissector_name = g_strsplit(opt_arg, ",", -1);
799         g_assert(dissector_name[0]);
800
801         /* Use dissector name to find SRT table */
802         srt = get_srt_table_by_name(dissector_name[0]);
803         g_assert(srt);
804
805         srt_table_get_filter(srt, opt_arg, &filter, &err);
806
807         if (err != NULL)
808         {
809                 gchar* cmd_str = srt_table_get_tap_string(srt);
810                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "invalid \"-z %s,%s\" argument", cmd_str, err);
811                 g_free(cmd_str);
812                 g_free(err);
813                 return;
814         }
815
816         init_srt_tables(srt, filter);
817 }
818
819 static tap_param srt_stat_params[] = {
820         { PARAM_FILTER, "filter", "Filter", NULL, TRUE }
821 };
822
823 /* XXX - Temporary hack/workaround until a more generic approach can be implemented */
824 static const enum_val_t scsi_command_sets[] = {
825         { "sbc", "SBC (disk)",         SCSI_DEV_SBC },
826         { "ssc", "SSC (tape)",         SCSI_DEV_SSC },
827         { "mmc", "MMC (cd/dvd)",       SCSI_DEV_CDROM },
828         { "smc", "SMC (tape robot)",   SCSI_DEV_SMC },
829         { "osd", "OSD (object based)", SCSI_DEV_OSD },
830         { NULL, NULL, 0 }
831 };
832
833 static tap_param scsi_stat_params[] = {
834         { PARAM_ENUM,   "cmdset", "Command set", scsi_command_sets, FALSE },
835         { PARAM_FILTER, "filter", "Filter", NULL, TRUE }
836 };
837
838
839 void register_service_response_tables(gpointer data, gpointer user_data _U_)
840 {
841         register_srt_t *srt = (register_srt_t*)data;
842         const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
843         tap_param_dlg* srt_dlg;
844
845         /* XXX - These dissectors haven't been converted over to due to an "interactive input dialog" for their
846            tap data.  Let those specific dialogs register for themselves */
847         if ((strcmp(short_name, "RPC") == 0) ||
848                 (strcmp(short_name, "DCERPC") == 0))
849                 return;
850
851         srt_dlg = g_new(tap_param_dlg, 1);
852
853         srt_dlg->win_title = g_strdup_printf("%s SRT Statistics", short_name);
854         srt_dlg->init_string = srt_table_get_tap_string(srt);
855         srt_dlg->tap_init_cb = gtk_srtstat_init;
856         srt_dlg->index = -1;
857         srt_dlg->user_data = srt; /* TODO: Actually use this */
858         if (get_srt_proto_id(srt) == proto_get_id_by_filter_name("scsi"))
859         {
860                 srt_dlg->nparams = G_N_ELEMENTS(scsi_stat_params);
861                 srt_dlg->params = scsi_stat_params;
862         }
863         else
864         {
865                 srt_dlg->nparams = G_N_ELEMENTS(srt_stat_params);
866                 srt_dlg->params = srt_stat_params;
867         }
868
869         register_param_stat(srt_dlg, short_name, REGISTER_STAT_GROUP_RESPONSE_TIME);
870 }
871
872
873 /*
874  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
875  *
876  * Local variables:
877  * c-basic-offset: 8
878  * tab-width: 8
879  * indent-tabs-mode: t
880  * End:
881  *
882  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
883  * :indentSize=8:tabSize=8:noTabs=false:
884  */