do freeze/thaw in packet_list_set_selected_row only, if the list must be moved. This...
[obnox/wireshark/wip.git] / gtk / hostlist_table.c
1 /* hostlist_table.c   2004 Ian Schorr
2  * modified from endpoint_talkers_table.c   2003 Ronnie Sahlberg
3  * Helper routines common to all host list taps.
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <math.h>
34
35 #include <gtk/gtk.h>
36
37 #include <epan/packet_info.h>
38 #include <epan/to_str.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/tap.h>
41
42 #include "compat_macros.h"
43 #include "hostlist_table.h"
44 #include "image/clist_ascend.xpm"
45 #include "image/clist_descend.xpm"
46 #include "simple_dialog.h"
47 #include "globals.h"
48 #include "find_dlg.h"
49 #include "color.h"
50 #include "gtk/color_dlg.h"
51 #include "gtkglobals.h"
52 #include "main.h"
53 #include "gui_utils.h"
54 #include "dlg_utils.h"
55 #include "help_dlg.h"
56
57
58 #define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
59
60 #define NUM_COLS 8
61
62
63 /* convert a port number into a string */
64 static char *
65 hostlist_port_to_str(int port_type, guint32 port)
66 {
67         static int i=0;
68         static gchar *strp, str[4][12];
69
70         i++;
71         if(i>=4){
72                 i=0;
73         }
74         strp=str[i];
75
76         switch(port_type){
77         case PT_TCP:
78         case PT_UDP:
79                 g_snprintf(strp, 11, "%d", port);
80                 return strp;
81         }
82         return NULL;
83 }
84
85
86 #define FN_ANY_ADDRESS          0
87 #define FN_ANY_PORT             1
88
89 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp
90    a port_type and a name_type (FN_...)
91    return a string for the filter name
92
93    some addresses, like AT_ETHER may actually be any of multiple types
94    of protocols,   either ethernet, tokenring, fddi etc so we must be more
95    specific there  thats why we need specific_addr_type
96 */
97 static const char *
98 hostlist_get_filter_name(address *addr, int specific_addr_type, int port_type, int name_type)
99 {
100         switch(name_type){
101         case FN_ANY_ADDRESS:
102                 switch(addr->type){
103                 case AT_ETHER:
104                         switch(specific_addr_type){
105                         case SAT_ETHER:
106                                 return "eth.addr";
107                         case SAT_FDDI:
108                                 return "fddi.addr";
109                         case SAT_TOKENRING:
110                                 return "tr.addr";
111                         default :
112                                 ;
113                         }
114                         break;
115                 case AT_IPv4:
116                         return "ip.addr";
117                 case AT_IPv6:
118                         return "ipv6.addr";
119                 case AT_IPX:
120                         return "ipx.addr";
121                 case AT_FC:
122                         return "fc.id";
123                 case AT_URI:
124                         switch(specific_addr_type){
125                         case SAT_JXTA:
126                                 return "jxta.message.address";
127                         default:
128                             ;
129                         }
130                         break;
131                 default:
132                         ;
133                 }
134         case FN_ANY_PORT:
135                 switch(port_type){
136                 case PT_TCP:
137                         return "tcp.port";
138                 case PT_UDP:
139                         return "udp.port";
140                 }
141                 break;
142         }
143
144         g_assert_not_reached();
145         return NULL;
146 }
147
148
149 typedef struct column_arrows {
150         GtkWidget *table;
151         GtkWidget *ascend_pm;
152         GtkWidget *descend_pm;
153 } column_arrows;
154
155
156 static void
157 reset_hostlist_table_data(hostlist_table *hosts)
158 {
159     guint32 i;
160     char title[256];
161         
162         /* Allow clist to update */
163     gtk_clist_thaw(hosts->table);
164
165     if(hosts->page_lb) {
166         g_snprintf(title, 255, "Endpoints: %s", cf_get_display_name(&cfile));
167         gtk_window_set_title(GTK_WINDOW(hosts->win), title);
168         g_snprintf(title, 255, "%s", hosts->name);
169         gtk_label_set_text(GTK_LABEL(hosts->page_lb), title);
170         gtk_widget_set_sensitive(hosts->page_lb, FALSE);
171     } else {
172         g_snprintf(title, 255, "%s Endpoints: %s", hosts->name, cf_get_display_name(&cfile));
173         gtk_window_set_title(GTK_WINDOW(hosts->win), title);
174     }
175
176     /* remove all entries from the clist */
177     gtk_clist_clear(hosts->table);
178
179     /* delete all hosts */
180     for(i=0;i<hosts->num_hosts;i++){
181         g_free((gpointer)hosts->hosts[i].address.data);
182     }
183     g_free(hosts->hosts);
184     hosts->hosts=NULL;
185     hosts->num_hosts=0;
186 }
187
188 static void
189 reset_hostlist_table_data_cb(void *arg)
190 {
191     reset_hostlist_table_data(arg);
192 }
193
194 void protect_thread_critical_region(void);
195 void unprotect_thread_critical_region(void);
196 static void
197 hostlist_win_destroy_cb(GtkWindow *win _U_, gpointer data)
198 {
199         hostlist_table *hosts=(hostlist_table *)data;
200
201         protect_thread_critical_region();
202         remove_tap_listener(hosts);
203         unprotect_thread_critical_region();
204
205         reset_hostlist_table_data(hosts);
206         g_free(hosts);
207 }
208
209 static gint
210 hostlist_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
211 {
212         char *text1 = NULL;
213         char *text2 = NULL;
214         guint64 i1, i2;
215
216         const GtkCListRow *row1 = ptr1;
217         const GtkCListRow *row2 = ptr2;
218
219         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
220         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
221
222         switch(clist->sort_column){
223         case 0:
224         case 2:
225                 return strcmp (text1, text2);
226         case 1:
227         case 3:
228         case 4:
229         case 5:
230         case 6:
231         case 7:
232                 sscanf(text1, "%" PRIu64, &i1);
233                 sscanf(text2, "%" PRIu64, &i2);
234         /* XXX - this might cause trouble because of overflow problems */
235         /* XXX - is this correct anyway? Subtracting two unsigned values will still be an unsigned value, which will never become negative */
236                 return (gint) (i1-i2);
237         }
238         g_assert_not_reached();
239         
240         /* Allow clist to redraw */
241         
242         gtk_clist_thaw(clist);
243         gtk_clist_freeze(clist);
244         
245         return 0;
246 }
247
248 static void
249 hostlist_click_column_cb(GtkCList *clist, gint column, gpointer data)
250 {
251         column_arrows *col_arrows = (column_arrows *) data;
252         int i;
253
254         for (i = 0; i < NUM_COLS; i++) {
255                 gtk_widget_hide(col_arrows[i].ascend_pm);
256                 gtk_widget_hide(col_arrows[i].descend_pm);
257         }
258
259         if (column == clist->sort_column) {
260                 if (clist->sort_type == GTK_SORT_ASCENDING) {
261                         clist->sort_type = GTK_SORT_DESCENDING;
262                         gtk_widget_show(col_arrows[column].descend_pm);
263                 } else {
264                         clist->sort_type = GTK_SORT_ASCENDING;
265                         gtk_widget_show(col_arrows[column].ascend_pm);
266                 }
267         } else {
268                 clist->sort_type = GTK_SORT_DESCENDING;
269                 gtk_widget_show(col_arrows[column].descend_pm);
270                 gtk_clist_set_sort_column(clist, column);
271         }
272
273         gtk_clist_sort(clist);
274
275         /* Allow update of clist */
276         gtk_clist_thaw(clist);
277         gtk_clist_freeze(clist);
278 }
279
280
281 /* action is encoded as
282    filter_action*256+filter_type
283
284    filter_action:
285         0: Match
286         1: Prepare
287         2: Find Frame
288         3:   Find Next
289         4:   Find Previous
290         5: Colorize Host Traffic
291    filter_type:
292         0: Selected
293         1: Not Selected
294         2: And Selected
295         3: Or Selected
296         4: And Not Selected
297         5: Or Not Selected
298 */
299 static void
300 hostlist_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
301 {
302         int action, type;
303         int selection;
304         hostlist_table *hl=(hostlist_table *)callback_data;
305         char dirstr[128];
306         char str[256];
307         const char *current_filter;
308         char *sport;
309
310         action = (callback_action>>8)&0xff;
311         type = callback_action&0xff;
312
313         selection=GPOINTER_TO_INT(g_list_nth_data(GTK_CLIST(hl->table)->selection, 0));
314         if(selection>=(int)hl->num_hosts){
315                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No hostlist selected");
316                 return;
317         }
318         /* translate it back from row index to index in hostlist array */
319         selection=GPOINTER_TO_INT(gtk_clist_get_row_data(hl->table, selection));
320
321         sport=hostlist_port_to_str(hl->hosts[selection].port_type, hl->hosts[selection].port);
322
323         g_snprintf(dirstr, 127, "%s==%s %s%s%s%s",
324                 hostlist_get_filter_name(&hl->hosts[selection].address,
325                 hl->hosts[selection].sat, hl->hosts[selection].port_type,  FN_ANY_ADDRESS),
326                 address_to_str(&hl->hosts[selection].address),
327                 sport?" && ":"",
328                 sport?hostlist_get_filter_name(&hl->hosts[selection].address, hl->hosts[selection].sat, hl->hosts[selection].port_type,  FN_ANY_PORT):"",
329                 sport?"==":"",
330                 sport?sport:"");
331
332         current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
333         switch(type){
334         case 0:
335                 /* selected */
336                 g_snprintf(str, 255, "%s", dirstr);
337                 break;
338         case 1:
339                 /* not selected */
340                 g_snprintf(str, 255, "!(%s)", dirstr);
341                 break;
342         case 2:
343                 /* and selected */
344                 g_snprintf(str, 255, "(%s) && (%s)", current_filter, dirstr);
345                 break;
346         case 3:
347                 /* or selected */
348                 g_snprintf(str, 255, "(%s) || (%s)", current_filter, dirstr);
349                 break;
350         case 4:
351                 /* and not selected */
352                 g_snprintf(str, 255, "(%s) && !(%s)", current_filter, dirstr);
353                 break;
354         case 5:
355                 /* or not selected */
356                 g_snprintf(str, 255, "(%s) || !(%s)", current_filter, dirstr);
357                 break;
358         }
359
360         switch(action){
361         case 0:
362                 /* match */
363                 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
364                 main_filter_packets(&cfile, str, FALSE);
365                 gdk_window_raise(top_level->window);
366                 break;
367         case 1:
368                 /* prepare */
369                 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
370                 break;
371         case 2:
372                 /* find frame */
373                 find_frame_with_filter(str);
374                 break;
375         case 3:
376                 /* find next */
377                 find_previous_next_frame_with_filter(str, FALSE);
378                 break;
379         case 4:
380                 /* find previous */
381                 find_previous_next_frame_with_filter(str, TRUE);
382                 break;
383         case 5:
384                 /* colorize host traffic */
385                 color_display_with_filter(str);
386                 break;
387         }
388 }
389 static gint
390 hostlist_show_popup_menu_cb(void *widg _U_, GdkEvent *event, hostlist_table *et)
391 {
392     GdkEventButton *bevent = (GdkEventButton *)event;
393     gint row;
394     gint column;
395
396     /* To qoute the "Gdk Event Structures" doc:
397      * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
398     if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
399         /* if this is a right click on one of our columns, select it and popup the context menu */
400         if(gtk_clist_get_selection_info(et->table,
401                                           (gint) (((GdkEventButton *)event)->x),
402                                           (gint) (((GdkEventButton *)event)->y),
403                                              &row, &column)) {
404             gtk_clist_unselect_all(et->table);
405             gtk_clist_select_row(et->table, row, -1);
406
407             gtk_menu_popup(GTK_MENU(et->menu), NULL, NULL, NULL, NULL,
408                 bevent->button, bevent->time);
409         }
410     }
411
412     return FALSE;
413 }
414
415 static GtkItemFactoryEntry hostlist_list_menu_items[] =
416 {
417         /* Match */
418         ITEM_FACTORY_ENTRY("/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL),
419         ITEM_FACTORY_ENTRY("/Apply as Filter/Selected", NULL,
420                 hostlist_select_filter_cb, 0*256+0, NULL, NULL),
421         ITEM_FACTORY_ENTRY("/Apply as Filter/Not Selected", NULL,
422                 hostlist_select_filter_cb, 0*256+1, NULL, NULL),
423         ITEM_FACTORY_ENTRY("/Apply as Filter/... and Selected", NULL,
424                 hostlist_select_filter_cb, 0*256+2, NULL, NULL),
425         ITEM_FACTORY_ENTRY("/Apply as Filter/... or Selected", NULL,
426                 hostlist_select_filter_cb, 0*256+3, NULL, NULL),
427         ITEM_FACTORY_ENTRY("/Apply as Filter/... and not Selected", NULL,
428                 hostlist_select_filter_cb, 0*256+4, NULL, NULL),
429         ITEM_FACTORY_ENTRY("/Apply as Filter/... or not Selected", NULL,
430                 hostlist_select_filter_cb, 0*256+5, NULL, NULL),
431
432         /* Prepare */
433         ITEM_FACTORY_ENTRY("/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL),
434         ITEM_FACTORY_ENTRY("/Prepare a Filter/Selected", NULL,
435                 hostlist_select_filter_cb, 1*256+0, NULL, NULL),
436         ITEM_FACTORY_ENTRY("/Prepare a Filter/Not Selected", NULL,
437                 hostlist_select_filter_cb, 1*256+1, NULL, NULL),
438         ITEM_FACTORY_ENTRY("/Prepare a Filter/... and Selected", NULL,
439                 hostlist_select_filter_cb, 1*256+2, NULL, NULL),
440         ITEM_FACTORY_ENTRY("/Prepare a Filter/... or Selected", NULL,
441                 hostlist_select_filter_cb, 1*256+3, NULL, NULL),
442         ITEM_FACTORY_ENTRY("/Prepare a Filter/... and not Selected", NULL,
443                 hostlist_select_filter_cb, 1*256+4, NULL, NULL),
444         ITEM_FACTORY_ENTRY("/Prepare a Filter/... or not Selected", NULL,
445                 hostlist_select_filter_cb, 1*256+5, NULL, NULL),
446
447         /* Find Frame */
448         ITEM_FACTORY_ENTRY("/Find Frame", NULL, NULL, 0, "<Branch>", NULL),
449         ITEM_FACTORY_ENTRY("/Find Frame/Find Frame", NULL,
450                 hostlist_select_filter_cb, 2*256+0, NULL, NULL),
451         /* Find Next */
452         ITEM_FACTORY_ENTRY("/Find Frame/Find Next", NULL,
453                 hostlist_select_filter_cb, 3*256+0, NULL, NULL),
454         /* Find Previous */
455         ITEM_FACTORY_ENTRY("/Find Frame/Find Previous", NULL,
456                 hostlist_select_filter_cb, 4*256+0, NULL, NULL),
457         /* Colorize Host Traffic */
458         ITEM_FACTORY_ENTRY("/Colorize Host Traffic", NULL,
459                 hostlist_select_filter_cb, 5*256+0, NULL, NULL),
460
461 };
462
463 static void
464 hostlist_create_popup_menu(hostlist_table *hl)
465 {
466     GtkItemFactory *item_factory;
467
468     item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
469
470     gtk_item_factory_create_items_ac(item_factory, sizeof(hostlist_list_menu_items)/sizeof(hostlist_list_menu_items[0]), hostlist_list_menu_items, hl, 2);
471
472     hl->menu = gtk_item_factory_get_widget(item_factory, "<main>");
473     SIGNAL_CONNECT(hl->table, "button_press_event", hostlist_show_popup_menu_cb, hl);
474 }
475
476
477 /* Draw/refresh the address field of a single entry at the specified index */
478 static void
479 draw_hostlist_table_address(hostlist_table *hl, int hostlist_idx)
480 {
481     const char *entry;
482     char *port;
483     guint32 pt;
484     int rownum;
485
486     rownum=gtk_clist_find_row_from_data(hl->table, (gpointer)hostlist_idx);
487
488     if (!hl->resolve_names)
489         entry=address_to_str(&hl->hosts[hostlist_idx].address);
490     else
491         entry=get_addr_name(&hl->hosts[hostlist_idx].address);
492
493                 gtk_clist_set_text(hl->table, rownum, 0, entry);
494
495     pt = hl->hosts[hostlist_idx].port_type;
496     if(!hl->resolve_names) pt = PT_NONE;
497     switch(pt) {
498     case(PT_TCP):
499         entry=get_tcp_port(hl->hosts[hostlist_idx].port);
500         break;
501     case(PT_UDP):
502         entry=get_udp_port(hl->hosts[hostlist_idx].port);
503         break;
504     default:
505         port=hostlist_port_to_str(hl->hosts[hostlist_idx].port_type, hl->hosts[hostlist_idx].port);
506         entry=port?port:"";
507     }
508     gtk_clist_set_text(hl->table, rownum, 1, entry);
509 }
510
511 /* Refresh the address fields of all entries in the list */
512 static void
513 draw_hostlist_table_addresses(hostlist_table *hl)
514 {
515     guint32 i;
516
517     for(i=0;i<hl->num_hosts;i++){
518         draw_hostlist_table_address(hl, i);
519     }
520 }
521
522
523 static void
524 draw_hostlist_table_data(hostlist_table *hl)
525 {
526     guint32 i;
527     int j;
528     char title[256];
529
530     if (hl->page_lb) {
531         if(hl->num_hosts) {
532             g_snprintf(title, 255, "%s: %u", hl->name, hl->num_hosts);
533         } else {
534             g_snprintf(title, 255, "%s", hl->name);
535         }
536         gtk_label_set_text(GTK_LABEL(hl->page_lb), title);
537         gtk_widget_set_sensitive(hl->page_lb, hl->num_hosts);
538     }
539
540     for(i=0;i<hl->num_hosts;i++){
541         char str[16];
542
543         j=gtk_clist_find_row_from_data(hl->table, (gpointer)i);
544
545         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].tx_frames+hl->hosts[i].rx_frames);
546         gtk_clist_set_text(hl->table, j, 2, str);
547         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].tx_bytes+hl->hosts[i].rx_bytes);
548         gtk_clist_set_text(hl->table, j, 3, str);
549
550
551         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].tx_frames);
552         gtk_clist_set_text(hl->table, j, 4, str);
553         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].tx_bytes);
554         gtk_clist_set_text(hl->table, j, 5, str);
555
556
557         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].rx_frames);
558         gtk_clist_set_text(hl->table, j, 6, str);
559         g_snprintf(str, 16, "%" PRIu64, hl->hosts[i].rx_bytes);
560         gtk_clist_set_text(hl->table, j, 7, str);
561
562     }
563         
564     draw_hostlist_table_addresses(hl);
565         
566     gtk_clist_sort(hl->table);
567
568     /* Allow table to redraw. */
569     gtk_clist_thaw(hl->table);
570     gtk_clist_freeze(hl->table);
571 }
572
573 static void
574 draw_hostlist_table_data_cb(void *arg)
575 {
576     draw_hostlist_table_data(arg);
577 }
578
579 #if (GTK_MAJOR_VERSION >= 2)
580 static void
581 copy_as_csv_cb(GtkWindow *win _U_, gpointer data)
582 {
583    guint32         i,j;
584    gchar           *table_entry;
585    GtkClipboard    *cb;  
586    GString         *CSV_str = g_string_new("");
587    
588    hostlist_table *hosts=(hostlist_table *)data;
589    
590    /* Add the column headers to the CSV data */
591    for(i=0;i<hosts->num_columns;i++){                  /* all columns         */
592     if(i==1 && !hosts->has_ports) continue;            /* Don't add the port column if it's empty */
593      g_string_append(CSV_str,hosts->default_titles[i]);/* add the column heading to the CSV string */
594     if(i!=hosts->num_columns-1)
595      g_string_append(CSV_str,",");
596    }
597    g_string_append(CSV_str,"\n");                      /* new row */
598  
599    /* Add the column values to the CSV data */
600    for(i=0;i<hosts->num_hosts;i++){                    /* all rows            */
601     for(j=0;j<hosts->num_columns;j++){                 /* all columns         */
602      if(j==1 && !hosts->has_ports) continue;           /* Don't add the port column if it's empty */
603      gtk_clist_get_text(hosts->table,i,j,&table_entry);/* copy table item into string */
604      g_string_append(CSV_str,table_entry);             /* add the table entry to the CSV string */
605     if(j!=hosts->num_columns-1)
606      g_string_append(CSV_str,",");
607     } 
608     g_string_append(CSV_str,"\n");                     /* new row */  
609    }
610
611    /* Now that we have the CSV data, copy it into the default clipboard */
612    cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);    /* Get the default clipboard */
613    gtk_clipboard_set_text(cb, CSV_str->str, -1);       /* Copy the CSV data into the clipboard */
614    g_string_free(CSV_str, TRUE);                       /* Free the memory */
615
616 #endif
617
618
619 static gboolean
620 init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
621 {
622     int i;
623     column_arrows *col_arrows;
624     GtkStyle *win_style;
625     GtkWidget *column_lb;
626     GString *error_string;
627     char title[256];
628 #if (GTK_MAJOR_VERSION >= 2)
629     GtkWidget *copy_bt;
630     GtkTooltips *tooltips = gtk_tooltips_new();
631 #endif           
632
633
634     hosttable->num_columns=NUM_COLS; 
635     hosttable->default_titles[0] = "Address";
636     hosttable->default_titles[1] = "Port";
637     hosttable->default_titles[2] = "Packets";
638     hosttable->default_titles[3] = "Bytes";
639     hosttable->default_titles[4] = "Tx Packets";
640     hosttable->default_titles[5] = "Tx Bytes";
641     hosttable->default_titles[6] = "Rx Packets";
642     hosttable->default_titles[7] = "Rx Bytes";
643     hosttable->has_ports=!hide_ports;
644     hosttable->num_hosts = 0;
645     hosttable->resolve_names=TRUE;
646
647     g_snprintf(title, 255, "%s Endpoints", table_name); 
648     hosttable->page_lb = gtk_label_new(title);                                                 
649     gtk_box_pack_start(GTK_BOX(vbox), hosttable->page_lb, FALSE, FALSE, 0);
650
651     hosttable->scrolled_window=scrolled_window_new(NULL, NULL);
652     gtk_box_pack_start(GTK_BOX(vbox), hosttable->scrolled_window, TRUE, TRUE, 0);
653
654     hosttable->table=(GtkCList *)gtk_clist_new(NUM_COLS);
655
656     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
657     win_style = gtk_widget_get_style(hosttable->scrolled_window);
658     for (i = 0; i < NUM_COLS; i++) {
659         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
660         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
661         column_lb = gtk_label_new(hosttable->default_titles[i]);
662         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
663         gtk_widget_show(column_lb);
664
665         col_arrows[i].ascend_pm = xpm_to_widget((const char **) clist_ascend_xpm);
666         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
667         col_arrows[i].descend_pm = xpm_to_widget((const char **) clist_descend_xpm);
668         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
669         /* make total frames be the default sort order */
670         if (i == 4) {
671             gtk_widget_show(col_arrows[i].descend_pm);
672         }
673         gtk_clist_set_column_widget(GTK_CLIST(hosttable->table), i, col_arrows[i].table);
674         gtk_widget_show(col_arrows[i].table);
675     }
676     gtk_clist_column_titles_show(GTK_CLIST(hosttable->table));
677
678     gtk_clist_set_compare_func(hosttable->table, hostlist_sort_column);
679     gtk_clist_set_sort_column(hosttable->table, 4);
680     gtk_clist_set_sort_type(hosttable->table, GTK_SORT_DESCENDING);
681
682     gtk_clist_set_column_auto_resize(hosttable->table, 0, TRUE);
683     gtk_clist_set_column_auto_resize(hosttable->table, 1, TRUE);
684     gtk_clist_set_column_auto_resize(hosttable->table, 2, TRUE);
685     gtk_clist_set_column_auto_resize(hosttable->table, 3, TRUE);
686     gtk_clist_set_column_auto_resize(hosttable->table, 4, TRUE);
687     gtk_clist_set_column_auto_resize(hosttable->table, 5, TRUE);
688     gtk_clist_set_column_auto_resize(hosttable->table, 6, TRUE);
689     gtk_clist_set_column_auto_resize(hosttable->table, 7, TRUE);
690
691     gtk_clist_set_shadow_type(hosttable->table, GTK_SHADOW_IN);
692     gtk_clist_column_titles_show(hosttable->table);
693     gtk_container_add(GTK_CONTAINER(hosttable->scrolled_window), (GtkWidget *)hosttable->table);
694
695     SIGNAL_CONNECT(hosttable->table, "click-column", hostlist_click_column_cb, col_arrows);
696
697     hosttable->num_hosts=0;
698     hosttable->hosts=NULL;
699
700     /* hide srcport and dstport if we don't use ports */
701     if(hide_ports){
702         gtk_clist_set_column_visibility(hosttable->table, 1, FALSE);
703     }
704
705     /* create popup menu for this table */
706     hostlist_create_popup_menu(hosttable);
707
708 #if (GTK_MAJOR_VERSION >= 2)
709     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
710     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
711     copy_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_COPY);
712     gtk_tooltips_set_tip(tooltips, copy_bt, 
713         "Copy all statistical values of this page to the clipboard in CSV (Comma Seperated Values) format.", NULL);
714     SIGNAL_CONNECT(copy_bt, "clicked", copy_as_csv_cb,(gpointer *) hosttable);
715     gtk_box_pack_start(GTK_BOX(vbox), copy_bt, FALSE, FALSE, 0);
716 #endif
717
718     /* register the tap and rerun the taps on the packet list */
719     error_string=register_tap_listener(tap_name, hosttable, filter, reset_hostlist_table_data_cb, packet_func, draw_hostlist_table_data_cb);
720     if(error_string){
721         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
722         g_string_free(error_string, TRUE);
723         g_free(hosttable);
724         return FALSE;
725     }
726
727     return TRUE;
728 }
729
730
731 void
732 init_hostlist_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
733 {
734     hostlist_table *hosttable;
735     char title[256];
736     GtkWidget *vbox;
737     GtkWidget *bbox;
738     GtkWidget *close_bt, *help_bt;
739     gboolean ret;
740
741
742     hosttable=g_malloc(sizeof(hostlist_table));
743
744     hosttable->name=table_name;
745     g_snprintf(title, 255, "%s Endpoints: %s", table_name, cf_get_display_name(&cfile));
746     hosttable->win=window_new(GTK_WINDOW_TOPLEVEL, title);
747     
748     gtk_window_set_default_size(GTK_WINDOW(hosttable->win), 750, 400);
749
750     vbox=gtk_vbox_new(FALSE, 3);
751     gtk_container_add(GTK_CONTAINER(hosttable->win), vbox);
752     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
753
754     ret = init_hostlist_table_page(hosttable, vbox, hide_ports, table_name, tap_name, filter, packet_func);
755     if(ret == FALSE) {
756         g_free(hosttable);
757         return;
758     }
759
760     /* Button row. */
761     if(topic_available(HELP_STATS_ENDPOINTS_DIALOG)) {
762         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
763     } else {
764         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
765     }
766     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
767
768     close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
769     window_set_cancel_button(hosttable->win, close_bt, window_cancel_button_cb);
770
771     if(topic_available(HELP_STATS_ENDPOINTS_DIALOG)) {
772         help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
773         SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_STATS_ENDPOINTS_DIALOG);
774     }
775
776     SIGNAL_CONNECT(hosttable->win, "delete_event", window_delete_event_cb, NULL);
777     SIGNAL_CONNECT(hosttable->win, "destroy", hostlist_win_destroy_cb, hosttable);
778
779     gtk_widget_show_all(hosttable->win);
780     window_present(hosttable->win);
781
782     cf_retap_packets(&cfile, FALSE);
783         
784     /* Keep clist frozen to cause modifications to the clist (inserts, appends, others that are extremely slow
785            in GTK2) to not be drawn, allow refreshes to occur at strategic points for performance */
786         gtk_clist_freeze(hosttable->table);
787
788
789     /* after retapping, redraw table */
790     draw_hostlist_table_data(hosttable);
791 }
792
793
794 static void
795 hostlist_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
796 {
797     void ** pages = data;
798     int page;
799
800     /* first "page" contains the number of pages */
801     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
802         hostlist_win_destroy_cb(NULL, pages[page]);
803     }
804 }
805
806
807
808
809 static hostlist_table *
810 init_hostlist_notebook_page_cb(gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
811 {
812     gboolean ret;
813     GtkWidget *page_vbox;
814     hostlist_table *hosttable;
815
816     hosttable=g_malloc(sizeof(hostlist_table));
817     hosttable->name=table_name;
818     hosttable->resolve_names=TRUE;
819
820     page_vbox=gtk_vbox_new(FALSE, 6);
821     hosttable->win = page_vbox;
822     gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
823
824     ret = init_hostlist_table_page(hosttable, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
825     if(ret == FALSE) {
826         g_free(hosttable);
827         return NULL;
828     }
829
830     return hosttable;
831 }
832
833
834 typedef struct {
835     gboolean hide_ports;       /* hide TCP / UDP port columns */
836     char *table_name;          /* GUI output name */
837     char *tap_name;            /* internal name */
838     char *filter;              /* display filter string (unused) */
839     tap_packet_cb packet_func; /* function to be called for new incoming packets */
840 } register_hostlist_t;
841
842
843 static GSList *registered_hostlist_tables = NULL;
844
845 void
846 register_hostlist_table(gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
847 {
848     register_hostlist_t *table;
849
850     table = g_malloc(sizeof(register_hostlist_t));
851
852     table->hide_ports   = hide_ports;
853     table->table_name   = table_name;
854     table->tap_name     = tap_name;
855     table->filter       = filter;
856     table->packet_func  = packet_func;
857
858     registered_hostlist_tables = g_slist_append(registered_hostlist_tables, table);
859 }
860
861
862 static void
863 hostlist_resolve_toggle_dest(GtkWidget *widget, gpointer data)
864 {
865     int page;
866     void ** pages = data;
867     gboolean resolve_names;
868     hostlist_table *hosttable;
869
870
871     resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
872
873     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
874         hosttable = pages[page];
875         hosttable->resolve_names = resolve_names;
876
877         draw_hostlist_table_addresses(hosttable);
878
879         gtk_clist_thaw(hosttable->table);
880         gtk_clist_freeze(hosttable->table);
881     }
882 }
883
884
885 void
886 init_hostlist_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
887 {
888     hostlist_table *hosttable;
889     char title[256];
890     GtkWidget *vbox;
891     GtkWidget *hbox;
892     GtkWidget *bbox;
893     GtkWidget *close_bt, *help_bt;
894     GtkWidget *win;
895     GtkWidget *resolv_cb;
896     int page;
897     void ** pages;
898     GtkWidget *nb;
899     GtkWidget *page_lb;
900     GSList  *current_table;
901     register_hostlist_t *registered;
902     GtkTooltips *tooltips = gtk_tooltips_new();
903
904
905     pages = g_malloc(sizeof(void *) * (g_slist_length(registered_hostlist_tables) + 1));
906
907     win=window_new(GTK_WINDOW_TOPLEVEL, "hostlist");
908     g_snprintf(title, 255, "Endpoints: %s", cf_get_display_name(&cfile));
909     gtk_window_set_title(GTK_WINDOW(win), title);
910     gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
911
912     vbox=gtk_vbox_new(FALSE, 6);
913     gtk_container_add(GTK_CONTAINER(win), vbox);
914     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
915
916     nb = gtk_notebook_new();
917     gtk_container_add(GTK_CONTAINER(vbox), nb);
918
919     page = 0;
920
921     current_table = registered_hostlist_tables;
922     while(current_table) {
923         registered = current_table->data;
924         page_lb = gtk_label_new("");
925         hosttable = init_hostlist_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
926             registered->filter, registered->packet_func);
927         gtk_notebook_append_page(GTK_NOTEBOOK(nb), hosttable->win, page_lb);
928         hosttable->win = win;
929         hosttable->page_lb = page_lb;
930         pages[++page] = hosttable;
931
932         current_table = g_slist_next(current_table);
933     }
934
935     pages[0] = GINT_TO_POINTER(page);
936
937     hbox = gtk_hbox_new(FALSE, 3);
938     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
939
940     resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Name resolution", NULL);
941     gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
942     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
943     gtk_tooltips_set_tip(tooltips, resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
944         "Please note: The corresponding name resolution must be enabled.", NULL);
945
946     SIGNAL_CONNECT(resolv_cb, "toggled", hostlist_resolve_toggle_dest, pages);
947
948     /* Button row. */
949     if(topic_available(HELP_STATS_ENDPOINTS_DIALOG)) {
950         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
951     } else {
952         bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
953     }
954     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
955
956     close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
957     window_set_cancel_button(win, close_bt, window_cancel_button_cb);
958
959     if(topic_available(HELP_STATS_ENDPOINTS_DIALOG)) {
960         help_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
961         SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_STATS_ENDPOINTS_DIALOG);
962     }
963
964     SIGNAL_CONNECT(win, "delete_event", window_delete_event_cb, NULL);
965     SIGNAL_CONNECT(win, "destroy", hostlist_win_destroy_notebook_cb, pages);
966
967     gtk_widget_show_all(win);
968     window_present(win);
969
970     cf_retap_packets(&cfile, FALSE);
971
972     /* after retapping, redraw table */
973     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
974         draw_hostlist_table_data(pages[page]);
975     }
976 }
977
978
979
980 void
981 add_hostlist_table_data(hostlist_table *hl, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, SAT_E sat, int port_type)
982 {
983     hostlist_talker_t *talker=NULL;
984     int talker_idx=0;
985     gboolean new_talker;
986
987     new_talker=FALSE;
988     /* XXX should be optimized to allocate n extra entries at a time
989        instead of just one */
990     /* if we dont have any entries at all yet */
991     if(hl->hosts==NULL){
992         hl->hosts=g_malloc(sizeof(hostlist_talker_t));
993         hl->num_hosts=1;
994         talker=&hl->hosts[0];
995         talker_idx=0;
996         new_talker=TRUE;
997     }
998
999     /* try to find it among the existing known hosts */
1000     if(talker==NULL){
1001         guint32 i;
1002         for(i=0;i<hl->num_hosts;i++){
1003             if(  (!CMP_ADDRESS(&hl->hosts[i].address, addr))&&(hl->hosts[i].port==port) ){
1004                 talker=&hl->hosts[i];
1005                 talker_idx=i;
1006                 break;
1007             }
1008         }
1009     }
1010
1011     /* if we still dont know what talker this is it has to be a new one
1012        and we have to allocate it and append it to the end of the list */
1013     if(talker==NULL){
1014         new_talker=TRUE;
1015         hl->num_hosts++;
1016         hl->hosts=g_realloc(hl->hosts, hl->num_hosts*sizeof(hostlist_talker_t));
1017         talker=&hl->hosts[hl->num_hosts-1];
1018         talker_idx=hl->num_hosts-1;
1019     }
1020
1021     /* if this is a new talker we need to initialize the struct */
1022     if(new_talker){
1023         COPY_ADDRESS(&talker->address, addr);
1024         talker->sat=sat;
1025         talker->port_type=port_type;
1026         talker->port=port;
1027         talker->rx_frames=0;
1028         talker->tx_frames=0;
1029         talker->rx_bytes=0;
1030         talker->tx_bytes=0;
1031     }
1032
1033     /* update the talker struct */
1034     if( sender ){
1035         talker->tx_frames+=num_frames;
1036         talker->tx_bytes+=num_bytes;
1037     } else {
1038         talker->rx_frames+=num_frames;
1039         talker->rx_bytes+=num_bytes;
1040     }
1041
1042     /* if this was a new talker we have to create a clist row for it */
1043     if(new_talker){
1044         char *entries[NUM_COLS];
1045         char frames[16],bytes[16],txframes[16],txbytes[16],rxframes[16],rxbytes[16];
1046
1047         /* these values will be filled by call to draw_hostlist_table_addresses() below */
1048         entries[0]="";
1049         entries[1]="";
1050
1051         g_snprintf(frames, 16, "%" PRIu64, talker->tx_frames+talker->rx_frames);
1052         entries[2]=frames;
1053         g_snprintf(bytes, 16, "%" PRIu64, talker->tx_bytes+talker->rx_bytes);
1054         entries[3]=bytes;
1055
1056         g_snprintf(txframes, 16, "%" PRIu64, talker->tx_frames);
1057         entries[4]=txframes;
1058         g_snprintf(txbytes, 16, "%" PRIu64, talker->tx_bytes);
1059         entries[5]=txbytes;
1060
1061         g_snprintf(rxframes, 16, "%" PRIu64, talker->rx_frames);
1062         entries[6]=rxframes;
1063         g_snprintf(rxbytes, 16, "%" PRIu64, talker->rx_bytes);
1064         entries[7]=rxbytes;
1065
1066         gtk_clist_insert(hl->table, talker_idx, entries);
1067         gtk_clist_set_row_data(hl->table, talker_idx, (gpointer) talker_idx);
1068
1069                 draw_hostlist_table_address(hl, talker_idx);
1070     }
1071 }