f06c8226a7c57079599ab2658ff781eba489e5c2
[obnox/wireshark/wip.git] / gtk / expert_comp_table.c
1 /* expert_comp_table.c
2  * expert_comp_table   2005 Greg Morris
3  * Portions copied from service_response_time_table.c by Ronnie Sahlberg
4  * Helper routines common to all composite expert statistics
5  * 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 <stdlib.h>
34 #include <stdio.h>
35 #include <math.h>
36
37 #include <gtk/gtk.h>
38
39 #include "epan/packet_info.h"
40 #include "epan/strutil.h"
41
42 #include <epan/expert.h>
43
44 #include "../simple_dialog.h"
45 #include "../globals.h"
46 #include "../color.h"
47
48 #include "gtk/expert_comp_table.h"
49 #include "gtk/filter_utils.h"
50 #include "gtk/find_dlg.h"
51 #include "gtk/color_dlg.h"
52 #include "gtk/main.h"
53 #include "gtk/gui_utils.h"
54 #include "gtk/gtkglobals.h"
55 #include "gtk/webbrowser.h"
56 #include "gtk/stock_icons.h"
57
58 const char  *packet = "Packet:";
59
60 enum
61 {
62    GROUP_COLUMN,
63    PROTOCOL_COLUMN,
64    SUMMARY_COLUMN,
65    COUNT_COLUMN,
66    N_COLUMNS
67 };
68
69 static void
70 proto_data_func (GtkTreeViewColumn *column _U_,
71                            GtkCellRenderer   *renderer,
72                            GtkTreeModel      *model,
73                            GtkTreeIter       *iter,
74                            gpointer           user_data)
75 {       
76          gchar *str = NULL;
77          gchar *grp = NULL; /* type pointer, don't free */
78
79      /* The col to get data from is in userdata */
80      gint data_column = GPOINTER_TO_INT(user_data);
81
82      gtk_tree_model_get(model, iter, data_column, &str, -1);
83      gtk_tree_model_get(model, iter, GROUP_COLUMN, &grp, -1);
84          /* XXX should we check that str is non NULL and print a warning or do assert? */
85
86      g_object_set(renderer, "text", str, NULL);
87      if (grp == packet) {
88          /* it's a number right align */
89          g_object_set(renderer, "xalign", 1.0, NULL);
90      }
91      else {
92          g_object_set(renderer, "xalign", 0.0, NULL);
93      }
94      g_free(str);
95 }
96
97 static gint
98 proto_sort_func(GtkTreeModel *model,
99                                                         GtkTreeIter *a,
100                                                         GtkTreeIter *b,
101                                                         gpointer user_data)
102 {
103          gchar *str_a = NULL;
104          gchar *str_b = NULL;
105          gchar *grp = NULL; /* type pointer, don't free */
106          gint ret = 0;
107
108          /* The col to get data from is in userdata */
109          gint data_column = GPOINTER_TO_INT(user_data);
110
111      gtk_tree_model_get(model, a, data_column, &str_a, -1);
112      gtk_tree_model_get(model, b, data_column, &str_b, -1);
113      gtk_tree_model_get(model, a, GROUP_COLUMN, &grp, -1);
114
115         if (str_a == str_b) {
116                 ret = 0;
117         } 
118         else if (str_a == NULL || str_b == NULL) {
119                 ret = (str_a == NULL) ? -1 : 1;
120         } 
121         else {
122         if (grp == packet) {
123           gint int_a = atoi(str_a);
124           gint int_b = atoi(str_b);
125           if (int_a == int_b)
126               ret = 0;
127           else if (int_a < int_b)
128               ret = -1;
129           else 
130               ret = 1;
131         }
132         else
133                     ret = g_ascii_strcasecmp(str_a,str_b);
134         }
135         g_free(str_a);
136         g_free(str_b);
137         return ret;
138 }
139
140 static gint find_summary_data(error_equiv_table *err, const expert_info_t *expert_data)
141 {
142     guint i;
143     error_procedure_t *procedure;
144     
145     /* First time thru values will be 0 */
146     if (err->num_procs==0) {
147         return -1;
148     }
149     for (i=0;i<err->num_procs;i++) {
150         procedure = &g_array_index(err->procs_array, error_procedure_t, i);
151         if (strcmp(procedure->entries[0], expert_data->protocol) == 0 &&
152             strcmp(procedure->entries[1], expert_data->summary) == 0) {
153             return i;
154         }
155     }
156     return -1;
157 }
158
159 static void
160 error_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
161 {
162     int action, type, selection;
163     error_equiv_table *err = (error_equiv_table *)callback_data;
164     char str[512];
165     const char *current_filter;
166     error_procedure_t *procedure;
167
168     GtkTreeIter iter;
169     GtkTreeModel *model;
170     expert_info_t expert_data;
171     gchar *grp;
172
173     action=FILTER_ACTION(callback_action);
174     type=FILTER_ACTYPE(callback_action);
175
176
177         if(!gtk_tree_selection_get_selected(err->select, &model, &iter)){
178                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No selection made or the table is empty");
179                 return;
180         }
181
182     gtk_tree_model_get (model, &iter, 
183                         GROUP_COLUMN,    &grp,
184                         PROTOCOL_COLUMN, &expert_data.protocol,
185                         SUMMARY_COLUMN,  &expert_data.summary,
186                         -1);
187     
188     if (strcmp(grp, packet)==0) {
189         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You cannot filter or search for packet number. Click on a valid item header.");
190         g_free(expert_data.summary);
191         return;
192     }
193
194     /* XXX: find_summary_data doesn't (currently) reference expert_data.group.   */
195     /*      If "group" is required, then the message from GROUP_COLUMN will need */
196     /*       to be translated to the group number (or the actual group number    */
197     /*       will also need to be stored in the TreeModel).                      */
198     selection = find_summary_data(err, &expert_data);
199     /* g_free(expert_data.protocol); - const */
200     g_free(expert_data.summary);
201
202     if(selection>=(int)err->num_procs){
203         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No items are selected");
204         return;
205     }
206     current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
207
208     /* Some expert data doesn't pass an expert item. Without this we cannot create a filter */
209     /* But allow for searching of internet for error string */
210     procedure = &g_array_index(err->procs_array, error_procedure_t, selection);
211
212     if (action != ACTION_WEB_LOOKUP && action != ACTION_COPY) {
213         char *msg;
214         if (0 /*procedure->fvalue_value==NULL*/) {
215             if (action != ACTION_FIND_FRAME && action != ACTION_FIND_NEXT && action != ACTION_FIND_PREVIOUS) {
216                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Wireshark cannot create a filter on this item - %s, try using find instead.", 
217                               procedure->entries[1]);
218                 return;
219             }
220         }
221         msg = g_malloc(escape_string_len(procedure->entries[1]));
222         escape_string(msg, procedure->entries[1]);
223         switch(type){
224         case ACTYPE_SELECTED:
225             /* if no expert item was passed */
226             if (procedure->fvalue_value==NULL) {
227                 g_snprintf(str, sizeof(str), "expert.message==%s", msg);
228             }
229             else
230             {
231                 /* expert item exists. Use it. */
232                 g_strlcpy(str, procedure->fvalue_value, sizeof(str));
233             }
234             break;
235         case ACTYPE_NOT_SELECTED:
236             /* if no expert item was passed */
237             if (procedure->fvalue_value==NULL) {
238                 g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
239             }
240             else
241             {
242                 /* expert item exists. Use it. */
243                 g_snprintf(str, sizeof(str), "!(%s)", procedure->fvalue_value);
244             }
245             break;
246             /* the remaining cases will only exist if the expert item exists so no need to check */
247         case ACTYPE_AND_SELECTED:
248             if ((!current_filter) || (0 == strlen(current_filter)))
249                 g_snprintf(str, sizeof(str), "expert.message==%s", msg);
250             else
251                 g_snprintf(str, sizeof(str), "(%s) && (expert.message==%s)", current_filter, msg);
252             break;
253         case ACTYPE_OR_SELECTED:
254             if ((!current_filter) || (0 == strlen(current_filter)))
255                 g_snprintf(str, sizeof(str), "expert.message==%s", msg);
256             else
257                 g_snprintf(str, sizeof(str), "(%s) || (expert.message==%s)", current_filter, msg);
258             break;
259         case ACTYPE_AND_NOT_SELECTED:
260             if ((!current_filter) || (0 == strlen(current_filter)))
261                 g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
262             else
263                 g_snprintf(str, sizeof(str), "(%s) && !(expert.message==%s)", current_filter, msg);
264             break;
265         case ACTYPE_OR_NOT_SELECTED:
266             if ((!current_filter) || (0 == strlen(current_filter)))
267                 g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
268             else
269                 g_snprintf(str, sizeof(str), "(%s) || !(expert.message==%s)", current_filter, msg);
270             break;
271         default:
272             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Can't find menu type - %u", type);
273         }
274         g_free(msg);
275     }
276
277     switch(action){
278     case ACTION_MATCH:
279         main_filter_packets(&cfile, str, FALSE);
280         break;
281     case ACTION_PREPARE:
282         gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
283         break;
284     case ACTION_FIND_FRAME:
285         /* When trying to perform a find without expert item, we must pass
286          * the expert string to the find window. The user might need to modify
287          * the string and click on the text search to locate the packet in question.
288          * So regardless of the type we will just bring up the find window and allow
289          * the user to modify the search criteria and options.
290          */
291         find_frame_with_filter(str);
292         break;
293     case ACTION_FIND_NEXT:
294         /* In the case of find next, if there was no expert item, then most likely the expert
295          * string was modified to locate the text inside the message. So we can't just perform
296          * a find with the expert string or we will not really be performing a find next.
297          * In an effort to allow the user to modify the string and/or continue searching, we
298          * will just present the user with the find window again with the default expert string.
299          * A better aproach would be to attempt in capturing the last find string and utilize this 
300          * with a find next/previous. Also a better approach might be to just send a <Ctl-N> keystroke.
301          */
302         if (procedure->fvalue_value==NULL) {
303             find_frame_with_filter(str);
304         }
305         else
306         { 
307             /* We have an expert item so just continue search without find dialog. */
308             cf_find_packet_dfilter_string(&cfile, str, SD_FORWARD);
309         }
310         break;
311     case ACTION_FIND_PREVIOUS:
312         /* In the case of find previous, if there was no expert item, then most likely the expert
313          * string was modified to locate the text inside the message. So we can't just perform
314          * a find with the expert string or we will not really be performing a find previous.
315          * In an effort to allow the user to modify the string and/or continue searching, we
316          * will just present the user with the find window again with the default expert string.
317          * A better aproach would be to attempt in capturing the last find string and utilize this 
318          * with a find next/previous. Also a better approach might be to just send a <Ctl-B> keystroke.
319          */
320         if (procedure->fvalue_value==NULL) {
321             find_frame_with_filter(str);
322         }
323         else
324         { 
325             /* We have an expert item so just continue search without find dialog. */
326             cf_find_packet_dfilter_string(&cfile, str, SD_BACKWARD);
327         }
328         break;
329     case ACTION_COLORIZE:
330         color_display_with_filter(str);
331         break;
332     case ACTION_WEB_LOOKUP:
333         /* Lookup expert string on internet. Default search via www.google.com */
334         g_snprintf(str, sizeof(str), "http://www.google.com/search?hl=en&q=%s+'%s'", procedure->entries[0], procedure->entries[1]);
335         browser_open_url(str);
336         break;
337     case ACTION_COPY:
338         {
339             GString *copyString = g_string_sized_new(0);
340             g_string_printf(copyString, "%s:  %s",
341                             procedure->entries[0], procedure->entries[1]);
342             copy_to_clipboard(copyString);
343             g_string_free(copyString, TRUE);
344         }
345         break;
346
347     default:
348         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Can't find menu action - %u", action);
349     }
350 }
351
352 static gboolean
353 error_show_popup_menu_cb(void *widg _U_, GdkEvent *event, gpointer user_data)
354 {
355         error_equiv_table *err = user_data;
356         GdkEventButton *bevent = (GdkEventButton *)event;
357
358         if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
359                 gtk_menu_popup(GTK_MENU(err->menu), NULL, NULL, NULL, NULL, 
360                         bevent->button, bevent->time);
361         }
362
363         return FALSE;
364 }
365 #define EXPERT_USE_GUIMANAGER 1
366 #ifndef EXPERT_USE_GUIMANAGER
367 static GtkItemFactoryEntry error_list_menu_items[] =
368 {
369         /* Match */
370         {"/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL,},
371         {"/Apply as Filter/Selected", NULL,
372                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_SELECTED, 0),
373                 NULL, NULL,},
374         {"/Apply as Filter/... not Selected", NULL,
375                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0),
376                 NULL, NULL,},
377         {"/Apply as Filter/.. and Selected", NULL,
378                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0),
379                 NULL, NULL,},
380         {"/Apply as Filter/... or Selected", NULL,
381                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0),
382                 NULL, NULL,},
383         {"/Apply as Filter/... and not Selected", NULL,
384                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0),
385                 NULL, NULL,},
386         {"/Apply as Filter/... or not Selected", NULL,
387                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0),
388                 NULL, NULL,},
389
390         /* Prepare */
391         {"/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL,},
392         {"/Prepare a Filter/Selected", NULL,
393                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_SELECTED, 0),
394                 NULL, NULL,},
395         {"/Prepare a Filter/Not Selected", NULL,
396                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0),
397                 NULL, NULL,},
398         {"/Prepare a Filter/... and Selected", NULL,
399                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0),
400                 NULL, NULL,},
401         {"/Prepare a Filter/... or Selected", NULL,
402                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0),
403                 NULL, NULL,},
404         {"/Prepare a Filter/... and not Selected", NULL,
405                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0),
406                 NULL, NULL,},
407         {"/Prepare a Filter/... or not Selected", NULL,
408                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0),
409                 NULL, NULL,},
410
411         /* Find Frame */
412         {"/Find Frame", NULL, NULL, 0, "<Branch>", NULL,},
413         {"/Find Frame/Find Frame", NULL, NULL, 0, "<Branch>", NULL,},
414         {"/Find Frame/Find Frame/Selected", NULL,
415                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0),
416                 NULL, NULL,},
417         {"/Find Frame/Find Frame/Not Selected", NULL,
418                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0),
419                 NULL, NULL,},
420         /* Find Next */
421         {"/Find Frame/Find Next", NULL, NULL, 0, "<Branch>", NULL,},
422         {"/Find Frame/Find Next/Selected", NULL,
423                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0),
424                 NULL, NULL,},
425         {"/Find Frame/Find Next/Not Selected", NULL,
426                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0),
427                 NULL, NULL,},
428
429         /* Find Previous */
430         {"/Find Frame/Find Previous", NULL, NULL, 0, "<Branch>", NULL,},
431         {"/Find Frame/Find Previous/Selected", NULL,
432                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0),
433                 NULL, NULL,},
434         {"/Find Frame/Find Previous/Not Selected", NULL,
435                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0),
436                 NULL, NULL,},
437
438         /* Colorize Procedure */
439         {"/Colorize Procedure", NULL, NULL, 0, "<Branch>", NULL,},
440         {"/Colorize Procedure/Selected", NULL,
441                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_SELECTED, 0),
442                 NULL, NULL,},
443         {"/Colorize Procedure/Not Selected", NULL,
444                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_COLORIZE(ACTYPE_NOT_SELECTED, 0),
445                 NULL, NULL,},
446
447         /* Search Internet */
448         {"/Internet Search for Info Text", NULL,
449                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_WEB_LOOKUP, NULL, NULL,},
450
451         /* Copy item text (protocol plus summary)*/
452         {"/Copy", NULL,
453                 GTK_MENU_FUNC(error_select_filter_cb), CALLBACK_COPY, NULL, NULL,},
454
455 };
456
457
458 #else
459 /* Prepare to change GtkItemFactory to GtkUIManager */
460 static void
461 apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
462 {
463         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
464 }
465 static void
466 apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
467 {
468         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
469 }
470 static void
471 apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
472 {
473         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
474 }
475 static void
476 apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
477 {
478         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
479 }
480 static void
481 apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
482 {
483         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
484 }
485 static void
486 apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
487 {
488         error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
489 }
490
491 static void
492 prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
493 {
494         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
495 }
496 static void
497 prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
498 {
499         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
500 }
501 static void
502 prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
503 {
504         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
505 }
506 static void
507 prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
508 {
509         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
510 }
511 static void
512 prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
513 {
514         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
515 }
516 static void
517 prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
518 {
519         error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
520 }
521
522 static void
523 find_selected_cb(GtkWidget *widget, gpointer user_data)
524 {
525         error_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
526 }
527 static void
528 find_not_selected_cb(GtkWidget *widget, gpointer user_data)
529 {
530         error_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
531 }
532 static void
533 find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
534 {
535         error_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
536 }
537 static void
538 find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
539 {
540         error_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
541 }
542 static void
543 find_next_selected_cb(GtkWidget *widget, gpointer user_data)
544 {
545         error_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
546 }
547 static void
548 find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
549 {
550         error_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
551 }
552 static void
553 color_selected_cb(GtkWidget *widget, gpointer user_data)
554 {
555         error_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
556 }
557 static void
558 color_not_selected_cb(GtkWidget *widget, gpointer user_data)
559 {
560         error_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
561 }
562 static void
563 internet_search_cb(GtkWidget *widget, gpointer user_data)
564 {
565         error_select_filter_cb( widget , user_data, CALLBACK_WEB_LOOKUP);
566 }
567 static void
568 copy_cb(GtkWidget *widget, gpointer user_data)
569 {
570         error_select_filter_cb( widget , user_data, CALLBACK_COPY);
571 }
572
573 static const char *ui_desc_expert_filter_popup =
574 "<ui>\n"
575 "  <popup name='ExpertFilterPopup'>\n"
576 "    <menu action='/Apply as Filter'>\n"
577 "      <menuitem action='/Apply as Filter/Selected'/>\n"
578 "      <menuitem action='/Apply as Filter/... not Selected'/>\n"
579 "      <menuitem action='/Apply as Filter/... and Selected'/>\n"
580 "      <menuitem action='/Apply as Filter/... or Selected'/>\n"
581 "      <menuitem action='/Apply as Filter/... and not Selected'/>\n"
582 "      <menuitem action='/Apply as Filter/... or not Selected'/>\n"
583 "    </menu>\n"
584 "    <menu action='/Prepare a Filter'>\n"
585 "      <menuitem action='/Prepare a Filter/Selected'/>\n"
586 "      <menuitem action='/Prepare a Filter/... not Selected'/>\n"
587 "      <menuitem action='/Prepare a Filter/... and Selected'/>\n"
588 "      <menuitem action='/Prepare a Filter/... or Selected'/>\n"
589 "      <menuitem action='/Prepare a Filter/... and not Selected'/>\n"
590 "      <menuitem action='/Prepare a Filter/... or not Selected'/>\n"
591 "    </menu>\n"
592 "    <menu action='/Find Frame'>\n"
593 "      <menu action='/Find Frame/Find Frame'>\n"
594 "        <menuitem action='/Find Frame/Selected'/>\n"
595 "        <menuitem action='/Find Frame/Not Selected'/>\n"
596 "      </menu>\n"
597 "      <menu action='/Find Frame/Find Next'>\n"
598 "        <menuitem action='/Find Next/Selected'/>\n"
599 "        <menuitem action='/Find Next/Not Selected'/>\n"
600 "      </menu>\n"
601 "      <menu action='/Find Frame/Find Previous'>\n"
602 "        <menuitem action='/Find Previous/Selected'/>\n"
603 "        <menuitem action='/Find Previous/Not Selected'/>\n"
604 "      </menu>\n"
605 "    </menu>\n"
606 "    <menu action='/Colorize Procedure'>\n"
607 "     <menuitem action='/Colorize Procedure/Selected'/>\n"
608 "     <menuitem action='/Colorize Procedure/Not Selected'/>\n"
609 "    </menu>\n"
610 "    <menu action='/Internet Search'>\n"
611 "     <menuitem action='/For Info Text'/>\n"
612 "    </menu>\n"
613 "    <menu action='/Copy'>\n"
614 "     <menuitem action='/Copy/Protocol Plus Summary'/>\n"
615 "    </menu>\n"
616 "  </popup>\n"
617 "</ui>\n";
618
619
620 /* 
621  * GtkActionEntry
622  * typedef struct {
623  *   const gchar     *name;
624  *   const gchar     *stock_id;
625  *   const gchar     *label;
626  *   const gchar     *accelerator;
627  *   const gchar     *tooltip;
628  *   GCallback  callback;
629  * } GtkActionEntry;
630  * const gchar *name;                   The name of the action.  
631  * const gchar *stock_id;               The stock id for the action, or the name of an icon from the icon theme.  
632  * const gchar *label;                  The label for the action. This field should typically be marked for translation, 
633  *                                                              see gtk_action_group_set_translation_domain(). 
634  *                                                              If label is NULL, the label of the stock item with id stock_id is used.  
635  * const gchar *accelerator;    The accelerator for the action, in the format understood by gtk_accelerator_parse().  
636  * const gchar *tooltip;                The tooltip for the action. This field should typically be marked for translation, 
637  *                              see gtk_action_group_set_translation_domain().  
638  * GCallback callback;                  The function to call when the action is activated.  
639  *
640  */
641 static const GtkActionEntry expert_popup_entries[] = {
642   { "/Apply as Filter",                                                 NULL, "Apply as Filter",                                NULL, NULL,                                                             NULL },
643   { "/Prepare a Filter",                                                NULL, "Prepare a Filter",                               NULL, NULL,                                                             NULL },
644   { "/Find Frame",                                                              NULL, "Find Frame",                                             NULL, NULL,                                                             NULL },
645   { "/Find Frame/Find Frame",                                   NULL, "Find Frame",                                             NULL, NULL,                                                             NULL },
646   { "/Find Frame/Find Next",                                    NULL, "Find Next" ,                                             NULL, NULL,                                                             NULL },
647   { "/Find Frame/Find Previous",                                NULL, "Find Previous",                                  NULL, NULL,                                                             NULL },
648   { "/Colorize Procedure",                                              NULL, "Colorize Procedure",                             NULL, NULL,                                                             NULL },
649   { "/Apply as Filter/Selected",                                NULL, "Selected",                                               NULL, "Selected",                                               G_CALLBACK(apply_as_selected_cb) },
650   { "/Apply as Filter/... not Selected",                NULL, "... not Selected",                               NULL, "... not Selected",                               G_CALLBACK(apply_as_not_selected_cb) },
651   { "/Apply as Filter/... and Selected",                NULL, "... and Selected",                               NULL, "... and Selected",                               G_CALLBACK(apply_as_and_selected_cb) },
652   { "/Apply as Filter/... or Selected",                 NULL, "... or Selected",                                NULL, "... or Selected",                                G_CALLBACK(apply_as_or_selected_cb) },
653   { "/Apply as Filter/... and not Selected",    NULL, "... and not Selected",                   NULL, "... and not Selected",                   G_CALLBACK(apply_as_and_not_selected_cb) },
654   { "/Apply as Filter/... or not Selected",             NULL, "... or not Selected",                    NULL, "... or not Selected",                    G_CALLBACK(apply_as_or_not_selected_cb) },
655   { "/Prepare a Filter/Selected",                               NULL, "Selected",                                               NULL, "selcted",                                                G_CALLBACK(prep_as_selected_cb) },
656   { "/Prepare a Filter/... not Selected",               NULL, "... not Selected",                               NULL, "... not Selected",                               G_CALLBACK(prep_as_not_selected_cb) },
657   { "/Prepare a Filter/... and Selected",               NULL, "... and Selected",                               NULL, "... and Selected",                               G_CALLBACK(prep_as_and_selected_cb) },
658   { "/Prepare a Filter/... or Selected",                NULL, "... or Selected",                                NULL, "... or Selected",                                G_CALLBACK(prep_as_or_selected_cb) },
659   { "/Prepare a Filter/... and not Selected",   NULL, "... and not Selected",                   NULL, "... and not Selected",                   G_CALLBACK(prep_as_and_not_selected_cb) },
660   { "/Prepare a Filter/... or not Selected",    NULL, "... or not Selected",                    NULL, "... or not Selected",                    G_CALLBACK(prep_as_or_not_selected_cb) },
661   { "/Find Frame/Selected",                                             NULL, "Selected",                                               NULL, "Selected",                                               G_CALLBACK(find_selected_cb) },
662   { "/Find Frame/Not Selected",                                 NULL, "Not Selected",                                   NULL, "Not Selected",                                   G_CALLBACK(find_not_selected_cb) },
663   { "/Find Previous/Selected",                                  NULL, "Selected",                                               NULL, "Selected",                                               G_CALLBACK(find_prev_selected_cb) },
664   { "/Find Previous/Not Selected",                              NULL, "Not Selected",                                   NULL, "Not Selected",                                   G_CALLBACK(find_prev_not_selected_cb) },
665   { "/Find Next/Selected",                                              NULL, "Selected",                                               NULL, "Selected",                                               G_CALLBACK(find_next_selected_cb) },
666   { "/Find Next/Not Selected",                                  NULL, "Not Selected",                                   NULL, "Not Selected",                                   G_CALLBACK(find_next_not_selected_cb) },
667   { "/Colorize Procedure/Selected",                             NULL, "Selected",                                               NULL, "Selected",                                               G_CALLBACK(color_selected_cb) },
668   { "/Colorize Procedure/Not Selected",                 NULL, "Not Selected",                                   NULL, "Not Selected",                                   G_CALLBACK(color_not_selected_cb) },
669   { "/Internet Search",     WIRESHARK_STOCK_INTERNET, "Internet Search",                                NULL, "Internet Search",                                NULL },
670   { "/For Info Text",                                                   NULL, "For Info Text",                                  NULL, "For Info Text",                                  G_CALLBACK(internet_search_cb) },
671   { "/Copy",                                                                    NULL, "Copy",                                                   NULL, "Copy",                                                   NULL },
672   { "/Copy/Protocol Plus Summary",                              NULL, "Protocol Plus Summary",                  NULL, "Protocol Plus Summary",                  G_CALLBACK(copy_cb) },
673 };
674 #endif
675 static void
676 expert_goto_pkt_cb (GtkTreeSelection *selection, gpointer data _U_)
677 {
678         GtkTreeIter iter;
679         GtkTreeModel *model;
680         gchar *pkt;
681         gchar *grp;
682
683         if (gtk_tree_selection_get_selected (selection, &model, &iter))
684         {
685                 gtk_tree_model_get (model, &iter, 
686                                     PROTOCOL_COLUMN, &pkt,
687                                     GROUP_COLUMN,    &grp,
688                                     -1);
689
690                 if (strcmp(grp, packet)==0) {
691                     cf_goto_frame(&cfile, atoi(pkt));
692                 }
693                 g_free (pkt);
694         }
695 }
696
697 static void
698 error_create_popup_menu(error_equiv_table *err)
699 {
700 #ifndef EXPERT_USE_GUIMANAGER
701     GtkItemFactory *item_factory;
702
703     err->select = gtk_tree_view_get_selection (GTK_TREE_VIEW (err->tree_view));
704     gtk_tree_selection_set_mode (err->select, GTK_SELECTION_SINGLE);
705     g_signal_connect (G_OBJECT (err->select), "changed", G_CALLBACK(expert_goto_pkt_cb), NULL);
706     item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
707
708     gtk_item_factory_create_items_ac(item_factory, sizeof(error_list_menu_items)/sizeof(error_list_menu_items[0]), error_list_menu_items, err, 2);
709
710     err->menu = gtk_item_factory_get_widget(item_factory, "<main>");
711     g_signal_connect(err->tree_view, "button_press_event", G_CALLBACK(error_show_popup_menu_cb), err);
712 #else
713         GtkUIManager *ui_manager;
714         GtkActionGroup *action_group;
715         GError *error = NULL;
716
717     err->select = gtk_tree_view_get_selection (GTK_TREE_VIEW (err->tree_view));
718     gtk_tree_selection_set_mode (err->select, GTK_SELECTION_SINGLE);
719     g_signal_connect (G_OBJECT (err->select), "changed", G_CALLBACK(expert_goto_pkt_cb), NULL);
720
721         action_group = gtk_action_group_new ("ExpertFilterPopupActionGroup"); 
722         gtk_action_group_add_actions (action_group,                                                     /* the action group */
723                                                                 expert_popup_entries,                                   /* an array of action descriptions */
724                                                                 G_N_ELEMENTS(expert_popup_entries),             /* the number of entries */
725                                                                 err);                                                                   /* data to pass to the action callbacks */
726
727         ui_manager = gtk_ui_manager_new ();
728         gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
729         gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_expert_filter_popup, -1, &error); 
730         if (error != NULL) 
731     { 
732         fprintf (stderr, "Warning: building expert filter popup failed: %s\n", 
733                 error->message); 
734         g_error_free (error); 
735         error = NULL; 
736     } 
737         err->menu = gtk_ui_manager_get_widget(ui_manager, "/ExpertFilterPopup");
738         g_signal_connect(err->tree_view, "button_press_event", G_CALLBACK(error_show_popup_menu_cb), err);
739 #endif
740
741 }
742
743 void
744 init_error_table(error_equiv_table *err, guint num_procs, GtkWidget *vbox)
745 {
746     GtkTreeStore *store;
747     GtkWidget *tree;
748     GtkTreeViewColumn *column;
749     GtkCellRenderer *renderer;
750     GtkTreeSortable *sortable;
751
752     /* Create the store */
753     store = gtk_tree_store_new (4,       /* Total number of columns */
754                                G_TYPE_POINTER,   /* Group              */
755                                G_TYPE_STRING,   /* Protocol           */
756                                G_TYPE_STRING,   /* Summary            */
757                                G_TYPE_INT);     /* Count              */
758
759     /* Create a view */
760     tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
761     err->tree_view = GTK_TREE_VIEW(tree);
762     sortable = GTK_TREE_SORTABLE(store);
763
764 #if GTK_CHECK_VERSION(2,6,0)
765         /* Speed up the list display */
766         gtk_tree_view_set_fixed_height_mode(err->tree_view, TRUE);
767 #endif
768
769     gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW (tree), FALSE);
770
771     /* The view now holds a reference.  We can get rid of our own reference */
772     g_object_unref (G_OBJECT (store));
773
774     /* Create a cell renderer */
775     renderer = gtk_cell_renderer_text_new ();
776
777     /* Create the first column, associating the "text" attribute of the
778      * cell_renderer to the first column of the model */
779     column = gtk_tree_view_column_new_with_attributes ("Group", renderer, NULL);
780     gtk_tree_view_column_set_sort_column_id(column, GROUP_COLUMN);
781     gtk_tree_view_column_set_resizable(column, TRUE);
782         gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func, 
783                 GINT_TO_POINTER(GROUP_COLUMN), NULL);
784
785         gtk_tree_sortable_set_sort_func(sortable, GROUP_COLUMN, str_ptr_sort_func,
786                 GINT_TO_POINTER(GROUP_COLUMN), NULL);
787
788     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
789     gtk_tree_view_column_set_min_width(column, 80);
790     /* Add the column to the view. */
791     gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
792  
793     /* Second column.. Protocol. */
794     renderer = gtk_cell_renderer_text_new ();
795     column = gtk_tree_view_column_new_with_attributes ("Protocol", renderer, "text", PROTOCOL_COLUMN, NULL);
796     gtk_tree_view_column_set_sort_column_id(column, PROTOCOL_COLUMN);
797     gtk_tree_view_column_set_resizable(column, TRUE);
798         gtk_tree_view_column_set_cell_data_func(column, renderer, proto_data_func, 
799                 GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
800
801         gtk_tree_sortable_set_sort_func(sortable, PROTOCOL_COLUMN, proto_sort_func,
802                 GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
803
804     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
805     gtk_tree_view_column_set_min_width(column, 40);
806         gtk_tree_view_column_set_fixed_width(column, 100);
807     gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
808  
809     /* Third column.. Summary. */
810     renderer = gtk_cell_renderer_text_new ();
811     column = gtk_tree_view_column_new_with_attributes ("Summary", renderer, "text", SUMMARY_COLUMN, NULL);
812     gtk_tree_view_column_set_sort_column_id(column, SUMMARY_COLUMN);
813     gtk_tree_view_column_set_resizable(column, TRUE);
814     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
815     gtk_tree_view_column_set_min_width(column, 80);
816         gtk_tree_view_column_set_fixed_width(column, 230);
817     gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
818  
819     /* Last column.. Count. */
820     renderer = gtk_cell_renderer_text_new ();
821     /* right align */
822     g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
823     column = gtk_tree_view_column_new_with_attributes ("Count", renderer, "text", COUNT_COLUMN, NULL);
824     gtk_tree_view_column_set_sort_column_id(column, COUNT_COLUMN);
825     gtk_tree_view_column_set_resizable(column, TRUE);
826     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
827     gtk_tree_view_column_set_min_width(column, 80);
828     gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
829  
830     err->scrolled_window=scrolled_window_new(NULL, NULL);
831
832     gtk_container_add(GTK_CONTAINER(err->scrolled_window), GTK_WIDGET (err->tree_view));
833
834     gtk_box_pack_start(GTK_BOX(vbox), err->scrolled_window, TRUE, TRUE, 0);
835
836     gtk_tree_view_set_search_column (err->tree_view, SUMMARY_COLUMN); /* Allow searching the summary */
837     gtk_tree_view_set_reorderable (err->tree_view, TRUE);   /* Allow user to reorder data with drag n drop */
838     
839     /* Now enable the sorting of each column */
840     gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(err->tree_view), TRUE);
841     gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(err->tree_view), TRUE);
842
843     gtk_widget_show(err->scrolled_window);
844
845     err->num_procs=num_procs;
846
847     err->text = g_string_chunk_new(100);
848     err->procs_array = g_array_sized_new(FALSE, FALSE, sizeof(error_procedure_t), num_procs);
849
850     /* create popup menu for this table */
851     error_create_popup_menu(err);
852 }
853
854 void
855 init_error_table_row(error_equiv_table *err, const expert_info_t *expert_data)
856 {
857     guint old_num_procs=err->num_procs;
858     gint row=0;
859     error_procedure_t *procedure;
860     GtkTreeStore *store;
861     GtkTreeIter   new_iter;
862     gchar num[10];
863
864     /* we have discovered a new procedure. Extend the table accordingly */
865     row = find_summary_data(err, expert_data);
866     if(row==-1){
867         error_procedure_t new_procedure;
868         /* First time we have seen this event so initialize memory table */
869         row = old_num_procs; /* Number of expert events since this is a new event */
870
871         new_procedure.count=0; /* count of events for this item */
872         new_procedure.fvalue_value = NULL; /* Filter string value */
873
874         g_array_append_val(err->procs_array, new_procedure);
875         procedure = &g_array_index(err->procs_array, error_procedure_t, row);
876         
877         /* Create the item in our memory table */
878         procedure->entries[0]=(char *)g_string_chunk_insert_const(err->text, expert_data->protocol);    /* Protocol */
879         procedure->entries[1]=(char *)g_string_chunk_insert_const(err->text, expert_data->summary);     /* Summary */
880
881         /* Create a new item in our tree view */
882         store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view)); /* Get store */
883         gtk_tree_store_append (store, &procedure->iter, NULL);  /* Acquire an iterator */
884         
885         /* match_strval return a static constant  or null */        
886         gtk_tree_store_set (store, &procedure->iter,
887                     GROUP_COLUMN, match_strval(expert_data->group, expert_group_vals), 
888                     PROTOCOL_COLUMN, procedure->entries[0],
889                     SUMMARY_COLUMN,  procedure->entries[1], -1);
890         
891         /* If an expert item was passed then build the filter string */
892         if (expert_data->pitem) {
893             char *filter;
894             
895             g_assert(PITEM_FINFO(expert_data->pitem));
896             filter = proto_construct_match_selected_string(PITEM_FINFO(expert_data->pitem), NULL);
897             if (filter != NULL)
898                 procedure->fvalue_value = g_string_chunk_insert_const(err->text, filter);
899         }
900         /* Store the updated count of events */
901         err->num_procs = ++old_num_procs;
902     }
903
904     /* Update our memory table with event data */
905     procedure = &g_array_index(err->procs_array, error_procedure_t, row);
906     procedure->count++; /* increment the count of events for this item */
907
908     /* Update the tree with new count for this event */
909     store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view));
910     gtk_tree_store_set(store, &procedure->iter, 
911                        COUNT_COLUMN, procedure->count, 
912                                            -1);
913
914     g_snprintf(num, sizeof(num), "%d", expert_data->packet_num);
915 #if 0
916         This does not have a big performance improvment :(
917 if GTK_CHECK_VERSION(2,10,0)
918         gtk_tree_store_insert_with_values   (store,
919                        &new_iter,   /* *iter */
920                        &procedure->iter, /* *parent*/
921                        G_MAXINT,    /* position */
922
923 #else
924
925     /* FIXME gtk is plagued with slow algorithms 
926        gtk_tree_store_append call new_path and its nice recursive linear search....
927     */
928     if (procedure->count > 1000) {
929         /* If there's more than 1000 sub rows give up and prepend new rows, at least 
930            it will end in a reasonable time. Anyway with so many rows it's not
931            very useful and if sorted the right order is restored. 
932         */
933         gtk_tree_store_prepend(store, &new_iter, &procedure->iter);
934     }
935     else {
936         gtk_tree_store_append(store, &new_iter, &procedure->iter);
937     }
938     gtk_tree_store_set(store, &new_iter,
939 #endif
940                        GROUP_COLUMN,    packet,
941                        PROTOCOL_COLUMN, num,
942                        COUNT_COLUMN,    1,
943                        -1);
944 }
945
946 void
947 reset_error_table_data(error_equiv_table *err)
948 {
949     GtkTreeStore    *store;
950
951     store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view));
952     gtk_tree_store_clear(store);
953     err->num_procs = 0;
954     /* g_string_chunk_clear() is introduced in glib 2.14 */
955     g_string_chunk_free(err->text);
956     err->text = g_string_chunk_new(100);
957
958     g_array_set_size(err->procs_array, 0);
959 }
960
961 void
962 free_error_table_data(error_equiv_table *err)
963 {
964     err->num_procs=0;
965     g_string_chunk_free(err->text);
966     g_array_free(err->procs_array, TRUE);
967 }