Add a Find Next/Previous menu option to the ServiceResponseTimeStatistics dialog
[obnox/wireshark/wip.git] / 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  * $Id: service_response_time_table.c,v 1.8 2003/10/10 08:52:19 sahlberg Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <math.h>
35 #include <gtk/gtk.h>
36 #include "compat_macros.h"
37 #include "epan/packet_info.h"
38 #include "service_response_time_table.h"
39 #include "image/clist_ascend.xpm"
40 #include "image/clist_descend.xpm"
41 #include "simple_dialog.h"
42 #include "globals.h"
43 #include "gtk/find_dlg.h"
44
45 extern GtkWidget   *main_display_filter_widget;
46 #define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
47
48
49 typedef struct column_arrows {
50         GtkWidget *table;
51         GtkWidget *ascend_pm;
52         GtkWidget *descend_pm;
53 } column_arrows;
54
55
56 static void
57 srt_click_column_cb(GtkCList *clist, gint column, gpointer data)
58 {
59         column_arrows *col_arrows = (column_arrows *) data;
60         int i;
61
62         gtk_clist_freeze(clist);
63
64         for (i = 0; i < 6; i++) {
65                 gtk_widget_hide(col_arrows[i].ascend_pm);
66                 gtk_widget_hide(col_arrows[i].descend_pm);
67         }
68
69         if (column == clist->sort_column) {
70                 if (clist->sort_type == GTK_SORT_ASCENDING) {
71                         clist->sort_type = GTK_SORT_DESCENDING;
72                         gtk_widget_show(col_arrows[column].descend_pm);
73                 } else {
74                         clist->sort_type = GTK_SORT_ASCENDING;
75                         gtk_widget_show(col_arrows[column].ascend_pm);
76                 }
77         } else {
78                 /* Columns 2-5   Count, Min, Max, Avg are sorted in descending
79                         order by default.
80                    Columns 0 and 1 sort by ascending order by default
81                 */
82                 if(column>=2){
83                         clist->sort_type = GTK_SORT_DESCENDING;
84                         gtk_widget_show(col_arrows[column].descend_pm);
85                 } else {
86                         clist->sort_type = GTK_SORT_ASCENDING;
87                         gtk_widget_show(col_arrows[column].ascend_pm);
88                 }
89                 gtk_clist_set_sort_column(clist, column);
90         }
91         gtk_clist_thaw(clist);
92
93         gtk_clist_sort(clist);
94 }
95
96 static gint
97 srt_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
98 {
99         char *text1 = NULL;
100         char *text2 = NULL;
101         int i1, i2;
102         float f1,f2;
103
104         GtkCListRow *row1 = (GtkCListRow *) ptr1;
105         GtkCListRow *row2 = (GtkCListRow *) ptr2;
106
107         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
108         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
109
110         switch(clist->sort_column){
111         case 1:
112                 return strcmp (text1, text2);
113         case 0:
114         case 2:
115                 i1=atoi(text1);
116                 i2=atoi(text2);
117                 return i1-i2;
118         case 3:
119         case 4:
120         case 5:
121                 sscanf(text1,"%f",&f1);
122                 sscanf(text2,"%f",&f2);
123                 if(fabs(f1-f2)<0.000005)
124                         return 0;
125                 if(f1>f2)
126                         return 1;
127                 return -1;
128         }
129         g_assert_not_reached();
130         return 0;
131 }
132
133
134
135 /* action is encoded as 
136    filter_action*256+filter_type
137
138    filter_action:
139         0: Match
140         1: Prepare
141         2: Find Frame
142         3:   Find Next
143         4:   Find Previous
144    filter_type:
145         0: Selected
146         1: Not Selected
147         2: And Selected
148         3: Or Selected
149         4: And Not Selected
150         5: Or Not Selected
151 */
152 static void
153 srt_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
154 {
155         int action, type, selection;
156         srt_stat_table *rst = (srt_stat_table *)callback_data;
157         char str[256];
158         char *current_filter;
159
160
161         if(rst->filter_string==NULL){
162                 return;
163         }
164
165         action=(callback_action>>8)&0xff;
166         type=callback_action&0xff;
167
168         selection=GPOINTER_TO_INT(g_list_nth_data(GTK_CLIST(rst->table)->selection, 0));
169         if(selection>=(int)rst->num_procs){
170                 simple_dialog(ESD_TYPE_WARN, NULL, "No procedure selected");
171                 return;
172         }
173         /* translate it back from row index to index in procedures array */
174         selection=GPOINTER_TO_INT(gtk_clist_get_row_data(rst->table, selection));
175
176         current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
177
178         switch(type){
179         case 0:
180                 /* selected */
181                 snprintf(str, 255, "%s==%d", rst->filter_string, selection);
182                 break;
183         case 1:
184                 /* not selected */
185                 snprintf(str, 255, "!(%s==%d)", rst->filter_string, selection);
186                 break;
187         case 2:
188                 /* and selected */
189                 snprintf(str, 255, "(%s) && (%s==%d)", current_filter, rst->filter_string, selection);
190                 break;
191         case 3:
192                 /* or selected */
193                 snprintf(str, 255, "(%s) || (%s==%d)", current_filter, rst->filter_string, selection);
194                 break;
195         case 4:
196                 /* and not selected */
197                 snprintf(str, 255, "(%s) && !(%s==%d)", current_filter, rst->filter_string, selection);
198                 break;
199         case 5:
200                 /* or not selected */
201                 snprintf(str, 255, "(%s) || !(%s==%d)", current_filter, rst->filter_string, selection);
202                 break;
203         }
204
205         gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
206
207         switch(action){
208         case 0:
209                 /* match */
210                 filter_packets(&cfile, str);
211         case 1:
212                 /* prepare */
213                 /* do nothing */
214                 break;
215         case 2:
216                 /* find frame */
217                 find_frame_with_filter(str);
218                 break;
219         case 3:
220                 /* find next */
221                 find_previous_next_frame_with_filter(str, FALSE);
222                 break;
223         case 4:
224                 /* find previous */
225                 find_previous_next_frame_with_filter(str, TRUE);
226                 break;
227         }
228
229 }
230
231 static gint
232 srt_show_popup_menu_cb(void *widg _U_, GdkEvent *event, srt_stat_table *rst)
233 {
234         GdkEventButton *bevent = (GdkEventButton *)event;
235
236         if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
237                 gtk_menu_popup(GTK_MENU(rst->menu), NULL, NULL, NULL, NULL, 
238                         bevent->button, bevent->time);
239         }
240
241         return FALSE;
242 }
243
244 static GtkItemFactoryEntry srt_list_menu_items[] =
245 {
246         /* Match */
247         ITEM_FACTORY_ENTRY("/Match Display Filter", NULL, NULL, 0, "<Branch>", NULL),
248         ITEM_FACTORY_ENTRY("/Match Display Filter/Selected", NULL,
249                 srt_select_filter_cb, 0*256+0, NULL, NULL),
250         ITEM_FACTORY_ENTRY("/Match Display Filter/Not Selected", NULL,
251                 srt_select_filter_cb, 0*256+1, NULL, NULL),
252         ITEM_FACTORY_ENTRY("/Match Display Filter/And Selected", NULL,
253                 srt_select_filter_cb, 0*256+2, NULL, NULL),
254         ITEM_FACTORY_ENTRY("/Match Display Filter/Or Selected", NULL,
255                 srt_select_filter_cb, 0*256+3, NULL, NULL),
256         ITEM_FACTORY_ENTRY("/Match Display Filter/And Not Selected", NULL,
257                 srt_select_filter_cb, 0*256+4, NULL, NULL),
258         ITEM_FACTORY_ENTRY("/Match Display Filter/Or Not Selected", NULL,
259                 srt_select_filter_cb, 0*256+5, NULL, NULL),
260
261         /* Prepare */
262         ITEM_FACTORY_ENTRY("/Prepare Display Filter", NULL, NULL, 0, "<Branch>", NULL),
263         ITEM_FACTORY_ENTRY("/Prepare Display Filter/Selected", NULL,
264                 srt_select_filter_cb, 1*256+0, NULL, NULL),
265         ITEM_FACTORY_ENTRY("/Prepare Display Filter/Not Selected", NULL,
266                 srt_select_filter_cb, 1*256+1, NULL, NULL),
267         ITEM_FACTORY_ENTRY("/Prepare Display Filter/And Selected", NULL,
268                 srt_select_filter_cb, 1*256+2, NULL, NULL),
269         ITEM_FACTORY_ENTRY("/Prepare Display Filter/Or Selected", NULL,
270                 srt_select_filter_cb, 1*256+3, NULL, NULL),
271         ITEM_FACTORY_ENTRY("/Prepare Display Filter/And Not Selected", NULL,
272                 srt_select_filter_cb, 1*256+4, NULL, NULL),
273         ITEM_FACTORY_ENTRY("/Prepare Display Filter/Or Not Selected", NULL,
274                 srt_select_filter_cb, 1*256+5, NULL, NULL),
275
276         /* Find Frame */
277         ITEM_FACTORY_ENTRY("/Find Frame", NULL, NULL, 0, "<Branch>", NULL),
278         ITEM_FACTORY_ENTRY("/Find Frame/Find Frame", NULL, NULL, 0, "<Branch>", NULL),
279         ITEM_FACTORY_ENTRY("/Find Frame/Find Frame/Selected", NULL,
280                 srt_select_filter_cb, 2*256+0, NULL, NULL),
281         ITEM_FACTORY_ENTRY("/Find Frame/Find Frame/Not Selected", NULL,
282                 srt_select_filter_cb, 2*256+1, NULL, NULL),
283         /* Find Next */
284         ITEM_FACTORY_ENTRY("/Find Frame/Find Next", NULL, NULL, 0, "<Branch>", NULL),
285         ITEM_FACTORY_ENTRY("/Find Frame/Find Next/Selected", NULL,
286                 srt_select_filter_cb, 3*256+0, NULL, NULL),
287         ITEM_FACTORY_ENTRY("/Find Frame/Find Next/Not Selected", NULL,
288                 srt_select_filter_cb, 3*256+1, NULL, NULL),
289
290         ITEM_FACTORY_ENTRY("/Find Frame/Find Previous", NULL, NULL, 0, "<Branch>", NULL),
291         ITEM_FACTORY_ENTRY("/Find Frame/Find Previous/Selected", NULL,
292                 srt_select_filter_cb, 4*256+0, NULL, NULL),
293         ITEM_FACTORY_ENTRY("/Find Frame/Find Previous/Not Selected", NULL,
294                 srt_select_filter_cb, 4*256+1, NULL, NULL),
295
296 };
297
298 static void
299 srt_create_popup_menu(srt_stat_table *rst)
300 {
301         GtkItemFactory *item_factory;
302
303         item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
304
305         gtk_item_factory_create_items_ac(item_factory, sizeof(srt_list_menu_items)/sizeof(srt_list_menu_items[0]), srt_list_menu_items, rst, 2);
306
307         rst->menu = gtk_item_factory_get_widget(item_factory, "<main>");
308         SIGNAL_CONNECT(rst->table, "button_press_event", srt_show_popup_menu_cb, rst);
309 }
310
311
312 void
313 init_srt_table(srt_stat_table *rst, int num_procs, GtkWidget *vbox, char *filter_string)
314 {
315         int i, j;
316         column_arrows *col_arrows;
317         GdkBitmap *ascend_bm, *descend_bm;
318         GdkPixmap *ascend_pm, *descend_pm;
319         GtkStyle *win_style;
320         GtkWidget *column_lb;
321         char *default_titles[] = { "Index", "Procedure", "Calls", "Min SRT", "Max SRT", "Avg SRT" };
322
323
324         if(filter_string){
325                 rst->filter_string=g_strdup(filter_string);
326         } else {
327                 rst->filter_string=NULL;
328         }
329         rst->scrolled_window=gtk_scrolled_window_new(NULL, NULL);
330         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(rst->scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
331         gtk_box_pack_start(GTK_BOX(vbox), rst->scrolled_window, TRUE, TRUE, 0);
332
333         rst->table=(GtkCList *)gtk_clist_new(6);
334
335         gtk_widget_show(GTK_WIDGET(rst->table));
336         gtk_widget_show(rst->scrolled_window);
337
338         col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * 6);
339         win_style = gtk_widget_get_style(rst->scrolled_window);
340         ascend_pm = gdk_pixmap_create_from_xpm_d(rst->scrolled_window->window,
341                         &ascend_bm,
342                         &win_style->bg[GTK_STATE_NORMAL],
343                         (gchar **)clist_ascend_xpm);
344         descend_pm = gdk_pixmap_create_from_xpm_d(rst->scrolled_window->window,
345                         &descend_bm,
346                         &win_style->bg[GTK_STATE_NORMAL],
347                         (gchar **)clist_descend_xpm);
348         for (i = 0; i < 6; i++) {
349                 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
350                 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
351                 column_lb = gtk_label_new(default_titles[i]);
352                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
353                 gtk_widget_show(column_lb);
354
355                 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
356                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
357                 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
358                 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
359                 if (i == 2) {
360                         gtk_widget_show(col_arrows[i].descend_pm);
361                 }
362                 gtk_clist_set_column_widget(GTK_CLIST(rst->table), i, col_arrows[i].table);
363                 gtk_widget_show(col_arrows[i].table);
364         }
365         gtk_clist_column_titles_show(GTK_CLIST(rst->table));
366
367         gtk_clist_set_compare_func(rst->table, srt_sort_column);
368         gtk_clist_set_sort_column(rst->table, 2);
369         gtk_clist_set_sort_type(rst->table, GTK_SORT_DESCENDING);
370
371
372         /*XXX instead of this we should probably have some code to
373                 dynamically adjust the width of the columns */
374         gtk_clist_set_column_width(rst->table, 0, 32);
375         gtk_clist_set_column_width(rst->table, 1, 160);
376         gtk_clist_set_column_width(rst->table, 2, 50);
377         gtk_clist_set_column_width(rst->table, 3, 60);
378         gtk_clist_set_column_width(rst->table, 4, 60);
379         gtk_clist_set_column_width(rst->table, 5, 60);
380
381         gtk_clist_set_shadow_type(rst->table, GTK_SHADOW_IN);
382         gtk_clist_column_titles_show(rst->table);
383         gtk_container_add(GTK_CONTAINER(rst->scrolled_window), (GtkWidget *)rst->table);
384
385         SIGNAL_CONNECT(rst->table, "click-column", srt_click_column_cb, col_arrows);
386
387         gtk_widget_show(GTK_WIDGET(rst->table));
388         gtk_widget_show(rst->scrolled_window);
389
390
391         rst->num_procs=num_procs;
392         rst->procedures=g_malloc(sizeof(srt_procedure_t)*num_procs);
393         for(i=0;i<num_procs;i++){
394                 rst->procedures[i].num=0;
395                 rst->procedures[i].min.secs=0;
396                 rst->procedures[i].min.nsecs=0;
397                 rst->procedures[i].max.secs=0;
398                 rst->procedures[i].max.nsecs=0;
399                 rst->procedures[i].tot.secs=0;
400                 rst->procedures[i].tot.nsecs=0;
401                 for(j=0;j<6;j++){
402                         rst->procedures[i].entries[j]=NULL;
403                 }
404         }
405
406         /* create popup menu for this table */
407         if(rst->filter_string){
408                 srt_create_popup_menu(rst);
409         }
410 }
411
412 void
413 init_srt_table_row(srt_stat_table *rst, int index, char *procedure)
414 {
415         char str[10];
416
417
418         sprintf(str,"%d",index);
419         rst->procedures[index].entries[0]=g_strdup(str);
420
421         rst->procedures[index].entries[1]=g_strdup(procedure);
422
423         rst->procedures[index].entries[2]=g_strdup("0");
424         rst->procedures[index].entries[3]=g_strdup("0");
425         rst->procedures[index].entries[4]=g_strdup("0");
426         rst->procedures[index].entries[5]=g_strdup("0");
427
428         gtk_clist_insert(rst->table, index, rst->procedures[index].entries);
429         gtk_clist_set_row_data(rst->table, index, (gpointer) index);
430 }
431
432 void
433 add_srt_table_data(srt_stat_table *rst, int index, nstime_t *req_time, packet_info *pinfo)
434 {
435         srt_procedure_t *rp;
436         nstime_t delta;
437
438         rp=&rst->procedures[index];
439
440         /* calculate time delta between request and reply */
441         delta.secs=pinfo->fd->abs_secs-req_time->secs;
442         delta.nsecs=pinfo->fd->abs_usecs*1000-req_time->nsecs;
443         if(delta.nsecs<0){
444                 delta.nsecs+=1000000000;
445                 delta.secs--;
446         }
447
448         if(rp->num==0){
449                 rp->max.secs=delta.secs;
450                 rp->max.nsecs=delta.nsecs;
451         }
452
453         if(rp->num==0){
454                 rp->min.secs=delta.secs;
455                 rp->min.nsecs=delta.nsecs;
456         }
457
458         if( (delta.secs<rp->min.secs)
459         ||( (delta.secs==rp->min.secs)
460           &&(delta.nsecs<rp->min.nsecs) ) ){
461                 rp->min.secs=delta.secs;
462                 rp->min.nsecs=delta.nsecs;
463         }
464
465         if( (delta.secs>rp->max.secs)
466         ||( (delta.secs==rp->max.secs)
467           &&(delta.nsecs>rp->max.nsecs) ) ){
468                 rp->max.secs=delta.secs;
469                 rp->max.nsecs=delta.nsecs;
470         }
471
472         rp->tot.secs += delta.secs;
473         rp->tot.nsecs += delta.nsecs;
474         if(rp->tot.nsecs>1000000000){
475                 rp->tot.nsecs-=1000000000;
476                 rp->tot.secs++;
477         }
478
479         rp->num++;
480 }
481
482 void
483 draw_srt_table_data(srt_stat_table *rst)
484 {
485         int i,j;
486 #ifdef G_HAVE_UINT64
487         guint64 td;
488 #else
489         guint32 td;
490 #endif
491         char str[256], *strp;
492
493         for(i=0;i<rst->num_procs;i++){
494                 /* scale it to units of 10us.*/
495                 /* for long captures with a large tot time, this can overflow on 32bit */
496                 td=(int)rst->procedures[i].tot.secs;
497                 td=td*100000+(int)rst->procedures[i].tot.nsecs/10000;
498                 if(rst->procedures[i].num){
499                         td/=rst->procedures[i].num;
500                 } else {
501                         td=0;
502                 }
503
504                 j=gtk_clist_find_row_from_data(rst->table, (gpointer)i);
505
506                 sprintf(str,"%d", rst->procedures[i].num);
507                 strp=g_strdup(str);
508                 gtk_clist_set_text(rst->table, j, 2, strp);
509                 g_free(rst->procedures[i].entries[2]);
510                 rst->procedures[i].entries[2]=strp;
511
512
513                 sprintf(str,"%3d.%05d", (int)rst->procedures[i].min.secs,rst->procedures[i].min.nsecs/10000);
514                 strp=g_strdup(str);
515                 gtk_clist_set_text(rst->table, j, 3, strp);
516                 g_free(rst->procedures[i].entries[3]);
517                 rst->procedures[i].entries[3]=strp;
518
519
520                 sprintf(str,"%3d.%05d", (int)rst->procedures[i].max.secs,rst->procedures[i].max.nsecs/10000);
521                 strp=g_strdup(str);
522                 gtk_clist_set_text(rst->table, j, 4, strp);
523                 g_free(rst->procedures[i].entries[4]);
524                 rst->procedures[i].entries[4]=strp;
525
526                 sprintf(str,"%3d.%05d", td/100000, td%100000);
527                 strp=g_strdup(str);
528                 gtk_clist_set_text(rst->table, j, 5, strp);
529                 g_free(rst->procedures[i].entries[5]);
530                 rst->procedures[i].entries[5]=strp;
531         }
532
533         gtk_clist_sort(rst->table);
534 }
535
536
537 void
538 reset_srt_table_data(srt_stat_table *rst)
539 {
540         int i;
541
542         for(i=0;i<rst->num_procs;i++){
543                 rst->procedures[i].num=0;
544                 rst->procedures[i].min.secs=0;
545                 rst->procedures[i].min.nsecs=0;
546                 rst->procedures[i].max.secs=0;
547                 rst->procedures[i].max.nsecs=0;
548                 rst->procedures[i].tot.secs=0;
549                 rst->procedures[i].tot.nsecs=0;
550         }
551 }
552
553 void
554 free_srt_table_data(srt_stat_table *rst)
555 {
556         int i,j;
557
558         for(i=0;i<rst->num_procs;i++){
559                 for(j=0;j<6;j++){
560                         if(rst->procedures[i].entries[j]){
561                                 g_free(rst->procedures[i].entries[j]);
562                                 rst->procedures[i].entries[j]=NULL;
563                         }
564                 }
565         }
566         g_free(rst->filter_string);
567         rst->filter_string=NULL;
568         g_free(rst->procedures);
569         rst->procedures=NULL;
570         rst->num_procs=0;
571 }
572