Removed some unused variables and unused assignments.
[obnox/wireshark/wip.git] / gtk / conversations_table.c
1 /* conversations_table.c
2  * conversations_table   2003 Ronnie Sahlberg
3  * Helper routines common to all endpoint conversations tap.
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
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 <ctype.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <math.h>
35 #include <locale.h>
36
37 #include <gtk/gtk.h>
38
39 #include <epan/packet_info.h>
40 #include <epan/to_str.h>
41 #include <epan/address.h>
42 #include <epan/addr_resolv.h>
43 #include <epan/tap.h>
44 #include <epan/nstime.h>
45
46 #include "../simple_dialog.h"
47 #include "../globals.h"
48
49 #include "gtk/sat.h"
50 #include "gtk/conversations_table.h"
51 #include "gtk/filter_utils.h"
52 #include "gtk/gtkglobals.h"
53 #include "gtk/gui_utils.h"
54 #include "gtk/dlg_utils.h"
55 #include "gtk/help_dlg.h"
56 #include "gtk/main.h"
57
58 #define COL_STR_LEN 16
59 #define CONV_PTR_KEY "conversations-pointer"
60 #define NB_PAGES_KEY "notebook-pages"
61 #define NO_BPS_STR "N/A"
62
63 #define CMP_NUM(n1, n2)                         \
64     if ((n1) > (n2))                            \
65         return 1;                               \
66     else if ((n1) < (n2))                       \
67         return -1;                              \
68     else                                        \
69         return 0;
70
71 /* convert a port number into a string */
72 static char *
73 ct_port_to_str(int port_type, guint32 port)
74 {
75     static int i=0;
76     static gchar *strp, str[4][12];
77     gchar *bp;
78
79     strp=str[i];
80
81     switch(port_type){
82     case PT_TCP:
83     case PT_UDP:
84     case PT_SCTP:
85     case PT_NCP:
86         i = (i+1)%4;
87         bp = &strp[11];
88   
89         *bp = 0;
90         do {
91           *--bp = (port % 10) +'0';
92         } while ((port /= 10) != 0 && bp > strp);
93         return bp;
94     }
95     return NULL;
96 }
97
98 #define FN_SRC_ADDRESS          0
99 #define FN_DST_ADDRESS          1
100 #define FN_ANY_ADDRESS          2
101 #define FN_SRC_PORT             3
102 #define FN_DST_PORT             4
103 #define FN_ANY_PORT             5
104 /* given an address (to distinguis between ipv4 and ipv6 for tcp/udp
105    a port_type and a name_type (FN_...)
106    return a string for the filter name
107
108    some addresses, like AT_ETHER may actually be any of multiple types
109    of protocols,   either ethernet, tokenring, fddi, wlan etc so we must be
110    more specific there  thats why we need specific_addr_type
111 */
112 static const char *
113 ct_get_filter_name(address *addr, int specific_addr_type, int port_type, int name_type)
114 {
115     switch(name_type){
116     case FN_SRC_ADDRESS:
117         switch(addr->type){
118         case AT_ETHER:
119             switch(specific_addr_type){
120             case SAT_ETHER:
121                 return "eth.src";
122             case SAT_WLAN:
123                 return "wlan.sa";
124             case SAT_FDDI:
125                 return "fddi.src";
126             case SAT_TOKENRING:
127                 return "tr.src";
128             default:
129                 break;
130             }
131             break;
132         case AT_IPv4:
133             return "ip.src";
134         case AT_IPv6:
135             return "ipv6.src";
136         case AT_IPX:
137             return "ipx.src";
138         case AT_FC:
139             return "fc.s_id";
140         case AT_URI:
141             switch(specific_addr_type){
142             case SAT_JXTA:
143                 return "jxta.message.src";
144             default:
145                 break;
146             }
147             break;
148         case AT_USB:
149             return "usb.sa";
150         default:
151             break;
152         }
153         break;
154     case FN_DST_ADDRESS:
155         switch(addr->type){
156         case AT_ETHER:
157             switch(specific_addr_type){
158             case SAT_ETHER:
159                 return "eth.dst";
160             case SAT_WLAN:
161                 return "wlan.da";
162             case SAT_FDDI:
163                 return "fddi.dst";
164             case SAT_TOKENRING:
165                 return "tr.dst";
166             default:
167                 break;
168             }
169             break;
170         case AT_IPv4:
171             return "ip.dst";
172         case AT_IPv6:
173             return "ipv6.dst";
174         case AT_IPX:
175             return "ipx.dst";
176         case AT_FC:
177             return "fc.d_id";
178         case AT_URI:
179             switch(specific_addr_type){
180             case SAT_JXTA:
181                 return "jxta.message.dst";
182             default:
183                 break;
184             }
185             break;
186         case AT_USB:
187             return "usb.da";
188         default:
189             break;
190         }
191         break;
192     case FN_ANY_ADDRESS:
193         switch(addr->type){
194         case AT_ETHER:
195             switch(specific_addr_type){
196             case SAT_ETHER:
197                 return "eth.addr";
198             case SAT_WLAN:
199                 return "wlan.addr";
200             case SAT_FDDI:
201                 return "fddi.addr";
202             case SAT_TOKENRING:
203                 return "tr.addr";
204             default:
205                 break;
206             }
207             break;
208         case AT_IPv4:
209             return "ip.addr";
210         case AT_IPv6:
211             return "ipv6.addr";
212         case AT_IPX:
213             return "ipx.addr";
214         case AT_FC:
215             return "fc.id";
216         case AT_URI:
217             switch(specific_addr_type){
218             case SAT_JXTA:
219                 return "jxta.message.address";
220             default:
221                 break;
222             }
223             break;
224         case AT_USB:
225             return "usb.addr";
226         default:
227             break;
228         }
229         break;
230     case FN_SRC_PORT:
231         switch(port_type){
232         case PT_TCP:
233             return "tcp.srcport";
234         case PT_UDP:
235             return "udp.srcport";
236         case PT_SCTP:
237             return "sctp.srcport";
238         case PT_NCP:
239             return "ncp.connection";
240         default:
241             break;
242         }
243         break;
244     case FN_DST_PORT:
245         switch(port_type){
246         case PT_TCP:
247             return "tcp.dstport";
248         case PT_UDP:
249             return "udp.dstport";
250         case PT_SCTP:
251             return "sctp.dstport";
252         case PT_NCP:
253             return "ncp.connection";
254         default:
255             break;
256         }
257         break;
258     case FN_ANY_PORT:
259         switch(port_type){
260         case PT_TCP:
261             return "tcp.port";
262         case PT_UDP:
263             return "udp.port";
264         case PT_SCTP:
265             return "sctp.port";
266         case PT_NCP:
267             return "ncp.connection";
268         default:
269             break;
270         }
271         break;
272     }
273
274     g_assert_not_reached();
275     return NULL;
276 }
277
278 static void
279 reset_ct_table_data(conversations_table *ct)
280 {
281     guint32 i;
282     char title[256];
283     GString *error_string;
284     const char *filter;
285     GtkListStore *store;
286
287     if (ct->use_dfilter) {
288         filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
289     } else {
290         filter = ct->filter;
291     }
292
293     error_string = set_tap_dfilter (ct, filter);
294     if (error_string) {
295         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
296         g_string_free(error_string, TRUE);
297         return;
298     }
299
300     if(ct->page_lb) {
301         g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
302         gtk_window_set_title(GTK_WINDOW(ct->win), title);
303         g_snprintf(title, sizeof(title), "%s", ct->name);
304         gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
305         gtk_widget_set_sensitive(ct->page_lb, FALSE);
306
307         if (ct->use_dfilter) {
308             if (filter && strlen(filter)) {
309                 g_snprintf(title, sizeof(title), "%s Conversations - Filter: %s", ct->name, filter);
310             } else {
311                 g_snprintf(title, sizeof(title), "%s Conversations - No Filter", ct->name);
312             }
313         } else {
314             g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
315         }
316         gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
317     } else {
318         g_snprintf(title, sizeof(title), "%s Conversations: %s", ct->name, cf_get_display_name(&cfile));
319         gtk_window_set_title(GTK_WINDOW(ct->win), title);
320     }
321
322     /* remove all entries from the list */
323     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ct->table)));
324     gtk_list_store_clear(store);
325
326     /* delete all conversations */
327     for(i=0;i<ct->num_conversations;i++){
328         conv_t *conv = &g_array_index(ct->conversations, conv_t, i);
329         g_free((gpointer)conv->src_address.data);
330         g_free((gpointer)conv->dst_address.data);
331     }
332     if (ct->conversations)
333         g_array_free(ct->conversations, TRUE);
334
335     if (ct->hashtable != NULL)
336         g_hash_table_destroy(ct->hashtable);
337
338     ct->conversations=NULL;
339     ct->hashtable=NULL;
340     ct->num_conversations=0;
341 }
342
343 static void
344 reset_ct_table_data_cb(void *arg)
345 {
346     reset_ct_table_data(arg);
347 }
348
349 static void
350 ct_win_destroy_cb(GtkWindow *win _U_, gpointer data)
351 {
352     conversations_table *conversations=(conversations_table *)data;
353
354     protect_thread_critical_region();
355     remove_tap_listener(conversations);
356     unprotect_thread_critical_region();
357
358     reset_ct_table_data(conversations);
359     g_free(conversations);
360 }
361
362 enum 
363 {
364   SRC_ADR_COLUMN,
365   SRC_PORT_COLUMN,
366   DST_ADR_COLUMN,
367   DST_PORT_COLUMN,
368   PACKETS_COLUMN,
369   BYTES_COLUMN,
370   PKT_AB_COLUMN,
371   BYTES_AB_COLUMN,
372   PKT_BA_COLUMN,
373   BYTES_BA_COLUMN,
374   START_COLUMN,
375   DURATION_COLUMN,
376   BPS_AB_COLUMN,
377   BPS_BA_COLUMN,
378   INDEX_COLUMN,
379   N_COLUMNS
380 };
381   
382 static gint
383 ct_sort_func(GtkTreeModel *model,
384                                 GtkTreeIter *a,
385                                 GtkTreeIter *b,
386                                 gpointer user_data)
387 {
388     guint32 idx1, idx2;
389     /* The col to get data from is in userdata */
390     gint data_column = GPOINTER_TO_INT(user_data);
391
392     conversations_table *ct = g_object_get_data(G_OBJECT(model), CONV_PTR_KEY);
393     conv_t *conv1 = NULL;
394     conv_t *conv2 = NULL;
395     double duration1, duration2;
396
397     gtk_tree_model_get(model, a, INDEX_COLUMN, &idx1, -1);
398     gtk_tree_model_get(model, b, INDEX_COLUMN, &idx2, -1);
399
400     if (!ct || idx1 >= ct->num_conversations || idx2 >= ct->num_conversations)
401         return 0;
402
403     conv1 = &g_array_index(ct->conversations, conv_t, idx1); 
404     conv2 = &g_array_index(ct->conversations, conv_t, idx2); 
405
406
407     switch(data_column){
408     case SRC_ADR_COLUMN: /* Source address */
409         return(CMP_ADDRESS(&conv1->src_address, &conv2->src_address));
410     case DST_ADR_COLUMN: /* Destination address */
411         return(CMP_ADDRESS(&conv1->dst_address, &conv2->dst_address));
412     case SRC_PORT_COLUMN: /* Source port */
413         CMP_NUM(conv1->src_port, conv2->src_port);
414     case DST_PORT_COLUMN: /* Destination port */
415         CMP_NUM(conv1->dst_port, conv2->dst_port);
416     case START_COLUMN: /* Start time */
417         return nstime_cmp(&conv1->start_time, &conv2->start_time);
418     }
419
420     duration1 = nstime_to_sec(&conv1->stop_time) - nstime_to_sec(&conv1->start_time);
421     duration2 = nstime_to_sec(&conv2->stop_time) - nstime_to_sec(&conv2->start_time);
422     
423     switch(data_column){
424     case DURATION_COLUMN: /* Duration */
425         CMP_NUM(duration1, duration2);
426     case BPS_AB_COLUMN: /* bps A->B */
427         if (duration1 > 0 && conv1->tx_frames > 1 && duration2 > 0 && conv2->tx_frames > 1) {
428             CMP_NUM((gint64) conv1->tx_bytes / duration1, (gint64) conv2->tx_bytes / duration2);
429         } else {
430             CMP_NUM(conv1->tx_bytes, conv2->tx_bytes);
431         }
432     case BPS_BA_COLUMN: /* bps A<-B */
433         if (duration1 > 0 && conv1->rx_frames > 1 && duration2 > 0 && conv2->rx_frames > 1) {
434             CMP_NUM((gint64) conv1->rx_bytes / duration1, (gint64) conv2->rx_bytes / duration2);
435         } else {
436             CMP_NUM(conv1->rx_bytes, conv2->rx_bytes);
437         }
438     default:
439         g_assert_not_reached();
440     }
441
442     return 0;
443 }
444
445 /* Filter direction */
446 #define DIR_A_TO_FROM_B         0
447 #define DIR_A_TO_B              1
448 #define DIR_A_FROM_B            2
449 #define DIR_A_TO_FROM_ANY       3
450 #define DIR_A_TO_ANY            4
451 #define DIR_A_FROM_ANY          5
452 #define DIR_ANY_TO_FROM_B       6
453 #define DIR_ANY_FROM_B          7
454 #define DIR_ANY_TO_B            8
455
456 static void
457 ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
458 {
459     int direction;
460     guint32 index = 0;
461     conversations_table *ct = (conversations_table *)callback_data;
462     GtkTreeIter iter;
463     GtkTreeModel *model;
464     GtkTreeSelection  *sel;
465     char *str = NULL;
466     char *sport, *dport;
467     conv_t *conv;
468
469     direction=FILTER_EXTRA(callback_action);
470
471     sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(ct->table));
472     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
473         return;
474
475     gtk_tree_model_get (model, &iter, 
476                             INDEX_COLUMN, &index, 
477                             -1);
478
479     if(index>= ct->num_conversations){
480         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected");
481         return;
482     }
483     conv = &g_array_index(ct->conversations, conv_t, index);
484     sport=ct_port_to_str(conv->port_type, conv->src_port);
485     dport=ct_port_to_str(conv->port_type, conv->dst_port);
486
487     switch(direction){
488     case DIR_A_TO_FROM_B:
489         /* A <-> B */
490         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
491                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_ANY_ADDRESS),
492                               ep_address_to_str(&conv->src_address),
493                               sport?" && ":"",
494                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_ANY_PORT):"",
495                               sport?"==":"",
496                               sport?sport:"",
497                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_ANY_ADDRESS),
498                               ep_address_to_str(&conv->dst_address),
499                               dport?" && ":"",
500                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_ANY_PORT):"",
501                               dport?"==":"",
502                               dport?dport:""
503             );
504         break;
505     case DIR_A_TO_B:
506         /* A --> B */
507         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
508                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_SRC_ADDRESS),
509                               ep_address_to_str(&conv->src_address),
510                               sport?" && ":"",
511                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_SRC_PORT):"",
512                               sport?"==":"",
513                               sport?sport:"",
514                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_DST_ADDRESS),
515                               ep_address_to_str(&conv->dst_address),
516                               dport?" && ":"",
517                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_DST_PORT):"",
518                               dport?"==":"",
519                               dport?dport:""
520             );
521         break;
522     case DIR_A_FROM_B:
523         /* A <-- B */
524         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
525                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_DST_ADDRESS),
526                               ep_address_to_str(&conv->src_address),
527                               sport?" && ":"",
528                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_DST_PORT):"",
529                               sport?"==":"",
530                               sport?sport:"",
531                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_SRC_ADDRESS),
532                               ep_address_to_str(&conv->dst_address),
533                               dport?" && ":"",
534                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_SRC_PORT):"",
535                               dport?"==":"",
536                               dport?dport:""
537             );
538         break;
539     case DIR_A_TO_FROM_ANY:
540         /* A <-> ANY */
541         str = g_strdup_printf("%s==%s%s%s%s%s",
542                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_ANY_ADDRESS),
543                               ep_address_to_str(&conv->src_address),
544                               sport?" && ":"",
545                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_ANY_PORT):"",
546                               sport?"==":"",
547                               sport?sport:""
548             );
549         break;
550     case DIR_A_TO_ANY:
551         /* A --> ANY */
552         str = g_strdup_printf("%s==%s%s%s%s%s",
553                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_SRC_ADDRESS),
554                               ep_address_to_str(&conv->src_address),
555                               sport?" && ":"",
556                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_SRC_PORT):"",
557                               sport?"==":"",
558                               sport?sport:""
559             );
560         break;
561     case DIR_A_FROM_ANY:
562         /* A <-- ANY */
563         str = g_strdup_printf("%s==%s%s%s%s%s",
564                               ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_DST_ADDRESS),
565                               ep_address_to_str(&conv->src_address),
566                               sport?" && ":"",
567                               sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type,  FN_DST_PORT):"",
568                               sport?"==":"",
569                               sport?sport:""
570             );
571         break;
572     case DIR_ANY_TO_FROM_B:
573         /* ANY <-> B */
574         str = g_strdup_printf("%s==%s%s%s%s%s",
575                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_ANY_ADDRESS),
576                               ep_address_to_str(&conv->dst_address),
577                               dport?" && ":"",
578                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_ANY_PORT):"",
579                               dport?"==":"",
580                               dport?dport:""
581             );
582         break;
583     case DIR_ANY_FROM_B:
584         /* ANY <-- B */
585         str = g_strdup_printf("%s==%s%s%s%s%s",
586                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_SRC_ADDRESS),
587                               ep_address_to_str(&conv->dst_address),
588                               dport?" && ":"",
589                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_SRC_PORT):"",
590                               dport?"==":"",
591                               dport?dport:""
592             );
593         break;
594     case DIR_ANY_TO_B:
595         /* ANY --> B */
596         str = g_strdup_printf("%s==%s%s%s%s%s",
597                               ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_DST_ADDRESS),
598                               ep_address_to_str(&conv->dst_address),
599                               dport?" && ":"",
600                               dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type,  FN_DST_PORT):"",
601                               dport?"==":"",
602                               dport?dport:""
603             );
604         break;
605     default:
606         g_assert_not_reached();
607     }
608
609     apply_selected_filter (callback_action, str);
610
611     g_free (str);
612 }
613
614 static gint
615 ct_show_popup_menu_cb(void *widg _U_, GdkEvent *event, conversations_table *ct)
616 {
617     GdkEventButton *bevent = (GdkEventButton *)event;
618
619     if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
620             gtk_menu_popup(GTK_MENU(ct->menu), NULL, NULL, NULL, NULL,
621                            bevent->button, bevent->time);
622     }
623
624     return FALSE;
625 }
626
627 static GtkItemFactoryEntry ct_list_menu_items[] =
628 {
629     /* Match */
630     {"/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL,},
631     {"/Apply as Filter/Selected", NULL, NULL, 0, "<Branch>", NULL,},
632     {"/Apply as Filter/Selected/A <-> B", NULL,
633      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
634     {"/Apply as Filter/Selected/A --> B", NULL,
635      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
636     {"/Apply as Filter/Selected/A <-- B", NULL,
637      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
638     {"/Apply as Filter/Selected/A <-> ANY", NULL,
639      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
640     {"/Apply as Filter/Selected/A --> ANY", NULL,
641      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
642     {"/Apply as Filter/Selected/A <-- ANY", NULL,
643      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
644     {"/Apply as Filter/Selected/ANY <-> B", NULL,
645      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
646     {"/Apply as Filter/Selected/ANY <-- B", NULL,
647      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
648     {"/Apply as Filter/Selected/ANY --> B", NULL,
649      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
650
651     {"/Apply as Filter/Not Selected", NULL, NULL, 0, "<Branch>", NULL,},
652     {"/Apply as Filter/Not Selected/A <-> B", NULL,
653      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
654     {"/Apply as Filter/Not Selected/A --> B", NULL,
655      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
656     {"/Apply as Filter/Not Selected/A <-- B", NULL,
657      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
658     {"/Apply as Filter/Not Selected/A --> ANY", NULL,
659      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
660     {"/Apply as Filter/Not Selected/A <-> ANY", NULL,
661      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
662     {"/Apply as Filter/Not Selected/A <-- ANY", NULL,
663      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
664     {"/Apply as Filter/Not Selected/ANY <-> B", NULL,
665      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
666     {"/Apply as Filter/Not Selected/ANY <-- B", NULL,
667      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
668     {"/Apply as Filter/Not Selected/ANY --> B", NULL,
669      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
670
671
672     {"/Apply as Filter/... and Selected", NULL, NULL, 0, "<Branch>", NULL,},
673     {"/Apply as Filter/... and Selected/A <-> B", NULL,
674      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
675     {"/Apply as Filter/... and Selected/A --> B", NULL,
676      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_B), NULL, NULL,},
677     {"/Apply as Filter/... and Selected/A <-- B", NULL,
678      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_B), NULL, NULL,},
679     {"/Apply as Filter/... and Selected/A <-> ANY", NULL,
680      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
681     {"/Apply as Filter/... and Selected/A --> ANY", NULL,
682      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
683     {"/Apply as Filter/... and Selected/A <-- ANY", NULL,
684      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
685     {"/Apply as Filter/... and Selected/ANY <-> B", NULL,
686      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
687     {"/Apply as Filter/... and Selected/ANY <-- B", NULL,
688      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
689     {"/Apply as Filter/... and Selected/ANY --> B", NULL,
690      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
691
692     {"/Apply as Filter/... or Selected", NULL, NULL, 0, "<Branch>", NULL,},
693     {"/Apply as Filter/... or Selected/A <-> B", NULL,
694      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
695     {"/Apply as Filter/... or Selected/A --> B", NULL,
696      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_B), NULL, NULL,},
697     {"/Apply as Filter/... or Selected/A <-- B", NULL,
698      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_B), NULL, NULL,},
699     {"/Apply as Filter/... or Selected/A <-> ANY", NULL,
700      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
701     {"/Apply as Filter/... or Selected/A --> ANY", NULL,
702      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
703     {"/Apply as Filter/... or Selected/A <-- ANY", NULL,
704      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
705     {"/Apply as Filter/... or Selected/ANY <-> B", NULL,
706      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
707     {"/Apply as Filter/... or Selected/ANY <-- B", NULL,
708      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
709     {"/Apply as Filter/... or Selected/ANY --> B", NULL,
710      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
711
712     {"/Apply as Filter/... and not Selected", NULL, NULL, 0, "<Branch>", NULL,},
713     {"/Apply as Filter/... and not Selected/A <-> B", NULL,
714      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
715     {"/Apply as Filter/... and not Selected/A --> B", NULL,
716      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
717     {"/Apply as Filter/... and not Selected/A <-- B", NULL,
718      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
719     {"/Apply as Filter/... and not Selected/A <-> ANY", NULL,
720      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
721     {"/Apply as Filter/... and not Selected/A --> ANY", NULL,
722      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
723     {"/Apply as Filter/... and not Selected/A <-- ANY", NULL,
724      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
725     {"/Apply as Filter/... and not Selected/ANY <-> B", NULL,
726      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
727     {"/Apply as Filter/... and not Selected/ANY <-- B", NULL,
728      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
729     {"/Apply as Filter/... and not Selected/ANY --> B", NULL,
730      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
731
732     {"/Apply as Filter/... or not Selected", NULL, NULL, 0, "<Branch>", NULL,},
733     {"/Apply as Filter/... or not Selected/A <-> B", NULL,
734      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
735     {"/Apply as Filter/... or not Selected/A --> B", NULL,
736      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
737     {"/Apply as Filter/... or not Selected/A <-- B", NULL,
738      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
739     {"/Apply as Filter/... or not Selected/A <-> ANY", NULL,
740      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
741     {"/Apply as Filter/... or not Selected/A --> ANY", NULL,
742      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
743     {"/Apply as Filter/... or not Selected/A <-- ANY", NULL,
744      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
745     {"/Apply as Filter/... or not Selected/ANY <-> B", NULL,
746      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
747     {"/Apply as Filter/... or not Selected/ANY <-- B", NULL,
748      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
749     {"/Apply as Filter/... or not Selected/ANY --> B", NULL,
750      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
751
752     /* Prepare */
753     {"/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL,},
754     {"/Prepare a Filter/Selected", NULL, NULL, 0, "<Branch>", NULL,},
755     {"/Prepare a Filter/Selected/A <-> B", NULL,
756      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
757     {"/Prepare a Filter/Selected/A --> B", NULL,
758      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
759     {"/Prepare a Filter/Selected/A <-- B", NULL,
760      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
761     {"/Prepare a Filter/Selected/A <-> ANY", NULL,
762      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
763     {"/Prepare a Filter/Selected/A --> ANY", NULL,
764      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
765     {"/Prepare a Filter/Selected/A <-- ANY", NULL,
766      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
767     {"/Prepare a Filter/Selected/ANY <-> B", NULL,
768      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
769     {"/Prepare a Filter/Selected/ANY <-- B", NULL,
770      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
771     {"/Prepare a Filter/Selected/ANY --> B", NULL,
772      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
773
774     {"/Prepare a Filter/Not Selected", NULL, NULL, 0, "<Branch>", NULL,},
775     {"/Prepare a Filter/Not Selected/A <-> B", NULL,
776      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
777     {"/Prepare a Filter/Not Selected/A --> B", NULL,
778      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
779     {"/Prepare a Filter/Not Selected/A <-- B", NULL,
780      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
781     {"/Prepare a Filter/Not Selected/A <-> ANY", NULL,
782      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
783     {"/Prepare a Filter/Not Selected/A --> ANY", NULL,
784      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
785     {"/Prepare a Filter/Not Selected/A <-- ANY", NULL,
786      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
787     {"/Prepare a Filter/Not Selected/ANY <-> B", NULL,
788      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
789     {"/Prepare a Filter/Not Selected/ANY <-- B", NULL,
790      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
791     {"/Prepare a Filter/Not Selected/ANY --> B", NULL,
792      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
793
794     {"/Prepare a Filter/... and Selected", NULL, NULL, 0, "<Branch>", NULL,},
795     {"/Prepare a Filter/... and Selected/A <-> B", NULL,
796      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
797     {"/Prepare a Filter/... and Selected/A --> B", NULL,
798      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_B), NULL, NULL,},
799     {"/Prepare a Filter/... and Selected/A <-- B", NULL,
800      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_B), NULL, NULL,},
801     {"/Prepare a Filter/... and Selected/A <-> ANY", NULL,
802      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
803     {"/Prepare a Filter/... and Selected/A --> ANY", NULL,
804      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
805     {"/Prepare a Filter/... and Selected/A <-- ANY", NULL,
806      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
807     {"/Prepare a Filter/... and Selected/ANY <-> B", NULL,
808      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
809     {"/Prepare a Filter/... and Selected/ANY <-- B", NULL,
810      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
811     {"/Prepare a Filter/... and Selected/ANY --> B", NULL,
812      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
813
814     {"/Prepare a Filter/... or Selected", NULL, NULL, 0, "<Branch>", NULL,},
815     {"/Prepare a Filter/... or Selected/A <-> B", NULL,
816      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
817     {"/Prepare a Filter/... or Selected/A --> B", NULL,
818      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_B), NULL, NULL,},
819     {"/Prepare a Filter/... or Selected/A <-- B", NULL,
820      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_B), NULL, NULL,},
821     {"/Prepare a Filter/... or Selected/A <-> ANY", NULL,
822      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
823     {"/Prepare a Filter/... or Selected/A --> ANY", NULL,
824      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
825     {"/Prepare a Filter/... or Selected/A <-- ANY", NULL,
826      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
827     {"/Prepare a Filter/... or Selected/ANY <-> B", NULL,
828      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
829     {"/Prepare a Filter/... or Selected/ANY <-- B", NULL,
830      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
831     {"/Prepare a Filter/... or Selected/ANY --> B", NULL,
832      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
833
834     {"/Prepare a Filter/... and not Selected", NULL, NULL, 0, "<Branch>", NULL,},
835     {"/Prepare a Filter/... and not Selected/A <-> B", NULL,
836      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
837     {"/Prepare a Filter/... and not Selected/A --> B", NULL,
838      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
839     {"/Prepare a Filter/... and not Selected/A <-- B", NULL,
840      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
841     {"/Prepare a Filter/... and not Selected/A <-> ANY", NULL,
842      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
843     {"/Prepare a Filter/... and not Selected/A --> ANY", NULL,
844      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
845     {"/Prepare a Filter/... and not Selected/A <-- ANY", NULL,
846      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
847     {"/Prepare a Filter/... and not Selected/ANY <-> B", NULL,
848      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
849     {"/Prepare a Filter/... and not Selected/ANY <-- B", NULL,
850      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
851     {"/Prepare a Filter/... and not Selected/ANY --> B", NULL,
852      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
853
854     {"/Prepare a Filter/... or not Selected", NULL, NULL, 0, "<Branch>", NULL,},
855     {"/Prepare a Filter/... or not Selected/A <-> B", NULL,
856      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
857     {"/Prepare a Filter/... or not Selected/A --> B", NULL,
858      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
859     {"/Prepare a Filter/... or not Selected/A <-- B", NULL,
860      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
861     {"/Prepare a Filter/... or not Selected/A <-> ANY", NULL,
862      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
863     {"/Prepare a Filter/... or not Selected/A --> ANY", NULL,
864      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
865     {"/Prepare a Filter/... or not Selected/A <-- ANY", NULL,
866      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
867     {"/Prepare a Filter/... or not Selected/ANY <-> B", NULL,
868      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
869     {"/Prepare a Filter/... or not Selected/ANY <-- B", NULL,
870      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
871     {"/Prepare a Filter/... or not Selected/ANY --> B", NULL,
872      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
873
874     /* Find Packet */
875     {"/Find Packet", NULL, NULL, 0, "<Branch>", NULL,},
876     {"/Find Packet/Find Packet", NULL, NULL, 0, "<Branch>", NULL,},
877     {"/Find Packet/Find Packet/A <-> B", NULL,
878      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
879     {"/Find Packet/Find Packet/A --> B", NULL,
880      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
881     {"/Find Packet/Find Packet/A <-- B", NULL,
882      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
883     {"/Find Packet/Find Packet/A <-> ANY", NULL,
884      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
885     {"/Find Packet/Find Packet/A --> ANY", NULL,
886      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
887     {"/Find Packet/Find Packet/A <-- ANY", NULL,
888      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
889     {"/Find Packet/Find Packet/ANY <-> B", NULL,
890      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
891     {"/Find Packet/Find Packet/ANY <-- B", NULL,
892      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
893     {"/Find Packet/Find Packet/ANY --> B", NULL,
894      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
895     /* Find Next */
896     {"/Find Packet/Find Next", NULL, NULL, 0, "<Branch>", NULL,},
897     {"/Find Packet/Find Next/A <-> B", NULL,
898      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
899     {"/Find Packet/Find Next/A --> B", NULL,
900      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
901     {"/Find Packet/Find Next/A <-- B", NULL,
902      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
903     {"/Find Packet/Find Next/A <-> ANY", NULL,
904      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
905     {"/Find Packet/Find Next/A --> ANY", NULL,
906      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
907     {"/Find Packet/Find Next/A <-- ANY", NULL,
908      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
909     {"/Find Packet/Find Next/ANY <-> B", NULL,
910      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
911     {"/Find Packet/Find Next/ANY <-- B", NULL,
912      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
913     {"/Find Packet/Find Next/ANY --> B", NULL,
914      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
915     /* Find Previous */
916     {"/Find Packet/Find Previous", NULL, NULL, 0, "<Branch>", NULL,},
917     {"/Find Packet/Find Previous/A <-> B", NULL,
918      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
919     {"/Find Packet/Find Previous/A --> B", NULL,
920      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
921     {"/Find Packet/Find Previous/A <-- B", NULL,
922      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
923     {"/Find Packet/Find Previous/A <-> ANY", NULL,
924      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
925     {"/Find Packet/Find Previous/A --> ANY", NULL,
926      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
927     {"/Find Packet/Find Previous/A <-- ANY", NULL,
928      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
929     {"/Find Packet/Find Previous/ANY <-> B", NULL,
930      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
931     {"/Find Packet/Find Previous/ANY <-- B", NULL,
932      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
933     {"/Find Packet/Find Previous/ANY --> B", NULL,
934      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
935
936     /* Colorize Conversation */
937     {"/Colorize Conversation", NULL, NULL, 0, "<Branch>", NULL,},
938     {"/Colorize Conversation/A <-> B", NULL,
939      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
940     {"/Colorize Conversation/A --> B", NULL,
941      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
942     {"/Colorize Conversation/A <-- B", NULL,
943      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
944     {"/Colorize Conversation/A <-> ANY", NULL,
945      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
946     {"/Colorize Conversation/A --> ANY", NULL,
947      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
948     {"/Colorize Conversation/A <-- ANY", NULL,
949      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
950     {"/Colorize Conversation/ANY <-> B", NULL,
951      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
952     {"/Colorize Conversation/ANY <-- B", NULL,
953      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
954     {"/Colorize Conversation/ANY --> B", NULL,
955      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,}
956 };
957
958 static void
959 ct_create_popup_menu(conversations_table *ct)
960 {
961     GtkItemFactory *item_factory;
962
963     item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
964
965     gtk_item_factory_create_items_ac(item_factory, sizeof(ct_list_menu_items)/sizeof(ct_list_menu_items[0]), ct_list_menu_items, ct, 2);
966
967     ct->menu = gtk_item_factory_get_widget(item_factory, "<main>");
968     g_signal_connect(ct->table, "button_press_event", G_CALLBACK(ct_show_popup_menu_cb), ct);
969 }
970
971 /* Draw/refresh the address fields of a single entry at the specified index */
972 static void
973 get_ct_table_address(conversations_table *ct, conv_t *conv, char **entries)
974 {
975     char *port;
976     guint32 pt;
977
978     if(!ct->resolve_names)
979         entries[0] = ep_address_to_str(&conv->src_address);
980     else {
981         entries[0] = (char *)get_addr_name(&conv->src_address);
982     }
983
984     pt = conv->port_type;
985     if(!ct->resolve_names) pt = PT_NONE;
986     switch(pt) {
987     case(PT_TCP):
988         entries[1] = get_tcp_port(conv->src_port);
989         break;
990     case(PT_UDP):
991         entries[1] = get_udp_port(conv->src_port);
992         break;
993     case(PT_SCTP):
994         entries[1] = get_sctp_port(conv->src_port);
995         break;
996     default:
997         port=ct_port_to_str(conv->port_type, conv->src_port);
998         entries[1] = port?port:"";
999     }
1000
1001     if(!ct->resolve_names)
1002         entries[2]=ep_address_to_str(&conv->dst_address);
1003     else {
1004         entries[2]=(char *)get_addr_name(&conv->dst_address);
1005     }
1006
1007     switch(pt) {
1008     case(PT_TCP):
1009         entries[3]=get_tcp_port(conv->dst_port);
1010         break;
1011     case(PT_UDP):
1012         entries[3]=get_udp_port(conv->dst_port);
1013         break;
1014     case(PT_SCTP):
1015         entries[3]=get_sctp_port(conv->dst_port);
1016         break;
1017     default:
1018         port=ct_port_to_str(conv->port_type, conv->dst_port);
1019         entries[3]=port?port:"";
1020     }
1021 }
1022
1023 /* Refresh the address fields of all entries in the list */
1024 static void
1025 draw_ct_table_addresses(conversations_table *ct)
1026 {
1027     guint32 i;
1028     char *entries[4];
1029     GtkListStore *store;
1030
1031     if (!ct->num_conversations)
1032         return;
1033         
1034     store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table)); 
1035     g_object_ref(store);
1036     gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL);
1037
1038     for(i=0;i<ct->num_conversations;i++){
1039         conv_t *conv = &g_array_index(ct->conversations, conv_t, i);
1040         if (!conv->iter_valid) 
1041             continue;
1042         get_ct_table_address(ct, conv, entries);
1043         gtk_list_store_set (store, &conv->iter,
1044                   SRC_ADR_COLUMN, entries[0],   
1045                   SRC_PORT_COLUMN, entries[1],
1046                   DST_ADR_COLUMN, entries[2],
1047                   DST_PORT_COLUMN, entries[3],
1048                     -1);
1049     }
1050     
1051     gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store));
1052     g_object_unref(store);
1053 }
1054
1055 static void
1056 draw_ct_table_data(conversations_table *ct)
1057 {
1058     guint32 i;
1059     char title[256];
1060     GtkListStore *store;
1061     gboolean first = TRUE;
1062
1063     if (ct->page_lb) {
1064         if(ct->num_conversations) {
1065             g_snprintf(title, sizeof(title), "%s: %u", ct->name, ct->num_conversations);
1066         } else {
1067             g_snprintf(title, sizeof(title), "%s", ct->name);
1068         }
1069         gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
1070         gtk_widget_set_sensitive(ct->page_lb, ct->num_conversations);
1071     } else {
1072         if(ct->num_conversations) {
1073             g_snprintf(title, sizeof(title), "%s Conversations: %u", ct->name, ct->num_conversations);
1074         } else {
1075             g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
1076         }
1077         gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
1078     }
1079     
1080     store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table)); 
1081
1082     for(i=0;i<ct->num_conversations;i++){
1083         char start_time[COL_STR_LEN], duration[COL_STR_LEN],
1084              txbps[COL_STR_LEN], rxbps[COL_STR_LEN];
1085         char *tx_ptr, *rx_ptr;
1086         double duration_s;
1087         conv_t *conversation = &g_array_index(ct->conversations, conv_t, i);
1088
1089         if (!conversation->modified)
1090             continue;
1091             
1092         if (first) {
1093             g_object_ref(store);
1094             gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL);
1095
1096             first = FALSE;
1097         }
1098         duration_s = nstime_to_sec(&conversation->stop_time) - nstime_to_sec(&conversation->start_time);
1099         g_snprintf(start_time, COL_STR_LEN, "%s", rel_time_to_secs_str(&conversation->start_time));
1100         g_snprintf(duration, COL_STR_LEN, "%.4f", duration_s);
1101
1102         if (duration_s > 0 && conversation->tx_frames > 1) {
1103             g_snprintf(txbps, COL_STR_LEN, "%.2f", (gint64) conversation->tx_bytes * 8 / duration_s);
1104             tx_ptr = txbps;
1105         } else {
1106             tx_ptr =  NO_BPS_STR;
1107         }
1108         if (duration_s > 0 && conversation->rx_frames > 1) {
1109             g_snprintf(rxbps, COL_STR_LEN, "%.2f", (gint64) conversation->rx_bytes * 8 / duration_s);
1110             rx_ptr = rxbps;
1111         } else {
1112             rx_ptr = NO_BPS_STR;
1113         }
1114         conversation->modified = FALSE;
1115         if (!conversation->iter_valid) {
1116             char *entries[4];
1117         
1118             get_ct_table_address(ct, conversation, entries);
1119             conversation->iter_valid = TRUE;
1120 #if GTK_CHECK_VERSION(2,6,0)
1121             gtk_list_store_insert_with_values( store , &conversation->iter, G_MAXINT,
1122 #else
1123             gtk_list_store_append(store, &conversation->iter);
1124             gtk_list_store_set (store, &conversation->iter,
1125 #endif        
1126                   SRC_ADR_COLUMN,  entries[0],
1127                   SRC_PORT_COLUMN, entries[1],
1128                   DST_ADR_COLUMN,  entries[2],
1129                   DST_PORT_COLUMN, entries[3],
1130                   PACKETS_COLUMN,  conversation->tx_frames+conversation->rx_frames,
1131                   BYTES_COLUMN,    conversation->tx_bytes+conversation->rx_bytes,
1132                   PKT_AB_COLUMN,   conversation->tx_frames,
1133                   BYTES_AB_COLUMN, conversation->tx_bytes,
1134                   PKT_BA_COLUMN,   conversation->rx_frames,
1135                   BYTES_BA_COLUMN, conversation->rx_bytes,
1136                   START_COLUMN,    start_time,
1137                   DURATION_COLUMN, duration,
1138                   BPS_AB_COLUMN,   tx_ptr,
1139                   BPS_BA_COLUMN,   rx_ptr,
1140                   INDEX_COLUMN,    i,
1141                     -1);
1142         }
1143         else {
1144             gtk_list_store_set (store, &conversation->iter,
1145                   PACKETS_COLUMN,  conversation->tx_frames+conversation->rx_frames,
1146                   BYTES_COLUMN,    conversation->tx_bytes+conversation->rx_bytes,
1147                   PKT_AB_COLUMN,   conversation->tx_frames,
1148                   BYTES_AB_COLUMN, conversation->tx_bytes,
1149                   PKT_BA_COLUMN,   conversation->rx_frames,
1150                   BYTES_BA_COLUMN, conversation->rx_bytes,
1151                   START_COLUMN,    start_time,
1152                   DURATION_COLUMN, duration,
1153                   BPS_AB_COLUMN,   tx_ptr,
1154                   BPS_BA_COLUMN,   rx_ptr,
1155                     -1);
1156         }
1157     }
1158     if (!first) {
1159             if (!ct->fixed_col && ct->num_conversations >= 1000) {
1160                 /* finding the right size for a column isn't easy
1161                  * let it run in autosize a little (1000 is arbitrary)
1162                  * and then switch to fixed width.
1163                 */
1164                 ct->fixed_col = TRUE;
1165
1166                 switch_to_fixed_col(ct->table);
1167             }
1168
1169             gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store));
1170             g_object_unref(store);
1171     }
1172 }
1173
1174 static void
1175 draw_ct_table_data_cb(void *arg)
1176 {
1177     draw_ct_table_data(arg);
1178 }
1179
1180 typedef struct {
1181     int                 nb_cols;
1182     gint                columns_order[N_COLUMNS];
1183     GString             *CSV_str;
1184     conversations_table *talkers;
1185 } csv_t;
1186
1187 /* output in C locale */
1188 static gboolean
1189 csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
1190               gpointer data)
1191 {
1192         csv_t   *csv = (csv_t *)data;
1193         gchar   *table_text;
1194         int      i;
1195         unsigned index;
1196         conv_t   *conv;
1197         double duration_s;
1198         guint64  value;
1199
1200         gtk_tree_model_get(model, iter, INDEX_COLUMN, &index, -1);
1201         conv=&g_array_index(csv->talkers->conversations, conv_t, index);
1202         duration_s = nstime_to_sec(&conv->stop_time) - nstime_to_sec(&conv->start_time);
1203
1204         for (i=0; i< csv->nb_cols; i++) {
1205             if (i)
1206                 g_string_append(csv->CSV_str, ",");
1207
1208             switch(csv->columns_order[i]) {
1209             case SRC_ADR_COLUMN:
1210             case SRC_PORT_COLUMN:
1211             case DST_ADR_COLUMN:
1212             case DST_PORT_COLUMN:
1213                 gtk_tree_model_get(model, iter, csv->columns_order[i], &table_text, -1);
1214                 if (table_text) {
1215                     g_string_append(csv->CSV_str, table_text);
1216                     g_free(table_text);
1217                 }
1218                 break;
1219             case PACKETS_COLUMN:
1220             case BYTES_COLUMN:
1221             case PKT_AB_COLUMN:
1222             case BYTES_AB_COLUMN:
1223             case PKT_BA_COLUMN:
1224             case BYTES_BA_COLUMN:
1225                 gtk_tree_model_get(model, iter, csv->columns_order[i], &value, -1);
1226                 g_string_append_printf(csv->CSV_str, "%" G_GINT64_MODIFIER "u", value);
1227                 break;
1228             case START_COLUMN:
1229                 g_string_append_printf(csv->CSV_str, "%s", rel_time_to_secs_str(&conv->start_time));
1230                 break;
1231             case DURATION_COLUMN:
1232                  g_string_append_printf(csv->CSV_str, "%.4f", duration_s);
1233                 break;
1234             case BPS_AB_COLUMN:
1235                 if (duration_s > 0 && conv->tx_frames > 1) {
1236                     g_string_append_printf(csv->CSV_str, "%.2f", (gint64) conv->tx_bytes * 8 / duration_s);
1237                 } else {
1238                   g_string_append(csv->CSV_str, NO_BPS_STR);
1239                 }
1240                 break;
1241             case BPS_BA_COLUMN:
1242                 if (duration_s > 0 && conv->rx_frames > 1) {
1243                     g_string_append_printf(csv->CSV_str, "%.2f", (gint64) conv->rx_bytes * 8 / duration_s);
1244                 } else {
1245                   g_string_append(csv->CSV_str, NO_BPS_STR);
1246                 }
1247                 break;
1248             default:
1249                 break;
1250             }
1251         }
1252         g_string_append(csv->CSV_str,"\n");
1253
1254         return FALSE;
1255 }
1256
1257
1258 static void
1259 copy_as_csv_cb(GtkWindow *copy_bt, gpointer data _U_)
1260 {
1261     GtkClipboard    *cb;
1262     char            *savelocale;
1263     GList           *columns, *list;
1264     GtkTreeViewColumn *column;
1265     GtkListStore    *store;
1266     csv_t            csv;
1267
1268     csv.talkers=g_object_get_data(G_OBJECT(copy_bt), CONV_PTR_KEY);
1269     if (!csv.talkers)
1270         return;
1271
1272     savelocale = setlocale(LC_NUMERIC, NULL);
1273     setlocale(LC_NUMERIC, "C");
1274     csv.CSV_str = g_string_new("");
1275
1276     columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(csv.talkers->table));
1277     csv.nb_cols = 0;
1278     list = columns;
1279     while(columns) {
1280         column = columns->data;
1281         if (gtk_tree_view_column_get_visible(column)) {
1282             csv.columns_order[csv.nb_cols] = gtk_tree_view_column_get_sort_column_id(column);
1283             if (csv.nb_cols)
1284                 g_string_append(csv.CSV_str, ",");
1285             g_string_append(csv.CSV_str, gtk_tree_view_column_get_title(column));
1286             csv.nb_cols++;
1287         }
1288         columns = g_list_next(columns);
1289     }
1290     g_list_free(list);
1291
1292     g_string_append(csv.CSV_str,"\n");
1293     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(csv.talkers->table)));
1294     gtk_tree_model_foreach(GTK_TREE_MODEL(store), csv_handle, &csv);
1295
1296     /* Now that we have the CSV data, copy it into the default clipboard */
1297     cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);      /* Get the default clipboard */
1298     gtk_clipboard_set_text(cb, csv.CSV_str->str, -1);    /* Copy the CSV data into the clipboard */
1299     setlocale(LC_NUMERIC, savelocale);
1300     g_string_free(csv.CSV_str, TRUE);                    /* Free the memory */
1301 }
1302
1303 static gint default_col_size[N_COLUMNS];
1304
1305 static void
1306 init_default_col_size(GtkWidget *view)
1307 {
1308
1309     default_col_size[SRC_ADR_COLUMN] = get_default_col_size(view, "00000000.000000000000");
1310     default_col_size[DST_ADR_COLUMN] = default_col_size[SRC_ADR_COLUMN];
1311     default_col_size[SRC_PORT_COLUMN] = get_default_col_size(view, "000000");
1312     default_col_size[DST_PORT_COLUMN] = default_col_size[SRC_PORT_COLUMN];
1313     default_col_size[PACKETS_COLUMN] = get_default_col_size(view, "00 000 000");
1314     default_col_size[BYTES_COLUMN] = get_default_col_size(view, "0 000 000 000");
1315     default_col_size[PKT_AB_COLUMN] = default_col_size[PACKETS_COLUMN]; 
1316     default_col_size[PKT_BA_COLUMN] = default_col_size[PACKETS_COLUMN];
1317     default_col_size[BYTES_AB_COLUMN] = default_col_size[BYTES_COLUMN];
1318     default_col_size[BYTES_BA_COLUMN] = default_col_size[BYTES_COLUMN];
1319     default_col_size[START_COLUMN] = get_default_col_size(view, "000000.000000000");
1320     default_col_size[DURATION_COLUMN] = get_default_col_size(view, "000000.0000");
1321     default_col_size[BPS_AB_COLUMN] = get_default_col_size(view, "000000000.00");
1322     default_col_size[BPS_BA_COLUMN] = default_col_size[BPS_AB_COLUMN];
1323 }
1324
1325 static gboolean
1326 init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, 
1327     tap_packet_cb packet_func)
1328 {
1329     int i;
1330     GString *error_string;
1331     char title[256];
1332
1333     GtkListStore *store;
1334     GtkWidget *tree;
1335     GtkTreeViewColumn *column;
1336     GtkCellRenderer *renderer;
1337     GtkTreeSortable *sortable;
1338     GtkTreeSelection  *sel;
1339     static gboolean col_size = FALSE;
1340
1341     conversations->page_lb=NULL;
1342     conversations->resolve_names=TRUE;
1343     conversations->has_ports=!hide_ports;
1344     conversations->fixed_col = FALSE;
1345     conversations->default_titles[0]="Address A",
1346     conversations->default_titles[1]="Port A";
1347     conversations->default_titles[2]="Address B";
1348     conversations->default_titles[3]="Port B";
1349     conversations->default_titles[4]="Packets";
1350     conversations->default_titles[5]="Bytes";
1351     conversations->default_titles[6]="Packets A->B";
1352     conversations->default_titles[7]="Bytes A->B";
1353     conversations->default_titles[8]="Packets A<-B";
1354     conversations->default_titles[9]="Bytes A<-B";
1355     conversations->default_titles[10]="Rel Start";
1356     conversations->default_titles[11]="Duration";
1357     conversations->default_titles[12]="bps A->B";
1358     conversations->default_titles[13]="bps A<-B";
1359
1360     if (strcmp(table_name, "NCP")==0) {
1361         conversations->default_titles[1]="Connection A";
1362         conversations->default_titles[3]="Connection B";
1363     }
1364
1365     g_snprintf(title, sizeof(title), "%s Conversations", table_name);
1366     conversations->name_lb=gtk_label_new(title);
1367
1368     
1369     /* Create the store */
1370     store = gtk_list_store_new (N_COLUMNS,  /* Total number of columns */
1371                                G_TYPE_STRING,   /* Address A */
1372                                G_TYPE_STRING,   /* Port A    */
1373                                G_TYPE_STRING,   /* Address B */
1374                                G_TYPE_STRING,   /* Port B    */
1375                                G_TYPE_UINT64,   /* Packets   */
1376                                G_TYPE_UINT64,   /* Bytes     */
1377                                G_TYPE_UINT64,   /* Packets A->B */
1378                                G_TYPE_UINT64,   /* Bytes  A->B  */
1379                                G_TYPE_UINT64,   /* Packets A<-B */
1380                                G_TYPE_UINT64,   /* Bytes  A<-B */
1381                                G_TYPE_STRING,   /* Start */
1382                                G_TYPE_STRING,   /* Duration */
1383                                G_TYPE_STRING,   /* bps A->B */
1384                                G_TYPE_STRING,   /* bps A<-B */
1385                                G_TYPE_UINT);    /* Index */
1386
1387     gtk_box_pack_start(GTK_BOX(vbox), conversations->name_lb, FALSE, FALSE, 0);
1388
1389     conversations->scrolled_window=scrolled_window_new(NULL, NULL);
1390     gtk_box_pack_start(GTK_BOX(vbox), conversations->scrolled_window, TRUE, TRUE, 0);
1391
1392     tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1393     conversations->table = GTK_TREE_VIEW(tree);
1394     sortable = GTK_TREE_SORTABLE(store);
1395
1396     if (!col_size) {
1397         col_size = TRUE;
1398         init_default_col_size(GTK_WIDGET(conversations->table));
1399     }
1400
1401     /* The view now holds a reference.  We can get rid of our own reference */
1402     g_object_unref (G_OBJECT (store));
1403
1404     g_object_set_data(G_OBJECT(store), CONV_PTR_KEY, conversations);
1405     g_object_set_data(G_OBJECT(conversations->table), CONV_PTR_KEY, conversations);
1406
1407     for (i = 0; i < N_COLUMNS -1; i++) {
1408         renderer = gtk_cell_renderer_text_new ();
1409         g_object_set(renderer, "ypad", 0, NULL);
1410         switch(i) {
1411         case 0: /* addresses and ports */
1412         case 1:
1413         case 2:
1414         case 3:
1415             column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text", 
1416                                 i, NULL);
1417             if(hide_ports && (i == 1 || i == 3)){
1418               /* hide srcport and dstport if we don't use ports */
1419               gtk_tree_view_column_set_visible(column, FALSE);
1420             }
1421             gtk_tree_sortable_set_sort_func(sortable, i, ct_sort_func, GINT_TO_POINTER(i), NULL);
1422             break;
1423         case 4: /* counts */
1424         case 5:
1425         case 6:
1426         case 7:
1427         case 8:
1428         case 9: /* right align numbers */
1429             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1430             column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, NULL);
1431             gtk_tree_view_column_set_cell_data_func(column, renderer, u64_data_func,  GINT_TO_POINTER(i), NULL);
1432             break;
1433         default: /* times and bps */
1434             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1435             column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text", 
1436                                 i, NULL);
1437
1438             gtk_tree_sortable_set_sort_func(sortable, i, ct_sort_func, GINT_TO_POINTER(i), NULL);
1439             break;
1440         }
1441         gtk_tree_view_column_set_sort_column_id(column, i);
1442         gtk_tree_view_column_set_resizable(column, TRUE);
1443         gtk_tree_view_column_set_reorderable(column, TRUE);
1444         gtk_tree_view_column_set_min_width(column, 40);
1445         gtk_tree_view_column_set_fixed_width(column, default_col_size[i]);
1446         gtk_tree_view_append_column (conversations->table, column);
1447 #if 0
1448         /* for capture with ten thousands conversations it's too slow */
1449         if (i == PACKETS_COLUMN) {
1450               gtk_tree_view_column_clicked(column);
1451               gtk_tree_view_column_clicked(column);
1452         }
1453 #endif
1454     }
1455     gtk_container_add(GTK_CONTAINER(conversations->scrolled_window), (GtkWidget *)conversations->table);
1456     gtk_tree_view_set_rules_hint(conversations->table, TRUE);
1457     gtk_tree_view_set_headers_clickable(conversations->table, TRUE);
1458     gtk_tree_view_set_reorderable (conversations->table, TRUE);
1459
1460     conversations->num_conversations=0;
1461     conversations->conversations=NULL;
1462     conversations->hashtable=NULL;
1463
1464     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(conversations->table));
1465     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
1466
1467     /* create popup menu for this table */
1468     ct_create_popup_menu(conversations);
1469
1470     /* register the tap and rerun the taps on the packet list */
1471     error_string=register_tap_listener(tap_name, conversations, filter, 0, reset_ct_table_data_cb, packet_func, 
1472         draw_ct_table_data_cb);
1473     if(error_string){
1474         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1475         g_string_free(error_string, TRUE);
1476         return FALSE;
1477     }
1478
1479     return TRUE;
1480 }
1481
1482
1483 void
1484 init_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1485 {
1486     conversations_table *conversations;
1487     char title[256];
1488     GtkWidget *vbox;
1489     GtkWidget *bbox;
1490     GtkWidget *close_bt, *help_bt;
1491     gboolean ret;
1492     GtkWidget *copy_bt;
1493     GtkTooltips *tooltips = gtk_tooltips_new();
1494
1495     conversations=g_malloc0(sizeof(conversations_table));
1496
1497     conversations->name=table_name;
1498     conversations->filter=filter;
1499     conversations->use_dfilter=FALSE;
1500     g_snprintf(title, sizeof(title), "%s Conversations: %s", table_name, cf_get_display_name(&cfile));
1501         conversations->win = dlg_window_new(title);  /* transient_for top_level */
1502         gtk_window_set_destroy_with_parent (GTK_WINDOW(conversations->win), TRUE);
1503
1504     gtk_window_set_default_size(GTK_WINDOW(conversations->win), 750, 400);
1505
1506     vbox=gtk_vbox_new(FALSE, 3);
1507     gtk_container_add(GTK_CONTAINER(conversations->win), vbox);
1508     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1509
1510     ret = init_ct_table_page(conversations, vbox, hide_ports, table_name, tap_name, filter, packet_func);
1511     if(ret == FALSE) {
1512         g_free(conversations);
1513         return;
1514     }
1515
1516     /* Button row. */
1517     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1518     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1519     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
1520     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
1521
1522     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1523     window_set_cancel_button(conversations->win, close_bt, window_cancel_button_cb);
1524
1525     copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
1526     gtk_tooltips_set_tip(tooltips, copy_bt,
1527                          "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.", NULL);
1528     g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, conversations);
1529     g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
1530
1531     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1532     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
1533
1534     g_signal_connect(conversations->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1535     g_signal_connect(conversations->win, "destroy", G_CALLBACK(ct_win_destroy_cb), conversations);
1536
1537     gtk_widget_show_all(conversations->win);
1538     window_present(conversations->win);
1539
1540     cf_retap_packets(&cfile);
1541     gdk_window_raise(conversations->win->window);
1542
1543 }
1544
1545
1546
1547 static void
1548 ct_nb_switch_page_cb(GtkNotebook *nb, GtkNotebookPage *pg _U_, guint page, gpointer data)
1549 {
1550     GtkWidget *copy_bt = (GtkWidget *) data;
1551     void ** pages = g_object_get_data(G_OBJECT(nb), NB_PAGES_KEY);
1552
1553     page++;
1554
1555     if (pages && page > 0 && (int) page <= GPOINTER_TO_INT(pages[0]) && copy_bt) {
1556         g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
1557     }
1558 }
1559
1560 static void
1561 ct_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
1562 {
1563     void ** pages = data;
1564     int page;
1565
1566     /* first "page" contains the number of pages */
1567     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1568         ct_win_destroy_cb(NULL, pages[page]);
1569     }
1570     g_free(pages);
1571 }
1572
1573 static conversations_table *
1574 init_ct_notebook_page_cb(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1575 {
1576     gboolean ret;
1577     GtkWidget *page_vbox;
1578     conversations_table *conversations;
1579
1580     conversations=g_malloc0(sizeof(conversations_table));
1581     conversations->name=table_name;
1582     conversations->filter=filter;
1583     conversations->resolve_names=TRUE;
1584     conversations->use_dfilter=FALSE;
1585
1586     page_vbox=gtk_vbox_new(FALSE, 6);
1587     conversations->win = page_vbox;
1588     gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
1589
1590     ret = init_ct_table_page(conversations, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
1591     if(ret == FALSE) {
1592         g_free(conversations);
1593         return NULL;
1594     }
1595
1596     return conversations;
1597 }
1598
1599
1600 typedef struct {
1601     gboolean hide_ports;       /* hide TCP / UDP port columns */
1602     const char *table_name;    /* GUI output name */
1603     const char *tap_name;      /* internal name */
1604     const char *filter;        /* display filter string (unused) */
1605     tap_packet_cb packet_func; /* function to be called for new incoming packets */
1606 } register_ct_t;
1607
1608
1609 static GSList *registered_ct_tables = NULL;
1610
1611 void
1612 register_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1613 {
1614     register_ct_t *table;
1615
1616     table = g_malloc(sizeof(register_ct_t));
1617
1618     table->hide_ports   = hide_ports;
1619     table->table_name   = table_name;
1620     table->tap_name     = tap_name;
1621     table->filter       = filter;
1622     table->packet_func  = packet_func;
1623
1624     registered_ct_tables = g_slist_append(registered_ct_tables, table);
1625 }
1626
1627
1628 static void
1629 ct_resolve_toggle_dest(GtkWidget *widget, gpointer data)
1630 {
1631     int page;
1632     void ** pages = data;
1633     gboolean resolve_names;
1634     conversations_table *conversations;
1635
1636
1637     resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1638
1639     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1640         conversations = pages[page];
1641         conversations->resolve_names = resolve_names;
1642
1643         draw_ct_table_addresses(conversations);
1644
1645     }
1646 }
1647
1648
1649 static void
1650 ct_filter_toggle_dest(GtkWidget *widget, gpointer data)
1651 {
1652     int page;
1653     void ** pages = data;
1654     gboolean use_filter;
1655     conversations_table *conversations = NULL;
1656
1657     use_filter = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1658
1659     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1660         conversations = pages[page];
1661         conversations->use_dfilter = use_filter;
1662         reset_ct_table_data(conversations);
1663     }
1664
1665     cf_retap_packets(&cfile);
1666     if (conversations) {
1667         gdk_window_raise(conversations->win->window);
1668     }
1669 }
1670
1671
1672 void
1673 init_conversation_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
1674 {
1675     conversations_table *conversations;
1676     char title[256];
1677     GtkWidget *vbox;
1678     GtkWidget *hbox;
1679     GtkWidget *bbox;
1680     GtkWidget *close_bt, *help_bt;
1681     GtkWidget *win;
1682     GtkWidget *resolv_cb;
1683     GtkWidget *filter_cb;
1684     int page;
1685     void ** pages;
1686     GtkWidget *nb;
1687     GtkWidget *page_lb;
1688     GSList  *current_table;
1689     register_ct_t *registered;
1690     GtkTooltips *tooltips = gtk_tooltips_new();
1691
1692     GtkWidget *copy_bt;
1693
1694     pages = g_malloc(sizeof(void *) * (g_slist_length(registered_ct_tables) + 1));
1695
1696     g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
1697         win = dlg_window_new(title);  /* transient_for top_level */
1698         gtk_window_set_destroy_with_parent (GTK_WINDOW(win), TRUE);
1699
1700     gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
1701
1702     vbox=gtk_vbox_new(FALSE, 6);
1703     gtk_container_add(GTK_CONTAINER(win), vbox);
1704     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1705
1706     nb = gtk_notebook_new();
1707     gtk_container_add(GTK_CONTAINER(vbox), nb);
1708     g_object_set_data(G_OBJECT(nb), NB_PAGES_KEY, pages);
1709
1710     page = 0;
1711
1712     current_table = registered_ct_tables;
1713     while(current_table) {
1714         registered = current_table->data;
1715         page_lb = gtk_label_new("");
1716         conversations = init_ct_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
1717                                                  registered->filter, registered->packet_func);
1718         g_object_set_data(G_OBJECT(conversations->win), CONV_PTR_KEY, conversations);
1719         gtk_notebook_append_page(GTK_NOTEBOOK(nb), conversations->win, page_lb);
1720         conversations->win = win;
1721         conversations->page_lb = page_lb;
1722         pages[++page] = conversations;
1723
1724         current_table = g_slist_next(current_table);
1725     }
1726
1727     pages[0] = GINT_TO_POINTER(page);
1728
1729     hbox = gtk_hbox_new(FALSE, 3);
1730     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1731
1732     resolv_cb = gtk_check_button_new_with_mnemonic("Name resolution");
1733     gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
1734     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
1735     gtk_tooltips_set_tip(tooltips, resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
1736                          "Please note: The corresponding name resolution must be enabled.", NULL);
1737
1738     g_signal_connect(resolv_cb, "toggled", G_CALLBACK(ct_resolve_toggle_dest), pages);
1739
1740     filter_cb = gtk_check_button_new_with_mnemonic("Limit to display filter");
1741     gtk_container_add(GTK_CONTAINER(hbox), filter_cb);
1742     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
1743     gtk_tooltips_set_tip(tooltips, filter_cb, "Limit the list to conversations matching the current display filter.", NULL);
1744
1745     g_signal_connect(filter_cb, "toggled", G_CALLBACK(ct_filter_toggle_dest), pages);
1746
1747     /* Button row. */
1748     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1749     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1750     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
1751
1752     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
1753
1754     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1755     window_set_cancel_button(win, close_bt, window_cancel_button_cb);
1756
1757     copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
1758     gtk_tooltips_set_tip(tooltips, copy_bt,
1759                          "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.", NULL);
1760     g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
1761     g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
1762
1763     g_signal_connect(nb, "switch-page", G_CALLBACK(ct_nb_switch_page_cb), copy_bt);
1764
1765     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1766     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
1767
1768     g_signal_connect(win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1769     g_signal_connect(win, "destroy", G_CALLBACK(ct_win_destroy_notebook_cb), pages);
1770
1771     gtk_widget_show_all(win);
1772     window_present(win);
1773
1774     cf_retap_packets(&cfile);
1775     gdk_window_raise(win->window);
1776
1777 }
1778
1779 typedef struct _key {
1780         address addr1;
1781         address addr2;
1782         guint32 port1;
1783         guint32 port2;
1784 } conv_key_t;
1785
1786
1787 /*
1788  * Compute the hash value for two given address/port pairs if the match
1789  * is to be exact.
1790  */
1791 static guint
1792 conversation_hash(gconstpointer v)
1793 {
1794         const conv_key_t *key = (const conv_key_t *)v;
1795         guint hash_val;
1796
1797         hash_val = 0;
1798         ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
1799         hash_val += key->port1;
1800         ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
1801         hash_val += key->port2;
1802
1803         return hash_val;
1804 }
1805
1806 /*
1807  * Compare two conversation keys for an exact match.
1808  */
1809 static gint
1810 conversation_match(gconstpointer v, gconstpointer w)
1811 {
1812         const conv_key_t *v1 = (const conv_key_t *)v;
1813         const conv_key_t *v2 = (const conv_key_t *)w;
1814
1815         if (v1->port1 == v2->port1 &&
1816             v1->port2 == v2->port2 &&
1817             ADDRESSES_EQUAL(&v1->addr1, &v2->addr1) &&
1818             ADDRESSES_EQUAL(&v1->addr2, &v2->addr2)) {
1819                 return 1;
1820         }
1821
1822         if (v1->port2 == v2->port1 &&
1823             v1->port1 == v2->port2 &&
1824             ADDRESSES_EQUAL(&v1->addr2, &v2->addr1) &&
1825             ADDRESSES_EQUAL(&v1->addr1, &v2->addr2)) {
1826                 return 1;
1827         }
1828
1829         /*
1830          * The addresses or the ports don't match.
1831          */
1832         return 0;
1833 }
1834
1835
1836 void
1837 add_conversation_table_data(conversations_table *ct, const address *src, const address *dst, guint32 src_port, guint32 dst_port, int num_frames, int num_bytes, nstime_t *ts, SAT_E sat, int port_type)
1838 {
1839     const address *addr1, *addr2;
1840     guint32 port1, port2;
1841     conv_t *conversation=NULL;
1842     unsigned int conversation_idx=0;
1843
1844     if(src_port>dst_port){
1845         addr1=src;
1846         addr2=dst;
1847         port1=src_port;
1848         port2=dst_port;
1849     } else if(src_port<dst_port){
1850         addr2=src;
1851         addr1=dst;
1852         port2=src_port;
1853         port1=dst_port;
1854     } else if(CMP_ADDRESS(src, dst)<0){
1855         addr1=src;
1856         addr2=dst;
1857         port1=src_port;
1858         port2=dst_port;
1859     } else {
1860         addr2=src;
1861         addr1=dst;
1862         port2=src_port;
1863         port1=dst_port;
1864     }
1865
1866     /* if we dont have any entries at all yet */
1867     if(ct->conversations==NULL){
1868         ct->conversations= g_array_sized_new(FALSE, FALSE, sizeof(conv_t), 10000);
1869
1870         ct->hashtable = g_hash_table_new_full(conversation_hash,
1871                         conversation_match, /* key_equal_func */
1872                         g_free,                         /* key_destroy_func */
1873                         NULL);                          /* value_destroy_func */
1874
1875     }
1876     else {
1877         /* try to find it among the existing known conversations */
1878         conv_key_t existing_key;
1879
1880         existing_key.addr1 = *addr1;
1881         existing_key.addr2 = *addr2;
1882         existing_key.port1 = port1;
1883         existing_key.port2 = port2;
1884         conversation_idx = GPOINTER_TO_UINT(g_hash_table_lookup(ct->hashtable, &existing_key));
1885         if (conversation_idx) {
1886             conversation_idx--;
1887             conversation=&g_array_index(ct->conversations, conv_t, conversation_idx);
1888         }
1889     }
1890
1891     /* if we still dont know what conversation this is it has to be a new one
1892        and we have to allocate it and append it to the end of the list */
1893     if(conversation==NULL){
1894         conv_key_t *new_key;
1895         conv_t conv;
1896         
1897         COPY_ADDRESS(&conv.src_address, addr1);
1898         COPY_ADDRESS(&conv.dst_address, addr2);
1899         conv.sat=sat;
1900         conv.port_type=port_type;
1901         conv.src_port=port1;
1902         conv.dst_port=port2;
1903         conv.rx_frames=0;
1904         conv.tx_frames=0;
1905         conv.rx_bytes=0;
1906         conv.tx_bytes=0;
1907         conv.iter_valid = FALSE;
1908         conv.modified = TRUE;
1909         
1910         if (ts) {
1911             memcpy(&conv.start_time, ts, sizeof(conv.start_time));
1912             memcpy(&conv.stop_time, ts, sizeof(conv.stop_time));
1913         } else {
1914             nstime_set_unset(&conv.start_time);
1915             nstime_set_unset(&conv.stop_time);
1916         }
1917         g_array_append_val(ct->conversations, conv);
1918         conversation_idx=ct->num_conversations;
1919         conversation=&g_array_index(ct->conversations, conv_t, conversation_idx);
1920
1921         /* ct->conversations address is not a constant but src/dst_address.data are */
1922         new_key = g_new(conv_key_t, 1);
1923                 SET_ADDRESS(&new_key->addr1, conversation->src_address.type, conversation->src_address.len, conversation->src_address.data);
1924                 SET_ADDRESS(&new_key->addr2, conversation->dst_address.type, conversation->dst_address.len, conversation->dst_address.data);
1925                 new_key->port1 = port1;
1926                 new_key->port2 = port2;
1927         g_hash_table_insert(ct->hashtable, new_key, GUINT_TO_POINTER(conversation_idx +1));
1928
1929         ct->num_conversations++;
1930     }
1931
1932     /* update the conversation struct */
1933     conversation->modified = TRUE;
1934     if( (!CMP_ADDRESS(src, addr1))&&(!CMP_ADDRESS(dst, addr2))&&(src_port==port1)&&(dst_port==port2) ){
1935         conversation->tx_frames+=num_frames;
1936         conversation->tx_bytes+=num_bytes;
1937     } else {
1938         conversation->rx_frames+=num_frames;
1939         conversation->rx_bytes+=num_bytes;
1940     }
1941
1942     if (ts) {
1943         if (nstime_cmp(ts, &conversation->stop_time) > 0) {
1944             memcpy(&conversation->stop_time, ts, sizeof(conversation->stop_time));
1945         } else if (nstime_cmp(ts, &conversation->start_time) < 0) {
1946             memcpy(&conversation->start_time, ts, sizeof(conversation->start_time));
1947         }
1948     }
1949 }
1950
1951 /*
1952  * Editor modelines
1953  *
1954  * Local Variables:
1955  * c-basic-offset: 4
1956  * tab-width: 8
1957  * indent-tabs-mode: nil
1958  * End:
1959  *
1960  * ex: set shiftwidth=4 tabstop=8 expandtab
1961  * :indentSize=4:tabSize=8:noTabs=true:
1962  */
1963