using button compatibility macros
[obnox/wireshark/wip.git] / gtk / wsp_stat.c
1 /* wsp_stat.c
2  * wsp_stat   2003 Jean-Michel FAYARD
3  *
4  * $Id: wsp_stat.c,v 1.11 2004/01/10 16:27:43 ulfl Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /* #define DEBUG        do{ printf("%s:%d  ",__FILE__,__LINE__);} while(0); */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "epan/packet_info.h"
35 #include "epan/epan.h"
36 #include "menu.h"
37 #include "simple_dialog.h"
38 #include "dlg_utils.h"
39 #include "tap.h"
40 #include "../register.h"
41 #include "../globals.h"
42 #include "compat_macros.h"
43 #include "../packet-wsp.h"
44
45 /* used to keep track of the stats for a specific PDU type*/
46 typedef struct _wsp_pdu_t {
47         GtkLabel        *widget;
48         guint32          packets;
49 } wsp_pdu_t;
50
51 /* used to keep track of the statictics for an entire program interface */
52 typedef struct _wsp_stats_t {
53         char            *filter;
54         wsp_pdu_t       *pdu_stats;
55         guint32          num_pdus;
56         GtkWidget       *win;
57         GHashTable      *hash;
58         GtkWidget       *table_pdu_types;       
59         GtkWidget       *table_status_code;
60         guint            index; /* Number of status code to display */
61 } wspstat_t;
62 /* used to keep track of a single type of status code */
63 typedef struct _wsp_status_code_t {
64         gchar           *name;
65         guint32          packets;
66         GtkWidget       *widget;/* label in which we print the number of packets */
67         wspstat_t       *sp;    /* entire program interface */
68 } wsp_status_code_t;
69
70 static GtkWidget *dlg=NULL;
71 static GtkWidget *filter_entry;
72
73 static void
74 wsp_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
75 {
76         g_free(key);
77         g_free(value);
78 }
79 static void
80 wsp_reset_hash(gchar *key _U_ , wsp_status_code_t *data, gpointer ptr _U_ ) 
81 {       
82         data->packets = 0;
83 }
84
85 /* Update the entry corresponding to the number of packets of a special status code
86  * or create it if it don't exist.
87  */
88 static void
89 wsp_draw_statuscode(gchar *key _U_, wsp_status_code_t *data, gchar * string_buff )
90 {
91         if ((data==NULL) || (data->packets==0))
92                 return;
93         if (data->widget==NULL){        /* create an entry in the table */
94                 GtkWidget       *tmp;
95                 int x = 2*((data->sp->index) % 2);
96                 int y = (data->sp->index) /2;
97
98
99                 /* Maybe we should display the hexadecimal value ? */
100                 /* sprintf(string_buff, "%s  (0X%x)", data->name, *key); */
101                 tmp = gtk_label_new( data->name  /* string_buff */ );
102                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), tmp, x, x+1, y, y+1);
103                 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
104                 gtk_widget_show(tmp);
105
106                 sprintf( string_buff, "%9d", data->packets );
107                 data->widget = gtk_label_new( string_buff );
108                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), data->widget, x+1, x+2, y, y+1);
109                 gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_LEFT);
110                 gtk_widget_show( data->widget );
111
112                 data->sp->index++;
113         } else {
114                 /* Just update the label string */
115                 sprintf( string_buff, "%9d", data->packets );
116                 gtk_label_set( GTK_LABEL(data->widget), string_buff);
117         }
118 }
119 static void
120 wspstat_reset(void *psp)
121 {
122         wspstat_t *sp=psp;
123         guint32 i;
124
125         for(i=1;i<=sp->num_pdus;i++)
126         {
127                 sp->pdu_stats[i].packets=0;
128         }
129         g_hash_table_foreach( sp->hash, (GHFunc)wsp_reset_hash, NULL);  
130 }
131 static gint 
132 pdut2index(gint pdut)
133 {
134         if (pdut<=0x09)         return pdut;
135         if (pdut>=0x40){
136                 if (pdut <= 0x44){
137                         return pdut-54;
138                 } else if (pdut==0x60||pdut==0x61){
139                         return pdut-81;
140                 }
141         }
142         return 0;
143 }
144 static gint
145 index2pdut(gint pdut)
146 {
147         if (pdut<=0x09)
148                 return pdut;
149         if (pdut<=14)
150                 return pdut+54;
151         if (pdut<=16)
152                 return pdut+81;
153         return 0;
154 }
155
156 static int
157 wspstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
158 {
159         wspstat_t *sp=psp;
160         wsp_info_value_t *value=pri;
161         gint index = pdut2index(value->pdut);
162         int retour=0;
163
164         if (value->status_code != 0) {
165                 gint *key=g_malloc( sizeof(gint) );
166                 wsp_status_code_t *sc;
167                 *key=value->status_code ;
168                 sc = g_hash_table_lookup( 
169                                 sp->hash, 
170                                 key);
171                 if (!sc) {
172                         g_warning("%s:%d What's Wrong, doc ?\n", __FILE__, __LINE__);
173                         sc = g_malloc( sizeof(wsp_status_code_t) );
174                         sc -> packets = 1;
175                         sc -> name = NULL;
176                         sc -> widget=NULL;
177                         sc -> sp = sp;
178                         g_hash_table_insert(
179                                 sp->hash,
180                                 key,
181                                 sc);
182                 } else {
183                         sc->packets++;
184                 }
185                 retour=1;
186         }
187
188                 
189
190         if (index!=0) {
191                 sp->pdu_stats[ index ].packets++;
192                 retour = 1;
193         }
194         return retour;
195
196 }
197
198
199
200 static void
201 wspstat_draw(void *psp)
202 {
203         wspstat_t *sp=psp;
204         guint32 i;
205         char str[256];
206         guint index;
207
208         for(i=1;i<=sp->num_pdus ; i++)
209         {
210                 sprintf(str, "%9d",  sp->pdu_stats[i ].packets);
211                 gtk_label_set( GTK_LABEL(sp->pdu_stats[i].widget), str);
212         }
213
214         index=sp->index;
215         g_hash_table_foreach( sp->hash, (GHFunc) wsp_draw_statuscode, str );
216         if (index != sp->index){
217                 /* We have inserted a new entry corresponding to a status code ,
218                  * let's resize the table */
219                 gtk_table_resize ( GTK_TABLE(sp->table_status_code), sp->index  % 2 , 4);
220         }
221         
222 }
223
224
225
226 /* since the gtk2 implementation of tap is multithreaded we must protect
227  * remove_tap_listener() from modifying the list while draw_tap_listener()
228  * is running.  the other protected block is in main.c
229  *
230  * there should not be any other critical regions in gtk2
231  */
232 void protect_thread_critical_region(void);
233 void unprotect_thread_critical_region(void);
234 static void
235 win_destroy_cb(GtkWindow *win _U_, gpointer data)
236 {
237         wspstat_t *sp=(wspstat_t *)data;
238
239         protect_thread_critical_region();
240         remove_tap_listener(sp);
241         unprotect_thread_critical_region();
242
243         g_free(sp->pdu_stats);
244         g_free(sp->filter);
245         g_hash_table_foreach( sp->hash, (GHFunc)wsp_free_hash, NULL);
246         g_hash_table_destroy( sp->hash);        
247         g_free(sp);
248 }
249
250 static void
251 add_table_entry(wspstat_t *sp, char *str, int x, int y, int index)
252 {
253         GtkWidget *tmp;
254
255         tmp=gtk_label_new( str );
256         gtk_table_attach_defaults(GTK_TABLE(sp->table_pdu_types), tmp, x, x+1, y, y+1);
257         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
258         gtk_widget_show(tmp);
259         if (index != 0) {
260                 sp->pdu_stats [index] .widget = GTK_LABEL( tmp ) ;
261         }
262 }
263
264
265 static void
266 wsp_init_table(wspstat_t *sp)
267 {
268         int pos=0;
269         guint32 i;
270         /* gchar        buffer[51];     */
271         
272         add_table_entry( sp, "PDU Type               "  , 0, pos, 0);
273         add_table_entry( sp, "packets  "        , 1, pos, 0);
274         add_table_entry( sp, "PDU Type               "  , 2, pos, 0);
275         add_table_entry( sp, "packets  "        , 3, pos, 0);
276         pos++;
277         for (i=1 ; i <= sp->num_pdus ; i++ )
278         {
279                 int x = 0;
280                 if (i> (sp->num_pdus+1) /2 ){
281                         x=2;
282                 }
283                 /* Maybe we should display the hexadecimal value ? */
284                 /* snprintf(buffer, 50, "%s  (0X%x)", match_strval( index2pdut( i ), vals_pdu_type), index2pdut(i) );*/
285                 add_table_entry( sp, 
286                                 match_strval(index2pdut(i), vals_pdu_type), /* or buffer, */
287                                 x,
288                                 pos,
289                                 0
290                                 );
291                 add_table_entry( sp, "0", x+1, pos
292                                 , i /* keep a pointer to this widget to update it in _draw() */
293                                 );      
294                 pos++;
295                 if (i== (sp->num_pdus+1) /2) {
296                         pos=1;
297                 }
298         }
299 }
300
301 /* When called, this function will create a new instance of gtk2-wspstat.
302  */
303 static void
304 gtk_wspstat_init(char *optarg)
305 {
306         wspstat_t *sp;
307         char            *filter=NULL;
308         char            *title=NULL;
309         GString         *error_string;
310         GtkWidget       *main_vb, *pdutypes_fr, *statuscode_fr ;
311         guint32          i;
312         wsp_status_code_t *sc;
313         
314         
315         if (!strncmp (optarg, "wsp,stat,", 9)){
316                 filter=optarg+9;
317         } else {
318                 filter=NULL;
319         }
320         
321         sp = g_malloc( sizeof(wspstat_t) );
322         sp->hash = g_hash_table_new( g_int_hash, g_int_equal);
323         for (i=0 ; vals_status[i].strptr ; i++ )
324         {
325                 gint *key;
326                 sc=g_malloc( sizeof(wsp_status_code_t) );
327                 key=g_malloc( sizeof(gint) );
328                 sc->name=vals_status[i].strptr;
329                 sc->packets=0;
330                 sc->widget=NULL;
331                 sc->sp = sp;
332                 *key=vals_status[i].value;
333                 g_hash_table_insert(
334                                 sp->hash,
335                                 key,
336                                 sc);
337         }
338         sp->num_pdus = 16;
339         sp->pdu_stats=g_malloc( (sp->num_pdus+1) * sizeof( wsp_pdu_t) );
340         if(filter){
341                 sp->filter=g_malloc(strlen(filter)+1);
342                 strcpy(sp->filter,filter);
343                 title=g_strdup_printf("Ethereal: WSP statistics with filter: %s", filter);
344         } else {
345                 sp->filter=NULL;
346                 title=g_strdup("Ethereal: WSP statistics");
347         }
348         for (i=0;i<=sp->num_pdus; i++)
349         {
350                 sp->pdu_stats[i].packets=0;
351         }
352
353         sp->win = gtk_window_new( GTK_WINDOW_TOPLEVEL);
354         gtk_window_set_title( GTK_WINDOW(sp->win), title );
355         g_free(title);
356         SIGNAL_CONNECT( sp->win, "destroy", win_destroy_cb, sp);
357
358
359         /* container for the two frames */
360         main_vb = gtk_vbox_new(FALSE, 10);
361         gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
362         gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
363         gtk_widget_show(main_vb);
364
365         /* PDU Types frame */
366         pdutypes_fr = gtk_frame_new("Summary of PDU Types (wsp.pdu_type)");
367         gtk_container_add(GTK_CONTAINER(main_vb), pdutypes_fr);
368         gtk_widget_show(pdutypes_fr);
369         
370         sp->table_pdu_types = gtk_table_new( (sp->num_pdus+1) / 2 + 1, 4, FALSE);
371         gtk_container_add( GTK_CONTAINER( pdutypes_fr), sp->table_pdu_types);
372         gtk_container_set_border_width( GTK_CONTAINER(sp->table_pdu_types) , 10);
373
374         wsp_init_table(sp);
375         gtk_widget_show( sp->table_pdu_types );
376
377         /* Status Codes frame */
378         statuscode_fr = gtk_frame_new("Summary of Status Code (wsp.reply.status)");
379         gtk_container_add(GTK_CONTAINER(main_vb), statuscode_fr);
380         gtk_widget_show(statuscode_fr);
381         
382         sp->table_status_code = gtk_table_new( 0, 4, FALSE);
383         gtk_container_add( GTK_CONTAINER( statuscode_fr), sp->table_status_code);
384         gtk_container_set_border_width( GTK_CONTAINER(sp->table_status_code) , 10);
385         sp->index = 0;          /* No answers to display yet */
386
387
388         error_string = register_tap_listener( 
389                         "wsp",
390                         sp,
391                         filter,
392                         wspstat_reset,
393                         wspstat_packet,
394                         wspstat_draw);
395         if (error_string){
396                 /* error, we failed to attach to the tap. clean up */
397                 simple_dialog( ESD_TYPE_WARN, NULL, error_string->str );
398                 g_free(sp->pdu_stats);
399                 g_free(sp->filter);
400                 g_free(sp);
401                 g_string_free(error_string, TRUE);
402                 return ;
403         }
404         gtk_widget_show_all( sp->win );
405         redissect_packets(&cfile);
406 }
407
408
409
410 static void
411 wspstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
412 {
413         GString *str;
414         char *filter;
415
416         str = g_string_new("wsp,stat,");
417         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
418         str = g_string_append(str, filter);
419         gtk_wspstat_init(str->str);
420         g_string_free(str, TRUE);
421 }
422
423 static void
424 dlg_destroy_cb(void)
425 {
426         dlg=NULL;
427 }
428
429 static void
430 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
431 {
432         gtk_widget_destroy(GTK_WIDGET(parent_w));
433 }
434
435 static void
436 gtk_wspstat_cb(GtkWidget *w _U_, gpointer d _U_)
437 {
438         GtkWidget *dlg_box;
439         GtkWidget *filter_box, *filter_label;
440         GtkWidget *bbox, *start_button, *cancel_button;
441
442         /* if the window is already open, bring it to front */
443         if(dlg){
444                 gdk_window_raise(dlg->window);
445                 return;
446         }
447
448         dlg=dlg_window_new("Ethereal: Compute WSP statistics");
449         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
450
451         dlg_box=gtk_vbox_new(FALSE, 10);
452         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
453         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
454         gtk_widget_show(dlg_box);
455
456         /* Filter box */
457         filter_box=gtk_hbox_new(FALSE, 3);
458
459         /* Filter label */
460         filter_label=gtk_label_new("Filter:");
461         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
462         gtk_widget_show(filter_label);
463
464         /* Filter entry */
465         filter_entry=gtk_entry_new();
466         WIDGET_SET_SIZE(filter_entry, 300, -2);
467         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
468         gtk_widget_show(filter_entry);
469         
470         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
471         gtk_widget_show(filter_box);
472
473         /* button box */
474         bbox = gtk_hbutton_box_new();
475         gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
476         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
477         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
478         gtk_widget_show(bbox);
479
480         /* the start button */
481         start_button=gtk_button_new_with_label("Create Stat");
482         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
483                               wspstat_start_button_clicked, NULL);
484         gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
485         GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
486         gtk_widget_grab_default(start_button);
487         gtk_widget_show(start_button);
488
489         cancel_button=BUTTON_NEW_FROM_STOCK(GTK_STOCK_CANCEL);
490         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
491         GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
492         gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
493         gtk_widget_show(cancel_button);
494
495         /* Catch the "activate" signal on the filter text entry, so that
496            if the user types Return there, we act as if the "Create Stat"
497            button had been selected, as happens if Return is typed if
498            some widget that *doesn't* handle the Return key has the input
499            focus. */
500         dlg_set_activate(filter_entry, start_button);
501
502         /* Catch the "key_press_event" signal in the window, so that we can
503            catch the ESC key being pressed and act as if the "Cancel" button
504            had been selected. */
505         dlg_set_cancel(dlg, cancel_button);
506
507         /* Give the initial focus to the "Filter" entry box. */
508         gtk_widget_grab_focus(filter_entry);
509
510         gtk_widget_show_all(dlg);
511 }
512
513
514 void
515 register_tap_listener_gtkwspstat(void)
516 {
517         register_ethereal_tap("wsp,stat,", gtk_wspstat_init);
518 }
519
520 void
521 register_tap_menu_gtkwspstat(void)
522 {
523         register_tap_menu_item("_Statistics/Watch protocol/WAP-WSP...",
524             gtk_wspstat_cb, NULL, NULL, NULL);
525 }