Warningfix:
[obnox/wireshark/wip.git] / gtk / conversations_table.c
1 /* mem leak   should free the column_arrows when the table is destroyed */
2
3 /* conversations_table.c
4  * conversations_table   2003 Ronnie Sahlberg
5  * Helper routines common to all endpoint conversations tap.
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <string.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <math.h>
37
38 #include <gtk/gtk.h>
39
40 #include <epan/packet_info.h>
41 #include <epan/to_str.h>
42 #include <epan/address.h>
43 #include <epan/addr_resolv.h>
44 #include <epan/tap.h>
45 #include <epan/nstime.h>
46
47 #include "../simple_dialog.h"
48 #include "../globals.h"
49 #include "../color.h"
50
51 #include "gtk/sat.h"
52 #include "gtk/conversations_table.h"
53 #include "gtk/filter_utils.h"
54 #include "gtk/gtkglobals.h"
55 #include "gtk/gui_utils.h"
56 #include "gtk/dlg_utils.h"
57 #include "gtk/help_dlg.h"
58 #include "gtk/main.h"
59
60 #include "image/clist_ascend.xpm"
61 #include "image/clist_descend.xpm"
62
63
64 #define NUM_COLS 14
65 #define COL_STR_LEN 16
66 #define CONV_PTR_KEY "conversations-pointer"
67 #define NB_PAGES_KEY "notebook-pages"
68 #define NO_BPS_STR "N/A"
69
70 #define CMP_NUM(n1, n2)                         \
71     if ((n1) > (n2))                            \
72         return 1;                               \
73     else if ((n1) < (n2))                       \
74         return -1;                              \
75     else                                        \
76         return 0;
77
78 /* convert a port number into a string */
79 static char *
80 ct_port_to_str(int port_type, guint32 port)
81 {
82     static int i=0;
83     static gchar str[4][12];
84
85     switch(port_type){
86     case PT_TCP:
87     case PT_UDP:
88     case PT_SCTP:
89     case PT_NCP:
90         i = (i+1)%4;
91         g_snprintf(str[i], sizeof(str[0]), "%d", port);
92         return str[i];
93     }
94     return NULL;
95 }
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
279 typedef struct column_arrows {
280     GtkWidget *table;
281     GtkWidget *ascend_pm;
282     GtkWidget *descend_pm;
283 } column_arrows;
284
285
286
287 static void
288 reset_ct_table_data(conversations_table *ct)
289 {
290     guint32 i;
291     char title[256];
292     GString *error_string;
293     const char *filter;
294
295     if (ct->use_dfilter) {
296         filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
297     } else {
298         filter = ct->filter;
299     }
300
301     error_string = set_tap_dfilter (ct, filter);
302     if (error_string) {
303         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
304         g_string_free(error_string, TRUE);
305         return;
306     }
307
308     /* Allow clist to update */
309     gtk_clist_thaw(ct->table);
310
311     if(ct->page_lb) {
312         g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
313         gtk_window_set_title(GTK_WINDOW(ct->win), title);
314         g_snprintf(title, sizeof(title), "%s", ct->name);
315         gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
316         gtk_widget_set_sensitive(ct->page_lb, FALSE);
317
318         if (ct->use_dfilter) {
319             if (filter && strlen(filter)) {
320                 g_snprintf(title, sizeof(title), "%s Conversations - Filter: %s", ct->name, filter);
321             } else {
322                 g_snprintf(title, sizeof(title), "%s Conversations - No Filter", ct->name);
323             }
324         } else {
325             g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
326         }
327         gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
328     } else {
329         g_snprintf(title, sizeof(title), "%s Conversations: %s", ct->name, cf_get_display_name(&cfile));
330         gtk_window_set_title(GTK_WINDOW(ct->win), title);
331     }
332
333     /* remove all entries from the clist */
334     gtk_clist_clear(ct->table);
335
336     /* delete all conversations */
337     for(i=0;i<ct->num_conversations;i++){
338         g_free((gpointer)ct->conversations[i].src_address.data);
339         g_free((gpointer)ct->conversations[i].dst_address.data);
340     }
341     g_free(ct->conversations);
342     ct->conversations=NULL;
343     ct->num_conversations=0;
344 }
345
346 static void
347 reset_ct_table_data_cb(void *arg)
348 {
349     reset_ct_table_data(arg);
350 }
351
352 static void
353 ct_win_destroy_cb(GtkWindow *win _U_, gpointer data)
354 {
355     conversations_table *conversations=(conversations_table *)data;
356
357     protect_thread_critical_region();
358     remove_tap_listener(conversations);
359     unprotect_thread_critical_region();
360
361     reset_ct_table_data(conversations);
362     g_free(conversations);
363 }
364
365
366
367 static gint
368 ct_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
369 {
370     guint32 idx1, idx2;
371     conversations_table *ct = g_object_get_data(G_OBJECT(clist), CONV_PTR_KEY);
372     conv_t *conv1 = NULL;
373     conv_t *conv2 = NULL;
374     double duration1, duration2;
375
376     const GtkCListRow *row1 = ptr1;
377     const GtkCListRow *row2 = ptr2;
378
379     idx1 = GPOINTER_TO_INT(row1->data);
380     idx2 = GPOINTER_TO_INT(row2->data);
381
382     if (!ct || idx1 >= ct->num_conversations || idx2 >= ct->num_conversations)
383         return 0;
384
385     conv1 = &ct->conversations[idx1];
386     conv2 = &ct->conversations[idx2];
387
388     duration1 = nstime_to_sec(&conv1->stop_time) - nstime_to_sec(&conv1->start_time);
389     duration2 = nstime_to_sec(&conv2->stop_time) - nstime_to_sec(&conv2->start_time);
390
391     switch(clist->sort_column){
392     case 0: /* Source address */
393         return(CMP_ADDRESS(&conv1->src_address, &conv2->src_address));
394     case 2: /* Destination address */
395         return(CMP_ADDRESS(&conv1->dst_address, &conv2->dst_address));
396     case 1: /* Source port */
397         CMP_NUM(conv1->src_port, conv2->src_port);
398     case 3: /* Destination port */
399         CMP_NUM(conv1->dst_port, conv2->dst_port);
400     case 4: /* Packets */
401         CMP_NUM(conv1->tx_frames+conv1->rx_frames,
402                 conv2->tx_frames+conv2->rx_frames);
403     case 5: /* Bytes */
404         CMP_NUM(conv1->tx_bytes+conv1->rx_bytes,
405                 conv2->tx_bytes+conv2->rx_bytes);
406     case 6: /* Packets A->B */
407         CMP_NUM(conv1->tx_frames, conv2->tx_frames);
408     case 7: /* Bytes A->B */
409         CMP_NUM(conv1->tx_bytes, conv2->tx_bytes);
410     case 8: /* Packets A<-B */
411         CMP_NUM(conv1->rx_frames, conv2->rx_frames);
412     case 9: /* Bytes A<-B */
413         CMP_NUM(conv1->rx_bytes, conv2->rx_bytes);
414     case 10: /* Start time */
415         return nstime_cmp(&conv1->start_time, &conv2->start_time);
416     case 11: /* Duration */
417         CMP_NUM(duration1, duration2);
418     case 12: /* bps A->B */
419         if (duration1 > 0 && conv1->tx_frames > 1 && duration2 > 0 && conv2->tx_frames > 1) {
420             CMP_NUM((gint64) conv1->tx_bytes / duration1, (gint64) conv2->tx_bytes / duration2);
421         } else {
422             CMP_NUM(conv1->tx_bytes, conv2->tx_bytes);
423         }
424     case 13: /* bps A<-B */
425         if (duration1 > 0 && conv1->rx_frames > 1 && duration2 > 0 && conv2->rx_frames > 1) {
426             CMP_NUM((gint64) conv1->rx_bytes / duration1, (gint64) conv2->rx_bytes / duration2);
427         } else {
428             CMP_NUM(conv1->rx_bytes, conv2->rx_bytes);
429         }
430     default:
431         g_assert_not_reached();
432     }
433
434     return 0;
435 }
436
437
438 static void
439 ct_click_column_cb(GtkCList *clist, gint column, gpointer data)
440 {
441     column_arrows *col_arrows = (column_arrows *) data;
442     int i;
443
444     for (i = 0; i < NUM_COLS; i++) {
445         gtk_widget_hide(col_arrows[i].ascend_pm);
446         gtk_widget_hide(col_arrows[i].descend_pm);
447     }
448
449     if (column == clist->sort_column) {
450         if (clist->sort_type == GTK_SORT_ASCENDING) {
451             clist->sort_type = GTK_SORT_DESCENDING;
452             gtk_widget_show(col_arrows[column].descend_pm);
453         } else {
454             clist->sort_type = GTK_SORT_ASCENDING;
455             gtk_widget_show(col_arrows[column].ascend_pm);
456         }
457     } else {
458         clist->sort_type = GTK_SORT_ASCENDING;
459         gtk_widget_show(col_arrows[column].ascend_pm);
460         gtk_clist_set_sort_column(clist, column);
461     }
462
463     gtk_clist_sort(clist);
464
465     /* Allow update of clist */
466     gtk_clist_thaw(clist);
467     gtk_clist_freeze(clist);
468
469 }
470
471
472 /* Filter direction */
473 #define DIR_A_TO_FROM_B         0
474 #define DIR_A_TO_B              1
475 #define DIR_A_FROM_B            2
476 #define DIR_A_TO_FROM_ANY       3
477 #define DIR_A_TO_ANY            4
478 #define DIR_A_FROM_ANY          5
479 #define DIR_ANY_TO_FROM_B       6
480 #define DIR_ANY_FROM_B          7
481 #define DIR_ANY_TO_B            8
482
483 static void
484 ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
485 {
486     int direction;
487     int selection;
488     conversations_table *ct = (conversations_table *)callback_data;
489     char *str = NULL;
490     char *sport, *dport;
491
492     direction=FILTER_EXTRA(callback_action);
493
494     selection=GPOINTER_TO_INT(g_list_nth_data(GTK_CLIST(ct->table)->selection, 0));
495     if(selection>=(int)ct->num_conversations){
496         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected");
497         return;
498     }
499     /* translate it back from row index to index in enndpoint array */
500     selection=GPOINTER_TO_INT(gtk_clist_get_row_data(ct->table, selection));
501
502     sport=ct_port_to_str(ct->conversations[selection].port_type, ct->conversations[selection].src_port);
503     dport=ct_port_to_str(ct->conversations[selection].port_type, ct->conversations[selection].dst_port);
504
505     switch(direction){
506     case DIR_A_TO_FROM_B:
507         /* A <-> B */
508         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
509                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_ADDRESS),
510                               address_to_str(&ct->conversations[selection].src_address),
511                               sport?" && ":"",
512                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_PORT):"",
513                               sport?"==":"",
514                               sport?sport:"",
515                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_ADDRESS),
516                               address_to_str(&ct->conversations[selection].dst_address),
517                               dport?" && ":"",
518                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_PORT):"",
519                               dport?"==":"",
520                               dport?dport:""
521             );
522         break;
523     case DIR_A_TO_B:
524         /* A --> B */
525         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
526                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_ADDRESS),
527                               address_to_str(&ct->conversations[selection].src_address),
528                               sport?" && ":"",
529                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_PORT):"",
530                               sport?"==":"",
531                               sport?sport:"",
532                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_ADDRESS),
533                               address_to_str(&ct->conversations[selection].dst_address),
534                               dport?" && ":"",
535                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_PORT):"",
536                               dport?"==":"",
537                               dport?dport:""
538             );
539         break;
540     case DIR_A_FROM_B:
541         /* A <-- B */
542         str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
543                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_ADDRESS),
544                               address_to_str(&ct->conversations[selection].src_address),
545                               sport?" && ":"",
546                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_PORT):"",
547                               sport?"==":"",
548                               sport?sport:"",
549                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_ADDRESS),
550                               address_to_str(&ct->conversations[selection].dst_address),
551                               dport?" && ":"",
552                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_PORT):"",
553                               dport?"==":"",
554                               dport?dport:""
555             );
556         break;
557     case DIR_A_TO_FROM_ANY:
558         /* A <-> ANY */
559         str = g_strdup_printf("%s==%s%s%s%s%s",
560                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_ADDRESS),
561                               address_to_str(&ct->conversations[selection].src_address),
562                               sport?" && ":"",
563                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_PORT):"",
564                               sport?"==":"",
565                               sport?sport:""
566             );
567         break;
568     case DIR_A_TO_ANY:
569         /* A --> ANY */
570         str = g_strdup_printf("%s==%s%s%s%s%s",
571                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_ADDRESS),
572                               address_to_str(&ct->conversations[selection].src_address),
573                               sport?" && ":"",
574                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_PORT):"",
575                               sport?"==":"",
576                               sport?sport:""
577             );
578         break;
579     case DIR_A_FROM_ANY:
580         /* A <-- ANY */
581         str = g_strdup_printf("%s==%s%s%s%s%s",
582                               ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_ADDRESS),
583                               address_to_str(&ct->conversations[selection].src_address),
584                               sport?" && ":"",
585                               sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_PORT):"",
586                               sport?"==":"",
587                               sport?sport:""
588             );
589         break;
590     case DIR_ANY_TO_FROM_B:
591         /* ANY <-> B */
592         str = g_strdup_printf("%s==%s%s%s%s%s",
593                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_ADDRESS),
594                               address_to_str(&ct->conversations[selection].dst_address),
595                               dport?" && ":"",
596                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_ANY_PORT):"",
597                               dport?"==":"",
598                               dport?dport:""
599             );
600         break;
601     case DIR_ANY_FROM_B:
602         /* ANY <-- B */
603         str = g_strdup_printf("%s==%s%s%s%s%s",
604                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_ADDRESS),
605                               address_to_str(&ct->conversations[selection].dst_address),
606                               dport?" && ":"",
607                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_SRC_PORT):"",
608                               dport?"==":"",
609                               dport?dport:""
610             );
611         break;
612     case DIR_ANY_TO_B:
613         /* ANY --> B */
614         str = g_strdup_printf("%s==%s%s%s%s%s",
615                               ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_ADDRESS),
616                               address_to_str(&ct->conversations[selection].dst_address),
617                               dport?" && ":"",
618                               dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type,  FN_DST_PORT):"",
619                               dport?"==":"",
620                               dport?dport:""
621             );
622         break;
623     default:
624         g_assert_not_reached();
625     }
626
627     apply_selected_filter (callback_action, str);
628
629     g_free (str);
630 }
631
632 static gint
633 ct_show_popup_menu_cb(void *widg _U_, GdkEvent *event, conversations_table *ct)
634 {
635     GdkEventButton *bevent = (GdkEventButton *)event;
636     gint row;
637     gint column;
638
639
640     /* To quote the "Gdk Event Structures" doc:
641      * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
642     if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
643         /* if this is a right click on one of our columns, select it and popup the context menu */
644         if(gtk_clist_get_selection_info(ct->table,
645                                         (gint) (((GdkEventButton *)event)->x),
646                                         (gint) (((GdkEventButton *)event)->y),
647                                         &row, &column)) {
648             gtk_clist_unselect_all(ct->table);
649             gtk_clist_select_row(ct->table, row, -1);
650
651             gtk_menu_popup(GTK_MENU(ct->menu), NULL, NULL, NULL, NULL,
652                            bevent->button, bevent->time);
653         }
654     }
655
656     return FALSE;
657 }
658
659 static GtkItemFactoryEntry ct_list_menu_items[] =
660 {
661     /* Match */
662     {"/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL,},
663     {"/Apply as Filter/Selected", NULL, NULL, 0, "<Branch>", NULL,},
664     {"/Apply as Filter/Selected/A <-> B", NULL,
665      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
666     {"/Apply as Filter/Selected/A --> B", NULL,
667      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
668     {"/Apply as Filter/Selected/A <-- B", NULL,
669      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
670     {"/Apply as Filter/Selected/A <-> ANY", NULL,
671      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
672     {"/Apply as Filter/Selected/A --> ANY", NULL,
673      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
674     {"/Apply as Filter/Selected/A <-- ANY", NULL,
675      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
676     {"/Apply as Filter/Selected/ANY <-> B", NULL,
677      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
678     {"/Apply as Filter/Selected/ANY <-- B", NULL,
679      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
680     {"/Apply as Filter/Selected/ANY --> B", NULL,
681      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
682
683     {"/Apply as Filter/Not Selected", NULL, NULL, 0, "<Branch>", NULL,},
684     {"/Apply as Filter/Not Selected/A <-> B", NULL,
685      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
686     {"/Apply as Filter/Not Selected/A --> B", NULL,
687      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
688     {"/Apply as Filter/Not Selected/A <-- B", NULL,
689      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
690     {"/Apply as Filter/Not Selected/A --> ANY", NULL,
691      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
692     {"/Apply as Filter/Not Selected/A <-> ANY", NULL,
693      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
694     {"/Apply as Filter/Not Selected/A <-- ANY", NULL,
695      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
696     {"/Apply as Filter/Not Selected/ANY <-> B", NULL,
697      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
698     {"/Apply as Filter/Not Selected/ANY <-- B", NULL,
699      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
700     {"/Apply as Filter/Not Selected/ANY --> B", NULL,
701      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
702
703
704     {"/Apply as Filter/... and Selected", NULL, NULL, 0, "<Branch>", NULL,},
705     {"/Apply as Filter/... and Selected/A <-> B", NULL,
706      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
707     {"/Apply as Filter/... and Selected/A --> B", NULL,
708      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_B), NULL, NULL,},
709     {"/Apply as Filter/... and Selected/A <-- B", NULL,
710      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_B), NULL, NULL,},
711     {"/Apply as Filter/... and Selected/A <-> ANY", NULL,
712      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
713     {"/Apply as Filter/... and Selected/A --> ANY", NULL,
714      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
715     {"/Apply as Filter/... and Selected/A <-- ANY", NULL,
716      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
717     {"/Apply as Filter/... and Selected/ANY <-> B", NULL,
718      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
719     {"/Apply as Filter/... and Selected/ANY <-- B", NULL,
720      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
721     {"/Apply as Filter/... and Selected/ANY --> B", NULL,
722      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
723
724     {"/Apply as Filter/... or Selected", NULL, NULL, 0, "<Branch>", NULL,},
725     {"/Apply as Filter/... or Selected/A <-> B", NULL,
726      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
727     {"/Apply as Filter/... or Selected/A --> B", NULL,
728      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_B), NULL, NULL,},
729     {"/Apply as Filter/... or Selected/A <-- B", NULL,
730      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_B), NULL, NULL,},
731     {"/Apply as Filter/... or Selected/A <-> ANY", NULL,
732      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
733     {"/Apply as Filter/... or Selected/A --> ANY", NULL,
734      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
735     {"/Apply as Filter/... or Selected/A <-- ANY", NULL,
736      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
737     {"/Apply as Filter/... or Selected/ANY <-> B", NULL,
738      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
739     {"/Apply as Filter/... or Selected/ANY <-- B", NULL,
740      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
741     {"/Apply as Filter/... or Selected/ANY --> B", NULL,
742      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
743
744     {"/Apply as Filter/... and not Selected", NULL, NULL, 0, "<Branch>", NULL,},
745     {"/Apply as Filter/... and not Selected/A <-> B", NULL,
746      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
747     {"/Apply as Filter/... and not Selected/A --> B", NULL,
748      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
749     {"/Apply as Filter/... and not Selected/A <-- B", NULL,
750      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
751     {"/Apply as Filter/... and not Selected/A <-> ANY", NULL,
752      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
753     {"/Apply as Filter/... and not Selected/A --> ANY", NULL,
754      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
755     {"/Apply as Filter/... and not Selected/A <-- ANY", NULL,
756      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
757     {"/Apply as Filter/... and not Selected/ANY <-> B", NULL,
758      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
759     {"/Apply as Filter/... and not Selected/ANY <-- B", NULL,
760      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
761     {"/Apply as Filter/... and not Selected/ANY --> B", NULL,
762      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
763
764     {"/Apply as Filter/... or not Selected", NULL, NULL, 0, "<Branch>", NULL,},
765     {"/Apply as Filter/... or not Selected/A <-> B", NULL,
766      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
767     {"/Apply as Filter/... or not Selected/A --> B", NULL,
768      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
769     {"/Apply as Filter/... or not Selected/A <-- B", NULL,
770      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
771     {"/Apply as Filter/... or not Selected/A <-> ANY", NULL,
772      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
773     {"/Apply as Filter/... or not Selected/A --> ANY", NULL,
774      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
775     {"/Apply as Filter/... or not Selected/A <-- ANY", NULL,
776      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
777     {"/Apply as Filter/... or not Selected/ANY <-> B", NULL,
778      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
779     {"/Apply as Filter/... or not Selected/ANY <-- B", NULL,
780      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
781     {"/Apply as Filter/... or not Selected/ANY --> B", NULL,
782      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
783
784     /* Prepare */
785     {"/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL,},
786     {"/Prepare a Filter/Selected", NULL, NULL, 0, "<Branch>", NULL,},
787     {"/Prepare a Filter/Selected/A <-> B", NULL,
788      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
789     {"/Prepare a Filter/Selected/A --> B", NULL,
790      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
791     {"/Prepare a Filter/Selected/A <-- B", NULL,
792      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
793     {"/Prepare a Filter/Selected/A <-> ANY", NULL,
794      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
795     {"/Prepare a Filter/Selected/A --> ANY", NULL,
796      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
797     {"/Prepare a Filter/Selected/A <-- ANY", NULL,
798      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
799     {"/Prepare a Filter/Selected/ANY <-> B", NULL,
800      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
801     {"/Prepare a Filter/Selected/ANY <-- B", NULL,
802      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
803     {"/Prepare a Filter/Selected/ANY --> B", NULL,
804      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
805
806     {"/Prepare a Filter/Not Selected", NULL, NULL, 0, "<Branch>", NULL,},
807     {"/Prepare a Filter/Not Selected/A <-> B", NULL,
808      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
809     {"/Prepare a Filter/Not Selected/A --> B", NULL,
810      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
811     {"/Prepare a Filter/Not Selected/A <-- B", NULL,
812      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
813     {"/Prepare a Filter/Not Selected/A <-> ANY", NULL,
814      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
815     {"/Prepare a Filter/Not Selected/A --> ANY", NULL,
816      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
817     {"/Prepare a Filter/Not Selected/A <-- ANY", NULL,
818      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
819     {"/Prepare a Filter/Not Selected/ANY <-> B", NULL,
820      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
821     {"/Prepare a Filter/Not Selected/ANY <-- B", NULL,
822      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
823     {"/Prepare a Filter/Not Selected/ANY --> B", NULL,
824      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
825
826     {"/Prepare a Filter/... and Selected", NULL, NULL, 0, "<Branch>", NULL,},
827     {"/Prepare a Filter/... and Selected/A <-> B", NULL,
828      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
829     {"/Prepare a Filter/... and Selected/A --> B", NULL,
830      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_B), NULL, NULL,},
831     {"/Prepare a Filter/... and Selected/A <-- B", NULL,
832      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_B), NULL, NULL,},
833     {"/Prepare a Filter/... and Selected/A <-> ANY", NULL,
834      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
835     {"/Prepare a Filter/... and Selected/A --> ANY", NULL,
836      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
837     {"/Prepare a Filter/... and Selected/A <-- ANY", NULL,
838      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
839     {"/Prepare a Filter/... and Selected/ANY <-> B", NULL,
840      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
841     {"/Prepare a Filter/... and Selected/ANY <-- B", NULL,
842      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
843     {"/Prepare a Filter/... and Selected/ANY --> B", NULL,
844      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
845
846     {"/Prepare a Filter/... or Selected", NULL, NULL, 0, "<Branch>", NULL,},
847     {"/Prepare a Filter/... or Selected/A <-> B", NULL,
848      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
849     {"/Prepare a Filter/... or Selected/A --> B", NULL,
850      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_B), NULL, NULL,},
851     {"/Prepare a Filter/... or Selected/A <-- B", NULL,
852      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_B), NULL, NULL,},
853     {"/Prepare a Filter/... or Selected/A <-> ANY", NULL,
854      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
855     {"/Prepare a Filter/... or Selected/A --> ANY", NULL,
856      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
857     {"/Prepare a Filter/... or Selected/A <-- ANY", NULL,
858      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
859     {"/Prepare a Filter/... or Selected/ANY <-> B", NULL,
860      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
861     {"/Prepare a Filter/... or Selected/ANY <-- B", NULL,
862      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
863     {"/Prepare a Filter/... or Selected/ANY --> B", NULL,
864      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
865
866     {"/Prepare a Filter/... and not Selected", NULL, NULL, 0, "<Branch>", NULL,},
867     {"/Prepare a Filter/... and not Selected/A <-> B", NULL,
868      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
869     {"/Prepare a Filter/... and not Selected/A --> B", NULL,
870      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
871     {"/Prepare a Filter/... and not Selected/A <-- B", NULL,
872      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
873     {"/Prepare a Filter/... and not Selected/A <-> ANY", NULL,
874      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
875     {"/Prepare a Filter/... and not Selected/A --> ANY", NULL,
876      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
877     {"/Prepare a Filter/... and not Selected/A <-- ANY", NULL,
878      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
879     {"/Prepare a Filter/... and not Selected/ANY <-> B", NULL,
880      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
881     {"/Prepare a Filter/... and not Selected/ANY <-- B", NULL,
882      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
883     {"/Prepare a Filter/... and not Selected/ANY --> B", NULL,
884      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
885
886     {"/Prepare a Filter/... or not Selected", NULL, NULL, 0, "<Branch>", NULL,},
887     {"/Prepare a Filter/... or not Selected/A <-> B", NULL,
888      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
889     {"/Prepare a Filter/... or not Selected/A --> B", NULL,
890      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_B), NULL, NULL,},
891     {"/Prepare a Filter/... or not Selected/A <-- B", NULL,
892      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B), NULL, NULL,},
893     {"/Prepare a Filter/... or not Selected/A <-> ANY", NULL,
894      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
895     {"/Prepare a Filter/... or not Selected/A --> ANY", NULL,
896      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
897     {"/Prepare a Filter/... or not Selected/A <-- ANY", NULL,
898      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
899     {"/Prepare a Filter/... or not Selected/ANY <-> B", NULL,
900      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
901     {"/Prepare a Filter/... or not Selected/ANY <-- B", NULL,
902      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
903     {"/Prepare a Filter/... or not Selected/ANY --> B", NULL,
904      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
905
906     /* Find Packet */
907     {"/Find Packet", NULL, NULL, 0, "<Branch>", NULL,},
908     {"/Find Packet/Find Packet", NULL, NULL, 0, "<Branch>", NULL,},
909     {"/Find Packet/Find Packet/A <-> B", NULL,
910      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
911     {"/Find Packet/Find Packet/A --> B", NULL,
912      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
913     {"/Find Packet/Find Packet/A <-- B", NULL,
914      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
915     {"/Find Packet/Find Packet/A <-> ANY", NULL,
916      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
917     {"/Find Packet/Find Packet/A --> ANY", NULL,
918      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
919     {"/Find Packet/Find Packet/A <-- ANY", NULL,
920      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
921     {"/Find Packet/Find Packet/ANY <-> B", NULL,
922      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
923     {"/Find Packet/Find Packet/ANY <-- B", NULL,
924      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
925     {"/Find Packet/Find Packet/ANY --> B", NULL,
926      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
927     /* Find Next */
928     {"/Find Packet/Find Next", NULL, NULL, 0, "<Branch>", NULL,},
929     {"/Find Packet/Find Next/A <-> B", NULL,
930      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
931     {"/Find Packet/Find Next/A --> B", NULL,
932      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
933     {"/Find Packet/Find Next/A <-- B", NULL,
934      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
935     {"/Find Packet/Find Next/A <-> ANY", NULL,
936      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
937     {"/Find Packet/Find Next/A --> ANY", NULL,
938      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
939     {"/Find Packet/Find Next/A <-- ANY", NULL,
940      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
941     {"/Find Packet/Find Next/ANY <-> B", NULL,
942      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
943     {"/Find Packet/Find Next/ANY <-- B", NULL,
944      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
945     {"/Find Packet/Find Next/ANY --> B", NULL,
946      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
947     /* Find Previous */
948     {"/Find Packet/Find Previous", NULL, NULL, 0, "<Branch>", NULL,},
949     {"/Find Packet/Find Previous/A <-> B", NULL,
950      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
951     {"/Find Packet/Find Previous/A --> B", NULL,
952      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
953     {"/Find Packet/Find Previous/A <-- B", NULL,
954      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
955     {"/Find Packet/Find Previous/A <-> ANY", NULL,
956      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
957     {"/Find Packet/Find Previous/A --> ANY", NULL,
958      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
959     {"/Find Packet/Find Previous/A <-- ANY", NULL,
960      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
961     {"/Find Packet/Find Previous/ANY <-> B", NULL,
962      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
963     {"/Find Packet/Find Previous/ANY <-- B", NULL,
964      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
965     {"/Find Packet/Find Previous/ANY --> B", NULL,
966      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,},
967
968     /* Colorize Conversation */
969     {"/Colorize Conversation", NULL, NULL, 0, "<Branch>", NULL,},
970     {"/Colorize Conversation/A <-> B", NULL,
971      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_B), NULL, NULL,},
972     {"/Colorize Conversation/A --> B", NULL,
973      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_B), NULL, NULL,},
974     {"/Colorize Conversation/A <-- B", NULL,
975      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_B), NULL, NULL,},
976     {"/Colorize Conversation/A <-> ANY", NULL,
977      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY), NULL, NULL,},
978     {"/Colorize Conversation/A --> ANY", NULL,
979      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_ANY), NULL, NULL,},
980     {"/Colorize Conversation/A <-- ANY", NULL,
981      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_ANY), NULL, NULL,},
982     {"/Colorize Conversation/ANY <-> B", NULL,
983      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B), NULL, NULL,},
984     {"/Colorize Conversation/ANY <-- B", NULL,
985      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_FROM_B), NULL, NULL,},
986     {"/Colorize Conversation/ANY --> B", NULL,
987      GTK_MENU_FUNC(ct_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_B), NULL, NULL,}
988 };
989
990 static void
991 ct_create_popup_menu(conversations_table *ct)
992 {
993     GtkItemFactory *item_factory;
994
995     item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
996
997     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);
998
999     ct->menu = gtk_item_factory_get_widget(item_factory, "<main>");
1000     g_signal_connect(ct->table, "button_press_event", G_CALLBACK(ct_show_popup_menu_cb), ct);
1001 }
1002
1003 /* Draw/refresh the address fields of a single entry at the specified index */
1004 static void
1005 draw_ct_table_address(conversations_table *ct, int conversation_idx)
1006 {
1007     const char *entry;
1008     char *port;
1009     guint32 pt;
1010     int rownum;
1011
1012     rownum=gtk_clist_find_row_from_data(ct->table, (gpointer)(long)conversation_idx);
1013
1014     if(!ct->resolve_names)
1015         entry=address_to_str(&ct->conversations[conversation_idx].src_address);
1016     else {
1017         entry=get_addr_name(&ct->conversations[conversation_idx].src_address);
1018     }
1019     gtk_clist_set_text(ct->table, rownum, 0, entry);
1020
1021     pt = ct->conversations[conversation_idx].port_type;
1022     if(!ct->resolve_names) pt = PT_NONE;
1023     switch(pt) {
1024     case(PT_TCP):
1025         entry=get_tcp_port(ct->conversations[conversation_idx].src_port);
1026         break;
1027     case(PT_UDP):
1028         entry=get_udp_port(ct->conversations[conversation_idx].src_port);
1029         break;
1030     case(PT_SCTP):
1031         entry=get_sctp_port(ct->conversations[conversation_idx].src_port);
1032         break;
1033     default:
1034         port=ct_port_to_str(ct->conversations[conversation_idx].port_type, ct->conversations[conversation_idx].src_port);
1035         entry=port?port:"";
1036     }
1037     gtk_clist_set_text(ct->table, rownum, 1, entry);
1038
1039     if(!ct->resolve_names)
1040         entry=address_to_str(&ct->conversations[conversation_idx].dst_address);
1041     else {
1042         entry=get_addr_name(&ct->conversations[conversation_idx].dst_address);
1043     }
1044     gtk_clist_set_text(ct->table, rownum, 2, entry);
1045
1046     switch(pt) {
1047     case(PT_TCP):
1048         entry=get_tcp_port(ct->conversations[conversation_idx].dst_port);
1049         break;
1050     case(PT_UDP):
1051         entry=get_udp_port(ct->conversations[conversation_idx].dst_port);
1052         break;
1053     case(PT_SCTP):
1054         entry=get_sctp_port(ct->conversations[conversation_idx].dst_port);
1055         break;
1056     default:
1057         port=ct_port_to_str(ct->conversations[conversation_idx].port_type, ct->conversations[conversation_idx].dst_port);
1058         entry=port?port:"";
1059     }
1060     gtk_clist_set_text(ct->table, rownum, 3, entry);
1061 }
1062
1063 /* Refresh the address fields of all entries in the list */
1064 static void
1065 draw_ct_table_addresses(conversations_table *ct)
1066 {
1067     guint32 i;
1068
1069     for(i=0;i<ct->num_conversations;i++){
1070         draw_ct_table_address(ct, i);
1071     }
1072 }
1073
1074
1075 static void
1076 draw_ct_table_data(conversations_table *ct)
1077 {
1078     guint32 i;
1079     int j;
1080     char title[256];
1081     double duration_s;
1082
1083     if (ct->page_lb) {
1084         if(ct->num_conversations) {
1085             g_snprintf(title, sizeof(title), "%s: %u", ct->name, ct->num_conversations);
1086         } else {
1087             g_snprintf(title, sizeof(title), "%s", ct->name);
1088         }
1089         gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
1090         gtk_widget_set_sensitive(ct->page_lb, ct->num_conversations);
1091     } else {
1092         if(ct->num_conversations) {
1093             g_snprintf(title, sizeof(title), "%s Conversations: %u", ct->name, ct->num_conversations);
1094         } else {
1095             g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
1096         }
1097         gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
1098     }
1099
1100     for(i=0;i<ct->num_conversations;i++){
1101         char str[COL_STR_LEN];
1102
1103         j=gtk_clist_find_row_from_data(ct->table, (gpointer)(unsigned long)i);
1104
1105         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_frames+ct->conversations[i].rx_frames);
1106         gtk_clist_set_text(ct->table, j, 4, str);
1107         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_bytes+ct->conversations[i].rx_bytes);
1108         gtk_clist_set_text(ct->table, j, 5, str);
1109
1110
1111         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_frames);
1112         gtk_clist_set_text(ct->table, j, 6, str);
1113         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_bytes);
1114         gtk_clist_set_text(ct->table, j, 7, str);
1115
1116
1117         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].rx_frames);
1118         gtk_clist_set_text(ct->table, j, 8, str);
1119         g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].rx_bytes);
1120         gtk_clist_set_text(ct->table, j, 9, str);
1121
1122         duration_s = nstime_to_sec(&ct->conversations[i].stop_time) - nstime_to_sec(&ct->conversations[i].start_time);
1123         g_snprintf(str, COL_STR_LEN, "%s", rel_time_to_secs_str(&ct->conversations[i].start_time));
1124         gtk_clist_set_text(ct->table, j, 10, str);
1125         g_snprintf(str, COL_STR_LEN, "%.4f", duration_s);
1126         gtk_clist_set_text(ct->table, j, 11, str);
1127         if (duration_s > 0 && ct->conversations[i].tx_frames > 1) {
1128             /* XXX - The gint64 casts below are needed for MSVC++ 6.0 */
1129             g_snprintf(str, COL_STR_LEN, "%.2f", (gint64) ct->conversations[i].tx_bytes * 8 / duration_s);
1130             gtk_clist_set_text(ct->table, j, 12, str);
1131         } else {
1132             gtk_clist_set_text(ct->table, j, 12, NO_BPS_STR);
1133         }
1134         if (duration_s > 0 && ct->conversations[i].rx_frames > 1) {
1135             /* XXX - The gint64 casts below are needed for MSVC++ 6.0 */
1136             g_snprintf(str, COL_STR_LEN, "%.2f", (gint64) ct->conversations[i].rx_bytes * 8 / duration_s);
1137             gtk_clist_set_text(ct->table, j, 13, str);
1138         } else {
1139             gtk_clist_set_text(ct->table, j, 13, NO_BPS_STR);
1140         }
1141     }
1142
1143     draw_ct_table_addresses(ct);
1144
1145     gtk_clist_sort(ct->table);
1146
1147     /* Allow table to redraw */
1148     gtk_clist_thaw(ct->table);
1149     gtk_clist_freeze(ct->table);
1150 }
1151
1152 static void
1153 draw_ct_table_data_cb(void *arg)
1154 {
1155     draw_ct_table_data(arg);
1156 }
1157
1158 static void
1159 copy_as_csv_cb(GtkWindow *copy_bt, gpointer data _U_)
1160 {
1161     guint32         i,j;
1162     gchar           *table_entry;
1163     GtkClipboard    *cb;
1164     GString         *CSV_str = g_string_new("");
1165
1166     conversations_table *talkers=g_object_get_data(G_OBJECT(copy_bt), CONV_PTR_KEY);
1167     if (!talkers)
1168         return;
1169
1170     /* Add the column headers to the CSV data */
1171     for(i=0;i<talkers->num_columns;i++){                  /* all columns         */
1172         if((i==1 || i==3) && !talkers->has_ports) continue;  /* Don't add the port column if it's empty */
1173         g_string_append(CSV_str,talkers->default_titles[i]);/* add the column heading to the CSV string */
1174         if(i!=talkers->num_columns-1)
1175             g_string_append(CSV_str,",");
1176     }
1177     g_string_append(CSV_str,"\n");                        /* new row */
1178
1179     /* Add the column values to the CSV data */
1180     for(i=0;i<talkers->num_conversations;i++){            /* all rows            */
1181         for(j=0;j<talkers->num_columns;j++){                 /* all columns         */
1182             if((j==1 || j==3) && !talkers->has_ports) continue; /* Don't add the port column if it's empty */
1183             gtk_clist_get_text(talkers->table,i,j,&table_entry);/* copy table item into string */
1184             g_string_append(CSV_str,table_entry);               /* add the table entry to the CSV string */
1185             if(j!=talkers->num_columns-1)
1186                 g_string_append(CSV_str,",");
1187         }
1188         g_string_append(CSV_str,"\n");                       /* new row */
1189     }
1190
1191     /* Now that we have the CSV data, copy it into the default clipboard */
1192     cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);      /* Get the default clipboard */
1193     gtk_clipboard_set_text(cb, CSV_str->str, -1);         /* Copy the CSV data into the clipboard */
1194     g_string_free(CSV_str, TRUE);                         /* Free the memory */
1195 }
1196
1197
1198 static gboolean
1199 init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1200 {
1201     int i;
1202     column_arrows *col_arrows;
1203     GtkStyle *win_style;
1204     GtkWidget *column_lb;
1205     GString *error_string;
1206     char title[256];
1207
1208     conversations->page_lb=NULL;
1209     conversations->resolve_names=TRUE;
1210     conversations->has_ports=!hide_ports;
1211     conversations->num_columns=NUM_COLS;
1212     conversations->default_titles[0]="Address A",
1213         conversations->default_titles[1]="Port A";
1214     conversations->default_titles[2]="Address B";
1215     conversations->default_titles[3]="Port B";
1216     conversations->default_titles[4]="Packets";
1217     conversations->default_titles[5]="Bytes";
1218     conversations->default_titles[6]="Packets A->B";
1219     conversations->default_titles[7]="Bytes A->B";
1220     conversations->default_titles[8]="Packets A<-B";
1221     conversations->default_titles[9]="Bytes A<-B";
1222     conversations->default_titles[10]="Rel Start";
1223     conversations->default_titles[11]="Duration";
1224     conversations->default_titles[12]="bps A->B";
1225     conversations->default_titles[13]="bps A<-B";
1226     if (strcmp(table_name, "NCP")==0) {
1227         conversations->default_titles[1]="Connection A";
1228         conversations->default_titles[3]="Connection B";
1229     }
1230
1231     g_snprintf(title, sizeof(title), "%s Conversations", table_name);
1232     conversations->name_lb=gtk_label_new(title);
1233     gtk_box_pack_start(GTK_BOX(vbox), conversations->name_lb, FALSE, FALSE, 0);
1234
1235
1236     conversations->scrolled_window=scrolled_window_new(NULL, NULL);
1237     gtk_box_pack_start(GTK_BOX(vbox), conversations->scrolled_window, TRUE, TRUE, 0);
1238
1239     conversations->table=(GtkCList *)gtk_clist_new(NUM_COLS);
1240     g_object_set_data(G_OBJECT(conversations->table), CONV_PTR_KEY, conversations);
1241
1242     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
1243     win_style = gtk_widget_get_style(conversations->scrolled_window);
1244     for (i = 0; i < NUM_COLS; i++) {
1245         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
1246         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
1247         column_lb = gtk_label_new(conversations->default_titles[i]);
1248         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1249         gtk_widget_show(column_lb);
1250
1251         col_arrows[i].ascend_pm = xpm_to_widget((const char **) clist_ascend_xpm);
1252         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
1253         col_arrows[i].descend_pm = xpm_to_widget((const char **) clist_descend_xpm);
1254         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
1255         /* make total frames be the default sort order */
1256         if (i == 4) {
1257             gtk_widget_show(col_arrows[i].ascend_pm);
1258         }
1259         gtk_clist_set_column_widget(GTK_CLIST(conversations->table), i, col_arrows[i].table);
1260         gtk_widget_show(col_arrows[i].table);
1261     }
1262     gtk_clist_column_titles_show(GTK_CLIST(conversations->table));
1263
1264     gtk_clist_set_compare_func(conversations->table, ct_sort_column);
1265     gtk_clist_set_sort_column(conversations->table, 4);
1266     gtk_clist_set_sort_type(conversations->table, GTK_SORT_ASCENDING);
1267
1268
1269     for (i = 0; i < NUM_COLS; i++) {
1270         gtk_clist_set_column_auto_resize(conversations->table, i, TRUE);
1271     }
1272
1273     gtk_clist_set_shadow_type(conversations->table, GTK_SHADOW_IN);
1274     gtk_clist_column_titles_show(conversations->table);
1275     gtk_container_add(GTK_CONTAINER(conversations->scrolled_window), (GtkWidget *)conversations->table);
1276
1277     g_signal_connect(conversations->table, "click-column", G_CALLBACK(ct_click_column_cb), col_arrows);
1278
1279     conversations->num_conversations=0;
1280     conversations->conversations=NULL;
1281
1282     /* hide srcport and dstport if we don't use ports */
1283     if(hide_ports){
1284         gtk_clist_set_column_visibility(conversations->table, 1, FALSE);
1285         gtk_clist_set_column_visibility(conversations->table, 3, FALSE);
1286     }
1287
1288     /* create popup menu for this table */
1289     ct_create_popup_menu(conversations);
1290
1291     /* register the tap and rerun the taps on the packet list */
1292     error_string=register_tap_listener(tap_name, conversations, filter, reset_ct_table_data_cb, packet_func, draw_ct_table_data_cb);
1293     if(error_string){
1294         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1295         g_string_free(error_string, TRUE);
1296         return FALSE;
1297     }
1298
1299     return TRUE;
1300 }
1301
1302
1303 void
1304 init_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1305 {
1306     conversations_table *conversations;
1307     char title[256];
1308     GtkWidget *vbox;
1309     GtkWidget *bbox;
1310     GtkWidget *close_bt, *help_bt;
1311     gboolean ret;
1312     GtkWidget *copy_bt;
1313     GtkTooltips *tooltips = gtk_tooltips_new();
1314
1315     conversations=g_malloc(sizeof(conversations_table));
1316
1317     conversations->name=table_name;
1318     conversations->filter=filter;
1319     conversations->use_dfilter=FALSE;
1320     g_snprintf(title, sizeof(title), "%s Conversations: %s", table_name, cf_get_display_name(&cfile));
1321     conversations->win=window_new(GTK_WINDOW_TOPLEVEL, title);
1322
1323     gtk_window_set_default_size(GTK_WINDOW(conversations->win), 750, 400);
1324
1325     vbox=gtk_vbox_new(FALSE, 3);
1326     gtk_container_add(GTK_CONTAINER(conversations->win), vbox);
1327     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1328
1329     ret = init_ct_table_page(conversations, vbox, hide_ports, table_name, tap_name, filter, packet_func);
1330     if(ret == FALSE) {
1331         g_free(conversations);
1332         return;
1333     }
1334
1335     /* Button row. */
1336     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1337     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1338     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
1339     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
1340
1341     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1342     window_set_cancel_button(conversations->win, close_bt, window_cancel_button_cb);
1343
1344     copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
1345     gtk_tooltips_set_tip(tooltips, copy_bt,
1346                          "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.", NULL);
1347     g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, conversations);
1348     g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
1349
1350     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1351     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
1352
1353     g_signal_connect(conversations->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1354     g_signal_connect(conversations->win, "destroy", G_CALLBACK(ct_win_destroy_cb), conversations);
1355
1356     gtk_widget_show_all(conversations->win);
1357     window_present(conversations->win);
1358
1359     cf_retap_packets(&cfile, FALSE);
1360     gdk_window_raise(conversations->win->window);
1361
1362     /* Keep clist frozen to cause modifications to the clist (inserts, appends, others that are extremely slow
1363        in GTK2) to not be drawn, allow refreshes to occur at strategic points for performance */
1364     gtk_clist_freeze(conversations->table);
1365
1366     /* after retapping, redraw table */
1367     draw_ct_table_data(conversations);
1368 }
1369
1370
1371
1372 static void
1373 ct_nb_switch_page_cb(GtkNotebook *nb, GtkNotebookPage *pg _U_, guint page, gpointer data)
1374 {
1375     GtkWidget *copy_bt = (GtkWidget *) data;
1376     void ** pages = g_object_get_data(G_OBJECT(nb), NB_PAGES_KEY);
1377
1378     page++;
1379
1380     if (pages && page > 0 && (int) page <= GPOINTER_TO_INT(pages[0]) && copy_bt) {
1381         g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
1382     }
1383 }
1384
1385 static void
1386 ct_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
1387 {
1388     void ** pages = data;
1389     int page;
1390
1391     /* first "page" contains the number of pages */
1392     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1393         ct_win_destroy_cb(NULL, pages[page]);
1394     }
1395     g_free(pages);
1396 }
1397
1398 static conversations_table *
1399 init_ct_notebook_page_cb(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1400 {
1401     gboolean ret;
1402     GtkWidget *page_vbox;
1403     conversations_table *conversations;
1404
1405     conversations=g_malloc(sizeof(conversations_table));
1406     conversations->name=table_name;
1407     conversations->filter=filter;
1408     conversations->resolve_names=TRUE;
1409     conversations->use_dfilter=FALSE;
1410
1411     page_vbox=gtk_vbox_new(FALSE, 6);
1412     conversations->win = page_vbox;
1413     gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
1414
1415     ret = init_ct_table_page(conversations, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
1416     if(ret == FALSE) {
1417         g_free(conversations);
1418         return NULL;
1419     }
1420
1421     return conversations;
1422 }
1423
1424
1425 typedef struct {
1426     gboolean hide_ports;       /* hide TCP / UDP port columns */
1427     const char *table_name;    /* GUI output name */
1428     const char *tap_name;      /* internal name */
1429     const char *filter;        /* display filter string (unused) */
1430     tap_packet_cb packet_func; /* function to be called for new incoming packets */
1431 } register_ct_t;
1432
1433
1434 static GSList *registered_ct_tables = NULL;
1435
1436 void
1437 register_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
1438 {
1439     register_ct_t *table;
1440
1441     table = g_malloc(sizeof(register_ct_t));
1442
1443     table->hide_ports   = hide_ports;
1444     table->table_name   = table_name;
1445     table->tap_name     = tap_name;
1446     table->filter       = filter;
1447     table->packet_func  = packet_func;
1448
1449     registered_ct_tables = g_slist_append(registered_ct_tables, table);
1450 }
1451
1452
1453 static void
1454 ct_resolve_toggle_dest(GtkWidget *widget, gpointer data)
1455 {
1456     int page;
1457     void ** pages = data;
1458     gboolean resolve_names;
1459     conversations_table *conversations;
1460
1461
1462     resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1463
1464     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1465         conversations = pages[page];
1466         conversations->resolve_names = resolve_names;
1467
1468         draw_ct_table_addresses(conversations);
1469
1470         /* Allow table to redraw */
1471         gtk_clist_thaw(conversations->table);
1472         gtk_clist_freeze(conversations->table);
1473     }
1474 }
1475
1476
1477 static void
1478 ct_filter_toggle_dest(GtkWidget *widget, gpointer data)
1479 {
1480     int page;
1481     void ** pages = data;
1482     gboolean use_filter;
1483     conversations_table *conversations = NULL;
1484
1485     use_filter = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
1486
1487     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1488         conversations = pages[page];
1489         conversations->use_dfilter = use_filter;
1490         reset_ct_table_data(conversations);
1491     }
1492
1493     cf_retap_packets(&cfile, FALSE);
1494     if (conversations) {
1495         gdk_window_raise(conversations->win->window);
1496     }
1497
1498     /* after retapping, redraw table */
1499     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1500         draw_ct_table_data(pages[page]);
1501     }
1502 }
1503
1504
1505 void
1506 init_conversation_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
1507 {
1508     conversations_table *conversations;
1509     char title[256];
1510     GtkWidget *vbox;
1511     GtkWidget *hbox;
1512     GtkWidget *bbox;
1513     GtkWidget *close_bt, *help_bt;
1514     GtkWidget *win;
1515     GtkWidget *resolv_cb;
1516     GtkWidget *filter_cb;
1517     int page;
1518     void ** pages;
1519     GtkWidget *nb;
1520     GtkWidget *page_lb;
1521     GSList  *current_table;
1522     register_ct_t *registered;
1523     GtkTooltips *tooltips = gtk_tooltips_new();
1524     GtkWidget *copy_bt;
1525
1526     pages = g_malloc(sizeof(void *) * (g_slist_length(registered_ct_tables) + 1));
1527
1528     g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
1529     win=window_new(GTK_WINDOW_TOPLEVEL, title);
1530     gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
1531
1532     vbox=gtk_vbox_new(FALSE, 6);
1533     gtk_container_add(GTK_CONTAINER(win), vbox);
1534     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
1535
1536     nb = gtk_notebook_new();
1537     gtk_container_add(GTK_CONTAINER(vbox), nb);
1538     g_object_set_data(G_OBJECT(nb), NB_PAGES_KEY, pages);
1539
1540     page = 0;
1541
1542     current_table = registered_ct_tables;
1543     while(current_table) {
1544         registered = current_table->data;
1545         page_lb = gtk_label_new("");
1546         conversations = init_ct_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
1547                                                  registered->filter, registered->packet_func);
1548         g_object_set_data(G_OBJECT(conversations->win), CONV_PTR_KEY, conversations);
1549         gtk_notebook_append_page(GTK_NOTEBOOK(nb), conversations->win, page_lb);
1550         conversations->win = win;
1551         conversations->page_lb = page_lb;
1552         pages[++page] = conversations;
1553
1554         current_table = g_slist_next(current_table);
1555     }
1556
1557     pages[0] = GINT_TO_POINTER(page);
1558
1559     hbox = gtk_hbox_new(FALSE, 3);
1560     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1561
1562     resolv_cb = gtk_check_button_new_with_mnemonic("Name resolution");
1563     gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
1564     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
1565     gtk_tooltips_set_tip(tooltips, resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
1566                          "Please note: The corresponding name resolution must be enabled.", NULL);
1567
1568     g_signal_connect(resolv_cb, "toggled", G_CALLBACK(ct_resolve_toggle_dest), pages);
1569
1570     filter_cb = gtk_check_button_new_with_mnemonic("Limit to display filter");
1571     gtk_container_add(GTK_CONTAINER(hbox), filter_cb);
1572     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
1573     gtk_tooltips_set_tip(tooltips, filter_cb, "Limit the list to conversations matching the current display filter.", NULL);
1574
1575     g_signal_connect(filter_cb, "toggled", G_CALLBACK(ct_filter_toggle_dest), pages);
1576
1577     /* Button row. */
1578     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1579     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1580     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
1581
1582     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
1583
1584     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1585     window_set_cancel_button(win, close_bt, window_cancel_button_cb);
1586
1587     copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
1588     gtk_tooltips_set_tip(tooltips, copy_bt,
1589                          "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.", NULL);
1590     g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
1591     g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
1592
1593     g_signal_connect(nb, "switch-page", G_CALLBACK(ct_nb_switch_page_cb), copy_bt);
1594
1595     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1596     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
1597
1598     g_signal_connect(win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1599     g_signal_connect(win, "destroy", G_CALLBACK(ct_win_destroy_notebook_cb), pages);
1600
1601     gtk_widget_show_all(win);
1602     window_present(win);
1603
1604     cf_retap_packets(&cfile, FALSE);
1605     gdk_window_raise(win->window);
1606
1607     /* after retapping, redraw table */
1608     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
1609         draw_ct_table_data(pages[page]);
1610     }
1611 }
1612
1613
1614 void
1615 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)
1616 {
1617     const address *addr1, *addr2;
1618     guint32 port1, port2;
1619     conv_t *conversation=NULL;
1620     int conversation_idx=0;
1621     gboolean new_conversation;
1622
1623     if(src_port>dst_port){
1624         addr1=src;
1625         addr2=dst;
1626         port1=src_port;
1627         port2=dst_port;
1628     } else if(src_port<dst_port){
1629         addr2=src;
1630         addr1=dst;
1631         port2=src_port;
1632         port1=dst_port;
1633     } else if(CMP_ADDRESS(src, dst)<0){
1634         addr1=src;
1635         addr2=dst;
1636         port1=src_port;
1637         port2=dst_port;
1638     } else {
1639         addr2=src;
1640         addr1=dst;
1641         port2=src_port;
1642         port1=dst_port;
1643     }
1644
1645
1646     new_conversation=FALSE;
1647     /* XXX should be optimized to allocate n extra entries at a time
1648        instead of just one */
1649     /* if we dont have any entries at all yet */
1650     if(ct->conversations==NULL){
1651         ct->conversations=g_malloc(sizeof(conv_t));
1652         ct->num_conversations=1;
1653         conversation=&ct->conversations[0];
1654         conversation_idx=0;
1655         new_conversation=TRUE;
1656     }
1657
1658     /* try to find it among the existing known conversations */
1659     if(conversation==NULL){
1660         guint32 i;
1661         for(i=0;i<ct->num_conversations;i++){
1662             if(  (!CMP_ADDRESS(&ct->conversations[i].src_address, addr1))&&(!CMP_ADDRESS(&ct->conversations[i].dst_address, addr2))&&(ct->conversations[i].src_port==port1)&&(ct->conversations[i].dst_port==port2) ){
1663                 conversation=&ct->conversations[i];
1664                 conversation_idx=i;
1665                 break;
1666             }
1667             if( (!CMP_ADDRESS(&ct->conversations[i].src_address, addr2))&&(!CMP_ADDRESS(&ct->conversations[i].dst_address, addr1))&&(ct->conversations[i].src_port==port2)&&(ct->conversations[i].dst_port==port1) ){
1668                 conversation=&ct->conversations[i];
1669                 conversation_idx=i;
1670                 break;
1671             }
1672         }
1673     }
1674
1675     /* if we still dont know what conversation this is it has to be a new one
1676        and we have to allocate it and append it to the end of the list */
1677     if(conversation==NULL){
1678         new_conversation=TRUE;
1679         ct->num_conversations++;
1680         ct->conversations=g_realloc(ct->conversations, ct->num_conversations*sizeof(conv_t));
1681         conversation=&ct->conversations[ct->num_conversations-1];
1682         conversation_idx=ct->num_conversations-1;
1683     }
1684
1685     /* if this is a new conversation we need to initialize the struct */
1686     if(new_conversation){
1687         COPY_ADDRESS(&conversation->src_address, addr1);
1688         COPY_ADDRESS(&conversation->dst_address, addr2);
1689         conversation->sat=sat;
1690         conversation->port_type=port_type;
1691         conversation->src_port=port1;
1692         conversation->dst_port=port2;
1693         conversation->rx_frames=0;
1694         conversation->tx_frames=0;
1695         conversation->rx_bytes=0;
1696         conversation->tx_bytes=0;
1697         if (ts) {
1698             memcpy(&conversation->start_time, ts, sizeof(conversation->start_time));
1699             memcpy(&conversation->stop_time, ts, sizeof(conversation->stop_time));
1700         } else {
1701             nstime_set_unset(&conversation->start_time);
1702             nstime_set_unset(&conversation->stop_time);
1703         }
1704     }
1705
1706     /* update the conversation struct */
1707     if( (!CMP_ADDRESS(src, addr1))&&(!CMP_ADDRESS(dst, addr2))&&(src_port==port1)&&(dst_port==port2) ){
1708         conversation->tx_frames+=num_frames;
1709         conversation->tx_bytes+=num_bytes;
1710     } else {
1711         conversation->rx_frames+=num_frames;
1712         conversation->rx_bytes+=num_bytes;
1713     }
1714
1715     if (ts) {
1716         if (nstime_cmp(ts, &conversation->stop_time) > 0) {
1717             memcpy(&conversation->stop_time, ts, sizeof(conversation->stop_time));
1718         } else if (nstime_cmp(ts, &conversation->start_time) < 0) {
1719             memcpy(&conversation->start_time, ts, sizeof(conversation->start_time));
1720         }
1721     }
1722
1723     /* if this was a new conversation we have to create a clist row for it */
1724     if(new_conversation){
1725         char *entries[NUM_COLS];
1726         char frames[COL_STR_LEN], bytes[COL_STR_LEN],
1727             txframes[COL_STR_LEN], txbytes[COL_STR_LEN],
1728             rxframes[COL_STR_LEN], rxbytes[COL_STR_LEN],
1729             start_time[COL_STR_LEN], duration[COL_STR_LEN],
1730             txbps[COL_STR_LEN], rxbps[COL_STR_LEN];
1731         double duration_s;
1732
1733         /* these values will be filled by call to draw_ct_table_addresses() below */
1734         entries[0] = "";
1735         entries[1] = "";
1736         entries[2] = "";
1737         entries[3] = "";
1738
1739         g_snprintf(frames, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_frames+conversation->rx_frames);
1740         entries[4]=frames;
1741         g_snprintf(bytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_bytes+conversation->rx_bytes);
1742         entries[5]=bytes;
1743
1744         g_snprintf(txframes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_frames);
1745         entries[6]=txframes;
1746         g_snprintf(txbytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_bytes);
1747         entries[7]=txbytes;
1748
1749         g_snprintf(rxframes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->rx_frames);
1750         entries[8]=rxframes;
1751         g_snprintf(rxbytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->rx_bytes);
1752         entries[9]=rxbytes;
1753
1754         duration_s = nstime_to_sec(&conversation->start_time) - nstime_to_sec(&conversation->stop_time);
1755         g_snprintf(start_time, COL_STR_LEN, "%s", rel_time_to_secs_str(&conversation->start_time));
1756         g_snprintf(duration, COL_STR_LEN, "%.4f", duration_s);
1757         entries[10]=start_time;
1758         entries[11]=duration;
1759         if (duration_s > 0 && conversation->tx_frames > 1) {
1760             g_snprintf(txbps, COL_STR_LEN, "%.2f", (gint64) conversation->tx_bytes * 8 / duration_s);
1761             entries[12]=txbps;
1762         } else {
1763             entries[12] = NO_BPS_STR;
1764         }
1765         if (duration_s > 0 && conversation->rx_frames > 1) {
1766             g_snprintf(rxbps, COL_STR_LEN, "%.2f", (gint64) conversation->rx_bytes * 8 / duration_s);
1767             entries[13]=rxbps;
1768         } else {
1769             entries[13] = NO_BPS_STR;
1770         }
1771
1772         gtk_clist_insert(ct->table, conversation_idx, entries);
1773         gtk_clist_set_row_data(ct->table, conversation_idx, (gpointer)(long) conversation_idx);
1774
1775         draw_ct_table_address(ct, conversation_idx);
1776     }
1777 }
1778
1779 /*
1780  * Editor modelines
1781  *
1782  * Local Variables:
1783  * c-basic-offset: 4
1784  * tab-width: 8
1785  * indent-tabs-mode: nil
1786  * End:
1787  *
1788  * ex: set shiftwidth=4 tabstop=8 expandtab
1789  * :indentSize=4:tabSize=8:noTabs=true:
1790  */
1791